Commit | Line | Data |
---|---|---|
9f1c0bf9 | 1 | /*#define DEBUG 1*/ |
eac289cc WJ |
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 | * | |
37 | * @(#)fd.c 7.4 (Berkeley) 5/25/91 | |
9f1c0bf9 JE |
38 | * |
39 | * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE | |
40 | * -------------------- ----- ---------------------- | |
41 | * CURRENT PATCH LEVEL: 1 00153 | |
42 | * -------------------- ----- ---------------------- | |
43 | * | |
44 | * 20 Apr 93 Julian Elischer Heavily re worked, see notes below | |
45 | * | |
46 | * Largely rewritten to handle multiple controllers and drives | |
47 | * By Julian Elischer, Sun Apr 4 16:34:33 WST 1993 | |
48 | */ | |
49 | char rev[] = "$Revision: 1.10 $"; | |
50 | /* | |
51 | * $Header: /usr/src/sys.386bsd/i386/isa/RCS/fd.c,v 1.10 93/04/13 16:53:29 root Exp $ | |
52 | */ | |
53 | /* | |
54 | * $Log: fd.c,v $ | |
55 | * Revision 1.10 93/04/13 16:53:29 root | |
56 | * make sure turning off a drive motor doesn't deselect another | |
57 | * drive active at the time. | |
58 | * Also added a pointer from the fd_data to it's fd_type. | |
59 | * | |
60 | * Revision 1.9 93/04/13 15:31:02 root | |
61 | * make all seeks go through DOSEEK state so are sure of being done right. | |
62 | * | |
63 | * Revision 1.8 93/04/12 21:20:13 root | |
64 | * only check if old fd is the one we are working on if there IS | |
65 | * an old fd pointer. (in fdstate()) | |
66 | * | |
67 | * Revision 1.7 93/04/11 17:05:35 root | |
68 | * cleanup timeouts etc. | |
69 | * also fix bug to select teh correct drive when running > 1 drive | |
70 | * at a time. | |
71 | * | |
72 | * Revision 1.6 93/04/05 00:48:45 root | |
73 | * change a timeout and add version to banner message | |
74 | * | |
75 | * Revision 1.5 93/04/04 16:39:08 root | |
76 | * first working version.. some floppy controllers don't seem to | |
77 | * like 2 int. status inquiries in a row. | |
78 | * | |
eac289cc WJ |
79 | */ |
80 | ||
81 | #include "fd.h" | |
82 | #if NFD > 0 | |
83 | ||
84 | #include "param.h" | |
85 | #include "dkbad.h" | |
86 | #include "systm.h" | |
87 | #include "conf.h" | |
88 | #include "file.h" | |
89 | #include "ioctl.h" | |
90 | #include "buf.h" | |
91 | #include "uio.h" | |
9f1c0bf9 | 92 | #include "i386/isa/isa.h" |
eac289cc WJ |
93 | #include "i386/isa/isa_device.h" |
94 | #include "i386/isa/fdreg.h" | |
95 | #include "i386/isa/icu.h" | |
96 | #include "i386/isa/rtc.h" | |
97 | #undef NFD | |
98 | #define NFD 2 | |
99 | ||
100 | #define FDUNIT(s) ((s>>3)&1) | |
101 | #define FDTYPE(s) ((s)&7) | |
102 | ||
103 | #define b_cylin b_resid | |
eac289cc WJ |
104 | #define FDBLK 512 |
105 | #define NUMTYPES 4 | |
106 | ||
107 | struct fd_type { | |
108 | int sectrac; /* sectors per track */ | |
109 | int secsize; /* size code for sectors */ | |
110 | int datalen; /* data len when secsize = 0 */ | |
111 | int gap; /* gap len between sectors */ | |
112 | int tracks; /* total num of tracks */ | |
113 | int size; /* size of disk in sectors */ | |
114 | int steptrac; /* steps per cylinder */ | |
115 | int trans; /* transfer speed code */ | |
9f1c0bf9 | 116 | int heads; /* number of heads */ |
eac289cc WJ |
117 | }; |
118 | ||
9f1c0bf9 JE |
119 | struct fd_type fd_types[NUMTYPES] = |
120 | { | |
121 | { 18,2,0xFF,0x1B,80,2880,1,0,2 }, /* 1.44 meg HD 3.5in floppy */ | |
122 | { 15,2,0xFF,0x1B,80,2400,1,0,2 }, /* 1.2 meg HD floppy */ | |
123 | { 9,2,0xFF,0x23,40,720,2,1,2 }, /* 360k floppy in 1.2meg drive */ | |
124 | { 9,2,0xFF,0x2A,40,720,1,1,2 }, /* 360k floppy in DD drive */ | |
eac289cc WJ |
125 | }; |
126 | ||
9f1c0bf9 JE |
127 | #define DRVS_PER_CTLR 2 |
128 | /***********************************************************************\ | |
129 | * Per controller structure. * | |
130 | \***********************************************************************/ | |
131 | struct fdc_data | |
132 | { | |
133 | int fdcu; /* our unit number */ | |
134 | int baseport; | |
135 | int dmachan; | |
136 | int flags; | |
137 | #define FDC_ATTACHED 0x01 | |
138 | struct fd_data *fd; | |
139 | int fdu; /* the active drive */ | |
eac289cc WJ |
140 | struct buf head; /* Head of buf chain */ |
141 | struct buf rhead; /* Raw head of buf chain */ | |
9f1c0bf9 JE |
142 | int state; |
143 | int retry; | |
144 | int status[7]; /* copy of the registers */ | |
145 | }fdc_data[(NFD+1)/DRVS_PER_CTLR]; | |
146 | ||
147 | /***********************************************************************\ | |
148 | * Per drive structure. * | |
149 | * N per controller (presently 2) (DRVS_PER_CTLR) * | |
150 | \***********************************************************************/ | |
151 | struct fd_data { | |
152 | struct fdc_data *fdc; | |
153 | int fdu; /* this unit number */ | |
154 | int fdsu; /* this units number on this controller */ | |
155 | int type; /* Drive type (HD, DD */ | |
156 | struct fd_type *ft; /* pointer to the type descriptor */ | |
157 | int flags; | |
158 | #define FD_OPEN 0x01 /* it's open */ | |
159 | #define FD_ACTIVE 0x02 /* it's active */ | |
160 | #define FD_MOTOR 0x04 /* motor should be on */ | |
161 | #define FD_MOTOR_WAIT 0x08 /* motor coming up */ | |
162 | int skip; | |
163 | int hddrv; | |
164 | int track; /* where we think the head is */ | |
165 | } fd_data[NFD]; | |
166 | ||
167 | /***********************************************************************\ | |
168 | * Throughout this file the following conventions will be used: * | |
169 | * fd is a pointer to the fd_data struct for the drive in question * | |
170 | * fdc is a pointer to the fdc_data struct for the controller * | |
171 | * fdu is the floppy drive unit number * | |
172 | * fdcu is the floppy controller unit number * | |
173 | * fdsu is the floppy drive unit number on that controller. (sub-unit) * | |
174 | \***********************************************************************/ | |
175 | typedef int fdu_t; | |
176 | typedef int fdcu_t; | |
177 | typedef int fdsu_t; | |
178 | typedef struct fd_data *fd_p; | |
179 | typedef struct fdc_data *fdc_p; | |
180 | ||
181 | #define DEVIDLE 0 | |
182 | #define FINDWORK 1 | |
183 | #define DOSEEK 2 | |
184 | #define SEEKCOMPLETE 3 | |
185 | #define IOCOMPLETE 4 | |
186 | #define RECALCOMPLETE 5 | |
187 | #define STARTRECAL 6 | |
188 | #define RESETCTLR 7 | |
189 | #define SEEKWAIT 8 | |
190 | #define RECALWAIT 9 | |
191 | #define MOTORWAIT 10 | |
192 | #define IOTIMEDOUT 11 | |
193 | ||
194 | #ifdef DEBUG | |
195 | char *fdstates[] = | |
196 | { | |
197 | "DEVIDLE", | |
198 | "FINDWORK", | |
199 | "DOSEEK", | |
200 | "SEEKCOMPLETE", | |
201 | "IOCOMPLETE", | |
202 | "RECALCOMPLETE", | |
203 | "STARTRECAL", | |
204 | "RESETCTLR", | |
205 | "SEEKWAIT", | |
206 | "RECALWAIT", | |
207 | "MOTORWAIT", | |
208 | "IOTIMEDOUT" | |
209 | }; | |
eac289cc WJ |
210 | |
211 | ||
9f1c0bf9 JE |
212 | int fd_debug = 1; |
213 | #define TRACE0(arg) if(fd_debug) printf(arg) | |
214 | #define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) | |
215 | #else DEBUG | |
216 | #define TRACE0(arg) | |
217 | #define TRACE1(arg1,arg2) | |
218 | #endif DEBUG | |
eac289cc | 219 | |
9f1c0bf9 | 220 | extern int hz; |
eac289cc | 221 | /* state needed for current transfer */ |
eac289cc WJ |
222 | |
223 | /****************************************************************************/ | |
224 | /* autoconfiguration stuff */ | |
225 | /****************************************************************************/ | |
226 | int fdprobe(), fdattach(), fd_turnoff(); | |
227 | ||
228 | struct isa_driver fddriver = { | |
229 | fdprobe, fdattach, "fd", | |
230 | }; | |
231 | ||
232 | /* | |
233 | * probe for existance of controller | |
234 | */ | |
235 | fdprobe(dev) | |
236 | struct isa_device *dev; | |
237 | { | |
9f1c0bf9 JE |
238 | fdcu_t fdcu = dev->id_unit; |
239 | if(fdc_data[fdcu].flags & FDC_ATTACHED) | |
240 | { | |
241 | printf("fdc: same unit (%d) used multiple times\n",fdcu); | |
242 | return 0; | |
243 | } | |
244 | ||
245 | fdc_data[fdcu].baseport = dev->id_iobase; | |
eac289cc WJ |
246 | |
247 | /* see if it can handle a command */ | |
9f1c0bf9 JE |
248 | if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) |
249 | { | |
eac289cc WJ |
250 | return(0); |
251 | } | |
9f1c0bf9 JE |
252 | out_fdc(fdcu,0xDF); |
253 | out_fdc(fdcu,2); | |
254 | return (IO_FDCSIZE); | |
eac289cc WJ |
255 | } |
256 | ||
257 | /* | |
258 | * wire controller into system, look for floppy units | |
259 | */ | |
260 | fdattach(dev) | |
261 | struct isa_device *dev; | |
262 | { | |
eac289cc | 263 | unsigned fdt,st0, cyl; |
9f1c0bf9 JE |
264 | int hdr; |
265 | fdu_t fdu; | |
266 | fdcu_t fdcu = dev->id_unit; | |
267 | fdc_p fdc = fdc_data + fdcu; | |
268 | fd_p fd; | |
269 | int fdsu; | |
270 | ||
271 | fdc->fdcu = fdcu; | |
272 | fdc->flags |= FDC_ATTACHED; | |
273 | fdc->dmachan = dev->id_drq; | |
274 | fdc->state = DEVIDLE; | |
eac289cc WJ |
275 | |
276 | fdt = rtcin(RTC_FDISKETTE); | |
277 | hdr = 0; | |
278 | ||
279 | /* check for each floppy drive */ | |
9f1c0bf9 JE |
280 | for (fdu = (fdcu * DRVS_PER_CTLR),fdsu = 0; |
281 | ((fdu < NFD) && (fdsu < DRVS_PER_CTLR)); | |
282 | fdu++,fdsu++) | |
283 | { | |
eac289cc WJ |
284 | /* is there a unit? */ |
285 | if ((fdt & 0xf0) == RTCFDT_NONE) | |
286 | continue; | |
287 | ||
288 | #ifdef notyet | |
289 | /* select it */ | |
9f1c0bf9 JE |
290 | fd_turnon1(fdu); |
291 | spinwait(1000); /* 1 sec */ | |
292 | out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ | |
293 | out_fdc(fdcu,fdsu); | |
294 | spinwait(1000); /* 1 sec */ | |
eac289cc WJ |
295 | |
296 | /* anything responding */ | |
9f1c0bf9 JE |
297 | out_fdc(fdcu,NE7CMD_SENSEI); |
298 | st0 = in_fdc(fdcu); | |
299 | cyl = in_fdc(fdcu); | |
eac289cc WJ |
300 | if (st0 & 0xd0) |
301 | continue; | |
302 | ||
303 | #endif | |
9f1c0bf9 JE |
304 | fd_data[fdu].track = -2; |
305 | fd_data[fdu].fdc = fdc; | |
306 | fd_data[fdu].fdsu = fdsu; | |
eac289cc WJ |
307 | /* yes, announce it */ |
308 | if (!hdr) | |
309 | printf(" drives "); | |
310 | else | |
311 | printf(", "); | |
9f1c0bf9 | 312 | printf("%d: ", fdu); |
eac289cc | 313 | |
9f1c0bf9 | 314 | |
eac289cc WJ |
315 | if ((fdt & 0xf0) == RTCFDT_12M) { |
316 | printf("1.2M"); | |
9f1c0bf9 JE |
317 | fd_data[fdu].type = 1; |
318 | fd_data[fdu].ft = fd_types + 1; | |
319 | ||
eac289cc WJ |
320 | } |
321 | if ((fdt & 0xf0) == RTCFDT_144M) { | |
322 | printf("1.44M"); | |
9f1c0bf9 JE |
323 | fd_data[fdu].type = 0; |
324 | fd_data[fdu].ft = fd_types + 0; | |
eac289cc WJ |
325 | } |
326 | ||
327 | fdt <<= 4; | |
9f1c0bf9 | 328 | fd_turnoff(fdu); |
eac289cc WJ |
329 | hdr = 1; |
330 | } | |
331 | ||
9f1c0bf9 | 332 | printf(" %s ",rev); |
eac289cc | 333 | /* Set transfer to 500kbps */ |
9f1c0bf9 | 334 | outb(fdc->baseport+fdctl,0); /*XXX*/ |
eac289cc WJ |
335 | } |
336 | ||
337 | int | |
338 | fdsize(dev) | |
339 | dev_t dev; | |
340 | { | |
341 | return(0); | |
342 | } | |
343 | ||
344 | /****************************************************************************/ | |
345 | /* fdstrategy */ | |
346 | /****************************************************************************/ | |
347 | fdstrategy(bp) | |
348 | register struct buf *bp; /* IO operation to perform */ | |
349 | { | |
350 | register struct buf *dp,*dp0,*dp1; | |
351 | long nblocks,blknum; | |
9f1c0bf9 JE |
352 | int s; |
353 | fdcu_t fdcu; | |
354 | fdu_t fdu; | |
355 | fdc_p fdc; | |
356 | fd_p fd; | |
357 | ||
358 | fdu = FDUNIT(minor(bp->b_dev)); | |
359 | fd = &fd_data[fdu]; | |
360 | fdc = fd->fdc; | |
361 | fdcu = fdc->fdcu; | |
eac289cc | 362 | /*type = FDTYPE(minor(bp->b_dev));*/ |
eac289cc | 363 | |
9f1c0bf9 JE |
364 | if ((fdu >= NFD) || (bp->b_blkno < 0)) { |
365 | printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", | |
366 | fdu, bp->b_blkno, bp->b_bcount); | |
eac289cc WJ |
367 | pg("fd:error in fdstrategy"); |
368 | bp->b_error = EINVAL; | |
369 | bp->b_flags |= B_ERROR; | |
370 | goto bad; | |
371 | } | |
372 | /* | |
373 | * Set up block calculations. | |
374 | */ | |
375 | blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; | |
9f1c0bf9 | 376 | nblocks = fd->ft->size; |
eac289cc WJ |
377 | if (blknum + (bp->b_bcount / FDBLK) > nblocks) { |
378 | if (blknum == nblocks) { | |
379 | bp->b_resid = bp->b_bcount; | |
380 | } else { | |
381 | bp->b_error = ENOSPC; | |
382 | bp->b_flags |= B_ERROR; | |
383 | } | |
384 | goto bad; | |
385 | } | |
9f1c0bf9 JE |
386 | bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); |
387 | dp = &(fdc->head); | |
eac289cc WJ |
388 | s = splbio(); |
389 | disksort(dp, bp); | |
9f1c0bf9 JE |
390 | untimeout(fd_turnoff,fdu); /* a good idea */ |
391 | fdstart(fdcu); | |
eac289cc WJ |
392 | splx(s); |
393 | return; | |
394 | ||
395 | bad: | |
396 | biodone(bp); | |
397 | } | |
398 | ||
399 | /****************************************************************************/ | |
400 | /* motor control stuff */ | |
9f1c0bf9 | 401 | /* remember to not deselect the drive we're working on */ |
eac289cc | 402 | /****************************************************************************/ |
9f1c0bf9 | 403 | set_motor(fdcu_t fdcu, fdu_t fdu, int reset) |
eac289cc WJ |
404 | { |
405 | int m0,m1; | |
9f1c0bf9 JE |
406 | int selunit; |
407 | fd_p fd; | |
408 | if(fd = fdc_data[fdcu].fd)/* yes an assign! */ | |
409 | { | |
410 | selunit = fd->fdsu; | |
411 | } | |
412 | else | |
413 | { | |
414 | selunit = 0; | |
415 | } | |
416 | m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; | |
417 | m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; | |
418 | outb(fdc_data[fdcu].baseport+fdout, | |
419 | selunit | |
eac289cc WJ |
420 | | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) |
421 | | (m0 ? FDO_MOEN0 : 0) | |
422 | | (m1 ? FDO_MOEN1 : 0)); | |
9f1c0bf9 JE |
423 | TRACE1("[0x%x->fdout]",( |
424 | selunit | |
425 | | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) | |
426 | | (m0 ? FDO_MOEN0 : 0) | |
427 | | (m1 ? FDO_MOEN1 : 0))); | |
428 | } | |
429 | ||
430 | fd_turnoff(fdu_t fdu) | |
431 | { | |
432 | fd_p fd = fd_data + fdu; | |
433 | fd->flags &= ~FD_MOTOR; | |
434 | set_motor(fd->fdc->fdcu,fd->fdsu,0); | |
435 | } | |
436 | ||
437 | fd_motor_on(fdu_t fdu) | |
438 | { | |
439 | fd_p fd = fd_data + fdu; | |
440 | fd->flags &= ~FD_MOTOR_WAIT; | |
441 | if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) | |
442 | { | |
443 | fd_pseudointr(fd->fdc->fdcu); | |
444 | } | |
eac289cc WJ |
445 | } |
446 | ||
9f1c0bf9 | 447 | fd_turnon(fdu_t fdu) |
eac289cc | 448 | { |
9f1c0bf9 JE |
449 | fd_p fd = fd_data + fdu; |
450 | if(!(fd->flags & FD_MOTOR)) | |
451 | { | |
452 | fd_turnon1(fdu); | |
453 | fd->flags |= FD_MOTOR_WAIT; | |
454 | timeout(fd_motor_on,fdu,hz); /* in 1 sec its ok */ | |
455 | } | |
eac289cc WJ |
456 | } |
457 | ||
9f1c0bf9 | 458 | fd_turnon1(fdu_t fdu) |
eac289cc | 459 | { |
9f1c0bf9 JE |
460 | fd_p fd = fd_data + fdu; |
461 | fd->flags |= FD_MOTOR; | |
462 | set_motor(fd->fdc->fdcu,fd->fdsu,0); | |
eac289cc WJ |
463 | } |
464 | ||
465 | /****************************************************************************/ | |
466 | /* fdc in/out */ | |
467 | /****************************************************************************/ | |
468 | int | |
9f1c0bf9 | 469 | in_fdc(fdcu_t fdcu) |
eac289cc | 470 | { |
9f1c0bf9 | 471 | int baseport = fdc_data[fdcu].baseport; |
eac289cc | 472 | int i, j = 100000; |
9f1c0bf9 JE |
473 | while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) |
474 | != (NE7_DIO|NE7_RQM) && j-- > 0) | |
eac289cc WJ |
475 | if (i == NE7_RQM) return -1; |
476 | if (j <= 0) | |
477 | return(-1); | |
9f1c0bf9 JE |
478 | #ifdef DEBUG |
479 | i = inb(baseport+fddata); | |
480 | TRACE1("[fddata->0x%x]",(unsigned char)i); | |
481 | return(i); | |
482 | #else | |
483 | return inb(baseport+fddata); | |
484 | #endif | |
eac289cc WJ |
485 | } |
486 | ||
9f1c0bf9 | 487 | out_fdc(fdcu_t fdcu,int x) |
eac289cc | 488 | { |
9f1c0bf9 | 489 | int baseport = fdc_data[fdcu].baseport; |
eac289cc WJ |
490 | int i = 100000; |
491 | ||
9f1c0bf9 JE |
492 | while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); |
493 | while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); | |
eac289cc | 494 | if (i <= 0) return (-1); |
9f1c0bf9 JE |
495 | outb(baseport+fddata,x); |
496 | TRACE1("[0x%x->fddata]",x); | |
eac289cc WJ |
497 | return (0); |
498 | } | |
499 | ||
500 | static fdopenf; | |
501 | /****************************************************************************/ | |
502 | /* fdopen/fdclose */ | |
503 | /****************************************************************************/ | |
504 | Fdopen(dev, flags) | |
505 | dev_t dev; | |
506 | int flags; | |
507 | { | |
9f1c0bf9 | 508 | fdu_t fdu = FDUNIT(minor(dev)); |
eac289cc WJ |
509 | /*int type = FDTYPE(minor(dev));*/ |
510 | int s; | |
511 | ||
eac289cc | 512 | /* check bounds */ |
9f1c0bf9 | 513 | if (fdu >= NFD) return(ENXIO); |
eac289cc | 514 | /*if (type >= NUMTYPES) return(ENXIO);*/ |
9f1c0bf9 | 515 | fd_data[fdu].flags |= FD_OPEN; |
eac289cc | 516 | |
eac289cc WJ |
517 | return 0; |
518 | } | |
519 | ||
520 | fdclose(dev, flags) | |
521 | dev_t dev; | |
522 | { | |
9f1c0bf9 JE |
523 | fdu_t fdu = FDUNIT(minor(dev)); |
524 | fd_data[fdu].flags &= ~FD_OPEN; | |
eac289cc WJ |
525 | return(0); |
526 | } | |
527 | ||
528 | ||
9f1c0bf9 JE |
529 | /***************************************************************\ |
530 | * fdstart * | |
531 | * We have just queued something.. if the controller is not busy * | |
532 | * then simulate the case where it has just finished a command * | |
533 | * So that it (the interrupt routine) looks on the queue for more* | |
534 | * work to do and picks up what we just added. * | |
535 | * If the controller is already busy, we need do nothing, as it * | |
536 | * will pick up our work when the present work completes * | |
537 | \***************************************************************/ | |
538 | fdstart(fdcu_t fdcu) | |
eac289cc WJ |
539 | { |
540 | register struct buf *dp,*bp; | |
541 | int s; | |
9f1c0bf9 | 542 | fdu_t fdu; |
eac289cc | 543 | |
eac289cc | 544 | s = splbio(); |
9f1c0bf9 JE |
545 | if(fdc_data[fdcu].state == DEVIDLE) |
546 | { | |
547 | fdintr(fdcu); | |
eac289cc WJ |
548 | } |
549 | splx(s); | |
550 | } | |
551 | ||
9f1c0bf9 | 552 | fd_timeout(fdcu_t fdcu) |
eac289cc | 553 | { |
9f1c0bf9 | 554 | fdu_t fdu = fdc_data[fdcu].fdu; |
eac289cc WJ |
555 | int st0, st3, cyl; |
556 | struct buf *dp,*bp; | |
557 | ||
9f1c0bf9 | 558 | dp = &fdc_data[fdcu].head; |
eac289cc WJ |
559 | bp = dp->b_actf; |
560 | ||
9f1c0bf9 JE |
561 | out_fdc(fdcu,NE7CMD_SENSED); |
562 | out_fdc(fdcu,fd_data[fdu].hddrv); | |
563 | st3 = in_fdc(fdcu); | |
564 | ||
565 | out_fdc(fdcu,NE7CMD_SENSEI); | |
566 | st0 = in_fdc(fdcu); | |
567 | cyl = in_fdc(fdcu); | |
568 | printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", | |
569 | fdu, | |
570 | st0, | |
571 | NE7_ST0BITS, | |
572 | cyl, | |
573 | st3, | |
574 | NE7_ST3BITS); | |
575 | ||
576 | if (bp) | |
577 | { | |
578 | retrier(fdcu); | |
579 | fdc_data[fdcu].status[0] = 0xc0; | |
580 | fdc_data[fdcu].state = IOTIMEDOUT; | |
581 | if( fdc_data[fdcu].retry < 6) | |
582 | fdc_data[fdcu].retry = 6; | |
583 | } | |
584 | else | |
585 | { | |
586 | fdc_data[fdcu].fd = (fd_p) 0; | |
587 | fdc_data[fdcu].fdu = -1; | |
588 | fdc_data[fdcu].state = DEVIDLE; | |
589 | } | |
590 | fd_pseudointr(fdcu); | |
591 | } | |
eac289cc | 592 | |
9f1c0bf9 JE |
593 | /* just ensure it has the right spl */ |
594 | fd_pseudointr(fdcu_t fdcu) | |
595 | { | |
596 | int s; | |
597 | s = splbio(); | |
598 | fdintr(fdcu); | |
599 | splx(s); | |
600 | } | |
eac289cc | 601 | |
9f1c0bf9 JE |
602 | /***********************************************************************\ |
603 | * fdintr * | |
604 | * keep calling the state machine until it returns a 0 * | |
605 | * ALWAYS called at SPLBIO * | |
606 | \***********************************************************************/ | |
607 | fdintr(fdcu_t fdcu) | |
608 | { | |
609 | fdc_p fdc = fdc_data + fdcu; | |
610 | while(fdstate(fdcu, fdc)); | |
eac289cc WJ |
611 | } |
612 | ||
9f1c0bf9 JE |
613 | /***********************************************************************\ |
614 | * The controller state machine. * | |
615 | * if it returns a non zero value, it should be called again immediatly * | |
616 | \***********************************************************************/ | |
617 | int fdstate(fdcu_t fdcu, fdc_p fdc) | |
eac289cc | 618 | { |
eac289cc WJ |
619 | int read,head,trac,sec,i,s,sectrac,cyl,st0; |
620 | unsigned long blknum; | |
9f1c0bf9 JE |
621 | fdu_t fdu = fdc->fdu; |
622 | fd_p fd; | |
623 | register struct buf *dp,*bp; | |
eac289cc | 624 | |
9f1c0bf9 | 625 | dp = &(fdc->head); |
eac289cc | 626 | bp = dp->b_actf; |
9f1c0bf9 JE |
627 | if(!bp) |
628 | { | |
629 | /***********************************************\ | |
630 | * nothing left for this controller to do * | |
631 | * Force into the IDLE state, * | |
632 | \***********************************************/ | |
633 | fdc->state = DEVIDLE; | |
634 | if(fdc->fd) | |
635 | { | |
636 | printf("unexpected valid fd pointer (fdu = %d)\n" | |
637 | ,fdc->fdu); | |
638 | fdc->fd = (fd_p) 0; | |
639 | fdc->fdu = -1; | |
640 | } | |
641 | TRACE1("[fdc%d IDLE]",fdcu); | |
642 | return(0); | |
643 | } | |
644 | fdu = FDUNIT(minor(bp->b_dev)); | |
645 | fd = fd_data + fdu; | |
646 | if (fdc->fd && (fd != fdc->fd)) | |
647 | { | |
648 | printf("confused fd pointers\n"); | |
649 | } | |
eac289cc | 650 | read = bp->b_flags & B_READ; |
9f1c0bf9 JE |
651 | TRACE1("fd%d",fdu); |
652 | TRACE1("[%s]",fdstates[fdc->state]); | |
653 | TRACE1("(0x%x)",fd->flags); | |
654 | untimeout(fd_turnoff, fdu); | |
655 | timeout(fd_turnoff,fdu,4 * hz); | |
656 | switch (fdc->state) | |
657 | { | |
658 | case DEVIDLE: | |
659 | case FINDWORK: /* we have found new work */ | |
660 | fdc->retry = 0; | |
661 | fd->skip = 0; | |
662 | fdc->fd = fd; | |
663 | fdc->fdu = fdu; | |
664 | /*******************************************************\ | |
665 | * If the next drive has a motor startup pending, then * | |
666 | * it will start up in it's own good time * | |
667 | \*******************************************************/ | |
668 | if(fd->flags & FD_MOTOR_WAIT) | |
669 | { | |
670 | fdc->state = MOTORWAIT; | |
671 | return(0); /* come back later */ | |
672 | } | |
673 | /*******************************************************\ | |
674 | * Maybe if it's not starting, it SHOULD be starting * | |
675 | \*******************************************************/ | |
676 | if (!(fd->flags & FD_MOTOR)) | |
677 | { | |
678 | fdc->state = MOTORWAIT; | |
679 | fd_turnon(fdu); | |
680 | return(0); | |
681 | } | |
682 | else /* at least make sure we are selected */ | |
683 | { | |
684 | set_motor(fdcu,fd->fdsu,0); | |
685 | } | |
686 | fdc->state = DOSEEK; | |
687 | break; | |
688 | case DOSEEK: | |
689 | if (bp->b_cylin == fd->track) | |
690 | { | |
691 | fdc->state = SEEKCOMPLETE; | |
692 | break; | |
693 | } | |
694 | out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ | |
695 | out_fdc(fdcu,fd->fdsu); /* Drive number */ | |
696 | out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); | |
697 | fd->track = -2; | |
698 | fdc->state = SEEKWAIT; | |
699 | return(0); /* will return later */ | |
700 | case SEEKWAIT: | |
701 | /* allow heads to settle */ | |
702 | timeout(fd_pseudointr,fdcu,hz/50); | |
703 | fdc->state = SEEKCOMPLETE; | |
704 | return(0); /* will return later */ | |
705 | break; | |
706 | ||
707 | case SEEKCOMPLETE : /* SEEK DONE, START DMA */ | |
eac289cc | 708 | /* Make sure seek really happened*/ |
9f1c0bf9 JE |
709 | if(fd->track == -2) |
710 | { | |
711 | int descyl = bp->b_cylin * fd->ft->steptrac; | |
712 | out_fdc(fdcu,NE7CMD_SENSEI); | |
713 | i = in_fdc(fdcu); | |
714 | cyl = in_fdc(fdcu); | |
715 | if (cyl != descyl) | |
716 | { | |
717 | printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, | |
718 | descyl, cyl, i, NE7_ST0BITS); | |
719 | return(retrier(fdcu)); | |
eac289cc WJ |
720 | } |
721 | } | |
722 | ||
9f1c0bf9 JE |
723 | fd->track = bp->b_cylin; |
724 | isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, | |
725 | FDBLK, fdc->dmachan); | |
eac289cc | 726 | blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK |
9f1c0bf9 JE |
727 | + fd->skip/FDBLK; |
728 | sectrac = fd->ft->sectrac; | |
729 | sec = blknum % (sectrac * fd->ft->heads); | |
eac289cc WJ |
730 | head = sec / sectrac; |
731 | sec = sec % sectrac + 1; | |
9f1c0bf9 JE |
732 | /*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; |
733 | ||
734 | if (read) | |
735 | { | |
736 | out_fdc(fdcu,NE7CMD_READ); /* READ */ | |
eac289cc | 737 | } |
9f1c0bf9 JE |
738 | else |
739 | { | |
740 | out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ | |
741 | } | |
742 | out_fdc(fdcu,head << 2 | fdu); /* head & unit */ | |
743 | out_fdc(fdcu,fd->track); /* track */ | |
744 | out_fdc(fdcu,head); | |
745 | out_fdc(fdcu,sec); /* sector XXX +1? */ | |
746 | out_fdc(fdcu,fd->ft->secsize); /* sector size */ | |
747 | out_fdc(fdcu,sectrac); /* sectors/track */ | |
748 | out_fdc(fdcu,fd->ft->gap); /* gap size */ | |
749 | out_fdc(fdcu,fd->ft->datalen); /* data length */ | |
750 | fdc->state = IOCOMPLETE; | |
751 | timeout(fd_timeout,fdcu,2 * hz); | |
752 | return(0); /* will return later */ | |
753 | case IOCOMPLETE: /* IO DONE, post-analyze */ | |
754 | untimeout(fd_timeout,fdcu); | |
755 | for(i=0;i<7;i++) | |
756 | { | |
757 | fdc->status[i] = in_fdc(fdcu); | |
758 | } | |
759 | case IOTIMEDOUT: /*XXX*/ | |
760 | isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, | |
761 | FDBLK, fdc->dmachan); | |
762 | if (fdc->status[0]&0xF8) | |
763 | { | |
764 | return(retrier(fdcu)); | |
eac289cc WJ |
765 | } |
766 | /* All OK */ | |
9f1c0bf9 JE |
767 | fd->skip += FDBLK; |
768 | if (fd->skip < bp->b_bcount) | |
769 | { | |
770 | /* set up next transfer */ | |
771 | blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK | |
772 | + fd->skip/FDBLK; | |
773 | bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); | |
774 | fdc->state = DOSEEK; | |
775 | } | |
776 | else | |
777 | { | |
eac289cc | 778 | /* ALL DONE */ |
9f1c0bf9 | 779 | fd->skip = 0; |
eac289cc WJ |
780 | bp->b_resid = 0; |
781 | dp->b_actf = bp->av_forw; | |
782 | biodone(bp); | |
9f1c0bf9 JE |
783 | fdc->fd = (fd_p) 0; |
784 | fdc->fdu = -1; | |
785 | fdc->state = FINDWORK; | |
eac289cc | 786 | } |
9f1c0bf9 JE |
787 | return(1); |
788 | case RESETCTLR: | |
eac289cc | 789 | /* Try a reset, keep motor on */ |
9f1c0bf9 | 790 | set_motor(fdcu,fd->fdsu,1); |
eac289cc | 791 | DELAY(100); |
9f1c0bf9 JE |
792 | set_motor(fdcu,fd->fdsu,0); |
793 | outb(fdc->baseport+fdctl,fd->ft->trans); | |
794 | TRACE1("[0x%x->fdctl]",fd->ft->trans); | |
795 | fdc->retry++; | |
796 | fdc->state = STARTRECAL; | |
eac289cc | 797 | break; |
9f1c0bf9 JE |
798 | case STARTRECAL: |
799 | out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ | |
800 | out_fdc(fdcu,0xDF); | |
801 | out_fdc(fdcu,2); | |
802 | out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ | |
803 | out_fdc(fdcu,fdu); | |
804 | fdc->state = RECALWAIT; | |
805 | return(0); /* will return later */ | |
806 | case RECALWAIT: | |
eac289cc | 807 | /* allow heads to settle */ |
9f1c0bf9 JE |
808 | timeout(fd_pseudointr,fdcu,hz/30); |
809 | fdc->state = RECALCOMPLETE; | |
810 | return(0); /* will return later */ | |
811 | case RECALCOMPLETE: | |
812 | out_fdc(fdcu,NE7CMD_SENSEI); | |
813 | st0 = in_fdc(fdcu); | |
814 | cyl = in_fdc(fdcu); | |
815 | if (cyl != 0) | |
816 | { | |
817 | printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, | |
818 | st0, NE7_ST0BITS, cyl); | |
819 | return(retrier(fdcu)); | |
820 | } | |
821 | fd->track = 0; | |
822 | /* Seek (probably) necessary */ | |
823 | fdc->state = DOSEEK; | |
824 | return(1); /* will return immediatly */ | |
825 | case MOTORWAIT: | |
826 | if(fd->flags & FD_MOTOR_WAIT) | |
827 | { | |
828 | return(0); /* time's not up yet */ | |
829 | } | |
830 | fdc->state = DOSEEK; | |
831 | return(1); /* will return immediatly */ | |
eac289cc WJ |
832 | default: |
833 | printf("Unexpected FD int->"); | |
9f1c0bf9 JE |
834 | out_fdc(fdcu,NE7CMD_SENSEI); |
835 | st0 = in_fdc(fdcu); | |
836 | cyl = in_fdc(fdcu); | |
eac289cc | 837 | printf("ST0 = %lx, PCN = %lx\n",i,sec); |
9f1c0bf9 JE |
838 | out_fdc(fdcu,0x4A); |
839 | out_fdc(fdcu,fd->fdsu); | |
eac289cc | 840 | for(i=0;i<7;i++) { |
9f1c0bf9 | 841 | fdc->status[i] = in_fdc(fdcu); |
eac289cc WJ |
842 | } |
843 | printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", | |
9f1c0bf9 JE |
844 | fdc->status[0], |
845 | fdc->status[1], | |
846 | fdc->status[2], | |
847 | fdc->status[3], | |
848 | fdc->status[4], | |
849 | fdc->status[5], | |
850 | fdc->status[6] ); | |
851 | return(0); | |
eac289cc | 852 | } |
9f1c0bf9 JE |
853 | return(1); /* Come back immediatly to new state */ |
854 | } | |
855 | ||
856 | retrier(fdcu_t fdcu) | |
857 | { | |
858 | fdc_p fdc = fdc_data + fdcu; | |
859 | register struct buf *dp,*bp; | |
860 | ||
861 | dp = &(fdc->head); | |
862 | bp = dp->b_actf; | |
863 | ||
864 | switch(fdc->retry) | |
865 | { | |
866 | case 0: case 1: case 2: | |
867 | fdc->state = SEEKCOMPLETE; | |
868 | break; | |
869 | case 3: case 4: case 5: | |
870 | fdc->state = STARTRECAL; | |
eac289cc | 871 | break; |
eac289cc | 872 | case 6: |
9f1c0bf9 JE |
873 | fdc->state = RESETCTLR; |
874 | break; | |
eac289cc WJ |
875 | case 7: |
876 | break; | |
877 | default: | |
9f1c0bf9 JE |
878 | { |
879 | printf("fd%d: hard error (ST0 %b ", | |
880 | fdc->fdu, fdc->status[0], NE7_ST0BITS); | |
881 | printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); | |
882 | printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS); | |
883 | printf(" ST3 %b ", fdc->status[3], NE7_ST3BITS); | |
884 | printf("cyl %d hd %d sec %d)\n", | |
885 | fdc->status[4], fdc->status[5], fdc->status[6]); | |
886 | } | |
887 | bp->b_flags |= B_ERROR; | |
888 | bp->b_error = EIO; | |
889 | bp->b_resid = bp->b_bcount - fdc->fd->skip; | |
890 | dp->b_actf = bp->av_forw; | |
891 | fdc->fd->skip = 0; | |
892 | biodone(bp); | |
893 | fdc->state = FINDWORK; | |
894 | fdc->fd = (fd_p) 0; | |
895 | fdc->fdu = -1; | |
896 | return(1); | |
eac289cc | 897 | } |
9f1c0bf9 JE |
898 | fdc->retry++; |
899 | return(1); | |
eac289cc WJ |
900 | } |
901 | ||
eac289cc | 902 | #endif |
9f1c0bf9 | 903 |