xref: /btstack/test/mesh/btstack_uart_posix_pty.c (revision cd5f23a3250874824c01a2b3326a9522fea3f99f)
1 /*
2  * Copyright (C) 2016 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define __BTSTACK_FILE__ "btstack_uart_posix.c"
39 
40 /*
41  *  btstack_uart_block_posix.c
42  *
43  *  Common code to access serial port via asynchronous block read/write commands
44  *
45  */
46 
47 #include "btstack_uart_block.h"
48 #include "btstack_run_loop.h"
49 #include "btstack_debug.h"
50 
51 #include <termios.h>  /* POSIX terminal control definitions */
52 #include <fcntl.h>    /* File control definitions */
53 #include <unistd.h>   /* UNIX standard function definitions */
54 #include <string.h>
55 #include <errno.h>
56 #ifdef __APPLE__
57 #include <sys/ioctl.h>
58 #include <IOKit/serial/ioss.h>
59 #endif
60 
61 // uart config
62 static const btstack_uart_config_t * uart_config;
63 
64 // data source for integration with BTstack Runloop
65 static btstack_data_source_t transport_data_source;
66 
67 // block write
68 static int             write_bytes_len;
69 static const uint8_t * write_bytes_data;
70 
71 // block read
72 static uint16_t  read_bytes_len;
73 static uint8_t * read_bytes_data;
74 
75 // callbacks
76 static void (*block_sent)(void);
77 static void (*block_received)(void);
78 
79 
80 static int btstack_uart_posix_init(const btstack_uart_config_t * config){
81     uart_config = config;
82     return 0;
83 }
84 
85 static void btstack_uart_posix_process_write(btstack_data_source_t *ds) {
86 
87     if (write_bytes_len == 0) return;
88 
89     uint32_t start = btstack_run_loop_get_time_ms();
90 
91     // write up to write_bytes_len to fd
92     int bytes_written = (int) write(ds->source.fd, write_bytes_data, write_bytes_len);
93     if (bytes_written < 0) {
94         btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
95         return;
96     }
97 
98     uint32_t end = btstack_run_loop_get_time_ms();
99     if (end - start > 10){
100         log_info("h4_process: write took %u ms", end - start);
101     }
102 
103     write_bytes_data += bytes_written;
104     write_bytes_len  -= bytes_written;
105 
106     if (write_bytes_len){
107         btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
108         return;
109     }
110 
111     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
112 
113     // notify done
114     if (block_sent){
115         block_sent();
116     }
117 }
118 
119 static void btstack_uart_posix_process_read(btstack_data_source_t *ds) {
120 
121     if (read_bytes_len == 0) {
122         log_info("btstack_uart_posix_process_read but no read requested");
123         btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
124     }
125 
126     uint32_t start = btstack_run_loop_get_time_ms();
127 
128     // read up to bytes_to_read data in
129     ssize_t bytes_read = read(ds->source.fd, read_bytes_data, read_bytes_len);
130     // log_info("btstack_uart_posix_process_read need %u bytes, got %d", read_bytes_len, (int) bytes_read);
131     uint32_t end = btstack_run_loop_get_time_ms();
132     if (end - start > 10){
133         log_info("h4_process: read took %u ms", end - start);
134     }
135     if (bytes_read < 0) return;
136 
137     read_bytes_len   -= bytes_read;
138     read_bytes_data  += bytes_read;
139     if (read_bytes_len > 0) return;
140 
141     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
142 
143     if (block_received){
144         block_received();
145     }
146 }
147 
148 static void hci_transport_h5_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) {
149     if (ds->source.fd < 0) return;
150     switch (callback_type){
151         case DATA_SOURCE_CALLBACK_READ:
152             btstack_uart_posix_process_read(ds);
153             break;
154         case DATA_SOURCE_CALLBACK_WRITE:
155             btstack_uart_posix_process_write(ds);
156             break;
157         default:
158             break;
159     }
160 }
161 
162 static int btstack_uart_posix_set_baudrate(uint32_t baudrate){
163     UNUSED(baudrate);
164 #if 0
165     int fd = transport_data_source.source.fd;
166 
167     log_info("h4_set_baudrate %u", baudrate);
168 
169 #ifdef __APPLE__
170 
171     // From https://developer.apple.com/library/content/samplecode/SerialPortSample/Listings/SerialPortSample_SerialPortSample_c.html
172 
173     // The IOSSIOSPEED ioctl can be used to set arbitrary baud rates
174     // other than those specified by POSIX. The driver for the underlying serial hardware
175     // ultimately determines which baud rates can be used. This ioctl sets both the input
176     // and output speed.
177 
178     speed_t speed = baudrate;
179     if (ioctl(fd, IOSSIOSPEED, &speed) == -1) {
180         log_error("btstack_uart_posix_set_baudrate: error calling ioctl(..., IOSSIOSPEED, %u) - %s(%d).\n", baudrate, strerror(errno), errno);
181         return -1;
182     }
183 
184 #else
185     struct termios toptions;
186 
187     if (tcgetattr(fd, &toptions) < 0) {
188         log_error("btstack_uart_posix_set_baudrate: Couldn't get term attributes");
189         return -1;
190     }
191 
192     speed_t brate = baudrate; // let you override switch below if needed
193     switch(baudrate) {
194         case 57600:  brate=B57600;  break;
195         case 115200: brate=B115200; break;
196 #ifdef B230400
197         case 230400: brate=B230400; break;
198 #endif
199 #ifdef B460800
200         case 460800: brate=B460800; break;
201 #endif
202 #ifdef B921600
203         case 921600: brate=B921600; break;
204 #endif
205 
206 // Hacks to switch to 2/3 mbps on FTDI FT232 chipsets
207 // requires special config in Info.plist or Registry
208         case 2000000:
209 #if defined(HAVE_POSIX_B300_MAPPED_TO_2000000)
210             log_info("hci_transport_posix: using B300 for 2 mbps");
211             brate=B300;
212 #elif defined(HAVE_POSIX_B1200_MAPPED_TO_2000000)
213            log_info("hci_transport_posix: using B1200 for 2 mbps");
214             brate=B1200;
215 #endif
216             break;
217         case 3000000:
218 #if defined(HAVE_POSIX_B600_MAPPED_TO_3000000)
219             log_info("hci_transport_posix: using B600 for 3 mbps");
220             brate=B600;
221 #elif defined(HAVE_POSIX_B2400_MAPPED_TO_3000000)
222             log_info("hci_transport_posix: using B2400 for 3 mbps");
223             brate=B2400;
224 #endif
225             break;
226         default:
227             break;
228     }
229     cfsetospeed(&toptions, brate);
230     cfsetispeed(&toptions, brate);
231 
232     if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
233         log_error("btstack_uart_posix_set_baudrate: Couldn't set term attributes");
234         return -1;
235     }
236 #endif
237 #endif
238     return 0;
239 }
240 
241 static void btstack_uart_posix_set_parity_option(struct termios * toptions, int parity){
242     if (parity){
243         // enable even parity
244         toptions->c_cflag |= PARENB;
245     } else {
246         // disable even parity
247         toptions->c_cflag &= ~PARENB;
248     }
249 }
250 
251 static void btstack_uart_posix_set_flowcontrol_option(struct termios * toptions, int flowcontrol){
252     if (flowcontrol) {
253         // with flow control
254         toptions->c_cflag |= CRTSCTS;
255     } else {
256         // no flow control
257         toptions->c_cflag &= ~CRTSCTS;
258     }
259 }
260 
261 static int btstack_uart_posix_set_parity(int parity){
262     int fd = transport_data_source.source.fd;
263     struct termios toptions;
264     if (tcgetattr(fd, &toptions) < 0) {
265         log_error("btstack_uart_posix_set_parity: Couldn't get term attributes");
266         return -1;
267     }
268     btstack_uart_posix_set_parity_option(&toptions, parity);
269     if(tcsetattr(fd, TCSANOW, &toptions) < 0) {
270         log_error("posix_set_parity: Couldn't set term attributes");
271         return -1;
272     }
273     return 0;
274 }
275 
276 
277 static int btstack_uart_posix_set_flowcontrol(int flowcontrol){
278     int fd = transport_data_source.source.fd;
279     struct termios toptions;
280     if (tcgetattr(fd, &toptions) < 0) {
281         log_error("btstack_uart_posix_set_parity: Couldn't get term attributes");
282         return -1;
283     }
284     btstack_uart_posix_set_flowcontrol_option(&toptions, flowcontrol);
285     if(tcsetattr(fd, TCSANOW, &toptions) < 0) {
286         log_error("posix_set_flowcontrol: Couldn't set term attributes");
287         return -1;
288     }
289     return 0;
290 }
291 
292 static int btstack_uart_posix_open(void){
293 
294     const char * device_name = uart_config->device_name;
295     const int flowcontrol    = uart_config->flowcontrol;
296     const uint32_t baudrate  = uart_config->baudrate;
297 
298     struct termios toptions;
299     int flags = O_RDWR | O_NOCTTY | O_NONBLOCK;
300     int fd = open(device_name, flags);
301     if (fd == -1)  {
302         log_error("posix_open: Unable to open port %s", device_name);
303         return -1;
304     }
305 
306     if (tcgetattr(fd, &toptions) < 0) {
307         log_error("posix_open: Couldn't get term attributes");
308         return -1;
309     }
310 
311     cfmakeraw(&toptions);   // make raw
312 
313     // 8N1
314     toptions.c_cflag &= ~CSTOPB;
315     toptions.c_cflag |= CS8;
316 
317     toptions.c_cflag |= CREAD | CLOCAL;  // turn on READ & ignore ctrl lines
318     toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
319 
320     // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
321     toptions.c_cc[VMIN]  = 1;
322     toptions.c_cc[VTIME] = 0;
323 
324     // no parity
325     btstack_uart_posix_set_parity_option(&toptions, 0);
326 
327     // flowcontrol
328     btstack_uart_posix_set_flowcontrol_option(&toptions, flowcontrol);
329 
330     if(tcsetattr(fd, TCSANOW, &toptions) < 0) {
331         log_error("posix_open: Couldn't set term attributes");
332         return -1;
333     }
334 
335     // store fd in data source
336     transport_data_source.source.fd = fd;
337 
338     // also set baudrate
339     if (btstack_uart_posix_set_baudrate(baudrate) < 0){
340         return -1;
341     }
342 
343     // set up data_source
344     btstack_run_loop_set_data_source_fd(&transport_data_source, fd);
345     btstack_run_loop_set_data_source_handler(&transport_data_source, &hci_transport_h5_process);
346     btstack_run_loop_add_data_source(&transport_data_source);
347 
348     // wait a bit - at least cheap FTDI232 clones might send the first byte out incorrectly
349     usleep(100000);
350 
351     return 0;
352 }
353 
354 static int btstack_uart_posix_close_new(void){
355 
356     // first remove run loop handler
357     btstack_run_loop_remove_data_source(&transport_data_source);
358 
359     // then close device
360     close(transport_data_source.source.fd);
361     transport_data_source.source.fd = -1;
362     return 0;
363 }
364 
365 static void btstack_uart_posix_set_block_received( void (*block_handler)(void)){
366     block_received = block_handler;
367 }
368 
369 static void btstack_uart_posix_set_block_sent( void (*block_handler)(void)){
370     block_sent = block_handler;
371 }
372 
373 static void btstack_uart_posix_send_block(const uint8_t *data, uint16_t size){
374     // setup async write
375     write_bytes_data = data;
376     write_bytes_len  = size;
377 
378     // go
379     // btstack_uart_posix_process_write(&transport_data_source);
380     btstack_run_loop_enable_data_source_callbacks(&transport_data_source, DATA_SOURCE_CALLBACK_WRITE);
381 }
382 
383 static void btstack_uart_posix_receive_block(uint8_t *buffer, uint16_t len){
384     read_bytes_data = buffer;
385     read_bytes_len = len;
386     btstack_run_loop_enable_data_source_callbacks(&transport_data_source, DATA_SOURCE_CALLBACK_READ);
387 
388     // go
389     // btstack_uart_posix_process_read(&transport_data_source);
390 }
391 
392 // static void btstack_uart_posix_set_sleep(uint8_t sleep){
393 // }
394 // static void btstack_uart_posix_set_csr_irq_handler( void (*csr_irq_handler)(void)){
395 // }
396 
397 static const btstack_uart_block_t btstack_uart_posix = {
398     /* int  (*init)(hci_transport_config_uart_t * config); */         &btstack_uart_posix_init,
399     /* int  (*open)(void); */                                         &btstack_uart_posix_open,
400     /* int  (*close)(void); */                                        &btstack_uart_posix_close_new,
401     /* void (*set_block_received)(void (*handler)(void)); */          &btstack_uart_posix_set_block_received,
402     /* void (*set_block_sent)(void (*handler)(void)); */              &btstack_uart_posix_set_block_sent,
403     /* int  (*set_baudrate)(uint32_t baudrate); */                    &btstack_uart_posix_set_baudrate,
404     /* int  (*set_parity)(int parity); */                             &btstack_uart_posix_set_parity,
405     /* int  (*set_flowcontrol)(int flowcontrol); */                   &btstack_uart_posix_set_flowcontrol,
406     /* void (*receive_block)(uint8_t *buffer, uint16_t len); */       &btstack_uart_posix_receive_block,
407     /* void (*send_block)(const uint8_t *buffer, uint16_t length); */ &btstack_uart_posix_send_block,
408     /* int (*get_supported_sleep_modes); */                           NULL,
409     /* void (*set_sleep)(btstack_uart_sleep_mode_t sleep_mode); */    NULL,
410     /* void (*set_wakeup_handler)(void (*handler)(void)); */          NULL,
411 };
412 
413 const btstack_uart_block_t * btstack_uart_posix_instance(void){
414 	return &btstack_uart_posix;
415 }
416