/* $Header: /ker/io.386/RCS/al.c,v 2.6 93/10/29 00:58:23 nigel Exp Locker: nigel $ */ /* (-lgl * COHERENT Device Driver Kit version 1.2.0 * Copyright (c) 1982, 1991 by Mark Williams Company. * All rights reserved. May not be copied without permission. -lgl) */ /* * Driver for an IBM PC asyncronous * line, using interrupts. The interface * uses a Natty/WD 8250 chip. * * $Log: al.c,v $ * Revision 2.6 93/10/29 00:58:23 nigel * R98 (aka 4.2 Beta) prior to removing System Global memory * * Revision 2.5 93/09/13 08:05:53 nigel * Changed to reflect the fact that entry points are 'void' once more * * Revision 2.4 93/08/19 10:38:23 nigel * r83 ioctl (), corefile, new headers * * Revision 2.3 93/08/19 04:02:03 nigel * Nigel's R83 * * Revision 2.2 93/07/26 15:27:50 nigel * Nigel's R80 * * Revision 1.8 93/04/14 10:09:40 root * r75 * * Revision 1.7 92/07/27 18:16:05 hal * Kernel #59 * * Revision 1.6 92/04/30 08:59:22 hal * Add asy. Remove silos from tty struct. * * Revision 1.5 92/04/13 10:13:01 hal * Add AL_ADDR table. * Change chip sensing for weird 16550 chips lacking SCR register. * * Revision 1.4 92/02/20 17:50:52 hal * Do 286->S5 sgtty conversion. * * Revision 1.11 92/01/13 08:37:52 hal * alclose() - decrement open count in alx.c * * Revision 1.10 91/12/20 14:09:50 hal * Don't use loopback during chip sense. * * Revision 1.9 91/12/10 08:01:11 hal * Set ALCNT automatically. * Set interrupt vector before calling uart_sense(). * * Revision 1.8 91/12/05 09:35:25 hal * Working 16550A code. Nfg on GeeSee. * * Revision 1.7 91/12/02 19:22:00 hal * Last version before FIFO testing. */ #include <sys/coherent.h> #include <sys/con.h> #include <sys/errno.h> #include <sys/stat.h> #include <sys/tty.h> #include <sys/clist.h> #include <sys/ins8250.h> #include <sys/sched.h> #include <sys/al.h> #include <sys/devices.h> #define minor_st(dev) (dev & 0x0f) /* up to 16 ports per driver */ #define DEV_TTY (alttab[minor_st(dev)]) #define ALPORT (((COM_DDP *)(DEV_TTY.t_ddp))->port) /* * This driver can be compiled to drive any possible * async port by appropriate definitions of: * ALPORT[ab] the io port address(es) * ALNUM[ab] com index number (0..3 for com[1..4]) * ALINT the interrupt level * ALNAME the xxcon name * ALMAJ the major device number * ALCNT number of ports sharing the interrupt * * NOTE: if ALCNT is changed, alttab and alintr will need hacking * Common code for the different ports is handled by alx.c */ #ifdef ALCOM1 /* COM1_3 definitions */ #define ALPORTa 0x3F8 /* Base of com1 port */ #define ALPORTb 0x3E8 /* Base of com3 port */ #define ALNUMa 0 /* com1 has com number of 0 */ #define ALNUMb 2 /* com3 has com number of 2 */ #define ALINT 4 /* Interrupt level of com1_3 ports */ #define ALNAME a0con /* CON name of com1_3 ports */ #define ALMAJ AL0_MAJOR /* Major number of com1_3 port */ #define ALCNT A0CNT /* Number of ports for this IRQ */ #define ALSPEEDa C1BAUD /* Name of patchable variable for com1 speed */ #define ALSPEEDb C3BAUD /* Name of patchable variable for com3 speed */ #endif #ifdef ALCOM2 /* COM2_4 definitions */ #define ALPORTa 0x2F8 /* Base of com2 port */ #define ALPORTb 0x2E8 /* Base of com4 port */ #define ALNUMa 1 /* com2 has com number of 1 */ #define ALNUMb 3 /* com4 has com number of 3 */ #define ALINT 3 /* Interrupt level of com2_4 ports */ #define ALNAME a1con /* CON name of com2_4 ports */ #define ALMAJ AL1_MAJOR /* Major number of com2_4 ports */ #define ALCNT A1CNT /* Number of ports for this IRQ */ #define ALSPEEDa C2BAUD /* Name of patchable variable for com2 speed */ #define ALSPEEDb C4BAUD /* Name of patchable variable for com4 speed */ #endif /* * Functions. */ void alxopen(); void alxclose(); void alxioctl(); void alxtimer(); void alxparam(); void alxcycle(); void alxstart(); void alxbreak(); /* * Terminal structures. */ static COM_DDP * ddp; static TTY * alttab; static TTY * irqtty; /* point to alttab entry which is IRQ-enabled */ /* * to change default speeds - patch kernel variables C1BAUD..C4BAUD * new value should be one of B0..B9600 in /usr/include/sgtty.h */ int ALSPEEDa = B9600; int ALSPEEDb = B9600; /* * to enable com[34], patch here * A0CNT should be 2 if you want com3, 1 otherwise * A1CNT should be 2 if you want com4, 1 otherwise */ int ALCNT = 2; static void alintr() { alxintr(irqtty); } static void alload() { register int s; static int init; extern int albaud[]; int port, i; int usa, usb; extern int AL_ADDR[]; /* * Set interrupt vector early in case uart_sense() causes bogus irpts. */ setivec(ALINT, alintr); /* set interrupt vector */ usa = uart_sense(AL_ADDR[ALNUMa]); usb = uart_sense(AL_ADDR[ALNUMb]); putchar('\n'); if (usa == US_NONE && usb == US_NONE) { ALCNT = 0; } else { if (usb == US_NONE) ALCNT = 1; else ALCNT = 2; } if (init == 0 && ALCNT && (alttab = (TTY *)kalloc(ALCNT * sizeof(TTY))) != NULL && (ddp = (COM_DDP *)kalloc(ALCNT * sizeof(COM_DDP))) != NULL) { kclear(alttab, ALCNT*sizeof(TTY)); kclear(ddp, ALCNT*sizeof(COM_DDP)); ++init; s = sphi(); alttab[0].t_dispeed = alttab[0].t_dospeed = ALSPEEDa; alttab[0].t_ddp = (char *)&ddp[0]; tp_table[ALNUMa] = alttab; /* set TTY pointers for polling */ ddp[0].port = AL_ADDR[ALNUMa]; ddp[0].com_num = ALNUMa; com_usage[ALNUMa].uart_type = usa; if (ALCNT > 1) { alttab[1].t_dispeed = alttab[1].t_dospeed = ALSPEEDb; alttab[1].t_ddp = (char *)&ddp[1]; tp_table[ALNUMb] = alttab+1; ddp[1].port = AL_ADDR[ALNUMb]; ddp[1].com_num = ALNUMb; com_usage[ALNUMb].uart_type = usb; } for (i = 0; i < ALCNT; i++) { int speed = alttab[i].t_dospeed; /* port = base I/O address */ port = ((COM_DDP *)(alttab[i].t_ddp))->port; outb(port+IER, 0); /* disable port interrupts */ outb(port+MCR, 0); /* hangup port */ outb(port+LCR, LC_DLAB); outb(port+DLL, albaud[speed]); outb(port+DLH, albaud[speed] >> 8); outb(port+LCR, LC_CS8); alttab[i].t_start = alxstart; alttab[i].t_param = alxparam; } spl(s); } else { /* Load failed - no ports or no RAM available! */ clrivec(ALINT); } return; } static void alunload() { int port, i; for (i = 0; i < ALCNT; i++) { port = ((COM_DDP *) alttab[i].t_ddp)->port; outb(port+IER, 0); /* disable port interrupts */ outb(port+MCR, 0); /* hangup port */ timeout(alttab[i].t_rawtim, 0, NULL, 0);/* cancel timer */ } if (ALCNT) { clrivec(ALINT); /* release interrupt vector */ kfree(alttab); kfree(ddp); } } static void alopen(dev, mode) dev_t dev; int mode; { if (minor_st (dev) < ALCNT) alxopen (dev, mode, & DEV_TTY, & irqtty); else set_user_error (ENXIO); } static void alclose(dev, mode) dev_t dev; int mode; { /* * The real work is in alx.c. */ alxclose(dev, mode, &DEV_TTY); } static void alread(dev, iop) dev_t dev; IO *iop; { ttread (& DEV_TTY, iop, 0); } static void alwrite(dev, iop) dev_t dev; register IO *iop; { register int c; /* * Treat user writes through tty driver. */ if (iop->io_seg != IOSYS) { ttwrite(&DEV_TTY, iop, 0); return; } /* * Treat kernel writes by blocking on transmit buffer. */ while ((c = iogetc(iop)) >= 0) { /* * Wait until transmit buffer is empty. * Check twice to prevent critical race with interrupt handler. */ for (;;) { if (inb(ALPORT+LSR) & LS_TxRDY) if (inb(ALPORT+LSR) & LS_TxRDY) break; } /* * Output the next character. */ outb(ALPORT+DREG, c); } } static void alioctl(dev, com, vec) dev_t dev; int com; struct sgttyb *vec; { alxioctl(dev, com, vec, & DEV_TTY); } static int alpoll(dev, ev, msec) dev_t dev; int ev; int msec; { return ttpoll(&DEV_TTY, ev, msec); } /* * Configuration table. */ CON ALNAME ={ DFCHR|DFPOL, /* Flags */ ALMAJ, /* Major index */ alopen, /* Open */ alclose, /* Close */ NULL, /* Block */ alread, /* Read */ alwrite, /* Write */ alioctl, /* Ioctl */ NULL, /* Powerfail */ alxtimer, /* Timeout */ alload, /* Load */ alunload, /* Unload */ alpoll /* Poll */ };