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