1 | /* $NetBSD: fd.c,v 1.110 2015/12/08 20:36:15 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2003, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Charles M. Hannum. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /*- |
33 | * Copyright (c) 1990 The Regents of the University of California. |
34 | * All rights reserved. |
35 | * |
36 | * This code is derived from software contributed to Berkeley by |
37 | * Don Ahn. |
38 | * |
39 | * Redistribution and use in source and binary forms, with or without |
40 | * modification, are permitted provided that the following conditions |
41 | * are met: |
42 | * 1. Redistributions of source code must retain the above copyright |
43 | * notice, this list of conditions and the following disclaimer. |
44 | * 2. Redistributions in binary form must reproduce the above copyright |
45 | * notice, this list of conditions and the following disclaimer in the |
46 | * documentation and/or other materials provided with the distribution. |
47 | * 3. Neither the name of the University nor the names of its contributors |
48 | * may be used to endorse or promote products derived from this software |
49 | * without specific prior written permission. |
50 | * |
51 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
52 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
53 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
54 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
55 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
56 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
57 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
58 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
59 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
60 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
61 | * SUCH DAMAGE. |
62 | * |
63 | * @(#)fd.c 7.4 (Berkeley) 5/25/91 |
64 | */ |
65 | |
66 | /* |
67 | * Floppy formatting facilities merged from FreeBSD fd.c driver: |
68 | * Id: fd.c,v 1.53 1995/03/12 22:40:56 joerg Exp |
69 | * which carries the same copyright/redistribution notice as shown above with |
70 | * the addition of the following statement before the "Redistribution and |
71 | * use ..." clause: |
72 | * |
73 | * Copyright (c) 1993, 1994 by |
74 | * jc@irbs.UUCP (John Capo) |
75 | * vak@zebub.msk.su (Serge Vakulenko) |
76 | * ache@astral.msk.su (Andrew A. Chernov) |
77 | * |
78 | * Copyright (c) 1993, 1994, 1995 by |
79 | * joerg_wunsch@uriah.sax.de (Joerg Wunsch) |
80 | * dufault@hda.com (Peter Dufault) |
81 | */ |
82 | |
83 | #include <sys/cdefs.h> |
84 | __KERNEL_RCSID(0, "$NetBSD: fd.c,v 1.110 2015/12/08 20:36:15 christos Exp $" ); |
85 | |
86 | #include "opt_ddb.h" |
87 | |
88 | /* |
89 | * XXX This driver should be properly MI'd some day, but this allows us |
90 | * XXX to eliminate a lot of code duplication for now. |
91 | */ |
92 | #if !defined(alpha) && !defined(algor) && !defined(atari) && \ |
93 | !defined(bebox) && !defined(evbmips) && !defined(i386) && \ |
94 | !defined(prep) && !defined(sandpoint) && !defined(x86_64) && \ |
95 | !defined(mvmeppc) && !defined(ofppc) |
96 | #error platform not supported by this driver, yet |
97 | #endif |
98 | |
99 | #include <sys/param.h> |
100 | #include <sys/systm.h> |
101 | #include <sys/callout.h> |
102 | #include <sys/kernel.h> |
103 | #include <sys/file.h> |
104 | #include <sys/ioctl.h> |
105 | #include <sys/device.h> |
106 | #include <sys/disklabel.h> |
107 | #include <sys/disk.h> |
108 | #include <sys/buf.h> |
109 | #include <sys/bufq.h> |
110 | #include <sys/malloc.h> |
111 | #include <sys/uio.h> |
112 | #include <sys/syslog.h> |
113 | #include <sys/queue.h> |
114 | #include <sys/proc.h> |
115 | #include <sys/fdio.h> |
116 | #include <sys/conf.h> |
117 | #include <sys/vnode.h> |
118 | #include <sys/rndsource.h> |
119 | |
120 | #include <prop/proplib.h> |
121 | |
122 | #include <dev/cons.h> |
123 | |
124 | #include <sys/cpu.h> |
125 | #include <sys/bus.h> |
126 | |
127 | #include "locators.h" |
128 | |
129 | #if defined(atari) |
130 | /* |
131 | * On the atari, it is configured as fdcisa |
132 | */ |
133 | #define FDCCF_DRIVE FDCISACF_DRIVE |
134 | #define FDCCF_DRIVE_DEFAULT FDCISACF_DRIVE_DEFAULT |
135 | |
136 | #define fd_cd fdisa_cd |
137 | #endif /* atari */ |
138 | |
139 | #include <sys/intr.h> |
140 | |
141 | #include <dev/isa/isavar.h> |
142 | #include <dev/isa/isadmavar.h> |
143 | |
144 | #include <dev/isa/fdreg.h> |
145 | #include <dev/isa/fdcvar.h> |
146 | |
147 | #if defined(i386) || defined(x86_64) |
148 | |
149 | #include <dev/ic/mc146818reg.h> /* for NVRAM access */ |
150 | #include <i386/isa/nvram.h> |
151 | |
152 | #if defined(i386) |
153 | #include "mca.h" |
154 | #if NMCA > 0 |
155 | #include <machine/mca_machdep.h> /* for MCA_system */ |
156 | #endif |
157 | #endif |
158 | |
159 | #endif /* i386 || x86_64 */ |
160 | |
161 | #include <dev/isa/fdvar.h> |
162 | |
163 | #define FDUNIT(dev) (minor(dev) / 8) |
164 | #define FDTYPE(dev) (minor(dev) % 8) |
165 | |
166 | /* (mis)use device use flag to identify format operation */ |
167 | #define B_FORMAT B_DEVPRIVATE |
168 | |
169 | /* controller driver configuration */ |
170 | int fdprint(void *, const char *); |
171 | |
172 | #if NMCA > 0 |
173 | /* MCA - specific entries */ |
174 | const struct fd_type mca_fd_types[] = { |
175 | { 18,2,36,2,0xff,0x0f,0x1b,0x6c,80,2880,1,FDC_500KBPS,0xf6,1, "1.44MB" }, /* 1.44MB diskette - XXX try 16ms step rate */ |
176 | { 9,2,18,2,0xff,0x4f,0x2a,0x50,80,1440,1,FDC_250KBPS,0xf6,1, "720KB" }, /* 3.5 inch 720kB diskette - XXX try 24ms step rate */ |
177 | }; |
178 | #endif /* NMCA > 0 */ |
179 | |
180 | /* The order of entries in the following table is important -- BEWARE! */ |
181 | |
182 | #if defined(atari) |
183 | const struct fd_type fd_types[] = { |
184 | { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS,0xf6,1, "360KB/PC" }, /* 360kB PC diskettes */ |
185 | { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS,0xf6,1, "720KB" }, /* 3.5 inch 720kB diskette */ |
186 | { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,0xf6,1, "1.44MB" }, /* 1.44MB diskette */ |
187 | }; |
188 | #else |
189 | const struct fd_type fd_types[] = { |
190 | { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,0xf6,1, "1.44MB" }, /* 1.44MB diskette */ |
191 | { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS,0xf6,1, "1.2MB" }, /* 1.2 MB AT-diskettes */ |
192 | { 9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS,0xf6,1, "360KB/AT" }, /* 360kB in 1.2MB drive */ |
193 | { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS,0xf6,1, "360KB/PC" }, /* 360kB PC diskettes */ |
194 | { 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS,0xf6,1, "720KB" }, /* 3.5 inch 720kB diskette */ |
195 | { 9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS,0xf6,1, "720KB/x" }, /* 720kB in 1.2MB drive */ |
196 | { 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS,0xf6,1, "360KB/x" }, /* 360kB in 720kB drive */ |
197 | }; |
198 | #endif /* defined(atari) */ |
199 | |
200 | void fdcfinishattach(device_t); |
201 | int fdprobe(device_t, cfdata_t, void *); |
202 | void fdattach(device_t, device_t, void *); |
203 | static int fddetach(device_t, int); |
204 | static int fdcintr1(struct fdc_softc *); |
205 | static void fdcintrcb(void *); |
206 | static bool fdcsuspend(device_t, const pmf_qual_t *); |
207 | static bool fdcresume(device_t, const pmf_qual_t *); |
208 | |
209 | extern struct cfdriver fd_cd; |
210 | |
211 | #ifdef atari |
212 | CFATTACH_DECL_NEW(fdisa, sizeof(struct fd_softc), |
213 | fdprobe, fdattach, fddetach, NULL); |
214 | #else |
215 | CFATTACH_DECL_NEW(fd, sizeof(struct fd_softc), |
216 | fdprobe, fdattach, fddetach, NULL); |
217 | #endif |
218 | |
219 | dev_type_open(fdopen); |
220 | dev_type_close(fdclose); |
221 | dev_type_read(fdread); |
222 | dev_type_write(fdwrite); |
223 | dev_type_ioctl(fdioctl); |
224 | dev_type_strategy(fdstrategy); |
225 | |
226 | const struct bdevsw fd_bdevsw = { |
227 | .d_open = fdopen, |
228 | .d_close = fdclose, |
229 | .d_strategy = fdstrategy, |
230 | .d_ioctl = fdioctl, |
231 | .d_dump = nodump, |
232 | .d_psize = nosize, |
233 | .d_discard = nodiscard, |
234 | .d_flag = D_DISK |
235 | }; |
236 | |
237 | const struct cdevsw fd_cdevsw = { |
238 | .d_open = fdopen, |
239 | .d_close = fdclose, |
240 | .d_read = fdread, |
241 | .d_write = fdwrite, |
242 | .d_ioctl = fdioctl, |
243 | .d_stop = nostop, |
244 | .d_tty = notty, |
245 | .d_poll = nopoll, |
246 | .d_mmap = nommap, |
247 | .d_kqfilter = nokqfilter, |
248 | .d_discard = nodiscard, |
249 | .d_flag = D_DISK |
250 | }; |
251 | |
252 | void fdgetdisklabel(struct fd_softc *); |
253 | int fd_get_parms(struct fd_softc *); |
254 | void fdstart(struct fd_softc *); |
255 | |
256 | struct dkdriver fddkdriver = { |
257 | .d_strategy = fdstrategy, |
258 | .d_minphys = minphys |
259 | }; |
260 | |
261 | #if defined(i386) || defined(x86_64) |
262 | const struct fd_type *fd_nvtotype(const char *, int, int); |
263 | #endif /* i386 || x86_64 */ |
264 | void fd_set_motor(struct fdc_softc *fdc, int reset); |
265 | void fd_motor_off(void *arg); |
266 | void fd_motor_on(void *arg); |
267 | int fdcresult(struct fdc_softc *fdc); |
268 | void fdcstart(struct fdc_softc *fdc); |
269 | void fdcstatus(device_t, int, const char *); |
270 | void fdctimeout(void *arg); |
271 | void fdcretry(struct fdc_softc *fdc); |
272 | void fdfinish(struct fd_softc *fd, struct buf *bp); |
273 | static const struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t); |
274 | int fdformat(dev_t, struct ne7_fd_formb *, struct lwp *); |
275 | static void fd_set_geometry(struct fd_softc *fd); |
276 | |
277 | void fd_mountroot_hook(device_t); |
278 | |
279 | /* |
280 | * Arguments passed between fdcattach and fdprobe. |
281 | */ |
282 | struct fdc_attach_args { |
283 | int fa_drive; |
284 | const struct fd_type *fa_deftype; |
285 | }; |
286 | |
287 | /* |
288 | * Print the location of a disk drive (called just before attaching the |
289 | * the drive). If `fdc' is not NULL, the drive was found but was not |
290 | * in the system config file; print the drive name as well. |
291 | * Return QUIET (config_find ignores this if the device was configured) to |
292 | * avoid printing `fdN not configured' messages. |
293 | */ |
294 | int |
295 | fdprint(void *aux, const char *fdc) |
296 | { |
297 | struct fdc_attach_args *fa = aux; |
298 | |
299 | if (!fdc) |
300 | aprint_normal(" drive %d" , fa->fa_drive); |
301 | return QUIET; |
302 | } |
303 | |
304 | static bool |
305 | fdcresume(device_t self, const pmf_qual_t *qual) |
306 | { |
307 | struct fdc_softc *fdc = device_private(self); |
308 | |
309 | mutex_enter(&fdc->sc_mtx); |
310 | (void)fdcintr1(fdc); |
311 | mutex_exit(&fdc->sc_mtx); |
312 | return true; |
313 | } |
314 | |
315 | static bool |
316 | fdcsuspend(device_t self, const pmf_qual_t *qual) |
317 | { |
318 | struct fdc_softc *fdc = device_private(self); |
319 | int drive; |
320 | struct fd_softc *fd; |
321 | |
322 | mutex_enter(&fdc->sc_mtx); |
323 | while (fdc->sc_state != DEVIDLE) |
324 | cv_wait(&fdc->sc_cv, &fdc->sc_mtx); |
325 | for (drive = 0; drive < 4; drive++) { |
326 | if ((fd = fdc->sc_fd[drive]) == NULL) |
327 | continue; |
328 | fd->sc_flags &= ~(FD_MOTOR|FD_MOTOR_WAIT); |
329 | } |
330 | fd_set_motor(fdc, 0); |
331 | mutex_exit(&fdc->sc_mtx); |
332 | return true; |
333 | } |
334 | |
335 | void |
336 | fdc_childdet(device_t self, device_t child) |
337 | { |
338 | struct fdc_softc *fdc = device_private(self); |
339 | struct fd_softc *fd = device_private(child); |
340 | int drive = fd->sc_drive; |
341 | |
342 | KASSERT(fdc->sc_fd[drive] == fd); /* but the kid is not my son */ |
343 | fdc->sc_fd[drive] = NULL; |
344 | } |
345 | |
346 | int |
347 | fdcdetach(device_t self, int flags) |
348 | { |
349 | int rc; |
350 | struct fdc_softc *fdc = device_private(self); |
351 | |
352 | if ((rc = config_detach_children(self, flags)) != 0) |
353 | return rc; |
354 | |
355 | pmf_device_deregister(self); |
356 | |
357 | isa_dmamap_destroy(fdc->sc_ic, fdc->sc_drq); |
358 | isa_drq_free(fdc->sc_ic, fdc->sc_drq); |
359 | |
360 | callout_destroy(&fdc->sc_intr_ch); |
361 | callout_destroy(&fdc->sc_timo_ch); |
362 | |
363 | cv_destroy(&fdc->sc_cv); |
364 | mutex_destroy(&fdc->sc_mtx); |
365 | |
366 | return 0; |
367 | } |
368 | |
369 | void |
370 | fdcattach(struct fdc_softc *fdc) |
371 | { |
372 | mutex_init(&fdc->sc_mtx, MUTEX_DEFAULT, IPL_BIO); |
373 | cv_init(&fdc->sc_cv, "fdcwake" ); |
374 | callout_init(&fdc->sc_timo_ch, 0); |
375 | callout_init(&fdc->sc_intr_ch, 0); |
376 | |
377 | fdc->sc_state = DEVIDLE; |
378 | TAILQ_INIT(&fdc->sc_drives); |
379 | |
380 | fdc->sc_maxiosize = isa_dmamaxsize(fdc->sc_ic, fdc->sc_drq); |
381 | |
382 | if (isa_drq_alloc(fdc->sc_ic, fdc->sc_drq) != 0) { |
383 | aprint_normal_dev(fdc->sc_dev, "can't reserve drq %d\n" , |
384 | fdc->sc_drq); |
385 | return; |
386 | } |
387 | |
388 | if (isa_dmamap_create(fdc->sc_ic, fdc->sc_drq, fdc->sc_maxiosize, |
389 | BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) { |
390 | aprint_normal_dev(fdc->sc_dev, "can't set up ISA DMA map\n" ); |
391 | return; |
392 | } |
393 | |
394 | config_interrupts(fdc->sc_dev, fdcfinishattach); |
395 | |
396 | if (!pmf_device_register(fdc->sc_dev, fdcsuspend, fdcresume)) { |
397 | aprint_error_dev(fdc->sc_dev, |
398 | "cannot set power mgmt handler\n" ); |
399 | } |
400 | } |
401 | |
402 | void |
403 | fdcfinishattach(device_t self) |
404 | { |
405 | struct fdc_softc *fdc = device_private(self); |
406 | bus_space_tag_t iot = fdc->sc_iot; |
407 | bus_space_handle_t ioh = fdc->sc_ioh; |
408 | struct fdc_attach_args fa; |
409 | |
410 | /* |
411 | * Reset the controller to get it into a known state. Not all |
412 | * probes necessarily need do this to discover the controller up |
413 | * front, so don't assume anything. |
414 | */ |
415 | |
416 | bus_space_write_1(iot, ioh, fdout, 0); |
417 | delay(100); |
418 | bus_space_write_1(iot, ioh, fdout, FDO_FRST); |
419 | |
420 | /* see if it can handle a command */ |
421 | if (out_fdc(iot, ioh, NE7CMD_SPECIFY) < 0) { |
422 | aprint_normal_dev(fdc->sc_dev, "can't reset controller\n" ); |
423 | return; |
424 | } |
425 | out_fdc(iot, ioh, 0xdf); |
426 | out_fdc(iot, ioh, 2); |
427 | |
428 | #if defined(i386) || defined(x86_64) |
429 | /* |
430 | * The NVRAM info only tells us about the first two disks on the |
431 | * `primary' floppy controller. |
432 | */ |
433 | /* XXX device_unit() abuse */ |
434 | if (device_unit(fdc->sc_dev) == 0) { |
435 | int type = mc146818_read(NULL, NVRAM_DISKETTE); /* XXX softc */ |
436 | fdc->sc_known = 1; |
437 | fdc->sc_knownfds[0] = fd_nvtotype(device_xname(fdc->sc_dev), |
438 | type, 0); |
439 | if (fdc->sc_knownfds[0] != NULL) |
440 | fdc->sc_present |= 1; |
441 | fdc->sc_knownfds[1] = fd_nvtotype(device_xname(fdc->sc_dev), |
442 | type, 1); |
443 | if (fdc->sc_knownfds[1] != NULL) |
444 | fdc->sc_present |= 2; |
445 | } |
446 | #endif /* i386 || x86_64 */ |
447 | |
448 | /* physical limit: four drives per controller. */ |
449 | fdc->sc_state = PROBING; |
450 | for (fa.fa_drive = 0; fa.fa_drive < 4; fa.fa_drive++) { |
451 | if (fdc->sc_known) { |
452 | if (fdc->sc_present & (1 << fa.fa_drive)) { |
453 | fa.fa_deftype = fdc->sc_knownfds[fa.fa_drive]; |
454 | config_found(fdc->sc_dev, (void *)&fa, |
455 | fdprint); |
456 | } |
457 | } else { |
458 | #if defined(atari) |
459 | /* |
460 | * Atari has a different ordening, defaults to 1.44 |
461 | */ |
462 | fa.fa_deftype = &fd_types[2]; |
463 | #else |
464 | /* |
465 | * Default to 1.44MB on Alpha and BeBox. How do we tell |
466 | * on these platforms? |
467 | */ |
468 | fa.fa_deftype = &fd_types[0]; |
469 | #endif |
470 | (void)config_found_ia(fdc->sc_dev, "fdc" , (void *)&fa, fdprint); |
471 | } |
472 | } |
473 | fdc->sc_state = DEVIDLE; |
474 | } |
475 | |
476 | int |
477 | fdprobe(device_t parent, cfdata_t match, void *aux) |
478 | { |
479 | struct fdc_softc *fdc = device_private(parent); |
480 | cfdata_t cf = match; |
481 | struct fdc_attach_args *fa = aux; |
482 | int drive = fa->fa_drive; |
483 | bus_space_tag_t iot = fdc->sc_iot; |
484 | bus_space_handle_t ioh = fdc->sc_ioh; |
485 | int n; |
486 | |
487 | if (cf->cf_loc[FDCCF_DRIVE] != FDCCF_DRIVE_DEFAULT && |
488 | cf->cf_loc[FDCCF_DRIVE] != drive) |
489 | return 0; |
490 | /* |
491 | * XXX |
492 | * This is to work around some odd interactions between this driver |
493 | * and SMC Ethernet cards. |
494 | */ |
495 | if (cf->cf_loc[FDCCF_DRIVE] == FDCCF_DRIVE_DEFAULT && drive >= 2) |
496 | return 0; |
497 | |
498 | /* Use PNP information if available */ |
499 | if (fdc->sc_known) |
500 | return 1; |
501 | |
502 | mutex_enter(&fdc->sc_mtx); |
503 | /* toss any interrupt status */ |
504 | for (n = 0; n < 4; n++) { |
505 | out_fdc(iot, ioh, NE7CMD_SENSEI); |
506 | (void) fdcresult(fdc); |
507 | } |
508 | /* select drive and turn on motor */ |
509 | bus_space_write_1(iot, ioh, fdout, drive | FDO_FRST | FDO_MOEN(drive)); |
510 | /* wait for motor to spin up */ |
511 | /* XXX check sc_probe */ |
512 | (void) cv_timedwait(&fdc->sc_cv, &fdc->sc_mtx, hz / 4); |
513 | out_fdc(iot, ioh, NE7CMD_RECAL); |
514 | out_fdc(iot, ioh, drive); |
515 | /* wait for recalibrate, up to 2s */ |
516 | /* XXX check sc_probe */ |
517 | if (cv_timedwait(&fdc->sc_cv, &fdc->sc_mtx, 2 * hz) != EWOULDBLOCK){ |
518 | #ifdef FD_DEBUG |
519 | /* XXX */ |
520 | printf("fdprobe: got intr\n" ); |
521 | #endif |
522 | } |
523 | out_fdc(iot, ioh, NE7CMD_SENSEI); |
524 | n = fdcresult(fdc); |
525 | #ifdef FD_DEBUG |
526 | { |
527 | int i; |
528 | printf("fdprobe: status" ); |
529 | for (i = 0; i < n; i++) |
530 | printf(" %x" , fdc->sc_status[i]); |
531 | printf("\n" ); |
532 | } |
533 | #endif |
534 | /* turn off motor */ |
535 | bus_space_write_1(iot, ioh, fdout, FDO_FRST); |
536 | mutex_exit(&fdc->sc_mtx); |
537 | |
538 | #if defined(bebox) /* XXX What is this about? --thorpej@NetBSD.org */ |
539 | if (n != 2 || (fdc->sc_status[1] != 0)) |
540 | return 0; |
541 | #else |
542 | if (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20) |
543 | return 0; |
544 | #endif /* bebox */ |
545 | |
546 | return 1; |
547 | } |
548 | |
549 | /* |
550 | * Controller is working, and drive responded. Attach it. |
551 | */ |
552 | void |
553 | fdattach(device_t parent, device_t self, void *aux) |
554 | { |
555 | struct fdc_softc *fdc = device_private(parent); |
556 | struct fd_softc *fd = device_private(self); |
557 | struct fdc_attach_args *fa = aux; |
558 | const struct fd_type *type = fa->fa_deftype; |
559 | int drive = fa->fa_drive; |
560 | |
561 | fd->sc_dev = self; |
562 | |
563 | callout_init(&fd->sc_motoron_ch, 0); |
564 | callout_init(&fd->sc_motoroff_ch, 0); |
565 | |
566 | /* XXX Allow `flags' to override device type? */ |
567 | |
568 | if (type) |
569 | aprint_normal(": %s, %d cyl, %d head, %d sec\n" , type->name, |
570 | type->cyls, type->heads, type->sectrac); |
571 | else |
572 | aprint_normal(": density unknown\n" ); |
573 | |
574 | bufq_alloc(&fd->sc_q, "disksort" , BUFQ_SORT_CYLINDER); |
575 | fd->sc_cylin = -1; |
576 | fd->sc_drive = drive; |
577 | fd->sc_deftype = type; |
578 | fdc->sc_fd[drive] = fd; |
579 | |
580 | /* |
581 | * Initialize and attach the disk structure. |
582 | */ |
583 | disk_init(&fd->sc_dk, device_xname(fd->sc_dev), &fddkdriver); |
584 | disk_attach(&fd->sc_dk); |
585 | |
586 | /* |
587 | * Establish a mountroot hook. |
588 | */ |
589 | fd->sc_roothook = |
590 | mountroothook_establish(fd_mountroot_hook, fd->sc_dev); |
591 | |
592 | rnd_attach_source(&fd->rnd_source, device_xname(fd->sc_dev), |
593 | RND_TYPE_DISK, RND_FLAG_DEFAULT); |
594 | |
595 | fd_set_geometry(fd); |
596 | |
597 | if (!pmf_device_register(self, NULL, NULL)) |
598 | aprint_error_dev(self, "cannot set power mgmt handler\n" ); |
599 | } |
600 | |
601 | static int |
602 | fddetach(device_t self, int flags) |
603 | { |
604 | struct fd_softc *fd = device_private(self); |
605 | int bmaj, cmaj, i, mn; |
606 | |
607 | fd_motor_off(fd); |
608 | |
609 | /* locate the major number */ |
610 | bmaj = bdevsw_lookup_major(&fd_bdevsw); |
611 | cmaj = cdevsw_lookup_major(&fd_cdevsw); |
612 | |
613 | /* Nuke the vnodes for any open instances. */ |
614 | for (i = 0; i < MAXPARTITIONS; i++) { |
615 | mn = DISKMINOR(device_unit(self), i); |
616 | vdevgone(bmaj, mn, mn, VBLK); |
617 | vdevgone(cmaj, mn, mn, VCHR); |
618 | } |
619 | |
620 | pmf_device_deregister(self); |
621 | |
622 | #if 0 /* XXX need to undo at detach? */ |
623 | fd_set_geometry(fd); |
624 | #endif |
625 | |
626 | rnd_detach_source(&fd->rnd_source); |
627 | |
628 | disk_detach(&fd->sc_dk); |
629 | disk_destroy(&fd->sc_dk); |
630 | |
631 | /* Kill off any queued buffers. */ |
632 | bufq_drain(fd->sc_q); |
633 | |
634 | bufq_free(fd->sc_q); |
635 | |
636 | callout_destroy(&fd->sc_motoroff_ch); |
637 | callout_destroy(&fd->sc_motoron_ch); |
638 | |
639 | return 0; |
640 | } |
641 | |
642 | #if defined(i386) || defined(x86_64) |
643 | /* |
644 | * Translate nvram type into internal data structure. Return NULL for |
645 | * none/unknown/unusable. |
646 | */ |
647 | const struct fd_type * |
648 | fd_nvtotype(const char *fdc, int nvraminfo, int drive) |
649 | { |
650 | int type; |
651 | |
652 | type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0; |
653 | switch (type) { |
654 | case NVRAM_DISKETTE_NONE: |
655 | return NULL; |
656 | case NVRAM_DISKETTE_12M: |
657 | return &fd_types[1]; |
658 | case NVRAM_DISKETTE_TYPE5: |
659 | case NVRAM_DISKETTE_TYPE6: |
660 | /* XXX We really ought to handle 2.88MB format. */ |
661 | case NVRAM_DISKETTE_144M: |
662 | #if NMCA > 0 |
663 | if (MCA_system) |
664 | return &mca_fd_types[0]; |
665 | else |
666 | #endif /* NMCA > 0 */ |
667 | return &fd_types[0]; |
668 | case NVRAM_DISKETTE_360K: |
669 | return &fd_types[3]; |
670 | case NVRAM_DISKETTE_720K: |
671 | #if NMCA > 0 |
672 | if (MCA_system) |
673 | return &mca_fd_types[1]; |
674 | else |
675 | #endif /* NMCA > 0 */ |
676 | return &fd_types[4]; |
677 | default: |
678 | printf("%s: drive %d: unknown device type 0x%x\n" , |
679 | fdc, drive, type); |
680 | return NULL; |
681 | } |
682 | } |
683 | #endif /* i386 || x86_64 */ |
684 | |
685 | static const struct fd_type * |
686 | fd_dev_to_type(struct fd_softc *fd, dev_t dev) |
687 | { |
688 | u_int type = FDTYPE(dev); |
689 | |
690 | if (type > __arraycount(fd_types)) |
691 | return NULL; |
692 | return type ? &fd_types[type - 1] : fd->sc_deftype; |
693 | } |
694 | |
695 | void |
696 | fdstrategy(struct buf *bp) |
697 | { |
698 | struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(bp->b_dev)); |
699 | struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev)); |
700 | int sz; |
701 | |
702 | /* Valid unit, controller, and request? */ |
703 | if (bp->b_blkno < 0 || |
704 | ((bp->b_bcount % FDC_BSIZE) != 0 && |
705 | (bp->b_flags & B_FORMAT) == 0)) { |
706 | bp->b_error = EINVAL; |
707 | goto done; |
708 | } |
709 | |
710 | /* If it's a null transfer, return immediately. */ |
711 | if (bp->b_bcount == 0) |
712 | goto done; |
713 | |
714 | sz = howmany(bp->b_bcount, FDC_BSIZE); |
715 | |
716 | if (bp->b_blkno + sz > fd->sc_type->size) { |
717 | sz = fd->sc_type->size - bp->b_blkno; |
718 | if (sz == 0) { |
719 | /* If exactly at end of disk, return EOF. */ |
720 | goto done; |
721 | } |
722 | if (sz < 0) { |
723 | /* If past end of disk, return EINVAL. */ |
724 | bp->b_error = EINVAL; |
725 | goto done; |
726 | } |
727 | /* Otherwise, truncate request. */ |
728 | bp->b_bcount = sz << DEV_BSHIFT; |
729 | } |
730 | |
731 | bp->b_rawblkno = bp->b_blkno; |
732 | bp->b_cylinder = |
733 | bp->b_blkno / (FDC_BSIZE / DEV_BSIZE) / fd->sc_type->seccyl; |
734 | |
735 | #ifdef FD_DEBUG |
736 | printf("fdstrategy: b_blkno %llu b_bcount %d blkno %llu cylin %d " |
737 | "sz %d\n" , (unsigned long long)bp->b_blkno, bp->b_bcount, |
738 | (unsigned long long)fd->sc_blkno, bp->b_cylinder, sz); |
739 | #endif |
740 | |
741 | /* Queue transfer on drive, activate drive and controller if idle. */ |
742 | mutex_enter(&fdc->sc_mtx); |
743 | bufq_put(fd->sc_q, bp); |
744 | callout_stop(&fd->sc_motoroff_ch); /* a good idea */ |
745 | if (fd->sc_active == 0) |
746 | fdstart(fd); |
747 | #ifdef DIAGNOSTIC |
748 | else { |
749 | if (fdc->sc_state == DEVIDLE) { |
750 | printf("fdstrategy: controller inactive\n" ); |
751 | fdcstart(fdc); |
752 | } |
753 | } |
754 | #endif |
755 | mutex_exit(&fdc->sc_mtx); |
756 | return; |
757 | |
758 | done: |
759 | /* Toss transfer; we're done early. */ |
760 | bp->b_resid = bp->b_bcount; |
761 | biodone(bp); |
762 | } |
763 | |
764 | void |
765 | fdstart(struct fd_softc *fd) |
766 | { |
767 | struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev)); |
768 | int active = !TAILQ_EMPTY(&fdc->sc_drives); |
769 | |
770 | KASSERT(mutex_owned(&fdc->sc_mtx)); |
771 | /* Link into controller queue. */ |
772 | fd->sc_active = 1; |
773 | TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); |
774 | |
775 | /* If controller not already active, start it. */ |
776 | if (!active) |
777 | fdcstart(fdc); |
778 | } |
779 | |
780 | void |
781 | fdfinish(struct fd_softc *fd, struct buf *bp) |
782 | { |
783 | struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev)); |
784 | |
785 | /* |
786 | * Move this drive to the end of the queue to give others a `fair' |
787 | * chance. We only force a switch if N operations are completed while |
788 | * another drive is waiting to be serviced, since there is a long motor |
789 | * startup delay whenever we switch. |
790 | */ |
791 | (void)bufq_get(fd->sc_q); |
792 | if (TAILQ_NEXT(fd, sc_drivechain) && ++fd->sc_ops >= 8) { |
793 | fd->sc_ops = 0; |
794 | TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); |
795 | if (bufq_peek(fd->sc_q) != NULL) |
796 | TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); |
797 | else |
798 | fd->sc_active = 0; |
799 | } |
800 | bp->b_resid = fd->sc_bcount; |
801 | fd->sc_skip = 0; |
802 | |
803 | rnd_add_uint32(&fd->rnd_source, bp->b_blkno); |
804 | |
805 | biodone(bp); |
806 | /* turn off motor 5s from now */ |
807 | callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd); |
808 | fdc->sc_state = DEVIDLE; |
809 | } |
810 | |
811 | int |
812 | fdread(dev_t dev, struct uio *uio, int flags) |
813 | { |
814 | |
815 | return (physio(fdstrategy, NULL, dev, B_READ, minphys, uio)); |
816 | } |
817 | |
818 | int |
819 | fdwrite(dev_t dev, struct uio *uio, int flags) |
820 | { |
821 | |
822 | return (physio(fdstrategy, NULL, dev, B_WRITE, minphys, uio)); |
823 | } |
824 | |
825 | void |
826 | fd_set_motor(struct fdc_softc *fdc, int reset) |
827 | { |
828 | struct fd_softc *fd; |
829 | u_char status; |
830 | int n; |
831 | |
832 | if ((fd = TAILQ_FIRST(&fdc->sc_drives)) != NULL) |
833 | status = fd->sc_drive; |
834 | else |
835 | status = 0; |
836 | if (!reset) |
837 | status |= FDO_FRST | FDO_FDMAEN; |
838 | for (n = 0; n < 4; n++) |
839 | if ((fd = fdc->sc_fd[n]) && (fd->sc_flags & FD_MOTOR)) |
840 | status |= FDO_MOEN(n); |
841 | bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, status); |
842 | } |
843 | |
844 | void |
845 | fd_motor_off(void *arg) |
846 | { |
847 | struct fd_softc *fd = arg; |
848 | struct fdc_softc *fdc; |
849 | |
850 | fdc = device_private(device_parent(fd->sc_dev)); |
851 | |
852 | mutex_enter(&fdc->sc_mtx); |
853 | fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); |
854 | fd_set_motor(fdc, 0); |
855 | mutex_exit(&fdc->sc_mtx); |
856 | } |
857 | |
858 | void |
859 | fd_motor_on(void *arg) |
860 | { |
861 | struct fd_softc *fd = arg; |
862 | struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev)); |
863 | |
864 | mutex_enter(&fdc->sc_mtx); |
865 | fd->sc_flags &= ~FD_MOTOR_WAIT; |
866 | if (TAILQ_FIRST(&fdc->sc_drives) == fd && fdc->sc_state == MOTORWAIT) |
867 | (void)fdcintr1(fdc); |
868 | mutex_exit(&fdc->sc_mtx); |
869 | } |
870 | |
871 | int |
872 | fdcresult(struct fdc_softc *fdc) |
873 | { |
874 | bus_space_tag_t iot = fdc->sc_iot; |
875 | bus_space_handle_t ioh = fdc->sc_ioh; |
876 | u_char i; |
877 | u_int j = 100000, |
878 | n = 0; |
879 | |
880 | for (; j; j--) { |
881 | i = bus_space_read_1(iot, ioh, fdsts) & |
882 | (NE7_DIO | NE7_RQM | NE7_CB); |
883 | if (i == NE7_RQM) |
884 | return n; |
885 | if (i == (NE7_DIO | NE7_RQM | NE7_CB)) { |
886 | if (n >= sizeof(fdc->sc_status)) { |
887 | log(LOG_ERR, "fdcresult: overrun\n" ); |
888 | return -1; |
889 | } |
890 | fdc->sc_status[n++] = |
891 | bus_space_read_1(iot, ioh, fddata); |
892 | } |
893 | delay(10); |
894 | } |
895 | log(LOG_ERR, "fdcresult: timeout\n" ); |
896 | return -1; |
897 | } |
898 | |
899 | int |
900 | out_fdc(bus_space_tag_t iot, bus_space_handle_t ioh, u_char x) |
901 | { |
902 | u_char i; |
903 | u_int j = 100000; |
904 | |
905 | for (; j; j--) { |
906 | i = bus_space_read_1(iot, ioh, fdsts) & |
907 | (NE7_DIO | NE7_RQM); |
908 | if (i == NE7_RQM) { |
909 | bus_space_write_1(iot, ioh, fddata, x); |
910 | return 0; |
911 | } |
912 | delay(10); |
913 | } |
914 | return -1; |
915 | } |
916 | |
917 | int |
918 | fdopen(dev_t dev, int flags, int mode, struct lwp *l) |
919 | { |
920 | struct fd_softc *fd; |
921 | const struct fd_type *type; |
922 | |
923 | fd = device_lookup_private(&fd_cd, FDUNIT(dev)); |
924 | if (fd == NULL) |
925 | return (ENXIO); |
926 | |
927 | type = fd_dev_to_type(fd, dev); |
928 | if (type == NULL) |
929 | return ENXIO; |
930 | |
931 | if ((fd->sc_flags & FD_OPEN) != 0 && |
932 | memcmp(fd->sc_type, type, sizeof(*type))) |
933 | return EBUSY; |
934 | |
935 | fd->sc_type_copy = *type; |
936 | fd->sc_type = &fd->sc_type_copy; |
937 | fd->sc_cylin = -1; |
938 | fd->sc_flags |= FD_OPEN; |
939 | |
940 | fd_set_geometry(fd); |
941 | |
942 | return 0; |
943 | } |
944 | |
945 | int |
946 | fdclose(dev_t dev, int flags, int mode, struct lwp *l) |
947 | { |
948 | struct fd_softc *fd = |
949 | device_lookup_private(&fd_cd, FDUNIT(dev)); |
950 | |
951 | fd->sc_flags &= ~FD_OPEN; |
952 | fd->sc_opts &= ~(FDOPT_NORETRY|FDOPT_SILENT); |
953 | return 0; |
954 | } |
955 | |
956 | void |
957 | fdcstart(struct fdc_softc *fdc) |
958 | { |
959 | |
960 | KASSERT(mutex_owned(&fdc->sc_mtx)); |
961 | |
962 | if (!device_is_active(fdc->sc_dev)) |
963 | return; |
964 | |
965 | #ifdef DIAGNOSTIC |
966 | /* only got here if controller's drive queue was inactive; should |
967 | be in idle state */ |
968 | if (fdc->sc_state != DEVIDLE) { |
969 | printf("fdcstart: not idle\n" ); |
970 | return; |
971 | } |
972 | #endif |
973 | (void)fdcintr1(fdc); |
974 | } |
975 | |
976 | static void |
977 | fdcpstatus(int n, struct fdc_softc *fdc) |
978 | { |
979 | char bits[64]; |
980 | |
981 | switch (n) { |
982 | case 0: |
983 | printf("\n" ); |
984 | break; |
985 | case 2: |
986 | snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]); |
987 | printf(" (st0 %s cyl %d)\n" , bits, fdc->sc_status[1]); |
988 | break; |
989 | case 7: |
990 | snprintb(bits, sizeof(bits), NE7_ST0BITS, fdc->sc_status[0]); |
991 | printf(" (st0 %s" , bits); |
992 | snprintb(bits, sizeof(bits), NE7_ST1BITS, fdc->sc_status[1]); |
993 | printf(" st1 %s" , bits); |
994 | snprintb(bits, sizeof(bits), NE7_ST2BITS, fdc->sc_status[2]); |
995 | printf(" st2 %s" , bits); |
996 | printf(" cyl %d head %d sec %d)\n" , |
997 | fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); |
998 | break; |
999 | #ifdef DIAGNOSTIC |
1000 | default: |
1001 | printf("\nfdcstatus: weird size" ); |
1002 | break; |
1003 | #endif |
1004 | } |
1005 | } |
1006 | |
1007 | void |
1008 | fdcstatus(device_t dv, int n, const char *s) |
1009 | { |
1010 | struct fdc_softc *fdc = device_private(device_parent(dv)); |
1011 | |
1012 | if (n == 0) { |
1013 | out_fdc(fdc->sc_iot, fdc->sc_ioh, NE7CMD_SENSEI); |
1014 | (void) fdcresult(fdc); |
1015 | n = 2; |
1016 | } |
1017 | fdcpstatus(n, fdc); |
1018 | |
1019 | aprint_normal_dev(dv, "%s" , s); |
1020 | |
1021 | } |
1022 | |
1023 | void |
1024 | fdctimeout(void *arg) |
1025 | { |
1026 | struct fdc_softc *fdc = arg; |
1027 | struct fd_softc *fd = TAILQ_FIRST(&fdc->sc_drives); |
1028 | |
1029 | mutex_enter(&fdc->sc_mtx); |
1030 | #ifdef DEBUG |
1031 | log(LOG_ERR, "fdctimeout: state %d\n" , fdc->sc_state); |
1032 | #endif |
1033 | fdcstatus(fd->sc_dev, 0, "timeout" ); |
1034 | |
1035 | if (bufq_peek(fd->sc_q) != NULL) |
1036 | fdc->sc_state++; |
1037 | else |
1038 | fdc->sc_state = DEVIDLE; |
1039 | |
1040 | (void)fdcintr1(fdc); |
1041 | mutex_exit(&fdc->sc_mtx); |
1042 | } |
1043 | |
1044 | static int |
1045 | fdcintr1(struct fdc_softc *fdc) |
1046 | { |
1047 | #define st0 fdc->sc_status[0] |
1048 | #define cyl fdc->sc_status[1] |
1049 | struct fd_softc *fd; |
1050 | struct buf *bp; |
1051 | bus_space_tag_t iot = fdc->sc_iot; |
1052 | bus_space_handle_t ioh = fdc->sc_ioh; |
1053 | int read, head, sec, i, nblks; |
1054 | struct fd_type *type; |
1055 | struct ne7_fd_formb *finfo = NULL; |
1056 | |
1057 | KASSERT(mutex_owned(&fdc->sc_mtx)); |
1058 | if (fdc->sc_state == PROBING) { |
1059 | #ifdef DEBUG |
1060 | printf("fdcintr: got probe interrupt\n" ); |
1061 | #endif |
1062 | fdc->sc_probe++; |
1063 | goto out; |
1064 | } |
1065 | |
1066 | loop: |
1067 | /* Is there a drive for the controller to do a transfer with? */ |
1068 | fd = TAILQ_FIRST(&fdc->sc_drives); |
1069 | if (fd == NULL) { |
1070 | fdc->sc_state = DEVIDLE; |
1071 | goto out; |
1072 | } |
1073 | |
1074 | /* Is there a transfer to this drive? If not, deactivate drive. */ |
1075 | bp = bufq_peek(fd->sc_q); |
1076 | if (bp == NULL) { |
1077 | fd->sc_ops = 0; |
1078 | TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); |
1079 | fd->sc_active = 0; |
1080 | goto loop; |
1081 | } |
1082 | |
1083 | if (bp->b_flags & B_FORMAT) |
1084 | finfo = (struct ne7_fd_formb *)bp->b_data; |
1085 | |
1086 | switch (fdc->sc_state) { |
1087 | case DEVIDLE: |
1088 | fdc->sc_errors = 0; |
1089 | fd->sc_skip = 0; |
1090 | fd->sc_bcount = bp->b_bcount; |
1091 | fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE); |
1092 | callout_stop(&fd->sc_motoroff_ch); |
1093 | if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) { |
1094 | fdc->sc_state = MOTORWAIT; |
1095 | return 1; |
1096 | } |
1097 | if ((fd->sc_flags & FD_MOTOR) == 0) { |
1098 | /* Turn on the motor, being careful about pairing. */ |
1099 | struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1]; |
1100 | if (ofd && ofd->sc_flags & FD_MOTOR) { |
1101 | callout_stop(&ofd->sc_motoroff_ch); |
1102 | ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); |
1103 | } |
1104 | fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT; |
1105 | fd_set_motor(fdc, 0); |
1106 | fdc->sc_state = MOTORWAIT; |
1107 | /* Allow .25s for motor to stabilize. */ |
1108 | callout_reset(&fd->sc_motoron_ch, hz / 4, |
1109 | fd_motor_on, fd); |
1110 | return 1; |
1111 | } |
1112 | /* Make sure the right drive is selected. */ |
1113 | fd_set_motor(fdc, 0); |
1114 | |
1115 | /* fall through */ |
1116 | case DOSEEK: |
1117 | doseek: |
1118 | if (fd->sc_cylin == bp->b_cylinder) |
1119 | goto doio; |
1120 | |
1121 | out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */ |
1122 | out_fdc(iot, ioh, fd->sc_type->steprate); |
1123 | out_fdc(iot, ioh, 6); /* XXX head load time == 6ms */ |
1124 | |
1125 | out_fdc(iot, ioh, NE7CMD_SEEK); /* seek function */ |
1126 | out_fdc(iot, ioh, fd->sc_drive); /* drive number */ |
1127 | out_fdc(iot, ioh, bp->b_cylinder * fd->sc_type->step); |
1128 | |
1129 | fd->sc_cylin = -1; |
1130 | fdc->sc_state = SEEKWAIT; |
1131 | |
1132 | iostat_seek(fd->sc_dk.dk_stats); |
1133 | disk_busy(&fd->sc_dk); |
1134 | |
1135 | callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc); |
1136 | return 1; |
1137 | |
1138 | case DOIO: |
1139 | doio: |
1140 | type = fd->sc_type; |
1141 | if (finfo) |
1142 | fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) - |
1143 | (char *)finfo; |
1144 | sec = fd->sc_blkno % type->seccyl; |
1145 | nblks = type->seccyl - sec; |
1146 | nblks = min(nblks, fd->sc_bcount / FDC_BSIZE); |
1147 | nblks = min(nblks, fdc->sc_maxiosize / FDC_BSIZE); |
1148 | fd->sc_nblks = nblks; |
1149 | fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FDC_BSIZE; |
1150 | head = sec / type->sectrac; |
1151 | sec -= head * type->sectrac; |
1152 | #ifdef DIAGNOSTIC |
1153 | { |
1154 | int block; |
1155 | block = (fd->sc_cylin * type->heads + head) |
1156 | * type->sectrac + sec; |
1157 | if (block != fd->sc_blkno) { |
1158 | printf("fdcintr: block %d != blkno " |
1159 | "%" PRId64 "\n" , block, fd->sc_blkno); |
1160 | #ifdef DDB |
1161 | Debugger(); |
1162 | #endif |
1163 | } |
1164 | } |
1165 | #endif |
1166 | read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; |
1167 | isa_dmastart(fdc->sc_ic, fdc->sc_drq, |
1168 | (char *)bp->b_data + fd->sc_skip, fd->sc_nbytes, |
1169 | NULL, read | DMAMODE_DEMAND, BUS_DMA_NOWAIT); |
1170 | bus_space_write_1(iot, fdc->sc_fdctlioh, 0, type->rate); |
1171 | #ifdef FD_DEBUG |
1172 | printf("fdcintr: %s drive %d track %d head %d sec %d nblks %d\n" , |
1173 | read ? "read" : "write" , fd->sc_drive, fd->sc_cylin, |
1174 | head, sec, nblks); |
1175 | #endif |
1176 | if (finfo) { |
1177 | /* formatting */ |
1178 | if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) { |
1179 | fdc->sc_errors = 4; |
1180 | fdcretry(fdc); |
1181 | goto loop; |
1182 | } |
1183 | out_fdc(iot, ioh, (head << 2) | fd->sc_drive); |
1184 | out_fdc(iot, ioh, finfo->fd_formb_secshift); |
1185 | out_fdc(iot, ioh, finfo->fd_formb_nsecs); |
1186 | out_fdc(iot, ioh, finfo->fd_formb_gaplen); |
1187 | out_fdc(iot, ioh, finfo->fd_formb_fillbyte); |
1188 | } else { |
1189 | if (read) |
1190 | out_fdc(iot, ioh, NE7CMD_READ); /* READ */ |
1191 | else |
1192 | out_fdc(iot, ioh, NE7CMD_WRITE); /* WRITE */ |
1193 | out_fdc(iot, ioh, (head << 2) | fd->sc_drive); |
1194 | out_fdc(iot, ioh, fd->sc_cylin); /* track */ |
1195 | out_fdc(iot, ioh, head); |
1196 | out_fdc(iot, ioh, sec + 1); /* sector +1 */ |
1197 | out_fdc(iot, ioh, type->secsize);/* sector size */ |
1198 | out_fdc(iot, ioh, type->sectrac);/* sectors/track */ |
1199 | out_fdc(iot, ioh, type->gap1); /* gap1 size */ |
1200 | out_fdc(iot, ioh, type->datalen);/* data length */ |
1201 | } |
1202 | fdc->sc_state = IOCOMPLETE; |
1203 | |
1204 | disk_busy(&fd->sc_dk); |
1205 | |
1206 | /* allow 2 seconds for operation */ |
1207 | callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc); |
1208 | return 1; /* will return later */ |
1209 | |
1210 | case SEEKWAIT: |
1211 | callout_stop(&fdc->sc_timo_ch); |
1212 | fdc->sc_state = SEEKCOMPLETE; |
1213 | /* allow 1/50 second for heads to settle */ |
1214 | callout_reset(&fdc->sc_intr_ch, hz / 50, fdcintrcb, fdc); |
1215 | return 1; |
1216 | |
1217 | case SEEKCOMPLETE: |
1218 | /* no data on seek */ |
1219 | disk_unbusy(&fd->sc_dk, 0, 0); |
1220 | |
1221 | /* Make sure seek really happened. */ |
1222 | out_fdc(iot, ioh, NE7CMD_SENSEI); |
1223 | if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || |
1224 | cyl != bp->b_cylinder * fd->sc_type->step) { |
1225 | #ifdef FD_DEBUG |
1226 | fdcstatus(fd->sc_dev, 2, "seek failed" ); |
1227 | #endif |
1228 | fdcretry(fdc); |
1229 | goto loop; |
1230 | } |
1231 | fd->sc_cylin = bp->b_cylinder; |
1232 | goto doio; |
1233 | |
1234 | case IOTIMEDOUT: |
1235 | isa_dmaabort(fdc->sc_ic, fdc->sc_drq); |
1236 | case SEEKTIMEDOUT: |
1237 | case RECALTIMEDOUT: |
1238 | case RESETTIMEDOUT: |
1239 | fdcretry(fdc); |
1240 | goto loop; |
1241 | |
1242 | case IOCOMPLETE: /* IO DONE, post-analyze */ |
1243 | callout_stop(&fdc->sc_timo_ch); |
1244 | |
1245 | disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid), |
1246 | (bp->b_flags & B_READ)); |
1247 | |
1248 | if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) { |
1249 | isa_dmaabort(fdc->sc_ic, fdc->sc_drq); |
1250 | #ifdef FD_DEBUG |
1251 | fdcstatus(fd->sc_dev, 7, bp->b_flags & B_READ ? |
1252 | "read failed" : "write failed" ); |
1253 | printf("blkno %llu nblks %d\n" , |
1254 | (unsigned long long)fd->sc_blkno, fd->sc_nblks); |
1255 | #endif |
1256 | fdcretry(fdc); |
1257 | goto loop; |
1258 | } |
1259 | isa_dmadone(fdc->sc_ic, fdc->sc_drq); |
1260 | if (fdc->sc_errors) { |
1261 | diskerr(bp, "fd" , "soft error (corrected)" , LOG_PRINTF, |
1262 | fd->sc_skip / FDC_BSIZE, NULL); |
1263 | printf("\n" ); |
1264 | fdc->sc_errors = 0; |
1265 | } |
1266 | fd->sc_blkno += fd->sc_nblks; |
1267 | fd->sc_skip += fd->sc_nbytes; |
1268 | fd->sc_bcount -= fd->sc_nbytes; |
1269 | if (!finfo && fd->sc_bcount > 0) { |
1270 | bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl; |
1271 | goto doseek; |
1272 | } |
1273 | fdfinish(fd, bp); |
1274 | goto loop; |
1275 | |
1276 | case DORESET: |
1277 | /* try a reset, keep motor on */ |
1278 | fd_set_motor(fdc, 1); |
1279 | delay(100); |
1280 | fd_set_motor(fdc, 0); |
1281 | fdc->sc_state = RESETCOMPLETE; |
1282 | callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc); |
1283 | return 1; /* will return later */ |
1284 | |
1285 | case RESETCOMPLETE: |
1286 | callout_stop(&fdc->sc_timo_ch); |
1287 | /* clear the controller output buffer */ |
1288 | for (i = 0; i < 4; i++) { |
1289 | out_fdc(iot, ioh, NE7CMD_SENSEI); |
1290 | (void) fdcresult(fdc); |
1291 | } |
1292 | |
1293 | /* fall through */ |
1294 | case DORECAL: |
1295 | out_fdc(iot, ioh, NE7CMD_RECAL); /* recalibrate function */ |
1296 | out_fdc(iot, ioh, fd->sc_drive); |
1297 | fdc->sc_state = RECALWAIT; |
1298 | callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc); |
1299 | return 1; /* will return later */ |
1300 | |
1301 | case RECALWAIT: |
1302 | callout_stop(&fdc->sc_timo_ch); |
1303 | fdc->sc_state = RECALCOMPLETE; |
1304 | /* allow 1/30 second for heads to settle */ |
1305 | callout_reset(&fdc->sc_intr_ch, hz / 30, fdcintrcb, fdc); |
1306 | return 1; /* will return later */ |
1307 | |
1308 | case RECALCOMPLETE: |
1309 | out_fdc(iot, ioh, NE7CMD_SENSEI); |
1310 | if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) { |
1311 | #ifdef FD_DEBUG |
1312 | fdcstatus(fd->sc_dev, 2, "recalibrate failed" ); |
1313 | #endif |
1314 | fdcretry(fdc); |
1315 | goto loop; |
1316 | } |
1317 | fd->sc_cylin = 0; |
1318 | goto doseek; |
1319 | |
1320 | case MOTORWAIT: |
1321 | if (fd->sc_flags & FD_MOTOR_WAIT) |
1322 | return 1; /* time's not up yet */ |
1323 | goto doseek; |
1324 | |
1325 | default: |
1326 | fdcstatus(fd->sc_dev, 0, "stray interrupt" ); |
1327 | return 1; |
1328 | } |
1329 | #undef st0 |
1330 | #undef cyl |
1331 | |
1332 | out: |
1333 | cv_signal(&fdc->sc_cv); |
1334 | return 1; |
1335 | } |
1336 | |
1337 | static void |
1338 | fdcintrcb(void *arg) |
1339 | { |
1340 | (void)fdcintr(arg); |
1341 | } |
1342 | |
1343 | int |
1344 | fdcintr(void *arg) |
1345 | { |
1346 | int rc; |
1347 | struct fdc_softc *fdc = arg; |
1348 | |
1349 | mutex_enter(&fdc->sc_mtx); |
1350 | rc = fdcintr1(fdc); |
1351 | mutex_exit(&fdc->sc_mtx); |
1352 | return rc; |
1353 | } |
1354 | |
1355 | void |
1356 | fdcretry(struct fdc_softc *fdc) |
1357 | { |
1358 | struct fd_softc *fd; |
1359 | struct buf *bp; |
1360 | |
1361 | fd = TAILQ_FIRST(&fdc->sc_drives); |
1362 | bp = bufq_peek(fd->sc_q); |
1363 | |
1364 | if (fd->sc_opts & FDOPT_NORETRY) |
1365 | goto fail; |
1366 | switch (fdc->sc_errors) { |
1367 | case 0: |
1368 | /* try again */ |
1369 | fdc->sc_state = DOSEEK; |
1370 | break; |
1371 | |
1372 | case 1: case 2: case 3: |
1373 | /* didn't work; try recalibrating */ |
1374 | fdc->sc_state = DORECAL; |
1375 | break; |
1376 | |
1377 | case 4: |
1378 | /* still no go; reset the bastard */ |
1379 | fdc->sc_state = DORESET; |
1380 | break; |
1381 | |
1382 | default: |
1383 | fail: |
1384 | if ((fd->sc_opts & FDOPT_SILENT) == 0) { |
1385 | diskerr(bp, "fd" , "hard error" , LOG_PRINTF, |
1386 | fd->sc_skip / FDC_BSIZE, NULL); |
1387 | fdcpstatus(7, fdc); |
1388 | } |
1389 | |
1390 | bp->b_error = EIO; |
1391 | fdfinish(fd, bp); |
1392 | } |
1393 | fdc->sc_errors++; |
1394 | } |
1395 | |
1396 | int |
1397 | fdioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) |
1398 | { |
1399 | struct fd_softc *fd = |
1400 | device_lookup_private(&fd_cd, FDUNIT(dev)); |
1401 | struct fdformat_parms *form_parms; |
1402 | struct fdformat_cmd *form_cmd; |
1403 | struct ne7_fd_formb *fd_formb; |
1404 | struct disklabel *lp = fd->sc_dk.dk_label; |
1405 | int error; |
1406 | unsigned int scratch; |
1407 | int il[FD_MAX_NSEC + 1]; |
1408 | int i, j; |
1409 | #ifdef __HAVE_OLD_DISKLABEL |
1410 | struct disklabel newlabel; |
1411 | #endif |
1412 | |
1413 | switch (cmd) { |
1414 | case DIOCGPARTINFO: |
1415 | case DIOCGDINFO: |
1416 | #ifdef __HAVE_OLD_DISKLABEL |
1417 | case ODIOCGDINFO: |
1418 | #endif |
1419 | memset(lp, 0, sizeof(*lp)); |
1420 | |
1421 | lp->d_type = DKTYPE_FLOPPY; |
1422 | lp->d_secsize = FDC_BSIZE; |
1423 | lp->d_nsectors = fd->sc_type->sectrac; |
1424 | lp->d_ntracks = fd->sc_type->heads; |
1425 | lp->d_ncylinders = fd->sc_type->cyls; |
1426 | lp->d_secpercyl = fd->sc_type->seccyl; |
1427 | lp->d_secperunit = fd->sc_type->size; |
1428 | |
1429 | if (readdisklabel(dev, fdstrategy, lp, NULL) != NULL) |
1430 | return EINVAL; |
1431 | break; |
1432 | } |
1433 | |
1434 | error = disk_ioctl(&fd->sc_dk, dev, cmd, addr, flag, l); |
1435 | if (error != EPASSTHROUGH) |
1436 | return error; |
1437 | |
1438 | switch (cmd) { |
1439 | case DIOCWLABEL: |
1440 | if ((flag & FWRITE) == 0) |
1441 | return EBADF; |
1442 | /* XXX do something */ |
1443 | return 0; |
1444 | |
1445 | case DIOCWDINFO: |
1446 | #ifdef __HAVE_OLD_DISKLABEL |
1447 | case ODIOCWDINFO: |
1448 | #endif |
1449 | { |
1450 | if ((flag & FWRITE) == 0) |
1451 | return EBADF; |
1452 | #ifdef __HAVE_OLD_DISKLABEL |
1453 | if (cmd == ODIOCWDINFO) { |
1454 | memset(&newlabel, 0, sizeof newlabel); |
1455 | memcpy(&newlabel, addr, sizeof (struct olddisklabel)); |
1456 | addr = &newlabel; |
1457 | } |
1458 | #endif |
1459 | error = setdisklabel(lp, addr, 0, NULL); |
1460 | if (error) |
1461 | return error; |
1462 | |
1463 | error = writedisklabel(dev, fdstrategy, lp, NULL); |
1464 | return error; |
1465 | } |
1466 | |
1467 | case FDIOCGETFORMAT: |
1468 | form_parms = (struct fdformat_parms *)addr; |
1469 | form_parms->fdformat_version = FDFORMAT_VERSION; |
1470 | form_parms->nbps = 128 * (1 << fd->sc_type->secsize); |
1471 | form_parms->ncyl = fd->sc_type->cyls; |
1472 | form_parms->nspt = fd->sc_type->sectrac; |
1473 | form_parms->ntrk = fd->sc_type->heads; |
1474 | form_parms->stepspercyl = fd->sc_type->step; |
1475 | form_parms->gaplen = fd->sc_type->gap2; |
1476 | form_parms->fillbyte = fd->sc_type->fillbyte; |
1477 | form_parms->interleave = fd->sc_type->interleave; |
1478 | switch (fd->sc_type->rate) { |
1479 | case FDC_500KBPS: |
1480 | form_parms->xfer_rate = 500 * 1024; |
1481 | break; |
1482 | case FDC_300KBPS: |
1483 | form_parms->xfer_rate = 300 * 1024; |
1484 | break; |
1485 | case FDC_250KBPS: |
1486 | form_parms->xfer_rate = 250 * 1024; |
1487 | break; |
1488 | default: |
1489 | return EINVAL; |
1490 | } |
1491 | return 0; |
1492 | |
1493 | case FDIOCSETFORMAT: |
1494 | if((flag & FWRITE) == 0) |
1495 | return EBADF; /* must be opened for writing */ |
1496 | form_parms = (struct fdformat_parms *)addr; |
1497 | if (form_parms->fdformat_version != FDFORMAT_VERSION) |
1498 | return EINVAL; /* wrong version of formatting prog */ |
1499 | |
1500 | scratch = form_parms->nbps >> 7; |
1501 | if ((form_parms->nbps & 0x7f) || ffs(scratch) == 0 || |
1502 | scratch & ~(1 << (ffs(scratch)-1))) |
1503 | /* not a power-of-two multiple of 128 */ |
1504 | return EINVAL; |
1505 | |
1506 | switch (form_parms->xfer_rate) { |
1507 | case 500 * 1024: |
1508 | fd->sc_type->rate = FDC_500KBPS; |
1509 | break; |
1510 | case 300 * 1024: |
1511 | fd->sc_type->rate = FDC_300KBPS; |
1512 | break; |
1513 | case 250 * 1024: |
1514 | fd->sc_type->rate = FDC_250KBPS; |
1515 | break; |
1516 | default: |
1517 | return EINVAL; |
1518 | } |
1519 | |
1520 | if (form_parms->nspt > FD_MAX_NSEC || |
1521 | form_parms->fillbyte > 0xff || |
1522 | form_parms->interleave > 0xff) |
1523 | return EINVAL; |
1524 | fd->sc_type->sectrac = form_parms->nspt; |
1525 | if (form_parms->ntrk != 2 && form_parms->ntrk != 1) |
1526 | return EINVAL; |
1527 | fd->sc_type->heads = form_parms->ntrk; |
1528 | fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk; |
1529 | fd->sc_type->secsize = ffs(scratch)-1; |
1530 | fd->sc_type->gap2 = form_parms->gaplen; |
1531 | fd->sc_type->cyls = form_parms->ncyl; |
1532 | fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl * |
1533 | form_parms->nbps / DEV_BSIZE; |
1534 | fd->sc_type->step = form_parms->stepspercyl; |
1535 | fd->sc_type->fillbyte = form_parms->fillbyte; |
1536 | fd->sc_type->interleave = form_parms->interleave; |
1537 | return 0; |
1538 | |
1539 | case FDIOCFORMAT_TRACK: |
1540 | if((flag & FWRITE) == 0) |
1541 | return EBADF; /* must be opened for writing */ |
1542 | form_cmd = (struct fdformat_cmd *)addr; |
1543 | if (form_cmd->formatcmd_version != FDFORMAT_VERSION) |
1544 | return EINVAL; /* wrong version of formatting prog */ |
1545 | |
1546 | if (form_cmd->head >= fd->sc_type->heads || |
1547 | form_cmd->cylinder >= fd->sc_type->cyls) { |
1548 | return EINVAL; |
1549 | } |
1550 | |
1551 | fd_formb = malloc(sizeof(struct ne7_fd_formb), |
1552 | M_TEMP, M_NOWAIT); |
1553 | if (fd_formb == 0) |
1554 | return ENOMEM; |
1555 | |
1556 | fd_formb->head = form_cmd->head; |
1557 | fd_formb->cyl = form_cmd->cylinder; |
1558 | fd_formb->transfer_rate = fd->sc_type->rate; |
1559 | fd_formb->fd_formb_secshift = fd->sc_type->secsize; |
1560 | fd_formb->fd_formb_nsecs = fd->sc_type->sectrac; |
1561 | fd_formb->fd_formb_gaplen = fd->sc_type->gap2; |
1562 | fd_formb->fd_formb_fillbyte = fd->sc_type->fillbyte; |
1563 | |
1564 | memset(il, 0, sizeof il); |
1565 | for (j = 0, i = 1; i <= fd_formb->fd_formb_nsecs; i++) { |
1566 | while (il[(j%fd_formb->fd_formb_nsecs)+1]) |
1567 | j++; |
1568 | il[(j%fd_formb->fd_formb_nsecs)+1] = i; |
1569 | j += fd->sc_type->interleave; |
1570 | } |
1571 | for (i = 0; i < fd_formb->fd_formb_nsecs; i++) { |
1572 | fd_formb->fd_formb_cylno(i) = form_cmd->cylinder; |
1573 | fd_formb->fd_formb_headno(i) = form_cmd->head; |
1574 | fd_formb->fd_formb_secno(i) = il[i+1]; |
1575 | fd_formb->fd_formb_secsize(i) = fd->sc_type->secsize; |
1576 | } |
1577 | |
1578 | error = fdformat(dev, fd_formb, l); |
1579 | free(fd_formb, M_TEMP); |
1580 | return error; |
1581 | |
1582 | case FDIOCGETOPTS: /* get drive options */ |
1583 | *(int *)addr = fd->sc_opts; |
1584 | return 0; |
1585 | |
1586 | case FDIOCSETOPTS: /* set drive options */ |
1587 | fd->sc_opts = *(int *)addr; |
1588 | return 0; |
1589 | |
1590 | default: |
1591 | return ENOTTY; |
1592 | } |
1593 | |
1594 | #ifdef DIAGNOSTIC |
1595 | panic("fdioctl: impossible" ); |
1596 | #endif |
1597 | } |
1598 | |
1599 | int |
1600 | fdformat(dev_t dev, struct ne7_fd_formb *finfo, struct lwp *l) |
1601 | { |
1602 | int rv = 0; |
1603 | struct fd_softc *fd = |
1604 | device_lookup_private(&fd_cd, FDUNIT(dev)); |
1605 | struct fd_type *type = fd->sc_type; |
1606 | struct buf *bp; |
1607 | |
1608 | /* set up a buffer header for fdstrategy() */ |
1609 | bp = getiobuf(NULL, false); |
1610 | if (bp == NULL) |
1611 | return ENOBUFS; |
1612 | |
1613 | bp->b_cflags = BC_BUSY; |
1614 | bp->b_flags = B_PHYS | B_FORMAT; |
1615 | bp->b_proc = l->l_proc; |
1616 | bp->b_dev = dev; |
1617 | |
1618 | /* |
1619 | * calculate a fake blkno, so fdstrategy() would initiate a |
1620 | * seek to the requested cylinder |
1621 | */ |
1622 | bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads) |
1623 | + finfo->head * type->sectrac) * FDC_BSIZE / DEV_BSIZE; |
1624 | |
1625 | bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; |
1626 | bp->b_data = (void *)finfo; |
1627 | |
1628 | #ifdef FD_DEBUG |
1629 | printf("fdformat: blkno %" PRIx64 " count %x\n" , |
1630 | bp->b_blkno, bp->b_bcount); |
1631 | #endif |
1632 | |
1633 | /* now do the format */ |
1634 | fdstrategy(bp); |
1635 | |
1636 | /* ...and wait for it to complete */ |
1637 | rv = biowait(bp); |
1638 | putiobuf(bp); |
1639 | return rv; |
1640 | } |
1641 | |
1642 | /* |
1643 | * Mountroot hook: prompt the user to enter the root file system |
1644 | * floppy. |
1645 | */ |
1646 | void |
1647 | fd_mountroot_hook(device_t dev) |
1648 | { |
1649 | int c; |
1650 | |
1651 | printf("Insert filesystem floppy and press return." ); |
1652 | cnpollc(1); |
1653 | for (;;) { |
1654 | c = cngetc(); |
1655 | if ((c == '\r') || (c == '\n')) { |
1656 | printf("\n" ); |
1657 | break; |
1658 | } |
1659 | } |
1660 | cnpollc(0); |
1661 | } |
1662 | |
1663 | static void |
1664 | fd_set_geometry(struct fd_softc *fd) |
1665 | { |
1666 | const struct fd_type *fdt; |
1667 | |
1668 | fdt = fd->sc_type; |
1669 | if (fdt == NULL) { |
1670 | fdt = fd->sc_deftype; |
1671 | if (fdt == NULL) |
1672 | return; |
1673 | } |
1674 | |
1675 | struct disk_geom *dg = &fd->sc_dk.dk_geom; |
1676 | |
1677 | memset(dg, 0, sizeof(*dg)); |
1678 | dg->dg_secperunit = fdt->size; |
1679 | dg->dg_nsectors = fdt->sectrac; |
1680 | switch (fdt->secsize) { |
1681 | case 2: |
1682 | dg->dg_secsize = 512; |
1683 | break; |
1684 | case 3: |
1685 | dg->dg_secsize = 1024; |
1686 | break; |
1687 | default: |
1688 | break; |
1689 | } |
1690 | dg->dg_ntracks = fdt->heads; |
1691 | dg->dg_ncylinders = fdt->cyls; |
1692 | disk_set_info(fd->sc_dev, &fd->sc_dk, NULL); |
1693 | } |
1694 | |