home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / c / scsidrvr.arc / scsi.c < prev    next >
C/C++ Source or Header  |  1989-01-06  |  7KB  |  410 lines

  1.  
  2.  
  3. #include <stdio.h>
  4. #include <dos.h>
  5. #include "scsi.h"
  6.  
  7.  
  8. /* This is the host adapter address */
  9. #define SCSIPORT 0x320
  10.  
  11. /* These are the variables that hold the data for the scsiop routine*/
  12.  
  13. static char busid;
  14. static char far *cptr; 
  15. static char far *dptr;
  16. static unsigned dlen;
  17. static unsigned adlen;
  18.  
  19. static struct scsibuf *curbuf;
  20.  
  21. static char rwflag;
  22. static int status;
  23. static int message;
  24.  
  25.     char scsimsg[80];
  26.  
  27. /* Following are the commands for SCSI operations */
  28.  
  29. void
  30. scsiop(req)
  31. struct scsireq *req;
  32. {
  33.     int s;
  34.     long timeout;
  35.     long maxtimeout;
  36.     long contimeout;
  37.     int statuscnt;
  38.  
  39.     cptr = req->cptr;
  40.     dptr = req->dptr;
  41.     dlen = req->dlen;
  42.     busid = req->busid;
  43.  
  44.     req->error = 0;
  45.  
  46.     if (req->timeout > 0)
  47.     {
  48.         maxtimeout = req->timeout * 5000L;
  49.         contimeout = req->timeout * 5000L;
  50.     }
  51.     else
  52.     {
  53.         maxtimeout = 100 * 5000L;  /* 10 second default */
  54.         contimeout = 100 * 5000L;
  55.     }
  56.  
  57.     dma_connect();   /* tell controller to obey dma chip */
  58.  
  59.     if (connect(contimeout) != 0)     /* select the controller on the scsi bus */
  60.     {
  61.         req->error |= S_BUSERROR | S_NOCONNECT;
  62.         return;
  63.     }
  64.  
  65.     statuscnt = 0;
  66.  
  67.     for (;;)
  68.     {
  69.         timeout = maxtimeout;
  70.         while (((s = inportb(SCSIPORT+1)) & 0x80) == 0)
  71.         {
  72.             if (timeout-- == 0)
  73.                 goto timedout;
  74.  
  75.             if ((s & 0x10) == 0)
  76.                 goto nomore;        /* No longer connected */
  77.         }
  78.  
  79.         switch (s & 0x68)
  80.         {
  81.         case 0x40:    /* Get data */
  82.             if (dlen <= 0)
  83.             {
  84.                 req->error |= S_BUSERROR | S_OVERRUN;
  85.                 inportb(SCSIPORT);
  86.             }
  87.             else
  88.             {
  89.                 rwflag = 0;
  90.                 do_dma();
  91.             }
  92.             break;
  93.  
  94.         case 0x00:    /* Put data */
  95.             if (dlen <= 0)
  96.             {
  97.                 req->error |= S_BUSERROR | S_OVERRUN;
  98.                 outportb(SCSIPORT, 0);
  99.             }
  100.             else
  101.             {
  102.                 rwflag = 1;
  103.                 do_dma();
  104.             }
  105.             break;
  106.  
  107.         case 0x20:    /* Put command */
  108.             outportb(SCSIPORT, *cptr++);
  109.             short_delay();
  110.             break;
  111.  
  112.         case 0x60:    /* Get status */
  113.             status = inportb(SCSIPORT);
  114.             statuscnt++;
  115.             req->error |= (status & 0xff);
  116.             short_delay();
  117.             break;
  118.  
  119.         case 0x68:    /* Get message */
  120.             message = inportb(SCSIPORT);
  121.             if (message != 0)
  122.                 req->error |= S_BUSERROR | S_BADMESSAGE;
  123.             short_delay();
  124.             break;
  125.  
  126.         default:
  127.             req->error |= S_BUSERROR | S_BADTRANS;
  128.             if (s & 0x40)
  129.                 inportb(SCSIPORT);
  130.             else
  131.                 outportb(SCSIPORT, 0);
  132.             short_delay();
  133.             break;
  134.         }
  135.  
  136.     }
  137.  
  138. nomore:
  139.     if (statuscnt != 1)
  140.         req->error |= S_BUSERROR | S_BADSTATUS;
  141.     outportb(SCSIPORT, 0);    /* Turn off data bus lines */
  142.     return;
  143.  
  144. timedout:
  145.     req->error |= S_BUSERROR | S_TIMEOUT;
  146.     outportb(SCSIPORT, 0);    /* Turn off data bus lines */
  147.     return;
  148. }
  149.  
  150.  
  151.  
  152. /* This waits until REQ is deasserted.  It will time out after a while */
  153. short_delay()
  154. {
  155.     int j;
  156.  
  157.     j = 200;
  158.     while (j--)
  159.     {
  160.         inportb(SCSIPORT+1);
  161.     }
  162. }
  163.  
  164.  
  165. do_dma()
  166. {
  167.     /* If the data buffer crosses a 64k boundary, it may take
  168.     more than 1 operation to get it. Thus the loop */
  169.  
  170.     while (dlen > 0)
  171.     {
  172.         setup_dma();
  173.         arm_dma();
  174.         if (dma_wait() < 0)        /* wait for actual i/o */
  175.             break;
  176.         disarm_dma();
  177.     }
  178. }
  179.  
  180.  
  181. do_vdma()
  182. {
  183.     /* If the data buffer crosses a 64k boundary, it may take
  184.     more than 1 operation to get it. Thus the loop */
  185.  
  186.     /* printf((CHARPTR)"        Doing vdma with curbuf: %Fp curbuf->dptr: %Fp\n", curbuf,  curbuf->dptr); */
  187.  
  188.     while (curbuf->dptr != NULL)
  189.     {
  190.         dptr = curbuf->dptr;
  191.         dlen = curbuf->dlen;
  192.         curbuf++;
  193.  
  194.         /* printf((CHARPTR)"        Doing a VDMA iteration from %Fp of %d bytes\n", dptr, dlen); */
  195.  
  196.         while (dlen > 0)
  197.         {
  198.             setup_dma();
  199.             arm_dma();
  200.             if (dma_wait() < 0)        /* wait for actual i/o */
  201.                 break;
  202.             disarm_dma();
  203.         }
  204.     }
  205. }
  206.  
  207.  
  208. /* This resets the scsi bus: */
  209.  
  210. reset_scsi()
  211. {
  212.     static int cnt;
  213.  
  214.     cnt = 256;
  215.     while (cnt-- > 0)
  216.     {
  217.         outportb(SCSIPORT+1,0);  /* was al */
  218.         inportb(SCSIPORT+2);
  219.         if ((inportb(SCSIPORT+1) & 0xfc) == 0)
  220.             return (0);
  221.     };
  222.     return (-1);
  223. }
  224.  
  225.  
  226.  
  227.  
  228. /* This sets up the dma based on dptr, dlen and rwflag */
  229.  
  230. setup_dma()
  231. {
  232.     unsigned pseg, poff;  /* physical address segment and offset */
  233.  
  234.     if (dlen <= 0)
  235.         return;
  236.  
  237.     poff = (unsigned)((FP_SEG(dptr) << 4) + FP_OFF(dptr));
  238.     pseg = (unsigned)((FP_SEG(dptr) + (FP_OFF(dptr) >> 4)) >> 12);
  239.  
  240.     if ( poff + dlen < poff) /* wraparound */
  241.         adlen = -poff;  /* # of bytes in current 64k chunk */
  242.     else
  243.         adlen = dlen;
  244.  
  245.     disable();
  246.     outportb(0x0a, 0x07); /* ;clear channel 3 mask */
  247.  
  248.     /* write to mode register for: single mode, increment,
  249.         auto. init. disable, read or write, channel 3. */
  250.  
  251.     outportb( 0x0b, rwflag ? 0x4b : 0x47); /* see if read or write */
  252.  
  253.     /* 4 high addr bits go in special i/o port */
  254.     outportb (0x82, pseg);
  255.  
  256.     outportb(0x0c, 0); /* clear byte pointer flip-flop */
  257.  
  258.     /* send both bytes of address to address register 3 */
  259.     outportb(0x06, poff & 0xff);
  260.     outportb(0x06, poff >> 8);
  261.  
  262.     outportb(0x07, (adlen-1) & 0xff);
  263.     outportb(0x07, (adlen-1) >> 8);      /* send byte count - 1 to dma count reg 3 */
  264.  
  265.     enable();  /* restore interrupts */
  266.  
  267.     dptr += adlen;
  268.     dlen -= adlen;
  269.  
  270. }
  271.  
  272.  
  273.  
  274. /* This connects to the correct address on the scsi bus */
  275.  
  276. connect(count)
  277. long count;
  278. {
  279.  
  280.     if (free_wait(count) != 0)
  281.         return (-2);
  282.  
  283.     outportb(SCSIPORT, busid);    /* output bus id for connect */
  284.     outportb(SCSIPORT+2, 0); /* assert select */
  285.  
  286.     while (count--)
  287.     {
  288.           if ((inportb(SCSIPORT+1) & 0x10) == 0x10)
  289.         {
  290.             /* Clear select */
  291.             inportb(SCSIPORT+2);
  292.             return(0);
  293.         }
  294.     }
  295.  
  296.     /* Timed out */
  297.     return (-1);
  298. }
  299.  
  300.  
  301. /* This waits until the bus is free before making a connection */
  302.  
  303. free_wait(count)
  304. long count;
  305. {
  306.     while (count--)
  307.     {
  308.           if ((inportb(SCSIPORT+1) & 0xfc) == 0)
  309.             return (0);
  310.     }
  311.  
  312.     return (-1);  /* time out */
  313. }
  314.  
  315.  
  316.  
  317. /* This tests the bus status against ah, and returns whether or
  318. not the condition is met. We check twice. */
  319.  
  320. scsi_test(cond)
  321. int cond;
  322. {
  323.     if ((inportb(SCSIPORT+1) & 0xf8) == cond && (inportb(SCSIPORT+1) & 0xf8) == cond)
  324.         return (0);
  325.     else
  326.         return(-1);
  327. }
  328.  
  329.  
  330.  
  331. /* This loops, waiting for the DMA finish, for the data to run out.
  332. It has an extra-long timeout. dmatimeout gives the timeout length */
  333.  
  334. dma_wait()
  335. {
  336.     static long cnt;
  337. #ifdef DEBUG
  338.     static int lobyte;
  339. #endif
  340.  
  341.     cnt = 200000L;
  342.     while (cnt--)
  343.     {
  344.         /* See if we have reached the status phase */
  345.         /* if ((inportb(SCSIPORT+1) & 0xf8) == 0xf0) */
  346.         if (scsi_test(0xf0) == 0)
  347.         {
  348. #ifdef DEBUG
  349.             outportb(0x0c, 0); /* clear byte pointer flip-flop */
  350.             lobyte = inportb(0x07);
  351.             printf((CHARPTR)"DMA residue at status: %d bytes\n",
  352.                 256 * inportb(0x07) + lobyte + 1);
  353. #endif
  354.             return (1);
  355.         }
  356.  
  357.         /* See if DMA has reached Terminal Count */
  358.         if (inportb(0x08) & 0x08)
  359.         {
  360. #ifdef DEBUG
  361.             outportb(0x0c, 0); /* clear byte pointer flip-flop */
  362.             lobyte = inportb(0x07);
  363.             printf((CHARPTR)"DMA residue at TC: %d bytes\n",
  364.                 256 * inportb(0x07) + lobyte + 1);
  365. #endif
  366.             return (2);
  367.         }
  368.         
  369.     }
  370. #ifdef DEBUG
  371.     printf((CHARPTR)"dma_wait times out\n");
  372.     outportb(0x0c, 0); /* clear byte pointer flip-flop */
  373.     lobyte = inportb(0x07);
  374.     printf((CHARPTR)"DMA residue: %d bytes\n", 256 * inportb(0x07) + lobyte + 1);
  375. #endif
  376.  
  377.     return (-1);
  378. }
  379.  
  380.  
  381. arm_dma()
  382. {
  383.     /* clear mask bit of channel 3, allowing dma to start */
  384.     outportb(0x0a, 0x03);
  385. }
  386.  
  387.  
  388. disarm_dma()
  389. {
  390.     /* set mask bit of channel 3 */
  391.     outportb(0x0a, 0x07);
  392. }
  393.  
  394.  
  395. /* These either enable or disable the host adapter's DRQ line
  396. (for the DMA) or its INT line (to the interrupt controller). */
  397.  
  398. dma_connect()
  399. {
  400.     outportb(SCSIPORT+3, 0x01);
  401. }
  402.  
  403.  
  404. dma_disconnect()
  405. {
  406.     outportb(SCSIPORT+3, 0);
  407. }
  408.  
  409.  
  410.