blob: c5c10bbeab21d0fd2244772a8484ea270e87e335 [file] [log] [blame]
b.liuced8dd02024-06-28 13:28:29 +08001#include <stdio.h>
2#include <stdlib.h>
3#include <stdint.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7#include <string.h>
8#include <unistd.h>
9#include <errno.h>
10#include <termios.h>
11#include <assert.h>
12#include "aboot-tiny.h"
13#include "jacana_serialport.h"
14#include <time.h>
15#include "jacana_usleep.h"
16
17#ifndef O_CLOEXEC
18#define O_CLOEXEC 0
19#endif
20
21#ifndef SERIAL_RX_BUF_SIZE
22#define SERIAL_RX_BUF_SIZE (1024)
23#endif
24
25typedef enum {
26 SERIALPORT_PARITY_NONE = 1,
27 SERIALPORT_PARITY_MARK = 2,
28 SERIALPORT_PARITY_EVEN = 3,
29 SERIALPORT_PARITY_ODD = 4,
30 SERIALPORT_PARITY_SPACE = 5
31} serialport_parity_t;
32
33typedef enum {
34 SERIALPORT_STOPBITS_ONE = 1,
35 SERIALPORT_STOPBITS_ONE_FIVE = 2,
36 SERIALPORT_STOPBITS_TWO = 3
37} serialport_stop_bits_t;
38
39typedef struct {
40 int baud;
41 int data_bits;
42 bool rtscts;
43 bool xon;
44 bool xoff;
45 bool xany;
46 bool dsrdtr;
47 bool hupcl;
48 serialport_parity_t parity;
49 serialport_stop_bits_t stop_bits;
50} serial_port_connect_opt_t;
51/*---------------------------------------------------------------------------*/
52typedef struct serial_tx_memb {
53 struct serial_tx_memb *next;
54 uint8_t *data;
55 int len;
56 int remain;
57} serial_tx_memb_t;
58/*---------------------------------------------------------------------------*/
59static uint8_t rx_buf[SERIAL_RX_BUF_SIZE];
60static aboot_tiny_uart_rx_callback_t rx_cb;
61static int serial_fd = -1;
62static int fd_error;
63/*---------------------------------------------------------------------------*/
64static serial_tx_memb_t *tx_queue_list;
65static serial_tx_memb_t **tx_queue = &tx_queue_list;
66/*---------------------------------------------------------------------------*/
67static inline void
68queue_init(serial_tx_memb_t **queue)
69{
70 *queue = NULL;
71}
72/*---------------------------------------------------------------------------*/
73static inline bool
74queue_is_empty(serial_tx_memb_t **queue)
75{
76 return *queue == NULL ? true : false;
77}
78/*---------------------------------------------------------------------------*/
79static inline serial_tx_memb_t *
80queue_peek(serial_tx_memb_t **queue)
81{
82 return *queue;
83}
84/*---------------------------------------------------------------------------*/
85static inline serial_tx_memb_t *
86queue_dequeue(serial_tx_memb_t **queue)
87{
88 serial_tx_memb_t *l;
89 l = *queue;
90 if(*queue != NULL) {
91 *queue = ((serial_tx_memb_t *)*queue)->next;
92 }
93
94 return l;
95}
96/*---------------------------------------------------------------------------*/
97static inline void
98queue_enqueue(serial_tx_memb_t **queue, serial_tx_memb_t *element)
99{
100 element->next = NULL;
101
102 if(*queue == NULL) {
103 *queue = element;
104 } else {
105 serial_tx_memb_t *l;
106 for(l = *queue; l->next != NULL; l = l->next) {
107 }
108 l->next = element;
109 }
110}
111/*---------------------------------------------------------------------------*/
112static int
113set_fd(fd_set *rset, fd_set *wset)
114{
115 if(serial_fd > 0 && !fd_error) {
116 FD_SET(serial_fd, rset);
117 if(!queue_is_empty(tx_queue)) {
118 FD_SET(serial_fd, wset);
119 }
120 return 1;
121 } else {
122 return 0;
123 }
124}
125
126/*---------------------------------------------------------------------------*/
127static void
128serial_do_tx(void)
129{
130 serial_tx_memb_t *p;
131 int cnt, offset;
132
133 p = queue_peek(tx_queue);
134 if(p) {
135 offset = p->len - p->remain;
136 cnt = write(serial_fd, p->data + offset, p->remain);
137
138#if 0
139 unsigned int ii,j;
140 ii = p->remain;
141 if(ii > 128)
142 {
143 ii = 128;
144 }
145
146 char buff[512];
147 memset(buff, 0, sizeof(buff));
148 for(j=0;j<ii;j++)
149 {
150 sprintf(&buff[j], "%c", *(p->data + offset+j));
151 }
152
153 aboot_tiny_log_printf("Tlen=%d,data=%s", p->remain, buff);
154#endif
155
156 if(cnt > 0) {
157 p->remain -= cnt;
158 if(p->remain == 0) {
159 queue_dequeue(tx_queue);
160 aboot_tiny_mem_free(p->data);
161 aboot_tiny_mem_free(p);
162 }
163 } else if(cnt < 0) {
164 aboot_tiny_log_printf("write error: error code %d\n", errno);
165 fd_error = errno;
166 }
167 }
168}
169/*---------------------------------------------------------------------------*/
170static void
171handle_fd(fd_set *rset, fd_set *wset)
172{
173 int cnt;
174 if(serial_fd >= 0 && !fd_error) {
175 if(FD_ISSET(serial_fd, wset)) {
176 serial_do_tx();
177 if(queue_is_empty(tx_queue)) {
178 FD_CLR(serial_fd, wset);
179 }
180 }
181
182 if(FD_ISSET(serial_fd, rset)) {
183 cnt = read(serial_fd, rx_buf, sizeof(rx_buf));
184#if 0
185 unsigned int ii,j;
186 ii = cnt;
187 if(ii > 64)
188 {
189 ii = 64;
190 }
191
192 char buf[256];
193 memset(buf, 0, sizeof(buf));
194 for(j=0;j<ii;j++)
195 {
196 sprintf(&buf[j], "%c,", rx_buf[j]);
197 }
198
199 aboot_tiny_log_printf("Rlen=%d,data=%s", cnt, buf);
200#endif
201 if((cnt > 0) && rx_cb) {
202 rx_cb(rx_buf, cnt);
203 } else if(cnt < 0) {
204 aboot_tiny_log_printf("read error: error code %d\n", errno);
205 fd_error = errno;
206 }
207 }
208 }
209}
210/*---------------------------------------------------------------------------*/
211static const struct select_callback serial_select_callback = {
212 set_fd,
213 handle_fd,
214};
215/*---------------------------------------------------------------------------*/
216static int
217to_baud_const(int baud)
218{
219 switch(baud) {
220 case 0: return B0;
221 case 50: return B50;
222 case 75: return B75;
223 case 110: return B110;
224 case 134: return B134;
225 case 150: return B150;
226 case 200: return B200;
227 case 300: return B300;
228 case 600: return B600;
229 case 1200: return B1200;
230 case 1800: return B1800;
231 case 2400: return B2400;
232 case 4800: return B4800;
233 case 9600: return B9600;
234 case 19200: return B19200;
235 case 38400: return B38400;
236 case 57600: return B57600;
237 case 115200: return B115200;
238 case 230400: return B230400;
239#if defined(__linux__)
240 case 460800: return B460800;
241 case 500000: return B500000;
242 case 576000: return B576000;
243 case 921600: return B921600;
244 case 1000000: return B1000000;
245 case 1152000: return B1152000;
246 case 1500000: return B1500000;
247 case 2000000: return B2000000;
248 case 2500000: return B2500000;
249 case 3000000: return B3000000;
250 case 3500000: return B3500000;
251 case 4000000: return B4000000;
252#endif
253 }
254 return -1;
255}
256/*---------------------------------------------------------------------------*/
257static int
258to_data_bits_const(int data_bits)
259{
260 switch(data_bits) {
261 case 8: default: return CS8;
262 case 7: return CS7;
263 case 6: return CS6;
264 case 5: return CS5;
265 }
266 return -1;
267}
268/*---------------------------------------------------------------------------*/
269static int
270set_baudrate(int fd, serial_port_connect_opt_t *opt)
271{
272 /* lookup the standard baudrates from the table */
273 int baud_rate = to_baud_const(opt->baud);
274
275 /* get port options */
276 struct termios options;
277
278 if(-1 == tcgetattr(fd, &options)) {
279 aboot_tiny_log_printf("Error: %s setting custom baud rate of %d", strerror(errno), opt->baud);
280 return -1;
281 }
282
283 if(-1 == baud_rate) {
284 aboot_tiny_log_printf("Error baud rate of %d is not supported on your platform", opt->baud);
285 return -1;
286 }
287
288 /* If we have a good baud rate set it and lets go */
289 cfsetospeed(&options, baud_rate);
290 cfsetispeed(&options, baud_rate);
291 /* throw away all the buffered data */
292 tcflush(fd, TCIOFLUSH);
293 /* make the changes now */
294 tcsetattr(fd, TCSANOW, &options);
295
296 return 1;
297}
298
299/*---------------------------------------------------------------------------*/
300static int
301set_default_baudrate(int fd)
302{
303 /* Default BaudRate => 115200. Just for AP UART. */
304 int baud_rate = to_baud_const(115200);
305
306 /* get port options */
307 struct termios options;
308
309 if(-1 == tcgetattr(fd, &options))
310 return -1;
311
312
313 if(-1 == baud_rate)
314 return -1;
315
316 /* If we have a good baud rate set it and lets go */
317 cfsetospeed(&options, baud_rate);
318 cfsetispeed(&options, baud_rate);
319 /* throw away all the buffered data */
320 tcflush(fd, TCIOFLUSH);
321 /* make the changes now */
322 tcsetattr(fd, TCSANOW, &options);
323
324 return 1;
325}
326
327
328/*---------------------------------------------------------------------------*/
329static int
330setup(int fd, serial_port_connect_opt_t *opt)
331{
332 int data_bits = to_data_bits_const(opt->data_bits);
333 if(data_bits == -1) {
334 aboot_tiny_log_printf("Invalid data bits %d\n", opt->data_bits);
335 return -1;
336 }
337 if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
338 aboot_tiny_log_printf("Set FD_CLOEXEC failed\n");
339 return -1;
340 }
341
342 /* Get port configuration for modification */
343 struct termios options;
344 tcgetattr(fd, &options);
345
346 /* IGNPAR: ignore bytes with parity errors */
347 options.c_iflag = IGNPAR;
348 /* ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input) */
349 /* Future potential option */
350 /* options.c_iflag = ICRNL; */
351 /* otherwise make device raw (no other input processing) */
352
353 /* Specify data bits */
354 options.c_cflag &= ~CSIZE;
355 options.c_cflag |= data_bits;
356
357 options.c_cflag &= ~(CRTSCTS);
358
359 if(opt->rtscts) {
360 options.c_cflag |= CRTSCTS;
361 }
362
363 options.c_iflag &= ~(IXON | IXOFF | IXANY);
364
365 if(opt->xon) {
366 options.c_iflag |= IXON;
367 }
368
369 if(opt->xoff) {
370 options.c_iflag |= IXOFF;
371 }
372
373 if(opt->xany) {
374 options.c_iflag |= IXANY;
375 }
376
377 switch(opt->parity) {
378 case SERIALPORT_PARITY_NONE:
379 options.c_cflag &= ~PARENB;
380 break;
381 case SERIALPORT_PARITY_ODD:
382 options.c_cflag |= PARENB;
383 options.c_cflag |= PARODD;
384 break;
385 case SERIALPORT_PARITY_EVEN:
386 options.c_cflag |= PARENB;
387 options.c_cflag &= ~PARODD;
388 break;
389 default:
390 aboot_tiny_log_printf("Invalid parity setting %d", opt->parity);
391 return -1;
392 }
393
394 switch(opt->stop_bits) {
395 case SERIALPORT_STOPBITS_ONE:
396 options.c_cflag &= ~CSTOPB;
397 break;
398 case SERIALPORT_STOPBITS_TWO:
399 options.c_cflag |= CSTOPB;
400 break;
401 default:
402 aboot_tiny_log_printf("Invalid stop bits setting %d", opt->stop_bits);
403 return -1;
404 }
405
406
407 options.c_cflag |= CLOCAL; /* ignore status lines */
408 options.c_cflag |= CREAD; /* enable receiver */
409 options.c_oflag = 0;
410 /* ICANON makes partial lines not readable. It should be optional. */
411 /* It works with ICRNL. */
412 options.c_lflag = 0; /* ICANON; */
413 options.c_cc[VMIN] = 0;
414 options.c_cc[VTIME] = 0;
415
416 tcsetattr(fd, TCSANOW, &options);
417
418
419 if(set_baudrate(fd, opt) == -1) {
420 return -1;
421 }
422
423 return 1;
424}
425/*---------------------------------------------------------------------------*/
426static int
427serial_port_open(const char *dev, serial_port_connect_opt_t *opt)
428{
429 assert(serial_fd == -1); /* previously port has not been closed */
430 serial_fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC);
431 if(serial_fd < 0) {
432 return -1;
433 }
434 if(setup(serial_fd, opt) < 0) {
435 return -1;
436 }
437 rx_cb = NULL;
438 fd_error = 0;
439 aboot_tiny_log_printf("serial fd=%d rate=%d", serial_fd, opt->baud);
440 aboot_tiny_select_set_callback(serial_fd, &serial_select_callback);
441 queue_init(tx_queue);
442
443 return 0;
444}
445static void
446serial_port_close(void)
447{
448 serial_tx_memb_t *p;
449
450 if(serial_fd >= 0) {
451 aboot_tiny_select_set_callback(serial_fd, NULL);
452 rx_cb = NULL;
453 /* BaudRate = 115200bps. */
454 set_default_baudrate(serial_fd);
455 close(serial_fd);
456 serial_fd = -1;
457 while(1) {
458 p = queue_dequeue(tx_queue);
459 if(!p) {
460 break;
461 }
462 aboot_tiny_mem_free(p->data);
463 aboot_tiny_mem_free(p);
464 }
465 }
466}
467/*---------------------------------------------------------------------------*/
468int
469jacana_serialport_write(const uint8_t *buf, size_t len)
470{
471 serial_tx_memb_t *p;
472
473 if(serial_fd < 0 || fd_error) {
474 return len;
475 }
476
477 p = aboot_tiny_mem_alloc(sizeof(serial_tx_memb_t));
478 if(!p) {
479 aboot_tiny_log_printf("out of memory\n");
480 return -1;
481 }
482
483 p->data = aboot_tiny_mem_alloc(len);
484 if(!p->data) {
485 aboot_tiny_mem_free((void *)p);
486 aboot_tiny_log_printf("out of memory\n");
487 return -1;
488 }
489 memcpy(p->data, (void *)buf, len);
490 p->len = p->remain = len;
491 queue_enqueue(tx_queue, p);
492
493 return len;
494}
495/*---------------------------------------------------------------------------*/
496int
497jacana_serialport_init(const char *dev, int baud,
498 aboot_tiny_uart_rx_callback_t cb)
499{
500 serial_port_connect_opt_t connect_op;
501 connect_op.baud = baud;
502 connect_op.data_bits = 8;
503 connect_op.rtscts = false;
504 connect_op.xon = false;
505 connect_op.xoff = false;
506 connect_op.xany = false;
507 connect_op.dsrdtr = false;
508 connect_op.hupcl = false;
509 connect_op.parity = SERIALPORT_PARITY_NONE;
510 connect_op.stop_bits = SERIALPORT_STOPBITS_ONE;
511
512 if(!serial_port_open(dev, &connect_op)) {
513 rx_cb = cb;
514 return 0;
515 } else {
516 aboot_tiny_log_printf("open device \"%s\" failed\n", dev);
517 return -1;
518 }
519}
520/*---------------------------------------------------------------------------*/
521void
522jacana_serialport_exit(void)
523{
524 serial_port_close();
525}
526/*---------------------------------------------------------------------------*/