Commit | Line | Data |
---|---|---|
9d3549b2 NW |
1 | static int wdtest = 0; |
2 | ||
3 | /*- | |
4 | * Copyright (c) 1990 The Regents of the University of California. | |
5 | * All rights reserved. | |
6 | * | |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * William Jolitz. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by the University of | |
21 | * California, Berkeley and its contributors. | |
22 | * 4. Neither the name of the University nor the names of its contributors | |
23 | * may be used to endorse or promote products derived from this software | |
24 | * without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
36 | * SUCH DAMAGE. | |
37 | * | |
38 | * from: @(#)wx.c 7.2 (Berkeley) 5/9/91 | |
19345f8a | 39 | * $Id: wx.c,v 1.5 1993/11/19 06:30:02 davidg Exp $ |
9d3549b2 NW |
40 | */ |
41 | ||
42 | /* TODO:peel out buffer at low ipl, speed improvement */ | |
43 | ||
44 | ||
45 | #include "wx.h" | |
46 | #if NWX > 0 | |
47 | ||
48 | #include "param.h" | |
49 | #include "dkbad.h" | |
50 | #include "systm.h" | |
51 | #include "kernel.h" | |
52 | #include "conf.h" | |
53 | #include "file.h" | |
54 | #include "stat.h" | |
55 | #include "ioctl.h" | |
56 | #include "disklabel.h" | |
57 | #include "buf.h" | |
58 | #include "uio.h" | |
59 | #include "malloc.h" | |
60 | #include "machine/cpu.h" | |
61 | #include "i386/isa/isa.h" | |
62 | #include "i386/isa/isa_device.h" | |
63 | #include "i386/isa/icu.h" | |
64 | #include "i386/isa/wxreg.h" | |
65 | #include "syslog.h" | |
66 | #include "vm/vm.h" | |
67 | ||
68 | #define _NWD (NWX - 1) /* One is for the controller XXX 31 Jul 92*/ | |
69 | ||
70 | #ifndef WDCTIMEOUT | |
71 | #define WDCTIMEOUT 10000000 /* arbitrary timeout for drive ready waits */ | |
72 | #endif | |
73 | ||
74 | #define RETRIES 5 /* number of retries before giving up */ | |
75 | #define RECOVERYTIME 500000 /* usec for controller to recover after err */ | |
76 | #define MAXTRANSFER 32 /* max size of transfer in page clusters */ | |
77 | ||
78 | #define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */ | |
79 | #define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */ | |
80 | #define wdunit(dev) ((minor(dev) & 0x38) >> 3) | |
81 | #define wdpart(dev) (minor(dev) & 0x7) | |
82 | #define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part))) | |
83 | #define WDRAW 3 /* 'd' partition isn't a partition! */ | |
84 | ||
85 | #define b_cylin b_resid /* cylinder number for doing IO to */ | |
86 | /* shares an entry in the buf struct */ | |
87 | ||
88 | /* | |
89 | * Drive states. Used to initialize drive. | |
90 | */ | |
91 | ||
92 | #define CLOSED 0 /* disk is closed. */ | |
93 | #define WANTOPEN 1 /* open requested, not started */ | |
94 | #define RECAL 2 /* doing restore */ | |
95 | #define OPEN 3 /* done with open */ | |
96 | ||
97 | /* | |
98 | * The structure of a disk drive. | |
99 | */ | |
100 | struct disk { | |
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_port; /* i/o port base */ | |
108 | ||
109 | u_long dk_copenpart; /* character units open on this drive */ | |
110 | u_long dk_bopenpart; /* block units open on this drive */ | |
111 | u_long dk_openpart; /* all units open on this drive */ | |
112 | short dk_wlabel; /* label writable? */ | |
113 | short dk_flags; /* drive characteistics found */ | |
114 | #define DKFL_DOSPART 0x00001 /* has DOS partition table */ | |
115 | #define DKFL_SINGLE 0x00004 /* sector at a time mode */ | |
116 | #define DKFL_ERROR 0x00008 /* processing a disk error */ | |
117 | #define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */ | |
118 | #define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */ | |
119 | #define DKFL_WRITEPROT 0x00040 /* manual unit write protect */ | |
120 | #define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */ | |
121 | struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ | |
122 | struct disklabel dk_dd; /* device configuration data */ | |
123 | struct dos_partition | |
124 | dk_dospartitions[NDOSPART]; /* DOS view of disk */ | |
125 | struct dkbad dk_bad; /* bad sector table */ | |
126 | }; | |
127 | ||
128 | static struct disk *wddrives[_NWD]; /* table of units */ | |
129 | static struct buf wdtab; | |
130 | static struct buf wdutab[_NWD]; /* head of queue per drive */ | |
131 | #ifdef notyet | |
132 | static struct buf rwdbuf[_NWD]; /* buffers for raw IO */ | |
133 | #endif | |
134 | static long wdxfer[_NWD]; /* count of transfers */ | |
135 | ||
136 | static int wdprobe(struct isa_device *dvp); | |
137 | static int wdattach(struct isa_device *dvp); | |
138 | static void wdustart(struct disk *du); | |
139 | static void wdstart(void); | |
140 | static int wdcontrol(struct buf *bp); | |
141 | static int wdcommand(struct disk *du, u_int cylinder, u_int head, | |
142 | u_int sector, u_int count, u_int command); | |
143 | static int wdsetctlr(struct disk *du); | |
144 | static int wdwsetctlr(struct disk *du); | |
145 | static int wdgetctlr(struct disk *du); | |
146 | static void wderror(struct buf *bp, struct disk *du, char *mesg); | |
147 | static int wdreset(struct disk *du); | |
148 | static void wdsleep(char *wmesg); | |
149 | static int wdunwedge(struct disk *du); | |
150 | static int wdwait(struct disk *du, u_char bits_wanted); | |
151 | ||
152 | struct isa_driver wxdriver = { | |
153 | wdprobe, wdattach, "wd", | |
154 | }; | |
155 | ||
156 | /* | |
157 | * Probe for controller. | |
158 | */ | |
159 | static int | |
160 | wdprobe(struct isa_device *dvp) | |
161 | { | |
162 | int unit = dvp->id_unit; | |
163 | int u; | |
164 | struct disk *du; | |
165 | int wdc; | |
166 | ||
167 | if (unit >= _NWD) /* 31 Jul 92*/ | |
168 | return(0); | |
169 | ||
170 | if ((du = wddrives[unit]) == 0) { | |
171 | du = wddrives[unit] = (struct disk *) | |
172 | malloc (sizeof(struct disk), M_TEMP, M_NOWAIT); | |
173 | bzero (du, sizeof(struct disk)); /* 31 Jul 92*/ | |
174 | du->dk_unit = unit; | |
175 | } | |
176 | ||
177 | wdc = du->dk_port = dvp->id_iobase; | |
178 | ||
179 | /* | |
180 | * Skip probing and resetting the controller if it has already | |
181 | * been successfully done. | |
182 | * XXX - should finish probing for drives (using wdgetctlr) or | |
183 | * only probe for controllers. | |
184 | */ | |
185 | for (u = 0; u < _NWD; u++) | |
186 | if (u != unit && wddrives[u] != NULL | |
187 | && wddrives[u]->dk_port == wdc) | |
188 | return (IO_WDCSIZE); | |
189 | ||
190 | /* check if we have registers that work */ | |
191 | outb(wdc+wd_cyl_lo, 0xa5) ; /* wd_cyl_lo is read/write */ | |
192 | if(inb(wdc+wd_cyl_lo) != 0xa5) | |
193 | goto nodevice; | |
194 | ||
195 | if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0) | |
196 | goto nodevice; | |
197 | ||
198 | /* execute a controller only command */ | |
199 | if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 | |
200 | || wdwait(du, 0) != 0) | |
201 | goto nodevice; | |
202 | ||
203 | return (IO_WDCSIZE); | |
204 | ||
205 | nodevice: | |
206 | free(du, M_TEMP); | |
207 | wddrives[unit] = 0; | |
208 | return (0); | |
209 | } | |
210 | ||
211 | /* | |
212 | * Attach each drive if possible. | |
213 | */ | |
214 | static int | |
215 | wdattach(struct isa_device *dvp) | |
216 | { | |
217 | int unit; | |
218 | ||
219 | for (unit=0; unit< _NWD; unit++) { | |
220 | struct disk *du; | |
221 | if ((du = wddrives[unit]) == 0) { | |
222 | du = wddrives[unit] = (struct disk *) | |
223 | malloc (sizeof(struct disk), M_TEMP, M_NOWAIT); | |
224 | bzero (du, sizeof(struct disk)); | |
225 | du->dk_unit = unit; | |
226 | du->dk_port = dvp->id_iobase; | |
227 | } | |
228 | ||
229 | /* print out description of drive, suppressing multiple blanks*/ | |
230 | if (wdgetctlr(du) == 0) { | |
231 | int i, blank; | |
232 | ||
233 | printf("wd%d: unit %d type ", unit, unit); | |
234 | for (i = blank = 0 ; i < sizeof(du->dk_params.wdp_model); i++) { | |
235 | char c = du->dk_params.wdp_model[i]; | |
236 | ||
237 | if (blank && c == ' ') continue; | |
238 | if (blank && c != ' ') { | |
239 | printf(" %c", c); | |
240 | blank = 0; | |
241 | continue; | |
242 | } | |
243 | if (c == ' ') | |
244 | blank = 1; | |
245 | else | |
246 | printf("%c", c); | |
247 | } | |
248 | printf("\n"); | |
249 | } | |
250 | else { | |
251 | free(du, M_TEMP); | |
252 | wddrives[unit] = 0; | |
253 | } | |
254 | } | |
255 | return(1); | |
256 | } | |
257 | ||
258 | /* Read/write routine for a buffer. Finds the proper unit, range checks | |
259 | * arguments, and schedules the transfer. Does not wait for the transfer | |
260 | * to complete. Multi-page transfers are supported. All I/O requests must | |
261 | * be a multiple of a sector in length. | |
262 | */ | |
263 | void | |
264 | wdstrategy(register struct buf *bp) | |
265 | { | |
266 | register struct buf *dp; | |
267 | struct disk *du; /* Disk unit to do the IO. */ | |
268 | int unit = wdunit(bp->b_dev); | |
269 | int s; | |
270 | ||
271 | /* valid unit, controller, and request? */ | |
272 | if (unit >= _NWD || bp->b_blkno < 0 || (du = wddrives[unit]) == 0) { | |
273 | ||
274 | bp->b_error = EINVAL; | |
275 | bp->b_flags |= B_ERROR; | |
276 | goto done; | |
277 | } | |
278 | ||
279 | /* "soft" write protect check */ | |
280 | if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) { | |
281 | bp->b_error = EROFS; | |
282 | bp->b_flags |= B_ERROR; | |
283 | goto done; | |
284 | } | |
285 | ||
286 | /* have partitions and want to use them? */ | |
287 | if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) { | |
288 | ||
289 | /* | |
290 | * do bounds checking, adjust transfer. if error, process. | |
291 | * if end of partition, just return | |
292 | */ | |
293 | if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0) | |
294 | goto done; | |
295 | /* otherwise, process transfer request */ | |
296 | } | |
297 | ||
298 | /* queue transfer on drive, activate drive and controller if idle */ | |
299 | dp = &wdutab[unit]; | |
300 | s = splbio(); | |
301 | disksort(dp, bp); | |
302 | if (dp->b_active == 0) | |
303 | wdustart(du); /* start drive */ | |
304 | ||
305 | /* Pick up changes made by readdisklabel(). */ | |
306 | if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) { | |
307 | wdsleep("wdlab"); | |
308 | du->dk_state = WANTOPEN; | |
309 | } | |
310 | ||
311 | if (wdtab.b_active == 0) | |
312 | wdstart(); /* start controller */ | |
313 | splx(s); | |
314 | return; | |
315 | ||
316 | done: | |
317 | /* toss transfer, we're done early */ | |
318 | biodone(bp); | |
319 | } | |
320 | ||
321 | /* | |
322 | * Routine to queue a command to the controller. The unit's | |
323 | * request is linked into the active list for the controller. | |
324 | * If the controller is idle, the transfer is started. | |
325 | */ | |
326 | static void | |
327 | wdustart(register struct disk *du) | |
328 | { | |
329 | register struct buf *bp, *dp = &wdutab[du->dk_unit]; | |
330 | ||
331 | /* unit already active? */ | |
332 | if (dp->b_active) | |
333 | return; | |
334 | ||
335 | /* anything to start? */ | |
336 | bp = dp->b_actf; | |
337 | if (bp == NULL) | |
338 | return; | |
339 | ||
340 | /* link onto controller queue */ | |
341 | dp->b_forw = NULL; | |
342 | if (wdtab.b_actf == NULL) | |
343 | wdtab.b_actf = dp; | |
344 | else | |
345 | wdtab.b_actl->b_forw = dp; | |
346 | wdtab.b_actl = dp; | |
347 | ||
348 | /* mark the drive unit as busy */ | |
349 | dp->b_active = 1; | |
350 | } | |
351 | ||
352 | /* | |
353 | * Controller startup routine. This does the calculation, and starts | |
354 | * a single-sector read or write operation. Called to start a transfer, | |
355 | * or from the interrupt routine to continue a multi-sector transfer. | |
356 | * RESTRICTIONS: | |
357 | * 1. The transfer length must be an exact multiple of the sector size. | |
358 | */ | |
359 | ||
360 | static void | |
361 | wdstart(void) | |
362 | { | |
363 | register struct disk *du; /* disk unit for IO */ | |
364 | register struct buf *bp; | |
365 | struct disklabel *lp; | |
366 | struct buf *dp; | |
367 | register struct bt_bad *bt_ptr; | |
368 | long blknum, cylin, head, sector; | |
369 | long secpertrk, secpercyl, addr; | |
370 | int unit, wdc; | |
371 | ||
372 | loop: | |
373 | /* is there a drive for the controller to do a transfer with? */ | |
374 | dp = wdtab.b_actf; | |
375 | if (dp == NULL) | |
376 | return; | |
377 | ||
378 | /* is there a transfer to this drive ? if so, link it on | |
379 | the controller's queue */ | |
380 | bp = dp->b_actf; | |
381 | if (bp == NULL) { | |
382 | wdtab.b_actf = dp->b_forw; | |
383 | goto loop; | |
384 | } | |
385 | ||
386 | /* obtain controller and drive information */ | |
387 | unit = wdunit(bp->b_dev); | |
388 | du = wddrives[unit]; | |
389 | wdc = du->dk_port; | |
390 | ||
391 | /* if not really a transfer, do control operations specially */ | |
392 | if (du->dk_state < OPEN) { | |
393 | if (du->dk_state != WANTOPEN) | |
394 | printf("wd%d: wdstart: weird dk_state %d\n", | |
395 | du->dk_unit, du->dk_state); | |
396 | if (wdcontrol(bp) != 0) | |
397 | printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n", | |
398 | du->dk_unit, du->dk_state); | |
399 | return; | |
400 | } | |
401 | ||
402 | /* calculate transfer details */ | |
403 | blknum = bp->b_blkno + du->dk_skip; | |
404 | #ifdef WDDEBUG | |
405 | if (du->dk_skip == 0) | |
406 | printf("wd%d: wdstart: %s %d@%d; map ", unit, | |
407 | (bp->b_flags & B_READ) ? "read" : "write", | |
408 | bp->b_bcount, blknum); | |
409 | else | |
410 | printf(" %d)%x", du->dk_skip, inb(wdc+wd_altsts)); | |
411 | #endif | |
412 | addr = (int) bp->b_un.b_addr; | |
413 | if (du->dk_skip == 0) | |
414 | du->dk_bc = bp->b_bcount; | |
415 | ||
416 | lp = &du->dk_dd; | |
417 | secpertrk = lp->d_nsectors; | |
418 | secpercyl = lp->d_secpercyl; | |
419 | if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) | |
420 | blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset; | |
421 | cylin = blknum / secpercyl; | |
422 | head = (blknum % secpercyl) / secpertrk; | |
423 | sector = blknum % secpertrk; | |
424 | ||
425 | /* | |
426 | * See if the current block is in the bad block list. | |
427 | * (If we have one, and not formatting.) | |
428 | */ | |
429 | if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/ | |
430 | == (DKFL_SINGLE|DKFL_BADSECT)) | |
431 | for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != 0xffff; | |
432 | bt_ptr++) { | |
433 | if (bt_ptr->bt_cyl > cylin) | |
434 | /* Sorted list, and we passed our cylinder. quit. */ | |
435 | break; | |
436 | if (bt_ptr->bt_cyl == cylin && | |
437 | bt_ptr->bt_trksec == (head << 8) + sector) { | |
438 | /* | |
439 | * Found bad block. Calculate new block addr. | |
440 | * This starts at the end of the disk (skip the | |
441 | * last track which is used for the bad block list), | |
442 | * and works backwards to the front of the disk. | |
443 | */ | |
444 | #ifdef WDDEBUG | |
445 | printf("--- badblock code -> Old = %d; ", | |
446 | blknum); | |
447 | #endif | |
448 | ||
449 | /* | |
450 | * XXX the offset of the bad sector table ought | |
451 | * to be stored in the in-core copy of the table. | |
452 | */ | |
453 | #define BAD144_PART 2 /* XXX scattered magic numbers */ | |
454 | #define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */ | |
455 | if (lp->d_partitions[BSD_PART].p_offset != 0) | |
456 | blknum = lp->d_partitions[BAD144_PART].p_offset | |
457 | + lp->d_partitions[BAD144_PART].p_size; | |
458 | else | |
459 | blknum = lp->d_secperunit; | |
460 | blknum -= lp->d_nsectors + (bt_ptr - du->dk_bad.bt_bad) | |
461 | + 1; | |
462 | ||
463 | cylin = blknum / secpercyl; | |
464 | head = (blknum % secpercyl) / secpertrk; | |
465 | sector = blknum % secpertrk; | |
466 | #ifdef WDDEBUG | |
467 | printf("new = %d\n", blknum); | |
468 | #endif | |
469 | break; | |
470 | } | |
471 | } | |
472 | ||
473 | wdtab.b_active = 1; /* mark controller active */ | |
474 | ||
475 | /* if starting a multisector transfer, or doing single transfers */ | |
476 | if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) { | |
477 | int command; | |
478 | u_int count; | |
479 | ||
480 | if (wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) | |
481 | du->dk_bc += DEV_BSIZE; | |
482 | ||
483 | #ifdef B_FORMAT | |
484 | if (bp->b_flags & B_FORMAT) { | |
485 | command = WDCC_FORMAT; | |
486 | count = lp->d_nsectors; | |
487 | sector = lp->d_gap3 - 1; /* + 1 later */ | |
488 | } else | |
489 | #endif | |
490 | { | |
491 | if (du->dk_flags & DKFL_SINGLE) | |
492 | count = 1; | |
493 | else | |
494 | count = howmany(du->dk_bc, DEV_BSIZE); | |
495 | command = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE; | |
496 | } | |
497 | ||
498 | /* | |
499 | * XXX this loop may never terminate. The code to handle | |
500 | * counting down of retries and eventually failing the i/o is | |
501 | * in wdintr() and we can't get there from here. | |
502 | */ | |
503 | if (wdtest != 0) { | |
504 | if (--wdtest == 0) { | |
505 | wdtest = 100; | |
506 | printf("dummy wdunwedge\n"); | |
507 | wdunwedge(du); | |
508 | } | |
509 | } | |
510 | while (wdcommand(du, cylin, head, sector, count, command) != 0) | |
511 | { | |
512 | wderror(bp, du, | |
513 | "wdstart: timeout waiting to send command"); | |
514 | wdunwedge(du); | |
515 | } | |
516 | #ifdef WDDEBUG | |
517 | printf("sector %d cylin %d head %d addr %x sts %x\n", | |
518 | sector, cylin, head, addr, inb(wdc+wd_altsts)); | |
519 | #endif | |
520 | } | |
521 | ||
522 | /* if this is a read operation, just go away until it's done. */ | |
523 | if (bp->b_flags & B_READ) return; | |
524 | ||
525 | /* ready to send data? */ | |
526 | if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) { | |
527 | wderror(bp, du, "wdstart: timeout waiting for DRQ"); | |
528 | /* | |
529 | * XXX what do we do now? If we've just issued the command, | |
530 | * then we can treat this failure the same as a command | |
531 | * failure. But if we are continuing a multi-sector write, | |
532 | * the command was issued ages ago, so we can't simply | |
533 | * restart it. | |
534 | * | |
535 | * XXX we waste a lot of time unnecessarily translating | |
536 | * block numbers to cylin/head/sector for continued i/o's. | |
537 | */ | |
538 | } | |
539 | ||
540 | /* then send it! */ | |
541 | outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE, | |
542 | DEV_BSIZE/sizeof(short)); | |
543 | du->dk_bc -= DEV_BSIZE; | |
544 | } | |
545 | ||
546 | /* Interrupt routine for the controller. Acknowledge the interrupt, check for | |
547 | * errors on the current operation, mark it done if necessary, and start | |
548 | * the next request. Also check for a partially done transfer, and | |
549 | * continue with the next chunk if so. | |
550 | */ | |
551 | void | |
552 | wdintr(int unit) | |
553 | { | |
554 | register struct disk *du; | |
555 | register struct buf *bp, *dp; | |
556 | int wdc; | |
557 | ||
558 | if (!wdtab.b_active) { | |
559 | printf("wd%d: extra interrupt\n", unit); | |
560 | return; | |
561 | } | |
562 | ||
563 | dp = wdtab.b_actf; | |
564 | bp = dp->b_actf; | |
565 | du = wddrives[wdunit(bp->b_dev)]; | |
566 | wdc = du->dk_port; | |
567 | ||
568 | if (wdwait(du, 0) < 0) { | |
569 | wderror(bp, du, "wdintr: timeout waiting for status"); | |
570 | du->dk_status |= WDCS_ERR; /* XXX */ | |
571 | } | |
572 | ||
573 | /* is it not a transfer, but a control operation? */ | |
574 | if (du->dk_state < OPEN) { | |
575 | wdtab.b_active = 0; | |
576 | if (wdcontrol(bp)) | |
577 | wdstart(); | |
578 | return; | |
579 | } | |
580 | ||
581 | /* have we an error? */ | |
582 | if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) { | |
583 | oops: | |
584 | #ifdef WDDEBUG | |
585 | wderror(bp, du, "wdintr"); | |
586 | #endif | |
587 | if((du->dk_flags & DKFL_SINGLE) == 0) { | |
588 | du->dk_flags |= DKFL_ERROR; | |
589 | goto outt; | |
590 | } | |
591 | #ifdef B_FORMAT | |
592 | if (bp->b_flags & B_FORMAT) { | |
593 | bp->b_error = EIO; /* 17 Sep 92*/ | |
594 | bp->b_flags |= B_ERROR; | |
595 | goto done; | |
596 | } | |
597 | #endif | |
598 | ||
599 | /* error or error correction? */ | |
600 | if (du->dk_status & WDCS_ERR) { | |
601 | if (++wdtab.b_errcnt < RETRIES) { | |
602 | wdtab.b_active = 0; | |
603 | } else { | |
604 | wderror(bp, du, "hard error"); | |
605 | bp->b_error = EIO; /* 17 Sep 92*/ | |
606 | bp->b_flags |= B_ERROR; /* flag the error */ | |
607 | } | |
608 | } else | |
609 | wderror(bp, du, "soft ecc"); | |
610 | } | |
611 | ||
612 | /* | |
613 | * If this was a successful read operation, fetch the data. | |
614 | */ | |
615 | if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) { | |
616 | int chk, dummy; | |
617 | ||
618 | chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short)); | |
619 | ||
620 | /* ready to receive data? */ | |
621 | if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) | |
622 | != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) | |
623 | wderror(bp, du, "wdintr: read intr arrived early"); | |
624 | if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) { | |
625 | wderror(bp, du, "wdintr: read error detected late"); | |
626 | goto oops; | |
627 | } | |
628 | ||
629 | /* suck in data */ | |
630 | insw (wdc+wd_data, | |
631 | (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk); | |
632 | du->dk_bc -= chk * sizeof(short); | |
633 | ||
634 | /* for obselete fractional sector reads */ | |
635 | while (chk++ < 256) insw (wdc+wd_data, &dummy, 1); | |
636 | } | |
637 | ||
638 | wdxfer[du->dk_unit]++; | |
639 | outt: | |
640 | if (wdtab.b_active) { | |
641 | if ((bp->b_flags & B_ERROR) == 0) { | |
642 | du->dk_skip++; /* Add to successful sectors. */ | |
643 | if (wdtab.b_errcnt) | |
644 | wderror(bp, du, "soft error"); | |
645 | wdtab.b_errcnt = 0; | |
646 | ||
647 | /* see if more to transfer */ | |
648 | if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) { | |
649 | wdtab.b_active = 0; | |
650 | wdstart(); | |
651 | return; /* next chunk is started */ | |
652 | } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR)) | |
653 | == DKFL_ERROR) { | |
654 | du->dk_skip = 0; | |
655 | du->dk_flags &= ~DKFL_ERROR; | |
656 | du->dk_flags |= DKFL_SINGLE; | |
657 | wdtab.b_active = 0; | |
658 | wdstart(); | |
659 | return; /* redo xfer sector by sector */ | |
660 | } | |
661 | } | |
662 | ||
663 | #ifdef B_FORMAT | |
664 | done: ; | |
665 | #endif | |
666 | /* done with this transfer, with or without error */ | |
667 | du->dk_flags &= ~DKFL_SINGLE; | |
668 | wdtab.b_actf = dp->b_forw; | |
669 | wdtab.b_errcnt = 0; | |
670 | du->dk_skip = 0; | |
671 | dp->b_active = 0; | |
672 | dp->b_actf = bp->av_forw; | |
673 | dp->b_errcnt = 0; | |
674 | bp->b_resid = 0; | |
675 | biodone(bp); | |
676 | } | |
677 | ||
678 | /* controller idle */ | |
679 | wdtab.b_active = 0; | |
680 | ||
681 | /* anything more on drive queue? */ | |
682 | if (dp->b_actf) | |
683 | wdustart(du); | |
684 | /* anything more for controller to do? */ | |
685 | if (wdtab.b_actf) | |
686 | wdstart(); | |
687 | } | |
688 | ||
689 | /* | |
690 | * Initialize a drive. | |
691 | */ | |
692 | int | |
693 | wdopen(dev_t dev, int flags, int fmt, struct proc *p) | |
694 | { | |
695 | register unsigned int unit; | |
696 | register struct disk *du; | |
697 | int part = wdpart(dev), mask = 1 << part; | |
698 | struct partition *pp; | |
699 | char *msg; | |
700 | struct disklabel save_label; | |
701 | ||
702 | unit = wdunit(dev); | |
703 | if (unit >= _NWD) return (ENXIO) ; | |
704 | ||
705 | du = wddrives[unit]; | |
706 | if (du == 0) return (ENXIO) ; | |
707 | ||
708 | while (du->dk_flags & DKFL_LABELLING) | |
709 | tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1); | |
710 | if ((du->dk_flags & DKFL_BSDLABEL) == 0) { | |
711 | /* | |
712 | * wdtab.b_active != 0 implies wdutab[unit].b_actf == NULL (?) | |
713 | * so the following guards most things (until the next i/o). | |
714 | * It doesn't guard against a new i/o starting and being | |
715 | * affected by the label being changed. Sigh. | |
716 | */ | |
717 | wdsleep("wdopn1"); | |
718 | ||
719 | du->dk_flags |= DKFL_LABELLING | DKFL_WRITEPROT; | |
720 | du->dk_state = WANTOPEN; | |
721 | wdutab[unit].b_actf = NULL; | |
722 | ||
723 | /* | |
724 | * Read label using WDRAW partition. | |
725 | * | |
726 | * If the drive has an MBR, then the current geometry (from | |
727 | * wdgetctlr()) is used to read it; then the BIOS/DOS | |
728 | * geometry is inferred and used to read the label off the | |
729 | * 'c' partition. Otherwise the label is read using the | |
730 | * current geometry. The label gives the final geometry. | |
731 | * If bad sector handling is enabled, then this geometry | |
732 | * is used to read the bad sector table. The geometry | |
733 | * changes occur inside readdisklabel() and are propagated | |
734 | * to the driver by resetting the state machine. | |
735 | */ | |
736 | save_label = du->dk_dd; | |
737 | #define WDSTRATEGY ((int (*)(struct buf *)) wdstrategy) /* XXX */ | |
738 | msg = readdisklabel(makewddev(major(dev), unit, WDRAW), | |
739 | WDSTRATEGY, &du->dk_dd, | |
740 | du->dk_dospartitions, &du->dk_bad, | |
741 | (struct buf **)NULL); | |
742 | du->dk_flags &= ~DKFL_LABELLING; | |
743 | if (msg != NULL) { | |
744 | du->dk_dd = save_label; | |
745 | log(LOG_WARNING, "wd%d: cannot find label (%s)\n", | |
746 | unit, msg); | |
747 | if (part != WDRAW) | |
748 | return (EINVAL); /* XXX needs translation */ | |
749 | } else { | |
750 | du->dk_flags |= DKFL_BSDLABEL; | |
751 | du->dk_flags &= ~DKFL_WRITEPROT; | |
752 | if (du->dk_dd.d_flags & D_BADSECT) | |
753 | du->dk_flags |= DKFL_BADSECT; | |
754 | } | |
755 | ||
756 | /* Pick up changes made by readdisklabel(). */ | |
757 | wdsleep("wdopn2"); | |
758 | du->dk_state = WANTOPEN; | |
759 | } | |
760 | ||
761 | /* | |
762 | * Warn if a partion is opened | |
763 | * that overlaps another partition which is open | |
764 | * unless one is the "raw" partition (whole disk). | |
765 | */ | |
766 | if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) { | |
767 | int start, end; | |
768 | ||
769 | pp = &du->dk_dd.d_partitions[part]; | |
770 | start = pp->p_offset; | |
771 | end = pp->p_offset + pp->p_size; | |
772 | for (pp = du->dk_dd.d_partitions; | |
773 | pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; | |
774 | pp++) { | |
775 | if (pp->p_offset + pp->p_size <= start || | |
776 | pp->p_offset >= end) | |
777 | continue; | |
778 | if (pp - du->dk_dd.d_partitions == WDRAW) | |
779 | continue; | |
780 | if (du->dk_openpart & (1 << (pp - | |
781 | du->dk_dd.d_partitions))) | |
782 | log(LOG_WARNING, | |
783 | "wd%d%c: overlaps open partition (%c)\n", | |
784 | unit, part + 'a', | |
785 | pp - du->dk_dd.d_partitions + 'a'); | |
786 | } | |
787 | } | |
788 | if (part >= du->dk_dd.d_npartitions && part != WDRAW) | |
789 | return (ENXIO); | |
790 | ||
791 | /* insure only one open at a time */ | |
792 | du->dk_openpart |= mask; | |
793 | switch (fmt) { | |
794 | case S_IFCHR: | |
795 | du->dk_copenpart |= mask; | |
796 | break; | |
797 | case S_IFBLK: | |
798 | du->dk_bopenpart |= mask; | |
799 | break; | |
800 | } | |
801 | return (0); | |
802 | } | |
803 | ||
804 | /* | |
805 | * Implement operations other than read/write. | |
806 | * Called from wdstart or wdintr during opens and formats. | |
807 | * Uses finite-state-machine to track progress of operation in progress. | |
808 | * Returns 0 if operation still in progress, 1 if completed. | |
809 | */ | |
810 | static int | |
811 | wdcontrol(register struct buf *bp) | |
812 | { | |
813 | register struct disk *du; | |
814 | ||
815 | du = wddrives[wdunit(bp->b_dev)]; | |
816 | ||
817 | switch (du->dk_state) { | |
818 | case WANTOPEN: | |
819 | tryagainrecal: | |
820 | wdtab.b_active = 1; | |
821 | if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) { | |
822 | wderror(bp, du, "wdcontrol: wdcommand failed"); | |
823 | goto maybe_retry; | |
824 | } | |
825 | du->dk_state = RECAL; | |
826 | return(0); | |
827 | case RECAL: | |
828 | if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) { | |
829 | wderror(bp, du, "wdcontrol: recal failed"); | |
830 | maybe_retry: | |
831 | if (du->dk_status & WDCS_ERR) | |
832 | wdunwedge(du); | |
833 | if (++wdtab.b_errcnt < RETRIES) { | |
834 | du->dk_state = WANTOPEN; | |
835 | goto tryagainrecal; | |
836 | } | |
837 | bp->b_error = ENXIO; /* XXX needs translation */ | |
838 | bp->b_flags |= B_ERROR; | |
839 | return(1); | |
840 | } | |
841 | wdtab.b_errcnt = 0; | |
842 | du->dk_state = OPEN; | |
843 | /* | |
844 | * The rest of the initialization can be done | |
845 | * by normal means. | |
846 | */ | |
847 | return(1); | |
848 | } | |
849 | panic("wdcontrol"); | |
850 | return (1); | |
851 | } | |
852 | ||
853 | /* | |
854 | * Wait uninterruptibly until controller is not busy, then send it a command. | |
855 | * The wait usually terminates immediately because we waited for the previous | |
856 | * command to terminate. | |
857 | */ | |
858 | static int | |
859 | wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, | |
860 | u_int count, u_int command) | |
861 | { | |
862 | u_int wdc; | |
863 | ||
864 | if (wdwait(du, 0) < 0) | |
865 | return (1); | |
866 | wdc = du->dk_port; | |
867 | outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4); | |
868 | outb(wdc + wd_cyl_lo, cylinder); | |
869 | outb(wdc + wd_cyl_hi, cylinder >> 8); | |
19345f8a | 870 | outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | (head & 0x0f)); |
5e84d633 NW |
871 | DELAY(10); /* XXX give drive time to see change */ |
872 | if (wdwait(du, WDCS_READY) < 0) | |
873 | return(1); | |
9d3549b2 NW |
874 | outb(wdc + wd_sector, sector + 1); |
875 | outb(wdc + wd_seccnt, count); | |
876 | outb(du->dk_port + wd_command, command); | |
877 | return (0); | |
878 | } | |
879 | ||
880 | /* | |
881 | * issue IDC to drive to tell it just what geometry it is to be. | |
882 | */ | |
883 | static int | |
884 | wdsetctlr(struct disk *du) | |
885 | { | |
886 | #ifdef WDDEBUG | |
887 | printf("wd%d: wdsetctlr C %lu H %lu S %lu\n", du->dk_unit, | |
888 | du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, | |
889 | du->dk_dd.d_nsectors); | |
890 | #endif | |
891 | if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, | |
892 | du->dk_dd.d_nsectors, WDCC_IDC) != 0 | |
893 | || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0) { | |
894 | wderror((struct buf *)NULL, du, "wdsetctlr failed"); | |
895 | return (1); | |
896 | } | |
897 | return (0); | |
898 | } | |
899 | ||
900 | /* | |
901 | * Wait until driver is inactive, then set up controller. | |
902 | */ | |
903 | static int | |
904 | wdwsetctlr(struct disk *du) | |
905 | { | |
906 | int stat; | |
907 | int x; | |
908 | ||
909 | wdsleep("wdwset"); | |
910 | x = splbio(); | |
911 | stat = wdsetctlr(du); | |
912 | splx(x); | |
913 | return (stat); | |
914 | } | |
915 | ||
916 | /* | |
917 | * issue READP to drive to ask it what it is. | |
918 | */ | |
919 | static int | |
920 | wdgetctlr(struct disk *du) { | |
921 | int i; | |
922 | char tb[DEV_BSIZE]; | |
923 | struct wdparams *wp; | |
924 | ||
925 | if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0 | |
926 | || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) { | |
927 | ||
6967b885 | 928 | #ifndef MFM |
9d3549b2 NW |
929 | /* Old drives don't support WDCC_READP. Try a seek to 0. */ |
930 | if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 | |
931 | || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0) | |
932 | return (1); | |
6967b885 NW |
933 | #else /* IDE */ |
934 | /* | |
935 | * Some IDE drives return trash if there is not a unit 1 | |
936 | * out there, need to make sure that it is READY and not BUSY | |
937 | * before you assume it is there ! | |
938 | */ | |
939 | outb(du->dk_port+wd_sdh, WDSD_IBM | (du->dk_unit<<4)); | |
940 | DELAY(5000); | |
941 | if ((inb(du->dk_port+wd_status) & (WDCS_READY|WDCS_BUSY)) != | |
942 | WDCS_READY) { | |
943 | return (1); | |
944 | } | |
945 | #endif /* MFM */ | |
9d3549b2 NW |
946 | |
947 | /* Fake minimal drive geometry for reading the MBR or label. */ | |
948 | du->dk_dd.d_secsize = DEV_BSIZE; | |
949 | du->dk_dd.d_nsectors = 17; | |
950 | du->dk_dd.d_ntracks = 1; | |
951 | du->dk_dd.d_ncylinders = 1; | |
952 | du->dk_dd.d_secpercyl = 17; | |
953 | ||
954 | /* | |
955 | * Fake some more of the label for printing by disklabel(1) | |
956 | * in case there is no real label. | |
957 | */ | |
958 | du->dk_dd.d_type = DTYPE_ST506; | |
959 | du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; | |
960 | strncpy(du->dk_dd.d_typename, "Fake geometry", | |
961 | sizeof du->dk_dd.d_typename); | |
962 | ||
963 | /* Fake the model name for printing by wdattach(). */ | |
964 | strncpy(du->dk_params.wdp_model, "Unknown Type", | |
965 | sizeof du->dk_params.wdp_model); | |
966 | ||
967 | return (0); | |
968 | } | |
969 | ||
970 | /* obtain parameters */ | |
971 | wp = &du->dk_params; | |
972 | insw(du->dk_port + wd_data, tb, sizeof(tb)/sizeof(short)); | |
973 | bcopy(tb, wp, sizeof(struct wdparams)); | |
974 | ||
975 | /* shuffle string byte order */ | |
976 | for (i=0; i < sizeof(wp->wdp_model) ;i+=2) { | |
977 | u_short *p; | |
978 | p = (u_short *) (wp->wdp_model + i); | |
979 | *p = ntohs(*p); | |
980 | } | |
981 | #ifdef WDDEBUG | |
982 | printf( | |
983 | "\nwdgetctlr wd%d: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", | |
984 | du->dk_unit, wp->wdp_config, | |
985 | wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads, | |
986 | wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz, | |
987 | wp->wdp_model); | |
988 | #endif | |
989 | ||
990 | /* update disklabel given drive information */ | |
991 | du->dk_dd.d_secsize = DEV_BSIZE; | |
992 | du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/; | |
993 | du->dk_dd.d_ntracks = wp->wdp_heads; | |
994 | du->dk_dd.d_nsectors = wp->wdp_sectors; | |
995 | du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; | |
996 | /* dubious ... */ | |
997 | bcopy("ESDI/IDE", du->dk_dd.d_typename, 9); | |
998 | bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1); | |
999 | /* better ... */ | |
1000 | du->dk_dd.d_type = DTYPE_ESDI; | |
1001 | du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; | |
1002 | ||
1003 | return (0); | |
1004 | } | |
1005 | ||
1006 | ||
1007 | /* ARGSUSED */ | |
1008 | int | |
1009 | wdclose(dev_t dev, int flags, int fmt) | |
1010 | { | |
1011 | register struct disk *du; | |
1012 | int part = wdpart(dev), mask = 1 << part; | |
1013 | ||
1014 | du = wddrives[wdunit(dev)]; | |
1015 | ||
1016 | /* insure only one open at a time */ | |
1017 | du->dk_openpart &= ~mask; | |
1018 | switch (fmt) { | |
1019 | case S_IFCHR: | |
1020 | du->dk_copenpart &= ~mask; | |
1021 | break; | |
1022 | case S_IFBLK: | |
1023 | du->dk_bopenpart &= ~mask; | |
1024 | break; | |
1025 | } | |
1026 | return(0); | |
1027 | } | |
1028 | ||
1029 | int | |
1030 | wdioctl(dev_t dev, int cmd, caddr_t addr, int flag) | |
1031 | { | |
1032 | int unit = wdunit(dev); | |
1033 | register struct disk *du; | |
1034 | int error = 0; | |
1035 | #ifdef notyet | |
1036 | struct uio auio; | |
1037 | struct iovec aiov; | |
1038 | #endif | |
1039 | ||
1040 | du = wddrives[unit]; | |
1041 | ||
1042 | switch (cmd) { | |
1043 | ||
1044 | case DIOCSBAD: | |
1045 | if ((flag & FWRITE) == 0) | |
1046 | error = EBADF; | |
1047 | else | |
1048 | du->dk_bad = *(struct dkbad *)addr; | |
1049 | break; | |
1050 | ||
1051 | case DIOCGDINFO: | |
1052 | *(struct disklabel *)addr = du->dk_dd; | |
1053 | break; | |
1054 | ||
1055 | case DIOCGPART: | |
1056 | ((struct partinfo *)addr)->disklab = &du->dk_dd; | |
1057 | ((struct partinfo *)addr)->part = | |
1058 | &du->dk_dd.d_partitions[wdpart(dev)]; | |
1059 | break; | |
1060 | ||
1061 | case DIOCSDINFO: | |
1062 | if ((flag & FWRITE) == 0) | |
1063 | error = EBADF; | |
1064 | else | |
1065 | error = setdisklabel(&du->dk_dd, | |
1066 | (struct disklabel *)addr, | |
1067 | /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0, | |
1068 | du->dk_dospartitions); | |
1069 | if (error == 0) { | |
1070 | du->dk_flags |= DKFL_BSDLABEL; | |
1071 | wdwsetctlr(du); /* XXX - check */ | |
1072 | } | |
1073 | break; | |
1074 | ||
1075 | case DIOCWLABEL: | |
1076 | du->dk_flags &= ~DKFL_WRITEPROT; | |
1077 | if ((flag & FWRITE) == 0) | |
1078 | error = EBADF; | |
1079 | else | |
1080 | du->dk_wlabel = *(int *)addr; | |
1081 | break; | |
1082 | ||
1083 | case DIOCWDINFO: | |
1084 | du->dk_flags &= ~DKFL_WRITEPROT; | |
1085 | if ((flag & FWRITE) == 0) | |
1086 | error = EBADF; | |
1087 | else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr, | |
1088 | /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0, | |
1089 | du->dk_dospartitions)) == 0) { | |
1090 | int wlab; | |
1091 | ||
1092 | du->dk_flags |= DKFL_BSDLABEL; | |
1093 | wdwsetctlr(du); /* XXX - check */ | |
1094 | ||
1095 | /* simulate opening partition 0 so write succeeds */ | |
1096 | du->dk_openpart |= (1 << 0); /* XXX */ | |
1097 | wlab = du->dk_wlabel; | |
1098 | du->dk_wlabel = 1; | |
1099 | error = writedisklabel(dev, WDSTRATEGY, | |
1100 | &du->dk_dd, du->dk_dospartitions); | |
1101 | du->dk_openpart = du->dk_copenpart | du->dk_bopenpart; | |
1102 | du->dk_wlabel = wlab; | |
1103 | } | |
1104 | break; | |
1105 | ||
1106 | #ifdef notyet | |
1107 | case DIOCGDINFOP: | |
1108 | *(struct disklabel **)addr = &(du->dk_dd); | |
1109 | break; | |
1110 | ||
1111 | case DIOCWFORMAT: | |
1112 | if ((flag & FWRITE) == 0) | |
1113 | error = EBADF; | |
1114 | else { | |
1115 | register struct format_op *fop; | |
1116 | ||
1117 | fop = (struct format_op *)addr; | |
1118 | aiov.iov_base = fop->df_buf; | |
1119 | aiov.iov_len = fop->df_count; | |
1120 | auio.uio_iov = &aiov; | |
1121 | auio.uio_iovcnt = 1; | |
1122 | auio.uio_resid = fop->df_count; | |
1123 | auio.uio_segflg = 0; | |
1124 | auio.uio_offset = | |
1125 | fop->df_startblk * du->dk_dd.d_secsize; | |
519fb2b7 | 1126 | error = physio(wdformat, &rwdbuf[unit], 0, dev, B_WRITE, |
9d3549b2 NW |
1127 | minphys, &auio); |
1128 | fop->df_count -= auio.uio_resid; | |
1129 | fop->df_reg[0] = du->dk_status; | |
1130 | fop->df_reg[1] = du->dk_error; | |
1131 | } | |
1132 | break; | |
1133 | #endif | |
1134 | ||
1135 | default: | |
1136 | error = ENOTTY; | |
1137 | break; | |
1138 | } | |
1139 | return (error); | |
1140 | } | |
1141 | ||
1142 | #ifdef B_FORMAT | |
1143 | int | |
1144 | wdformat(struct buf *bp) | |
1145 | { | |
1146 | ||
1147 | bp->b_flags |= B_FORMAT; | |
1148 | return (wdstrategy(bp)); | |
1149 | } | |
1150 | #endif | |
1151 | ||
1152 | int | |
1153 | wdsize(dev_t dev) | |
1154 | { | |
1155 | int unit = wdunit(dev), part = wdpart(dev), val; | |
1156 | struct disk *du; | |
1157 | ||
1158 | if (unit >= _NWD) /* 31 Jul 92*/ | |
1159 | return(-1); | |
1160 | ||
1161 | du = wddrives[unit]; | |
1162 | val = 0; | |
1163 | if (du == 0 || du->dk_state == CLOSED) | |
1164 | val = wdopen (makewddev(major(dev), unit, WDRAW), FREAD, S_IFBLK, 0); | |
1165 | if (du == 0 || val != 0 || du->dk_flags & DKFL_WRITEPROT) | |
1166 | return (-1); | |
1167 | ||
1168 | return((int)du->dk_dd.d_partitions[part].p_size); | |
1169 | } | |
1170 | ||
1171 | extern char *vmmap; /* poor name! */ | |
1172 | ||
1173 | int | |
1174 | wddump(dev_t dev) /* dump core after a system crash */ | |
1175 | { | |
1176 | register struct disk *du; /* disk unit to do the IO */ | |
1177 | #ifdef notyet | |
1178 | register struct bt_bad *bt_ptr; | |
1179 | #endif | |
1180 | long num; /* number of sectors to write */ | |
1181 | int unit, part, wdc; | |
1182 | long blkoff, blknum; | |
1183 | #ifdef notdef | |
1184 | long blkcnt; | |
1185 | #endif | |
1186 | long cylin, head, sector; | |
1187 | long secpertrk, secpercyl, nblocks, i; | |
1188 | char *addr; | |
1189 | extern int Maxmem; | |
1190 | static wddoingadump = 0 ; | |
1191 | extern caddr_t CADDR1; | |
1192 | ||
1193 | addr = (char *) 0; /* starting address */ | |
1194 | ||
1195 | /* toss any characters present prior to dump */ | |
1196 | while (sgetc(1)) | |
1197 | ; | |
1198 | ||
1199 | /* size of memory to dump */ | |
1200 | num = Maxmem; | |
1201 | unit = wdunit(dev); /* eventually support floppies? */ | |
1202 | part = wdpart(dev); /* file system */ | |
1203 | /* check for acceptable drive number */ | |
1204 | if (unit >= _NWD) return(ENXIO); /* 31 Jul 92*/ | |
1205 | ||
1206 | du = wddrives[unit]; | |
1207 | if (du == 0) return(ENXIO); | |
1208 | /* was it ever initialized ? */ | |
1209 | if (du->dk_state < OPEN) return (ENXIO) ; | |
1210 | if (du->dk_flags & DKFL_WRITEPROT) return(ENXIO); | |
1211 | wdc = du->dk_port; | |
1212 | ||
1213 | /* Convert to disk sectors */ | |
1214 | num = (u_long) num * NBPG / du->dk_dd.d_secsize; | |
1215 | ||
1216 | /* check if controller active */ | |
1217 | /*if (wdtab.b_active) return(EFAULT); */ | |
1218 | if (wddoingadump) return(EFAULT); | |
1219 | ||
1220 | secpertrk = du->dk_dd.d_nsectors; | |
1221 | secpercyl = du->dk_dd.d_secpercyl; | |
1222 | nblocks = du->dk_dd.d_partitions[part].p_size; | |
1223 | blkoff = du->dk_dd.d_partitions[part].p_offset; | |
1224 | ||
1225 | /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/ | |
1226 | /* check transfer bounds against partition size */ | |
1227 | if ((dumplo < 0) || ((dumplo + num) > nblocks)) | |
1228 | return(EINVAL); | |
1229 | ||
1230 | #if 0 | |
1231 | wdtab.b_active = 1; /* mark controller active for if we | |
1232 | panic during the dump */ | |
1233 | #endif | |
1234 | wddoingadump = 1 ; i = 100000 ; | |
7f4d626f DG |
1235 | /* must delay 5us to conform to ATA spec */ |
1236 | DELAY(5); | |
9d3549b2 NW |
1237 | while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ; |
1238 | outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); | |
1239 | outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); | |
7f4d626f DG |
1240 | /* must delay 5us to conform to ATA spec */ |
1241 | DELAY(5); | |
9d3549b2 NW |
1242 | while (inb(wdc+wd_status) & WDCS_BUSY) ; |
1243 | ||
1244 | /* some compaq controllers require this ... */ | |
1245 | wdsetctlr(du); | |
1246 | ||
1247 | blknum = dumplo + blkoff; | |
1248 | while (num > 0) { | |
1249 | #ifdef notdef | |
1250 | if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; | |
1251 | if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) | |
1252 | blkcnt = secpercyl - (blknum % secpercyl); | |
1253 | /* keep transfer within current cylinder */ | |
1254 | #endif | |
1255 | pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE); | |
1256 | ||
1257 | /* compute disk address */ | |
1258 | cylin = blknum / secpercyl; | |
1259 | head = (blknum % secpercyl) / secpertrk; | |
1260 | sector = blknum % secpertrk; | |
1261 | ||
1262 | #ifdef notyet | |
1263 | /* | |
1264 | * See if the current block is in the bad block list. | |
1265 | * (If we have one.) | |
1266 | */ | |
1267 | for (bt_ptr = du->dk_bad.bt_bad; | |
1268 | bt_ptr->bt_cyl != -1; bt_ptr++) { | |
1269 | if (bt_ptr->bt_cyl > cylin) | |
1270 | /* Sorted list, and we passed our cylinder. | |
1271 | quit. */ | |
1272 | break; | |
1273 | if (bt_ptr->bt_cyl == cylin && | |
1274 | bt_ptr->bt_trksec == (head << 8) + sector) { | |
1275 | /* | |
1276 | * Found bad block. Calculate new block addr. | |
1277 | * This starts at the end of the disk (skip the | |
1278 | * last track which is used for the bad block list), | |
1279 | * and works backwards to the front of the disk. | |
1280 | */ | |
1281 | /* XXX as usual */ | |
1282 | if (lp->d_partitions[BSD_PART].p_offset != 0) | |
1283 | blknum = lp->d_partitions[BAD144_PART] | |
1284 | .p_offset | |
1285 | + lp->d_partitions[BAD144_PART] | |
1286 | .p_size; | |
1287 | else | |
1288 | blknum = lp->d_secperunit; | |
1289 | blknum -= du->dk_dd.d_nsectors | |
1290 | + (bt_ptr - du->dk_bad.bt_bad) + 1; | |
1291 | cylin = blknum / secpercyl; | |
1292 | head = (blknum % secpercyl) / secpertrk; | |
1293 | sector = blknum % secpertrk; | |
1294 | break; | |
1295 | } | |
1296 | } | |
1297 | #endif | |
1298 | sector++; /* origin 1 */ | |
1299 | ||
1300 | /* select drive. */ | |
1301 | outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); | |
1302 | while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; | |
1303 | ||
1304 | /* transfer some blocks */ | |
1305 | outb(wdc+wd_sector, sector); | |
1306 | outb(wdc+wd_seccnt,1); | |
1307 | outb(wdc+wd_cyl_lo, cylin); | |
1308 | outb(wdc+wd_cyl_hi, cylin >> 8); | |
1309 | #ifdef notdef | |
1310 | /* lets just talk about this first...*/ | |
1311 | pg ("sdh 0%o sector %d cyl %d addr 0x%x", | |
1312 | inb(wdc+wd_sdh), inb(wdc+wd_sector), | |
1313 | inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ; | |
1314 | #endif | |
1315 | outb(wdc+wd_command, WDCC_WRITE); | |
1316 | ||
1317 | /* Ready to send data? */ | |
1318 | while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; | |
1319 | if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; | |
1320 | ||
1321 | outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256); | |
1322 | ||
1323 | if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; | |
1324 | /* Check data request (should be done). */ | |
1325 | if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ; | |
1326 | ||
1327 | /* wait for completion */ | |
7f4d626f DG |
1328 | /* must delay 5us to conform to ATA spec */ |
1329 | DELAY(5); | |
9d3549b2 NW |
1330 | for ( i = WDCTIMEOUT ; inb(wdc+wd_status) & WDCS_BUSY ; i--) { |
1331 | if (i < 0) return (EIO) ; | |
1332 | } | |
1333 | /* error check the xfer */ | |
1334 | if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; | |
1335 | ||
1336 | if ((unsigned)addr % (1024*1024) == 0) printf("%ld ", num/2048); | |
1337 | /* update block count */ | |
1338 | num--; | |
1339 | blknum++ ; | |
1340 | (int) addr += 512; | |
1341 | ||
1342 | /* operator aborting dump? */ | |
1343 | if (sgetc(1)) | |
1344 | return(EINTR); | |
1345 | } | |
1346 | return(0); | |
1347 | } | |
1348 | ||
1349 | static void | |
1350 | wderror(struct buf *bp, struct disk *du, char *mesg) | |
1351 | { | |
1352 | if (bp == NULL) | |
1353 | printf("wd%d: %s:", du->dk_unit, mesg); | |
1354 | else | |
1355 | diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, &du->dk_dd); | |
1356 | printf(" status %b error %b\n", | |
1357 | du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS); | |
1358 | } | |
1359 | ||
1360 | /* | |
1361 | * Reset the controller. | |
1362 | */ | |
1363 | static int | |
1364 | wdreset(struct disk *du) | |
1365 | { | |
1366 | int wdc; | |
1367 | ||
1368 | wdc = du->dk_port; | |
1369 | (void)wdwait(du, 0); | |
1370 | outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST); | |
1371 | DELAY(10 * 1000); | |
1372 | outb(wdc + wd_ctlr, WDCTL_IDS); | |
1373 | if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0 | |
1374 | || (du->dk_error = inb(wdc + wd_error)) != 0x01) | |
1375 | return (1); | |
1376 | outb(wdc + wd_ctlr, WDCTL_4BIT); | |
1377 | return (0); | |
1378 | } | |
1379 | ||
1380 | /* | |
1381 | * Sleep until driver is inactive. | |
1382 | * This is used only for avoiding rare race conditions, so it is unimportant | |
1383 | * that the sleep may be far too short or too long. | |
1384 | */ | |
1385 | static void | |
1386 | wdsleep(char *wmesg) | |
1387 | { | |
1388 | while (wdtab.b_active) | |
1389 | tsleep((caddr_t)&wdtab.b_active, PZERO - 1, wmesg, 1); | |
1390 | } | |
1391 | ||
1392 | /* | |
1393 | * Reset the controller after it has become wedged. This is different from | |
1394 | * wdreset() so that wdreset() can be used in the probe and so that this | |
1395 | * can restore the geometry . | |
1396 | */ | |
1397 | static int | |
1398 | wdunwedge(struct disk *du) | |
1399 | { | |
1400 | struct disk *du1; | |
1401 | int unit; | |
1402 | ||
1403 | /* Schedule other drives for recalibration. */ | |
1404 | for (unit = 0; unit < _NWD; unit++) | |
1405 | if ((du1 = wddrives[unit]) != NULL && du1 != du | |
1406 | && du1->dk_state > WANTOPEN) | |
1407 | du1->dk_state = WANTOPEN; | |
1408 | ||
1409 | DELAY(RECOVERYTIME); | |
1410 | if (wdreset(du) == 0) { | |
1411 | /* | |
1412 | * XXX - recalibrate current drive now because some callers | |
1413 | * aren't prepared to have its state change. | |
1414 | */ | |
1415 | if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0 | |
1416 | && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) == 0 | |
1417 | && wdsetctlr(du) == 0) | |
1418 | return (0); | |
1419 | } | |
1420 | wderror((struct buf *)NULL, du, "wdunwedge failed"); | |
1421 | return (1); | |
1422 | } | |
1423 | ||
1424 | /* | |
1425 | * Wait uninterruptibly until controller is not busy and either certain | |
1426 | * status bits are set or an error has occurred. | |
1427 | * The wait is usually short unless it is for the controller to process | |
1428 | * an entire critical command. | |
1429 | * Return 1 for (possibly stale) controller errors, -1 for timeout errors, | |
1430 | * or 0 for no errors. | |
1431 | * Return controller status in du->dk_status and, if there was a controller | |
1432 | * error, return the error code in du->dk_error. | |
1433 | */ | |
1434 | static int min_retries; | |
1435 | static int | |
1436 | wdwait(struct disk *du, u_char bits_wanted) | |
1437 | { | |
1438 | int retries; | |
1439 | int wdc; | |
1440 | u_char status; | |
1441 | #define POLLING 1000 | |
1442 | #define TIMEOUT 1000 /* WDCC_DIAGNOSE can take > 300 msec */ | |
1443 | ||
1444 | wdc = du->dk_port; | |
1445 | retries = POLLING + TIMEOUT; | |
1446 | do { | |
1447 | if (min_retries > retries || min_retries == 0) | |
1448 | min_retries = retries; | |
7f4d626f DG |
1449 | /* must delay 5us to conform to ATA spec */ |
1450 | DELAY(5); | |
9d3549b2 NW |
1451 | du->dk_status = status = inb(wdc + wd_status); |
1452 | if (!(status & WDCS_BUSY)) { | |
1453 | if (status & WDCS_ERR) { | |
1454 | du->dk_error = inb(wdc + wd_error); | |
1455 | return (1); | |
1456 | } | |
1457 | if ((status & bits_wanted) == bits_wanted) | |
1458 | return (0); | |
1459 | } | |
1460 | if (retries < TIMEOUT) | |
1461 | /* | |
1462 | * Switch to a polling rate of about 1 KHz so that | |
1463 | * the timeout is almost machine-independent. The | |
1464 | * controller is taking a long time to respond, so | |
1465 | * an extra msec won't matter. | |
1466 | */ | |
1467 | DELAY(1000); | |
1468 | } while (--retries != 0); | |
1469 | return (-1); | |
1470 | } | |
1471 | ||
1472 | #endif /* NWD > 0 */ |