The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/drivers/libdriver/driver.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 692dc8a553541265270185ff63711368


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.