xref: /btstack/test/mesh/btstack_uart_posix_pty.c (revision 1707474d4963569c4305dfdcf0e8d12ea8ff1909)
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     cfsetospeed(&toptions, brate);
207     cfsetispeed(&toptions, brate);
208 
209     if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
210         log_error("btstack_uart_posix_set_baudrate: Couldn't set term attributes");
211         return -1;
212     }
213 #endif
214 #endif
215     return 0;
216 }
217 
218 static void btstack_uart_posix_set_parity_option(struct termios * toptions, int parity){
219     if (parity){
220         // enable even parity
221         toptions->c_cflag |= PARENB;
222     } else {
223         // disable even parity
224         toptions->c_cflag &= ~PARENB;
225     }
226 }
227 
228 static void btstack_uart_posix_set_flowcontrol_option(struct termios * toptions, int flowcontrol){
229     if (flowcontrol) {
230         // with flow control
231         toptions->c_cflag |= CRTSCTS;
232     } else {
233         // no flow control
234         toptions->c_cflag &= ~CRTSCTS;
235     }
236 }
237 
238 static int btstack_uart_posix_set_parity(int parity){
239     int fd = transport_data_source.source.fd;
240     struct termios toptions;
241     if (tcgetattr(fd, &toptions) < 0) {
242         log_error("btstack_uart_posix_set_parity: Couldn't get term attributes");
243         return -1;
244     }
245     btstack_uart_posix_set_parity_option(&toptions, parity);
246     if(tcsetattr(fd, TCSANOW, &toptions) < 0) {
247         log_error("posix_set_parity: Couldn't set term attributes");
248         return -1;
249     }
250     return 0;
251 }
252 
253 
254 static int btstack_uart_posix_set_flowcontrol(int flowcontrol){
255     int fd = transport_data_source.source.fd;
256     struct termios toptions;
257     if (tcgetattr(fd, &toptions) < 0) {
258         log_error("btstack_uart_posix_set_parity: Couldn't get term attributes");
259         return -1;
260     }
261     btstack_uart_posix_set_flowcontrol_option(&toptions, flowcontrol);
262     if(tcsetattr(fd, TCSANOW, &toptions) < 0) {
263         log_error("posix_set_flowcontrol: Couldn't set term attributes");
264         return -1;
265     }
266     return 0;
267 }
268 
269 static int btstack_uart_posix_open(void){
270 
271     const char * device_name = uart_config->device_name;
272     const int flowcontrol    = uart_config->flowcontrol;
273     const uint32_t baudrate  = uart_config->baudrate;
274 
275     struct termios toptions;
276     int flags = O_RDWR | O_NOCTTY | O_NONBLOCK;
277     int fd = open(device_name, flags);
278     if (fd == -1)  {
279         log_error("posix_open: Unable to open port %s", device_name);
280         return -1;
281     }
282 
283     if (tcgetattr(fd, &toptions) < 0) {
284         log_error("posix_open: Couldn't get term attributes");
285         return -1;
286     }
287 
288     cfmakeraw(&toptions);   // make raw
289 
290     // 8N1
291     toptions.c_cflag &= ~CSTOPB;
292     toptions.c_cflag |= CS8;
293 
294     toptions.c_cflag |= CREAD | CLOCAL;  // turn on READ & ignore ctrl lines
295     toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
296 
297     // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
298     toptions.c_cc[VMIN]  = 1;
299     toptions.c_cc[VTIME] = 0;
300 
301     // no parity
302     btstack_uart_posix_set_parity_option(&toptions, 0);
303 
304     // flowcontrol
305     btstack_uart_posix_set_flowcontrol_option(&toptions, flowcontrol);
306 
307     if(tcsetattr(fd, TCSANOW, &toptions) < 0) {
308         log_error("posix_open: Couldn't set term attributes");
309         return -1;
310     }
311 
312     // store fd in data source
313     transport_data_source.source.fd = fd;
314 
315     // also set baudrate
316     if (btstack_uart_posix_set_baudrate(baudrate) < 0){
317         return -1;
318     }
319 
320     // set up data_source
321     btstack_run_loop_set_data_source_fd(&transport_data_source, fd);
322     btstack_run_loop_set_data_source_handler(&transport_data_source, &hci_transport_h5_process);
323     btstack_run_loop_add_data_source(&transport_data_source);
324 
325     // wait a bit - at least cheap FTDI232 clones might send the first byte out incorrectly
326     usleep(100000);
327 
328     return 0;
329 }
330 
331 static int btstack_uart_posix_close_new(void){
332 
333     // first remove run loop handler
334     btstack_run_loop_remove_data_source(&transport_data_source);
335 
336     // then close device
337     close(transport_data_source.source.fd);
338     transport_data_source.source.fd = -1;
339     return 0;
340 }
341 
342 static void btstack_uart_posix_set_block_received( void (*block_handler)(void)){
343     block_received = block_handler;
344 }
345 
346 static void btstack_uart_posix_set_block_sent( void (*block_handler)(void)){
347     block_sent = block_handler;
348 }
349 
350 static void btstack_uart_posix_send_block(const uint8_t *data, uint16_t size){
351     // setup async write
352     write_bytes_data = data;
353     write_bytes_len  = size;
354 
355     // go
356     // btstack_uart_posix_process_write(&transport_data_source);
357     btstack_run_loop_enable_data_source_callbacks(&transport_data_source, DATA_SOURCE_CALLBACK_WRITE);
358 }
359 
360 static void btstack_uart_posix_receive_block(uint8_t *buffer, uint16_t len){
361     read_bytes_data = buffer;
362     read_bytes_len = len;
363     btstack_run_loop_enable_data_source_callbacks(&transport_data_source, DATA_SOURCE_CALLBACK_READ);
364 
365     // go
366     // btstack_uart_posix_process_read(&transport_data_source);
367 }
368 
369 // static void btstack_uart_posix_set_sleep(uint8_t sleep){
370 // }
371 // static void btstack_uart_posix_set_csr_irq_handler( void (*csr_irq_handler)(void)){
372 // }
373 
374 static const btstack_uart_block_t btstack_uart_posix = {
375     /* int  (*init)(hci_transport_config_uart_t * config); */         &btstack_uart_posix_init,
376     /* int  (*open)(void); */                                         &btstack_uart_posix_open,
377     /* int  (*close)(void); */                                        &btstack_uart_posix_close_new,
378     /* void (*set_block_received)(void (*handler)(void)); */          &btstack_uart_posix_set_block_received,
379     /* void (*set_block_sent)(void (*handler)(void)); */              &btstack_uart_posix_set_block_sent,
380     /* int  (*set_baudrate)(uint32_t baudrate); */                    &btstack_uart_posix_set_baudrate,
381     /* int  (*set_parity)(int parity); */                             &btstack_uart_posix_set_parity,
382     /* int  (*set_flowcontrol)(int flowcontrol); */                   &btstack_uart_posix_set_flowcontrol,
383     /* void (*receive_block)(uint8_t *buffer, uint16_t len); */       &btstack_uart_posix_receive_block,
384     /* void (*send_block)(const uint8_t *buffer, uint16_t length); */ &btstack_uart_posix_send_block,
385     /* int (*get_supported_sleep_modes); */                           NULL,
386     /* void (*set_sleep)(btstack_uart_sleep_mode_t sleep_mode); */    NULL,
387     /* void (*set_wakeup_handler)(void (*handler)(void)); */          NULL,
388     NULL, NULL, NULL, NULL,
389 };
390 
391 const btstack_uart_block_t * btstack_uart_posix_instance(void){
392 	return &btstack_uart_posix;
393 }
394