initial
[smouse] / src / unix / serial.c
1 #include <stdio.h>
2 #include <stdarg.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <sys/ioctl.h>
7 #include <sys/select.h>
8 #include <sys/time.h>
9 #include <fcntl.h>
10 #include <termios.h>
11 #include "serial.h"
12
13 static int baud_id(int baud);
14
15 int ser_open(const char *port, int baud, unsigned int mode)
16 {
17         int fd;
18         struct termios term;
19
20         if((baud = baud_id(baud)) == -1) {
21                 fprintf(stderr, "ser_open: invalid baud number: %d\n", baud);
22                 return -1;
23         }
24
25         if((fd = open(port, O_RDWR | O_NONBLOCK | O_NOCTTY)) == -1) {
26                 return -1;
27         }
28
29         /*memset(&term, 0, sizeof term);*/
30         tcgetattr(fd, &term);
31
32         term.c_oflag = 0;
33         term.c_lflag = 0;
34         term.c_cc[VMIN] = 0;
35         term.c_cc[VTIME] = 0;
36
37         term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL | CRTSCTS;
38         if(mode & SER_8N2) {
39                 term.c_cflag |= CSTOPB;
40         }
41
42         term.c_iflag = IGNBRK | IGNPAR;
43
44         cfsetispeed(&term, baud);
45         cfsetospeed(&term, baud);
46
47         if(tcsetattr(fd, TCSANOW, &term) < 0) {
48                 fprintf(stderr, "ser_open: failed to set terminal attributes\n");
49                 close(fd);
50                 return -1;
51         }
52
53         if(mode & SER_HWFLOW) {
54                 int st;
55                 if(ioctl(fd, TIOCMGET, &st) == -1) {
56                         perror("ser_open: failed to get modem status");
57                         close(fd);
58                         return -1;
59                 }
60                 st |= TIOCM_DTR | TIOCM_RTS;
61                 if(ioctl(fd, TIOCMSET, &st) == -1) {
62                         perror("ser_open: failed to set flow control");
63                         close(fd);
64                         return -1;
65                 }
66         }
67
68         return fd;
69 }
70
71 void ser_close(int fd)
72 {
73         close(fd);
74 }
75
76 int ser_pending(int fd)
77 {
78         static struct timeval tv_zero;
79         fd_set rd;
80
81         FD_ZERO(&rd);
82         FD_SET(fd, &rd);
83
84         while(select(fd + 1, &rd, 0, 0, &tv_zero) == -1 && errno == EINTR);
85         return FD_ISSET(fd, &rd);
86 }
87
88 int ser_wait(int fd, long msec)
89 {
90         struct timeval tv, tv0;
91         fd_set rd;
92
93         FD_ZERO(&rd);
94         FD_SET(fd, &rd);
95
96         tv.tv_sec = msec / 1000;
97         tv.tv_usec = msec * 1000;
98
99         gettimeofday(&tv0, 0);
100
101         while(select(fd + 1, &rd, 0, 0, msec >= 0 ? &tv : 0) == -1 && errno == EINTR) {
102                 /* interrupted, recalc timeout and go back to sleep */
103                 if(msec >= 0) {
104                         gettimeofday(&tv, 0);
105                         msec -= (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
106                         if(msec < 0) msec = 0;
107
108                         tv.tv_sec = msec / 1000;
109                         tv.tv_usec = msec * 1000;
110                 }
111         }
112
113         return FD_ISSET(fd, &rd);
114 }
115
116 int ser_write(int fd, const char *buf, int count)
117 {
118         return write(fd, buf, count);
119 }
120
121 int ser_read(int fd, char *buf, int count)
122 {
123         return read(fd, buf, count);
124 }
125
126 void ser_printf(int fd, const char *fmt, ...)
127 {
128         va_list ap;
129
130         va_start(ap, fmt);
131         vdprintf(fd, fmt, ap);
132         va_end(ap);
133 }
134
135 char *ser_getline(int fd, char *buf, int bsz)
136 {
137         static char linebuf[512];
138         static int widx;
139         int i, rd, size;
140         char *ptr;
141
142         size = sizeof linebuf - widx;
143         while(size && (rd = read(fd, linebuf + widx, size)) > 0) {
144                 widx += rd;
145                 size -= rd;
146         }
147
148         ptr = linebuf;
149         for(i=0; i<widx; i++) {
150                 if(*ptr == '\r' || *ptr == '\n') {
151                         *ptr++ = '\n';
152                         if(i < widx - 1 && ptr[1] == '\n') {
153                                 *ptr++ = 0;
154                         }
155
156                         size = widx >= bsz ? bsz - 1 : widx;
157                         memcpy(buf, linebuf, size);
158                         buf[size] = 0;
159
160                         memmove(linebuf, linebuf + widx, sizeof linebuf - widx);
161                         return buf;
162                 } else {
163                         ++ptr;
164                 }
165         }
166         return 0;
167 }
168
169 static int baud_id(int baud)
170 {
171         switch(baud) {
172         case 50: return B50;
173         case 75: return B75;
174         case 110: return B110;
175         case 134: return B134;
176         case 150: return B150;
177         case 200: return B200;
178         case 300: return B300;
179         case 600: return B600;
180         case 1200: return B1200;
181         case 1800: return B1800;
182         case 2400: return B2400;
183         case 4800: return B4800;
184         case 9600: return B9600;
185         case 19200: return B19200;
186         case 38400: return B38400;
187         case 57600: return B57600;
188         case 115200: return B115200;
189         default:
190                 break;
191         }
192         return -1;
193 }