dropped in the dos stuff
[smouse] / src / dos / serial.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdarg.h>
5 #include <ctype.h>
6 #include <dos.h>
7 #include <conio.h>
8 #include <i86.h>
9 #include "serial.h"
10
11 #define UART1_BASE      0x3f8
12 #define UART2_BASE      0x2f8
13 #define UART1_IRQ       4
14 #define UART2_IRQ       3
15
16 #define UART_DATA       0
17 #define UART_INTR       1
18 #define UART_DIVLO      0
19 #define UART_DIVHI      1
20 #define UART_FIFO       2
21 #define UART_IID        2
22 #define UART_LCTL       3
23 #define UART_MCTL       4
24 #define UART_LSTAT      5
25 #define UART_MSTAT      6
26
27 /* interrupt enable register bits */
28 #define INTR_RECV       1
29 #define INTR_SEND       2
30 #define INTR_LSTAT      4
31 #define INTR_DELTA      8
32
33 /* fifo control register bits */
34 #define FIFO_ENABLE             0x01
35 #define FIFO_RECV_CLEAR 0x02
36 #define FIFO_SEND_CLEAR 0x04
37 #define FIFO_DMA                0x08
38 #define FIFO_TRIG_4             0x40
39 #define FIFO_TRIG_8             0x80
40 #define FIFO_TRIG_14    0xc0
41
42 /* interrupt id register bits */
43 #define IID_PENDING             0x01
44 #define IID_ID0                 0x02
45 #define IID_ID1                 0x04
46 #define IID_ID2                 0x08
47 #define IID_FIFO_EN             0xc0
48
49 #define IID_SOURCE              0xe
50
51 #define IID_DELTA               0
52 #define IID_SEND                0x2
53 #define IID_RECV                0x4
54 #define IID_FIFO                0xc
55 #define IID_STATUS              0x6
56
57 /* line control register bits */
58 #define LCTL_BITS_8     0x03
59 #define LCTL_STOP_2     0x04
60 #define LCTL_DLAB       0x80
61 #define LCTL_8N1        LCTL_BITS_8
62 #define LCTL_8N2        (LCTL_BITS_8 | LCTL_STOP_2)
63
64 /* modem control register bits */
65 #define MCTL_DTR        0x01
66 #define MCTL_RTS        0x02
67 #define MCTL_OUT1       0x04
68 #define MCTL_OUT2       0x08
69 #define MCTL_LOOP       0x10
70
71 /* line status register bits */
72 #define LST_DRDY                0x01
73 #define LST_ERR_OVER    0x02
74 #define LST_ERR_PARITY  0x04
75 #define LST_ERR_FRAME   0x08
76 #define LST_ERR_BRK             0x10
77 #define LST_TREG_EMPTY  0x20
78 #define LST_TIDLE               0x40
79 #define LST_ERROR               0x80
80
81 /* modem status register bits */
82 #define MST_DELTA_CTS   0x01
83 #define MST_DELTA_DSR   0x02
84 #define MST_TERI                0x04
85 #define MST_DELTA_DCD   0x08
86 #define MST_CTS                 0x10
87 #define MST_DSR                 0x20
88 #define MST_RING                0x40
89 #define MST_DCD                 0x80
90
91 /* interrupt controller stuff */
92 #define PIC1_CMD_PORT   0x20
93 #define PIC1_DATA_PORT  0x21
94 #define PIC2_CMD_PORT   0xa0
95 #define PIC2_DATA_PORT  0xa1
96 #define OCW2_EOI                0x20
97
98 #define COM_FMT_8N1             LCTL_8N1
99 #define COM_FMT_8N2             LCTL_8N2
100
101 struct serial_port {
102         int base, intr;
103         int blocking;
104
105         char inbuf[256];
106         int inbuf_ridx, inbuf_widx;
107 };
108
109 #define BNEXT(x)        (((x) + 1) & 0xff)
110 #define BEMPTY(b)       (b##_ridx == b##_widx)
111
112 static void ser_putc(int uart_base, char c);
113 static void ser_puts(int uart_base, const char *s);
114 static int ser_getc(int uart_base);
115 static int have_recv(int base);
116 static void __interrupt __far recv_intr(void);
117
118 static struct serial_port ports[2];
119 static int num_open;
120
121 static int uart_base[] = {UART1_BASE, UART2_BASE};
122 static int uart_irq[] = {UART1_IRQ, UART2_IRQ};
123
124 static void (__interrupt __far *prev_recv_intr)(void);
125
126
127 int ser_open(const char *port, int baud, unsigned int mode)
128 {
129         unsigned char ctl;
130         unsigned short div = 115200 / baud;
131         int i, pidx, base, intr, fd;
132         unsigned int fmt;
133
134         if(toupper(port[0]) != 'C' || toupper(port[1]) != 'O' || toupper(port[2]) != 'M'
135                         || !isdigit(port[3])) {
136                 fprintf(stderr, "ser_open: invalid com port: %s\n", port);
137                 return -1;
138         }
139         pidx = port[3] - '0';
140         if(pidx < 1 || pidx > 2) {
141                 fprintf(stderr, "ser_open: invalid com port: %d\n", pidx);
142                 return -1;
143         }
144
145         if(ports[pidx].base) {
146                 fprintf(stderr, "ser_open: port %d already open!\n", pidx);
147                 return -1;
148         }
149         memset(ports + pidx, 0, sizeof ports[pidx]);
150
151         base = uart_base[pidx];
152         intr = uart_irq[pidx] | 8;
153
154         if(mode & SER_8N2) {
155                 fmt = COM_FMT_8N2;
156         } else {
157                 fmt = COM_FMT_8N1;
158         }
159
160         _disable();
161         if(!prev_recv_intr) {
162                 prev_recv_intr = _dos_getvect(intr);
163                 _dos_setvect(intr, recv_intr);
164         }
165         /* unmask the appropriate interrupt */
166         outp(PIC1_DATA_PORT, inp(PIC1_DATA_PORT) & ~(1 << uart_irq[pidx]));
167
168         outp(base + UART_LCTL, LCTL_DLAB);
169         outp(base + UART_DIVLO, div & 0xff);
170         outp(base + UART_DIVHI, (div >> 8) & 0xff);
171         outp(base + UART_LCTL, fmt);    /* fmt should be LCTL_8N1, LCTL_8N2 etc */
172         outp(base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR);
173         outp(base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2);
174         outp(base + UART_INTR, INTR_RECV);
175
176         _enable();
177
178         ports[pidx].base = base;
179         ports[pidx].intr = intr;
180         ports[pidx].blocking = 1;
181         ++num_open;
182         return pidx;
183 }
184
185 void ser_close(int fd)
186 {
187         if(--num_open == 0) {
188                 _disable();
189                 outp(ports[fd].base + UART_INTR, 0);
190                 outp(ports[fd].base + UART_MCTL, 0);
191                 _dos_setvect(ports[fd].intr, prev_recv_intr);
192                 prev_recv_intr = 0;
193                 _enable();
194         }
195
196         ports[fd].base = 0;
197 }
198
199 int ser_block(int fd)
200 {
201         ports[fd].blocking = 1;
202         return 0;
203 }
204
205 int ser_nonblock(int fd)
206 {
207         ports[fd].blocking = 0;
208         return 0;
209 }
210
211 int ser_pending(int fd)
212 {
213         return !BEMPTY(ports[fd].inbuf);
214 }
215
216 /* if msec < 0: wait for ever */
217 int ser_wait(int fd, long msec)
218 {
219         int res;
220         while(!(res = ser_pending(fd))) {
221                 delay(25);
222                 if(msec >= 0) {
223                         if((msec -= 25) < 0) msec = 0;
224                 }
225         }
226         return res;
227 }
228
229 static int can_send(int fd)
230 {
231         int base = ports[fd].base;
232         return inp(base + UART_LSTAT) & LST_TREG_EMPTY;
233 }
234
235 static void ser_putc(int fd, char c)
236 {
237         int base = ports[fd].base;
238         while(!can_send(fd));
239         while((inp(base + UART_MSTAT) & MST_CTS) == 0);
240         outp(base + UART_DATA, c);
241 }
242
243 static void ser_puts(int fd, const char *s)
244 {
245         int base = ports[fd].base;
246         while(*s) {
247                 ser_putc(fd, *s++);
248         }
249 }
250
251 static int ser_getc(int fd)
252 {
253         struct serial_port *p = ports + fd;
254         int have, c = -1;
255
256         if(p->blocking) {
257                 while(!(have = ser_pending(fd)));
258         } else {
259                 have = ser_pending(fd);
260         }
261
262         if(have) {
263                 c = p->inbuf[p->inbuf_ridx];
264                 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
265         }
266         return c;
267 }
268
269 int ser_write(int fd, const char *buf, int count)
270 {
271         int n = count;
272         while(n--) {
273                 ser_putc(fd, *buf++);
274         }
275         return count;
276 }
277
278 int ser_read(int fd, char *buf, int count)
279 {
280         int c, n = 0;
281         while(n < count && (c = ser_getc(fd)) != -1) {
282                 *buf++ = c;
283                 ++n;
284         }
285         return n;
286 }
287
288 void ser_printf(int fd, const char *fmt, ...)
289 {
290         static char buf[128];
291         va_list ap;
292
293         va_start(ap, fmt);
294         vsprintf(buf, fmt, ap);
295         va_end(ap);
296
297         ser_puts(fd, buf);
298 }
299
300 char *ser_getline(int fd, char *buf, int bsz)
301 {
302         static char linebuf[512];
303         static int widx;
304         int i, rd, size, offs;
305
306         size = sizeof linebuf - widx;
307         while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) {
308                 widx += rd;
309                 size -= rd;
310         }
311
312         linebuf[widx] = 0;
313
314         for(i=0; i<widx; i++) {
315                 if(linebuf[i] == '\r' || linebuf[i] == '\n') {
316                         size = i >= bsz ? bsz - 1 : i;
317                         memcpy(buf, linebuf, size);
318                         buf[size] = 0;
319
320                         offs = i + 1;
321                         memmove(linebuf, linebuf + offs, widx - offs);
322                         widx -= offs;
323                         return buf;
324                 }
325         }
326         return 0;
327 }
328
329 static int have_recv(int base)
330 {
331         unsigned short stat = inp(base + UART_LSTAT);
332         if(stat & LST_ERROR) {
333                 fprintf(stderr, "receive error\n");
334                 abort();
335         }
336         return stat & LST_DRDY;
337 }
338
339 static void __interrupt __far recv_intr()
340 {
341         int i, idreg, c;
342
343         for(i=0; i<2; i++) {
344                 int base = uart_base[i];
345                 struct serial_port *p = ports + i;
346
347                 while(((idreg = inp(base + UART_IID)) & IID_PENDING) == 0) {
348                         while(have_recv(base)) {
349                                 c = inp(base + UART_DATA);
350
351                                 p->inbuf[p->inbuf_widx] = inp(base + UART_DATA);
352                                 p->inbuf_widx = BNEXT(p->inbuf_widx);
353
354                                 if(p->inbuf_widx == p->inbuf_ridx) {
355                                         /* we overflowed, drop the oldest */
356                                         p->inbuf_ridx = BNEXT(p->inbuf_ridx);
357                                 }
358                         }
359                 }
360         }
361
362         outp(PIC1_CMD_PORT, OCW2_EOI);
363 }