diff options
Diffstat (limited to 'sys/dev/ida/ida_disk.c')
-rw-r--r-- | sys/dev/ida/ida_disk.c | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/sys/dev/ida/ida_disk.c b/sys/dev/ida/ida_disk.c new file mode 100644 index 0000000..c3c94f5 --- /dev/null +++ b/sys/dev/ida/ida_disk.c @@ -0,0 +1,331 @@ +/*- + * Copyright (c) 1999 Jonathan Lemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* + * Disk driver for Compaq SMART RAID adapters. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <sys/buf.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/devicestat.h> +#include <sys/disklabel.h> +#include <sys/diskslice.h> + +#if NPCI > 0 +#include <machine/bus_memio.h> +#endif +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/clock.h> +#include <sys/rman.h> + +#include <dev/ida/idareg.h> +#include <dev/ida/idavar.h> + +/* prototypes */ +static void id_drvinit(void); +static int idprobe(device_t dev); +static int idattach(device_t dev); + +static d_open_t idopen; +static d_close_t idclose; +static d_strategy_t idstrategy; +static d_ioctl_t idioctl; +static d_psize_t idsize; + +#define ID_BDEV_MAJOR 29 +#define ID_CDEV_MAJOR 109 + +#define WD_BDEV_MAJOR 0 +#define WD_CDEV_MAJOR 3 + +static struct cdevsw id_cdevsw = { + /* open */ idopen, + /* close */ idclose, + /* read */ physread, + /* write */ physwrite, + /* ioctl */ idioctl, + /* stop */ nostop, + /* reset */ noreset, + /* devtotty */ nodevtotty, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ idstrategy, + /* name */ "id", + /* parms */ noparms, + /* maj */ ID_CDEV_MAJOR, + /* dump */ nodump, + /* psize */ idsize, + /* flags */ D_DISK, + /* maxio */ 0, + /* bmaj */ ID_BDEV_MAJOR +}; +static struct cdevsw stolen_cdevsw; + +static devclass_t id_devclass; + +static device_method_t id_methods[] = { + DEVMETHOD(device_probe, idprobe), + DEVMETHOD(device_attach, idattach), + { 0, 0 } +}; + +static driver_t id_driver = { + "id", + id_methods, + sizeof(struct id_softc) +}; + +static __inline struct id_softc * +idgetsoftc(dev_t dev) +{ + int unit; + + unit = dkunit(dev); + return ((struct id_softc *)devclass_get_softc(id_devclass, unit)); +} + +static int +idopen(dev_t dev, int flags, int fmt, struct proc *p) +{ + struct id_softc *drv; + struct disklabel label; + int error; + + drv = idgetsoftc(dev); + if (drv == NULL) + return (ENXIO); + + /* XXX block against race where > 1 person is reading label? */ + + bzero(&label, sizeof(label)); + label.d_type = DTYPE_SCSI; /* XXX should this be DTYPE_RAID? */ +/* + strncpy(label.d_typename, ... + strncpy(label.d_packname, ... +*/ + label.d_secsize = drv->secsize; + label.d_nsectors = drv->sectors; + label.d_ntracks = drv->heads; + label.d_ncylinders = drv->cylinders; + label.d_secpercyl = drv->sectors * drv->heads; + label.d_secperunit = drv->secperunit; + + /* Initialize slice tables. */ + error = dsopen("id", dev, fmt, 0, &drv->slices, &label, + idstrategy, (ds_setgeom_t *)NULL, &id_cdevsw); + + return (error); +} + +static int +idclose(dev_t dev, int flags, int fmt, struct proc *p) +{ + struct id_softc *drv; + + drv = idgetsoftc(dev); + if (drv == NULL) + return (ENXIO); + dsclose(dev, fmt, drv->slices); + return (0); +} + +static int +idioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) +{ + struct id_softc *drv; + int error; + + drv = idgetsoftc(dev); + if (drv == NULL) + return (ENXIO); + + error = dsioctl("id", dev, cmd, addr, flag, &drv->slices, + idstrategy, (ds_setgeom_t *)NULL); + + if (error == ENOIOCTL) + return (ENOTTY); + + return (error); +} + +static int +idsize(dev_t dev) +{ + struct id_softc *drv; + + drv = idgetsoftc(dev); + if (drv == NULL) + return (ENXIO); + return (dssize(dev, &drv->slices, idopen, idclose)); +} + +/* + * Read/write routine for a buffer. Finds the proper unit, range checks + * arguments, and schedules the transfer. Does not wait for the transfer + * to complete. Multi-page transfers are supported. All I/O requests must + * be a multiple of a sector in length. + */ +static void +idstrategy(struct buf *bp) +{ + struct id_softc *drv; + int s; + + drv = idgetsoftc(bp->b_dev); + if (drv == NULL) { + bp->b_error = EINVAL; + goto bad; + } + + if (dscheck(bp, drv->slices) <= 0) + goto done; + + /* + * software write protect check + */ + if (drv->flags & DRV_WRITEPROT && (bp->b_flags & B_READ) == 0) { + bp->b_error = EROFS; + goto bad; + } + + /* + * If it's a null transfer, return immediately + */ + if (bp->b_bcount == 0) + goto done; + + bp->b_driver1 = drv; + s = splbio(); + devstat_start_transaction(&drv->stats); + ida_submit_buf(drv->controller, bp); + splx(s); + return; + +bad: + bp->b_flags |= B_ERROR; + +done: + /* + * Correctly set the buf to indicate a completed transfer + */ + bp->b_resid = bp->b_bcount; + biodone(bp); + return; +} + +void +id_intr(struct buf *bp) +{ + struct id_softc *drv = (struct id_softc *)bp->b_driver1; + + if (bp->b_flags & B_ERROR) + bp->b_error = EIO; + else + bp->b_resid = 0; + + biodone(bp); + devstat_end_transaction(&drv->stats, + bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE, + (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE); +} + +static void +id_drvinit(void) +{ + static int devsw_installed = 0; + + if (devsw_installed) + return; /* XXX is this needed? */ + + cdevsw_add(&id_cdevsw); + stolen_cdevsw = id_cdevsw; + stolen_cdevsw.d_maj = WD_CDEV_MAJOR; + stolen_cdevsw.d_bmaj = WD_BDEV_MAJOR; + cdevsw_add(&stolen_cdevsw); + devsw_installed = 1; +} + +static int +idprobe(device_t dev) +{ + + id_drvinit(); + device_set_desc(dev, "Compaq Logical Drive"); + return (0); +} + +static int +idattach(device_t dev) +{ + struct ida_drive_info dinfo; + struct id_softc *drv; + device_t parent; + int error; + + drv = (struct id_softc *)device_get_softc(dev); + parent = device_get_parent(dev); + drv->controller = (struct ida_softc *)device_get_softc(parent); + drv->unit = device_get_unit(dev); + + error = ida_command(drv->controller, CMD_GET_LOG_DRV_INFO, + &dinfo, sizeof(dinfo), drv->unit, DMA_DATA_IN); + if (error) { + device_printf(dev, "CMD_GET_LOG_DRV_INFO failed\n"); + return (ENXIO); + } + + drv->cylinders = dinfo.ncylinders; + drv->heads = dinfo.nheads; + drv->sectors = dinfo.nsectors; + drv->secsize = dinfo.secsize; + drv->secperunit = dinfo.secperunit; + + /* XXX + * other initialization + */ + device_printf(dev, "%uMB (%u sectors), blocksize=%d\n", + drv->secperunit / ((1024 * 1024) / drv->secsize), + drv->secperunit, drv->secsize); + + devstat_add_entry(&drv->stats, "id", drv->unit, drv->secsize, + DEVSTAT_NO_ORDERED_TAGS, + DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_DA); + + return (0); +} + +DEV_DRIVER_MODULE(id, ida, id_driver, id_devclass, + ID_CDEV_MAJOR, ID_BDEV_MAJOR, id_cdevsw, 0, 0); |