Merge from vendor branch LIBSTDC++:
[dragonfly.git] / sys / dev / disk / ata / atapi-fd.c
1 /*-
2  * Copyright (c) 1998,1999,2000,2001,2002 Søren Schmidt <sos@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/sys/dev/ata/atapi-fd.c,v 1.44.2.9 2002/07/31 11:19:26 sos Exp $
29  * $DragonFly: src/sys/dev/disk/ata/atapi-fd.c,v 1.9 2003/11/30 20:14:18 dillon Exp $
30  */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/ata.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/buf.h>
38 #include <sys/bus.h>
39 #include <sys/conf.h>
40 #include <sys/disk.h>
41 #include <sys/devicestat.h>
42 #include <sys/cdio.h>
43 #include <sys/proc.h>
44 #include <sys/buf2.h>
45 #include "ata-all.h"
46 #include "atapi-all.h"
47 #include "atapi-fd.h"
48
49 /* device structures */
50 static  d_open_t        afdopen;
51 static  d_close_t       afdclose;
52 static  d_ioctl_t       afdioctl;
53 static  d_strategy_t    afdstrategy;
54
55 static struct cdevsw afd_cdevsw = {
56         /* name */      "afd",
57         /* maj */       118,
58         /* flags */     D_DISK | D_TRACKCLOSE,
59         /* port */      NULL,
60         /* autoq */     0,
61
62         /* open */      afdopen,
63         /* close */     afdclose,
64         /* read */      physread,
65         /* write */     physwrite,
66         /* ioctl */     afdioctl,
67         /* poll */      nopoll,
68         /* mmap */      nommap,
69         /* strategy */  afdstrategy,
70         /* dump */      nodump,
71         /* psize */     nopsize
72 };
73
74 /* prototypes */
75 static int afd_sense(struct afd_softc *);
76 static void afd_describe(struct afd_softc *);
77 static int afd_done(struct atapi_request *);
78 static int afd_eject(struct afd_softc *, int);
79 static int afd_start_stop(struct afd_softc *, int);
80 static int afd_prevent_allow(struct afd_softc *, int);
81
82 /* internal vars */
83 static u_int32_t afd_lun_map = 0;
84 static MALLOC_DEFINE(M_AFD, "AFD driver", "ATAPI floppy driver buffers");
85
86 int 
87 afdattach(struct ata_device *atadev)
88 {
89     struct afd_softc *fdp;
90     dev_t dev;
91
92     fdp = malloc(sizeof(struct afd_softc), M_AFD, M_WAITOK | M_ZERO);
93     if (!fdp) {
94         ata_prtdev(atadev, "out of memory\n");
95         return 0;
96     }
97
98     fdp->device = atadev;
99     fdp->lun = ata_get_lun(&afd_lun_map);
100     ata_set_name(atadev, "afd", fdp->lun);
101     bufq_init(&fdp->queue);
102
103     if (afd_sense(fdp)) {
104         free(fdp, M_AFD);
105         return 0;
106     }
107
108     devstat_add_entry(&fdp->stats, "afd", fdp->lun, DEV_BSIZE,
109                       DEVSTAT_NO_ORDERED_TAGS,
110                       DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE,
111                       DEVSTAT_PRIORITY_WFD);
112     dev = disk_create(fdp->lun, &fdp->disk, 0, &afd_cdevsw);
113     dev->si_drv1 = fdp;
114     fdp->dev = dev;
115
116     if (!strncmp(atadev->param->model, "IOMEGA ZIP", 10) ||
117         !strncmp(atadev->param->model, "IOMEGA Clik!", 12))
118         fdp->dev->si_iosize_max = 64 * DEV_BSIZE;
119     else
120         fdp->dev->si_iosize_max = 252 * DEV_BSIZE;
121
122     afd_describe(fdp);
123     atadev->flags |= ATA_D_MEDIA_CHANGED;
124     atadev->driver = fdp;
125     return 1;
126 }
127
128 void
129 afddetach(struct ata_device *atadev)
130 {   
131     struct afd_softc *fdp = atadev->driver;
132     struct buf *bp;
133     
134     while ((bp = bufq_first(&fdp->queue))) {
135         bufq_remove(&fdp->queue, bp);
136         bp->b_flags |= B_ERROR;
137         bp->b_error = ENXIO;
138         biodone(bp);
139     }
140     disk_invalidate(&fdp->disk);
141     disk_destroy(&fdp->disk);
142     devstat_remove_entry(&fdp->stats);
143     ata_free_name(atadev);
144     ata_free_lun(&afd_lun_map, fdp->lun);
145     free(fdp, M_AFD);
146     atadev->driver = NULL;
147 }   
148
149 static int 
150 afd_sense(struct afd_softc *fdp)
151 {
152     int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_REWRITEABLE_CAP_PAGE,
153                        0, 0, 0, 0, sizeof(struct afd_cappage) >> 8,
154                        sizeof(struct afd_cappage) & 0xff, 0, 0, 0, 0, 0, 0, 0 };
155     int count, error = 0;
156
157     /* The IOMEGA Clik! doesn't support reading the cap page, fake it */
158     if (!strncmp(fdp->device->param->model, "IOMEGA Clik!", 12)) {
159         fdp->cap.transfer_rate = 500;
160         fdp->cap.heads = 1;
161         fdp->cap.sectors = 2;
162         fdp->cap.cylinders = 39441;
163         fdp->cap.sector_size = 512;
164         atapi_test_ready(fdp->device);
165         return 0;
166     }
167
168     /* get drive capabilities, some drives needs this repeated */
169     for (count = 0 ; count < 5 ; count++) {
170         if (!(error = atapi_queue_cmd(fdp->device, ccb, (caddr_t)&fdp->cap,
171                                       sizeof(struct afd_cappage),
172                                       ATPR_F_READ, 30, NULL, NULL)))
173             break;
174     }
175     if (error || fdp->cap.page_code != ATAPI_REWRITEABLE_CAP_PAGE)
176         return 1;   
177     fdp->cap.cylinders = ntohs(fdp->cap.cylinders);
178     fdp->cap.sector_size = ntohs(fdp->cap.sector_size);
179     fdp->cap.transfer_rate = ntohs(fdp->cap.transfer_rate);
180     return 0;
181 }
182
183 static void 
184 afd_describe(struct afd_softc *fdp)
185 {
186     if (bootverbose) {
187         ata_prtdev(fdp->device,
188                    "<%.40s/%.8s> rewriteable drive at ata%d as %s\n",
189                    fdp->device->param->model, fdp->device->param->revision,
190                    device_get_unit(fdp->device->channel->dev),
191                    (fdp->device->unit == ATA_MASTER) ? "master" : "slave");
192         ata_prtdev(fdp->device,
193                    "%luMB (%u sectors), %u cyls, %u heads, %u S/T, %u B/S\n",
194                    (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) / 
195                    ((1024L * 1024L) / fdp->cap.sector_size),
196                    fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors,
197                    fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors,
198                    fdp->cap.sector_size);
199         ata_prtdev(fdp->device, "%dKB/s,", fdp->cap.transfer_rate / 8);
200         printf(" %s\n", ata_mode2str(fdp->device->mode));
201         if (fdp->cap.medium_type) {
202             ata_prtdev(fdp->device, "Medium: ");
203             switch (fdp->cap.medium_type) {
204             case MFD_2DD:
205                 printf("720KB DD disk"); break;
206
207             case MFD_HD_12:
208                 printf("1.2MB HD disk"); break;
209
210             case MFD_HD_144:
211                 printf("1.44MB HD disk"); break;
212
213             case MFD_UHD: 
214                 printf("120MB UHD disk"); break;
215
216             default:
217                 printf("Unknown (0x%x)", fdp->cap.medium_type);
218             }
219             if (fdp->cap.wp) printf(", writeprotected");
220         }
221         printf("\n");
222     }
223     else {
224         ata_prtdev(fdp->device, "%luMB <%.40s> [%d/%d/%d] at ata%d-%s %s\n",
225                    (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) /
226                    ((1024L * 1024L) / fdp->cap.sector_size),    
227                    fdp->device->param->model,
228                    fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors,
229                    device_get_unit(fdp->device->channel->dev),
230                    (fdp->device->unit == ATA_MASTER) ? "master" : "slave",
231                    ata_mode2str(fdp->device->mode));
232     }
233 }
234
235 static int
236 afdopen(dev_t dev, int flags, int fmt, struct thread *td)
237 {
238     struct afd_softc *fdp = dev->si_drv1;
239     struct disklabel *label = &fdp->disk.d_label;
240
241     atapi_test_ready(fdp->device);
242
243     if (count_dev(dev) == 1)
244         afd_prevent_allow(fdp, 1);
245
246     if (afd_sense(fdp))
247         ata_prtdev(fdp->device, "sense media type failed\n");
248
249     fdp->device->flags &= ~ATA_D_MEDIA_CHANGED;
250
251     bzero(label, sizeof *label);
252     label->d_secsize = fdp->cap.sector_size;
253     label->d_nsectors = fdp->cap.sectors;  
254     label->d_ntracks = fdp->cap.heads;
255     label->d_ncylinders = fdp->cap.cylinders;
256     label->d_secpercyl = fdp->cap.sectors * fdp->cap.heads;
257     label->d_secperunit = label->d_secpercyl * fdp->cap.cylinders;
258     return 0;
259 }
260
261 static int 
262 afdclose(dev_t dev, int flags, int fmt, struct thread *td)
263 {
264     struct afd_softc *fdp = dev->si_drv1;
265
266     if (count_dev(dev) == 1)
267         afd_prevent_allow(fdp, 0); 
268     return 0;
269 }
270
271 static int 
272 afdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
273 {
274     struct afd_softc *fdp = dev->si_drv1;
275
276     switch (cmd) {
277     case CDIOCEJECT:
278         if (count_dev(dev) > 1)
279             return EBUSY;
280         return afd_eject(fdp, 0);
281
282     case CDIOCCLOSE:
283         if (count_dev(dev) > 1)
284             return 0;
285         return afd_eject(fdp, 1);
286
287     default:
288         return ENOIOCTL;
289     }
290 }
291
292 static void 
293 afdstrategy(struct buf *bp)
294 {
295     struct afd_softc *fdp = bp->b_dev->si_drv1;
296     int s;
297
298     if (fdp->device->flags & ATA_D_DETACHING) {
299         bp->b_flags |= B_ERROR;
300         bp->b_error = ENXIO;
301         biodone(bp);
302         return;
303     }
304
305     /* if it's a null transfer, return immediatly. */
306     if (bp->b_bcount == 0) {
307         bp->b_resid = 0;
308         biodone(bp);
309         return;
310     }
311
312     s = splbio();
313     bufqdisksort(&fdp->queue, bp);
314     splx(s);
315     ata_start(fdp->device->channel);
316 }
317
318 void 
319 afd_start(struct ata_device *atadev)
320 {
321     struct afd_softc *fdp = atadev->driver;
322     struct buf *bp = bufq_first(&fdp->queue);
323     u_int32_t lba;
324     u_int16_t count;
325     int8_t ccb[16];
326     caddr_t data_ptr;
327
328     if (!bp)
329         return;
330
331     bufq_remove(&fdp->queue, bp);
332
333     /* should reject all queued entries if media have changed. */
334     if (fdp->device->flags & ATA_D_MEDIA_CHANGED) {
335         bp->b_flags |= B_ERROR;
336         bp->b_error = EIO;
337         biodone(bp);
338         return;
339     }
340
341     lba = bp->b_pblkno;
342     count = bp->b_bcount / fdp->cap.sector_size;
343     data_ptr = bp->b_data;
344     bp->b_resid = bp->b_bcount; 
345
346     bzero(ccb, sizeof(ccb));
347
348     if (bp->b_flags & B_READ)
349         ccb[0] = ATAPI_READ_BIG;
350     else
351         ccb[0] = ATAPI_WRITE_BIG;
352
353     ccb[2] = lba>>24;
354     ccb[3] = lba>>16;
355     ccb[4] = lba>>8;
356     ccb[5] = lba;
357     ccb[7] = count>>8;
358     ccb[8] = count;
359
360     devstat_start_transaction(&fdp->stats);
361
362     atapi_queue_cmd(fdp->device, ccb, data_ptr, count * fdp->cap.sector_size,
363                     (bp->b_flags & B_READ) ? ATPR_F_READ : 0, 30,
364                     afd_done, bp);
365 }
366
367 static int 
368 afd_done(struct atapi_request *request)
369 {
370     struct buf *bp = request->driver;
371     struct afd_softc *fdp = request->device->driver;
372
373     if (request->error || (bp->b_flags & B_ERROR)) {
374         bp->b_error = request->error;
375         bp->b_flags |= B_ERROR;
376     }
377     else
378         bp->b_resid = bp->b_bcount - request->donecount;
379     devstat_end_transaction_buf(&fdp->stats, bp);
380     biodone(bp);
381     return 0;
382 }
383
384 static int 
385 afd_eject(struct afd_softc *fdp, int close)
386 {
387     int error;
388      
389     if ((error = afd_start_stop(fdp, 0)) == EBUSY) {
390         if (!close)
391             return 0;
392         if ((error = afd_start_stop(fdp, 3)))
393             return error;
394         return afd_prevent_allow(fdp, 1);
395     }
396     if (error)
397         return error;
398     if (close)
399         return 0;
400     if ((error = afd_prevent_allow(fdp, 0)))
401         return error;
402     fdp->device->flags |= ATA_D_MEDIA_CHANGED;
403     return afd_start_stop(fdp, 2);
404 }
405
406 static int
407 afd_start_stop(struct afd_softc *fdp, int start)
408 {
409     int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start,
410                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
411
412     return atapi_queue_cmd(fdp->device, ccb, NULL, 0, 0, 30, NULL, NULL);
413 }
414
415 static int
416 afd_prevent_allow(struct afd_softc *fdp, int lock)
417 {
418     int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
419                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
420     
421     if (!strncmp(fdp->device->param->model, "IOMEGA Clik!", 12))
422         return 0;
423     return atapi_queue_cmd(fdp->device, ccb, NULL, 0, 0, 30, NULL, NULL);
424 }