wd.c revision 1.29 1 /*
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * from: @(#)wd.c 7.2 (Berkeley) 5/9/91
37 * $Id: wd.c,v 1.29 1993/12/20 09:06:50 mycroft Exp $
38 */
39
40 /* Note: This code heavily modified by tih (at) barsoom.nhh.no; use at own risk! */
41 /* The following defines represent only a very small part of the mods, most */
42 /* of them are not marked in any way. -tih */
43
44 #define TIHMODS /* wdopen() workaround, some splx() calls */
45 #define QUIETWORKS /* define this when wdopen() can actually set DKFL_QUIET */
46 #define INSTRUMENT /* Add instrumentation stuff by Brad Parker */
47 #define TIPCAT /* theo says: whatever it is, it looks important! */
48
49 /* TODO: peel out buffer at low ipl, speed improvement */
50 /* TODO: find and fix the timing bugs apparent on some controllers */
51
52 #include "wd.h"
53 #if NWDC > 0
54
55 #include <sys/param.h>
56 #include <sys/dkbad.h>
57 #include <sys/systm.h>
58 #include <sys/conf.h>
59 #include <sys/file.h>
60 #include <sys/stat.h>
61 #include <sys/ioctl.h>
62 #include <sys/disklabel.h>
63 #include <sys/buf.h>
64 #include <sys/uio.h>
65 #include <sys/malloc.h>
66 #include <sys/syslog.h>
67 #ifdef INSTRUMENT
68 #include <sys/dkstat.h>
69 #endif
70
71 #include <vm/vm.h>
72
73 #include <machine/cpu.h>
74 #include <machine/pio.h>
75
76 #include <i386/isa/isa.h>
77 #include <i386/isa/isa_device.h>
78 #include <i386/isa/icu.h>
79 #include <i386/isa/wdreg.h>
80
81 #ifndef WDCNDELAY
82 #define WDCNDELAY 400000 /* delay = 25us; so 10s for a controller state change */
83 #endif
84 #define WDCDELAY 25
85
86 /* if you enable this, it will report any delays more than 25us * N long */
87 /*#define WDCNDELAY_DEBUG 6 */
88
89 #define WDIORETRIES 5 /* number of retries before giving up */
90
91 #define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
92 #define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */
93 #define wdunit(dev) ((minor(dev) & 0x38) >> 3)
94 #define wdpart(dev) (minor(dev) & 0x7)
95 #define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part)))
96 #define WDRAW 3 /* 'd' partition isn't a partition! */
97
98 #define b_cylin b_resid /* cylinder number for doing IO to */
99 /* shares an entry in the buf struct */
100
101 /*
102 * Drive states. Used to initialize drive.
103 */
104
105 #define CLOSED 0 /* disk is closed. */
106 #define WANTOPEN 1 /* open requested, not started */
107 #define RECAL 2 /* doing restore */
108 #define OPEN 3 /* done with open */
109
110 /*
111 * The structure of a disk drive.
112 */
113 struct disk {
114 long dk_bc; /* byte count left */
115 long dk_bct; /* total byte count left */
116 short dk_skip; /* blocks already transferred */
117 short dk_skipm; /* blocks already transferred for multi */
118 char dk_ctrlr; /* physical controller number */
119 char dk_unit; /* physical unit number */
120 char dk_lunit; /* logical unit number */
121 char dk_state; /* control state */
122 u_char dk_status; /* copy of status reg. */
123 u_char dk_error; /* copy of error reg. */
124 short dk_port; /* i/o port base */
125
126 u_long dk_copenpart; /* character units open on this drive */
127 u_long dk_bopenpart; /* block units open on this drive */
128 u_long dk_openpart; /* all units open on this drive */
129 short dk_wlabel; /* label writable? */
130 short dk_flags; /* drive characteistics found */
131 #define DKFL_DOSPART 0x00001 /* has DOS partition table */
132 #define DKFL_QUIET 0x00002 /* report errors back, but don't complain */
133 #define DKFL_SINGLE 0x00004 /* sector at a time mode */
134 #define DKFL_ERROR 0x00008 /* processing a disk error */
135 #define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
136 #define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
137 #define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
138 struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
139 struct disklabel dk_dd; /* device configuration data */
140 struct cpu_disklabel dk_cpd;
141 long dk_badsect[127]; /* 126 plus trailing -1 marker */
142 };
143
144 struct board {
145 short dkc_port;
146 };
147
148 struct board wdcontroller[NWDC];
149 struct disk *wddrives[NWD]; /* table of units */
150 struct buf wdtab[NWDC]; /* various per-controller info */
151 struct buf wdutab[NWD]; /* head of queue per drive */
152 struct buf rwdbuf[NWD]; /* buffers for raw IO */
153 long wdxfer[NWD]; /* count of transfers */
154 int wdtimeoutstatus[NWD]; /* timeout counters */
155
156 int wdprobe(), wdattach();
157
158 struct isa_driver wdcdriver = {
159 wdprobe, wdattach, "wdc",
160 };
161
162 static void wdustart(struct disk *);
163 static void wdstart(int);
164 static int wdcommand(struct disk *, int);
165 static int wdcontrol(struct buf *);
166 static int wdsetctlr(dev_t, struct disk *);
167 static int wdgetctlr(int, struct disk *);
168 static void bad144intern(struct disk *);
169 static void wddisksort();
170 static int wdreset(int, int, int);
171 static int wdtimeout(caddr_t);
172
173 /*
174 * Probe for controller.
175 */
176 int
177 wdprobe(struct isa_device *dvp)
178 {
179 struct disk *du;
180 int wdc;
181
182 if (dvp->id_unit >= NWDC)
183 return(0);
184
185 du = (struct disk *) malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
186 bzero(du, sizeof(struct disk));
187
188 du->dk_ctrlr = dvp->id_unit;
189 du->dk_unit = 0;
190 du->dk_lunit = 0;
191 wdcontroller[dvp->id_unit].dkc_port = dvp->id_iobase;
192
193 wdc = du->dk_port = dvp->id_iobase;
194
195 /* check if we have registers that work */
196 outb(wdc+wd_error, 0x5a); /* error register not writable */
197 outb(wdc+wd_cyl_lo, 0xa5); /* but all of cyllo are implemented */
198 if(inb(wdc+wd_error) == 0x5a || inb(wdc+wd_cyl_lo) != 0xa5)
199 goto nodevice;
200
201 wdreset(dvp->id_unit, wdc, 0);
202
203 /* execute a controller only command */
204 if (wdcommand(du, WDCC_DIAGNOSE) < 0)
205 goto nodevice;
206
207 bzero(&wdtab[du->dk_ctrlr], sizeof(struct buf));
208
209 free(du, M_TEMP);
210 return (8);
211
212 nodevice:
213 free(du, M_TEMP);
214 return (0);
215 }
216
217 /*
218 * Called for the controller too
219 * Attach each drive if possible.
220 */
221 int
222 wdattach(struct isa_device *dvp)
223 {
224 int unit, lunit;
225 struct disk *du;
226
227 if (dvp->id_masunit == -1)
228 return(0);
229 if (dvp->id_masunit >= NWDC)
230 return(0);
231
232 lunit = dvp->id_unit;
233 if (lunit == -1) {
234 printf("wdc%d: cannot support unit ?\n", dvp->id_masunit);
235 return 0;
236 }
237 if (lunit >= NWD)
238 return(0);
239 unit = dvp->id_physid;
240
241 du = wddrives[lunit] = (struct disk *)
242 malloc(sizeof(struct disk), M_TEMP, M_NOWAIT);
243 bzero(du, sizeof(struct disk));
244 bzero(&wdutab[lunit], sizeof(struct buf));
245 bzero(&rwdbuf[lunit], sizeof(struct buf));
246 wdxfer[lunit] = 0;
247 wdtimeoutstatus[lunit] = 0;
248 wdtimeout(lunit);
249 du->dk_ctrlr = dvp->id_masunit;
250 du->dk_unit = unit;
251 du->dk_lunit = lunit;
252 du->dk_port = wdcontroller[dvp->id_masunit].dkc_port;
253
254 if(wdgetctlr(unit, du) == 0) {
255 int i, blank;
256
257 printf("wd%d at wdc%d targ %d: ",
258 dvp->id_unit, dvp->id_masunit, dvp->id_physid);
259 if(du->dk_params.wdp_heads==0)
260 printf("(unknown size) <");
261 else
262 printf("%dMB %d cyl, %d head, %d sec <",
263 du->dk_dd.d_ncylinders * du->dk_dd.d_secpercyl / 2048,
264 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
265 du->dk_dd.d_nsectors);
266 for (i=blank=0; i<sizeof(du->dk_params.wdp_model); i++) {
267 char c = du->dk_params.wdp_model[i];
268 if (!c)
269 break;
270 if (blank && c == ' ')
271 continue;
272 if (blank && c != ' ') {
273 printf(" %c", c);
274 blank = 0;
275 continue;
276 }
277 if (c == ' ')
278 blank = 1;
279 else
280 printf("%c", c);
281 }
282 printf(">\n");
283 } else {
284 /*printf("wd%d at wdc%d slave %d -- error\n",
285 lunit, dvp->id_masunit, unit);*/
286 wddrives[lunit] = 0;
287 free(du, M_TEMP);
288 return 0;
289 }
290 return 1;
291 }
292
293 /* Read/write routine for a buffer. Finds the proper unit, range checks
294 * arguments, and schedules the transfer. Does not wait for the transfer
295 * to complete. Multi-page transfers are supported. All I/O requests must
296 * be a multiple of a sector in length.
297 */
298 int
299 wdstrategy(register struct buf *bp)
300 {
301 register struct buf *dp;
302 struct disk *du; /* Disk unit to do the IO. */
303 int lunit = wdunit(bp->b_dev);
304 int s;
305
306 /* valid unit, controller, and request? */
307 if (lunit >= NWD || bp->b_blkno < 0 ||
308 howmany(bp->b_bcount, DEV_BSIZE) >= (1<<NBBY) ||
309 (du = wddrives[lunit]) == 0) {
310 bp->b_error = EINVAL;
311 bp->b_flags |= B_ERROR;
312 goto done;
313 }
314
315 /* "soft" write protect check */
316 if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
317 bp->b_error = EROFS;
318 bp->b_flags |= B_ERROR;
319 goto done;
320 }
321
322 /* have partitions and want to use them? */
323 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) {
324 /*
325 * do bounds checking, adjust transfer. if error, process.
326 * if end of partition, just return
327 */
328 if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0)
329 goto done;
330 /* otherwise, process transfer request */
331 }
332
333 /* queue transfer on drive, activate drive and controller if idle */
334 dp = &wdutab[lunit];
335 s = splbio();
336 wddisksort(dp, bp);
337 if (dp->b_active == 0)
338 wdustart(du); /* start drive */
339 if (wdtab[du->dk_ctrlr].b_active == 0)
340 wdstart(du->dk_ctrlr); /* start controller */
341 splx(s);
342 return 0;
343
344 done:
345 /* toss transfer, we're done early */
346 biodone(bp);
347 return 0;
348 }
349
350 /*
351 * Routine to queue a command to the controller. The unit's
352 * request is linked into the active list for the controller.
353 * If the controller is idle, the transfer is started.
354 */
355 static void
356 wdustart(register struct disk *du)
357 {
358 register struct buf *bp, *dp = &wdutab[du->dk_lunit];
359 int ctrlr = du->dk_ctrlr;
360
361 /* unit already active? */
362 if (dp->b_active)
363 return;
364
365 /* anything to start? */
366 bp = dp->b_actf;
367 if (bp == NULL)
368 return;
369
370 /* link onto controller queue */
371 dp->b_forw = NULL;
372 if (wdtab[ctrlr].b_actf == NULL)
373 wdtab[ctrlr].b_actf = dp;
374 else
375 wdtab[ctrlr].b_actl->b_forw = dp;
376 wdtab[ctrlr].b_actl = dp;
377
378 /* mark the drive unit as busy */
379 dp->b_active = 1;
380 }
381
382 /*
383 * Controller startup routine. This does the calculation, and starts
384 * a single-sector read or write operation. Called to start a transfer,
385 * or from the interrupt routine to continue a multi-sector transfer.
386 * RESTRICTIONS:
387 * 1. The transfer length must be an exact multiple of the sector size.
388 */
389 static void
390 wdstart(int ctrlr)
391 {
392 register struct disk *du; /* disk unit for IO */
393 register struct buf *bp;
394 struct disklabel *lp;
395 struct buf *dp;
396 long blknum, cylin, head, sector;
397 long secpertrk, secpercyl, addr, timeout;
398 int lunit, wdc;
399 int xfrblknum;
400 unsigned char status;
401
402 loop:
403 /* is there a drive for the controller to do a transfer with? */
404 dp = wdtab[ctrlr].b_actf;
405 if (dp == NULL)
406 return;
407
408 /* is there a transfer to this drive ? if so, link it on
409 the controller's queue */
410 bp = dp->b_actf;
411 if (bp == NULL) {
412 wdtab[ctrlr].b_actf = dp->b_forw;
413 goto loop;
414 }
415
416 /* obtain controller and drive information */
417 lunit = wdunit(bp->b_dev);
418 du = wddrives[lunit];
419
420 /* if not really a transfer, do control operations specially */
421 if (du->dk_state < OPEN) {
422 (void) wdcontrol(bp);
423 return;
424 }
425
426 /* calculate transfer details */
427 blknum = bp->b_blkno + du->dk_skip;
428 #ifdef WDDEBUG
429 if (du->dk_skip == 0)
430 printf("\nwdstart %d: %s %d@%d; map ", lunit,
431 (bp->b_flags & B_READ) ? "read" : "write",
432 bp->b_bcount, blknum);
433 else
434 printf(" %d)%x", du->dk_skip, inb(du->dk_port+wd_altsts));
435 #endif
436 addr = (int) bp->b_un.b_addr;
437 if (du->dk_skip == 0) {
438 du->dk_bc = bp->b_bcount;
439 }
440 if (du->dk_skipm == 0) {
441 struct buf *oldbp, *nextbp;
442 oldbp = bp;
443 nextbp = bp->av_forw;
444 du->dk_bct = du->dk_bc;
445 oldbp->b_flags |= B_XXX;
446 while( nextbp
447 && (oldbp->b_flags & DKFL_SINGLE) == 0
448 && oldbp->b_dev == nextbp->b_dev
449 && nextbp->b_blkno == (oldbp->b_blkno + (oldbp->b_bcount/DEV_BSIZE))
450 && (oldbp->b_flags & B_READ) == (nextbp->b_flags & B_READ)) {
451 if( (du->dk_bct+nextbp->b_bcount)/DEV_BSIZE >= 240) {
452 break;
453 }
454 du->dk_bct += nextbp->b_bcount;
455 oldbp->b_flags |= B_XXX;
456 oldbp = nextbp;
457 nextbp = nextbp->av_forw;
458 }
459 }
460
461 lp = &du->dk_dd;
462 secpertrk = lp->d_nsectors;
463 secpercyl = lp->d_secpercyl;
464 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
465 blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
466 cylin = blknum / secpercyl;
467 head = (blknum % secpercyl) / secpertrk;
468 sector = blknum % secpertrk;
469
470 /* Check for bad sectors if we have them, and not formatting */
471 /* Only do this in single-sector mode, or when starting a */
472 /* multiple-sector transfer. */
473 #ifdef B_FORMAT
474 if ((du->dk_flags & DKFL_BADSECT) && !(bp->b_flags & B_FORMAT) &&
475 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) {
476 #else
477 if ((du->dk_flags & DKFL_BADSECT) &&
478 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) {
479 #endif
480
481 long blkchk, blkend, blknew;
482 int i;
483
484 blkend = blknum + howmany(du->dk_bct, DEV_BSIZE) - 1;
485 for (i = 0; (blkchk = du->dk_badsect[i]) != -1; i++) {
486 if (blkchk > blkend) {
487 break; /* transfer is completely OK; done */
488 } else if (blkchk == blknum) {
489 blknew = lp->d_secperunit - lp->d_nsectors - i - 1;
490 cylin = blknew / secpercyl;
491 head = (blknew % secpercyl) / secpertrk;
492 sector = blknew % secpertrk;
493 du->dk_flags |= DKFL_SINGLE;
494 /* found and replaced first blk of transfer; done */
495 break;
496 } else if (blkchk > blknum) {
497 du->dk_flags |= DKFL_SINGLE;
498 break; /* bad block inside transfer; done */
499 }
500 }
501 }
502 if( du->dk_flags & DKFL_SINGLE) {
503 du->dk_bct = du->dk_bc;
504 du->dk_skipm = du->dk_skip;
505 }
506
507 #ifdef WDDEBUG
508 pg("c%d h%d s%d ", cylin, head, sector);
509 #endif
510
511 sector += 1; /* sectors begin with 1, not 0 */
512
513 wdtab[ctrlr].b_active = 1; /* mark controller active */
514 wdc = du->dk_port;
515
516 #ifdef INSTRUMENT
517 /* instrumentation */
518 if (du->dk_unit >= 0 && du->dk_skip == 0) {
519 dk_busy |= 1 << du->dk_lunit;
520 dk_wds[du->dk_lunit] += bp->b_bcount >> 6;
521 }
522 if (du->dk_unit >= 0 && du->dk_skipm == 0) {
523 ++dk_seek[du->dk_lunit];
524 ++dk_xfer[du->dk_lunit];
525 }
526 #endif
527
528 retry:
529 /* if starting a multisector transfer, or doing single transfers */
530 if (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE)) {
531 if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) {
532 du->dk_bc += DEV_BSIZE;
533 du->dk_bct += DEV_BSIZE;
534 }
535
536 /* controller idle? */
537 for (timeout=0; inb(wdc+wd_status) & WDCS_BUSY; ) {
538 DELAY(WDCDELAY);
539 if (++timeout < WDCNDELAY)
540 continue;
541 wdreset(ctrlr, wdc, 1);
542 break;
543 }
544 #ifdef WDCNDELAY_DEBUG
545 if(timeout>WDCNDELAY_DEBUG)
546 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout);
547 #endif
548
549 /* stuff the task file */
550 outb(wdc+wd_precomp, lp->d_precompcyl / 4);
551 #ifdef B_FORMAT
552 if (bp->b_flags & B_FORMAT) {
553 outb(wdc+wd_sector, lp->d_gap3);
554 outb(wdc+wd_seccnt, lp->d_nsectors);
555 } else {
556 if (du->dk_flags & DKFL_SINGLE)
557 outb(wdc+wd_seccnt, 1);
558 else
559 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
560 outb(wdc+wd_sector, sector);
561 }
562 #else
563 if (du->dk_flags & DKFL_SINGLE)
564 outb(wdc+wd_seccnt, 1);
565 else
566 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
567 outb(wdc+wd_sector, sector);
568 #endif
569 outb(wdc+wd_cyl_lo, cylin);
570 outb(wdc+wd_cyl_hi, cylin >> 8);
571
572 /* set up the SDH register (select drive) */
573 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
574
575 /* wait for drive to become ready */
576 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) {
577 DELAY(WDCDELAY);
578 if (++timeout < WDCNDELAY)
579 continue;
580 wdreset(ctrlr, wdc, 1);
581 goto retry;
582 }
583 #ifdef WDCNDELAY_DEBUG
584 if(timeout>WDCNDELAY_DEBUG)
585 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout);
586 #endif
587
588 /* initiate command! */
589 #ifdef B_FORMAT
590 if (bp->b_flags & B_FORMAT)
591 outb(wdc+wd_command, WDCC_FORMAT);
592 else
593 outb(wdc+wd_command,
594 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
595 #else
596 outb(wdc+wd_command, (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
597 #endif
598 #ifdef WDDEBUG
599 printf("sector %d cylin %d head %d addr %x sts %x\n",
600 sector, cylin, head, addr, inb(wdc+wd_altsts));
601 #endif
602 }
603
604 /* if this is a read operation, just go away until it's done. */
605 if (bp->b_flags & B_READ) {
606 wdtimeoutstatus[lunit] = 2;
607 return;
608 }
609
610 /* ready to send data? */
611 for (timeout=0; (inb(wdc+wd_altsts) & WDCS_DRQ) == 0; ) {
612 DELAY(WDCDELAY);
613 if (++timeout < WDCNDELAY)
614 continue;
615 wdreset(ctrlr, wdc, 1);
616 goto retry;
617 }
618 #ifdef WDCNDELAY_DEBUG
619 if(timeout>WDCNDELAY_DEBUG)
620 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout);
621 #endif
622
623 /* then send it! */
624 outagain:
625 outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
626 DEV_BSIZE/sizeof(short));
627 du->dk_bc -= DEV_BSIZE;
628 du->dk_bct -= DEV_BSIZE;
629 wdtimeoutstatus[lunit] = 2;
630 }
631
632 /* Interrupt routine for the controller. Acknowledge the interrupt, check for
633 * errors on the current operation, mark it done if necessary, and start
634 * the next request. Also check for a partially done transfer, and
635 * continue with the next chunk if so.
636 */
637 void
638 wdintr(struct intrframe wdif)
639 {
640 register struct disk *du;
641 register struct buf *bp, *dp;
642 int status, wdc, ctrlr, timeout;
643
644 ctrlr = wdif.if_vec;
645
646 if (!wdtab[ctrlr].b_active) {
647 printf("wdc%d: extra interrupt\n", ctrlr);
648 return;
649 }
650
651 dp = wdtab[ctrlr].b_actf;
652 bp = dp->b_actf;
653 du = wddrives[wdunit(bp->b_dev)];
654 wdc = du->dk_port;
655 wdtimeoutstatus[wdunit(bp->b_dev)] = 0;
656
657 #ifdef WDDEBUG
658 printf("I%d ", ctrlr);
659 #endif
660
661 for (timeout=0; ((status=inb(wdc+wd_status)) & WDCS_BUSY); ) {
662 DELAY(WDCDELAY);
663 if (++timeout < WDCNDELAY/20)
664 continue;
665 wdstart(ctrlr);
666 /* #ifdef WDDEBUG */
667 printf("wdc%d: timeout in wdintr WDCS_BUSY\n", ctrlr);
668 /* #endif */
669 }
670
671 /* is it not a transfer, but a control operation? */
672 if (du->dk_state < OPEN) {
673 if (wdcontrol(bp))
674 wdstart(ctrlr);
675 return;
676 }
677
678 /* have we an error? */
679 if (status & (WDCS_ERR | WDCS_ECCCOR)) {
680 du->dk_status = status;
681 du->dk_error = inb(wdc + wd_error);
682 #ifdef WDDEBUG
683 printf("status %x error %x\n", status, du->dk_error);
684 #endif
685 if((du->dk_flags & DKFL_SINGLE) == 0) {
686 du->dk_flags |= DKFL_ERROR;
687 goto outt;
688 }
689 #ifdef B_FORMAT
690 if (bp->b_flags & B_FORMAT) {
691 bp->b_flags |= B_ERROR;
692 goto done;
693 }
694 #endif
695
696 /* error or error correction? */
697 if (status & WDCS_ERR) {
698 if (++wdtab[ctrlr].b_errcnt < WDIORETRIES) {
699 wdtab[ctrlr].b_active = 0;
700 } else {
701 if((du->dk_flags & DKFL_QUIET) == 0) {
702 diskerr(bp, "wd", "hard error",
703 LOG_PRINTF, du->dk_skip,
704 &du->dk_dd);
705 #ifdef WDDEBUG
706 printf( "status %b error %b\n",
707 status, WDCS_BITS,
708 inb(wdc+wd_error), WDERR_BITS);
709 #endif
710 }
711 bp->b_flags |= B_ERROR; /* flag the error */
712 }
713 } else if((du->dk_flags & DKFL_QUIET) == 0) {
714 diskerr(bp, "wd", "soft ecc", 0,
715 du->dk_skip, &du->dk_dd);
716 }
717 }
718 outt:
719
720 /*
721 * If this was a successful read operation, fetch the data.
722 */
723 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab[ctrlr].b_active) {
724 int chk, dummy;
725
726 chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
727
728 /* ready to receive data? */
729 for (timeout=0; (inb(wdc+wd_status) & WDCS_DRQ) == 0; ) {
730 DELAY(WDCDELAY);
731 if (++timeout < WDCNDELAY/20)
732 continue;
733 wdstart(ctrlr);
734 /* #ifdef WDDEBUG */
735 printf("wdc%d: timeout in wdintr WDCS_DRQ\n", ctrlr);
736 /* #endif */
737 break;
738 }
739
740 /* suck in data */
741 insw (wdc+wd_data,
742 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
743 du->dk_bc -= chk * sizeof(short);
744 du->dk_bct -= chk * sizeof(short);
745
746 /* for obselete fractional sector reads */
747 while (chk++ < (DEV_BSIZE / sizeof(short)))
748 insw(wdc+wd_data, &dummy, 1);
749 }
750
751 wdxfer[du->dk_lunit]++;
752 if (wdtab[ctrlr].b_active) {
753 #ifdef INSTRUMENT
754 if (du->dk_unit >= 0)
755 dk_busy &=~ (1 << du->dk_unit);
756 #endif
757 if ((bp->b_flags & B_ERROR) == 0) {
758 du->dk_skip++; /* Add to succ. sect */
759 du->dk_skipm++; /* Add to succ. sect for multitransfer */
760 if (wdtab[ctrlr].b_errcnt && (du->dk_flags & DKFL_QUIET) == 0)
761 diskerr(bp, "wd", "soft error", 0,
762 du->dk_skip, &du->dk_dd);
763 wdtab[ctrlr].b_errcnt = 0;
764
765 /* see if more to transfer */
766 if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
767 if( (du->dk_flags & DKFL_SINGLE)
768 || (du->dk_flags & B_READ) == 0) {
769 wdstart(ctrlr);
770 return; /* next chunk is started */
771 }
772 } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR)) == DKFL_ERROR) {
773 du->dk_skip = 0;
774 du->dk_skipm = 0;
775 du->dk_flags &= ~DKFL_ERROR;
776 du->dk_flags |= DKFL_SINGLE;
777 wdstart(ctrlr);
778 return; /* redo xfer sector by sector */
779 }
780 }
781
782 done:
783 /* done with this transfer, with or without error */
784 du->dk_flags &= ~DKFL_SINGLE;
785 wdtab[ctrlr].b_errcnt = 0;
786 du->dk_skip = 0;
787 if( du->dk_bct == 0) {
788 wdtab[ctrlr].b_actf = dp->b_forw;
789 du->dk_skipm = 0;
790 dp->b_active = 0;
791 }
792 dp->b_actf = bp->av_forw;
793 dp->b_errcnt = 0;
794 bp->b_resid = 0;
795 bp->b_flags &= ~B_XXX;
796 biodone(bp);
797 }
798
799 /* anything more on drive queue? */
800 if (dp->b_actf && du->dk_bct == 0)
801 wdustart(du);
802
803 /* anything more for controller to do? */
804 if (wdtab[ctrlr].b_actf)
805 wdstart(ctrlr);
806
807 if (!wdtab[ctrlr].b_actf)
808 wdtab[ctrlr].b_active = 0;
809 }
810
811 /*
812 * Initialize a drive.
813 */
814 int
815 wdopen(dev_t dev, int flags, int fmt, struct proc *p)
816 {
817 register unsigned int lunit;
818 register struct disk *du;
819 int part = wdpart(dev), mask = 1 << part;
820 struct partition *pp;
821 int error = 0;
822 char *msg;
823
824 lunit = wdunit(dev);
825 if (lunit >= NWD)
826 return (ENXIO);
827
828 du = wddrives[lunit];
829
830 if (du == 0)
831 return (ENXIO);
832
833 #ifdef QUIETWORKS
834 if (part == WDRAW)
835 du->dk_flags |= DKFL_QUIET;
836 else
837 du->dk_flags &= ~DKFL_QUIET;
838 #else
839 du->dk_flags &= ~DKFL_QUIET;
840 #endif
841
842 if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
843 du->dk_flags |= DKFL_WRITEPROT;
844 wdutab[lunit].b_actf = NULL;
845
846 /*
847 * Use the default sizes until we've read the label,
848 * or longer if there isn't one there.
849 */
850 bzero(&du->dk_dd, sizeof(du->dk_dd));
851 #undef d_type /* fix goddamn segments.h! XXX */
852 du->dk_dd.d_type = DTYPE_ST506;
853 du->dk_dd.d_ncylinders = 1024;
854 du->dk_dd.d_secsize = DEV_BSIZE;
855 du->dk_dd.d_ntracks = 8;
856 du->dk_dd.d_nsectors = 17;
857 du->dk_dd.d_secpercyl = 17*8;
858 du->dk_dd.d_secperunit = 17*8*1024;
859 du->dk_state = WANTOPEN;
860
861 /* read label using "raw" partition */
862 #if defined(TIHMODS) && defined(garbage)
863 /* wdsetctlr(dev, du); */ /* Maybe do this TIH */
864 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
865 wdstrategy, &du->dk_dd, &du->dk_cpd);
866 wdsetctlr(dev, du);
867 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
868 wdstrategy, &du->dk_dd, &du->dk_cpd);
869 if (msg) {
870 #ifdef QUIETWORKS
871 if((du->dk_flags & DKFL_QUIET) == 0) {
872 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
873 lunit, msg);
874 error = EINVAL; /* XXX needs translation */
875 }
876 #else
877 log(LOG_WARNING, "wd%d: cannot find label (%s)\n", lunit, msg);
878 if(part != WDRAW) {
879 error = EINVAL; /* XXX needs translation */
880 }
881 #endif
882 goto done;
883 } else {
884 wdsetctlr(dev, du);
885 du->dk_flags |= DKFL_BSDLABEL;
886 du->dk_flags &= ~DKFL_WRITEPROT;
887 if (du->dk_dd.d_flags & D_BADSECT)
888 du->dk_flags |= DKFL_BADSECT;
889 }
890 #else
891 if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
892 wdstrategy, &du->dk_dd, &du->dk_cpd) ) {
893 if((du->dk_flags & DKFL_QUIET) == 0) {
894 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
895 lunit, msg);
896 error = EINVAL; /* XXX needs translation */
897 }
898 goto done;
899 } else {
900 wdsetctlr(dev, du);
901 du->dk_flags |= DKFL_BSDLABEL;
902 du->dk_flags &= ~DKFL_WRITEPROT;
903 if (du->dk_dd.d_flags & D_BADSECT)
904 du->dk_flags |= DKFL_BADSECT;
905 }
906 #endif
907
908 done:
909 if (error) {
910 return(error);
911 }
912 }
913
914 if (du->dk_flags & DKFL_BADSECT)
915 bad144intern(du);
916
917 /*
918 * Warn if a partion is opened
919 * that overlaps another partition which is open
920 * unless one is the "raw" partition (whole disk).
921 */
922 if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) {
923 int start, end;
924
925 pp = &du->dk_dd.d_partitions[part];
926 start = pp->p_offset;
927 end = pp->p_offset + pp->p_size;
928 for (pp = du->dk_dd.d_partitions;
929 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; pp++) {
930 if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end)
931 continue;
932 /*if (pp - du->dk_dd.d_partitions == RAWPART)
933 continue; */
934 if (pp - du->dk_dd.d_partitions == WDRAW)
935 continue;
936 if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions)))
937 log(LOG_WARNING,
938 "wd%d%c: overlaps open partition (%c)\n",
939 lunit, part + 'a',
940 pp - du->dk_dd.d_partitions + 'a');
941 }
942 }
943
944 if (part >= du->dk_dd.d_npartitions && part != WDRAW) {
945 return (ENXIO);
946 }
947
948 /* insure only one open at a time */
949 du->dk_openpart |= mask;
950 switch (fmt) {
951 case S_IFCHR:
952 du->dk_copenpart |= mask;
953 break;
954 case S_IFBLK:
955 du->dk_bopenpart |= mask;
956 break;
957 }
958 return (0);
959 }
960
961 /*
962 * Implement operations other than read/write.
963 * Called from wdstart or wdintr during opens and formats.
964 * Uses finite-state-machine to track progress of operation in progress.
965 * Returns 0 if operation still in progress, 1 if completed.
966 */
967 static int
968 wdcontrol(register struct buf *bp)
969 {
970 register struct disk *du;
971 register unit, lunit;
972 unsigned char stat;
973 int s, ctrlr, timeout;
974 int wdc;
975
976 du = wddrives[wdunit(bp->b_dev)];
977 ctrlr = du->dk_ctrlr;
978 unit = du->dk_unit;
979 lunit = du->dk_lunit;
980 wdc = du->dk_port;
981
982 switch (du->dk_state) {
983 tryagainrecal:
984 case WANTOPEN: /* set SDH, step rate, do restore */
985 #ifdef WDDEBUG
986 printf("wd%d: recal ", lunit);
987 #endif
988 s = splbio(); /* not called from intr level ... */
989 wdgetctlr(unit, du);
990 #ifdef TIPCAT
991
992 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) {
993 DELAY(WDCDELAY);
994 if (++timeout < WDCNDELAY)
995 continue;
996 wdreset(ctrlr, wdc, 1);
997 goto tryagainrecal;
998 }
999 #ifdef WDCNDELAY_DEBUG
1000 if(timeout>WDCNDELAY_DEBUG)
1001 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout);
1002 #endif
1003 #endif
1004 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
1005 wdtab[ctrlr].b_active = 1;
1006 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
1007 #ifdef TIPCAT
1008 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) {
1009 DELAY(WDCDELAY);
1010 if (++timeout < WDCNDELAY)
1011 continue;
1012 wdreset(ctrlr, wdc, 1);
1013 goto tryagainrecal;
1014 }
1015 #ifdef WDCNDELAY_DEBUG
1016 if(timeout>WDCNDELAY_DEBUG)
1017 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout);
1018 #endif
1019 #endif
1020 du->dk_state = RECAL;
1021 splx(s);
1022 return(0);
1023 case RECAL:
1024 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
1025 if ((du->dk_flags & DKFL_QUIET) == 0) {
1026 printf("wd%d: recal", du->dk_lunit);
1027 printf(": status %b error %b\n",
1028 stat, WDCS_BITS, inb(wdc+wd_error),
1029 WDERR_BITS);
1030 }
1031 if (++wdtab[ctrlr].b_errcnt < WDIORETRIES)
1032 goto tryagainrecal;
1033 bp->b_error = ENXIO; /* XXX needs translation */
1034 goto badopen;
1035 }
1036
1037 /* some controllers require this ... */
1038 wdsetctlr(bp->b_dev, du);
1039
1040 wdtab[ctrlr].b_errcnt = 0;
1041 du->dk_state = OPEN;
1042 /*
1043 * The rest of the initialization can be done
1044 * by normal means.
1045 */
1046 return(1);
1047 default:
1048 panic("wdcontrol");
1049 }
1050 /* NOTREACHED */
1051
1052 badopen:
1053 if ((du->dk_flags & DKFL_QUIET) == 0)
1054 printf(": status %b error %b\n",
1055 stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS);
1056 bp->b_flags |= B_ERROR;
1057 return(1);
1058 }
1059
1060 /*
1061 * send a command and wait uninterruptibly until controller is finished.
1062 * return -1 if controller busy for too long, otherwise
1063 * return status. intended for brief controller commands at critical points.
1064 * assumes interrupts are blocked.
1065 */
1066 static int
1067 wdcommand(struct disk *du, int cmd)
1068 {
1069 int timeout, stat, wdc;
1070
1071 /*DELAY(2000);*/
1072 wdc = du->dk_port;
1073
1074 /* controller ready for command? */
1075 for (timeout=0; (stat=inb(wdc+wd_status)) & WDCS_BUSY; ) {
1076 DELAY(WDCDELAY);
1077 if(++timeout > WDCNDELAY)
1078 return -1;
1079 }
1080 #ifdef WDCNDELAY_DEBUG
1081 if(timeout>WDCNDELAY_DEBUG)
1082 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout);
1083 #endif
1084
1085 /* send command, await results */
1086 outb(wdc+wd_command, cmd);
1087 for (timeout=0; (stat=inb(wdc+wd_status)) & WDCS_BUSY; ) {
1088 DELAY(WDCDELAY);
1089 if(++timeout > WDCNDELAY)
1090 return -1;
1091 }
1092 #ifdef WDCNDELAY_DEBUG
1093 if(timeout>WDCNDELAY_DEBUG)
1094 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout);
1095 #endif
1096 if (cmd != WDCC_READP)
1097 return (stat);
1098
1099 /* is controller ready to return data? */
1100 for (timeout=0; ((stat=inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0; ) {
1101 DELAY(WDCDELAY);
1102 if(++timeout > WDCNDELAY)
1103 return -1;
1104 }
1105 #ifdef WDCNDELAY_DEBUG
1106 if(timeout>WDCNDELAY_DEBUG)
1107 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout);
1108 #endif
1109 return (stat);
1110 }
1111
1112 /*
1113 * issue IDC to drive to tell it just what geometry it is to be.
1114 */
1115 static int
1116 wdsetctlr(dev_t dev, struct disk *du)
1117 {
1118 int stat, x, wdc;
1119
1120 /*
1121 printf("wd(%d,%d) C%dH%dS%d\n", du->dk_ctrlr, du->dk_unit,
1122 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors);
1123 */
1124
1125 wdc = du->dk_port;
1126
1127 /*DELAY(2000);*/
1128
1129 x = splbio();
1130 outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); /* TIH: was ...ders+1 */
1131 outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8); /* TIH: was ...ders+1 */
1132 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4) + du->dk_dd.d_ntracks-1);
1133 outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
1134 stat = wdcommand(du, WDCC_IDC);
1135
1136 #ifndef TIHMODS
1137 if (stat < 0)
1138 return(stat);
1139 #endif
1140 if (stat & WDCS_ERR)
1141 printf("wdsetctlr: status %b error %b\n",
1142 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
1143 splx(x);
1144 return(stat);
1145 }
1146
1147 /*
1148 * issue READP to drive to ask it what it is.
1149 */
1150 static int
1151 wdgetctlr(int u, struct disk *du)
1152 {
1153 int stat, x, i, wdc;
1154 char tb[DEV_BSIZE];
1155 struct wdparams *wp;
1156 int timeout;
1157
1158 x = splbio(); /* not called from intr level ... */
1159 wdc = du->dk_port;
1160 #ifdef TIPCAT
1161 for (timeout=0; (inb(wdc+wd_status) & WDCS_BUSY); ) {
1162 DELAY(WDCDELAY);
1163 if(++timeout > WDCNDELAY) {
1164 splx(x);
1165 return -1;
1166 }
1167 }
1168 #ifdef WDCNDELAY_DEBUG
1169 if(timeout>WDCNDELAY_DEBUG)
1170 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout);
1171 #endif
1172 #endif
1173 outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
1174 #ifdef TIPCAT
1175 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) {
1176 DELAY(WDCDELAY);
1177 if(++timeout > WDCNDELAY) {
1178 splx(x);
1179 return -1;
1180 }
1181 }
1182 #ifdef WDCNDELAY_DEBUG
1183 if(timeout>WDCNDELAY_DEBUG)
1184 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout);
1185 #endif
1186 #endif
1187 stat = wdcommand(du, WDCC_READP);
1188 #ifdef TIPCAT
1189 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) {
1190 DELAY(WDCDELAY);
1191 if(++timeout > WDCNDELAY) {
1192 splx(x);
1193 return -1;
1194 }
1195 }
1196 #ifdef WDCNDELAY_DEBUG
1197 if(timeout>WDCNDELAY_DEBUG)
1198 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout);
1199 #endif
1200 #endif
1201
1202 #ifndef TIHMODS
1203 if (stat < 0)
1204 return(stat);
1205 #else
1206 if (stat < 0) {
1207 splx(x);
1208 return(stat);
1209 }
1210 #endif
1211
1212 if( (stat & WDCS_ERR) == 0) {
1213 /* obtain parameters */
1214 wp = &du->dk_params;
1215 insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short));
1216 bcopy(tb, wp, sizeof(struct wdparams));
1217
1218 /* shuffle string byte order */
1219 for (i=0; i < sizeof(wp->wdp_model); i+=2) {
1220 u_short *p;
1221 p = (u_short *) (wp->wdp_model + i);
1222 *p = ntohs(*p);
1223 }
1224
1225 strncpy(du->dk_dd.d_typename, "ESDI/IDE", sizeof du->dk_dd.d_typename);
1226 du->dk_dd.d_type = DTYPE_ESDI;
1227 bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
1228
1229 /* update disklabel given drive information */
1230 du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
1231 du->dk_dd.d_ntracks = wp->wdp_heads;
1232 du->dk_dd.d_nsectors = wp->wdp_sectors;
1233 du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
1234 du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl *
1235 wp->wdp_sectors;
1236 du->dk_dd.d_partitions[1].p_offset = 0;
1237 } else {
1238 /*
1239 * If WDCC_READP fails then we might have an old drive
1240 * so we try a seek to 0; if that passes then the
1241 * drive is there but it's OLD AND KRUSTY.
1242 */
1243 stat = wdcommand(du, WDCC_RESTORE | WD_STEP);
1244 if(stat & WDCS_ERR) {
1245 splx(x);
1246 return(inb(wdc+wd_error));
1247 }
1248
1249 strncpy(du->dk_dd.d_typename, "ST506", sizeof du->dk_dd.d_typename);
1250 strncpy(du->dk_params.wdp_model, "Unknown Type",
1251 sizeof du->dk_params.wdp_model);
1252 du->dk_dd.d_type = DTYPE_ST506;
1253 }
1254
1255 #if 0
1256 printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config,
1257 wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors,
1258 wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);
1259 #endif
1260
1261 /* better ... */
1262 du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
1263
1264 /* XXX sometimes possibly needed */
1265 (void) inb(wdc+wd_status);
1266 #ifdef TIHMODS
1267 splx(x);
1268 #endif
1269 return (0);
1270 }
1271
1272
1273 /* ARGSUSED */
1274 int
1275 wdclose(dev_t dev, int flags, int fmt)
1276 {
1277 register struct disk *du;
1278 int part = wdpart(dev), mask = 1 << part;
1279
1280 du = wddrives[wdunit(dev)];
1281
1282 /* insure only one open at a time */
1283 du->dk_openpart &= ~mask;
1284 switch (fmt) {
1285 case S_IFCHR:
1286 du->dk_copenpart &= ~mask;
1287 break;
1288 case S_IFBLK:
1289 du->dk_bopenpart &= ~mask;
1290 break;
1291 }
1292 return(0);
1293 }
1294
1295 int
1296 wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
1297 {
1298 int lunit = wdunit(dev);
1299 register struct disk *du;
1300 int error = 0;
1301 struct uio auio;
1302 struct iovec aiov;
1303
1304 du = wddrives[lunit];
1305
1306 switch (cmd) {
1307 case DIOCSBAD:
1308 if ((flag & FWRITE) == 0)
1309 error = EBADF;
1310 else {
1311 du->dk_cpd.bad = *(struct dkbad *)addr;
1312 bad144intern(du);
1313 }
1314 break;
1315
1316 case DIOCGDINFO:
1317 *(struct disklabel *)addr = du->dk_dd;
1318 break;
1319
1320 case DIOCGPART:
1321 ((struct partinfo *)addr)->disklab = &du->dk_dd;
1322 ((struct partinfo *)addr)->part =
1323 &du->dk_dd.d_partitions[wdpart(dev)];
1324 break;
1325
1326 case DIOCSDINFO:
1327 if ((flag & FWRITE) == 0)
1328 error = EBADF;
1329 else {
1330 error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
1331 /*(du->dk_flags&DKFL_BSDLABEL) ? du->dk_openpart : */0,
1332 &du->dk_cpd);
1333 }
1334 if (error == 0) {
1335 du->dk_flags |= DKFL_BSDLABEL;
1336 wdsetctlr(dev, du);
1337 }
1338 break;
1339
1340 case DIOCWLABEL:
1341 du->dk_flags &= ~DKFL_WRITEPROT;
1342 if ((flag & FWRITE) == 0)
1343 error = EBADF;
1344 else
1345 du->dk_wlabel = *(int *)addr;
1346 break;
1347
1348 case DIOCWDINFO:
1349 du->dk_flags &= ~DKFL_WRITEPROT;
1350 if ((flag & FWRITE) == 0)
1351 error = EBADF;
1352 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
1353 /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0,
1354 &du->dk_cpd)) == 0) {
1355 int wlab;
1356
1357 du->dk_flags |= DKFL_BSDLABEL;
1358 wdsetctlr(dev, du);
1359
1360 /* simulate opening partition 0 so write succeeds */
1361 du->dk_openpart |= (1 << 0); /* XXX */
1362 wlab = du->dk_wlabel;
1363 du->dk_wlabel = 1;
1364 error = writedisklabel(dev, wdstrategy, &du->dk_dd, &du->dk_cpd);
1365 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
1366 du->dk_wlabel = wlab;
1367 }
1368 break;
1369
1370 #ifdef notyet
1371 case DIOCGDINFOP:
1372 *(struct disklabel **)addr = &(du->dk_dd);
1373 break;
1374
1375 case DIOCWFORMAT:
1376 if ((flag & FWRITE) == 0)
1377 error = EBADF;
1378 else {
1379 register struct format_op *fop;
1380
1381 fop = (struct format_op *)addr;
1382 aiov.iov_base = fop->df_buf;
1383 aiov.iov_len = fop->df_count;
1384 auio.uio_iov = &aiov;
1385 auio.uio_iovcnt = 1;
1386 auio.uio_resid = fop->df_count;
1387 auio.uio_segflg = 0;
1388 auio.uio_offset = fop->df_startblk * du->dk_dd.d_secsize;
1389 error = physio(wdformat, &rwdbuf[lunit], dev, B_WRITE,
1390 minphys, &auio);
1391 fop->df_count -= auio.uio_resid;
1392 fop->df_reg[0] = du->dk_status;
1393 fop->df_reg[1] = du->dk_error;
1394 }
1395 break;
1396 #endif
1397
1398 default:
1399 error = ENOTTY;
1400 break;
1401 }
1402 return (error);
1403 }
1404
1405 #ifdef B_FORMAT
1406 int
1407 wdformat(struct buf *bp)
1408 {
1409 bp->b_flags |= B_FORMAT;
1410 return (wdstrategy(bp));
1411 }
1412 #endif
1413
1414 int
1415 wdsize(dev_t dev)
1416 {
1417 int lunit = wdunit(dev), part = wdpart(dev);
1418 struct disk *du;
1419
1420 if (lunit >= NWD)
1421 return(-1);
1422
1423 if ((du = wddrives[lunit]) == 0)
1424 return (-1);
1425
1426 if (du->dk_state < OPEN || (du->dk_flags & DKFL_BSDLABEL) == 0) {
1427 int val;
1428 val = wdopen(makewddev(major(dev), lunit, WDRAW), FREAD, S_IFBLK, 0);
1429 if (val != 0)
1430 return (-1);
1431 }
1432
1433 if ((du->dk_flags & (DKFL_WRITEPROT|DKFL_BSDLABEL)) != DKFL_BSDLABEL)
1434 return (-1);
1435 else
1436 return((int)du->dk_dd.d_partitions[part].p_size);
1437 }
1438
1439 extern char *vmmap; /* poor name! */
1440
1441 /* dump core after a system crash */
1442 int
1443 wddump(dev_t dev)
1444 {
1445 register struct disk *du; /* disk unit to do the IO */
1446 long num; /* number of sectors to write */
1447 int ctrlr, lunit, part, wdc;
1448 long blkoff, blknum;
1449 long cylin, head, sector, stat;
1450 long secpertrk, secpercyl, nblocks, i;
1451 char *addr;
1452 static wddoingadump = 0;
1453 extern caddr_t CADDR1;
1454
1455 addr = (char *) 0; /* starting address */
1456
1457 #if DO_NOT_KNOW_HOW
1458 /* toss any characters present prior to dump, ie. non-blocking getc */
1459 while (cngetc())
1460 ;
1461 #endif
1462
1463 /* size of memory to dump */
1464 lunit = wdunit(dev); /* eventually support floppies? */
1465 part = wdpart(dev); /* file system */
1466 /* check for acceptable drive number */
1467 if (lunit >= NWD)
1468 return(ENXIO);
1469
1470 du = wddrives[lunit];
1471 if (du == 0)
1472 return(ENXIO);
1473 /* was it ever initialized ? */
1474 if (du->dk_state < OPEN)
1475 return (ENXIO);
1476 if (du->dk_flags & DKFL_WRITEPROT)
1477 return(ENXIO);
1478 wdc = du->dk_port;
1479 ctrlr = du->dk_ctrlr;
1480
1481 /* Convert to disk sectors */
1482 num = ctob(physmem) / du->dk_dd.d_secsize;
1483
1484 /* check if controller active */
1485 /*if (wdtab[ctrlr].b_active)
1486 return(EFAULT); */
1487 if (wddoingadump)
1488 return(EFAULT);
1489
1490 secpertrk = du->dk_dd.d_nsectors;
1491 secpercyl = du->dk_dd.d_secpercyl;
1492 nblocks = du->dk_dd.d_partitions[part].p_size;
1493 blkoff = du->dk_dd.d_partitions[part].p_offset;
1494
1495 /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
1496 /* check transfer bounds against partition size */
1497 if ((dumplo < 0) || ((dumplo + num) > nblocks))
1498 return(EINVAL);
1499
1500 /* mark controller active for if we panic during the dump */
1501 /* wdtab[ctrlr].b_active = 1; */
1502 wddoingadump = 1;
1503 i = 200000000;
1504 while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0))
1505 ;
1506 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
1507 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
1508 while (inb(wdc+wd_status) & WDCS_BUSY)
1509 ;
1510
1511 /* some compaq controllers require this ... */
1512 wdsetctlr(dev, du);
1513
1514 blknum = dumplo + blkoff;
1515 while (num > 0) {
1516 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
1517
1518 /* compute disk address */
1519 cylin = blknum / secpercyl;
1520 head = (blknum % secpercyl) / secpertrk;
1521 sector = blknum % secpertrk;
1522
1523 if (du->dk_flags & DKFL_BADSECT) {
1524 long newblk;
1525 int i;
1526
1527 for (i = 0; du->dk_badsect[i] != -1; i++) {
1528 if (blknum < du->dk_badsect[i]) {
1529 break; /* sorted list, passed our block by */
1530 } else if (blknum == du->dk_badsect[i]) {
1531 newblk = du->dk_dd.d_secperunit -
1532 du->dk_dd.d_nsectors - i - 1;
1533 cylin = newblk / secpercyl;
1534 head = (newblk % secpercyl) / secpertrk;
1535 sector = newblk % secpertrk;
1536 /* found and repl; done scanning bad144 table */
1537 break;
1538 }
1539 }
1540 }
1541 sector++; /* origin 1 */
1542
1543 /* select drive. */
1544 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
1545 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
1546 ;
1547
1548 /* transfer some blocks */
1549 outb(wdc+wd_sector, sector);
1550 outb(wdc+wd_seccnt,1);
1551 outb(wdc+wd_cyl_lo, cylin);
1552 outb(wdc+wd_cyl_hi, cylin >> 8);
1553 #ifdef notdef
1554 /* lets just talk about this first...*/
1555 pg ("sdh 0%o sector %d cyl %d addr 0x%x",
1556 inb(wdc+wd_sdh), inb(wdc+wd_sector),
1557 inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr);
1558 #endif
1559 outb(wdc+wd_command, WDCC_WRITE);
1560
1561 /* Ready to send data? */
1562 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
1563 ;
1564 if (inb(wdc+wd_status) & WDCS_ERR)
1565 return(EIO);
1566
1567 outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
1568
1569 if (inb(wdc+wd_status) & WDCS_ERR)
1570 return(EIO);
1571 /* Check data request (should be done). */
1572 if (inb(wdc+wd_status) & WDCS_DRQ)
1573 return(EIO);
1574
1575 /* wait for completion */
1576 for (i=200000000; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
1577 if (i < 0)
1578 return (EIO);
1579 }
1580
1581 /* error check the xfer */
1582 if (inb(wdc+wd_status) & WDCS_ERR)
1583 return(EIO);
1584
1585 if ((unsigned)addr % (1024*1024) == 0)
1586 printf("%d ", num/2048);
1587
1588 /* update block count */
1589 num--;
1590 blknum++;
1591 (int) addr += 512;
1592
1593 #if DO_NOT_KNOW_HOW
1594 /* operator aborting dump? non-blocking getc() */
1595 if (cngetc())
1596 return(EINTR);
1597 #endif
1598 }
1599 return(0);
1600 }
1601 #endif
1602
1603 /*
1604 * Internalize the bad sector table.
1605 */
1606 void
1607 bad144intern(struct disk *du)
1608 {
1609 int i;
1610 if (du->dk_flags & DKFL_BADSECT) {
1611 for (i = 0; i < 127; i++) {
1612 du->dk_badsect[i] = -1;
1613 }
1614
1615 for (i = 0; i < 126; i++) {
1616 if (du->dk_cpd.bad.bt_bad[i].bt_cyl == 0xffff) {
1617 break;
1618 } else {
1619 du->dk_badsect[i] =
1620 du->dk_cpd.bad.bt_bad[i].bt_cyl *
1621 du->dk_dd.d_secpercyl +
1622 (du->dk_cpd.bad.bt_bad[i].bt_trksec >> 8) *
1623 du->dk_dd.d_nsectors +
1624 (du->dk_cpd.bad.bt_bad[i].bt_trksec & 0x00ff);
1625 }
1626 }
1627 }
1628 }
1629
1630 /* this routine was adopted from the kernel sources */
1631 /* more efficient because b_cylin is not really as useful at this level */
1632 /* so I eliminate the processing, I believe that sorting the sectors */
1633 /* is adequate */
1634 void
1635 wddisksort(struct buf *dp, struct buf *bp)
1636 {
1637 register struct buf *ap;
1638
1639 /*
1640 * If nothing on the activity queue, then
1641 * we become the only thing.
1642 */
1643 ap = dp->b_actf;
1644 if(ap == NULL) {
1645 dp->b_actf = bp;
1646 dp->b_actl = bp;
1647 bp->av_forw = NULL;
1648 return;
1649 }
1650 while( ap->b_flags & B_XXX) {
1651 if( ap->av_forw == 0 || (ap->av_forw->b_flags & B_XXX) == 0)
1652 break;
1653 ap = ap->av_forw;
1654 }
1655 /*
1656 * If we lie after the first (currently active)
1657 * request, then we must locate the second request list
1658 * and add ourselves to it.
1659 */
1660 if (bp->b_blkno < ap->b_blkno) {
1661 while (ap->av_forw) {
1662 /*
1663 * Check for an ``inversion'' in the
1664 * normally ascending cylinder numbers,
1665 * indicating the start of the second request list.
1666 */
1667 if (ap->av_forw->b_blkno < ap->b_blkno) {
1668 /*
1669 * Search the second request list
1670 * for the first request at a larger
1671 * cylinder number. We go before that;
1672 * if there is no such request, we go at end.
1673 */
1674 do {
1675 if (bp->b_blkno < ap->av_forw->b_blkno)
1676 goto insert;
1677 ap = ap->av_forw;
1678 } while (ap->av_forw);
1679 goto insert; /* after last */
1680 }
1681 ap = ap->av_forw;
1682 }
1683 /*
1684 * No inversions... we will go after the last, and
1685 * be the first request in the second request list.
1686 */
1687 goto insert;
1688 }
1689 /*
1690 * Request is at/after the current request...
1691 * sort in the first request list.
1692 */
1693 while (ap->av_forw) {
1694 /*
1695 * We want to go after the current request
1696 * if there is an inversion after it (i.e. it is
1697 * the end of the first request list), or if
1698 * the next request is a larger cylinder than our request.
1699 */
1700 if (ap->av_forw->b_blkno < ap->b_blkno ||
1701 bp->b_blkno < ap->av_forw->b_blkno )
1702 goto insert;
1703 ap = ap->av_forw;
1704 }
1705 /*
1706 * Neither a second list nor a larger
1707 * request... we go at the end of the first list,
1708 * which is the same as the end of the whole schebang.
1709 */
1710 insert:
1711 bp->av_forw = ap->av_forw;
1712 ap->av_forw = bp;
1713 if (ap == dp->b_actl)
1714 dp->b_actl = bp;
1715 }
1716
1717 static int
1718 wdreset(int ctrlr, int wdc, int err)
1719 {
1720 int stat, timeout;
1721
1722 if(err)
1723 printf("wdc%d: busy too long, resetting\n", ctrlr);
1724
1725 /* reset the device */
1726 outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
1727 DELAY(1000);
1728 outb(wdc+wd_ctlr, WDCTL_4BIT);
1729
1730 for (timeout=0; (stat=inb(wdc+wd_status)) & WDCS_BUSY; ) {
1731 DELAY(WDCDELAY);
1732 if(++timeout > WDCNDELAY) {
1733 printf("wdc%d: failed to reset controller\n", ctrlr);
1734 break;
1735 }
1736 }
1737 #ifdef WDCNDELAY_DEBUG
1738 if(timeout>WDCNDELAY_DEBUG)
1739 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout);
1740 #endif
1741 }
1742
1743
1744 static int
1745 wdtimeout(caddr_t arg)
1746 {
1747 int x = splbio();
1748 register int unit = (int) arg;
1749
1750 if (wdtimeoutstatus[unit]) {
1751 if (--wdtimeoutstatus[unit] == 0) {
1752 struct disk *du = wddrives[unit];
1753 int wdc = du->dk_port;
1754 /* #ifdef WDDEBUG */
1755 printf("wd%d: lost interrupt - status %x, error %x\n",
1756 unit, inb(wdc+wd_status), inb(wdc+wd_error));
1757 /* #endif */
1758 outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
1759 DELAY(1000);
1760 outb(wdc+wd_ctlr, WDCTL_IDS);
1761 DELAY(1000);
1762 (void) inb(wdc+wd_error);
1763 outb(wdc+wd_ctlr, WDCTL_4BIT);
1764 du->dk_skip = 0;
1765 du->dk_flags |= DKFL_SINGLE;
1766 wdstart(du->dk_ctrlr); /* start controller */
1767 }
1768 }
1769 timeout((timeout_t)wdtimeout, (caddr_t)unit, 50);
1770 splx(x);
1771 return (0);
1772 }
1773