home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / communic / asycnch / asynch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-01  |  12.1 KB  |  471 lines

  1. /*
  2.  * ASYCNCH.C A simple, low-level, interrupt driven comm driver for
  3.  * Turbo/Borland C. No assembly required!
  4.  *
  5.  * (C) Copyright 1991 Steve Resnick, Asylum Software.
  6.  * License granted for personal or educational use.
  7.  * Commercial use licensed only through registration with the author.
  8.  *
  9.  * Steve Resnick - 530 Lawrence Expressway, Box 374, Sunnyvale, Ca 94086
  10.  * resnicks@netcom.com, steve@apple!camphq,
  11.  * steve.resnick@f105.n143.z1.FIDONET.ORG
  12. */
  13. #include <stdio.h>
  14. #include <dos.h>
  15. #include "asynch.h"
  16. /*
  17.  * This table is used to translate a BPS rate to a baud rate divisor for the
  18.  * UART
  19. */
  20. struct _baud
  21. {
  22.     word    Rate, Divisor;
  23. } BaudTable[] =
  24. {
  25.     110,1040,150,768,300,384,600,192,1200,96,2400,48,4800,24,9600,12,
  26.     19200,6,        /* I have no idea if this works */
  27. } ;
  28. #if DEBUG
  29. byte PICVals[2][2];        /* Mirror PIC values to these arrays */
  30. byte DataTable[2][20];        /* Mirror UART values to these arrays */
  31. byte portinb(word port);    /* Use our own port I/O functions */
  32. void portoutb(word port, byte data);
  33. #else
  34. #define portinb(p) inportb(p)
  35. #define portoutb(p,b) outportb(p,b)
  36. #endif
  37. uart cports[4] = {COM1_DEFAULTS,COM2_DEFAULTS};
  38.  
  39. /*
  40.  * SetPortParms sets up the UART line parameters and baud rate.
  41.  * If Base, IRQ, and BreakLength are specified as 0, defaults are used.
  42.  * (See ASYNCH.H)
  43.  * This may only be called for an open port.
  44. */
  45. int SetPortParms(int PortNo, int Baud, int Parity, int Data, int Stop,
  46.          int Base, int IRQ, int BreakLength)
  47. {
  48.     int i, b;
  49.     if (PortNo > 3 || PortNo < 0)
  50.         return EINVPORT;
  51.     if (cports[PortNo].status & PS_ENABLED == 0)
  52.         return EPORTNOTOPEN;
  53.         
  54.     if (Base)
  55.         cports[PortNo].addr = Base;
  56.     if (IRQ)
  57.         cports[PortNo].irqline = IRQ;
  58.     if (BreakLength)
  59.         cports[PortNo].break_length = BreakLength;
  60.  
  61.     cports[PortNo].parms = 0;
  62.     cports[PortNo].parms |= Data;
  63.     cports[PortNo].parms |= Stop;
  64.     cports[PortNo].parms |= Parity;
  65.     if (Baud != 0)
  66.     {
  67.         for (i = 0, b = -1; i < dim(BaudTable); i++)
  68.             if (BaudTable[i].Rate == Baud)
  69.                 b = i;
  70.         if (b == -1)
  71.             return EINVBAUDRATE;
  72.  
  73.         cports[PortNo].baud = BaudTable[b].Divisor;
  74.         InitBaud(PortNo);
  75.     }
  76.     else if (cports[PortNo].baud == 0)
  77.         return EINVBAUDRATE;
  78.     portoutb(cports[PortNo].addr+LCR_REG,cports[PortNo].parms);
  79.     return 0;
  80. }
  81. /*
  82.  * copen opens a comm port spcified by PortNo. Input and Output buffers are
  83.  * allocated according to the BufSiz parameter. 
  84.  * If Base, IRQ, and BreakLength are specified as 0, defaults are used.
  85.  * (See ASYNCH.H)
  86.  *
  87.  * When the port is opened, the following takes place:
  88.  * Buffer memory is allocated
  89.  * CommPort options are set
  90.  * The interrupt vector is set based on IRQ+8
  91.  * The 8259 is enabled to recieve interrupts.
  92.  * The 8250 IE mask is set
  93.  * The 8250 OUT2 is asserted, enabling the interrupt line from the UART to the
  94.  * PIC.
  95. */
  96. int copen(int PortNo, int BufSiz, int Baud, int Parity, int Data, int Stop,
  97.           int Base, int IRQ, int BreakLength)
  98. {
  99.     uart * Cp;
  100.     int Rc;
  101.     if (PortNo > 3 || PortNo < 0)
  102.         return EINVPORT;
  103.     if (cports[PortNo].status & PS_ENABLED)
  104.         return EPORTALREADYOPEN;
  105.     Cp = &cports[PortNo];
  106.     if ((Cp->InBuffer = Qalloc(BufSiz)) == NULL)
  107.         return EMEMALLOC;
  108.     if ((Cp->OutBuffer = Qalloc(BufSiz)) == NULL)
  109.     {
  110.         Qfree(Cp->InBuffer);
  111.         return EMEMALLOC;
  112.     }
  113.     Cp->status |= PS_ENABLED;
  114.     Rc = SetPortParms(PortNo,Baud,Parity,Data,Stop,Base,IRQ,BreakLength);
  115.     if (Rc)
  116.     {
  117.         Qfree(Cp->InBuffer);
  118.         Qfree(Cp->OutBuffer);
  119.         Cp->status = 0;
  120.         return Rc;
  121.     }
  122.     SetIntVector(PortNo);
  123.     disable();
  124.     SetPIC(PortNo,1);
  125.     SetUARTIe(PortNo);
  126.     enable();
  127.     ControlDTR(PortNo,ON);
  128.     return 0;
  129. }
  130. /*
  131.  * cclose closes a serial port by disabling interrupts on the UART, then on
  132.  * the PIC and finally, returns the original vector to the IVT.
  133. */
  134. int cclose(int PortNo)
  135. {
  136.     uart * Cp;
  137.     int Rc;
  138.     if (PortNo > 3 || PortNo < 0)
  139.         return EINVPORT;
  140.     if (cports[PortNo].status & PS_ENABLED == 0)
  141.         return EPORTNOTOPEN;
  142.     Cp = &cports[PortNo];
  143.     disable();
  144.     ClrIntVector(PortNo);
  145.     SetPIC(PortNo,0);
  146.     enable();
  147.     Cp->status = 0;
  148.     Qfree(Cp->InBuffer);
  149.     Qfree(Cp->OutBuffer);
  150.     portoutb(Cp->addr + IE_REG,0);
  151.     portoutb(Cp->addr + MCR_REG,1);
  152.     return 0;
  153. }
  154. /*
  155.  * InitBaud sets the baudrate on the UART by setting the divisor latch access
  156.  * bit (DLAB) then writing the divisor's low byte,then the divisors high byte.
  157.  * For baudrates >= 600 BPS, the high byte should be 0. (See ASYNCH.H)
  158. */
  159. int InitBaud(int PortNo)
  160. {
  161.     uart * Cp;
  162.     byte bdata;
  163.     if (PortNo > 3 || PortNo < 0)
  164.         return EINVPORT;
  165.     if (cports[PortNo].status & PS_ENABLED == 0)
  166.         return EPORTNOTOPEN;
  167.     Cp = &cports[PortNo];
  168.     bdata = portinb(Cp->addr + LCR_REG);
  169.     portoutb(Cp->addr + LCR_REG,bdata | DLAB);
  170.     portoutb(Cp->addr + DIV_LOW,LOBYTE(Cp->baud));
  171.     portoutb(Cp->addr + DIV_HI, HIBYTE(Cp->baud));
  172.     portoutb(Cp->addr + LCR_REG,Cp->parms);
  173.     return 0;
  174. }
  175. /*
  176.  * SetPIC sets or clears the interrupt enable mask on the PIC for a
  177.  * particular interrupt. This function will work for both the master
  178.  * and the slave 8259A's in an AT.
  179. */
  180. SetPIC(int PortNo, int State)
  181. {
  182. static    int States[] = {-1,-1,-1,-1};
  183.     int PICAddr = PIC;
  184.     byte mask;
  185.     if (PortNo > 3 || PortNo < 0)
  186.         return EINVPORT;
  187.     if(State == 0 && States[PortNo] == -1)
  188.         return -1;
  189.     if (cports[PortNo].irqline > 7)
  190.     {
  191.         mask = ( 1 << (cports[PortNo].irqline - 8));
  192.         PICAddr = 0xA0;
  193.     }
  194.     else
  195.         mask = (1 << cports[PortNo].irqline);
  196.  
  197.     if (State == 0)
  198.     {
  199.         States[PortNo] = portinb(PIC+1);
  200.         portoutb(PICAddr+1,(byte)States[PortNo]|mask);
  201.         return 0;
  202.     }
  203.     mask = ~mask;    
  204.     States[PortNo] = portinb(PICAddr+1);
  205.     portoutb(PICAddr+1,States[PortNo] & mask);
  206.     portinb(PICAddr+1);
  207.     return 0;
  208. }
  209. /*
  210.  * EOI Sends a non-specific end of interrupt to the 8259A specific to the
  211.  * IRQ line of a com port
  212. */
  213. int EOI(int PortNo)
  214. {
  215.     if (PortNo > 3 || PortNo < 0)
  216.         return EINVPORT;
  217.     if (cports[PortNo].status & PS_ENABLED == 0)
  218.         return EPORTNOTOPEN;
  219.     if (cports[PortNo].irqline > 7)
  220.         portoutb(0xA0,0x20);
  221.     else
  222.         portoutb(0x20,0x20);
  223.     return 0;
  224. }
  225. /*
  226.  * This is the interrupt handler for ALL comm ports. Each ISR for a comm
  227.  * port calls this routine passing the index into the UART definition array
  228.  * so that values may be extracted for things like UART address, etc.
  229.  *
  230.  * This is a re-entrant function, although interrupts are disabled to ensure
  231.  * that interrupts are handled one at a time for each UART. This may be
  232.  * over-kill and may need to be changed in the future.
  233. */
  234. void IsrHandler(int PortNo)
  235. {
  236.     uart * Cp;
  237.     byte Idv, IIDvalue=0;
  238.     disable();
  239.     Cp = &cports[PortNo];
  240.     IIDvalue = portinb(Cp->addr+ IID_REG);
  241.     if ((IIDvalue & IID_PENDING) == 0)
  242.     {
  243.         IIDvalue &= IID_MASK ;
  244.         Idv = IIDvalue >> 1;
  245.         if (Idv & IID_DATA)
  246.             QinsertChar(Cp->InBuffer,portinb(Cp->addr));
  247.  
  248.         if (Idv & IID_TEMPTY)
  249.             DrainOutQueue(PortNo);
  250.  
  251.         if (Idv & IID_LSTAT)
  252.             Cp->lstat = portinb(Cp->addr+ LSR_REG);
  253.  
  254.         if (Idv & IID_MSTAT)
  255.             Cp->mstat = portinb(Cp->addr+ MSR_REG);
  256.     
  257.         portinb(Cp->addr+5);
  258.         portinb(Cp->addr+6);
  259.     } 
  260.     enable();
  261.     SetUartOUT2(PortNo);
  262.     EOI(PortNo);
  263. }
  264. /*
  265.  * Dummy ISR's
  266.  * Each comm port needs to have it's own ISR, but we need only one real
  267.  * handler. Since an ISR shared my multiple interrupts cannot determine
  268.  * it's source, we set up these dummy ISR's to call IsrHandler specifying
  269.  * the port which needs service.
  270. */
  271. void interrupt com1isr()
  272. {
  273.     IsrHandler(0);
  274. }
  275. void interrupt com2isr()
  276. {
  277.     IsrHandler(1);
  278. }
  279. void interrupt com3isr()
  280. {
  281.     IsrHandler(2);
  282. }
  283. void interrupt com4isr()
  284. {
  285.     IsrHandler(3);
  286. }
  287. /*
  288.  * SetIntVector sets the appropriate interrupt vector (correctly for both
  289.  * (master and slaved IRQ's) based on the IRQ. The original vector is saved
  290.  * in the UART structure so that it may be reset upon termination.
  291. */
  292. SetIntVector(int PortNo)
  293. {
  294.     int NewInt;
  295. static    void interrupt (*isrs[])() = {com1isr,com2isr,com3isr,com4isr};
  296.     if (PortNo > 3 || PortNo < 0)
  297.         return EINVPORT;
  298.     if (cports[PortNo].status & PS_ENABLED == 0)
  299.         return EPORTNOTOPEN;
  300.     cports[PortNo].oldisr = getvect(IRQINT(cports[PortNo].irqline));
  301.     NewInt = IRQINT(cports[PortNo].irqline);
  302.     setvect(NewInt,isrs[PortNo]);
  303.     return 0;
  304. }
  305. /*
  306.  * ClrIntVector revectors interrupts back to the original vector before we
  307.  * loaded up.
  308. */
  309. ClrIntVector(int PortNo)
  310. {
  311.     if (PortNo > 3 || PortNo < 0)
  312.         return EINVPORT;
  313.     if (cports[PortNo].status & PS_ENABLED == 0)
  314.         return EPORTNOTOPEN;
  315.     setvect(IRQINT(cports[PortNo].irqline),cports[PortNo].oldisr);
  316.     return 0;
  317. }
  318. /*
  319.  * SetUARTIe sets the interrupt enable mask on the UART.
  320.  * SetUartOUT2 is then called to unmask interrupts from the UART to the PIC
  321. */
  322. SetUARTIe(int PortNo)
  323. {
  324.     int i;
  325.     word IntFlags = IIV_LSTAT | IIV_RDATA | IIV_MSTAT | IIV_XMITE;
  326.     if (PortNo > 3 || PortNo < 0)
  327.         return EINVPORT;
  328.     if (cports[PortNo].status & PS_ENABLED == 0)
  329.         return EPORTNOTOPEN;
  330.     for (i = 5; i < 8; i++)
  331.         inportb(cports[PortNo].addr+i);
  332.  
  333.     portoutb(cports[PortNo].addr + IE_REG, IntFlags);
  334.     SetUartOUT2(PortNo);
  335.     return 0;
  336. }
  337. /*
  338.  * SetUARTOut2 set's the OUT2 line on the UART high, allowing interrupts
  339.  * to pass from the UART to the PIC (thanks, IBM)
  340. */
  341. SetUartOUT2(int PortNo)
  342. {
  343.     byte IoData;    
  344.     IoData = portinb(cports[PortNo].addr + MCR_REG);
  345.     portoutb(cports[PortNo].addr + MCR_REG,IoData | UARTIEMASK);
  346.     return 0;
  347. }
  348. /*
  349.  * DrainOutQueue reads characters from the output queue to the UART, as
  350.  * long as the UART is ready for data. If it is not ready, we return
  351.  * immediately.
  352. */
  353. DrainOutQueue(int PortNo)
  354. {
  355.     byte IoData;
  356.     char Ch;
  357.     if (PortNo > 3 || PortNo < 0)
  358.         return EINVPORT;
  359.     if (cports[PortNo].status & PS_ENABLED == 0)
  360.         return EPORTNOTOPEN;
  361.  
  362.     while(1)
  363.     {
  364.         IoData = portinb(cports[PortNo].addr + LSR_REG);
  365.         if (IoData & 0x20 || IoData & 0x40)
  366.         {
  367.             Ch = QreadChar(cports[PortNo].OutBuffer);
  368.             if (Ch == -1)
  369.                 break ;
  370.             portoutb(cports[PortNo].addr,Ch);
  371.             continue ;
  372.         }
  373.         break ;
  374.     }
  375.     return 0;
  376. }
  377. /*****************************************************************************
  378.  ****************************** User Functions ******************************/
  379. /*
  380.  * cgetc reads a character from the input queue and returns it, or -1 if no
  381.  * character is available.
  382. */ 
  383. cgetc(int PortNo)
  384. {
  385.     if (PortNo > 3 || PortNo < 0)
  386.         return -10 - EINVPORT;
  387.     if (cports[PortNo].status & PS_ENABLED == 0)
  388.         return -10 - EPORTNOTOPEN;
  389.     return QreadChar(cports[PortNo].InBuffer);
  390. }
  391. /*
  392.  * cputc writes a character to the output queue
  393. */
  394. cputc(int PortNo, char Ch)
  395. {
  396.     if (PortNo > 3 || PortNo < 0)
  397.         return -10 - EINVPORT;
  398.     if (cports[PortNo].status & PS_ENABLED == 0)
  399.         return -10 - EPORTNOTOPEN;
  400.  
  401.     QinsertChar(cports[PortNo].OutBuffer,Ch);
  402.     DrainOutQueue(PortNo);
  403.     return 0;
  404. }
  405. /*
  406.  * SendBreak sets a break condition on the serial line and holds it
  407.  * for break_lenght miliseconds
  408. */
  409. int SendBreak(int PortNo)
  410. {
  411.     byte IoData;
  412.     if (PortNo > 3 || PortNo < 0)
  413.         return EINVPORT;
  414.     if (cports[PortNo].status & PS_ENABLED == 0)
  415.         return EPORTNOTOPEN;
  416.     IoData = portinb(cports[PortNo].addr + LCR_REG);
  417.     portoutb(cports[PortNo].addr + LCR_REG,IoData | 0x40);
  418.     delay(cports[PortNo].break_length);
  419.     portoutb(cports[PortNo].addr + LCR_REG,IoData);
  420.     return 0;    
  421. }
  422. /*
  423.  * ControlDTR raises or lowers DTR on a specific port
  424. */
  425. int ControlDTR(int PortNo, int State)
  426. {
  427.     byte IoData;
  428.     if (PortNo > 3 || PortNo < 0)
  429.         return EINVPORT;
  430.     if (cports[PortNo].status & PS_ENABLED == 0)
  431.         return EPORTNOTOPEN;
  432.     IoData = portinb(cports[PortNo].addr + MCR_REG);
  433.     if (State == ON)
  434.         portoutb(cports[PortNo].addr + MCR_REG,(byte)(IoData | 1));
  435.     else 
  436.         portoutb(cports[PortNo].addr + MCR_REG,(byte)(IoData & ~1)&0xFF);
  437.     return 0;
  438. }
  439. /******************************************************************************
  440. ****************************** Debugging Functions ***************************/
  441. #if DEBUG
  442. void portoutb(word port, byte data)
  443. {
  444.     int Idx = port - 0x3f8;
  445.     if (Idx > -1 && Idx < 20)
  446.         DataTable[0][Idx] = data;
  447.     if (port >0x1F && port < 0x22)
  448.     {
  449.         Idx = port - 0x20;
  450.         PICVals[0][Idx] = data;
  451.     }
  452.     outportb(port,data);
  453. }
  454. byte portinb(word port)
  455. {
  456.     int Idx = port - 0x3F8;
  457.     byte Data;
  458.     Data = inportb(port);
  459.     if (port >0x1F && port < 0x22)
  460.     {
  461.         Idx = port - 0x20;
  462.         PICVals[1][Idx] = Data;
  463.     }
  464.     else if (Idx > -1 && Idx < 20)
  465.         DataTable[1][Idx] = Data;
  466.     return Data;
  467. }
  468. #endif
  469.  
  470.     
  471.