New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2018 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~) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/17/2018 03:33:25 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include "comport.h" |
| | | |
| | | static void set_settings(comport_t *comport, const char *settings); |
| | | |
| | | #ifdef COM_DEBUG |
| | | void disp_settings(comport_t *comport); |
| | | #endif |
| | | |
| | | |
| | | |
| | | /* |
| | | * description: Open the serial port |
| | | * |
| | | * 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' |
| | | * |
| | | * return value: The comport opened file description, <0 means failure |
| | | */ |
| | | int comport_open(comport_t *comport, const char *devname, long baudrate, const char *settings) |
| | | { |
| | | int rv = -1; |
| | | struct termios old_cfg, new_cfg; |
| | | int old_flags; |
| | | long tmp; |
| | | |
| | | if( !comport || !devname ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | return -1; |
| | | } |
| | | |
| | | memset(comport, 0, sizeof(*comport)); |
| | | strncpy(comport->devname, devname, DEVNAME_LEN); |
| | | comport->baudrate = baudrate; |
| | | comport->fd = -1; |
| | | comport->frag_size = 128; |
| | | |
| | | set_settings(comport, settings); |
| | | #ifdef COM_DEBUG |
| | | disp_settings(comport); |
| | | #endif |
| | | |
| | | /* Not a TTY device */ |
| | | if( !strstr(comport->devname, "tty")) |
| | | { |
| | | COM_PRINT("Open Not tty device \"%s\"\n", comport->devname); |
| | | return -2; |
| | | } |
| | | |
| | | comport->fd = open(comport->devname, O_RDWR | O_NOCTTY | O_NONBLOCK); |
| | | if (comport->fd < 0) |
| | | { |
| | | rv = -3; |
| | | goto CleanUp; |
| | | } |
| | | 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))) |
| | | { |
| | | /* Flush input and output */ |
| | | if (-1 == tcflush(comport->fd, TCIOFLUSH)) |
| | | { |
| | | rv = -4; |
| | | goto CleanUp; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | rv = -5; |
| | | goto CleanUp; |
| | | } |
| | | |
| | | if (0 != tcgetattr(comport->fd, &old_cfg)) |
| | | { |
| | | rv = -6; // Failed to get Com settings |
| | | goto CleanUp; |
| | | } |
| | | |
| | | 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); |
| | | new_cfg.c_oflag &= ~(OPOST); |
| | | |
| | | /* Set the data bit */ |
| | | switch (comport->databit) |
| | | { |
| | | case 0x07: |
| | | new_cfg.c_cflag |= CS7; |
| | | break; |
| | | case 0x06: |
| | | new_cfg.c_cflag |= CS6; |
| | | break; |
| | | case 0x05: |
| | | new_cfg.c_cflag |= CS5; |
| | | break; |
| | | default: |
| | | new_cfg.c_cflag |= CS8; |
| | | break; |
| | | } |
| | | |
| | | /* Set the parity */ |
| | | switch (comport->parity) |
| | | { |
| | | case 0x01: // Odd |
| | | new_cfg.c_cflag |= (PARENB | PARODD); |
| | | new_cfg.c_cflag |= (INPCK | ISTRIP); |
| | | break; |
| | | case 0x02: // Even |
| | | new_cfg.c_cflag |= PARENB; |
| | | new_cfg.c_cflag &= ~PARODD;; |
| | | new_cfg.c_cflag |= (INPCK | ISTRIP); |
| | | break; |
| | | case 0x03: |
| | | new_cfg.c_cflag &= ~PARENB; |
| | | new_cfg.c_cflag &= ~CSTOPB; |
| | | break; |
| | | default: |
| | | new_cfg.c_cflag &= ~PARENB; |
| | | } |
| | | |
| | | /* Set Stop bit */ |
| | | if (0x01 != comport->stopbit) |
| | | { |
| | | new_cfg.c_cflag |= CSTOPB; |
| | | } |
| | | else |
| | | { |
| | | new_cfg.c_cflag &= ~CSTOPB; |
| | | } |
| | | |
| | | /* Set flow control */ |
| | | switch (comport->flowctrl) |
| | | { |
| | | 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 |
| | | new_cfg.c_iflag &= ~(IXON | IXOFF); |
| | | break; |
| | | default: // NONE |
| | | new_cfg.c_cflag &= ~(CRTSCTS); |
| | | new_cfg.c_iflag &= ~(IXON | IXOFF); |
| | | break; |
| | | } |
| | | |
| | | /* Set baudrate */ |
| | | switch (comport->baudrate) |
| | | { |
| | | case 115200: |
| | | tmp = B115200; |
| | | break; |
| | | case 57600: |
| | | tmp = B57600; |
| | | break; |
| | | case 38400: |
| | | tmp = B38400; |
| | | break; |
| | | case 19200: |
| | | tmp = B19200; |
| | | break; |
| | | case 9600: |
| | | tmp = B9600; |
| | | break; |
| | | case 4800: |
| | | tmp = B4800; |
| | | break; |
| | | case 2400: |
| | | tmp = B2400; |
| | | break; |
| | | case 1800: |
| | | tmp = B1800; |
| | | break; |
| | | case 1200: |
| | | tmp = B1200; |
| | | break; |
| | | case 600: |
| | | tmp = B600; |
| | | break; |
| | | case 300: |
| | | tmp = B300; |
| | | break; |
| | | case 200: |
| | | tmp = B200; |
| | | break; |
| | | case 150: |
| | | tmp = B150; |
| | | break; |
| | | case 134: |
| | | tmp = B134; |
| | | break; |
| | | case 110: |
| | | tmp = B110; |
| | | break; |
| | | case 75: |
| | | tmp = B75; |
| | | break; |
| | | case 50: |
| | | tmp = B50; |
| | | break; |
| | | default: |
| | | tmp = B115200; |
| | | } |
| | | cfsetispeed(&new_cfg, tmp); |
| | | cfsetispeed(&new_cfg, tmp); |
| | | |
| | | /* Set the Com port timeout settings */ |
| | | new_cfg.c_cc[VMIN] = 0; |
| | | new_cfg.c_cc[VTIME] = 0; |
| | | |
| | | tcflush(comport->fd, TCIFLUSH); |
| | | if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg)) |
| | | { |
| | | rv = -7; // 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 |
| | | */ |
| | | |
| | | void comport_close(comport_t *comport) |
| | | { |
| | | if( !comport ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | return ; |
| | | } |
| | | |
| | | if ( comport->fd >= 0 ) |
| | | { |
| | | COM_PRINT("Close device \"%s\"\n", comport->devname); |
| | | close(comport->fd); |
| | | } |
| | | |
| | | comport->fd = -1; |
| | | return ; |
| | | } |
| | | |
| | | /* |
| | | * description: write $send_bytes bytes data from $buf to $comport |
| | | * return value: 0: write ok <0: write failure |
| | | */ |
| | | |
| | | int comport_send(comport_t *comport, char *buf, int send_bytes) |
| | | { |
| | | int rv = 0; |
| | | char *ptr, *end; |
| | | int send = 0; |
| | | |
| | | if ( !comport || !buf || send_bytes<=0 ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | rv = -1; |
| | | goto CleanUp; |
| | | } |
| | | |
| | | if ( comport->fd < 0 ) // Comport not opened ? |
| | | { |
| | | rv = -3; |
| | | COM_PRINT("Serail not connected.\n"); |
| | | goto CleanUp; |
| | | } |
| | | |
| | | //printf("Send %s with %d bytes.\n", buf, send_bytes); |
| | | |
| | | // Large data, then slice them and send |
| | | if (comport->frag_size < send_bytes) |
| | | { |
| | | ptr = buf; |
| | | end = buf + send_bytes; |
| | | |
| | | do |
| | | { |
| | | // 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); |
| | | } |
| | | } |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | CleanUp: |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | /* |
| | | * description: read data from $comport in $timeout <ms> to $buf no more than $bufsize 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 rv = 0; |
| | | int iRet; |
| | | fd_set rdfds, exfds; |
| | | struct timeval stTime; |
| | | |
| | | if ( !comport || !buf || bufsize<=0 ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | rv = -1; |
| | | goto CleanUp; |
| | | } |
| | | |
| | | if ( comport->fd < 0 ) |
| | | { |
| | | COM_PRINT("%s() comport not connected.\n", __FUNCTION__); |
| | | rv = -2; |
| | | goto CleanUp; |
| | | } |
| | | |
| | | FD_ZERO(&rdfds); |
| | | FD_ZERO(&exfds); |
| | | FD_SET(comport->fd, &rdfds); |
| | | FD_SET(comport->fd, &exfds); |
| | | |
| | | if (0xFFFFFFFF != 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; |
| | | } |
| | | } |
| | | |
| | | usleep(10000); /* sleep for 10ms for data incoming */ |
| | | |
| | | // Get data from Com port |
| | | iRet = read(comport->fd, buf, bufsize); |
| | | if (0 > iRet) |
| | | { |
| | | 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++) |
| | | { |
| | | printf("0x%02x ", buf[i]); |
| | | } |
| | | printf("\n"); |
| | | } |
| | | #endif |
| | | |
| | | rv = iRet; |
| | | |
| | | 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" |
| | | * Output Args: NONE |
| | | * Return Value: NONE |
| | | *************************************************************************************/ |
| | | void set_settings(comport_t * comport, const char *settings) |
| | | { |
| | | if( !settings || !comport ) |
| | | { |
| | | COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__); |
| | | return ; |
| | | } |
| | | |
| | | switch (settings[0]) /* data bit */ |
| | | { |
| | | case '7': |
| | | comport->databit = 7; |
| | | break; |
| | | case '8': |
| | | default: |
| | | comport->databit = 8; |
| | | break; |
| | | } |
| | | |
| | | switch (settings[1]) /* parity */ |
| | | { |
| | | case 'O': |
| | | case 'o': |
| | | comport->parity = 1; |
| | | break; |
| | | case 'E': |
| | | case 'e': |
| | | comport->parity = 2; |
| | | break; |
| | | case 'S': |
| | | case 's': |
| | | comport->parity = 3; |
| | | break; |
| | | case 'N': |
| | | case 'n': |
| | | default: |
| | | comport->parity = 0; |
| | | break; |
| | | } |
| | | |
| | | switch (settings[2]) /* stop bit */ |
| | | { |
| | | case '0': |
| | | comport->stopbit = 0; |
| | | break; |
| | | case '1': |
| | | default: |
| | | comport->stopbit = 1; |
| | | break; |
| | | } |
| | | |
| | | switch (settings[3]) /* flow control */ |
| | | { |
| | | case 'S': |
| | | case 's': |
| | | comport->flowctrl = 1; |
| | | break; |
| | | case 'H': |
| | | case 'h': |
| | | comport->flowctrl = 2; |
| | | break; |
| | | case 'B': |
| | | case 'b': |
| | | comport->flowctrl = 3; |
| | | break; |
| | | case 'N': |
| | | case 'n': |
| | | default: |
| | | comport->flowctrl = 0; |
| | | 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 |
| | | |
| | | |