wd.c revision 1.2 1 /*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * 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.
23 *
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 * from:@(#)wd.c 7.2 (Berkeley) 5/9/91
37 *
38 * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
39 * -------------------- ----- ----------------------
40 * CURRENT PATCH LEVEL: 4 00072
41 * -------------------- ----- ----------------------
42 *
43 * 17 Sep 92 Frank Maclachlan Fixed I/O error reporting on raw device
44 * 31 Jul 92 Christoph Robitschko Fixed second disk recognition,
45 * bzero of malloced memory for warm
46 * boot problem.
47 * 19 Aug 92 Frank Maclachlan Fixed bug when first sector of a
48 * multisector read is in bad144 table.
49 * 17 Jan 93 B. Evans & A.Chernov Fixed bugs from previous patches,
50 * driver initialization, and cylinder
51 * boundary conditions.
52 */
53
54 /* TODO:peel out buffer at low ipl, speed improvement */
55
56
57 #include "wd.h"
58 #if NWD > 0
59
60 #include "param.h"
61 #include "dkbad.h"
62 #include "systm.h"
63 #include "conf.h"
64 #include "file.h"
65 #include "stat.h"
66 #include "ioctl.h"
67 #include "disklabel.h"
68 #include "buf.h"
69 #include "uio.h"
70 #include "malloc.h"
71 #include "machine/cpu.h"
72 #include "i386/isa/isa_device.h"
73 #include "i386/isa/icu.h"
74 #include "i386/isa/wdreg.h"
75 #include "syslog.h"
76 #include "vm/vm.h"
77
78 #define _NWD (NWD - 1) /* One is for the controller XXX 31 Jul 92*/
79
80 #define RETRIES 5 /* number of retries before giving up */
81 #define MAXTRANSFER 32 /* max size of transfer in page clusters */
82
83 #define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
84 #define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */
85 #define wdunit(dev) ((minor(dev) & 0x38) >> 3)
86 #define wdpart(dev) (minor(dev) & 0x7)
87 #define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part)))
88 #define WDRAW 3 /* 'd' partition isn't a partition! */
89
90 #define b_cylin b_resid /* cylinder number for doing IO to */
91 /* shares an entry in the buf struct */
92
93 /*
94 * Drive states. Used to initialize drive.
95 */
96
97 #define CLOSED 0 /* disk is closed. */
98 #define WANTOPEN 1 /* open requested, not started */
99 #define RECAL 2 /* doing restore */
100 #define OPEN 3 /* done with open */
101
102 /*
103 * The structure of a disk drive.
104 */
105 struct disk {
106 long dk_bc; /* byte count left */
107 short dk_skip; /* blocks already transferred */
108 char dk_unit; /* physical unit number */
109 char dk_state; /* control state */
110 u_char dk_status; /* copy of status reg. */
111 u_char dk_error; /* copy of error reg. */
112 short dk_port; /* i/o port base */
113
114 u_long dk_copenpart; /* character units open on this drive */
115 u_long dk_bopenpart; /* block units open on this drive */
116 u_long dk_openpart; /* all units open on this drive */
117 short dk_wlabel; /* label writable? */
118 short dk_flags; /* drive characteistics found */
119 #define DKFL_DOSPART 0x00001 /* has DOS partition table */
120 #define DKFL_QUIET 0x00002 /* report errors back, but don't complain */
121 #define DKFL_SINGLE 0x00004 /* sector at a time mode */
122 #define DKFL_ERROR 0x00008 /* processing a disk error */
123 #define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
124 #define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
125 #define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
126 struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
127 struct disklabel dk_dd; /* device configuration data */
128 struct dos_partition
129 dk_dospartitions[NDOSPART]; /* DOS view of disk */
130 struct dkbad dk_bad; /* bad sector table */
131 };
132
133 struct disk *wddrives[_NWD]; /* table of units */
134 struct buf wdtab;
135 struct buf wdutab[_NWD]; /* head of queue per drive */
136 struct buf rwdbuf[_NWD]; /* buffers for raw IO */
137 long wdxfer[_NWD]; /* count of transfers */
138 #ifdef WDDEBUG
139 int wddebug;
140 #endif
141
142 struct isa_driver wddriver = {
143 wdprobe, wdattach, "wd",
144 };
145
146 void wdustart(struct disk *);
147 void wdstart();
148 int wdcommand(struct disk *, int);
149 int wdcontrol(struct buf *);
150 int wdsetctlr(dev_t, struct disk *);
151 int wdgetctlr(int, struct disk *);
152
153 /*
154 * Probe for controller.
155 */
156 int
157 wdprobe(struct isa_device *dvp)
158 {
159 int unit = dvp->id_unit;
160 struct disk *du;
161 int wdc;
162
163 if (unit >= _NWD) /* 31 Jul 92*/
164 return(0);
165
166 if ((du = wddrives[unit]) == 0) {
167 du = wddrives[unit] = (struct disk *)
168 malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
169 bzero (du, sizeof(struct disk)); /* 31 Jul 92*/
170 du->dk_unit = unit;
171 }
172
173 wdc = du->dk_port = dvp->id_iobase;
174
175 /* check if we have registers that work */
176 outb(wdc+wd_cyl_lo, 0xa5) ; /* wd_cyl_lo is read/write */
177 if(inb(wdc+wd_cyl_lo) != 0xa5)
178 goto nodevice;
179
180 /* reset the device */
181 outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
182 DELAY(1000);
183 outb(wdc+wd_ctlr, WDCTL_IDS);
184 DELAY(1000);
185
186 /* execute a controller only command */
187 if (wdcommand(du, WDCC_DIAGNOSE) < 0)
188 goto nodevice;
189
190 (void) inb(wdc+wd_error); /* XXX! */
191 outb(wdc+wd_ctlr, WDCTL_4BIT);
192 return (1);
193
194 nodevice:
195 free(du, M_TEMP);
196 wddrives[unit] = 0;
197 return (0);
198 }
199
200 /*
201 * Attach each drive if possible.
202 */
203 int
204 wdattach(struct isa_device *dvp)
205 {
206 int unit;
207 /* int unit = dvp->id_unit;*/
208
209 for (unit=0; unit< _NWD; unit++) {
210 struct disk *du;
211 if ((du = wddrives[unit]) == 0) {
212 du = wddrives[unit] = (struct disk *)
213 malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
214 bzero (du, sizeof(struct disk));
215 du->dk_unit = unit;
216 du->dk_port = dvp->id_iobase;
217 }
218
219 /* print out description of drive, suppressing multiple blanks*/
220 if(wdgetctlr(unit, du) == 0) {
221 int i, blank;
222 char c;
223 printf(" %d:<", unit);
224 for (i = blank = 0 ; i < sizeof(du->dk_params.wdp_model); i++) {
225 char c = du->dk_params.wdp_model[i];
226
227 if (blank && c == ' ') continue;
228 if (blank && c != ' ') {
229 printf(" %c", c);
230 blank = 0;
231 continue;
232 }
233 if (c == ' ')
234 blank = 1;
235 else
236 printf("%c", c);
237 }
238 printf(">");
239 du->dk_unit = unit;
240 }
241 else {
242 /* old ST506 controller */
243 printf(" %d:<wdgetctlr failed, assuming OK>",
244 unit);
245 }
246 }
247 return(1);
248 }
249
250 /* Read/write routine for a buffer. Finds the proper unit, range checks
251 * arguments, and schedules the transfer. Does not wait for the transfer
252 * to complete. Multi-page transfers are supported. All I/O requests must
253 * be a multiple of a sector in length.
254 */
255 int
256 wdstrategy(register struct buf *bp)
257 {
258 register struct buf *dp;
259 struct disklabel *lp;
260 register struct partition *p;
261 struct disk *du; /* Disk unit to do the IO. */
262 long maxsz, sz;
263 int unit = wdunit(bp->b_dev);
264 int s;
265
266 /* valid unit, controller, and request? */
267 if (unit >= _NWD || bp->b_blkno < 0 || (du = wddrives[unit]) == 0) {
268
269 bp->b_error = EINVAL;
270 bp->b_flags |= B_ERROR;
271 goto done;
272 }
273
274 /* "soft" write protect check */
275 if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
276 bp->b_error = EROFS;
277 bp->b_flags |= B_ERROR;
278 goto done;
279 }
280
281 /* have partitions and want to use them? */
282 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) {
283
284 /*
285 * do bounds checking, adjust transfer. if error, process.
286 * if end of partition, just return
287 */
288 if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0)
289 goto done;
290 /* otherwise, process transfer request */
291 }
292
293 q:
294 /* queue transfer on drive, activate drive and controller if idle */
295 dp = &wdutab[unit];
296 s = splbio();
297 disksort(dp, bp);
298 if (dp->b_active == 0)
299 wdustart(du); /* start drive */
300 if (wdtab.b_active == 0)
301 wdstart(s); /* start controller */
302 splx(s);
303 return;
304
305 done:
306 /* toss transfer, we're done early */
307 biodone(bp);
308 }
309
310 /*
311 * Routine to queue a command to the controller. The unit's
312 * request is linked into the active list for the controller.
313 * If the controller is idle, the transfer is started.
314 */
315 static void
316 wdustart(register struct disk *du)
317 {
318 register struct buf *bp, *dp = &wdutab[du->dk_unit];
319
320 /* unit already active? */
321 if (dp->b_active)
322 return;
323
324 /* anything to start? */
325 bp = dp->b_actf;
326 if (bp == NULL)
327 return;
328
329 /* link onto controller queue */
330 dp->b_forw = NULL;
331 if (wdtab.b_actf == NULL)
332 wdtab.b_actf = dp;
333 else
334 wdtab.b_actl->b_forw = dp;
335 wdtab.b_actl = dp;
336
337 /* mark the drive unit as busy */
338 dp->b_active = 1;
339 }
340
341 /*
342 * Controller startup routine. This does the calculation, and starts
343 * a single-sector read or write operation. Called to start a transfer,
344 * or from the interrupt routine to continue a multi-sector transfer.
345 * RESTRICTIONS:
346 * 1. The transfer length must be an exact multiple of the sector size.
347 */
348
349 static void
350 wdstart()
351 {
352 register struct disk *du; /* disk unit for IO */
353 register struct buf *bp;
354 struct disklabel *lp;
355 struct buf *dp;
356 register struct bt_bad *bt_ptr;
357 long blknum, pagcnt, cylin, head, sector;
358 long secpertrk, secpercyl, addr, i;
359 int unit, s, wdc;
360
361 loop:
362 /* is there a drive for the controller to do a transfer with? */
363 dp = wdtab.b_actf;
364 if (dp == NULL)
365 return;
366
367 /* is there a transfer to this drive ? if so, link it on
368 the controller's queue */
369 bp = dp->b_actf;
370 if (bp == NULL) {
371 wdtab.b_actf = dp->b_forw;
372 goto loop;
373 }
374
375 /* obtain controller and drive information */
376 unit = wdunit(bp->b_dev);
377 du = wddrives[unit];
378
379 /* if not really a transfer, do control operations specially */
380 if (du->dk_state < OPEN) {
381 (void) wdcontrol(bp);
382 return;
383 }
384
385 /* calculate transfer details */
386 blknum = bp->b_blkno + du->dk_skip;
387 /*if(wddebug)printf("bn%d ", blknum);*/
388 #ifdef WDDEBUG
389 if (du->dk_skip == 0)
390 printf("\nwdstart %d: %s %d@%d; map ", unit,
391 (bp->b_flags & B_READ) ? "read" : "write",
392 bp->b_bcount, blknum);
393 else
394 printf(" %d)%x", du->dk_skip, inb(wdc+wd_altsts));
395 #endif
396 addr = (int) bp->b_un.b_addr;
397 if (du->dk_skip == 0)
398 du->dk_bc = bp->b_bcount;
399
400 lp = &du->dk_dd;
401 secpertrk = lp->d_nsectors;
402 secpercyl = lp->d_secpercyl;
403 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
404 blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
405 cylin = blknum / secpercyl;
406 head = (blknum % secpercyl) / secpertrk;
407 sector = blknum % secpertrk;
408
409 /*
410 * See if the current block is in the bad block list.
411 * (If we have one, and not formatting.)
412 */
413 if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/
414 == (DKFL_SINGLE|DKFL_BADSECT))
415 for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
416 if (bt_ptr->bt_cyl > cylin)
417 /* Sorted list, and we passed our cylinder. quit. */
418 break;
419 if (bt_ptr->bt_cyl == cylin &&
420 bt_ptr->bt_trksec == (head << 8) + sector) {
421 /*
422 * Found bad block. Calculate new block addr.
423 * This starts at the end of the disk (skip the
424 * last track which is used for the bad block list),
425 * and works backwards to the front of the disk.
426 */
427 #ifdef WDDEBUG
428 printf("--- badblock code -> Old = %d; ",
429 blknum);
430 #endif
431 blknum = lp->d_secperunit - lp->d_nsectors
432 - (bt_ptr - du->dk_bad.bt_bad) - 1;
433 cylin = blknum / secpercyl;
434 head = (blknum % secpercyl) / secpertrk;
435 sector = blknum % secpertrk;
436 #ifdef WDDEBUG
437 printf("new = %d\n", blknum);
438 #endif
439 break;
440 }
441 }
442 /*if(wddebug)pg("c%d h%d s%d ", cylin, head, sector);*/
443 sector += 1; /* sectors begin with 1, not 0 */
444
445 wdtab.b_active = 1; /* mark controller active */
446 wdc = du->dk_port;
447
448 /* if starting a multisector transfer, or doing single transfers */
449 if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
450 if (wdtab.b_errcnt && (bp->b_flags & B_READ) == 0)
451 du->dk_bc += DEV_BSIZE;
452
453 /* controller idle? */
454 while (inb(wdc+wd_status) & WDCS_BUSY)
455 ;
456
457 /* stuff the task file */
458 outb(wdc+wd_precomp, lp->d_precompcyl / 4);
459 #ifdef B_FORMAT
460 if (bp->b_flags & B_FORMAT) {
461 outb(wdc+wd_sector, lp->d_gap3);
462 outb(wdc+wd_seccnt, lp->d_nsectors);
463 } else {
464 #endif
465 if (du->dk_flags & DKFL_SINGLE)
466 outb(wdc+wd_seccnt, 1);
467 else
468 outb(wdc+wd_seccnt, howmany(du->dk_bc, DEV_BSIZE));
469 outb(wdc+wd_sector, sector);
470
471 #ifdef B_FORMAT
472 }
473 #endif
474
475 outb(wdc+wd_cyl_lo, cylin);
476 outb(wdc+wd_cyl_hi, cylin >> 8);
477
478 /* set up the SDH register (select drive) */
479 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
480
481 /* wait for drive to become ready */
482 while ((inb(wdc+wd_status) & WDCS_READY) == 0)
483 ;
484
485 /* initiate command! */
486 #ifdef B_FORMAT
487 if (bp->b_flags & B_FORMAT)
488 outb(wdc+wd_command, WDCC_FORMAT);
489 else
490 #endif
491 outb(wdc+wd_command,
492 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
493 #ifdef WDDEBUG
494 printf("sector %d cylin %d head %d addr %x sts %x\n",
495 sector, cylin, head, addr, inb(wdc+wd_altsts));
496 #endif
497 }
498
499 /* if this is a read operation, just go away until it's done. */
500 if (bp->b_flags & B_READ) return;
501
502 /* ready to send data? */
503 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
504 ;
505
506 /* then send it! */
507 outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
508 DEV_BSIZE/sizeof(short));
509 du->dk_bc -= DEV_BSIZE;
510 }
511
512 /* Interrupt routine for the controller. Acknowledge the interrupt, check for
513 * errors on the current operation, mark it done if necessary, and start
514 * the next request. Also check for a partially done transfer, and
515 * continue with the next chunk if so.
516 */
517 void
518 wdintr(struct intrframe wdif)
519 {
520 register struct disk *du;
521 register struct buf *bp, *dp;
522 int status, wdc;
523 char partch ;
524
525 if (!wdtab.b_active) {
526 #ifdef nyet
527 printf("wd: extra interrupt\n");
528 #endif
529 return;
530 }
531
532 dp = wdtab.b_actf;
533 bp = dp->b_actf;
534 du = wddrives[wdunit(bp->b_dev)];
535 wdc = du->dk_port;
536
537 #ifdef WDDEBUG
538 printf("I ");
539 #endif
540
541 while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ;
542
543 /* is it not a transfer, but a control operation? */
544 if (du->dk_state < OPEN) {
545 if (wdcontrol(bp))
546 wdstart();
547 return;
548 }
549
550 /* have we an error? */
551 if (status & (WDCS_ERR | WDCS_ECCCOR)) {
552
553 du->dk_status = status;
554 du->dk_error = inb(wdc + wd_error);
555 #ifdef WDDEBUG
556 printf("status %x error %x\n", status, du->dk_error);
557 #endif
558 if((du->dk_flags & DKFL_SINGLE) == 0) {
559 du->dk_flags |= DKFL_ERROR;
560 goto outt;
561 }
562 #ifdef B_FORMAT
563 if (bp->b_flags & B_FORMAT) {
564 bp->b_error = EIO; /* 17 Sep 92*/
565 bp->b_flags |= B_ERROR;
566 goto done;
567 }
568 #endif
569
570 /* error or error correction? */
571 if (status & WDCS_ERR) {
572 if (++wdtab.b_errcnt < RETRIES) {
573 wdtab.b_active = 0;
574 } else {
575 if((du->dk_flags&DKFL_QUIET) == 0) {
576 diskerr(bp, "wd", "hard error",
577 LOG_PRINTF, du->dk_skip,
578 &du->dk_dd);
579 #ifdef WDDEBUG
580 printf( "status %b error %b\n",
581 status, WDCS_BITS,
582 inb(wdc+wd_error), WDERR_BITS);
583 #endif
584 }
585 bp->b_error = EIO; /* 17 Sep 92*/
586 bp->b_flags |= B_ERROR; /* flag the error */
587 }
588 } else if((du->dk_flags&DKFL_QUIET) == 0) {
589 diskerr(bp, "wd", "soft ecc", 0,
590 du->dk_skip, &du->dk_dd);
591 }
592 }
593 outt:
594
595 /*
596 * If this was a successful read operation, fetch the data.
597 */
598 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
599 int chk, dummy;
600
601 chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
602
603 /* ready to receive data? */
604 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
605 ;
606
607 /* suck in data */
608 insw (wdc+wd_data,
609 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
610 du->dk_bc -= chk * sizeof(short);
611
612 /* for obselete fractional sector reads */
613 while (chk++ < 256) insw (wdc+wd_data, &dummy, 1);
614 }
615
616 wdxfer[du->dk_unit]++;
617 if (wdtab.b_active) {
618 if ((bp->b_flags & B_ERROR) == 0) {
619 du->dk_skip++; /* Add to successful sectors. */
620 if (wdtab.b_errcnt && (du->dk_flags & DKFL_QUIET) == 0)
621 diskerr(bp, "wd", "soft error", 0,
622 du->dk_skip, &du->dk_dd);
623 wdtab.b_errcnt = 0;
624
625 /* see if more to transfer */
626 if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
627 wdstart();
628 return; /* next chunk is started */
629 } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR))
630 == DKFL_ERROR) {
631 du->dk_skip = 0;
632 du->dk_flags &= ~DKFL_ERROR;
633 du->dk_flags |= DKFL_SINGLE;
634 wdstart();
635 return; /* redo xfer sector by sector */
636 }
637 }
638
639 done:
640 /* done with this transfer, with or without error */
641 du->dk_flags &= ~DKFL_SINGLE;
642 wdtab.b_actf = dp->b_forw;
643 wdtab.b_errcnt = 0;
644 du->dk_skip = 0;
645 dp->b_active = 0;
646 dp->b_actf = bp->av_forw;
647 dp->b_errcnt = 0;
648 bp->b_resid = 0;
649 biodone(bp);
650 }
651
652 /* controller idle */
653 wdtab.b_active = 0;
654
655 /* anything more on drive queue? */
656 if (dp->b_actf)
657 wdustart(du);
658 /* anything more for controller to do? */
659 if (wdtab.b_actf)
660 wdstart();
661 }
662
663 /*
664 * Initialize a drive.
665 */
666 int
667 wdopen(dev_t dev, int flags, int fmt, struct proc *p)
668 {
669 register unsigned int unit;
670 register struct disk *du;
671 int part = wdpart(dev), mask = 1 << part;
672 struct partition *pp;
673 struct dkbad *db;
674 int i, error = 0;
675 char *msg;
676
677 unit = wdunit(dev);
678 if (unit >= _NWD) return (ENXIO) ;
679
680 du = wddrives[unit];
681 if (du == 0) return (ENXIO) ;
682
683 if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
684 du->dk_flags |= DKFL_WRITEPROT;
685 wdutab[unit].b_actf = NULL;
686
687 /*
688 * Use the default sizes until we've read the label,
689 * or longer if there isn't one there.
690 */
691 bzero(&du->dk_dd, sizeof(du->dk_dd));
692 #undef d_type /* fix goddamn segments.h! XXX */
693 du->dk_dd.d_type = DTYPE_ST506;
694 du->dk_dd.d_ncylinders = 1024;
695 du->dk_dd.d_secsize = DEV_BSIZE;
696 du->dk_dd.d_ntracks = 8;
697 du->dk_dd.d_nsectors = 17;
698 du->dk_dd.d_secpercyl = 17*8;
699 du->dk_state = WANTOPEN;
700 du->dk_unit = unit;
701 if (part == WDRAW)
702 du->dk_flags |= DKFL_QUIET;
703 else
704 du->dk_flags &= ~DKFL_QUIET;
705
706 /* read label using "c" partition */
707 if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
708 wdstrategy, &du->dk_dd, du->dk_dospartitions,
709 &du->dk_bad, 0)) {
710 if((du->dk_flags&DKFL_QUIET) == 0) {
711 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
712 unit, msg);
713 error = EINVAL; /* XXX needs translation */
714 }
715 goto done;
716 } else {
717
718 wdsetctlr(dev, du);
719 du->dk_flags |= DKFL_BSDLABEL;
720 du->dk_flags &= ~DKFL_WRITEPROT;
721 if (du->dk_dd.d_flags & D_BADSECT)
722 du->dk_flags |= DKFL_BADSECT;
723 }
724
725 done:
726 if (error)
727 return(error);
728
729 }
730 /*
731 * Warn if a partion is opened
732 * that overlaps another partition which is open
733 * unless one is the "raw" partition (whole disk).
734 */
735 if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) {
736 int start, end;
737
738 pp = &du->dk_dd.d_partitions[part];
739 start = pp->p_offset;
740 end = pp->p_offset + pp->p_size;
741 for (pp = du->dk_dd.d_partitions;
742 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
743 pp++) {
744 if (pp->p_offset + pp->p_size <= start ||
745 pp->p_offset >= end)
746 continue;
747 /*if (pp - du->dk_dd.d_partitions == RAWPART)
748 continue; */
749 if (pp - du->dk_dd.d_partitions == WDRAW)
750 continue;
751 if (du->dk_openpart & (1 << (pp -
752 du->dk_dd.d_partitions)))
753 log(LOG_WARNING,
754 "wd%d%c: overlaps open partition (%c)\n",
755 unit, part + 'a',
756 pp - du->dk_dd.d_partitions + 'a');
757 }
758 }
759 if (part >= du->dk_dd.d_npartitions && part != WDRAW)
760 return (ENXIO);
761
762 /* insure only one open at a time */
763 du->dk_openpart |= mask;
764 switch (fmt) {
765 case S_IFCHR:
766 du->dk_copenpart |= mask;
767 break;
768 case S_IFBLK:
769 du->dk_bopenpart |= mask;
770 break;
771 }
772 return (0);
773 }
774
775 /*
776 * Implement operations other than read/write.
777 * Called from wdstart or wdintr during opens and formats.
778 * Uses finite-state-machine to track progress of operation in progress.
779 * Returns 0 if operation still in progress, 1 if completed.
780 */
781 static int
782 wdcontrol(register struct buf *bp)
783 {
784 register struct disk *du;
785 register unit;
786 unsigned char stat;
787 int s, cnt;
788 extern int bootdev;
789 int cyl, trk, sec, i, wdc;
790 struct wdparams foo;
791
792 du = wddrives[wdunit(bp->b_dev)];
793 unit = du->dk_unit;
794 wdc = du->dk_port;
795
796 switch (du->dk_state) {
797
798 tryagainrecal:
799 case WANTOPEN: /* set SDH, step rate, do restore */
800 #ifdef WDDEBUG
801 printf("wd%d: recal ", unit);
802 #endif
803 s = splbio(); /* not called from intr level ... */
804 wdgetctlr(unit, du);
805
806 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
807 wdtab.b_active = 1;
808
809 /* wait for drive and controller to become ready */
810 for (i = 1000000; (inb(wdc+wd_status) & (WDCS_READY|WDCS_BUSY))
811 != WDCS_READY && i-- != 0; )
812 ;
813 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
814 du->dk_state++;
815 splx(s);
816 return(0);
817
818 case RECAL:
819 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
820 if ((du->dk_flags & DKFL_QUIET) == 0) {
821 printf("wd%d: recal", du->dk_unit);
822 printf(": status %b error %b\n",
823 stat, WDCS_BITS, inb(wdc+wd_error),
824 WDERR_BITS);
825 }
826 if (++wdtab.b_errcnt < RETRIES) {
827 du->dk_state = WANTOPEN;
828 goto tryagainrecal;
829 }
830 bp->b_error = ENXIO; /* XXX needs translation */
831 goto badopen;
832 }
833
834 /* some controllers require this ... */
835 wdsetctlr(bp->b_dev, du);
836
837 wdtab.b_errcnt = 0;
838 du->dk_state = OPEN;
839 /*
840 * The rest of the initialization can be done
841 * by normal means.
842 */
843 return(1);
844
845 default:
846 panic("wdcontrol");
847 }
848 /* NOTREACHED */
849
850 badopen:
851 if ((du->dk_flags & DKFL_QUIET) == 0)
852 printf(": status %b error %b\n",
853 stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS);
854 bp->b_flags |= B_ERROR;
855 return(1);
856 }
857
858 /*
859 * send a command and wait uninterruptibly until controller is finished.
860 * return -1 if controller busy for too long, otherwise
861 * return status. intended for brief controller commands at critical points.
862 * assumes interrupts are blocked.
863 */
864 static int
865 wdcommand(struct disk *du, int cmd) {
866 int timeout = 1000000, stat, wdc;
867
868 /* controller ready for command? */
869 wdc = du->dk_port;
870 while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout > 0)
871 timeout--;
872 if (timeout <= 0)
873 return(-1);
874
875 /* send command, await results */
876 outb(wdc+wd_command, cmd);
877 while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout > 0)
878 timeout--;
879 if (timeout <= 0)
880 return(-1);
881 if (cmd != WDCC_READP)
882 return (stat);
883
884 /* is controller ready to return data? */
885 while (((stat = inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0 &&
886 timeout > 0)
887 timeout--;
888 if (timeout <= 0)
889 return(-1);
890
891 return (stat);
892 }
893
894 /*
895 * issue IDC to drive to tell it just what geometry it is to be.
896 */
897 static int
898 wdsetctlr(dev_t dev, struct disk *du) {
899 int stat, x, wdc;
900
901 /*printf("C%dH%dS%d ", du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
902 du->dk_dd.d_nsectors);*/
903
904 x = splbio();
905 wdc = du->dk_port;
906 outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders+1);
907 outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders+1)>>8);
908 outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1);
909 outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
910 stat = wdcommand(du, WDCC_IDC);
911
912 if (stat < 0)
913 return(stat);
914 if (stat & WDCS_ERR)
915 printf("wdsetctlr: status %b error %b\n",
916 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
917 splx(x);
918 return(stat);
919 }
920
921 /*
922 * issue READP to drive to ask it what it is.
923 */
924 static int
925 wdgetctlr(int u, struct disk *du) {
926 int stat, x, i, wdc;
927 char tb[DEV_BSIZE];
928 struct wdparams *wp;
929
930 x = splbio(); /* not called from intr level ... */
931 wdc = du->dk_port;
932 outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
933 stat = wdcommand(du, WDCC_READP);
934
935 if (stat < 0)
936 return(stat);
937 if (stat & WDCS_ERR) {
938 splx(x);
939 return(inb(wdc+wd_error));
940 }
941
942 /* obtain parameters */
943 wp = &du->dk_params;
944 insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short));
945 bcopy(tb, wp, sizeof(struct wdparams));
946
947 /* shuffle string byte order */
948 for (i=0; i < sizeof(wp->wdp_model) ;i+=2) {
949 u_short *p;
950 p = (u_short *) (wp->wdp_model + i);
951 *p = ntohs(*p);
952 }
953 /*printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config,
954 wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors,
955 wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);*/
956
957 /* update disklabel given drive information */
958 du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
959 du->dk_dd.d_ntracks = wp->wdp_heads;
960 du->dk_dd.d_nsectors = wp->wdp_sectors;
961 du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
962 du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl *
963 wp->wdp_sectors;
964 du->dk_dd.d_partitions[1].p_offset = 0;
965 /* dubious ... */
966 bcopy("ESDI/IDE", du->dk_dd.d_typename, 9);
967 bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
968 /* better ... */
969 du->dk_dd.d_type = DTYPE_ESDI;
970 du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
971
972 /* XXX sometimes possibly needed */
973 (void) inb(wdc+wd_status);
974 return (0);
975 }
976
977
978 /* ARGSUSED */
979 int
980 wdclose(dev_t dev, int flags, int fmt)
981 {
982 register struct disk *du;
983 int part = wdpart(dev), mask = 1 << part;
984
985 du = wddrives[wdunit(dev)];
986
987 /* insure only one open at a time */
988 du->dk_openpart &= ~mask;
989 switch (fmt) {
990 case S_IFCHR:
991 du->dk_copenpart &= ~mask;
992 break;
993 case S_IFBLK:
994 du->dk_bopenpart &= ~mask;
995 break;
996 }
997 return(0);
998 }
999
1000 int
1001 wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
1002 {
1003 int unit = wdunit(dev);
1004 register struct disk *du;
1005 int error = 0;
1006 struct uio auio;
1007 struct iovec aiov;
1008
1009 du = wddrives[unit];
1010
1011 switch (cmd) {
1012
1013 case DIOCSBAD:
1014 if ((flag & FWRITE) == 0)
1015 error = EBADF;
1016 else
1017 du->dk_bad = *(struct dkbad *)addr;
1018 break;
1019
1020 case DIOCGDINFO:
1021 *(struct disklabel *)addr = du->dk_dd;
1022 break;
1023
1024 case DIOCGPART:
1025 ((struct partinfo *)addr)->disklab = &du->dk_dd;
1026 ((struct partinfo *)addr)->part =
1027 &du->dk_dd.d_partitions[wdpart(dev)];
1028 break;
1029
1030 case DIOCSDINFO:
1031 if ((flag & FWRITE) == 0)
1032 error = EBADF;
1033 else
1034 error = setdisklabel(&du->dk_dd,
1035 (struct disklabel *)addr,
1036 /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0,
1037 du->dk_dospartitions);
1038 if (error == 0) {
1039 du->dk_flags |= DKFL_BSDLABEL;
1040 wdsetctlr(dev, du);
1041 }
1042 break;
1043
1044 case DIOCWLABEL:
1045 du->dk_flags &= ~DKFL_WRITEPROT;
1046 if ((flag & FWRITE) == 0)
1047 error = EBADF;
1048 else
1049 du->dk_wlabel = *(int *)addr;
1050 break;
1051
1052 case DIOCWDINFO:
1053 du->dk_flags &= ~DKFL_WRITEPROT;
1054 if ((flag & FWRITE) == 0)
1055 error = EBADF;
1056 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
1057 /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0,
1058 du->dk_dospartitions)) == 0) {
1059 int wlab;
1060
1061 du->dk_flags |= DKFL_BSDLABEL;
1062 wdsetctlr(dev, du);
1063
1064 /* simulate opening partition 0 so write succeeds */
1065 du->dk_openpart |= (1 << 0); /* XXX */
1066 wlab = du->dk_wlabel;
1067 du->dk_wlabel = 1;
1068 error = writedisklabel(dev, wdstrategy,
1069 &du->dk_dd, du->dk_dospartitions);
1070 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
1071 du->dk_wlabel = wlab;
1072 }
1073 break;
1074
1075 #ifdef notyet
1076 case DIOCGDINFOP:
1077 *(struct disklabel **)addr = &(du->dk_dd);
1078 break;
1079
1080 case DIOCWFORMAT:
1081 if ((flag & FWRITE) == 0)
1082 error = EBADF;
1083 else {
1084 register struct format_op *fop;
1085
1086 fop = (struct format_op *)addr;
1087 aiov.iov_base = fop->df_buf;
1088 aiov.iov_len = fop->df_count;
1089 auio.uio_iov = &aiov;
1090 auio.uio_iovcnt = 1;
1091 auio.uio_resid = fop->df_count;
1092 auio.uio_segflg = 0;
1093 auio.uio_offset =
1094 fop->df_startblk * du->dk_dd.d_secsize;
1095 error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
1096 minphys, &auio);
1097 fop->df_count -= auio.uio_resid;
1098 fop->df_reg[0] = du->dk_status;
1099 fop->df_reg[1] = du->dk_error;
1100 }
1101 break;
1102 #endif
1103
1104 default:
1105 error = ENOTTY;
1106 break;
1107 }
1108 return (error);
1109 }
1110
1111 #ifdef B_FORMAT
1112 int
1113 wdformat(struct buf *bp)
1114 {
1115
1116 bp->b_flags |= B_FORMAT;
1117 return (wdstrategy(bp));
1118 }
1119 #endif
1120
1121 int
1122 wdsize(dev_t dev)
1123 {
1124 int unit = wdunit(dev), part = wdpart(dev), val;
1125 struct disk *du;
1126
1127 if (unit >= _NWD) /* 31 Jul 92*/
1128 return(-1);
1129
1130 du = wddrives[unit];
1131 if (du == 0 || du->dk_state == 0)
1132 val = wdopen (makewddev(major(dev), unit, WDRAW), FREAD, S_IFBLK, 0);
1133 if (du == 0 || val != 0 || du->dk_flags & DKFL_WRITEPROT)
1134 return (-1);
1135
1136 return((int)du->dk_dd.d_partitions[part].p_size);
1137 }
1138
1139 extern char *vmmap; /* poor name! */
1140
1141 int
1142 wddump(dev_t dev) /* dump core after a system crash */
1143 {
1144 register struct disk *du; /* disk unit to do the IO */
1145 register struct bt_bad *bt_ptr;
1146 long num; /* number of sectors to write */
1147 int unit, part, wdc;
1148 long blkoff, blknum, blkcnt;
1149 long cylin, head, sector, stat;
1150 long secpertrk, secpercyl, nblocks, i;
1151 char *addr;
1152 extern int Maxmem;
1153 static wddoingadump = 0 ;
1154 extern caddr_t CADDR1;
1155
1156 addr = (char *) 0; /* starting address */
1157
1158 /* toss any characters present prior to dump */
1159 while (sgetc(1))
1160 ;
1161
1162 /* size of memory to dump */
1163 num = Maxmem;
1164 unit = wdunit(dev); /* eventually support floppies? */
1165 part = wdpart(dev); /* file system */
1166 /* check for acceptable drive number */
1167 if (unit >= _NWD) return(ENXIO); /* 31 Jul 92*/
1168
1169 du = wddrives[unit];
1170 if (du == 0) return(ENXIO);
1171 /* was it ever initialized ? */
1172 if (du->dk_state < OPEN) return (ENXIO) ;
1173 if (du->dk_flags & DKFL_WRITEPROT) return(ENXIO);
1174 wdc = du->dk_port;
1175
1176 /* Convert to disk sectors */
1177 num = (u_long) num * NBPG / du->dk_dd.d_secsize;
1178
1179 /* check if controller active */
1180 /*if (wdtab.b_active) return(EFAULT); */
1181 if (wddoingadump) return(EFAULT);
1182
1183 secpertrk = du->dk_dd.d_nsectors;
1184 secpercyl = du->dk_dd.d_secpercyl;
1185 nblocks = du->dk_dd.d_partitions[part].p_size;
1186 blkoff = du->dk_dd.d_partitions[part].p_offset;
1187
1188 /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
1189 /* check transfer bounds against partition size */
1190 if ((dumplo < 0) || ((dumplo + num) > nblocks))
1191 return(EINVAL);
1192
1193 /*wdtab.b_active = 1; /* mark controller active for if we
1194 panic during the dump */
1195 wddoingadump = 1 ; i = 100000 ;
1196 while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
1197 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
1198 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
1199 while (inb(wdc+wd_status) & WDCS_BUSY) ;
1200
1201 /* some compaq controllers require this ... */
1202 wdsetctlr(dev, du);
1203
1204 blknum = dumplo + blkoff;
1205 while (num > 0) {
1206 #ifdef notdef
1207 if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
1208 if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
1209 blkcnt = secpercyl - (blknum % secpercyl);
1210 /* keep transfer within current cylinder */
1211 #endif
1212 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
1213
1214 /* compute disk address */
1215 cylin = blknum / secpercyl;
1216 head = (blknum % secpercyl) / secpertrk;
1217 sector = blknum % secpertrk;
1218
1219 #ifdef notyet
1220 /*
1221 * See if the current block is in the bad block list.
1222 * (If we have one.)
1223 */
1224 for (bt_ptr = du->dk_bad.bt_bad;
1225 bt_ptr->bt_cyl != -1; bt_ptr++) {
1226 if (bt_ptr->bt_cyl > cylin)
1227 /* Sorted list, and we passed our cylinder.
1228 quit. */
1229 break;
1230 if (bt_ptr->bt_cyl == cylin &&
1231 bt_ptr->bt_trksec == (head << 8) + sector) {
1232 /*
1233 * Found bad block. Calculate new block addr.
1234 * This starts at the end of the disk (skip the
1235 * last track which is used for the bad block list),
1236 * and works backwards to the front of the disk.
1237 */
1238 blknum = (du->dk_dd.d_secperunit)
1239 - du->dk_dd.d_nsectors
1240 - (bt_ptr - du->dk_bad.bt_bad) - 1;
1241 cylin = blknum / secpercyl;
1242 head = (blknum % secpercyl) / secpertrk;
1243 sector = blknum % secpertrk;
1244 break;
1245 }
1246
1247 #endif
1248 sector++; /* origin 1 */
1249
1250 /* select drive. */
1251 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
1252 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
1253
1254 /* transfer some blocks */
1255 outb(wdc+wd_sector, sector);
1256 outb(wdc+wd_seccnt,1);
1257 outb(wdc+wd_cyl_lo, cylin);
1258 outb(wdc+wd_cyl_hi, cylin >> 8);
1259 #ifdef notdef
1260 /* lets just talk about this first...*/
1261 pg ("sdh 0%o sector %d cyl %d addr 0x%x",
1262 inb(wdc+wd_sdh), inb(wdc+wd_sector),
1263 inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ;
1264 #endif
1265 outb(wdc+wd_command, WDCC_WRITE);
1266
1267 /* Ready to send data? */
1268 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
1269 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
1270
1271 outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
1272
1273 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
1274 /* Check data request (should be done). */
1275 if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
1276
1277 /* wait for completion */
1278 for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
1279 if (i < 0) return (EIO) ;
1280 }
1281 /* error check the xfer */
1282 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
1283
1284 if ((unsigned)addr % (1024*1024) == 0) printf("%d ", num/2048) ;
1285 /* update block count */
1286 num--;
1287 blknum++ ;
1288 (int) addr += 512;
1289
1290 /* operator aborting dump? */
1291 if (sgetc(1))
1292 return(EINTR);
1293 }
1294 return(0);
1295 }
1296 #endif
1297