1 /*- 2 * Copyright (c) 2000 Michael Smith 3 * Copyright (c) 2001 Scott Long 4 * Copyright (c) 2000 BSDi 5 * Copyright (c) 2001 Adaptec, Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include "opt_aac.h" 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/sysctl.h> 38 39 #include <dev/aac/aac_compat.h> 40 #include <sys/bus.h> 41 #include <sys/conf.h> 42 #include <sys/devicestat.h> 43 #include <sys/disk.h> 44 45 #include <vm/vm.h> 46 #include <vm/pmap.h> 47 48 #include <machine/md_var.h> 49 #include <machine/bus.h> 50 #include <sys/rman.h> 51 52 #include <dev/aac/aacreg.h> 53 #include <dev/aac/aac_ioctl.h> 54 #include <dev/aac/aacvar.h> 55 56 /* 57 * Interface to parent. 58 */ 59 static int aac_disk_probe(device_t dev); 60 static int aac_disk_attach(device_t dev); 61 static int aac_disk_detach(device_t dev); 62 63 /* 64 * Interface to the device switch. 65 */ 66 static d_open_t aac_disk_open; 67 static d_close_t aac_disk_close; 68 static d_strategy_t aac_disk_strategy; 69 static d_dump_t aac_disk_dump; 70 71 #define AAC_DISK_CDEV_MAJOR 151 72 73 static struct cdevsw aac_disk_cdevsw = { 74 /* open */ aac_disk_open, 75 /* close */ aac_disk_close, 76 /* read */ physread, 77 /* write */ physwrite, 78 /* ioctl */ noioctl, 79 /* poll */ nopoll, 80 /* mmap */ nommap, 81 /* strategy */ aac_disk_strategy, 82 /* name */ "aacd", 83 /* maj */ AAC_DISK_CDEV_MAJOR, 84 /* dump */ aac_disk_dump, 85 /* psize */ nopsize, 86 /* flags */ D_DISK, 87 #if __FreeBSD_version < 500005 88 /* bmaj */ -1 89 #endif 90 }; 91 92 static devclass_t aac_disk_devclass; 93 static struct cdevsw aac_disk_disk_cdevsw; 94 #ifdef FREEBSD_4 95 static int disks_registered = 0; 96 #endif 97 98 static device_method_t aac_disk_methods[] = { 99 DEVMETHOD(device_probe, aac_disk_probe), 100 DEVMETHOD(device_attach, aac_disk_attach), 101 DEVMETHOD(device_detach, aac_disk_detach), 102 { 0, 0 } 103 }; 104 105 static driver_t aac_disk_driver = { 106 "aacd", 107 aac_disk_methods, 108 sizeof(struct aac_disk) 109 }; 110 111 #define AAC_MAXIO 65536 112 113 DRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0); 114 115 /* sysctl tunables */ 116 static unsigned int aac_iosize_max = AAC_MAXIO; /* due to limits of the card */ 117 TUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max); 118 119 SYSCTL_DECL(_hw_aac); 120 SYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0, 121 "Max I/O size per transfer to an array"); 122 123 /* 124 * Handle open from generic layer. 125 * 126 * This is called by the diskslice code on first open in order to get the 127 * basic device geometry paramters. 128 */ 129 static int 130 aac_disk_open(dev_t dev, int flags, int fmt, d_thread_t *td) 131 { 132 struct aac_disk *sc; 133 134 debug_called(4); 135 136 sc = (struct aac_disk *)dev->si_drv1; 137 138 if (sc == NULL) 139 return (ENXIO); 140 141 /* check that the controller is up and running */ 142 if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) 143 return(ENXIO); 144 145 sc->ad_disk.d_sectorsize = AAC_BLOCK_SIZE; 146 sc->ad_disk.d_mediasize = (off_t)sc->ad_size * AAC_BLOCK_SIZE; 147 sc->ad_disk.d_fwsectors = sc->ad_sectors; 148 sc->ad_disk.d_fwheads = sc->ad_heads; 149 150 sc->ad_flags |= AAC_DISK_OPEN; 151 return (0); 152 } 153 154 /* 155 * Handle last close of the disk device. 156 */ 157 static int 158 aac_disk_close(dev_t dev, int flags, int fmt, d_thread_t *td) 159 { 160 struct aac_disk *sc; 161 162 debug_called(4); 163 164 sc = (struct aac_disk *)dev->si_drv1; 165 166 if (sc == NULL) 167 return (ENXIO); 168 169 sc->ad_flags &= ~AAC_DISK_OPEN; 170 return (0); 171 } 172 173 /* 174 * Handle an I/O request. 175 */ 176 static void 177 aac_disk_strategy(struct bio *bp) 178 { 179 struct aac_disk *sc; 180 181 debug_called(4); 182 183 sc = (struct aac_disk *)bp->bio_dev->si_drv1; 184 185 /* bogus disk? */ 186 if (sc == NULL) { 187 bp->bio_flags |= BIO_ERROR; 188 bp->bio_error = EINVAL; 189 biodone(bp); 190 return; 191 } 192 193 /* do-nothing operation? */ 194 if (bp->bio_bcount == 0) { 195 bp->bio_resid = bp->bio_bcount; 196 biodone(bp); 197 return; 198 } 199 200 /* perform accounting */ 201 devstat_start_transaction(&sc->ad_stats); 202 203 /* pass the bio to the controller - it can work out who we are */ 204 aac_submit_bio(bp); 205 return; 206 } 207 208 /* 209 * Map the S/G elements for doing a dump. 210 */ 211 static void 212 aac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 213 { 214 struct aac_fib *fib; 215 struct aac_blockwrite *bw; 216 struct aac_sg_table *sg; 217 int i; 218 219 fib = (struct aac_fib *)arg; 220 bw = (struct aac_blockwrite *)&fib->data[0]; 221 sg = &bw->SgMap; 222 223 if (sg != NULL) { 224 sg->SgCount = nsegs; 225 for (i = 0; i < nsegs; i++) { 226 sg->SgEntry[i].SgAddress = segs[i].ds_addr; 227 sg->SgEntry[i].SgByteCount = segs[i].ds_len; 228 } 229 fib->Header.Size = nsegs * sizeof(struct aac_sg_entry); 230 } 231 } 232 233 /* 234 * Dump memory out to an array 235 * 236 * Send out one command at a time with up to AAC_MAXIO of data. 237 */ 238 static int 239 aac_disk_dump(dev_t dev, void *virtual, vm_offset_t physical, off_t offset, size_t length) 240 { 241 struct aac_disk *ad; 242 struct aac_softc *sc; 243 struct aac_fib *fib; 244 struct aac_blockwrite *bw; 245 size_t len; 246 int size; 247 static bus_dmamap_t dump_datamap; 248 static int first = 0; 249 250 ad = dev->si_drv1; 251 252 if (ad == NULL) 253 return (EINVAL); 254 255 sc= ad->ad_controller; 256 257 if (!first) { 258 first = 1; 259 if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) { 260 printf("bus_dmamap_create failed\n"); 261 return (ENOMEM); 262 } 263 } 264 265 aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE); 266 bw = (struct aac_blockwrite *)&fib->data[0]; 267 268 while (length > 0) { 269 len = (length > AAC_MAXIO) ? AAC_MAXIO : length; 270 bw->Command = VM_CtBlockWrite; 271 bw->ContainerId = ad->ad_container->co_mntobj.ObjectId; 272 bw->BlockNumber = offset / AAC_BLOCK_SIZE; 273 bw->ByteCount = len; 274 bw->Stable = CUNSTABLE; 275 bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual, 276 len, aac_dump_map_sg, fib, 0); 277 bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap, 278 BUS_DMASYNC_PREWRITE); 279 280 /* fib->Header.Size is set in aac_dump_map_sg */ 281 size = fib->Header.Size + sizeof(struct aac_blockwrite); 282 283 if (aac_sync_fib(sc, ContainerCommand, 0, fib, size)) { 284 printf("Error dumping block 0x%x\n", physical); 285 return (EIO); 286 } 287 length -= len; 288 offset += len; 289 } 290 291 return (0); 292 } 293 294 /* 295 * Handle completion of an I/O request. 296 */ 297 void 298 aac_biodone(struct bio *bp) 299 { 300 struct aac_disk *sc; 301 302 debug_called(4); 303 304 sc = (struct aac_disk *)bp->bio_dev->si_drv1; 305 306 devstat_end_transaction_bio(&sc->ad_stats, bp); 307 if (bp->bio_flags & BIO_ERROR) { 308 #if __FreeBSD_version > 500039 309 disk_err(bp, "hard error", -1, 1); 310 #elif __FreeBSD_version > 500005 311 int blkno; 312 blkno = (sc->ad_label.d_nsectors) ? 0 : -1; 313 diskerr(bp, (char *)bp->bio_driver1, blkno, &sc->ad_label); 314 #else 315 int blkno; 316 blkno = (sc->ad_label.d_nsectors) ? 0 : -1; 317 diskerr(bp, (char *)bp->bio_driver1, 0, blkno, &sc->ad_label); 318 #endif 319 } 320 biodone(bp); 321 } 322 323 /* 324 * Stub only. 325 */ 326 static int 327 aac_disk_probe(device_t dev) 328 { 329 330 debug_called(2); 331 332 return (0); 333 } 334 335 /* 336 * Attach a unit to the controller. 337 */ 338 static int 339 aac_disk_attach(device_t dev) 340 { 341 struct aac_disk *sc; 342 343 debug_called(1); 344 345 sc = (struct aac_disk *)device_get_softc(dev); 346 347 /* initialise our softc */ 348 sc->ad_controller = 349 (struct aac_softc *)device_get_softc(device_get_parent(dev)); 350 sc->ad_container = device_get_ivars(dev); 351 sc->ad_dev = dev; 352 353 /* 354 * require that extended translation be enabled - other drivers read the 355 * disk! 356 */ 357 sc->ad_size = sc->ad_container->co_mntobj.Capacity; 358 if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */ 359 sc->ad_heads = 255; 360 sc->ad_sectors = 63; 361 } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */ 362 sc->ad_heads = 128; 363 sc->ad_sectors = 32; 364 } else { 365 sc->ad_heads = 64; 366 sc->ad_sectors = 32; 367 } 368 sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors)); 369 370 device_printf(dev, "%uMB (%u sectors)\n", 371 sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), 372 sc->ad_size); 373 374 devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev), 375 AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS, 376 DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, 377 DEVSTAT_PRIORITY_ARRAY); 378 379 /* attach a generic disk device to ourselves */ 380 sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0, 381 &aac_disk_cdevsw, &aac_disk_disk_cdevsw); 382 sc->ad_dev_t->si_drv1 = sc; 383 #ifdef FREEBSD_4 384 disks_registered++; 385 #endif 386 387 sc->ad_dev_t->si_iosize_max = aac_iosize_max; 388 sc->unit = device_get_unit(dev); 389 390 return (0); 391 } 392 393 /* 394 * Disconnect ourselves from the system. 395 */ 396 static int 397 aac_disk_detach(device_t dev) 398 { 399 struct aac_disk *sc; 400 401 debug_called(2); 402 403 sc = (struct aac_disk *)device_get_softc(dev); 404 405 if (sc->ad_flags & AAC_DISK_OPEN) 406 return(EBUSY); 407 408 devstat_remove_entry(&sc->ad_stats); 409 disk_destroy(sc->ad_dev_t); 410 #ifdef FREEBSD_4 411 if (--disks_registered == 0) 412 cdevsw_remove(&aac_disk_cdevsw); 413 #endif 414 415 return(0); 416 } 417