auto conf, bus control, conventions, interrupt control, per host config
[unix-history] / usr / src / sys / i386 / isa / wd.c
CommitLineData
cae41ec5
WN
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 * %sccs.include.386.c%
9 *
4686adac 10 * @(#)wd.c 5.8 (Berkeley) %G%
cae41ec5 11 */
88b2b5dc 12
cae41ec5
WN
13#include "wd.h"
14#if NWD > 0
cae41ec5
WN
15
16#include "param.h"
17#include "dkbad.h"
18#include "systm.h"
19#include "conf.h"
20#include "file.h"
21#include "dir.h"
22#include "user.h"
23#include "ioctl.h"
24#include "disk.h"
25#include "buf.h"
26#include "vm.h"
27#include "uio.h"
28#include "machine/pte.h"
4686adac 29#include "machine/isa/isa_device.h"
cae41ec5
WN
30#include "icu.h"
31#include "wdreg.h"
32#include "syslog.h"
33
34#define RETRIES 5 /* number of retries before giving up */
35#define MAXTRANSFER 256 /* max size of transfer in page clusters */
36
37#define WDUNIT(dev) ((minor(dev) & 070) >> 3)
38
39#define b_cylin b_resid /* cylinder number for doing IO to */
40 /* shares an entry in the buf struct */
41
42/*
43 * Drive states. Used for open and format operations.
44 * States < OPEN (> 0) are transient, during an open operation.
45 * OPENRAW is used for unlabeled disks, and for floppies, to inhibit
46 * bad-sector forwarding.
47 */
48#define RAWDISK 8 /* raw disk operation, no translation*/
49#define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */
50#define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless
51 of raw or cooked mode? */
52
53#define CLOSED 0 /* disk is closed. */
54 /* "cooked" disk states */
55#define WANTOPEN 1 /* open requested, not started */
56#define RECAL 2 /* doing restore */
57#define RDLABEL 3 /* reading pack label */
58#define RDBADTBL 4 /* reading bad-sector table */
59#define OPEN 5 /* done with open */
60
61#define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */
62#define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */
63#define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */
64
65
66/*
67 * The structure of a disk drive.
68 */
69struct disk {
70 struct disklabel dk_dd; /* device configuration data */
71 long dk_bc; /* byte count left */
72 short dk_skip; /* blocks already transferred */
73 char dk_unit; /* physical unit number */
74 char dk_sdh; /* sdh prototype */
75 char dk_state; /* control state */
76 u_char dk_status; /* copy of status reg. */
77 u_char dk_error; /* copy of error reg. */
78 short dk_open; /* open/closed refcnt */
79};
80
81/*
82 * This label is used as a default when initializing a new or raw disk.
83 * It really only lets us access the first track until we know more.
84 */
85struct disklabel dflt_sizes = {
86 DISKMAGIC, DTYPE_ST506,
87 {
88 512, /* sector size */
d405e6a8 89 36, /* # of sectors per track */
cae41ec5 90 15, /* # of tracks per cylinder */
d405e6a8
BJ
91 1224, /* # of cylinders per unit */
92 36*15, /* # of sectors per cylinder */
93 1224*15*36, /* # of sectors per unit */
cae41ec5
WN
94 0 /* write precomp cylinder (none) */
95 },
d405e6a8
BJ
96 21600, 0, /* A=root filesystem */
97 21600, 40,
98 660890, 0, /* C=whole disk */
99 216000, 80,
cae41ec5 100 0, 0,
cae41ec5
WN
101 0, 0,
102 0, 0,
d405e6a8 103 399600, 480
cae41ec5 104};
d405e6a8 105
cae41ec5
WN
106static struct dkbad dkbad[NWD];
107struct disk wddrives[NWD] = {0}; /* table of units */
108struct buf wdtab = {0};
109struct buf wdutab[NWD] = {0}; /* head of queue per drive */
110struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */
111long wdxfer[NWD] = {0}; /* count of transfers */
112int writeprotected[NWD] = { 0 };
113int wdprobe(), wdattach(), wdintr();
db8f0de7 114struct isa_driver wddriver = {
cae41ec5
WN
115 wdprobe, wdattach, "wd",
116};
117#include "dbg.h"
118\f
db8f0de7 119static wdc;
cae41ec5
WN
120/*
121 * Probe routine
122 */
123wdprobe(dvp)
db8f0de7 124 struct isa_device *dvp;
cae41ec5 125{
db8f0de7 126wdc = dvp->id_iobase;
cae41ec5
WN
127
128#ifdef lint
129 wdintr(0);
130#endif
131 outb(wdc+wd_error, 0x5a) ; /* error register not writable */
132 /*wdp->wd_cyl_hi = 0xff ;/* only two bits of cylhi are implemented */
133 outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */
134 if(inb(wdc+wd_error) != 0x5a /*&& wdp->wd_cyl_hi == 3*/
135 && inb(wdc+wd_cyl_lo) == 0xa5)
136 return(1) ;
137 return (0);
138}
139
140/*
141 * attach each drive if possible.
142 */
143wdattach(dvp)
db8f0de7 144 struct isa_device *dvp;
cae41ec5 145{
db8f0de7 146 int unit = dvp->id_unit;
cae41ec5 147
db8f0de7
BJ
148 outb(wdc+wd_ctlr,12);
149 DELAY(1000);
150 outb(wdc+wd_ctlr,8);
cae41ec5
WN
151}
152
153/* Read/write routine for a buffer. Finds the proper unit, range checks
154 * arguments, and schedules the transfer. Does not wait for the transfer
155 * to complete. Multi-page transfers are supported. All I/O requests must
156 * be a multiple of a sector in length.
157 */
158wdstrategy(bp)
159 register struct buf *bp; /* IO operation to perform */
160{
161 register struct buf *dp;
162 register struct disk *du; /* Disk unit to do the IO. */
163 long nblocks, cyloff, blknum;
164 int unit = WDUNIT(bp->b_dev), xunit = minor(bp->b_dev) & 7;
165 int s;
166
167 if ((unit >= NWD) || (bp->b_blkno < 0)) {
88b2b5dc 168 printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n",
cae41ec5 169 unit, bp->b_blkno, bp->b_bcount);
88b2b5dc 170 pg("wd:error in wdstrategy");
cae41ec5
WN
171 bp->b_flags |= B_ERROR;
172 goto bad;
173 }
174 if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) {
175 printf("wd%d: write protected\n", unit);
176 goto bad;
177 }
178 du = &wddrives[unit];
179 if (DISKSTATE(du->dk_state) != OPEN)
180 goto q;
181 /*
182 * Convert DEV_BSIZE "blocks" to sectors.
183 * Note: doing the conversions this way limits the partition size
184 * to about 8 million sectors (1-8 Gb).
185 */
186 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize;
187 if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.dk_secsize != 0) ||
88b2b5dc 188 bp->b_bcount >= MAXTRANSFER * CLBYTES) {
cae41ec5 189 bp->b_flags |= B_ERROR;
cae41ec5
WN
190 goto bad;
191 }
192 nblocks = du->dk_dd.dk_partition[xunit].nblocks;
193 cyloff = du->dk_dd.dk_partition[xunit].cyloff;
194 if (blknum + (bp->b_bcount / du->dk_dd.dk_secsize) > nblocks) {
cae41ec5
WN
195 if (blknum == nblocks)
196 bp->b_resid = bp->b_bcount;
197 else
198 bp->b_flags |= B_ERROR;
199 goto bad;
200 }
201 bp->b_cylin = blknum / du->dk_dd.dk_secpercyl + cyloff;
202q:
203 dp = &wdutab[unit];
d405e6a8 204 s = splhigh();
cae41ec5
WN
205 disksort(dp, bp);
206 if (dp->b_active == 0)
207 wdustart(du); /* start drive if idle */
208 if (wdtab.b_active == 0)
209 wdstart(s); /* start IO if controller idle */
210 splx(s);
211 return;
212
213bad:
214 bp->b_error = EINVAL;
215 biodone(bp);
216}
217
218/* Routine to queue a read or write command to the controller. The request is
219 * linked into the active list for the controller. If the controller is idle,
220 * the transfer is started.
221 */
222wdustart(du)
223 register struct disk *du;
224{
225 register struct buf *bp, *dp;
226
227 dp = &wdutab[du->dk_unit];
228 if (dp->b_active)
229 return;
230 bp = dp->b_actf;
231 if (bp == NULL)
232 return;
233 dp->b_forw = NULL;
234 if (wdtab.b_actf == NULL) /* link unit into active list */
235 wdtab.b_actf = dp;
236 else
237 wdtab.b_actl->b_forw = dp;
238 wdtab.b_actl = dp;
239 dp->b_active = 1; /* mark the drive as busy */
240}
241
242/*
243 * Controller startup routine. This does the calculation, and starts
244 * a single-sector read or write operation. Called to start a transfer,
245 * or from the interrupt routine to continue a multi-sector transfer.
246 * RESTRICTIONS:
247 * 1. The transfer length must be an exact multiple of the sector size.
248 */
249
d405e6a8
BJ
250static wd_sebyse;
251
cae41ec5
WN
252wdstart()
253{
254 register struct disk *du; /* disk unit for IO */
cae41ec5
WN
255 register struct buf *bp;
256 struct buf *dp;
257 register struct bt_bad *bt_ptr;
258 long blknum, pagcnt, cylin, head, sector;
259 long secpertrk, secpercyl, addr, i;
260 int minor_dev, unit, s;
261
262loop:
263 dp = wdtab.b_actf;
264 if (dp == NULL)
265 return;
266 bp = dp->b_actf;
267 if (bp == NULL) {
268 wdtab.b_actf = dp->b_forw;
269 goto loop;
270 }
271 unit = WDUNIT(bp->b_dev);
272 du = &wddrives[unit];
273 if (DISKSTATE(du->dk_state) <= RDLABEL) {
274 if (wdcontrol(bp)) {
275 dp->b_actf = bp->av_forw;
276 goto loop; /* done */
277 }
278 return;
279 }
280 minor_dev = minor(bp->b_dev) & 7;
281 secpertrk = du->dk_dd.dk_nsectors;
282 secpercyl = du->dk_dd.dk_secpercyl;
283 /*
284 * Convert DEV_BSIZE "blocks" to sectors.
285 */
286 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize
287 + du->dk_skip;
288#ifdef WDDEBUG
289 if (du->dk_skip == 0) {
290 dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit,
291 (bp->b_flags & B_READ) ? "read" : "write",
292 bp->b_bcount, blknum);
293 } else {
d405e6a8 294 dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts));
cae41ec5
WN
295 }
296#endif
297
298 addr = (int) bp->b_un.b_addr;
299 if(du->dk_skip==0) du->dk_bc = bp->b_bcount;
300 cylin = blknum / secpercyl;
301 head = (blknum % secpercyl) / secpertrk;
d405e6a8 302 sector = blknum % secpertrk;
cae41ec5
WN
303 if (DISKSTATE(du->dk_state) == OPEN)
304 cylin += du->dk_dd.dk_partition[minor_dev].cyloff;
305
cae41ec5
WN
306 /*
307 * See if the current block is in the bad block list.
308 * (If we have one, and not formatting.)
309 */
d405e6a8 310 if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse)
cae41ec5
WN
311 for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
312 if (bt_ptr->bt_cyl > cylin)
313 /* Sorted list, and we passed our cylinder. quit. */
314 break;
315 if (bt_ptr->bt_cyl == cylin &&
316 bt_ptr->bt_trksec == (head << 8) + sector) {
317 /*
318 * Found bad block. Calculate new block addr.
319 * This starts at the end of the disk (skip the
320 * last track which is used for the bad block list),
321 * and works backwards to the front of the disk.
322 */
323#ifdef WDDEBUG
324 dprintf(DDSK,"--- badblock code -> Old = %d; ",
325 blknum);
326#endif
327 blknum = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors
328 - (bt_ptr - dkbad[unit].bt_bad) - 1;
329 cylin = blknum / secpercyl;
330 head = (blknum % secpercyl) / secpertrk;
331 sector = blknum % secpertrk;
332#ifdef WDDEBUG
333 dprintf(DDSK, "new = %d\n", blknum);
334#endif
335 break;
336 }
337 }
d405e6a8 338 sector += 1; /* sectors begin with 1, not 0 */
cae41ec5
WN
339
340 wdtab.b_active = 1; /* mark controller active */
341
d405e6a8
BJ
342 if(du->dk_skip==0 || wd_sebyse) {
343 if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512;
344 while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ;
345 /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/
cae41ec5
WN
346 outb(wdc+wd_precomp, 0xff);
347 /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/
348 /*if (bp->b_flags & B_FORMAT) {
349 wr(wdc+wd_sector, du->dk_dd.dk_gap3);
350 wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
351 } else {*/
d405e6a8
BJ
352 if(wd_sebyse)
353 outb(wdc+wd_seccnt, 1);
354 else
355 outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512));
cae41ec5
WN
356 outb(wdc+wd_sector, sector);
357
358 outb(wdc+wd_cyl_lo, cylin);
359 outb(wdc+wd_cyl_hi, cylin >> 8);
360
361 /* Set up the SDH register (select drive). */
362 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
edf6b89d 363 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
cae41ec5
WN
364
365 /*if (bp->b_flags & B_FORMAT)
366 wr(wdc+wd_command, WDCC_FORMAT);
367 else*/
368 outb(wdc+wd_command,
369 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
370#ifdef WDDEBUG
d405e6a8
BJ
371 dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n",
372 sector, cylin, head, addr, inb(wdc+wd_altsts));
cae41ec5 373#endif
d405e6a8 374}
cae41ec5
WN
375
376 /* If this is a read operation, just go away until it's done. */
377 if (bp->b_flags & B_READ) return;
378
379 /* Ready to send data? */
edf6b89d 380 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
cae41ec5
WN
381 nulldev(); /* So compiler won't optimize out */
382
383 /* ASSUMES CONTIGUOUS MEMORY */
d405e6a8 384 outsw (wdc+wd_data, addr+du->dk_skip*512, 256);
cae41ec5
WN
385 du->dk_bc -= 512;
386}
387
388/*
389 * these are globally defined so they can be found
390 * by the debugger easily in the case of a system crash
391 */
392daddr_t wd_errsector;
393daddr_t wd_errbn;
394unsigned char wd_errstat;
395
396/* Interrupt routine for the controller. Acknowledge the interrupt, check for
397 * errors on the current operation, mark it done if necessary, and start
398 * the next request. Also check for a partially done transfer, and
399 * continue with the next chunk if so.
400 */
401wdintr()
402{
403 register struct disk *du;
cae41ec5
WN
404 register struct buf *bp, *dp;
405 int status;
406 char partch ;
d405e6a8
BJ
407static shit[32];
408static wd_haderror;
cae41ec5
WN
409
410 /* Shouldn't need this, but it may be a slow controller. */
d405e6a8 411 while ((status = inb(wdc+wd_status)) & WDCS_BUSY)
cae41ec5
WN
412 nulldev();
413 if (!wdtab.b_active) {
414 printf("wd: extra interrupt\n");
415 return;
416 }
417
ceab7d1a 418#ifdef WDDEBUG
cae41ec5
WN
419 dprintf(DDSK,"I ");
420#endif
421 dp = wdtab.b_actf;
422 bp = dp->b_actf;
423 du = &wddrives[WDUNIT(bp->b_dev)];
424 partch = "abcdefgh"[minor(bp->b_dev)&7] ;
425 if (DISKSTATE(du->dk_state) <= RDLABEL) {
426 if (wdcontrol(bp))
427 goto done;
428 return;
429 }
430 if (status & (WDCS_ERR | WDCS_ECCCOR)) {
d405e6a8 431 wd_errstat = inb(wdc+wd_error); /* save error status */
cae41ec5 432#ifdef WDDEBUG
d405e6a8 433 printf("status %x error %x\n", status, wd_errstat);
cae41ec5 434#endif
d405e6a8
BJ
435 if(wd_sebyse == 0) {
436 wd_haderror = 1;
437 goto outt;
438 }
cae41ec5
WN
439 /*if (bp->b_flags & B_FORMAT) {
440 du->dk_status = status;
441 du->dk_error = wdp->wd_error;
442 bp->b_flags |= B_ERROR;
443 goto done;
444 }*/
445
cae41ec5
WN
446 wd_errsector = (bp->b_cylin * du->dk_dd.dk_secpercyl) +
447 (((unsigned long) bp->b_blkno * DEV_BSIZE /
448 du->dk_dd.dk_secsize) % du->dk_dd.dk_secpercyl) +
449 du->dk_skip;
450 wd_errbn = bp->b_blkno
451 + du->dk_skip * du->dk_dd.dk_secsize / DEV_BSIZE ;
452 if (status & WDCS_ERR) {
d405e6a8 453 if (++wdtab.b_errcnt < RETRIES) {
cae41ec5 454 wdtab.b_active = 0;
d405e6a8
BJ
455 /*while ((inb(wdc+wd_status) & WDCS_DRQ))
456 insw(wdc+wd_data, &shit, sizeof(shit)/2);*/
457 } else {
cae41ec5
WN
458 printf("wd%d%c: ", du->dk_unit, partch);
459 printf(
460 "hard %s error, sn %d bn %d status %b error %b\n",
461 (bp->b_flags & B_READ)? "read":"write",
462 wd_errsector, wd_errbn, status, WDCS_BITS,
463 wd_errstat, WDERR_BITS);
464 bp->b_flags |= B_ERROR; /* flag the error */
465 }
466 } else
467 log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n",
468 du->dk_unit, partch, wd_errsector,
469 wd_errbn);
470 }
d405e6a8 471outt:
cae41ec5
WN
472
473 /*
474 * If this was a successful read operation, fetch the data.
475 */
476 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
477 int chk, dummy;
478
d405e6a8 479 chk = min(256,du->dk_bc/2);
cae41ec5
WN
480 /* Ready to receive data? */
481 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
482 nulldev();
483
484/*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/
485 insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk);
88b2b5dc 486 du->dk_bc -= 2*chk;
d405e6a8 487 while (chk++ < 256) insw (wdc+wd_data,&dummy,1);
cae41ec5
WN
488 }
489
490 wdxfer[du->dk_unit]++;
491 if (wdtab.b_active) {
492 if ((bp->b_flags & B_ERROR) == 0) {
493 du->dk_skip++; /* Add to successful sectors. */
494 if (wdtab.b_errcnt) {
495 log(LOG_WARNING, "wd%d%c: ",
496 du->dk_unit, partch);
497 log(LOG_WARNING,
498 "soft %s error, sn %d bn %d error %b retries %d\n",
499 (bp->b_flags & B_READ) ? "read" : "write",
500 wd_errsector, wd_errbn, wd_errstat,
501 WDERR_BITS, wdtab.b_errcnt);
502 }
503 wdtab.b_errcnt = 0;
504
505 /* see if more to transfer */
88b2b5dc 506 /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/
d405e6a8 507 if (du->dk_bc > 0 && wd_haderror == 0) {
cae41ec5
WN
508 wdstart();
509 return; /* next chunk is started */
d405e6a8
BJ
510 } else if (wd_haderror && wd_sebyse == 0) {
511 du->dk_skip = 0;
512 wd_haderror = 0;
513 wd_sebyse = 1;
514 wdstart();
515 return; /* redo xfer sector by sector */
cae41ec5
WN
516 }
517 }
518
519done:
d405e6a8 520 wd_sebyse = 0;
cae41ec5
WN
521 /* done with this transfer, with or without error */
522 wdtab.b_actf = dp->b_forw;
523 wdtab.b_errcnt = 0;
524 du->dk_skip = 0;
525 dp->b_active = 0;
526 dp->b_actf = bp->av_forw;
527 dp->b_errcnt = 0;
528 bp->b_resid = 0;
529 biodone(bp);
530 }
531 wdtab.b_active = 0;
532 if (dp->b_actf)
533 wdustart(du); /* requeue disk if more io to do */
534 if (wdtab.b_actf)
535 wdstart(); /* start IO on next drive */
536}
537
538/*
539 * Initialize a drive.
540 */
541wdopen(dev, flags)
542 dev_t dev;
543 int flags;
544{
545 register unsigned int unit;
546 register struct buf *bp;
547 register struct disk *du;
548 struct dkbad *db;
549 int i, error = 0;
550
551 unit = WDUNIT(dev);
cae41ec5
WN
552 if (unit >= NWD) return (ENXIO) ;
553 du = &wddrives[unit];
554 if (du->dk_open){
555 du->dk_open++ ;
556 return(0); /* already is open, don't mess with it */
557 }
558#ifdef THE_BUG
559 if (du->dk_state && DISKSTATE(du->dk_state) <= OPEN)
560 return(0);
561#endif
562 du->dk_unit = unit;
563 wdutab[unit].b_actf = NULL;
564 /*if (flags & O_NDELAY)
565 du->dk_state = WANTOPENRAW;
566 else*/
567 du->dk_state = WANTOPEN;
568 /*
569 * Use the default sizes until we've read the label,
570 * or longer if there isn't one there.
571 */
572 du->dk_dd = dflt_sizes;
573
574 /*
575 * Recal, read of disk label will be done in wdcontrol
576 * during first read operation.
577 */
578 bp = geteblk(512);
ceab7d1a 579 bp->b_dev = dev & 0xff00;
cae41ec5
WN
580 bp->b_blkno = bp->b_bcount = 0;
581 bp->b_flags = B_READ;
582 wdstrategy(bp);
583 biowait(bp);
584 if (bp->b_flags & B_ERROR) {
585 u.u_error = 0; /* XXX */
586 error = ENXIO;
587 du->dk_state = CLOSED;
588 goto done;
589 }
590 if (du->dk_state == OPENRAW) {
591 du->dk_state = OPENRAW;
592 goto done;
593 }
cae41ec5
WN
594 /*
595 * Read bad sector table into memory.
596 */
597 i = 0;
598 do {
599 u.u_error = 0; /* XXX */
600 bp->b_flags = B_BUSY | B_READ;
601 bp->b_blkno = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors
602 + i;
603 if (du->dk_dd.dk_secsize > DEV_BSIZE)
604 bp->b_blkno *= du->dk_dd.dk_secsize / DEV_BSIZE;
605 else
606 bp->b_blkno /= DEV_BSIZE / du->dk_dd.dk_secsize;
607 bp->b_bcount = du->dk_dd.dk_secsize;
608 bp->b_cylin = du->dk_dd.dk_ncylinders - 1;
609 wdstrategy(bp);
610 biowait(bp);
611 } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
612 i < du->dk_dd.dk_nsectors);
613 db = (struct dkbad *)(bp->b_un.b_addr);
d405e6a8 614#define DKBAD_MAGIC 0x4321
cae41ec5
WN
615 if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
616 db->bt_flag == DKBAD_MAGIC) {
617 dkbad[unit] = *db;
618 du->dk_state = OPEN;
619 } else {
620 printf("wd%d: %s bad-sector file\n", unit,
621 (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
622 u.u_error = 0; /* XXX */
623 /*error = ENXIO ;*/
624 du->dk_state = OPENRAW;
625 }
cae41ec5
WN
626done:
627 bp->b_flags = B_INVAL | B_AGE;
628 brelse(bp);
629 if (error == 0)
630 du->dk_open = 1;
631 return (error);
632}
633
634/*
635 * Implement operations other than read/write.
636 * Called from wdstart or wdintr during opens and formats.
637 * Uses finite-state-machine to track progress of operation in progress.
638 * Returns 0 if operation still in progress, 1 if completed.
639 */
640wdcontrol(bp)
641 register struct buf *bp;
642{
643 register struct disk *du;
cae41ec5
WN
644 register unit;
645 unsigned char stat;
646 int s, cnt;
647 extern int bootdev, cyloffset;
648
cae41ec5
WN
649 du = &wddrives[WDUNIT(bp->b_dev)];
650 unit = du->dk_unit;
651 switch (DISKSTATE(du->dk_state)) {
652
653 tryagainrecal:
654 case WANTOPEN: /* set SDH, step rate, do restore */
655#ifdef WDDEBUG
656 dprintf(DDSK,"wd%d: recal ", unit);
657#endif
658 s = splbio(); /* not called from intr level ... */
db8f0de7
BJ
659
660#ifdef notdef
661 /* some compaq controllers require this ... */
662 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)
663 + du->dk_dd.dk_ntracks-1);
664 outb(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
665 outb(wdc+wd_command, 0x91);
666 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev();
667#endif
668
cae41ec5
WN
669 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
670 wdtab.b_active = 1;
671 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
672 du->dk_state++;
673 splx(s);
674 return(0);
675
676 case RECAL:
edf6b89d 677 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
cae41ec5
WN
678 printf("wd%d: recal", du->dk_unit);
679 if (unit == 0) {
680 printf(": status %b error %b\n",
681 stat, WDCS_BITS,
682 inb(wdc+wd_error), WDERR_BITS);
683 if (++wdtab.b_errcnt < RETRIES)
684 goto tryagainrecal;
685 }
686 goto badopen;
687 }
688 wdtab.b_errcnt = 0;
689 if (ISRAWSTATE(du->dk_state)) {
690 du->dk_state = OPENRAW;
691 return(1);
692 }
693retry:
694#ifdef WDDEBUG
695 dprintf(DDSK,"rdlabel ");
696#endif
db8f0de7 697if( cyloffset < 0 || cyloffset > 2048) cyloffset=0;
cae41ec5
WN
698 /*
699 * Read in sector 0 to get the pack label and geometry.
700 */
701 outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */
702 outb(wdc+wd_seccnt, 1);
703 outb(wdc+wd_sector, 1);
704 /*if (bp->b_dev == bootdev) {
705 (wdc+wd_cyl_lo = cyloffset & 0xff;
706 (wdc+wd_cyl_hi = cyloffset >> 8;
707 } else {
708 (wdc+wd_cyl_lo = 0;
709 (wdc+wd_cyl_hi = 0;
710 }*/
711 outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
712 outb(wdc+wd_cyl_hi, (cyloffset >> 8));
713 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
714 outb(wdc+wd_command, WDCC_READ);
715 du->dk_state = RDLABEL;
716 return(0);
717
718 case RDLABEL:
719 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
720 if (++wdtab.b_errcnt < RETRIES)
721 goto retry;
722 printf("wd%d: read label", unit);
723 goto badopen;
724 }
725
726 insw(wdc+wd_data, bp->b_un.b_addr, 256);
727
728 if (((struct disklabel *)
729 (bp->b_un.b_addr + LABELOFFSET))->dk_magic == DISKMAGIC) {
730 du->dk_dd =
731 * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET);
732 } else {
733 printf("wd%d: bad disk label\n", du->dk_unit);
734 du->dk_state = OPENRAW;
735 }
db8f0de7
BJ
736
737 s = splbio(); /* not called from intr level ... */
738 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev();
739 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)
740 + du->dk_dd.dk_ntracks-1);
741 outb(wdc+wd_cyl_lo, du->dk_dd.dk_ncylinders);
742 outb(wdc+wd_cyl_hi, du->dk_dd.dk_ncylinders>>8);
743 outb(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
744 outb(wdc+wd_command, 0x91);
745 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev();
746 outb(wdc+wd_seccnt, 0);
747 splx(s);
748
cae41ec5
WN
749 if (du->dk_state == RDLABEL)
750 du->dk_state = RDBADTBL;
751 /*
752 * The rest of the initialization can be done
753 * by normal means.
754 */
755 return(1);
756
757 default:
758 panic("wdcontrol %x", du->dk_state );
759 }
760 /* NOTREACHED */
761
762badopen:
763 printf(": status %b error %b\n",
764 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
765 du->dk_state = OPENRAW;
766 return(1);
767}
768
769wdclose(dev)
770 dev_t dev;
771{ struct disk *du;
772
773 du = &wddrives[WDUNIT(dev)];
774 du->dk_open-- ;
775 /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
776}
777
778wdioctl(dev,cmd,addr,flag)
779 dev_t dev;
780 caddr_t addr;
781{
782 int unit = WDUNIT(dev);
783 register struct disk *du;
784 int error = 0;
785 struct uio auio;
786 struct iovec aiov;
787 /*int wdformat();*/
788
789 du = &wddrives[unit];
790
791 switch (cmd) {
792
793 case DIOCGDINFO:
794 *(struct disklabel *)addr = du->dk_dd;
795 break;
796
797 case DIOCGDINFOP:
798 *(struct disklabel **)addr = &(du->dk_dd);
799 break;
800
801#ifdef notyet
802 case DIOCWFORMAT:
803 if ((flag & FWRITE) == 0)
804 error = EBADF;
805 else {
806 register struct format_op *fop;
807
808 fop = (struct format_op *)addr;
809 aiov.iov_base = fop->df_buf;
810 aiov.iov_len = fop->df_count;
811 auio.uio_iov = &aiov;
812 auio.uio_iovcnt = 1;
813 auio.uio_resid = fop->df_count;
814 auio.uio_segflg = 0;
815 auio.uio_offset =
816 fop->df_startblk * du->dk_dd.dk_secsize;
817 error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
818 minphys, &auio);
819 fop->df_count -= auio.uio_resid;
820 fop->df_reg[0] = du->dk_status;
821 fop->df_reg[1] = du->dk_error;
822 }
823 break;
824#endif
825
826 default:
827 error = ENOTTY;
828 break;
829 }
830 return (error);
831}
832
833/*wdformat(bp)
834 struct buf *bp;
835{
836
837 bp->b_flags |= B_FORMAT;
838 return (wdstrategy(bp));
839}*/
840
841/*
842 * Routines to do raw IO for a unit.
843 */
844wdread(dev, uio) /* character read routine */
845 dev_t dev;
846 struct uio *uio;
847{
848 int unit = WDUNIT(dev) ;
849
850 if (unit >= NWD) return(ENXIO);
851 return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio));
852}
853
854
855wdwrite(dev, uio) /* character write routine */
856 dev_t dev;
857 struct uio *uio;
858{
859 int unit = WDUNIT(dev) ;
860
861 if (unit >= NWD) return(ENXIO);
862 return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio));
863}
864
865wdsize(dev)
866 dev_t dev;
867{
868 register unit = WDUNIT(dev) ;
869 register xunit = minor(dev) & 07;
870 register struct disk *du;
871 register val ;
872
d405e6a8 873 return(21600);
cae41ec5
WN
874#ifdef notdef
875 if (unit >= NWD) return(-1);
876 if (wddrives[unit].dk_state == 0) /*{
877 val = wdopen (dev, 0) ;
878 if (val < 0) return (val) ;
879 }*/ return (-1) ;
880 du = &wddrives[unit];
881 return((int)((u_long)du->dk_dd.dk_partition[xunit].nblocks *
882 du->dk_dd.dk_secsize / 512));
883#endif
884}
885
886wddump(dev) /* dump core after a system crash */
887 dev_t dev;
888{
889#ifdef notyet
890 register struct disk *du; /* disk unit to do the IO */
891 register struct wd1010 *wdp = (struct wd1010 *) VA_WD;
892 register struct bt_bad *bt_ptr;
893 long num; /* number of sectors to write */
894 int unit, xunit;
895 long cyloff, blknum, blkcnt;
896 long cylin, head, sector;
897 long secpertrk, secpercyl, nblocks, i;
898 register char *addr;
899 char *end;
900 extern int dumplo, totalclusters;
901 static wddoingadump = 0 ;
902
903 addr = (char *) PA_RAM; /* starting address */
904 /* size of memory to dump */
905 num = totalclusters * CLSIZE - PA_RAM / PGSIZE;
906 unit = WDUNIT(dev) ; /* eventually support floppies? */
907 xunit = minor(dev) & 7; /* file system */
908 /* check for acceptable drive number */
909 if (unit >= NWD) return(ENXIO);
910
911 du = &wddrives[unit];
912 /* was it ever initialized ? */
913 if (du->dk_state < OPEN) return (ENXIO) ;
914
915 /* Convert to disk sectors */
916 num = (u_long) num * PGSIZE / du->dk_dd.dk_secsize;
917
918 /* check if controller active */
919 /*if (wdtab.b_active) return(EFAULT); */
920 if (wddoingadump) return(EFAULT);
921
922 secpertrk = du->dk_dd.dk_nsectors;
923 secpercyl = du->dk_dd.dk_secpercyl;
924 nblocks = du->dk_dd.dk_partition[xunit].nblocks;
925 cyloff = du->dk_dd.dk_partition[xunit].cyloff;
926
927 /* check transfer bounds against partition size */
928 if ((dumplo < 0) || ((dumplo + num) >= nblocks))
929 return(EINVAL);
930
931 /*wdtab.b_active = 1; /* mark controller active for if we
932 panic during the dump */
933 wddoingadump = 1 ; i = 100000 ;
934 while ((wdp->wd_status & WDCS_BUSY) && (i-- > 0)) nulldev() ;
935 inb(wdc+wd_sdh = du->dk_sdh ;
936 inb(wdc+wd_command = WDCC_RESTORE | WD_STEP;
937 while (inb(wdc+wd_status & WDCS_BUSY) nulldev() ;
938
939 blknum = dumplo;
940 while (num > 0) {
941#ifdef notdef
942 if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
943 if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
944 blkcnt = secpercyl - (blknum % secpercyl);
945 /* keep transfer within current cylinder */
946#endif
947
948 /* compute disk address */
949 cylin = blknum / secpercyl;
950 head = (blknum % secpercyl) / secpertrk;
951 sector = blknum % secpertrk;
952 sector++; /* origin 1 */
88b2b5dc 953 cylin += cyloff;
cae41ec5
WN
954
955 /*
956 * See if the current block is in the bad block list.
957 * (If we have one.)
958 */
959 for (bt_ptr = dkbad[unit].bt_bad;
960 bt_ptr->bt_cyl != -1; bt_ptr++) {
961 if (bt_ptr->bt_cyl > cylin)
962 /* Sorted list, and we passed our cylinder.
963 quit. */
964 break;
965 if (bt_ptr->bt_cyl == cylin &&
966 bt_ptr->bt_trksec == (head << 8) + sector) {
967 /*
968 * Found bad block. Calculate new block addr.
969 * This starts at the end of the disk (skip the
970 * last track which is used for the bad block list),
971 * and works backwards to the front of the disk.
972 */
973 blknum = (du->dk_dd.dk_secperunit)
974 - du->dk_dd.dk_nsectors
975 - (bt_ptr - dkbad[unit].bt_bad) - 1;
976 cylin = blknum / secpercyl;
977 head = (blknum % secpercyl) / secpertrk;
978 sector = blknum % secpertrk;
979 break;
980 }
981
982 /* select drive. */
983 inb(wdc+wd_sdh = du->dk_sdh | (head&07);
984 while ((inb(wdc+wd_status & WDCS_READY) == 0) nulldev();
985
986 /* transfer some blocks */
987 inb(wdc+wd_sector = sector;
988 inb(wdc+wd_seccnt = 1;
989 inb(wdc+wd_cyl_lo = cylin;
990 if (du->dk_dd.dk_ntracks > 8) {
991 if (head > 7)
992 inb(wdc+wd_precomp = 0; /* set 3rd head bit */
993 else
994 inb(wdc+wd_precomp = 0xff; /* set 3rd head bit */
995 }
996 inb(wdc+wd_cyl_hi = cylin >> 8;
997#ifdef notdef
998 /* lets just talk about this first...*/
999 printf ("sdh 0%o sector %d cyl %d addr 0x%x\n",
1000 wdp->wd_sdh, wdp->wd_sector,
1001 wdp->wd_cyl_hi*256+wdp->wd_cyl_lo, addr) ;
1002 for (i=10000; i > 0 ; i--)
1003 ;
1004 continue;
1005#endif
1006 inb(wdc+wd_command = WDCC_WRITE;
1007
1008 /* Ready to send data? */
1009 while ((inb(wdc+wd_status & WDCS_DRQ) == 0) nulldev();
1010 if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
1011
1012 end = (char *)addr + du->dk_dd.dk_secsize;
1013 for (; addr < end; addr += 8) {
1014 wdp->wd_data = addr[0];
1015 wdp->wd_data = addr[1];
1016 wdp->wd_data = addr[2];
1017 wdp->wd_data = addr[3];
1018 wdp->wd_data = addr[4];
1019 wdp->wd_data = addr[5];
1020 wdp->wd_data = addr[6];
1021 wdp->wd_data = addr[7];
1022 }
1023 if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
1024 /* Check data request (should be done). */
1025 if (inb(wdc+wd_status & WDCS_DRQ) return(EIO) ;
1026
1027 /* wait for completion */
1028 for ( i = 1000000 ; inb(wdc+wd_status & WDCS_BUSY ; i--) {
1029 if (i < 0) return (EIO) ;
1030 nulldev () ;
1031 }
1032 /* error check the xfer */
1033 if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
1034 /* update block count */
1035 num--;
1036 blknum++ ;
1037#ifdef WDDEBUG
1038if (num % 100 == 0) printf(".") ;
1039#endif
1040 }
1041 return(0);
1042#endif
1043}
1044#endif