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