1 /*- 2 * Copyright (c) 1999,2000 Jonathan Lemon 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 /* 30 * Disk driver for Compaq SMART RAID adapters. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/malloc.h> 36 #include <sys/kernel.h> 37 38 #include <sys/buf.h> 39 #include <sys/bus.h> 40 #include <sys/conf.h> 41 #include <sys/devicestat.h> 42 #include <sys/disk.h> 43 44 #if NPCI > 0 45 #include <machine/bus_memio.h> 46 #endif 47 #include <machine/bus_pio.h> 48 #include <machine/bus.h> 49 #include <machine/clock.h> 50 #include <sys/rman.h> 51 52 #include <dev/ida/idareg.h> 53 #include <dev/ida/idavar.h> 54 55 /* prototypes */ 56 static int idprobe(device_t dev); 57 static int idattach(device_t dev); 58 static int iddetach(device_t dev); 59 60 static d_open_t idopen; 61 static d_close_t idclose; 62 static d_strategy_t idstrategy; 63 64 #define ID_BDEV_MAJOR 29 65 #define ID_CDEV_MAJOR 109 66 67 #define WD_BDEV_MAJOR 0 68 #define WD_CDEV_MAJOR 3 69 70 static struct cdevsw id_cdevsw = { 71 /* open */ idopen, 72 /* close */ idclose, 73 /* read */ physread, 74 /* write */ physwrite, 75 /* ioctl */ noioctl, 76 /* poll */ nopoll, 77 /* mmap */ nommap, 78 /* strategy */ idstrategy, 79 /* name */ "id", 80 /* maj */ ID_CDEV_MAJOR, 81 /* dump */ nodump, 82 /* psize */ nopsize, 83 /* flags */ D_DISK, 84 /* bmaj */ ID_BDEV_MAJOR 85 }; 86 87 static devclass_t id_devclass; 88 static struct cdevsw iddisk_cdevsw; 89 static int disks_registered = 0; 90 91 static device_method_t id_methods[] = { 92 DEVMETHOD(device_probe, idprobe), 93 DEVMETHOD(device_attach, idattach), 94 DEVMETHOD(device_detach, iddetach), 95 { 0, 0 } 96 }; 97 98 static driver_t id_driver = { 99 "idad", 100 id_methods, 101 sizeof(struct id_softc) 102 }; 103 104 static __inline struct id_softc * 105 idgetsoftc(dev_t dev) 106 { 107 108 return ((struct id_softc *)dev->si_drv1); 109 } 110 111 static int 112 idopen(dev_t dev, int flags, int fmt, struct proc *p) 113 { 114 struct id_softc *drv; 115 struct disklabel *label; 116 117 drv = idgetsoftc(dev); 118 if (drv == NULL) 119 return (ENXIO); 120 121 label = &drv->disk.d_label; 122 bzero(label, sizeof(*label)); 123 label->d_type = DTYPE_SCSI; 124 label->d_secsize = drv->secsize; 125 label->d_nsectors = drv->sectors; 126 label->d_ntracks = drv->heads; 127 label->d_ncylinders = drv->cylinders; 128 label->d_secpercyl = drv->sectors * drv->heads; 129 label->d_secperunit = drv->secperunit; 130 131 return (0); 132 } 133 134 static int 135 idclose(dev_t dev, int flags, int fmt, struct proc *p) 136 { 137 struct id_softc *drv; 138 139 drv = idgetsoftc(dev); 140 if (drv == NULL) 141 return (ENXIO); 142 return (0); 143 } 144 145 /* 146 * Read/write routine for a buffer. Finds the proper unit, range checks 147 * arguments, and schedules the transfer. Does not wait for the transfer 148 * to complete. Multi-page transfers are supported. All I/O requests must 149 * be a multiple of a sector in length. 150 */ 151 static void 152 idstrategy(struct buf *bp) 153 { 154 struct id_softc *drv; 155 int s; 156 157 drv = idgetsoftc(bp->b_dev); 158 if (drv == NULL) { 159 bp->b_error = EINVAL; 160 goto bad; 161 } 162 163 /* 164 * software write protect check 165 */ 166 if (drv->flags & DRV_WRITEPROT && (bp->b_iocmd == BIO_WRITE)) { 167 bp->b_error = EROFS; 168 goto bad; 169 } 170 171 /* 172 * If it's a null transfer, return immediately 173 */ 174 if (bp->b_bcount == 0) 175 goto done; 176 177 bp->b_driver1 = drv; 178 s = splbio(); 179 devstat_start_transaction(&drv->stats); 180 ida_submit_buf(drv->controller, bp); 181 splx(s); 182 return; 183 184 bad: 185 bp->b_ioflags |= BIO_ERROR; 186 187 done: 188 /* 189 * Correctly set the buf to indicate a completed transfer 190 */ 191 bp->b_resid = bp->b_bcount; 192 biodone(bp); 193 return; 194 } 195 196 void 197 id_intr(struct buf *bp) 198 { 199 struct id_softc *drv = (struct id_softc *)bp->b_driver1; 200 201 if (bp->b_ioflags & BIO_ERROR) 202 bp->b_error = EIO; 203 else 204 bp->b_resid = 0; 205 206 devstat_end_transaction_buf(&drv->stats, bp); 207 biodone(bp); 208 } 209 210 static int 211 idprobe(device_t dev) 212 { 213 214 device_set_desc(dev, "Compaq Logical Drive"); 215 return (0); 216 } 217 218 static int 219 idattach(device_t dev) 220 { 221 struct ida_drive_info dinfo; 222 struct id_softc *drv; 223 device_t parent; 224 dev_t dsk; 225 int error; 226 227 drv = (struct id_softc *)device_get_softc(dev); 228 parent = device_get_parent(dev); 229 drv->controller = (struct ida_softc *)device_get_softc(parent); 230 drv->unit = device_get_unit(dev); 231 232 error = ida_command(drv->controller, CMD_GET_LOG_DRV_INFO, 233 &dinfo, sizeof(dinfo), drv->unit, DMA_DATA_IN); 234 if (error) { 235 device_printf(dev, "CMD_GET_LOG_DRV_INFO failed\n"); 236 return (ENXIO); 237 } 238 239 drv->cylinders = dinfo.ncylinders; 240 drv->heads = dinfo.nheads; 241 drv->sectors = dinfo.nsectors; 242 drv->secsize = dinfo.secsize; 243 drv->secperunit = dinfo.secperunit; 244 245 /* XXX 246 * other initialization 247 */ 248 device_printf(dev, "%uMB (%u sectors), blocksize=%d\n", 249 drv->secperunit / ((1024 * 1024) / drv->secsize), 250 drv->secperunit, drv->secsize); 251 252 devstat_add_entry(&drv->stats, "idad", drv->unit, drv->secsize, 253 DEVSTAT_NO_ORDERED_TAGS, 254 DEVSTAT_TYPE_STORARRAY| DEVSTAT_TYPE_IF_OTHER, 255 DEVSTAT_PRIORITY_ARRAY); 256 257 dsk = disk_create(drv->unit, &drv->disk, 0, 258 &id_cdevsw, &iddisk_cdevsw); 259 260 dsk->si_drv1 = drv; 261 dsk->si_iosize_max = 256 * drv->secsize; /* XXX guess? */ 262 disks_registered++; 263 264 return (0); 265 } 266 267 static int 268 iddetach(device_t dev) 269 { 270 struct id_softc *drv; 271 272 drv = (struct id_softc *)device_get_softc(dev); 273 devstat_remove_entry(&drv->stats); 274 275 if (--disks_registered == 0) 276 cdevsw_remove(&iddisk_cdevsw); 277 return (0); 278 } 279 280 DRIVER_MODULE(idad, ida, id_driver, id_devclass, 0, 0); 281