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