os161-1.99
 All Data Structures
lhd.c
00001 /*
00002  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
00003  *      The President and Fellows of Harvard College.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the University nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  */
00029 
00030 /*
00031  * LAMEbus hard disk (lhd) driver.
00032  */
00033 
00034 #include <types.h>
00035 #include <kern/errno.h>
00036 #include <lib.h>
00037 #include <uio.h>
00038 #include <synch.h>
00039 #include <platform/bus.h>
00040 #include <vfs.h>
00041 #include <lamebus/lhd.h>
00042 #include "autoconf.h"
00043 
00044 /* Registers (offsets within slot) */
00045 #define LHD_REG_NSECT   0   /* Number of sectors */
00046 #define LHD_REG_STAT    4   /* Status */
00047 #define LHD_REG_SECT    8   /* Sector for I/O */
00048 #define LHD_REG_RPM     12  /* Disk rotation speed (revs per minute) */
00049 
00050 /* Status codes */
00051 #define LHD_IDLE        0   /* Device idle */
00052 #define LHD_WORKING     1   /* Operation in progress */
00053 #define LHD_OK          4   /* Operation succeeded */
00054 #define LHD_INVSECT     12  /* Invalid sector requested */
00055 #define LHD_MEDIA       20  /* Media error */
00056 #define LHD_ISWRITE     2   /* OR with above: I/O is a write */
00057 #define LHD_STATEMASK   0x1d  /* mask for masking out LHD_ISWRITE */
00058 
00059 /* Buffer (offset within slot)  */
00060 #define LHD_BUFFER      32768
00061 
00062 /*
00063  * Shortcut for reading a register.
00064  */
00065 static
00066 inline
00067 uint32_t lhd_rdreg(struct lhd_softc *lh, uint32_t reg)
00068 {
00069         return bus_read_register(lh->lh_busdata, lh->lh_buspos, reg);
00070 }
00071 
00072 /*
00073  * Shortcut for writing a register.
00074  */
00075 static
00076 inline
00077 void lhd_wreg(struct lhd_softc *lh, uint32_t reg, uint32_t val)
00078 {
00079         bus_write_register(lh->lh_busdata, lh->lh_buspos, reg, val);
00080 }
00081 
00082 /*
00083  * Convert a result code from the hardware to an errno value.
00084  */
00085 static
00086 int lhd_code_to_errno(struct lhd_softc *lh, int code)
00087 {
00088         switch (code & LHD_STATEMASK) {
00089             case LHD_OK: return 0;
00090             case LHD_INVSECT: return EINVAL;
00091             case LHD_MEDIA: return EIO;
00092         }
00093         kprintf("lhd%d: Unknown result code %d\n", lh->lh_unit, code);
00094         return EAGAIN;
00095 }
00096 
00097 /*
00098  * Record that an I/O has completed: save the result and poke the
00099  * completion semaphore.
00100  */
00101 static
00102 void
00103 lhd_iodone(struct lhd_softc *lh, int err)
00104 {
00105         lh->lh_result = err;
00106         V(lh->lh_done);
00107 }
00108 
00109 /*
00110  * Interrupt handler for lhd.
00111  * Read the status register; if an operation finished, clear the status
00112  * register and report completion.
00113  */
00114 void
00115 lhd_irq(void *vlh)
00116 {
00117         struct lhd_softc *lh = vlh;
00118         uint32_t val;
00119         
00120         val = lhd_rdreg(lh, LHD_REG_STAT);
00121 
00122         switch (val & LHD_STATEMASK) {
00123             case LHD_IDLE:
00124             case LHD_WORKING:
00125                 break;
00126             case LHD_OK:
00127             case LHD_INVSECT:
00128             case LHD_MEDIA:
00129                 lhd_wreg(lh, LHD_REG_STAT, 0);
00130                 lhd_iodone(lh, lhd_code_to_errno(lh, val));
00131                 break;
00132         }
00133 }
00134 
00135 /*
00136  * Function called when we are open()'d.
00137  */
00138 static
00139 int
00140 lhd_open(struct device *d, int openflags)
00141 {
00142         /*
00143          * Don't need to do anything.
00144          */
00145         (void)d;
00146         (void)openflags;
00147 
00148         return 0;
00149 }
00150 
00151 /*
00152  * Function called when we are close()'d.
00153  */
00154 static
00155 int
00156 lhd_close(struct device *d)
00157 {
00158         /*
00159          * Don't need to do anything.
00160          */
00161         (void)d;
00162 
00163         return 0;
00164 }
00165 
00166 /*
00167  * Function for handling ioctls.
00168  */
00169 static
00170 int
00171 lhd_ioctl(struct device *d, int op, userptr_t data)
00172 {
00173         /*
00174          * We don't support any ioctls.
00175          */
00176         (void)d;
00177         (void)op;
00178         (void)data;
00179         return EIOCTL;
00180 }
00181 
00182 #if 0
00183 /*
00184  * Reset the device.
00185  * This could be used, for instance, on timeout, if you implement suitable
00186  * facilities.
00187  */
00188 static
00189 void
00190 lhd_reset(struct lhd_softc *lh)
00191 {
00192         lhd_wreg(lh, LHD_REG_STAT, 0);
00193 }
00194 #endif
00195 
00196 /*
00197  * I/O function (for both reads and writes)
00198  */
00199 static
00200 int
00201 lhd_io(struct device *d, struct uio *uio)
00202 {
00203         struct lhd_softc *lh = d->d_data;
00204 
00205         uint32_t sector = uio->uio_offset / LHD_SECTSIZE;
00206         uint32_t sectoff = uio->uio_offset % LHD_SECTSIZE;
00207         uint32_t len = uio->uio_resid / LHD_SECTSIZE;
00208         uint32_t lenoff = uio->uio_resid % LHD_SECTSIZE;
00209         uint32_t i;
00210         uint32_t statval = LHD_WORKING;
00211         int result;
00212 
00213         /* Don't allow I/O that isn't sector-aligned. */
00214         if (sectoff != 0 || lenoff != 0) {
00215                 return EINVAL;
00216         }
00217 
00218         /* Don't allow I/O past the end of the disk. */
00219         if (sector+len > lh->lh_dev.d_blocks) {
00220                 return EINVAL;
00221         }
00222 
00223         /* Set up the value to write into the status register. */
00224         if (uio->uio_rw==UIO_WRITE) {
00225                 statval |= LHD_ISWRITE;
00226         }
00227 
00228         /* Loop over all the sectors we were asked to do. */
00229         for (i=0; i<len; i++) {
00230 
00231                 /* Wait until nobody else is using the device. */
00232                 P(lh->lh_clear);
00233 
00234                 /*
00235                  * Are we writing? If so, transfer the data to the
00236                  * on-card buffer.
00237                  */
00238                 if (uio->uio_rw == UIO_WRITE) {
00239                         result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
00240                         if (result) {
00241                                 V(lh->lh_clear);
00242                                 return result;
00243                         }
00244                 }
00245 
00246                 /* Tell it what sector we want... */
00247                 lhd_wreg(lh, LHD_REG_SECT, sector+i);
00248 
00249                 /* and start the operation. */
00250                 lhd_wreg(lh, LHD_REG_STAT, statval);
00251 
00252                 /* Now wait until the interrupt handler tells us we're done. */
00253                 P(lh->lh_done);
00254 
00255                 /* Get the result value saved by the interrupt handler. */
00256                 result = lh->lh_result;
00257 
00258                 /*
00259                  * Are we reading? If so, and if we succeeded,
00260                  * transfer the data out of the on-card buffer.
00261                  */
00262                 if (result==0 && uio->uio_rw==UIO_READ) {
00263                         result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
00264                 }
00265 
00266                 /* Tell another thread it's cleared to go ahead. */
00267                 V(lh->lh_clear);
00268 
00269                 /* If we failed, return the error. */
00270                 if (result) {
00271                         return result;
00272                 }
00273         }
00274 
00275         return 0;
00276 }
00277 
00278 /*
00279  * Setup routine called by autoconf.c when an lhd is found.
00280  */
00281 int
00282 config_lhd(struct lhd_softc *lh, int lhdno)
00283 {
00284         char name[32];
00285 
00286         /* Figure out what our name is. */
00287         snprintf(name, sizeof(name), "lhd%d", lhdno);
00288 
00289         /* Get a pointer to the on-chip buffer. */
00290         lh->lh_buf = bus_map_area(lh->lh_busdata, lh->lh_buspos, LHD_BUFFER);
00291 
00292         /* Create the semaphores. */
00293         lh->lh_clear = sem_create("lhd-clear", 1);
00294         if (lh->lh_clear == NULL) {
00295                 return ENOMEM;
00296         }
00297         lh->lh_done = sem_create("lhd-done", 0);
00298         if (lh->lh_done == NULL) {
00299                 sem_destroy(lh->lh_clear);
00300                 lh->lh_clear = NULL;
00301                 return ENOMEM;
00302         }
00303 
00304         /* Set up the VFS device structure. */
00305         lh->lh_dev.d_open = lhd_open;
00306         lh->lh_dev.d_close = lhd_close;
00307         lh->lh_dev.d_io = lhd_io;
00308         lh->lh_dev.d_ioctl = lhd_ioctl;
00309         lh->lh_dev.d_blocks = bus_read_register(lh->lh_busdata, lh->lh_buspos,
00310                                                 LHD_REG_NSECT);
00311         lh->lh_dev.d_blocksize = LHD_SECTSIZE;
00312         lh->lh_dev.d_data = lh;
00313 
00314         /* Add the VFS device structure to the VFS device list. */
00315         return vfs_adddev(name, &lh->lh_dev, 1);
00316 }
 All Data Structures