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