diff options
| author | Poul-Henning Kamp <phk@FreeBSD.org> | 2008-03-17 10:33:23 +0000 |
|---|---|---|
| committer | Poul-Henning Kamp <phk@FreeBSD.org> | 2008-03-17 10:33:23 +0000 |
| commit | 72d945abcc61c6a0c4c9dc35a5cfab4159ef8e62 (patch) | |
| tree | 42facee2a8a6b82282cf32d98ef5558ca3765746 /sys/dev | |
| parent | 272870cf7bbcb8fb0204ec80e2ef0c6849eabd23 (diff) | |
Notes
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/ata/ata-all.c | 6 | ||||
| -rw-r--r-- | sys/dev/ata/ata-all.h | 3 | ||||
| -rw-r--r-- | sys/dev/ata/ata-disk.c | 50 | ||||
| -rw-r--r-- | sys/dev/ata/ata-disk.h | 2 |
4 files changed, 59 insertions, 2 deletions
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c index 963b2b506dca..e275b6a042fa 100644 --- a/sys/dev/ata/ata-all.c +++ b/sys/dev/ata/ata-all.c @@ -514,6 +514,12 @@ ata_device_ioctl(device_t dev, u_long cmd, caddr_t data) case IOCATAGMODE: *mode = atadev->mode; return 0; + case IOCATASSPINDOWN: + atadev->spindown = *mode; + return 0; + case IOCATAGSPINDOWN: + *mode = atadev->spindown; + return 0; default: return ENOTTY; } diff --git a/sys/dev/ata/ata-all.h b/sys/dev/ata/ata-all.h index f590c35a694b..28adac9daba6 100644 --- a/sys/dev/ata/ata-all.h +++ b/sys/dev/ata/ata-all.h @@ -403,6 +403,9 @@ struct ata_device { struct ata_params param; /* ata param structure */ int mode; /* current transfermode */ u_int32_t max_iosize; /* max IO size */ + int spindown; /* idle spindown timeout */ + struct callout spindown_timer; + int spindown_state; int flags; #define ATA_D_USE_CHS 0x0001 #define ATA_D_MEDIA_CHANGED 0x0002 diff --git a/sys/dev/ata/ata-disk.c b/sys/dev/ata/ata-disk.c index bac091fd771a..1ed4c208fbf6 100644 --- a/sys/dev/ata/ata-disk.c +++ b/sys/dev/ata/ata-disk.c @@ -171,6 +171,8 @@ ad_attach(device_t dev) device_add_child(dev, "subdisk", device_get_unit(dev)); ad_firmware_geom_adjust(dev, adp->disk); bus_generic_attach(dev); + + callout_init(&atadev->spindown_timer, 1); return 0; } @@ -178,6 +180,7 @@ static int ad_detach(device_t dev) { struct ad_softc *adp = device_get_ivars(dev); + struct ata_device *atadev = device_get_softc(dev); device_t *children; int nchildren, i; @@ -185,6 +188,9 @@ ad_detach(device_t dev) if (!device_get_ivars(dev)) return ENXIO; + /* destroy the power timeout */ + callout_drain(&atadev->spindown_timer); + /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) @@ -229,6 +235,38 @@ ad_reinit(device_t dev) return 0; } +static void +ad_power_callback(struct ata_request *request) +{ + device_printf(request->dev, "drive spun down.\n"); + ata_free_request(request); +} + +static void +ad_spindown(void *priv) +{ + device_t dev = priv; + struct ata_device *atadev = device_get_softc(dev); + struct ata_request *request; + + if(atadev->spindown == 0) + return; + device_printf(dev, "Idle, spin down\n"); + atadev->spindown_state = 1; + if (!(request = ata_alloc_request())) { + device_printf(dev, "FAILURE - out of memory in ad_spindown\n"); + return; + } + request->flags = ATA_R_CONTROL; + request->dev = dev; + request->timeout = 5; + request->retries = 1; + request->callback = ad_power_callback; + request->u.ata.command = ATA_STANDBY_IMMEDIATE; + ata_queue_request(request); +} + + static void ad_strategy(struct bio *bp) { @@ -236,6 +274,10 @@ ad_strategy(struct bio *bp) struct ata_device *atadev = device_get_softc(dev); struct ata_request *request; + if (atadev->spindown != 0) + callout_reset(&atadev->spindown_timer, hz * atadev->spindown, + ad_spindown, dev); + if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in start\n"); biofinish(bp, NULL, ENOMEM); @@ -246,7 +288,13 @@ ad_strategy(struct bio *bp) request->dev = dev; request->bio = bp; request->callback = ad_done; - request->timeout = 5; + if (atadev->spindown_state) { + device_printf(dev, "request while spun down, starting.\n"); + atadev->spindown_state = 0; + request->timeout = 31; + } else { + request->timeout = 5; + } request->retries = 2; request->data = bp->bio_data; request->bytecount = bp->bio_bcount; diff --git a/sys/dev/ata/ata-disk.h b/sys/dev/ata/ata-disk.h index 1ebd3e0d5b64..562b32ff04c8 100644 --- a/sys/dev/ata/ata-disk.h +++ b/sys/dev/ata/ata-disk.h @@ -34,7 +34,7 @@ struct ad_softc { u_int32_t transfersize; /* size of each transfer */ int num_tags; /* number of tags supported */ int flags; /* drive flags */ -#define AD_F_LABELLING 0x0001 +#define AD_F_LABELLING 0x0001 #define AD_F_CHS_USED 0x0002 #define AD_F_32B_ENABLED 0x0004 #define AD_F_TAG_ENABLED 0x0008 |
