]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/fdc/fdc.c
This commit was generated by cvs2svn to compensate for changes in r41220,
[FreeBSD/FreeBSD.git] / sys / dev / fdc / fdc.c
1 /*
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Don Ahn.
7  *
8  * Copyright (c) 1993, 1994 by
9  *  jc@irbs.UUCP (John Capo)
10  *  vak@zebub.msk.su (Serge Vakulenko)
11  *  ache@astral.msk.su (Andrew A. Chernov)
12  *
13  * Copyright (c) 1993, 1994, 1995 by
14  *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
15  *  dufault@hda.com (Peter Dufault)
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  * 3. All advertising materials mentioning features or use of this software
26  *    must display the following acknowledgement:
27  *      This product includes software developed by the University of
28  *      California, Berkeley and its contributors.
29  * 4. Neither the name of the University nor the names of its contributors
30  *    may be used to endorse or promote products derived from this software
31  *    without specific prior written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43  * SUCH DAMAGE.
44  *
45  *      from:   @(#)fd.c        7.4 (Berkeley) 5/25/91
46  *      $Id: fd.c,v 1.123 1998/09/15 22:07:24 gibbs Exp $
47  *
48  */
49
50 #include "ft.h"
51 #if NFT < 1
52 #undef NFDC
53 #endif
54 #include "fd.h"
55 #include "opt_devfs.h"
56 #include "opt_fdc.h"
57
58 #if NFDC > 0
59
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/kernel.h>
63 #include <sys/conf.h>
64 #include <sys/fcntl.h>
65 #include <machine/clock.h>
66 #include <machine/ioctl_fd.h>
67 #include <sys/disklabel.h>
68 #include <sys/buf.h>
69 #include <sys/devicestat.h>
70 #include <sys/malloc.h>
71 #include <sys/proc.h>
72 #include <sys/syslog.h>
73 #include <i386/isa/isa.h>
74 #include <i386/isa/isa_device.h>
75 #include <i386/isa/fdreg.h>
76 #include <i386/isa/fdc.h>
77 #include <i386/isa/rtc.h>
78 #include <machine/stdarg.h>
79 #if NFT > 0
80 #include <sys/ftape.h>
81 #include <i386/isa/ftreg.h>
82 #endif
83 #ifdef  DEVFS
84 #include <sys/devfsext.h>
85 #endif  /* DEVFS */
86
87 /* misuse a flag to identify format operation */
88 #define B_FORMAT B_XXX
89
90 /* configuration flags */
91 #define FDC_PRETEND_D0  (1 << 0)        /* pretend drive 0 to be there */
92
93 /* internally used only, not really from CMOS: */
94 #define RTCFDT_144M_PRETENDED   0x1000
95
96 /*
97  * this biotab field doubles as a field for the physical unit number
98  * on the controller
99  */
100 #define id_physid id_scsiid
101
102 /* error returns for fd_cmd() */
103 #define FD_FAILED -1
104 #define FD_NOT_VALID -2
105 #define FDC_ERRMAX      100     /* do not log more */
106
107 #define NUMTYPES 14
108 #define NUMDENS  (NUMTYPES - 6)
109
110 /* These defines (-1) must match index for fd_types */
111 #define F_TAPE_TYPE     0x020   /* bit for fd_types to indicate tape */
112 #define NO_TYPE         0       /* must match NO_TYPE in ft.c */
113 #define FD_1720         1
114 #define FD_1480         2
115 #define FD_1440         3
116 #define FD_1200         4
117 #define FD_820          5
118 #define FD_800          6
119 #define FD_720          7
120 #define FD_360          8
121
122 #define FD_1480in5_25   9
123 #define FD_1440in5_25   10
124 #define FD_820in5_25    11
125 #define FD_800in5_25    12
126 #define FD_720in5_25    13
127 #define FD_360in5_25    14
128
129
130 static struct fd_type fd_types[NUMTYPES] =
131 {
132 { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
133 { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
134 { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
135 { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /*  1.2M in HD 5.25/3.5 */
136 { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /*  820K in HD 3.5in */
137 { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /*  800K in HD 3.5in */
138 {  9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /*  720K in HD 3.5in */
139 {  9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /*  360K in DD 5.25in */
140
141 { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
142 { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
143 { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /*  820K in HD 5.25in */
144 { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /*  800K in HD 5.25in */
145 {  9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /*  720K in HD 5.25in */
146 {  9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /*  360K in HD 5.25in */
147 };
148
149 #define DRVS_PER_CTLR 2         /* 2 floppies */
150
151 /***********************************************************************\
152 * Per controller structure.                                             *
153 \***********************************************************************/
154 struct fdc_data fdc_data[NFDC];
155
156 /***********************************************************************\
157 * Per drive structure.                                                  *
158 * N per controller  (DRVS_PER_CTLR)                                     *
159 \***********************************************************************/
160 static struct fd_data {
161         struct  fdc_data *fdc;  /* pointer to controller structure */
162         int     fdsu;           /* this units number on this controller */
163         int     type;           /* Drive type (FD_1440...) */
164         struct  fd_type *ft;    /* pointer to the type descriptor */
165         int     flags;
166 #define FD_OPEN         0x01    /* it's open            */
167 #define FD_ACTIVE       0x02    /* it's active          */
168 #define FD_MOTOR        0x04    /* motor should be on   */
169 #define FD_MOTOR_WAIT   0x08    /* motor coming up      */
170         int     skip;
171         int     hddrv;
172 #define FD_NO_TRACK -2
173         int     track;          /* where we think the head is */
174         int     options;        /* user configurable options, see ioctl_fd.h */
175         struct  callout_handle toffhandle;
176         struct  callout_handle tohandle;
177         struct  devstat device_stats;
178 #ifdef DEVFS
179         void    *bdevs[1 + NUMDENS + MAXPARTITIONS];
180         void    *cdevs[1 + NUMDENS + MAXPARTITIONS];
181 #endif
182 } fd_data[NFD];
183
184 /***********************************************************************\
185 * Throughout this file the following conventions will be used:          *
186 * fd is a pointer to the fd_data struct for the drive in question       *
187 * fdc is a pointer to the fdc_data struct for the controller            *
188 * fdu is the floppy drive unit number                                   *
189 * fdcu is the floppy controller unit number                             *
190 * fdsu is the floppy drive unit number on that controller. (sub-unit)   *
191 \***********************************************************************/
192
193 #if NFT > 0
194 int ftopen(dev_t, int);
195 int ftintr(ftu_t ftu);
196 int ftclose(dev_t, int);
197 void ftstrategy(struct buf *);
198 int ftioctl(dev_t, unsigned long, caddr_t, int, struct proc *);
199 int ftdump(dev_t);
200 int ftsize(dev_t);
201 int ftattach(struct isa_device *, struct isa_device *, int);
202 #endif
203
204 /* autoconfig functions */
205 static int fdprobe(struct isa_device *);
206 static int fdattach(struct isa_device *);
207
208 /* needed for ft driver, thus exported */
209 int in_fdc(fdcu_t);
210 int out_fdc(fdcu_t, int);
211
212 /* internal functions */
213 static void set_motor(fdcu_t, int, int);
214 #  define TURNON 1
215 #  define TURNOFF 0
216 static timeout_t fd_turnoff;
217 static timeout_t fd_motor_on;
218 static void fd_turnon(fdu_t);
219 static void fdc_reset(fdc_p);
220 static int fd_in(fdcu_t, int *);
221 static void fdstart(fdcu_t);
222 static timeout_t fd_iotimeout;
223 static timeout_t fd_pseudointr;
224 static ointhand2_t fdintr;
225 static int fdstate(fdcu_t, fdc_p);
226 static int retrier(fdcu_t);
227 static int fdformat(dev_t, struct fd_formb *, struct proc *);
228
229 static int enable_fifo(fdc_p fdc);
230
231 static int fifo_threshold = 8;  /* XXX: should be accessible via sysctl */
232
233
234 #define DEVIDLE         0
235 #define FINDWORK        1
236 #define DOSEEK          2
237 #define SEEKCOMPLETE    3
238 #define IOCOMPLETE      4
239 #define RECALCOMPLETE   5
240 #define STARTRECAL      6
241 #define RESETCTLR       7
242 #define SEEKWAIT        8
243 #define RECALWAIT       9
244 #define MOTORWAIT       10
245 #define IOTIMEDOUT      11
246 #define RESETCOMPLETE   12
247
248 #ifdef  FDC_DEBUG
249 static char const * const fdstates[] =
250 {
251 "DEVIDLE",
252 "FINDWORK",
253 "DOSEEK",
254 "SEEKCOMPLETE",
255 "IOCOMPLETE",
256 "RECALCOMPLETE",
257 "STARTRECAL",
258 "RESETCTLR",
259 "SEEKWAIT",
260 "RECALWAIT",
261 "MOTORWAIT",
262 "IOTIMEDOUT",
263 "RESETCOMPLETE",
264 };
265
266 /* CAUTION: fd_debug causes huge amounts of logging output */
267 static int volatile fd_debug = 0;
268 #define TRACE0(arg) if(fd_debug) printf(arg)
269 #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
270 #else /* FDC_DEBUG */
271 #define TRACE0(arg)
272 #define TRACE1(arg1, arg2)
273 #endif /* FDC_DEBUG */
274
275 /* autoconfig structure */
276
277 struct  isa_driver fdcdriver = {
278         fdprobe, fdattach, "fdc",
279 };
280
281 static  d_open_t        Fdopen; /* NOTE, not fdopen */
282 static  d_read_t        fdread;
283 static  d_write_t       fdwrite;
284 static  d_close_t       fdclose;
285 static  d_ioctl_t       fdioctl;
286 static  d_strategy_t    fdstrategy;
287
288 /* even if SLICE defined, these are needed for the ft support. */
289 #define CDEV_MAJOR 9
290 #define BDEV_MAJOR 2
291
292
293 static struct cdevsw fd_cdevsw = {
294           Fdopen,       fdclose,        fdread, fdwrite,
295           fdioctl,      nostop,         nullreset,      nodevtotty,
296           seltrue,      nommap,         fdstrategy,     "fd",
297           NULL,         -1,             nodump,         nopsize,
298           D_DISK,       0,              -1 };
299
300
301 static struct isa_device *fdcdevs[NFDC];
302
303
304 static int
305 fdc_err(fdcu_t fdcu, const char *s)
306 {
307         fdc_data[fdcu].fdc_errs++;
308         if(s) {
309                 if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX)
310                         printf("fdc%d: %s", fdcu, s);
311                 else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX)
312                         printf("fdc%d: too many errors, not logging any more\n",
313                                fdcu);
314         }
315
316         return FD_FAILED;
317 }
318
319 /*
320  * fd_cmd: Send a command to the chip.  Takes a varargs with this structure:
321  * Unit number,
322  * # of output bytes, output bytes as ints ...,
323  * # of input bytes, input bytes as ints ...
324  */
325
326 static int
327 fd_cmd(fdcu_t fdcu, int n_out, ...)
328 {
329         u_char cmd;
330         int n_in;
331         int n;
332         va_list ap;
333
334         va_start(ap, n_out);
335         cmd = (u_char)(va_arg(ap, int));
336         va_end(ap);
337         va_start(ap, n_out);
338         for (n = 0; n < n_out; n++)
339         {
340                 if (out_fdc(fdcu, va_arg(ap, int)) < 0)
341                 {
342                         char msg[50];
343                         sprintf(msg,
344                                 "cmd %x failed at out byte %d of %d\n",
345                                 cmd, n + 1, n_out);
346                         return fdc_err(fdcu, msg);
347                 }
348         }
349         n_in = va_arg(ap, int);
350         for (n = 0; n < n_in; n++)
351         {
352                 int *ptr = va_arg(ap, int *);
353                 if (fd_in(fdcu, ptr) < 0)
354                 {
355                         char msg[50];
356                         sprintf(msg,
357                                 "cmd %02x failed at in byte %d of %d\n",
358                                 cmd, n + 1, n_in);
359                         return fdc_err(fdcu, msg);
360                 }
361         }
362
363         return 0;
364 }
365
366 static int 
367 enable_fifo(fdc_p fdc)
368 {
369         int i, j;
370
371         if ((fdc->flags & FDC_HAS_FIFO) == 0) {
372                 
373                 /*
374                  * XXX: 
375                  * Cannot use fd_cmd the normal way here, since
376                  * this might be an invalid command. Thus we send the
377                  * first byte, and check for an early turn of data directon.
378                  */
379                 
380                 if (out_fdc(fdc->fdcu, I8207X_CONFIGURE) < 0)
381                         return fdc_err(fdc->fdcu, "Enable FIFO failed\n");
382                 
383                 /* If command is invalid, return */
384                 j = 100000;
385                 while ((i = inb(fdc->baseport + FDSTS) & (NE7_DIO | NE7_RQM))
386                        != NE7_RQM && j-- > 0)
387                         if (i == (NE7_DIO | NE7_RQM)) {
388                                 fdc_reset(fdc);
389                                 return FD_FAILED;
390                         }
391                 if (j<0 || 
392                     fd_cmd(fdc->fdcu, 3,
393                            0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) {
394                         fdc_reset(fdc);
395                         return fdc_err(fdc->fdcu, "Enable FIFO failed\n");
396                 }
397                 fdc->flags |= FDC_HAS_FIFO;
398                 return 0;
399         }
400         if (fd_cmd(fdc->fdcu, 4,
401                    I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0)
402                 return fdc_err(fdc->fdcu, "Re-enable FIFO failed\n");
403         return 0;
404 }
405
406 static int
407 fd_sense_drive_status(fdc_p fdc, int *st3p)
408 {
409         int st3;
410
411         if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
412         {
413                 return fdc_err(fdc->fdcu, "Sense Drive Status failed\n");
414         }
415         if (st3p)
416                 *st3p = st3;
417
418         return 0;
419 }
420
421 static int
422 fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
423 {
424         int st0, cyl;
425
426         int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0);
427
428         if (ret)
429         {
430                 (void)fdc_err(fdc->fdcu,
431                               "sense intr err reading stat reg 0\n");
432                 return ret;
433         }
434
435         if (st0p)
436                 *st0p = st0;
437
438         if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV)
439         {
440                 /*
441                  * There doesn't seem to have been an interrupt.
442                  */
443                 return FD_NOT_VALID;
444         }
445
446         if (fd_in(fdc->fdcu, &cyl) < 0)
447         {
448                 return fdc_err(fdc->fdcu, "can't get cyl num\n");
449         }
450
451         if (cylp)
452                 *cylp = cyl;
453
454         return 0;
455 }
456
457
458 static int
459 fd_read_status(fdc_p fdc, int fdsu)
460 {
461         int i, ret;
462
463         for (i = 0; i < 7; i++)
464         {
465                 /*
466                  * XXX types are poorly chosen.  Only bytes can by read
467                  * from the hardware, but fdc->status[] wants u_ints and
468                  * fd_in() gives ints.
469                  */
470                 int status;
471
472                 ret = fd_in(fdc->fdcu, &status);
473                 fdc->status[i] = status;
474                 if (ret != 0)
475                         break;
476         }
477
478         if (ret == 0)
479                 fdc->flags |= FDC_STAT_VALID;
480         else
481                 fdc->flags &= ~FDC_STAT_VALID;
482
483         return ret;
484 }
485
486 /****************************************************************************/
487 /*                      autoconfiguration stuff                             */
488 /****************************************************************************/
489
490 /*
491  * probe for existance of controller
492  */
493 static int
494 fdprobe(struct isa_device *dev)
495 {
496         fdcu_t  fdcu = dev->id_unit;
497         if(fdc_data[fdcu].flags & FDC_ATTACHED)
498         {
499                 printf("fdc%d: unit used multiple times\n", fdcu);
500                 return 0;
501         }
502
503         fdcdevs[fdcu] = dev;
504         fdc_data[fdcu].baseport = dev->id_iobase;
505
506         /* First - lets reset the floppy controller */
507         outb(dev->id_iobase+FDOUT, 0);
508         DELAY(100);
509         outb(dev->id_iobase+FDOUT, FDO_FRST);
510
511         /* see if it can handle a command */
512         if (fd_cmd(fdcu,
513                    3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
514                    0))
515         {
516                 return(0);
517         }
518         return (IO_FDCSIZE);
519 }
520
521 /*
522  * wire controller into system, look for floppy units
523  */
524 static int
525 fdattach(struct isa_device *dev)
526 {
527         unsigned fdt;
528         fdu_t   fdu;
529         fdcu_t  fdcu = dev->id_unit;
530         fdc_p   fdc = fdc_data + fdcu;
531         fd_p    fd;
532         int     fdsu, st0, st3, i;
533 #if NFT > 0
534         int     unithasfd;
535 #endif
536         struct isa_device *fdup;
537         int ic_type = 0;
538 #ifdef DEVFS
539         int     mynor;
540         int     typemynor;
541         int     typesize;
542 #endif
543
544         dev->id_ointr = fdintr;
545         fdc->fdcu = fdcu;
546         fdc->flags |= FDC_ATTACHED;
547         fdc->dmachan = dev->id_drq;
548         /* Acquire the DMA channel forever, The driver will do the rest */
549         isa_dma_acquire(fdc->dmachan);
550         isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
551         fdc->state = DEVIDLE;
552         /* reset controller, turn motor off, clear fdout mirror reg */
553         outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
554         bufq_init(&fdc->head);
555
556         /* check for each floppy drive */
557         for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
558                 if (fdup->id_iobase != dev->id_iobase)
559                         continue;
560                 fdu = fdup->id_unit;
561                 fd = &fd_data[fdu];
562                 if (fdu >= (NFD+NFT))
563                         continue;
564                 fdsu = fdup->id_physid;
565                 /* look up what bios thinks we have */
566                 switch (fdu) {
567                         case 0: if (dev->id_flags & FDC_PRETEND_D0)
568                                         fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
569                                 else
570                                         fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
571                                 break;
572                         case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
573                                 break;
574                         default: fdt = RTCFDT_NONE;
575                                 break;
576                 }
577                 /* is there a unit? */
578                 if ((fdt == RTCFDT_NONE)
579 #if NFT > 0
580                     || (fdsu >= DRVS_PER_CTLR)) {
581 #else
582                 ) {
583                         fd->type = NO_TYPE;
584 #endif
585 #if NFT > 0
586                         /* If BIOS says no floppy, or > 2nd device */
587                         /* Probe for and attach a floppy tape.     */
588                         /* Tell FT if there was already a disk     */
589                         /* with this unit number found.            */
590
591                         unithasfd = 0;
592                         if (fdu < NFD && fd->type != NO_TYPE)
593                                 unithasfd = 1;
594                         if (ftattach(dev, fdup, unithasfd))
595                                 continue;
596                         if (fdsu < DRVS_PER_CTLR)
597                                 fd->type = NO_TYPE;
598 #endif
599                         continue;
600                 }
601
602                 /* select it */
603                 set_motor(fdcu, fdsu, TURNON);
604                 DELAY(1000000); /* 1 sec */
605
606                 if (ic_type == 0 &&
607                     fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0)
608                 {
609 #ifdef FDC_PRINT_BOGUS_CHIPTYPE
610                         printf("fdc%d: ", fdcu);
611 #endif
612                         ic_type = (u_char)ic_type;
613                         switch( ic_type ) {
614                         case 0x80:
615 #ifdef FDC_PRINT_BOGUS_CHIPTYPE
616                                 printf("NEC 765\n");
617 #endif
618                                 fdc->fdct = FDC_NE765;
619                                 break;
620                         case 0x81:
621 #ifdef FDC_PRINT_BOGUS_CHIPTYPE
622                                 printf("Intel 82077\n");
623 #endif
624                                 fdc->fdct = FDC_I82077;
625                                 break;
626                         case 0x90:
627 #ifdef FDC_PRINT_BOGUS_CHIPTYPE
628                                 printf("NEC 72065B\n");
629 #endif
630                                 fdc->fdct = FDC_NE72065;
631                                 break;
632                         default:
633 #ifdef FDC_PRINT_BOGUS_CHIPTYPE
634                                 printf("unknown IC type %02x\n", ic_type);
635 #endif
636                                 fdc->fdct = FDC_UNKNOWN;
637                                 break;
638                         }
639                         if (fdc->fdct != FDC_NE765 &&
640                             fdc->fdct != FDC_UNKNOWN && 
641                             enable_fifo(fdc) == 0) {
642                                 printf("fdc%d: FIFO enabled", fdcu);
643                                 printf(", %d bytes threshold\n", 
644                                        fifo_threshold);
645                         }
646                 }
647                 if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
648                     (st3 & NE7_ST3_T0)) {
649                         /* if at track 0, first seek inwards */
650                         /* seek some steps: */
651                         (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
652                         DELAY(300000); /* ...wait a moment... */
653                         (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
654                 }
655
656                 /* If we're at track 0 first seek inwards. */
657                 if ((fd_sense_drive_status(fdc, &st3) == 0) &&
658                     (st3 & NE7_ST3_T0)) {
659                         /* Seek some steps... */
660                         if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
661                                 /* ...wait a moment... */
662                                 DELAY(300000);
663                                 /* make ctrlr happy: */
664                                 (void)fd_sense_int(fdc, 0, 0);
665                         }
666                 }
667
668                 for(i = 0; i < 2; i++) {
669                         /*
670                          * we must recalibrate twice, just in case the
671                          * heads have been beyond cylinder 76, since most
672                          * FDCs still barf when attempting to recalibrate
673                          * more than 77 steps
674                          */
675                         /* go back to 0: */
676                         if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
677                                 /* a second being enough for full stroke seek*/
678                                 DELAY(i == 0? 1000000: 300000);
679
680                                 /* anything responding? */
681                                 if (fd_sense_int(fdc, &st0, 0) == 0 &&
682                                 (st0 & NE7_ST0_EC) == 0)
683                                         break; /* already probed succesfully */
684                         }
685                 }
686
687                 set_motor(fdcu, fdsu, TURNOFF);
688
689                 if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
690                         continue;
691
692                 fd->track = FD_NO_TRACK;
693                 fd->fdc = fdc;
694                 fd->fdsu = fdsu;
695                 fd->options = 0;
696                 callout_handle_init(&fd->toffhandle);
697                 callout_handle_init(&fd->tohandle);
698                 printf("fd%d: ", fdu);
699
700                 switch (fdt) {
701                 case RTCFDT_12M:
702                         printf("1.2MB 5.25in\n");
703                         fd->type = FD_1200;
704                         break;
705                 case RTCFDT_144M | RTCFDT_144M_PRETENDED:
706                         printf("config-pretended ");
707                         fdt = RTCFDT_144M;
708                         /* fallthrough */
709                 case RTCFDT_144M:
710                         printf("1.44MB 3.5in\n");
711                         fd->type = FD_1440;
712                         break;
713                 case RTCFDT_288M:
714                 case RTCFDT_288M_1:
715                         printf("2.88MB 3.5in - 1.44MB mode\n");
716                         fd->type = FD_1440;
717                         break;
718                 case RTCFDT_360K:
719                         printf("360KB 5.25in\n");
720                         fd->type = FD_360;
721                         break;
722                 case RTCFDT_720K:
723                         printf("720KB 3.5in\n");
724                         fd->type = FD_720;
725                         break;
726                 default:
727                         printf("unknown\n");
728                         fd->type = NO_TYPE;
729                         continue;
730                 }
731 #ifdef DEVFS
732                 mynor = fdu << 6;
733                 fd->bdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_BLK,
734                                                 UID_ROOT, GID_OPERATOR, 0640,
735                                                 "fd%d", fdu);
736                 fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
737                                                 UID_ROOT, GID_OPERATOR, 0640,
738                                                 "rfd%d", fdu);
739                 for (i = 1; i < 1 + NUMDENS; i++) {
740                         /*
741                          * XXX this and the lookup in Fdopen() should be
742                          * data driven.
743                          */
744                         switch (fd->type) {
745                         case FD_360:
746                                 if (i != FD_360)
747                                         continue;
748                                 break;
749                         case FD_720:
750                                 if (i != FD_720 && i != FD_800 && i != FD_820)
751                                         continue;
752                                 break;
753                         case FD_1200:
754                                 if (i != FD_360 && i != FD_720 && i != FD_800
755                                     && i != FD_820 && i != FD_1200
756                                     && i != FD_1440 && i != FD_1480)
757                                         continue;
758                                 break;
759                         case FD_1440:
760                                 if (i != FD_720 && i != FD_800 && i != FD_820
761                                     && i != FD_1200 && i != FD_1440
762                                     && i != FD_1480 && i != FD_1720)
763                                         continue;
764                                 break;
765                         }
766                         typesize = fd_types[i - 1].size / 2;
767                         /*
768                          * XXX all these conversions give bloated code and
769                          * confusing names.
770                          */
771                         if (typesize == 1476)
772                                 typesize = 1480;
773                         if (typesize == 1722)
774                                 typesize = 1720;
775                         typemynor = mynor | i;
776                         fd->bdevs[i] =
777                                 devfs_add_devswf(&fd_cdevsw, typemynor, DV_BLK,
778                                                  UID_ROOT, GID_OPERATOR, 0640,
779                                                  "fd%d.%d", fdu, typesize);
780                         fd->cdevs[i] =
781                                 devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR,
782                                                  UID_ROOT, GID_OPERATOR, 0640,
783                                                  "rfd%d.%d", fdu, typesize);
784                 }
785
786                 for (i = 0; i < MAXPARTITIONS; i++) {
787                         fd->bdevs[1 + NUMDENS + i] = devfs_link(fd->bdevs[0],
788                                            "fd%d%c", fdu, 'a' + i);
789                         fd->cdevs[1 + NUMDENS + i] =
790                                 devfs_link(fd->cdevs[0],
791                                            "rfd%d%c", fdu, 'a' + i);
792                 }
793 #endif /* DEVFS */
794                 /*
795                  * Export the drive to the devstat interface.
796                  */
797                 devstat_add_entry(&fd->device_stats, "fd", 
798                                   fdu, 512,
799                                   DEVSTAT_NO_ORDERED_TAGS,
800                                   DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER);
801                 
802         }
803
804         return (1);
805 }
806
807
808
809 /****************************************************************************/
810 /*                            motor control stuff                           */
811 /*              remember to not deselect the drive we're working on         */
812 /****************************************************************************/
813 static void
814 set_motor(fdcu_t fdcu, int fdsu, int turnon)
815 {
816         int fdout = fdc_data[fdcu].fdout;
817         int needspecify = 0;
818
819         if(turnon) {
820                 fdout &= ~FDO_FDSEL;
821                 fdout |= (FDO_MOEN0 << fdsu) + fdsu;
822         } else
823                 fdout &= ~(FDO_MOEN0 << fdsu);
824
825         if(!turnon
826            && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
827                 /* gonna turn off the last drive, put FDC to bed */
828                 fdout &= ~ (FDO_FRST|FDO_FDMAEN);
829         else {
830                 /* make sure controller is selected and specified */
831                 if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
832                         needspecify = 1;
833                 fdout |= (FDO_FRST|FDO_FDMAEN);
834         }
835
836         outb(fdc_data[fdcu].baseport+FDOUT, fdout);
837         fdc_data[fdcu].fdout = fdout;
838         TRACE1("[0x%x->FDOUT]", fdout);
839
840         if(needspecify) {
841                 /*
842                  * XXX
843                  * special case: since we have just woken up the FDC
844                  * from its sleep, we silently assume the command will
845                  * be accepted, and do not test for a timeout
846                  */
847                 (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
848                              NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
849                              0);
850                 if (fdc_data[fdcu].flags & FDC_HAS_FIFO)
851                         (void) enable_fifo(&fdc_data[fdcu]);
852         }
853 }
854
855 static void
856 fd_turnoff(void *arg1)
857 {
858         fdu_t fdu = (fdu_t)arg1;
859         int     s;
860         fd_p fd = fd_data + fdu;
861
862         TRACE1("[fd%d: turnoff]", fdu);
863
864         /*
865          * Don't turn off the motor yet if the drive is active.
866          * XXX shouldn't even schedule turnoff until drive is inactive
867          * and nothing is queued on it.
868          */
869         if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) {
870                 fd->toffhandle = timeout(fd_turnoff, arg1, 4 * hz);
871                 return;
872         }
873
874         s = splbio();
875         fd->flags &= ~FD_MOTOR;
876         set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF);
877         splx(s);
878 }
879
880 static void
881 fd_motor_on(void *arg1)
882 {
883         fdu_t fdu = (fdu_t)arg1;
884         int     s;
885
886         fd_p fd = fd_data + fdu;
887         s = splbio();
888         fd->flags &= ~FD_MOTOR_WAIT;
889         if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
890         {
891                 fdintr(fd->fdc->fdcu);
892         }
893         splx(s);
894 }
895
896 static void
897 fd_turnon(fdu_t fdu)
898 {
899         fd_p fd = fd_data + fdu;
900         if(!(fd->flags & FD_MOTOR))
901         {
902                 fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
903                 set_motor(fd->fdc->fdcu, fd->fdsu, TURNON);
904                 timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
905         }
906 }
907
908 static void
909 fdc_reset(fdc_p fdc)
910 {
911         fdcu_t fdcu = fdc->fdcu;
912
913         /* Try a reset, keep motor on */
914         outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
915         TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
916         DELAY(100);
917         /* enable FDC, but defer interrupts a moment */
918         outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
919         TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
920         DELAY(100);
921         outb(fdc->baseport + FDOUT, fdc->fdout);
922         TRACE1("[0x%x->FDOUT]", fdc->fdout);
923
924         /* XXX after a reset, silently believe the FDC will accept commands */
925         (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
926                      NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
927                      0);
928         if (fdc->flags & FDC_HAS_FIFO)
929                 (void) enable_fifo(fdc);
930 }
931
932 /****************************************************************************/
933 /*                             fdc in/out                                   */
934 /****************************************************************************/
935 int
936 in_fdc(fdcu_t fdcu)
937 {
938         int baseport = fdc_data[fdcu].baseport;
939         int i, j = 100000;
940         while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
941                 != (NE7_DIO|NE7_RQM) && j-- > 0)
942                 if (i == NE7_RQM)
943                         return fdc_err(fdcu, "ready for output in input\n");
944         if (j <= 0)
945                 return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
946 #ifdef  FDC_DEBUG
947         i = inb(baseport+FDDATA);
948         TRACE1("[FDDATA->0x%x]", (unsigned char)i);
949         return(i);
950 #else   /* !FDC_DEBUG */
951         return inb(baseport+FDDATA);
952 #endif  /* FDC_DEBUG */
953 }
954
955 /*
956  * fd_in: Like in_fdc, but allows you to see if it worked.
957  */
958 static int
959 fd_in(fdcu_t fdcu, int *ptr)
960 {
961         int baseport = fdc_data[fdcu].baseport;
962         int i, j = 100000;
963         while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
964                 != (NE7_DIO|NE7_RQM) && j-- > 0)
965                 if (i == NE7_RQM)
966                         return fdc_err(fdcu, "ready for output in input\n");
967         if (j <= 0)
968                 return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
969 #ifdef  FDC_DEBUG
970         i = inb(baseport+FDDATA);
971         TRACE1("[FDDATA->0x%x]", (unsigned char)i);
972         *ptr = i;
973         return 0;
974 #else   /* !FDC_DEBUG */
975         i = inb(baseport+FDDATA);
976         if (ptr)
977                 *ptr = i;
978         return 0;
979 #endif  /* FDC_DEBUG */
980 }
981
982 int
983 out_fdc(fdcu_t fdcu, int x)
984 {
985         int baseport = fdc_data[fdcu].baseport;
986         int i;
987
988         /* Check that the direction bit is set */
989         i = 100000;
990         while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
991         if (i <= 0) return fdc_err(fdcu, "direction bit not set\n");
992
993         /* Check that the floppy controller is ready for a command */
994         i = 100000;
995         while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
996         if (i <= 0)
997                 return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0);
998
999         /* Send the command and return */
1000         outb(baseport+FDDATA, x);
1001         TRACE1("[0x%x->FDDATA]", x);
1002         return (0);
1003 }
1004
1005 /****************************************************************************/
1006 /*                           fdopen/fdclose                                 */
1007 /****************************************************************************/
1008 int
1009 Fdopen(dev_t dev, int flags, int mode, struct proc *p)
1010 {
1011         fdu_t fdu = FDUNIT(minor(dev));
1012         int type = FDTYPE(minor(dev));
1013         fdc_p   fdc;
1014
1015 #if NFT > 0
1016         /* check for a tape open */
1017         if (type & F_TAPE_TYPE)
1018                 return(ftopen(dev, flags));
1019 #endif
1020         /* check bounds */
1021         if (fdu >= NFD)
1022                 return(ENXIO);
1023         fdc = fd_data[fdu].fdc;
1024         if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
1025                 return(ENXIO);
1026         if (type > NUMDENS)
1027                 return(ENXIO);
1028         if (type == 0)
1029                 type = fd_data[fdu].type;
1030         else {
1031                 /*
1032                  * For each type of basic drive, make sure we are trying
1033                  * to open a type it can do,
1034                  */
1035                 if (type != fd_data[fdu].type) {
1036                         switch (fd_data[fdu].type) {
1037                         case FD_360:
1038                                 return(ENXIO);
1039                         case FD_720:
1040                                 if (   type != FD_820
1041                                     && type != FD_800
1042                                    )
1043                                         return(ENXIO);
1044                                 break;
1045                         case FD_1200:
1046                                 switch (type) {
1047                                 case FD_1480:
1048                                         type = FD_1480in5_25;
1049                                         break;
1050                                 case FD_1440:
1051                                         type = FD_1440in5_25;
1052                                         break;
1053                                 case FD_820:
1054                                         type = FD_820in5_25;
1055                                         break;
1056                                 case FD_800:
1057                                         type = FD_800in5_25;
1058                                         break;
1059                                 case FD_720:
1060                                         type = FD_720in5_25;
1061                                         break;
1062                                 case FD_360:
1063                                         type = FD_360in5_25;
1064                                         break;
1065                                 default:
1066                                         return(ENXIO);
1067                                 }
1068                                 break;
1069                         case FD_1440:
1070                                 if (   type != FD_1720
1071                                     && type != FD_1480
1072                                     && type != FD_1200
1073                                     && type != FD_820
1074                                     && type != FD_800
1075                                     && type != FD_720
1076                                     )
1077                                         return(ENXIO);
1078                                 break;
1079                         }
1080                 }
1081         }
1082         fd_data[fdu].ft = fd_types + type - 1;
1083         fd_data[fdu].flags |= FD_OPEN;
1084
1085         return 0;
1086 }
1087
1088 int
1089 fdclose(dev_t dev, int flags, int mode, struct proc *p)
1090 {
1091         fdu_t fdu = FDUNIT(minor(dev));
1092
1093 #if NFT > 0
1094         int type = FDTYPE(minor(dev));
1095
1096         if (type & F_TAPE_TYPE)
1097                 return ftclose(dev, flags);
1098 #endif
1099         fd_data[fdu].flags &= ~FD_OPEN;
1100         fd_data[fdu].options &= ~FDOPT_NORETRY;
1101
1102         return(0);
1103 }
1104
1105 static int
1106 fdread(dev_t dev, struct uio *uio, int ioflag)
1107 {
1108         return (physio(fdstrategy, NULL, dev, 1, minphys, uio));
1109 }
1110
1111 static int
1112 fdwrite(dev_t dev, struct uio *uio, int ioflag)
1113 {
1114         return (physio(fdstrategy, NULL, dev, 0, minphys, uio));
1115 }
1116
1117
1118 /****************************************************************************/
1119 /*                               fdstrategy                                 */
1120 /****************************************************************************/
1121 void
1122 fdstrategy(struct buf *bp)
1123 {
1124         unsigned nblocks, blknum, cando;
1125         int     s;
1126         fdcu_t  fdcu;
1127         fdu_t   fdu;
1128         fdc_p   fdc;
1129         fd_p    fd;
1130         size_t  fdblk;
1131
1132         fdu = FDUNIT(minor(bp->b_dev));
1133         fd = &fd_data[fdu];
1134         fdc = fd->fdc;
1135         fdcu = fdc->fdcu;
1136
1137 #if NFT > 0
1138         if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) {
1139                 /* ft tapes do not (yet) support strategy i/o */
1140                 bp->b_error = ENODEV;
1141                 bp->b_flags |= B_ERROR;
1142                 goto bad;
1143         }
1144         /* check for controller already busy with tape */
1145         if (fdc->flags & FDC_TAPE_BUSY) {
1146                 bp->b_error = EBUSY;
1147                 bp->b_flags |= B_ERROR;
1148                 goto bad;
1149         }
1150 #endif
1151         fdblk = 128 << (fd->ft->secsize);
1152         if (!(bp->b_flags & B_FORMAT)) {
1153                 if ((fdu >= NFD) || (bp->b_blkno < 0)) {
1154                         printf(
1155                 "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
1156                                fdu, (u_long)bp->b_blkno, bp->b_bcount);
1157                         bp->b_error = EINVAL;
1158                         bp->b_flags |= B_ERROR;
1159                         goto bad;
1160                 }
1161                 if ((bp->b_bcount % fdblk) != 0) {
1162                         bp->b_error = EINVAL;
1163                         bp->b_flags |= B_ERROR;
1164                         goto bad;
1165                 }
1166         }
1167
1168         /*
1169          * Set up block calculations.
1170          */
1171         if (bp->b_blkno > 20000000) {
1172                 /*
1173                  * Reject unreasonably high block number, prevent the
1174                  * multiplication below from overflowing.
1175                  */
1176                 bp->b_error = EINVAL;
1177                 bp->b_flags |= B_ERROR;
1178                 goto bad;
1179         }
1180         blknum = (unsigned) bp->b_blkno * DEV_BSIZE/fdblk;
1181         nblocks = fd->ft->size;
1182         bp->b_resid = 0;
1183         if (blknum + (bp->b_bcount / fdblk) > nblocks) {
1184                 if (blknum <= nblocks) {
1185                         cando = (nblocks - blknum) * fdblk;
1186                         bp->b_resid = bp->b_bcount - cando;
1187                         if (cando == 0)
1188                                 goto bad;       /* not actually bad but EOF */
1189                 } else {
1190                         bp->b_error = EINVAL;
1191                         bp->b_flags |= B_ERROR;
1192                         goto bad;
1193                 }
1194         }
1195         bp->b_pblkno = bp->b_blkno;
1196         s = splbio();
1197         bufqdisksort(&fdc->head, bp);
1198         untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle); /* a good idea */
1199
1200         /* Tell devstat we are starting on the transaction */
1201         devstat_start_transaction(&fd->device_stats);
1202
1203         fdstart(fdcu);
1204         splx(s);
1205         return;
1206
1207 bad:
1208         biodone(bp);
1209 }
1210
1211 /***************************************************************\
1212 *                               fdstart                         *
1213 * We have just queued something.. if the controller is not busy *
1214 * then simulate the case where it has just finished a command   *
1215 * So that it (the interrupt routine) looks on the queue for more*
1216 * work to do and picks up what we just added.                   *
1217 * If the controller is already busy, we need do nothing, as it  *
1218 * will pick up our work when the present work completes         *
1219 \***************************************************************/
1220 static void
1221 fdstart(fdcu_t fdcu)
1222 {
1223         int s;
1224
1225         s = splbio();
1226         if(fdc_data[fdcu].state == DEVIDLE)
1227         {
1228                 fdintr(fdcu);
1229         }
1230         splx(s);
1231 }
1232
1233 static void
1234 fd_iotimeout(void *arg1)
1235 {
1236         fdc_p fdc;
1237         fdcu_t fdcu;
1238         int s;
1239
1240         fdcu = (fdcu_t)arg1;
1241         fdc = fdc_data + fdcu;
1242         TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
1243
1244         /*
1245          * Due to IBM's brain-dead design, the FDC has a faked ready
1246          * signal, hardwired to ready == true. Thus, any command
1247          * issued if there's no diskette in the drive will _never_
1248          * complete, and must be aborted by resetting the FDC.
1249          * Many thanks, Big Blue!
1250          * The FDC must not be reset directly, since that would
1251          * interfere with the state machine.  Instead, pretend that
1252          * the command completed but was invalid.  The state machine
1253          * will reset the FDC and retry once.
1254          */
1255         s = splbio();
1256         fdc->status[0] = NE7_ST0_IC_IV;
1257         fdc->flags &= ~FDC_STAT_VALID;
1258         fdc->state = IOTIMEDOUT;
1259         fdintr(fdcu);
1260         splx(s);
1261 }
1262
1263 /* just ensure it has the right spl */
1264 static void
1265 fd_pseudointr(void *arg1)
1266 {
1267         fdcu_t fdcu = (fdcu_t)arg1;
1268         int     s;
1269
1270         s = splbio();
1271         fdintr(fdcu);
1272         splx(s);
1273 }
1274
1275 /***********************************************************************\
1276 *                                 fdintr                                *
1277 * keep calling the state machine until it returns a 0                   *
1278 * ALWAYS called at SPLBIO                                               *
1279 \***********************************************************************/
1280 static void
1281 fdintr(fdcu_t fdcu)
1282 {
1283         fdc_p fdc = fdc_data + fdcu;
1284 #if NFT > 0
1285         fdu_t fdu = fdc->fdu;
1286
1287         if (fdc->flags & FDC_TAPE_BUSY)
1288                 (ftintr(fdu));
1289         else
1290 #endif
1291                 while(fdstate(fdcu, fdc))
1292                         ;
1293 }
1294
1295 /***********************************************************************\
1296 * The controller state machine.                                         *
1297 * if it returns a non zero value, it should be called again immediatly  *
1298 \***********************************************************************/
1299 static int
1300 fdstate(fdcu_t fdcu, fdc_p fdc)
1301 {
1302         struct subdev *sd;
1303         int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
1304         unsigned blknum = 0, b_cylinder = 0;
1305         fdu_t fdu = fdc->fdu;
1306         fd_p fd;
1307         register struct buf *bp;
1308         struct fd_formb *finfo = NULL;
1309         size_t fdblk;
1310
1311         bp = bufq_first(&fdc->head);
1312         if(!bp) {
1313                 /***********************************************\
1314                 * nothing left for this controller to do        *
1315                 * Force into the IDLE state,                    *
1316                 \***********************************************/
1317                 fdc->state = DEVIDLE;
1318                 if(fdc->fd)
1319                 {
1320                         printf("fd%d: unexpected valid fd pointer\n",
1321                                fdc->fdu);
1322                         fdc->fd = (fd_p) 0;
1323                         fdc->fdu = -1;
1324                 }
1325                 TRACE1("[fdc%d IDLE]", fdcu);
1326                 return(0);
1327         }
1328         fdu = FDUNIT(minor(bp->b_dev));
1329         fd = fd_data + fdu;
1330         fdblk = 128 << fd->ft->secsize;
1331         if (fdc->fd && (fd != fdc->fd))
1332         {
1333                 printf("fd%d: confused fd pointers\n", fdu);
1334         }
1335         read = bp->b_flags & B_READ;
1336         format = bp->b_flags & B_FORMAT;
1337         if(format) {
1338                 finfo = (struct fd_formb *)bp->b_data;
1339                 fd->skip = (char *)&(finfo->fd_formb_cylno(0))
1340                         - (char *)finfo;
1341         }
1342         if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) {
1343                 blknum = (unsigned) bp->b_pblkno * DEV_BSIZE/fdblk +
1344                         fd->skip/fdblk;
1345                 b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads);
1346         }
1347         TRACE1("fd%d", fdu);
1348         TRACE1("[%s]", fdstates[fdc->state]);
1349         TRACE1("(0x%x)", fd->flags);
1350         untimeout(fd_turnoff, (caddr_t)fdu, fd->toffhandle);
1351         fd->toffhandle = timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
1352         switch (fdc->state)
1353         {
1354         case DEVIDLE:
1355         case FINDWORK:  /* we have found new work */
1356                 fdc->retry = 0;
1357                 fd->skip = 0;
1358                 fdc->fd = fd;
1359                 fdc->fdu = fdu;
1360                 outb(fdc->baseport+FDCTL, fd->ft->trans);
1361                 TRACE1("[0x%x->FDCTL]", fd->ft->trans);
1362                 /*******************************************************\
1363                 * If the next drive has a motor startup pending, then   *
1364                 * it will start up in its own good time         *
1365                 \*******************************************************/
1366                 if(fd->flags & FD_MOTOR_WAIT)
1367                 {
1368                         fdc->state = MOTORWAIT;
1369                         return(0); /* come back later */
1370                 }
1371                 /*******************************************************\
1372                 * Maybe if it's not starting, it SHOULD be starting     *
1373                 \*******************************************************/
1374                 if (!(fd->flags & FD_MOTOR))
1375                 {
1376                         fdc->state = MOTORWAIT;
1377                         fd_turnon(fdu);
1378                         return(0);
1379                 }
1380                 else    /* at least make sure we are selected */
1381                 {
1382                         set_motor(fdcu, fd->fdsu, TURNON);
1383                 }
1384                 if (fdc->flags & FDC_NEEDS_RESET) {
1385                         fdc->state = RESETCTLR;
1386                         fdc->flags &= ~FDC_NEEDS_RESET;
1387                 } else
1388                         fdc->state = DOSEEK;
1389                 break;
1390         case DOSEEK:
1391                 if (b_cylinder == (unsigned)fd->track)
1392                 {
1393                         fdc->state = SEEKCOMPLETE;
1394                         break;
1395                 }
1396                 if (fd_cmd(fdcu, 3, NE7CMD_SEEK,
1397                            fd->fdsu, b_cylinder * fd->ft->steptrac,
1398                            0))
1399                 {
1400                         /*
1401                          * seek command not accepted, looks like
1402                          * the FDC went off to the Saints...
1403                          */
1404                         fdc->retry = 6; /* try a reset */
1405                         return(retrier(fdcu));
1406                 }
1407                 fd->track = FD_NO_TRACK;
1408                 fdc->state = SEEKWAIT;
1409                 return(0);      /* will return later */
1410         case SEEKWAIT:
1411                 /* allow heads to settle */
1412                 timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16);
1413                 fdc->state = SEEKCOMPLETE;
1414                 return(0);      /* will return later */
1415         case SEEKCOMPLETE : /* SEEK DONE, START DMA */
1416                 /* Make sure seek really happened*/
1417                 if(fd->track == FD_NO_TRACK)
1418                 {
1419                         int descyl = b_cylinder * fd->ft->steptrac;
1420                         do {
1421                                 /*
1422                                  * This might be a "ready changed" interrupt,
1423                                  * which cannot really happen since the
1424                                  * RDY pin is hardwired to + 5 volts.  This
1425                                  * generally indicates a "bouncing" intr
1426                                  * line, so do one of the following:
1427                                  *
1428                                  * When running on an enhanced FDC that is
1429                                  * known to not go stuck after responding
1430                                  * with INVALID, fetch all interrupt states
1431                                  * until seeing either an INVALID or a
1432                                  * real interrupt condition.
1433                                  *
1434                                  * When running on a dumb old NE765, give
1435                                  * up immediately.  The controller will
1436                                  * provide up to four dummy RC interrupt
1437                                  * conditions right after reset (for the
1438                                  * corresponding four drives), so this is
1439                                  * our only chance to get notice that it
1440                                  * was not the FDC that caused the interrupt.
1441                                  */
1442                                 if (fd_sense_int(fdc, &st0, &cyl)
1443                                     == FD_NOT_VALID)
1444                                         return 0;
1445                                 if(fdc->fdct == FDC_NE765
1446                                    && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1447                                         return 0; /* hope for a real intr */
1448                         } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1449
1450                         if (0 == descyl)
1451                         {
1452                                 int failed = 0;
1453                                 /*
1454                                  * seek to cyl 0 requested; make sure we are
1455                                  * really there
1456                                  */
1457                                 if (fd_sense_drive_status(fdc, &st3))
1458                                         failed = 1;
1459                                 if ((st3 & NE7_ST3_T0) == 0) {
1460                                         printf(
1461                 "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
1462                                                fdu, st3, NE7_ST3BITS);
1463                                         failed = 1;
1464                                 }
1465
1466                                 if (failed)
1467                                 {
1468                                         if(fdc->retry < 3)
1469                                                 fdc->retry = 3;
1470                                         return(retrier(fdcu));
1471                                 }
1472                         }
1473
1474                         if (cyl != descyl)
1475                         {
1476                                 printf(
1477                 "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
1478                                        fdu, descyl, cyl, st0);
1479                                 if (fdc->retry < 3)
1480                                         fdc->retry = 3;
1481                                 return(retrier(fdcu));
1482                         }
1483                 }
1484
1485                 fd->track = b_cylinder;
1486                 isa_dmastart(bp->b_flags, bp->b_data+fd->skip,
1487                         format ? bp->b_bcount : fdblk, fdc->dmachan);
1488                 sectrac = fd->ft->sectrac;
1489                 sec = blknum %  (sectrac * fd->ft->heads);
1490                 head = sec / sectrac;
1491                 sec = sec % sectrac + 1;
1492                 fd->hddrv = ((head&1)<<2)+fdu;
1493
1494                 if(format || !read)
1495                 {
1496                         /* make sure the drive is writable */
1497                         if(fd_sense_drive_status(fdc, &st3) != 0)
1498                         {
1499                                 /* stuck controller? */
1500                                 isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
1501                                             format ? bp->b_bcount : fdblk,
1502                                             fdc->dmachan);
1503                                 fdc->retry = 6; /* reset the beast */
1504                                 return(retrier(fdcu));
1505                         }
1506                         if(st3 & NE7_ST3_WP)
1507                         {
1508                                 /*
1509                                  * XXX YES! this is ugly.
1510                                  * in order to force the current operation
1511                                  * to fail, we will have to fake an FDC
1512                                  * error - all error handling is done
1513                                  * by the retrier()
1514                                  */
1515                                 fdc->status[0] = NE7_ST0_IC_AT;
1516                                 fdc->status[1] = NE7_ST1_NW;
1517                                 fdc->status[2] = 0;
1518                                 fdc->status[3] = fd->track;
1519                                 fdc->status[4] = head;
1520                                 fdc->status[5] = sec;
1521                                 fdc->retry = 8; /* break out immediately */
1522                                 fdc->state = IOTIMEDOUT; /* not really... */
1523                                 return (1);
1524                         }
1525                 }
1526
1527                 if(format)
1528                 {
1529                         /* formatting */
1530                         if(fd_cmd(fdcu, 6,
1531                                   NE7CMD_FORMAT,
1532                                   head << 2 | fdu,
1533                                   finfo->fd_formb_secshift,
1534                                   finfo->fd_formb_nsecs,
1535                                   finfo->fd_formb_gaplen,
1536                                   finfo->fd_formb_fillbyte,
1537                                   0))
1538                         {
1539                                 /* controller fell over */
1540                                 isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
1541                                             format ? bp->b_bcount : fdblk,
1542                                             fdc->dmachan);
1543                                 fdc->retry = 6;
1544                                 return(retrier(fdcu));
1545                         }
1546                 }
1547                 else
1548                 {
1549                         if (fd_cmd(fdcu, 9,
1550                                    (read ? NE7CMD_READ : NE7CMD_WRITE),
1551                                    head << 2 | fdu,  /* head & unit */
1552                                    fd->track,        /* track */
1553                                    head,
1554                                    sec,              /* sector + 1 */
1555                                    fd->ft->secsize,  /* sector size */
1556                                    sectrac,          /* sectors/track */
1557                                    fd->ft->gap,      /* gap size */
1558                                    fd->ft->datalen,  /* data length */
1559                                    0))
1560                         {
1561                                 /* the beast is sleeping again */
1562                                 isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
1563                                             format ? bp->b_bcount : fdblk,
1564                                             fdc->dmachan);
1565                                 fdc->retry = 6;
1566                                 return(retrier(fdcu));
1567                         }
1568                 }
1569                 fdc->state = IOCOMPLETE;
1570                 fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz);
1571                 return(0);      /* will return later */
1572         case IOCOMPLETE: /* IO DONE, post-analyze */
1573                 untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle);
1574
1575                 if (fd_read_status(fdc, fd->fdsu))
1576                 {
1577                         isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
1578                                     format ? bp->b_bcount : fdblk,
1579                                     fdc->dmachan);
1580                         if (fdc->retry < 6)
1581                                 fdc->retry = 6; /* force a reset */
1582                         return retrier(fdcu);
1583                 }
1584
1585                 fdc->state = IOTIMEDOUT;
1586
1587                 /* FALLTHROUGH */
1588
1589         case IOTIMEDOUT:
1590                 isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
1591                             format ? bp->b_bcount : fdblk, fdc->dmachan);
1592                 if (fdc->status[0] & NE7_ST0_IC)
1593                 {
1594                         if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
1595                             && fdc->status[1] & NE7_ST1_OR) {
1596                                 /*
1597                                  * DMA overrun. Someone hogged the bus
1598                                  * and didn't release it in time for the
1599                                  * next FDC transfer.
1600                                  * Just restart it, don't increment retry
1601                                  * count. (vak)
1602                                  */
1603                                 fdc->state = SEEKCOMPLETE;
1604                                 return (1);
1605                         }
1606                         else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
1607                                 && fdc->retry < 6)
1608                                 fdc->retry = 6; /* force a reset */
1609                         else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
1610                                 && fdc->status[2] & NE7_ST2_WC
1611                                 && fdc->retry < 3)
1612                                 fdc->retry = 3; /* force recalibrate */
1613                         return(retrier(fdcu));
1614                 }
1615                 /* All OK */
1616                 fd->skip += fdblk;
1617                 if (!format && fd->skip < bp->b_bcount - bp->b_resid)
1618                 {
1619                         /* set up next transfer */
1620                         fdc->state = DOSEEK;
1621                 }
1622                 else
1623                 {
1624                         /* ALL DONE */
1625                         fd->skip = 0;
1626                         bufq_remove(&fdc->head, bp);
1627                         /* Tell devstat we have finished with the transaction */
1628                         devstat_end_transaction(&fd->device_stats,
1629                                                 bp->b_bcount - bp->b_resid,
1630                                                 DEVSTAT_TAG_NONE,
1631                                                 (bp->b_flags & B_READ) ?
1632                                                 DEVSTAT_READ : DEVSTAT_WRITE);
1633                         biodone(bp);
1634                         fdc->fd = (fd_p) 0;
1635                         fdc->fdu = -1;
1636                         fdc->state = FINDWORK;
1637                 }
1638                 return(1);
1639         case RESETCTLR:
1640                 fdc_reset(fdc);
1641                 fdc->retry++;
1642                 fdc->state = RESETCOMPLETE;
1643                 return (0);
1644         case RESETCOMPLETE:
1645                 /*
1646                  * Discard all the results from the reset so that they
1647                  * can't cause an unexpected interrupt later.
1648                  */
1649                 for (i = 0; i < 4; i++)
1650                         (void)fd_sense_int(fdc, &st0, &cyl);
1651                 fdc->state = STARTRECAL;
1652                 /* Fall through. */
1653         case STARTRECAL:
1654                 if(fd_cmd(fdcu,
1655                           2, NE7CMD_RECAL, fdu,
1656                           0)) /* Recalibrate Function */
1657                 {
1658                         /* arrgl */
1659                         fdc->retry = 6;
1660                         return(retrier(fdcu));
1661                 }
1662                 fdc->state = RECALWAIT;
1663                 return(0);      /* will return later */
1664         case RECALWAIT:
1665                 /* allow heads to settle */
1666                 timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8);
1667                 fdc->state = RECALCOMPLETE;
1668                 return(0);      /* will return later */
1669         case RECALCOMPLETE:
1670                 do {
1671                         /*
1672                          * See SEEKCOMPLETE for a comment on this:
1673                          */
1674                         if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
1675                                 return 0;
1676                         if(fdc->fdct == FDC_NE765
1677                            && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
1678                                 return 0; /* hope for a real intr */
1679                 } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
1680                 if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
1681                 {
1682                         if(fdc->retry > 3)
1683                                 /*
1684                                  * a recalibrate from beyond cylinder 77
1685                                  * will "fail" due to the FDC limitations;
1686                                  * since people used to complain much about
1687                                  * the failure message, try not logging
1688                                  * this one if it seems to be the first
1689                                  * time in a line
1690                                  */
1691                                 printf("fd%d: recal failed ST0 %b cyl %d\n",
1692                                        fdu, st0, NE7_ST0BITS, cyl);
1693                         if(fdc->retry < 3) fdc->retry = 3;
1694                         return(retrier(fdcu));
1695                 }
1696                 fd->track = 0;
1697                 /* Seek (probably) necessary */
1698                 fdc->state = DOSEEK;
1699                 return(1);      /* will return immediatly */
1700         case MOTORWAIT:
1701                 if(fd->flags & FD_MOTOR_WAIT)
1702                 {
1703                         return(0); /* time's not up yet */
1704                 }
1705                 if (fdc->flags & FDC_NEEDS_RESET) {
1706                         fdc->state = RESETCTLR;
1707                         fdc->flags &= ~FDC_NEEDS_RESET;
1708                 } else {
1709                         /*
1710                          * If all motors were off, then the controller was
1711                          * reset, so it has lost track of the current
1712                          * cylinder.  Recalibrate to handle this case.
1713                          */
1714                         fdc->state = STARTRECAL;
1715                 }
1716                 return(1);      /* will return immediatly */
1717         default:
1718                 printf("fdc%d: Unexpected FD int->", fdcu);
1719                 if (fd_read_status(fdc, fd->fdsu) == 0)
1720                         printf("FDC status :%x %x %x %x %x %x %x   ",
1721                                fdc->status[0],
1722                                fdc->status[1],
1723                                fdc->status[2],
1724                                fdc->status[3],
1725                                fdc->status[4],
1726                                fdc->status[5],
1727                                fdc->status[6] );
1728                 else
1729                         printf("No status available   ");
1730                 if (fd_sense_int(fdc, &st0, &cyl) != 0)
1731                 {
1732                         printf("[controller is dead now]\n");
1733                         return(0);
1734                 }
1735                 printf("ST0 = %x, PCN = %x\n", st0, cyl);
1736                 return(0);
1737         }
1738         /*XXX confusing: some branches return immediately, others end up here*/
1739         return(1); /* Come back immediatly to new state */
1740 }
1741
1742 static int
1743 retrier(fdcu)
1744         fdcu_t fdcu;
1745 {
1746         struct subdev *sd;
1747         fdc_p fdc = fdc_data + fdcu;
1748         register struct buf *bp;
1749         int fdu;
1750
1751         bp = bufq_first(&fdc->head);
1752
1753         if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY)
1754                 goto fail;
1755         switch(fdc->retry)
1756         {
1757         case 0: case 1: case 2:
1758                 fdc->state = SEEKCOMPLETE;
1759                 break;
1760         case 3: case 4: case 5:
1761                 fdc->state = STARTRECAL;
1762                 break;
1763         case 6:
1764                 fdc->state = RESETCTLR;
1765                 break;
1766         case 7:
1767                 break;
1768         default:
1769         fail:
1770                 {
1771                         dev_t sav_b_dev = bp->b_dev;
1772                         /* Trick diskerr */
1773                         bp->b_dev = makedev(major(bp->b_dev),
1774                                     (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
1775                         diskerr(bp, "fd", "hard error", LOG_PRINTF,
1776                                 fdc->fd->skip / DEV_BSIZE,
1777                                 (struct disklabel *)NULL);
1778                         bp->b_dev = sav_b_dev;
1779                         if (fdc->flags & FDC_STAT_VALID)
1780                         {
1781                                 printf(
1782                         " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n",
1783                                        fdc->status[0], NE7_ST0BITS,
1784                                        fdc->status[1], NE7_ST1BITS,
1785                                        fdc->status[2], NE7_ST2BITS,
1786                                        fdc->status[3], fdc->status[4],
1787                                        fdc->status[5]);
1788                         }
1789                         else
1790                                 printf(" (No status)\n");
1791                 }
1792                 bp->b_flags |= B_ERROR;
1793                 bp->b_error = EIO;
1794                 bp->b_resid += bp->b_bcount - fdc->fd->skip;
1795                 bufq_remove(&fdc->head, bp);
1796         
1797                 /* Tell devstat we have finished with the transaction */
1798                 devstat_end_transaction(&fdc->fd->device_stats,
1799                                         bp->b_bcount - bp->b_resid,
1800                                         DEVSTAT_TAG_NONE,
1801                                         (bp->b_flags & B_READ) ? DEVSTAT_READ :
1802                                                                  DEVSTAT_WRITE);
1803                 fdc->fd->skip = 0;
1804                 biodone(bp);
1805                 fdc->state = FINDWORK;
1806                 fdc->flags |= FDC_NEEDS_RESET;
1807                 fdc->fd = (fd_p) 0;
1808                 fdc->fdu = -1;
1809                 return(1);
1810         }
1811         fdc->retry++;
1812         return(1);
1813 }
1814
1815 static int
1816 fdformat(dev, finfo, p)
1817         dev_t dev;
1818         struct fd_formb *finfo;
1819         struct proc *p;
1820 {
1821         fdu_t   fdu;
1822         fd_p    fd;
1823
1824         struct buf *bp;
1825         int rv = 0, s;
1826         size_t fdblk;
1827
1828         fdu     = FDUNIT(minor(dev));
1829         fd      = &fd_data[fdu];
1830         fdblk = 128 << fd->ft->secsize;
1831
1832         /* set up a buffer header for fdstrategy() */
1833         bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
1834         if(bp == 0)
1835                 return ENOBUFS;
1836         /*
1837          * keep the process from being swapped
1838          */
1839         p->p_flag |= P_PHYSIO;
1840         bzero((void *)bp, sizeof(struct buf));
1841         bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
1842         bp->b_proc = p;
1843
1844         /*
1845          * calculate a fake blkno, so fdstrategy() would initiate a
1846          * seek to the requested cylinder
1847          */
1848         bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
1849                 + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
1850
1851         bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
1852         bp->b_data = (caddr_t)finfo;
1853
1854         /* now do the format */
1855         bp->b_dev = dev;
1856         fdstrategy(bp);
1857
1858         /* ...and wait for it to complete */
1859         s = splbio();
1860         while(!(bp->b_flags & B_DONE))
1861         {
1862                 rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
1863                 if(rv == EWOULDBLOCK)
1864                         break;
1865         }
1866         splx(s);
1867
1868         if(rv == EWOULDBLOCK) {
1869                 /* timed out */
1870                 rv = EIO;
1871                 biodone(bp);
1872         }
1873         if(bp->b_flags & B_ERROR)
1874                 rv = bp->b_error;
1875         /*
1876          * allow the process to be swapped
1877          */
1878         p->p_flag &= ~P_PHYSIO;
1879         free(bp, M_TEMP);
1880         return rv;
1881 }
1882
1883 /*
1884  * TODO: don't allocate buffer on stack.
1885  */
1886
1887 static int
1888 fdioctl(dev, cmd, addr, flag, p)
1889         dev_t dev;
1890         u_long cmd;
1891         caddr_t addr;
1892         int flag;
1893         struct proc *p;
1894 {
1895         fdu_t   fdu = FDUNIT(minor(dev));
1896         fd_p    fd = &fd_data[fdu];
1897         size_t fdblk;
1898
1899         struct fd_type *fdt;
1900         struct disklabel *dl;
1901         char buffer[DEV_BSIZE];
1902         int error = 0;
1903
1904 #if NFT > 0
1905         int type = FDTYPE(minor(dev));
1906
1907         /* check for a tape ioctl */
1908         if (type & F_TAPE_TYPE)
1909                 return ftioctl(dev, cmd, addr, flag, p);
1910 #endif
1911
1912         fdblk = 128 << fd->ft->secsize;
1913
1914         switch (cmd)
1915         {
1916         case DIOCGDINFO:
1917                 bzero(buffer, sizeof (buffer));
1918                 dl = (struct disklabel *)buffer;
1919                 dl->d_secsize = fdblk;
1920                 fdt = fd_data[FDUNIT(minor(dev))].ft;
1921                 dl->d_secpercyl = fdt->size / fdt->tracks;
1922                 dl->d_type = DTYPE_FLOPPY;
1923
1924                 if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl)
1925                     == NULL)
1926                         error = 0;
1927                 else
1928                         error = EINVAL;
1929
1930                 *(struct disklabel *)addr = *dl;
1931                 break;
1932
1933         case DIOCSDINFO:
1934                 if ((flag & FWRITE) == 0)
1935                         error = EBADF;
1936                 break;
1937
1938         case DIOCWLABEL:
1939                 if ((flag & FWRITE) == 0)
1940                         error = EBADF;
1941                 break;
1942
1943         case DIOCWDINFO:
1944                 if ((flag & FWRITE) == 0)
1945                 {
1946                         error = EBADF;
1947                         break;
1948                 }
1949
1950                 dl = (struct disklabel *)addr;
1951
1952                 if ((error = setdisklabel((struct disklabel *)buffer, dl,
1953                                           (u_long)0)) != 0)
1954                         break;
1955
1956                 error = writedisklabel(dev, fdstrategy,
1957                                        (struct disklabel *)buffer);
1958                 break;
1959         case FD_FORM:
1960                 if((flag & FWRITE) == 0)
1961                         error = EBADF;  /* must be opened for writing */
1962                 else if(((struct fd_formb *)addr)->format_version !=
1963                         FD_FORMAT_VERSION)
1964                         error = EINVAL; /* wrong version of formatting prog */
1965                 else
1966                         error = fdformat(dev, (struct fd_formb *)addr, p);
1967                 break;
1968
1969         case FD_GTYPE:                  /* get drive type */
1970                 *(struct fd_type *)addr = *fd->ft;
1971                 break;
1972
1973         case FD_STYPE:                  /* set drive type */
1974                 /* this is considered harmful; only allow for superuser */
1975                 if(suser(p->p_ucred, &p->p_acflag) != 0)
1976                         return EPERM;
1977                 *fd->ft = *(struct fd_type *)addr;
1978                 break;
1979
1980         case FD_GOPTS:                  /* get drive options */
1981                 *(int *)addr = fd->options;
1982                 break;
1983
1984         case FD_SOPTS:                  /* set drive options */
1985                 fd->options = *(int *)addr;
1986                 break;
1987
1988         default:
1989                 error = ENOTTY;
1990                 break;
1991         }
1992         return (error);
1993 }
1994
1995
1996 static fd_devsw_installed = 0;
1997
1998 static void     fd_drvinit(void *notused )
1999 {
2000
2001         if( ! fd_devsw_installed ) {
2002                 cdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_cdevsw);
2003                 fd_devsw_installed = 1;
2004         }
2005 }
2006
2007 SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL)
2008
2009
2010 #endif
2011
2012 /*
2013  * Hello emacs, these are the
2014  * Local Variables:
2015  *  c-indent-level:               8
2016  *  c-continued-statement-offset: 8
2017  *  c-continued-brace-offset:     0
2018  *  c-brace-offset:              -8
2019  *  c-brace-imaginary-offset:     0
2020  *  c-argdecl-indent:             8
2021  *  c-label-offset:              -8
2022  *  c++-hanging-braces:           1
2023  *  c++-access-specifier-offset: -8
2024  *  c++-empty-arglist-indent:     8
2025  *  c++-friend-offset:            0
2026  * End:
2027  */