/********************************************************************************* * Copyright: (C) 2018 Guo Wenxue * All rights reserved. * * Filename: cp_comport.c * Description: It's the comport operate library. * * Version: 2.0.0(10/17/2018~) * Author: Guo Wenxue * 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) { /* 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; 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 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; idatabit = 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