Commit | Line | Data |
---|---|---|
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 | */ | |
69 | struct 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 | */ | |
85 | struct 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 |
106 | static struct dkbad dkbad[NWD]; |
107 | struct disk wddrives[NWD] = {0}; /* table of units */ | |
108 | struct buf wdtab = {0}; | |
109 | struct buf wdutab[NWD] = {0}; /* head of queue per drive */ | |
110 | struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */ | |
111 | long wdxfer[NWD] = {0}; /* count of transfers */ | |
112 | int writeprotected[NWD] = { 0 }; | |
113 | int wdprobe(), wdattach(), wdintr(); | |
db8f0de7 | 114 | struct isa_driver wddriver = { |
cae41ec5 WN |
115 | wdprobe, wdattach, "wd", |
116 | }; | |
117 | #include "dbg.h" | |
118 | \f | |
db8f0de7 | 119 | static wdc; |
cae41ec5 WN |
120 | /* |
121 | * Probe routine | |
122 | */ | |
123 | wdprobe(dvp) | |
db8f0de7 | 124 | struct isa_device *dvp; |
cae41ec5 | 125 | { |
db8f0de7 | 126 | wdc = 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 | */ | |
143 | wdattach(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 | */ | |
158 | wdstrategy(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; | |
202 | q: | |
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 | ||
213 | bad: | |
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 | */ | |
222 | wdustart(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 |
250 | static wd_sebyse; |
251 | ||
cae41ec5 WN |
252 | wdstart() |
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 | ||
262 | loop: | |
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 | */ | |
392 | daddr_t wd_errsector; | |
393 | daddr_t wd_errbn; | |
394 | unsigned 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 | */ | |
401 | wdintr() | |
402 | { | |
403 | register struct disk *du; | |
cae41ec5 WN |
404 | register struct buf *bp, *dp; |
405 | int status; | |
406 | char partch ; | |
d405e6a8 BJ |
407 | static shit[32]; |
408 | static 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 | 471 | outt: |
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 | ||
519 | done: | |
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 | */ | |
541 | wdopen(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 |
626 | done: |
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 | */ | |
640 | wdcontrol(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 | } | |
693 | retry: | |
694 | #ifdef WDDEBUG | |
695 | dprintf(DDSK,"rdlabel "); | |
696 | #endif | |
db8f0de7 | 697 | if( 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 | ||
762 | badopen: | |
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 | ||
769 | wdclose(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 | ||
778 | wdioctl(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 | */ | |
844 | wdread(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 | ||
855 | wdwrite(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 | ||
865 | wdsize(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 | ||
886 | wddump(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 | |
1038 | if (num % 100 == 0) printf(".") ; | |
1039 | #endif | |
1040 | } | |
1041 | return(0); | |
1042 | #endif | |
1043 | } | |
1044 | #endif |