1: /* This file contains device independent device driver interface.
   2:  *                                                      Author: Kees J. Bot.
   3:  *
   4:  * The drivers support the following operations (using message format m2):
   5:  *
   6:  *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADRRESS
   7:  * ----------------------------------------------------------------
   8:  * |  DEV_OPEN  | device  | proc nr |         |         |         |
   9:  * |------------+---------+---------+---------+---------+---------|
  10:  * |  DEV_CLOSE | device  | proc nr |         |         |         |
  11:  * |------------+---------+---------+---------+---------+---------|
  12:  * |  DEV_READ  | device  | proc nr |  bytes  |  offset | buf ptr |
  13:  * |------------+---------+---------+---------+---------+---------|
  14:  * |  DEV_WRITE | device  | proc nr |  bytes  |  offset | buf ptr |
  15:  * |------------+---------+---------+---------+---------+---------|
  16:  * | DEV_GATHER | device  | proc nr | iov len |  offset | iov ptr |
  17:  * |------------+---------+---------+---------+---------+---------|
  18:  * | DEV_SCATTER| device  | proc nr | iov len |  offset | iov ptr |
  19:  * |------------+---------+---------+---------+---------+---------|
  20:  * |  DEV_IOCTL | device  | proc nr |func code|         | buf ptr |
  21:  * ----------------------------------------------------------------
  22:  *
  23:  * The file contains one entry point:
  24:  *
  25:  *   driver_task:       called by the device dependent task entry
  26:  *
  27:  *
  28:  * Constructed 92/04/02 by Kees J. Bot from the old AT wini and floppy driver.
  29:  */
  30: 
  31: #include "kernel.h"
  32: #include <sys/ioc_disk.h>
  33: #include "driver.h"
  34: 
  35: #if (CHIP == INTEL)
  36: #if ENABLE_AHA1540_SCSI && DMA_BUF_SIZE < 2048
  37: /* A bit extra scratch for the Adaptec driver. */
  38: #define BUF_EXTRA       (2048 - DMA_BUF_SIZE)
  39: #else
  40: #define BUF_EXTRA       0
  41: #endif
  42: 
  43: /* Claim space for variables. */
  44: PRIVATE u8_t buffer[(unsigned) 2 * DMA_BUF_SIZE + BUF_EXTRA];
  45: u8_t *tmp_buf;                  /* the DMA buffer eventually */
  46: phys_bytes tmp_phys;            /* phys address of DMA buffer */
  47: 
  48: #else /* CHIP != INTEL */
  49: 
  50: /* Claim space for variables. */
  51: u8_t tmp_buf[DMA_BUF_SIZE];     /* the DMA buffer */
  52: phys_bytes tmp_phys;            /* phys address of DMA buffer */
  53: 
  54: #endif /* CHIP != INTEL */
  55: 
  56: FORWARD _PROTOTYPE( void init_buffer, (void) );
  57: FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp) );
  58: FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp) );
  59: 
  60: 
  61: /*===========================================================================*
  62:  *                              driver_task                                  *
  63:  *===========================================================================*/
  64: PUBLIC void driver_task(dp)
  65: struct driver *dp;      /* Device dependent entry points. */
  66: {
  67: /* Main program of any device driver task. */
  68: 
  69:   int r, caller, proc_nr;
  70:   message mess;
  71: 
  72:   init_buffer();        /* Get a DMA buffer. */
  73: 
  74: 
  75:   /* Here is the main loop of the disk task.  It waits for a message, carries
  76:    * it out, and sends a reply.
  77:    */
  78: 
  79:   while (TRUE) {
  80:         /* Check if a timer expired. */
  81:         if (proc_ptr->p_exptimers != NULL) tmr_exptimers();
  82: 
  83:         /* Wait for a request to read or write a disk block. */
  84:         receive(ANY, &mess);
  85: 
  86:         caller = mess.m_source;
  87:         proc_nr = mess.PROC_NR;
  88: 
  89:         /* Check if legitimate caller: FS or a task. */
  90:         if (caller != FS_PROC_NR && caller >= 0) {
  91:                 printf("%s: got message from %d\n", (*dp->dr_name)(), caller);
  92:                 continue;
  93:         }
  94: 
  95:         /* Now carry out the work. */
  96:         switch(mess.m_type) {
  97:         case DEV_OPEN:          r = (*dp->dr_open)(dp, &mess);    break;
  98:         case DEV_CLOSE:         r = (*dp->dr_close)(dp, &mess);   break;
  99:         case DEV_IOCTL:         r = (*dp->dr_ioctl)(dp, &mess);   break;
 100: 
 101:         case DEV_READ:
 102:         case DEV_WRITE:         r = do_rdwt(dp, &mess);          break;
 103: 
 104:         case DEV_GATHER:
 105:         case DEV_SCATTER:       r = do_vrdwt(dp, &mess); break;
 106: 
 107:         case HARD_INT:          /* Leftover interrupt or expired timer. */
 108:                                 continue;
 109: 
 110:         default:                r = EINVAL;                     break;
 111:         }
 112: 
 113:         /* Clean up leftover state. */
 114:         (*dp->dr_cleanup)();
 115: 
 116:         /* Finally, prepare and send the reply message. */
 117:         mess.m_type = TASK_REPLY;
 118:         mess.REP_PROC_NR = proc_nr;
 119: 
 120:         mess.REP_STATUS = r;    /* # of bytes transferred or error code */
 121:         send(caller, &mess);     /* send reply to caller */
 122:   }
 123: }
 124: 
 125: 
 126: /*===========================================================================*
 127:  *                              init_buffer                                  *
 128:  *===========================================================================*/
 129: PRIVATE void init_buffer()
 130: {
 131: /* Select a buffer that can safely be used for dma transfers.  It may also
 132:  * be used to read partition tables and such.  Its absolute address is
 133:  * 'tmp_phys', the normal address is 'tmp_buf'.
 134:  */
 135: 
 136: #if (CHIP == INTEL)
 137:   unsigned left;
 138: 
 139:   tmp_buf = buffer;
 140:   tmp_phys = vir2phys(buffer);
 141: 
 142:   if ((left = dma_bytes_left(tmp_phys)) < DMA_BUF_SIZE) {
 143:         /* First half of buffer crosses a 64K boundary, can't DMA into that */
 144:         tmp_buf += left;
 145:         tmp_phys += left;
 146:   }
 147: #else /* CHIP != INTEL */
 148:   tmp_phys = vir2phys(tmp_buf);
 149: #endif /* CHIP != INTEL */
 150: }
 151: 
 152: 
 153: /*===========================================================================*
 154:  *                              do_rdwt                                      *
 155:  *===========================================================================*/
 156: PRIVATE int do_rdwt(dp, mp)
 157: struct driver *dp;              /* device dependent entry points */
 158: message *mp;                    /* pointer to read or write message */
 159: {
 160: /* Carry out a single read or write request. */
 161:   iovec_t iovec1;
 162:   int r, opcode;
 163:   phys_bytes phys_addr;
 164: 
 165:   /* Disk address?  Address and length of the user buffer? */
 166:   if (mp->COUNT < 0) return(EINVAL);
 167: 
 168:   /* Check the user buffer. */
 169:   phys_addr = numap(mp->PROC_NR, (vir_bytes) mp->ADDRESS, mp->COUNT);
 170:   if (phys_addr == 0) return(EFAULT);
 171: 
 172:   /* Prepare for I/O. */
 173:   if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
 174: 
 175:   /* Create a one element scatter/gather vector for the buffer. */
 176:   opcode = mp->m_type == DEV_READ ? DEV_GATHER : DEV_SCATTER;
 177:   iovec1.iov_addr = (vir_bytes) mp->ADDRESS;
 178:   iovec1.iov_size = mp->COUNT;
 179: 
 180:   /* Transfer bytes from/to the device. */
 181:   r = (*dp->dr_transfer)(mp->PROC_NR, opcode, mp->POSITION, &iovec1, 1);
 182: 
 183:   /* Return the number of bytes transferred or an error code. */
 184:   return(r == OK ? (mp->COUNT - iovec1.iov_size) : r);
 185: }
 186: 
 187: 
 188: /*==========================================================================*
 189:  *                              do_vrdwt                                    *
 190:  *==========================================================================*/
 191: PRIVATE int do_vrdwt(dp, mp)
 192: struct driver *dp;      /* device dependent entry points */
 193: message *mp;            /* pointer to read or write message */
 194: {
 195: /* Carry out an device read or write to/from a vector of user addresses.
 196:  * The "user addresses" are assumed to be safe, i.e. FS transferring to/from
 197:  * its own buffers, so they are not checked.
 198:  */
 199:   static iovec_t iovec[NR_IOREQS];
 200:   iovec_t *iov;
 201:   phys_bytes iovec_phys, user_iovec_phys;
 202:   size_t iovec_size;
 203:   unsigned nr_req;
 204:   int r;
 205: 
 206:   nr_req = mp->COUNT;    /* Length of I/O vector */
 207: 
 208:   if (mp->m_source < 0) {
 209:         /* Called by a task, no need to copy vector. */
 210:         iov = (iovec_t *) mp->ADDRESS;
 211:   } else {
 212:         /* Copy the vector from the caller to kernel space. */
 213:         if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
 214:         iovec_size = nr_req * sizeof(iovec[0]);
 215:         user_iovec_phys = numap(mp->m_source,
 216:                                 (vir_bytes) mp->ADDRESS, iovec_size);
 217:         if (user_iovec_phys == 0) panic("bad I/O vector by", mp->m_source);
 218: 
 219:         iovec_phys = vir2phys(iovec);
 220:         phys_copy(user_iovec_phys, iovec_phys, (phys_bytes) iovec_size);
 221:         iov = iovec;
 222:   }
 223: 
 224:   /* Prepare for I/O. */
 225:   if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
 226: 
 227:   /* Transfer bytes from/to the device. */
 228:   r = (*dp->dr_transfer)(mp->PROC_NR, mp->m_type, mp->POSITION, iov, nr_req);
 229: 
 230:   /* Copy the I/O vector back to the caller. */
 231:   if (mp->m_source >= 0) {
 232:           phys_copy(iovec_phys, user_iovec_phys, (phys_bytes) iovec_size);
 233:   }
 234:   return(r);
 235: }
 236: 
 237: 
 238: /*===========================================================================*
 239:  *                              no_name                                      *
 240:  *===========================================================================*/
 241: PUBLIC char *no_name()
 242: {
 243: /* If no specific name for the device. */
 244: 
 245:   return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);
 246: }
 247: 
 248: 
 249: /*============================================================================*
 250:  *                              do_nop                                        *
 251:  *============================================================================*/
 252: PUBLIC int do_nop(dp, mp)
 253: struct driver *dp;
 254: message *mp;
 255: {
 256: /* Nothing there, or nothing to do. */
 257: 
 258:   switch (mp->m_type) {
 259:   case DEV_OPEN:        return(ENODEV);
 260:   case DEV_CLOSE:       return(OK);
 261:   case DEV_IOCTL:       return(ENOTTY);
 262:   default:              return(EIO);
 263:   }
 264: }
 265: 
 266: 
 267: /*===========================================================================*
 268:  *                              nop_prepare                                  *
 269:  *===========================================================================*/
 270: PUBLIC struct device *nop_prepare(device)
 271: {
 272: /* Nothing to prepare for. */
 273:   return(NIL_DEV);
 274: }
 275: 
 276: 
 277: 
 278: /*===========================================================================*
 279:  *                              nop_cleanup                                  *
 280:  *===========================================================================*/
 281: PUBLIC void nop_cleanup()
 282: {
 283: /* Nothing to clean up. */
 284: }
 285: 
 286: 
 287: 
 288: /*===========================================================================*
 289:  *                              nop_task                                     *
 290:  *===========================================================================*/
 291: PUBLIC void nop_task()
 292: {
 293: /* Unused controllers are "serviced" by this task. */
 294:   struct driver nop_tab = {
 295:         no_name,
 296:         do_nop,
 297:         do_nop,
 298:         do_nop,
 299:         nop_prepare,
 300:         NULL,
 301:         nop_cleanup,
 302:         NULL,
 303:   };
 304:   driver_task(&nop_tab);
 305: }
 306: 
 307: 
 308: /*============================================================================*
 309:  *                              do_diocntl                                    *
 310:  *============================================================================*/
 311: PUBLIC int do_diocntl(dp, mp)
 312: struct driver *dp;
 313: message *mp;                    /* pointer to ioctl request */
 314: {
 315: /* Carry out a partition setting/getting request. */
 316:   struct device *dv;
 317:   phys_bytes user_phys, entry_phys;
 318:   struct partition entry;
 319: 
 320:   if (mp->REQUEST != DIOCSETP && mp->REQUEST != DIOCGETP) return(ENOTTY);
 321: 
 322:   /* Decode the message parameters. */
 323:   if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NIL_DEV) return(ENXIO);
 324: 
 325:   user_phys = numap(mp->PROC_NR, (vir_bytes) mp->ADDRESS, sizeof(entry));
 326:   if (user_phys == 0) return(EFAULT);
 327: 
 328:   entry_phys = vir2phys(&entry);
 329: 
 330:   if (mp->REQUEST == DIOCSETP) {
 331:         /* Copy just this one partition table entry. */
 332:         phys_copy(user_phys, entry_phys, (phys_bytes) sizeof(entry));
 333:         dv->dv_base = entry.base;
 334:         dv->dv_size = entry.size;
 335:   } else {
 336:         /* Return a partition table entry and the geometry of the drive. */
 337:         entry.base = dv->dv_base;
 338:         entry.size = dv->dv_size;
 339:         (*dp->dr_geometry)(&entry);
 340:         phys_copy(entry_phys, user_phys, (phys_bytes) sizeof(entry));
 341:   }
 342:   return(OK);
 343: }