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