Commit | Line | Data |
---|---|---|
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 | ||
27 | int noretries = 0; | |
28 | int wdquiet = 0; | |
29 | #ifdef WDDEBUG | |
30 | int wdinfoflag = 0; | |
31 | #endif | |
32 | ||
33 | #ifdef SMALL | |
34 | extern struct disklabel disklabel; | |
35 | #else | |
36 | struct disklabel wdsizes[NWD]; | |
37 | extern struct disklabel *dlp; | |
38 | #endif | |
39 | int 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 | ||
48 | struct dkbad dkbad[NWD]; | |
49 | ||
50 | wdopen(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 | ||
69 | wdstrategy(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 | */ | |
132 | wdio(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 | |
194 | retry: | |
195 | printf("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 | } | |
232 | i=getchar();*/ | |
233 | ||
234 | return (0); | |
235 | error: | |
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 | ||
246 | wdinit(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 | /* | |
270 | code to tell disk controller geometry of disk | |
271 | not currently used | |
272 | ||
273 | outb(wdc+wd_sdh, 0xa7); | |
274 | wdwait(); | |
275 | outb(wdc+wd_seccnt, 17); | |
276 | outb(wdc+wd_cyl_lo, 0); | |
277 | outb(wdc+wd_command, 0x91); | |
278 | wdwait();*/ | |
279 | ||
280 | tryagainrecal: | |
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; | |
297 | retry: | |
298 | cyloffset = 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; | |
340 | open[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 | ||
368 | wdwait() | |
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 | ||
382 | wddumpregs(wdc){ | |
383 | ||
384 | printf("err %x ", inb(wdc+wd_error)); | |
385 | printf("seccnt %d ", inb(wdc+wd_seccnt)); | |
386 | printf("sector %d ", inb(wdc+wd_sector)); | |
387 | printf("cyl %d:", inb(wdc+wd_cyl_lo)); | |
388 | printf("%d ", inb(wdc+wd_cyl_hi)); | |
389 | printf("sdh %x ", inb(wdc+wd_sdh)); | |
390 | printf("sts %x ", inb(wdc+wd_status)); | |
391 | printf("alt %x ", inb(wdc+wd_altsts)); | |
392 | printf("dig %x\n", inb(wdc+wd_digin)); | |
393 | ||
394 | } |