iwm_fd.c revision 1.11.12.1 1 /* $NetBSD: iwm_fd.c,v 1.11.12.1 2002/09/17 21:15:33 nathanw Exp $ */
2
3 /*
4 * Copyright (c) 1997, 1998 Hauke Fath. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * iwm_fd.c -- Sony (floppy disk) driver for m68k Macintoshes
31 *
32 * The present implementation supports the GCR format (800K) on
33 * non-{DMA,IOP} machines.
34 */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/callout.h>
38 #include <sys/kernel.h>
39 #include <sys/file.h>
40 #include <sys/ioctl.h>
41 #include <sys/malloc.h>
42 #include <sys/device.h>
43
44 #define FSTYPENAMES
45 #define DKTYPENAMES
46 #include <sys/disklabel.h>
47
48 #include <sys/disk.h>
49 #include <sys/dkbad.h>
50 #include <sys/buf.h>
51 #include <sys/uio.h>
52 #include <sys/stat.h>
53 #include <sys/syslog.h>
54 #include <sys/conf.h>
55
56 #include <machine/autoconf.h>
57 #include <machine/cpu.h>
58
59 #include <mac68k/obio/iwmreg.h>
60 #include <mac68k/obio/iwm_fdvar.h>
61
62 /**
63 ** Private functions
64 **/
65 static int map_iwm_base __P((vm_offset_t base));
66
67 /* Autoconfig */
68 int iwm_match __P((struct device *, struct cfdata *, void *));
69 void iwm_attach __P((struct device *, struct device *, void *));
70 int iwm_print __P((void *, const char *));
71 int fd_match __P((struct device *, struct cfdata *, void *));
72 void fd_attach __P((struct device *, struct device *, void *));
73 int fd_print __P((void *, const char *));
74
75 /* Disklabel stuff */
76 static void fdGetDiskLabel __P((fd_softc_t *fd, dev_t dev));
77 static void fdPrintDiskLabel __P((struct disklabel *lp));
78
79 static fdInfo_t *getFDType __P((short unit));
80 static fdInfo_t *fdDeviceToType __P((fd_softc_t *fd, dev_t dev));
81
82 static void fdstart __P((fd_softc_t *fd));
83 static void remap_geometry __P((daddr_t block, int heads, diskPosition_t *loc));
84 static void motor_off __P((void *param));
85 static int seek __P((fd_softc_t *fd, int style));
86 static int checkTrack __P((diskPosition_t *loc, int debugFlag));
87 static int initCylinderCache __P((fd_softc_t *fd));
88 static void invalidateCylinderCache __P((fd_softc_t *fd));
89
90 #ifdef _LKM
91 static int probe_fd __P((void));
92 int fd_mod_init __P((void));
93 void fd_mod_free __P((void));
94 #endif
95
96 static int fdstart_Init __P((fd_softc_t *fd));
97 static int fdstart_Seek __P((fd_softc_t *fd));
98 static int fdstart_Read __P((fd_softc_t *fd));
99 static int fdstart_Write __P((fd_softc_t *fd));
100 static int fdstart_Flush __P((fd_softc_t *fd));
101 static int fdstart_IOFinish __P((fd_softc_t *fd));
102 static int fdstart_IOErr __P((fd_softc_t *fd));
103 static int fdstart_Fault __P((fd_softc_t *fd));
104 static int fdstart_Exit __P((fd_softc_t *fd));
105
106
107 /**
108 ** Driver debugging
109 **/
110
111 #ifdef DEBUG
112 #define IWM_DEBUG
113 #endif
114
115
116 static void hexDump __P((u_char *buf, int len));
117
118 /*
119 * Stuff taken from Egan/Teixeira ch 8: 'if(TRACE_FOO)' debug output
120 * statements don't break indentation, and when DEBUG is not defined,
121 * the compiler code optimizer drops them as dead code.
122 */
123 #ifdef IWM_DEBUG
124 #define M_TRACE_CONFIG 0x0001
125 #define M_TRACE_OPEN 0x0002
126 #define M_TRACE_CLOSE 0x0004
127 #define M_TRACE_READ 0x0008
128 #define M_TRACE_WRITE 0x0010
129 #define M_TRACE_STRAT (M_TRACE_READ | M_TRACE_WRITE)
130 #define M_TRACE_IOCTL 0x0020
131 #define M_TRACE_STEP 0x0040
132 #define M_TRACE_ALL 0xFFFF
133
134 #define TRACE_CONFIG (iwmDebugging & M_TRACE_CONFIG)
135 #define TRACE_OPEN (iwmDebugging & M_TRACE_OPEN)
136 #define TRACE_CLOSE (iwmDebugging & M_TRACE_CLOSE)
137 #define TRACE_READ (iwmDebugging & M_TRACE_READ)
138 #define TRACE_WRITE (iwmDebugging & M_TRACE_WRITE)
139 #define TRACE_STRAT (iwmDebugging & M_TRACE_STRAT)
140 #define TRACE_IOCTL (iwmDebugging & M_TRACE_IOCTL)
141 #define TRACE_STEP (iwmDebugging & M_TRACE_STEP)
142 #define TRACE_ALL (iwmDebugging & M_TRACE_ALL)
143
144 /* -1 = all active */
145 int iwmDebugging = 0 /* | M_TRACE_OPEN | M_TRACE_STRAT | M_TRACE_IOCTL */ ;
146
147 #else
148 #define TRACE_CONFIG 0
149 #define TRACE_OPEN 0
150 #define TRACE_CLOSE 0
151 #define TRACE_READ 0
152 #define TRACE_WRITE 0
153 #define TRACE_STRAT 0
154 #define TRACE_IOCTL 0
155 #define TRACE_STEP 0
156 #define TRACE_ALL 0
157 #endif
158
159 #define DISABLED 0
160
161
162
163 /**
164 ** Module-global Variables
165 **/
166
167 /* The IWM base address */
168 u_long IWMBase;
169
170 /*
171 * Table of supported disk types.
172 * The table order seems to be pretty standardized across NetBSD ports, but
173 * then, they are all MFM... So we roll our own for now.
174 */
175 static fdInfo_t fdTypes[] = {
176 {1, 80, 512, 10, 10, 800, 12, 2, IWM_GCR, "400K Sony"},
177 {2, 80, 512, 10, 20, 1600, 12, 2, IWM_GCR, "800K Sony"}
178 };
179
180 /* Table of GCR disk zones for one side (see IM II-211, The Disk Driver) */
181 static diskZone_t diskZones[] = {
182 {16, 12, 0, 191},
183 {16, 11, 192, 367},
184 {16, 10, 368, 527},
185 {16, 9, 528, 671},
186 {16, 8, 672, 799}
187 };
188
189 /* Drive format codes/indexes */
190 enum {
191 IWM_400K_GCR = 0,
192 IWM_800K_GCR = 1,
193 IWM_720K_MFM = 2,
194 IWM_1440K_MFM = 3
195 };
196
197
198 /**
199 ** Autoconfiguration code
200 **/
201
202 /*
203 * Autoconfig data structures
204 *
205 * These data structures (see <sys/device.h>) are referenced in
206 * compile/$KERNEL/ioconf.c, which is generated by config(8).
207 * Their names are formed like {device}_{ca,cd}.
208 *
209 * {device}_ca
210 * is used for dynamically allocating driver data, probing and
211 * attaching a device;
212 *
213 * {device}_cd
214 * references all found devices of a type.
215 */
216 #ifdef _LKM
217
218 struct cfdriver iwm_cd = {
219 NULL, /* Ptr to array of devices found */
220 "iwm", /* Device name string */
221 DV_DULL, /* Device classification */
222 0 /* Number of devices found */
223 };
224 struct cfdriver fd_cd = {
225 NULL,
226 "fd",
227 DV_DISK,
228 0
229 };
230
231 #else /* defined _LKM */
232
233 extern struct cfdriver iwm_cd;
234 extern struct cfdriver fd_cd;
235
236 #endif /* defined _LKM */
237
238 /* IWM floppy disk controller */
239 struct cfattach iwm_ca = {
240 sizeof(iwm_softc_t), /* Size of device data for malloc() */
241 iwm_match, /* Probe device and return match level */
242 iwm_attach /* Initialize and attach device */
243 };
244
245 /* Attached floppy disk drives */
246 struct cfattach fd_ca = {
247 sizeof(fd_softc_t),
248 fd_match,
249 fd_attach
250 };
251
252 dev_type_open(fdopen);
253 dev_type_close(fdclose);
254 dev_type_read(fdread);
255 dev_type_write(fdwrite);
256 dev_type_ioctl(fdioctl);
257 dev_type_strategy(fdstrategy);
258
259 const struct bdevsw fd_bdevsw = {
260 fdopen, fdclose, fdstrategy, fdioctl, nodump, nosize, D_DISK
261 };
262
263 const struct cdevsw fd_cdevsw = {
264 fdopen, fdclose, fdread, fdwrite, fdioctl,
265 nostop, notty, nopoll, nommap, D_DISK
266 };
267
268 /* disk(9) framework device switch */
269 struct dkdriver fd_dkDriver = {
270 fdstrategy
271 };
272
273 /*** Configure the IWM controller ***/
274
275 /*
276 * iwm_match
277 *
278 * Is the IWM chip present? Here, *auxp is a ptr to struct confargs
279 * (see <mac68k/mac68k/autoconf.h>), which does not hold any information
280 * to match against. After all, that's what the obio concept is
281 * about: Onboard components that are present depending (only)
282 * on machine type.
283 */
284 int
285 iwm_match(parent, match, auxp)
286 struct device *parent;
287 struct cfdata *match;
288 void *auxp;
289 {
290 int matched;
291 #ifdef _LKM
292 int iwmErr;
293 #endif
294 extern u_long IOBase; /* from mac68k/machdep.c */
295 extern u_long IWMBase;
296
297 if (0 == map_iwm_base(IOBase)) {
298 /*
299 * Unknown machine HW:
300 * The SWIM II/III chips that are present in post-Q700
301 * '040 Macs have dropped the IWM register structure.
302 * We know next to nothing about the SWIM.
303 */
304 matched = 0;
305 if (TRACE_CONFIG)
306 printf("IWM or SWIM not found: Unknown location (SWIM II?).\n");
307 } else {
308 matched = 1;
309 if (TRACE_CONFIG) {
310 printf("iwm: IWMBase mapped to 0x%lx in VM.\n",
311 IWMBase);
312 }
313 #ifdef _LKM
314 iwmErr = iwmInit();
315 if (TRACE_CONFIG)
316 printf("initIWM() says %d.\n", iwmErr);
317 matched = (iwmErr == 0) ? 1 : 0;
318 #endif
319 }
320 return matched;
321 }
322
323
324 /*
325 * iwm_attach
326 *
327 * The IWM is present, initialize it. Then look up the connected drives
328 * and attach them.
329 */
330 void
331 iwm_attach(parent, self, auxp)
332 struct device *parent;
333 struct device *self;
334 void *auxp;
335 {
336 int iwmErr;
337 iwm_softc_t *iwm;
338 iwmAttachArgs_t ia;
339
340 printf(": Apple GCR floppy disk controller\n");
341 iwm = (iwm_softc_t *)self;
342
343 iwmErr = iwmInit();
344 if (TRACE_CONFIG)
345 printf("initIWM() says %d.\n", iwmErr);
346
347 if (0 == iwmErr) {
348 /* Set up the IWM softc */
349 iwm->maxRetries = 10;
350
351 /* Look for attached drives */
352 for (ia.unit = 0; ia.unit < IWM_MAX_DRIVE; ia.unit++) {
353 iwm->fd[ia.unit] = NULL;
354 ia.driveType = getFDType(ia.unit);
355 if (NULL != ia.driveType)
356 config_found_sm(self, (void *)&ia,
357 fd_print, NULL);
358 }
359 if (TRACE_CONFIG)
360 printf("iwm: Initialization completed.\n");
361 } else {
362 printf("iwm: Chip revision not supported (%d)\n", iwmErr);
363 }
364 }
365
366
367 /*
368 * iwm_print -- print device configuration.
369 *
370 * If the device is not configured 'controller' it is NULL and
371 * we print a message in the *Attach routine; the return value
372 * of *Print() is ignored.
373 */
374 int
375 iwm_print(auxp, controller)
376 void *auxp;
377 const char *controller;
378 {
379 return UNCONF;
380 }
381
382
383 /*
384 * map_iwm_base
385 *
386 * Map physical IO address of IWM to VM address
387 */
388 static int
389 map_iwm_base(base)
390 vm_offset_t base;
391 {
392 int known;
393 extern u_long IWMBase;
394
395 switch (current_mac_model->class) {
396 case MACH_CLASSQ:
397 case MACH_CLASSQ2:
398 case MACH_CLASSP580:
399 IWMBase = base + 0x1E000;
400 known = 1;
401 break;
402 case MACH_CLASSII:
403 case MACH_CLASSPB:
404 case MACH_CLASSDUO:
405 case MACH_CLASSIIci:
406 case MACH_CLASSIIsi:
407 case MACH_CLASSIIvx:
408 case MACH_CLASSLC:
409 IWMBase = base + 0x16000;
410 known = 1;
411 break;
412 case MACH_CLASSIIfx:
413 case MACH_CLASSAV:
414 default:
415 /*
416 * Neither IIfx/Q9[05]0 style IOP controllers nor
417 * Q[68]40AV DMA based controllers are supported.
418 */
419 if (TRACE_CONFIG)
420 printf("Unknown floppy controller chip.\n");
421 IWMBase = 0L;
422 known = 0;
423 break;
424 }
425 return known;
426 }
427
428
429 /*** Configure Sony disk drive(s) ***/
430
431 /*
432 * fd_match
433 */
434 int
435 fd_match(parent, match, auxp)
436 struct device *parent;
437 struct cfdata *match;
438 void *auxp;
439 {
440 int matched, cfUnit;
441 struct cfdata *cfp;
442 iwmAttachArgs_t *fdParams;
443
444 cfp = match;
445 fdParams = (iwmAttachArgs_t *)auxp;
446 cfUnit = cfp->cf_loc[0];
447 matched = (cfUnit == fdParams->unit || cfUnit == -1) ? 1 : 0;
448 if (TRACE_CONFIG) {
449 printf("fdMatch() drive %d ? cfUnit = %d\n",
450 fdParams->unit, cfp->cf_loc[0]);
451 }
452 return matched;
453 }
454
455
456 /*
457 * fd_attach
458 *
459 * We have checked that the IWM is fine and the drive is present,
460 * so we can attach it.
461 */
462 void
463 fd_attach(parent, self, auxp)
464 struct device *parent;
465 struct device *self;
466 void *auxp;
467 {
468 iwm_softc_t *iwm;
469 fd_softc_t *fd;
470 iwmAttachArgs_t *ia;
471 int driveInfo;
472
473 iwm = (iwm_softc_t *)parent;
474 fd = (fd_softc_t *)self;
475 ia = (iwmAttachArgs_t *)auxp;
476
477 driveInfo = iwmCheckDrive(ia->unit);
478
479 fd->currentType = ia->driveType;
480 fd->unit = ia->unit;
481 fd->defaultType = &fdTypes[IWM_800K_GCR];
482 fd->stepDirection = 0;
483
484 iwm->fd[ia->unit] = fd; /* iwm has ptr to this drive */
485 iwm->drives++;
486
487 bufq_alloc(&fd->bufQueue, BUFQ_DISKSORT|BUFQ_SORT_CYLINDER);
488 callout_init(&fd->motor_ch);
489
490 printf(" drive %d: ", fd->unit);
491
492 if (IWM_NO_DISK & driveInfo) {
493 printf("(drive empty)\n");
494 } else
495 if (!(IWM_DD_DISK & driveInfo)) {
496 printf("(HD disk -- not supported)\n");
497 iwmDiskEject(fd->unit); /* XXX */
498 } else {
499 printf("%s %d cyl, %d head(s)\n",
500 fd->currentType->description,
501 fd->currentType->tracks,
502 fd->currentType->heads);
503 }
504 if (TRACE_CONFIG) {
505 int reg, flags, spl;
506
507 /* List contents of drive status registers */
508 spl = spl6();
509 for (reg = 0; reg < 0x10; reg++) {
510 flags = iwmQueryDrvFlag(fd->unit, reg);
511 printf("iwm: Drive register 0x%x = 0x%x\n", reg, flags);
512 }
513 splx(spl);
514 }
515 fd->diskInfo.dk_name = fd->devInfo.dv_xname;
516 fd->diskInfo.dk_driver = &fd_dkDriver;
517 disk_attach(&fd->diskInfo);
518 }
519
520
521 /*
522 * fdPrint -- print device configuration.
523 *
524 * If the device is not configured 'controller' refers to a name string
525 * we print here.
526 * Else it is NULL and we print a message in the *Attach routine; the
527 * return value of *Print() is ignored.
528 */
529 int
530 fd_print(auxp, controller)
531 void *auxp;
532 const char *controller;
533 {
534 iwmAttachArgs_t *ia;
535
536 ia = (iwmAttachArgs_t *)auxp;
537 if (NULL != controller)
538 printf("fd%d at %s", ia->unit, controller);
539 return UNCONF;
540 }
541
542
543 #ifdef _LKM
544
545 static iwm_softc_t *iwm;
546
547 /*
548 * fd_mod_init
549 *
550 * Any initializations necessary after loading the module happen here.
551 */
552 int
553 fd_mod_init(void)
554 {
555 int err;
556
557 iwm = (iwm_softc_t *)malloc(sizeof(iwm_softc_t), M_DEVBUF, M_WAITOK);
558
559 err = (1 == iwm_match(NULL, NULL, NULL)) ? 0 : EIO;
560 if (!err) {
561 memset(iwm, 0, sizeof(iwm_softc_t));
562 iwm->maxRetries = 10;
563 err = (0 == probe_fd()) ? 0 : EIO;
564 }
565 return err;
566 }
567
568
569 /*
570 * fd_mod_free
571 *
572 * Necessary clean-up before unloading the module.
573 */
574 void
575 fd_mod_free(void)
576 {
577 int unit, spl;
578
579 spl = splbio();
580 /* Release any allocated memory */
581 for (unit = 0; unit < IWM_MAX_DRIVE; unit++)
582 if (iwm->fd[unit] != NULL) {
583 /*
584 * Let's hope there is only one task per drive,
585 * see callout(9).
586 */
587 callout_stop(&iwm->fd[unit]->motor_ch);
588 disk_detach(&iwm->fd[unit]->diskInfo);
589 free(iwm->fd[unit], M_DEVBUF);
590 iwm->fd[unit] = NULL;
591 }
592 free(iwm, M_DEVBUF);
593 splx(spl);
594 }
595
596
597 /*
598 * probe_fd
599 *
600 * See if there are any drives out there and configure them.
601 * If we find a drive we allocate a softc structure for it and
602 * insert its address into the iwm_softc.
603 *
604 * XXX Merge the remainder of probeFD() with the autoconfig framework.
605 */
606 static int
607 probe_fd(void)
608 {
609 fd_softc_t *fd;
610 iwmAttachArgs_t ia;
611 int err, unit;
612
613 err = 0;
614 for (ia.unit = 0; ia.unit < IWM_MAX_DRIVE; ia.unit++) {
615 ia.driveType = getFDType(ia.unit);
616 if (NULL == ia.driveType) {
617 iwm->fd[ia.unit] = NULL;
618 continue;
619 }
620 fd = (fd_softc_t *)malloc(sizeof(fd_softc_t),
621 M_DEVBUF, M_WAITOK);
622 if (fd == NULL) {
623 err = ENOMEM;
624 break;
625 } else {
626 memset(fd, 0, sizeof(fd_softc_t));
627
628 /* This is usually set by the autoconfig framework */
629 sprintf(fd->devInfo.dv_xname, "fd%d%c", ia.unit, 'a');
630 fd_attach((struct device *)iwm, (struct device *)fd,
631 &ia);
632 }
633 }
634 if (err) {
635 /* Release any allocated memory */
636 for (unit = 0; unit < IWM_MAX_DRIVE; unit++)
637 if (iwm->fd[unit] != NULL) {
638 free(iwm->fd[unit], M_DEVBUF);
639 iwm->fd[unit] = NULL;
640 }
641 }
642 return err;
643 }
644
645 #endif /* defined _LKM */
646
647
648 /**
649 ** Implementation section of driver interface
650 **
651 ** The prototypes for these functions are set up automagically
652 ** by macros in mac68k/conf.c. Their names are generated from {fd}
653 ** and {open,close,strategy,dump,size,read,write}. The driver entry
654 ** points are then plugged into bdevsw[] and cdevsw[].
655 **/
656
657
658 /*
659 * fdopen
660 *
661 * Open a floppy disk device.
662 */
663 int
664 fdopen(dev, flags, devType, proc)
665 dev_t dev;
666 int flags;
667 int devType;
668 struct proc *proc;
669 {
670 fd_softc_t *fd;
671 fdInfo_t *info;
672 int partitionMask;
673 int fdType, fdUnit;
674 int ierr, err;
675 #ifndef _LKM
676 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
677 #endif
678 info = NULL; /* XXX shut up egcs */
679
680 /*
681 * See <device.h> for struct cfdriver, <disklabel.h> for
682 * DISKUNIT() and <atari/atari/device.h> for getsoftc().
683 */
684 fdType = minor(dev) % MAXPARTITIONS;
685 fdUnit = minor(dev) / MAXPARTITIONS;
686 if (TRACE_OPEN)
687 printf("iwm: Open drive %d", fdUnit);
688
689 /* Check if device # is valid */
690 err = (iwm->drives < fdUnit) ? ENXIO : 0;
691 if (!err) {
692 (void)iwmSelectDrive(fdUnit);
693 if (TRACE_OPEN)
694 printf(".\n Get softc");
695
696 /* Get fd state */
697 fd = iwm->fd[fdUnit];
698 err = (NULL == fd) ? ENXIO : 0;
699 }
700 if (!err) {
701 if (fd->state & IWM_FD_IS_OPEN) {
702 /*
703 * Allow multiple open calls only if for identical
704 * floppy format.
705 */
706 if (TRACE_OPEN)
707 printf(".\n Drive already opened!\n");
708 err = (fd->partition == fdType) ? 0 : ENXIO;
709 } else {
710 if (TRACE_OPEN)
711 printf(".\n Get format info");
712
713 /* Get format type */
714 info = fdDeviceToType(fd, dev);
715 if (NULL == info) {
716 err = ENXIO;
717 if (TRACE_OPEN)
718 printf(".\n No such drive.\n");
719 }
720 }
721 }
722 if (!err && !(fd->state & IWM_FD_IS_OPEN)) {
723 if (TRACE_OPEN)
724 printf(".\n Set diskInfo flags.\n");
725
726 fd->writeLabel = 0; /* XXX currently unused */
727 fd->partition = fdType;
728 fd->currentType = info;
729 fd->drvFlags = iwmCheckDrive(fd->unit);
730
731 if (fd->drvFlags & IWM_NO_DISK) {
732 err = EIO;
733 #ifdef DIAGNOSTIC
734 printf(" Drive %d is empty.\n", fd->unit);
735 #endif
736 } else {
737 if (!(fd->drvFlags & IWM_WRITEABLE) && (flags & FWRITE)) {
738
739 err = EPERM;
740 #ifdef DIAGNOSTIC
741 printf(" Disk is write protected.\n");
742 #endif
743 } else {
744 if (!(fd->drvFlags & IWM_DD_DISK)) {
745 err = ENXIO;
746 #ifdef DIAGNOSTIC
747 printf(" HD format not supported.\n");
748 #endif
749 (void)iwmDiskEject(fd->unit);
750 } else {
751 /* We're open now! */
752 fd->state |= IWM_FD_IS_OPEN;
753 err = initCylinderCache(fd);
754 }
755 }
756 }
757 }
758 if (!err) {
759 /*
760 * Later, we might not want to recalibrate the drive when it
761 * is already open. For now, it doesn't hurt.
762 */
763 if (TRACE_OPEN)
764 printf(" Seek track 00 says");
765
766 memset(&fd->pos, 0, sizeof(diskPosition_t));
767 ierr = seek(fd, IWM_SEEK_RECAL);
768 if (TRACE_OPEN)
769 printf(" %d.\n", ierr);
770 err = (0 == ierr) ? 0 : EIO;
771 }
772 if (!err) {
773 /*
774 * Update disklabel if we are not yet open.
775 * (We shouldn't be: We are synchronous.)
776 */
777 if (fd->diskInfo.dk_openmask == 0)
778 fdGetDiskLabel(fd, dev);
779
780 partitionMask = (1 << fdType);
781
782 switch (devType) {
783 case S_IFCHR:
784 fd->diskInfo.dk_copenmask |= partitionMask;
785 break;
786
787 case S_IFBLK:
788 fd->diskInfo.dk_bopenmask |= partitionMask;
789 break;
790 }
791 fd->diskInfo.dk_openmask =
792 fd->diskInfo.dk_copenmask | fd->diskInfo.dk_bopenmask;
793 }
794 if (TRACE_OPEN)
795 printf("iwm: fdopen() says %d.\n", err);
796 return err;
797 }
798
799
800 /*
801 * fdclose
802 */
803 int
804 fdclose(dev, flags, devType, proc)
805 dev_t dev;
806 int flags;
807 int devType;
808 struct proc *proc;
809 {
810 fd_softc_t *fd;
811 int partitionMask, fdUnit, fdType;
812 #ifndef _LKM
813 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
814 #endif
815
816 if (TRACE_CLOSE)
817 printf("iwm: Closing driver.");
818 fdUnit = minor(dev) / MAXPARTITIONS;
819 fdType = minor(dev) % MAXPARTITIONS;
820 fd = iwm->fd[fdUnit];
821 /* release cylinder cache memory */
822 if (fd->cbuf != NULL)
823 free(fd->cbuf, M_DEVBUF);
824
825 partitionMask = (1 << fdType);
826
827 /* Set state flag. */
828 fd->state &= ~IWM_FD_IS_OPEN;
829
830 switch (devType) {
831 case S_IFCHR:
832 fd->diskInfo.dk_copenmask &= ~partitionMask;
833 break;
834
835 case S_IFBLK:
836 fd->diskInfo.dk_bopenmask &= ~partitionMask;
837 break;
838 }
839 fd->diskInfo.dk_openmask =
840 fd->diskInfo.dk_copenmask | fd->diskInfo.dk_bopenmask;
841 return 0;
842 }
843
844
845 /*
846 * fdioctl
847 *
848 * We deal with all the disk-specific ioctls in <sys/dkio.h> here even if
849 * we do not support them.
850 */
851 int
852 fdioctl(dev, cmd, data, flags, proc)
853 dev_t dev;
854 u_long cmd;
855 caddr_t data;
856 int flags;
857 struct proc *proc;
858 {
859 int result, fdUnit, fdType;
860 fd_softc_t *fd;
861 #ifndef _LKM
862 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
863 #endif
864
865 if (TRACE_IOCTL)
866 printf("iwm: Execute ioctl... ");
867
868 /* Check if device # is valid and get its softc */
869 fdUnit = minor(dev) / MAXPARTITIONS;
870 fdType = minor(dev) % MAXPARTITIONS;
871 if (fdUnit >= iwm->drives) {
872 if (TRACE_IOCTL) {
873 printf("iwm: Wanted device no (%d) is >= %d.\n",
874 fdUnit, iwm->drives);
875 }
876 return ENXIO;
877 }
878 fd = iwm->fd[fdUnit];
879 result = 0;
880
881 switch (cmd) {
882 case DIOCGDINFO:
883 if (TRACE_IOCTL)
884 printf(" DIOCGDINFO: Get in-core disklabel.\n");
885 *(struct disklabel *) data = *(fd->diskInfo.dk_label);
886 result = 0;
887 break;
888
889 case DIOCSDINFO:
890 if (TRACE_IOCTL)
891 printf(" DIOCSDINFO: Set in-core disklabel.\n");
892 result = ((flags & FWRITE) == 0) ? EBADF : 0;
893 if (result == 0)
894 result = setdisklabel(fd->diskInfo.dk_label,
895 (struct disklabel *)data, 0,
896 fd->diskInfo.dk_cpulabel);
897 break;
898
899 case DIOCWDINFO:
900 if (TRACE_IOCTL)
901 printf(" DIOCWDINFO: Set in-core disklabel "
902 "& update disk.\n");
903 result = ((flags & FWRITE) == 0) ? EBADF : 0;
904
905 if (result == 0)
906 result = setdisklabel(fd->diskInfo.dk_label,
907 (struct disklabel *)data, 0,
908 fd->diskInfo.dk_cpulabel);
909 if (result == 0)
910 result = writedisklabel(dev, fdstrategy,
911 fd->diskInfo.dk_label,
912 fd->diskInfo.dk_cpulabel);
913 break;
914
915 case DIOCGPART:
916 if (TRACE_IOCTL)
917 printf(" DIOCGPART: Get disklabel & partition table.\n");
918 ((struct partinfo *)data)->disklab = fd->diskInfo.dk_label;
919 ((struct partinfo *)data)->part =
920 &fd->diskInfo.dk_label->d_partitions[fdType];
921 result = 0;
922 break;
923
924 case DIOCRFORMAT:
925 case DIOCWFORMAT:
926 if (TRACE_IOCTL)
927 printf(" DIOC{R,W}FORMAT: No formatter support (yet?).\n");
928 result = EINVAL;
929 break;
930
931 case DIOCSSTEP:
932 if (TRACE_IOCTL)
933 printf(" DIOCSSTEP: IWM does step handshake.\n");
934 result = EINVAL;
935 break;
936
937 case DIOCSRETRIES:
938 if (TRACE_IOCTL)
939 printf(" DIOCSRETRIES: Set max. # of retries.\n");
940 if (*(int *)data < 0)
941 result = EINVAL;
942 else {
943 iwm->maxRetries = *(int *)data;
944 result = 0;
945 }
946 break;
947
948 case DIOCWLABEL:
949 if (TRACE_IOCTL)
950 printf(" DIOCWLABEL: Set write access to disklabel.\n");
951 result = ((flags & FWRITE) == 0) ? EBADF : 0;
952
953 if (result == 0)
954 fd->writeLabel = *(int *)data;
955 break;
956
957 case DIOCSBAD:
958 if (TRACE_IOCTL)
959 printf(" DIOCSBAD: No bad144-style handling.\n");
960 result = EINVAL;
961 break;
962
963 case ODIOCEJECT:
964 case DIOCEJECT:
965 /* XXX Eject disk only when unlocked */
966 if (TRACE_IOCTL)
967 printf(" DIOCEJECT: Eject disk from unit %d.\n",
968 fd->unit);
969 result = iwmDiskEject(fd->unit);
970 break;
971
972 case DIOCLOCK:
973 /* XXX Use lock to prevent ejectimg a mounted disk */
974 if (TRACE_IOCTL)
975 printf(" DIOCLOCK: No need to (un)lock Sony drive.\n");
976 result = 0;
977 break;
978
979 default:
980 if (TRACE_IOCTL)
981 printf(" Not a disk related ioctl!\n");
982 result = ENOTTY;
983 break;
984 }
985 return result;
986 }
987
988
989 /*
990 * fdread
991 */
992 int
993 fdread(dev, uio, flags)
994 dev_t dev;
995 struct uio *uio;
996 int flags;
997 {
998 return physio(fdstrategy, NULL, dev, B_READ, minphys, uio);
999 }
1000
1001
1002 /*
1003 * fdwrite
1004 */
1005 int
1006 fdwrite(dev, uio, flags)
1007 dev_t dev;
1008 struct uio *uio;
1009 int flags;
1010 {
1011 return physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio);
1012 }
1013
1014
1015 /*
1016 * fdstrategy
1017 *
1018 * Entry point for read and write requests. The strategy routine usually
1019 * queues io requests and kicks off the next transfer if the device is idle;
1020 * but we get no interrupts from the IWM and have to do synchronous
1021 * transfers - no queue.
1022 */
1023 void
1024 fdstrategy(bp)
1025 struct buf *bp;
1026 {
1027 int fdUnit, err, done, spl;
1028 int sectSize, transferSize;
1029 diskPosition_t physDiskLoc;
1030 fd_softc_t *fd;
1031 #ifndef _LKM
1032 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
1033 #endif
1034
1035 err = 0;
1036 done = 0;
1037
1038 fdUnit = minor(bp->b_dev) / MAXPARTITIONS;
1039 if (TRACE_STRAT) {
1040 printf("iwm: fdstrategy()...\n");
1041 printf(" struct buf is at %p\n", bp);
1042 printf(" Allocated buffer size (b_bufsize): 0x0%lx\n",
1043 bp->b_bufsize);
1044 printf(" Base address of buffer (b_data): %p\n",
1045 bp->b_data);
1046 printf(" Bytes to be transferred (b_bcount): 0x0%lx\n",
1047 bp->b_bcount);
1048 printf(" Remaining I/O (b_resid): 0x0%lx\n",
1049 bp->b_resid);
1050 }
1051 /* Check for valid fd unit, controller and io request */
1052
1053 if (fdUnit >= iwm->drives) {
1054 if (TRACE_STRAT)
1055 printf(" No such unit (%d)\n", fdUnit);
1056 err = EINVAL;
1057 }
1058 if (!err) {
1059 fd = iwm->fd[fdUnit];
1060 err = (NULL == fd) ? EINVAL : 0;
1061 }
1062 if (!err) {
1063 sectSize = fd->currentType->sectorSize;
1064 if (bp->b_blkno < 0
1065 || (bp->b_bcount % sectSize) != 0) {
1066 if (TRACE_STRAT)
1067 printf(" Illegal transfer size: "
1068 "block %d, %ld bytes\n",
1069 bp->b_blkno, bp->b_bcount);
1070 err = EINVAL;
1071 }
1072 }
1073 if (!err) {
1074 /* Null transfer: Return, nothing to do. */
1075 if (0 == bp->b_bcount) {
1076 if (TRACE_STRAT)
1077 printf(" Zero transfer length.\n");
1078 done = 1;
1079 }
1080 }
1081 if (!err && !done) {
1082 /* What to do if we touch the boundaries of the disk? */
1083 transferSize = (bp->b_bcount + (sectSize - 1)) / sectSize;
1084 if (bp->b_blkno + transferSize > fd->currentType->secPerDisk) {
1085 if (TRACE_STRAT) {
1086 printf("iwm: Transfer beyond end of disk!\n" \
1087 " (Starting block %d, # of blocks %d," \
1088 " last disk block %d).\n",
1089 bp->b_blkno, transferSize,
1090 fd->currentType->secPerDisk);
1091 }
1092 /* Return EOF if we are exactly at the end of the
1093 * disk, EINVAL if we try to reach past the end; else
1094 * truncate the request. */
1095 transferSize = fd->currentType->secPerDisk -
1096 bp->b_blkno;
1097 if (0 == transferSize) {
1098 bp->b_resid = bp->b_bcount;
1099 done = 1;
1100 } else
1101 if (0 > transferSize)
1102 err = EINVAL;
1103 else
1104 bp->b_bcount = transferSize << DEV_BSHIFT;
1105 }
1106 }
1107 if (!err && !done) {
1108 /*
1109 * Calculate cylinder # for disksort().
1110 *
1111 * XXX Shouldn't we use the (fake) logical cyl no here?
1112 */
1113 remap_geometry(bp->b_blkno, fd->currentType->heads,
1114 &physDiskLoc);
1115 bp->b_rawblkno = bp->b_blkno;
1116 bp->b_cylinder = physDiskLoc.track;
1117
1118 if (TRACE_STRAT) {
1119 printf(" This job starts at b_blkno %d; ",
1120 bp->b_blkno);
1121 printf("it gets sorted for cylinder # %ld.\n",
1122 bp->b_cylinder);
1123 }
1124 spl = splbio();
1125 callout_stop(&fd->motor_ch);
1126 BUFQ_PUT(&fd->bufQueue, bp);
1127 if (fd->sc_active == 0)
1128 fdstart(fd);
1129 splx(spl);
1130 }
1131 /* Clean up, if necessary */
1132 else {
1133 if (TRACE_STRAT)
1134 printf(" fdstrategy() finished early, err = %d.\n",
1135 err);
1136 if (err) {
1137 bp->b_error = err;
1138 bp->b_flags |= B_ERROR;
1139 }
1140 bp->b_resid = bp->b_bcount;
1141 biodone(bp);
1142 }
1143 /* Comment on results */
1144 if (TRACE_STRAT) {
1145 printf("iwm: fdstrategy() done.\n");
1146 printf(" We have b_resid = %ld bytes left, " \
1147 "b_error is %d;\n", bp->b_resid, bp->b_error);
1148 printf(" b_flags are 0x0%lx.\n", bp->b_flags);
1149 }
1150 }
1151
1152
1153
1154 /* ======================================================================== */
1155
1156
1157 /*
1158 * fdstart
1159 *
1160 * we are called from the strategy() routine to perform a data transfer.
1161 *
1162 * The disk(9) framework demands we run at splbio(); our caller
1163 * takes care of that.
1164 *
1165 * Wish we had pascalish local functions here...
1166 */
1167
1168 /* fdstart FSM states */
1169 enum {
1170 state_Init = 0,
1171 state_Seek,
1172 state_Read,
1173 state_Write,
1174 state_Flush,
1175 state_IOFinish,
1176 state_IOErr,
1177 state_Fault,
1178 state_Exit,
1179 state_Done
1180 };
1181
1182 static void
1183 fdstart(fd)
1184 fd_softc_t *fd;
1185 {
1186 int st;
1187
1188 static char *stateDesc[] = {
1189 "Init",
1190 "Seek",
1191 "Read",
1192 "Write",
1193 "Flush",
1194 "IOFinish",
1195 "IOErr",
1196 "Fault",
1197 "Exit",
1198 "Done"
1199 };
1200 int (*state[])(fd_softc_t *fd) = {
1201 fdstart_Init,
1202 fdstart_Seek,
1203 fdstart_Read,
1204 fdstart_Write,
1205 fdstart_Flush,
1206 fdstart_IOFinish,
1207 fdstart_IOErr,
1208 fdstart_Fault,
1209 fdstart_Exit
1210 };
1211
1212 st = state_Init;
1213 do {
1214 if (TRACE_STRAT)
1215 printf(" fdstart state %d [%s] ",
1216 st, stateDesc[st]);
1217
1218 st = (*state[st])(fd);
1219
1220 if (TRACE_STRAT)
1221 printf(".\n");
1222 } while (st != state_Done);
1223 }
1224
1225
1226 /*
1227 * fdstart_Init
1228 *
1229 * Set up things
1230 */
1231 static int
1232 fdstart_Init(fd)
1233 fd_softc_t *fd;
1234 {
1235 struct buf *bp;
1236
1237 /*
1238 * Get the first entry from the queue. This is the buf we gave to
1239 * fdstrategy(); disksort() put it into our softc.
1240 */
1241 bp = BUFQ_PEEK(&fd->bufQueue);
1242 if (NULL == bp) {
1243 if (TRACE_STRAT)
1244 printf("Queue empty: Nothing to do");
1245 return state_Done;
1246 }
1247 fd->ioDirection = bp->b_flags & B_READ;
1248
1249 disk_busy(&fd->diskInfo);
1250 if (!(fd->state & IWM_FD_MOTOR_ON)) {
1251 iwmMotor(fd->unit, 1);
1252 fd->state |= IWM_FD_MOTOR_ON;
1253 }
1254 fd->current_buffer = bp->b_data;
1255
1256 /* XXX - assumes blocks of 512 bytes */
1257 fd->startBlk = bp->b_blkno;
1258
1259 fd->iwmErr = 0;
1260 fd->ioRetries = 0; /* XXX */
1261 fd->seekRetries = 0;
1262 fd->bytesDone = 0;
1263 fd->bytesLeft = bp->b_bcount;
1264 return state_Seek;
1265 }
1266
1267
1268 /*
1269 * fdstart_Seek
1270 */
1271 static int
1272 fdstart_Seek(fd)
1273 fd_softc_t *fd;
1274 {
1275 int state;
1276
1277 /* Calculate the side/track/sector our block is at. */
1278 if (TRACE_STRAT)
1279 printf(" Remap block %d ", fd->startBlk);
1280 remap_geometry(fd->startBlk,
1281 fd->currentType->heads, &fd->pos);
1282 if (TRACE_STRAT)
1283 printf("to c%d_h%d_s%d ", fd->pos.track,
1284 fd->pos.side, fd->pos.sector);
1285
1286 if (fd->cachedSide != fd->pos.side) {
1287 if (TRACE_STRAT)
1288 printf(" (invalidate cache) ");
1289 invalidateCylinderCache(fd);
1290 fd->cachedSide = fd->pos.side;
1291 }
1292
1293 /*
1294 * If necessary, seek to wanted track. Note that
1295 * seek() performs any necessary retries.
1296 */
1297 if (fd->pos.track != fd->pos.oldTrack &&
1298 0 != (fd->iwmErr = seek(fd, IWM_SEEK_VANILLA))) {
1299 state = state_Fault;
1300 } else {
1301 state = (fd->ioDirection == IWM_WRITE)
1302 ? state_Write : state_Read;
1303 }
1304 return state;
1305 }
1306
1307
1308 /*
1309 * fdstart_Read
1310 *
1311 * Transfer a sector from disk. Get it from the track cache, if available;
1312 * otherwise, while we are at it, store in the cache all the sectors we find
1313 * on the way.
1314 *
1315 * Track buffering reads:
1316 * o Look if the sector is already cached.
1317 * o Else, read sectors into track cache until we meet the header of
1318 * the sector we want.
1319 * o Read that sector directly to fs buffer and return.
1320 */
1321 static int
1322 fdstart_Read(fd)
1323 fd_softc_t *fd;
1324 {
1325 int i;
1326 diskPosition_t *pos;
1327 sectorHdr_t *shdr;
1328 #ifndef _LKM
1329 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
1330 #endif
1331
1332 /* Initialize retry counters */
1333 fd->seekRetries = 0;
1334 fd->sectRetries = 0;
1335 pos = &fd->pos;
1336 shdr = &fd->sHdr;
1337
1338 if (TRACE_STRAT)
1339 printf("<%s c%d_h%d_s%d> ",
1340 fd->ioDirection ? "Read" : "Write",
1341 pos->track, pos->side, pos->sector);
1342
1343 /* Sector already cached? */
1344 i = pos->sector;
1345 if (fd->r_slots[i].valid) {
1346 if (TRACE_STRAT)
1347 printf("(cached)");
1348 memcpy(fd->current_buffer, fd->r_slots[i].secbuf,
1349 fd->currentType->sectorSize);
1350 return state_IOFinish;
1351 }
1352
1353 /* Get sector from disk */
1354 shdr->side = pos->side;
1355 shdr->sector = pos->sector;
1356 shdr->track = pos->track;
1357
1358 (void)iwmSelectSide(pos->side);
1359 fd->iwmErr = iwmReadSector(&fd->sHdr, fd->r_slots,
1360 fd->current_buffer);
1361
1362 /* Check possible error conditions */
1363 if (TRACE_STRAT)
1364 printf("c%d_h%d_s%d_err(%d)_sr%d ",
1365 shdr->track, shdr->side >> 3,
1366 shdr->sector, fd->iwmErr, fd->sectRetries);
1367
1368 /* IWM IO error? */
1369 if (fd->iwmErr != 0)
1370 return state_IOErr;
1371
1372 /* Bad seek? Retry */
1373 if (shdr->track != pos->track) {
1374 if (TRACE_STRAT) {
1375 printf("Wanted track %d, got %d, %d seek retries.\n",
1376 pos->track, shdr->track, fd->seekRetries);
1377 }
1378 if (iwm->maxRetries > fd->seekRetries++) {
1379 fd->iwmErr = seek(fd, IWM_SEEK_RECAL);
1380 if (TRACE_STRAT) {
1381 printf("[%d]", fd->seekRetries);
1382 (void)checkTrack(&fd->pos, 1);
1383 }
1384 } else
1385 fd->iwmErr = seekErr;
1386 return (0 == fd->iwmErr) ? state_Read : state_Fault;
1387 }
1388
1389 /* Sector not found? */
1390 if (shdr->sector != pos->sector) {
1391 if (TRACE_STRAT)
1392 printf("c%d_h%d_s%d sect not found, %d retries ",
1393 shdr->track, shdr->side >> 3,
1394 shdr->sector, fd->sectRetries);
1395 fd->iwmErr = noAdrMkErr;
1396 return state_Fault;
1397 }
1398
1399 /* Success */
1400 return state_IOFinish;
1401 }
1402
1403
1404 /*
1405 * fdstart_Write
1406 *
1407 * Insert a sector into a write buffer slot and mark the slot dirty.
1408 */
1409 static int
1410 fdstart_Write(fd)
1411 fd_softc_t *fd;
1412 {
1413 int i;
1414
1415 /* XXX let's see... */
1416 fd->sHdr.side = fd->pos.side;
1417 fd->sHdr.sector = fd->pos.sector;
1418 fd->sHdr.track = fd->pos.track;
1419
1420 i = fd->pos.sector;
1421 fd->w_slots[i].secbuf = fd->current_buffer;
1422 fd->w_slots[i].valid = 1; /* "valid" is a dirty buffer here */
1423
1424 if (TRACE_STRAT)
1425 printf("<%s c%d_h%d_s%d> (cached) ",
1426 fd->ioDirection ? "Read" : "Write",
1427 fd->pos.track, fd->pos.side, fd->pos.sector);
1428 return state_IOFinish;
1429 }
1430
1431
1432
1433 /*
1434 * fdstart_Flush
1435 *
1436 * Flush dirty buffers in the track cache to disk.
1437 */
1438 static int
1439 fdstart_Flush(fd)
1440 fd_softc_t *fd;
1441 {
1442 int state;
1443 int i, dcnt;
1444 diskPosition_t *pos;
1445 sectorHdr_t *shdr;
1446 #ifndef _LKM
1447 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
1448 #endif
1449 dcnt = 0;
1450 pos = &fd->pos;
1451 shdr = &fd->sHdr;
1452
1453 if (TRACE_STRAT) {
1454 for (i=0; i < IWM_MAX_GCR_SECTORS; i++)
1455 if (fd->w_slots[i].valid) {
1456 printf("|%d", i);
1457 dcnt++;
1458 }
1459 printf("|\n");
1460
1461 printf(" <%s c%d_h%d_#s%d>\n",
1462 fd->ioDirection ? "Read" : "Write",
1463 pos->track, pos->side, dcnt);
1464 }
1465 (void)iwmSelectSide(pos->side);
1466 fd->iwmErr = iwmWriteSector(&fd->sHdr, fd->w_slots);
1467
1468 switch (fd->iwmErr) {
1469 case noErr: /* Success */
1470 #ifdef DIAGNOSTIC
1471 /* XXX Panic if buffer not clean? */
1472 for (i=0; i<IWM_MAX_GCR_SECTORS; i++)
1473 if (0 != fd->w_slots[i].valid)
1474 printf("Oops! <c%d_h%d_s%d> not flushed.\n",
1475 fd->pos.track, fd->pos.side,
1476 fd->pos.sector);
1477 #endif
1478 if (TRACE_STRAT)
1479 printf("(Cache flushed, re-initialize) ");
1480 for (i=0; i < IWM_MAX_GCR_SECTORS; i++) {
1481 fd->w_slots[i].valid = 0;
1482 fd->w_slots[i].secbuf = NULL;
1483 }
1484 fd->seekRetries = 0;
1485 state = state_Exit;
1486 break;
1487
1488 case seekErr: /* Bad seek? Retry */
1489 if (TRACE_STRAT) {
1490 printf("Wanted track %d, got %d, %d seek retries.\n",
1491 pos->track, shdr->track, fd->seekRetries);
1492 }
1493 if (iwm->maxRetries > fd->seekRetries++) {
1494 fd->iwmErr = seek(fd, IWM_SEEK_RECAL);
1495 if (TRACE_STRAT) {
1496 printf("[%d]", fd->seekRetries);
1497 }
1498 }
1499 state = (0 == fd->iwmErr) ? state_Exit : state_Fault;
1500 break;
1501
1502 default: /* General IWM IO error? */
1503 state = state_IOErr;
1504 }
1505 return state;
1506 }
1507
1508
1509 /*
1510 * fdstart_IOFinish
1511 *
1512 * Prepare for next block, if any is available
1513 */
1514 static int
1515 fdstart_IOFinish(fd)
1516 fd_softc_t *fd;
1517 {
1518 int state;
1519
1520 if (DISABLED && TRACE_STRAT)
1521 printf("%s c%d_h%d_s%d ok ",
1522 fd->ioDirection ? "Read" : "Write",
1523 fd->sHdr.track, fd->sHdr.side >> 3, fd->sHdr.sector);
1524
1525 fd->bytesDone += fd->currentType->sectorSize;
1526 fd->bytesLeft -= fd->currentType->sectorSize;
1527 fd->current_buffer += fd->currentType->sectorSize;
1528 /*
1529 * Instead of recalculating the chs mapping for
1530 * each and every sector, check for
1531 * 'current sector# <= max sector#' and recalculate
1532 * after overflow.
1533 */
1534 fd->startBlk++;
1535 if (fd->bytesLeft > 0) {
1536 if (++fd->pos.sector < fd->pos.maxSect) {
1537 if (TRACE_STRAT)
1538 printf("continue");
1539 state = (fd->ioDirection == IWM_WRITE)
1540 ? state_Write : state_Read;
1541 }
1542 else {
1543 /*
1544 * Invalidate read cache when changing track;
1545 * flush write cache to disk.
1546 */
1547 if (fd->ioDirection == IWM_WRITE) {
1548 if (TRACE_STRAT)
1549 printf("flush ");
1550 state = (state_Exit == fdstart_Flush(fd))
1551 ? state_Seek : state_IOErr;
1552 }
1553 else {
1554 if (TRACE_STRAT)
1555 printf("step ");
1556 invalidateCylinderCache(fd);
1557 state = state_Seek;
1558 }
1559 }
1560 } else {
1561 state = (fd->ioDirection == IWM_WRITE)
1562 ? state_Flush : state_Exit;
1563 }
1564 return state;
1565 }
1566
1567
1568 /*
1569 * fdstart_IOErr
1570 *
1571 * Bad IO, repeat
1572 */
1573 static int
1574 fdstart_IOErr(fd)
1575 fd_softc_t *fd;
1576 {
1577 int state;
1578 #ifndef _LKM
1579 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
1580 #endif
1581
1582 #ifdef DIAGNOSTIC
1583 printf("iwm%sSector() err = %d, %d retries, on c%d_h%d_s%d.\n",
1584 fd->ioDirection ? "Read" : "Write",
1585 fd->iwmErr, fd->ioRetries, fd->pos.track,
1586 fd->pos.side, fd->pos.sector);
1587 #endif
1588 /* XXX Do statistics */
1589 if (fd->ioRetries++ < iwm->maxRetries)
1590 state = (fd->ioDirection == IWM_WRITE)
1591 ? state_Flush : state_Read;
1592 else
1593 state = state_Fault;
1594 return state;
1595 }
1596
1597
1598 /*
1599 * fdstart_Fault
1600 *
1601 * A non-recoverable error
1602 */
1603 static int
1604 fdstart_Fault(fd)
1605 fd_softc_t *fd;
1606 {
1607 #ifdef DIAGNOSTIC
1608 printf("Seek retries %d, IO retries %d, sect retries %d :\n" \
1609 "\t\t only found c%d_h%d_s%d \n",
1610 fd->seekRetries, fd->ioRetries, fd->sectRetries,
1611 fd->sHdr.track, fd->sHdr.side >> 3, fd->sHdr.sector);
1612 printf("A non-recoverable error: %d ", fd->iwmErr);
1613 #else
1614 /* ARGSUSED */
1615 #endif
1616 return state_Exit;
1617 }
1618
1619
1620 /*
1621 * fdstart_Exit
1622 *
1623 * We are done, for good or bad
1624 */
1625 static int
1626 fdstart_Exit(fd)
1627 fd_softc_t *fd;
1628 {
1629 struct buf *bp;
1630 #ifdef DIAGNOSTIC
1631 int i;
1632 #endif
1633
1634 invalidateCylinderCache(fd);
1635
1636 #ifdef DIAGNOSTIC
1637 /* XXX Panic if buffer not clean? */
1638 for (i=0; i<IWM_MAX_GCR_SECTORS; i++)
1639 if (0 != fd->w_slots[i].valid)
1640 printf("Oops! <c%d_h%d_s%d> not flushed.\n",
1641 fd->pos.track, fd->pos.side, fd->pos.sector);
1642 #endif
1643
1644 bp = BUFQ_GET(&fd->bufQueue);
1645
1646 bp->b_resid = fd->bytesLeft;
1647 bp->b_error = (0 == fd->iwmErr) ? 0 : EIO;
1648 if (fd->iwmErr)
1649 bp->b_flags |= B_ERROR;
1650
1651 if (TRACE_STRAT) {
1652 printf(" fdstart() finished job; fd->iwmErr = %d, b_error = %d",
1653 fd->iwmErr, bp->b_error);
1654 if (DISABLED)
1655 hexDump(bp->b_data, bp->b_bcount);
1656 }
1657 if (DISABLED && TRACE_STRAT)
1658 printf(" Next buf (bufQueue first) at %p\n",
1659 BUFQ_PEEK(&fd->bufQueue));
1660 disk_unbusy(&fd->diskInfo, bp->b_bcount - bp->b_resid);
1661 biodone(bp);
1662 /*
1663 * Stop motor after 10s
1664 *
1665 * XXX Unloading the module while the timeout is still
1666 * running WILL crash the machine.
1667 */
1668 callout_reset(&fd->motor_ch, 10 * hz, motor_off, fd);
1669
1670 return state_Done;
1671 }
1672
1673
1674 /*
1675 * remap_geometry
1676 *
1677 * Remap the rigid UN*X view of a disk's cylinder/sector geometry
1678 * to our zone recorded real Sony drive by splitting the disk
1679 * into zones.
1680 *
1681 * Loop {
1682 * Look if logical block number is in current zone
1683 * NO: Add # of tracks for current zone to track counter
1684 * Process next zone
1685 *
1686 * YES: Subtract (number of first sector of current zone times heads)
1687 * from logical block number, then break up the difference
1688 * in tracks/side/sectors (spt is constant within a zone).
1689 * Done
1690 * }
1691 */
1692 static void
1693 remap_geometry(block, heads, loc)
1694 daddr_t block;
1695 int heads;
1696 diskPosition_t *loc;
1697 {
1698 int zone, spt;
1699 extern diskZone_t diskZones[];
1700
1701 spt = 0; /* XXX Shut up egcs warning */
1702 loc->oldTrack = loc->track;
1703 loc->track = 0;
1704
1705 for (zone = 0; zone < IWM_GCR_DISK_ZONES; zone++) {
1706 if (block >= heads * (diskZones[zone].lastBlock + 1)) {
1707 /* Process full zones */
1708 loc->track += diskZones[zone].tracks;
1709 } else {
1710 /* Process partial zone */
1711 spt = diskZones[zone].sectPerTrack;
1712 block -= heads * diskZones[zone].firstBlock;
1713 loc->track += block / (spt * heads);
1714 loc->sector = (block % spt);
1715 loc->side = (block % (spt * heads)) / spt;
1716 break;
1717 }
1718 }
1719 loc->maxSect = spt;
1720 }
1721
1722
1723 /*
1724 * motor_off
1725 *
1726 * Callback for timeout()
1727 */
1728 static void
1729 motor_off(param)
1730 void *param;
1731 {
1732 int spl;
1733 fd_softc_t *fd;
1734
1735 fd = (fd_softc_t *)param;
1736 if (TRACE_STRAT)
1737 printf("iwm: Switching motor OFF (timeout).\n");
1738 spl = spl6();
1739 (void)iwmMotor(fd->unit, 0);
1740 fd->state &= ~IWM_FD_MOTOR_ON;
1741 splx(spl);
1742 }
1743
1744
1745 /*
1746 * fdGetDiskLabel
1747 *
1748 * Set up disk label with parameters from current disk type.
1749 * Then call the generic disklabel read routine which tries to
1750 * read a label from disk and insert it. If it doesn't exist use
1751 * our defaults.
1752 */
1753 static void
1754 fdGetDiskLabel(fd, dev)
1755 fd_softc_t *fd;
1756 dev_t dev;
1757 {
1758 char *msg;
1759 int fdType;
1760 struct disklabel *lp;
1761 struct cpu_disklabel *clp;
1762
1763 if (TRACE_IOCTL)
1764 printf("iwm: fdGetDiskLabel() for disk %d.\n",
1765 minor(dev) / MAXPARTITIONS);
1766 fdType = minor(dev) % MAXPARTITIONS;
1767 lp = fd->diskInfo.dk_label;
1768 clp = fd->diskInfo.dk_cpulabel;
1769 memset(lp, 0, sizeof(struct disklabel));
1770 memset(clp, 0, sizeof(struct cpu_disklabel));
1771 /*
1772 * How to describe a drive with a variable # of sectors per
1773 * track (8..12) and variable rpm (300..550)? Apple came up
1774 * with ZBR in 1983! Un*x drive management sucks.
1775 */
1776 lp->d_type = DTYPE_FLOPPY;
1777 lp->d_rpm = 300;
1778 lp->d_secsize = fd->currentType->sectorSize;
1779 lp->d_ntracks = fd->currentType->heads;
1780 lp->d_ncylinders = fd->currentType->tracks;
1781 lp->d_nsectors = fd->currentType->secPerTrack;
1782 lp->d_secpercyl = fd->currentType->secPerCyl;
1783 lp->d_secperunit = fd->currentType->secPerDisk;
1784 lp->d_interleave = fd->currentType->interleave;
1785 lp->d_trkseek = fd->currentType->stepRate;
1786
1787 strcpy(lp->d_typename, dktypenames[DTYPE_FLOPPY]);
1788 strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
1789
1790 lp->d_npartitions = fdType + 1;
1791 lp->d_partitions[fdType].p_offset = 0;
1792 lp->d_partitions[fdType].p_size = lp->d_secperunit;
1793 lp->d_partitions[fdType].p_fstype = FS_BSDFFS;
1794 lp->d_partitions[fdType].p_fsize = 512;
1795 lp->d_partitions[fdType].p_frag = 8;
1796
1797 lp->d_magic = DISKMAGIC;
1798 lp->d_magic2 = DISKMAGIC;
1799 lp->d_checksum = dkcksum(lp);
1800 /*
1801 * Call the generic disklabel extraction routine. If we don't
1802 * find a label on disk, keep our faked one.
1803 */
1804 if (TRACE_OPEN)
1805 printf(" now calling readdisklabel()...\n");
1806
1807 msg = readdisklabel(dev, fdstrategy, lp, clp);
1808 if (msg == NULL) {
1809 strncpy(lp->d_packname, "default label",
1810 sizeof(lp->d_packname)); /* XXX - ?? */
1811 }
1812 #ifdef IWM_DEBUG
1813 else
1814 printf("iwm: %s.\n", msg);
1815 #endif
1816 if (TRACE_OPEN)
1817 fdPrintDiskLabel(lp);
1818 }
1819
1820
1821
1822 /*
1823 * initCylinderCache
1824 *
1825 * Allocate cylinder cache and set up pointers to sectors.
1826 */
1827 static int
1828 initCylinderCache(fd)
1829 fd_softc_t *fd;
1830 {
1831 int i;
1832 int err;
1833 int secsize;
1834
1835 err = 0;
1836 secsize = fd->currentType->sectorSize;
1837 fd->cachedSide = 0;
1838
1839 fd->cbuf = (unsigned char *) malloc(IWM_MAX_GCR_SECTORS
1840 * secsize, M_DEVBUF, M_WAITOK);
1841 if (NULL == fd->cbuf)
1842 err = ENOMEM;
1843 else
1844 for (i=0; i < IWM_MAX_GCR_SECTORS; i++) {
1845 fd->w_slots[i].valid = 0;
1846 fd->w_slots[i].secbuf = NULL;
1847
1848 fd->r_slots[i].valid = 0;
1849 fd->r_slots[i].secbuf = fd->cbuf + i * secsize;
1850 }
1851 return err;
1852 }
1853
1854
1855 /*
1856 * invalidateCylinderCache
1857 *
1858 * Switching cylinders (tracks?) invalidates the read cache.
1859 */
1860 static void
1861 invalidateCylinderCache(fd)
1862 fd_softc_t *fd;
1863 {
1864 int i;
1865
1866 fd->cachedSide = 0;
1867 for (i=0; i < IWM_MAX_GCR_SECTORS; i++) {
1868 fd->r_slots[i].valid = 0;
1869 }
1870 }
1871
1872
1873 /*
1874 * getFDType
1875 *
1876 * return pointer to disk format description
1877 */
1878 static fdInfo_t *
1879 getFDType(unit)
1880 short unit;
1881 {
1882 int driveFlags;
1883 fdInfo_t *thisType;
1884 extern fdInfo_t fdTypes[];
1885
1886 driveFlags = iwmCheckDrive(unit);
1887 /*
1888 * Drive flags are: Bit 0 - 1 = Drive is double sided
1889 * 1 - 1 = No disk inserted
1890 * 2 - 1 = Motor is off
1891 * 3 - 1 = Disk is writeable
1892 * 4 - 1 = Disk is DD (800/720K)
1893 * 31 - 1 = No drive / invalid drive #
1894 */
1895 if (TRACE_CONFIG) {
1896 printf("iwm: Drive %d says 0x0%x (%d)\n",
1897 unit, driveFlags, driveFlags);
1898 }
1899 if (driveFlags < 0)
1900 thisType = NULL;/* no such drive */
1901 else
1902 if (driveFlags & 0x01)
1903 thisType = &fdTypes[1]; /* double sided */
1904 else
1905 thisType = &fdTypes[0]; /* single sided */
1906
1907 return thisType;
1908 }
1909
1910
1911 /*
1912 * fdDeviceToType
1913 *
1914 * maps the minor device number (elsewhere: partition type) to
1915 * a corresponding disk format.
1916 * This is currently:
1917 * fdXa default (800K GCR)
1918 * fdXb 400K GCR
1919 * fdXc 800K GCR
1920 */
1921 static fdInfo_t *
1922 fdDeviceToType(fd, dev)
1923 fd_softc_t *fd;
1924 dev_t dev;
1925 {
1926 int type;
1927 fdInfo_t *thisInfo;
1928 /* XXX This broke with egcs 1.0.2 */
1929 /* extern fdInfo_t fdTypes[]; */
1930
1931 type = minor(dev) % MAXPARTITIONS; /* 1,2,... */
1932 if (type > sizeof(fdTypes) / sizeof(fdTypes[0]))
1933 thisInfo = NULL;
1934 else
1935 thisInfo = (type == 0) ? fd->defaultType : &fdTypes[type - 1];
1936 return thisInfo;
1937 }
1938
1939
1940 /*
1941 * seek
1942 *
1943 * Step to given track; optionally restore to track zero before
1944 * and/or verify correct track.
1945 * Note that any necessary retries are done here.
1946 * We keep the current position on disk in a 'struct diskPosition'.
1947 */
1948 static int
1949 seek(fd, style)
1950 fd_softc_t *fd;
1951 int style;
1952 {
1953 int state, done;
1954 int err, ierr;
1955 int steps;
1956
1957 diskPosition_t *loc;
1958 sectorHdr_t hdr;
1959 char action[32];
1960 #ifndef _LKM
1961 iwm_softc_t *iwm = iwm_cd.cd_devs[0];
1962 #endif
1963
1964 char *stateDesc[] = {
1965 "Init",
1966 "Seek",
1967 "Recalibrate",
1968 "Verify",
1969 "Exit"
1970 };
1971 enum {
1972 state_Init = 0,
1973 state_Seek,
1974 state_Recalibrate,
1975 state_Verify,
1976 state_Exit
1977 };
1978 /* XXX egcs */
1979 done = err = ierr = 0;
1980 fd->seekRetries = 0;
1981 fd->verifyRetries = 0;
1982
1983 loc = &fd->pos;
1984
1985 state = state_Init;
1986 do {
1987 if (TRACE_STEP)
1988 printf(" seek state %d [%s].\n",
1989 state, stateDesc[state]);
1990 switch (state) {
1991
1992 case state_Init:
1993 if (TRACE_STEP)
1994 printf("Current track is %d, new track %d.\n",
1995 loc->oldTrack, loc->track);
1996 memset(&hdr, 0, sizeof(hdr));
1997 err = ierr = 0;
1998 fd->seekRetries = 0;
1999 fd->verifyRetries = 0;
2000 state = (style == IWM_SEEK_RECAL)
2001 ? state_Recalibrate : state_Seek;
2002 done = 0;
2003 break;
2004
2005 case state_Recalibrate:
2006 ierr = iwmTrack00();
2007 if (ierr == 0) {
2008 loc->oldTrack = 0;
2009 state = state_Seek;
2010 } else {
2011 strncpy(action, "Recalibrate (track 0)",
2012 sizeof(action));
2013 state = state_Exit;
2014 }
2015 break;
2016
2017 case state_Seek:
2018 ierr = 0;
2019 steps = loc->track - loc->oldTrack;
2020
2021 if (steps != 0)
2022 ierr = iwmSeek(steps);
2023 if (ierr == 0) {
2024 /* No error or nothing to do */
2025 state = (style == IWM_SEEK_VERIFY)
2026 ? state_Verify : state_Exit;
2027 } else {
2028 if (fd->seekRetries++ < iwm->maxRetries)
2029 state = state_Recalibrate;
2030 else {
2031 strncpy(action, "Seek retries",
2032 sizeof(action));
2033 state = state_Exit;
2034 }
2035 }
2036 break;
2037
2038 case state_Verify:
2039 ierr = checkTrack(loc, TRACE_STEP);
2040 if (ierr == 0 && loc->track == hdr.track)
2041 state = state_Exit;
2042 else {
2043 if (fd->verifyRetries++ < iwm->maxRetries)
2044 state = state_Recalibrate;
2045 else {
2046 strncpy(action, "Verify retries",
2047 sizeof(action));
2048 state = state_Exit;
2049 }
2050 }
2051 break;
2052
2053 case state_Exit:
2054 if (ierr == 0) {
2055 loc->oldTrack = loc->track;
2056 err = 0;
2057 /* Give the head some time to settle down */
2058 delay(3000);
2059 } else {
2060 #ifdef DIAGNOSTIC
2061 printf(" seek() action \"%s\", err = %d.\n",
2062 action, ierr);
2063 #endif
2064 err = EIO;
2065 }
2066 done = 1;
2067 break;
2068 }
2069 } while (!done);
2070 return err;
2071 }
2072
2073
2074 /*
2075 * checkTrack
2076 *
2077 * After positioning, get a sector header for validation
2078 */
2079 static int
2080 checkTrack(loc, debugFlag)
2081 diskPosition_t *loc;
2082 int debugFlag;
2083 {
2084 int spl;
2085 int iwmErr;
2086 sectorHdr_t hdr;
2087
2088 spl = spl6();
2089 iwmSelectSide(loc->side);
2090 iwmErr = iwmReadSectHdr(&hdr);
2091 splx(spl);
2092 if (debugFlag) {
2093 printf("Seeked for %d, got at %d, Hdr read err %d.\n",
2094 loc->track, hdr.track, iwmErr);
2095 }
2096 return iwmErr;
2097 }
2098
2099
2100 /* Debugging stuff */
2101
2102 static void
2103 hexDump(buf, len)
2104 u_char *buf;
2105 int len;
2106 {
2107 int i, j;
2108 u_char ch;
2109
2110 printf("\nDump %d from %p:\n", len, buf);
2111 i = j = 0;
2112 if (NULL != buf) do {
2113 printf("%04x: ", i);
2114 for (j = 0; j < 8; j++)
2115 printf("%02x ", buf[i + j]);
2116 printf(" ");
2117 for (j = 8; j < 16; j++)
2118 printf("%02x ", buf[i + j]);
2119 printf(" ");
2120 for (j = 0; j < 16; j++) {
2121 ch = buf[i + j];
2122 if (ch > 31 && ch < 127)
2123 printf("%c", ch);
2124 else
2125 printf(".");
2126 }
2127 printf("\n");
2128 i += 16;
2129 } while (len > i);
2130 }
2131
2132
2133 static void
2134 fdPrintDiskLabel(lp)
2135 struct disklabel *lp;
2136 {
2137 int i;
2138
2139 printf("iwm: Disklabel entries of current floppy.\n");
2140 printf("\t d_type:\t%d (%s)\n", lp->d_type,
2141 dktypenames[lp->d_type]);
2142 printf("\t d_typename:\t%s\n", lp->d_typename);
2143 printf("\t d_packname:\t%s\n", lp->d_packname);
2144
2145 printf("\t d_secsize:\t%d\n", lp->d_secsize);
2146 printf("\t d_nsectors:\t%d\n", lp->d_nsectors);
2147 printf("\t d_ntracks:\t%d\n", lp->d_ntracks);
2148 printf("\t d_ncylinders:\t%d\n", lp->d_ncylinders);
2149 printf("\t d_secpercyl:\t%d\n", lp->d_secpercyl);
2150 printf("\t d_secperunit:\t%d\n", lp->d_secperunit);
2151
2152 printf("\t d_rpm: \t%d\n", lp->d_rpm);
2153 printf("\t d_interleave:\t%d\n", lp->d_interleave);
2154 printf("\t d_trkseek:\t%d [ms]\n", lp->d_trkseek);
2155
2156 printf(" d_npartitions:\t%d\n", lp->d_npartitions);
2157 for (i = 0; i < lp->d_npartitions; i++) {
2158 printf("\t d_partitions[%d].p_offset:\t%d\n", i,
2159 lp->d_partitions[i].p_offset);
2160 printf("\t d_partitions[%d].p_size:\t%d\n", i,
2161 lp->d_partitions[i].p_size);
2162 printf("\t d_partitions[%d].p_fstype:\t%d (%s)\n", i,
2163 lp->d_partitions[i].p_fstype,
2164 fstypenames[lp->d_partitions[i].p_fstype]);
2165 printf("\t d_partitions[%d].p_frag:\t%d\n", i,
2166 lp->d_partitions[i].p_frag);
2167 printf("\n");
2168 }
2169 }
2170