RaspberrPi project source code
guowenxue
2024-03-14 7fa125e56f1de17c2f6aeb9a410ff02ac4e78e85
commit | author | age
d6b4a7 1 /*********************************************************************************
G 2  *      Copyright:  (C) 2023 LingYun IoT System Studio.
3  *                  All rights reserved.
4  *
5  *       Filename:  comport.c
6  *    Description:  This file is linux comport common API functions
7  *                 
8  *        Version:  1.0.0(11/08/23)
9  *         Author:  Guo Wenxue <guowenxue@gmail.com>
10  *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
11  *                 
12  ********************************************************************************/
13
14 #include "comport.h"
15
16 #define CONFIG_PRINT_LOGGER
17 //#define CONFIG_PRINT_STDOUT
18
19 #if ( defined CONFIG_PRINT_LOGGER )
20 #include "logger.h"
21 #define dbg_print(format,args...) log_error(format, ##args)
22
23 #elif ( defined CONFIG_PRINT_STDOUT )
24 #define dbg_print(format,args...) printf(format, ##args)
25
26 #else
27 #define dbg_print(format,args...) do{} while(0);
28 #endif
29
30
31 static inline void set_settings(comport_t * comport, const char *settings);
32
33 /*
34  *  description: Open the serial port
35  *
36  *   input args: $comport:  corresponding comport point
37  *               $dev_name:  The comport device name path, such as '/dev/ttyS3'
38  *               $baudrate:  The baudrate, such as 115200
39  *               $settings:  The databit,parity,stopbit,flowctrl settings, such as '8N1N'
40  *
41  * return value: The comport opened file description, <0 means failure
42  */
43 int comport_open(comport_t *comport, const char *devname, long baudrate, const char *settings)
44 {
45     int                         rv = -1;
46     struct termios              old_cfg, new_cfg;
47     int                         old_flags;
48     long                        tmp;
49
50     if( !comport || !devname )
51     {
52         dbg_print("invalid input arugments\n");
53         return -1;
54     }
55
56     /*+-----------------------+
57      *| open the serial port  |
58      *+-----------------------+*/
59
60     memset(comport, 0, sizeof(*comport));
61     strncpy(comport->devname, devname, sizeof(comport->devname));
62     comport->baudrate = baudrate;
63     comport->fd = -1;
64     comport->fragsize = CONFIG_DEF_FRAGSIZE;
65     set_settings(comport, settings);
66
67     if( !strstr(comport->devname, "tty") )
68     {
69         dbg_print("comport device \"%s\" is not tty device\n", comport->devname);
70         return -2;
71     }
72
73     comport->fd = open(comport->devname, O_RDWR | O_NOCTTY | O_NONBLOCK);
74     if( comport->fd<0 )
75     {
76         dbg_print("comport open \"%s\" failed:%s\n", comport->devname, strerror(errno));
77         return -3;
78     }
79
80     if(   (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0)))
81        && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)) )
82     {
83         /* Flush input and output */
84         tcflush(comport->fd, TCIOFLUSH);
85     }
86     else
87     {
88         rv = -4;
89         goto CleanUp;
90     }
91
92     if (0 != tcgetattr(comport->fd, &old_cfg))
93     {
94         rv = -5;
95         goto CleanUp;
96     }
97
98
99     /*+-----------------------+
100      *| configure serial port |
101      *+-----------------------+*/
102
103     memset(&new_cfg, 0, sizeof(new_cfg));
104     new_cfg.c_cflag &= ~CSIZE;
105     new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
106     new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
107     new_cfg.c_oflag &= ~(OPOST);
108
109     /* Set the data bit */
110     switch (comport->databit)
111     {
112         case 0x07:
113             new_cfg.c_cflag |= CS7;
114             break;
115         case 0x06:
116             new_cfg.c_cflag |= CS6;
117             break;
118         case 0x05:
119             new_cfg.c_cflag |= CS5;
120             break;
121         default:
122             new_cfg.c_cflag |= CS8;
123             break;
124     }
125
126     /* Set the parity */
127     switch (comport->parity)
128     {
129         case 0x01:    /* Odd */
130             new_cfg.c_cflag |= (PARENB | PARODD);
131             new_cfg.c_cflag |= (INPCK | ISTRIP);
132             break;
133         case 0x02:    /* Even */
134             new_cfg.c_cflag |= PARENB;
135             new_cfg.c_cflag &= ~PARODD;;
136             new_cfg.c_cflag |= (INPCK | ISTRIP);
137             break;
138         case 0x03:
139             new_cfg.c_cflag &= ~PARENB;
140             new_cfg.c_cflag &= ~CSTOPB;
141             break;
142         default:
143             new_cfg.c_cflag &= ~PARENB;
144     }
145
146     /* Set Stop bit */
147     if (0x01 != comport->stopbit)
148     {
149         new_cfg.c_cflag |= CSTOPB;
150     }
151     else
152     {
153         new_cfg.c_cflag &= ~CSTOPB;
154     }
155
156     /* Set flow control */
157     switch (comport->flowctrl)
158     {
159         case 1:       /* Software control */
160         case 3:
161             new_cfg.c_cflag &= ~(CRTSCTS);
162             new_cfg.c_iflag |= (IXON | IXOFF);
163             break;
164         case 2:       /* Hardware control */
165             new_cfg.c_cflag |= CRTSCTS;
166             new_cfg.c_iflag &= ~(IXON | IXOFF);
167             break;
168         default:      /* NONE */
169             new_cfg.c_cflag &= ~(CRTSCTS);
170             new_cfg.c_iflag &= ~(IXON | IXOFF);
171             break;
172     }
173
174     /* Set baudrate */
175     switch (comport->baudrate)
176     {
177         /* Upper is not POSIX(bits/termios-baud.h) */
178         case 4000000:
179             tmp = B4000000;
180             break;
181         case 3500000:
182             tmp = B3500000;
183             break;
184         case 3000000:
185             tmp = B3000000;
186             break;
187         case 2500000:
188             tmp = B2500000;
189             break;
190         case 2000000:
191             tmp = B2000000;
192             break;
193         case 1500000:
194             tmp = B1500000;
195             break;
196         case 1152000:
197             tmp = B1152000;
198             break;
199         case 1000000:
200             tmp = B1000000;
201             break;
202         case 921600:
203             tmp = B921600;
204             break;
205         case 576000:
206             tmp = B576000;
207             break;
208         case 500000:
209             tmp = B500000;
210             break;
211         case 460800:
212             tmp = B460800;
213             break;
214         case 230400:
215             tmp = B230400;
216             break;
217         case 115200:
218             tmp = B115200;
219             break;
220         case 57600:
221             tmp = B57600;
222             break;
223
224         /* Below is POSIX(bits/termios.h) */
225         case 38400:
226             tmp = B38400;
227             break;
228         case 19200:
229             tmp = B19200;
230             break;
231         case 9600:
232             tmp = B9600;
233             break;
234         case 4800:
235             tmp = B4800;
236             break;
237         case 2400:
238             tmp = B2400;
239             break;
240         case 1800:
241             tmp = B1800;
242             break;
243         case 1200:
244             tmp = B1200;
245             break;
246         case 600:
247             tmp = B600;
248             break;
249         case 300:
250             tmp = B300;
251             break;
252         case 200:
253             tmp = B200;
254             break;
255         case 150:
256             tmp = B150;
257             break;
258         case 134:
259             tmp = B134;
260             break;
261         case 110:
262             tmp = B110;
263             break;
264         case 75:
265             tmp = B75;
266             break;
267         case 50:
268             tmp = B50;
269             break;
270         default:
271             tmp = B115200;
272     }
273     cfsetispeed(&new_cfg, tmp);
274     cfsetispeed(&new_cfg, tmp);
275
276     /* Set the Com port timeout settings */
277     new_cfg.c_cc[VMIN] = 0;
278     new_cfg.c_cc[VTIME] = 0;
279
280     tcflush(comport->fd, TCIFLUSH);
281     if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg))
282     {
283         rv = -6;          // Failed to set device com port settings
284         goto CleanUp;
285     }
286
287     rv = comport->fd;
288
289 CleanUp:
290     return rv;
291 }
292
293
294 /*
295  *  description: close comport
296  *   input args: $comport:  corresponding comport point
297  */
298
299 void comport_close(comport_t *comport)
300 {
301     if( !comport )
302     {
303         dbg_print("invalid input arugments\n");
304         return ;
305     }
306
307     if ( comport->fd >= 0 )
308     {
309         close(comport->fd);
310     }
311
312     comport->fd = -1;
313     return ;
314 }
315
316 /*
317  *  description: write $data_bytes $data to $comport
318  * return value: 0: write ok  <0: write failure
319  */
320
321 int comport_send(comport_t *comport, char *data, int data_bytes)
322 {
323     char                       *ptr;
324     int                         left, bytes = 0;
325     int                         rv = 0;
326
327     if( !comport || !data || data_bytes<=0 )
328     {
329         dbg_print("invalid input arugments\n");
330         return -1;
331     }
332
333     if( comport->fd < 0 )
334     {
335         dbg_print("Serail port not opened\n");
336         return -2;
337     }
338
339     ptr = data;
340     left = data_bytes;
341
342     while( left > 0 )
343     {
344         /* Large data, then slice them to frag and send */
345         bytes = left>comport->fragsize ? comport->fragsize : left;
346
347         rv = write(comport->fd, ptr, bytes);
348         if( rv<0 )
349         {
350             rv = -3;
351             break;
352         }
353
354         left -= rv;
355         ptr += rv;
356     }
357
358     return rv;
359 }
360
361
362 /*
363  *  description: read data from $comport in $timeout <ms> to $buf no more than $buf_size bytes
364  * return value: the actual read data bytes, <0: read failure
365  */
366
367 int comport_recv(comport_t *comport, char *buf, int buf_size, unsigned long timeout)
368 {
369     fd_set                      rdfds, exfds;
370     struct timeval              to, *to_ptr = NULL;
371     int                         ret, rv = 0;
372     int                         bytes = 0;
373
374     if ( !comport || !buf || buf_size<=0 )
375     {
376         dbg_print("invalid input arugments\n");
377         return -1;
378     }
379
380     if ( comport->fd < 0 )
381     {
382         dbg_print("Serail port not opened\n");
383         return -2;
384     }
385
386     memset(buf, 0, buf_size);
387
388     FD_ZERO(&rdfds);
389     FD_ZERO(&exfds);
390     FD_SET(comport->fd, &rdfds);
391     FD_SET(comport->fd, &exfds);
392
393     if( TIMEOUT_NONE != timeout )
394     {
395         to.tv_sec = (time_t) (timeout / 1000);
396         to.tv_usec = (long)(1000 * (timeout % 1000));
397         to_ptr = &to;
398     }
399
400     while( 1 )
401     {
402         /* check got data arrive or not */
403         ret = select(comport->fd+1, &rdfds, 0, &exfds, to_ptr);
404         if( ret<0 )
405         {
406             /* EINTR means catch interrupt signal */
407             dbg_print("comport select() failed: %s\n", strerror(errno));
408             rv = EINTR==errno ? 0 : -3;
409             break;
410         }
411         else if( 0 == ret ) /* timeout */
412         {
413             break;
414         }
415
416         /* read data from comport */
417         ret = read(comport->fd, buf+bytes, buf_size-bytes);
418         if(ret <= 0)
419         {
420             dbg_print("comport read() failed: %s\n", strerror(errno));
421             break;
422         }
423
424         bytes += ret;
425         if( bytes >= buf_size )
426             break;
427
428         /* try to read data in 1ms again, if no data arrive it will break */
429         to.tv_sec = 0;
430         to.tv_usec = 10000;
431         to_ptr = &to;
432     }
433
434     if( !rv )
435         rv = bytes;
436
437     return rv;
438 }
439
440
441 /**************************************************************************************
442  *  Description: Set the comport databit,parity,stopbit,flowctrl into the comport structure
443  *   Input Args: comport: the comport_t pointer
444  *               settings: The databit/parity/stopbit/flowctrl settings as like "8N1N"
445  *  Output Args: NONE
446  * Return Value: NONE
447  *************************************************************************************/
448 static inline void set_settings(comport_t * comport, const char *settings)
449 {
450     if( !settings || !comport )
451     {
452         dbg_print("invalid input arugments\n");
453         return ;
454     }
455
456     switch (settings[0])        /* data bit */
457     {
458         case '7':
459             comport->databit = 7;
460             break;
461         case '8':
462         default:
463             comport->databit = 8;
464             break;
465     }
466
467     switch (settings[1])        /* parity */
468     {
469         case 'O':
470         case 'o':
471             comport->parity = 1;
472             break;
473         case 'E':
474         case 'e':
475             comport->parity = 2;
476             break;
477         case 'S':
478         case 's':
479             comport->parity = 3;
480             break;
481         case 'N':
482         case 'n':
483         default:
484             comport->parity = 0;
485             break;
486     }
487
488     switch (settings[2])        /* stop bit */
489     {
490         case '0':
491             comport->stopbit = 0;
492             break;
493         case '1':
494         default:
495             comport->stopbit = 1;
496             break;
497     }
498
499     switch (settings[3])        /* flow control */
500     {
501         case 'S':
502         case 's':
503             comport->flowctrl = 1;
504             break;
505         case 'H':
506         case 'h':
507             comport->flowctrl = 2;
508             break;
509         case 'B':
510         case 'b':
511             comport->flowctrl = 3;
512             break;
513         case 'N':
514         case 'n':
515         default:
516             comport->flowctrl = 0;
517             break;
518     }
519 }
520