wd.c revision 1.11 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 */
38
39 /* Note: This code heavily modified by tih (at) barsoom.nhh.no; use at own risk! */
40 /* The following defines represent only a very small part of the mods, most */
41 /* of them are not marked in any way. -tih */
42
43 #undef WDOPENLOCK /* Prevent reentrancy in wdopen() for testing */
44 #define TIHMODS /* wdopen() workaround, some splx() calls */
45 #define TIHBAD144 /* new bad144 bad sector handling */
46 #define QUIETWORKS /* define this when wdopen() can actually set DKFL_QUIET */
47 #define INSTRUMENT /* Add instrumentation stuff by Brad Parker */
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 "param.h"
56 #include "dkbad.h"
57 #include "systm.h"
58 #include "conf.h"
59 #include "file.h"
60 #include "stat.h"
61 #include "ioctl.h"
62 #include "disklabel.h"
63 #include "buf.h"
64 #include "uio.h"
65 #include "malloc.h"
66 #include "machine/cpu.h"
67 #ifdef INSTRUMENT
68 #include "sys/dkstat.h"
69 #endif
70 #include "i386/isa/isa.h"
71 #include "i386/isa/isa_device.h"
72 #include "i386/isa/icu.h"
73 #include "i386/isa/wdreg.h"
74 #include "syslog.h"
75 #include "vm/vm.h"
76
77 #define RETRIES 5 /* number of retries before giving up */
78
79 #define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
80 #define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */
81 #define wdunit(dev) ((minor(dev) & 0x38) >> 3)
82 #define wdpart(dev) (minor(dev) & 0x7)
83 #define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part)))
84 #define WDRAW 3 /* 'd' partition isn't a partition! */
85
86 #define b_cylin b_resid /* cylinder number for doing IO to */
87 /* shares an entry in the buf struct */
88
89 /*
90 * Drive states. Used to initialize drive.
91 */
92
93 #define CLOSED 0 /* disk is closed. */
94 #define WANTOPEN 1 /* open requested, not started */
95 #define RECAL 2 /* doing restore */
96 #define OPEN 3 /* done with open */
97
98 /*
99 * The structure of a disk drive.
100 */
101 struct disk {
102 long dk_bc; /* byte count left */
103 long dk_bct; /* total byte count left */
104 short dk_skip; /* blocks already transferred */
105 short dk_skipm; /* blocks already transferred for multi */
106 char dk_ctrlr; /* physical controller number */
107 char dk_unit; /* physical unit number */
108 char dk_lunit; /* logical unit number */
109 char dk_state; /* control state */
110 u_char dk_status; /* copy of status reg. */
111 u_char dk_error; /* copy of error reg. */
112 short dk_port; /* i/o port base */
113
114 u_long dk_copenpart; /* character units open on this drive */
115 u_long dk_bopenpart; /* block units open on this drive */
116 u_long dk_openpart; /* all units open on this drive */
117 short dk_wlabel; /* label writable? */
118 short dk_flags; /* drive characteistics found */
119 #define DKFL_DOSPART 0x00001 /* has DOS partition table */
120 #define DKFL_QUIET 0x00002 /* report errors back, but don't complain */
121 #define DKFL_SINGLE 0x00004 /* sector at a time mode */
122 #define DKFL_ERROR 0x00008 /* processing a disk error */
123 #define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
124 #define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
125 #define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
126 struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
127 struct disklabel dk_dd; /* device configuration data */
128 struct dos_partition
129 dk_dospartitions[NDOSPART]; /* DOS view of disk */
130 struct dkbad dk_bad; /* bad sector table */
131 #ifdef TIHBAD144
132 long dk_badsect[127]; /* 126 plus trailing -1 marker */
133 #endif
134 };
135
136 struct board {
137 short dkc_port;
138 };
139
140 #ifdef TIHBAD144
141 void bad144intern(struct disk *);
142 #endif
143 void wddisksort();
144
145 struct board wdcontroller[NWDC];
146 struct disk *wddrives[NWD]; /* table of units */
147 struct buf wdtab[NWDC]; /* various per-controller info */
148 struct buf wdutab[NWD]; /* head of queue per drive */
149 struct buf rwdbuf[NWD]; /* buffers for raw IO */
150 long wdxfer[NWD]; /* count of transfers */
151
152 #ifdef WDOPENLOCK
153 int wdopenbusy = 0;
154 #endif
155
156 int wdprobe(), wdattach();
157
158 struct isa_driver wdcdriver = {
159 wdprobe, wdattach, "wdc",
160 };
161
162 void wdustart(struct disk *);
163 void wdstart(int);
164 int wdcommand(struct disk *, int);
165 int wdcontrol(struct buf *);
166 int wdsetctlr(dev_t, struct disk *);
167 int wdgetctlr(int, struct disk *);
168
169 /*
170 * Probe for controller.
171 */
172 int
173 wdprobe(struct isa_device *dvp)
174 {
175 struct disk *du;
176 int wdc;
177
178 #ifdef WDOPENLOCK
179 wdopenbusy = 0;
180 #endif
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 /* reset the device */
202 outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
203 DELAY(1000);
204 outb(wdc+wd_ctlr, WDCTL_IDS);
205 DELAY(1000);
206
207 /* execute a controller only command */
208 if (wdcommand(du, WDCC_DIAGNOSE) < 0)
209 goto nodevice;
210
211 (void) inb(wdc+wd_error); /* XXX! */
212 outb(wdc+wd_ctlr, WDCTL_4BIT);
213 DELAY(1000);
214
215 bzero(&wdtab[du->dk_ctrlr], sizeof(struct buf));
216
217 free(du, M_TEMP);
218 return (8);
219
220 nodevice:
221 free(du, M_TEMP);
222 return (0);
223 }
224
225 /*
226 * Called for the controller too
227 * Attach each drive if possible.
228 */
229 int
230 wdattach(struct isa_device *dvp)
231 {
232 int unit, lunit;
233 struct isa_device *wdup;
234 struct disk *du;
235 int first = 0;
236
237 if (dvp->id_masunit == -1)
238 return(0);
239 if (dvp->id_masunit >= NWDC)
240 return(0);
241
242 lunit = dvp->id_unit;
243 if (lunit == -1) {
244 printf("wdc%d: cannot support unit ?\n", dvp->id_masunit);
245 return 0;
246 }
247 if (lunit >= NWD)
248 return(0);
249 unit = dvp->id_physid;
250
251 du = wddrives[lunit] = (struct disk *)
252 malloc(sizeof(struct disk), M_TEMP, M_NOWAIT);
253 bzero(du, sizeof(struct disk));
254 bzero(&wdutab[lunit], sizeof(struct buf));
255 bzero(&rwdbuf[lunit], sizeof(struct buf));
256 wdxfer[lunit] = 0;
257
258 du->dk_ctrlr = dvp->id_masunit;
259 du->dk_unit = unit;
260 du->dk_lunit = lunit;
261 du->dk_port = wdcontroller[dvp->id_masunit].dkc_port;
262
263 if(wdgetctlr(unit, du) == 0) {
264 int i, blank;
265
266 printf("wd%d at wdc%d slave %d: <",
267 lunit, dvp->id_masunit, unit);
268 for (i=blank=0; i<sizeof(du->dk_params.wdp_model); i++) {
269 char c = du->dk_params.wdp_model[i];
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;
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 #ifdef TIHBAD144
471 /* Check for bad sectors if we have them, and not formatting */
472 /* Only do this in single-sector mode, or when starting a */
473 /* multiple-sector transfer. */
474 #ifdef B_FORMAT
475 if ((du->dk_flags & DKFL_BADSECT) && !(bp->b_flags & B_FORMAT) &&
476 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) {
477 #else
478 if ((du->dk_flags & DKFL_BADSECT) &&
479 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) {
480 #endif
481
482 long blkchk, blkend, blknew;
483 int i;
484
485 blkend = blknum + howmany(du->dk_bct, DEV_BSIZE) - 1;
486 for (i = 0; (blkchk = du->dk_badsect[i]) != -1; i++) {
487 if (blkchk > blkend) {
488 break; /* transfer is completely OK; done */
489 } else if (blkchk == blknum) {
490 blknew = lp->d_secperunit - lp->d_nsectors - i - 1;
491 cylin = blknew / secpercyl;
492 head = (blknew % secpercyl) / secpertrk;
493 sector = blknew % secpertrk;
494 du->dk_flags |= DKFL_SINGLE;
495 /* found and replaced first blk of transfer; done */
496 break;
497 } else if (blkchk > blknum) {
498 du->dk_flags |= DKFL_SINGLE;
499 break; /* bad block inside transfer; done */
500 }
501 }
502 }
503 #endif
504 if( du->dk_flags & DKFL_SINGLE) {
505 du->dk_bct = du->dk_bc;
506 du->dk_skipm = du->dk_skip;
507 }
508
509 #ifdef WDDEBUG
510 pg("c%d h%d s%d ", cylin, head, sector);
511 #endif
512
513 sector += 1; /* sectors begin with 1, not 0 */
514
515 wdtab[ctrlr].b_active = 1; /* mark controller active */
516 wdc = du->dk_port;
517
518 #ifdef INSTRUMENT
519 /* instrumentation */
520 if (du->dk_unit >= 0 && du->dk_skip == 0) {
521 dk_busy |= 1 << du->dk_lunit;
522 dk_wds[du->dk_lunit] += bp->b_bcount >> 6;
523 }
524 if (du->dk_unit >= 0 && du->dk_skipm == 0) {
525 ++dk_seek[du->dk_lunit];
526 ++dk_xfer[du->dk_lunit];
527 }
528 #endif
529
530 /* if starting a multisector transfer, or doing single transfers */
531 if (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE)) {
532 if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) {
533 du->dk_bc += DEV_BSIZE;
534 du->dk_bct += DEV_BSIZE;
535 }
536
537 /* controller idle? */
538 while (inb(wdc+wd_status) & WDCS_BUSY)
539 ;
540
541 /* stuff the task file */
542 outb(wdc+wd_precomp, lp->d_precompcyl / 4);
543 #ifdef B_FORMAT
544 if (bp->b_flags & B_FORMAT) {
545 outb(wdc+wd_sector, lp->d_gap3);
546 outb(wdc+wd_seccnt, lp->d_nsectors);
547 } else {
548 if (du->dk_flags & DKFL_SINGLE)
549 outb(wdc+wd_seccnt, 1);
550 else
551 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
552 outb(wdc+wd_sector, sector);
553 }
554 #else
555 if (du->dk_flags & DKFL_SINGLE)
556 outb(wdc+wd_seccnt, 1);
557 else
558 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
559 outb(wdc+wd_sector, sector);
560 #endif
561 outb(wdc+wd_cyl_lo, cylin);
562 outb(wdc+wd_cyl_hi, cylin >> 8);
563
564 /* set up the SDH register (select drive) */
565 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
566
567 /* wait for drive to become ready */
568 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
569 ;
570
571 /* initiate command! */
572 #ifdef B_FORMAT
573 if (bp->b_flags & B_FORMAT)
574 outb(wdc+wd_command, WDCC_FORMAT);
575 else
576 outb(wdc+wd_command,
577 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
578 #else
579 outb(wdc+wd_command, (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
580 #endif
581 #ifdef WDDEBUG
582 printf("sector %d cylin %d head %d addr %x sts %x\n",
583 sector, cylin, head, addr, inb(wdc+wd_altsts));
584 #endif
585 }
586
587 /* if this is a read operation, just go away until it's done. */
588 if (bp->b_flags & B_READ)
589 return;
590
591 /* ready to send data? */
592 while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0)
593 ;
594
595 /* then send it! */
596 outagain:
597 outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
598 DEV_BSIZE/sizeof(short));
599 du->dk_bc -= DEV_BSIZE;
600 du->dk_bct -= DEV_BSIZE;
601 }
602
603 /* Interrupt routine for the controller. Acknowledge the interrupt, check for
604 * errors on the current operation, mark it done if necessary, and start
605 * the next request. Also check for a partially done transfer, and
606 * continue with the next chunk if so.
607 */
608 void
609 wdintr(struct intrframe wdif)
610 {
611 register struct disk *du;
612 register struct buf *bp, *dp;
613 int status, wdc, ctrlr;
614
615 ctrlr = wdif.if_vec;
616
617 if (!wdtab[ctrlr].b_active) {
618 printf("wdc%d: extra interrupt\n", ctrlr);
619 return;
620 }
621
622 dp = wdtab[ctrlr].b_actf;
623 bp = dp->b_actf;
624 du = wddrives[wdunit(bp->b_dev)];
625 wdc = du->dk_port;
626
627 #ifdef WDDEBUG
628 printf("I%d ", ctrlr);
629 #endif
630
631 while ((status = inb(wdc+wd_status)) & WDCS_BUSY)
632 ;
633
634 /* is it not a transfer, but a control operation? */
635 if (du->dk_state < OPEN) {
636 if (wdcontrol(bp))
637 wdstart(ctrlr);
638 return;
639 }
640
641 /* have we an error? */
642 if (status & (WDCS_ERR | WDCS_ECCCOR)) {
643 du->dk_status = status;
644 du->dk_error = inb(wdc + wd_error);
645 #ifdef WDDEBUG
646 printf("status %x error %x\n", status, du->dk_error);
647 #endif
648 if((du->dk_flags & DKFL_SINGLE) == 0) {
649 du->dk_flags |= DKFL_ERROR;
650 goto outt;
651 }
652 #ifdef B_FORMAT
653 if (bp->b_flags & B_FORMAT) {
654 bp->b_flags |= B_ERROR;
655 goto done;
656 }
657 #endif
658
659 /* error or error correction? */
660 if (status & WDCS_ERR) {
661 if (++wdtab[ctrlr].b_errcnt < RETRIES) {
662 wdtab[ctrlr].b_active = 0;
663 } else {
664 if((du->dk_flags & DKFL_QUIET) == 0) {
665 diskerr(bp, "wd", "hard error",
666 LOG_PRINTF, du->dk_skip,
667 &du->dk_dd);
668 #ifdef WDDEBUG
669 printf( "status %b error %b\n",
670 status, WDCS_BITS,
671 inb(wdc+wd_error), WDERR_BITS);
672 #endif
673 }
674 bp->b_flags |= B_ERROR; /* flag the error */
675 }
676 } else if((du->dk_flags & DKFL_QUIET) == 0) {
677 diskerr(bp, "wd", "soft ecc", 0,
678 du->dk_skip, &du->dk_dd);
679 }
680 }
681 outt:
682
683 /*
684 * If this was a successful read operation, fetch the data.
685 */
686 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab[ctrlr].b_active) {
687 int chk, dummy;
688
689 chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
690
691 /* ready to receive data? */
692 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
693 ;
694
695 /* suck in data */
696 insw (wdc+wd_data,
697 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
698 du->dk_bc -= chk * sizeof(short);
699 du->dk_bct -= chk * sizeof(short);
700
701 /* for obselete fractional sector reads */
702 while (chk++ < (DEV_BSIZE / sizeof(short)))
703 insw(wdc+wd_data, &dummy, 1);
704 }
705
706 wdxfer[du->dk_lunit]++;
707 if (wdtab[ctrlr].b_active) {
708 #ifdef INSTRUMENT
709 if (du->dk_unit >= 0)
710 dk_busy &=~ (1 << du->dk_unit);
711 #endif
712 if ((bp->b_flags & B_ERROR) == 0) {
713 du->dk_skip++; /* Add to succ. sect */
714 du->dk_skipm++; /* Add to succ. sect for multitransfer */
715 if (wdtab[ctrlr].b_errcnt && (du->dk_flags & DKFL_QUIET) == 0)
716 diskerr(bp, "wd", "soft error", 0,
717 du->dk_skip, &du->dk_dd);
718 wdtab[ctrlr].b_errcnt = 0;
719
720 /* see if more to transfer */
721 if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
722 if( (du->dk_flags & DKFL_SINGLE)
723 || (du->dk_flags & B_READ) == 0)
724 wdstart(ctrlr);
725 return; /* next chunk is started */
726 } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR)) == DKFL_ERROR) {
727 du->dk_skip = 0;
728 du->dk_skipm = 0;
729 du->dk_flags &= ~DKFL_ERROR;
730 du->dk_flags |= DKFL_SINGLE;
731 wdstart(ctrlr);
732 return; /* redo xfer sector by sector */
733 }
734 }
735
736 done:
737 /* done with this transfer, with or without error */
738 du->dk_flags &= ~DKFL_SINGLE;
739 wdtab[ctrlr].b_errcnt = 0;
740 du->dk_skip = 0;
741 if( du->dk_bct == 0) {
742 wdtab[ctrlr].b_actf = dp->b_forw;
743 du->dk_skipm = 0;
744 dp->b_active = 0;
745 }
746 dp->b_actf = bp->av_forw;
747 dp->b_errcnt = 0;
748 bp->b_resid = 0;
749 bp->b_flags &= ~B_XXX;
750 biodone(bp);
751 }
752
753 /* anything more on drive queue? */
754 if (dp->b_actf && du->dk_bct == 0)
755 wdustart(du);
756
757 /* anything more for controller to do? */
758 if (wdtab[ctrlr].b_actf)
759 wdstart(ctrlr);
760
761 if (!wdtab[ctrlr].b_actf)
762 wdtab[ctrlr].b_active = 0;
763 }
764
765 /*
766 * Initialize a drive.
767 */
768 int
769 wdopen(dev_t dev, int flags, int fmt, struct proc *p)
770 {
771 register unsigned int lunit;
772 register struct disk *du;
773 int part = wdpart(dev), mask = 1 << part;
774 struct partition *pp;
775 int error = 0;
776 char *msg;
777
778 lunit = wdunit(dev);
779 if (lunit >= NWD)
780 return (ENXIO);
781
782 du = wddrives[lunit];
783
784 if (du == 0)
785 return (ENXIO);
786
787 #ifdef WDOPENLOCK
788 printf("[Enter wd%d%c]\n", lunit, part+'a');
789 if (wdopenbusy) {
790 printf("[Sleep wd%d%c]\n", lunit, part+'a');
791 while (wdopenbusy)
792 ;
793 }
794 printf("[Lock wd%d%c]\n", lunit, part+'a');
795 wdopenbusy = 1;
796 #endif
797
798 #ifdef QUIETWORKS
799 if (part == WDRAW)
800 du->dk_flags |= DKFL_QUIET;
801 else
802 du->dk_flags &= ~DKFL_QUIET;
803 #else
804 du->dk_flags &= ~DKFL_QUIET;
805 #endif
806
807 if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
808 du->dk_flags |= DKFL_WRITEPROT;
809 #ifdef WDOPENLOCK
810 printf("[Init wd%d%c]\n", lunit, part+'a');
811 #endif
812 wdutab[lunit].b_actf = NULL;
813
814 /*
815 * Use the default sizes until we've read the label,
816 * or longer if there isn't one there.
817 */
818 bzero(&du->dk_dd, sizeof(du->dk_dd));
819 #undef d_type /* fix goddamn segments.h! XXX */
820 du->dk_dd.d_type = DTYPE_ST506;
821 du->dk_dd.d_ncylinders = 1024;
822 du->dk_dd.d_secsize = DEV_BSIZE;
823 du->dk_dd.d_ntracks = 8;
824 du->dk_dd.d_nsectors = 17;
825 du->dk_dd.d_secpercyl = 17*8;
826 du->dk_dd.d_secperunit = 17*8*1024;
827 du->dk_state = WANTOPEN;
828
829 /* read label using "raw" partition */
830 #if defined(TIHMODS) && defined(garbage)
831 /* wdsetctlr(dev, du); */ /* Maybe do this TIH */
832 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
833 wdstrategy, &du->dk_dd, du->dk_dospartitions,
834 0, 0);
835 wdsetctlr(dev, du);
836 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
837 wdstrategy, &du->dk_dd, du->dk_dospartitions,
838 &du->dk_bad, 0);
839 if (msg) {
840 #ifdef QUIETWORKS
841 if((du->dk_flags & DKFL_QUIET) == 0) {
842 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
843 lunit, msg);
844 error = EINVAL; /* XXX needs translation */
845 }
846 #else
847 log(LOG_WARNING, "wd%d: cannot find label (%s)\n", lunit, msg);
848 if(part != WDRAW) {
849 error = EINVAL; /* XXX needs translation */
850 }
851 #endif
852 goto done;
853 } else {
854 wdsetctlr(dev, du);
855 du->dk_flags |= DKFL_BSDLABEL;
856 du->dk_flags &= ~DKFL_WRITEPROT;
857 if (du->dk_dd.d_flags & D_BADSECT)
858 du->dk_flags |= DKFL_BADSECT;
859 }
860 #else
861 if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
862 wdstrategy, &du->dk_dd, du->dk_dospartitions,
863 &du->dk_bad, 0)) {
864 if((du->dk_flags & DKFL_QUIET) == 0) {
865 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
866 lunit, msg);
867 error = EINVAL; /* XXX needs translation */
868 }
869 goto done;
870 } else {
871 wdsetctlr(dev, du);
872 du->dk_flags |= DKFL_BSDLABEL;
873 du->dk_flags &= ~DKFL_WRITEPROT;
874 if (du->dk_dd.d_flags & D_BADSECT)
875 du->dk_flags |= DKFL_BADSECT;
876 }
877 #endif
878
879 done:
880 if (error) {
881 #ifdef WDOPENLOCK
882 printf("[Error wd%d%c]\n", lunit, part+'a');
883 wdopenbusy = 0;
884 #endif
885 return(error);
886 }
887 }
888
889 #ifdef TIHBAD144
890 if (du->dk_flags & DKFL_BADSECT)
891 bad144intern(du);
892 #endif
893
894 /*
895 * Warn if a partion is opened
896 * that overlaps another partition which is open
897 * unless one is the "raw" partition (whole disk).
898 */
899 if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) {
900 int start, end;
901
902 pp = &du->dk_dd.d_partitions[part];
903 start = pp->p_offset;
904 end = pp->p_offset + pp->p_size;
905 for (pp = du->dk_dd.d_partitions;
906 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; pp++) {
907 if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end)
908 continue;
909 /*if (pp - du->dk_dd.d_partitions == RAWPART)
910 continue; */
911 if (pp - du->dk_dd.d_partitions == WDRAW)
912 continue;
913 if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions)))
914 log(LOG_WARNING,
915 "wd%d%c: overlaps open partition (%c)\n",
916 lunit, part + 'a',
917 pp - du->dk_dd.d_partitions + 'a');
918 }
919 }
920
921 if (part >= du->dk_dd.d_npartitions && part != WDRAW) {
922 #ifdef WDOPENLOCK
923 printf("[ENXIO wd%d%c]\n", lunit, part+'a');
924 wdopenbusy = 0;
925 #endif
926 return (ENXIO);
927 }
928
929 /* insure only one open at a time */
930 du->dk_openpart |= mask;
931 switch (fmt) {
932 case S_IFCHR:
933 du->dk_copenpart |= mask;
934 break;
935 case S_IFBLK:
936 du->dk_bopenpart |= mask;
937 break;
938 }
939 #ifdef WDOPENLOCK
940 printf("[Done wd%d%c]\n", lunit, part+'a');
941 wdopenbusy = 0;
942 #endif
943 return (0);
944 }
945
946 /*
947 * Implement operations other than read/write.
948 * Called from wdstart or wdintr during opens and formats.
949 * Uses finite-state-machine to track progress of operation in progress.
950 * Returns 0 if operation still in progress, 1 if completed.
951 */
952 static int
953 wdcontrol(register struct buf *bp)
954 {
955 register struct disk *du;
956 register unit, lunit;
957 unsigned char stat;
958 int s, ctrlr;
959 int wdc;
960
961 du = wddrives[wdunit(bp->b_dev)];
962 ctrlr = du->dk_ctrlr;
963 unit = du->dk_unit;
964 lunit = du->dk_lunit;
965 wdc = du->dk_port;
966
967 switch (du->dk_state) {
968 tryagainrecal:
969 case WANTOPEN: /* set SDH, step rate, do restore */
970 #ifdef WDDEBUG
971 printf("wd%d: recal ", lunit);
972 #endif
973 s = splbio(); /* not called from intr level ... */
974 wdgetctlr(unit, du);
975 #ifdef TIPCAT
976 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
977 ;
978 #endif
979 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
980 wdtab[ctrlr].b_active = 1;
981 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
982 #ifdef TIPCAT
983 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
984 ;
985 #endif
986 du->dk_state = RECAL;
987 splx(s);
988 return(0);
989 case RECAL:
990 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
991 if ((du->dk_flags & DKFL_QUIET) == 0) {
992 printf("wd%d: recal", du->dk_lunit);
993 printf(": status %b error %b\n",
994 stat, WDCS_BITS, inb(wdc+wd_error),
995 WDERR_BITS);
996 }
997 if (++wdtab[ctrlr].b_errcnt < RETRIES)
998 goto tryagainrecal;
999 bp->b_error = ENXIO; /* XXX needs translation */
1000 goto badopen;
1001 }
1002
1003 /* some controllers require this ... */
1004 wdsetctlr(bp->b_dev, du);
1005
1006 wdtab[ctrlr].b_errcnt = 0;
1007 du->dk_state = OPEN;
1008 /*
1009 * The rest of the initialization can be done
1010 * by normal means.
1011 */
1012 return(1);
1013 default:
1014 panic("wdcontrol");
1015 }
1016 /* NOTREACHED */
1017
1018 badopen:
1019 if ((du->dk_flags & DKFL_QUIET) == 0)
1020 printf(": status %b error %b\n",
1021 stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS);
1022 bp->b_flags |= B_ERROR;
1023 return(1);
1024 }
1025
1026 /*
1027 * send a command and wait uninterruptibly until controller is finished.
1028 * return -1 if controller busy for too long, otherwise
1029 * return status. intended for brief controller commands at critical points.
1030 * assumes interrupts are blocked.
1031 */
1032 static int
1033 wdcommand(struct disk *du, int cmd)
1034 {
1035 int timeout = 1000000, stat, wdc;
1036
1037 DELAY(2000);
1038 /* controller ready for command? */
1039 wdc = du->dk_port;
1040 while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout > 0)
1041 timeout--;
1042 if (timeout <= 0)
1043 return(-1);
1044
1045 /* send command, await results */
1046 outb(wdc+wd_command, cmd);
1047 while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout > 0)
1048 timeout--;
1049 if (timeout <= 0)
1050 return(-1);
1051 if (cmd != WDCC_READP)
1052 return (stat);
1053
1054 /* is controller ready to return data? */
1055 while (((stat = inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0 &&
1056 timeout > 0)
1057 timeout--;
1058 if (timeout <= 0)
1059 return(-1);
1060 return (stat);
1061 }
1062
1063 /*
1064 * issue IDC to drive to tell it just what geometry it is to be.
1065 */
1066 static int
1067 wdsetctlr(dev_t dev, struct disk *du)
1068 {
1069 int stat, x, wdc;
1070
1071 /*
1072 printf("wd(%d,%d) C%dH%dS%d\n", du->dk_ctrlr, du->dk_unit,
1073 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors);
1074 */
1075
1076 DELAY(2000);
1077 x = splbio();
1078 wdc = du->dk_port;
1079 outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); /* TIH: was ...ders+1 */
1080 outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8); /* TIH: was ...ders+1 */
1081 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4) + du->dk_dd.d_ntracks-1);
1082 outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
1083 stat = wdcommand(du, WDCC_IDC);
1084
1085 #ifndef TIHMODS
1086 if (stat < 0)
1087 return(stat);
1088 #endif
1089 if (stat & WDCS_ERR)
1090 printf("wdsetctlr: status %b error %b\n",
1091 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
1092 splx(x);
1093 return(stat);
1094 }
1095
1096 /*
1097 * issue READP to drive to ask it what it is.
1098 */
1099 static int
1100 wdgetctlr(int u, struct disk *du)
1101 {
1102 int stat, x, i, wdc;
1103 char tb[DEV_BSIZE];
1104 struct wdparams *wp;
1105 long timeout = 5000000;
1106
1107 x = splbio(); /* not called from intr level ... */
1108 wdc = du->dk_port;
1109 #ifdef TIPCAT
1110 while ((inb(wdc+wd_status) & WDCS_READY) == 0 && timeout > 0)
1111 timeout--;
1112 if (timeout <= 0) {
1113 splx(x);
1114 return (-1);
1115 }
1116 #endif
1117 outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
1118 stat = wdcommand(du, WDCC_READP);
1119 #ifdef TIPCAT
1120 while ((inb(wdc+wd_status) & WDCS_READY) == 0 && timeout > 0)
1121 timeout--;
1122 if (timeout <= 0) {
1123 splx(x);
1124 return (-1);
1125 }
1126 #endif
1127
1128 #ifndef TIHMODS
1129 if (stat < 0)
1130 return(stat);
1131 #else
1132 if (stat < 0) {
1133 splx(x);
1134 return(stat);
1135 }
1136 #endif
1137
1138 /*
1139 * If WDCC_READP fails then we might have an old drive so we try
1140 * a seek to 0; if that passes then the drive is there but it's
1141 * OLD AND KRUSTY.
1142 */
1143 if (stat & WDCS_ERR) {
1144 stat = wdcommand(du, WDCC_RESTORE | WD_STEP);
1145 if(stat & WDCS_ERR) {
1146 splx(x);
1147 return(inb(wdc+wd_error));
1148 }
1149 stat = 0x7f; /* MFM/RLL marker for later. */
1150 }
1151
1152 /* obtain parameters */
1153 wp = &du->dk_params;
1154 insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short));
1155 bcopy(tb, wp, sizeof(struct wdparams));
1156
1157 /* shuffle string byte order */
1158 for (i=0; i < sizeof(wp->wdp_model); i+=2) {
1159 u_short *p;
1160 p = (u_short *) (wp->wdp_model + i);
1161 *p = ntohs(*p);
1162 }
1163
1164 /*
1165 printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config,
1166 wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors,
1167 wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);
1168 */
1169
1170 /* update disklabel given drive information */
1171 du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
1172 du->dk_dd.d_ntracks = wp->wdp_heads;
1173 du->dk_dd.d_nsectors = wp->wdp_sectors;
1174 du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
1175 du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl * wp->wdp_sectors;
1176 du->dk_dd.d_partitions[1].p_offset = 0;
1177
1178 /* dubious ... */
1179 if(stat == 0x7f) {
1180 strncpy(du->dk_dd.d_typename, "ST506", sizeof du->dk_dd.d_typename);
1181 for(i=0; i<sizeof(wp->wdp_model); i++)
1182 wp->wdp_model[i] = ' ';
1183 strncpy(du->dk_params.wdp_model, "Unknown Type",
1184 sizeof du->dk_params.wdp_model);
1185 du->dk_dd.d_type = DTYPE_ST506;
1186 } else {
1187 strncpy(du->dk_dd.d_typename, "ESDI/IDE", sizeof du->dk_dd.d_typename);
1188 du->dk_dd.d_type = DTYPE_ESDI;
1189 bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
1190 }
1191 /* better ... */
1192 du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
1193
1194 /* XXX sometimes possibly needed */
1195 (void) inb(wdc+wd_status);
1196 #ifdef TIHMODS
1197 splx(x);
1198 #endif
1199 return (0);
1200 }
1201
1202
1203 /* ARGSUSED */
1204 int
1205 wdclose(dev_t dev, int flags, int fmt)
1206 {
1207 register struct disk *du;
1208 int part = wdpart(dev), mask = 1 << part;
1209
1210 du = wddrives[wdunit(dev)];
1211
1212 /* insure only one open at a time */
1213 du->dk_openpart &= ~mask;
1214 switch (fmt) {
1215 case S_IFCHR:
1216 du->dk_copenpart &= ~mask;
1217 break;
1218 case S_IFBLK:
1219 du->dk_bopenpart &= ~mask;
1220 break;
1221 }
1222 return(0);
1223 }
1224
1225 int
1226 wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
1227 {
1228 int lunit = wdunit(dev);
1229 register struct disk *du;
1230 int error = 0;
1231 struct uio auio;
1232 struct iovec aiov;
1233
1234 du = wddrives[lunit];
1235
1236 switch (cmd) {
1237 case DIOCSBAD:
1238 if ((flag & FWRITE) == 0)
1239 error = EBADF;
1240 else {
1241 du->dk_bad = *(struct dkbad *)addr;
1242 #ifdef TIHBAD144
1243 bad144intern(du);
1244 #endif
1245 }
1246 break;
1247
1248 case DIOCGDINFO:
1249 *(struct disklabel *)addr = du->dk_dd;
1250 break;
1251
1252 case DIOCGPART:
1253 ((struct partinfo *)addr)->disklab = &du->dk_dd;
1254 ((struct partinfo *)addr)->part =
1255 &du->dk_dd.d_partitions[wdpart(dev)];
1256 break;
1257
1258 case DIOCSDINFO:
1259 if ((flag & FWRITE) == 0)
1260 error = EBADF;
1261 else
1262 error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
1263 /*(du->dk_flags&DKFL_BSDLABEL) ? du->dk_openpart : */0,
1264 du->dk_dospartitions);
1265 if (error == 0) {
1266 du->dk_flags |= DKFL_BSDLABEL;
1267 wdsetctlr(dev, du);
1268 }
1269 break;
1270
1271 case DIOCWLABEL:
1272 du->dk_flags &= ~DKFL_WRITEPROT;
1273 if ((flag & FWRITE) == 0)
1274 error = EBADF;
1275 else
1276 du->dk_wlabel = *(int *)addr;
1277 break;
1278
1279 case DIOCWDINFO:
1280 du->dk_flags &= ~DKFL_WRITEPROT;
1281 if ((flag & FWRITE) == 0)
1282 error = EBADF;
1283 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
1284 /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0,
1285 du->dk_dospartitions)) == 0) {
1286 int wlab;
1287
1288 du->dk_flags |= DKFL_BSDLABEL;
1289 wdsetctlr(dev, du);
1290
1291 /* simulate opening partition 0 so write succeeds */
1292 du->dk_openpart |= (1 << 0); /* XXX */
1293 wlab = du->dk_wlabel;
1294 du->dk_wlabel = 1;
1295 error = writedisklabel(dev, wdstrategy,
1296 &du->dk_dd, du->dk_dospartitions);
1297 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
1298 du->dk_wlabel = wlab;
1299 }
1300 break;
1301
1302 #ifdef notyet
1303 case DIOCGDINFOP:
1304 *(struct disklabel **)addr = &(du->dk_dd);
1305 break;
1306
1307 case DIOCWFORMAT:
1308 if ((flag & FWRITE) == 0)
1309 error = EBADF;
1310 else {
1311 register struct format_op *fop;
1312
1313 fop = (struct format_op *)addr;
1314 aiov.iov_base = fop->df_buf;
1315 aiov.iov_len = fop->df_count;
1316 auio.uio_iov = &aiov;
1317 auio.uio_iovcnt = 1;
1318 auio.uio_resid = fop->df_count;
1319 auio.uio_segflg = 0;
1320 auio.uio_offset = fop->df_startblk * du->dk_dd.d_secsize;
1321 error = physio(wdformat, &rwdbuf[lunit], dev, B_WRITE,
1322 minphys, &auio);
1323 fop->df_count -= auio.uio_resid;
1324 fop->df_reg[0] = du->dk_status;
1325 fop->df_reg[1] = du->dk_error;
1326 }
1327 break;
1328 #endif
1329
1330 default:
1331 error = ENOTTY;
1332 break;
1333 }
1334 return (error);
1335 }
1336
1337 #ifdef B_FORMAT
1338 int
1339 wdformat(struct buf *bp)
1340 {
1341 bp->b_flags |= B_FORMAT;
1342 return (wdstrategy(bp));
1343 }
1344 #endif
1345
1346 int
1347 wdsize(dev_t dev)
1348 {
1349 int lunit = wdunit(dev), part = wdpart(dev);
1350 struct disk *du;
1351
1352 if (lunit >= NWD)
1353 return(-1);
1354
1355 if ((du = wddrives[lunit]) == 0)
1356 return (-1);
1357
1358 if (du->dk_state < OPEN || (du->dk_flags & DKFL_BSDLABEL) == 0) {
1359 int val;
1360 val = wdopen(makewddev(major(dev), lunit, WDRAW), FREAD, S_IFBLK, 0);
1361 if (val != 0)
1362 return (-1);
1363 }
1364
1365 if ((du->dk_flags & (DKFL_WRITEPROT|DKFL_BSDLABEL)) != DKFL_BSDLABEL)
1366 return (-1);
1367 else
1368 return((int)du->dk_dd.d_partitions[part].p_size);
1369 }
1370
1371 extern char *vmmap; /* poor name! */
1372
1373 /* dump core after a system crash */
1374 int
1375 wddump(dev_t dev)
1376 {
1377 register struct disk *du; /* disk unit to do the IO */
1378 long num; /* number of sectors to write */
1379 int ctrlr, lunit, part, wdc;
1380 long blkoff, blknum;
1381 long cylin, head, sector, stat;
1382 long secpertrk, secpercyl, nblocks, i;
1383 char *addr;
1384 extern int Maxmem;
1385 static wddoingadump = 0;
1386 extern caddr_t CADDR1;
1387
1388 addr = (char *) 0; /* starting address */
1389
1390 /* toss any characters present prior to dump */
1391 while (sgetc(1))
1392 ;
1393
1394 /* size of memory to dump */
1395 num = Maxmem;
1396 lunit = wdunit(dev); /* eventually support floppies? */
1397 part = wdpart(dev); /* file system */
1398 /* check for acceptable drive number */
1399 if (lunit >= NWD)
1400 return(ENXIO);
1401
1402 du = wddrives[lunit];
1403 if (du == 0)
1404 return(ENXIO);
1405 /* was it ever initialized ? */
1406 if (du->dk_state < OPEN)
1407 return (ENXIO);
1408 if (du->dk_flags & DKFL_WRITEPROT)
1409 return(ENXIO);
1410 wdc = du->dk_port;
1411 ctrlr = du->dk_ctrlr;
1412
1413 /* Convert to disk sectors */
1414 num = (u_long) num * NBPG / du->dk_dd.d_secsize;
1415
1416 /* check if controller active */
1417 /*if (wdtab[ctrlr].b_active)
1418 return(EFAULT); */
1419 if (wddoingadump)
1420 return(EFAULT);
1421
1422 secpertrk = du->dk_dd.d_nsectors;
1423 secpercyl = du->dk_dd.d_secpercyl;
1424 nblocks = du->dk_dd.d_partitions[part].p_size;
1425 blkoff = du->dk_dd.d_partitions[part].p_offset;
1426
1427 /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
1428 /* check transfer bounds against partition size */
1429 if ((dumplo < 0) || ((dumplo + num) > nblocks))
1430 return(EINVAL);
1431
1432 /* mark controller active for if we panic during the dump */
1433 /* wdtab[ctrlr].b_active = 1; */
1434 wddoingadump = 1;
1435 i = 200000000;
1436 while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0))
1437 ;
1438 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
1439 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
1440 while (inb(wdc+wd_status) & WDCS_BUSY)
1441 ;
1442
1443 /* some compaq controllers require this ... */
1444 wdsetctlr(dev, du);
1445
1446 blknum = dumplo + blkoff;
1447 while (num > 0) {
1448 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
1449
1450 /* compute disk address */
1451 cylin = blknum / secpercyl;
1452 head = (blknum % secpercyl) / secpertrk;
1453 sector = blknum % secpertrk;
1454
1455 #ifdef TIHBAD144
1456 if (du->dk_flags & DKFL_BADSECT) {
1457 long newblk;
1458 int i;
1459
1460 for (i = 0; du->dk_badsect[i] != -1; i++) {
1461 if (blknum < du->dk_badsect[i]) {
1462 break; /* sorted list, passed our block by */
1463 } else if (blknum == du->dk_badsect[i]) {
1464 newblk = du->dk_dd.d_secperunit -
1465 du->dk_dd.d_nsectors - i - 1;
1466 cylin = newblk / secpercyl;
1467 head = (newblk % secpercyl) / secpertrk;
1468 sector = newblk % secpertrk;
1469 /* found and repl; done scanning bad144 table */
1470 break;
1471 }
1472 }
1473 }
1474 #endif
1475 sector++; /* origin 1 */
1476
1477 /* select drive. */
1478 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
1479 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
1480 ;
1481
1482 /* transfer some blocks */
1483 outb(wdc+wd_sector, sector);
1484 outb(wdc+wd_seccnt,1);
1485 outb(wdc+wd_cyl_lo, cylin);
1486 outb(wdc+wd_cyl_hi, cylin >> 8);
1487 #ifdef notdef
1488 /* lets just talk about this first...*/
1489 pg ("sdh 0%o sector %d cyl %d addr 0x%x",
1490 inb(wdc+wd_sdh), inb(wdc+wd_sector),
1491 inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr);
1492 #endif
1493 outb(wdc+wd_command, WDCC_WRITE);
1494
1495 /* Ready to send data? */
1496 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
1497 ;
1498 if (inb(wdc+wd_status) & WDCS_ERR)
1499 return(EIO);
1500
1501 outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
1502
1503 if (inb(wdc+wd_status) & WDCS_ERR)
1504 return(EIO);
1505 /* Check data request (should be done). */
1506 if (inb(wdc+wd_status) & WDCS_DRQ)
1507 return(EIO);
1508
1509 /* wait for completion */
1510 for (i=200000000; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
1511 if (i < 0)
1512 return (EIO);
1513 }
1514
1515 /* error check the xfer */
1516 if (inb(wdc+wd_status) & WDCS_ERR)
1517 return(EIO);
1518
1519 if ((unsigned)addr % (1024*1024) == 0)
1520 printf("%d ", num/2048);
1521
1522 /* update block count */
1523 num--;
1524 blknum++;
1525 (int) addr += 512;
1526
1527 /* operator aborting dump? */
1528 if (sgetc(1))
1529 return(EINTR);
1530 }
1531 return(0);
1532 }
1533 #endif
1534
1535 #ifdef TIHBAD144
1536 /*
1537 * Internalize the bad sector table.
1538 */
1539 void
1540 bad144intern(struct disk *du)
1541 {
1542 int i;
1543 if (du->dk_flags & DKFL_BADSECT) {
1544 for (i = 0; i < 127; i++) {
1545 du->dk_badsect[i] = -1;
1546 }
1547
1548 for (i = 0; i < 126; i++) {
1549 if (du->dk_bad.bt_bad[i].bt_cyl == 0xffff) {
1550 break;
1551 } else {
1552 du->dk_badsect[i] =
1553 du->dk_bad.bt_bad[i].bt_cyl * du->dk_dd.d_secpercyl +
1554 (du->dk_bad.bt_bad[i].bt_trksec >> 8) * du->dk_dd.d_nsectors +
1555 (du->dk_bad.bt_bad[i].bt_trksec & 0x00ff);
1556 }
1557 }
1558 }
1559 }
1560 #endif
1561
1562 wdprint() {}
1563
1564
1565 /* this routine was adopted from the kernel sources */
1566 /* more efficient because b_cylin is not really as useful at this level */
1567 /* so I eliminate the processing, I believe that sorting the sectors */
1568 /* is adequate */
1569 void
1570 wddisksort(struct buf *dp, struct buf *bp)
1571 {
1572 register struct buf *ap;
1573
1574 /*
1575 * If nothing on the activity queue, then
1576 * we become the only thing.
1577 */
1578 ap = dp->b_actf;
1579 if(ap == NULL) {
1580 dp->b_actf = bp;
1581 dp->b_actl = bp;
1582 bp->av_forw = NULL;
1583 return;
1584 }
1585 while( ap->b_flags & B_XXX) {
1586 if( ap->av_forw == 0 || (ap->av_forw->b_flags & B_XXX) == 0)
1587 break;
1588 ap = ap->av_forw;
1589 }
1590 /*
1591 * If we lie after the first (currently active)
1592 * request, then we must locate the second request list
1593 * and add ourselves to it.
1594 */
1595 if (bp->b_blkno < ap->b_blkno) {
1596 while (ap->av_forw) {
1597 /*
1598 * Check for an ``inversion'' in the
1599 * normally ascending cylinder numbers,
1600 * indicating the start of the second request list.
1601 */
1602 if (ap->av_forw->b_blkno < ap->b_blkno) {
1603 /*
1604 * Search the second request list
1605 * for the first request at a larger
1606 * cylinder number. We go before that;
1607 * if there is no such request, we go at end.
1608 */
1609 do {
1610 if (bp->b_blkno < ap->av_forw->b_blkno)
1611 goto insert;
1612 ap = ap->av_forw;
1613 } while (ap->av_forw);
1614 goto insert; /* after last */
1615 }
1616 ap = ap->av_forw;
1617 }
1618 /*
1619 * No inversions... we will go after the last, and
1620 * be the first request in the second request list.
1621 */
1622 goto insert;
1623 }
1624 /*
1625 * Request is at/after the current request...
1626 * sort in the first request list.
1627 */
1628 while (ap->av_forw) {
1629 /*
1630 * We want to go after the current request
1631 * if there is an inversion after it (i.e. it is
1632 * the end of the first request list), or if
1633 * the next request is a larger cylinder than our request.
1634 */
1635 if (ap->av_forw->b_blkno < ap->b_blkno ||
1636 bp->b_blkno < ap->av_forw->b_blkno )
1637 goto insert;
1638 ap = ap->av_forw;
1639 }
1640 /*
1641 * Neither a second list nor a larger
1642 * request... we go at the end of the first list,
1643 * which is the same as the end of the whole schebang.
1644 */
1645 insert:
1646 bp->av_forw = ap->av_forw;
1647 ap->av_forw = bp;
1648 if (ap == dp->b_actl)
1649 dp->b_actl = bp;
1650 }
1651
1652