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