/* ******************************************************************************** * Copyright: (C) 2012 Guo Wenxue * All rights reserved. * * Filename: cp_comport.c * Description: It's the comport operate library. * * Version: 1.0.0(10/17/2011~) * Author: Guo Wenxue * ChangeLog: 1, Release initial version on "10/17/2011 03:33:25 PM" * ********************************************************************************/ #include "cp_comport.h" /************************************************************************************** * Description: Set the comport structure * Input Args: 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' * Output Args: NONE * Return Value: The cp_comport_t structure pointer. *************************************************************************************/ cp_comport_t *comport_init(const char *dev_name, int baudrate, const char *settings) { cp_comport_t *comport = NULL; if (NULL == (comport = (cp_comport_t *) malloc(sizeof(cp_comport_t)))) { return NULL; } memset(comport, 0, sizeof(cp_comport_t)); comport->connected = 0; comport->frag_size = 128; strncpy(comport->dev_name, dev_name, DEVNAME_LEN); comport->baudrate = baudrate; set_settings(comport, settings); #ifdef COM_DEBUG disp_settings(comport); #endif return comport; } #ifdef COM_DEBUG void disp_settings(cp_comport_t * comport) { COM_PRINT("Device:\t\t\t\"%s\"\n", comport->dev_name); 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 /************************************************************************************** * Description: Set the comport databit,parity,stopbit,flowctrl * Input Args: comport: the cp_comport_t pointer * settings: The databit/parity/stopbit/flowctrl settings as like "8N1N" * Output Args: NONE * Return Value: NONE *************************************************************************************/ void set_settings(cp_comport_t * comport, const char *settings) { if(NULL==settings || NULL==comport) 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; } } void comport_close(cp_comport_t * comport) { if (0 != comport->fd) { COM_PRINT("Close device \"%s\"\n", comport->dev_name); close(comport->fd); } comport->connected = 0x00; comport->fd = -1; } void cp_comport_term(cp_comport_t * comport) { if(NULL == comport) return; if (0 != comport->fd) { comport_close(comport); } memset(comport, 0x00, sizeof(cp_comport_t)); free(comport); comport = NULL; return; } int comport_open(cp_comport_t * comport) { int retval = -1; struct termios old_cfg, new_cfg; int old_flags; long tmp; if(NULL==comport) return -1; comport_close(comport); /* Not a TTY device */ if( !strstr(comport->dev_name, "tty")) { COM_PRINT("Open Not tty device \"%s\"\n", comport->dev_name); comport->fd = open(comport->dev_name, O_RDWR); retval = comport->fd<0 ? -2 : comport->fd; goto CleanUp; } comport->fd = open(comport->dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK); if (comport->fd < 0) { retval = -3; goto CleanUp; } COM_PRINT("Open device \"%s\"\n", comport->dev_name); 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)) { retval = -4; goto CleanUp; } } else // Failure { retval = -5; goto CleanUp; } if (0 != tcgetattr(comport->fd, &old_cfg)) { retval = -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)) { retval = -7; // Failed to set device com port settings goto CleanUp; } COM_PRINT("Connected device \"%s\".\n", comport->dev_name); comport->connected = 0x01; retval = comport->fd; CleanUp: COM_PRINT("Open device \"%s\" %s.\n", comport->dev_name, retval>0 ? "successfully" : "failure"); return retval; } void nonblock() { struct termios ttystate; //get the terminal state tcgetattr(STDIN_FILENO, &ttystate); //turn off canonical mode ttystate.c_lflag &= ~ICANON; //minimum of number input read. ttystate.c_cc[VMIN] = 1; //set the terminal attributes. tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); } int kbhit() { struct timeval tv; fd_set fds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); return FD_ISSET(STDIN_FILENO, &fds); } int comport_recv(cp_comport_t * comport, char *buf, int buf_size, unsigned long timeout) { int retval = 0; // Function return value int iRet; fd_set stReadFds, stExcpFds; struct timeval stTime; if (NULL == buf || 0 >= buf_size) { COM_PRINT("%s() usage error.\n", __FUNCTION__); retval = -1; goto CleanUp; } if (0x01 != comport->connected) { COM_PRINT("%s() comport not connected.\n", __FUNCTION__); retval = -2; goto CleanUp; } //printf("bufsize=%d timeout=%lu\n", buf_size, timeout); FD_ZERO(&stReadFds); FD_ZERO(&stExcpFds); FD_SET(comport->fd, &stReadFds); FD_SET(comport->fd, &stExcpFds); if (0xFFFFFFFF != timeout) { stTime.tv_sec = (time_t) (timeout / 1000); stTime.tv_usec = (long)(1000 * (timeout % 1000)); iRet = select(comport->fd + 1, &stReadFds, 0, &stExcpFds, &stTime); if (0 == iRet) { retval = 0; // No data in Com port buffer goto CleanUp; } else if (0 < iRet) { if (0 != FD_ISSET(comport->fd, &stExcpFds)) { retval = -6; // Error during checking recv status COM_PRINT("Error checking recv status.\n"); goto CleanUp; } if (0 == FD_ISSET(comport->fd, &stReadFds)) { retval = 0; // No incoming data COM_PRINT("No incoming data.\n"); goto CleanUp; } } else { if (EINTR == errno) { COM_PRINT("catch interrupt signal.\n"); retval = 0; // Interrupted signal catched } else { COM_PRINT("Check recv status failure.\n"); retval = -7; // Error during checking recv status } goto CleanUp; } } usleep(10000); /* sleep for 10ms for data incoming */ // Get data from Com port iRet = read(comport->fd, buf, buf_size); if (0 > iRet) { if (EINTR == errno) retval = 0; // Interrupted signal catched else retval = -3; // Failed to read Com port goto CleanUp; } #if 0 { int i=0; printf("Receive %d bytes data: \n", iRet); for(i=0; i= send_bytes) { COM_PRINT("%s() Usage error.\n", __FUNCTION__); retval = -1; goto CleanUp; } if (0x01 != comport->connected) // Comport not opened ? { retval = -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) { retval = -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) { retval = -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) { retval = -5; goto CleanUp; } } CleanUp: return retval; }