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