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