home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / s920603.zip / N8250.C < prev    next >
C/C++ Source or Header  |  1992-06-03  |  16KB  |  694 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.  * Feb '91      RLSD line control reorganized by Bill_Simpson@um.cc.umich.edu
  9.  * Sep '91      All control signals reorganized by Bill Simpson
  10.  * Apr '92    Control signals redone again by Phil Karn
  11.  */
  12. #include <stdio.h>
  13. #include <dos.h>
  14. #include "global.h"
  15. #include "mbuf.h"
  16. #include "proc.h"
  17. #include "iface.h"
  18. #include "n8250.h"
  19. #include "asy.h"
  20. #include "devparam.h"
  21. #include "pc.h"
  22.  
  23. static int asyrxint __ARGS((struct asy *asyp));
  24. static void asytxint __ARGS((int dev));
  25. static void asymsint __ARGS((int dev));
  26. static void pasy __ARGS((struct asy *asyp));
  27.  
  28. struct asy Asy[ASY_MAX];
  29.  
  30. /* ASY interrupt handlers */
  31. static INTERRUPT (*Handle[ASY_MAX])() = {
  32. asy0vec,asy1vec,asy2vec,asy3vec,asy4vec,asy5vec
  33. };
  34.  
  35. /* Initialize asynch port "dev" */
  36. int
  37. asy_init(dev,ifp,arg1,arg2,bufsize,trigchar,speed,cts,rlsd)
  38. int dev;
  39. struct iface *ifp;
  40. char *arg1,*arg2;    /* Attach args for address and vector */
  41. int16 bufsize;
  42. int trigchar;
  43. long speed;
  44. int cts;        /* Use CTS flow control */
  45. int rlsd;        /* Use Received Line Signal Detect (aka CD) */
  46. {
  47.     register unsigned base;
  48.     register struct fifo *fp;
  49.     register struct asy *ap;
  50.  
  51.     ap = &Asy[dev];
  52.     ap->iface = ifp;
  53.     ap->addr = htoi(arg1);
  54.     ap->vec = atoi(arg2);
  55.     if(strchr(arg2,'c') != NULLCHAR)
  56.         ap->chain = 1;
  57.     else
  58.         ap->chain = 0;
  59.  
  60.     /* Set up receiver FIFO */
  61.     fp = &ap->fifo;
  62.     fp->buf = mallocw(bufsize);
  63.     fp->bufsize = bufsize;
  64.     fp->wp = fp->rp = fp->buf;
  65.     fp->cnt = 0;
  66.     fp->hiwat = 0;
  67.     fp->overrun = 0;
  68.     base = ap->addr;
  69.     ap->trigchar = trigchar;
  70.  
  71.     /* Purge the receive data buffer */
  72.     (void)inportb(base+RBR);
  73.  
  74.     DISABLE();
  75.  
  76.     /* Save original interrupt vector, mask state, control bits */
  77.     ap->save.vec = getirq(ap->vec);
  78.     ap->save.mask = getmask(ap->vec);
  79.     ap->save.lcr = inportb(base+LCR);
  80.     ap->save.ier = inportb(base+IER);
  81.     ap->save.mcr = inportb(base+MCR);
  82.     ap->msr = ap->save.msr = inportb(base+MSR);
  83.  
  84.     /* save speed bytes */
  85.     setbit(base+LCR,LCR_DLAB);
  86.     ap->save.divl = inportb(base+DLL);
  87.     ap->save.divh = inportb(base+DLM);
  88.     clrbit(base+LCR,LCR_DLAB);
  89.  
  90.     /* save modem control flags */
  91.     ap->cts = cts;
  92.     ap->rlsd = rlsd;
  93.  
  94.     /* Set interrupt vector to SIO handler */
  95.     setirq(ap->vec,Handle[dev]);
  96.  
  97.     /* Set line control register: 8 bits, no parity */
  98.     outportb(base+LCR,(char)LCR_8BITS);
  99.  
  100.     /* determine if 16550A, turn on FIFO mode and clear RX and TX FIFOs */
  101.     outportb(base+FCR,(char) FIFO_ENABLE);
  102.  
  103.     /* According to National ap note AN-493, the FIFO in the 16550 chip
  104.      * is broken and must not be used. To determine if this is a 16550A
  105.      * (which has a good FIFO implementation) check that both bits 7
  106.      * and 6 of the IIR are 1 after setting the fifo enable bit. If
  107.      * not, don't try to use the FIFO.
  108.      */
  109.     if ((inportb(base+IIR) & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) {
  110.         ap->is_16550a = TRUE;
  111.         outportb(base+FCR,(char) FIFO_SETUP);
  112.     } else {
  113.         /* Chip is not a 16550A. In case it's a 16550 (which has a
  114.          * broken FIFO), turn off the FIFO bit.
  115.          */
  116.         outportb(base+FCR,(char)0);
  117.         ap->is_16550a = FALSE;
  118.     }
  119.  
  120.     /* Turn on receive interrupts and optionally modem interrupts;
  121.      * leave transmit interrupts off until we actually send data.
  122.      */
  123.     if(ap->rlsd || ap->cts)
  124.         outportb(base+IER,IER_MS|IER_DAV);
  125.     else
  126.         outportb(base+IER,IER_DAV);
  127.  
  128.     /* Turn on 8250 master interrupt enable (connected to OUT2) */
  129.     setbit(base+MCR,MCR_OUT2);
  130.  
  131.     /* Enable interrupt */
  132.     maskon(ap->vec);
  133.     RESTORE();
  134.  
  135.     asy_speed(dev,speed);
  136.  
  137.     return 0;
  138. }
  139.  
  140.  
  141. int
  142. asy_stop(ifp)
  143. struct iface *ifp;
  144. {
  145.     register unsigned base;
  146.     register struct asy *ap;
  147.  
  148.     ap = &Asy[ifp->dev];
  149.  
  150.     if(ap->iface == NULLIF)
  151.         return -1;    /* Not allocated */        
  152.     ap->iface = NULLIF;
  153.  
  154.     base = ap->addr;
  155.  
  156.     stop_timer(&ap->idle);    /* Stop the idle timer, if running */
  157.     (void)inportb(base+RBR); /* Purge the receive data buffer */
  158.  
  159.     if(ap->is_16550a){
  160.         /* Purge hardware FIFOs and disable. Apparently some
  161.          * other comm programs can't handle 16550s already in
  162.          * FIFO mode; they expect 16450 compatibility mode.
  163.          */
  164.         outportb(base+FCR,(char) FIFO_SETUP);
  165.         outportb(base+FCR,0);
  166.     }
  167.     /* Restore original interrupt vector and 8259 mask state */
  168.     DISABLE();
  169.     setirq(ap->vec,ap->save.vec);
  170.     if(ap->save.mask)
  171.         maskon(ap->vec);
  172.     else
  173.         maskoff(ap->vec);
  174.  
  175.     /* Restore speed regs */
  176.     setbit(base+LCR,LCR_DLAB);
  177.     outportb(base+DLL,ap->save.divl);    /* Low byte */
  178.     outportb(base+DLM,ap->save.divh);    /* Hi byte */
  179.     clrbit(base+LCR,LCR_DLAB);
  180.  
  181.     /* Restore control regs */
  182.     outportb(base+LCR,ap->save.lcr);
  183.     outportb(base+IER,ap->save.ier);
  184.     outportb(base+MCR,ap->save.mcr);
  185.  
  186.     RESTORE();
  187.     free(ap->fifo.buf);
  188.     return 0;
  189. }
  190.  
  191.  
  192. /* Set asynch line speed */
  193. int
  194. asy_speed(dev,bps)
  195. int dev;
  196. long bps;
  197. {
  198.     register unsigned base;
  199.     register long divisor;
  200.     struct asy *asyp;
  201.  
  202.     if(bps <= 0 || dev >= ASY_MAX)
  203.         return -1;
  204.     asyp = &Asy[dev];
  205.     if(asyp->iface == NULLIF)
  206.         return -1;
  207.  
  208.     if(bps == 0)
  209.         return -1;
  210.     asyp->speed = bps;
  211.  
  212.     base = asyp->addr;
  213.     divisor = BAUDCLK / bps;
  214.  
  215.     DISABLE();
  216.  
  217.     /* Purge the receive data buffer */
  218.     (void)inportb(base+RBR);
  219.     if (asyp->is_16550a)        /* clear tx+rx fifos */
  220.         outportb(base+FCR,(char) FIFO_SETUP);
  221.  
  222.     /* Turn on divisor latch access bit */
  223.     setbit(base+LCR,LCR_DLAB);
  224.  
  225.     /* Load the two bytes of the register */
  226.     outportb(base+DLL,(char)(divisor & 0xff));        /* Low byte */
  227.     outportb(base+DLM,(char)((divisor >> 8) & 0xff));    /* Hi byte */
  228.  
  229.     /* Turn off divisor latch access bit */
  230.     clrbit(base+LCR,LCR_DLAB);
  231.  
  232.     RESTORE();
  233.     return 0;
  234. }
  235.  
  236.  
  237. /* Asynchronous line I/O control */
  238. int32
  239. asy_ioctl(ifp,cmd,set,val)
  240. struct iface *ifp;
  241. int cmd;
  242. int set;
  243. int32 val;
  244. {
  245.     struct asy *ap = &Asy[ifp->dev];
  246.     int16 base = ap->addr;
  247.  
  248.     switch(cmd){
  249.     case PARAM_SPEED:
  250.         if(set)
  251.             asy_speed(ifp->dev,val);
  252.         return ap->speed;
  253.     case PARAM_DTR:
  254.         if(set) {
  255.             writebit(base+MCR,MCR_DTR,(int)val);
  256.         }
  257.         return (inportb(base+MCR) & MCR_DTR) ? TRUE : FALSE;
  258.     case PARAM_RTS:
  259.         if(set) {
  260.             writebit(base+MCR,MCR_RTS,(int)val);
  261.         }
  262.         return (inportb(base+MCR) & MCR_RTS) ? TRUE : FALSE;
  263.     case PARAM_DOWN:
  264.         clrbit(base+MCR,MCR_RTS);
  265.         clrbit(base+MCR,MCR_DTR);
  266.         return FALSE;
  267.     case PARAM_UP:
  268.         setbit(base+MCR,MCR_RTS);
  269.         setbit(base+MCR,MCR_DTR);
  270.         return TRUE;
  271.     }
  272.     return -1;
  273. }
  274.  
  275.  
  276. /* Send a buffer on the serial transmitter and wait for completion */
  277. int
  278. asy_write(dev,buf,cnt)
  279. int dev;
  280. char *buf;
  281. unsigned short cnt;
  282. {
  283.     register struct dma *dp;
  284.     unsigned base;
  285.     struct asy *asyp;
  286.     int tmp;
  287.  
  288.     if(dev < 0 || dev >= ASY_MAX)
  289.         return -1;
  290.     asyp = &Asy[dev];
  291.     if(asyp->iface == NULLIF)
  292.         return -1;
  293.  
  294.     base = asyp->addr;
  295.     dp = &asyp->dma;
  296.  
  297.     if(dp->busy)
  298.         return -1;    /* Already busy */
  299.  
  300.     dp->data = buf;
  301.     dp->cnt = cnt;
  302.     dp->busy = 1;
  303.  
  304.     /* If CTS flow control is disabled or CTS is true,
  305.      * enable transmit interrupts here so we'll take an immediate
  306.      * interrupt to get things going. Otherwise let the
  307.      * modem control interrupt enable transmit interrupts
  308.      * when CTS comes up. If we do turn on TxE,
  309.      * "kick start" the transmitter interrupt routine, in case just
  310.      * setting the interrupt enable bit doesn't cause an interrupt
  311.      */
  312.     if(!asyp->cts || (asyp->msr & MSR_CTS)){
  313.         setbit(base+IER,IER_TxE);
  314.         asytxint(dev);
  315.     }
  316.     /* Wait for completion */
  317.     for(;;){
  318.         DISABLE();
  319.         tmp = dp->busy;
  320.         RESTORE();
  321.         if(tmp == 0)
  322.             break;
  323.         pwait(&asyp->dma);
  324.     }
  325.     return cnt;
  326. }
  327.  
  328. /* Blocking read from asynch line
  329.  * Returns character or -1 if aborting
  330.  */
  331. int
  332. get_asy(dev)
  333. int dev;
  334. {
  335.     register struct fifo *fp;
  336.     char c;
  337.     int tmp;
  338.  
  339.     fp = &Asy[dev].fifo;
  340.  
  341.     for(;;){
  342.         /* Atomic read and decrement of fp->cnt */
  343.         DISABLE();
  344.         tmp = fp->cnt;
  345.         if(tmp != 0){
  346.             fp->cnt--;
  347.             RESTORE();
  348.             break;
  349.         }
  350.         RESTORE();
  351.         if(pwait(fp) != 0)
  352.             return -1;
  353.     }        
  354.     c = *fp->rp++;
  355.     if(fp->rp >= &fp->buf[fp->bufsize])
  356.         fp->rp = fp->buf;
  357.  
  358.     return uchar(c);
  359. }
  360.  
  361.  
  362. /* Interrupt handler for 8250 asynch chip (called from asyvec.asm) */
  363. INTERRUPT (far *(asyint)(dev))()
  364. int dev;
  365. {
  366.     struct asy *asyp;
  367.     unsigned base;
  368.     char iir;
  369.  
  370.     asyp = &Asy[dev];
  371.     base = asyp->addr;
  372.     while(((iir = inportb(base+IIR)) & IIR_IP) == 0){
  373.         switch(iir & IIR_ID_MASK){
  374.         case IIR_RDA:    /* Receiver interrupt */
  375.             asyrxint(asyp);
  376.             break;
  377.         case IIR_THRE:    /* Transmit interrupt */
  378.             asytxint(dev);
  379.             break;
  380.         case IIR_MSTAT:    /* Modem status change */
  381.             asymsint(dev);
  382.             asyp->msint_count++;
  383.             break;
  384.         }
  385.         /* should happen at end of a single packet */
  386.         if(iir & IIR_FIFO_TIMEOUT)
  387.             asyp->fifotimeouts++;
  388.     }
  389.     return asyp->chain ? asyp->save.vec : NULL;
  390. }
  391.  
  392.  
  393. /* Process 8250 receiver interrupts */
  394. static int
  395. asyrxint(asyp)
  396. struct asy *asyp;
  397. {
  398.     register struct fifo *fp;
  399.     unsigned base;
  400.     char c,lsr;
  401.     int cnt = 0;
  402.     int trigseen = FALSE;
  403.  
  404.     asyp->rxints++;
  405.     base = asyp->addr;
  406.     fp = &asyp->fifo;
  407.     for(;;){
  408.         lsr = inportb(base+LSR);
  409.         if(lsr & LSR_OE)
  410.             asyp->overrun++;
  411.  
  412.         if(lsr & LSR_DR){
  413.             asyp->rxchar++;
  414.             c = inportb(base+RBR);
  415.             if(asyp->trigchar == uchar(c))
  416.                 trigseen = TRUE;
  417.             /* If buffer is full, we have no choice but
  418.              * to drop the character
  419.              */
  420.             if(fp->cnt != fp->bufsize){
  421.                 *fp->wp++ = c;
  422.                 if(fp->wp >= &fp->buf[fp->bufsize])
  423.                     /* Wrap around */
  424.                     fp->wp = fp->buf;
  425.                 fp->cnt++;
  426.                 if(fp->cnt > fp->hiwat)
  427.                     fp->hiwat = fp->cnt;
  428.                 cnt++;
  429.             } else
  430.                 fp->overrun++;
  431.         } else
  432.             break;
  433.     }
  434.     if(cnt > asyp->rxhiwat)
  435.         asyp->rxhiwat = cnt;
  436.     if(trigseen)
  437.         psignal(fp,1);
  438.     return cnt;
  439. }
  440.  
  441.  
  442. /* Handle 8250 transmitter interrupts */
  443. static void
  444. asytxint(dev)
  445. int dev;
  446. {
  447.     register struct dma *dp;
  448.     register unsigned base;
  449.     register int count;
  450.     struct asy *asyp;
  451.  
  452.     asyp = &Asy[dev];
  453.     base = asyp->addr;
  454.     dp = &asyp->dma;
  455.     asyp->txints++;
  456.     if(!dp->busy || (asyp->cts && !(asyp->msr & MSR_CTS))){
  457.         /* These events "shouldn't happen". Either the
  458.          * transmitter is idle, in which case the transmit
  459.          * interrupts should have been disabled, or flow control
  460.          * is enabled but CTS is low, and interrupts should also
  461.          * have been disabled.
  462.          */
  463.         clrbit(base+IER,IER_TxE);
  464.         return;    /* Nothing to send */
  465.     }
  466.     if(!(inportb(base+LSR) & LSR_THRE))
  467.         return;    /* Not really ready */
  468.  
  469.     /* If it's a 16550A, load up to 16 chars into the tx hw fifo
  470.      * at once. With an 8250, it can be one char at most.
  471.      */
  472.     if(asyp->is_16550a){
  473.         count = min(dp->cnt,OUTPUT_FIFO_SIZE);
  474.  
  475.         /* 16550A: LSR_THRE will drop after the first char loaded
  476.          * so we can't look at this bit to determine if the hw fifo is
  477.          * full. There seems to be no way to determine if the tx fifo
  478.          * is full (any clues?). So we should never get here while the
  479.          * fifo isn't empty yet.
  480.          */
  481.         asyp->txchar += count;
  482.         dp->cnt -= count;
  483. #ifdef    notdef    /* This is apparently too fast for some chips */
  484.         dp->data = outbuf(base+THR,dp->data,count);
  485. #else
  486.         while(count-- != 0)
  487.             outportb(base+THR,*dp->data++);
  488. #endif
  489.     } else {    /* 8250 */
  490.         do {
  491.             asyp->txchar++;
  492.             outportb(base+THR,*dp->data++);
  493.         } while(--dp->cnt != 0 && (inportb(base+LSR) & LSR_THRE));
  494.     }
  495.     if(dp->cnt == 0){
  496.         dp->busy = 0;
  497.         /* Disable further transmit interrupts */
  498.         clrbit(base+IER,IER_TxE);
  499.         psignal(&asyp->dma,1);
  500.     }
  501. }
  502.  
  503. /* Handle 8250 modem status change
  504.  *    If the signals are unchanging, we ignore them.
  505.  *    If they change, we use them to condition the line.
  506.  */
  507. static void
  508. asymsint(dev)
  509. int dev;
  510. {
  511.     struct asy *ap = &Asy[dev];
  512.     unsigned base = ap->addr;
  513.  
  514.     ap->msr = inportb(base+MSR);
  515.  
  516.     if(ap->cts && (ap->msr & MSR_DCTS)){
  517.         /* CTS has changed and we care */
  518.         if(ap->msr & MSR_CTS){
  519.             /* CTS went up */
  520.             if(ap->dma.busy){
  521.                 /* enable transmit interrupts and kick */
  522.                 setbit(base+IER,IER_TxE);
  523.                 asytxint(dev);
  524.             }
  525.         } else {
  526.             /* CTS now dropped, disable Transmit interrupts */
  527.             clrbit(base+IER,IER_TxE);
  528.         }
  529.     }
  530.     if(ap->rlsd && (ap->msr & MSR_DRLSD)){
  531.         /* RLSD just changed and we care, signal it */
  532.         psignal( &(ap->rlsd), 1 );
  533.         /* Keep count */
  534.         if(ap->msr & MSR_RLSD)
  535.             ap->answers++;
  536.         else
  537.             ap->remdrops++;
  538.     }
  539.     if(ap->msr & (MSR_TERI | MSR_RI)){
  540.         asy_ioctl(ap->iface, PARAM_UP, TRUE, 0L);
  541.     }
  542. }
  543.  
  544. /* Wait for a signal that the RLSD modem status has changed */
  545. int
  546. get_rlsd_asy(dev, new_rlsd)
  547. int dev;
  548. int new_rlsd;
  549. {
  550.     struct asy *ap = &Asy[dev];
  551.  
  552.     if(ap->rlsd == 0)
  553.         return -1;
  554.  
  555.     for(;;){
  556.         if(new_rlsd && (ap->msr & MSR_RLSD))
  557.             return 1;
  558.         if(!new_rlsd && !(ap->msr & MSR_RLSD))
  559.             return 0;
  560.  
  561.         /* Wait for state change to requested value */
  562.         pause(2L);
  563.         pwait( &(ap->rlsd) );
  564.     }
  565. }
  566.  
  567. /* Poll the asynch input queues; called on every clock tick.
  568.  * This helps limit the interrupt ring buffer occupancy when long
  569.  * packets are being received.
  570.  */
  571. void
  572. asytimer()
  573. {
  574.     register struct asy *asyp;
  575.     register struct fifo *fp;
  576.     register int i;
  577.  
  578.     for(i=0;i<ASY_MAX;i++){
  579.         asyp = &Asy[i];
  580.         fp = &asyp->fifo;
  581.         if(fp->cnt != 0)
  582.             psignal(fp,1);
  583.         if(asyp->dma.busy
  584.          && (inportb(asyp->addr+LSR) & LSR_THRE)
  585.          && (!asyp->cts || (asyp->msr & MSR_CTS))){
  586.             asyp->txto++;
  587.             DISABLE();
  588.             asytxint(asyp->iface->dev);
  589.             RESTORE();
  590.         }
  591.     }
  592. }
  593. int
  594. doasystat(argc,argv,p)
  595. int argc;
  596. char *argv[];
  597. void *p;
  598. {
  599.     register struct asy *asyp;
  600.     struct iface *ifp;
  601.     int i;
  602.  
  603.     if(argc < 2){
  604.         for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
  605.             if(asyp->iface != NULLIF)
  606.                 pasy(asyp);
  607.         }
  608.         return 0;
  609.     }
  610.     for(i=1;i<argc;i++){
  611.         if((ifp = if_lookup(argv[i])) == NULLIF){
  612.             printf("Interface %s unknown\n",argv[i]);
  613.             continue;
  614.         }
  615.         for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
  616.             if(asyp->iface == ifp){
  617.                 pasy(asyp);
  618.                 break;
  619.             }
  620.         }
  621.         if(asyp == &Asy[ASY_MAX])
  622.             printf("Interface %s not asy\n",argv[i]);
  623.     }
  624.  
  625.     return 0;
  626. }
  627.  
  628. static void
  629. pasy(asyp)
  630. struct asy *asyp;
  631. {
  632.     int mcr;
  633.  
  634.     printf("%s:",asyp->iface->name);
  635.     if(asyp->is_16550a)
  636.         printf(" [NS16550A]");
  637.     if(asyp->trigchar != -1)
  638.         printf(" [trigger 0x%02x]",asyp->trigchar);
  639.     if(asyp->cts)
  640.         printf(" [cts flow control]");
  641.     if(asyp->rlsd)
  642.         printf(" [rlsd line control]");
  643.  
  644.     printf(" %lu bps\n",asyp->speed);
  645.  
  646.     mcr = inportb(asyp->addr+MCR);
  647.     printf(" MC: int %lu DTR %s  RTS %s  CTS %s  DSR %s  RI %s  CD %s\n",
  648.      asyp->msint_count,
  649.      (mcr & MCR_DTR) ? "On" : "Off",
  650.      (mcr & MCR_RTS) ? "On" : "Off",
  651.      (asyp->msr & MSR_CTS) ? "On" : "Off",
  652.      (asyp->msr & MSR_DSR) ? "On" : "Off",
  653.      (asyp->msr & MSR_RI) ? "On" : "Off",
  654.      (asyp->msr & MSR_RLSD) ? "On" : "Off");
  655.     
  656.     printf(" RX: int %lu chars %lu hw over %lu hw hi %lu",
  657.      asyp->rxints,asyp->rxchar,asyp->overrun,asyp->rxhiwat);
  658.     asyp->rxhiwat = 0;
  659.     if(asyp->is_16550a)
  660.         printf(" fifo TO %lu",asyp->fifotimeouts);
  661.     printf(" sw over %lu sw hi %u\n",
  662.      asyp->fifo.overrun,asyp->fifo.hiwat);
  663.     asyp->fifo.hiwat = 0;
  664.  
  665.     printf(" TX: int %lu chars %lu THRE TO %lu%s\n",
  666.      asyp->txints,asyp->txchar,asyp->txto,
  667.      asyp->dma.busy ? " BUSY" : "");
  668. }
  669. /* Send a message on the specified serial line */
  670. int
  671. asy_send(dev,bp)
  672. int dev;
  673. struct mbuf *bp;
  674. {
  675.     struct asy *asyp;
  676.  
  677.     if(dev < 0 || dev >= ASY_MAX){
  678.         free_p(bp);
  679.         return -1;
  680.     }
  681.     asyp = &Asy[dev];
  682.  
  683.     dialer_kick(asyp);
  684.     while(bp != NULLBUF){
  685.         /* Send the buffer */
  686.         asy_write(dev,bp->data,bp->cnt);
  687.         /* Now do next buffer on chain */
  688.         bp = free_mbuf(bp);
  689.     }
  690.     start_timer(&asyp->idle);    /* Restart idle timeout */
  691.     return 0;
  692. }
  693.  
  694.