wd.c revision 1.13 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 disk *du;
234
235 if (dvp->id_masunit == -1)
236 return(0);
237 if (dvp->id_masunit >= NWDC)
238 return(0);
239
240 lunit = dvp->id_unit;
241 if (lunit == -1) {
242 printf("wdc%d: cannot support unit ?\n", dvp->id_masunit);
243 return 0;
244 }
245 if (lunit >= NWD)
246 return(0);
247 unit = dvp->id_physid;
248
249 du = wddrives[lunit] = (struct disk *)
250 malloc(sizeof(struct disk), M_TEMP, M_NOWAIT);
251 bzero(du, sizeof(struct disk));
252 bzero(&wdutab[lunit], sizeof(struct buf));
253 bzero(&rwdbuf[lunit], sizeof(struct buf));
254 wdxfer[lunit] = 0;
255
256 du->dk_ctrlr = dvp->id_masunit;
257 du->dk_unit = unit;
258 du->dk_lunit = lunit;
259 du->dk_port = wdcontroller[dvp->id_masunit].dkc_port;
260
261 if(wdgetctlr(unit, du) == 0) {
262 int i, blank;
263
264 printf("wd%d at wdc%d targ %d: ",
265 dvp->id_unit, dvp->id_masunit, dvp->id_physid);
266 if(du->dk_params.wdp_heads==0)
267 printf("(unknown size) <");
268 else
269 printf("%dMB %d cyl, %d head, %d sec <",
270 du->dk_dd.d_ncylinders * du->dk_dd.d_secpercyl / 2048,
271 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
272 du->dk_dd.d_nsectors);
273 for (i=blank=0; i<sizeof(du->dk_params.wdp_model); i++) {
274 char c = du->dk_params.wdp_model[i];
275 if (blank && c == ' ')
276 continue;
277 if (blank && c != ' ') {
278 printf(" %c", c);
279 blank = 0;
280 continue;
281 }
282 if (c == ' ')
283 blank = 1;
284 else
285 printf("%c", c);
286 }
287 printf(">\n");
288 } else {
289 /*printf("wd%d at wdc%d slave %d -- error\n",
290 lunit, dvp->id_masunit, unit);*/
291 wddrives[lunit] = 0;
292 free(du, M_TEMP);
293 return 0;
294 }
295 return 1;
296 }
297
298 /* Read/write routine for a buffer. Finds the proper unit, range checks
299 * arguments, and schedules the transfer. Does not wait for the transfer
300 * to complete. Multi-page transfers are supported. All I/O requests must
301 * be a multiple of a sector in length.
302 */
303 int
304 wdstrategy(register struct buf *bp)
305 {
306 register struct buf *dp;
307 struct disk *du; /* Disk unit to do the IO. */
308 int lunit = wdunit(bp->b_dev);
309 int s;
310
311 /* valid unit, controller, and request? */
312 if (lunit >= NWD || bp->b_blkno < 0 ||
313 howmany(bp->b_bcount, DEV_BSIZE) >= (1<<NBBY) ||
314 (du = wddrives[lunit]) == 0) {
315 bp->b_error = EINVAL;
316 bp->b_flags |= B_ERROR;
317 goto done;
318 }
319
320 /* "soft" write protect check */
321 if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
322 bp->b_error = EROFS;
323 bp->b_flags |= B_ERROR;
324 goto done;
325 }
326
327 /* have partitions and want to use them? */
328 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) {
329 /*
330 * do bounds checking, adjust transfer. if error, process.
331 * if end of partition, just return
332 */
333 if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0)
334 goto done;
335 /* otherwise, process transfer request */
336 }
337
338 /* queue transfer on drive, activate drive and controller if idle */
339 dp = &wdutab[lunit];
340 s = splbio();
341 wddisksort(dp, bp);
342 if (dp->b_active == 0)
343 wdustart(du); /* start drive */
344 if (wdtab[du->dk_ctrlr].b_active == 0)
345 wdstart(du->dk_ctrlr); /* start controller */
346 splx(s);
347 return 0;
348
349 done:
350 /* toss transfer, we're done early */
351 biodone(bp);
352 return 0;
353 }
354
355 /*
356 * Routine to queue a command to the controller. The unit's
357 * request is linked into the active list for the controller.
358 * If the controller is idle, the transfer is started.
359 */
360 static void
361 wdustart(register struct disk *du)
362 {
363 register struct buf *bp, *dp = &wdutab[du->dk_lunit];
364 int ctrlr = du->dk_ctrlr;
365
366 /* unit already active? */
367 if (dp->b_active)
368 return;
369
370 /* anything to start? */
371 bp = dp->b_actf;
372 if (bp == NULL)
373 return;
374
375 /* link onto controller queue */
376 dp->b_forw = NULL;
377 if (wdtab[ctrlr].b_actf == NULL)
378 wdtab[ctrlr].b_actf = dp;
379 else
380 wdtab[ctrlr].b_actl->b_forw = dp;
381 wdtab[ctrlr].b_actl = dp;
382
383 /* mark the drive unit as busy */
384 dp->b_active = 1;
385 }
386
387 /*
388 * Controller startup routine. This does the calculation, and starts
389 * a single-sector read or write operation. Called to start a transfer,
390 * or from the interrupt routine to continue a multi-sector transfer.
391 * RESTRICTIONS:
392 * 1. The transfer length must be an exact multiple of the sector size.
393 */
394 static void
395 wdstart(int ctrlr)
396 {
397 register struct disk *du; /* disk unit for IO */
398 register struct buf *bp;
399 struct disklabel *lp;
400 struct buf *dp;
401 long blknum, cylin, head, sector;
402 long secpertrk, secpercyl, addr;
403 int lunit, wdc;
404 int xfrblknum;
405 unsigned char status;
406
407 loop:
408 /* is there a drive for the controller to do a transfer with? */
409 dp = wdtab[ctrlr].b_actf;
410 if (dp == NULL)
411 return;
412
413 /* is there a transfer to this drive ? if so, link it on
414 the controller's queue */
415 bp = dp->b_actf;
416 if (bp == NULL) {
417 wdtab[ctrlr].b_actf = dp->b_forw;
418 goto loop;
419 }
420
421 /* obtain controller and drive information */
422 lunit = wdunit(bp->b_dev);
423 du = wddrives[lunit];
424
425 /* if not really a transfer, do control operations specially */
426 if (du->dk_state < OPEN) {
427 (void) wdcontrol(bp);
428 return;
429 }
430
431 /* calculate transfer details */
432 blknum = bp->b_blkno + du->dk_skip;
433 #ifdef WDDEBUG
434 if (du->dk_skip == 0)
435 printf("\nwdstart %d: %s %d@%d; map ", lunit,
436 (bp->b_flags & B_READ) ? "read" : "write",
437 bp->b_bcount, blknum);
438 else
439 printf(" %d)%x", du->dk_skip, inb(du->dk_port+wd_altsts));
440 #endif
441 addr = (int) bp->b_un.b_addr;
442 if (du->dk_skip == 0) {
443 du->dk_bc = bp->b_bcount;
444 }
445 if (du->dk_skipm == 0) {
446 struct buf *oldbp, *nextbp;
447 oldbp = bp;
448 nextbp = bp->av_forw;
449 du->dk_bct = du->dk_bc;
450 oldbp->b_flags |= B_XXX;
451 while( nextbp
452 && (oldbp->b_flags & DKFL_SINGLE) == 0
453 && oldbp->b_dev == nextbp->b_dev
454 && nextbp->b_blkno == (oldbp->b_blkno + (oldbp->b_bcount/DEV_BSIZE))
455 && (oldbp->b_flags & B_READ) == (nextbp->b_flags & B_READ)) {
456 if( (du->dk_bct+nextbp->b_bcount)/DEV_BSIZE >= 240) {
457 break;
458 }
459 du->dk_bct += nextbp->b_bcount;
460 oldbp->b_flags |= B_XXX;
461 oldbp = nextbp;
462 nextbp = nextbp->av_forw;
463 }
464 }
465
466 lp = &du->dk_dd;
467 secpertrk = lp->d_nsectors;
468 secpercyl = lp->d_secpercyl;
469 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
470 blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
471 cylin = blknum / secpercyl;
472 head = (blknum % secpercyl) / secpertrk;
473 sector = blknum % secpertrk;
474
475 #ifdef TIHBAD144
476 /* Check for bad sectors if we have them, and not formatting */
477 /* Only do this in single-sector mode, or when starting a */
478 /* multiple-sector transfer. */
479 #ifdef B_FORMAT
480 if ((du->dk_flags & DKFL_BADSECT) && !(bp->b_flags & B_FORMAT) &&
481 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) {
482 #else
483 if ((du->dk_flags & DKFL_BADSECT) &&
484 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) {
485 #endif
486
487 long blkchk, blkend, blknew;
488 int i;
489
490 blkend = blknum + howmany(du->dk_bct, DEV_BSIZE) - 1;
491 for (i = 0; (blkchk = du->dk_badsect[i]) != -1; i++) {
492 if (blkchk > blkend) {
493 break; /* transfer is completely OK; done */
494 } else if (blkchk == blknum) {
495 blknew = lp->d_secperunit - lp->d_nsectors - i - 1;
496 cylin = blknew / secpercyl;
497 head = (blknew % secpercyl) / secpertrk;
498 sector = blknew % secpertrk;
499 du->dk_flags |= DKFL_SINGLE;
500 /* found and replaced first blk of transfer; done */
501 break;
502 } else if (blkchk > blknum) {
503 du->dk_flags |= DKFL_SINGLE;
504 break; /* bad block inside transfer; done */
505 }
506 }
507 }
508 #endif
509 if( du->dk_flags & DKFL_SINGLE) {
510 du->dk_bct = du->dk_bc;
511 du->dk_skipm = du->dk_skip;
512 }
513
514 #ifdef WDDEBUG
515 pg("c%d h%d s%d ", cylin, head, sector);
516 #endif
517
518 sector += 1; /* sectors begin with 1, not 0 */
519
520 wdtab[ctrlr].b_active = 1; /* mark controller active */
521 wdc = du->dk_port;
522
523 #ifdef INSTRUMENT
524 /* instrumentation */
525 if (du->dk_unit >= 0 && du->dk_skip == 0) {
526 dk_busy |= 1 << du->dk_lunit;
527 dk_wds[du->dk_lunit] += bp->b_bcount >> 6;
528 }
529 if (du->dk_unit >= 0 && du->dk_skipm == 0) {
530 ++dk_seek[du->dk_lunit];
531 ++dk_xfer[du->dk_lunit];
532 }
533 #endif
534
535 /* if starting a multisector transfer, or doing single transfers */
536 if (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE)) {
537 if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) {
538 du->dk_bc += DEV_BSIZE;
539 du->dk_bct += DEV_BSIZE;
540 }
541
542 /* controller idle? */
543 while (inb(wdc+wd_status) & WDCS_BUSY)
544 ;
545
546 /* stuff the task file */
547 outb(wdc+wd_precomp, lp->d_precompcyl / 4);
548 #ifdef B_FORMAT
549 if (bp->b_flags & B_FORMAT) {
550 outb(wdc+wd_sector, lp->d_gap3);
551 outb(wdc+wd_seccnt, lp->d_nsectors);
552 } else {
553 if (du->dk_flags & DKFL_SINGLE)
554 outb(wdc+wd_seccnt, 1);
555 else
556 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
557 outb(wdc+wd_sector, sector);
558 }
559 #else
560 if (du->dk_flags & DKFL_SINGLE)
561 outb(wdc+wd_seccnt, 1);
562 else
563 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
564 outb(wdc+wd_sector, sector);
565 #endif
566 outb(wdc+wd_cyl_lo, cylin);
567 outb(wdc+wd_cyl_hi, cylin >> 8);
568
569 /* set up the SDH register (select drive) */
570 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
571
572 /* wait for drive to become ready */
573 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
574 ;
575
576 /* initiate command! */
577 #ifdef B_FORMAT
578 if (bp->b_flags & B_FORMAT)
579 outb(wdc+wd_command, WDCC_FORMAT);
580 else
581 outb(wdc+wd_command,
582 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
583 #else
584 outb(wdc+wd_command, (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
585 #endif
586 #ifdef WDDEBUG
587 printf("sector %d cylin %d head %d addr %x sts %x\n",
588 sector, cylin, head, addr, inb(wdc+wd_altsts));
589 #endif
590 }
591
592 /* if this is a read operation, just go away until it's done. */
593 if (bp->b_flags & B_READ)
594 return;
595
596 /* ready to send data? */
597 while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0)
598 ;
599
600 /* then send it! */
601 outagain:
602 outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
603 DEV_BSIZE/sizeof(short));
604 du->dk_bc -= DEV_BSIZE;
605 du->dk_bct -= DEV_BSIZE;
606 }
607
608 /* Interrupt routine for the controller. Acknowledge the interrupt, check for
609 * errors on the current operation, mark it done if necessary, and start
610 * the next request. Also check for a partially done transfer, and
611 * continue with the next chunk if so.
612 */
613 void
614 wdintr(struct intrframe wdif)
615 {
616 register struct disk *du;
617 register struct buf *bp, *dp;
618 int status, wdc, ctrlr;
619
620 ctrlr = wdif.if_vec;
621
622 if (!wdtab[ctrlr].b_active) {
623 printf("wdc%d: extra interrupt\n", ctrlr);
624 return;
625 }
626
627 dp = wdtab[ctrlr].b_actf;
628 bp = dp->b_actf;
629 du = wddrives[wdunit(bp->b_dev)];
630 wdc = du->dk_port;
631
632 #ifdef WDDEBUG
633 printf("I%d ", ctrlr);
634 #endif
635
636 while ((status = inb(wdc+wd_status)) & WDCS_BUSY)
637 ;
638
639 /* is it not a transfer, but a control operation? */
640 if (du->dk_state < OPEN) {
641 if (wdcontrol(bp))
642 wdstart(ctrlr);
643 return;
644 }
645
646 /* have we an error? */
647 if (status & (WDCS_ERR | WDCS_ECCCOR)) {
648 du->dk_status = status;
649 du->dk_error = inb(wdc + wd_error);
650 #ifdef WDDEBUG
651 printf("status %x error %x\n", status, du->dk_error);
652 #endif
653 if((du->dk_flags & DKFL_SINGLE) == 0) {
654 du->dk_flags |= DKFL_ERROR;
655 goto outt;
656 }
657 #ifdef B_FORMAT
658 if (bp->b_flags & B_FORMAT) {
659 bp->b_flags |= B_ERROR;
660 goto done;
661 }
662 #endif
663
664 /* error or error correction? */
665 if (status & WDCS_ERR) {
666 if (++wdtab[ctrlr].b_errcnt < RETRIES) {
667 wdtab[ctrlr].b_active = 0;
668 } else {
669 if((du->dk_flags & DKFL_QUIET) == 0) {
670 diskerr(bp, "wd", "hard error",
671 LOG_PRINTF, du->dk_skip,
672 &du->dk_dd);
673 #ifdef WDDEBUG
674 printf( "status %b error %b\n",
675 status, WDCS_BITS,
676 inb(wdc+wd_error), WDERR_BITS);
677 #endif
678 }
679 bp->b_flags |= B_ERROR; /* flag the error */
680 }
681 } else if((du->dk_flags & DKFL_QUIET) == 0) {
682 diskerr(bp, "wd", "soft ecc", 0,
683 du->dk_skip, &du->dk_dd);
684 }
685 }
686 outt:
687
688 /*
689 * If this was a successful read operation, fetch the data.
690 */
691 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab[ctrlr].b_active) {
692 int chk, dummy;
693
694 chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
695
696 /* ready to receive data? */
697 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
698 ;
699
700 /* suck in data */
701 insw (wdc+wd_data,
702 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
703 du->dk_bc -= chk * sizeof(short);
704 du->dk_bct -= chk * sizeof(short);
705
706 /* for obselete fractional sector reads */
707 while (chk++ < (DEV_BSIZE / sizeof(short)))
708 insw(wdc+wd_data, &dummy, 1);
709 }
710
711 wdxfer[du->dk_lunit]++;
712 if (wdtab[ctrlr].b_active) {
713 #ifdef INSTRUMENT
714 if (du->dk_unit >= 0)
715 dk_busy &=~ (1 << du->dk_unit);
716 #endif
717 if ((bp->b_flags & B_ERROR) == 0) {
718 du->dk_skip++; /* Add to succ. sect */
719 du->dk_skipm++; /* Add to succ. sect for multitransfer */
720 if (wdtab[ctrlr].b_errcnt && (du->dk_flags & DKFL_QUIET) == 0)
721 diskerr(bp, "wd", "soft error", 0,
722 du->dk_skip, &du->dk_dd);
723 wdtab[ctrlr].b_errcnt = 0;
724
725 /* see if more to transfer */
726 if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
727 if( (du->dk_flags & DKFL_SINGLE)
728 || (du->dk_flags & B_READ) == 0)
729 wdstart(ctrlr);
730 return; /* next chunk is started */
731 } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR)) == DKFL_ERROR) {
732 du->dk_skip = 0;
733 du->dk_skipm = 0;
734 du->dk_flags &= ~DKFL_ERROR;
735 du->dk_flags |= DKFL_SINGLE;
736 wdstart(ctrlr);
737 return; /* redo xfer sector by sector */
738 }
739 }
740
741 done:
742 /* done with this transfer, with or without error */
743 du->dk_flags &= ~DKFL_SINGLE;
744 wdtab[ctrlr].b_errcnt = 0;
745 du->dk_skip = 0;
746 if( du->dk_bct == 0) {
747 wdtab[ctrlr].b_actf = dp->b_forw;
748 du->dk_skipm = 0;
749 dp->b_active = 0;
750 }
751 dp->b_actf = bp->av_forw;
752 dp->b_errcnt = 0;
753 bp->b_resid = 0;
754 bp->b_flags &= ~B_XXX;
755 biodone(bp);
756 }
757
758 /* anything more on drive queue? */
759 if (dp->b_actf && du->dk_bct == 0)
760 wdustart(du);
761
762 /* anything more for controller to do? */
763 if (wdtab[ctrlr].b_actf)
764 wdstart(ctrlr);
765
766 if (!wdtab[ctrlr].b_actf)
767 wdtab[ctrlr].b_active = 0;
768 }
769
770 /*
771 * Initialize a drive.
772 */
773 int
774 wdopen(dev_t dev, int flags, int fmt, struct proc *p)
775 {
776 register unsigned int lunit;
777 register struct disk *du;
778 int part = wdpart(dev), mask = 1 << part;
779 struct partition *pp;
780 int error = 0;
781 char *msg;
782
783 lunit = wdunit(dev);
784 if (lunit >= NWD)
785 return (ENXIO);
786
787 du = wddrives[lunit];
788
789 if (du == 0)
790 return (ENXIO);
791
792 #ifdef WDOPENLOCK
793 printf("[Enter wd%d%c]\n", lunit, part+'a');
794 if (wdopenbusy) {
795 printf("[Sleep wd%d%c]\n", lunit, part+'a');
796 while (wdopenbusy)
797 ;
798 }
799 printf("[Lock wd%d%c]\n", lunit, part+'a');
800 wdopenbusy = 1;
801 #endif
802
803 #ifdef QUIETWORKS
804 if (part == WDRAW)
805 du->dk_flags |= DKFL_QUIET;
806 else
807 du->dk_flags &= ~DKFL_QUIET;
808 #else
809 du->dk_flags &= ~DKFL_QUIET;
810 #endif
811
812 if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
813 du->dk_flags |= DKFL_WRITEPROT;
814 #ifdef WDOPENLOCK
815 printf("[Init wd%d%c]\n", lunit, part+'a');
816 #endif
817 wdutab[lunit].b_actf = NULL;
818
819 /*
820 * Use the default sizes until we've read the label,
821 * or longer if there isn't one there.
822 */
823 bzero(&du->dk_dd, sizeof(du->dk_dd));
824 #undef d_type /* fix goddamn segments.h! XXX */
825 du->dk_dd.d_type = DTYPE_ST506;
826 du->dk_dd.d_ncylinders = 1024;
827 du->dk_dd.d_secsize = DEV_BSIZE;
828 du->dk_dd.d_ntracks = 8;
829 du->dk_dd.d_nsectors = 17;
830 du->dk_dd.d_secpercyl = 17*8;
831 du->dk_dd.d_secperunit = 17*8*1024;
832 du->dk_state = WANTOPEN;
833
834 /* read label using "raw" partition */
835 #if defined(TIHMODS) && defined(garbage)
836 /* wdsetctlr(dev, du); */ /* Maybe do this TIH */
837 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
838 wdstrategy, &du->dk_dd, du->dk_dospartitions,
839 0, 0);
840 wdsetctlr(dev, du);
841 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
842 wdstrategy, &du->dk_dd, du->dk_dospartitions,
843 &du->dk_bad, 0);
844 if (msg) {
845 #ifdef QUIETWORKS
846 if((du->dk_flags & DKFL_QUIET) == 0) {
847 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
848 lunit, msg);
849 error = EINVAL; /* XXX needs translation */
850 }
851 #else
852 log(LOG_WARNING, "wd%d: cannot find label (%s)\n", lunit, msg);
853 if(part != WDRAW) {
854 error = EINVAL; /* XXX needs translation */
855 }
856 #endif
857 goto done;
858 } else {
859 wdsetctlr(dev, du);
860 du->dk_flags |= DKFL_BSDLABEL;
861 du->dk_flags &= ~DKFL_WRITEPROT;
862 if (du->dk_dd.d_flags & D_BADSECT)
863 du->dk_flags |= DKFL_BADSECT;
864 }
865 #else
866 if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
867 wdstrategy, &du->dk_dd, du->dk_dospartitions,
868 &du->dk_bad, 0)) {
869 if((du->dk_flags & DKFL_QUIET) == 0) {
870 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
871 lunit, msg);
872 error = EINVAL; /* XXX needs translation */
873 }
874 goto done;
875 } else {
876 wdsetctlr(dev, du);
877 du->dk_flags |= DKFL_BSDLABEL;
878 du->dk_flags &= ~DKFL_WRITEPROT;
879 if (du->dk_dd.d_flags & D_BADSECT)
880 du->dk_flags |= DKFL_BADSECT;
881 }
882 #endif
883
884 done:
885 if (error) {
886 #ifdef WDOPENLOCK
887 printf("[Error wd%d%c]\n", lunit, part+'a');
888 wdopenbusy = 0;
889 #endif
890 return(error);
891 }
892 }
893
894 #ifdef TIHBAD144
895 if (du->dk_flags & DKFL_BADSECT)
896 bad144intern(du);
897 #endif
898
899 /*
900 * Warn if a partion is opened
901 * that overlaps another partition which is open
902 * unless one is the "raw" partition (whole disk).
903 */
904 if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) {
905 int start, end;
906
907 pp = &du->dk_dd.d_partitions[part];
908 start = pp->p_offset;
909 end = pp->p_offset + pp->p_size;
910 for (pp = du->dk_dd.d_partitions;
911 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; pp++) {
912 if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end)
913 continue;
914 /*if (pp - du->dk_dd.d_partitions == RAWPART)
915 continue; */
916 if (pp - du->dk_dd.d_partitions == WDRAW)
917 continue;
918 if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions)))
919 log(LOG_WARNING,
920 "wd%d%c: overlaps open partition (%c)\n",
921 lunit, part + 'a',
922 pp - du->dk_dd.d_partitions + 'a');
923 }
924 }
925
926 if (part >= du->dk_dd.d_npartitions && part != WDRAW) {
927 #ifdef WDOPENLOCK
928 printf("[ENXIO wd%d%c]\n", lunit, part+'a');
929 wdopenbusy = 0;
930 #endif
931 return (ENXIO);
932 }
933
934 /* insure only one open at a time */
935 du->dk_openpart |= mask;
936 switch (fmt) {
937 case S_IFCHR:
938 du->dk_copenpart |= mask;
939 break;
940 case S_IFBLK:
941 du->dk_bopenpart |= mask;
942 break;
943 }
944 #ifdef WDOPENLOCK
945 printf("[Done wd%d%c]\n", lunit, part+'a');
946 wdopenbusy = 0;
947 #endif
948 return (0);
949 }
950
951 /*
952 * Implement operations other than read/write.
953 * Called from wdstart or wdintr during opens and formats.
954 * Uses finite-state-machine to track progress of operation in progress.
955 * Returns 0 if operation still in progress, 1 if completed.
956 */
957 static int
958 wdcontrol(register struct buf *bp)
959 {
960 register struct disk *du;
961 register unit, lunit;
962 unsigned char stat;
963 int s, ctrlr;
964 int wdc;
965
966 du = wddrives[wdunit(bp->b_dev)];
967 ctrlr = du->dk_ctrlr;
968 unit = du->dk_unit;
969 lunit = du->dk_lunit;
970 wdc = du->dk_port;
971
972 switch (du->dk_state) {
973 tryagainrecal:
974 case WANTOPEN: /* set SDH, step rate, do restore */
975 #ifdef WDDEBUG
976 printf("wd%d: recal ", lunit);
977 #endif
978 s = splbio(); /* not called from intr level ... */
979 wdgetctlr(unit, du);
980 #ifdef TIPCAT
981 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
982 ;
983 #endif
984 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
985 wdtab[ctrlr].b_active = 1;
986 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
987 #ifdef TIPCAT
988 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
989 ;
990 #endif
991 du->dk_state = RECAL;
992 splx(s);
993 return(0);
994 case RECAL:
995 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
996 if ((du->dk_flags & DKFL_QUIET) == 0) {
997 printf("wd%d: recal", du->dk_lunit);
998 printf(": status %b error %b\n",
999 stat, WDCS_BITS, inb(wdc+wd_error),
1000 WDERR_BITS);
1001 }
1002 if (++wdtab[ctrlr].b_errcnt < RETRIES)
1003 goto tryagainrecal;
1004 bp->b_error = ENXIO; /* XXX needs translation */
1005 goto badopen;
1006 }
1007
1008 /* some controllers require this ... */
1009 wdsetctlr(bp->b_dev, du);
1010
1011 wdtab[ctrlr].b_errcnt = 0;
1012 du->dk_state = OPEN;
1013 /*
1014 * The rest of the initialization can be done
1015 * by normal means.
1016 */
1017 return(1);
1018 default:
1019 panic("wdcontrol");
1020 }
1021 /* NOTREACHED */
1022
1023 badopen:
1024 if ((du->dk_flags & DKFL_QUIET) == 0)
1025 printf(": status %b error %b\n",
1026 stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS);
1027 bp->b_flags |= B_ERROR;
1028 return(1);
1029 }
1030
1031 /*
1032 * send a command and wait uninterruptibly until controller is finished.
1033 * return -1 if controller busy for too long, otherwise
1034 * return status. intended for brief controller commands at critical points.
1035 * assumes interrupts are blocked.
1036 */
1037 static int
1038 wdcommand(struct disk *du, int cmd)
1039 {
1040 int timeout = 1000000, stat, wdc;
1041
1042 DELAY(2000);
1043 /* controller ready for command? */
1044 wdc = du->dk_port;
1045 while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout > 0)
1046 timeout--;
1047 if (timeout <= 0)
1048 return(-1);
1049
1050 /* send command, await results */
1051 outb(wdc+wd_command, cmd);
1052 while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout > 0)
1053 timeout--;
1054 if (timeout <= 0)
1055 return(-1);
1056 if (cmd != WDCC_READP)
1057 return (stat);
1058
1059 /* is controller ready to return data? */
1060 while (((stat = inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0 &&
1061 timeout > 0)
1062 timeout--;
1063 if (timeout <= 0)
1064 return(-1);
1065 return (stat);
1066 }
1067
1068 /*
1069 * issue IDC to drive to tell it just what geometry it is to be.
1070 */
1071 static int
1072 wdsetctlr(dev_t dev, struct disk *du)
1073 {
1074 int stat, x, wdc;
1075
1076 /*
1077 printf("wd(%d,%d) C%dH%dS%d\n", du->dk_ctrlr, du->dk_unit,
1078 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors);
1079 */
1080
1081 DELAY(2000);
1082 x = splbio();
1083 wdc = du->dk_port;
1084 outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); /* TIH: was ...ders+1 */
1085 outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8); /* TIH: was ...ders+1 */
1086 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4) + du->dk_dd.d_ntracks-1);
1087 outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
1088 stat = wdcommand(du, WDCC_IDC);
1089
1090 #ifndef TIHMODS
1091 if (stat < 0)
1092 return(stat);
1093 #endif
1094 if (stat & WDCS_ERR)
1095 printf("wdsetctlr: status %b error %b\n",
1096 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
1097 splx(x);
1098 return(stat);
1099 }
1100
1101 /*
1102 * issue READP to drive to ask it what it is.
1103 */
1104 static int
1105 wdgetctlr(int u, struct disk *du)
1106 {
1107 int stat, x, i, wdc;
1108 char tb[DEV_BSIZE];
1109 struct wdparams *wp;
1110 long timeout = 5000000;
1111
1112 x = splbio(); /* not called from intr level ... */
1113 wdc = du->dk_port;
1114 #ifdef TIPCAT
1115 while ((inb(wdc+wd_status) & WDCS_READY) == 0 && timeout > 0)
1116 timeout--;
1117 if (timeout <= 0) {
1118 splx(x);
1119 return (-1);
1120 }
1121 #endif
1122 outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
1123 stat = wdcommand(du, WDCC_READP);
1124 #ifdef TIPCAT
1125 while ((inb(wdc+wd_status) & WDCS_READY) == 0 && timeout > 0)
1126 timeout--;
1127 if (timeout <= 0) {
1128 splx(x);
1129 return (-1);
1130 }
1131 #endif
1132
1133 #ifndef TIHMODS
1134 if (stat < 0)
1135 return(stat);
1136 #else
1137 if (stat < 0) {
1138 splx(x);
1139 return(stat);
1140 }
1141 #endif
1142
1143 if( (stat & WDCS_ERR) == 0) {
1144 /* obtain parameters */
1145 wp = &du->dk_params;
1146 insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short));
1147 bcopy(tb, wp, sizeof(struct wdparams));
1148
1149 /* shuffle string byte order */
1150 for (i=0; i < sizeof(wp->wdp_model); i+=2) {
1151 u_short *p;
1152 p = (u_short *) (wp->wdp_model + i);
1153 *p = ntohs(*p);
1154 }
1155
1156 strncpy(du->dk_dd.d_typename, "ESDI/IDE", sizeof du->dk_dd.d_typename);
1157 du->dk_dd.d_type = DTYPE_ESDI;
1158 bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
1159
1160 /* update disklabel given drive information */
1161 du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
1162 du->dk_dd.d_ntracks = wp->wdp_heads;
1163 du->dk_dd.d_nsectors = wp->wdp_sectors;
1164 du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
1165 du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl *
1166 wp->wdp_sectors;
1167 du->dk_dd.d_partitions[1].p_offset = 0;
1168 } else {
1169 /*
1170 * If WDCC_READP fails then we might have an old drive
1171 * so we try a seek to 0; if that passes then the
1172 * drive is there but it's OLD AND KRUSTY.
1173 */
1174 stat = wdcommand(du, WDCC_RESTORE | WD_STEP);
1175 if(stat & WDCS_ERR) {
1176 splx(x);
1177 return(inb(wdc+wd_error));
1178 }
1179
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
1187 /* XXX -- HOW DO WE FAKE THIS?? */
1188 du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
1189 du->dk_dd.d_ntracks = wp->wdp_heads;
1190 du->dk_dd.d_nsectors = wp->wdp_sectors;
1191 du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
1192 du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl *
1193 wp->wdp_sectors;
1194 du->dk_dd.d_partitions[1].p_offset = 0;
1195 }
1196
1197 #if 0
1198 printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config,
1199 wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors,
1200 wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);
1201 #endif
1202
1203 /* better ... */
1204 du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
1205
1206 /* XXX sometimes possibly needed */
1207 (void) inb(wdc+wd_status);
1208 #ifdef TIHMODS
1209 splx(x);
1210 #endif
1211 return (0);
1212 }
1213
1214
1215 /* ARGSUSED */
1216 int
1217 wdclose(dev_t dev, int flags, int fmt)
1218 {
1219 register struct disk *du;
1220 int part = wdpart(dev), mask = 1 << part;
1221
1222 du = wddrives[wdunit(dev)];
1223
1224 /* insure only one open at a time */
1225 du->dk_openpart &= ~mask;
1226 switch (fmt) {
1227 case S_IFCHR:
1228 du->dk_copenpart &= ~mask;
1229 break;
1230 case S_IFBLK:
1231 du->dk_bopenpart &= ~mask;
1232 break;
1233 }
1234 return(0);
1235 }
1236
1237 int
1238 wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
1239 {
1240 int lunit = wdunit(dev);
1241 register struct disk *du;
1242 int error = 0;
1243 struct uio auio;
1244 struct iovec aiov;
1245
1246 du = wddrives[lunit];
1247
1248 switch (cmd) {
1249 case DIOCSBAD:
1250 if ((flag & FWRITE) == 0)
1251 error = EBADF;
1252 else {
1253 du->dk_bad = *(struct dkbad *)addr;
1254 #ifdef TIHBAD144
1255 bad144intern(du);
1256 #endif
1257 }
1258 break;
1259
1260 case DIOCGDINFO:
1261 *(struct disklabel *)addr = du->dk_dd;
1262 break;
1263
1264 case DIOCGPART:
1265 ((struct partinfo *)addr)->disklab = &du->dk_dd;
1266 ((struct partinfo *)addr)->part =
1267 &du->dk_dd.d_partitions[wdpart(dev)];
1268 break;
1269
1270 case DIOCSDINFO:
1271 if ((flag & FWRITE) == 0)
1272 error = EBADF;
1273 else
1274 error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
1275 /*(du->dk_flags&DKFL_BSDLABEL) ? du->dk_openpart : */0,
1276 du->dk_dospartitions);
1277 if (error == 0) {
1278 du->dk_flags |= DKFL_BSDLABEL;
1279 wdsetctlr(dev, du);
1280 }
1281 break;
1282
1283 case DIOCWLABEL:
1284 du->dk_flags &= ~DKFL_WRITEPROT;
1285 if ((flag & FWRITE) == 0)
1286 error = EBADF;
1287 else
1288 du->dk_wlabel = *(int *)addr;
1289 break;
1290
1291 case DIOCWDINFO:
1292 du->dk_flags &= ~DKFL_WRITEPROT;
1293 if ((flag & FWRITE) == 0)
1294 error = EBADF;
1295 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
1296 /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0,
1297 du->dk_dospartitions)) == 0) {
1298 int wlab;
1299
1300 du->dk_flags |= DKFL_BSDLABEL;
1301 wdsetctlr(dev, du);
1302
1303 /* simulate opening partition 0 so write succeeds */
1304 du->dk_openpart |= (1 << 0); /* XXX */
1305 wlab = du->dk_wlabel;
1306 du->dk_wlabel = 1;
1307 error = writedisklabel(dev, wdstrategy,
1308 &du->dk_dd, du->dk_dospartitions);
1309 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
1310 du->dk_wlabel = wlab;
1311 }
1312 break;
1313
1314 #ifdef notyet
1315 case DIOCGDINFOP:
1316 *(struct disklabel **)addr = &(du->dk_dd);
1317 break;
1318
1319 case DIOCWFORMAT:
1320 if ((flag & FWRITE) == 0)
1321 error = EBADF;
1322 else {
1323 register struct format_op *fop;
1324
1325 fop = (struct format_op *)addr;
1326 aiov.iov_base = fop->df_buf;
1327 aiov.iov_len = fop->df_count;
1328 auio.uio_iov = &aiov;
1329 auio.uio_iovcnt = 1;
1330 auio.uio_resid = fop->df_count;
1331 auio.uio_segflg = 0;
1332 auio.uio_offset = fop->df_startblk * du->dk_dd.d_secsize;
1333 error = physio(wdformat, &rwdbuf[lunit], dev, B_WRITE,
1334 minphys, &auio);
1335 fop->df_count -= auio.uio_resid;
1336 fop->df_reg[0] = du->dk_status;
1337 fop->df_reg[1] = du->dk_error;
1338 }
1339 break;
1340 #endif
1341
1342 default:
1343 error = ENOTTY;
1344 break;
1345 }
1346 return (error);
1347 }
1348
1349 #ifdef B_FORMAT
1350 int
1351 wdformat(struct buf *bp)
1352 {
1353 bp->b_flags |= B_FORMAT;
1354 return (wdstrategy(bp));
1355 }
1356 #endif
1357
1358 int
1359 wdsize(dev_t dev)
1360 {
1361 int lunit = wdunit(dev), part = wdpart(dev);
1362 struct disk *du;
1363
1364 if (lunit >= NWD)
1365 return(-1);
1366
1367 if ((du = wddrives[lunit]) == 0)
1368 return (-1);
1369
1370 if (du->dk_state < OPEN || (du->dk_flags & DKFL_BSDLABEL) == 0) {
1371 int val;
1372 val = wdopen(makewddev(major(dev), lunit, WDRAW), FREAD, S_IFBLK, 0);
1373 if (val != 0)
1374 return (-1);
1375 }
1376
1377 if ((du->dk_flags & (DKFL_WRITEPROT|DKFL_BSDLABEL)) != DKFL_BSDLABEL)
1378 return (-1);
1379 else
1380 return((int)du->dk_dd.d_partitions[part].p_size);
1381 }
1382
1383 extern char *vmmap; /* poor name! */
1384
1385 /* dump core after a system crash */
1386 int
1387 wddump(dev_t dev)
1388 {
1389 register struct disk *du; /* disk unit to do the IO */
1390 long num; /* number of sectors to write */
1391 int ctrlr, lunit, part, wdc;
1392 long blkoff, blknum;
1393 long cylin, head, sector, stat;
1394 long secpertrk, secpercyl, nblocks, i;
1395 char *addr;
1396 extern int Maxmem;
1397 static wddoingadump = 0;
1398 extern caddr_t CADDR1;
1399
1400 addr = (char *) 0; /* starting address */
1401
1402 /* toss any characters present prior to dump */
1403 while (sgetc(1))
1404 ;
1405
1406 /* size of memory to dump */
1407 num = Maxmem;
1408 lunit = wdunit(dev); /* eventually support floppies? */
1409 part = wdpart(dev); /* file system */
1410 /* check for acceptable drive number */
1411 if (lunit >= NWD)
1412 return(ENXIO);
1413
1414 du = wddrives[lunit];
1415 if (du == 0)
1416 return(ENXIO);
1417 /* was it ever initialized ? */
1418 if (du->dk_state < OPEN)
1419 return (ENXIO);
1420 if (du->dk_flags & DKFL_WRITEPROT)
1421 return(ENXIO);
1422 wdc = du->dk_port;
1423 ctrlr = du->dk_ctrlr;
1424
1425 /* Convert to disk sectors */
1426 num = (u_long) num * NBPG / du->dk_dd.d_secsize;
1427
1428 /* check if controller active */
1429 /*if (wdtab[ctrlr].b_active)
1430 return(EFAULT); */
1431 if (wddoingadump)
1432 return(EFAULT);
1433
1434 secpertrk = du->dk_dd.d_nsectors;
1435 secpercyl = du->dk_dd.d_secpercyl;
1436 nblocks = du->dk_dd.d_partitions[part].p_size;
1437 blkoff = du->dk_dd.d_partitions[part].p_offset;
1438
1439 /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
1440 /* check transfer bounds against partition size */
1441 if ((dumplo < 0) || ((dumplo + num) > nblocks))
1442 return(EINVAL);
1443
1444 /* mark controller active for if we panic during the dump */
1445 /* wdtab[ctrlr].b_active = 1; */
1446 wddoingadump = 1;
1447 i = 200000000;
1448 while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0))
1449 ;
1450 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
1451 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
1452 while (inb(wdc+wd_status) & WDCS_BUSY)
1453 ;
1454
1455 /* some compaq controllers require this ... */
1456 wdsetctlr(dev, du);
1457
1458 blknum = dumplo + blkoff;
1459 while (num > 0) {
1460 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
1461
1462 /* compute disk address */
1463 cylin = blknum / secpercyl;
1464 head = (blknum % secpercyl) / secpertrk;
1465 sector = blknum % secpertrk;
1466
1467 #ifdef TIHBAD144
1468 if (du->dk_flags & DKFL_BADSECT) {
1469 long newblk;
1470 int i;
1471
1472 for (i = 0; du->dk_badsect[i] != -1; i++) {
1473 if (blknum < du->dk_badsect[i]) {
1474 break; /* sorted list, passed our block by */
1475 } else if (blknum == du->dk_badsect[i]) {
1476 newblk = du->dk_dd.d_secperunit -
1477 du->dk_dd.d_nsectors - i - 1;
1478 cylin = newblk / secpercyl;
1479 head = (newblk % secpercyl) / secpertrk;
1480 sector = newblk % secpertrk;
1481 /* found and repl; done scanning bad144 table */
1482 break;
1483 }
1484 }
1485 }
1486 #endif
1487 sector++; /* origin 1 */
1488
1489 /* select drive. */
1490 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
1491 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
1492 ;
1493
1494 /* transfer some blocks */
1495 outb(wdc+wd_sector, sector);
1496 outb(wdc+wd_seccnt,1);
1497 outb(wdc+wd_cyl_lo, cylin);
1498 outb(wdc+wd_cyl_hi, cylin >> 8);
1499 #ifdef notdef
1500 /* lets just talk about this first...*/
1501 pg ("sdh 0%o sector %d cyl %d addr 0x%x",
1502 inb(wdc+wd_sdh), inb(wdc+wd_sector),
1503 inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr);
1504 #endif
1505 outb(wdc+wd_command, WDCC_WRITE);
1506
1507 /* Ready to send data? */
1508 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
1509 ;
1510 if (inb(wdc+wd_status) & WDCS_ERR)
1511 return(EIO);
1512
1513 outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
1514
1515 if (inb(wdc+wd_status) & WDCS_ERR)
1516 return(EIO);
1517 /* Check data request (should be done). */
1518 if (inb(wdc+wd_status) & WDCS_DRQ)
1519 return(EIO);
1520
1521 /* wait for completion */
1522 for (i=200000000; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
1523 if (i < 0)
1524 return (EIO);
1525 }
1526
1527 /* error check the xfer */
1528 if (inb(wdc+wd_status) & WDCS_ERR)
1529 return(EIO);
1530
1531 if ((unsigned)addr % (1024*1024) == 0)
1532 printf("%d ", num/2048);
1533
1534 /* update block count */
1535 num--;
1536 blknum++;
1537 (int) addr += 512;
1538
1539 /* operator aborting dump? */
1540 if (sgetc(1))
1541 return(EINTR);
1542 }
1543 return(0);
1544 }
1545 #endif
1546
1547 #ifdef TIHBAD144
1548 /*
1549 * Internalize the bad sector table.
1550 */
1551 void
1552 bad144intern(struct disk *du)
1553 {
1554 int i;
1555 if (du->dk_flags & DKFL_BADSECT) {
1556 for (i = 0; i < 127; i++) {
1557 du->dk_badsect[i] = -1;
1558 }
1559
1560 for (i = 0; i < 126; i++) {
1561 if (du->dk_bad.bt_bad[i].bt_cyl == 0xffff) {
1562 break;
1563 } else {
1564 du->dk_badsect[i] =
1565 du->dk_bad.bt_bad[i].bt_cyl * du->dk_dd.d_secpercyl +
1566 (du->dk_bad.bt_bad[i].bt_trksec >> 8) * du->dk_dd.d_nsectors +
1567 (du->dk_bad.bt_bad[i].bt_trksec & 0x00ff);
1568 }
1569 }
1570 }
1571 }
1572 #endif
1573
1574 wdprint() {}
1575
1576
1577 /* this routine was adopted from the kernel sources */
1578 /* more efficient because b_cylin is not really as useful at this level */
1579 /* so I eliminate the processing, I believe that sorting the sectors */
1580 /* is adequate */
1581 void
1582 wddisksort(struct buf *dp, struct buf *bp)
1583 {
1584 register struct buf *ap;
1585
1586 /*
1587 * If nothing on the activity queue, then
1588 * we become the only thing.
1589 */
1590 ap = dp->b_actf;
1591 if(ap == NULL) {
1592 dp->b_actf = bp;
1593 dp->b_actl = bp;
1594 bp->av_forw = NULL;
1595 return;
1596 }
1597 while( ap->b_flags & B_XXX) {
1598 if( ap->av_forw == 0 || (ap->av_forw->b_flags & B_XXX) == 0)
1599 break;
1600 ap = ap->av_forw;
1601 }
1602 /*
1603 * If we lie after the first (currently active)
1604 * request, then we must locate the second request list
1605 * and add ourselves to it.
1606 */
1607 if (bp->b_blkno < ap->b_blkno) {
1608 while (ap->av_forw) {
1609 /*
1610 * Check for an ``inversion'' in the
1611 * normally ascending cylinder numbers,
1612 * indicating the start of the second request list.
1613 */
1614 if (ap->av_forw->b_blkno < ap->b_blkno) {
1615 /*
1616 * Search the second request list
1617 * for the first request at a larger
1618 * cylinder number. We go before that;
1619 * if there is no such request, we go at end.
1620 */
1621 do {
1622 if (bp->b_blkno < ap->av_forw->b_blkno)
1623 goto insert;
1624 ap = ap->av_forw;
1625 } while (ap->av_forw);
1626 goto insert; /* after last */
1627 }
1628 ap = ap->av_forw;
1629 }
1630 /*
1631 * No inversions... we will go after the last, and
1632 * be the first request in the second request list.
1633 */
1634 goto insert;
1635 }
1636 /*
1637 * Request is at/after the current request...
1638 * sort in the first request list.
1639 */
1640 while (ap->av_forw) {
1641 /*
1642 * We want to go after the current request
1643 * if there is an inversion after it (i.e. it is
1644 * the end of the first request list), or if
1645 * the next request is a larger cylinder than our request.
1646 */
1647 if (ap->av_forw->b_blkno < ap->b_blkno ||
1648 bp->b_blkno < ap->av_forw->b_blkno )
1649 goto insert;
1650 ap = ap->av_forw;
1651 }
1652 /*
1653 * Neither a second list nor a larger
1654 * request... we go at the end of the first list,
1655 * which is the same as the end of the whole schebang.
1656 */
1657 insert:
1658 bp->av_forw = ap->av_forw;
1659 ap->av_forw = bp;
1660 if (ap == dp->b_actl)
1661 dp->b_actl = bp;
1662 }
1663
1664