| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- #include "serial.h"
- #include <errno.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <sys/time.h>
- #include <termios.h>
- #include <unistd.h>
- #define DBG_ENABLE
- #define DBG_COLOR
- #define DBG_SECTION_NAME "serial"
- #define DBG_LEVEL DBG_LOG
- #include "dbg_log.h"
- int serial_init(const char *device,
- int baud, char parity, int data_bit,
- int stop_bit, struct termios *old_tios)
- {
- struct termios tios;
- speed_t speed;
- int flags;
- /* The O_NOCTTY flag tells UNIX that this program doesn't want
- to be the "controlling terminal" for that port. If you
- don't specify this then any input (such as keyboard abort
- signals and so forth) will affect your process
- Timeouts are ignored in canonical input mode or when the
- NDELAY option is set on the file via open or fcntl */
- flags = O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL;
- #ifdef O_CLOEXEC
- flags |= O_CLOEXEC;
- #endif
- int s = open(device, flags);
- if (s == -1) {
- LOG_E("ERROR Can't open the device %s (%s)", device, strerror(errno));
- return -1;
- }
- flags = fcntl(s, F_GETFL, 0);
- flags |= O_NONBLOCK;
- fcntl(s, F_SETFL, flags);
- flags = fcntl(s, F_GETFD);
- flags |= FD_CLOEXEC;
- fcntl(s, F_SETFD, flags);
- /* Save */
- tcgetattr(s, old_tios);
- memset(&tios, 0, sizeof(struct termios));
- /* C_ISPEED Input baud (new interface)
- C_OSPEED Output baud (new interface)
- */
- switch (baud) {
- case 110:
- speed = B110;
- break;
- case 300:
- speed = B300;
- break;
- case 600:
- speed = B600;
- break;
- case 1200:
- speed = B1200;
- break;
- case 2400:
- speed = B2400;
- break;
- case 4800:
- speed = B4800;
- break;
- case 9600:
- speed = B9600;
- break;
- case 19200:
- speed = B19200;
- break;
- case 38400:
- speed = B38400;
- break;
- #ifdef B57600
- case 57600:
- speed = B57600;
- break;
- #endif
- #ifdef B115200
- case 115200:
- speed = B115200;
- break;
- #endif
- #ifdef B230400
- case 230400:
- speed = B230400;
- break;
- #endif
- #ifdef B460800
- case 460800:
- speed = B460800;
- break;
- #endif
- #ifdef B500000
- case 500000:
- speed = B500000;
- break;
- #endif
- #ifdef B576000
- case 576000:
- speed = B576000;
- break;
- #endif
- #ifdef B921600
- case 921600:
- speed = B921600;
- break;
- #endif
- #ifdef B1000000
- case 1000000:
- speed = B1000000;
- break;
- #endif
- #ifdef B1152000
- case 1152000:
- speed = B1152000;
- break;
- #endif
- #ifdef B1500000
- case 1500000:
- speed = B1500000;
- break;
- #endif
- #ifdef B2500000
- case 2500000:
- speed = B2500000;
- break;
- #endif
- #ifdef B3000000
- case 3000000:
- speed = B3000000;
- break;
- #endif
- #ifdef B3500000
- case 3500000:
- speed = B3500000;
- break;
- #endif
- #ifdef B4000000
- case 4000000:
- speed = B4000000;
- break;
- #endif
- default:
- speed = B9600;
- LOG_W("WARNING Unknown baud rate %d for %s (B9600 used)", baud, device);
- }
- /* Set the baud rate */
- if ((cfsetispeed(&tios, speed) < 0) ||
- (cfsetospeed(&tios, speed) < 0)) {
- close(s);
- s = -1;
- return -1;
- }
- /* C_CFLAG Control options
- CLOCAL Local line - do not change "owner" of port
- CREAD Enable receiver
- */
- tios.c_cflag |= (CREAD | CLOCAL);
- /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */
- /* Set data bits (5, 6, 7, 8 bits)
- CSIZE Bit mask for data bits
- */
- tios.c_cflag &= ~CSIZE;
- switch (data_bit) {
- case 5:
- tios.c_cflag |= CS5;
- break;
- case 6:
- tios.c_cflag |= CS6;
- break;
- case 7:
- tios.c_cflag |= CS7;
- break;
- case 8:
- default:
- tios.c_cflag |= CS8;
- break;
- }
- /* Stop bit (1 or 2) */
- if (stop_bit == 1)
- tios.c_cflag &= ~CSTOPB;
- else /* 2 */
- tios.c_cflag |= CSTOPB;
- /* PARENB Enable parity bit
- PARODD Use odd parity instead of even */
- if (parity == 'N') {
- /* None */
- tios.c_cflag &= ~PARENB;
- } else if (parity == 'E') {
- /* Even */
- tios.c_cflag |= PARENB;
- tios.c_cflag &= ~PARODD;
- } else {
- /* Odd */
- tios.c_cflag |= PARENB;
- tios.c_cflag |= PARODD;
- }
- /* Read the man page of termios if you need more information. */
- /* This field isn't used on POSIX systems
- tios.c_line = 0;
- */
- /* C_LFLAG Line options
- ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
- ICANON Enable canonical input (else raw)
- XCASE Map uppercase \lowercase (obsolete)
- ECHO Enable echoing of input characters
- ECHOE Echo erase character as BS-SP-BS
- ECHOK Echo NL after kill character
- ECHONL Echo NL
- NOFLSH Disable flushing of input buffers after
- interrupt or quit characters
- IEXTEN Enable extended functions
- ECHOCTL Echo control characters as ^char and delete as ~?
- ECHOPRT Echo erased character as character erased
- ECHOKE BS-SP-BS entire line on line kill
- FLUSHO Output being flushed
- PENDIN Retype pending input at next read or input char
- TOSTOP Send SIGTTOU for background output
- Canonical input is line-oriented. Input characters are put
- into a buffer which can be edited interactively by the user
- until a CR (carriage return) or LF (line feed) character is
- received.
- Raw input is unprocessed. Input characters are passed
- through exactly as they are received, when they are
- received. Generally you'll deselect the ICANON, ECHO,
- ECHOE, and ISIG options when using raw input
- */
- /* Raw input */
- tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
- /* C_IFLAG Input options
- Constant Description
- INPCK Enable parity check
- IGNPAR Ignore parity errors
- PARMRK Mark parity errors
- ISTRIP Strip parity bits
- IXON Enable software flow control (outgoing)
- IXOFF Enable software flow control (incoming)
- IXANY Allow any character to start flow again
- IGNBRK Ignore break condition
- BRKINT Send a SIGINT when a break condition is detected
- INLCR Map NL to CR
- IGNCR Ignore CR
- ICRNL Map CR to NL
- IUCLC Map uppercase to lowercase
- IMAXBEL Echo BEL on input line too long
- */
- if (parity == 'N') {
- /* None */
- tios.c_iflag &= ~INPCK;
- } else {
- tios.c_iflag |= INPCK;
- }
- /* Software flow control is disabled */
- tios.c_iflag &= ~(IXON | IXOFF | IXANY);
- /* C_OFLAG Output options
- OPOST Postprocess output (not set = raw output)
- ONLCR Map NL to CR-NL
- ONCLR ant others needs OPOST to be enabled
- */
- /* Raw ouput */
- tios.c_oflag &= ~OPOST;
- /* C_CC Control characters
- VMIN Minimum number of characters to read
- VTIME Time to wait for data (tenths of seconds)
- UNIX serial interface drivers provide the ability to
- specify character and packet timeouts. Two elements of the
- c_cc array are used for timeouts: VMIN and VTIME. Timeouts
- are ignored in canonical input mode or when the NDELAY
- option is set on the file via open or fcntl.
- VMIN specifies the minimum number of characters to read. If
- it is set to 0, then the VTIME value specifies the time to
- wait for every character read. Note that this does not mean
- that a read call for N bytes will wait for N characters to
- come in. Rather, the timeout will apply to the first
- character and the read call will return the number of
- characters immediately available (up to the number you
- request).
- If VMIN is non-zero, VTIME specifies the time to wait for
- the first character read. If a character is read within the
- time given, any read will block (wait) until all VMIN
- characters are read. That is, once the first character is
- read, the serial interface driver expects to receive an
- entire packet of characters (VMIN bytes total). If no
- character is read within the time allowed, then the call to
- read returns 0. This method allows you to tell the serial
- driver you need exactly N bytes and any read call will
- return 0 or N bytes. However, the timeout only applies to
- the first character read, so if for some reason the driver
- misses one character inside the N byte packet then the read
- call could block forever waiting for additional input
- characters.
- VTIME specifies the amount of time to wait for incoming
- characters in tenths of seconds. If VTIME is set to 0 (the
- default), reads will block (wait) indefinitely unless the
- NDELAY option is set on the port with open or fcntl.
- */
- /* Unused because we use open with the NDELAY option */
- tios.c_cc[VMIN] = 0;
- tios.c_cc[VTIME] = 0;
- if (tcsetattr(s, TCSANOW, &tios) < 0) {
- close(s);
- s = -1;
- return -1;
- }
- return s;
- }
- void serial_close(int s, struct termios *old_tios)
- {
- if (s != -1) {
- tcsetattr(s, TCSANOW, old_tios);
- close(s);
- }
- }
- int serial_send(int s, const uint8_t *buf, int length)
- {
- return write(s, buf, length);
- }
- int serial_receive(int s, uint8_t *buf, int bufsz, int timeout)
- {
- int len = 0;
- int rc = 0;
- fd_set rset;
- struct timeval tv;
- while (bufsz > 0) {
- FD_ZERO(&rset);
- FD_SET(s, &rset);
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
- rc = select(s + 1, &rset, NULL, NULL, &tv);
- if (rc == -1) {
- if (errno == EINTR)
- continue;
- }
- if (rc <= 0) {
- break;
- }
- rc = read(s, buf + len, bufsz);
- if (rc <= 0) {
- break;
- }
- len += rc;
- bufsz -= rc;
- timeout = 20;
- }
- if (rc >= 0) {
- rc = len;
- }
- return rc;
- }
- int serial_flush(int s)
- {
- if (s != -1) {
- tcflush(s, TCIOFLUSH);
- }
- return 0;
- }
|