summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata/ata-disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ata/ata-disk.c')
-rw-r--r--sys/dev/ata/ata-disk.c469
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);
OpenPOWER on IntegriCloud