1 /*- 2 * Copyright (c) 1999 Jonathan Lemon 3 * Copyright (c) 1999 Michael Smith 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 /* 31 * Disk driver for Mylex DAC960 RAID adapters. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/malloc.h> 37 #include <sys/kernel.h> 38 39 #include <sys/buf.h> 40 #include <sys/bus.h> 41 #include <sys/conf.h> 42 #include <sys/devicestat.h> 43 #include <sys/disk.h> 44 45 #include <machine/bus.h> 46 #include <machine/clock.h> 47 #include <sys/rman.h> 48 49 #include <dev/mlx/mlxio.h> 50 #include <dev/mlx/mlxvar.h> 51 #include <dev/mlx/mlxreg.h> 52 53 /* prototypes */ 54 static int mlxd_probe(device_t dev); 55 static int mlxd_attach(device_t dev); 56 static int mlxd_detach(device_t dev); 57 58 static d_open_t mlxd_open; 59 static d_close_t mlxd_close; 60 static d_strategy_t mlxd_strategy; 61 static d_ioctl_t mlxd_ioctl; 62 63 #define MLXD_BDEV_MAJOR 27 64 #define MLXD_CDEV_MAJOR 131 65 66 static struct cdevsw mlxd_cdevsw = { 67 /* open */ mlxd_open, 68 /* close */ mlxd_close, 69 /* read */ physread, 70 /* write */ physwrite, 71 /* ioctl */ mlxd_ioctl, 72 /* poll */ nopoll, 73 /* mmap */ nommap, 74 /* strategy */ mlxd_strategy, 75 /* name */ "mlxd", 76 /* maj */ MLXD_CDEV_MAJOR, 77 /* dump */ nodump, 78 /* psize */ nopsize, 79 /* flags */ D_DISK, 80 /* bmaj */ MLXD_BDEV_MAJOR 81 }; 82 83 static devclass_t mlxd_devclass; 84 static struct cdevsw mlxddisk_cdevsw; 85 static int disks_registered = 0; 86 87 static device_method_t mlxd_methods[] = { 88 DEVMETHOD(device_probe, mlxd_probe), 89 DEVMETHOD(device_attach, mlxd_attach), 90 DEVMETHOD(device_detach, mlxd_detach), 91 { 0, 0 } 92 }; 93 94 static driver_t mlxd_driver = { 95 "mlxd", 96 mlxd_methods, 97 sizeof(struct mlxd_softc) 98 }; 99 100 DRIVER_MODULE(mlxd, mlx, mlxd_driver, mlxd_devclass, 0, 0); 101 102 static int 103 mlxd_open(dev_t dev, int flags, int fmt, struct proc *p) 104 { 105 struct mlxd_softc *sc = (struct mlxd_softc *)dev->si_drv1; 106 struct disklabel *label; 107 108 debug_called(1); 109 110 if (sc == NULL) 111 return (ENXIO); 112 113 /* controller not active? */ 114 if (sc->mlxd_controller->mlx_state & MLX_STATE_SHUTDOWN) 115 return(ENXIO); 116 117 label = &sc->mlxd_disk.d_label; 118 bzero(label, sizeof(*label)); 119 label->d_type = DTYPE_SCSI; 120 label->d_secsize = MLX_BLKSIZE; 121 label->d_nsectors = sc->mlxd_drive->ms_sectors; 122 label->d_ntracks = sc->mlxd_drive->ms_heads; 123 label->d_ncylinders = sc->mlxd_drive->ms_cylinders; 124 label->d_secpercyl = sc->mlxd_drive->ms_sectors * sc->mlxd_drive->ms_heads; 125 label->d_secperunit = sc->mlxd_drive->ms_size; 126 127 sc->mlxd_flags |= MLXD_OPEN; 128 return (0); 129 } 130 131 static int 132 mlxd_close(dev_t dev, int flags, int fmt, struct proc *p) 133 { 134 struct mlxd_softc *sc = (struct mlxd_softc *)dev->si_drv1; 135 136 debug_called(1); 137 138 if (sc == NULL) 139 return (ENXIO); 140 sc->mlxd_flags &= ~MLXD_OPEN; 141 return (0); 142 } 143 144 static int 145 mlxd_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) 146 { 147 struct mlxd_softc *sc = (struct mlxd_softc *)dev->si_drv1; 148 int error; 149 150 debug_called(1); 151 152 if (sc == NULL) 153 return (ENXIO); 154 155 if ((error = mlx_submit_ioctl(sc->mlxd_controller, sc->mlxd_drive, cmd, addr, flag, p)) != ENOIOCTL) { 156 debug(0, "mlx_submit_ioctl returned %d\n", error); 157 return(error); 158 } 159 return (ENOTTY); 160 } 161 162 /* 163 * Read/write routine for a buffer. Finds the proper unit, range checks 164 * arguments, and schedules the transfer. Does not wait for the transfer 165 * to complete. Multi-page transfers are supported. All I/O requests must 166 * be a multiple of a sector in length. 167 */ 168 static void 169 mlxd_strategy(struct buf *bp) 170 { 171 struct mlxd_softc *sc = (struct mlxd_softc *)bp->b_dev->si_drv1; 172 173 debug_called(1); 174 175 /* bogus disk? */ 176 if (sc == NULL) { 177 bp->b_error = EINVAL; 178 goto bad; 179 } 180 181 /* XXX may only be temporarily offline - sleep? */ 182 if (sc->mlxd_drive->ms_state == MLX_SYSD_OFFLINE) { 183 bp->b_error = ENXIO; 184 goto bad; 185 } 186 187 /* do-nothing operation */ 188 if (bp->b_bcount == 0) 189 goto done; 190 191 devstat_start_transaction(&sc->mlxd_stats); 192 mlx_submit_buf(sc->mlxd_controller, bp); 193 return; 194 195 bad: 196 bp->b_flags |= B_ERROR; 197 198 done: 199 /* 200 * Correctly set the buf to indicate a completed transfer 201 */ 202 bp->b_resid = bp->b_bcount; 203 biodone(bp); 204 return; 205 } 206 207 void 208 mlxd_intr(void *data) 209 { 210 struct buf *bp = (struct buf *)data; 211 struct mlxd_softc *sc = (struct mlxd_softc *)bp->b_dev->si_drv1; 212 213 debug_called(1); 214 215 if (bp->b_flags & B_ERROR) 216 bp->b_error = EIO; 217 else 218 bp->b_resid = 0; 219 220 devstat_end_transaction_buf(&sc->mlxd_stats, bp); 221 biodone(bp); 222 } 223 224 static int 225 mlxd_probe(device_t dev) 226 { 227 228 debug_called(1); 229 230 device_set_desc(dev, "Mylex System Drive"); 231 return (0); 232 } 233 234 static int 235 mlxd_attach(device_t dev) 236 { 237 struct mlxd_softc *sc = (struct mlxd_softc *)device_get_softc(dev); 238 device_t parent; 239 char *state; 240 dev_t dsk; 241 242 debug_called(1); 243 244 parent = device_get_parent(dev); 245 sc->mlxd_controller = (struct mlx_softc *)device_get_softc(parent); 246 sc->mlxd_unit = device_get_unit(dev); 247 sc->mlxd_drive = device_get_ivars(dev); 248 sc->mlxd_dev = dev; 249 250 switch(sc->mlxd_drive->ms_state) { 251 case MLX_SYSD_ONLINE: 252 state = "online"; 253 break; 254 case MLX_SYSD_CRITICAL: 255 state = "critical"; 256 break; 257 case MLX_SYSD_OFFLINE: 258 state = "offline"; 259 break; 260 default: 261 state = "unknown state"; 262 } 263 264 device_printf(dev, "%uMB (%u sectors) RAID %d (%s)\n", 265 sc->mlxd_drive->ms_size / ((1024 * 1024) / MLX_BLKSIZE), 266 sc->mlxd_drive->ms_size, sc->mlxd_drive->ms_raidlevel, state); 267 268 devstat_add_entry(&sc->mlxd_stats, "mlxd", sc->mlxd_unit, MLX_BLKSIZE, 269 DEVSTAT_NO_ORDERED_TAGS, 270 DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, 271 DEVSTAT_PRIORITY_ARRAY); 272 273 dsk = disk_create(sc->mlxd_unit, &sc->mlxd_disk, 0, &mlxd_cdevsw, &mlxddisk_cdevsw); 274 dsk->si_drv1 = sc; 275 disks_registered++; 276 277 /* set maximum I/O size */ 278 dsk->si_iosize_max = sc->mlxd_controller->mlx_enq2->me_maxblk * MLX_BLKSIZE; 279 280 return (0); 281 } 282 283 static int 284 mlxd_detach(device_t dev) 285 { 286 struct mlxd_softc *sc = (struct mlxd_softc *)device_get_softc(dev); 287 288 debug_called(1); 289 290 devstat_remove_entry(&sc->mlxd_stats); 291 292 /* hack to handle lack of destroy_disk() */ 293 if (--disks_registered == 0) 294 cdevsw_remove(&mlxddisk_cdevsw); 295 296 return(0); 297 } 298 299