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