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 | * | |
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 | */ | |
70 | struct 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 | */ | |
86 | struct 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 |
107 | static struct dkbad dkbad[NWD]; |
108 | struct disk wddrives[NWD] = {0}; /* table of units */ | |
109 | struct buf wdtab = {0}; | |
110 | struct buf wdutab[NWD] = {0}; /* head of queue per drive */ | |
111 | struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */ | |
112 | long wdxfer[NWD] = {0}; /* count of transfers */ | |
113 | int writeprotected[NWD] = { 0 }; | |
114 | int wdprobe(), wdattach(), wdintr(); | |
115 | struct driver wddriver = { | |
116 | wdprobe, wdattach, "wd", | |
117 | }; | |
118 | #include "dbg.h" | |
119 | \f | |
120 | /* | |
121 | * Probe routine | |
122 | */ | |
123 | wdprobe(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 | */ | |
143 | wdattach(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 | */ | |
157 | wdstrategy(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; | |
201 | q: | |
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 | ||
212 | bad: | |
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 | */ | |
221 | wdustart(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 |
249 | static wd_sebyse; |
250 | ||
cae41ec5 WN |
251 | wdstart() |
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 | ||
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; | |
404 | register wdc = IO_WD0; /*XXX*/ | |
405 | register struct buf *bp, *dp; | |
406 | int status; | |
407 | char partch ; | |
d405e6a8 BJ |
408 | static shit[32]; |
409 | static 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 |
433 | outb(wdc+wd_command, WDCC_RESTORE); |
434 | while (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 | 474 | outt: |
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 | ||
522 | done: | |
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 | */ | |
544 | wdopen(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 |
629 | done: |
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 | */ | |
643 | wdcontrol(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 | } | |
687 | retry: | |
688 | #ifdef WDDEBUG | |
689 | dprintf(DDSK,"rdlabel "); | |
690 | #endif | |
d405e6a8 | 691 | cyloffset=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 | ||
743 | badopen: | |
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 | ||
750 | wdclose(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 | ||
759 | wdioctl(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 | */ | |
825 | wdread(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 | ||
836 | wdwrite(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 | ||
846 | wdsize(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 | ||
867 | wddump(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 | |
1019 | if (num % 100 == 0) printf(".") ; | |
1020 | #endif | |
1021 | } | |
1022 | return(0); | |
1023 | #endif | |
1024 | } | |
1025 | #endif |