new copyright notice
[unix-history] / usr / src / sys / i386 / stand / wd.c
CommitLineData
edfe6365
WN
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * %sccs.include.noredist.c%
9 *
10 * @(#)wd.c 7.1 (Berkeley) %G%
11 */
12
13/* device driver for winchester disk */
14
15#include "../h/param.h"
16#include "../h/inode.h"
17#include "../h/fs.h"
18#include "../h/dkbad.h"
19#include "../h/disk.h"
20#include "../isa/atio.h"
21#include "../isa/wdreg.h"
22#include "saio.h"
23
24#define NWD 2 /* number of hard disk units supported, max 2 */
25#define RETRIES 5 /* number of retries before giving up */
26
27int noretries = 0;
28int wdquiet = 0;
29#ifdef WDDEBUG
30int wdinfoflag = 0;
31#endif
32
33#ifdef SMALL
34extern struct disklabel disklabel;
35#else
36struct disklabel wdsizes[NWD];
37extern struct disklabel *dlp;
38#endif
39int cyloffset;
40
41/*
42 * Record for the bad block forwarding code.
43 * This is initialized to be empty until the bad-sector table
44 * is read from the disk.
45 */
46#define TRKSEC(trk,sec) ((trk << 8) + sec)
47
48struct dkbad dkbad[NWD];
49
50wdopen(io)
51 register struct iob *io;
52{
53 register struct disklabel *dd;
54 int unit, partition;
55
56 unit = minor_unit(minor(io->i_ino.i_dev));
57 partition = minor_partition(minor(io->i_ino.i_dev));
58#ifdef SMALL
59 dd = &disklabel;
60#else
61 dd = &wdsizes[unit];
62 if (partition >= 8)
63 _stop("Invalid partition number");
64#endif
65 if (wdinit(io))
66 _stop("wd initialization error");
67}
68
69wdstrategy(io,func)
70 register struct iob *io;
71{
72 register int iosize; /* number of sectors to do IO for this loop */
73 register daddr_t sector;
74 int nblocks, cyloff;
75 int unit, partition;
76 char *address;
77 register struct disklabel *dd;
78
79 unit = minor_unit(minor(io->i_ino.i_dev));
80 partition = minor_partition(minor(io->i_ino.i_dev));
81 if ((unsigned)unit >= NWD) {
82 printf("wd: unit %d\n", unit);
83 return(-1);
84 }
85#ifdef SMALL
86 dd = &disklabel;
87#else
88 dd = &wdsizes[unit];
89#endif
90 iosize = io->i_cc / dd->dk_secsize;
91 /*
92 * Convert PGSIZE "blocks" to sectors.
93 * Note: doing the conversions this way limits the partition size
94 * to about 8 million sectors (1-8 Gb).
95 */
96 sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->dk_secsize;
97 nblocks = dd->dk_partition[partition].nblocks;
98 cyloff = dd->dk_partition[partition].cyloff;
99 if (iosize < 0 || sector + iosize > nblocks || sector < 0) {
100#ifdef WDDEBUG
101 printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n",
102 io->i_bn, iosize, partition, nblocks);
103#endif
104 printf("wdstrategy - I/O out of filesystem boundaries\n");
105 return(-1);
106 }
107 if (io->i_bn * DEV_BSIZE % dd->dk_secsize) {
108 printf("wdstrategy - transfer starts in midsector\n");
109 return(-1);
110 }
111 if (io->i_cc % dd->dk_secsize) {
112 printf("wd: transfer of partial sector\n");
113 return(-1);
114 }
115
116 address = io->i_ma;
117 sector += cyloff * dd->dk_secpercyl;
118 while (iosize > 0) {
119 if (wdio(func, unit, sector, address))
120 return(-1);
121 iosize--;
122 sector++;
123 address += dd->dk_secsize;
124 }
125 return(io->i_cc);
126}
127
128/*
129 * Routine to do a one-sector I/O operation, and wait for it
130 * to complete.
131 */
132wdio(func, unit, blknm, addr)
133 short *addr;
134{
135 struct disklabel *dd;
136 int wdc = IO_WD0;
137 struct bt_bad *bt_ptr;
138 int i;
139 int retries = 0;
140 long cylin, head, sector;
141 u_char opcode;
142
143#ifdef SMALL
144 dd = &disklabel;
145#else
146 dd = &wdsizes[unit];
147#endif
148 if (func == WRITE)
149 opcode = WDCC_WRITE;
150 else
151 opcode = WDCC_READ;
152
153 /* Calculate data for output. */
154 cylin = blknm / dd->dk_secpercyl;
155 head = (blknm % dd->dk_secpercyl) / dd->dk_nsectors;
156 sector = blknm % dd->dk_nsectors + 1;
157
158#ifdef notyet
159 /*
160 * See if the current block is in the bad block list.
161 */
162 if (blknm > 7) /* should be BBSIZE */
163 for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
164 if (bt_ptr->bt_cyl > cylin)
165 /* Sorted list, and we passed our cylinder. quit. */
166 break;
167 if (bt_ptr->bt_cyl == cylin &&
168 bt_ptr->bt_trksec == (head << 8) + sector) {
169 /*
170 * Found bad block. Calculate new block addr.
171 * This starts at the end of the disk (skip the
172 * last track which is used for the bad block list),
173 * and works backwards to the front of the disk.
174 */
175#ifdef WDDEBUG
176 if (wdinfoflag)
177 printf("--- badblock code -> Old = %d; ",
178 blknm);
179#endif
180 blknm = dd->dk_secperunit - dd->dk_nsectors
181 - (bt_ptr - dkbad[unit].bt_bad) - 1;
182 cylin = blknm / dd->dk_secpercyl;
183 head = (blknm % dd->dk_secpercyl) / dd->dk_nsectors;
184 sector = blknm % dd->dk_nsectors;
185#ifdef WDDEBUG
186 if (wdinfoflag)
187 printf("new = %d\n", blknm);
188#endif
189 break;
190 }
191 }
192
193#endif
194retry:
195printf("sec %d sdh %x cylin %d\n", sector
196 , WDSD_IBM | (unit<<4) | (head & 0xf), cylin);
197 outb(wdc+wd_precomp, 0xff);
198 outb(wdc+wd_seccnt, 1);
199 outb(wdc+wd_sector, sector);
200 outb(wdc+wd_cyl_lo, cylin);
201 outb(wdc+wd_cyl_hi, cylin >> 8);
202
203 /* Set up the SDH register (select drive). */
204 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
205 while ((inb(wdc+wd_altsts) & WDCS_READY) == 0) ;
206
207 outb(wdc+wd_command, opcode);
208 while (opcode == WDCC_READ && (inb(wdc+wd_altsts) & WDCS_BUSY))
209 ;
210 /* Did we get an error? */
211 if (opcode == WDCC_READ && (inb(wdc+wd_altsts) & WDCS_ERR))
212 goto error;
213
214 /* Ready to remove data? */
215 while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) ;
216
217 if (opcode == WDCC_READ)
218 insw(wdc+wd_data,addr,256);
219 else outsw(wdc+wd_data,addr,256);
220
221 /* Check data request (should be done). */
222 if (inb(wdc+wd_altsts) & WDCS_DRQ) goto error;
223
224 while (opcode == WDCC_WRITE && (inb(wdc+wd_altsts) & WDCS_BUSY)) ;
225
226 if (inb(wdc+wd_altsts) & WDCS_ERR) goto error;
227
228/*for (i=0; i < 256 ; i++){
229 if((i%20) == 0) printf("\n");
230 printf("%x ", addr[i]);
231}
232i=getchar();*/
233
234 return (0);
235error:
236 wddumpregs(wdc);
237 if (++retries < RETRIES)
238 goto retry;
239 if (!wdquiet)
240 printf("wd%d: hard %s error: sector %d, status %b error %b\n", unit,
241 opcode == WDCC_READ? "read" : "write", blknm,
242 inb(wdc+wd_status), WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
243 return (-1);
244}
245
246wdinit(io)
247 struct iob *io;
248{
249 int wdc = IO_WD0;
250 struct disklabel *dd;
251 unsigned int unit;
252 struct dkbad *db;
253 int i, errcnt = 0;
254 char buf[512];
255 static open[NWD];
256
257 /* reset controller */
258 outb(wdc+wd_ctlr, 4); wait(10); outb(wdc+wd_ctlr, 0);
259 wdwait();
260
261 unit = minor_unit(minor(io->i_ino.i_dev));
262#ifdef SMALL
263 dd = &disklabel;
264 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
265 wdwait();
266#else
267 dd = &wdsizes[unit];
268 if (open[unit]) return(0);
269/*
270code to tell disk controller geometry of disk
271not currently used
272
273 outb(wdc+wd_sdh, 0xa7);
274wdwait();
275 outb(wdc+wd_seccnt, 17);
276 outb(wdc+wd_cyl_lo, 0);
277 outb(wdc+wd_command, 0x91);
278 wdwait();*/
279
280tryagainrecal:
281 /* set SDH, step rate, do restore */
282 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
283 wdwait();
284 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
285 wdwait();
286 if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
287 printf("wd%d: recal status %b error %b\n",
288 unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
289 wddumpregs(wdc);
290 if (++errcnt < 10)
291 goto tryagainrecal;
292 return(-1);
293 }
294#endif SMALL
295#ifndef SMALL
296 errcnt = 0;
297retry:
298cyloffset = 290;
299 /*
300 * Read in sector 0 to get the pack label and geometry.
301 */
302 outb(wdc+wd_precomp, 0xff); /* sometimes this is head bit 3 */
303 outb(wdc+wd_seccnt, 1);
304 outb(wdc+wd_sector, 1);
305 outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
306 outb(wdc+wd_cyl_hi, (cyloffset >> 8));
307 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
308 wdwait();
309 outb(wdc+wd_command, WDCC_READ);
310 wdwait();
311 if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
312 wddumpregs(wdc);
313 if (++errcnt < RETRIES)
314 goto retry;
315 if (!wdquiet)
316 printf("wd%d: reading label, status %b error %b\n",
317 unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
318 return(-1);
319 }
320
321 /* Ready to remove data? */
322 while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) ;
323
324 i = insw(wdc+wd_data, buf, 256);
325
326 /*printf("magic %x,insw %x, %x\n",
327 ((struct disklabel *) (buf + LABELOFFSET))->dk_magic, i, buf);*/
328 if (((struct disklabel *) (buf + LABELOFFSET))->dk_magic == DISKMAGIC) {
329 *dd = * (struct disklabel *) (buf + LABELOFFSET);
330 open[unit] = 1;
331 } else {
332 if (!wdquiet)
333 printf("wd%d: bad disk label\n", unit);
334 if (io->i_flgs & F_FILE) return(-1);
335 dkbad[unit].bt_bad[0].bt_cyl = -1;
336 dd->dk_secpercyl = 1999999 ; dd->dk_nsectors = 17 ;
337 dd->dk_secsize = 512;
338 outb(wdc+wd_precomp, 0xff); /* force head 3 bit off */
339/**dd = *dlp;
340open[unit] = 1;*/
341 return (0) ;
342 }
343 dkbad[unit].bt_bad[0].bt_cyl = -1;
344 outb(wdc+wd_precomp, dd->dk_precompcyl / 4);
345#endif SMALL
346
347#ifdef notyet
348 /*
349 * Read bad sector table into memory.
350 */
351 i = 0;
352 do {
353 int blknm = dd->dk_secperunit - dd->dk_nsectors + i;
354 errcnt = wdio(READ, unit, blknm, buf);
355 } while (errcnt && (i += 2) < 10 && i < dd->dk_nsectors);
356 db = (struct dkbad *)(buf);
357 if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC)
358 dkbad[unit] = *db;
359 else {
360 if (!wdquiet)
361 printf("wd%d: error in bad-sector file\n", unit);
362 dkbad[unit].bt_bad[0].bt_cyl = -1;
363 }
364#endif
365 return(0);
366}
367
368wdwait()
369{
370 register wdc = IO_WD0;
371 register i = 0;
372
373 while (inb(wdc+wd_altsts) & WDCS_BUSY)
374 ;
375 while ((inb(wdc+wd_altsts) & WDCS_READY) == 0)
376 if (i++ > 100000)
377 return(-1);
378 return(0);
379}
380
381
382wddumpregs(wdc){
383
384printf("err %x ", inb(wdc+wd_error));
385printf("seccnt %d ", inb(wdc+wd_seccnt));
386printf("sector %d ", inb(wdc+wd_sector));
387printf("cyl %d:", inb(wdc+wd_cyl_lo));
388printf("%d ", inb(wdc+wd_cyl_hi));
389printf("sdh %x ", inb(wdc+wd_sdh));
390printf("sts %x ", inb(wdc+wd_status));
391printf("alt %x ", inb(wdc+wd_altsts));
392printf("dig %x\n", inb(wdc+wd_digin));
393
394}