home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / src0131 / 8250.c next >
C/C++ Source or Header  |  1991-01-26  |  17KB  |  727 lines

  1. /* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  *
  4.  * 16550A support plus some statistics added mah@hpuviea.at 15/7/89
  5.  *
  6.  * CTS hardware flow control from dkstevens@ucdavis,
  7.  * additional stats from delaroca@oac.ucla.edu added by karn 4/17/90
  8.  */
  9. #include <stdio.h>
  10. #include <dos.h>
  11. #include "global.h"
  12. #include "config.h"
  13. #include "mbuf.h"
  14. #include "iface.h"
  15. #include "asy.h"
  16. #include "pc.h"
  17. #include "slip.h"
  18. #include "nrs.h"
  19. #include "proc.h"
  20. #include "8250.h"
  21.  
  22. static int asyrxint __ARGS((struct asy *asyp));
  23. static void asytxint __ARGS((int dev));
  24. static void asymsint __ARGS((int dev));
  25.  
  26. struct asy Asy[ASY_MAX];
  27. /* ASY interrupt handlers */
  28. static INTERRUPT (*Handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
  29.  
  30. /* Initialize asynch port "dev" */
  31. int
  32. asy_init(dev,iface,arg1,arg2,bufsize,trigchar,cts,rlsd)
  33. int dev;
  34. struct iface *iface;
  35. char *arg1,*arg2;    /* Attach args for address and vector */
  36. unsigned bufsize;
  37. int trigchar;
  38. char cts;
  39. char rlsd;
  40. {
  41.     register unsigned base;
  42.     register struct fifo *fp;
  43.     register struct asy *ap;
  44.     char i_state;
  45.  
  46.     ap = &Asy[dev];
  47.     ap->iface = iface;
  48.     ap->addr = htoi(arg1);
  49.     ap->vec = htoi(arg2);
  50.     ap->cts_flow_control = cts;
  51.     ap->rlsd_line_control = rlsd;
  52.     /* Set up receiver FIFO */
  53.     fp = &ap->fifo;
  54.     fp->buf = mallocw(bufsize);
  55.     fp->bufsize = bufsize;
  56.     fp->wp = fp->rp = fp->buf;
  57.     fp->cnt = 0;
  58.     fp->hiwat = 0;
  59.     fp->overrun = 0;
  60.     base = ap->addr;
  61.     ap->trigchar = trigchar;
  62.  
  63.     /* Purge the receive data buffer */
  64.     (void)inportb(base+RBR);
  65.  
  66.     i_state = dirps();
  67.  
  68.     /* Save original interrupt vector, mask state, control bits */
  69.     ap->save.vec = getirq(ap->vec);
  70.     ap->save.mask = getmask(ap->vec);
  71.     ap->save.lcr = inportb(base+LCR);
  72.     ap->save.ier = inportb(base+IER);
  73.     ap->save.mcr = inportb(base+MCR);
  74.  
  75.     /* save speed bytes */
  76.     setbit(base+LCR,LCR_DLAB);
  77.     ap->save.divl = inportb(base+DLL);
  78.     ap->save.divh = inportb(base+DLM);
  79.     clrbit(base+LCR,LCR_DLAB);
  80.  
  81.     /* Set interrupt vector to SIO handler */
  82.     setirq(ap->vec,Handle[dev]);
  83.  
  84.     /* Set line control register: 8 bits, no parity */
  85.     outportb(base+LCR,(char)LCR_8BITS);
  86.  
  87.     /* determine if 16550A, turn on FIFO mode and clear RX and TX FIFOs */
  88.     outportb(base+FCR,(char) FIFO_ENABLE);
  89.  
  90.     /* According to National ap note AN-493, the FIFO in the 16550 chip
  91.      * is broken and must not be used. To determine if this is a 16550A
  92.      * (which has a good FIFO implementation) check that both bits 7
  93.      * and 6 of the IIR are 1 after setting the fifo enable bit. If
  94.      * not, don't try to use the FIFO.
  95.      */
  96.     if ((inportb(base+IIR) & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) {
  97.         ap->is_16550a = 1;
  98.         outportb(base+FCR,(char) FIFO_SETUP);
  99.     } else {
  100.         /* Chip is not a 16550A. In case it's a 16550 (which has a
  101.          * broken FIFO), turn off the FIFO bit.
  102.          */
  103.         outportb(base+FCR,(char)0);
  104.         ap->is_16550a = 0;
  105.      }
  106.     if (ap->rlsd_line_control) {
  107.         /* Physical link is down when RLSD signal is down;
  108.          * link okay for traffic when RLSD signal is asserted
  109.          */
  110.         /* Turn on modem status interrupt, leave receive
  111.          * and transmit interrupts off until RLSD is asserted
  112.          */
  113.         outportb(base+IER, (char)IER_MS);
  114.         /* DTR and RTS should match the RLSD signal */
  115.         asymsint(dev);
  116.         /* Turn on 8250 master interrupt enable (connected to OUT2) */
  117.         outportb(base+MCR, (char)MCR_OUT2);
  118.     } else {
  119.         /* Turn on receive interrupt enable in 8250, leave transmit
  120.          * and modem status interrupts turned off for now
  121.          */
  122.         outportb(base+IER,(char)IER_DAV);
  123.  
  124.         /* Set modem control register: assert DTR, RTS, turn on 8250
  125.          * master interrupt enable (connected to OUT2)
  126.          */
  127.         outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));
  128.     }
  129.     /* Enable interrupt */
  130.     maskon(ap->vec);
  131.     restore(i_state);
  132.     return 0;
  133. }
  134. int
  135. asy_stop(iface)
  136. struct iface *iface;
  137. {
  138.     register unsigned base;
  139.     register struct asy *ap;
  140.     char i_state;
  141.  
  142.     ap = &Asy[iface->dev];
  143.     ap->iface = NULLIF;
  144.     /* Release slip or nrs block */
  145. #ifdef    SLIP
  146.     if(Slip[iface->xdev].iface == iface)
  147.         Slip[iface->xdev].iface = NULLIF;
  148. #endif
  149. #ifdef    NRS
  150.     if(Nrs[iface->xdev].iface == iface)
  151.         Nrs[iface->xdev].iface = NULLIF;    
  152. #endif
  153.  
  154.     base = ap->addr;
  155.  
  156.     /* Purge the receive data buffer */
  157.     (void)inportb(base+RBR);
  158.  
  159.     /* and hardware fifos if available */
  160.     if (ap->is_16550a)
  161.         outportb(base+FCR,(char) FIFO_SETUP);
  162.  
  163.     /* Restore original interrupt vector and 8259 mask state */
  164.     i_state = dirps();
  165.     setirq(ap->vec,ap->save.vec);
  166.     if(ap->save.mask)
  167.         maskon(ap->vec);
  168.     else
  169.         maskoff(ap->vec);
  170.  
  171.     /* Restore speed regs */
  172.     setbit(base+LCR,LCR_DLAB);
  173.     outportb(base+DLL,ap->save.divl);    /* Low byte */
  174.     outportb(base+DLM,ap->save.divh);    /* Hi byte */
  175.     clrbit(base+LCR,LCR_DLAB);
  176.  
  177.     /* Restore control regs */
  178.     outportb(base+LCR,ap->save.lcr);
  179.     outportb(base+IER,ap->save.ier);
  180.     outportb(base+MCR,ap->save.mcr);
  181.     restore(i_state);
  182.     free(ap->fifo.buf);
  183.     return 0;
  184. }
  185. /* Asynchronous line I/O control */
  186. int
  187. asy_ioctl(iface,argc,argv)
  188. struct iface *iface;
  189. int argc;
  190. char *argv[];
  191. {
  192.     int16 base;
  193.  
  194.     if(argc < 1){
  195.         tprintf("%u\n",Asy[iface->dev].speed);
  196.         return 0;
  197.     }
  198.     if(argc < 2)
  199.         return asy_speed(iface->dev,(long)atoi(argv[0]),Asy[iface->dev].autospeed);
  200.     switch(argv[0][0]) {
  201.     case 's':
  202.         return asy_speed(iface->dev,(long)atoi(argv[1]),Asy[iface->dev].autospeed);
  203.     case 'd':
  204.         base = Asy[iface->dev].addr;
  205.         if(argv[1][0] == '0') {
  206.             clrbit(base+MCR,MCR_RTS);
  207.             clrbit(base+MCR,MCR_DTR);
  208.         } else {
  209.             setbit(base+MCR,MCR_RTS);
  210.             setbit(base+MCR,MCR_DTR);
  211.         }
  212.         break;
  213.     }
  214.     return 0;
  215. }
  216. /* Set asynch line speed */
  217. int
  218. asy_speed(dev,speed,autospeed)
  219. int dev;
  220. long speed;
  221. long autospeed;
  222. {
  223.     register unsigned base;
  224.     register int divisor;
  225.     struct asy *asyp;
  226.     char i_state;
  227.  
  228.     if(speed == 0 || dev >= ASY_MAX)
  229.         return -1;
  230.     asyp = &Asy[dev];
  231.     if(asyp->iface == NULLIF)
  232.         return -1;
  233.     base = asyp->addr;
  234.     asyp->speed = speed;
  235.     asyp->autospeed = autospeed;
  236.  
  237.     divisor = BAUDCLK / speed;
  238.  
  239.     i_state = dirps();
  240.  
  241.     /* Purge the receive data buffer */
  242.     (void)inportb(base+RBR);
  243.     if (asyp->is_16550a)       /* clear tx+rx fifos */
  244.         outportb(base+FCR,(char) FIFO_SETUP);
  245.  
  246.     /* Turn on divisor latch access bit */
  247.     setbit(base+LCR,LCR_DLAB);
  248.  
  249.     /* Load the two bytes of the register */
  250.     outportb(base+DLL,(char)(divisor & 0xff));        /* Low byte */
  251.     outportb(base+DLM,(char)((divisor >> 8) & 0xff));    /* Hi byte */
  252.  
  253.     /* Turn off divisor latch access bit */
  254.     clrbit(base+LCR,LCR_DLAB);
  255.  
  256.     restore(i_state);
  257.     return 0;
  258. }
  259. /* Wait for a message to tell us the link speed */
  260. int
  261. asy_autobaud(dev)
  262. int dev;
  263. {
  264.     register struct asy *asyp;
  265.     char c;
  266.     int speedchars = 0;
  267.     unsigned speed = 0;
  268.  
  269.     asyp = &Asy[dev];
  270.     asy_speed(dev, asyp->autospeed, asyp->autospeed);
  271.     log(-1,"asy %d: autobaud: wait for speed message",dev);
  272.  
  273.     for ( ; ; ) {
  274.         c = get_asy(dev);
  275.         c &= 0x007f;
  276.         if (speedchars) {
  277.             if (c == '\n') {
  278.                 /* End of autobaud message, check validity */
  279.                 switch(speed) {
  280.                 case 300:
  281.                 case 1200:
  282.                 case 2400:
  283.                 case 4800:
  284.                 case 9600:
  285.                 case 19200:
  286.                     /* Got one of the supported speeds */
  287.                     break;
  288.                 default:
  289.                     /* Unsupported speed, wait again */
  290.                     speed = 0;
  291.                     speedchars = 0;
  292.                 }
  293.                 if (speed != 0)
  294.                     break;
  295.             }
  296.             speed *= 10;
  297.             speed += c - '0';
  298.         } else if (c == 'B') {
  299.             speedchars = 1;
  300.         }
  301.     }
  302.     asy_speed(dev, speed, asyp->autospeed);
  303.     log(-1,"asy %d: autobaud: set speed: %d",dev,speed);
  304.     return 0;
  305. }
  306.  
  307. /* Start transmission of a buffer on the serial transmitter */
  308. void
  309. asy_output(dev,buf,cnt)
  310. int dev;
  311. char *buf;
  312. unsigned short cnt;
  313. {
  314.     register struct dma *dp;
  315.     unsigned base;
  316.     char i_state;
  317.     char ier;
  318.     struct asy *asyp;
  319.  
  320.     if(dev < 0 || dev >= ASY_MAX)
  321.         return;
  322.     asyp = &Asy[dev];
  323.     if(asyp->iface == NULLIF)
  324.         return;
  325.     base = asyp->addr;
  326.     dp = &asyp->dma;
  327.     i_state = dirps();
  328.     if(dp->flags){
  329.         restore(i_state);
  330.         return;    /* Already busy */
  331.     }
  332.     dp->data = buf;
  333.     dp->cnt = cnt;
  334.     dp->flags = 1;
  335.  
  336.     if(asyp->cts_flow_control){
  337.         /* CTS flow control is enabled; let the modem control
  338.          * interrupt enable transmit interrupts if CTS is off
  339.          */
  340.         ier = IER_MS;
  341.         if(inportb(base+MSR) & MSR_CTS)
  342.             ier |= IER_TxE;
  343.     } else {
  344.         /* Enable transmit interrupts; this will cause an immediate
  345.          * interrupt that will start things going
  346.          */
  347.         ier = IER_TxE;
  348.     }
  349.     setbit(base+IER,ier);
  350.     /* "Kick start" the transmitter interrupt routine, in case just
  351.      * setting the interrupt enable bit doesn't case an interrupt
  352.      */
  353.     if(ier & IER_TxE)
  354.         asytxint(dev);
  355.     restore(i_state);
  356. }
  357. /* Blocking read from asynch line
  358.  * Returns count of characters read
  359.  */
  360. int
  361. get_asy(dev)
  362. int dev;
  363. {
  364.     char i_state;
  365.     register struct fifo *fp;
  366.     char c;
  367.  
  368.     fp = &Asy[dev].fifo;
  369.  
  370.     i_state = dirps();
  371.     while(fp->cnt == 0){
  372.         if(pwait(fp) != 0){
  373.             restore(i_state);
  374.             return -1;
  375.         }
  376.     }
  377.     fp->cnt--;
  378.     restore(i_state);
  379.  
  380.     c = *fp->rp++;
  381.     if(fp->rp >= &fp->buf[fp->bufsize])
  382.         fp->rp = fp->buf;
  383.  
  384.     return uchar(c);
  385. }
  386. /* Interrupt handler for 8250 asynch chip */
  387. void
  388. asyint(dev)
  389. int dev;
  390. {
  391.     struct asy *asyp;
  392.     unsigned base;
  393.     char iir;
  394.  
  395.     asyp = &Asy[dev];
  396.     base = asyp->addr;
  397.     while(((iir = inportb(base+IIR)) & IIR_IP) == 0){
  398.         switch(iir & IIR_ID){
  399.         case IIR_RDA:    /* Receiver interrupt */
  400.             asyrxint(asyp);
  401.             break;
  402.         case IIR_THRE:    /* Transmit interrupt */
  403.             asytxint(dev);
  404.             break;
  405.         case IIR_MSTAT:    /* Modem status change */
  406.             asymsint(dev);
  407.             asyp->cts_flow_ints++;
  408.             break;
  409.         }
  410.         /* should happen at end of a single slip packet */
  411.         if(iir & IIR_FIFO_TIMEOUT)
  412.             asyp->fifotimeouts++;
  413.     }
  414. }
  415.  
  416. /* Process 8250 receiver interrupts */
  417. static int
  418. asyrxint(asyp)
  419. struct asy *asyp;
  420. {
  421.     register struct fifo *fp;
  422.     unsigned base;
  423.     char c,lsr;
  424.     int cnt = 0;
  425.     int trigseen = 0;
  426.  
  427.     asyp->rxints++;
  428.     base = asyp->addr;
  429.     fp = &asyp->fifo;
  430.     for(;;){
  431.         lsr = inportb(base+LSR);
  432.         if(lsr & LSR_OE)
  433.             asyp->overrun++;
  434.  
  435.         if(lsr & LSR_DR){
  436.             asyp->rxchar++;
  437.             c = inportb(base+RBR);
  438.             if(asyp->trigchar == uchar(c))
  439.                 trigseen = 1;
  440.             /* If buffer is full, we have no choice but
  441.              * to drop the character
  442.              */
  443.             if(fp->cnt != fp->bufsize){
  444.                 *fp->wp++ = c;
  445.                 if(fp->wp >= &fp->buf[fp->bufsize])
  446.                     /* Wrap around */
  447.                     fp->wp = fp->buf;
  448.                 fp->cnt++;
  449.                 if(fp->cnt > fp->hiwat)
  450.                     fp->hiwat = fp->cnt;
  451.                 cnt++;
  452.             } else
  453.                 fp->overrun++;
  454.         } else
  455.             break;
  456.     }
  457.     if(cnt > asyp->rxhiwat)
  458.         asyp->rxhiwat = cnt;
  459.     if(trigseen)
  460.         psignal(fp,1);
  461.     return cnt;
  462. }
  463. /* Handle 8250 transmitter interrupts */
  464. static void
  465. asytxint(dev)
  466. int dev;
  467. {
  468.     register struct dma *dp;
  469.     register unsigned base;
  470.     register int count;
  471.     struct asy *asyp;
  472.  
  473.     asyp = &Asy[dev];
  474.     base = asyp->addr;
  475.     dp = &asyp->dma;
  476.     asyp->txints++;
  477.     if(!dp->flags){
  478.         /* "Shouldn't happen", but disable transmit
  479.          * interrupts anyway
  480.          */
  481.         clrbit(base+IER,IER_TxE);
  482.         return;    /* Nothing to send */
  483.     }
  484.     if(!(inportb(base+LSR) & LSR_THRE))
  485.         return;    /* Not really ready */
  486.  
  487.     /* If it's a 16550A, load up to 16 chars into the tx hw fifo 
  488.      * at once. With an 8250, it can be one char at most.
  489.      */
  490.     if(asyp->is_16550a){
  491.         count = min(dp->cnt,OUTPUT_FIFO_SIZE);
  492.  
  493.         /* 16550A: LSR_THRE will drop after the first char loaded
  494.          * so we can't look at this bit to determine if the hw fifo is
  495.          * full. There seems to be no way to determine if the tx fifo
  496.          * is full (any clues?). So we should never get here while the
  497.          * fifo isn't empty yet.
  498.          */
  499.         asyp->txchar += count;
  500.         dp->cnt -= count;
  501. #ifdef    notdef    /* This is apparently too fast for some chips */
  502.         dp->data = outbuf(base+THR,dp->data,count);
  503. #else
  504.         while(count-- != 0)
  505.             outportb(base+THR,*dp->data++);
  506. #endif
  507.         if(dp->cnt == 0){
  508.             dp->flags = 0;
  509.             /* Disable transmit interrupts */
  510.             clrbit(base+IER,IER_TxE);
  511.             psignal(asyp,1);
  512.         }
  513.     } else {    /* 8250 */
  514.         do {
  515.             asyp->txchar++;
  516.             outportb(base+THR,*dp->data++);
  517.  
  518.             if(--dp->cnt == 0){
  519.                 dp->flags = 0;
  520.                 /* Disable transmit interrupts */
  521.                 clrbit(base+IER,IER_TxE);
  522.                 psignal(asyp,1);
  523.                 break;
  524.             }
  525.         } while(inportb(base+LSR) & LSR_THRE);
  526.     }
  527. }
  528. int
  529. stxrdy(dev)
  530. int dev;
  531. {
  532.     return(!Asy[dev].dma.flags);
  533. }
  534. /* Handle 8250 modem status change */
  535. static void
  536. asymsint(dev)
  537. int dev;
  538. {
  539.     char msr;
  540.     unsigned base;
  541.     struct asy *asyp;
  542.     struct dma *dp;
  543.  
  544.     asyp = &Asy[dev];
  545.     base = asyp->addr;
  546.     msr = inportb(base+MSR); 
  547.     dp = &asyp->dma;
  548.  
  549.     /* Check status of modem control signals from remote host */
  550.     if (asyp->rlsd_line_control) {
  551.         if (msr & MSR_RLSD) {
  552.             /* RLSD is asserted */
  553.             if (asyp->rlsd_line_control == 1) {
  554.                 /* RLSD just went up */
  555.                 asyp->rlsd_line_control = 100;
  556.                 psignal(&(asyp->rlsd_line_control), 1);
  557.             }
  558.         } else {
  559.             /* RLSD not asserted */    
  560.             if (asyp->rlsd_line_control == 100) {
  561.                 /* RLSD just went down */
  562.                 asyp->rlsd_line_control = 1;
  563.                 psignal(&(asyp->rlsd_line_control), 1);
  564.             }
  565.         }
  566.     }
  567.     if (asyp->cts_flow_control) {
  568.         if(msr & MSR_CTS){
  569.             /* CTS now asserted, enable Transmit interrupts */
  570.             if(dp->flags)
  571.                     setbit(base+IER,IER_TxE);
  572.         } else {
  573.             /* CTS now dropped, disable Transmit interrupts */      
  574.             clrbit(base+IER,IER_TxE);
  575.         }
  576.     }
  577. }
  578. /* Wait for a signal that the RLSD modem status has changed */
  579. int
  580. get_rlsd_asy(dev, new_rlsd)
  581. int dev;
  582. int new_rlsd;
  583. {
  584.     struct asy *ap;
  585.     unsigned base;
  586.  
  587.     ap = &Asy[dev];
  588.     base = ap->addr;
  589.     if (new_rlsd == 0)
  590.         /* Just return the current value */
  591.         return(ap->rlsd_line_control);
  592.     if (ap->rlsd_line_control == new_rlsd)
  593.         /* Already at requested value */
  594.         return(new_rlsd);
  595.  
  596.     /* Wait for state change to requested value */
  597.     while (ap->rlsd_line_control != new_rlsd) {
  598.         pwait(&(ap->rlsd_line_control));
  599.         pause(2L);
  600.     }
  601.     if (ap->rlsd_line_control == 100) {
  602.         /* Enable receiver interrupts now */
  603.         setbit(base+IER,(char)IER_DAV);
  604.         /* Follow RLSD with RTS and DTR */
  605.         setbit(base+MCR,MCR_RTS);
  606.         setbit(base+MCR,MCR_DTR);
  607.     } else {
  608.         /* Disable receiver interrupts now */
  609.         clrbit(base+IER,(char)IER_DAV);
  610.         /* Follow RLSD with RTS and DTR */
  611.         clrbit(base+MCR,MCR_RTS);
  612.         clrbit(base+MCR,MCR_DTR);
  613.     }
  614.     return(ap->rlsd_line_control);
  615. }
  616.  
  617. /* Poll the asynch input queues; called on every clock tick.
  618.  * This helps limit the interrupt ring buffer occupancy when long
  619.  * packets are being received.
  620.  */
  621. void
  622. asytimer()
  623. {
  624.     register struct asy *asyp;
  625.     register struct fifo *fp;
  626.     register int i;
  627.  
  628.     for(i=0;i<ASY_MAX;i++){
  629.             asyp = &Asy[i];
  630.             fp = &asyp->fifo;
  631.         if(fp->cnt != 0)
  632.             psignal(fp,1);
  633.         if(asyp->dma.flags != 0 && (inportb(asyp->addr+LSR) & LSR_THRE) ) {
  634.             asyp->txto++;
  635.             asytxint(asyp->iface->dev);
  636.         }
  637.     }
  638. }
  639. int
  640. doasystat(argc,argv,p)
  641. int argc;
  642. char *argv[];
  643. void *p;
  644. {
  645.     register struct asy *asyp;
  646.  
  647.     for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
  648.         if(asyp->iface == NULLIF)
  649.             continue;
  650.         tprintf("%s:",asyp->iface->name);
  651.         if(asyp->is_16550a)
  652.             tprintf(" [NS16550A]");
  653.         if(asyp->trigchar != -1)
  654.             tprintf(" [trig char 0x%02x]",asyp->trigchar);
  655.         if(asyp->cts_flow_control)
  656.             tprintf(" [cts flow control]");
  657.         if(asyp->rlsd_line_control)
  658.             tprintf(" [rlsd line control]");
  659.         tprintf(" [%ld baud]",asyp->speed);
  660.         tprintf("\n");
  661.         tprintf(" RX: int %lu chr %lu hwovrn %lu hwhiwat %lu",
  662.          asyp->rxints,asyp->rxchar,asyp->overrun,asyp->rxhiwat);
  663.         asyp->rxhiwat = 0;
  664.         if(asyp->is_16550a)
  665.             tprintf(" fifotim %lu",asyp->fifotimeouts);
  666.         tprintf(" swovrn %lu swhiwat %u",asyp->fifo.overrun,
  667.          asyp->fifo.hiwat);
  668.         asyp->fifo.hiwat = 0;
  669.         tprintf("\n");
  670.         if(tprintf(" TX: int %lu chr %lu sndq %u CTS/RLSD int %lu THREtim %lu\n",
  671.          asyp->txints,asyp->txchar,len_q(asyp->sndq),
  672.          asyp->cts_flow_ints,asyp->txto) == EOF)
  673.             break;
  674.         /* Show more stats if SLIP and VJ TCP compression */
  675.         doslstat(asyp->iface);
  676.     }
  677.     return 0;
  678. }
  679. /* Send a message on the specified serial line */
  680. int
  681. asy_send(dev,bp)
  682. int dev;
  683. struct mbuf *bp;
  684. {
  685.     if(dev < 0 || dev >= ASY_MAX)
  686.         return -1;
  687.     enqueue(&Asy[dev].sndq,bp);
  688.     return 0;
  689. }
  690.  
  691. /* Serial transmit process, common to all protocols */
  692. void
  693. asy_tx(dev,p1,p2)
  694. int dev;
  695. void *p1;
  696. void *p2;
  697. {
  698.     register struct mbuf *bp;
  699.     struct asy *asyp;
  700.     struct dma *dp;
  701.     int i_state;
  702.  
  703.     asyp = &Asy[dev];
  704.     dp = &asyp->dma;
  705.  
  706.     for(;;){
  707.         /* Fetch a buffer for transmission */
  708.         while(asyp->sndq == NULLBUF)
  709.             pwait(&asyp->sndq);
  710.         bp = dequeue(&asyp->sndq);
  711.  
  712.         while(bp != NULLBUF){
  713.             /* Start the transmitter */
  714.             asy_output(dev,bp->data,bp->cnt);
  715.  
  716.             /* Wait for completion */    
  717.             i_state = dirps();
  718.             while(dp->flags == 1)
  719.                 pwait(asyp);
  720.             restore(i_state);
  721.  
  722.             /* Now do next buffer on chain */
  723.             bp = free_mbuf(bp);
  724.         }
  725.     }
  726. }
  727.