|
|
1.1 root 1: /* (-lgl
2: * COHERENT Device Driver Kit version 1.2.0
3: * Copyright (c) 1982, 1991 by Mark Williams Company.
4: * All rights reserved. May not be copied without permission.
5: *
6: * $Log: al.c,v $
7: * Revision 1.12 92/02/14 10:12:27 bin
8: * update by hal (post 321)
9: *
10: * Revision 1.11 92/01/13 08:37:52 hal
11: * alclose() - decrement open count in alx.c
12: *
13: * Revision 1.10 91/12/20 14:09:50 hal
14: * Don't use loopback during chip sense.
15: *
16: * Revision 1.9 91/12/10 08:01:11 hal
17: * Set ALCNT automatically.
18: * Set interrupt vector before calling uart_sense().
19: *
20: * Revision 1.8 91/12/05 09:35:25 hal
21: * Working 16550A code. Nfg on GeeSee.
22: *
23: * Revision 1.7 91/12/02 19:22:00 hal
24: * Last version before FIFO testing.
25: *
26: -lgl) */
27: /*
28: * Driver for an IBM PC asyncronous
29: * line, using interrupts. The interface
30: * uses a Natty/WD 8250 chip.
31: */
32:
33: #include <sys/coherent.h>
34: #ifndef _I386
35: #include <sys/i8086.h>
36: #endif
37: #include <sys/con.h>
38: #include <errno.h>
39: #include <sys/stat.h>
40: #include <sys/tty.h>
41: #include <sys/uproc.h>
42: #include <sys/clist.h>
43: #include <sys/ins8250.h>
44: #include <sys/sched.h>
45: #include <sys/al.h>
46: #include <sys/devices.h>
47:
48: #define minor_st(dev) (dev & 0x0f) /* up to 16 ports per driver */
49: #define DEV_TTY (alttab[minor_st(dev)])
50: #define ALPORT (((COM_DDP *)(DEV_TTY.t_ddp))->port)
51: #define TESTBAUD 0x03A5
52:
53: /*
54: * This driver can be compiled to drive any possible
55: * async port by appropriate definitions of:
56: * ALPORT[ab] the io port address(es)
57: * ALNUM[ab] com index number (0..3 for com[1..4])
58: * ALINT the interrupt level
59: * ALNAME the xxcon name
60: * ALMAJ the major device number
61: * ALCNT number of ports sharing the interrupt
62: *
63: * NOTE: if ALCNT is changed, alttab and alintr will need hacking
64: * Common code for the different ports is handled by alx.c
65: */
66:
67: #ifdef ALCOM1 /* COM1_3 definitions */
68: #define ALPORTa 0x3F8 /* Base of com1 port */
69: #define ALPORTb 0x3E8 /* Base of com3 port */
70: #define ALNUMa 0 /* com1 has com number of 0 */
71: #define ALNUMb 2 /* com3 has com number of 2 */
72: #define ALINT 4 /* Interrupt level of com1_3 ports */
73: #define ALNAME a0con /* CON name of com1_3 ports */
74: #define ALMAJ AL0_MAJOR /* Major number of com1_3 port */
75: #define ALCNT A0CNT /* Number of ports for this IRQ */
76: #define ALSPEEDa C1BAUD /* Name of patchable variable for com1 speed */
77: #define ALSPEEDb C3BAUD /* Name of patchable variable for com3 speed */
78: #endif
79:
80: #ifdef ALCOM2 /* COM2_4 definitions */
81: #define ALPORTa 0x2F8 /* Base of com2 port */
82: #define ALPORTb 0x2E8 /* Base of com4 port */
83: #define ALNUMa 1 /* com2 has com number of 1 */
84: #define ALNUMb 3 /* com4 has com number of 3 */
85: #define ALINT 3 /* Interrupt level of com2_4 ports */
86: #define ALNAME a1con /* CON name of com2_4 ports */
87: #define ALMAJ AL1_MAJOR /* Major number of com2_4 ports */
88: #define ALCNT A1CNT /* Number of ports for this IRQ */
89: #define ALSPEEDa C2BAUD /* Name of patchable variable for com2 speed */
90: #define ALSPEEDb C4BAUD /* Name of patchable variable for com4 speed */
91: #endif
92:
93: /*
94: * Functions.
95: */
96: int alxopen();
97: int alxclose();
98: int alxioctl();
99: int alxtimer();
100: int alxparam();
101: int alxcycle();
102: int alxstart();
103: int alxbreak();
104:
105: int alintr();
106: int alopen();
107: int alclose();
108: int alread();
109: int alwrite();
110: int alioctl();
111: int alload();
112: int alunload();
113: int alpoll();
114: int nulldev();
115: int nonedev();
116: static int uart_sense();
117:
118: /*
119: * Configuration table.
120: */
121: CON ALNAME ={
122: DFCHR|DFPOL, /* Flags */
123: ALMAJ, /* Major index */
124: alopen, /* Open */
125: alclose, /* Close */
126: nulldev, /* Block */
127: alread, /* Read */
128: alwrite, /* Write */
129: alioctl, /* Ioctl */
130: nulldev, /* Powerfail */
131: alxtimer, /* Timeout */
132: alload, /* Load */
133: alunload, /* Unload */
134: alpoll /* Poll */
135: };
136:
137: /*
138: * Terminal structures.
139: */
140: static COM_DDP * ddp;
141: static TTY * alttab;
142: static TTY * irqtty; /* point to alttab entry which is IRQ-enabled */
143:
144: /*
145: * to change default speeds - patch kernel variables C1BAUD..C4BAUD
146: * new value should be one of B0..B9600 in /usr/include/sgtty.h
147: */
148: int ALSPEEDa = B9600;
149: int ALSPEEDb = B9600;
150:
151: /*
152: * to enable com[34], patch here
153: * A0CNT should be 2 if you want com3, 1 otherwise
154: * A1CNT should be 2 if you want com4, 1 otherwise
155: */
156: int ALCNT = 2;
157:
158: static
159: alload()
160: {
161: register int s;
162: static int init;
163: extern int albaud[];
164: int port, i;
165: int usa, usb;
166:
167: /*
168: * Set interrupt vector early in case uart_sense() causes bogus irpts.
169: */
170: setivec(ALINT, alintr); /* set interrupt vector */
171: usa = uart_sense(ALPORTa);
172: usb = uart_sense(ALPORTb);
173: if (usa == US_NONE && usb == US_NONE) {
174: ALCNT = 0;
175: } else {
176: if (usb == US_NONE)
177: ALCNT = 1;
178: else
179: ALCNT = 2;
180: }
181: if (init == 0 && ALCNT
182: && (alttab = (TTY *)kalloc(ALCNT * sizeof(TTY)))
183: && (ddp = (COM_DDP *)kalloc(ALCNT * sizeof(COM_DDP)))) {
184: kclear(alttab, ALCNT*sizeof(TTY));
185: kclear(ddp, ALCNT*sizeof(COM_DDP));
186: ++init;
187:
188: s = sphi();
189: alttab[0].t_dispeed = alttab[0].t_dospeed = ALSPEEDa;
190: alttab[0].t_ddp = (char *)&ddp[0];
191: tp_table[ALNUMa] = alttab; /* set TTY pointers for polling */
192: ddp[0].port = ALPORTa;
193: ddp[0].com_num = ALNUMa;
194: com_usage[ALNUMa].uart_type = usa;
195:
196: if (ALCNT > 1) {
197: alttab[1].t_dispeed = alttab[1].t_dospeed = ALSPEEDb;
198: alttab[1].t_ddp = (char *)&ddp[1];
199: tp_table[ALNUMb] = alttab+1;
200: ddp[1].port = ALPORTb;
201: ddp[1].com_num = ALNUMb;
202: com_usage[ALNUMb].uart_type = usb;
203: }
204:
205: for (i = 0; i < ALCNT; i++) {
206: int speed = alttab[i].t_dospeed;
207:
208: /* port = base I/O address */
209: port = ((COM_DDP *)(alttab[i].t_ddp))->port;
210: outb(port+IER, 0); /* disable port interrupts */
211: outb(port+MCR, 0); /* hangup port */
212: outb(port+LCR, LC_DLAB);
213: outb(port+DLL, albaud[speed]);
214: outb(port+DLH, albaud[speed] >> 8);
215: outb(port+LCR, LC_CS8);
216: alttab[i].t_start = alxstart;
217: alttab[i].t_param = alxparam;
218: alttab[i].t_cs_sel= cs_sel();
219: }
220:
221: spl(s);
222: } else { /* Load failed - no ports or no RAM available! */
223: clrivec(ALINT);
224: }
225: return;
226: }
227:
228: static
229: alunload()
230: {
231: int port, i;
232:
233: for (i = 0; i < ALCNT; i++) {
234: port = ((COM_DDP *)(alttab[i].t_ddp))->port;
235: outb(port+IER, 0); /* disable port interrupts */
236: outb(port+MCR, 0); /* hangup port */
237: timeout(alttab[i].t_rawtim, 0, NULL, 0);/* cancel timer */
238: }
239: if (ALCNT) {
240: clrivec(ALINT); /* release interrupt vector */
241: kfree(alttab);
242: kfree(ddp);
243: }
244: }
245:
246: static
247: alopen(dev, mode)
248: dev_t dev;
249: int mode;
250: {
251: if (minor_st(dev) < ALCNT) {
252: alxopen(dev, mode, &DEV_TTY, &irqtty);
253: } else
254: u.u_error = ENXIO;
255: }
256:
257: static
258: alclose(dev, mode)
259: dev_t dev;
260: int mode;
261: {
262: /*
263: * The real work is in alx.c.
264: */
265: alxclose(dev, mode, &DEV_TTY);
266: }
267:
268: static
269: alread(dev, iop)
270: dev_t dev;
271: IO *iop;
272: {
273: ttread(&DEV_TTY, iop, 0);
274: }
275:
276: static
277: alwrite(dev, iop)
278: dev_t dev;
279: register IO *iop;
280: {
281: register int c;
282:
283: /*
284: * Treat user writes through tty driver.
285: */
286: if (iop->io_seg != IOSYS) {
287: ttwrite(&DEV_TTY, iop, 0);
288: return;
289: }
290:
291: /*
292: * Treat kernel writes by blocking on transmit buffer.
293: */
294: while ((c = iogetc(iop)) >= 0) {
295: /*
296: * Wait until transmit buffer is empty.
297: * Check twice to prevent critical race with interrupt handler.
298: */
299: for (;;) {
300: if (inb(ALPORT+LSR) & LS_TxRDY)
301: if (inb(ALPORT+LSR) & LS_TxRDY)
302: break;
303: }
304:
305: /*
306: * Output the next character.
307: */
308: outb(ALPORT+DREG, c);
309: }
310: }
311:
312: static
313: alioctl(dev, com, vec)
314: dev_t dev;
315: struct sgttyb *vec;
316: {
317: alxioctl(dev, com, vec, &DEV_TTY);
318: }
319:
320: static
321: alpoll(dev, ev, msec)
322: dev_t dev;
323: int ev;
324: int msec;
325: {
326: return ttpoll(&DEV_TTY, ev, msec);
327: }
328:
329: static
330: alintr()
331: {
332: alxintr(irqtty);
333: }
334:
335: /*
336: * uart_sense()
337: *
338: * Given port address, return what type of 8250-family chip is found there.
339: *
340: * 0 - no chip
341: * 1 - 8250 or 8250B
342: * 2 - 8250A or 16450
343: * 3 - 16550
344: * 4 - 16550A
345: *
346: * Only the last of these has usable on-chip FIFO.
347: */
348: static int uart_sense(port)
349: int port;
350: {
351: int ret;
352: unsigned ch;
353: short testbaud;
354:
355: /*
356: * See if UART is detected at port address.
357: * UART should have IER = 0000 xxxx
358: * MCR = 000x xxxx
359: * IIR = xx00 xxxx
360: * and should be write and read back the baud rate regs.
361: */
362: if (inb(port+IER) & 0xF0
363: || inb(port+MCR) & 0xE0
364: || inb(port+IIR) & 0x30) {
365: ret = US_NONE;
366: goto done;
367: }
368: outb(port+LCR, LC_DLAB);
369: outb(port+DLL, TESTBAUD & 0xFF);
370: outb(port+DLH, TESTBAUD >> 8);
371: testbaud = inb(port+DLL) | inb(port+DLH) << 8;
372: outb(port+LCR, LC_CS8);
373: if (testbaud != TESTBAUD){
374: ret = US_NONE;
375: goto done;
376: }
377:
378: /*
379: * Scratch register NOT found on 8250/8250B.
380: */
381: outb(port+SCR, 0x55);
382: ch = inb(port+SCR);
383: if (ch != 0x55) {
384: ret = US_8250;
385: goto done;
386: }
387:
388: /*
389: * After trying to turn on FIFO mode,
390: * If IIR is 00xx xxxx, it's 8250A/16450 (no FIFO).
391: * If IIR is 10xx xxxx, it's 16550 (broken FIFO).
392: * If IIR is 11xx xxxx, it's 16550A (usable FIFO).
393: */
394: outb(port+FCR, 0x01);
395: ch = inb(port+FCR);
396: switch (ch & 0xC0) {
397: case 0x00:
398: ret = US_16450;
399: break;
400: case 0x80:
401: ret = US_16550;
402: break;
403: case 0xC0:
404: ret = US_16550A;
405: break;
406: }
407: outb(port+FCR, 0x00);
408: done:
409: if (ret == US_NONE)
410: goto really_done;
411: switch(port){
412: case 0x3F8:
413: printf("com1 ");
414: break;
415: case 0x2F8:
416: printf("com2 ");
417: break;
418: case 0x3E8:
419: printf("com3 ");
420: break;
421: case 0x2E8:
422: printf("com4 ");
423: break;
424: }
425: printf("port %x: ", port);
426: switch (ret) {
427: case US_NONE:
428: printf("no UART\n");
429: break;
430: case US_8250:
431: printf("8250/8250B\n");
432: break;
433: case US_16450:
434: printf("8250A/16450\n");
435: break;
436: case US_16550:
437: printf("16550 - no FIFO\n");
438: break;
439: case US_16550A:
440: printf("16550A - FIFO\n");
441: break;
442: }
443: really_done:
444: return ret;
445: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.