1 /* $NetBSD: ed_mca.c,v 1.46 2009/05/12 13:15:24 cegger Exp $ */
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 * Disk drive goo for MCA ESDI controller driver.
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ed_mca.c,v 1.46 2009/05/12 13:15:24 cegger Exp $");
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
47 #include <sys/ioctl.h>
51 #include <sys/malloc.h>
52 #include <sys/device.h>
53 #include <sys/disklabel.h>
55 #include <sys/syslog.h>
57 #include <sys/vnode.h>
65 #include <dev/mca/mcavar.h>
67 #include <dev/mca/edcreg.h>
68 #include <dev/mca/edvar.h>
69 #include <dev/mca/edcvar.h>
71 /* #define ATADEBUG */
74 #define ATADEBUG_PRINT(args, level) printf args
76 #define ATADEBUG_PRINT(args, level)
79 #define EDLABELDEV(dev) (MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART))
81 static int ed_mca_probe (device_t
, cfdata_t
, void *);
82 static void ed_mca_attach (device_t
, device_t
, void *);
84 CFATTACH_DECL(ed_mca
, sizeof(struct ed_softc
),
85 ed_mca_probe
, ed_mca_attach
, NULL
, NULL
);
87 extern struct cfdriver ed_cd
;
89 static int ed_get_params(struct ed_softc
*, int *);
90 static void edgetdisklabel(dev_t
, struct ed_softc
*);
91 static void edgetdefaultlabel(struct ed_softc
*, struct disklabel
*);
93 dev_type_open(edmcaopen
);
94 dev_type_close(edmcaclose
);
95 dev_type_read(edmcaread
);
96 dev_type_write(edmcawrite
);
97 dev_type_ioctl(edmcaioctl
);
98 dev_type_strategy(edmcastrategy
);
99 dev_type_dump(edmcadump
);
100 dev_type_size(edmcasize
);
102 const struct bdevsw ed_bdevsw
= {
103 edmcaopen
, edmcaclose
, edmcastrategy
, edmcaioctl
,
104 edmcadump
, edmcasize
, D_DISK
107 const struct cdevsw ed_cdevsw
= {
108 edmcaopen
, edmcaclose
, edmcaread
, edmcawrite
, edmcaioctl
,
109 nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_DISK
112 static struct dkdriver eddkdriver
= { edmcastrategy
, minphys
};
115 * Just check if it's possible to identify the disk.
118 ed_mca_probe(device_t parent
, cfdata_t cf
,
121 u_int16_t cmd_args
[2];
122 struct edc_mca_softc
*sc
= (void *) parent
;
123 struct ed_attach_args
*eda
= (struct ed_attach_args
*) aux
;
127 * Get Device Configuration (09).
129 cmd_args
[0] = 14; /* Options: 00s110, s: 0=Physical 1=Pseudo */
131 if (edc_run_cmd(sc
, CMD_GET_DEV_CONF
, eda
->edc_drive
, cmd_args
, 2, 1))
138 ed_mca_attach(device_t parent
, device_t self
, void *aux
)
140 struct ed_softc
*ed
= device_private(self
);
141 struct edc_mca_softc
*sc
= device_private(parent
);
142 struct ed_attach_args
*eda
= (struct ed_attach_args
*) aux
;
147 ed
->sc_devno
= eda
->edc_drive
;
148 edc_add_disk(sc
, ed
);
150 bufq_alloc(&ed
->sc_q
, "disksort", BUFQ_SORT_RAWBLOCK
);
151 simple_lock_init(&ed
->sc_q_lock
);
153 if (ed_get_params(ed
, &drv_flags
)) {
154 printf(": IDENTIFY failed, no disk found\n");
158 format_bytes(pbuf
, sizeof(pbuf
),
159 (u_int64_t
) ed
->sc_capacity
* DEV_BSIZE
);
160 printf(": %s, %u cyl, %u head, %u sec, 512 bytes/sect x %u sectors\n",
162 ed
->cyl
, ed
->heads
, ed
->sectors
,
165 printf("%s: %u spares/cyl, %s, %s, %s, %s, %s\n",
166 device_xname(&ed
->sc_dev
), ed
->spares
,
167 (drv_flags
& (1 << 0)) ? "NoRetries" : "Retries",
168 (drv_flags
& (1 << 1)) ? "Removable" : "Fixed",
169 (drv_flags
& (1 << 2)) ? "SkewedFormat" : "NoSkew",
170 (drv_flags
& (1 << 3)) ? "ZeroDefect" : "Defects",
171 (drv_flags
& (1 << 4)) ? "InvalidSecondary" : "SecondaryOK"
175 * Initialize and attach the disk structure.
177 disk_init(&ed
->sc_dk
, device_xname(&ed
->sc_dev
), &eddkdriver
);
178 disk_attach(&ed
->sc_dk
);
180 rnd_attach_source(&ed
->rnd_source
, device_xname(&ed
->sc_dev
),
184 ed
->sc_flags
|= EDF_INIT
;
187 * XXX We should try to discovery wedges here, but
188 * XXX that would mean being able to do I/O. Should
189 * XXX use config_defer() here.
194 * Read/write routine for a buffer. Validates the arguments and schedules the
195 * transfer. Does not wait for the transfer to complete.
198 edmcastrategy(struct buf
*bp
)
201 struct disklabel
*lp
;
204 ed
= device_lookup_private(&ed_cd
, DISKUNIT(bp
->b_dev
));
205 lp
= ed
->sc_dk
.dk_label
;
207 ATADEBUG_PRINT(("edmcastrategy (%s)\n", device_xname(&ed
->sc_dev
)),
211 if (bp
->b_blkno
< 0 ||
212 (bp
->b_bcount
% lp
->d_secsize
) != 0 ||
213 (bp
->b_bcount
/ lp
->d_secsize
) >= (1 << NBBY
)) {
214 bp
->b_error
= EINVAL
;
218 /* If device invalidated (e.g. media change, door open), error. */
219 if ((ed
->sc_flags
& WDF_LOADED
) == 0) {
224 /* If it's a null transfer, return immediately. */
225 if (bp
->b_bcount
== 0)
229 * Do bounds checking, adjust transfer. if error, process.
230 * If end of partition, just return.
232 if (DISKPART(bp
->b_dev
) != RAW_PART
&&
233 bounds_check_with_label(&ed
->sc_dk
, bp
,
234 (ed
->sc_flags
& (WDF_WLABEL
|WDF_LABELLING
)) != 0) <= 0)
238 * Now convert the block number to absolute and put it in
239 * terms of the device's logical block size.
241 if (lp
->d_secsize
>= DEV_BSIZE
)
242 blkno
= bp
->b_blkno
/ (lp
->d_secsize
/ DEV_BSIZE
);
244 blkno
= bp
->b_blkno
* (DEV_BSIZE
/ lp
->d_secsize
);
246 if (DISKPART(bp
->b_dev
) != RAW_PART
)
247 blkno
+= lp
->d_partitions
[DISKPART(bp
->b_dev
)].p_offset
;
249 bp
->b_rawblkno
= blkno
;
251 /* Queue transfer on drive, activate drive and controller if idle. */
252 simple_lock(&ed
->sc_q_lock
);
253 bufq_put(ed
->sc_q
, bp
);
254 simple_unlock(&ed
->sc_q_lock
);
256 /* Ring the worker thread */
257 wakeup_one(ed
->edc_softc
);
261 /* Toss transfer; we're done early. */
262 bp
->b_resid
= bp
->b_bcount
;
267 edmcaread(dev_t dev
, struct uio
*uio
, int flags
)
269 ATADEBUG_PRINT(("edread\n"), DEBUG_XFERS
);
270 return (physio(edmcastrategy
, NULL
, dev
, B_READ
, minphys
, uio
));
274 edmcawrite(dev_t dev
, struct uio
*uio
, int flags
)
276 ATADEBUG_PRINT(("edwrite\n"), DEBUG_XFERS
);
277 return (physio(edmcastrategy
, NULL
, dev
, B_WRITE
, minphys
, uio
));
281 edmcaopen(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
286 ATADEBUG_PRINT(("edopen\n"), DEBUG_FUNCS
);
287 wd
= device_lookup_private(&ed_cd
, DISKUNIT(dev
));
288 if (wd
== NULL
|| (wd
->sc_flags
& EDF_INIT
) == 0)
291 part
= DISKPART(dev
);
293 mutex_enter(&wd
->sc_dk
.dk_openlock
);
296 * If there are wedges, and this is not RAW_PART, then we
299 if (wd
->sc_dk
.dk_nwedges
!= 0 && part
!= RAW_PART
) {
304 if (wd
->sc_dk
.dk_openmask
!= 0) {
306 * If any partition is open, but the disk has been invalidated,
307 * disallow further opens.
309 if ((wd
->sc_flags
& WDF_LOADED
) == 0) {
314 if ((wd
->sc_flags
& WDF_LOADED
) == 0) {
317 wd
->sc_flags
|= WDF_LOADED
;
319 /* Load the physical device parameters. */
321 ed_get_params(wd
, NULL
);
324 /* Load the partition info if not already loaded. */
325 edgetdisklabel(dev
, wd
);
329 /* Check that the partition exists. */
330 if (part
!= RAW_PART
&&
331 (part
>= wd
->sc_dk
.dk_label
->d_npartitions
||
332 wd
->sc_dk
.dk_label
->d_partitions
[part
].p_fstype
== FS_UNUSED
)) {
337 /* Insure only one open at a time. */
340 wd
->sc_dk
.dk_copenmask
|= (1 << part
);
343 wd
->sc_dk
.dk_bopenmask
|= (1 << part
);
346 wd
->sc_dk
.dk_openmask
=
347 wd
->sc_dk
.dk_copenmask
| wd
->sc_dk
.dk_bopenmask
;
351 mutex_exit(&wd
->sc_dk
.dk_openlock
);
356 edmcaclose(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
358 struct ed_softc
*wd
= device_lookup_private(&ed_cd
, DISKUNIT(dev
));
359 int part
= DISKPART(dev
);
361 ATADEBUG_PRINT(("edmcaclose\n"), DEBUG_FUNCS
);
363 mutex_enter(&wd
->sc_dk
.dk_openlock
);
367 wd
->sc_dk
.dk_copenmask
&= ~(1 << part
);
370 wd
->sc_dk
.dk_bopenmask
&= ~(1 << part
);
373 wd
->sc_dk
.dk_openmask
=
374 wd
->sc_dk
.dk_copenmask
| wd
->sc_dk
.dk_bopenmask
;
376 if (wd
->sc_dk
.dk_openmask
== 0) {
378 wd_flushcache(wd
, AT_WAIT
);
380 /* XXXX Must wait for I/O to complete! */
382 if (! (wd
->sc_flags
& WDF_KLABEL
))
383 wd
->sc_flags
&= ~WDF_LOADED
;
386 mutex_exit(&wd
->sc_dk
.dk_openlock
);
392 edgetdefaultlabel(struct ed_softc
*ed
, struct disklabel
*lp
)
394 ATADEBUG_PRINT(("edgetdefaultlabel\n"), DEBUG_FUNCS
);
395 memset(lp
, 0, sizeof(struct disklabel
));
397 lp
->d_secsize
= DEV_BSIZE
;
398 lp
->d_ntracks
= ed
->heads
;
399 lp
->d_nsectors
= ed
->sectors
;
400 lp
->d_ncylinders
= ed
->cyl
;
401 lp
->d_secpercyl
= lp
->d_ntracks
* lp
->d_nsectors
;
403 lp
->d_type
= DTYPE_ESDI
;
405 strncpy(lp
->d_typename
, "ESDI", 16);
406 strncpy(lp
->d_packname
, "fictitious", 16);
407 lp
->d_secperunit
= ed
->sc_capacity
;
409 lp
->d_interleave
= 1;
412 lp
->d_partitions
[RAW_PART
].p_offset
= 0;
413 lp
->d_partitions
[RAW_PART
].p_size
=
414 lp
->d_secperunit
* (lp
->d_secsize
/ DEV_BSIZE
);
415 lp
->d_partitions
[RAW_PART
].p_fstype
= FS_UNUSED
;
416 lp
->d_npartitions
= RAW_PART
+ 1;
418 lp
->d_magic
= DISKMAGIC
;
419 lp
->d_magic2
= DISKMAGIC
;
420 lp
->d_checksum
= dkcksum(lp
);
424 * Fabricate a default disk label, and try to read the correct one.
427 edgetdisklabel(dev_t dev
, struct ed_softc
*ed
)
429 struct disklabel
*lp
= ed
->sc_dk
.dk_label
;
430 const char *errstring
;
432 ATADEBUG_PRINT(("edgetdisklabel\n"), DEBUG_FUNCS
);
434 memset(ed
->sc_dk
.dk_cpulabel
, 0, sizeof(struct cpu_disklabel
));
436 edgetdefaultlabel(ed
, lp
);
438 errstring
= readdisklabel(
439 EDLABELDEV(dev
), edmcastrategy
, lp
, ed
->sc_dk
.dk_cpulabel
);
442 * This probably happened because the drive's default
443 * geometry doesn't match the DOS geometry. We
444 * assume the DOS geometry is now in the label and try
445 * again. XXX This is a kluge.
448 if (wd
->drvp
->state
> RECAL
)
449 wd
->drvp
->drive_flags
|= DRIVE_RESET
;
451 errstring
= readdisklabel(EDLABELDEV(dev
),
452 edmcastrategy
, lp
, ed
->sc_dk
.dk_cpulabel
);
455 printf("%s: %s\n", device_xname(&ed
->sc_dev
), errstring
);
461 edmcaioctl(dev_t dev
, u_long xfer
, void *addr
, int flag
, struct lwp
*l
)
463 struct ed_softc
*ed
= device_lookup_private(&ed_cd
, DISKUNIT(dev
));
466 ATADEBUG_PRINT(("edioctl\n"), DEBUG_FUNCS
);
468 if ((ed
->sc_flags
& WDF_LOADED
) == 0)
473 *(struct disklabel
*)addr
= *(ed
->sc_dk
.dk_label
);
477 ((struct partinfo
*)addr
)->disklab
= ed
->sc_dk
.dk_label
;
478 ((struct partinfo
*)addr
)->part
=
479 &ed
->sc_dk
.dk_label
->d_partitions
[DISKPART(dev
)];
485 struct disklabel
*lp
;
487 lp
= (struct disklabel
*)addr
;
489 if ((flag
& FWRITE
) == 0)
492 mutex_enter(&ed
->sc_dk
.dk_openlock
);
493 ed
->sc_flags
|= WDF_LABELLING
;
495 error
= setdisklabel(ed
->sc_dk
.dk_label
,
496 lp
, /*wd->sc_dk.dk_openmask : */0,
497 ed
->sc_dk
.dk_cpulabel
);
500 if (wd
->drvp
->state
> RECAL
)
501 wd
->drvp
->drive_flags
|= DRIVE_RESET
;
503 if (xfer
== DIOCWDINFO
)
504 error
= writedisklabel(EDLABELDEV(dev
),
505 edmcastrategy
, ed
->sc_dk
.dk_label
,
506 ed
->sc_dk
.dk_cpulabel
);
509 ed
->sc_flags
&= ~WDF_LABELLING
;
510 mutex_exit(&ed
->sc_dk
.dk_openlock
);
516 ed
->sc_flags
|= WDF_KLABEL
;
518 ed
->sc_flags
&= ~WDF_KLABEL
;
522 if ((flag
& FWRITE
) == 0)
525 ed
->sc_flags
|= WDF_WLABEL
;
527 ed
->sc_flags
&= ~WDF_WLABEL
;
531 edgetdefaultlabel(ed
, (struct disklabel
*)addr
);
536 if ((flag
& FWRITE
) == 0)
539 register struct format_op
*fop
;
543 fop
= (struct format_op
*)addr
;
544 aiov
.iov_base
= fop
->df_buf
;
545 aiov
.iov_len
= fop
->df_count
;
546 auio
.uio_iov
= &aiov
;
548 auio
.uio_resid
= fop
->df_count
;
551 fop
->df_startblk
* wd
->sc_dk
.dk_label
->d_secsize
;
553 error
= physio(wdformat
, NULL
, dev
, B_WRITE
, minphys
,
555 fop
->df_count
-= auio
.uio_resid
;
556 fop
->df_reg
[0] = wdc
->sc_status
;
557 fop
->df_reg
[1] = wdc
->sc_error
;
564 struct dkwedge_info
*dkw
= (void *) addr
;
566 if ((flag
& FWRITE
) == 0)
569 /* If the ioctl happens here, the parent is us. */
570 strlcpy(dkw
->dkw_parent
, device_xname(&ed
->sc_dev
),
571 sizeof(dkw
->dkw_parent
));
572 return (dkwedge_add(dkw
));
577 struct dkwedge_info
*dkw
= (void *) addr
;
579 if ((flag
& FWRITE
) == 0)
582 /* If the ioctl happens here, the parent is us. */
583 strlcpy(dkw
->dkw_parent
, device_xname(&ed
->sc_dev
),
584 sizeof(dkw
->dkw_parent
));
585 return (dkwedge_del(dkw
));
590 struct dkwedge_list
*dkwl
= (void *) addr
;
592 return (dkwedge_list(&ed
->sc_dk
, dkwl
, l
));
600 panic("edioctl: impossible");
611 ATADEBUG_PRINT(("edsize\n"), DEBUG_FUNCS
);
613 wd
= device_lookup_private(&ed_cd
, DISKUNIT(dev
));
617 part
= DISKPART(dev
);
618 omask
= wd
->sc_dk
.dk_openmask
& (1 << part
);
620 if (omask
== 0 && edmcaopen(dev
, 0, S_IFBLK
, NULL
) != 0)
622 if (wd
->sc_dk
.dk_label
->d_partitions
[part
].p_fstype
!= FS_SWAP
)
625 size
= wd
->sc_dk
.dk_label
->d_partitions
[part
].p_size
*
626 (wd
->sc_dk
.dk_label
->d_secsize
/ DEV_BSIZE
);
627 if (omask
== 0 && edmcaclose(dev
, 0, S_IFBLK
, NULL
) != 0)
632 /* #define WD_DUMP_NOT_TRUSTED if you just want to watch */
633 static int eddoingadump
= 0;
634 static int eddumprecalibrated
= 0;
635 static int eddumpmulti
= 1;
638 * Dump core after a system crash.
641 edmcadump(dev_t dev
, daddr_t blkno
, void *va
, size_t size
)
643 struct ed_softc
*ed
; /* disk unit to do the I/O */
644 struct disklabel
*lp
; /* disk's disklabel */
646 int nblks
; /* total number of sectors left to write */
649 /* Check if recursive dump; if so, punt. */
654 ed
= device_lookup_private(&ed_cd
, DISKUNIT(dev
));
658 part
= DISKPART(dev
);
660 /* Make sure it was initialized. */
661 if ((ed
->sc_flags
& EDF_INIT
) == 0)
664 /* Convert to disk sectors. Request must be a multiple of size. */
665 lp
= ed
->sc_dk
.dk_label
;
666 if ((size
% lp
->d_secsize
) != 0)
668 nblks
= size
/ lp
->d_secsize
;
669 blkno
= blkno
/ (lp
->d_secsize
/ DEV_BSIZE
);
671 /* Check transfer bounds against partition size. */
672 if ((blkno
< 0) || ((blkno
+ nblks
) > lp
->d_partitions
[part
].p_size
))
675 /* Offset block number to start of partition. */
676 blkno
+= lp
->d_partitions
[part
].p_offset
;
678 /* Recalibrate, if first dump transfer. */
679 if (eddumprecalibrated
== 0) {
680 eddumprecalibrated
= 1;
683 wd
->drvp
->state
= RESET
;
688 error
= edc_bio(ed
->edc_softc
, ed
, va
, blkno
,
689 min(nblks
, eddumpmulti
) * lp
->d_secsize
, 0, 1);
693 /* update block count */
694 nblks
-= min(nblks
, eddumpmulti
);
695 blkno
+= min(nblks
, eddumpmulti
);
696 va
= (char *)va
+ min(nblks
, eddumpmulti
) * lp
->d_secsize
;
704 ed_get_params(struct ed_softc
*ed
, int *drv_flags
)
706 u_int16_t cmd_args
[2];
709 * Get Device Configuration (09).
711 cmd_args
[0] = 14; /* Options: 00s110, s: 0=Physical 1=Pseudo */
713 if (edc_run_cmd(ed
->edc_softc
, CMD_GET_DEV_CONF
, ed
->sc_devno
,
717 ed
->spares
= ed
->sense_data
[1] >> 8;
719 *drv_flags
= ed
->sense_data
[1] & 0x1f;
720 ed
->rba
= ed
->sense_data
[2] | (ed
->sense_data
[3] << 16);
722 ed->cyl = ed->sense_data[4];
723 ed->heads = ed->sense_data[5] & 0xff;
724 ed->sectors = ed->sense_data[5] >> 8;
725 * we fabricate the numbers from RBA count, so that
726 * number of sectors is 32 and heads 64. This seems
727 * to be necessary for integrated ESDI controller.
731 ed
->cyl
= ed
->rba
/ (ed
->heads
* ed
->sectors
);
732 ed
->sc_capacity
= ed
->rba
;