diff options
Diffstat (limited to 'sys/dev/ata/ata-disk.c')
-rw-r--r-- | sys/dev/ata/ata-disk.c | 469 |
1 files changed, 241 insertions, 228 deletions
diff --git a/sys/dev/ata/ata-disk.c b/sys/dev/ata/ata-disk.c index 2a338c6..9183645 100644 --- a/sys/dev/ata/ata-disk.c +++ b/sys/dev/ata/ata-disk.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org> + * Copyright (c) 1998 - 2005 Søren Schmidt <sos@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/ata.h> #include <sys/kernel.h> +#include <sys/module.h> #include <sys/malloc.h> #include <sys/bio.h> #include <sys/bus.h> @@ -52,287 +53,225 @@ __FBSDID("$FreeBSD$"); #include <dev/ata/ata-pci.h> #include <dev/ata/ata-disk.h> #include <dev/ata/ata-raid.h> +#include <ata_if.h> /* prototypes */ -static void ad_detach(struct ata_device *); -static void ad_config(struct ata_device *); -static void ad_start(struct ata_device *); +static void ad_init(device_t); static void ad_done(struct ata_request *); -static disk_open_t adopen; -static disk_strategy_t adstrategy; -static dumper_t addump; -void ad_print(struct ad_softc *); +static void ad_describe(device_t dev); static int ad_version(u_int16_t); +static disk_strategy_t ad_strategy; +static dumper_t ad_dump; -/* internal vars */ +/* local vars */ static MALLOC_DEFINE(M_AD, "AD driver", "ATA disk driver"); -static u_int32_t adp_lun_map = 0; -void -ad_attach(struct ata_device *atadev) +static void +ad_identify(driver_t *driver, device_t parent) { + ata_identify(driver, parent, -1, "ad"); +} + +static int +ad_probe(device_t dev) +{ + return 0; +} + +static int +ad_attach(device_t dev) +{ + struct ata_channel *ch = device_get_softc(device_get_parent(dev)); + struct ata_device *atadev = device_get_softc(dev); struct ad_softc *adp; u_int32_t lbasize; u_int64_t lbasize48; + /* check that we have a virgin disk to attach */ + if (device_get_ivars(dev)) + return EEXIST; + if (!(adp = malloc(sizeof(struct ad_softc), M_AD, M_NOWAIT | M_ZERO))) { - ata_prtdev(atadev, "FAILURE - could not allocate driver storage\n"); - atadev->attach = NULL; - return; + device_printf(dev, "out of memory\n"); + device_set_softc(dev, NULL); + free(atadev, M_ATA); + return ENOMEM; } - atadev->softc = adp; - adp->device = atadev; - -#ifdef ATA_STATIC_ID - adp->lun = (device_get_unit(atadev->channel->dev)<<1)+ATA_DEV(atadev->unit); -#else - adp->lun = ata_get_lun(&adp_lun_map); -#endif - ata_set_name(atadev, "ad", adp->lun); - adp->heads = atadev->param->heads; - adp->sectors = atadev->param->sectors; - adp->total_secs = atadev->param->cylinders * adp->heads * adp->sectors; -#ifdef PC98 - /* - * During the BOOT process, the PC-98 BIOS sets fake geometry of - * xxxx/8/17 of the disk using 'INITIALIZE DEVICE PARAMETER (91h)' - * command. After this command, all access to the drive must be done - * via the new, fake geometry, rather than the old, native format. - * With ATA/ATAPI-6 or later, these parameters are obsolete, but - * PC-98s are still using them. - * - * This only really matters when we're talking to disks using CHS - * mode, not LBA mode. The CHS mode disks are still relatively - * common in these machines, so that case must be addressed. - * - * (ITF sets new CHS geometry to initialized disks.) - * - * obsolete54[0]: current cylinder - * obsolete54[1]: current heads - * obsolete54[2]: current sectors - * obsolete54[3-4]: current capacities(multiplied above 3 values) - */ - /* Get CHS geometry from set by Initialize Device Parameters command. */ - if ((atadev->param->atavalid & ATA_FLAG_54_58) || - (atadev->param->obsolete54[0] != 0 && - atadev->param->obsolete54[1] != 0 && - atadev->param->obsolete54[2] != 0)) { - adp->heads = atadev->param->obsolete54[1]; - adp->sectors = atadev->param->obsolete54[2]; - adp->total_secs = *(u_int32_t*)&(atadev->param->obsolete54[3]); - } -#endif - - mtx_init(&adp->queue_mtx, "ATA disk bioqueue lock", NULL, MTX_DEF); - bioq_init(&adp->queue); + device_set_ivars(dev, adp); - lbasize = (u_int32_t)atadev->param->lba_size_1 | - ((u_int32_t)atadev->param->lba_size_2 << 16); + if (atadev->param.atavalid & ATA_FLAG_54_58) { + adp->heads = atadev->param.current_heads; + adp->sectors = atadev->param.current_sectors; + adp->total_secs = (u_int32_t)atadev->param.current_size_1 | + ((u_int32_t)atadev->param.current_size_2 << 16); + } + else { + adp->heads = atadev->param.heads; + adp->sectors = atadev->param.sectors; + adp->total_secs = atadev->param.cylinders * adp->heads * adp->sectors; + } + lbasize = (u_int32_t)atadev->param.lba_size_1 | + ((u_int32_t)atadev->param.lba_size_2 << 16); /* does this device need oldstyle CHS addressing */ - if (!ad_version(atadev->param->version_major) || !lbasize) + if (!ad_version(atadev->param.version_major) || !lbasize) atadev->flags |= ATA_D_USE_CHS; /* use the 28bit LBA size if valid or bigger than the CHS mapping */ - if (atadev->param->cylinders == 16383 || adp->total_secs < lbasize) + if (atadev->param.cylinders == 16383 || adp->total_secs < lbasize) adp->total_secs = lbasize; - lbasize48 = ((u_int64_t)atadev->param->lba_size48_1) | - ((u_int64_t)atadev->param->lba_size48_2 << 16) | - ((u_int64_t)atadev->param->lba_size48_3 << 32) | - ((u_int64_t)atadev->param->lba_size48_4 << 48); - /* use the 48bit LBA size if valid */ - if ((atadev->param->support.command2 & ATA_SUPPORT_ADDRESS48) && + lbasize48 = ((u_int64_t)atadev->param.lba_size48_1) | + ((u_int64_t)atadev->param.lba_size48_2 << 16) | + ((u_int64_t)atadev->param.lba_size48_3 << 32) | + ((u_int64_t)atadev->param.lba_size48_4 << 48); + if ((atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) && lbasize48 > ATA_MAX_28BIT_LBA) adp->total_secs = lbasize48; - /* setup the function ptrs */ - atadev->detach = ad_detach; - atadev->config = ad_config; - atadev->start = ad_start; + /* init device parameters */ + ad_init(dev); - /* config device features */ - ad_config(atadev); + /* announce we are here */ + ad_describe(dev); - /* lets create the disk device */ + /* create the disk device */ adp->disk = disk_alloc(); - adp->disk->d_open = adopen; - adp->disk->d_strategy = adstrategy; - adp->disk->d_dump = addump; + adp->disk->d_strategy = ad_strategy; + adp->disk->d_dump = ad_dump; adp->disk->d_name = "ad"; - adp->disk->d_drv1 = adp; - if (atadev->channel->dma) - adp->disk->d_maxsize = atadev->channel->dma->max_iosize; + adp->disk->d_drv1 = dev; + if (ch->dma) + adp->disk->d_maxsize = ch->dma->max_iosize; else adp->disk->d_maxsize = DFLTPHYS; adp->disk->d_sectorsize = DEV_BSIZE; adp->disk->d_mediasize = DEV_BSIZE * (off_t)adp->total_secs; adp->disk->d_fwsectors = adp->sectors; adp->disk->d_fwheads = adp->heads; - adp->disk->d_unit = adp->lun; + adp->disk->d_unit = device_get_unit(dev); disk_create(adp->disk, DISK_VERSION); - - /* announce we are here */ - ad_print(adp); - -#ifdef DEV_ATARAID - ata_raiddisk_attach(adp); -#endif + device_add_child(dev, "subdisk", device_get_unit(dev)); + bus_generic_attach(dev); + return 0; } -static void -ad_detach(struct ata_device *atadev) +static int +ad_detach(device_t dev) { - struct ad_softc *adp = atadev->softc; + struct ata_channel *ch = device_get_softc(device_get_parent(dev)); + struct ata_device *atadev = device_get_softc(dev); + struct ad_softc *adp = device_get_ivars(dev); + device_t *children; + int nchildren, i; + + /* check that we have a valid disk to detach */ + if (!device_get_ivars(dev)) + return ENXIO; + + /* detach & delete all children */ + if (!device_get_children(dev, &children, &nchildren)) { + for (i = 0; i < nchildren; i++) + if (children[i]) + device_delete_child(dev, children[i]); + free(children, M_TEMP); + } -#ifdef DEV_ATARAID - if (adp->flags & AD_F_RAID_SUBDISK) - ata_raiddisk_detach(adp); -#endif + /* detroy disk from the system so we dont get any further requests */ disk_destroy(adp->disk); - ata_prtdev(atadev, "WARNING - removed from configuration\n"); - mtx_lock(&adp->queue_mtx); - bioq_flush(&adp->queue, NULL, ENXIO); - mtx_unlock(&adp->queue_mtx); - mtx_destroy(&adp->queue_mtx); - ata_free_name(atadev); - ata_free_lun(&adp_lun_map, adp->lun); - atadev->attach = NULL; - atadev->detach = NULL; - atadev->start = NULL; - atadev->softc = NULL; - atadev->flags = 0; + + /* fail requests on the queue and any thats "in flight" for this device */ + ata_fail_requests(ch, dev); + + /* dont leave anything behind */ + device_set_ivars(dev, NULL); free(adp, M_AD); + device_set_softc(dev, NULL); + free(atadev, M_ATA); + return 0; } static void -ad_config(struct ata_device *atadev) +ad_shutdown(device_t dev) { - struct ad_softc *adp = atadev->softc; - - /* enable read caching */ - ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0); - - /* enable write caching if enabled */ - if (ata_wc) - ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0); - else - ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0); - - /* use multiple sectors/interrupt if device supports it */ - adp->max_iosize = DEV_BSIZE; - if (ad_version(atadev->param->version_major)) { - int secsperint = max(1, min(atadev->param->sectors_intr, 16)); + struct ata_device *atadev = device_get_softc(dev); - if (!ata_controlcmd(atadev, ATA_SET_MULTI, 0, 0, secsperint)) - adp->max_iosize = secsperint * DEV_BSIZE; - } + if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) + ata_controlcmd(atadev, ATA_FLUSHCACHE, 0, 0, 0); } static int -adopen(struct disk *dp) +ad_reinit(device_t dev) { - struct ad_softc *adp = dp->d_drv1; + struct ata_channel *ch = device_get_softc(device_get_parent(dev)); + struct ata_device *atadev = device_get_softc(dev); - if (adp == NULL || adp->device->flags & ATA_D_DETACHING) - return ENXIO; + /* if detach pending flag set, return error */ + + if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATA_MASTER)) || + ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATA_SLAVE))) { + return 1; + } + ad_init(dev); return 0; } static void -adstrategy(struct bio *bp) -{ - struct ad_softc *adp = bp->bio_disk->d_drv1; - - mtx_lock(&adp->queue_mtx); - bioq_disksort(&adp->queue, bp); - mtx_unlock(&adp->queue_mtx); - ata_start(adp->device->channel); -} - -static void -ad_start(struct ata_device *atadev) +ad_strategy(struct bio *bp) { - struct ad_softc *adp = atadev->softc; - struct bio *bp; + device_t dev = bp->bio_disk->d_drv1; + struct ata_device *atadev = device_get_softc(dev); struct ata_request *request; - /* remove request from drive queue */ - mtx_lock(&adp->queue_mtx); - bp = bioq_first(&adp->queue); - if (!bp) { - mtx_unlock(&adp->queue_mtx); - return; - } - bioq_remove(&adp->queue, bp); - mtx_unlock(&adp->queue_mtx); - if (adp->device->flags & ATA_D_DETACHING) { - biofinish(bp, NULL, ENXIO); - return; - } - if (!(request = ata_alloc_request())) { - ata_prtdev(atadev, "FAILURE - out of memory in start\n"); + device_printf(dev, "FAILURE - out of memory in start\n"); biofinish(bp, NULL, ENOMEM); return; } /* setup request */ - request->device = atadev; + request->dev = dev; request->bio = bp; request->callback = ad_done; request->timeout = 5; request->retries = 2; request->data = bp->bio_data; request->bytecount = bp->bio_bcount; - - /* convert LBA contents if this is an old non-LBA device */ - if (atadev->flags & ATA_D_USE_CHS) { - int sector = (bp->bio_pblkno % adp->sectors) + 1; - int cylinder = bp->bio_pblkno / (adp->sectors * adp->heads); - int head = (bp->bio_pblkno % - (adp->sectors * adp->heads)) / adp->sectors; - - request->u.ata.lba = - (sector & 0xff) | (cylinder & 0xffff) << 8 | (head & 0xf) << 24; - } - else - request->u.ata.lba = bp->bio_pblkno; - + request->u.ata.lba = bp->bio_pblkno; request->u.ata.count = request->bytecount / DEV_BSIZE; - request->transfersize = min(bp->bio_bcount, adp->max_iosize); + request->transfersize = min(bp->bio_bcount, atadev->max_iosize); switch (bp->bio_cmd) { case BIO_READ: - request->flags |= ATA_R_READ; + request->flags = ATA_R_READ; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_READ_DMA; request->flags |= ATA_R_DMA; } - else if (adp->max_iosize > DEV_BSIZE) + else if (atadev->max_iosize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL; else request->u.ata.command = ATA_READ; - break; case BIO_WRITE: - request->flags |= ATA_R_WRITE; + request->flags = ATA_R_WRITE; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_WRITE_DMA; request->flags |= ATA_R_DMA; } - else if (adp->max_iosize > DEV_BSIZE) + else if (atadev->max_iosize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL; else request->u.ata.command = ATA_WRITE; break; default: - ata_prtdev(atadev, "FAILURE - unknown BIO operation\n"); + device_printf(dev, "FAILURE - unknown BIO operation\n"); ata_free_request(request); biofinish(bp, NULL, EIO); return; } + request->flags |= ATA_R_ORDERED; ata_queue_request(request); } @@ -350,25 +289,28 @@ ad_done(struct ata_request *request) } static int -addump(void *arg, void *virtual, vm_offset_t physical, +ad_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { - struct ata_request request; struct disk *dp = arg; - struct ad_softc *adp = dp->d_drv1; + device_t dev = dp->d_drv1; + struct ata_device *atadev = device_get_softc(dev); + struct ad_softc *adp = device_get_ivars(dev); + struct ata_channel *ch = device_get_softc(device_get_parent(dev)); + struct ata_request request; if (!adp) return ENXIO; bzero(&request, sizeof(struct ata_request)); - request.device = adp->device; + request.dev = dev; if (length) { request.data = virtual; request.bytecount = length; - request.transfersize = min(length, adp->max_iosize); + request.transfersize = min(length, atadev->max_iosize); request.flags = ATA_R_WRITE; - if (adp->max_iosize > DEV_BSIZE) + if (atadev->max_iosize > DEV_BSIZE) request.u.ata.command = ATA_WRITE_MUL; else request.u.ata.command = ATA_WRITE; @@ -379,13 +321,10 @@ addump(void *arg, void *virtual, vm_offset_t physical, request.u.ata.command = ATA_FLUSHCACHE; request.flags = ATA_R_CONTROL; } - - if (request.device->channel-> - hw.begin_transaction(&request) == ATA_OP_CONTINUES) { + if (ch->hw.begin_transaction(&request) == ATA_OP_CONTINUES) { do { DELAY(20); - } while (request.device->channel-> - hw.end_transaction(&request) == ATA_OP_CONTINUES); + } while (ch->hw.end_transaction(&request) == ATA_OP_CONTINUES); ata_finish(&request); } if (request.status & ATA_S_ERROR) @@ -393,43 +332,75 @@ addump(void *arg, void *virtual, vm_offset_t physical, return 0; } +static void +ad_init(device_t dev) +{ + struct ata_device *atadev = device_get_softc(dev); + + ATA_SETMODE(GRANDPARENT(dev), dev); + + /* enable read caching */ + ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0); + + /* enable write caching if enabled */ + if (ata_wc) + ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0); + else + ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0); + + /* use multiple sectors/interrupt if device supports it */ + if (ad_version(atadev->param.version_major)) { + int secsperint = max(1, min(atadev->param.sectors_intr, 16)); + + if (!ata_controlcmd(atadev, ATA_SET_MULTI, 0, 0, secsperint)) + atadev->max_iosize = secsperint * DEV_BSIZE; + } + else + atadev->max_iosize = DEV_BSIZE; +} + void -ad_print(struct ad_softc *adp) +ad_describe(device_t dev) { - if (bootverbose) { - ata_prtdev(adp->device, "<%.40s/%.8s> ATA-%d disk at ata%d-%s\n", - adp->device->param->model, adp->device->param->revision, - ad_version(adp->device->param->version_major), - device_get_unit(adp->device->channel->dev), - (adp->device->unit == ATA_MASTER) ? "master" : "slave"); - - ata_prtdev(adp->device, - "%lluMB (%llu sectors), %llu C, %u H, %u S, %u B\n", - (unsigned long long)(adp->total_secs / - ((1024L*1024L)/DEV_BSIZE)), - (unsigned long long)adp->total_secs, - (unsigned long long)(adp->total_secs / - (adp->heads * adp->sectors)), - adp->heads, adp->sectors, DEV_BSIZE); - - ata_prtdev(adp->device, "%d secs/int, %d depth queue, %s%s\n", - adp->max_iosize / DEV_BSIZE, adp->num_tags + 1, - (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", - ata_mode2str(adp->device->mode)); + struct ata_channel *ch = device_get_softc(device_get_parent(dev)); + struct ata_device *atadev = device_get_softc(dev); + struct ad_softc *adp = device_get_ivars(dev); + u_int8_t *marker, vendor[64], product[64]; + + /* try to seperate the ATA model string into vendor and model parts */ + if ((marker = index(atadev->param.model, ' ')) || + (marker = index(atadev->param.model, '-'))) { + int len = (marker - atadev->param.model); + + strncpy(vendor, atadev->param.model, len); + vendor[len++] = 0; + strcat(vendor, " "); + strncpy(product, atadev->param.model + len, 40 - len); + vendor[40 - len] = 0; } else { - ata_prtdev(adp->device, - "%lluMB <%.40s/%.8s> [%lld/%d/%d] at ata%d-%s %s%s\n", - (unsigned long long)(adp->total_secs / - ((1024L * 1024L) / DEV_BSIZE)), - adp->device->param->model, adp->device->param->revision, - (unsigned long long)(adp->total_secs / - (adp->heads * adp->sectors)), - adp->heads, adp->sectors, - device_get_unit(adp->device->channel->dev), - (adp->device->unit == ATA_MASTER) ? "master" : "slave", - (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", - ata_mode2str(adp->device->mode)); + if (!strncmp(atadev->param.model, "ST", 2)) + strcpy(vendor, "Seagate "); + else + strcpy(vendor, ""); + strncpy(product, atadev->param.model, 40); + } + + device_printf(dev, "%lluMB <%s%s %.8s> at ata%d-%s %s%s\n", + (unsigned long long)(adp->total_secs / (1048576 / DEV_BSIZE)), + vendor, product, atadev->param.revision, + device_get_unit(ch->dev), + (atadev->unit == ATA_MASTER) ? "master" : "slave", + (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", + ata_mode2str(atadev->mode)); + if (bootverbose) { + device_printf(dev, "%llu sectors [%lldC/%dH/%dS] " + "%d sectors/interrupt %d depth queue\n", + (unsigned long long)adp->total_secs, + (unsigned long long)(adp->total_secs / + (adp->heads * adp->sectors)), + adp->heads, adp->sectors, atadev->max_iosize / DEV_BSIZE, + adp->num_tags + 1); } } @@ -445,3 +416,45 @@ ad_version(u_int16_t version) return bit; return 0; } + +static device_method_t ad_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, ad_identify), + DEVMETHOD(device_probe, ad_probe), + DEVMETHOD(device_attach, ad_attach), + DEVMETHOD(device_detach, ad_detach), + DEVMETHOD(device_shutdown, ad_shutdown), + + /* ATA methods */ + DEVMETHOD(ata_reinit, ad_reinit), + + { 0, 0 } +}; + +static driver_t ad_driver = { + "ad", + ad_methods, + sizeof(struct ad_softc) +}; + +devclass_t ad_devclass; + +static int +ad_modevent(module_t mod, int what, void *arg) +{ + device_t *devs; + int ndevs, i; + + if (what == MOD_UNLOAD) { + if (!devclass_get_devices(ad_devclass, &devs, &ndevs) && devs) { + for (i = 0; i < ndevs; i++) + device_delete_child(device_get_parent(devs[i]), devs[i]); + free(devs, M_TEMP); + } + } + return 0; +} + +DRIVER_MODULE(ad, ata, ad_driver, ad_devclass, ad_modevent, NULL); +MODULE_VERSION(ad, 1); +MODULE_DEPEND(ad, ata, 1, 1, 1); |