144454Ssos/*-
2178067Ssos * Copyright (c) 1998 - 2008 S�ren Schmidt <sos@FreeBSD.org>
344454Ssos * All rights reserved.
444454Ssos *
544454Ssos * Redistribution and use in source and binary forms, with or without
644454Ssos * modification, are permitted provided that the following conditions
744454Ssos * are met:
844454Ssos * 1. Redistributions of source code must retain the above copyright
944454Ssos *    notice, this list of conditions and the following disclaimer,
1044454Ssos *    without modification, immediately at the beginning of the file.
1144454Ssos * 2. Redistributions in binary form must reproduce the above copyright
1244454Ssos *    notice, this list of conditions and the following disclaimer in the
1344454Ssos *    documentation and/or other materials provided with the distribution.
1444454Ssos *
1544454Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1644454Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1744454Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1844454Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1944454Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2044454Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2144454Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2244454Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2344454Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2444454Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2544454Ssos */
2644454Ssos
27119418Sobrien#include <sys/cdefs.h>
28119418Sobrien__FBSDID("$FreeBSD$");
29119418Sobrien
3044454Ssos#include <sys/param.h>
3144454Ssos#include <sys/systm.h>
3274302Ssos#include <sys/ata.h>
3344454Ssos#include <sys/kernel.h>
34144330Ssos#include <sys/module.h>
3544454Ssos#include <sys/malloc.h>
3660041Sphk#include <sys/bio.h>
3754594Ssos#include <sys/bus.h>
3851520Ssos#include <sys/conf.h>
39156320Ssos#include <sys/endian.h>
4044454Ssos#include <sys/cdio.h>
41124403Ssos#include <sys/sema.h>
42119404Ssos#include <sys/taskqueue.h>
43124534Ssos#include <vm/uma.h>
4493882Ssos#include <machine/bus.h>
45112946Sphk#include <geom/geom_disk.h>
4644454Ssos#include <dev/ata/ata-all.h>
4744454Ssos#include <dev/ata/atapi-fd.h>
48144330Ssos#include <ata_if.h>
4944454Ssos
50144330Ssos
5151520Ssos/* prototypes */
52144330Ssosstatic disk_open_t afd_open;
53144330Ssosstatic disk_close_t afd_close;
54144330Ssosstatic disk_strategy_t afd_strategy;
55146266Ssosstatic disk_ioctl_t afd_ioctl;
56144330Ssosstatic int afd_sense(device_t);
57144330Ssosstatic void afd_describe(device_t);
58119404Ssosstatic void afd_done(struct ata_request *);
59144330Ssosstatic int afd_prevent_allow(device_t, int);
60144330Ssosstatic int afd_test_ready(device_t);
6144454Ssos
6251520Ssos/* internal vars */
63151897Srwatsonstatic MALLOC_DEFINE(M_AFD, "afd_driver", "ATAPI floppy driver buffers");
6451520Ssos
65144330Ssosstatic int
66144330Ssosafd_probe(device_t dev)
67144330Ssos{
68145102Ssos    struct ata_device *atadev = device_get_softc(dev);
69145102Ssos    if ((atadev->param.config & ATA_PROTO_ATAPI) &&
70145102Ssos	(atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_DIRECT)
71145102Ssos	return 0;
72145102Ssos    else
73145102Ssos	return ENXIO;
74144330Ssos}
75144330Ssos
76144330Ssosstatic int
77144330Ssosafd_attach(device_t dev)
78144330Ssos{
79144330Ssos    struct ata_channel *ch = device_get_softc(device_get_parent(dev));
80144330Ssos    struct ata_device *atadev = device_get_softc(dev);
8144454Ssos    struct afd_softc *fdp;
8244454Ssos
83144330Ssos    if (!(fdp = malloc(sizeof(struct afd_softc), M_AFD, M_NOWAIT | M_ZERO))) {
84144330Ssos	device_printf(dev, "out of memory\n");
85144330Ssos	return ENOMEM;
8644454Ssos    }
87144330Ssos    device_set_ivars(dev, fdp);
88200171Smav    ata_setmode(dev);
8974564Ssos
90144330Ssos    if (afd_sense(dev)) {
91144330Ssos	device_set_ivars(dev, NULL);
9252067Ssos	free(fdp, M_AFD);
93144330Ssos	return ENXIO;
9444454Ssos    }
95119404Ssos    atadev->flags |= ATA_D_MEDIA_CHANGED;
96119404Ssos
97144330Ssos    /* announce we are here */
98144330Ssos    afd_describe(dev);
99144330Ssos
100144330Ssos    /* create the disk device */
101125975Sphk    fdp->disk = disk_alloc();
102125975Sphk    fdp->disk->d_open = afd_open;
103125975Sphk    fdp->disk->d_close = afd_close;
104144330Ssos    fdp->disk->d_strategy = afd_strategy;
105146266Ssos    fdp->disk->d_ioctl = afd_ioctl;
106125975Sphk    fdp->disk->d_name = "afd";
107144330Ssos    fdp->disk->d_drv1 = dev;
108178856Sgrehan    fdp->disk->d_maxsize = ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS;
109144330Ssos    fdp->disk->d_unit = device_get_unit(dev);
110125975Sphk    disk_create(fdp->disk, DISK_VERSION);
111144330Ssos    return 0;
11244454Ssos}
11344454Ssos
114144330Ssosstatic int
115144330Ssosafd_detach(device_t dev)
11657325Ssos{
117144330Ssos    struct afd_softc *fdp = device_get_ivars(dev);
118156320Ssos
119156320Ssos    /* check that we have a valid device to detach */
120156320Ssos    if (!device_get_ivars(dev))
121156320Ssos        return ENXIO;
12257325Ssos
123144330Ssos    /* detroy disk from the system so we dont get any further requests */
124125975Sphk    disk_destroy(fdp->disk);
125144330Ssos
126144330Ssos    /* fail requests on the queue and any thats "in flight" for this device */
127145713Ssos    ata_fail_requests(dev);
128144330Ssos
129144330Ssos    /* dont leave anything behind */
130144330Ssos    device_set_ivars(dev, NULL);
13157325Ssos    free(fdp, M_AFD);
132144330Ssos    return 0;
133144330Ssos}
13457325Ssos
135188126Simpstatic int
136144330Ssosafd_shutdown(device_t dev)
13744454Ssos{
138144330Ssos    struct ata_device *atadev = device_get_softc(dev);
13944454Ssos
140144330Ssos    if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
141145713Ssos	ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
142188126Simp    return 0;
14344454Ssos}
14444454Ssos
145144330Ssosstatic int
146144330Ssosafd_reinit(device_t dev)
14744454Ssos{
148144330Ssos    struct ata_channel *ch = device_get_softc(device_get_parent(dev));
149144330Ssos    struct ata_device *atadev = device_get_softc(dev);
150188897Smav
151188897Smav    /* if detach pending, return error */
152188897Smav    if (!(ch->devices & (ATA_ATAPI_MASTER << atadev->unit)))
153144330Ssos	return 1;
154188897Smav
155200171Smav    ata_setmode(dev);
156144330Ssos    return 0;
15744454Ssos}
15844454Ssos
15944475Ssosstatic int
160119404Ssosafd_open(struct disk *dp)
16144454Ssos{
162144330Ssos    device_t dev = dp->d_drv1;
163144330Ssos    struct ata_device *atadev = device_get_softc(dev);
164144330Ssos    struct afd_softc *fdp = device_get_ivars(dev);
16544454Ssos
166144330Ssos    if (!fdp)
167119404Ssos	return ENXIO;
168144330Ssos    if (!device_is_attached(dev))
169144330Ssos	return EBUSY;
170107562Ssos
171144330Ssos    afd_test_ready(dev);
172144330Ssos    afd_prevent_allow(dev, 1);
17356819Ssos
174144330Ssos    if (afd_sense(dev))
175144330Ssos	device_printf(dev, "sense media type failed\n");
176144330Ssos    atadev->flags &= ~ATA_D_MEDIA_CHANGED;
17756819Ssos
178156320Ssos    if (!fdp->mediasize)
179156396Ssos	return ENXIO;
180156320Ssos
181156320Ssos    fdp->disk->d_sectorsize = fdp->sectorsize;
182156320Ssos    fdp->disk->d_mediasize = fdp->mediasize;
183156320Ssos    fdp->disk->d_fwsectors = fdp->sectors;
184156320Ssos    fdp->disk->d_fwheads = fdp->heads;
18551520Ssos    return 0;
18644454Ssos}
18744454Ssos
18844475Ssosstatic int
189119404Ssosafd_close(struct disk *dp)
19044454Ssos{
191144330Ssos    device_t dev = dp->d_drv1;
19244454Ssos
193144330Ssos    afd_prevent_allow(dev, 0);
19444454Ssos    return 0;
19544454Ssos}
19644454Ssos
19744454Ssosstatic void
198144330Ssosafd_strategy(struct bio *bp)
19944454Ssos{
200144330Ssos    device_t dev = bp->bio_disk->d_drv1;
201144330Ssos    struct ata_device *atadev = device_get_softc(dev);
202144330Ssos    struct afd_softc *fdp = device_get_ivars(dev);
203144330Ssos    struct ata_request *request;
204144330Ssos    u_int16_t count;
205144330Ssos    int8_t ccb[16];
20644454Ssos
20798429Ssos    /* if it's a null transfer, return immediatly. */
20898429Ssos    if (bp->bio_bcount == 0) {
20998429Ssos	bp->bio_resid = 0;
21098429Ssos	biodone(bp);
21198429Ssos	return;
21298429Ssos    }
21398429Ssos
21448213Ssos    /* should reject all queued entries if media have changed. */
215144330Ssos    if (atadev->flags & ATA_D_MEDIA_CHANGED) {
21676322Sphk	biofinish(bp, NULL, EIO);
21751520Ssos	return;
21844454Ssos    }
21944454Ssos
220156320Ssos    count = bp->bio_bcount / fdp->sectorsize;
22198429Ssos    bp->bio_resid = bp->bio_bcount;
22244454Ssos
22347272Ssos    bzero(ccb, sizeof(ccb));
22447272Ssos
22559249Sphk    if (bp->bio_cmd == BIO_READ)
22644454Ssos	ccb[0] = ATAPI_READ_BIG;
22744454Ssos    else
22844454Ssos	ccb[0] = ATAPI_WRITE_BIG;
22944454Ssos
230144330Ssos    ccb[2] = bp->bio_pblkno >> 24;
231144330Ssos    ccb[3] = bp->bio_pblkno >> 16;
232144330Ssos    ccb[4] = bp->bio_pblkno >> 8;
233144330Ssos    ccb[5] = bp->bio_pblkno;
23444454Ssos    ccb[7] = count>>8;
23544454Ssos    ccb[8] = count;
23644454Ssos
237178278Ssos    if (!(request = ata_alloc_request())) {
238124419Ssos	biofinish(bp, NULL, ENOMEM);
239119404Ssos	return;
240119404Ssos    }
241178278Ssos    request->dev = dev;
242144330Ssos    request->bio = bp;
243198407Smav    bcopy(ccb, request->u.atapi.ccb, 16);
244119404Ssos    request->data = bp->bio_data;
245156320Ssos    request->bytecount = count * fdp->sectorsize;
246119404Ssos    request->transfersize = min(request->bytecount, 65534);
247119404Ssos    request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 60 : 30;
248119404Ssos    request->retries = 2;
249119404Ssos    request->callback = afd_done;
250119404Ssos    switch (bp->bio_cmd) {
251119404Ssos    case BIO_READ:
252144330Ssos	request->flags = (ATA_R_ATAPI | ATA_R_READ);
253119404Ssos	break;
254119404Ssos    case BIO_WRITE:
255144330Ssos	request->flags = (ATA_R_ATAPI | ATA_R_WRITE);
256119404Ssos	break;
257119404Ssos    default:
258144330Ssos	device_printf(dev, "unknown BIO operation\n");
259119404Ssos	ata_free_request(request);
260119404Ssos	biofinish(bp, NULL, EIO);
261119404Ssos	return;
262119404Ssos    }
263144330Ssos    if (atadev->mode >= ATA_DMA)
264144330Ssos	request->flags |= ATA_R_DMA;
265144330Ssos    request->flags |= ATA_R_ORDERED;
266119404Ssos    ata_queue_request(request);
26747334Ssos}
26844454Ssos
269119404Ssosstatic void
270119404Ssosafd_done(struct ata_request *request)
27144454Ssos{
272144330Ssos    struct bio *bp = request->bio;
27344454Ssos
274119404Ssos    /* finish up transfer */
275119404Ssos    if ((bp->bio_error = request->result))
27659249Sphk	bp->bio_flags |= BIO_ERROR;
277119404Ssos    bp->bio_resid = bp->bio_bcount - request->donecount;
278111979Sphk    biodone(bp);
279119404Ssos    ata_free_request(request);
28044454Ssos}
28144454Ssos
282146266Ssosstatic int
283146266Ssosafd_ioctl(struct disk *disk, u_long cmd, void *data, int flag,struct thread *td)
284146266Ssos{
285146266Ssos    return ata_device_ioctl(disk->d_drv1, cmd, data);
286146266Ssos}
287146266Ssos
28866070Ssosstatic int
289144330Ssosafd_sense(device_t dev)
29044454Ssos{
291144330Ssos    struct ata_device *atadev = device_get_softc(dev);
292144330Ssos    struct afd_softc *fdp = device_get_ivars(dev);
293156320Ssos    struct afd_capacity capacity;
294156320Ssos    struct afd_capacity_big capacity_big;
295156320Ssos    struct afd_capabilities capabilities;
296156320Ssos    int8_t ccb1[16] = { ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0,
297156320Ssos                        0, 0, 0, 0, 0, 0, 0, 0 };
298156320Ssos    int8_t ccb2[16] = { ATAPI_SERVICE_ACTION_IN, 0x10, 0, 0, 0, 0, 0, 0, 0, 0,
299156320Ssos			0, 0, 0, sizeof(struct afd_capacity_big) & 0xff, 0, 0 };
300156320Ssos    int8_t ccb3[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_REWRITEABLE_CAP_PAGE,
301156320Ssos		        0, 0, 0, 0, sizeof(struct afd_capabilities) >> 8,
302156320Ssos		        sizeof(struct afd_capabilities) & 0xff,
303156320Ssos			0, 0, 0, 0, 0, 0, 0 };
304156396Ssos    int timeout = 20;
305156425Ssos    int error, count;
306144330Ssos
307156425Ssos    fdp->mediasize = 0;
308156425Ssos
309156396Ssos    /* wait for device to get ready */
310156425Ssos    while ((error = afd_test_ready(dev)) && timeout--) {
311156396Ssos	DELAY(100000);
312156425Ssos    }
313156838Ssos    if (error == EBUSY)
314156425Ssos	return 1;
315156320Ssos
316144330Ssos    /* The IOMEGA Clik! doesn't support reading the cap page, fake it */
317144330Ssos    if (!strncmp(atadev->param.model, "IOMEGA Clik!", 12)) {
318156320Ssos	fdp->heads = 1;
319156320Ssos	fdp->sectors = 2;
320156320Ssos	fdp->mediasize = 39441 * 1024;
321156320Ssos	fdp->sectorsize = 512;
322144330Ssos	afd_test_ready(dev);
323144330Ssos	return 0;
324144330Ssos    }
325144330Ssos
326156320Ssos    /* get drive capacity */
327156320Ssos    if (!ata_atapicmd(dev, ccb1, (caddr_t)&capacity,
328156320Ssos		      sizeof(struct afd_capacity), ATA_R_READ, 30)) {
329156320Ssos	fdp->heads = 16;
330156320Ssos	fdp->sectors = 63;
331156320Ssos	fdp->sectorsize = be32toh(capacity.blocksize);
332156320Ssos	fdp->mediasize = (u_int64_t)be32toh(capacity.capacity)*fdp->sectorsize;
333156320Ssos	afd_test_ready(dev);
334156320Ssos	return 0;
335156320Ssos    }
336156320Ssos
337156320Ssos    /* get drive capacity big */
338156320Ssos    if (!ata_atapicmd(dev, ccb2, (caddr_t)&capacity_big,
339156320Ssos		      sizeof(struct afd_capacity_big),
340156320Ssos		      ATA_R_READ | ATA_R_QUIET, 30)) {
341156320Ssos	fdp->heads = 16;
342156320Ssos	fdp->sectors = 63;
343156320Ssos	fdp->sectorsize = be32toh(capacity_big.blocksize);
344156320Ssos	fdp->mediasize = be64toh(capacity_big.capacity)*fdp->sectorsize;
345156320Ssos	afd_test_ready(dev);
346156320Ssos	return 0;
347156320Ssos    }
348156320Ssos
349144330Ssos    /* get drive capabilities, some bugridden drives needs this repeated */
350144330Ssos    for (count = 0 ; count < 5 ; count++) {
351156320Ssos	if (!ata_atapicmd(dev, ccb3, (caddr_t)&capabilities,
352156320Ssos			  sizeof(struct afd_capabilities), ATA_R_READ, 30) &&
353156320Ssos	    capabilities.page_code == ATAPI_REWRITEABLE_CAP_PAGE) {
354156320Ssos	    fdp->heads = capabilities.heads;
355156320Ssos	    fdp->sectors = capabilities.sectors;
356156320Ssos	    fdp->sectorsize = be16toh(capabilities.sector_size);
357156320Ssos	    fdp->mediasize = be16toh(capabilities.cylinders) *
358156320Ssos			     fdp->heads * fdp->sectors * fdp->sectorsize;
359156320Ssos	    if (!capabilities.medium_type)
360156320Ssos		fdp->mediasize = 0;
36151520Ssos	    return 0;
362144330Ssos	}
36344454Ssos    }
364144330Ssos    return 1;
36544454Ssos}
36644454Ssos
36766070Ssosstatic int
368144330Ssosafd_prevent_allow(device_t dev, int lock)
36944454Ssos{
370144330Ssos    struct ata_device *atadev = device_get_softc(dev);
37151520Ssos    int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
37251520Ssos		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
37351520Ssos
374144330Ssos    if (!strncmp(atadev->param.model, "IOMEGA Clik!", 12))
37574251Ssos	return 0;
376145713Ssos    return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
37751520Ssos}
378119404Ssos
379119404Ssosstatic int
380144330Ssosafd_test_ready(device_t dev)
381119404Ssos{
382119404Ssos    int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
383119404Ssos		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
384119404Ssos
385145713Ssos    return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
386119404Ssos}
387144330Ssos
388144330Ssosstatic void
389144330Ssosafd_describe(device_t dev)
390144330Ssos{
391144330Ssos    struct ata_channel *ch = device_get_softc(device_get_parent(dev));
392144330Ssos    struct ata_device *atadev = device_get_softc(dev);
393144330Ssos    struct afd_softc *fdp = device_get_ivars(dev);
394157283Smarcel    char sizestring[16];
395144330Ssos
396156320Ssos    if (fdp->mediasize > 1048576 * 5)
397156320Ssos	sprintf(sizestring, "%juMB", fdp->mediasize / 1048576);
398156320Ssos    else if (fdp->mediasize)
399156320Ssos	sprintf(sizestring, "%juKB", fdp->mediasize / 1024);
400156320Ssos    else
401156320Ssos	strcpy(sizestring, "(no media)");
402156320Ssos
403200171Smav    device_printf(dev, "%s <%.40s %.8s> at ata%d-%s %s %s\n",
404156320Ssos		  sizestring, atadev->param.model, atadev->param.revision,
405178067Ssos		  device_get_unit(ch->dev), ata_unit2str(atadev),
406200171Smav		  ata_mode2str(atadev->mode),
407200171Smav		  ata_satarev2str(ATA_GETREV(device_get_parent(dev), atadev->unit)));
408144330Ssos    if (bootverbose) {
409156320Ssos	device_printf(dev, "%ju sectors [%juC/%dH/%dS]\n",
410156320Ssos	    	      fdp->mediasize / fdp->sectorsize,
411156320Ssos	    	      fdp->mediasize /(fdp->sectorsize*fdp->sectors*fdp->heads),
412156320Ssos	    	      fdp->heads, fdp->sectors);
413144330Ssos    }
414144330Ssos}
415144330Ssos
416144330Ssosstatic device_method_t afd_methods[] = {
417144330Ssos    /* device interface */
418144330Ssos    DEVMETHOD(device_probe,     afd_probe),
419144330Ssos    DEVMETHOD(device_attach,    afd_attach),
420144330Ssos    DEVMETHOD(device_detach,    afd_detach),
421144330Ssos    DEVMETHOD(device_shutdown,  afd_shutdown),
422144330Ssos
423144330Ssos    /* ATA methods */
424144330Ssos    DEVMETHOD(ata_reinit,       afd_reinit),
425144330Ssos
426233717Smarius    DEVMETHOD_END
427144330Ssos};
428144330Ssos
429144330Ssosstatic driver_t afd_driver = {
430144330Ssos    "afd",
431144330Ssos    afd_methods,
432144397Ssos    0,
433144330Ssos};
434144330Ssos
435144330Ssosstatic devclass_t afd_devclass;
436144330Ssos
437145102SsosDRIVER_MODULE(afd, ata, afd_driver, afd_devclass, NULL, NULL);
438144330SsosMODULE_VERSION(afd, 1);
439144330SsosMODULE_DEPEND(afd, ata, 1, 1, 1);
440144330Ssos
441