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