added serial spaceball support in the dos version. can be used for
[dosdemo] / src / dos / sball.c
1 #include <stdio.h>
2 #include <dos.h>
3 #include <conio.h>
4 #include <i86.h>
5 #include "sball.h"
6
7 struct motion {
8         int x, y, z;
9         int rx, ry, rz;
10 };
11
12 #define UART1_BASE      0x3f8
13 #define UART2_BASE      0x2f8
14 #define UART1_IRQ       4
15 #define UART2_IRQ       3
16
17 #define UART_DATA       0
18 #define UART_INTR       1
19 #define UART_DIVLO      0
20 #define UART_DIVHI      1
21 #define UART_FIFO       2
22 #define UART_IID        2
23 #define UART_LCTL       3
24 #define UART_MCTL       4
25 #define UART_LSTAT      5
26 #define UART_MSTAT      6
27
28 /* interrupt enable register bits */
29 #define INTR_RECV       1
30 #define INTR_SEND       2
31 #define INTR_LSTAT      4
32 #define INTR_DELTA      8
33
34 /* fifo control register bits */
35 #define FIFO_ENABLE             0x01
36 #define FIFO_RECV_CLEAR 0x02
37 #define FIFO_SEND_CLEAR 0x04
38 #define FIFO_DMA                0x08
39 #define FIFO_TRIG_4             0x40
40 #define FIFO_TRIG_8             0x80
41 #define FIFO_TRIG_14    0xc0
42
43 /* interrupt id register bits */
44 #define IID_PENDING             0x01
45 #define IID_ID0                 0x02
46 #define IID_ID1                 0x04
47 #define IID_ID2                 0x08
48 #define IID_FIFO_EN             0xc0
49
50 #define IID_SOURCE              0xe
51
52 #define IID_DELTA               0
53 #define IID_SEND                0x2
54 #define IID_RECV                0x4
55 #define IID_FIFO                0xc
56 #define IID_STATUS              0x6
57
58 /* line control register bits */
59 #define LCTL_BITS_8     0x03
60 #define LCTL_STOP_2     0x04
61 #define LCTL_DLAB       0x80
62 #define LCTL_8N1        LCTL_BITS_8
63 #define LCTL_8N2        (LCTL_BITS_8 | LCTL_STOP_2)
64
65 /* modem control register bits */
66 #define MCTL_DTR        0x01
67 #define MCTL_RTS        0x02
68 #define MCTL_OUT1       0x04
69 #define MCTL_OUT2       0x08
70 #define MCTL_LOOP       0x10
71
72 /* line status register bits */
73 #define LST_DRDY                0x01
74 #define LST_ERR_OVER    0x02
75 #define LST_ERR_PARITY  0x04
76 #define LST_ERR_FRAME   0x08
77 #define LST_ERR_BRK             0x10
78 #define LST_TREG_EMPTY  0x20
79 #define LST_TIDLE               0x40
80 #define LST_ERROR               0x80
81
82 /* modem status register bits */
83 #define MST_DELTA_CTS   0x01
84 #define MST_DELTA_DSR   0x02
85 #define MST_TERI                0x04
86 #define MST_DELTA_DCD   0x08
87 #define MST_CTS                 0x10
88 #define MST_DSR                 0x20
89 #define MST_RING                0x40
90 #define MST_DCD                 0x80
91
92 /* interrupt controller stuff */
93 #define PIC1_CMD_PORT   0x20
94 #define PIC1_DATA_PORT  0x21
95 #define PIC2_CMD_PORT   0xa0
96 #define PIC2_DATA_PORT  0xa1
97 #define OCW2_EOI                0x20
98
99 static int init_smouse(void);
100 static void read_motion(int *m, const char *s);
101 static void read_keystate(unsigned int *stptr, const char *s);
102 static void procpkt(struct packet *p);
103 static void enqueue_event(sball_event *ev);
104
105 #define COM_FMT_8N1             LCTL_8N1
106 #define COM_FMT_8N2             LCTL_8N2
107 static void com_setup(int port, int baud, unsigned int fmt);
108
109 static void com_putc(char c);
110 static void com_puts(const char *s);
111 static int com_getc(void);
112 static char *com_gets(char *buf, int sz);
113
114 static int com_have_recv(void);
115 static int com_can_send(void);
116
117 static void __interrupt __far recv_intr(void);
118
119 static int uart_base, uart_intr_num;
120 static void (__interrupt __far *prev_recv_intr)(void);
121
122 static struct packet {
123         int id;
124         char data[80];
125 } pktbuf[16];
126 static int pktbuf_ridx, pktbuf_widx;
127 #define BNEXT(x)        (((x) + 1) & 0xf)
128 #define BEMPTY(b)       (b##_ridx == b##_widx)
129
130 static sball_event evbuf[16];
131 static int evbuf_ridx, evbuf_widx;
132
133
134 int sball_init(void)
135 {
136         com_setup(0, 9600, COM_FMT_8N2);
137         init_smouse();
138         return 0;
139 }
140
141 void sball_shutdown(void)
142 {
143         com_close();
144 }
145
146 int sball_getdev(void)
147 {
148         return 0;
149 }
150
151 int sball_pending(void)
152 {
153         _disable();
154         while(!BEMPTY(pktbuf)) {
155                 procpkt(pktbuf + pktbuf_ridx);
156                 pktbuf_ridx = BNEXT(pktbuf_ridx);
157         }
158         _enable();
159         return !BEMPTY(evbuf);
160 }
161
162 int sball_getevent(sball_event *ev)
163 {
164         _disable();
165         while(!BEMPTY(pktbuf)) {
166                 procpkt(pktbuf + pktbuf_ridx);
167                 pktbuf_ridx = BNEXT(pktbuf_ridx);
168         }
169         _enable();
170
171         if(BEMPTY(evbuf)) {
172                 return 0;
173         }
174         *ev = evbuf[evbuf_ridx];
175         evbuf_ridx = BNEXT(evbuf_ridx);
176         return 1;
177 }
178
179 static int init_smouse(void)
180 {
181         /* try repeatedly zeroing the device until we get a response */
182         do {
183                 delay(500);
184                 com_puts("z\r");
185         } while(BEMPTY(pktbuf));
186
187         /* then ask for id string and request motion updates */
188         com_puts("vQ\r");
189         com_puts("m3\r");
190         return 0;
191 }
192
193 static void procpkt(struct packet *p)
194 {
195         static unsigned int bnstate;
196         int i;
197         unsigned int st, delta, prev;
198         sball_event *ev;
199
200         switch(p->id) {
201         case 'd':
202                 ev = evbuf + evbuf_widx;
203                 read_motion(ev->motion.motion, p->data);
204                 ev->type = SBALL_EV_MOTION;
205                 enqueue_event(ev);
206                 break;
207
208         case 'k':
209                 read_keystate(&st, p->data);
210
211                 delta = st ^ bnstate;
212                 prev = bnstate;
213                 bnstate = st;
214
215                 for(i=0; i<32; i++) {
216                         if(delta & 1) {
217                                 ev = evbuf + evbuf_widx;
218                                 ev->type = SBALL_EV_BUTTON;
219                                 ev->button.id = i;
220                                 ev->button.pressed = st & 1;
221                                 ev->button.state = prev ^ (1 << i);
222                                 enqueue_event(ev);
223                         }
224                         st >>= 1;
225                         delta >>= 1;
226                 }
227                 break;
228
229         case 'v':
230                 printf("Device: %s\n", p->data);
231                 break;
232         /*
233         default:
234                 printf("DBG %c -> %s\n", (char)p->id, p->data);
235         */
236         }
237 }
238
239 static void enqueue_event(sball_event *ev)
240 {
241         if(ev != evbuf + evbuf_widx) {
242                 evbuf[evbuf_widx] = *ev;
243         }
244
245         evbuf_widx = BNEXT(evbuf_widx);
246         if(evbuf_widx == evbuf_ridx) {
247                 fprintf(stderr, "enqueue_event: overflow, dropping oldest\n");
248                 evbuf_ridx = BNEXT(evbuf_ridx);
249         }
250 }
251
252 static void com_setup(int port, int baud, unsigned int fmt)
253 {
254         unsigned char ctl;
255         unsigned short div = 115200 / baud;
256         static int base[] = {UART1_BASE, UART2_BASE};
257         static int irq[] = {UART1_IRQ, UART2_IRQ};
258
259         uart_base = base[port];
260         uart_intr_num = irq[port] | 8;
261
262         _disable();
263         prev_recv_intr = _dos_getvect(uart_intr_num);
264         _dos_setvect(uart_intr_num, recv_intr);
265         /* unmask the appropriate interrupt */
266         outp(PIC1_DATA_PORT, inp(PIC1_DATA_PORT) & ~(1 << irq[port]));
267
268         outp(uart_base + UART_LCTL, LCTL_DLAB);
269         outp(uart_base + UART_DIVLO, div & 0xff);
270         outp(uart_base + UART_DIVHI, (div >> 8) & 0xff);
271         outp(uart_base + UART_LCTL, fmt);       /* fmt should be LCTL_8N1, LCTL_8N2 etc */
272         outp(uart_base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR);
273         outp(uart_base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2);
274         outp(uart_base + UART_INTR, INTR_RECV);
275
276         _enable();
277 }
278
279 static void com_close(void)
280 {
281         _disable();
282         outp(uart_base + UART_INTR, 0);
283         outp(uart_base + UART_MCTL, 0);
284         _dos_setvect(uart_intr_num, prev_recv_intr);
285         _enable();
286 }
287
288 static void com_putc(char c)
289 {
290         while(!com_can_send());
291         while((inp(uart_base + UART_MSTAT) & MST_CTS) == 0);
292         outp(uart_base + UART_DATA, c);
293 }
294
295 static void com_puts(const char *s)
296 {
297         while(*s) {
298                 com_putc(*s++);
299         }
300 }
301
302 static int com_getc(void)
303 {
304         int have;
305         while(!(have = com_have_recv()));
306         return inp(uart_base + UART_DATA);
307 }
308
309 static char *com_gets(char *buf, int sz)
310 {
311         int c;
312         char *ptr = buf;
313
314         while(sz-- > 1 && (c = com_getc()) != -1) {
315                 if(c == '\r') {
316                         *ptr++ = '\n';
317                         break;
318                 }
319                 *ptr++ = c;
320         }
321         if(c == -1) {
322                 return 0;
323         }
324         *ptr = 0;
325         return buf;
326 }
327
328 static int com_have_recv(void)
329 {
330         unsigned short stat = inp(uart_base + UART_LSTAT);
331         if(stat & LST_ERROR) {
332                 fprintf(stderr, "receive error\n");
333                 abort();
334         }
335         return stat & LST_DRDY;
336 }
337
338 static int com_can_send(void)
339 {
340         return inp(uart_base + UART_LSTAT) & LST_TREG_EMPTY;
341 }
342
343 static void __interrupt __far recv_intr()
344 {
345         static char buf[128];
346         static char *bptr = buf;
347         struct packet *pkt;
348         int idreg, c, datasz;
349
350         while(((idreg = inp(uart_base + UART_IID)) & IID_PENDING) == 0) {
351                 while(com_have_recv()) {
352                         if((c = inp(uart_base + UART_DATA)) == '\r') {
353                                 *bptr = 0;
354                                 datasz = bptr - buf;
355                                 bptr = buf;
356
357                                 pkt = pktbuf + pktbuf_widx;
358                                 pktbuf_widx = BNEXT(pktbuf_widx);
359
360                                 if(pktbuf_widx == pktbuf_ridx) {
361                                         /* we overflowed, drop the oldest packet */
362                                         pktbuf_ridx = BNEXT(pktbuf_ridx);
363                                 }
364
365                                 if(datasz > sizeof pkt->data) {
366                                         datasz = sizeof pkt->data;      /* truncate */
367                                 }
368                                 pkt->id = buf[0];
369                                 memcpy(pkt->data, buf + 1, datasz);
370
371                         } else if(bptr - buf < sizeof buf - 1) {
372                                 *bptr++ = c;
373                         }
374                 }
375         }
376
377         outp(PIC1_CMD_PORT, OCW2_EOI);
378 }
379
380 static void read_motion(int *m, const char *s)
381 {
382         int i;
383
384         for(i=0; i<6; i++) {
385                 long val = ((((long)s[0] & 0xf) << 12) |
386                         (((long)s[1] & 0xf) << 8) |
387                         (((long)s[2] & 0xf) << 4) |
388                         ((long)s[3] & 0xf)) - 32768;
389                 s += 4;
390                 *m++ = (int)val;
391         }
392 }
393
394 static void read_keystate(unsigned int *stptr, const char *s)
395 {
396         int i, bit = 0;
397         unsigned int st = 0;
398
399         for(i=0; i<3; i++) {
400                 st |= ((unsigned int)*s++ & 0xf) << bit;
401                 bit += 4;
402         }
403         *stptr = st;
404 }