31a70bfdf4ec4c35d8796738857c4cd84da5a3aa
[dragonfly.git] / sys / dev / disk / wfd / wfd.c
1 /*
2  * Copyright (c) 1997,1998  Junichi Satoh <junichi@astec.co.jp>
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 as
10  *    the first lines of this file unmodified.
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  *
15  * THIS SOFTWARE IS PROVIDED BY Junichi Satoh ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL Junichi Satoh BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/i386/isa/wfd.c,v 1.35 2000/01/29 16:00:33 peter Exp $
27  * $DragonFly: src/sys/dev/disk/wfd/Attic/wfd.c,v 1.5 2003/07/21 05:50:40 dillon Exp $
28  */
29
30 /*
31  * ATAPI Floppy, LS-120 driver
32  */
33
34 #include "wdc.h"
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/conf.h>
40 #include <sys/proc.h>
41 #include <sys/malloc.h>
42 #include <sys/buf.h>
43 #include <sys/devicestat.h>
44 #include <sys/disklabel.h>
45 #include <sys/diskslice.h>
46 #include <sys/cdio.h>
47
48 #include <i386/isa/atapi.h>
49
50 static  d_open_t        wfdopen;
51 static  d_close_t       wfdclose;
52 static  d_ioctl_t       wfdioctl;
53 static  d_strategy_t    wfdstrategy;
54
55 static struct cdevsw wfd_cdevsw = {
56         /* name */      "wfd",
57         /* maj */       WFD_CDEV_MAJOR,
58         /* flags */     D_DISK,
59         /* port */      NULL,
60         /* autoq */     0,
61
62         /* open */      wfdopen,
63         /* close */     wfdclose,
64         /* read */      physread,
65         /* write */     physwrite,
66         /* ioctl */     wfdioctl,
67         /* poll */      nopoll,
68         /* mmap */      nommap,
69         /* strategy */  wfdstrategy,
70         /* dump */      nodump,
71         /* psize */     nopsize,
72 };
73
74 int  wfdattach(struct atapi*, int, struct atapi_params*, int);
75
76 #define NUNIT   (NWDC*2)                /* Max. number of devices */
77 #define UNIT(d) ((minor(d) >> 3) & 3)   /* Unit part of minor device number */
78
79 #define F_BOPEN         0x0001          /* The block device is opened */
80 #define F_MEDIA_CHANGED 0x0002          /* The media have changed since open */
81 #define F_DEBUG         0x0004          /* Print debug info */
82
83 /*
84  * LS-120 Capabilities and Mechanical Status Page
85  */
86 struct cappage {
87     /* Mode data header */
88     u_short     data_length;
89     u_char      medium_type;
90 #define MDT_UNKNOWN     0x00
91 #define MDT_NO_DISC     0x70
92 #define MDT_DOOR_OPEN   0x71
93 #define MDT_FMT_ERROR   0x72
94
95 #define MDT_2DD_UN      0x10
96 #define MDT_2DD         0x11
97 #define MDT_2HD_UN      0x20
98 #define MDT_2HD_12_98   0x22
99 #define MDT_2HD_12      0x23
100 #define MDT_2HD_144     0x24
101 #define MDT_LS120       0x31
102
103     unsigned        reserved0       :7;
104     unsigned        wp              :1;     /* Write protect */
105     u_char          reserved1[4];
106
107     /* Capabilities page */
108     unsigned        page_code       :6;     /* Page code - Should be 0x5 */
109 #define CAP_PAGE        0x05
110     unsigned        reserved1_6     :1;     /* Reserved */
111     unsigned        ps              :1;     /* The device is capable of saving the page */
112     u_char          page_length;            /* Page Length - Should be 0x1e */
113     u_short         transfer_rate;          /* In kilobits per second */
114     u_char          heads, sectors;         /* Number of heads, Number of sectors per track */
115     u_short         sector_size;            /* Byes per sector */
116     u_short         cyls;                   /* Number of cylinders */
117     u_char          reserved10[10];
118     u_char          motor_delay;            /* Motor off delay */
119     u_char          reserved21[7];
120     u_short         rpm;                    /* Rotations per minute */
121     u_char          reserved30[2];
122 };
123
124 struct wfd {
125         struct atapi *ata;              /* Controller structure */
126         int unit;                       /* IDE bus drive unit */
127         int lun;                        /* Logical device unit */
128         int flags;                      /* Device state flags */
129         int refcnt;                     /* The number of raw opens */
130         int maxblks;                    /* transfer size limit */
131         struct buf_queue_head buf_queue;  /* Queue of i/o requests */
132         struct atapi_params *param;     /* Drive parameters table */
133         struct cappage cap;             /* Capabilities page info */
134         char description[80];           /* Device description */
135         struct diskslices *dk_slices;   /* virtual drives */
136
137         struct devstat device_stats;
138 };
139
140 static struct wfd *wfdtab[NUNIT]; /* Drive info by unit number */
141 static int wfdnlun = 0;           /* Number of configured drives */
142
143 static void wfd_start (struct wfd *t);
144 static void wfd_done (struct wfd *t, struct buf *bp, int resid,
145         struct atapires result);
146 static void wfd_error (struct wfd *t, struct atapires result);
147 static int wfd_request_wait (struct wfd *t, u_char cmd, u_char a1, u_char a2,
148         u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8,
149         u_char a9, char *addr, int count);
150 static void wfd_describe (struct wfd *t);
151 static int wfd_eject (struct wfd *t, int closeit);
152
153 /*
154  * Dump the array in hexadecimal format for debugging purposes.
155  */
156 static void wfd_dump (int lun, char *label, void *data, int len)
157 {
158         u_char *p = data;
159
160         printf ("wfd%d: %s %x", lun, label, *p++);
161         while (--len > 0)
162                 printf ("-%x", *p++);
163         printf ("\n");
164 }
165
166 int 
167 wfdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug)
168 {
169         struct wfd *t;
170         struct atapires result;
171         int lun, i;
172
173         if (wfdnlun >= NUNIT) {
174                 printf ("wfd: too many units\n");
175                 return (0);
176         }
177         if (!atapi_request_immediate) {
178                 printf("wfd: configuration error, ATAPI core code not present!\n");
179                 printf("wfd: check `options ATAPI_STATIC' in your kernel config file!\n");
180                 return (0);
181         }
182         t = malloc (sizeof (struct wfd), M_TEMP, M_NOWAIT);
183         if (! t) {
184                 printf ("wfd: out of memory\n");
185                 return (0);
186         }
187         wfdtab[wfdnlun] = t;
188         bzero (t, sizeof (struct wfd));
189         bufq_init(&t->buf_queue);
190         t->ata = ata;
191         t->unit = unit;
192         lun = t->lun = wfdnlun;
193         t->param = ap;
194         t->flags = F_MEDIA_CHANGED;
195         t->refcnt = 0;
196         if (debug) {
197                 t->flags |= F_DEBUG;
198                 /* Print params. */
199                 wfd_dump (t->lun, "info", ap, sizeof *ap);
200         }
201
202         /* Get drive capabilities. */
203         /* Do it twice to avoid the stale media changed state. */
204         for (i = 0; i < 2; i++) {
205                 result = atapi_request_immediate (ata, unit, ATAPI_MODE_SENSE,
206                         0, CAP_PAGE, 0, 0, 0, 0, 
207                         sizeof (t->cap) >> 8, sizeof (t->cap),
208                         0, 0, 0, 0, 0, 0, 0, (char*) &t->cap, sizeof (t->cap));
209         }
210
211         if (result.code == RES_ERR &&
212             (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION)
213                 result = atapi_request_immediate (ata, unit, ATAPI_MODE_SENSE,
214                         0, CAP_PAGE, 0, 0, 0, 0, sizeof (t->cap) >> 8,
215                         sizeof (t->cap), 0, 0, 0, 0, 0, 0, 0,
216                         (char*) &t->cap, sizeof (t->cap));
217
218         /* Some drives have shorter capabilities page. */
219         if (result.code == RES_UNDERRUN)
220                 result.code = 0;
221
222         if (result.code == 0) {
223                 wfd_describe (t);
224                 if (t->flags & F_DEBUG)
225                         wfd_dump (t->lun, "cap", &t->cap, sizeof t->cap);
226         } else
227                 return -1;
228
229         /*
230          * The IOMEGA ZIP 100, at firmware 21.* and 23.* at least
231          * is known to lock up if transfers > 64 blocks are
232          * requested.
233          */
234         if (!strcmp(ap->model, "IOMEGA  ZIP 100       ATAPI")) {
235                 printf("wfd%d: buggy Zip drive, 64-block transfer limit set\n",
236                        t->lun);
237                 t->maxblks = 64;
238         } else {
239                 t->maxblks = 0; /* no limit */
240         }
241         
242         
243
244         make_dev(&wfd_cdevsw, dkmakeminor(t->lun, WHOLE_DISK_SLICE, RAW_PART),
245             UID_ROOT, GID_OPERATOR, 0640, "rwfd%d", t->lun);
246
247         /*
248          * Export the drive to the devstat interface.
249          */
250         devstat_add_entry(&t->device_stats, "wfd", 
251                           t->lun, t->cap.sector_size,
252                           DEVSTAT_NO_ORDERED_TAGS,
253                           DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_IDE,
254                           DEVSTAT_PRIORITY_WFD);
255         wfdnlun++;
256         return (1);
257 }
258
259 void wfd_describe (struct wfd *t)
260 {
261         int no_print = 0;
262
263         t->cap.cyls = ntohs (t->cap.cyls);
264         t->cap.sector_size = ntohs (t->cap.sector_size);
265
266         printf ("wfd%d: ", t->lun);
267         switch (t->cap.medium_type) {
268         case MDT_UNKNOWN:
269                 printf ("medium type unknown (no disk)");
270                 no_print = 1;
271                 break;
272         case MDT_2DD_UN:
273                 printf ("2DD(capacity unknown) floppy disk loaded");
274                 no_print = 1;
275                 break;
276         case MDT_2DD:
277                 printf ("720KB floppy disk loaded");
278                 break;
279         case MDT_2HD_UN:
280                 printf ("2HD(capacity unknown) floppy disk loaded");
281                 no_print = 1;
282                 break;
283         case MDT_2HD_12_98:
284                 printf ("1.25MB(PC-9801 format) floppy disk loaded");
285                 break;
286         case MDT_2HD_12:
287                 printf ("1.2MB floppy disk loaded");
288                 break;
289         case MDT_2HD_144:
290                 printf ("1.44MB floppy disk loaded");
291                 break;
292         case MDT_LS120:
293                 printf ("120MB floppy disk loaded");
294                 break;
295         case MDT_NO_DISC:
296                 printf ("no disc inside");
297                 no_print = 1;
298                 break;
299         case MDT_DOOR_OPEN:
300                 printf ("door open");
301                 no_print = 1;
302                 break;
303         case MDT_FMT_ERROR:
304                 printf ("medium format error");
305                 no_print = 1;
306                 break;
307         default:
308                 printf ("medium type=0x%x", t->cap.medium_type);
309                 break;
310         }
311         if (t->cap.wp)
312                 printf(", write protected");
313         printf ("\n");
314
315         if (!no_print) {
316                 printf ("wfd%d: ", t->lun);
317                 printf ("%u cyls", t->cap.cyls);
318                 printf (", %u heads, %u S/T", t->cap.heads, t->cap.sectors);
319                 printf (", %u B/S", t->cap.sector_size);
320                 printf ("\n");
321         }
322 }
323
324 int wfdopen (dev_t dev, int flags, int fmt, struct proc *p)
325 {
326         int lun = UNIT(dev);
327         struct wfd *t;
328         struct atapires result;
329         int errcode = 0;
330         struct disklabel label;
331
332         /* Check that the device number is legal
333          * and the ATAPI driver is loaded. */
334         if (lun >= wfdnlun || ! atapi_request_immediate)
335                 return (ENXIO);
336         t = wfdtab[lun];
337
338         t->flags &= ~F_MEDIA_CHANGED;
339         /* Lock the media. */
340         wfd_request_wait (t, ATAPI_PREVENT_ALLOW,
341                           0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
342
343         /* Sense the media type */
344         result = atapi_request_wait (t->ata, t->unit, ATAPI_MODE_SENSE,
345                                      0, CAP_PAGE, 0, 0, 0, 0, 
346                                      sizeof (t->cap) >> 8, sizeof (t->cap),
347                                      0, 0, 0, 0, 0, 0, 0, 
348                                      (char*) &t->cap, sizeof (t->cap));
349         if (result.code)
350                 printf ("wfd%d: Sense the media type is failed.\n", t->lun);
351         else {
352                 t->cap.cyls = ntohs (t->cap.cyls);
353                 t->cap.sector_size = ntohs (t->cap.sector_size);
354         }
355
356         /* Build label for whole disk. */
357         bzero(&label, sizeof label);
358         label.d_secsize = t->cap.sector_size;
359         label.d_nsectors = t->cap.sectors;
360         label.d_ntracks = t->cap.heads;
361         label.d_ncylinders = t->cap.cyls;
362         label.d_secpercyl = t->cap.heads * t->cap.sectors;
363         label.d_rpm = 720;
364         label.d_secperunit = label.d_secpercyl * t->cap.cyls;
365
366         /* Initialize slice tables. */
367         errcode = dsopen(dev, fmt, 0, &t->dk_slices, &label);
368         if (errcode != 0)
369                 return errcode;
370
371         t->flags |= F_BOPEN;
372         return (0);
373 }
374
375 /*
376  * Close the device.  Only called if we are the LAST
377  * occurence of an open device.
378  */
379 int wfdclose (dev_t dev, int flags, int fmt, struct proc *p)
380 {
381         int lun = UNIT(dev);
382         struct wfd *t = wfdtab[lun];
383
384         dsclose(dev, fmt, t->dk_slices);
385         if(!dsisopen(t->dk_slices)) {
386                 /* If we were the last open of the entire device, release it. */
387                 if (! t->refcnt)
388                         wfd_request_wait (t, ATAPI_PREVENT_ALLOW,
389                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
390                 t->flags &= ~F_BOPEN;
391         }
392         return (0);
393 }
394
395 /*
396  * Actually translate the requested transfer into one the physical driver can
397  * understand. The transfer is described by a buf and will include only one
398  * physical transfer.
399  */
400 void wfdstrategy (struct buf *bp)
401 {
402         int lun = UNIT(bp->b_dev);
403         struct wfd *t = wfdtab[lun];
404         int x;
405
406         /* If it's a null transfer, return immediatly. */
407         if (bp->b_bcount == 0) {
408                 bp->b_resid = 0;
409                 biodone (bp);
410                 return;
411         }
412
413         /*
414          * Do bounds checking, adjust transfer, and set b_pblkno.
415          */
416         if (dscheck(bp, t->dk_slices) <= 0) {
417                 biodone(bp);
418                 return;
419         }
420
421         x = splbio();
422
423         /* Place it in the queue of disk activities for this disk. */
424         bufqdisksort (&t->buf_queue, bp);
425
426         /* Tell the device to get going on the transfer if it's
427          * not doing anything, otherwise just wait for completion. */
428         wfd_start (t);
429         splx(x);
430 }
431
432 /*
433  * Look to see if there is a buf waiting for the device
434  * and that the device is not already busy. If both are true,
435  * It dequeues the buf and creates an ATAPI command to perform the
436  * transfer in the buf.
437  * The bufs are queued by the strategy routine (wfdstrategy).
438  * Must be called at the correct (splbio) level.
439  */
440 static void wfd_start (struct wfd *t)
441 {
442         struct buf *bp = bufq_first(&t->buf_queue);
443         u_long blkno, nblk;
444         u_char op_code;
445         long count;
446         int pxcount, pxnblk;
447         u_char *pxdest;
448         
449
450         /* See if there is a buf to do and we are not already doing one. */
451         if (! bp)
452                 return;
453
454         /* Unqueue the request. */
455         bufq_remove(&t->buf_queue, bp);
456
457         /* Tell devstat we are starting on the transaction */
458         devstat_start_transaction(&t->device_stats);
459
460         /* We have a buf, now we should make a command
461          * First, translate the block to absolute and put it in terms of the
462          * logical blocksize of the device. */
463         blkno = bp->b_pblkno / (t->cap.sector_size / 512);
464         nblk = (bp->b_bcount + (t->cap.sector_size - 1)) / t->cap.sector_size;
465
466         if ((t->maxblks == 0) || (nblk <= t->maxblks)) {
467
468                 if(bp->b_flags & B_READ) {
469                         op_code = ATAPI_READ_BIG;
470                         count = bp->b_bcount;
471                 } else {
472                         op_code = ATAPI_WRITE_BIG;
473                         count = -bp->b_bcount;
474                 }
475
476                 /* only one transfer */
477                 (int)bp->b_driver1 = 0;
478                 (int)bp->b_driver2 = 0;
479                 atapi_request_callback (t->ata, t->unit, op_code, 0,
480                                         blkno>>24, blkno>>16, blkno>>8, blkno,
481                                         0, nblk>>8, nblk, 0, 0,
482                                         0, 0, 0, 0, 0, 
483                                         (u_char*) bp->b_data, count, 
484                                         (void*)wfd_done, t, bp);
485         } else {
486
487                 /*
488                  * We can't handle this request in a single
489                  * read/write operation.  Instead, queue a set of
490                  * transfers, and record the number of transfers
491                  * and the running residual in the b_driver
492                  * fields of the bp.
493                  */ 
494
495                 if(bp->b_flags & B_READ) {
496                         op_code = ATAPI_READ_BIG;
497                 } else {
498                         op_code = ATAPI_WRITE_BIG;
499                 }
500
501                 /* calculate number of transfers */
502                 (int)bp->b_driver1 = (nblk - 1) / t->maxblks;
503                 (int)bp->b_driver2 = 0;
504
505                 pxdest = (u_char *)bp->b_data;
506                 pxcount = bp->b_bcount;
507
508                 /* construct partial transfer requests */
509                 while (nblk > 0) {
510                         pxnblk = min(nblk, t->maxblks);
511                         count = min(pxcount, t->maxblks * t->cap.sector_size);
512
513                         atapi_request_callback(t->ata, t->unit, op_code, 0,
514                                                blkno>>24, blkno>>16, blkno>>8,
515                                                blkno, 0, pxnblk>>8, pxnblk, 
516                                                0, 0, 0, 0, 0, 0, 0,
517                                                pxdest, 
518                                                (bp->b_flags & B_READ) ?
519                                                count : -count, 
520                                                (void*)wfd_done, t, bp);
521                         nblk -= pxnblk;
522                         pxcount -= count;
523                         pxdest += count;
524                         blkno += pxnblk;
525                 }
526         }
527 }
528
529 static void wfd_done (struct wfd *t, struct buf *bp, int resid,
530         struct atapires result)
531 {
532                 
533         if (result.code) {
534                 wfd_error (t, result);
535                 bp->b_error = EIO;
536                 bp->b_flags |= B_ERROR;
537         } else
538                 (int)bp->b_driver2 += resid;
539         /*
540          * We can't call biodone until all outstanding
541          * transfer fragments are handled.  If one hits
542          * an error, we will be returning an error, but
543          * only when all are complete.
544          */
545         if (((int)bp->b_driver1)-- <= 0) {
546                 bp->b_resid = (int)bp->b_driver2;
547                 devstat_end_transaction_buf(&t->device_stats, bp);
548                 biodone (bp);
549         }
550         
551         wfd_start (t);
552 }
553
554 static void wfd_error (struct wfd *t, struct atapires result)
555 {
556         if (result.code != RES_ERR)
557                 return;
558         switch (result.error & AER_SKEY) {
559         case AER_SK_NOT_READY:
560                 if (result.error & ~AER_SKEY) {
561                         /* Not Ready */
562                         printf ("wfd%d: not ready\n", t->lun);
563                         return;
564                 }
565                 /* Tray open. */
566                 if (! (t->flags & F_MEDIA_CHANGED))
567                         printf ("wfd%d: tray open\n", t->lun);
568                 t->flags |= F_MEDIA_CHANGED;
569                 return;
570
571         case AER_SK_UNIT_ATTENTION:
572                 /* Media changed. */
573                 if (! (t->flags & F_MEDIA_CHANGED))
574                         printf ("wfd%d: media changed\n", t->lun);
575                 t->flags |= F_MEDIA_CHANGED;
576                 return;
577
578         case AER_SK_ILLEGAL_REQUEST:
579                 /* Unknown command or invalid command arguments. */
580                 if (t->flags & F_DEBUG)
581                         printf ("wfd%d: invalid command\n", t->lun);
582                 return;
583         }
584         printf ("wfd%d: i/o error, status=%b, error=%b\n", t->lun,
585                 result.status, ARS_BITS, result.error, AER_BITS);
586 }
587
588 static int wfd_request_wait (struct wfd *t, u_char cmd, u_char a1, u_char a2,
589         u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8,
590         u_char a9, char *addr, int count)
591 {
592         struct atapires result;
593
594         result = atapi_request_wait (t->ata, t->unit, cmd,
595                 a1, a2, a3, a4, a5, a6, a7, a8, a9, 0, 0, 0, 0, 0, 0,
596                 addr, count);
597         if (result.code) {
598                 wfd_error (t, result);
599                 return (EIO);
600         }
601         return (0);
602 }
603
604 /*
605  * Perform special action on behalf of the user.
606  * Knows about the internals of this device
607  */
608 int wfdioctl (dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
609 {
610         int lun = UNIT(dev);
611         struct wfd *t = wfdtab[lun];
612         int error = 0;
613
614         error = dsioctl(dev, cmd, addr, flag, &t->dk_slices);
615         if (error != ENOIOCTL)
616                 return (error);
617
618         if (t->flags & F_MEDIA_CHANGED)
619                 switch (cmd) {
620                 case CDIOCSETDEBUG:
621                 case CDIOCCLRDEBUG:
622                 case CDIOCRESET:
623                         /* These ops are media change transparent. */
624                         break;
625                 default:
626                         /* Lock the media. */
627                         wfd_request_wait (t, ATAPI_PREVENT_ALLOW,
628                                 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
629                         break;
630                 }
631         switch (cmd) {
632         case CDIOCSETDEBUG:
633                 error = suser(td);
634                 if (error)
635                         return (error);
636                 t->flags |= F_DEBUG;
637                 atapi_debug (t->ata, 1);
638                 return 0;
639         case CDIOCCLRDEBUG:
640                 error = suser(td);
641                 if (error)
642                         return (error);
643                 t->flags &= ~F_DEBUG;
644                 atapi_debug (t->ata, 0);
645                 return 0;
646         case CDIOCRESET:
647                 error = suser(td);
648                 if (error)
649                         return (error);
650                 return wfd_request_wait (t, ATAPI_TEST_UNIT_READY,
651                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
652         case CDIOCEJECT:
653                 /* Don't allow eject if the device is opened
654                  * by somebody (not us) in block mode. */
655                 if ((t->flags & F_BOPEN) && t->refcnt)
656                         return (EBUSY);
657                 return wfd_eject (t, 0);
658         case CDIOCCLOSE:
659                 if ((t->flags & F_BOPEN) && t->refcnt)
660                         return (0);
661                 return wfd_eject (t, 1);
662         default:
663                 return (ENOTTY);
664         }
665         return (error);
666 }
667
668 static int wfd_eject (struct wfd *t, int closeit)
669 {
670         struct atapires result;
671
672         /* Try to stop the disc. */
673         result = atapi_request_wait (t->ata, t->unit, ATAPI_START_STOP,
674                 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
675
676         if (result.code == RES_ERR &&
677             ((result.error & AER_SKEY) == AER_SK_NOT_READY ||
678             (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION)) {
679                 int err;
680
681                 if (!closeit)
682                         return (0);
683                 /*
684                  * The disc was unloaded.
685                  * Load it (close tray).
686                  * Read the table of contents.
687                  */
688                 err = wfd_request_wait (t, ATAPI_START_STOP,
689                         0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0);
690                 if (err)
691                         return (err);
692
693                 /* Lock the media. */
694                 wfd_request_wait (t, ATAPI_PREVENT_ALLOW,
695                         0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
696
697                 return (0);
698         }
699
700         if (result.code) {
701                 wfd_error (t, result);
702                 return (EIO);
703         }
704
705         if (closeit)
706                 return (0);
707
708         /* Give it some time to stop spinning. */
709         tsleep ((caddr_t)&lbolt, 0, "wfdej1", 0);
710         tsleep ((caddr_t)&lbolt, 0, "wfdej2", 0);
711
712         /* Unlock. */
713         wfd_request_wait (t, ATAPI_PREVENT_ALLOW,
714                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
715
716         /* Eject. */
717         t->flags |= F_MEDIA_CHANGED;
718         return wfd_request_wait (t, ATAPI_START_STOP,
719                 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0);
720 }
721
722 static void     wfd_drvinit(void *unused)
723 {
724         cdevsw_add(&wfd_cdevsw);
725 }
726
727 SYSINIT(wfddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wfd_drvinit,NULL)