Initial import from FreeBSD RELENG_4:
[games.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  */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/ata.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/buf.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/disk.h>
40 #include <sys/devicestat.h>
41 #include <sys/cdio.h>
42 #include <dev/ata/ata-all.h>
43 #include <dev/ata/atapi-all.h>
44 #include <dev/ata/atapi-fd.h>
45
46 /* device structures */
47 static  d_open_t        afdopen;
48 static  d_close_t       afdclose;
49 static  d_ioctl_t       afdioctl;
50 static  d_strategy_t    afdstrategy;
51 static struct cdevsw afd_cdevsw = {
52         /* open */      afdopen,
53         /* close */     afdclose,
54         /* read */      physread,
55         /* write */     physwrite,
56         /* ioctl */     afdioctl,
57         /* poll */      nopoll,
58         /* mmap */      nommap,
59         /* strategy */  afdstrategy,
60         /* name */      "afd",
61         /* maj */       118,
62         /* dump */      nodump,
63         /* psize */     nopsize,
64         /* flags */     D_DISK | D_TRACKCLOSE,
65 };
66 static struct cdevsw afddisk_cdevsw;
67
68 /* prototypes */
69 static int afd_sense(struct afd_softc *);
70 static void afd_describe(struct afd_softc *);
71 static int afd_done(struct atapi_request *);
72 static int afd_eject(struct afd_softc *, int);
73 static int afd_start_stop(struct afd_softc *, int);
74 static int afd_prevent_allow(struct afd_softc *, int);
75
76 /* internal vars */
77 static u_int32_t afd_lun_map = 0;
78 static MALLOC_DEFINE(M_AFD, "AFD driver", "ATAPI floppy driver buffers");
79
80 int 
81 afdattach(struct ata_device *atadev)
82 {
83     struct afd_softc *fdp;
84     dev_t dev;
85
86     fdp = malloc(sizeof(struct afd_softc), M_AFD, M_NOWAIT | M_ZERO);
87     if (!fdp) {
88         ata_prtdev(atadev, "out of memory\n");
89         return 0;
90     }
91
92     fdp->device = atadev;
93     fdp->lun = ata_get_lun(&afd_lun_map);
94     ata_set_name(atadev, "afd", fdp->lun);
95     bufq_init(&fdp->queue);
96
97     if (afd_sense(fdp)) {
98         free(fdp, M_AFD);
99         return 0;
100     }
101
102     devstat_add_entry(&fdp->stats, "afd", fdp->lun, DEV_BSIZE,
103                       DEVSTAT_NO_ORDERED_TAGS,
104                       DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE,
105                       DEVSTAT_PRIORITY_WFD);
106     dev = disk_create(fdp->lun, &fdp->disk, 0, &afd_cdevsw, &afddisk_cdevsw);
107     dev->si_drv1 = fdp;
108     fdp->dev = dev;
109
110     if (!strncmp(atadev->param->model, "IOMEGA ZIP", 10) ||
111         !strncmp(atadev->param->model, "IOMEGA Clik!", 12))
112         fdp->dev->si_iosize_max = 64 * DEV_BSIZE;
113     else
114         fdp->dev->si_iosize_max = 252 * DEV_BSIZE;
115
116     afd_describe(fdp);
117     atadev->flags |= ATA_D_MEDIA_CHANGED;
118     atadev->driver = fdp;
119     return 1;
120 }
121
122 void
123 afddetach(struct ata_device *atadev)
124 {   
125     struct afd_softc *fdp = atadev->driver;
126     struct buf *bp;
127     
128     while ((bp = bufq_first(&fdp->queue))) {
129         bufq_remove(&fdp->queue, bp);
130         bp->b_flags |= B_ERROR;
131         bp->b_error = ENXIO;
132         biodone(bp);
133     }
134     disk_invalidate(&fdp->disk);
135     disk_destroy(fdp->dev);
136     devstat_remove_entry(&fdp->stats);
137     ata_free_name(atadev);
138     ata_free_lun(&afd_lun_map, fdp->lun);
139     free(fdp, M_AFD);
140     atadev->driver = NULL;
141 }   
142
143 static int 
144 afd_sense(struct afd_softc *fdp)
145 {
146     int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_REWRITEABLE_CAP_PAGE,
147                        0, 0, 0, 0, sizeof(struct afd_cappage) >> 8,
148                        sizeof(struct afd_cappage) & 0xff, 0, 0, 0, 0, 0, 0, 0 };
149     int count, error = 0;
150
151     /* The IOMEGA Clik! doesn't support reading the cap page, fake it */
152     if (!strncmp(fdp->device->param->model, "IOMEGA Clik!", 12)) {
153         fdp->cap.transfer_rate = 500;
154         fdp->cap.heads = 1;
155         fdp->cap.sectors = 2;
156         fdp->cap.cylinders = 39441;
157         fdp->cap.sector_size = 512;
158         atapi_test_ready(fdp->device);
159         return 0;
160     }
161
162     /* get drive capabilities, some drives needs this repeated */
163     for (count = 0 ; count < 5 ; count++) {
164         if (!(error = atapi_queue_cmd(fdp->device, ccb, (caddr_t)&fdp->cap,
165                                       sizeof(struct afd_cappage),
166                                       ATPR_F_READ, 30, NULL, NULL)))
167             break;
168     }
169     if (error || fdp->cap.page_code != ATAPI_REWRITEABLE_CAP_PAGE)
170         return 1;   
171     fdp->cap.cylinders = ntohs(fdp->cap.cylinders);
172     fdp->cap.sector_size = ntohs(fdp->cap.sector_size);
173     fdp->cap.transfer_rate = ntohs(fdp->cap.transfer_rate);
174     return 0;
175 }
176
177 static void 
178 afd_describe(struct afd_softc *fdp)
179 {
180     if (bootverbose) {
181         ata_prtdev(fdp->device,
182                    "<%.40s/%.8s> rewriteable drive at ata%d as %s\n",
183                    fdp->device->param->model, fdp->device->param->revision,
184                    device_get_unit(fdp->device->channel->dev),
185                    (fdp->device->unit == ATA_MASTER) ? "master" : "slave");
186         ata_prtdev(fdp->device,
187                    "%luMB (%u sectors), %u cyls, %u heads, %u S/T, %u B/S\n",
188                    (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) / 
189                    ((1024L * 1024L) / fdp->cap.sector_size),
190                    fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors,
191                    fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors,
192                    fdp->cap.sector_size);
193         ata_prtdev(fdp->device, "%dKB/s,", fdp->lun, fdp->cap.transfer_rate/8);
194         printf(" %s\n", ata_mode2str(fdp->device->mode));
195         if (fdp->cap.medium_type) {
196             ata_prtdev(fdp->device, "Medium: ");
197             switch (fdp->cap.medium_type) {
198             case MFD_2DD:
199                 printf("720KB DD disk"); break;
200
201             case MFD_HD_12:
202                 printf("1.2MB HD disk"); break;
203
204             case MFD_HD_144:
205                 printf("1.44MB HD disk"); break;
206
207             case MFD_UHD: 
208                 printf("120MB UHD disk"); break;
209
210             default:
211                 printf("Unknown (0x%x)", fdp->cap.medium_type);
212             }
213             if (fdp->cap.wp) printf(", writeprotected");
214         }
215         printf("\n");
216     }
217     else {
218         ata_prtdev(fdp->device, "%luMB <%.40s> [%d/%d/%d] at ata%d-%s %s\n",
219                    (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) /
220                    ((1024L * 1024L) / fdp->cap.sector_size),    
221                    fdp->device->param->model,
222                    fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors,
223                    device_get_unit(fdp->device->channel->dev),
224                    (fdp->device->unit == ATA_MASTER) ? "master" : "slave",
225                    ata_mode2str(fdp->device->mode));
226     }
227 }
228
229 static int
230 afdopen(dev_t dev, int flags, int fmt, struct proc *p)
231 {
232     struct afd_softc *fdp = dev->si_drv1;
233     struct disklabel *label = &fdp->disk.d_label;
234
235     atapi_test_ready(fdp->device);
236
237     if (count_dev(dev) == 1)
238         afd_prevent_allow(fdp, 1);
239
240     if (afd_sense(fdp))
241         ata_prtdev(fdp->device, "sense media type failed\n");
242
243     fdp->device->flags &= ~ATA_D_MEDIA_CHANGED;
244
245     bzero(label, sizeof *label);
246     label->d_secsize = fdp->cap.sector_size;
247     label->d_nsectors = fdp->cap.sectors;  
248     label->d_ntracks = fdp->cap.heads;
249     label->d_ncylinders = fdp->cap.cylinders;
250     label->d_secpercyl = fdp->cap.sectors * fdp->cap.heads;
251     label->d_secperunit = label->d_secpercyl * fdp->cap.cylinders;
252     return 0;
253 }
254
255 static int 
256 afdclose(dev_t dev, int flags, int fmt, struct proc *p)
257 {
258     struct afd_softc *fdp = dev->si_drv1;
259
260     if (count_dev(dev) == 1)
261         afd_prevent_allow(fdp, 0); 
262     return 0;
263 }
264
265 static int 
266 afdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
267 {
268     struct afd_softc *fdp = dev->si_drv1;
269
270     switch (cmd) {
271     case CDIOCEJECT:
272         if (count_dev(dev) > 1)
273             return EBUSY;
274         return afd_eject(fdp, 0);
275
276     case CDIOCCLOSE:
277         if (count_dev(dev) > 1)
278             return 0;
279         return afd_eject(fdp, 1);
280
281     default:
282         return ENOIOCTL;
283     }
284 }
285
286 static void 
287 afdstrategy(struct buf *bp)
288 {
289     struct afd_softc *fdp = bp->b_dev->si_drv1;
290     int s;
291
292     if (fdp->device->flags & ATA_D_DETACHING) {
293         bp->b_flags |= B_ERROR;
294         bp->b_error = ENXIO;
295         biodone(bp);
296         return;
297     }
298
299     /* if it's a null transfer, return immediatly. */
300     if (bp->b_bcount == 0) {
301         bp->b_resid = 0;
302         biodone(bp);
303         return;
304     }
305
306     s = splbio();
307     bufqdisksort(&fdp->queue, bp);
308     splx(s);
309     ata_start(fdp->device->channel);
310 }
311
312 void 
313 afd_start(struct ata_device *atadev)
314 {
315     struct afd_softc *fdp = atadev->driver;
316     struct buf *bp = bufq_first(&fdp->queue);
317     u_int32_t lba;
318     u_int16_t count;
319     int8_t ccb[16];
320     caddr_t data_ptr;
321
322     if (!bp)
323         return;
324
325     bufq_remove(&fdp->queue, bp);
326
327     /* should reject all queued entries if media have changed. */
328     if (fdp->device->flags & ATA_D_MEDIA_CHANGED) {
329         bp->b_flags |= B_ERROR;
330         bp->b_error = EIO;
331         biodone(bp);
332         return;
333     }
334
335     lba = bp->b_pblkno;
336     count = bp->b_bcount / fdp->cap.sector_size;
337     data_ptr = bp->b_data;
338     bp->b_resid = bp->b_bcount; 
339
340     bzero(ccb, sizeof(ccb));
341
342     if (bp->b_flags & B_READ)
343         ccb[0] = ATAPI_READ_BIG;
344     else
345         ccb[0] = ATAPI_WRITE_BIG;
346
347     ccb[2] = lba>>24;
348     ccb[3] = lba>>16;
349     ccb[4] = lba>>8;
350     ccb[5] = lba;
351     ccb[7] = count>>8;
352     ccb[8] = count;
353
354     devstat_start_transaction(&fdp->stats);
355
356     atapi_queue_cmd(fdp->device, ccb, data_ptr, count * fdp->cap.sector_size,
357                     (bp->b_flags & B_READ) ? ATPR_F_READ : 0, 30,
358                     afd_done, bp);
359 }
360
361 static int 
362 afd_done(struct atapi_request *request)
363 {
364     struct buf *bp = request->driver;
365     struct afd_softc *fdp = request->device->driver;
366
367     if (request->error || (bp->b_flags & B_ERROR)) {
368         bp->b_error = request->error;
369         bp->b_flags |= B_ERROR;
370     }
371     else
372         bp->b_resid = bp->b_bcount - request->donecount;
373     devstat_end_transaction_buf(&fdp->stats, bp);
374     biodone(bp);
375     return 0;
376 }
377
378 static int 
379 afd_eject(struct afd_softc *fdp, int close)
380 {
381     int error;
382      
383     if ((error = afd_start_stop(fdp, 0)) == EBUSY) {
384         if (!close)
385             return 0;
386         if ((error = afd_start_stop(fdp, 3)))
387             return error;
388         return afd_prevent_allow(fdp, 1);
389     }
390     if (error)
391         return error;
392     if (close)
393         return 0;
394     if ((error = afd_prevent_allow(fdp, 0)))
395         return error;
396     fdp->device->flags |= ATA_D_MEDIA_CHANGED;
397     return afd_start_stop(fdp, 2);
398 }
399
400 static int
401 afd_start_stop(struct afd_softc *fdp, int start)
402 {
403     int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start,
404                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
405
406     return atapi_queue_cmd(fdp->device, ccb, NULL, 0, 0, 30, NULL, NULL);
407 }
408
409 static int
410 afd_prevent_allow(struct afd_softc *fdp, int lock)
411 {
412     int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
413                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
414     
415     if (!strncmp(fdp->device->param->model, "IOMEGA Clik!", 12))
416         return 0;
417     return atapi_queue_cmd(fdp->device, ccb, NULL, 0, 0, 30, NULL, NULL);
418 }