| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2018 LingYun IoT System Studio |
| | | * Copyright: (C) 2023 LingYun IoT System Studio. |
| | | * All rights reserved. |
| | | * |
| | | * Filename: cp_comport.c |
| | | * Description: It's the comport operate library. |
| | | * |
| | | * Version: 2.0.0(10/17/2018~) |
| | | * Filename: comport.c |
| | | * Description: This file is linux comport common API functions |
| | | * |
| | | * Version: 1.0.0(11/08/23) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/17/2018 03:33:25 PM" |
| | | * |
| | | * ChangeLog: 1, Release initial version on "11/08/23 16:18:43" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include "comport.h" |
| | | #include "comport.h" |
| | | |
| | | static void set_settings(comport_t *comport, const char *settings); |
| | | #define CONFIG_PRINT_LOGGER |
| | | //#define CONFIG_PRINT_STDOUT |
| | | |
| | | #ifdef COM_DEBUG |
| | | void disp_settings(comport_t *comport); |
| | | #if ( defined CONFIG_PRINT_LOGGER ) |
| | | #include "logger.h" |
| | | #define dbg_print(format,args...) log_error(format, ##args) |
| | | |
| | | #elif ( defined CONFIG_PRINT_STDOUT ) |
| | | #define dbg_print(format,args...) printf(format, ##args) |
| | | |
| | | #else |
| | | #define dbg_print(format,args...) do{} while(0); |
| | | #endif |
| | | |
| | | |
| | | static inline void set_settings(comport_t * comport, const char *settings); |
| | | |
| | | /* |
| | | /* |
| | | * description: Open the serial port |
| | | * |
| | | * input args: $comport: corresponding comport point |
| | | * input args: $comport: corresponding comport point |
| | | * $dev_name: The comport device name path, such as '/dev/ttyS3' |
| | | * $baudrate: The baudrate, such as 115200 |
| | | * $settings: The databit,parity,stopbit,flowctrl settings, such as '8N1N' |
| | |
| | | |
| | | if( !comport || !devname ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | dbg_print("invalid input arugments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | /*+-----------------------+ |
| | | *| open the serial port | |
| | | *+-----------------------+*/ |
| | | |
| | | memset(comport, 0, sizeof(*comport)); |
| | | strncpy(comport->devname, devname, DEVNAME_LEN); |
| | | strncpy(comport->devname, devname, sizeof(comport->devname)); |
| | | comport->baudrate = baudrate; |
| | | comport->fd = -1; |
| | | comport->frag_size = 128; |
| | | |
| | | comport->fragsize = CONFIG_DEF_FRAGSIZE; |
| | | set_settings(comport, settings); |
| | | #ifdef COM_DEBUG |
| | | disp_settings(comport); |
| | | #endif |
| | | |
| | | /* Not a TTY device */ |
| | | if( !strstr(comport->devname, "tty")) |
| | | if( !strstr(comport->devname, "tty") ) |
| | | { |
| | | COM_PRINT("Open Not tty device \"%s\"\n", comport->devname); |
| | | dbg_print("comport device \"%s\" is not tty device\n", comport->devname); |
| | | return -2; |
| | | } |
| | | |
| | | comport->fd = open(comport->devname, O_RDWR | O_NOCTTY | O_NONBLOCK); |
| | | if (comport->fd < 0) |
| | | if( comport->fd<0 ) |
| | | { |
| | | rv = -3; |
| | | goto CleanUp; |
| | | dbg_print("comport open \"%s\" failed:%s\n", comport->devname, strerror(errno)); |
| | | return -3; |
| | | } |
| | | COM_PRINT("Open device \"%s\"\n", comport->devname); |
| | | |
| | | if ((-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) |
| | | && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK))) |
| | | if( (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) |
| | | && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)) ) |
| | | { |
| | | /* Flush input and output */ |
| | | if (-1 == tcflush(comport->fd, TCIOFLUSH)) |
| | | { |
| | | rv = -4; |
| | | goto CleanUp; |
| | | } |
| | | tcflush(comport->fd, TCIOFLUSH); |
| | | } |
| | | else |
| | | else |
| | | { |
| | | rv = -5; |
| | | rv = -4; |
| | | goto CleanUp; |
| | | } |
| | | |
| | | if (0 != tcgetattr(comport->fd, &old_cfg)) |
| | | { |
| | | rv = -6; // Failed to get Com settings |
| | | rv = -5; |
| | | goto CleanUp; |
| | | } |
| | | |
| | | |
| | | /*+-----------------------+ |
| | | *| configure serial port | |
| | | *+-----------------------+*/ |
| | | |
| | | memset(&new_cfg, 0, sizeof(new_cfg)); |
| | | |
| | | /*=====================================*/ |
| | | /* Configure comport */ |
| | | /*=====================================*/ |
| | | |
| | | new_cfg.c_cflag &= ~CSIZE; |
| | | new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); |
| | | new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); |
| | |
| | | /* Set the parity */ |
| | | switch (comport->parity) |
| | | { |
| | | case 0x01: // Odd |
| | | case 0x01: /* Odd */ |
| | | new_cfg.c_cflag |= (PARENB | PARODD); |
| | | new_cfg.c_cflag |= (INPCK | ISTRIP); |
| | | break; |
| | | case 0x02: // Even |
| | | case 0x02: /* Even */ |
| | | new_cfg.c_cflag |= PARENB; |
| | | new_cfg.c_cflag &= ~PARODD;; |
| | | new_cfg.c_cflag |= (INPCK | ISTRIP); |
| | |
| | | /* Set flow control */ |
| | | switch (comport->flowctrl) |
| | | { |
| | | case 1: // Software control |
| | | case 1: /* Software control */ |
| | | case 3: |
| | | new_cfg.c_cflag &= ~(CRTSCTS); |
| | | new_cfg.c_iflag |= (IXON | IXOFF); |
| | | break; |
| | | case 2: // Hardware control |
| | | new_cfg.c_cflag |= CRTSCTS; // Also called CRTSCTS |
| | | case 2: /* Hardware control */ |
| | | new_cfg.c_cflag |= CRTSCTS; |
| | | new_cfg.c_iflag &= ~(IXON | IXOFF); |
| | | break; |
| | | default: // NONE |
| | | default: /* NONE */ |
| | | new_cfg.c_cflag &= ~(CRTSCTS); |
| | | new_cfg.c_iflag &= ~(IXON | IXOFF); |
| | | break; |
| | |
| | | /* Set baudrate */ |
| | | switch (comport->baudrate) |
| | | { |
| | | /* Upper is not POSIX(bits/termios-baud.h) */ |
| | | case 4000000: |
| | | tmp = B4000000; |
| | | break; |
| | | case 3500000: |
| | | tmp = B3500000; |
| | | break; |
| | | case 3000000: |
| | | tmp = B3000000; |
| | | break; |
| | | case 2500000: |
| | | tmp = B2500000; |
| | | break; |
| | | case 2000000: |
| | | tmp = B2000000; |
| | | break; |
| | | case 1500000: |
| | | tmp = B1500000; |
| | | break; |
| | | case 1152000: |
| | | tmp = B1152000; |
| | | break; |
| | | case 1000000: |
| | | tmp = B1000000; |
| | | break; |
| | | case 921600: |
| | | tmp = B921600; |
| | | break; |
| | | case 576000: |
| | | tmp = B576000; |
| | | break; |
| | | case 500000: |
| | | tmp = B500000; |
| | | break; |
| | | case 460800: |
| | | tmp = B460800; |
| | | break; |
| | | case 230400: |
| | | tmp = B230400; |
| | | break; |
| | | case 115200: |
| | | tmp = B115200; |
| | | break; |
| | | case 57600: |
| | | tmp = B57600; |
| | | break; |
| | | |
| | | /* Below is POSIX(bits/termios.h) */ |
| | | case 38400: |
| | | tmp = B38400; |
| | | break; |
| | |
| | | tcflush(comport->fd, TCIFLUSH); |
| | | if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg)) |
| | | { |
| | | rv = -7; // Failed to set device com port settings |
| | | rv = -6; // Failed to set device com port settings |
| | | goto CleanUp; |
| | | } |
| | | |
| | | COM_PRINT("Connected device \"%s\".\n", comport->devname); |
| | | |
| | | rv = comport->fd; |
| | | |
| | | CleanUp: |
| | | COM_PRINT("Open device \"%s\" %s.\n", comport->devname, rv>0 ? "successfully" : "failure"); |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | |
| | | /* |
| | | * description: close comport |
| | | * input args: $comport: corresponding comport point |
| | | * description: close comport |
| | | * input args: $comport: corresponding comport point |
| | | */ |
| | | |
| | | void comport_close(comport_t *comport) |
| | | { |
| | | if( !comport ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | dbg_print("invalid input arugments\n"); |
| | | return ; |
| | | } |
| | | |
| | | if ( comport->fd >= 0 ) |
| | | { |
| | | COM_PRINT("Close device \"%s\"\n", comport->devname); |
| | | close(comport->fd); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /* |
| | | * description: write $send_bytes bytes data from $buf to $comport |
| | | * description: write $data_bytes $data to $comport |
| | | * return value: 0: write ok <0: write failure |
| | | */ |
| | | |
| | | int comport_send(comport_t *comport, char *buf, int send_bytes) |
| | | int comport_send(comport_t *comport, char *data, int data_bytes) |
| | | { |
| | | char *ptr; |
| | | int left, bytes = 0; |
| | | int rv = 0; |
| | | char *ptr, *end; |
| | | int send = 0; |
| | | |
| | | if ( !comport || !buf || send_bytes<=0 ) |
| | | if( !comport || !data || data_bytes<=0 ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | rv = -1; |
| | | goto CleanUp; |
| | | dbg_print("invalid input arugments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | if ( comport->fd < 0 ) // Comport not opened ? |
| | | if( comport->fd < 0 ) |
| | | { |
| | | rv = -3; |
| | | COM_PRINT("Serail not connected.\n"); |
| | | goto CleanUp; |
| | | dbg_print("Serail port not opened\n"); |
| | | return -2; |
| | | } |
| | | |
| | | //printf("Send %s with %d bytes.\n", buf, send_bytes); |
| | | ptr = data; |
| | | left = data_bytes; |
| | | |
| | | // Large data, then slice them and send |
| | | if (comport->frag_size < send_bytes) |
| | | while( left > 0 ) |
| | | { |
| | | ptr = buf; |
| | | end = buf + send_bytes; |
| | | /* Large data, then slice them to frag and send */ |
| | | bytes = left>comport->fragsize ? comport->fragsize : left; |
| | | |
| | | do |
| | | rv = write(comport->fd, ptr, bytes); |
| | | if( rv<0 ) |
| | | { |
| | | // Large than frag_size |
| | | if (comport->frag_size < (end - ptr)) |
| | | { |
| | | send = write(comport->fd, ptr, comport->frag_size); |
| | | if (0 >= send || comport->frag_size != send) |
| | | { |
| | | rv = -4; |
| | | goto CleanUp; |
| | | } |
| | | ptr += comport->frag_size; |
| | | } |
| | | else // Less than frag_size, maybe last fragmention. |
| | | { |
| | | send = write(comport->fd, ptr, (end - ptr)); |
| | | if (0 >= send || (end - ptr) != send) |
| | | { |
| | | rv = -4; |
| | | goto CleanUp; |
| | | } |
| | | ptr += (end - ptr); |
| | | } |
| | | rv = -3; |
| | | break; |
| | | } |
| | | while (ptr < end); |
| | | } |
| | | else // The send data is not large than a fragmention. |
| | | { |
| | | send = write(comport->fd, buf, send_bytes); |
| | | if (0 >= send || send_bytes != send) |
| | | { |
| | | rv = -5; |
| | | goto CleanUp; |
| | | } |
| | | |
| | | left -= rv; |
| | | ptr += rv; |
| | | } |
| | | |
| | | CleanUp: |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | /* |
| | | * description: read data from $comport in $timeout <ms> to $buf no more than $bufsize bytes |
| | | * description: read data from $comport in $timeout <ms> to $buf no more than $buf_size bytes |
| | | * return value: the actual read data bytes, <0: read failure |
| | | */ |
| | | |
| | | int comport_recv(comport_t *comport, char *buf, int bufsize, unsigned long timeout) |
| | | int comport_recv(comport_t *comport, char *buf, int buf_size, unsigned long timeout) |
| | | { |
| | | int rv = 0; |
| | | int iRet; |
| | | fd_set rdfds, exfds; |
| | | struct timeval stTime; |
| | | struct timeval to, *to_ptr = NULL; |
| | | int ret, rv = 0; |
| | | int bytes = 0; |
| | | |
| | | if ( !comport || !buf || bufsize<=0 ) |
| | | if ( !comport || !buf || buf_size<=0 ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | rv = -1; |
| | | goto CleanUp; |
| | | dbg_print("invalid input arugments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | if ( comport->fd < 0 ) |
| | | { |
| | | COM_PRINT("%s() comport not connected.\n", __FUNCTION__); |
| | | rv = -2; |
| | | goto CleanUp; |
| | | dbg_print("Serail port not opened\n"); |
| | | return -2; |
| | | } |
| | | |
| | | memset(buf, 0, buf_size); |
| | | |
| | | FD_ZERO(&rdfds); |
| | | FD_ZERO(&exfds); |
| | | FD_SET(comport->fd, &rdfds); |
| | | FD_SET(comport->fd, &exfds); |
| | | |
| | | if (0xFFFFFFFF != timeout) |
| | | if( TIMEOUT_NONE != timeout ) |
| | | { |
| | | stTime.tv_sec = (time_t) (timeout / 1000); |
| | | stTime.tv_usec = (long)(1000 * (timeout % 1000)); |
| | | |
| | | iRet = select(comport->fd + 1, &rdfds, 0, &exfds, &stTime); |
| | | if (0 == iRet) |
| | | { |
| | | rv = 0; |
| | | goto CleanUp; |
| | | } |
| | | else if (0 < iRet) |
| | | { |
| | | if (0 != FD_ISSET(comport->fd, &exfds)) |
| | | { |
| | | rv = -6; |
| | | COM_PRINT("Error checking recv status.\n"); |
| | | goto CleanUp; |
| | | } |
| | | |
| | | if (0 == FD_ISSET(comport->fd, &rdfds)) |
| | | { |
| | | rv = 0; |
| | | COM_PRINT("No incoming data.\n"); |
| | | goto CleanUp; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (EINTR == errno) |
| | | { |
| | | COM_PRINT("catch interrupt signal.\n"); |
| | | rv = 0; |
| | | } |
| | | else |
| | | { |
| | | COM_PRINT("Check recv status failure.\n"); |
| | | rv = -7; |
| | | } |
| | | |
| | | goto CleanUp; |
| | | } |
| | | to.tv_sec = (time_t) (timeout / 1000); |
| | | to.tv_usec = (long)(1000 * (timeout % 1000)); |
| | | to_ptr = &to; |
| | | } |
| | | |
| | | usleep(10000); /* sleep for 10ms for data incoming */ |
| | | |
| | | // Get data from Com port |
| | | iRet = read(comport->fd, buf, bufsize); |
| | | if (0 > iRet) |
| | | while( 1 ) |
| | | { |
| | | if (EINTR == errno) |
| | | rv = 0; // Interrupted signal catched |
| | | else |
| | | rv = -3; // Failed to read Com port |
| | | |
| | | goto CleanUp; |
| | | } |
| | | |
| | | #if 0 |
| | | { |
| | | int i=0; |
| | | printf("Receive %d bytes data: \n", iRet); |
| | | for(i=0; i<iRet; i++) |
| | | /* check got data arrive or not */ |
| | | ret = select(comport->fd+1, &rdfds, 0, &exfds, to_ptr); |
| | | if( ret<0 ) |
| | | { |
| | | printf("0x%02x ", buf[i]); |
| | | /* EINTR means catch interrupt signal */ |
| | | dbg_print("comport select() failed: %s\n", strerror(errno)); |
| | | rv = EINTR==errno ? 0 : -3; |
| | | break; |
| | | } |
| | | printf("\n"); |
| | | else if( 0 == ret ) /* timeout */ |
| | | { |
| | | break; |
| | | } |
| | | |
| | | /* read data from comport */ |
| | | ret = read(comport->fd, buf+bytes, buf_size-bytes); |
| | | if(ret <= 0) |
| | | { |
| | | dbg_print("comport read() failed: %s\n", strerror(errno)); |
| | | break; |
| | | } |
| | | |
| | | bytes += ret; |
| | | if( bytes >= buf_size ) |
| | | break; |
| | | |
| | | /* try to read data in 1ms again, if no data arrive it will break */ |
| | | to.tv_sec = 0; |
| | | to.tv_usec = 10000; |
| | | to_ptr = &to; |
| | | } |
| | | #endif |
| | | |
| | | rv = iRet; |
| | | if( !rv ) |
| | | rv = bytes; |
| | | |
| | | CleanUp: |
| | | return rv; |
| | | |
| | | } |
| | | |
| | | |
| | | /************************************************************************************** |
| | | * Description: Set the comport databit,parity,stopbit,flowctrl into the comport structure |
| | | * Input Args: comport: the comport_t pointer |
| | | * settings: The databit/parity/stopbit/flowctrl settings as like "8N1N" |
| | | * settings: The databit/parity/stopbit/flowctrl settings as like "8N1N" |
| | | * Output Args: NONE |
| | | * Return Value: NONE |
| | | *************************************************************************************/ |
| | | void set_settings(comport_t * comport, const char *settings) |
| | | static inline void set_settings(comport_t * comport, const char *settings) |
| | | { |
| | | if( !settings || !comport ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | dbg_print("invalid input arugments\n"); |
| | | return ; |
| | | } |
| | | |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | #ifdef COM_DEBUG |
| | | void disp_settings(comport_t *comport) |
| | | { |
| | | COM_PRINT("Device:\t\t\t\"%s\"\n", comport->devname); |
| | | COM_PRINT("Baudrate:\t\t%ld\n", comport->baudrate); |
| | | COM_PRINT("DataBit:\t\t\'%d\'\n", comport->databit); |
| | | |
| | | switch (comport->parity) |
| | | { |
| | | case 0: |
| | | COM_PRINT("Parity:\t\t\t\'N\'\n"); |
| | | break; |
| | | case 1: |
| | | COM_PRINT("Parity:\t\t\t\'O\'\n"); |
| | | break; |
| | | case 2: |
| | | COM_PRINT("Parity:\t\t\t\'E\'\n"); |
| | | break; |
| | | case 3: |
| | | COM_PRINT("Parity:\t\t\t\'S\'\n"); |
| | | break; |
| | | } |
| | | COM_PRINT("StopBit:\t\t\'%ld\'\n", (long int)comport->stopbit); |
| | | switch (comport->flowctrl) |
| | | { |
| | | case 0: |
| | | COM_PRINT("FlowCtrl:\t\t\'N\'\n"); |
| | | break; |
| | | case 1: |
| | | COM_PRINT("FlowCtrl:\t\t\'S\'\n"); |
| | | break; |
| | | case 2: |
| | | COM_PRINT("FlowCtrl:\t\t\'H\'\n"); |
| | | break; |
| | | case 3: |
| | | COM_PRINT("FlowCtrl:\t\t\'B\'\n"); |
| | | break; |
| | | } |
| | | COM_PRINT("\n"); |
| | | return; |
| | | } |
| | | #endif |
| | | |
| | | |