Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /*#define DEBUG 1*/ |
2 | /*- | |
3 | * Copyright (c) 1990 The Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Don Ahn. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | |
17 | * 3. All advertising materials mentioning features or use of this software | |
18 | * must display the following acknowledgement: | |
19 | * This product includes software developed by the University of | |
20 | * California, Berkeley and its contributors. | |
21 | * 4. Neither the name of the University nor the names of its contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
26 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
31 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
34 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
35 | * SUCH DAMAGE. | |
36 | * | |
9b4b7d6e | 37 | * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 |
4c45483e | 38 | * $Id: fd.c,v 1.6 1993/09/23 15:22:57 rgrimes Exp $ |
15637ed4 | 39 | * |
15637ed4 RG |
40 | */ |
41 | ||
42 | #include "fd.h" | |
43 | #if NFD > 0 | |
44 | ||
45 | #include "param.h" | |
46 | #include "dkbad.h" | |
47 | #include "systm.h" | |
4c45483e | 48 | #include "kernel.h" |
15637ed4 RG |
49 | #include "conf.h" |
50 | #include "file.h" | |
51 | #include "ioctl.h" | |
293464ef | 52 | #include "disklabel.h" |
15637ed4 RG |
53 | #include "buf.h" |
54 | #include "uio.h" | |
70621ef2 | 55 | #include "syslog.h" |
15637ed4 RG |
56 | #include "i386/isa/isa.h" |
57 | #include "i386/isa/isa_device.h" | |
58 | #include "i386/isa/fdreg.h" | |
59 | #include "i386/isa/icu.h" | |
60 | #include "i386/isa/rtc.h" | |
61 | #undef NFD | |
62 | #define NFD 2 | |
63 | ||
64 | #define FDUNIT(s) ((s>>3)&1) | |
65 | #define FDTYPE(s) ((s)&7) | |
66 | ||
67 | #define b_cylin b_resid | |
68 | #define FDBLK 512 | |
69 | #define NUMTYPES 4 | |
70 | ||
71 | struct fd_type { | |
72 | int sectrac; /* sectors per track */ | |
73 | int secsize; /* size code for sectors */ | |
74 | int datalen; /* data len when secsize = 0 */ | |
75 | int gap; /* gap len between sectors */ | |
76 | int tracks; /* total num of tracks */ | |
77 | int size; /* size of disk in sectors */ | |
78 | int steptrac; /* steps per cylinder */ | |
79 | int trans; /* transfer speed code */ | |
80 | int heads; /* number of heads */ | |
81 | }; | |
82 | ||
83 | struct fd_type fd_types[NUMTYPES] = | |
84 | { | |
85 | { 18,2,0xFF,0x1B,80,2880,1,0,2 }, /* 1.44 meg HD 3.5in floppy */ | |
86 | { 15,2,0xFF,0x1B,80,2400,1,0,2 }, /* 1.2 meg HD floppy */ | |
87 | { 9,2,0xFF,0x23,40,720,2,1,2 }, /* 360k floppy in 1.2meg drive */ | |
88 | { 9,2,0xFF,0x2A,40,720,1,1,2 }, /* 360k floppy in DD drive */ | |
89 | }; | |
90 | ||
91 | #define DRVS_PER_CTLR 2 | |
92 | /***********************************************************************\ | |
93 | * Per controller structure. * | |
94 | \***********************************************************************/ | |
95 | struct fdc_data | |
96 | { | |
97 | int fdcu; /* our unit number */ | |
98 | int baseport; | |
99 | int dmachan; | |
100 | int flags; | |
101 | #define FDC_ATTACHED 0x01 | |
102 | struct fd_data *fd; | |
103 | int fdu; /* the active drive */ | |
104 | struct buf head; /* Head of buf chain */ | |
105 | struct buf rhead; /* Raw head of buf chain */ | |
106 | int state; | |
107 | int retry; | |
108 | int status[7]; /* copy of the registers */ | |
109 | }fdc_data[(NFD+1)/DRVS_PER_CTLR]; | |
110 | ||
111 | /***********************************************************************\ | |
112 | * Per drive structure. * | |
113 | * N per controller (presently 2) (DRVS_PER_CTLR) * | |
114 | \***********************************************************************/ | |
115 | struct fd_data { | |
116 | struct fdc_data *fdc; | |
117 | int fdu; /* this unit number */ | |
118 | int fdsu; /* this units number on this controller */ | |
119 | int type; /* Drive type (HD, DD */ | |
120 | struct fd_type *ft; /* pointer to the type descriptor */ | |
121 | int flags; | |
122 | #define FD_OPEN 0x01 /* it's open */ | |
123 | #define FD_ACTIVE 0x02 /* it's active */ | |
124 | #define FD_MOTOR 0x04 /* motor should be on */ | |
125 | #define FD_MOTOR_WAIT 0x08 /* motor coming up */ | |
126 | int skip; | |
127 | int hddrv; | |
128 | int track; /* where we think the head is */ | |
129 | } fd_data[NFD]; | |
130 | ||
131 | /***********************************************************************\ | |
132 | * Throughout this file the following conventions will be used: * | |
133 | * fd is a pointer to the fd_data struct for the drive in question * | |
134 | * fdc is a pointer to the fdc_data struct for the controller * | |
135 | * fdu is the floppy drive unit number * | |
136 | * fdcu is the floppy controller unit number * | |
137 | * fdsu is the floppy drive unit number on that controller. (sub-unit) * | |
138 | \***********************************************************************/ | |
139 | typedef int fdu_t; | |
140 | typedef int fdcu_t; | |
141 | typedef int fdsu_t; | |
142 | typedef struct fd_data *fd_p; | |
143 | typedef struct fdc_data *fdc_p; | |
144 | ||
145 | #define DEVIDLE 0 | |
146 | #define FINDWORK 1 | |
147 | #define DOSEEK 2 | |
148 | #define SEEKCOMPLETE 3 | |
149 | #define IOCOMPLETE 4 | |
150 | #define RECALCOMPLETE 5 | |
151 | #define STARTRECAL 6 | |
152 | #define RESETCTLR 7 | |
153 | #define SEEKWAIT 8 | |
154 | #define RECALWAIT 9 | |
155 | #define MOTORWAIT 10 | |
156 | #define IOTIMEDOUT 11 | |
157 | ||
158 | #ifdef DEBUG | |
159 | char *fdstates[] = | |
160 | { | |
161 | "DEVIDLE", | |
162 | "FINDWORK", | |
163 | "DOSEEK", | |
164 | "SEEKCOMPLETE", | |
165 | "IOCOMPLETE", | |
166 | "RECALCOMPLETE", | |
167 | "STARTRECAL", | |
168 | "RESETCTLR", | |
169 | "SEEKWAIT", | |
170 | "RECALWAIT", | |
171 | "MOTORWAIT", | |
172 | "IOTIMEDOUT" | |
173 | }; | |
174 | ||
175 | ||
176 | int fd_debug = 1; | |
177 | #define TRACE0(arg) if(fd_debug) printf(arg) | |
178 | #define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) | |
4c45483e | 179 | #else /* DEBUG */ |
15637ed4 RG |
180 | #define TRACE0(arg) |
181 | #define TRACE1(arg1,arg2) | |
4c45483e | 182 | #endif /* DEBUG */ |
15637ed4 | 183 | |
4c45483e GW |
184 | static void fdstart(fdcu_t); |
185 | void fdintr(fdcu_t); | |
186 | static void fd_turnoff(caddr_t, int); | |
15637ed4 RG |
187 | |
188 | /****************************************************************************/ | |
189 | /* autoconfiguration stuff */ | |
190 | /****************************************************************************/ | |
4c45483e GW |
191 | static int fdprobe(struct isa_device *); |
192 | static int fdattach(struct isa_device *); | |
15637ed4 RG |
193 | |
194 | struct isa_driver fddriver = { | |
195 | fdprobe, fdattach, "fd", | |
196 | }; | |
197 | ||
198 | /* | |
199 | * probe for existance of controller | |
200 | */ | |
4c45483e | 201 | int |
15637ed4 | 202 | fdprobe(dev) |
4c45483e | 203 | struct isa_device *dev; |
15637ed4 RG |
204 | { |
205 | fdcu_t fdcu = dev->id_unit; | |
206 | if(fdc_data[fdcu].flags & FDC_ATTACHED) | |
207 | { | |
208 | printf("fdc: same unit (%d) used multiple times\n",fdcu); | |
209 | return 0; | |
210 | } | |
211 | ||
212 | fdc_data[fdcu].baseport = dev->id_iobase; | |
213 | ||
214 | /* see if it can handle a command */ | |
215 | if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) | |
216 | { | |
217 | return(0); | |
218 | } | |
219 | out_fdc(fdcu,0xDF); | |
220 | out_fdc(fdcu,2); | |
221 | return (IO_FDCSIZE); | |
222 | } | |
223 | ||
224 | /* | |
225 | * wire controller into system, look for floppy units | |
226 | */ | |
4c45483e | 227 | int |
15637ed4 | 228 | fdattach(dev) |
4c45483e | 229 | struct isa_device *dev; |
15637ed4 RG |
230 | { |
231 | unsigned fdt,st0, cyl; | |
232 | int hdr; | |
233 | fdu_t fdu; | |
234 | fdcu_t fdcu = dev->id_unit; | |
235 | fdc_p fdc = fdc_data + fdcu; | |
236 | fd_p fd; | |
237 | int fdsu; | |
238 | ||
239 | fdc->fdcu = fdcu; | |
240 | fdc->flags |= FDC_ATTACHED; | |
241 | fdc->dmachan = dev->id_drq; | |
242 | fdc->state = DEVIDLE; | |
243 | ||
244 | fdt = rtcin(RTC_FDISKETTE); | |
245 | hdr = 0; | |
246 | ||
247 | /* check for each floppy drive */ | |
248 | for (fdu = (fdcu * DRVS_PER_CTLR),fdsu = 0; | |
249 | ((fdu < NFD) && (fdsu < DRVS_PER_CTLR)); | |
250 | fdu++,fdsu++) | |
251 | { | |
252 | /* is there a unit? */ | |
293464ef JH |
253 | if ((fdt & 0xf0) == RTCFDT_NONE) { |
254 | #define NO_TYPE NUMTYPES | |
255 | fd_data[fdu].type = NO_TYPE; | |
15637ed4 | 256 | continue; |
293464ef | 257 | } |
15637ed4 RG |
258 | |
259 | #ifdef notyet | |
260 | /* select it */ | |
261 | fd_turnon1(fdu); | |
262 | spinwait(1000); /* 1 sec */ | |
263 | out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ | |
264 | out_fdc(fdcu,fdsu); | |
265 | spinwait(1000); /* 1 sec */ | |
266 | ||
267 | /* anything responding */ | |
268 | out_fdc(fdcu,NE7CMD_SENSEI); | |
269 | st0 = in_fdc(fdcu); | |
270 | cyl = in_fdc(fdcu); | |
271 | if (st0 & 0xd0) | |
272 | continue; | |
273 | ||
274 | #endif | |
275 | fd_data[fdu].track = -2; | |
276 | fd_data[fdu].fdc = fdc; | |
277 | fd_data[fdu].fdsu = fdsu; | |
b7c9de13 | 278 | printf("fd%d: unit %d type ", fdcu, fdu); |
15637ed4 RG |
279 | |
280 | if ((fdt & 0xf0) == RTCFDT_12M) { | |
b7c9de13 | 281 | printf("1.2MB 5.25in\n"); |
15637ed4 RG |
282 | fd_data[fdu].type = 1; |
283 | fd_data[fdu].ft = fd_types + 1; | |
284 | ||
285 | } | |
286 | if ((fdt & 0xf0) == RTCFDT_144M) { | |
b7c9de13 | 287 | printf("1.44MB 3.5in\n"); |
15637ed4 RG |
288 | fd_data[fdu].type = 0; |
289 | fd_data[fdu].ft = fd_types + 0; | |
290 | } | |
291 | ||
292 | fdt <<= 4; | |
4c45483e | 293 | fd_turnoff((caddr_t)fdu, 0); |
15637ed4 RG |
294 | hdr = 1; |
295 | } | |
296 | ||
15637ed4 RG |
297 | /* Set transfer to 500kbps */ |
298 | outb(fdc->baseport+fdctl,0); /*XXX*/ | |
4c45483e | 299 | return 1; |
15637ed4 RG |
300 | } |
301 | ||
302 | int | |
303 | fdsize(dev) | |
4c45483e | 304 | dev_t dev; |
15637ed4 RG |
305 | { |
306 | return(0); | |
307 | } | |
308 | ||
309 | /****************************************************************************/ | |
310 | /* fdstrategy */ | |
311 | /****************************************************************************/ | |
4c45483e | 312 | void fdstrategy(struct buf *bp) |
15637ed4 RG |
313 | { |
314 | register struct buf *dp,*dp0,*dp1; | |
315 | long nblocks,blknum; | |
316 | int s; | |
317 | fdcu_t fdcu; | |
318 | fdu_t fdu; | |
319 | fdc_p fdc; | |
320 | fd_p fd; | |
321 | ||
322 | fdu = FDUNIT(minor(bp->b_dev)); | |
323 | fd = &fd_data[fdu]; | |
324 | fdc = fd->fdc; | |
325 | fdcu = fdc->fdcu; | |
326 | /*type = FDTYPE(minor(bp->b_dev));*/ | |
327 | ||
328 | if ((fdu >= NFD) || (bp->b_blkno < 0)) { | |
329 | printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", | |
330 | fdu, bp->b_blkno, bp->b_bcount); | |
331 | pg("fd:error in fdstrategy"); | |
332 | bp->b_error = EINVAL; | |
333 | bp->b_flags |= B_ERROR; | |
334 | goto bad; | |
335 | } | |
336 | /* | |
337 | * Set up block calculations. | |
338 | */ | |
339 | blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; | |
340 | nblocks = fd->ft->size; | |
341 | if (blknum + (bp->b_bcount / FDBLK) > nblocks) { | |
342 | if (blknum == nblocks) { | |
343 | bp->b_resid = bp->b_bcount; | |
344 | } else { | |
345 | bp->b_error = ENOSPC; | |
346 | bp->b_flags |= B_ERROR; | |
347 | } | |
348 | goto bad; | |
349 | } | |
350 | bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); | |
351 | dp = &(fdc->head); | |
352 | s = splbio(); | |
353 | disksort(dp, bp); | |
354 | untimeout(fd_turnoff,fdu); /* a good idea */ | |
355 | fdstart(fdcu); | |
356 | splx(s); | |
357 | return; | |
358 | ||
359 | bad: | |
360 | biodone(bp); | |
361 | } | |
362 | ||
363 | /****************************************************************************/ | |
364 | /* motor control stuff */ | |
365 | /* remember to not deselect the drive we're working on */ | |
366 | /****************************************************************************/ | |
4c45483e | 367 | void |
293464ef JH |
368 | set_motor(fdcu, fdu, reset) |
369 | fdcu_t fdcu; | |
370 | fdu_t fdu; | |
371 | int reset; | |
15637ed4 RG |
372 | { |
373 | int m0,m1; | |
374 | int selunit; | |
375 | fd_p fd; | |
376 | if(fd = fdc_data[fdcu].fd)/* yes an assign! */ | |
377 | { | |
378 | selunit = fd->fdsu; | |
379 | } | |
380 | else | |
381 | { | |
382 | selunit = 0; | |
383 | } | |
384 | m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; | |
385 | m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; | |
386 | outb(fdc_data[fdcu].baseport+fdout, | |
387 | selunit | |
388 | | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) | |
389 | | (m0 ? FDO_MOEN0 : 0) | |
390 | | (m1 ? FDO_MOEN1 : 0)); | |
391 | TRACE1("[0x%x->fdout]",( | |
392 | selunit | |
393 | | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) | |
394 | | (m0 ? FDO_MOEN0 : 0) | |
395 | | (m1 ? FDO_MOEN1 : 0))); | |
396 | } | |
397 | ||
4c45483e GW |
398 | static void |
399 | fd_turnoff(caddr_t arg1, int arg2) | |
15637ed4 | 400 | { |
4c45483e | 401 | fdu_t fdu = (fdu_t)arg1; |
293464ef JH |
402 | int s; |
403 | ||
15637ed4 | 404 | fd_p fd = fd_data + fdu; |
293464ef | 405 | s = splbio(); |
15637ed4 RG |
406 | fd->flags &= ~FD_MOTOR; |
407 | set_motor(fd->fdc->fdcu,fd->fdsu,0); | |
293464ef | 408 | splx(s); |
15637ed4 RG |
409 | } |
410 | ||
4c45483e GW |
411 | void |
412 | fd_motor_on(caddr_t arg1, int arg2) | |
15637ed4 | 413 | { |
4c45483e | 414 | fdu_t fdu = (fdu_t)arg1; |
293464ef JH |
415 | int s; |
416 | ||
15637ed4 | 417 | fd_p fd = fd_data + fdu; |
293464ef | 418 | s = splbio(); |
15637ed4 RG |
419 | fd->flags &= ~FD_MOTOR_WAIT; |
420 | if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) | |
421 | { | |
293464ef | 422 | fdintr(fd->fdc->fdcu); |
15637ed4 | 423 | } |
293464ef | 424 | splx(s); |
15637ed4 RG |
425 | } |
426 | ||
4c45483e GW |
427 | static void fd_turnon1(fdu_t); |
428 | ||
429 | void | |
293464ef JH |
430 | fd_turnon(fdu) |
431 | fdu_t fdu; | |
15637ed4 RG |
432 | { |
433 | fd_p fd = fd_data + fdu; | |
434 | if(!(fd->flags & FD_MOTOR)) | |
435 | { | |
436 | fd_turnon1(fdu); | |
437 | fd->flags |= FD_MOTOR_WAIT; | |
4c45483e | 438 | timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ |
15637ed4 RG |
439 | } |
440 | } | |
441 | ||
4c45483e GW |
442 | static void |
443 | fd_turnon1(fdu_t fdu) | |
15637ed4 RG |
444 | { |
445 | fd_p fd = fd_data + fdu; | |
446 | fd->flags |= FD_MOTOR; | |
447 | set_motor(fd->fdc->fdcu,fd->fdsu,0); | |
448 | } | |
449 | ||
450 | /****************************************************************************/ | |
451 | /* fdc in/out */ | |
452 | /****************************************************************************/ | |
453 | int | |
293464ef JH |
454 | in_fdc(fdcu) |
455 | fdcu_t fdcu; | |
15637ed4 RG |
456 | { |
457 | int baseport = fdc_data[fdcu].baseport; | |
458 | int i, j = 100000; | |
459 | while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) | |
460 | != (NE7_DIO|NE7_RQM) && j-- > 0) | |
461 | if (i == NE7_RQM) return -1; | |
462 | if (j <= 0) | |
463 | return(-1); | |
464 | #ifdef DEBUG | |
465 | i = inb(baseport+fddata); | |
466 | TRACE1("[fddata->0x%x]",(unsigned char)i); | |
467 | return(i); | |
468 | #else | |
469 | return inb(baseport+fddata); | |
470 | #endif | |
471 | } | |
472 | ||
4c45483e | 473 | int |
293464ef JH |
474 | out_fdc(fdcu, x) |
475 | fdcu_t fdcu; | |
476 | int x; | |
15637ed4 RG |
477 | { |
478 | int baseport = fdc_data[fdcu].baseport; | |
105c2acd | 479 | int i; |
15637ed4 | 480 | |
105c2acd RG |
481 | /* Check that the direction bit is set */ |
482 | i = 100000; | |
15637ed4 | 483 | while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); |
105c2acd RG |
484 | if (i <= 0) return (-1); /* Floppy timed out */ |
485 | ||
486 | /* Check that the floppy controller is ready for a command */ | |
487 | i = 100000; | |
15637ed4 | 488 | while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); |
105c2acd RG |
489 | if (i <= 0) return (-1); /* Floppy timed out */ |
490 | ||
491 | /* Send the command and return */ | |
15637ed4 RG |
492 | outb(baseport+fddata,x); |
493 | TRACE1("[0x%x->fddata]",x); | |
494 | return (0); | |
495 | } | |
496 | ||
15637ed4 RG |
497 | /****************************************************************************/ |
498 | /* fdopen/fdclose */ | |
499 | /****************************************************************************/ | |
4c45483e | 500 | int |
15637ed4 RG |
501 | Fdopen(dev, flags) |
502 | dev_t dev; | |
503 | int flags; | |
504 | { | |
505 | fdu_t fdu = FDUNIT(minor(dev)); | |
506 | /*int type = FDTYPE(minor(dev));*/ | |
507 | int s; | |
508 | ||
509 | /* check bounds */ | |
293464ef | 510 | if (fdu >= NFD || fd_data[fdu].type == NO_TYPE) return(ENXIO); |
15637ed4 RG |
511 | /*if (type >= NUMTYPES) return(ENXIO);*/ |
512 | fd_data[fdu].flags |= FD_OPEN; | |
513 | ||
514 | return 0; | |
515 | } | |
516 | ||
4c45483e | 517 | int |
15637ed4 RG |
518 | fdclose(dev, flags) |
519 | dev_t dev; | |
4c45483e | 520 | int flags; |
15637ed4 RG |
521 | { |
522 | fdu_t fdu = FDUNIT(minor(dev)); | |
523 | fd_data[fdu].flags &= ~FD_OPEN; | |
524 | return(0); | |
525 | } | |
526 | ||
527 | ||
528 | /***************************************************************\ | |
529 | * fdstart * | |
530 | * We have just queued something.. if the controller is not busy * | |
531 | * then simulate the case where it has just finished a command * | |
532 | * So that it (the interrupt routine) looks on the queue for more* | |
533 | * work to do and picks up what we just added. * | |
534 | * If the controller is already busy, we need do nothing, as it * | |
535 | * will pick up our work when the present work completes * | |
536 | \***************************************************************/ | |
4c45483e | 537 | static void |
293464ef JH |
538 | fdstart(fdcu) |
539 | fdcu_t fdcu; | |
15637ed4 RG |
540 | { |
541 | register struct buf *dp,*bp; | |
542 | int s; | |
543 | fdu_t fdu; | |
544 | ||
545 | s = splbio(); | |
546 | if(fdc_data[fdcu].state == DEVIDLE) | |
547 | { | |
548 | fdintr(fdcu); | |
549 | } | |
550 | splx(s); | |
551 | } | |
552 | ||
4c45483e GW |
553 | static void |
554 | fd_timeout(caddr_t arg1, int arg2) | |
15637ed4 | 555 | { |
4c45483e | 556 | fdcu_t fdcu = (fdcu_t)arg1; |
15637ed4 RG |
557 | fdu_t fdu = fdc_data[fdcu].fdu; |
558 | int st0, st3, cyl; | |
559 | struct buf *dp,*bp; | |
293464ef | 560 | int s; |
15637ed4 RG |
561 | |
562 | dp = &fdc_data[fdcu].head; | |
293464ef | 563 | s = splbio(); |
15637ed4 RG |
564 | bp = dp->b_actf; |
565 | ||
566 | out_fdc(fdcu,NE7CMD_SENSED); | |
567 | out_fdc(fdcu,fd_data[fdu].hddrv); | |
568 | st3 = in_fdc(fdcu); | |
569 | ||
570 | out_fdc(fdcu,NE7CMD_SENSEI); | |
571 | st0 = in_fdc(fdcu); | |
572 | cyl = in_fdc(fdcu); | |
573 | printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", | |
574 | fdu, | |
575 | st0, | |
576 | NE7_ST0BITS, | |
577 | cyl, | |
578 | st3, | |
579 | NE7_ST3BITS); | |
580 | ||
581 | if (bp) | |
582 | { | |
583 | retrier(fdcu); | |
584 | fdc_data[fdcu].status[0] = 0xc0; | |
585 | fdc_data[fdcu].state = IOTIMEDOUT; | |
586 | if( fdc_data[fdcu].retry < 6) | |
587 | fdc_data[fdcu].retry = 6; | |
588 | } | |
589 | else | |
590 | { | |
591 | fdc_data[fdcu].fd = (fd_p) 0; | |
592 | fdc_data[fdcu].fdu = -1; | |
593 | fdc_data[fdcu].state = DEVIDLE; | |
594 | } | |
293464ef JH |
595 | fdintr(fdcu); |
596 | splx(s); | |
15637ed4 RG |
597 | } |
598 | ||
599 | /* just ensure it has the right spl */ | |
4c45483e GW |
600 | static void |
601 | fd_pseudointr(caddr_t arg1, int arg2) | |
15637ed4 | 602 | { |
4c45483e | 603 | fdcu_t fdcu = (fdcu_t)arg1; |
15637ed4 RG |
604 | int s; |
605 | s = splbio(); | |
606 | fdintr(fdcu); | |
607 | splx(s); | |
608 | } | |
609 | ||
610 | /***********************************************************************\ | |
611 | * fdintr * | |
612 | * keep calling the state machine until it returns a 0 * | |
613 | * ALWAYS called at SPLBIO * | |
614 | \***********************************************************************/ | |
4c45483e GW |
615 | void |
616 | fdintr(fdcu_t fdcu) | |
15637ed4 RG |
617 | { |
618 | fdc_p fdc = fdc_data + fdcu; | |
4c45483e GW |
619 | while(fdstate(fdcu, fdc)) |
620 | ; | |
15637ed4 RG |
621 | } |
622 | ||
623 | /***********************************************************************\ | |
624 | * The controller state machine. * | |
625 | * if it returns a non zero value, it should be called again immediatly * | |
626 | \***********************************************************************/ | |
4c45483e GW |
627 | int |
628 | fdstate(fdcu, fdc) | |
293464ef JH |
629 | fdcu_t fdcu; |
630 | fdc_p fdc; | |
15637ed4 | 631 | { |
4c45483e | 632 | int read, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; |
15637ed4 RG |
633 | unsigned long blknum; |
634 | fdu_t fdu = fdc->fdu; | |
635 | fd_p fd; | |
636 | register struct buf *dp,*bp; | |
637 | ||
638 | dp = &(fdc->head); | |
639 | bp = dp->b_actf; | |
640 | if(!bp) | |
641 | { | |
642 | /***********************************************\ | |
643 | * nothing left for this controller to do * | |
644 | * Force into the IDLE state, * | |
645 | \***********************************************/ | |
646 | fdc->state = DEVIDLE; | |
647 | if(fdc->fd) | |
648 | { | |
649 | printf("unexpected valid fd pointer (fdu = %d)\n" | |
650 | ,fdc->fdu); | |
651 | fdc->fd = (fd_p) 0; | |
652 | fdc->fdu = -1; | |
653 | } | |
654 | TRACE1("[fdc%d IDLE]",fdcu); | |
655 | return(0); | |
656 | } | |
657 | fdu = FDUNIT(minor(bp->b_dev)); | |
658 | fd = fd_data + fdu; | |
659 | if (fdc->fd && (fd != fdc->fd)) | |
660 | { | |
661 | printf("confused fd pointers\n"); | |
662 | } | |
663 | read = bp->b_flags & B_READ; | |
664 | TRACE1("fd%d",fdu); | |
665 | TRACE1("[%s]",fdstates[fdc->state]); | |
666 | TRACE1("(0x%x)",fd->flags); | |
667 | untimeout(fd_turnoff, fdu); | |
4c45483e | 668 | timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); |
15637ed4 RG |
669 | switch (fdc->state) |
670 | { | |
671 | case DEVIDLE: | |
672 | case FINDWORK: /* we have found new work */ | |
673 | fdc->retry = 0; | |
674 | fd->skip = 0; | |
675 | fdc->fd = fd; | |
676 | fdc->fdu = fdu; | |
677 | /*******************************************************\ | |
678 | * If the next drive has a motor startup pending, then * | |
679 | * it will start up in it's own good time * | |
680 | \*******************************************************/ | |
681 | if(fd->flags & FD_MOTOR_WAIT) | |
682 | { | |
683 | fdc->state = MOTORWAIT; | |
684 | return(0); /* come back later */ | |
685 | } | |
686 | /*******************************************************\ | |
687 | * Maybe if it's not starting, it SHOULD be starting * | |
688 | \*******************************************************/ | |
689 | if (!(fd->flags & FD_MOTOR)) | |
690 | { | |
691 | fdc->state = MOTORWAIT; | |
692 | fd_turnon(fdu); | |
693 | return(0); | |
694 | } | |
695 | else /* at least make sure we are selected */ | |
696 | { | |
697 | set_motor(fdcu,fd->fdsu,0); | |
698 | } | |
699 | fdc->state = DOSEEK; | |
700 | break; | |
701 | case DOSEEK: | |
702 | if (bp->b_cylin == fd->track) | |
703 | { | |
704 | fdc->state = SEEKCOMPLETE; | |
705 | break; | |
706 | } | |
707 | out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ | |
708 | out_fdc(fdcu,fd->fdsu); /* Drive number */ | |
709 | out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); | |
710 | fd->track = -2; | |
711 | fdc->state = SEEKWAIT; | |
4c45483e | 712 | timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); |
15637ed4 RG |
713 | return(0); /* will return later */ |
714 | case SEEKWAIT: | |
293464ef | 715 | untimeout(fd_timeout,fdcu); |
15637ed4 | 716 | /* allow heads to settle */ |
4c45483e | 717 | timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50); |
15637ed4 RG |
718 | fdc->state = SEEKCOMPLETE; |
719 | return(0); /* will return later */ | |
720 | break; | |
721 | ||
722 | case SEEKCOMPLETE : /* SEEK DONE, START DMA */ | |
723 | /* Make sure seek really happened*/ | |
724 | if(fd->track == -2) | |
725 | { | |
726 | int descyl = bp->b_cylin * fd->ft->steptrac; | |
727 | out_fdc(fdcu,NE7CMD_SENSEI); | |
728 | i = in_fdc(fdcu); | |
729 | cyl = in_fdc(fdcu); | |
730 | if (cyl != descyl) | |
731 | { | |
732 | printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, | |
733 | descyl, cyl, i, NE7_ST0BITS); | |
734 | return(retrier(fdcu)); | |
735 | } | |
736 | } | |
737 | ||
738 | fd->track = bp->b_cylin; | |
739 | isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, | |
740 | FDBLK, fdc->dmachan); | |
741 | blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK | |
742 | + fd->skip/FDBLK; | |
743 | sectrac = fd->ft->sectrac; | |
744 | sec = blknum % (sectrac * fd->ft->heads); | |
745 | head = sec / sectrac; | |
746 | sec = sec % sectrac + 1; | |
747 | /*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; | |
748 | ||
749 | if (read) | |
750 | { | |
751 | out_fdc(fdcu,NE7CMD_READ); /* READ */ | |
752 | } | |
753 | else | |
754 | { | |
755 | out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ | |
756 | } | |
757 | out_fdc(fdcu,head << 2 | fdu); /* head & unit */ | |
758 | out_fdc(fdcu,fd->track); /* track */ | |
759 | out_fdc(fdcu,head); | |
760 | out_fdc(fdcu,sec); /* sector XXX +1? */ | |
761 | out_fdc(fdcu,fd->ft->secsize); /* sector size */ | |
762 | out_fdc(fdcu,sectrac); /* sectors/track */ | |
763 | out_fdc(fdcu,fd->ft->gap); /* gap size */ | |
764 | out_fdc(fdcu,fd->ft->datalen); /* data length */ | |
765 | fdc->state = IOCOMPLETE; | |
4c45483e | 766 | timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); |
15637ed4 RG |
767 | return(0); /* will return later */ |
768 | case IOCOMPLETE: /* IO DONE, post-analyze */ | |
769 | untimeout(fd_timeout,fdcu); | |
770 | for(i=0;i<7;i++) | |
771 | { | |
772 | fdc->status[i] = in_fdc(fdcu); | |
773 | } | |
774 | case IOTIMEDOUT: /*XXX*/ | |
775 | isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, | |
776 | FDBLK, fdc->dmachan); | |
777 | if (fdc->status[0]&0xF8) | |
778 | { | |
779 | return(retrier(fdcu)); | |
780 | } | |
781 | /* All OK */ | |
782 | fd->skip += FDBLK; | |
783 | if (fd->skip < bp->b_bcount) | |
784 | { | |
785 | /* set up next transfer */ | |
786 | blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK | |
787 | + fd->skip/FDBLK; | |
788 | bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); | |
789 | fdc->state = DOSEEK; | |
790 | } | |
791 | else | |
792 | { | |
793 | /* ALL DONE */ | |
794 | fd->skip = 0; | |
795 | bp->b_resid = 0; | |
796 | dp->b_actf = bp->av_forw; | |
797 | biodone(bp); | |
798 | fdc->fd = (fd_p) 0; | |
799 | fdc->fdu = -1; | |
800 | fdc->state = FINDWORK; | |
801 | } | |
802 | return(1); | |
803 | case RESETCTLR: | |
804 | /* Try a reset, keep motor on */ | |
805 | set_motor(fdcu,fd->fdsu,1); | |
806 | DELAY(100); | |
807 | set_motor(fdcu,fd->fdsu,0); | |
808 | outb(fdc->baseport+fdctl,fd->ft->trans); | |
809 | TRACE1("[0x%x->fdctl]",fd->ft->trans); | |
810 | fdc->retry++; | |
811 | fdc->state = STARTRECAL; | |
812 | break; | |
813 | case STARTRECAL: | |
814 | out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ | |
815 | out_fdc(fdcu,0xDF); | |
816 | out_fdc(fdcu,2); | |
817 | out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ | |
818 | out_fdc(fdcu,fdu); | |
819 | fdc->state = RECALWAIT; | |
820 | return(0); /* will return later */ | |
821 | case RECALWAIT: | |
822 | /* allow heads to settle */ | |
4c45483e | 823 | timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30); |
15637ed4 RG |
824 | fdc->state = RECALCOMPLETE; |
825 | return(0); /* will return later */ | |
826 | case RECALCOMPLETE: | |
827 | out_fdc(fdcu,NE7CMD_SENSEI); | |
828 | st0 = in_fdc(fdcu); | |
829 | cyl = in_fdc(fdcu); | |
830 | if (cyl != 0) | |
831 | { | |
832 | printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, | |
833 | st0, NE7_ST0BITS, cyl); | |
834 | return(retrier(fdcu)); | |
835 | } | |
836 | fd->track = 0; | |
837 | /* Seek (probably) necessary */ | |
838 | fdc->state = DOSEEK; | |
839 | return(1); /* will return immediatly */ | |
840 | case MOTORWAIT: | |
841 | if(fd->flags & FD_MOTOR_WAIT) | |
842 | { | |
843 | return(0); /* time's not up yet */ | |
844 | } | |
845 | fdc->state = DOSEEK; | |
846 | return(1); /* will return immediatly */ | |
847 | default: | |
848 | printf("Unexpected FD int->"); | |
849 | out_fdc(fdcu,NE7CMD_SENSEI); | |
850 | st0 = in_fdc(fdcu); | |
851 | cyl = in_fdc(fdcu); | |
852 | printf("ST0 = %lx, PCN = %lx\n",i,sec); | |
853 | out_fdc(fdcu,0x4A); | |
854 | out_fdc(fdcu,fd->fdsu); | |
855 | for(i=0;i<7;i++) { | |
856 | fdc->status[i] = in_fdc(fdcu); | |
857 | } | |
858 | printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", | |
859 | fdc->status[0], | |
860 | fdc->status[1], | |
861 | fdc->status[2], | |
862 | fdc->status[3], | |
863 | fdc->status[4], | |
864 | fdc->status[5], | |
865 | fdc->status[6] ); | |
866 | return(0); | |
867 | } | |
868 | return(1); /* Come back immediatly to new state */ | |
869 | } | |
870 | ||
4c45483e | 871 | int |
293464ef JH |
872 | retrier(fdcu) |
873 | fdcu_t fdcu; | |
15637ed4 RG |
874 | { |
875 | fdc_p fdc = fdc_data + fdcu; | |
876 | register struct buf *dp,*bp; | |
877 | ||
878 | dp = &(fdc->head); | |
879 | bp = dp->b_actf; | |
880 | ||
881 | switch(fdc->retry) | |
882 | { | |
883 | case 0: case 1: case 2: | |
884 | fdc->state = SEEKCOMPLETE; | |
885 | break; | |
886 | case 3: case 4: case 5: | |
887 | fdc->state = STARTRECAL; | |
888 | break; | |
889 | case 6: | |
890 | fdc->state = RESETCTLR; | |
891 | break; | |
892 | case 7: | |
893 | break; | |
894 | default: | |
895 | { | |
70621ef2 RG |
896 | diskerr(bp, "fd", "hard error", LOG_PRINTF, |
897 | fdc->fd->skip, (struct disklabel *)NULL); | |
898 | printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); | |
15637ed4 RG |
899 | printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); |
900 | printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS); | |
15637ed4 | 901 | printf("cyl %d hd %d sec %d)\n", |
70621ef2 | 902 | fdc->status[3], fdc->status[4], fdc->status[5]); |
15637ed4 RG |
903 | } |
904 | bp->b_flags |= B_ERROR; | |
905 | bp->b_error = EIO; | |
906 | bp->b_resid = bp->b_bcount - fdc->fd->skip; | |
907 | dp->b_actf = bp->av_forw; | |
908 | fdc->fd->skip = 0; | |
909 | biodone(bp); | |
70621ef2 | 910 | fdc->state = FINDWORK; |
15637ed4 RG |
911 | fdc->fd = (fd_p) 0; |
912 | fdc->fdu = -1; | |
293464ef | 913 | /* XXX abort current command, if any. */ |
70621ef2 | 914 | return(1); |
15637ed4 RG |
915 | } |
916 | fdc->retry++; | |
917 | return(1); | |
918 | } | |
919 | ||
293464ef JH |
920 | /* |
921 | * fdioctl() from jc@irbs.UUCP (John Capo) | |
922 | * i386/i386/conf.c needs to have fdioctl() declared and remove the line that | |
923 | * defines fdioctl to be enxio. | |
924 | * | |
925 | * TODO: Reformat. | |
926 | * Think about allocating buffer off stack. | |
927 | * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). | |
928 | * Watch out for NetBSD's different *disklabel() interface. | |
929 | */ | |
930 | ||
931 | int | |
932 | fdioctl (dev, cmd, addr, flag) | |
933 | dev_t dev; | |
934 | int cmd; | |
935 | caddr_t addr; | |
936 | int flag; | |
937 | { | |
938 | struct fd_type *fdt; | |
939 | struct disklabel *dl; | |
940 | char buffer[DEV_BSIZE]; | |
941 | int error; | |
942 | ||
943 | error = 0; | |
944 | ||
945 | switch (cmd) | |
946 | { | |
947 | case DIOCGDINFO: | |
948 | bzero(buffer, sizeof (buffer)); | |
949 | dl = (struct disklabel *)buffer; | |
950 | dl->d_secsize = FDBLK; | |
70621ef2 | 951 | fdt = fd_data[FDUNIT(minor(dev))].ft; |
293464ef JH |
952 | dl->d_secpercyl = fdt->size / fdt->tracks; |
953 | dl->d_type = DTYPE_FLOPPY; | |
954 | ||
955 | if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL) | |
956 | error = 0; | |
957 | else | |
958 | error = EINVAL; | |
959 | ||
960 | *(struct disklabel *)addr = *dl; | |
961 | break; | |
962 | ||
963 | case DIOCSDINFO: | |
964 | ||
965 | if ((flag & FWRITE) == 0) | |
966 | error = EBADF; | |
15637ed4 | 967 | |
293464ef JH |
968 | break; |
969 | ||
970 | case DIOCWLABEL: | |
971 | if ((flag & FWRITE) == 0) | |
972 | error = EBADF; | |
973 | ||
974 | break; | |
975 | ||
976 | case DIOCWDINFO: | |
977 | if ((flag & FWRITE) == 0) | |
978 | { | |
979 | error = EBADF; | |
980 | break; | |
981 | } | |
982 | ||
983 | dl = (struct disklabel *)addr; | |
984 | ||
985 | if (error = setdisklabel ((struct disklabel *)buffer, dl, 0, NULL)) | |
986 | break; | |
987 | ||
988 | error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer, NULL); | |
989 | break; | |
990 | ||
991 | default: | |
992 | error = EINVAL; | |
993 | break; | |
994 | } | |
995 | return (error); | |
996 | } | |
997 | ||
998 | #endif |