home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 307_01 / comx.c < prev    next >
C/C++ Source or Header  |  1990-03-20  |  25KB  |  742 lines

  1. /*
  2. HEADER:     ;
  3. TITLE:        PC com port driver;
  4. VERSION:    1.0;
  5.  
  6. DESCRIPTION:    "MS-DOS serial port device driver.  Provides buffered
  7.         input and output to serial port with optional XON/XOFF
  8.         flow control through standard read/write requests or
  9.         interrupt 0x14.
  10.  
  11.         Mixed memory model used.  COMX.C compiled as small
  12.         model with explicitly declared far pointers.  Front
  13.         end program coerces the linkage editor to produce a
  14.         tiny model executable.";
  15.  
  16. WARNINGS:    "Microsoft specific.  Startup code contained in
  17.         COMXFE.ASM does not initialize uninitialized static
  18.         variables to zero.";
  19. KEYWORDS:    Serial communications, device driver;
  20. SYSTEM:     MS-DOS v2 or later;
  21. FILENAME:    COMX.C;
  22.  
  23. SEE-ALSO:    COMX, COMXFE.ASM, COMXBE.C;
  24. AUTHORS:    Hugh Daschbach;
  25. COMPILERS:    Microsoft v5.0,v5.1;
  26. */
  27. /*----------------------------------------------------------------------*/
  28. /* comx.c: MS-DOS driver for multiple communication devices (com1 - com4).
  29.  */
  30. #include "comx.h"
  31.  
  32. /* Instalation:
  33.  * This program is installed as an MS-DOS device driver by including
  34.  * the following entry in the CONFIG.SYS file:
  35.  *        DEVICE = COMX.SYS xxx [xxx [xxx [xxx]]]
  36.  * where xxx is a com port address in hexadecimal (e.g. 3fa).  At least
  37.  * one port is required.  The driver will support as many as four.
  38.  *
  39.  * The communication devices are assumed to interrupt on either IRQ3 or
  40.  * IRQ4.  More than one device can share the same interrupt line as
  41.  * each defined port is polled on every interrupt.  Other versions of
  42.  * this driver support dedicated interrupt vectors, eliminating the
  43.  * polling operation at interrupt time.  However, even with the polling
  44.  * overhead, the driver has been tested successfully at 9600 baud on a
  45.  * 4.77 Mhz 8088.
  46.  */
  47.  
  48. /*----------------------------------------------------------------------*/
  49. /*              System Declarations                */
  50. /*----------------------------------------------------------------------*/
  51. #pragma pack(1)
  52. int inp(unsigned int);
  53. int outp(unsigned int, int);
  54. void _enable(void);
  55. void _disable(void);
  56. #pragma intrinsic(inp, outp, _enable, _disable)
  57.  
  58. /*----------------------------------------------------------------------*/
  59. /*            Local Data Declarations             */
  60. /*----------------------------------------------------------------------*/
  61. typedef unsigned char    uchar;
  62. typedef unsigned short    ushort;
  63.  
  64. /*----------------------------------------------------------------------*/
  65. /*             External Function Prototypes.            */
  66. /*----------------------------------------------------------------------*/
  67. extern void int10(unsigned ax, unsigned bx);
  68.  
  69. /*----------------------------------------------------------------------*/
  70. /*               Local function prototypes            */
  71. /*----------------------------------------------------------------------*/
  72. static    void check_block(struct comport *chan);
  73. static    int  com_int(void);
  74. static    char *define_lines(char far *cp);
  75. extern    void driver(struct rb far *rbp, int line_no);
  76. static    int  eol(int c);
  77. static    void get_char(struct comport *chan);
  78. static    void hookvect(unsigned int intnum, struct isr_block *isr,
  79.               void (cdecl *handler)());
  80. static    int  int14(ushort ax, ushort bx, ushort cx,
  81.            ushort dx, ushort ds, ushort es);
  82. static    int  isspace(int c);
  83. static    int  nop(int i);
  84. static    void put_char(struct comport *chan);
  85. static    void putc(int c);
  86. static    void puts(char *s);
  87. static    void putx(int i);
  88. static    void setspeed(struct comport *chan, int sti);
  89.  
  90. /*----------------------------------------------------------------------*/
  91. /*             Driver interface declarations            */
  92. /*----------------------------------------------------------------------*/
  93. enum opcodes {            /* Driver operation codes:        */
  94.     INIT = 0,            /* initialization            */
  95.     MEDIA_CHECK,        /* media check                */
  96.     BUILD_BPB,            /* build BIOS Parameter Block        */
  97.     INPUT_IOCTL,        /* input control string         */
  98.     INPUT,            /* read                 */
  99.     NON_DEST_INPUT,        /* non destructive input        */
  100.     INPUT_STATUS,        /* input available status        */
  101.     INPUT_FLUSH,        /* flush input buffer            */
  102.     OUTPUT,            /* write                */
  103.     OUTPUT_VERIFY,        /* write with verify            */
  104.     OUTPUT_STATUS,        /* output busy status            */
  105.     OUTPUT_FLUSH,        /* flush output buffer            */
  106.     OUTPUT_IOCTL,        /* output control string        */
  107.     DEVICE_OPEN,        /* device open (removable media)    */
  108.     DEVICE_CLOSE,        /* device close (removable media)    */
  109.     REMOVEABLE_MEDIA,        /* more removable media         */
  110.     GENERIC_IOCTL = 19,     /* more ioctl                */
  111.     GET_LOGICAL_DEVICE = 23,    /* get logical device            */
  112.     SET_LOGICAL_DEVICE = 24    /* set logical device            */
  113. };
  114.  
  115.                 /* Driver status indicators:        */
  116. #define DONE    0x0100        /* I/O done                */
  117. #define BUSY    0x0200        /* device busy                */
  118. #define ERROR    0x8000        /* I/O error                */
  119.  
  120. enum errcodes {         /* Driver error codes:            */
  121.     WRITE_PROTECT,        /* write protect violation        */
  122.     UNKNOWN_UNIT,        /* unknown unit             */
  123.     NOT_READY,            /* device non ready            */
  124.     UNKNOWN_COMMAND,        /* unknown command            */
  125.     CRC_ERROR,            /* CRC error                */
  126.     BAD_STRUCT_LENGTH,        /* bad drive request structure length    */
  127.     SEEK_ERROR,         /* seek error                */
  128.     UNKNOWN_MEDIA,        /* unknown media            */
  129.     SECTOR_NOT_FOUND,        /* sector not found            */
  130.     PAPER_OUT,            /* printer out of paper         */
  131.     WRITE_FAULT,        /* write fault                */
  132.     READ_FAULT,         /* read fault                */
  133.     GENERAL_FAILURE,        /* general failure            */
  134.     RESV_D,            /* reserved                */
  135.     RESV_E,            /* reserved                */
  136.     INVALID_DISK_CHANGE     /* invalid disk change            */
  137. };
  138.  
  139. typedef struct rb {        /* I/O Request block            */
  140.     uchar  len;         /* request block length         */
  141.     uchar  unit;        /* unit code                */
  142.     uchar  op_code;        /* operation code            */
  143.     int    status;        /* status code                */
  144.     char   resv[8];        /* reserved                */
  145.     union {
  146.     struct {
  147.         uchar    count;    /* unit count                */
  148.         char far *end;    /* end of resident module        */
  149.         char far *bpbp;    /* BPB array pointer            */
  150.         uchar    drive;    /* drive number             */
  151.     } init;
  152.     struct {
  153.         uchar    media;    /* media descriptor byte        */
  154.         char far *buf;    /* transfer buffer pointer        */
  155.         int      count;    /* byte/sector count            */
  156.         int      sector;    /* starting sector number        */
  157.         char far *vol_id;    /* volume idenitifier pointer        */
  158.     } inout;
  159.     char    ndi;        /* non destructive input data byte    */
  160.     } os;            /* operation specific            */
  161. } rb;
  162.  
  163. /*----------------------------------------------------------------------*/
  164. /*        Interrupt service routine interface declaration        */
  165. /*----------------------------------------------------------------------*/
  166. typedef struct isr_block {    /* Interrupt Service Routine        */
  167.     uchar   push_es;
  168.     uchar   push_ds;
  169.     uchar   push_dx;
  170.     uchar   push_cx;
  171.     uchar   push_bx;
  172.     uchar   push_ax;
  173.     ushort  mov_ax_cs;
  174.     ushort  mov_ds_ax;
  175.     uchar   call;
  176.     ushort  target;
  177.     ushort  or_ax_ax;
  178.     uchar   pop_ax;
  179.     uchar   pop_bx;
  180.     uchar   pop_cx;
  181.     uchar   pop_dx;
  182.     uchar   pop_ds;
  183.     uchar   pop_es;
  184.     ushort  jz_ip01;
  185.     uchar   cs_prefix;
  186.     ushort  jmp;
  187.     void    (far **fnpp)(void);
  188.     uchar   iret;
  189.     void    (far *fnp)(void);
  190. } isr_block;
  191.  
  192. /*----------------------------------------------------------------------*/
  193. /*            Hardware interface declarations            */
  194. /*----------------------------------------------------------------------*/
  195.                 /* Line Status Register definition    */
  196. #define LSR_RDA     1        /* receive data available        */
  197. #define LSR_OR        2        /* overrun error            */
  198. #define LSR_PE        4        /* parity error             */
  199. #define LSR_FE        8        /* framing error            */
  200. #define LSR_BI     0x10        /* break interupt            */
  201. #define LSR_TBA  0x20        /* transmit holding buffer available    */
  202. #define LSR_TSRE 0x40        /* transmit shift buffer empty        */
  203.  
  204.                 /* Modem Control Register definitions    */
  205. #define MCR_DTR     1        /* data terminal ready            */
  206. #define MCR_RTS     2        /* request to send            */
  207. #define MCR_OUT1    4        /* reset Hayes Smartmodem        */
  208. #define MCR_OUT2    8        /* enable Hayes Smartmodem interrupts    */
  209. #define MCR_LOOP 0x10        /* loop back test enable        */
  210.  
  211.                 /* Interrupt Identification Register    */
  212. #define IIR_PEND 1        /* interrupt pending            */
  213. #define IIR_MASK 6        /* select following values:        */
  214. #define IIR_RLS  6        /* receiver line status (error)     */
  215. #define IIR_RDA  4        /* receive data available        */
  216. #define IIR_TBA  2        /* transmit buffer available        */
  217. #define IIR_MS     0        /* modem status             */
  218.  
  219.                 /* Interrupt Enable Register definition */
  220. #define IER_RDA 1        /* receive data available interrupt    */
  221. #define IER_TBA 2        /* transmit buffer available interrupt    */
  222. #define IER_RLS 4        /* receive line status interrupt    */
  223. #define IER_MS    8        /* modem status interrupt        */
  224.  
  225.                 /* Line Control Register definition    */
  226. #define LCR_WLS0    1        /* word length select bit 0        */
  227. #define LCR_WLS1    2        /* word length select bit 1        */
  228. #define LCR_STB     4        /* number of stop bits            */
  229. #define LCR_PEN     8        /* parity enable            */
  230. #define LCR_EPS    16        /* even parity select            */
  231. #define LCR_STICK  32        /* stick parity             */
  232. #define LCR_BREAK  64        /* transmit break            */
  233. #define LCR_DLAB  128        /* divisor latch access bit        */
  234.  
  235.                 /* Modem Status Register definition    */
  236. #define MSR_DCTS 0x00001    /* delta clear to send            */
  237. #define MSR_DDSR 0x00002    /* delta data set ready         */
  238. #define MSR_TERI 0x00004    /* trailing edge ring indicator     */
  239. #define MSR_DCD  0x00008    /* carrier detect            */
  240. #define MSR_CTS  0x00010    /* clear to send            */
  241. #define MSR_DSR  0x00020    /* data set ready            */
  242. #define MSR_RI     0x00040    /* ring indicator            */
  243. #define MSR_CD     0x00080    /* carrier detect            */
  244.  
  245. typedef enum comregs {        /* INS8250 registers addresses        */
  246.     rx = 0,            /* receive data register        */
  247.     tx = 0,            /* transmit data register        */
  248.     ie,             /* interrupt enable register        */
  249.     ii,             /* interupt identification register    */
  250.     lc,             /* line control register        */
  251.     mc,             /* modem control register        */
  252.     ls,             /* line status register         */
  253.     ms,             /* modem status register        */
  254.     dl_lsb = 0,         /* divisor latch least significant byte */
  255.     dl_msb            /* divisor latch most  significant byte */
  256. } comregs;
  257.  
  258. #define PIC    0x20        /* 8259A Programmable Interrupt Controller */
  259. #define EOI    0x20        /* End of Interrupt command to PIC    */
  260.  
  261. /*----------------------------------------------------------------------*/
  262. /*             Local Data Definitions             */
  263. /*----------------------------------------------------------------------*/
  264. #define OBUFSZ        128         /* output character fifo size    */
  265.                     /* next output buffer position    */
  266. #define next_out(i) ((i + 1) & (OBUFSZ - 1))
  267.                     /* pervious output buffer position    */
  268. #define prev_out(i) ((i - 1) & (OBUFSZ - 1))
  269. #define IBUFSZ        128         /* input character fifo size    */
  270.                     /* next input buffer position    */
  271. #define next_in(i) ((i + 1) & (IBUFSZ - 1))
  272.                     /* pervious input buffer position    */
  273. #define prev_in(i) ((i - 1) & (IBUFSZ - 1))
  274. #define THOLD_OFF   (IBUFSZ/2)        /* input char count b4 sending XOFF */
  275. #define THOLD_ON    (THOLD_OFF/2)   /* input char count b4 sending XON    */
  276. #define XON        0x11        /* CTRL/Q                */
  277. #define XOFF        0x13        /* CTRL/S                */
  278.  
  279. typedef struct comport {
  280.     unsigned port;        /* base port address            */
  281.     uchar    *out_buf;        /* output buffer pointer        */
  282.     unsigned *in_buf;        /* input buffer pointer         */
  283.     uchar    flow;        /* flow control enabled         */
  284.     uchar    in_block;        /* we've transmitted X-OFF        */
  285.     uchar    out_block;     /* we've received X-OFF         */
  286.     int      out_head;        /* index to next empty buffer position    */
  287.     int      out_tail;        /* index to last printed character    */
  288.     int      in_head;        /* index to next empty buffer position    */
  289.     int      in_tail;        /* index to last returned character    */
  290. } comport;
  291. comport comx[4] = { { 0 }, { 0 }, { 0 }, { 0 } };
  292.  
  293. static int port_count;        /* defined port count            */
  294.  
  295. static unsigned speed_tbl[] = { /* baud rate generator divisor latch    */
  296.     1047, 768, 384, 192, 96, 48, 24, 12, 6, 3
  297. };
  298.  
  299. isr_block isr0c;        /* interrupt 0x0c service routine    */
  300. isr_block isr0b;        /* interrupt 0x0b service routine    */
  301. isr_block isr14;        /* interrupt 0x14 service routine    */
  302.  
  303. #ifdef DEBUG
  304. /*----------------------------------------------------------------------*/
  305. /*               Diagnostic output routines            */
  306. /*----------------------------------------------------------------------*/
  307. #define  putchar(c)  int10((14 << 8) + c, 0)
  308. #define  printnl(s) { puts(#s " = "); putx(s); puts("\n\r"); }
  309. #define  print(s) { puts(#s " = "); putx(s); puts("  "); }
  310. #define  dumpc(c) { puts(#c " = "); putc(c); puts("  "); }
  311. char *to_print = "0123456789abcdef";
  312.  
  313. static void putc(int c)
  314. {
  315.     putchar(to_print[c >> 4 & 0xf]);
  316.     putchar(to_print[c & 0xf]);
  317. }
  318.  
  319. static void puts(char *s)
  320. {
  321.     for (; *s; s++)
  322.     putchar(*s);
  323. }
  324.  
  325. static void putx(int  i)
  326. {
  327.     putchar(to_print[i >> 12 & 0xf]);
  328.     putchar(to_print[i >>  8 & 0xf]);
  329.     putchar(to_print[i >>  4 & 0xf]);
  330.     putchar(to_print[i         & 0xf]);
  331. }
  332. #endif
  333.  
  334. /*----------------------------------------------------------------------*/
  335. /*            Common service routines             */
  336. /*----------------------------------------------------------------------*/
  337. static int  nop(i)        /* flush instruction prefetch queue    */
  338. {                /* for timing btwn inp & outp        */
  339.     return(i);
  340. }
  341.  
  342. static void setspeed(register comport *chan, int sti)
  343. {
  344.     int  lcr;
  345.     lcr = inp(chan->port + lc);             (void) nop(0);
  346.     outp(chan->port + lc, lcr + LCR_DLAB);        (void) nop(0);
  347.     outp(chan->port + dl_msb, speed_tbl[sti] >> 8);    (void) nop(0);
  348.     outp(chan->port + dl_lsb, speed_tbl[sti] & 0xff);    (void) nop(0);
  349.     outp(chan->port + lc, lcr);
  350. }
  351.  
  352. /*----------------------------------------------------------------------*/
  353. /*               Interrupt service routines            */
  354. /*----------------------------------------------------------------------*/
  355. static int  com_int()
  356. {
  357.     register comport *chan;    /* current comx[] element pointer    */
  358.     int  int_id;
  359.     char active;
  360.  
  361.     _enable();            /* reenable interrupts            */
  362.     do {
  363.     active = 0;
  364.     for (chan = comx; chan->port; chan++)
  365.     {
  366.         while (((int_id = inp(chan->port + ii)) & IIR_PEND) == 0)
  367.         {
  368.         active = 1;
  369.         switch (int_id & IIR_MASK)
  370.         {
  371.         case IIR_RDA:            /* rx data avalable    */
  372.             get_char(chan);
  373.             break;
  374.         case IIR_RLS:            /* receiver line status */
  375.             chan->in_buf[chan->in_head] = inp(chan->port + ls) << 8;
  376.             chan->in_head = next_in(chan->in_head);
  377.             break;
  378.         case IIR_TBA:            /* tx buffer available    */
  379.             put_char(chan);
  380.             break;
  381.         case IIR_MS:            /* Modem status change    */
  382.             inp(chan->port + ms);
  383.             break;
  384.         }
  385.         }
  386.     }
  387.     } while (active);
  388.     outp(PIC, EOI);        /* reenable PIC interrupts        */
  389.     return(0);            /* skip the BIOS routines on exit    */
  390. }
  391.  
  392. /*----------------------------------------------------------------------*/
  393. static void get_char(chan)
  394. register comport *chan;     /* current comx[] element pointer    */
  395. {
  396.     unsigned c;
  397.     int   line_stat;
  398.     int   count;
  399.  
  400.     if ((line_stat = inp(chan->port + ls)) & LSR_RDA)
  401.     {
  402.     c = (unsigned) inp(chan->port + rx);
  403.     if ((chan->flow & PROT_XOUT) != 0 && c == XOFF)
  404.         chan->out_block = 1;
  405.     else if ((chan->flow & PROT_XOUT) != 0 && c == XON)
  406.     {
  407.         chan->out_block = 0;
  408.         put_char(chan);
  409.     }
  410.     else
  411.     {
  412.         if (next_in(chan->in_head) == chan->in_tail)
  413.         chan->in_buf[chan->in_head] |= LSR_OR << 8;
  414.         else
  415.         {
  416.         chan->in_buf[chan->in_head] = c | ((unsigned) line_stat << 8);
  417.         chan->in_head = next_in(chan->in_head);
  418.         }
  419.         if ((chan->flow & PROT_XIN) != 0)
  420.         {
  421.         if ((count = chan->in_head - chan->in_tail) < 0)
  422.             count += IBUFSZ;
  423.         if (count > THOLD_OFF && !chan->in_block)
  424.         {
  425.             chan->out_tail = prev_out(chan->out_tail);
  426.             chan->out_buf[chan->out_tail] = XOFF;
  427.             put_char(chan);
  428.             chan->in_block = 1;
  429.         }
  430.         } /* flow control enabled */
  431.     } /* received XON or XOFF */
  432.     } /* receive data available */
  433. }
  434.  
  435. /*----------------------------------------------------------------------*/
  436. static void put_char(chan)
  437. register comport *chan;     /* current comx[] element pointer    */
  438. {
  439.     if (inp(chan->port + ls) & LSR_TBA &&
  440.     !chan->out_block &&
  441.     chan->out_head != chan->out_tail)
  442.     {
  443.     outp(chan->port + tx, chan->out_buf[chan->out_tail]);
  444.     chan->out_tail = next_out(chan->out_tail);
  445.     }
  446. }
  447.  
  448. /*----------------------------------------------------------------------*/
  449. static void check_block(chan)    /* should we transmit XON ?        */
  450. register comport *chan;
  451. {
  452.     int count;
  453.  
  454.     if (chan->in_block)
  455.     {
  456.     if ((count = chan->in_head - chan->in_tail) < 0)
  457.         count += IBUFSZ;
  458.     if (count < THOLD_ON)
  459.     {
  460.         chan->out_tail = prev_out(chan->out_tail);
  461.         chan->out_buf[chan->out_tail] = XON;
  462.         put_char(chan);
  463.         chan->in_block = 0;
  464.     }
  465.     }
  466. }
  467.  
  468. /*----------------------------------------------------------------------*/
  469. /*                 Initialization                */
  470. /*----------------------------------------------------------------------*/
  471. static int eol(c)     { return(c == '\r' || c == '\n'); }
  472. static int isspace(c) { return(c == ' '  || c == '\t'); }
  473.  
  474. static char *define_lines(char far *cp)
  475. {
  476.     extern char near bss_end;    /* defined in comxbe.c            */
  477.     register comport *chan;    /* current comx[] element pointer    */
  478.     char   *bp;         /* buffer pointer            */
  479.     int    port;        /* port address             */
  480.  
  481.     bp = &bss_end;        /* initial buffer pointer        */
  482.     chan = comx;
  483.     while (!eol(*cp) && isspace(*cp))
  484.     cp++;
  485.     while (!eol(*cp) && !isspace(*cp))
  486.     cp++;
  487.     while (!eol(*cp) && isspace(*cp))
  488.     cp++;
  489.     while (!eol(*cp))
  490.     {
  491.     port = 0;
  492.     while (!eol(*cp) && !isspace(*cp))
  493.     {
  494.         port = (port << 4) +
  495.         ((*cp >= '0' && *cp <= '9') ? *cp - '0' :
  496.          (*cp >= 'a' && *cp <= 'f') ? *cp - 'a' + 10 :
  497.          (*cp >= 'A' && *cp <= 'F') ? *cp - 'A' + 10 : 0);
  498.         cp++;
  499.     }
  500.     chan->port = port;
  501.     outp(chan->port + mc, MCR_DTR | MCR_RTS | MCR_OUT2);
  502.     chan->in_buf = (unsigned *) bp;
  503.     bp += IBUFSZ * sizeof(unsigned);
  504.     chan->out_buf = (uchar *) bp;
  505.     bp += OBUFSZ * sizeof(uchar);
  506.     chan++;
  507.     while (!eol(*cp) && isspace(*cp))
  508.         cp++;
  509.     }
  510.     port_count = chan - comx;
  511.     return(bp);
  512. }
  513.  
  514. /*----------------------------------------------------------------------*/
  515. static void hookvect(unsigned    intnum,
  516.              isr_block    *isr,
  517.              void    (*handler)())
  518. {
  519.     void (far *(far *vect_ad))();
  520.     extern isr_block isr_prototype;
  521.  
  522.     *isr = isr_prototype;
  523.     isr->target = (char *) handler - (char *) &isr->call - 3;
  524.     vect_ad = (void (far *(far *))()) ((long) (intnum * 4));
  525.     isr->fnp  = *vect_ad;
  526.     isr->fnpp = &isr->fnp;
  527.     *vect_ad = (void (far *)()) isr;
  528. }
  529.  
  530. /*----------------------------------------------------------------------*/
  531. /*                   DOS driver                */
  532. /*----------------------------------------------------------------------*/
  533. void driver(rbp, line_no)
  534. rb far *rbp;
  535. int    line_no;
  536. {
  537.     comregs  *(far *chan_ptr) = (comregs *(far *)) 0x00400006l;
  538.     register comport  *chan;    /* current comx[] element pointer    */
  539.     static   char *endp;    /* end of driver pointer        */
  540.     int      i;
  541.  
  542.     chan = comx + line_no;
  543.     switch (rbp->op_code)
  544.     {
  545.     case INIT:
  546.     if (line_no == 0)
  547.     {
  548.         endp = define_lines(rbp->os.init.bpbp);
  549.         hookvect(0x0c, &isr0c, com_int);
  550.         hookvect(0x0b, &isr0b, com_int);
  551.         hookvect(0x14, &isr14, int14);
  552.     }
  553.     rbp->os.init.end = (char far *) endp;
  554.     break;
  555.     case INPUT_IOCTL:
  556.     if ((i = chan->in_head - chan->in_tail) < 0)
  557.         i += IBUFSZ;
  558.     if (rbp->os.inout.count == 2)
  559.         *((int far *) rbp->os.inout.buf) = i;
  560.     else
  561.         rbp->status |= ERROR | READ_FAULT;
  562.     break;
  563.     case INPUT:
  564.     while (chan->in_head == chan->in_tail)
  565.         ;
  566.     for (i = 0;
  567.          i < rbp->os.inout.count && chan->in_head != chan->in_tail;
  568.          i++)
  569.     {
  570.         if (chan->in_buf[chan->in_tail] &
  571.         ((LSR_OR | LSR_PE | LSR_FE | LSR_BI) << 8))
  572.         {
  573.         rbp->status |= ERROR | READ_FAULT;
  574.         break;
  575.         }
  576.         rbp->os.inout.buf[i] = (char) chan->in_buf[chan->in_tail];
  577.         chan->in_tail = next_in(chan->in_tail);
  578.     }
  579.     rbp->os.inout.count = i;
  580.     check_block(chan);
  581.     break;
  582.     case NON_DEST_INPUT:
  583.     if (chan->in_head == chan->in_tail)
  584.         rbp->status |= BUSY;
  585.     else
  586.         rbp->os.ndi = (char) chan->in_buf[chan->in_tail];
  587.     break;
  588.     case INPUT_STATUS:
  589.     rbp->status |= (chan->in_head != chan->in_tail) ? 0 : BUSY;
  590.     break;
  591.     case INPUT_FLUSH:
  592.     chan->in_tail = chan->in_head;
  593.     break;
  594.     case OUTPUT:
  595.     case OUTPUT_VERIFY:
  596.     for (i = 0; i < rbp->os.inout.count; i++)
  597.     {
  598.         if (next_out(chan->out_head) == chan->out_tail)
  599.         {
  600.         rbp->status |= BUSY;
  601.         break;
  602.         }
  603.         chan->out_buf[chan->out_head] = rbp->os.inout.buf[i];
  604.         chan->out_head = next_out(chan->out_head);
  605.     }
  606.     rbp->os.inout.count = i;
  607.     break;
  608.     case OUTPUT_STATUS:
  609.     rbp->status |= (chan->out_head == chan->out_tail) ? 0 : BUSY;
  610.     break;
  611.     case OUTPUT_FLUSH:
  612.     chan->out_tail = chan->out_head;
  613.     break;
  614.     default:
  615.     rbp->status |= ERROR | UNKNOWN_COMMAND;
  616.     }
  617.                 /* it seems wasteful to reset the interrupt
  618.                  * masks on every I/O operation.  But
  619.                  * usurping communication programs do not
  620.                  * always reset the hardware regs correctly
  621.                  * upon termination.  we pay the price here.
  622.                  */
  623.     outp(PIC + 1, inp(PIC + 1) & 0xe7);
  624.                 /* (re)loading the UART interrupt enable
  625.                  * register pops a tx bufffer available
  626.                  * interrupt (if it actually is).  This
  627.                  * initiates output data transfer after
  628.                  * data is written to the output buffer.
  629.                  */
  630.     outp(chan->port + ie, IER_RDA | IER_TBA | IER_RLS);
  631.     rbp->status |= DONE;
  632. }
  633.  
  634. /*----------------------------------------------------------------------*/
  635. /*             BIOS int 14h interface             */
  636. /*----------------------------------------------------------------------*/
  637. static int int14(ax, bx, cx, dx, ds, es)
  638. ushort ax, bx, cx, dx, ds, es;
  639. {
  640.     static   unsigned status;
  641.     register comport  *chan;     /* current comx[] element pointer    */
  642.     char far *cfp;
  643.  
  644.     _enable();
  645.     if (dx > port_count)
  646.     return(0);
  647.     chan = comx + dx;
  648.     switch (ax >> 8)
  649.     {
  650.     case INIT_232:        /* initialize port            */
  651.     outp(PIC + 1, inp(PIC + 1) & 0xe7);
  652.     outp(chan->port + mc, 0);
  653.     setspeed(chan, (ax >> 5) & 7);
  654.     outp(chan->port + lc, ax & 0x1f);
  655.     outp(chan->port + mc, MCR_DTR | MCR_RTS | MCR_OUT2);
  656.     break;
  657.     case PUTC_232:        /* put character            */
  658.     if (next_out(chan->out_head) == chan->out_tail)
  659.     {
  660.         ax = 0x8000;
  661.         break;
  662.     }
  663.     chan->out_buf[chan->out_head] = (char) (ax & 0xff);
  664.     chan->out_head = next_out(chan->out_head);
  665.     outp(chan->port + ie, IER_RDA | IER_TBA | IER_RLS);
  666.     break;
  667.     case GETC_232:        /* get character            */
  668.     if (chan->in_head == chan->in_tail)
  669.     {
  670.         ax = 0x8000;
  671.         break;
  672.     }
  673.     ax = chan->in_buf[chan->in_tail];
  674.     chan->in_tail = next_in(chan->in_tail);
  675.     check_block(chan);
  676.     break;
  677.     case STAT_232:        /* fetch status.  Use status saved by    */
  678.                 /* string read if any, or current    */
  679.                 /* hardware if not.            */
  680.     if (status == 0)
  681.         ax = (inp(chan->port + ls) << 8) + inp(chan->port + ms);
  682.     else
  683.     {
  684.         ax = status;
  685.         status = 0;
  686.     }
  687.     break;
  688.     case WRITE_232:        /* write string             */
  689.     cfp = (char far *) ((unsigned long) es << 16) + bx;
  690.     for (ax = 0; ax < cx; ax++)
  691.     {
  692.         if (next_out(chan->out_head) == chan->out_tail)
  693.         {
  694.         ax |= 0x8000;
  695.         break;
  696.         }
  697.         chan->out_buf[chan->out_head] = *cfp++;
  698.         chan->out_head = next_out(chan->out_head);
  699.     }
  700.     outp(chan->port + ie, IER_RDA | IER_TBA | IER_RLS);
  701.     break;
  702.     case READ_232:        /* read string                */
  703.     cfp = (char far *) ((unsigned long) es << 16) + bx;
  704.     for (ax = 0; ax < cx; ax++)
  705.     {
  706.         if (chan->in_buf[chan->in_tail] &
  707.         ((LSR_OR | LSR_PE | LSR_FE | LSR_BI) << 8))
  708.         {
  709.         ax |= 0x8000;    /* signal error, save status for    */
  710.                 /* subsequent inquiry            */
  711.         status = chan->in_buf[chan->in_tail] & ~0xff +
  712.              inp(chan->port + ms);
  713.         chan->in_tail = next_in(chan->in_tail);
  714.         break;
  715.         }
  716.         *cfp++ = (char) chan->in_buf[chan->in_tail];
  717.         chan->in_tail = next_in(chan->in_tail);
  718.     }
  719.     check_block(chan);
  720.     break;
  721.     case ICOUNT_232:        /* fetch input character count        */
  722.     if ((ax = chan->in_head - chan->in_tail) < 0)
  723.         ax += IBUFSZ;
  724.     break;
  725.     case OCOUNT_232:        /* fetch output buffer available    */
  726.     ax = (chan->out_head >= chan->out_tail) ?
  727.           OBUFSZ - (chan->out_head - chan->out_tail) :
  728.           chan->out_tail - chan->out_head;
  729.     break;
  730.     case SETPROT_232:        /* set protocol             */
  731.     chan->flow = (uchar) ax;
  732.     break;
  733.     case SETSPEED_232:        /* set line speed            */
  734.     if (cx > num_entries(speed_tbl))
  735.         ax = 0x8000;    /* report invalid speed         */
  736.     else
  737.         setspeed(chan, cx);
  738.     break;
  739.     }
  740.     return(0);
  741. }
  742.