1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright (c) 2018, Joyent, Inc.
28 */
29
30 /*
31 * Floppy Disk driver
32 */
33
34 /*
35 * Set CMOS feature:
36 * CMOS_CONF_MEM: CMOS memory contains configuration info
37 */
38 #define CMOS_CONF_MEM
39
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/buf.h>
44 #include <sys/file.h>
45 #include <sys/open.h>
46 #include <sys/ioctl.h>
47 #include <sys/uio.h>
48 #include <sys/conf.h>
49 #include <sys/stat.h>
50 #include <sys/autoconf.h>
51 #include <sys/vtoc.h>
52 #include <sys/dkio.h>
53 #include <sys/ddi.h>
54 #include <sys/sunddi.h>
55 #include <sys/kstat.h>
56 #include <sys/kmem.h>
57 #include <sys/ddidmareq.h>
58 #include <sys/fdio.h>
59 #include <sys/fdc.h>
60 #include <sys/fd_debug.h>
61 #include <sys/fdmedia.h>
62 #include <sys/debug.h>
63 #include <sys/modctl.h>
64
65 /*
66 * Local Function Prototypes
67 */
68 static int fd_unit_is_open(struct fdisk *);
69 static int fdgetlabel(struct fcu_obj *, int);
70 static void fdstart(struct fcu_obj *);
71 static int fd_build_label_vtoc(struct fcu_obj *, struct fdisk *,
72 struct vtoc *, struct dk_label *);
73 static void fd_build_user_vtoc(struct fcu_obj *, struct fdisk *,
74 struct vtoc *);
75 static int fd_rawioctl(struct fcu_obj *, int, caddr_t, int);
76 static void fd_media_watch(void *);
77
78 static int fd_open(dev_t *, int, int, cred_t *);
79 static int fd_close(dev_t, int, int, cred_t *);
80 static int fd_strategy(struct buf *);
81 static int fd_read(dev_t, struct uio *, cred_t *);
82 static int fd_write(dev_t, struct uio *, cred_t *);
83 static int fd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
84 static int fd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
85 caddr_t, int *);
86 static int fd_check_media(dev_t dev, enum dkio_state state);
87 static int fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag);
88
89 static struct cb_ops fd_cb_ops = {
90 fd_open, /* open */
91 fd_close, /* close */
92 fd_strategy, /* strategy */
93 nodev, /* print */
94 nodev, /* dump */
95 fd_read, /* read */
96 fd_write, /* write */
97 fd_ioctl, /* ioctl */
98 nodev, /* devmap */
99 nodev, /* mmap */
100 nodev, /* segmap */
101 nochpoll, /* poll */
102 fd_prop_op, /* cb_prop_op */
103 0, /* streamtab */
104 D_NEW | D_MP /* Driver compatibility flag */
105 };
106
107 static int fd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
108 static int fd_probe(dev_info_t *);
109 static int fd_attach(dev_info_t *, ddi_attach_cmd_t);
110 static int fd_detach(dev_info_t *, ddi_detach_cmd_t);
111
112 static struct dev_ops fd_ops = {
113 DEVO_REV, /* devo_rev, */
114 0, /* refcnt */
115 fd_getinfo, /* getinfo */
116 nulldev, /* identify */
117 fd_probe, /* probe */
118 fd_attach, /* attach */
119 fd_detach, /* detach */
120 nodev, /* reset */
121 &fd_cb_ops, /* driver operations */
122 (struct bus_ops *)0, /* bus operations */
123 NULL, /* power */
124 ddi_quiesce_not_supported, /* devo_quiesce */
125 };
126
127
128 /*
129 * static data
130 */
131 static void *fd_state_head; /* opaque handle top of state structs */
132 static int fd_check_media_time = 5000000; /* 5 second state check */
133
134 /*
135 * error handling
136 *
137 * for debugging,
138 * set fderrlevel to 1
139 * set fderrmask to 224 or 644
140 */
141 #ifdef DEBUG
142 static uint_t fderrmask = FDEM_ALL;
143 #endif
144 static int fderrlevel = 5;
145
146 #define KIOSP KSTAT_IO_PTR(fdp->d_iostat)
147
148 static struct driver_minor_data {
149 char *name;
150 int minor;
151 int type;
152 } fd_minor [] = {
153 { "a", 0, S_IFBLK},
154 { "b", 1, S_IFBLK},
155 { "c", 2, S_IFBLK},
156 { "a,raw", 0, S_IFCHR},
157 { "b,raw", 1, S_IFCHR},
158 { "c,raw", 2, S_IFCHR},
159 {0}
160 };
161
162 static struct modldrv modldrv = {
163 &mod_driverops, /* Type of module. This one is a driver */
164 "Floppy Disk driver", /* Name of the module. */
165 &fd_ops, /* driver ops */
166 };
167
168 static struct modlinkage modlinkage = {
169 MODREV_1, (void *)&modldrv, NULL
170 };
171
172
173 int
_init(void)174 _init(void)
175 {
176 int retval;
177
178 if ((retval = ddi_soft_state_init(&fd_state_head,
179 sizeof (struct fdisk) + sizeof (struct fd_drive) +
180 sizeof (struct fd_char) + sizeof (struct fdattr), 0)) != 0)
181 return (retval);
182
183 if ((retval = mod_install(&modlinkage)) != 0)
184 ddi_soft_state_fini(&fd_state_head);
185 return (retval);
186 }
187
188 int
_fini(void)189 _fini(void)
190 {
191 int retval;
192
193 if ((retval = mod_remove(&modlinkage)) != 0)
194 return (retval);
195 ddi_soft_state_fini(&fd_state_head);
196 return (retval);
197 }
198
199 int
_info(struct modinfo * modinfop)200 _info(struct modinfo *modinfop)
201 {
202 return (mod_info(&modlinkage, modinfop));
203 }
204
205
206 static int
fd_getdrive(dev_t dev,struct fcu_obj ** fjpp,struct fdisk ** fdpp)207 fd_getdrive(dev_t dev, struct fcu_obj **fjpp, struct fdisk **fdpp)
208 {
209 if (fdpp) {
210 *fdpp = ddi_get_soft_state(fd_state_head, DRIVE(dev));
211 if (*fdpp && fjpp) {
212 *fjpp = (*fdpp)->d_obj;
213 if (*fjpp)
214 return ((*fjpp)->fj_unit);
215 }
216 }
217 return (-1);
218 }
219
220 /*ARGSUSED*/
221 static int
fd_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)222 fd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
223 {
224 dev_t dev = (dev_t)arg;
225 struct fcu_obj *fjp = NULL;
226 struct fdisk *fdp = NULL;
227 int rval;
228
229 switch (cmd) {
230 case DDI_INFO_DEVT2DEVINFO:
231 (void) fd_getdrive(dev, &fjp, &fdp);
232 /*
233 * Ignoring return value because success is checked by
234 * verifying fjp and fdp and returned unit value is not used.
235 */
236 if (fjp && fdp) {
237 *result = fjp->fj_dip;
238 rval = DDI_SUCCESS;
239 } else
240 rval = DDI_FAILURE;
241 break;
242 case DDI_INFO_DEVT2INSTANCE:
243 *result = (void *)(uintptr_t)DRIVE(dev);
244 rval = DDI_SUCCESS;
245 break;
246 default:
247 rval = DDI_FAILURE;
248 }
249 return (rval);
250 }
251
252 #ifdef CMOS_CONF_MEM
253 #define CMOS_ADDR 0x70
254 #define CMOS_DATA 0x71
255 #define CMOS_FDRV 0x10
256 #endif /* CMOS_CONF_MEM */
257
258 static int
fd_probe(dev_info_t * dip)259 fd_probe(dev_info_t *dip)
260 {
261 #ifdef CMOS_CONF_MEM
262 int cmos;
263 int drive_type;
264 #endif /* CMOS_CONF_MEM */
265 int debug[2];
266 int drive_size;
267 int len;
268 int unit_num;
269 char density[8];
270
271 len = sizeof (debug);
272 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
273 DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) ==
274 DDI_PROP_SUCCESS) {
275 fderrlevel = debug[0];
276 #ifdef DEBUG
277 fderrmask = (uint_t)debug[1];
278 #endif
279 }
280 len = sizeof (unit_num);
281 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
282 DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) !=
283 DDI_PROP_SUCCESS) {
284 FDERRPRINT(FDEP_L3, FDEM_ATTA,
285 (CE_WARN, "fd_probe failed: dip %p", (void *)dip));
286 return (DDI_PROBE_FAILURE);
287 }
288
289 #ifdef CMOS_CONF_MEM
290 /* get the cmos memory values quick and dirty */
291 outb(CMOS_ADDR, CMOS_FDRV);
292 cmos = drive_type = (int)inb(CMOS_DATA);
293 #endif /* CMOS_CONF_MEM */
294
295 switch (unit_num) {
296 #ifdef CMOS_CONF_MEM
297 case 0:
298 drive_type = drive_type >> 4;
299 /* FALLTHROUGH */
300 case 1:
301 if (cmos && (drive_type & 0x0F)) {
302 break;
303 }
304 /*
305 * Some enhanced floppy-disk controller adaptor cards
306 * require NO drives defined in the CMOS configuration
307 * memory.
308 * So fall through
309 */
310 #endif /* CMOS_CONF_MEM */
311 /* FALLTHROUGH */
312 default: /* need to check conf file */
313 len = sizeof (density);
314 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
315 DDI_PROP_DONTPASS, "density", (caddr_t)&density, &len) !=
316 DDI_PROP_SUCCESS) {
317 FDERRPRINT(FDEP_L3, FDEM_ATTA,
318 (CE_WARN,
319 "fd_probe failed density: dip %p unit %d",
320 (void *)dip, unit_num));
321 return (DDI_PROBE_FAILURE);
322 }
323 len = sizeof (drive_size);
324 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
325 DDI_PROP_DONTPASS, "size", (caddr_t)&drive_size, &len) !=
326 DDI_PROP_SUCCESS) {
327 FDERRPRINT(FDEP_L3, FDEM_ATTA,
328 (CE_WARN, "fd_probe failed size: dip %p unit %d",
329 (void *)dip, unit_num));
330 return (DDI_PROBE_FAILURE);
331 }
332 }
333 FDERRPRINT(FDEP_L3, FDEM_ATTA,
334 (CE_WARN, "fd_probe dip %p unit %d", (void *)dip, unit_num));
335 return (DDI_PROBE_SUCCESS);
336 }
337
338
339 /* ARGSUSED */
340 static int
fd_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)341 fd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
342 {
343 struct fcu_obj *fjp;
344 struct fdisk *fdp;
345 struct driver_minor_data *dmdp;
346 int mode_3D;
347 int drive_num, drive_size, drive_type;
348 #ifdef CMOS_CONF_MEM
349 int cmos;
350 #endif /* CMOS_CONF_MEM */
351 int len, sig_minor;
352 int unit_num;
353 char density[8];
354 char name[MAXNAMELEN];
355
356 switch (cmd) {
357 case DDI_ATTACH:
358 len = sizeof (unit_num);
359 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
360 DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) !=
361 DDI_PROP_SUCCESS) {
362 FDERRPRINT(FDEP_L3, FDEM_ATTA,
363 (CE_WARN, "fd_attach failed: dip %p", (void *)dip));
364 return (DDI_FAILURE);
365 }
366
367 #ifdef CMOS_CONF_MEM
368 outb(CMOS_ADDR, CMOS_FDRV);
369 cmos = drive_type = (int)inb(CMOS_DATA);
370 #endif /* CMOS_CONF_MEM */
371
372 switch (unit_num) {
373 #ifdef CMOS_CONF_MEM
374 case 0:
375 drive_type = drive_type >> 4;
376 /* FALLTHROUGH */
377 case 1:
378 drive_type = drive_type & 0x0F;
379 if (cmos)
380 break;
381 /*
382 * Some enhanced floppy-disk controller adaptor cards
383 * require NO drives defined in the CMOS configuration
384 * memory.
385 * So fall through
386 */
387 #endif /* CMOS_CONF_MEM */
388 /* FALLTHROUGH */
389 default: /* need to check .conf file */
390 drive_type = 0;
391 len = sizeof (density);
392 if (ddi_prop_op(DDI_DEV_T_ANY, dip,
393 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "density",
394 (caddr_t)&density, &len) != DDI_PROP_SUCCESS)
395 density[0] = '\0';
396 len = sizeof (drive_size);
397 if (ddi_prop_op(DDI_DEV_T_ANY, dip,
398 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "size",
399 (caddr_t)&drive_size, &len) != DDI_PROP_SUCCESS)
400 drive_size = 0;
401 if (strcmp(density, "DSDD") == 0) {
402 if (drive_size == 5)
403 drive_type = 1;
404 else if (drive_size == 3)
405 drive_type = 3;
406 } else if (strcmp(density, "DSHD") == 0) {
407 if (drive_size == 5)
408 drive_type = 2;
409 else if (drive_size == 3)
410 drive_type = 4;
411 } else if (strcmp(density, "DSED") == 0 &&
412 drive_size == 3) {
413 drive_type = 6;
414 }
415 break;
416 }
417 if (drive_type == 0) {
418 FDERRPRINT(FDEP_L3, FDEM_ATTA,
419 (CE_WARN, "fd_attach failed type: dip %p unit %d",
420 (void *)dip, unit_num));
421 return (DDI_FAILURE);
422 }
423
424 drive_num = ddi_get_instance(dip);
425 if (ddi_soft_state_zalloc(fd_state_head, drive_num) != 0)
426 return (DDI_FAILURE);
427 fdp = ddi_get_soft_state(fd_state_head, drive_num);
428 fjp = fdp->d_obj = ddi_get_driver_private(dip);
429
430 mutex_init(&fjp->fj_lock, NULL, MUTEX_DRIVER, *fjp->fj_iblock);
431 sema_init(&fdp->d_ocsem, 1, NULL, SEMA_DRIVER, NULL);
432
433 fjp->fj_drive = (struct fd_drive *)(fdp + 1);
434 fjp->fj_chars = (struct fd_char *)(fjp->fj_drive + 1);
435 fjp->fj_attr = (struct fdattr *)(fjp->fj_chars + 1);
436
437 /*
438 * set default floppy drive characteristics & geometry
439 */
440 switch (drive_type) { /* assume doubled sided */
441 case 2: /* 5.25 high density */
442 *fjp->fj_drive = dfd_525HD;
443 fdp->d_media = 1<<FMT_5H | 1<<FMT_5D9 | 1<<FMT_5D8 |
444 1<<FMT_5D4 | 1<<FMT_5D16;
445 fdp->d_deffdtype = fdp->d_curfdtype = FMT_5H;
446 break;
447 case 4: /* 3.5 high density */
448 *fjp->fj_drive = dfd_350HD;
449 fdp->d_media = 1<<FMT_3H | 1<<FMT_3I | 1<<FMT_3D;
450 len = sizeof (mode_3D);
451 if (ddi_prop_op(DDI_DEV_T_ANY, dip,
452 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "mode_3D",
453 (caddr_t)&mode_3D, &len) != DDI_PROP_SUCCESS)
454 mode_3D = 0;
455 if (mode_3D && (fjp->fj_fdc->c_flags & FCFLG_3DMODE))
456 /*
457 * 3D mode should be enabled only if a dual-
458 * speed 3.5" high-density drive and a
459 * supported floppy controller are installed.
460 */
461 fdp->d_media |= 1 << FMT_3M;
462 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3H;
463 break;
464 case 1: /* 5.25 double density */
465 *fjp->fj_drive = dfd_525DD;
466 fdp->d_media = 1<<FMT_5D9 | 1<<FMT_5D8 | 1<<FMT_5D4 |
467 1<<FMT_5D16;
468 fdp->d_deffdtype = fdp->d_curfdtype = FMT_5D9;
469 break;
470 case 3: /* 3.5 double density */
471 *fjp->fj_drive = dfd_350HD;
472 fdp->d_media = 1<<FMT_3D;
473 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3D;
474 break;
475 case 5: /* 3.5 extended density */
476 case 6:
477 case 7:
478 *fjp->fj_drive = dfd_350ED;
479 fdp->d_media = 1<<FMT_3E | 1<<FMT_3H | 1<<FMT_3I |
480 1<<FMT_3D;
481 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3E;
482 break;
483 case 0: /* no drive defined */
484 default:
485 goto no_attach;
486 }
487 *fjp->fj_chars = *defchar[fdp->d_deffdtype];
488 *fjp->fj_attr = fdtypes[fdp->d_deffdtype];
489 bcopy(fdparts[fdp->d_deffdtype], fdp->d_part,
490 sizeof (struct partition) * NDKMAP);
491 fjp->fj_rotspd = fdtypes[fdp->d_deffdtype].fda_rotatespd;
492
493 sig_minor = drive_num << 3;
494 for (dmdp = fd_minor; dmdp->name != NULL; dmdp++) {
495 if (ddi_create_minor_node(dip, dmdp->name, dmdp->type,
496 sig_minor | dmdp->minor, DDI_NT_FD, 0)
497 == DDI_FAILURE) {
498 ddi_remove_minor_node(dip, NULL);
499 goto no_attach;
500 }
501 }
502
503 FDERRPRINT(FDEP_L3, FDEM_ATTA,
504 (CE_WARN, "fd_attach: dip %p unit %d",
505 (void *)dip, unit_num));
506 (void) sprintf(name, "fd%d", drive_num);
507 fdp->d_iostat = kstat_create("fd", drive_num, name, "disk",
508 KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
509 if (fdp->d_iostat) {
510 fdp->d_iostat->ks_lock = &fjp->fj_lock;
511 kstat_install(fdp->d_iostat);
512 }
513
514 fjp->fj_data = (caddr_t)fdp;
515 fjp->fj_flags |= FUNIT_DRVATCH;
516
517 /*
518 * Add a zero-length attribute to tell the world we support
519 * kernel ioctls (for layered drivers)
520 */
521 (void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
522 DDI_KERNEL_IOCTL, NULL, 0);
523
524 /*
525 * We want to get suspend/resume events, so that we can
526 * refuse to suspend when pcfs is mounted.
527 */
528 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
529 "pm-hardware-state", "needs-suspend-resume");
530
531 /*
532 * Ignoring return value because, for passed arguments, only
533 * DDI_SUCCESS is returned.
534 */
535 ddi_report_dev(dip);
536 return (DDI_SUCCESS);
537
538 case DDI_RESUME:
539 /* nothing for us to do */
540 return (DDI_SUCCESS);
541
542 default:
543 return (DDI_FAILURE);
544 }
545 no_attach:
546 fjp->fj_drive = NULL;
547 fjp->fj_chars = NULL;
548 fjp->fj_attr = NULL;
549 mutex_destroy(&fjp->fj_lock);
550 sema_destroy(&fdp->d_ocsem);
551 ddi_soft_state_free(fd_state_head, drive_num);
552 FDERRPRINT(FDEP_L3, FDEM_ATTA,
553 (CE_WARN, "fd_attach failed: dip %p unit %d",
554 (void *)dip, unit_num));
555 return (DDI_FAILURE);
556 }
557
558
559 /* ARGSUSED */
560 static int
fd_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)561 fd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
562 {
563 struct fcu_obj *fjp;
564 struct fdisk *fdp;
565 int drive_num;
566 int rval = DDI_SUCCESS;
567
568 FDERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fd_detach dip %p",
569 (void *)dip));
570
571 drive_num = ddi_get_instance(dip);
572 if (!(fdp = ddi_get_soft_state(fd_state_head, drive_num)))
573 return (rval);
574
575 switch (cmd) {
576 case DDI_DETACH:
577 if (fd_unit_is_open(fdp)) {
578 rval = DDI_FAILURE;
579 break;
580 }
581 kstat_delete(fdp->d_iostat);
582 fdp->d_iostat = NULL;
583 fjp = (struct fcu_obj *)fdp->d_obj;
584 fjp->fj_flags &= ~FUNIT_DRVATCH;
585 fjp->fj_data = NULL;
586 fjp->fj_drive = NULL;
587 fjp->fj_chars = NULL;
588 fjp->fj_attr = NULL;
589 ddi_prop_remove_all(dip);
590 mutex_destroy(&fjp->fj_lock);
591 sema_destroy(&fdp->d_ocsem);
592 ddi_soft_state_free(fd_state_head, drive_num);
593 break;
594
595 case DDI_SUSPEND:
596 /*
597 * Bad, bad, bad things will happen if someone
598 * *changes* the disk in the drive while it is mounted
599 * and the system is suspended. We have no way to
600 * detect that. (Undetected filesystem corruption.
601 * Its akin to changing the boot disk while the system
602 * is suspended. Don't do it!)
603 *
604 * So we refuse to suspend if there is a mounted filesystem.
605 * (We guess this by looking for a block open. Character
606 * opens are fine.) This limits some of the usability of
607 * suspend/resume, but it certainly avoids this
608 * potential filesystem corruption from pilot error.
609 * Given the decreasing popularity of floppy media, we
610 * don't see this as much of a limitation.
611 */
612 if (fdp->d_regopen[OTYP_BLK]) {
613 cmn_err(CE_NOTE,
614 "Unable to suspend while floppy is in use.");
615 rval = DDI_FAILURE;
616 }
617 break;
618
619 default:
620 rval = DDI_FAILURE;
621 break;
622 }
623 return (rval);
624 }
625
626
627 static int
fd_part_is_open(struct fdisk * fdp,int part)628 fd_part_is_open(struct fdisk *fdp, int part)
629 {
630 int i;
631
632 for (i = 0; i < (OTYPCNT - 1); i++)
633 if (fdp->d_regopen[i] & (1 << part))
634 return (1);
635 return (0);
636 }
637
638 static int
fd_unit_is_open(struct fdisk * fdp)639 fd_unit_is_open(struct fdisk *fdp)
640 {
641 int i;
642
643 for (i = 0; i < NDKMAP; i++)
644 if (fdp->d_lyropen[i])
645 return (1);
646 for (i = 0; i < (OTYPCNT - 1); i++)
647 if (fdp->d_regopen[i])
648 return (1);
649 return (0);
650 }
651
652 /*ARGSUSED*/
653 static int
fd_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)654 fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
655 {
656 struct fcu_obj *fjp = NULL;
657 struct fdisk *fdp = NULL;
658 struct partition *pp;
659 dev_t dev;
660 int part, unit;
661 int part_is_open;
662 int rval;
663 uint_t pbit;
664
665 dev = *devp;
666 unit = fd_getdrive(dev, &fjp, &fdp);
667 if (!fjp || !fdp)
668 return (ENXIO);
669 part = PARTITION(dev);
670 pbit = 1 << part;
671 pp = &fdp->d_part[part];
672
673 /*
674 * Serialize opens/closes
675 */
676 sema_p(&fdp->d_ocsem);
677 FDERRPRINT(FDEP_L1, FDEM_OPEN,
678 (CE_CONT, "fd_open: fd%d part %d flag %x otype %x\n", DRIVE(dev),
679 part, flag, otyp));
680
681 /*
682 * Check for previous exclusive open, or trying to exclusive open
683 * An "exclusive open" on any partition is not guaranteed to
684 * protect against opens on another partition that overlaps it.
685 */
686 if (otyp == OTYP_LYR) {
687 part_is_open = (fdp->d_lyropen[part] != 0);
688 } else {
689 part_is_open = fd_part_is_open(fdp, part);
690 }
691 if ((fdp->d_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) {
692 FDERRPRINT(FDEP_L0, FDEM_OPEN, (CE_CONT,
693 "fd_open: exclparts %lx openparts %lx lyrcnt %lx pbit %x\n",
694 fdp->d_exclmask, fdp->d_regopen[otyp], fdp->d_lyropen[part],
695 pbit));
696 sema_v(&fdp->d_ocsem);
697 return (EBUSY);
698 }
699
700 /*
701 * Ensure that drive is recalibrated on first open of new diskette.
702 */
703 fjp->fj_ops->fco_select(fjp, unit, 1);
704 if (fjp->fj_ops->fco_getchng(fjp, unit) != 0) {
705 if (fjp->fj_ops->fco_rcseek(fjp, unit, -1, 0)) {
706 FDERRPRINT(FDEP_L2, FDEM_OPEN,
707 (CE_NOTE, "fd_open fd%d: not ready", DRIVE(dev)));
708 fjp->fj_ops->fco_select(fjp, unit, 0);
709 sema_v(&fdp->d_ocsem);
710 return (ENXIO);
711 }
712 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
713 }
714 if (flag & (FNDELAY | FNONBLOCK)) {
715 /* don't attempt access, just return successfully */
716 fjp->fj_ops->fco_select(fjp, unit, 0);
717 goto out;
718 }
719
720 /*
721 * auto-sense the density/format of the diskette
722 */
723 rval = fdgetlabel(fjp, unit);
724 fjp->fj_ops->fco_select(fjp, unit, 0);
725 if (rval) {
726 /* didn't find label (couldn't read anything) */
727 FDERRPRINT(FDEP_L2, FDEM_OPEN,
728 (CE_NOTE, "fd%d: drive not ready", DRIVE(dev)));
729 sema_v(&fdp->d_ocsem);
730 return (EIO);
731 }
732 /* check partition */
733 if (pp->p_size == 0) {
734 sema_v(&fdp->d_ocsem);
735 return (ENXIO);
736 }
737 /*
738 * if opening for writing, check write protect on diskette
739 */
740 if ((flag & FWRITE) && (fdp->d_obj->fj_flags & FUNIT_WPROT)) {
741 sema_v(&fdp->d_ocsem);
742 return (EROFS);
743 }
744
745 out:
746 /*
747 * mark open as having succeeded
748 */
749 if (flag & FEXCL)
750 fdp->d_exclmask |= pbit;
751 if (otyp == OTYP_LYR)
752 fdp->d_lyropen[part]++;
753 else
754 fdp->d_regopen[otyp] |= 1 << part;
755
756 sema_v(&fdp->d_ocsem);
757 return (0);
758 }
759
760 /*
761 * fdgetlabel - read the SunOS label off the diskette
762 * if it can read a valid label it does so, else it will use a
763 * default. If it can`t read the diskette - that is an error.
764 *
765 * RETURNS: 0 for ok - meaning that it could at least read the device,
766 * !0 for error XXX TBD NYD error codes
767 */
768 static int
fdgetlabel(struct fcu_obj * fjp,int unit)769 fdgetlabel(struct fcu_obj *fjp, int unit)
770 {
771 struct dk_label *label;
772 struct fdisk *fdp;
773 char *newlabel;
774 short *sp;
775 short count;
776 short xsum;
777 int tries, try_this;
778 uint_t nexttype;
779 int rval;
780 short oldlvl;
781 int i;
782
783 FDERRPRINT(FDEP_L0, FDEM_GETL,
784 (CE_CONT, "fdgetlabel fd unit %d\n", unit));
785 fdp = (struct fdisk *)fjp->fj_data;
786 fjp->fj_flags &= ~(FUNIT_UNLABELED);
787
788 /*
789 * get some space to play with the label
790 */
791 label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
792 FDERRPRINT(FDEP_L0, FDEM_GETL, (CE_CONT,
793 "fdgetlabel fd unit %d kmem_zalloc: ptr = %p, size = %lx\n",
794 unit, (void *)label, (size_t)sizeof (struct dk_label)));
795
796 /*
797 * read block 0 (0/0/1) to find the label
798 * (disk is potentially not present or unformatted)
799 */
800 /* noerrprint since this is a private cmd */
801 oldlvl = fderrlevel;
802 fderrlevel = FDEP_LMAX;
803 /*
804 * try different characteristics (ie densities)
805 *
806 * if fdp->d_curfdtype is -1 then the current characteristics
807 * were set by ioctl and need to try it as well as everything
808 * in the table
809 */
810 nexttype = fdp->d_deffdtype;
811 try_this = 1; /* always try the current characteristics */
812
813 rval = ENXIO;
814 for (tries = nfdtypes; tries; tries--) {
815 if (try_this) {
816 fjp->fj_flags &= ~FUNIT_CHAROK;
817
818 /* try reading last sector of cyl 1, head 0 */
819 if (!(rval = fjp->fj_ops->fco_rw(fjp, unit,
820 FDREAD, 1, 0, fjp->fj_chars->fdc_secptrack,
821 (caddr_t)label,
822 sizeof (struct dk_label))) &&
823 /* and last sector plus 1 of cylinder 1 */
824 fjp->fj_ops->fco_rw(fjp, unit, FDREAD, 1,
825 0, fjp->fj_chars->fdc_secptrack + 1,
826 (caddr_t)label,
827 sizeof (struct dk_label)) &&
828 /* and label sector on cylinder 0 */
829 !(rval = fjp->fj_ops->fco_rw(fjp, unit,
830 FDREAD, 0, 0, 1, (caddr_t)label,
831 sizeof (struct dk_label))))
832 break;
833 if (rval == ENXIO)
834 break;
835 }
836 /*
837 * try the next entry in the characteristics tbl
838 */
839 fdp->d_curfdtype = (signed char)nexttype;
840 nexttype = (nexttype + 1) % nfdtypes;
841 if ((1 << fdp->d_curfdtype) & fdp->d_media) {
842 *fjp->fj_chars = *defchar[fdp->d_curfdtype];
843 *fjp->fj_attr = fdtypes[fdp->d_curfdtype];
844 bcopy(fdparts[fdp->d_curfdtype], fdp->d_part,
845 sizeof (struct partition) * NDKMAP);
846 /*
847 * check for a double_density diskette
848 * in a high_density 5.25" drive
849 */
850 if (fjp->fj_chars->fdc_transfer_rate == 250 &&
851 fjp->fj_rotspd > fjp->fj_attr->fda_rotatespd) {
852 /*
853 * yes - adjust transfer rate since we don't
854 * know if we have a 5.25" dual-speed drive
855 */
856 fjp->fj_attr->fda_rotatespd = 360;
857 fjp->fj_chars->fdc_transfer_rate = 300;
858 fjp->fj_chars->fdc_medium = 5;
859 }
860 if ((2 * fjp->fj_chars->fdc_ncyl) ==
861 defchar[fdp->d_deffdtype]->fdc_ncyl) {
862 /* yes - adjust steps per cylinder */
863 fjp->fj_chars->fdc_steps = 2;
864 } else
865 fjp->fj_chars->fdc_steps = 1;
866 try_this = 1;
867 } else
868 try_this = 0;
869 }
870 fderrlevel = oldlvl; /* print errors again */
871
872 if (rval) {
873 fdp->d_curfdtype = fdp->d_deffdtype;
874 goto out; /* couldn't read anything */
875 }
876
877 FDERRPRINT(FDEP_L0, FDEM_GETL,
878 (CE_CONT,
879 "fdgetlabel fd unit=%d ncyl=%d nsct=%d step=%d rpm=%d intlv=%d\n",
880 unit, fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_secptrack,
881 fjp->fj_chars->fdc_steps, fjp->fj_attr->fda_rotatespd,
882 fjp->fj_attr->fda_intrlv));
883
884 /*
885 * _something_ was read - look for unixtype label
886 */
887 if (label->dkl_magic != DKL_MAGIC ||
888 label->dkl_vtoc.v_sanity != VTOC_SANE) {
889 /* not a label - no magic number */
890 goto nolabel; /* no errors, but no label */
891 }
892
893 count = sizeof (struct dk_label) / sizeof (short);
894 sp = (short *)label;
895 xsum = 0;
896 while (count--)
897 xsum ^= *sp++; /* should add up to 0 */
898 if (xsum) {
899 /* not a label - checksum didn't compute */
900 goto nolabel; /* no errors, but no label */
901 }
902
903 /*
904 * the SunOS label overrides current diskette characteristics
905 */
906 fjp->fj_chars->fdc_ncyl = label->dkl_pcyl;
907 fjp->fj_chars->fdc_nhead = label->dkl_nhead;
908 fjp->fj_chars->fdc_secptrack = (label->dkl_nsect * DEV_BSIZE) /
909 fjp->fj_chars->fdc_sec_size;
910 if (defchar[fdp->d_deffdtype]->fdc_ncyl == 2 * fjp->fj_chars->fdc_ncyl)
911 fjp->fj_chars->fdc_steps = 2;
912 else
913 fjp->fj_chars->fdc_steps = 1;
914
915 fjp->fj_attr->fda_rotatespd = label->dkl_rpm;
916 fjp->fj_attr->fda_intrlv = label->dkl_intrlv;
917
918 fdp->d_vtoc_version = label->dkl_vtoc.v_version;
919 bcopy(label->dkl_vtoc.v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
920 bcopy(label->dkl_vtoc.v_asciilabel,
921 fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
922 /*
923 * logical partitions
924 */
925 for (i = 0; i < NDKMAP; i++) {
926 fdp->d_part[i].p_tag = label->dkl_vtoc.v_part[i].p_tag;
927 fdp->d_part[i].p_flag = label->dkl_vtoc.v_part[i].p_flag;
928 fdp->d_part[i].p_start = label->dkl_vtoc.v_part[i].p_start;
929 fdp->d_part[i].p_size = label->dkl_vtoc.v_part[i].p_size;
930
931 fdp->d_vtoc_timestamp[i] = label->dkl_vtoc.timestamp[i];
932 }
933
934 fjp->fj_flags |= FUNIT_LABELOK;
935 goto out;
936
937 nolabel:
938 /*
939 * if not found, fill in label info from default (mark default used)
940 */
941 if (fdp->d_media & (1<<FMT_3D))
942 newlabel = deflabel_35;
943 else /* if (fdp->d_media & (1<<FMT_5D9)) */
944 newlabel = deflabel_525;
945 bzero(fdp->d_vtoc_volume, LEN_DKL_VVOL);
946 (void) sprintf(fdp->d_vtoc_asciilabel, newlabel,
947 fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_nhead,
948 fjp->fj_chars->fdc_secptrack);
949 fjp->fj_flags |= FUNIT_UNLABELED;
950
951 out:
952 kmem_free(label, sizeof (struct dk_label));
953 return (rval);
954 }
955
956
957 /*ARGSUSED*/
958 static int
fd_close(dev_t dev,int flag,int otyp,cred_t * cred_p)959 fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
960 {
961 struct fcu_obj *fjp = NULL;
962 struct fdisk *fdp = NULL;
963 int part, part_is_closed;
964
965 #ifdef DEBUG
966 int unit;
967 #define DEBUG_ASSIGN unit=
968 #else
969 #define DEBUG_ASSIGN (void)
970 #endif
971
972 DEBUG_ASSIGN fd_getdrive(dev, &fjp, &fdp);
973 /*
974 * Ignoring return in non DEBUG mode because success is checked by
975 * verifying fjp and fdp and returned unit value is not used.
976 */
977 if (!fjp || !fdp)
978 return (ENXIO);
979 part = PARTITION(dev);
980
981 sema_p(&fdp->d_ocsem);
982 FDERRPRINT(FDEP_L1, FDEM_CLOS,
983 (CE_CONT, "fd_close: fd unit %d part %d otype %x\n",
984 unit, part, otyp));
985
986 if (otyp == OTYP_LYR) {
987 if (fdp->d_lyropen[part])
988 fdp->d_lyropen[part]--;
989 part_is_closed = (fdp->d_lyropen[part] == 0);
990 } else {
991 fdp->d_regopen[otyp] &= ~(1<<part);
992 part_is_closed = 1;
993 }
994 if (part_is_closed) {
995 if (part == 2 && fdp->d_exclmask&(1<<part))
996 fdp->d_exclmask = 0;
997 else
998 fdp->d_exclmask &= ~(1<<part);
999 FDERRPRINT(FDEP_L0, FDEM_CLOS,
1000 (CE_CONT,
1001 "fd_close: exclparts %lx openparts %lx lyrcnt %lx\n",
1002 fdp->d_exclmask, fdp->d_regopen[otyp],
1003 fdp->d_lyropen[part]));
1004
1005 if (fd_unit_is_open(fdp) == 0)
1006 fdp->d_obj->fj_flags &= ~FUNIT_CHANGED;
1007 }
1008 sema_v(&fdp->d_ocsem);
1009 return (0);
1010 }
1011
1012 /* ARGSUSED */
1013 static int
fd_read(dev_t dev,struct uio * uio,cred_t * cred_p)1014 fd_read(dev_t dev, struct uio *uio, cred_t *cred_p)
1015 {
1016 return (physio(fd_strategy, NULL, dev, B_READ, minphys, uio));
1017 }
1018
1019 /* ARGSUSED */
1020 static int
fd_write(dev_t dev,struct uio * uio,cred_t * cred_p)1021 fd_write(dev_t dev, struct uio *uio, cred_t *cred_p)
1022 {
1023 return (physio(fd_strategy, NULL, dev, B_WRITE, minphys, uio));
1024 }
1025
1026 /*
1027 * fd_strategy
1028 * checks operation, hangs buf struct off fdcntlr, calls fdstart
1029 * if not already busy. Note that if we call start, then the operation
1030 * will already be done on return (start sleeps).
1031 */
1032 static int
fd_strategy(struct buf * bp)1033 fd_strategy(struct buf *bp)
1034 {
1035 struct fcu_obj *fjp;
1036 struct fdisk *fdp;
1037 struct partition *pp;
1038
1039 FDERRPRINT(FDEP_L1, FDEM_STRA,
1040 (CE_CONT, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
1041 (void *)bp, bp->b_edev));
1042
1043 (void) fd_getdrive(bp->b_edev, &fjp, &fdp);
1044
1045 /*
1046 * Ignoring return because device exist.
1047 * Returned unit value is not used.
1048 */
1049 pp = &fdp->d_part[PARTITION(bp->b_edev)];
1050
1051 if (fjp->fj_chars->fdc_sec_size > NBPSCTR && (bp->b_blkno & 1)) {
1052 FDERRPRINT(FDEP_L3, FDEM_STRA,
1053 (CE_WARN, "fd%d: block %ld is not start of sector!",
1054 DRIVE(bp->b_edev), (long)bp->b_blkno));
1055 bp->b_error = EINVAL;
1056 goto bad;
1057 }
1058
1059 if ((bp->b_blkno > pp->p_size)) {
1060 FDERRPRINT(FDEP_L3, FDEM_STRA,
1061 (CE_WARN, "fd%d: block %ld is past the end! (nblk=%ld)",
1062 DRIVE(bp->b_edev), (long)bp->b_blkno, pp->p_size));
1063 bp->b_error = ENOSPC;
1064 goto bad;
1065 }
1066
1067 /* if at end of file, skip out now */
1068 if (bp->b_blkno == pp->p_size) {
1069 if ((bp->b_flags & B_READ) == 0) {
1070 /* a write needs to get an error! */
1071 bp->b_error = ENOSPC;
1072 goto bad;
1073 }
1074 bp->b_resid = bp->b_bcount;
1075 biodone(bp);
1076 return (0);
1077 }
1078
1079 /* if operation not a multiple of sector size, is error! */
1080 if (bp->b_bcount % fjp->fj_chars->fdc_sec_size) {
1081 FDERRPRINT(FDEP_L3, FDEM_STRA,
1082 (CE_WARN, "fd%d: count %ld must be a multiple of %d",
1083 DRIVE(bp->b_edev), bp->b_bcount,
1084 fjp->fj_chars->fdc_sec_size));
1085 bp->b_error = EINVAL;
1086 goto bad;
1087 }
1088
1089 /*
1090 * Put the buf request in the drive's queue, FIFO.
1091 */
1092 bp->av_forw = 0;
1093 mutex_enter(&fjp->fj_lock);
1094 if (fdp->d_iostat)
1095 kstat_waitq_enter(KIOSP);
1096 if (fdp->d_actf)
1097 fdp->d_actl->av_forw = bp;
1098 else
1099 fdp->d_actf = bp;
1100 fdp->d_actl = bp;
1101 if (!(fjp->fj_flags & FUNIT_BUSY)) {
1102 fdstart(fjp);
1103 }
1104 mutex_exit(&fjp->fj_lock);
1105 return (0);
1106
1107 bad:
1108 bp->b_resid = bp->b_bcount;
1109 bp->b_flags |= B_ERROR;
1110 biodone(bp);
1111 return (0);
1112 }
1113
1114 /*
1115 * fdstart
1116 * called from fd_strategy() or from fdXXXX() to setup and
1117 * start operations of read or write only (using buf structs).
1118 * Because the chip doesn't handle crossing cylinder boundaries on
1119 * the fly, this takes care of those boundary conditions. Note that
1120 * it sleeps until the operation is done *within fdstart* - so that
1121 * when fdstart returns, the operation is already done.
1122 */
1123 static void
fdstart(struct fcu_obj * fjp)1124 fdstart(struct fcu_obj *fjp)
1125 {
1126 struct buf *bp;
1127 struct fdisk *fdp = (struct fdisk *)fjp->fj_data;
1128 struct fd_char *chp;
1129 struct partition *pp;
1130 uint_t ptend;
1131 uint_t bincyl; /* (the number of the desired) block in cyl. */
1132 uint_t blk, len, tlen;
1133 uint_t secpcyl; /* number of sectors per cylinder */
1134 int cyl, head, sect;
1135 int sctrshft, unit;
1136 caddr_t addr;
1137
1138 ASSERT(MUTEX_HELD(&fjp->fj_lock));
1139 fjp->fj_flags |= FUNIT_BUSY;
1140
1141 while ((bp = fdp->d_actf) != NULL) {
1142 fdp->d_actf = bp->av_forw;
1143 fdp->d_current = bp;
1144 if (fdp->d_iostat) {
1145 kstat_waitq_to_runq(KIOSP);
1146 }
1147 mutex_exit(&fjp->fj_lock);
1148
1149 FDERRPRINT(FDEP_L0, FDEM_STRT,
1150 (CE_CONT, "fdstart: bp=0x%p blkno=0x%lx bcount=0x%lx\n",
1151 (void *)bp, (long)bp->b_blkno, bp->b_bcount));
1152 bp->b_flags &= ~B_ERROR;
1153 bp->b_error = 0;
1154 bp->b_resid = bp->b_bcount; /* init resid */
1155
1156 ASSERT(DRIVE(bp->b_edev) == ddi_get_instance(fjp->fj_dip));
1157 unit = fjp->fj_unit;
1158 fjp->fj_ops->fco_select(fjp, unit, 1);
1159
1160 bp_mapin(bp); /* map in buffers */
1161
1162 pp = &fdp->d_part[PARTITION(bp->b_edev)];
1163 /* starting blk adjusted for the partition */
1164 blk = bp->b_blkno + pp->p_start;
1165 ptend = pp->p_start + pp->p_size; /* end of the partition */
1166
1167 chp = fjp->fj_chars;
1168 secpcyl = chp->fdc_nhead * chp->fdc_secptrack;
1169 switch (chp->fdc_sec_size) {
1170 /* convert logical block numbers to sector numbers */
1171 case 1024:
1172 sctrshft = SCTRSHFT + 1;
1173 blk >>= 1;
1174 ptend >>= 1;
1175 break;
1176 default:
1177 case NBPSCTR:
1178 sctrshft = SCTRSHFT;
1179 break;
1180 case 256:
1181 sctrshft = SCTRSHFT - 1;
1182 blk <<= 1;
1183 ptend <<= 1;
1184 break;
1185 }
1186
1187 /*
1188 * If off the end, limit to actual amount that
1189 * can be transferred.
1190 */
1191 if ((blk + (bp->b_bcount >> sctrshft)) > ptend)
1192 /* to end of partition */
1193 len = (ptend - blk) << sctrshft;
1194 else
1195 len = bp->b_bcount;
1196 addr = bp->b_un.b_addr; /* data buffer address */
1197
1198 /*
1199 * now we have the real start blk, addr and len for xfer op
1200 */
1201 while (len != 0) {
1202 /* start cyl of req */
1203 cyl = blk / secpcyl;
1204 bincyl = blk % secpcyl;
1205 /* start head of req */
1206 head = bincyl / chp->fdc_secptrack;
1207 /* start sector of req */
1208 sect = (bincyl % chp->fdc_secptrack) + 1;
1209 /*
1210 * If the desired block and length will go beyond the
1211 * cylinder end, then limit it to the cylinder end.
1212 */
1213 if (bp->b_flags & B_READ) {
1214 if (len > ((secpcyl - bincyl) << sctrshft))
1215 tlen = (secpcyl - bincyl) << sctrshft;
1216 else
1217 tlen = len;
1218 } else {
1219 if (len >
1220 ((chp->fdc_secptrack - sect + 1) <<
1221 sctrshft))
1222 tlen =
1223 (chp->fdc_secptrack - sect + 1) <<
1224 sctrshft;
1225 else
1226 tlen = len;
1227 }
1228
1229 FDERRPRINT(FDEP_L0, FDEM_STRT, (CE_CONT,
1230 " blk 0x%x addr 0x%p len 0x%x "
1231 "cyl %d head %d sec %d\n resid 0x%lx, tlen %d\n",
1232 blk, (void *)addr, len, cyl, head, sect,
1233 bp->b_resid, tlen));
1234
1235 /*
1236 * (try to) do the operation - failure returns an errno
1237 */
1238 bp->b_error = fjp->fj_ops->fco_rw(fjp, unit,
1239 bp->b_flags & B_READ, cyl, head, sect, addr, tlen);
1240 if (bp->b_error != 0) {
1241 FDERRPRINT(FDEP_L3, FDEM_STRT, (CE_WARN,
1242 "fdstart: bad exec of bp: 0x%p, err=%d",
1243 (void *)bp, bp->b_error));
1244 bp->b_flags |= B_ERROR;
1245 break;
1246 }
1247 blk += tlen >> sctrshft;
1248 len -= tlen;
1249 addr += tlen;
1250 bp->b_resid -= tlen;
1251 }
1252 FDERRPRINT(FDEP_L0, FDEM_STRT,
1253 (CE_CONT, "fdstart done: b_resid %lu, b_count %lu\n",
1254 bp->b_resid, bp->b_bcount));
1255 if (fdp->d_iostat) {
1256 if (bp->b_flags & B_READ) {
1257 KIOSP->reads++;
1258 KIOSP->nread += (bp->b_bcount - bp->b_resid);
1259 } else {
1260 KIOSP->writes++;
1261 KIOSP->nwritten += (bp->b_bcount - bp->b_resid);
1262 }
1263 kstat_runq_exit(KIOSP);
1264 }
1265 bp_mapout(bp);
1266 biodone(bp);
1267
1268 fjp->fj_ops->fco_select(fjp, unit, 0);
1269 mutex_enter(&fjp->fj_lock);
1270 fdp->d_current = 0;
1271 }
1272 fjp->fj_flags ^= FUNIT_BUSY;
1273 }
1274
1275 /* ARGSUSED */
1276 static int
fd_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)1277 fd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
1278 int *rval_p)
1279 {
1280 union {
1281 struct dk_cinfo dki;
1282 struct dk_geom dkg;
1283 struct dk_allmap dka;
1284 struct fd_char fdchar;
1285 struct fd_drive drvchar;
1286 int temp;
1287 } cpy;
1288 struct vtoc vtoc;
1289 struct fcu_obj *fjp = NULL;
1290 struct fdisk *fdp = NULL;
1291 struct dk_map *dmp;
1292 struct dk_label *label;
1293 int nblks, part, unit;
1294 int rval = 0;
1295 enum dkio_state state;
1296
1297 unit = fd_getdrive(dev, &fjp, &fdp);
1298 if (!fjp || !fdp)
1299 return (ENXIO);
1300
1301 FDERRPRINT(FDEP_L1, FDEM_IOCT,
1302 (CE_CONT, "fd_ioctl fd unit %d: cmd %x, arg %lx\n",
1303 unit, cmd, arg));
1304
1305 switch (cmd) {
1306 case DKIOCINFO:
1307 fjp->fj_ops->fco_dkinfo(fjp, &cpy.dki);
1308 cpy.dki.dki_cnum = FDCTLR(fjp->fj_unit);
1309 cpy.dki.dki_unit = FDUNIT(fjp->fj_unit);
1310 cpy.dki.dki_partition = PARTITION(dev);
1311 if (ddi_copyout(&cpy.dki, (void *)arg, sizeof (cpy.dki), flag))
1312 rval = EFAULT;
1313 break;
1314
1315 case DKIOCG_PHYGEOM:
1316 case DKIOCG_VIRTGEOM:
1317 cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1318 goto get_geom;
1319 case DKIOCGGEOM:
1320 if (fjp->fj_flags & FUNIT_LABELOK)
1321 cpy.dkg.dkg_nsect = (fjp->fj_chars->fdc_secptrack *
1322 fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1323 else
1324 cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1325 get_geom:
1326 cpy.dkg.dkg_pcyl = fjp->fj_chars->fdc_ncyl;
1327 cpy.dkg.dkg_ncyl = fjp->fj_chars->fdc_ncyl;
1328 cpy.dkg.dkg_nhead = fjp->fj_chars->fdc_nhead;
1329 cpy.dkg.dkg_intrlv = fjp->fj_attr->fda_intrlv;
1330 cpy.dkg.dkg_rpm = fjp->fj_attr->fda_rotatespd;
1331 cpy.dkg.dkg_read_reinstruct =
1332 (int)(cpy.dkg.dkg_nsect * cpy.dkg.dkg_rpm * 4) / 60000;
1333 cpy.dkg.dkg_write_reinstruct = cpy.dkg.dkg_read_reinstruct;
1334 if (ddi_copyout(&cpy.dkg, (void *)arg, sizeof (cpy.dkg), flag))
1335 rval = EFAULT;
1336 break;
1337
1338 case DKIOCSGEOM:
1339 if (ddi_copyin((void *)arg, &cpy.dkg,
1340 sizeof (struct dk_geom), flag)) {
1341 rval = EFAULT;
1342 break;
1343 }
1344 mutex_enter(&fjp->fj_lock);
1345 fjp->fj_chars->fdc_ncyl = cpy.dkg.dkg_ncyl;
1346 fjp->fj_chars->fdc_nhead = cpy.dkg.dkg_nhead;
1347 fjp->fj_chars->fdc_secptrack = cpy.dkg.dkg_nsect;
1348 fjp->fj_attr->fda_intrlv = cpy.dkg.dkg_intrlv;
1349 fjp->fj_attr->fda_rotatespd = cpy.dkg.dkg_rpm;
1350 fdp->d_curfdtype = -1;
1351 mutex_exit(&fjp->fj_lock);
1352 break;
1353
1354 /*
1355 * return the map of all logical partitions
1356 */
1357 case DKIOCGAPART:
1358 /*
1359 * Note the conversion from starting sector number
1360 * to starting cylinder number.
1361 * Return error if division results in a remainder.
1362 */
1363 nblks = fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack;
1364
1365 #ifdef _MULTI_DATAMODEL
1366 switch (ddi_model_convert_from(flag & FMODELS)) {
1367 case DDI_MODEL_ILP32:
1368 {
1369 struct dk_allmap32 dka32;
1370
1371 for (part = 0; part < NDKMAP; part++) {
1372 if ((fdp->d_part[part].p_start % nblks) != 0)
1373 return (EINVAL);
1374 dka32.dka_map[part].dkl_cylno =
1375 fdp->d_part[part].p_start / nblks;
1376 dka32.dka_map[part].dkl_nblk =
1377 fdp->d_part[part].p_size;
1378 }
1379
1380 if (ddi_copyout(&dka32, (void *)arg,
1381 sizeof (struct dk_allmap32), flag))
1382 rval = EFAULT;
1383
1384 break;
1385 }
1386 case DDI_MODEL_NONE:
1387
1388 #endif /* _MULTI_DATAMODEL */
1389
1390 dmp = (struct dk_map *)&cpy.dka;
1391 for (part = 0; part < NDKMAP; part++) {
1392 if ((fdp->d_part[part].p_start % nblks) != 0)
1393 return (EINVAL);
1394 dmp->dkl_cylno =
1395 fdp->d_part[part].p_start / nblks;
1396 dmp->dkl_nblk = fdp->d_part[part].p_size;
1397 dmp++;
1398 }
1399
1400 if (ddi_copyout(&cpy.dka, (void *)arg,
1401 sizeof (struct dk_allmap), flag))
1402 rval = EFAULT;
1403 #ifdef _MULTI_DATAMODEL
1404 break;
1405
1406 }
1407 #endif /* _MULTI_DATAMODEL */
1408
1409 break;
1410
1411 /*
1412 * Set the map of all logical partitions
1413 */
1414 case DKIOCSAPART:
1415
1416 #ifdef _MULTI_DATAMODEL
1417 switch (ddi_model_convert_from(flag & FMODELS)) {
1418 case DDI_MODEL_ILP32:
1419 {
1420 struct dk_allmap32 dka32;
1421
1422 if (ddi_copyin((void *)arg, &dka32,
1423 sizeof (dka32), flag)) {
1424 rval = EFAULT;
1425 break;
1426 }
1427 for (part = 0; part < NDKMAP; part++) {
1428 cpy.dka.dka_map[part].dkl_cylno =
1429 dka32.dka_map[part].dkl_cylno;
1430 cpy.dka.dka_map[part].dkl_nblk =
1431 dka32.dka_map[part].dkl_nblk;
1432 }
1433 break;
1434 }
1435 case DDI_MODEL_NONE:
1436
1437 #endif /* _MULTI_DATAMODEL */
1438 if (ddi_copyin((void *)arg, &cpy.dka, sizeof (cpy.dka), flag))
1439 rval = EFAULT;
1440 #ifdef _MULTI_DATAMODEL
1441
1442 break;
1443 }
1444 #endif /* _MULTI_DATAMODEL */
1445
1446 if (rval != 0)
1447 break;
1448
1449 dmp = (struct dk_map *)&cpy.dka;
1450 nblks = fjp->fj_chars->fdc_nhead *
1451 fjp->fj_chars->fdc_secptrack;
1452 mutex_enter(&fjp->fj_lock);
1453 /*
1454 * Note the conversion from starting cylinder number
1455 * to starting sector number.
1456 */
1457 for (part = 0; part < NDKMAP; part++) {
1458 fdp->d_part[part].p_start = dmp->dkl_cylno *
1459 nblks;
1460 fdp->d_part[part].p_size = dmp->dkl_nblk;
1461 dmp++;
1462 }
1463 mutex_exit(&fjp->fj_lock);
1464
1465 break;
1466
1467 case DKIOCGVTOC:
1468 mutex_enter(&fjp->fj_lock);
1469
1470 /*
1471 * Exit if the diskette has no label.
1472 * Also, get the label to make sure the correct one is
1473 * being used since the diskette may have changed
1474 */
1475 fjp->fj_ops->fco_select(fjp, unit, 1);
1476 rval = fdgetlabel(fjp, unit);
1477 fjp->fj_ops->fco_select(fjp, unit, 0);
1478 if (rval) {
1479 mutex_exit(&fjp->fj_lock);
1480 rval = EINVAL;
1481 break;
1482 }
1483
1484 fd_build_user_vtoc(fjp, fdp, &vtoc);
1485 mutex_exit(&fjp->fj_lock);
1486
1487 #ifdef _MULTI_DATAMODEL
1488 switch (ddi_model_convert_from(flag & FMODELS)) {
1489 case DDI_MODEL_ILP32:
1490 {
1491 struct vtoc32 vtoc32;
1492
1493 vtoctovtoc32(vtoc, vtoc32);
1494
1495 if (ddi_copyout(&vtoc32, (void *)arg,
1496 sizeof (vtoc32), flag))
1497 rval = EFAULT;
1498
1499 break;
1500 }
1501 case DDI_MODEL_NONE:
1502
1503 #endif /* _MULTI_DATAMODEL */
1504 if (ddi_copyout(&vtoc, (void *)arg,
1505 sizeof (vtoc), flag))
1506 rval = EFAULT;
1507 #ifdef _MULTI_DATAMODEL
1508 break;
1509 }
1510 #endif /* _MULTI_DATAMODEL */
1511
1512 break;
1513
1514 case DKIOCSVTOC:
1515
1516 #ifdef _MULTI_DATAMODEL
1517 switch (ddi_model_convert_from(flag & FMODELS)) {
1518 case DDI_MODEL_ILP32:
1519 {
1520 struct vtoc32 vtoc32;
1521
1522 if (ddi_copyin((void *)arg, &vtoc32,
1523 sizeof (vtoc32), flag)) {
1524 rval = EFAULT;
1525 break;
1526 }
1527
1528 vtoc32tovtoc(vtoc32, vtoc);
1529
1530 break;
1531 }
1532 case DDI_MODEL_NONE:
1533
1534 #endif /* _MULTI_DATAMODEL */
1535 if (ddi_copyin((void *)arg, &vtoc, sizeof (vtoc), flag))
1536 rval = EFAULT;
1537 #ifdef _MULTI_DATAMODEL
1538 break;
1539 }
1540 #endif /* _MULTI_DATAMODEL */
1541
1542 if (rval != 0)
1543 break;
1544
1545
1546 label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
1547
1548 mutex_enter(&fjp->fj_lock);
1549
1550 if ((rval = fd_build_label_vtoc(fjp, fdp, &vtoc, label)) == 0) {
1551 fjp->fj_ops->fco_select(fjp, unit, 1);
1552 rval = fjp->fj_ops->fco_rw(fjp, unit, FDWRITE,
1553 0, 0, 1, (caddr_t)label, sizeof (struct dk_label));
1554 fjp->fj_ops->fco_select(fjp, unit, 0);
1555 }
1556 mutex_exit(&fjp->fj_lock);
1557 kmem_free(label, sizeof (struct dk_label));
1558 break;
1559
1560 case DKIOCSTATE:
1561 FDERRPRINT(FDEP_L1, FDEM_IOCT,
1562 (CE_CONT, "fd_ioctl fd unit %d: DKIOCSTATE\n", unit));
1563
1564 if (ddi_copyin((void *)arg, &state, sizeof (int), flag)) {
1565 rval = EFAULT;
1566 break;
1567 }
1568
1569 rval = fd_check_media(dev, state);
1570
1571 if (ddi_copyout(&fdp->d_media_state, (void *)arg,
1572 sizeof (int), flag))
1573 rval = EFAULT;
1574 break;
1575
1576 case FDIOGCHAR:
1577 if (ddi_copyout(fjp->fj_chars, (void *)arg,
1578 sizeof (struct fd_char), flag))
1579 rval = EFAULT;
1580 break;
1581
1582 case FDIOSCHAR:
1583 if (ddi_copyin((void *)arg, &cpy.fdchar,
1584 sizeof (struct fd_char), flag)) {
1585 rval = EFAULT;
1586 break;
1587 }
1588 switch (cpy.fdchar.fdc_transfer_rate) {
1589 case 417:
1590 if ((fdp->d_media & (1 << FMT_3M)) == 0) {
1591 cmn_err(CE_CONT,
1592 "fdioschar:Medium density not supported\n");
1593 rval = EINVAL;
1594 break;
1595 }
1596 mutex_enter(&fjp->fj_lock);
1597 fjp->fj_attr->fda_rotatespd = 360;
1598 mutex_exit(&fjp->fj_lock);
1599 /* cpy.fdchar.fdc_transfer_rate = 500; */
1600 /* FALLTHROUGH */
1601 case 1000:
1602 case 500:
1603 case 300:
1604 case 250:
1605 mutex_enter(&fjp->fj_lock);
1606 *(fjp->fj_chars) = cpy.fdchar;
1607 fdp->d_curfdtype = -1;
1608 fjp->fj_flags &= ~FUNIT_CHAROK;
1609 mutex_exit(&fjp->fj_lock);
1610
1611 break;
1612
1613 default:
1614 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1615 (CE_WARN, "fd_ioctl fd unit %d: FDIOSCHAR odd "
1616 "xfer rate %dkbs",
1617 unit, cpy.fdchar.fdc_transfer_rate));
1618 rval = EINVAL;
1619 break;
1620 }
1621 break;
1622
1623 /*
1624 * set all characteristics and geometry to the defaults
1625 */
1626 case FDDEFGEOCHAR:
1627 mutex_enter(&fjp->fj_lock);
1628 fdp->d_curfdtype = fdp->d_deffdtype;
1629 *fjp->fj_chars = *defchar[fdp->d_curfdtype];
1630 *fjp->fj_attr = fdtypes[fdp->d_curfdtype];
1631 bcopy(fdparts[fdp->d_curfdtype],
1632 fdp->d_part, sizeof (struct partition) * NDKMAP);
1633 fjp->fj_flags &= ~FUNIT_CHAROK;
1634 mutex_exit(&fjp->fj_lock);
1635 break;
1636
1637 case FDEJECT: /* eject disk */
1638 case DKIOCEJECT:
1639 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1640 rval = ENOSYS;
1641 break;
1642
1643 case FDGETCHANGE: /* disk changed */
1644 if (ddi_copyin((void *)arg, &cpy.temp, sizeof (int), flag)) {
1645 rval = EFAULT;
1646 break;
1647 }
1648 mutex_enter(&fjp->fj_lock);
1649 fjp->fj_ops->fco_select(fjp, unit, 1);
1650
1651 if (fjp->fj_flags & FUNIT_CHANGED)
1652 cpy.temp |= FDGC_HISTORY;
1653 else
1654 cpy.temp &= ~FDGC_HISTORY;
1655 fjp->fj_flags &= ~FUNIT_CHANGED;
1656
1657 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1658 cpy.temp |= FDGC_DETECTED;
1659 fjp->fj_ops->fco_resetchng(fjp, unit);
1660 /*
1661 * check diskette again only if it was removed
1662 */
1663 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1664 /*
1665 * no diskette is present
1666 */
1667 cpy.temp |= FDGC_CURRENT;
1668 if (fjp->fj_flags & FUNIT_CHGDET)
1669 /*
1670 * again no diskette; not a new change
1671 */
1672 cpy.temp ^= FDGC_DETECTED;
1673 else
1674 fjp->fj_flags |= FUNIT_CHGDET;
1675 } else {
1676 /*
1677 * a new diskette is present
1678 */
1679 cpy.temp &= ~FDGC_CURRENT;
1680 fjp->fj_flags &= ~FUNIT_CHGDET;
1681 }
1682 } else {
1683 cpy.temp &= ~(FDGC_DETECTED | FDGC_CURRENT);
1684 fjp->fj_flags &= ~FUNIT_CHGDET;
1685 }
1686 /*
1687 * also get state of write protection
1688 */
1689 if (fjp->fj_flags & FUNIT_WPROT) {
1690 cpy.temp |= FDGC_CURWPROT;
1691 } else {
1692 cpy.temp &= ~FDGC_CURWPROT;
1693 }
1694 fjp->fj_ops->fco_select(fjp, unit, 0);
1695 mutex_exit(&fjp->fj_lock);
1696
1697 if (ddi_copyout(&cpy.temp, (void *)arg, sizeof (int), flag))
1698 rval = EFAULT;
1699 break;
1700
1701 case FDGETDRIVECHAR:
1702 if (ddi_copyout(fjp->fj_drive, (void *)arg,
1703 sizeof (struct fd_drive), flag))
1704 rval = EFAULT;
1705 break;
1706
1707 case FDSETDRIVECHAR:
1708 if (ddi_copyin((void *)arg, &cpy.drvchar,
1709 sizeof (struct fd_drive), flag)) {
1710 rval = EFAULT;
1711 break;
1712 }
1713 mutex_enter(&fjp->fj_lock);
1714 *(fjp->fj_drive) = cpy.drvchar;
1715 fdp->d_curfdtype = -1;
1716 fjp->fj_flags &= ~FUNIT_CHAROK;
1717 mutex_exit(&fjp->fj_lock);
1718 break;
1719
1720 case DKIOCREMOVABLE: {
1721 int i = 1;
1722
1723 /* no brainer: floppies are always removable */
1724 if (ddi_copyout(&i, (void *)arg, sizeof (int), flag)) {
1725 rval = EFAULT;
1726 }
1727 break;
1728 }
1729
1730 case DKIOCGMEDIAINFO:
1731 rval = fd_get_media_info(fjp, (caddr_t)arg, flag);
1732 break;
1733
1734 case FDIOCMD:
1735 {
1736 struct fd_cmd fc;
1737 int cyl, head, spc, spt;
1738
1739 #ifdef _MULTI_DATAMODEL
1740 switch (ddi_model_convert_from(flag & FMODELS)) {
1741 case DDI_MODEL_ILP32:
1742 {
1743 struct fd_cmd32 fc32;
1744
1745 if (ddi_copyin((void *)arg, &fc32,
1746 sizeof (fc32), flag)) {
1747 rval = EFAULT;
1748 break;
1749 }
1750
1751 fc.fdc_cmd = fc32.fdc_cmd;
1752 fc.fdc_flags = fc32.fdc_flags;
1753 fc.fdc_blkno = fc32.fdc_blkno;
1754 fc.fdc_secnt = fc32.fdc_secnt;
1755 fc.fdc_bufaddr = (caddr_t)(uintptr_t)fc32.fdc_bufaddr;
1756 fc.fdc_buflen = fc32.fdc_buflen;
1757
1758 break;
1759 }
1760 case DDI_MODEL_NONE:
1761
1762 #endif /* _MULTI_DATAMODEL */
1763
1764 if (ddi_copyin((void *)arg, &fc, sizeof (fc), flag)) {
1765 rval = EFAULT;
1766 break;
1767 }
1768 #ifdef _MULTI_DATAMODEL
1769 break;
1770 }
1771 #endif /* _MULTI_DATAMODEL */
1772
1773 if (rval != 0)
1774 break;
1775
1776 if (fc.fdc_cmd == FDCMD_READ || fc.fdc_cmd == FDCMD_WRITE) {
1777 auto struct iovec aiov;
1778 auto struct uio auio;
1779 struct uio *uio = &auio;
1780
1781 spc = (fc.fdc_cmd == FDCMD_READ)? B_READ: B_WRITE;
1782
1783 bzero(&auio, sizeof (struct uio));
1784 bzero(&aiov, sizeof (struct iovec));
1785 aiov.iov_base = fc.fdc_bufaddr;
1786 aiov.iov_len = (uint_t)fc.fdc_secnt *
1787 fjp->fj_chars->fdc_sec_size;
1788 uio->uio_iov = &aiov;
1789
1790 uio->uio_iovcnt = 1;
1791 uio->uio_resid = aiov.iov_len;
1792 uio->uio_segflg = UIO_USERSPACE;
1793
1794 rval = physio(fd_strategy, (struct buf *)0, dev,
1795 spc, minphys, uio);
1796 break;
1797 } else if (fc.fdc_cmd == FDCMD_FORMAT_TRACK) {
1798 spt = fjp->fj_chars->fdc_secptrack; /* sec/trk */
1799 spc = fjp->fj_chars->fdc_nhead * spt; /* sec/cyl */
1800 cyl = fc.fdc_blkno / spc;
1801 head = (fc.fdc_blkno % spc) / spt;
1802 if ((cyl | head) == 0)
1803 fjp->fj_flags &=
1804 ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1805
1806 FDERRPRINT(FDEP_L0, FDEM_FORM,
1807 (CE_CONT, "fd_format cyl %d, hd %d\n", cyl, head));
1808 fjp->fj_ops->fco_select(fjp, unit, 1);
1809 rval = fjp->fj_ops->fco_format(fjp, unit, cyl, head,
1810 (int)fc.fdc_flags);
1811 fjp->fj_ops->fco_select(fjp, unit, 0);
1812
1813 break;
1814 }
1815 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1816 (CE_WARN, "fd_ioctl fd unit %d: FDIOCSCMD not yet complete",
1817 unit));
1818 rval = EINVAL;
1819 break;
1820 }
1821
1822 case FDRAW:
1823 rval = fd_rawioctl(fjp, unit, (caddr_t)arg, flag);
1824 break;
1825
1826 default:
1827 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1828 (CE_WARN, "fd_ioctl fd unit %d: invalid ioctl 0x%x",
1829 unit, cmd));
1830 rval = ENOTTY;
1831 break;
1832 }
1833 return (rval);
1834 }
1835
1836 static void
fd_build_user_vtoc(struct fcu_obj * fjp,struct fdisk * fdp,struct vtoc * vtocp)1837 fd_build_user_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp)
1838 {
1839 struct partition *vpart;
1840 int i;
1841 int xblk;
1842
1843 /*
1844 * Return vtoc structure fields in the provided VTOC area, addressed
1845 * by *vtocp.
1846 *
1847 */
1848 bzero(vtocp, sizeof (struct vtoc));
1849
1850 bcopy(fdp->d_vtoc_bootinfo,
1851 vtocp->v_bootinfo, sizeof (vtocp->v_bootinfo));
1852
1853 vtocp->v_sanity = VTOC_SANE;
1854 vtocp->v_version = fdp->d_vtoc_version;
1855 bcopy(fdp->d_vtoc_volume, vtocp->v_volume, LEN_DKL_VVOL);
1856 if (fjp->fj_flags & FUNIT_LABELOK) {
1857 vtocp->v_sectorsz = DEV_BSIZE;
1858 xblk = 1;
1859 } else {
1860 vtocp->v_sectorsz = fjp->fj_chars->fdc_sec_size;
1861 xblk = vtocp->v_sectorsz / DEV_BSIZE;
1862 }
1863 vtocp->v_nparts = 3; /* <= NDKMAP; */
1864
1865 /*
1866 * Copy partitioning information.
1867 */
1868 bcopy(fdp->d_part, vtocp->v_part, sizeof (struct partition) * NDKMAP);
1869 for (i = NDKMAP, vpart = vtocp->v_part; i && (xblk > 1); i--, vpart++) {
1870 /* correct partition info if sector size > 512 bytes */
1871 vpart->p_start /= xblk;
1872 vpart->p_size /= xblk;
1873 }
1874
1875 bcopy(fdp->d_vtoc_timestamp,
1876 vtocp->timestamp, sizeof (fdp->d_vtoc_timestamp));
1877 bcopy(fdp->d_vtoc_asciilabel, vtocp->v_asciilabel, LEN_DKL_ASCII);
1878 }
1879
1880
1881 static int
fd_build_label_vtoc(struct fcu_obj * fjp,struct fdisk * fdp,struct vtoc * vtocp,struct dk_label * labelp)1882 fd_build_label_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp,
1883 struct dk_label *labelp)
1884 {
1885 struct partition *vpart;
1886 int i;
1887 int nblks;
1888 int ncyl;
1889 ushort_t sum, *sp;
1890
1891
1892 /*
1893 * Sanity-check the vtoc
1894 */
1895 if (vtocp->v_sanity != VTOC_SANE ||
1896 vtocp->v_nparts > NDKMAP || vtocp->v_nparts <= 0) {
1897 FDERRPRINT(FDEP_L3, FDEM_IOCT,
1898 (CE_WARN, "fd_build_label: sanity check on vtoc failed"));
1899 return (EINVAL);
1900 }
1901
1902 /*
1903 * before copying the vtoc, the partition information in it should be
1904 * checked against the information the driver already has on the
1905 * diskette.
1906 */
1907
1908 nblks = (fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack *
1909 fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1910 if (nblks == 0 || fjp->fj_chars->fdc_ncyl == 0)
1911 return (EFAULT);
1912 vpart = vtocp->v_part;
1913
1914 /*
1915 * Check the partition information in the vtoc. The starting sectors
1916 * must lie along cylinder boundaries. (NDKMAP entries are checked
1917 * to ensure that the unused entries are set to 0 if vtoc->v_nparts
1918 * is less than NDKMAP)
1919 */
1920 for (i = NDKMAP; i; i--) {
1921 if ((vpart->p_start % nblks) != 0) {
1922 return (EINVAL);
1923 }
1924 ncyl = vpart->p_start / nblks;
1925 ncyl += vpart->p_size / nblks;
1926 if ((vpart->p_size % nblks) != 0)
1927 ncyl++;
1928 if (ncyl > (long)fjp->fj_chars->fdc_ncyl) {
1929 return (EINVAL);
1930 }
1931 vpart++;
1932 }
1933
1934
1935 bcopy(vtocp->v_bootinfo, fdp->d_vtoc_bootinfo,
1936 sizeof (vtocp->v_bootinfo));
1937 fdp->d_vtoc_version = vtocp->v_version;
1938 bcopy(vtocp->v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
1939
1940 /*
1941 * Copy partitioning information.
1942 */
1943 bcopy(vtocp->v_part, fdp->d_part, sizeof (struct partition) * NDKMAP);
1944 bcopy(vtocp->timestamp, fdp->d_vtoc_timestamp,
1945 sizeof (fdp->d_vtoc_timestamp));
1946 bcopy(vtocp->v_asciilabel, fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
1947
1948 /*
1949 * construct the diskette label in supplied buffer
1950 */
1951
1952 /* Put appropriate vtoc structure fields into the disk label */
1953 labelp->dkl_vtoc.v_bootinfo[0] = (uint32_t)vtocp->v_bootinfo[0];
1954 labelp->dkl_vtoc.v_bootinfo[1] = (uint32_t)vtocp->v_bootinfo[1];
1955 labelp->dkl_vtoc.v_bootinfo[2] = (uint32_t)vtocp->v_bootinfo[2];
1956
1957 labelp->dkl_vtoc.v_sanity = vtocp->v_sanity;
1958 labelp->dkl_vtoc.v_version = vtocp->v_version;
1959
1960 bcopy(vtocp->v_volume, labelp->dkl_vtoc.v_volume, LEN_DKL_VVOL);
1961
1962 labelp->dkl_vtoc.v_nparts = vtocp->v_nparts;
1963
1964 bcopy(vtocp->v_reserved, labelp->dkl_vtoc.v_reserved,
1965 sizeof (labelp->dkl_vtoc.v_reserved));
1966
1967 for (i = 0; i < (int)vtocp->v_nparts; i++) {
1968 labelp->dkl_vtoc.v_part[i].p_tag = vtocp->v_part[i].p_tag;
1969 labelp->dkl_vtoc.v_part[i].p_flag = vtocp->v_part[i].p_flag;
1970 labelp->dkl_vtoc.v_part[i].p_start = vtocp->v_part[i].p_start;
1971 labelp->dkl_vtoc.v_part[i].p_size = vtocp->v_part[i].p_size;
1972 }
1973
1974 for (i = 0; i < NDKMAP; i++) {
1975 labelp->dkl_vtoc.v_timestamp[i] = vtocp->timestamp[i];
1976 }
1977 bcopy(vtocp->v_asciilabel, labelp->dkl_asciilabel, LEN_DKL_ASCII);
1978
1979
1980 labelp->dkl_pcyl = fjp->fj_chars->fdc_ncyl;
1981 labelp->dkl_ncyl = fjp->fj_chars->fdc_ncyl;
1982 labelp->dkl_nhead = fjp->fj_chars->fdc_nhead;
1983 /*
1984 * The fdc_secptrack field of the fd_char structure is the number
1985 * of sectors per track where the sectors are fdc_sec_size.
1986 * The dkl_nsect field of the dk_label structure is the number of
1987 * DEV_BSIZE (512) byte sectors per track.
1988 */
1989 labelp->dkl_nsect = (fjp->fj_chars->fdc_secptrack *
1990 fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1991 labelp->dkl_intrlv = fjp->fj_attr->fda_intrlv;
1992 labelp->dkl_rpm = fjp->fj_attr->fda_rotatespd;
1993 labelp->dkl_read_reinstruct =
1994 (int)(labelp->dkl_nsect * labelp->dkl_rpm * 4) / 60000;
1995 labelp->dkl_write_reinstruct = labelp->dkl_read_reinstruct;
1996
1997 labelp->dkl_magic = DKL_MAGIC;
1998
1999 sum = 0;
2000 labelp->dkl_cksum = 0;
2001 sp = (ushort_t *)labelp;
2002 while (sp < &(labelp->dkl_cksum)) {
2003 sum ^= *sp++;
2004 }
2005 labelp->dkl_cksum = sum;
2006
2007 return (0);
2008 }
2009
2010 static int
fd_rawioctl(struct fcu_obj * fjp,int unit,caddr_t arg,int mode)2011 fd_rawioctl(struct fcu_obj *fjp, int unit, caddr_t arg, int mode)
2012 {
2013 struct fd_raw fdr;
2014 char *arg_result = NULL;
2015 int flag = B_READ;
2016 int rval = 0;
2017 caddr_t uaddr;
2018 uint_t ucount;
2019
2020 FDERRPRINT(FDEP_L1, FDEM_RAWI,
2021 (CE_CONT, "fd_rawioctl: cmd[0]=0x%x\n", fdr.fdr_cmd[0]));
2022
2023 if (fjp->fj_chars->fdc_medium != 3 && fjp->fj_chars->fdc_medium != 5) {
2024 cmn_err(CE_CONT, "fd_rawioctl: Medium density not supported\n");
2025 return (ENXIO);
2026 }
2027
2028 #ifdef _MULTI_DATAMODEL
2029 switch (ddi_model_convert_from(mode & FMODELS)) {
2030 case DDI_MODEL_ILP32:
2031 {
2032 struct fd_raw32 fdr32;
2033
2034 if (ddi_copyin(arg, &fdr32, sizeof (fdr32), mode))
2035 return (EFAULT);
2036
2037 bcopy(fdr32.fdr_cmd, fdr.fdr_cmd, sizeof (fdr.fdr_cmd));
2038 fdr.fdr_cnum = fdr32.fdr_cnum;
2039 fdr.fdr_nbytes = fdr32.fdr_nbytes;
2040 fdr.fdr_addr = (caddr_t)(uintptr_t)fdr32.fdr_addr;
2041 arg_result = ((struct fd_raw32 *)arg)->fdr_result;
2042
2043 break;
2044 }
2045 case DDI_MODEL_NONE:
2046 #endif /* ! _MULTI_DATAMODEL */
2047
2048 if (ddi_copyin(arg, &fdr, sizeof (fdr), mode))
2049 return (EFAULT);
2050
2051 arg_result = ((struct fd_raw *)arg)->fdr_result;
2052
2053 #ifdef _MULTI_DATAMODEL
2054 break;
2055 }
2056 #endif /* _MULTI_DATAMODEL */
2057
2058
2059
2060 /*
2061 * copy user address & nbytes from raw_req so that we can
2062 * put kernel address in req structure
2063 */
2064 uaddr = fdr.fdr_addr;
2065 ucount = (uint_t)fdr.fdr_nbytes;
2066 unit &= 3;
2067
2068 switch (fdr.fdr_cmd[0] & 0x0f) {
2069
2070 case FDRAW_FORMAT:
2071 ucount += 16;
2072 fdr.fdr_addr = kmem_zalloc(ucount, KM_SLEEP);
2073 if (ddi_copyin(uaddr, fdr.fdr_addr,
2074 (size_t)fdr.fdr_nbytes, mode)) {
2075 kmem_free(fdr.fdr_addr, ucount);
2076 return (EFAULT);
2077 }
2078 if ((*fdr.fdr_addr | fdr.fdr_addr[1]) == 0)
2079 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
2080 flag = B_WRITE;
2081 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2082 break;
2083
2084 case FDRAW_WRCMD:
2085 case FDRAW_WRITEDEL:
2086 flag = B_WRITE;
2087 /* FALLTHROUGH */
2088 case FDRAW_RDCMD:
2089 case FDRAW_READDEL:
2090 case FDRAW_READTRACK:
2091 if (ucount) {
2092 /*
2093 * In SunOS 4.X, we used to as_fault things in.
2094 * We really cannot do this in 5.0/SVr4. Unless
2095 * someone really believes that speed is of the
2096 * essence here, it is just much simpler to do
2097 * this in kernel space and use copyin/copyout.
2098 */
2099 fdr.fdr_addr = kmem_alloc((size_t)ucount, KM_SLEEP);
2100 if (flag == B_WRITE) {
2101 if (ddi_copyin(uaddr, fdr.fdr_addr, ucount,
2102 mode)) {
2103 kmem_free(fdr.fdr_addr, ucount);
2104 return (EFAULT);
2105 }
2106 }
2107 } else
2108 return (EINVAL);
2109 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2110 break;
2111
2112 case FDRAW_READID:
2113 case FDRAW_REZERO:
2114 case FDRAW_SEEK:
2115 case FDRAW_SENSE_DRV:
2116 ucount = 0;
2117 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2118 break;
2119
2120 case FDRAW_SPECIFY:
2121 fdr.fdr_cmd[2] &= 0xfe; /* keep NoDMA bit clear */
2122 /* FALLTHROUGH */
2123 case FDRAW_SENSE_INT:
2124 ucount = 0;
2125 break;
2126
2127 default:
2128 return (EINVAL);
2129 }
2130
2131 /*
2132 * Note that we ignore any error returns from controller
2133 * This is the way the driver has been, and it may be
2134 * that the raw ioctl senders simply don't want to
2135 * see any errors returned in this fashion.
2136 */
2137
2138 fjp->fj_ops->fco_select(fjp, unit, 1);
2139 rval = fjp->fj_ops->fco_rwioctl(fjp, unit, (caddr_t)&fdr);
2140
2141 if (ucount && flag == B_READ && rval == 0) {
2142 if (ddi_copyout(fdr.fdr_addr, uaddr, ucount, mode)) {
2143 rval = EFAULT;
2144 }
2145 }
2146 if (ddi_copyout(fdr.fdr_result, arg_result, sizeof (fdr.fdr_cmd), mode))
2147 rval = EFAULT;
2148
2149 fjp->fj_ops->fco_select(fjp, unit, 0);
2150 if (ucount)
2151 kmem_free(fdr.fdr_addr, ucount);
2152
2153 return (rval);
2154 }
2155
2156 /*
2157 * property operation routine. return the number of blocks for the partition
2158 * in question or forward the request to the property facilities.
2159 */
2160 static int
fd_prop_op(dev_t dev,dev_info_t * dip,ddi_prop_op_t prop_op,int mod_flags,char * name,caddr_t valuep,int * lengthp)2161 fd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
2162 char *name, caddr_t valuep, int *lengthp)
2163 {
2164 struct fcu_obj *fjp = NULL;
2165 struct fdisk *fdp = NULL;
2166 uint64_t nblocks64;
2167
2168 FDERRPRINT(FDEP_L1, FDEM_PROP,
2169 (CE_CONT, "fd_prop_op: dip %p %s\n", (void *)dip, name));
2170
2171 /*
2172 * Our dynamic properties are all device specific and size oriented.
2173 * Requests issued under conditions where size is valid are passed
2174 * to ddi_prop_op_nblocks with the size information, otherwise the
2175 * request is passed to ddi_prop_op.
2176 */
2177 if (dev == DDI_DEV_T_ANY) {
2178 pass: return (ddi_prop_op(dev, dip, prop_op, mod_flags,
2179 name, valuep, lengthp));
2180 } else {
2181 /*
2182 * Ignoring return value because success is checked by
2183 * verifying fjp and fdp and returned unit value is not used.
2184 */
2185 (void) fd_getdrive(dev, &fjp, &fdp);
2186 if (!fjp || !fdp)
2187 goto pass;
2188
2189 /* get nblocks value */
2190 nblocks64 = (ulong_t)fdp->d_part[PARTITION(dev)].p_size;
2191
2192 return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
2193 name, valuep, lengthp, nblocks64));
2194 }
2195 }
2196
2197 static void
fd_media_watch(void * arg)2198 fd_media_watch(void *arg)
2199 {
2200 struct fcu_obj *fjp;
2201 struct fdisk *fdp;
2202
2203 #ifdef DEBUG
2204 int unit;
2205 #define DEBUG_ASSIGN unit=
2206 #else
2207 #define DEBUG_ASSIGN (void)
2208 #endif
2209 DEBUG_ASSIGN fd_getdrive((dev_t)arg, &fjp, &fdp);
2210 /*
2211 * Ignoring return in non DEBUG mode because device exist.
2212 * Returned unit value is not used.
2213 */
2214
2215 FDERRPRINT(FDEP_L0, FDEM_IOCT,
2216 (CE_CONT, "fd_media_watch unit %d\n", unit));
2217
2218 /*
2219 * fd_get_media_state() cannot be called from this timeout function
2220 * because the floppy drive has to be selected first, and that could
2221 * force this function to sleep (while waiting for the select
2222 * semaphore).
2223 * Instead, just wakeup up driver.
2224 */
2225 mutex_enter(&fjp->fj_lock);
2226 cv_broadcast(&fdp->d_statecv);
2227 mutex_exit(&fjp->fj_lock);
2228 }
2229
2230 enum dkio_state
fd_get_media_state(struct fcu_obj * fjp,int unit)2231 fd_get_media_state(struct fcu_obj *fjp, int unit)
2232 {
2233 enum dkio_state state;
2234
2235 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2236 /* recheck disk only if DSKCHG "high" */
2237 fjp->fj_ops->fco_resetchng(fjp, unit);
2238 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2239 if (fjp->fj_flags & FUNIT_CHGDET) {
2240 /*
2241 * again no diskette; not a new change
2242 */
2243 state = DKIO_NONE;
2244 } else {
2245 /*
2246 * a new change; diskette was ejected
2247 */
2248 fjp->fj_flags |= FUNIT_CHGDET;
2249 state = DKIO_EJECTED;
2250 }
2251 } else {
2252 fjp->fj_flags &= ~FUNIT_CHGDET;
2253 state = DKIO_INSERTED;
2254 }
2255 } else {
2256 fjp->fj_flags &= ~FUNIT_CHGDET;
2257 state = DKIO_INSERTED;
2258 }
2259 FDERRPRINT(FDEP_L0, FDEM_IOCT,
2260 (CE_CONT, "fd_get_media_state unit %d: state %x\n", unit, state));
2261 return (state);
2262 }
2263
2264 static int
fd_check_media(dev_t dev,enum dkio_state state)2265 fd_check_media(dev_t dev, enum dkio_state state)
2266 {
2267 struct fcu_obj *fjp;
2268 struct fdisk *fdp;
2269 int unit;
2270 int err;
2271
2272 unit = fd_getdrive(dev, &fjp, &fdp);
2273
2274 mutex_enter(&fjp->fj_lock);
2275
2276 fjp->fj_ops->fco_select(fjp, unit, 1);
2277 fdp->d_media_state = fd_get_media_state(fjp, unit);
2278 fdp->d_media_timeout = drv_usectohz(fd_check_media_time);
2279
2280 while (fdp->d_media_state == state) {
2281 /* release the controller and drive */
2282 fjp->fj_ops->fco_select(fjp, unit, 0);
2283
2284 /* turn on timer */
2285 fdp->d_media_timeout_id = timeout(fd_media_watch,
2286 (void *)dev, fdp->d_media_timeout);
2287
2288 if (cv_wait_sig(&fdp->d_statecv, &fjp->fj_lock) == 0) {
2289 fdp->d_media_timeout = 0;
2290 mutex_exit(&fjp->fj_lock);
2291 return (EINTR);
2292 }
2293 fjp->fj_ops->fco_select(fjp, unit, 1);
2294 fdp->d_media_state = fd_get_media_state(fjp, unit);
2295 }
2296
2297 if (fdp->d_media_state == DKIO_INSERTED) {
2298 err = fdgetlabel(fjp, unit);
2299 if (err) {
2300 fjp->fj_ops->fco_select(fjp, unit, 0);
2301 mutex_exit(&fjp->fj_lock);
2302 return (EIO);
2303 }
2304 }
2305 fjp->fj_ops->fco_select(fjp, unit, 0);
2306 mutex_exit(&fjp->fj_lock);
2307 return (0);
2308 }
2309
2310 /*
2311 * fd_get_media_info :
2312 * Collects medium information for
2313 * DKIOCGMEDIAINFO ioctl.
2314 */
2315
2316 static int
fd_get_media_info(struct fcu_obj * fjp,caddr_t buf,int flag)2317 fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag)
2318 {
2319 struct dk_minfo media_info;
2320 int err = 0;
2321
2322 media_info.dki_media_type = DK_FLOPPY;
2323 media_info.dki_lbsize = fjp->fj_chars->fdc_sec_size;
2324 media_info.dki_capacity = fjp->fj_chars->fdc_ncyl *
2325 fjp->fj_chars->fdc_secptrack * fjp->fj_chars->fdc_nhead;
2326
2327 if (ddi_copyout(&media_info, buf, sizeof (struct dk_minfo), flag))
2328 err = EFAULT;
2329 return (err);
2330 }
2331