#include <sys/param.h>
#include <sys/buf.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/open.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/autoconf.h>
#include <sys/dklabel.h>
#include <sys/vtoc.h>
#include <sys/dkio.h>
#include <sys/fdio.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kstat.h>
#include <sys/cpu.h>
#include "sys/fdvar.h"
#include "sys/fdreg.h"
#include "sys/dma_i8237A.h"
#define KIOSP KSTAT_IO_PTR(un->un_iostat)
#define KIOIP KSTAT_INTR_PTR(fdc->c_intrstat)
#define MEDIUM_DENSITY 0x40
#define SEC_SIZE_CODE (fdctlr.c_csb->csb_unit]->un_chars->medium ? 3 : 2)
#define CMD_READ (MT + SK + FDRAW_RDCMD + MFM)
#define CMD_WRITE (MT + FDRAW_WRCMD + MFM)
#define C CE_CONT
#define FD_POLLABLE_PROP "pollable"
#define FD_MANUAL_EJECT "manual"
#define FD_UNIT "unit"
int max_fd_dma_len = 2048;
static void quiesce_fd_interrupt(struct fdctlr *);
static int fd_open(dev_t *, int, int, cred_t *);
static int fd_close(dev_t, int, int, cred_t *);
static int fd_strategy(struct buf *);
static int fd_read(dev_t, struct uio *, cred_t *);
static int fd_write(dev_t, struct uio *, cred_t *);
static int fd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int
fd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, caddr_t, int *);
static int fd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result);
static int fd_attach(dev_info_t *, ddi_attach_cmd_t);
static int fd_detach(dev_info_t *, ddi_detach_cmd_t);
static int fd_power(dev_info_t *dip, int component, int level);
static int fd_attach_check_drive(struct fdctlr *fdc);
static int fd_attach_det_ctlr(dev_info_t *dip, struct fdctlr *fdc);
static int fd_attach_map_regs(dev_info_t *dip, struct fdctlr *fdc);
static int fd_attach_register_interrupts(dev_info_t *dip, struct fdctlr *fdc,
int *hard);
static int fd_build_label_vtoc(struct fdunit *, struct vtoc *);
static void fd_build_user_vtoc(struct fdunit *, struct vtoc *);
static int fdcheckdisk(struct fdctlr *fdc, int unit);
static int fd_check_media(dev_t dev, enum dkio_state state);
static void fd_cleanup(dev_info_t *dip, struct fdctlr *fdc, int hard,
int locks);
static void fdeject(struct fdctlr *, int unit);
static int fdexec(struct fdctlr *fdc, int flags);
static void fdexec_turn_on_motor(struct fdctlr *fdc, int flags, uint_t unit);
static int fdformat(struct fdctlr *fdc, int unit, int cyl, int hd);
static caddr_t fd_getauxiova();
static struct fdctlr *fd_getctlr(dev_t);
static void fdgetcsb(struct fdctlr *);
static int fdgetlabel(struct fdctlr *fdc, int unit);
enum dkio_state fd_get_media_state(struct fdctlr *, int);
static uint_t fdintr_dma();
static int fd_isauxiodip(dev_info_t *);
static uint_t fd_lointr(caddr_t arg);
static void fd_media_watch(void *);
static void fdmotoff(void *);
static int fd_part_is_open(struct fdunit *un, int part);
static int fdrawioctl(struct fdctlr *, int, intptr_t, int);
static int fdrecalseek(struct fdctlr *fdc, int unit, int arg, int execflg);
static int fdrecover(struct fdctlr *);
static void fdretcsb(struct fdctlr *);
static int fdreset(struct fdctlr *);
static int fdrw(struct fdctlr *fdc, int, int, int, int, int, caddr_t, uint_t);
static void fdselect(struct fdctlr *fdc, int unit, int onoff);
static int fdsensedrv(struct fdctlr *fdc, int unit);
static int fdsense_chng(struct fdctlr *, int unit);
static void fdstart(struct fdctlr *);
static int fdstart_dma(register struct fdctlr *fdc, caddr_t addr, uint_t len);
static int fd_unit_is_open(struct fdunit *);
static void fdunpacklabel(struct packed_label *, struct dk_label *);
static int fd_unbind_handle(struct fdctlr *);
static void fdwatch(void *);
static void set_rotational_speed(struct fdctlr *, int);
static int fd_get_media_info(struct fdunit *un, caddr_t buf, int flag);
static int fd_pm_lower_power(struct fdctlr *fdc);
static int fd_pm_raise_power(struct fdctlr *fdc);
static void create_pm_components(dev_info_t *dip);
static void set_data_count_register(struct fdctlr *fdc, uint32_t count);
static uint32_t get_data_count_register(struct fdctlr *fdc);
static void reset_dma_controller(struct fdctlr *fdc);
static void set_data_address_register(struct fdctlr *fdc, uint32_t address);
static uint32_t get_dma_control_register(struct fdctlr *fdc);
static void set_dma_mode(struct fdctlr *fdc, int val);
static void set_dma_control_register(struct fdctlr *fdc, uint32_t val);
static void release_sb_dma(struct fdctlr *fdc);
extern uint_t fd_intr(caddr_t);
extern void set_auxioreg();
extern void call_debug();
#define CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc) \
{\
while (fdc->c_un->un_state == FD_STATE_SUSPENDED) {\
cv_wait(&fdc->c_suspend_cv, \
&fdc->c_lolock);\
}\
}
struct fdctlr *fdctlrs;
static int fd_check_media_time = 5000000;
static int fd_pollable = 0;
static uchar_t rwretry = 10;
static uchar_t skretry = 5;
static int fd_burstsize = DCSR_BURST_0 | DCSR_BURST_1;
static struct driver_minor_data {
char *name;
int minor;
int type;
} fd_minor [] = {
{ "a", 0, S_IFBLK},
{ "b", 1, S_IFBLK},
{ "c", 2, S_IFBLK},
{ "a,raw", 0, S_IFCHR},
{ "b,raw", 1, S_IFCHR},
{ "c,raw", 2, S_IFCHR},
{0}
};
char *panic_msg = "fd_intr: unexpected interrupt\n";
static uchar_t fdspec[2] = { 0xc2, 0x33 };
static uchar_t fdconf[3] = { 0x64, 0x58, 0x00 };
#define SPEC_DMA_MODE 0x32
static struct fd_char fdtypes[] = {
{
0,
500,
80,
2,
512,
21,
-1,
},
{
0,
500,
80,
2,
512,
18,
-1,
},
{
1,
500,
77,
2,
1024,
8,
-1,
},
{
0,
250,
80,
2,
512,
9,
-1,
}
};
static int nfdtypes = sizeof (fdtypes) / sizeof (fdtypes[0]);
static struct packed_label fdlbl_high_21 = {
{ "3.5\" floppy cyl 80 alt 0 hd 2 sec 21" },
300,
80,
0,
1,
80,
0,
2,
21,
{
{ 0, 79 * 2 * 21 },
{ 79, 1 * 2 * 21 },
{ 0, 80 * 2 * 21 },
},
{ 0,
"",
3,
{ 0 },
{ 0 },
VTOC_SANE,
{ 0 },
0,
},
};
static struct packed_label fdlbl_high_80 = {
{ "3.5\" floppy cyl 80 alt 0 hd 2 sec 18" },
300,
80,
0,
1,
80,
0,
2,
18,
{
{ 0, 79 * 2 * 18 },
{ 79, 1 * 2 * 18 },
{ 0, 80 * 2 * 18 },
},
{ 0,
"",
3,
{ 0 },
{ 0 },
VTOC_SANE,
{ 0 },
0,
},
};
static struct packed_label fdlbl_medium_80 = {
{ "3.5\" floppy cyl 77 alt 0 hd 2 sec 8" },
360,
77,
0,
1,
77,
0,
2,
16,
{
{ 0, 76 * 2 * 8 * 2 },
{ 76, 1 * 2 * 8 * 2 },
{ 0, 77 * 2 * 8 * 2 },
},
{ 0,
"",
3,
{ 0 },
{ 0 },
VTOC_SANE,
{ 0 },
0,
},
};
static struct packed_label fdlbl_low_80 = {
{ "3.5\" floppy cyl 80 alt 0 hd 2 sec 9" },
300,
80,
0,
1,
80,
0,
2,
9,
{
{ 0, 79 * 2 * 9 },
{ 79, 1 * 2 * 9 },
{ 0, 80 * 2 * 9 },
},
{ 0,
"",
3,
{ 0 },
{ 0 },
VTOC_SANE,
{ 0 },
0,
},
};
static struct fdcmdinfo {
char *cmdname;
uchar_t ncmdbytes;
uchar_t nrsltbytes;
uchar_t cmdtype;
} fdcmds[] = {
"", 0, 0, 0,
"", 0, 0, 0,
"read_track", 9, 7, 1,
"specify", 3, 0, 3,
"sense_drv_status", 2, 1, 3,
"write", 9, 7, 1,
"read", 9, 7, 1,
"recalibrate", 2, 0, 2,
"sense_int_status", 1, 2, 3,
"write_del", 9, 7, 1,
"read_id", 2, 7, 2,
"motor_on/off", 1, 0, 4,
"read_del", 9, 7, 1,
"format_track", 10, 7, 1,
"dump_reg", 1, 10, 4,
"seek", 3, 0, 2,
"", 0, 0, 0,
"", 0, 0, 0,
"", 0, 0, 0,
"configure", 4, 0, 4,
};
static struct cb_ops fd_cb_ops = {
fd_open,
fd_close,
fd_strategy,
nodev,
nodev,
fd_read,
fd_write,
fd_ioctl,
nodev,
nodev,
nodev,
nochpoll,
fd_prop_op,
0,
D_NEW | D_MP
};
static struct dev_ops fd_ops = {
DEVO_REV,
0,
fd_info,
nulldev,
nulldev,
fd_attach,
fd_detach,
nodev,
&fd_cb_ops,
(struct bus_ops *)0,
fd_power,
ddi_quiesce_not_supported,
};
static unsigned int fderrmask = (unsigned int)FDEM_ALL;
static int fderrlevel = 3;
static int tosec = 16;
#include <sys/modctl.h>
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"Floppy Driver",
&fd_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0)
return (e);
return (0);
}
static int
fd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
struct fdctlr *fdc;
struct driver_minor_data *dmdp;
int instance = ddi_get_instance(dip);
int hard_intr_set = 0;
FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: start\n"));
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
if (!(fdc = fd_getctlr(instance << FDINSTSHIFT))) {
return (DDI_FAILURE);
}
quiesce_fd_interrupt(fdc);
if (fdc->c_fdtype & FDCTYPE_SB)
if (ddi_add_intr(dip, 0, &fdc->c_block, 0,
fdintr_dma, (caddr_t)0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
(void) pm_raise_power(dip, 0, PM_LEVEL_ON);
mutex_enter(&fdc->c_lolock);
cv_broadcast(&fdc->c_suspend_cv);
mutex_exit(&fdc->c_lolock);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, FD_POLLABLE_PROP, 0))
fd_pollable = 1;
fdc = kmem_zalloc(sizeof (*fdc), KM_SLEEP);
fdc->c_dip = dip;
fdc->c_next = fdctlrs;
fdctlrs = fdc;
if (fd_attach_det_ctlr(dip, fdc) == DDI_FAILURE) {
fd_cleanup(dip, fdc, hard_intr_set, 0);
return (DDI_FAILURE);
}
if (fd_attach_map_regs(dip, fdc) == DDI_FAILURE) {
fd_cleanup(dip, fdc, hard_intr_set, 0);
return (DDI_FAILURE);
}
if (fdc->c_fdtype & FDCTYPE_DMA) {
fdc->c_fd_dma_lim.dma_attr_version = DMA_ATTR_V0;
fdc->c_fd_dma_lim.dma_attr_addr_lo = 0x00000000ull;
fdc->c_fd_dma_lim.dma_attr_addr_hi = 0xfffffffeull;
fdc->c_fd_dma_lim.dma_attr_count_max = 0xffffff;
if (fdc->c_fdtype & FDCTYPE_SB) {
fdc->c_fd_dma_lim.dma_attr_align = FD_SB_DMA_ALIGN;
} else {
fdc->c_fd_dma_lim.dma_attr_align = 1;
}
fdc->c_fd_dma_lim.dma_attr_burstsizes = 0x0;
fdc->c_fd_dma_lim.dma_attr_minxfer = 1;
fdc->c_fd_dma_lim.dma_attr_maxxfer = 0xffff;
fdc->c_fd_dma_lim.dma_attr_seg = 0xffff;
fdc->c_fd_dma_lim.dma_attr_sgllen = 1;
fdc->c_fd_dma_lim.dma_attr_granular = 512;
if (ddi_dma_alloc_handle(dip, &fdc->c_fd_dma_lim,
DDI_DMA_DONTWAIT, 0, &fdc->c_dmahandle) != DDI_SUCCESS) {
fd_cleanup(dip, fdc, hard_intr_set, 0);
return (DDI_FAILURE);
}
if (fdc->c_fdtype & FDCTYPE_SB) {
ddi_device_acc_attr_t dev_attr;
size_t rlen;
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
if (ddi_dma_mem_alloc(fdc->c_dmahandle,
(size_t)(32*1024), &dev_attr, DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP, NULL, (caddr_t *)&fdc->dma_buf,
&rlen, &fdc->c_dma_buf_handle) != DDI_SUCCESS) {
fd_cleanup(dip, fdc, hard_intr_set, 0);
return (DDI_FAILURE);
}
}
}
if (fd_attach_register_interrupts(dip, fdc,
&hard_intr_set) == DDI_FAILURE) {
fd_cleanup(dip, fdc, hard_intr_set, 0);
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fd_attach: registering interrupts failed\n"));
return (DDI_FAILURE);
}
fdc->c_un = kmem_zalloc(sizeof (struct fdunit), KM_SLEEP);
fdc->c_un->un_chars = kmem_alloc(sizeof (struct fd_char), KM_SLEEP);
fdc->c_un->un_iostat = kstat_create("fd", 0, "fd0", "disk",
KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
if (fdc->c_un->un_iostat) {
fdc->c_un->un_iostat->ks_lock = &fdc->c_lolock;
kstat_install(fdc->c_un->un_iostat);
}
fdc->c_un->un_drive = kmem_zalloc(sizeof (struct fd_drive), KM_SLEEP);
if (ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, FD_MANUAL_EJECT, 0)) {
fdc->c_un->un_drive->fdd_ejectable = 0;
} else {
fdc->c_un->un_drive->fdd_ejectable = -1;
}
FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: ejectable? %d\n",
fdc->c_un->un_drive->fdd_ejectable));
fdc->c_un->un_unit_no = ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, FD_UNIT, 0);
if (fdc->c_fdtype & FDCTYPE_SB) {
fdc->sb_dma_channel = ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "dma-channel", 0);
}
FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: unit %d\n",
fdc->c_un->un_unit_no));
fdc->c_un->un_curfdtype = 1;
*fdc->c_un->un_chars = fdtypes[fdc->c_un->un_curfdtype];
fdunpacklabel(&fdlbl_high_80, &fdc->c_un->un_label);
if (fd_attach_check_drive(fdc) == DDI_FAILURE) {
fd_cleanup(dip, fdc, hard_intr_set, 1);
return (DDI_FAILURE);
}
for (dmdp = fd_minor; dmdp->name != NULL; dmdp++) {
if (ddi_create_minor_node(dip, dmdp->name, dmdp->type,
(instance << FDINSTSHIFT) | dmdp->minor,
DDI_NT_FD, 0) == DDI_FAILURE) {
fd_cleanup(dip, fdc, hard_intr_set, 1);
return (DDI_FAILURE);
}
}
create_pm_components(dip);
(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
DDI_KERNEL_IOCTL, NULL, 0);
ddi_report_dev(dip);
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "attached 0x%x\n", ddi_get_instance(dip)));
return (DDI_SUCCESS);
}
static int
fd_attach_map_regs(dev_info_t *dip, struct fdctlr *fdc)
{
ddi_device_acc_attr_t attr;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
if (fdc->c_fdtype & FDCTYPE_SB) {
if (ddi_regs_map_setup(dip, 1, (caddr_t *)&fdc->c_dma_regs,
0, sizeof (struct sb_dma_reg), &attr,
&fdc->c_handlep_dma)) {
return (DDI_FAILURE);
}
} else if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
if (ddi_regs_map_setup(dip, 1, (caddr_t *)&fdc->c_dma_regs,
0, sizeof (struct cheerio_dma_reg), &attr,
&fdc->c_handlep_dma)) {
return (DDI_FAILURE);
}
}
reset_dma_controller(fdc);
set_dma_control_register(fdc, DCSR_INIT_BITS);
switch (fdc->c_fdtype & FDCTYPE_CTRLMASK) {
case FDCTYPE_82077:
FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "type is 82077\n"));
fdc->c_control =
(uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_control;
fdc->c_fifo = (uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_fifo;
fdc->c_dor = (uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_dor;
fdc->c_dir = (uchar_t *)&fdc->c_reg->fdc_82077_reg.fdc_dir;
FDERRPRINT(FDEP_L1, FDEM_ATTA, ((int)C,
(char *)"fdattach: msr/dsr at %p\n",
(void *)fdc->c_control));
fdconf[0] = 0;
quiesce_fd_interrupt(fdc);
break;
default:
break;
}
return (0);
}
static int
fd_attach_det_ctlr(dev_info_t *dip, struct fdctlr *fdc)
{
ddi_device_acc_attr_t attr;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach_det_cltr: start \n"));
if (ddi_regs_map_setup(dip, 0, (caddr_t *)&fdc->c_reg,
0, sizeof (union fdcreg),
&attr,
&fdc->c_handlep_cont)) {
return (DDI_FAILURE);
}
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach_det_cltr: mapped floppy regs\n"));
if (strcmp(ddi_get_name(dip), "SUNW,fdtwo") == 0) {
fdc->c_fdtype |= FDCTYPE_SLAVIO;
fdc->c_fdtype |= FDCTYPE_82077;
fdc->c_auxiova = fd_getauxiova(dip);
fdc->c_auxiodata = (uchar_t)(AUX_MBO4M|AUX_TC4M);
fdc->c_auxiodata2 = (uchar_t)AUX_TC4M;
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: slavio will be used!\n"));
} else if (strcmp(ddi_get_name(dip), "pnpALI,1533,0") == 0) {
fdc->c_fdtype |= FDCTYPE_SB;
fdc->c_fdtype |= FDCTYPE_82077;
fdc->c_fdtype |= FDCTYPE_DMA;
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: southbridge will be used!\n"));
fdc->c_fdtype |= FDCTYPE_DMA8237;
FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: DMA used\n"));
} else if (strcmp(ddi_get_name(dip), "fdthree") == 0) {
fdc->c_fdtype |= FDCTYPE_CHEERIO;
fdc->c_fdtype |= FDCTYPE_82077;
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: cheerio will be used!\n"));
if (ddi_regs_map_setup(dip, 2, (caddr_t *)&fdc->c_auxio_reg,
0, sizeof (uint_t), &attr, &fdc->c_handlep_aux)) {
return (DDI_FAILURE);
}
Set_auxio(fdc, AUX_HIGH_DENSITY);
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: auxio register 0x%x\n",
*fdc->c_auxio_reg));
fdc->c_fdtype |= FDCTYPE_DMA;
FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_attach: DMA used\n"));
}
if (fdc->c_fdtype == 0) {
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: no controller!\n"));
return (DDI_FAILURE);
} else {
return (0);
}
}
static int
fd_attach_register_interrupts(dev_info_t *dip, struct fdctlr *fdc, int *hard)
{
ddi_iblock_cookie_t iblock_cookie_soft;
int status;
if (ddi_get_iblock_cookie(dip, 0, &fdc->c_block) != DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: ddi_get_iblock_cookie failed\n"));
return (DDI_FAILURE);
}
mutex_init(&fdc->c_hilock, NULL, MUTEX_DRIVER, fdc->c_block);
if (fdc->c_fdtype & FDCTYPE_DMA) {
if (ddi_add_intr(dip, 0, &fdc->c_block, 0,
fdintr_dma, (caddr_t)0) == DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: standard intr\n"));
mutex_init(&fdc->c_lolock, NULL,
MUTEX_DRIVER, fdc->c_block);
*hard = 1;
} else {
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: can't add dma intr\n"));
mutex_destroy(&fdc->c_hilock);
return (DDI_FAILURE);
}
} else {
if (ddi_add_intr(dip, 0, &fdc->c_block, 0,
fd_intr, (caddr_t)0) == DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: standard intr\n"));
*hard = 1;
fdc->c_fasttrap = 0;
} else {
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: can't add intr\n"));
mutex_destroy(&fdc->c_hilock);
return (DDI_FAILURE);
}
status = ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
&iblock_cookie_soft);
if (status != DDI_SUCCESS) {
mutex_destroy(&fdc->c_hilock);
return (DDI_FAILURE);
}
mutex_init(&fdc->c_lolock, NULL, MUTEX_DRIVER,
iblock_cookie_soft);
if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &fdc->c_softid,
NULL, NULL,
fd_lointr,
(caddr_t)fdc) != DDI_SUCCESS) {
mutex_destroy(&fdc->c_hilock);
mutex_destroy(&fdc->c_lolock);
return (DDI_FAILURE);
}
}
fdc->c_intrstat = kstat_create("fd", 0, "fdc0", "controller",
KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
if (fdc->c_intrstat) {
fdc->c_hiintct = &KIOIP->intrs[KSTAT_INTR_HARD];
kstat_install(fdc->c_intrstat);
}
cv_init(&fdc->c_iocv, NULL, CV_DRIVER, NULL);
cv_init(&fdc->c_csbcv, NULL, CV_DRIVER, NULL);
cv_init(&fdc->c_motoncv, NULL, CV_DRIVER, NULL);
sema_init(&fdc->c_ocsem, 1, NULL, SEMA_DRIVER, NULL);
cv_init(&fdc->c_suspend_cv, NULL, CV_DRIVER, NULL);
return (0);
}
static int
fd_attach_check_drive(struct fdctlr *fdc)
{
int tmp_fderrlevel;
int unit = fdc->c_un->un_unit_no;
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fd_attach_check_drive\n"));
mutex_enter(&fdc->c_lolock);
switch (fdc->c_fdtype & FDCTYPE_CTRLMASK) {
case FDCTYPE_82077:
Set_dor(fdc, ~((MOTEN(unit))|DRVSEL|RESET), 0);
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: Dor 0x%x\n", Dor(fdc)));
drv_usecwait(5);
if (unit == 0) {
Set_dor(fdc, RESET|DRVSEL, 1);
} else {
Set_dor(fdc, DRVSEL, 0);
Set_dor(fdc, RESET, 1);
}
drv_usecwait(5);
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: Dor 0x%x\n", Dor(fdc)));
if (!((fdc->c_fdtype & FDCTYPE_CHEERIO) ||
(fdc->c_fdtype & FDCTYPE_SB))) {
set_auxioreg(AUX_TC4M, 0);
}
break;
default:
break;
}
fdgetcsb(fdc);
if (fdreset(fdc) != 0) {
mutex_exit(&fdc->c_lolock);
return (DDI_FAILURE);
}
tmp_fderrlevel = fderrlevel;
fderrlevel = FDEP_LMAX;
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: call fdrecalseek\n"));
if (fdrecalseek(fdc, unit, -1, 0) != 0) {
timeout_id_t timeid = fdc->c_mtimeid;
fderrlevel = tmp_fderrlevel;
fdc->c_mtimeid = 0;
mutex_exit(&fdc->c_lolock);
if (timeid) {
(void) untimeout(timeid);
}
FDERRPRINT(FDEP_L2, FDEM_ATTA,
(C, "fd_attach: no drive?\n"));
return (DDI_FAILURE);
}
fderrlevel = tmp_fderrlevel;
fdselect(fdc, unit, 0);
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
return (0);
}
static void
fd_cleanup(dev_info_t *dip, struct fdctlr *fdc, int hard, int locks)
{
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fd_cleanup instance: %d ctlr: 0x%p\n",
ddi_get_instance(dip), (void *)fdc));
if (fdc == NULL) {
return;
}
if (hard) {
ddi_remove_intr(dip, (uint_t)0, fdc->c_block);
}
if (fdc->c_softid != NULL)
ddi_remove_softintr(fdc->c_softid);
if (fdc->c_fdtype & FDCTYPE_82077) {
if (fdc->c_mtimeid)
(void) untimeout(fdc->c_mtimeid);
if (fdc->c_un != (struct fdunit *)NULL)
fdmotoff(fdc);
}
if (fdc->c_timeid)
(void) untimeout(fdc->c_timeid);
if (fdc->c_handlep_cont)
ddi_regs_map_free(&fdc->c_handlep_cont);
if (fdc->c_handlep_aux)
ddi_regs_map_free(&fdc->c_handlep_aux);
if (fdc->c_handlep_dma)
ddi_regs_map_free(&fdc->c_handlep_dma);
if (fdc->c_dma_buf_handle != NULL)
ddi_dma_mem_free(&fdc->c_dma_buf_handle);
if (fdc->c_dmahandle != NULL)
ddi_dma_free_handle(&fdc->c_dmahandle);
ddi_remove_minor_node(dip, NULL);
if (fdc->c_un != (struct fdunit *)NULL) {
ASSERT(!mutex_owned(&fdc->c_lolock));
if (fdc->c_un->un_iostat)
kstat_delete(fdc->c_un->un_iostat);
fdc->c_un->un_iostat = NULL;
if (fdc->c_un->un_chars)
kmem_free(fdc->c_un->un_chars, sizeof (struct fd_char));
if (fdc->c_un->un_drive)
kmem_free(fdc->c_un->un_drive,
sizeof (struct fd_drive));
kmem_free((caddr_t)fdc->c_un, sizeof (struct fdunit));
}
if (fdc->c_intrstat) {
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fd_cleanup: delete intrstat\n"));
kstat_delete(fdc->c_intrstat);
}
fdc->c_intrstat = NULL;
if (locks) {
cv_destroy(&fdc->c_iocv);
cv_destroy(&fdc->c_csbcv);
cv_destroy(&fdc->c_motoncv);
cv_destroy(&fdc->c_suspend_cv);
sema_destroy(&fdc->c_ocsem);
mutex_destroy(&fdc->c_hilock);
mutex_destroy(&fdc->c_lolock);
}
fdctlrs = fdc->c_next;
kmem_free(fdc, sizeof (*fdc));
}
static int
fd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
struct fdctlr *fdc = fd_getctlr(instance << FDINSTSHIFT);
timeout_id_t c_mtimeid;
FDERRPRINT(FDEP_L1, FDEM_ATTA, (C, "fd_detach\n"));
switch (cmd) {
case DDI_DETACH:
fd_cleanup(dip, fdc, 1, 1);
ddi_prop_remove_all(dip);
return (DDI_SUCCESS);
case DDI_SUSPEND:
if (!fdc)
return (DDI_FAILURE);
mutex_enter(&fdc->c_lolock);
fdgetcsb(fdc);
c_mtimeid = fdc->c_mtimeid;
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
(void) untimeout(c_mtimeid);
if (fdc->c_fdtype & FDCTYPE_SB)
ddi_remove_intr(dip, 0, fdc->c_block);
fdc->c_un->un_state = FD_STATE_SUSPENDED;
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
fd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
register struct fdctlr *fdc;
register int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if ((fdc = fd_getctlr((dev_t)arg)) == NULL) {
error = DDI_FAILURE;
} else {
*result = fdc->c_dip;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = 0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
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)
{
struct fdunit *un;
struct fdctlr *fdc;
uint64_t nblocks64;
if (dev == DDI_DEV_T_ANY) {
pass: return (ddi_prop_op(dev, dip, prop_op, mod_flags,
name, valuep, lengthp));
} else {
fdc = fd_getctlr(dev);
if (fdc == NULL)
goto pass;
un = fdc->c_un;
if ((un == NULL) || !fd_unit_is_open(fdc->c_un))
goto pass;
nblocks64 = (ulong_t)
un->un_label.dkl_map[FDPARTITION(dev)].dkl_nblk;
return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
name, valuep, lengthp, nblocks64));
}
}
static int
fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
{
dev_t dev;
int part;
struct fdctlr *fdc;
struct fdunit *un;
struct dk_map32 *dkm;
uchar_t pbit;
int err, part_is_open;
int unit;
dev = *devp;
fdc = fd_getctlr(dev);
if ((fdc == NULL) || ((un = fdc->c_un) == NULL)) {
return (ENXIO);
}
unit = fdc->c_un->un_unit_no;
sema_p(&fdc->c_ocsem);
part = FDPARTITION(dev);
pbit = 1 << part;
dkm = &un->un_label.dkl_map[part];
if (dkm->dkl_nblk == 0) {
sema_v(&fdc->c_ocsem);
return (ENXIO);
}
FDERRPRINT(FDEP_L1, FDEM_OPEN,
(C, "fdopen: ctlr %d unit %d part %d\n",
ddi_get_instance(fdc->c_dip), unit, part));
FDERRPRINT(FDEP_L1, FDEM_OPEN,
(C, "fdopen: flag 0x%x", flag));
(void) pm_busy_component(fdc->c_dip, 0);
mutex_enter(&fdc->c_lolock);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
!= DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
failed. \n"));
sema_v(&fdc->c_ocsem);
(void) pm_idle_component(fdc->c_dip, 0);
return (EIO);
}
mutex_enter(&fdc->c_lolock);
}
if (fd_unit_is_open(un) == 0) {
fdgetcsb(fdc);
err = fdrecalseek(fdc, unit, -1, 0);
fdretcsb(fdc);
if (err) {
FDERRPRINT(FDEP_L3, FDEM_OPEN,
(C, "fd%d: drive not ready\n", 0));
fdselect(fdc, unit, 0);
mutex_exit(&fdc->c_lolock);
sema_v(&fdc->c_ocsem);
(void) pm_idle_component(fdc->c_dip, 0);
return (EIO);
}
}
if (otyp == OTYP_LYR) {
part_is_open = (un->un_lyropen[part] != 0);
} else {
part_is_open = fd_part_is_open(un, part);
}
if ((un->un_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) {
mutex_exit(&fdc->c_lolock);
sema_v(&fdc->c_ocsem);
FDERRPRINT(FDEP_L2, FDEM_OPEN, (C, "fd:just return\n"));
(void) pm_idle_component(fdc->c_dip, 0);
return (EBUSY);
}
if (flag & (FNDELAY | FNONBLOCK)) {
FDERRPRINT(FDEP_L2, FDEM_OPEN,
(C, "fd: return busy..\n"));
goto out;
}
fdc->c_csb.csb_unit = (uchar_t)unit;
if (fdgetlabel(fdc, unit)) {
FDERRPRINT(FDEP_L3, FDEM_OPEN,
(C,
"fd%d: unformatted diskette or no diskette in the drive\n",
0));
if (fd_unit_is_open(un) == 0) {
fdselect(fdc, unit, 0);
}
mutex_exit(&fdc->c_lolock);
sema_v(&fdc->c_ocsem);
(void) pm_idle_component(fdc->c_dip, 0);
return (EIO);
}
if (flag & FWRITE) {
fdgetcsb(fdc);
err = fdsensedrv(fdc, unit) & WP_SR3;
fdretcsb(fdc);
if (err) {
if (fd_unit_is_open(un) == 0)
fdselect(fdc, unit, 0);
mutex_exit(&fdc->c_lolock);
sema_v(&fdc->c_ocsem);
(void) pm_idle_component(fdc->c_dip, 0);
return (EROFS);
}
}
out:
if (flag & FEXCL) {
un->un_exclmask |= pbit;
}
if (otyp == OTYP_LYR) {
un->un_lyropen[part]++;
} else {
un->un_regopen[otyp] |= pbit;
}
mutex_exit(&fdc->c_lolock);
sema_v(&fdc->c_ocsem);
(void) pm_idle_component(fdc->c_dip, 0);
return (0);
}
static int
fd_part_is_open(struct fdunit *un, int part)
{
int i;
for (i = 0; i < OTYPCNT - 1; i++)
if (un->un_regopen[i] & (1 << part))
return (1);
return (0);
}
static int
fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
int unit, part_is_closed, part;
register struct fdctlr *fdc;
register struct fdunit *un;
fdc = fd_getctlr(dev);
if (!fdc || !(un = fdc->c_un))
return (ENXIO);
unit = fdc->c_un->un_unit_no;
FDERRPRINT(FDEP_L1, FDEM_CLOS, (C, "fd_close\n"));
part = FDPARTITION(dev);
sema_p(&fdc->c_ocsem);
mutex_enter(&fdc->c_lolock);
if (otyp == OTYP_LYR) {
un->un_lyropen[part]--;
part_is_closed = (un->un_lyropen[part] == 0);
} else {
un->un_regopen[otyp] &= ~(1<<part);
part_is_closed = 1;
}
if (part_is_closed)
un->un_exclmask &= ~(1<<part);
if (fd_unit_is_open(un) == 0) {
fdselect(fdc, unit, 0);
un->un_flags &= ~FDUNIT_CHANGED;
}
mutex_exit(&fdc->c_lolock);
sema_v(&fdc->c_ocsem);
return (0);
}
static int
fd_strategy(register struct buf *bp)
{
struct fdctlr *fdc;
struct fdunit *un;
uint_t phys_blkno;
struct dk_map32 *dkm;
FDERRPRINT(FDEP_L1, FDEM_STRA,
(C, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
(void *)bp, bp->b_edev));
FDERRPRINT(FDEP_L1, FDEM_STRA,
(C, "b_blkno=%x b_flags=%x b_count=%x\n",
(int)bp->b_blkno, bp->b_flags, (int)bp->b_bcount));
fdc = fd_getctlr(bp->b_edev);
un = fdc->c_un;
dkm = &un->un_label.dkl_map[FDPARTITION(bp->b_edev)];
if (un->un_chars->fdc_medium) {
phys_blkno = (uint_t)bp->b_blkno >> 1;
if (bp->b_blkno & 1) {
FDERRPRINT(FDEP_L3, FDEM_STRA,
(C, "b_blkno=0x%lx is not 1k aligned\n",
(long)bp->b_blkno));
bp->b_error = EINVAL;
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_ERROR;
biodone(bp);
return (0);
}
} else {
phys_blkno = (uint_t)bp->b_blkno;
}
if ((phys_blkno > dkm->dkl_nblk)) {
FDERRPRINT(FDEP_L3, FDEM_STRA,
(C, "fd%d: block %ld is past the end! (nblk=%d)\n",
0, (long)bp->b_blkno, dkm->dkl_nblk));
bp->b_error = ENOSPC;
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_ERROR;
biodone(bp);
return (0);
}
if (phys_blkno == dkm->dkl_nblk) {
FDERRPRINT(FDEP_L1, FDEM_STRA,
(C, "b_blkno is at the end!\n"));
if ((bp->b_flags & B_READ) == 0) {
bp->b_error = ENOSPC;
bp->b_flags |= B_ERROR;
FDERRPRINT(FDEP_L1, FDEM_STRA,
(C, "block is at end and this is a write\n"));
}
bp->b_resid = bp->b_bcount;
biodone(bp);
return (0);
}
if (bp->b_bcount % un->un_chars->fdc_sec_size) {
FDERRPRINT(FDEP_L3, FDEM_STRA,
(C, "fd%d: requested transfer size(0x%lx) is not"
" multiple of sector size(0x%x)\n", 0,
bp->b_bcount, un->un_chars->fdc_sec_size));
FDERRPRINT(FDEP_L3, FDEM_STRA,
(C, " b_blkno=0x%lx b_flags=0x%x\n",
(long)bp->b_blkno, bp->b_flags));
bp->b_error = EINVAL;
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_ERROR;
biodone(bp);
return (0);
}
bp->av_forw = 0;
sema_p(&fdc->c_ocsem);
(void) pm_busy_component(fdc->c_dip, 0);
mutex_enter(&fdc->c_lolock);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
!= DDI_SUCCESS) {
sema_v(&fdc->c_ocsem);
(void) pm_idle_component(fdc->c_dip, 0);
bp->b_error = EIO;
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_ERROR;
biodone(bp);
return (0);
} else {
mutex_enter(&fdc->c_lolock);
}
}
if (un->un_iostat) {
kstat_waitq_enter(KIOSP);
}
if (fdc->c_actf)
fdc->c_actl->av_forw = bp;
else
fdc->c_actf = bp;
fdc->c_actl = bp;
fdstart(fdc);
mutex_exit(&fdc->c_lolock);
sema_v(&fdc->c_ocsem);
(void) pm_idle_component(fdc->c_dip, 0);
return (0);
}
static int
fd_read(dev_t dev, struct uio *uio, cred_t *cred_p)
{
FDERRPRINT(FDEP_L1, FDEM_RDWR, (C, "fd_read\n"));
return (physio(fd_strategy, NULL, dev, B_READ, minphys, uio));
}
static int
fd_write(dev_t dev, struct uio *uio, cred_t *cred_p)
{
FDERRPRINT(FDEP_L1, FDEM_RDWR, (C, "fd_write\n"));
return (physio(fd_strategy, NULL, dev, B_WRITE, minphys, uio));
}
static void
fdmotoff(void *arg)
{
struct fdctlr *fdc = arg;
int unit = fdc->c_un->un_unit_no;
mutex_enter(&fdc->c_lolock);
if (fdc->c_mtimeid == 0) {
mutex_exit(&fdc->c_lolock);
return;
}
FDERRPRINT(FDEP_L1, FDEM_MOFF, (C, "fdmotoff\n"));
fdc->c_mtimeid = 0;
if (!(Msr(fdc) & CB) && (Dor(fdc) & (MOTEN(unit)))) {
Set_dor(fdc, MOTEN(unit), 0);
}
mutex_exit(&fdc->c_lolock);
}
static int
fd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag,
cred_t *cred_p, int *rval_p)
{
union {
struct dk_cinfo dki;
struct dk_geom dkg;
struct dk_allmap32 dka;
struct fd_char fdchar;
struct fd_drive drvchar;
int temp;
} cpy;
struct vtoc vtoc;
struct fdunit *un;
struct fdctlr *fdc;
int unit, dkunit;
int err = 0;
uint_t sec_size;
enum dkio_state state;
int transfer_rate;
FDERRPRINT(FDEP_L1, FDEM_IOCT,
(C, "fd_ioctl: cmd 0x%x, arg 0x%lx\n", cmd, (long)arg));
if (FDUNIT(dev) != 0)
return (ENXIO);
fdc = fd_getctlr(dev);
unit = fdc->c_un->un_unit_no;
un = fdc->c_un;
sec_size = un->un_chars->fdc_sec_size;
bzero(&cpy, sizeof (cpy));
switch (cmd) {
case DKIOCINFO:
cpy.dki.dki_addr = 0;
cpy.dki.dki_cnum = FDCTLR(dev);
cpy.dki.dki_unit = FDUNIT(dev);
cpy.dki.dki_slave = 0;
cpy.dki.dki_ctype = (ushort_t)-1;
if (fdc->c_fdtype & FDCTYPE_82077)
cpy.dki.dki_ctype = DKC_INTEL82077;
cpy.dki.dki_flags = DKI_FMTTRK;
cpy.dki.dki_partition = FDPARTITION(dev);
cpy.dki.dki_maxtransfer = maxphys / DEV_BSIZE;
if (ddi_copyout((caddr_t)&cpy.dki, (caddr_t)arg,
sizeof (cpy.dki), flag))
err = EFAULT;
break;
case DKIOCGGEOM:
cpy.dkg.dkg_ncyl = un->un_chars->fdc_ncyl;
cpy.dkg.dkg_nhead = un->un_chars->fdc_nhead;
cpy.dkg.dkg_nsect = un->un_chars->fdc_secptrack;
cpy.dkg.dkg_intrlv = un->un_label.dkl_intrlv;
cpy.dkg.dkg_rpm = un->un_label.dkl_rpm;
cpy.dkg.dkg_pcyl = un->un_chars->fdc_ncyl;
cpy.dkg.dkg_read_reinstruct =
(int)(cpy.dkg.dkg_nsect * cpy.dkg.dkg_rpm * 4) / 60000;
cpy.dkg.dkg_write_reinstruct = cpy.dkg.dkg_read_reinstruct;
if (ddi_copyout((caddr_t)&cpy.dkg, (caddr_t)arg,
sizeof (cpy.dkg), flag))
err = EFAULT;
break;
case DKIOCSGEOM:
FDERRPRINT(FDEP_L3, FDEM_IOCT,
(C, "fd_ioctl: DKIOCSGEOM not supported\n"));
err = ENOTTY;
break;
case DKIOCGAPART:
if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) {
if (ddi_copyout(&un->un_label.dkl_map,
(void *)arg, sizeof (struct dk_allmap32), flag))
err = EFAULT;
}
#ifdef _MULTI_DATAMODEL
else {
struct dk_allmap dk_allmap;
ASSERT((flag & DATAMODEL_MASK) == DATAMODEL_LP64);
for (dkunit = 0; dkunit < NDKMAP; dkunit++) {
dk_allmap.dka_map[dkunit].dkl_cylno =
un->un_label.dkl_map[dkunit].dkl_cylno;
dk_allmap.dka_map[dkunit].dkl_nblk =
un->un_label.dkl_map[dkunit].dkl_nblk;
}
if (ddi_copyout(&dk_allmap, (void *)arg,
sizeof (struct dk_allmap), flag))
err = EFAULT;
}
#endif
break;
case DKIOCSAPART:
if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) {
if (ddi_copyin((const void *)arg, &cpy.dka,
sizeof (cpy.dka), flag))
return (EFAULT);
else {
mutex_enter(&fdc->c_lolock);
for (dkunit = 0; dkunit < NDKMAP; dkunit++) {
un->un_label.dkl_map[dkunit] =
cpy.dka.dka_map[dkunit];
}
mutex_exit(&fdc->c_lolock);
}
}
#ifdef _MULTI_DATAMODEL
else {
struct dk_allmap dk_allmap;
ASSERT((flag & DATAMODEL_MASK) == DATAMODEL_LP64);
if (ddi_copyin((const void *)arg, &dk_allmap,
sizeof (dk_allmap), flag))
return (EFAULT);
else {
mutex_enter(&fdc->c_lolock);
for (dkunit = 0; dkunit < NDKMAP; dkunit++) {
un->un_label.dkl_map[dkunit].dkl_cylno =
dk_allmap.dka_map[dkunit].dkl_cylno;
un->un_label.dkl_map[dkunit].dkl_nblk =
dk_allmap.dka_map[dkunit].dkl_nblk;
}
mutex_exit(&fdc->c_lolock);
}
}
#endif
break;
case DKIOCGVTOC:
mutex_enter(&fdc->c_lolock);
if (fdgetlabel(fdc, unit)) {
mutex_exit(&fdc->c_lolock);
err = EINVAL;
break;
}
fd_build_user_vtoc(un, &vtoc);
mutex_exit(&fdc->c_lolock);
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32: {
struct vtoc32 vtoc32;
vtoctovtoc32(vtoc, vtoc32);
if (ddi_copyout(&vtoc32, (void *)arg,
sizeof (struct vtoc32), flag))
return (EFAULT);
break;
}
case DDI_MODEL_NONE:
if (ddi_copyout(&vtoc, (void *)arg,
sizeof (vtoc), flag))
return (EFAULT);
break;
}
#else
if (ddi_copyout(&vtoc, (void *)arg, sizeof (vtoc), flag))
return (EFAULT);
#endif
break;
case DKIOCSVTOC:
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32: {
struct vtoc32 vtoc32;
if (ddi_copyin((const void *)arg, &vtoc32,
sizeof (struct vtoc32), flag)) {
return (EFAULT);
}
vtoc32tovtoc(vtoc32, vtoc);
break;
}
case DDI_MODEL_NONE:
if (ddi_copyin((const void *)arg, &vtoc,
sizeof (vtoc), flag)) {
return (EFAULT);
}
break;
}
#else
if (ddi_copyin((const void *)arg, &vtoc, sizeof (vtoc), flag))
return (EFAULT);
#endif
mutex_enter(&fdc->c_lolock);
if ((un->un_chars->fdc_ncyl == 0) ||
(un->un_chars->fdc_nhead == 0) ||
(un->un_chars->fdc_secptrack == 0)) {
mutex_exit(&fdc->c_lolock);
err = EINVAL;
break;
}
if ((err = fd_build_label_vtoc(un, &vtoc)) != 0) {
mutex_exit(&fdc->c_lolock);
break;
}
(void) pm_busy_component(fdc->c_dip, 0);
err = fdrw(fdc, unit, FDWRITE, 0, 0, 1,
(caddr_t)&un->un_label, sizeof (struct dk_label));
mutex_exit(&fdc->c_lolock);
(void) pm_idle_component(fdc->c_dip, 0);
break;
case DKIOCSTATE:
if (ddi_copyin((caddr_t)arg, (caddr_t)&state,
sizeof (int), flag)) {
err = EFAULT;
break;
}
(void) pm_busy_component(fdc->c_dip, 0);
err = fd_check_media(dev, state);
(void) pm_idle_component(fdc->c_dip, 0);
if (ddi_copyout((caddr_t)&un->un_media_state,
(caddr_t)arg, sizeof (int), flag))
err = EFAULT;
break;
case FDIOGCHAR:
if (ddi_copyout((caddr_t)un->un_chars, (caddr_t)arg,
sizeof (struct fd_char), flag))
err = EFAULT;
break;
case FDIOSCHAR:
if (ddi_copyin((caddr_t)arg, (caddr_t)&cpy.fdchar,
sizeof (struct fd_char), flag)) {
err = EFAULT;
break;
}
transfer_rate = cpy.fdchar.fdc_transfer_rate;
if ((transfer_rate != 500) && (transfer_rate != 300) &&
(transfer_rate != 250) && (transfer_rate != 1000)) {
FDERRPRINT(FDEP_L3, FDEM_IOCT,
(C, "fd_ioctl: FDIOSCHAR odd transfer rate %d\n",
cpy.fdchar.fdc_transfer_rate));
err = EINVAL;
break;
}
if ((cpy.fdchar.fdc_nhead < 1) ||
(cpy.fdchar.fdc_nhead > 2)) {
FDERRPRINT(FDEP_L3, FDEM_IOCT,
(C, "fd_ioctl: FDIOSCHAR bad no. of heads %d\n",
cpy.fdchar.fdc_nhead));
err = EINVAL;
break;
}
if ((cpy.fdchar.fdc_ncyl < 0) || (cpy.fdchar.fdc_ncyl > 255)) {
FDERRPRINT(FDEP_L3, FDEM_IOCT,
(C, "fd_ioctl: FDIOSCHAR bad cyl no %d\n",
cpy.fdchar.fdc_ncyl));
err = EINVAL;
break;
}
mutex_enter(&fdc->c_lolock);
*(un->un_chars) = cpy.fdchar;
un->un_curfdtype = -1;
mutex_exit(&fdc->c_lolock);
break;
case FDEJECT:
case DKIOCEJECT:
if (fdc->c_un->un_drive->fdd_ejectable == 0) {
err = ENOSYS;
} else {
(void) pm_busy_component(fdc->c_dip, 0);
mutex_enter(&fdc->c_lolock);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0,
PM_LEVEL_ON)) != DDI_SUCCESS) {
(void) pm_idle_component(fdc->c_dip, 0);
err = EIO;
}
mutex_enter(&fdc->c_lolock);
}
}
if (err == 0) {
fdselect(fdc, unit, 1);
fdeject(fdc, unit);
mutex_exit(&fdc->c_lolock);
}
(void) pm_idle_component(fdc->c_dip, 0);
if (fdc->c_fdtype & FDCTYPE_82077) {
if (fdc->c_mtimeid == 0) {
fdc->c_mtimeid = timeout(fdmotoff, fdc,
Motoff_delay);
}
}
break;
case FDGETCHANGE:
if (ddi_copyin((caddr_t)arg, (caddr_t)&cpy.temp,
sizeof (int), flag)) {
err = EFAULT;
break;
}
cpy.temp = 0;
(void) pm_busy_component(fdc->c_dip, 0);
mutex_enter(&fdc->c_lolock);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
!= DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power \
change failed. \n"));
(void) pm_idle_component(fdc->c_dip, 0);
return (EIO);
}
mutex_enter(&fdc->c_lolock);
}
if (un->un_flags & FDUNIT_CHANGED)
cpy.temp |= FDGC_HISTORY;
else
cpy.temp &= ~FDGC_HISTORY;
un->un_flags &= ~FDUNIT_CHANGED;
if (fd_pollable) {
if (fdsense_chng(fdc, unit)) {
cpy.temp |= FDGC_CURRENT;
} else {
cpy.temp &= ~FDGC_CURRENT;
}
} else {
if (fdsense_chng(fdc, unit)) {
if (fdcheckdisk(fdc, unit)) {
cpy.temp |= FDGC_CURRENT;
} else {
cpy.temp &= ~FDGC_CURRENT;
cpy.temp |= FDGC_HISTORY;
}
} else {
cpy.temp &= ~FDGC_CURRENT;
}
}
if (un->un_ejected && !(cpy.temp & FDGC_CURRENT)) {
cpy.temp |= FDGC_HISTORY;
}
un->un_ejected = 0;
fdgetcsb(fdc);
if (fdsensedrv(fdc, unit) & WP_SR3) {
cpy.temp |= FDGC_CURWPROT;
}
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
if (ddi_copyout((caddr_t)&cpy.temp, (caddr_t)arg,
sizeof (int), flag))
err = EFAULT;
(void) pm_idle_component(fdc->c_dip, 0);
break;
case FDGETDRIVECHAR:
if (ddi_copyin((caddr_t)arg, (caddr_t)&cpy.drvchar,
sizeof (struct fd_drive), flag)) {
err = EFAULT;
break;
}
cpy.drvchar.fdd_ejectable = fdc->c_un->un_drive->fdd_ejectable;
cpy.drvchar.fdd_maxsearch = nfdtypes;
if (fd_pollable)
cpy.drvchar.fdd_flags |= FDD_POLLABLE;
if (ddi_copyout((caddr_t)&cpy.drvchar, (caddr_t)arg,
sizeof (struct fd_drive), flag))
err = EFAULT;
break;
case FDSETDRIVECHAR:
FDERRPRINT(FDEP_L3, FDEM_IOCT,
(C, "fd_ioctl: FDSETDRIVECHAR not supportedn\n"));
err = ENOTTY;
break;
case DKIOCREMOVABLE: {
int i = 1;
if (ddi_copyout((caddr_t)&i, (caddr_t)arg, sizeof (int),
flag)) {
err = EFAULT;
}
break;
}
case DKIOCGMEDIAINFO:
err = fd_get_media_info(un, (caddr_t)arg, flag);
break;
case FDIOCMD:
{
struct fd_cmd fc;
int cyl, hd, spc, spt;
int nblks;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32: {
struct fd_cmd32 fc32;
if (ddi_copyin((const void *)arg, &fc32,
sizeof (fc32), flag)) {
return (EFAULT);
}
fc.fdc_cmd = fc32.fdc_cmd;
fc.fdc_flags = fc32.fdc_flags;
fc.fdc_blkno = (daddr_t)fc32.fdc_blkno;
fc.fdc_secnt = fc32.fdc_secnt;
fc.fdc_bufaddr = (caddr_t)(uintptr_t)fc32.fdc_bufaddr;
fc.fdc_buflen = fc32.fdc_buflen;
fc.fdc_cmd = fc32.fdc_cmd;
break;
}
case DDI_MODEL_NONE:
if (ddi_copyin((const void *)arg, &fc,
sizeof (fc), flag)) {
return (EFAULT);
}
break;
}
#else
if (ddi_copyin((const void *)arg, &fc, sizeof (fc), flag)) {
return (EFAULT);
}
#endif
if (fc.fdc_cmd == FDCMD_READ || fc.fdc_cmd == FDCMD_WRITE) {
auto struct iovec aiov;
auto struct uio auio;
struct uio *uio = &auio;
spc = (fc.fdc_cmd == FDCMD_READ)? B_READ: B_WRITE;
bzero(&auio, sizeof (struct uio));
bzero(&aiov, sizeof (struct iovec));
aiov.iov_base = fc.fdc_bufaddr;
aiov.iov_len = (uint_t)fc.fdc_secnt * sec_size;
uio->uio_iov = &aiov;
uio->uio_iovcnt = 1;
uio->uio_resid = aiov.iov_len;
uio->uio_segflg = UIO_USERSPACE;
FDERRPRINT(FDEP_L2, FDEM_IOCT,
(C, "fd_ioctl: call physio\n"));
err = physio(fd_strategy, NULL, dev,
spc, minphys, uio);
break;
} else if (fc.fdc_cmd != FDCMD_FORMAT_TRACK) {
FDERRPRINT(FDEP_L1, FDEM_IOCT,
(C, "fd_ioctl: FDIOCMD invalid command\n"));
err = EINVAL;
break;
}
spt = un->un_chars->fdc_secptrack;
spc = un->un_chars->fdc_nhead * spt;
cyl = fc.fdc_blkno / spc;
hd = (fc.fdc_blkno % spc) / spt;
nblks = spc * un->un_chars->fdc_ncyl;
if (fc.fdc_blkno < 0 || fc.fdc_blkno > (nblks - 1)) {
err = EINVAL;
break;
}
(void) pm_busy_component(fdc->c_dip, 0);
mutex_enter(&fdc->c_lolock);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
!= DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power \
change failed. \n"));
(void) pm_idle_component(fdc->c_dip, 0);
return (EIO);
}
mutex_enter(&fdc->c_lolock);
}
if (fdformat(fdc, unit, cyl, hd))
err = EIO;
mutex_exit(&fdc->c_lolock);
(void) pm_idle_component(fdc->c_dip, 0);
break;
}
case FDRAW:
(void) pm_busy_component(fdc->c_dip, 0);
err = fdrawioctl(fdc, unit, arg, flag);
(void) pm_idle_component(fdc->c_dip, 0);
break;
#ifdef FD_DEBUG
case IOCTL_DEBUG:
fderrlevel--;
if (fderrlevel < 0)
fderrlevel = 3;
cmn_err(C, "fdioctl: CHANGING debug to %d", fderrlevel);
return (0);
#endif
default:
FDERRPRINT(FDEP_L2, FDEM_IOCT,
(C, "fd_ioctl: invalid ioctl 0x%x\n", cmd));
err = ENOTTY;
break;
}
return (err);
}
static int
fdrawioctl(struct fdctlr *fdc, int unit, intptr_t arg, int mode)
{
struct fd_raw fdr;
#ifdef _MULTI_DATAMODEL
struct fd_raw32 fdr32;
#endif
struct fdcsb *csb;
int i, err, flag;
caddr_t fa;
uint_t fc;
size_t real_length;
int res;
ddi_device_acc_attr_t attr;
ddi_acc_handle_t mem_handle;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
ASSERT(fdc->c_un->un_unit_no == unit);
flag = B_READ;
err = 0;
fa = NULL;
fc = (uint_t)0;
switch (ddi_model_convert_from(mode)) {
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
if (ddi_copyin((caddr_t)arg, (caddr_t)&fdr32,
sizeof (fdr32), mode)) {
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: copyin error, args32\n"));
return (EFAULT);
}
bcopy(fdr32.fdr_cmd, fdr.fdr_cmd, sizeof (fdr.fdr_cmd));
fdr.fdr_cnum = fdr32.fdr_cnum;
bcopy(fdr32.fdr_result, fdr.fdr_result,
sizeof (fdr.fdr_result));
fdr.fdr_nbytes = fdr32.fdr_nbytes;
fdr.fdr_addr = (caddr_t)(uintptr_t)fdr32.fdr_addr;
break;
#endif
default:
case DDI_MODEL_NONE:
if (ddi_copyin((caddr_t)arg, (caddr_t)&fdr,
sizeof (fdr), mode)) {
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: copyin error, args\n"));
return (EFAULT);
}
break;
}
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: cmd[0]=0x%x\n", fdr.fdr_cmd[0]));
mutex_enter(&fdc->c_lolock);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
!= DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
failed. \n"));
(void) pm_idle_component(fdc->c_dip, 0);
return (EIO);
}
mutex_enter(&fdc->c_lolock);
}
fdgetcsb(fdc);
csb = &fdc->c_csb;
csb->csb_unit = (uchar_t)unit;
for (i = 0; i <= fdr.fdr_cnum; i++)
csb->csb_cmds[i] = fdr.fdr_cmd[i];
csb->csb_ncmds = (uchar_t)fdr.fdr_cnum;
csb->csb_maxretry = 0;
csb->csb_retrys = 0;
switch (fdr.fdr_cmd[0] & 0x0f) {
case FDRAW_SPECIFY:
if (fdc->c_fdtype & FDCTYPE_DMA)
csb->csb_cmds[2] = csb->csb_cmds[2] & 0xFE;
else
csb->csb_cmds[2] = csb->csb_cmds[2] | 0x1;
csb->csb_opflags = CSB_OFNORESULTS;
csb->csb_nrslts = 0;
break;
case FDRAW_SENSE_DRV:
csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
csb->csb_opflags = CSB_OFIMMEDIATE;
csb->csb_nrslts = 1;
break;
case FDRAW_REZERO:
case FDRAW_SEEK:
csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
csb->csb_opflags = CSB_OFSEEKOPS + CSB_OFTIMEIT;
csb->csb_nrslts = 2;
break;
case FDRAW_FORMAT:
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: cmd is fdfraw format\n"));
csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
csb->csb_opflags = CSB_OFXFEROPS + CSB_OFTIMEIT;
csb->csb_nrslts = NRBRW;
flag = B_WRITE;
if (fdc->c_fdtype & FDCTYPE_DMA) {
fc = (uint_t)(fdr.fdr_nbytes);
mutex_enter(&fdc->c_hilock);
res = ddi_dma_mem_alloc(fdc->c_dmahandle, fc,
&attr, DDI_DMA_STREAMING,
DDI_DMA_DONTWAIT, 0, &fa, &real_length,
&mem_handle);
if (res != DDI_SUCCESS) {
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
mutex_exit(&fdc->c_hilock);
return (EIO);
}
fdc->c_csb.csb_read = CSB_WRITE;
if (fdstart_dma(fdc, fa, fc) != 0) {
ddi_dma_mem_free(&mem_handle);
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
mutex_exit(&fdc->c_hilock);
return (EIO);
}
mutex_exit(&fdc->c_hilock);
} else {
fc = (uint_t)(fdr.fdr_nbytes + 16);
fa = kmem_zalloc(fc, KM_SLEEP);
}
if (ddi_copyin(fdr.fdr_addr, fa,
(uint_t)fdr.fdr_nbytes, mode)) {
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
if (fdc->c_fdtype & FDCTYPE_DMA) {
ddi_dma_mem_free(&mem_handle);
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: (err)free dma memory\n"));
} else {
kmem_free(fa, fc);
}
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: ddi_copyin error\n"));
return (EFAULT);
}
break;
case FDRAW_WRCMD:
case FDRAW_WRITEDEL:
flag = B_WRITE;
case FDRAW_RDCMD:
case FDRAW_READDEL:
case FDRAW_READTRACK:
csb->csb_cmds[1] = csb->csb_cmds[1] | (unit & DRV_MASK);
if (fdc->c_fdtype & FDCTYPE_SB)
csb->csb_cmds[1] |= IPS;
csb->csb_opflags = CSB_OFXFEROPS + CSB_OFTIMEIT;
csb->csb_nrslts = NRBRW;
break;
default:
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
return (EINVAL);
}
if ((csb->csb_opflags & CSB_OFXFEROPS) && (fdr.fdr_nbytes == 0)) {
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
return (EINVAL);
}
csb->csb_opflags |= CSB_OFRAWIOCTL;
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: nbytes = %u\n", fdr.fdr_nbytes));
if ((fdr.fdr_cmd[0] & 0x0f) != FDRAW_FORMAT) {
if ((fc = (uint_t)fdr.fdr_nbytes) > 0) {
if (fdc->c_fdtype & FDCTYPE_DMA) {
mutex_enter(&fdc->c_hilock);
res = ddi_dma_mem_alloc(fdc->c_dmahandle, fc,
&attr, DDI_DMA_STREAMING,
DDI_DMA_DONTWAIT, 0, &fa, &real_length,
&mem_handle);
if (res != DDI_SUCCESS) {
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
mutex_exit(&fdc->c_hilock);
return (EIO);
}
if (flag == B_WRITE)
fdc->c_csb.csb_read = CSB_WRITE;
else
fdc->c_csb.csb_read = CSB_READ;
if (fdstart_dma(fdc, fa, fc) != 0) {
ddi_dma_mem_free(&mem_handle);
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
mutex_exit(&fdc->c_hilock);
return (EIO);
}
mutex_exit(&fdc->c_hilock);
} else {
fa = kmem_zalloc(fc, KM_SLEEP);
}
if (flag == B_WRITE) {
if (ddi_copyin(fdr.fdr_addr, fa, fc, mode)) {
if (fdc->c_fdtype & FDCTYPE_DMA)
ddi_dma_mem_free(&mem_handle);
else
kmem_free(fa, fc);
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
FDERRPRINT(FDEP_L1, FDEM_RAWI, (C,
"fdrawioctl: can't copy data\n"));
return (EFAULT);
}
}
csb->csb_addr = fa;
csb->csb_len = fc;
} else {
csb->csb_addr = 0;
csb->csb_len = 0;
}
} else {
csb->csb_addr = fa;
csb->csb_len = fc;
}
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "cmd: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_cmds[0],
csb->csb_cmds[1], csb->csb_cmds[2], csb->csb_cmds[3],
csb->csb_cmds[4], csb->csb_cmds[5], csb->csb_cmds[6],
csb->csb_cmds[7], csb->csb_cmds[8], csb->csb_cmds[9]));
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "nbytes: %x, opflags: %x, addr: %p, len: %x\n",
csb->csb_ncmds, csb->csb_opflags, (void *)csb->csb_addr,
csb->csb_len));
if ((csb->csb_opflags & CSB_OFNORESULTS) ||
(csb->csb_opflags & CSB_OFIMMEDIATE)) {
(void) fdexec(fdc, 0);
} else {
(void) fdexec(fdc, FDXC_SLEEP | FDXC_CHECKCHG);
}
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "rslt: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_rslt[0],
csb->csb_rslt[1], csb->csb_rslt[2], csb->csb_rslt[3],
csb->csb_rslt[4], csb->csb_rslt[5], csb->csb_rslt[6],
csb->csb_rslt[7], csb->csb_rslt[8], csb->csb_rslt[9]));
if ((fdr.fdr_cmd[0] & 0x0f) != FDRAW_FORMAT && fc &&
flag == B_READ && err == 0) {
if (ddi_copyout(fa, fdr.fdr_addr, fc, mode)) {
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: can't copy read data\n"));
err = EFAULT;
}
}
if (fc) {
if (fdc->c_fdtype & FDCTYPE_DMA) {
ddi_dma_mem_free(&mem_handle);
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: free dma memory\n"));
} else {
kmem_free(fa, fc);
}
}
for (i = 0; (int)i <= (int)csb->csb_nrslts; i++)
fdr.fdr_result[i] = csb->csb_rslt[i];
fdr.fdr_nbytes = fdc->c_csb.csb_rlen;
switch (ddi_model_convert_from(mode)) {
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
bcopy(fdr.fdr_cmd, fdr32.fdr_cmd, sizeof (fdr32.fdr_cmd));
fdr32.fdr_cnum = fdr.fdr_cnum;
bcopy(fdr.fdr_result, fdr32.fdr_result,
sizeof (fdr32.fdr_result));
fdr32.fdr_nbytes = fdr.fdr_nbytes;
fdr32.fdr_addr = (caddr32_t)(uintptr_t)fdr.fdr_addr;
if (ddi_copyout(&fdr32, (caddr_t)arg, sizeof (fdr32), mode)) {
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: can't copy results32\n"));
err = EFAULT;
}
break;
#endif
case DDI_MODEL_NONE:
default:
if (ddi_copyout(&fdr, (caddr_t)arg, sizeof (fdr), mode)) {
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(C, "fdrawioctl: can't copy results\n"));
err = EFAULT;
}
break;
}
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
return (0);
}
static int
fdformat(struct fdctlr *fdc, int unit, int cyl, int hd)
{
struct fdcsb *csb;
struct fdunit *un;
struct fd_char *ch;
int cmdresult;
uchar_t *fmthdrs;
caddr_t fd;
int i;
size_t real_length;
ddi_device_acc_attr_t attr;
ddi_acc_handle_t mem_handle;
FDERRPRINT(FDEP_L1, FDEM_FORM,
(C, "fdformat cyl %d, hd %d\n", cyl, hd));
fdgetcsb(fdc);
ASSERT(fdc->c_un->un_unit_no == unit);
csb = &fdc->c_csb;
un = fdc->c_un;
ch = un->un_chars;
csb->csb_unit = (uchar_t)unit;
if (fdrecalseek(fdc, unit, cyl, FDXC_CHECKCHG)) {
fdretcsb(fdc);
return (EIO);
}
csb->csb_nrslts = NRBRW;
csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT;
csb->csb_cmds[0] = FDRAW_FORMAT;
csb->csb_cmds[0] |= MFM;
csb->csb_cmds[1] = (hd << 2) | (unit & 0x03);
csb->csb_cmds[2] = ch->fdc_medium ? 3 : 2;
csb->csb_cmds[3] = ch->fdc_secptrack;
csb->csb_cmds[4] = GPLF;
csb->csb_cmds[5] = FDATA;
csb->csb_ncmds = 6;
csb->csb_maxretry = rwretry;
csb->csb_retrys = 0;
if (fdc->c_fdtype & FDCTYPE_DMA) {
csb->csb_len = (uint_t)4 * ch->fdc_secptrack;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
mutex_enter(&fdc->c_hilock);
cmdresult = ddi_dma_mem_alloc(fdc->c_dmahandle, csb->csb_len,
&attr, DDI_DMA_STREAMING,
DDI_DMA_DONTWAIT, 0, &fd, &real_length,
&mem_handle);
if (cmdresult != DDI_SUCCESS) {
mutex_exit(&fdc->c_hilock);
return (cmdresult);
}
fdc->c_csb.csb_read = CSB_WRITE;
if (fdstart_dma(fdc, fd, csb->csb_len) != 0) {
ddi_dma_mem_free(&mem_handle);
mutex_exit(&fdc->c_hilock);
return (-1);
}
mutex_exit(&fdc->c_hilock);
} else {
csb->csb_len = (uint_t)4 * ch->fdc_secptrack + 16;
fd = kmem_zalloc(csb->csb_len, KM_SLEEP);
fmthdrs = (uchar_t *)fd;
}
csb->csb_addr = (caddr_t)fd;
for (i = 1; i <= ch->fdc_secptrack; i++) {
*fd++ = (uchar_t)cyl;
*fd++ = (uchar_t)hd;
*fd++ = (uchar_t)i;
*fd++ = ch->fdc_medium ? 3 : 2;
}
if ((cmdresult = fdexec(fdc, FDXC_SLEEP | FDXC_CHECKCHG)) == 0) {
if (csb->csb_cmdstat)
cmdresult = EIO;
}
if (fdc->c_fdtype & FDCTYPE_DMA) {
ddi_dma_mem_free(&mem_handle);
} else {
kmem_free((caddr_t)fmthdrs, csb->csb_len);
}
fdretcsb(fdc);
return (cmdresult);
}
static int slavio_index_pulse_work_around = 0;
static void
fdstart(struct fdctlr *fdc)
{
struct buf *bp;
struct fdcsb *csb;
struct fdunit *un;
struct fd_char *ch;
struct dk_map32 *dkm;
uint_t part;
uint_t start_part;
uint_t last_part;
uint_t blk;
uint_t sect;
uint_t cyl;
uint_t bincyl;
uint_t secpcyl;
uint_t phys_blkno;
uint_t head;
uint_t unit;
uint_t len, tlen;
caddr_t addr;
caddr_t temp_addr;
uint_t partial_read = 0;
int sb_temp_buf_used = 0;
bp = fdc->c_actf;
while (bp != NULL) {
fdc->c_actf = bp->av_forw;
fdc->c_current = bp;
bp->b_flags &= ~B_ERROR;
bp->b_error = 0;
bp->b_resid = bp->b_bcount;
bp_mapin(bp);
addr = bp->b_un.b_addr;
unit = fdc->c_un->un_unit_no;
un = fdc->c_un;
ch = un->un_chars;
part = FDPARTITION(bp->b_edev);
dkm = &un->un_label.dkl_map[part];
if (un->un_chars->fdc_medium) {
phys_blkno = bp->b_blkno >> 1;
} else {
phys_blkno = bp->b_blkno;
}
if (un->un_iostat) {
kstat_waitq_to_runq(KIOSP);
}
FDERRPRINT(FDEP_L1, FDEM_STRT,
(C, "fdstart: bp=0x%p blkno=0x%x bcount=0x%x\n",
(void *)bp, (int)bp->b_blkno, (int)bp->b_bcount));
fdgetcsb(fdc);
csb = &fdc->c_csb;
csb->csb_unit = unit;
if (bp->b_flags & B_READ) {
if (((fdc->c_fdtype & FDCTYPE_SLAVIO) &&
slavio_index_pulse_work_around) ||
(fdc->c_fdtype & FDCTYPE_TCBUG))
csb->csb_cmds[0] = SK | FDRAW_RDCMD | MFM;
else
csb->csb_cmds[0] = MT | SK | FDRAW_RDCMD | MFM;
} else {
if (fdc->c_fdtype & FDCTYPE_TCBUG)
csb->csb_cmds[0] = FDRAW_WRCMD | MFM;
else
csb->csb_cmds[0] = MT | FDRAW_WRCMD | MFM;
}
if (bp->b_flags & B_READ)
fdc->c_csb.csb_read = CSB_READ;
else
fdc->c_csb.csb_read = CSB_WRITE;
csb->csb_cmds[5] = ch->fdc_medium ? 3 : 2;
csb->csb_cmds[6] = ch->fdc_secptrack;
csb->csb_cmds[7] = GPLN;
csb->csb_cmds[8] = SSSDTL;
csb->csb_ncmds = NCBRW;
csb->csb_nrslts = NRBRW;
csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT;
blk = phys_blkno;
start_part = (dkm->dkl_cylno * ch->fdc_secptrack
* ch->fdc_nhead);
blk = blk + start_part;
last_part = start_part + dkm->dkl_nblk;
if ((blk + (bp->b_bcount / ch->fdc_sec_size)) > last_part)
len = (last_part - blk) * ch->fdc_sec_size;
else
len = (uint_t)bp->b_bcount;
secpcyl = ch->fdc_nhead * ch->fdc_secptrack;
while (len != 0) {
cyl = blk / secpcyl;
bincyl = blk % secpcyl;
head = bincyl / ch->fdc_secptrack;
sect = (bincyl % ch->fdc_secptrack) + 1;
if ((fdc->c_fdtype & FDCTYPE_SLAVIO) &&
slavio_index_pulse_work_around &&
(fdc->c_csb.csb_read == CSB_READ)) {
tlen = (ch->fdc_secptrack - sect + 1) *
ch->fdc_sec_size;
if (len < tlen) {
partial_read = 1;
temp_addr = (caddr_t)kmem_alloc(tlen,
KM_SLEEP);
}
} else if (fdc->c_fdtype & FDCTYPE_TCBUG) {
tlen = len;
if (len > ((ch->fdc_secptrack - sect + 1) *
ch->fdc_sec_size))
tlen = (ch->fdc_secptrack - sect + 1)
* ch->fdc_sec_size;
} else {
if (len > ((secpcyl - bincyl)
* ch->fdc_sec_size))
tlen = (secpcyl - bincyl)
* ch->fdc_sec_size;
else
tlen = len;
}
if (fdc->c_fdtype & FDCTYPE_SB) {
if (tlen > max_fd_dma_len)
tlen = max_fd_dma_len;
}
FDERRPRINT(FDEP_L1, FDEM_STRT,
(C, " blk 0x%x, addr 0x%p, len 0x%x\n",
blk, (void *)addr, len));
FDERRPRINT(FDEP_L1, FDEM_STRT,
(C, "cyl:%x, head:%x, sec:%x\n",
cyl, head, sect));
FDERRPRINT(FDEP_L1, FDEM_STRT,
(C, " resid 0x%lx, tlen %d\n",
bp->b_resid, tlen));
csb->csb_cmds[1] = (head << 2) | unit;
if (fdc->c_fdtype & FDCTYPE_SB)
csb->csb_cmds[1] |= IPS;
csb->csb_cmds[2] = cyl;
csb->csb_cmds[3] = head;
csb->csb_cmds[4] = sect;
if (fdc->c_fdtype & FDCTYPE_TCBUG)
csb->csb_cmds[6] = sect +
(tlen / ch->fdc_sec_size) - 1;
csb->csb_len = tlen;
if (partial_read)
csb->csb_addr = temp_addr;
else
csb->csb_addr = addr;
csb->csb_maxretry = rwretry;
csb->csb_retrys = 0;
if (fdc->c_fdtype & FDCTYPE_DMA) {
if ((fdc->c_fdtype & FDCTYPE_SB) &&
(((uint32_t)(uintptr_t)addr & 0xFFFF0000) !=
(((uint32_t)(uintptr_t)addr + tlen) &
0xFFFF0000))) {
csb->csb_addr = fdc->dma_buf;
sb_temp_buf_used = 1;
if (csb->csb_read != CSB_READ) {
bcopy(addr, fdc->dma_buf, tlen);
}
}
mutex_enter(&fdc->c_hilock);
if (fdstart_dma(fdc, csb->csb_addr,
tlen) != 0) {
bp->b_flags |= B_ERROR;
bp->b_error = EAGAIN;
mutex_exit(&fdc->c_hilock);
FDERRPRINT(FDEP_L1, FDEM_STRT,
(C, "fdstart: no dma resources\n"));
break;
}
mutex_exit(&fdc->c_hilock);
}
bp->b_error = fdexec(fdc, FDXC_SLEEP|FDXC_CHECKCHG);
if (bp->b_error != 0) {
FDERRPRINT(FDEP_L1, FDEM_STRT, (C,
"fdstart: bad exec of bp: 0x%p, err %d\n",
(void *)bp, bp->b_error));
bp->b_flags |= B_ERROR;
if (partial_read) {
partial_read = 0;
kmem_free(temp_addr, tlen);
}
break;
}
if (partial_read) {
partial_read = 0;
bcopy(temp_addr, addr, len);
kmem_free(temp_addr, tlen);
tlen = len;
}
if ((fdc->c_fdtype & FDCTYPE_SB) &&
(csb->csb_read == CSB_READ)) {
if (sb_temp_buf_used) {
bcopy(fdc->dma_buf, addr, tlen);
sb_temp_buf_used = 0;
}
}
blk += tlen / ch->fdc_sec_size;
len -= tlen;
addr += tlen;
bp->b_resid -= tlen;
}
FDERRPRINT(FDEP_L1, FDEM_STRT,
(C, "fdstart done: b_resid %lu, b_count %lu, csb_rlen %d\n",
bp->b_resid, bp->b_bcount, fdc->c_csb.csb_rlen));
fdc->c_current = 0;
fdretcsb(fdc);
if (un->un_iostat) {
if (bp->b_flags & B_READ) {
KIOSP->reads++;
KIOSP->nread +=
(bp->b_bcount - bp->b_resid);
} else {
KIOSP->writes++;
KIOSP->nwritten += (bp->b_bcount - bp->b_resid);
}
kstat_runq_exit(KIOSP);
}
biodone(bp);
bp = fdc->c_actf;
}
}
static int
fdstart_dma(struct fdctlr *fdc, caddr_t addr, uint_t len)
{
int flags;
int res;
FDERRPRINT(FDEP_L1, FDEM_SDMA, (C, "fdstart_dma: start\n"));
if (fdc->c_csb.csb_read == CSB_READ) {
flags = DDI_DMA_READ;
} else {
flags = DDI_DMA_WRITE;
}
flags = flags | DDI_DMA_PARTIAL;
FDERRPRINT(FDEP_L1, FDEM_SDMA, (C, "fdstart_dma: amt. asked for %d\n",
len));
bzero((char *)&fdc->c_csb.csb_dmacookie,
sizeof (fdc->c_csb.csb_dmacookie));
fdc->c_csb.csb_nwin = 0;
fdc->c_csb.csb_windex = 0;
fdc->c_csb.csb_ccount = 0;
res = ddi_dma_addr_bind_handle(fdc->c_dmahandle, NULL, addr, len,
flags, DDI_DMA_DONTWAIT, 0, &fdc->c_csb.csb_dmacookie,
&fdc->c_csb.csb_ccount);
switch (res) {
case DDI_DMA_MAPPED:
fdc->c_csb.csb_nwin = 1;
fdc->c_csb.csb_windex = 1;
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: DDI_DMA_MAPPED\n"));
break;
case DDI_DMA_PARTIAL_MAP:
if (ddi_dma_numwin(fdc->c_dmahandle,
&fdc->c_csb.csb_nwin) != DDI_SUCCESS) {
return (-1);
}
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: partially mapped %d windows\n",
fdc->c_csb.csb_nwin));
fdc->c_csb.csb_windex = 1;
break;
case DDI_DMA_NORESOURCES:
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: no resources\n"));
return (-1);
case DDI_DMA_NOMAPPING:
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: no mapping\n"));
return (-1);
case DDI_DMA_TOOBIG:
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: too big\n"));
return (-1);
case DDI_DMA_INUSE:
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: dma inuse\n"));
return (-1);
default:
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: result is 0x%x\n", res));
return (-1);
};
FDERRPRINT(FDEP_L1, FDEM_SDMA,
(C, "fdstart_dma: bound the handle\n"));
ASSERT(fdc->c_csb.csb_dmacookie.dmac_size);
FDERRPRINT(FDEP_L1, FDEM_SDMA, (C, "fdstart_dma: done\n"));
return (0);
}
static int
fd_unbind_handle(struct fdctlr *fdc)
{
if ((fdc->c_fdtype & FDCTYPE_DMA) &&
((fdc->c_csb.csb_read == CSB_READ) ||
(fdc->c_csb.csb_read == CSB_WRITE))) {
mutex_enter(&fdc->c_hilock);
if (fdc->c_fdtype & FDCTYPE_SB) {
if (fdc->sb_dma_lock) {
release_sb_dma(fdc);
}
}
if (get_data_count_register(fdc) != 0) {
FDERRPRINT(FDEP_L1, FDEM_EXEC,
(C, "unbind & byte count isn't zero\n"));
reset_dma_controller(fdc);
set_dma_control_register(fdc, DCSR_INIT_BITS);
}
if (ddi_dma_unbind_handle(fdc->c_dmahandle) != DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_EXEC,
(C, "problem unbinding the handle\n"));
mutex_exit(&fdc->c_hilock);
return (EIO);
}
mutex_exit(&fdc->c_hilock);
}
return (0);
}
static int
fdexec(struct fdctlr *fdc, int flags)
{
struct fdcsb *csb;
int i;
int to, unit;
uchar_t tmp;
caddr_t a = (caddr_t)fdc;
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: flags:%x\n", flags));
ASSERT(mutex_owned(&fdc->c_lolock));
csb = &fdc->c_csb;
unit = csb->csb_unit;
ASSERT(unit == fdc->c_un->un_unit_no);
retry:
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: cmd is %s\n",
fdcmds[csb->csb_cmds[0] & 0x1f].cmdname));
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: transfer rate = %d\n",
fdc->c_un->un_chars->fdc_transfer_rate));
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: sec size = %d\n",
fdc->c_un->un_chars->fdc_sec_size));
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: nblocks (512) = %d\n",
fdc->c_un->un_label.dkl_map[2].dkl_nblk));
if ((fdc->c_fdtype & FDCTYPE_CTRLMASK) == FDCTYPE_82077) {
fdexec_turn_on_motor(fdc, flags, unit);
}
fdselect(fdc, unit, 1);
switch (fdc->c_un->un_chars->fdc_transfer_rate) {
case 500:
Dsr(fdc, 0);
break;
case 300:
Dsr(fdc, 1);
break;
case 250:
Dsr(fdc, 2);
break;
}
drv_usecwait(2);
if ((flags & FDXC_CHECKCHG) && fdsense_chng(fdc, unit)) {
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "diskette changed\n"));
fdc->c_un->un_flags |= FDUNIT_CHANGED;
if (fdcheckdisk(fdc, unit)) {
(void) fd_unbind_handle(fdc);
return (ENXIO);
}
}
switch (csb->csb_cmds[0] & 0x1f) {
case FDRAW_RDCMD:
fdc->fdstats.rd++;
break;
case FDRAW_WRCMD:
fdc->fdstats.wr++;
break;
case FDRAW_REZERO:
fdc->fdstats.recal++;
break;
case FDRAW_FORMAT:
fdc->fdstats.form++;
break;
default:
fdc->fdstats.other++;
break;
}
csb->csb_raddr = 0;
csb->csb_rlen = 0;
if (csb->csb_opflags & CSB_OFSEEKOPS) {
csb->csb_opmode = 2;
} else if (csb->csb_opflags & CSB_OFIMMEDIATE) {
csb->csb_opmode = 0;
} else {
csb->csb_opmode = 1;
csb->csb_raddr = csb->csb_addr;
csb->csb_rlen = csb->csb_len;
}
bzero((caddr_t)csb->csb_rslt, 10);
csb->csb_status = 0;
csb->csb_cmdstat = 0;
if ((fdc->c_fdtype & FDCTYPE_DMA) &&
((fdc->c_csb.csb_read == CSB_READ) ||
(fdc->c_csb.csb_read == CSB_WRITE))) {
mutex_enter(&fdc->c_hilock);
reset_dma_controller(fdc);
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "cookie addr 0x%p\n",
(void *)fdc->c_csb.csb_dmacookie.dmac_laddress));
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "cookie length %ld\n",
fdc->c_csb.csb_dmacookie.dmac_size));
ASSERT(fdc->c_csb.csb_dmacookie.dmac_size);
set_data_count_register(fdc,
fdc->c_csb.csb_dmacookie.dmac_size);
set_data_address_register(fdc,
fdc->c_csb.csb_dmacookie.dmac_laddress);
if (fdc->c_csb.csb_read == CSB_READ)
set_dma_mode(fdc, CSB_READ);
else
set_dma_mode(fdc, CSB_WRITE);
mutex_exit(&fdc->c_hilock);
}
if (Msr(fdc) & CB) {
FDERRPRINT(FDEP_L3, FDEM_EXEC,
(C, "fdc: unexpectedly busy-stat 0x%x\n", Msr(fdc)));
csb->csb_cmdstat = 1;
(void) fd_unbind_handle(fdc);
return (EBUSY);
}
for (i = 0; i < (int)csb->csb_ncmds; i++) {
for (to = FD_CRETRY; to; to--) {
if ((Msr(fdc) & (DIO|RQM)) == RQM)
break;
}
if (to == 0) {
FDERRPRINT(FDEP_L2, FDEM_EXEC,
(C, "fdc: no RQM - stat 0x%x\n", Msr(fdc)));
csb->csb_cmdstat = 1;
(void) fd_unbind_handle(fdc);
return (EIO);
}
Set_Fifo(fdc, csb->csb_cmds[i]);
FDERRPRINT(FDEP_L1, FDEM_EXEC,
(C, "fdexec: sent 0x%x, Msr 0x%x\n", csb->csb_cmds[i],
Msr(fdc)));
}
if (csb->csb_opflags & CSB_OFTIMEIT) {
fdc->c_timeid = timeout(fdwatch, a,
tosec * drv_usectohz(1000000));
}
FDERRPRINT(FDEP_L1, FDEM_EXEC,
(C, "fdexec: cmd sent, Msr 0x%x\n", Msr(fdc)));
if (csb->csb_opflags & CSB_OFNORESULTS) {
if (fdc->c_fdtype & FDCTYPE_82077) {
if (fdc->c_mtimeid == 0) {
fdc->c_mtimeid = timeout(fdmotoff, a,
Motoff_delay);
}
}
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: O K ..\n"));
for (to = FD_CRETRY; to; to--) {
if ((Msr(fdc) & (DIO|RQM)) == RQM)
break;
}
if (to == 0) {
csb->csb_cmdstat = 1;
return (EIO);
}
return (0);
}
if (csb->csb_opflags & CSB_OFIMMEDIATE) {
to = FD_RRETRY;
csb->csb_nrslts = 0;
while ((tmp = Msr(fdc)) & CB) {
if ((tmp & (RQM|DIO|CB)) == (RQM|DIO|CB)) {
csb->csb_rslt[csb->csb_nrslts++] =
Fifo(fdc);
} else if (--to == 0) {
FDERRPRINT(FDEP_L4, FDEM_EXEC,
(C, "fdexec: timeout, Msr%x, nr%x\n",
Msr(fdc), csb->csb_nrslts));
csb->csb_status = 2;
if (fdc->c_fdtype & FDCTYPE_82077) {
if (fdc->c_mtimeid == 0) {
fdc->c_mtimeid = timeout(
fdmotoff, a, Motoff_delay);
}
}
return (EIO);
}
}
}
if (flags & FDXC_SLEEP) {
fdc->c_flags |= FDCFLG_WAITING;
while (fdc->c_flags & FDCFLG_WAITING) {
cv_wait(&fdc->c_iocv, &fdc->c_lolock);
}
}
if ((fdc->c_fdtype & FDCTYPE_TCBUG) &&
((csb->csb_rslt[0] & IC_SR0) == 0x40) &&
(csb->csb_rslt[1] & EN_SR1))
csb->csb_rslt[0] &= ~IC_SR0;
if (((csb->csb_rslt[0] & IC_SR0) || (fdc->c_csb.csb_dcsr_rslt) ||
(csb->csb_status)) &&
((csb->csb_cmds[0] != FDRAW_SENSE_DRV) &&
(csb->csb_cmds[0] != DUMPREG))) {
if (fdrecover(fdc) != 0) {
if (fdc->c_fdtype & FDCTYPE_82077) {
if (fdc->c_mtimeid == 0) {
fdc->c_mtimeid = timeout(fdmotoff,
a, Motoff_delay);
}
}
(void) fd_unbind_handle(fdc);
return (EIO);
} else {
goto retry;
}
}
if (fdc->c_fdtype & FDCTYPE_82077) {
if (fdc->c_mtimeid == 0) {
fdc->c_mtimeid = timeout(fdmotoff, a, Motoff_delay);
}
}
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "fdexec: O K ..........\n"));
if (fd_unbind_handle(fdc))
return (EIO);
return (0);
}
static void
fdexec_turn_on_motor(struct fdctlr *fdc, int flags, uint_t unit)
{
clock_t local_lbolt;
timeout_id_t timeid;
timeid = fdc->c_mtimeid;
fdc->c_mtimeid = 0;
if (timeid) {
mutex_exit(&fdc->c_lolock);
(void) untimeout(timeid);
mutex_enter(&fdc->c_lolock);
}
ASSERT(fdc->c_un->un_unit_no == unit);
set_rotational_speed(fdc, unit);
if (!(Dor(fdc) & (MOTEN(unit)))) {
FDERRPRINT(FDEP_L1, FDEM_EXEC,
(C, "fdexec: turning on motor\n"));
Set_dor(fdc, (MOTEN(unit)), 1);
if (flags & FDXC_SLEEP) {
local_lbolt = ddi_get_lbolt();
(void) cv_timedwait(&fdc->c_motoncv,
&fdc->c_lolock, local_lbolt + Moton_delay);
} else {
drv_usecwait(1000000);
}
}
}
static int
fdrecover(struct fdctlr *fdc)
{
struct fdcsb *csb;
FDERRPRINT(FDEP_L1, FDEM_RECO, (C, "fdrecover\n"));
csb = &fdc->c_csb;
if (fdc->c_flags & FDCFLG_TIMEDOUT) {
struct fdcsb savecsb;
fdc->c_flags ^= FDCFLG_TIMEDOUT;
csb->csb_rslt[1] |= TO_SR1;
FDERRPRINT(FDEP_L1, FDEM_RECO,
(C, "fd%d: %s timed out\n", csb->csb_unit,
fdcmds[csb->csb_cmds[0] & 0x1f].cmdname));
savecsb = fdc->c_csb;
bzero(&fdc->c_csb, sizeof (struct fdcsb));
FDERRPRINT(FDEP_L1, FDEM_RECO, (C, "fdc: resetting\n"));
(void) fdreset(fdc);
if (fdc->c_fdtype & FDCTYPE_DMA) {
mutex_enter(&fdc->c_hilock);
reset_dma_controller(fdc);
set_dma_control_register(fdc, DCSR_INIT_BITS);
mutex_exit(&fdc->c_hilock);
}
(void) fdrecalseek(fdc, savecsb.csb_unit, -1, 0);
fdc->c_csb = savecsb;
}
if (csb->csb_rslt[1] & DE_SR1) {
fdc->fdstats.de++;
}
if (csb->csb_rslt[1] & OR_SR1) {
fdc->fdstats.run++;
}
if (csb->csb_rslt[1] & (ND_SR1+MA_SR1)) {
fdc->fdstats.bfmt++;
}
if (csb->csb_rslt[1] & TO_SR1) {
fdc->fdstats.to++;
}
if (csb->csb_opflags & CSB_OFRAWIOCTL) {
return (1);
}
if (csb->csb_dcsr_rslt == 1) {
FDERRPRINT(FDEP_L3, FDEM_RECO,
(C, "fd%d: host bus error\n", 0));
return (1);
}
if (csb->csb_dma_rslt == 1) {
FDERRPRINT(FDEP_L1, FDEM_RECO,
(C, "fd%d: DMA interface error\n", csb->csb_unit));
return (1);
}
csb->csb_retrys++;
if (csb->csb_retrys > csb->csb_maxretry) {
FDERRPRINT(FDEP_L3, FDEM_RECO,
(C, "fd%d: %s failed (%x %x %x)\n",
0, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname,
csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2]));
if (csb->csb_rslt[1] & NW_SR1) {
FDERRPRINT(FDEP_L3, FDEM_RECO,
(C, "fd%d: not writable\n", 0));
}
if (csb->csb_rslt[1] & DE_SR1) {
FDERRPRINT(FDEP_L3, FDEM_RECO,
(C, "fd%d: crc error blk %d\n", 0,
(int)fdc->c_current->b_blkno));
}
if (csb->csb_rslt[1] & OR_SR1) {
if (fdc->c_fdtype & FDCTYPE_SB) {
if (csb->csb_retrys <= rwretry)
return (0);
}
FDERRPRINT(FDEP_L3, FDEM_RECO,
(C, "fd%d: over/underrun\n", 0));
}
if (csb->csb_rslt[1] & (ND_SR1+MA_SR1)) {
FDERRPRINT(FDEP_L3, FDEM_RECO,
(C, "fd%d: bad format\n", 0));
}
if (csb->csb_rslt[1] & TO_SR1) {
FDERRPRINT(FDEP_L3, FDEM_RECO,
(C, "fd%d: timeout\n", 0));
}
csb->csb_cmdstat = 1;
return (1);
}
if (csb->csb_opflags & CSB_OFSEEKOPS) {
FDERRPRINT(FDEP_L2, FDEM_RECO,
(C, "fd%d: %s error : st0 0x%x\n", csb->csb_unit,
fdcmds[csb->csb_cmds[0] & 0x1f].cmdname,
csb->csb_rslt[0]));
}
if (csb->csb_opflags & CSB_OFXFEROPS) {
FDERRPRINT(FDEP_L2, FDEM_RECO,
(C, "fd%d: %s error : st0=0x%x st1=0x%x st2=0x%x\n",
csb->csb_unit, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname,
csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2]));
}
return (0);
}
static uint_t
fdintr_dma()
{
struct fdctlr *fdc;
off_t off;
size_t len;
uint_t ccount;
uint_t windex;
uint_t done = 0;
int tmp_dcsr;
int to;
uchar_t tmp;
int i = 0;
int res = DDI_INTR_UNCLAIMED;
int not_cheerio = 1;
fdc = fdctlrs;
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
tmp_dcsr = get_dma_control_register(fdc);
if (!(tmp_dcsr & DCSR_INT_PEND) && !(DCSR_ERR_PEND & tmp_dcsr))
return (res);
not_cheerio = 0;
}
mutex_enter(&fdc->c_hilock);
if (fdc->c_csb.csb_opmode == 0x0) {
fdc->c_csb.csb_opmode = 2;
}
if (fdc->sb_dma_lock) {
release_sb_dma(fdc);
}
switch (fdc->c_csb.csb_opmode) {
case 0x1:
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: opmode 1\n"));
if (not_cheerio || (tmp_dcsr & DCSR_INT_PEND)) {
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: INT_PEND \n"));
res = DDI_INTR_CLAIMED;
to = FD_RRETRY;
fdc->c_csb.csb_nrslts = 0;
i = 0;
while (((tmp = Msr(fdc)) & CB) &&
(i < 1000001)) {
if ((tmp & (RQM|DIO|CB)) ==
(RQM|DIO|CB)) {
fdc->c_csb.csb_rslt
[fdc->c_csb.csb_nrslts++]
= Fifo(fdc);
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C,
"fdintr_dma: res 0x%x\n",
fdc->c_csb.csb_rslt
[fdc->c_csb.csb_nrslts
- 1]));
} else if (--to == 0) {
fdc->c_csb.csb_status = 2;
break;
}
i++;
}
if (i == 10000) {
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "First loop overran\n"));
}
}
if ((!not_cheerio) && (tmp_dcsr & DCSR_ERR_PEND)) {
res = DDI_INTR_CLAIMED;
done = 1;
fdc->c_csb.csb_dcsr_rslt = 1;
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: Error pending\n"));
reset_dma_controller(fdc);
set_dma_control_register(fdc, DCSR_INIT_BITS);
break;
}
if ((fdc->c_fdtype & FDCTYPE_TCBUG) &&
((fdc->c_csb.csb_rslt[0] & IC_SR0) == 0x40) &&
(fdc->c_csb.csb_rslt[1] & EN_SR1)) {
fdc->c_csb.csb_rslt[0] &= ~IC_SR0;
fdc->c_csb.csb_rslt[1] &= ~EN_SR1;
}
if (((fdc->c_csb.csb_rslt[0] & IC_SR0) != 0) ||
(fdc->c_csb.csb_rslt[1] != 0) ||
(fdc->c_csb.csb_rslt[2] != 0)) {
done = 1;
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: errors in command\n"));
break;
}
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: dbcr 0x%x\n",
get_data_count_register(fdc)));
if (fdc->c_csb.csb_ccount == 0) {
done = 1;
break;
}
fdc->c_csb.csb_ccount--;
ccount = fdc->c_csb.csb_ccount;
windex = fdc->c_csb.csb_windex;
if ((ccount == 0) && (windex == fdc->c_csb.csb_nwin)) {
done = 1;
break;
}
if (ccount != 0) {
ddi_dma_nextcookie(fdc->c_dmahandle,
&fdc->c_csb.csb_dmacookie);
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "cookie addr 0x%" PRIx64 "\n",
fdc->c_csb.csb_dmacookie.dmac_laddress));
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "cookie length %lu\n",
fdc->c_csb.csb_dmacookie.dmac_size));
} else {
(void) ddi_dma_getwin(fdc->c_dmahandle,
fdc->c_csb.csb_windex,
&off, &len,
&fdc->c_csb.csb_dmacookie,
&fdc->c_csb.csb_ccount);
fdc->c_csb.csb_windex++;
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: process %d window\n",
fdc->c_csb.csb_windex));
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: process no. cookies %d\n",
fdc->c_csb.csb_ccount));
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "cookie addr 0x%" PRIx64 "\n",
fdc->c_csb.csb_dmacookie.dmac_laddress));
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "cookie length %lu\n",
fdc->c_csb.csb_dmacookie.dmac_size));
}
ASSERT(fdc->c_csb.csb_dmacookie.dmac_size);
set_data_count_register(fdc,
fdc->c_csb.csb_dmacookie.dmac_size);
set_data_address_register(fdc,
fdc->c_csb.csb_dmacookie.dmac_laddress);
FDERRPRINT(FDEP_L1, FDEM_INTR, (C,
"fdintr_dma: size 0x%lx\n",
fdc->c_csb.csb_dmacookie.dmac_size));
fdc->c_csb.csb_cmds[2] = fdc->c_csb.csb_rslt[3];
fdc->c_csb.csb_cmds[3] = fdc->c_csb.csb_rslt[4];
fdc->c_csb.csb_cmds[4] = fdc->c_csb.csb_rslt[5];
fdc->c_csb.csb_cmds[1] = (fdc->c_csb.csb_cmds[1]
& ~0x04) | (fdc->c_csb.csb_rslt[4] << 2);
for (i = 0; i < (int)fdc->c_csb.csb_ncmds; i++) {
for (to = FD_CRETRY; to; to--) {
if ((Msr(fdc) & (DIO|RQM)) == RQM)
break;
}
if (to == 0) {
FDERRPRINT(FDEP_L2, FDEM_EXEC,
(C,
"fdc: no RQM - stat 0x%x\n",
Msr(fdc)));
fdc->c_csb.csb_status = 2;
done = 1;
break;
}
Set_Fifo(fdc, fdc->c_csb.csb_cmds[i]);
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C,
"fdintr_dma: sent 0x%x, Msr 0x%x\n",
fdc->c_csb.csb_cmds[i], Msr(fdc)));
}
if ((!not_cheerio) && (!done))
set_dma_control_register(fdc, tmp_dcsr |
DCSR_EN_DMA);
break;
case 0x2:
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fintr_dma: opmode 2\n"));
if ((!not_cheerio) && (tmp_dcsr & DCSR_ERR_PEND)) {
res = DDI_INTR_CLAIMED;
done = 1;
fdc->c_csb.csb_dcsr_rslt = 1;
reset_dma_controller(fdc);
set_dma_control_register(fdc, DCSR_INIT_BITS);
break;
}
if (not_cheerio || (tmp_dcsr & DCSR_INT_PEND)) {
res = DDI_INTR_CLAIMED;
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: interrupt pending\n"));
i = 0;
while (((Msr(fdc) & CB)) && (i < 10000)) {
i++;
}
if (i == 10000)
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "2nd loop overran !!!\n"));
i = 0;
while ((!(Msr(fdc) & RQM)) && (i < 10000)) {
i++;
}
if (i == 10000)
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "3rd loop overran !!!\n"));
Set_Fifo(fdc, SNSISTAT);
i = 0;
while ((!(Msr(fdc) & RQM)) && (i < 10000)) {
i++;
}
if (i == 10000)
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "4th loop overran !!!\n"));
fdc->c_csb.csb_rslt[0] = Fifo(fdc);
i = 0;
while ((!(Msr(fdc) & RQM)) && (i < 10000)) {
i++;
}
if (i == 10000)
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "5th loop overran !!!\n"));
fdc->c_csb.csb_rslt[1] = Fifo(fdc);
done = 1;
}
}
mutex_exit(&fdc->c_hilock);
if (done) {
mutex_enter(&fdc->c_lolock);
fdc->c_csb.csb_opmode = 0;
if (fdc->c_timeid) {
timeout_id_t timeid = fdc->c_timeid;
fdc->c_timeid = 0;
mutex_exit(&fdc->c_lolock);
(void) untimeout(timeid);
mutex_enter(&fdc->c_lolock);
}
if (fdc->c_flags & FDCFLG_WAITING) {
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: signal the waiter\n"));
fdc->c_flags ^= FDCFLG_WAITING;
cv_signal(&fdc->c_iocv);
} else {
FDERRPRINT(FDEP_L1, FDEM_INTR,
(C, "fdintr_dma: nobody sleeping (%x %x %x)\n",
fdc->c_csb.csb_rslt[0], fdc->c_csb.csb_rslt[1],
fdc->c_csb.csb_rslt[2]));
}
mutex_exit(&fdc->c_lolock);
}
if (fdc->c_intrstat)
KIOIP->intrs[KSTAT_INTR_HARD]++;
FDERRPRINT(FDEP_L1, FDEM_INTR, (C, "fdintr_dma: done\n"));
return (res);
}
static uint_t
fd_lointr(caddr_t arg)
{
struct fdctlr *fdc = (struct fdctlr *)arg;
struct fdcsb *csb;
csb = &fdc->c_csb;
FDERRPRINT(FDEP_L1, FDEM_INTR, (C, "fdintr: opmode %d\n",
csb->csb_opmode));
if (csb->csb_opmode != 4) {
if (fdc->c_intrstat)
KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
return (DDI_INTR_UNCLAIMED);
}
mutex_enter(&fdc->c_lolock);
csb->csb_opmode = 0;
if (fdc->c_timeid) {
timeout_id_t timeid = fdc->c_timeid;
fdc->c_timeid = 0;
mutex_exit(&fdc->c_lolock);
(void) untimeout(timeid);
mutex_enter(&fdc->c_lolock);
}
if (fdc->c_flags & FDCFLG_WAITING) {
fdc->c_flags ^= FDCFLG_WAITING;
cv_signal(&fdc->c_iocv);
} else {
FDERRPRINT(FDEP_L3, FDEM_INTR,
(C, "fdintr: nobody sleeping (%x %x %x)\n",
csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2]));
}
if (fdc->c_intrstat)
KIOIP->intrs[KSTAT_INTR_SOFT]++;
mutex_exit(&fdc->c_lolock);
return (DDI_INTR_CLAIMED);
}
static void
fdwatch(void *arg)
{
struct fdctlr *fdc = arg;
int old_opmode;
struct fdcsb *csb;
FDERRPRINT(FDEP_L1, FDEM_WATC, (C, "fdwatch\n"));
mutex_enter(&fdc->c_lolock);
if (fdc->c_timeid == 0) {
FDERRPRINT(FDEP_L1, FDEM_WATC,
(C, "fdwatch: no timeout\n"));
mutex_exit(&fdc->c_lolock);
return;
}
fdc->c_timeid = 0;
csb = &fdc->c_csb;
mutex_enter(&fdc->c_hilock);
old_opmode = csb->csb_opmode;
FDERRPRINT(FDEP_L1, FDEM_WATC,
(C, "fd%d: timeout, opmode:%d\n", csb->csb_unit, old_opmode));
csb->csb_opmode = 4;
mutex_exit(&fdc->c_hilock);
FDERRPRINT(FDEP_L1, FDEM_WATC, (C, "fdwatch: cmd %s timed out\n",
fdcmds[csb->csb_cmds[0] & 0x1f].cmdname));
fdc->c_flags |= FDCFLG_TIMEDOUT;
csb->csb_status = CSB_CMDTO;
if ((fdc->c_fdtype & FDCTYPE_DMA) == 0) {
ddi_trigger_softintr(fdc->c_softid);
KIOIP->intrs[KSTAT_INTR_WATCHDOG]++;
mutex_exit(&fdc->c_lolock);
} else {
mutex_exit(&fdc->c_lolock);
(void) fd_lointr((caddr_t)fdctlrs);
}
}
static void
fdgetcsb(struct fdctlr *fdc)
{
FDERRPRINT(FDEP_L1, FDEM_GETC, (C, "fdgetcsb\n"));
ASSERT(mutex_owned(&fdc->c_lolock));
while (fdc->c_flags & FDCFLG_BUSY) {
fdc->c_flags |= FDCFLG_WANT;
cv_wait(&fdc->c_csbcv, &fdc->c_lolock);
}
fdc->c_flags |= FDCFLG_BUSY;
}
static void
fdretcsb(struct fdctlr *fdc)
{
ASSERT(mutex_owned(&fdc->c_lolock));
FDERRPRINT(FDEP_L1, FDEM_RETC, (C, "fdretcsb\n"));
fdc->c_flags &= ~FDCFLG_BUSY;
fdc->c_csb.csb_read = 0;
if (fdc->c_flags & FDCFLG_WANT) {
fdc->c_flags ^= FDCFLG_WANT;
cv_broadcast(&fdc->c_csbcv);
}
}
static int
fdreset(struct fdctlr *fdc)
{
struct fdcsb *csb;
clock_t local_lbolt = 0;
timeout_id_t timeid;
FDERRPRINT(FDEP_L1, FDEM_RESE, (C, "fdreset\n"));
ASSERT(mutex_owned(&fdc->c_lolock));
fdc->fdstats.reset++;
if ((fdc->c_fdtype & FDCTYPE_CTRLMASK) == FDCTYPE_82077) {
timeid = fdc->c_mtimeid;
fdc->c_mtimeid = 0;
if (timeid) {
mutex_exit(&fdc->c_lolock);
(void) untimeout(timeid);
mutex_enter(&fdc->c_lolock);
}
Set_dor(fdc, DMAGATE, 0);
FDERRPRINT(FDEP_L1, FDEM_RESE, (C, "fdreset: set dor\n"));
}
Dsr(fdc, SWR);
drv_usecwait(5);
FDERRPRINT(FDEP_L1, FDEM_RESE,
(C, "fdreset: toggled software reset\n"));
Dsr(fdc, 0);
drv_usecwait(5);
switch (fdc->c_fdtype & FDCTYPE_CTRLMASK) {
case FDCTYPE_82077:
fdc->c_flags |= FDCFLG_WAITING;
Set_dor(fdc, DMAGATE|RESET, 1);
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdattach: Dor 0x%x\n", Dor(fdc)));
local_lbolt = ddi_get_lbolt();
if (cv_timedwait(&fdc->c_iocv, &fdc->c_lolock,
local_lbolt + drv_usectohz(1000000)) == -1) {
return (-1);
}
break;
default:
fdc->c_flags |= FDCFLG_WAITING;
cv_wait(&fdc->c_iocv, &fdc->c_lolock);
break;
}
csb = &fdc->c_csb;
csb->csb_unit = fdc->c_un->un_unit_no;
csb->csb_nrslts = 0;
csb->csb_opflags = CSB_OFNORESULTS;
csb->csb_maxretry = 0;
csb->csb_retrys = 0;
csb->csb_read = CSB_NULL;
csb->csb_cmds[0] = FDRAW_SPECIFY;
csb->csb_cmds[1] = fdspec[0];
if (fdc->c_fdtype & FDCTYPE_DMA)
csb->csb_cmds[2] = SPEC_DMA_MODE;
else
csb->csb_cmds[2] = fdspec[1];
csb->csb_ncmds = 3;
(void) fdexec(fdc, 0);
csb->csb_cmds[0] = CONFIGURE;
csb->csb_cmds[1] = fdconf[0];
csb->csb_cmds[2] = fdconf[1];
csb->csb_cmds[3] = fdconf[2];
csb->csb_ncmds = 4;
csb->csb_read = CSB_NULL;
csb->csb_retrys = 0;
(void) fdexec(fdc, 0);
return (0);
}
static int
fdrecalseek(struct fdctlr *fdc, int unit, int arg, int execflg)
{
struct fdcsb *csb;
int result;
ASSERT(fdc->c_un->un_unit_no == unit);
FDERRPRINT(FDEP_L1, FDEM_RECA, (C, "fdrecalseek to %d\n", arg));
csb = &fdc->c_csb;
csb->csb_unit = (uchar_t)unit;
csb->csb_cmds[1] = unit & 0x03;
if (arg == -1) {
csb->csb_cmds[0] = FDRAW_REZERO;
csb->csb_ncmds = 2;
} else {
csb->csb_cmds[0] = FDRAW_SEEK;
csb->csb_cmds[2] = (uchar_t)arg;
csb->csb_ncmds = 3;
}
csb->csb_nrslts = 2;
csb->csb_opflags = CSB_OFSEEKOPS | CSB_OFTIMEIT;
csb->csb_maxretry = skretry;
csb->csb_retrys = 0;
if (result = fdexec(fdc, FDXC_SLEEP | execflg)) {
goto out;
}
if (arg == -1) {
result = 0;
} else {
if ((csb->csb_rslt[0] & IC_SR0) || csb->csb_cmdstat)
result = ENODEV;
}
out:
return (result);
}
static int
fdsensedrv(struct fdctlr *fdc, int unit)
{
struct fdcsb *csb;
ASSERT(fdc->c_un->un_unit_no == unit);
csb = &fdc->c_csb;
csb->csb_unit = (uchar_t)unit;
csb->csb_opflags = CSB_OFIMMEDIATE;
csb->csb_cmds[0] = FDRAW_SENSE_DRV;
csb->csb_cmds[1] = MOT | (unit & 0x03);
csb->csb_ncmds = 2;
csb->csb_nrslts = 1;
csb->csb_maxretry = skretry;
csb->csb_retrys = 0;
(void) fdexec(fdc, 0);
FDERRPRINT(FDEP_L1, FDEM_CHEK,
(C, "fdsensedrv: result 0x%x", csb->csb_rslt[0]));
return (csb->csb_rslt[0]);
}
static int
fdcheckdisk(struct fdctlr *fdc, int unit)
{
auto struct fdcsb savecsb;
struct fdcsb *csb;
int err, st3;
int seekto;
FDERRPRINT(FDEP_L1, FDEM_CHEK,
(C, "fdcheckdisk, unit %d\n", unit));
ASSERT(fdc->c_un->un_unit_no == unit);
csb = &fdc->c_csb;
savecsb = fdc->c_csb;
bzero((caddr_t)csb, sizeof (*csb));
st3 = fdsensedrv(fdc, unit);
if (st3 & T0_SR3)
seekto = 1;
else
seekto = 0;
err = fdrecalseek(fdc, unit, seekto, 0);
fdc->c_csb = savecsb;
if (err) {
FDERRPRINT(FDEP_L2, FDEM_CHEK,
(C, "fdcheckdisk err %d\n", err));
return (err);
}
if (fdsense_chng(fdc, csb->csb_unit)) {
FDERRPRINT(FDEP_L2, FDEM_CHEK,
(C, "fdcheckdisk no disk\n"));
return (1);
}
return (0);
}
static void
fdselect(struct fdctlr *fdc, int unit, int on)
{
ASSERT(fdc->c_un->un_unit_no == unit);
FDERRPRINT(FDEP_L1, FDEM_DSEL,
(C, "fdselect, unit %d, on = %d\n", unit, on));
switch (fdc->c_fdtype & FDCTYPE_AUXIOMASK) {
case FDCTYPE_MACHIO:
set_auxioreg(AUX_DRVSELECT, on);
break;
case FDCTYPE_SLAVIO:
case FDCTYPE_CHEERIO:
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdselect: (before) Dor 0x%x\n", Dor(fdc)));
if (unit == 0) {
Set_dor(fdc, DRVSEL, !on);
} else {
Set_dor(fdc, DRVSEL, on);
}
FDERRPRINT(FDEP_L1, FDEM_ATTA,
(C, "fdselect: Dor 0x%x\n", Dor(fdc)));
break;
default:
break;
}
}
static void
fdeject(struct fdctlr *fdc, int unit)
{
struct fdunit *un;
ASSERT(fdc->c_un->un_unit_no == unit);
un = fdc->c_un;
FDERRPRINT(FDEP_L1, FDEM_EJEC, (C, "fdeject\n"));
switch (fdc->c_fdtype & FDCTYPE_AUXIOMASK) {
case FDCTYPE_MACHIO:
set_auxioreg(AUX_EJECT, 0);
drv_usecwait(2);
set_auxioreg(AUX_EJECT, 1);
break;
case FDCTYPE_SLAVIO:
if (!(Dor(fdc) & MOTEN(unit))) {
Set_dor(fdc, MOTEN(unit), 1);
}
drv_usecwait(2);
Set_dor(fdc, EJECT, 1);
drv_usecwait(2);
Set_dor(fdc, EJECT, 0);
break;
case FDCTYPE_CHEERIO:
if (!(Dor(fdc) & MOTEN(unit))) {
Set_dor(fdc, MOTEN(unit), 1);
}
drv_usecwait(2);
Set_dor(fdc, EJECT_DMA, 1);
drv_usecwait(2);
Set_dor(fdc, EJECT_DMA, 0);
break;
}
un->un_ejected = 1;
}
static int
fdsense_chng(struct fdctlr *fdc, int unit)
{
int changed = 0;
FDERRPRINT(FDEP_L1, FDEM_SCHG, (C, "fdsense_chng:start\n"));
ASSERT(fdc->c_un->un_unit_no == unit);
if (fd_pollable) {
FDERRPRINT(FDEP_L1, FDEM_SCHG, (C, "pollable: don't turn on motor\n"));
if (Dir(fdc) & DSKCHG)
changed = 0;
else
changed = 1;
return (changed);
}
switch (fdc->c_fdtype & FDCTYPE_AUXIOMASK) {
case FDCTYPE_MACHIO:
if (*fdc->c_auxiova & AUX_DISKCHG)
changed = 1;
break;
case FDCTYPE_SB:
case FDCTYPE_SLAVIO:
case FDCTYPE_CHEERIO:
if (!(Dor(fdc) & MOTEN(unit))) {
Set_dor(fdc, MOTEN(unit), 1);
}
drv_usecwait(2);
if (Dir(fdc) & DSKCHG)
changed = 1;
break;
}
FDERRPRINT(FDEP_L1, FDEM_SCHG, (C, "fdsense_chng:end\n"));
return (changed);
}
static int
fdgetlabel(struct fdctlr *fdc, int unit)
{
struct dk_label *label = NULL;
struct fdunit *un;
short *sp;
short count;
short xsum;
int i, tries;
int err = 0;
short oldlvl;
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetlabel: unit %d\n", unit));
un = fdc->c_un;
un->un_flags &= ~(FDUNIT_UNLABELED);
ASSERT(fdc->c_un->un_unit_no == unit);
oldlvl = fderrlevel;
fderrlevel = FDEP_L4;
label = (struct dk_label *)
kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
if (un->un_curfdtype == -1) {
tries = nfdtypes+1;
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: un_curfdtype is -1\n"));
} else {
tries = nfdtypes;
un->un_curfdtype = 0;
*(un->un_chars) = fdtypes[un->un_curfdtype];
}
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: no. of tries %d\n", tries));
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: no. of curfdtype %d\n", un->un_curfdtype));
for (i = 0; i < tries; i++) {
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: trying %d\n", i));
if (!(err = fdrw(fdc, unit, FDREAD, 0, 0,
un->un_chars->fdc_secptrack, (caddr_t)label,
sizeof (struct dk_label))) &&
fdrw(fdc, unit, FDREAD, 0, 0,
un->un_chars->fdc_secptrack + 1,
(caddr_t)label, sizeof (struct dk_label)) &&
!(err = fdrw(fdc, unit, FDREAD, 0, 0, 1, (caddr_t)label,
sizeof (struct dk_label)))) {
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: succeeded\n"));
break;
}
un->un_curfdtype = (un->un_curfdtype + 1) % nfdtypes;
*(un->un_chars) = fdtypes[un->un_curfdtype];
}
fderrlevel = oldlvl;
if (err) {
un->un_curfdtype = 1;
*(un->un_chars) = fdtypes[un->un_curfdtype];
fdunpacklabel(&fdlbl_high_80, &un->un_label);
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: Can't autosense diskette\n"));
goto out;
}
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: fdtype=%d !!!\n", un->un_curfdtype));
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: rate=%d ssize=%d !!!\n",
un->un_chars->fdc_transfer_rate, un->un_chars->fdc_sec_size));
if (label->dkl_magic != DKL_MAGIC) {
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: not unix label\n"));
goto nolabel;
}
count = sizeof (struct dk_label)/sizeof (short);
sp = (short *)label;
xsum = 0;
while (count--)
xsum ^= *sp++;
if (xsum) {
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetl: bad cksum\n"));
goto nolabel;
}
un->un_label = *label;
goto out;
nolabel:
FDERRPRINT(FDEP_L1, FDEM_GETL,
(C, "fdgetlabel: unit %d\n", unit));
un->un_flags |= FDUNIT_UNLABELED;
switch (un->un_chars->fdc_secptrack) {
case 9:
fdunpacklabel(&fdlbl_low_80, &un->un_label);
break;
case 8:
fdunpacklabel(&fdlbl_medium_80, &un->un_label);
break;
case 18:
fdunpacklabel(&fdlbl_high_80, &un->un_label);
break;
case 21:
fdunpacklabel(&fdlbl_high_21, &un->un_label);
break;
default:
fdunpacklabel(&fdlbl_high_80, &un->un_label);
break;
}
out:
if (label != NULL)
kmem_free((caddr_t)label, sizeof (struct dk_label));
return (err);
}
static int
fdrw(struct fdctlr *fdc, int unit, int rw, int cyl, int head,
int sector, caddr_t bufp, uint_t len)
{
struct fdcsb *csb;
struct fd_char *ch;
int cmdresult = 0;
caddr_t dma_addr;
size_t real_length;
int res;
ddi_device_acc_attr_t attr;
ddi_acc_handle_t mem_handle = NULL;
FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw\n"));
ASSERT(fdc->c_un->un_unit_no == unit);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
!= DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
failed. \n"));
mutex_enter(&fdc->c_lolock);
return (EIO);
}
mutex_enter(&fdc->c_lolock);
}
fdgetcsb(fdc);
csb = &fdc->c_csb;
ch = fdc->c_un->un_chars;
if (rw == FDREAD) {
if (fdc->c_fdtype & FDCTYPE_TCBUG) {
csb->csb_cmds[0] = SK + FDRAW_RDCMD;
} else
csb->csb_cmds[0] = MT + SK + FDRAW_RDCMD;
} else {
if (fdc->c_fdtype & FDCTYPE_TCBUG) {
csb->csb_cmds[0] = FDRAW_WRCMD;
} else
csb->csb_cmds[0] = MT + FDRAW_WRCMD;
}
if (rw == FDREAD)
fdc->c_csb.csb_read = CSB_READ;
else
fdc->c_csb.csb_read = CSB_WRITE;
csb->csb_cmds[0] |= MFM;
csb->csb_cmds[1] = (uchar_t)(unit | ((head & 0x1) << 2));
if (fdc->c_fdtype & FDCTYPE_SB)
csb->csb_cmds[1] |= IPS;
csb->csb_cmds[2] = (uchar_t)cyl;
csb->csb_cmds[3] = (uchar_t)head;
csb->csb_cmds[4] = (uchar_t)sector;
csb->csb_cmds[5] = ch->fdc_medium ? 3 : 2;
if (fdc->c_fdtype & FDCTYPE_TCBUG)
csb->csb_cmds[6] = sector + (len / ch->fdc_sec_size) - 1;
else
csb->csb_cmds[6] =
(uchar_t)max(fdc->c_un->un_chars->fdc_secptrack, sector);
csb->csb_len = len;
csb->csb_cmds[7] = GPLN;
csb->csb_cmds[8] = SSSDTL;
csb->csb_ncmds = NCBRW;
csb->csb_len = len;
csb->csb_maxretry = 2;
csb->csb_retrys = 0;
bzero(csb->csb_rslt, NRBRW);
csb->csb_nrslts = NRBRW;
csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT;
if (fdc->c_fdtype & FDCTYPE_DMA) {
mutex_enter(&fdc->c_hilock);
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
res = ddi_dma_mem_alloc(fdc->c_dmahandle, len,
&attr, DDI_DMA_STREAMING,
DDI_DMA_DONTWAIT, 0, &dma_addr, &real_length,
&mem_handle);
if (res != DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_RW,
(C, "fdrw: dma mem alloc failed\n"));
fdretcsb(fdc);
mutex_exit(&fdc->c_hilock);
return (EIO);
}
FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw: allocated memory"));
if (fdstart_dma(fdc, dma_addr, len) != 0) {
fdretcsb(fdc);
ddi_dma_mem_free(&mem_handle);
mutex_exit(&fdc->c_hilock);
return (-1);
}
if (fdc->c_csb.csb_read == CSB_WRITE) {
bcopy((char *)bufp, (char *)dma_addr, len);
}
csb->csb_addr = dma_addr;
mutex_exit(&fdc->c_hilock);
} else {
csb->csb_addr = bufp;
}
FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw: call fdexec\n"));
if (fdexec(fdc, FDXC_SLEEP | FDXC_CHECKCHG) != 0) {
fdretcsb(fdc);
if (mem_handle)
ddi_dma_mem_free(&mem_handle);
return (EIO);
}
FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fdrw: fdexec returned\n"));
if (fdc->c_fdtype & FDCTYPE_DMA) {
if (fdc->c_csb.csb_read == CSB_READ) {
bcopy((char *)dma_addr, (char *)bufp, len);
}
ddi_dma_mem_free(&mem_handle);
}
if (csb->csb_cmdstat)
cmdresult = EIO;
fdretcsb(fdc);
return (cmdresult);
}
static void
fdunpacklabel(struct packed_label *from, struct dk_label *to)
{
FDERRPRINT(FDEP_L1, FDEM_PACK, (C, "fdpacklabel\n"));
bzero((caddr_t)to, sizeof (*to));
bcopy((caddr_t)&from->dkl_vname, (caddr_t)to->dkl_asciilabel,
sizeof (to->dkl_asciilabel));
to->dkl_rpm = from->dkl_rpm;
to->dkl_pcyl = from->dkl_pcyl;
to->dkl_apc = from->dkl_apc;
to->dkl_intrlv = from->dkl_intrlv;
to->dkl_ncyl = from->dkl_ncyl;
to->dkl_acyl = from->dkl_acyl;
to->dkl_nhead = from->dkl_nhead;
to->dkl_nsect = from->dkl_nsect;
bcopy((caddr_t)from->dkl_map, (caddr_t)to->dkl_map,
sizeof (struct dk_map32) * NDKMAP);
to->dkl_vtoc = from->dkl_vtoc;
}
static struct fdctlr *
fd_getctlr(dev_t dev)
{
struct fdctlr *fdc = fdctlrs;
int ctlr = FDCTLR(dev);
while (fdc) {
if (ddi_get_instance(fdc->c_dip) == ctlr)
return (fdc);
fdc = fdc->c_next;
}
return (fdc);
}
static int
fd_unit_is_open(struct fdunit *un)
{
int i;
for (i = 0; i < NDKMAP; i++)
if (un->un_lyropen[i])
return (1);
for (i = 0; i < OTYPCNT - 1; i++)
if (un->un_regopen[i])
return (1);
return (0);
}
static void
fd_build_user_vtoc(struct fdunit *un, struct vtoc *vtoc)
{
int i;
int nblks;
struct dk_map2 *lpart;
struct dk_map32 *lmap;
struct partition *vpart;
bzero(vtoc, sizeof (struct vtoc));
vtoc->v_bootinfo[0] = un->un_label.dkl_vtoc.v_bootinfo[0];
vtoc->v_bootinfo[1] = un->un_label.dkl_vtoc.v_bootinfo[1];
vtoc->v_bootinfo[2] = un->un_label.dkl_vtoc.v_bootinfo[2];
vtoc->v_sanity = un->un_label.dkl_vtoc.v_sanity;
vtoc->v_version = un->un_label.dkl_vtoc.v_version;
bcopy(un->un_label.dkl_vtoc.v_volume,
vtoc->v_volume, LEN_DKL_VVOL);
vtoc->v_sectorsz = DEV_BSIZE;
vtoc->v_nparts = un->un_label.dkl_vtoc.v_nparts;
bcopy(un->un_label.dkl_vtoc.v_reserved,
vtoc->v_reserved, sizeof (un->un_label.dkl_vtoc.v_reserved));
lmap = un->un_label.dkl_map;
lpart = un->un_label.dkl_vtoc.v_part;
vpart = vtoc->v_part;
nblks = (un->un_chars->fdc_nhead * un->un_chars->fdc_secptrack *
un->un_chars->fdc_sec_size) / DEV_BSIZE;
for (i = 0; i < V_NUMPAR; i++) {
vpart->p_tag = lpart->p_tag;
vpart->p_flag = lpart->p_flag;
vpart->p_start = lmap->dkl_cylno * nblks;
vpart->p_size = lmap->dkl_nblk;
lmap++;
lpart++;
vpart++;
}
bcopy(un->un_label.dkl_vtoc.v_timestamp,
vtoc->timestamp, sizeof (vtoc->timestamp));
bcopy(un->un_label.dkl_asciilabel,
vtoc->v_asciilabel, LEN_DKL_ASCII);
}
static int
fd_build_label_vtoc(struct fdunit *un, struct vtoc *vtoc)
{
struct dk_map32 *lmap;
struct dk_map2 *lpart;
struct partition *vpart;
int nblks;
int ncyl;
int i;
short sum, *sp;
if ((vtoc->v_sanity != VTOC_SANE) ||
(vtoc->v_nparts > NDKMAP) || (vtoc->v_nparts <= 0)) {
FDERRPRINT(FDEP_L1, FDEM_IOCT,
(C, "fd_build_label: sanity check on vtoc failed\n"));
return (EINVAL);
}
nblks = (un->un_chars->fdc_nhead * un->un_chars->fdc_secptrack *
un->un_chars->fdc_sec_size) / DEV_BSIZE;
vpart = vtoc->v_part;
for (i = 0; i < NDKMAP; i++) {
if ((vpart->p_start % nblks) != 0) {
return (EINVAL);
}
ncyl = vpart->p_start % nblks;
ncyl += vpart->p_size % nblks;
if ((vpart->p_size % nblks) != 0)
ncyl++;
if (ncyl > un->un_chars->fdc_ncyl) {
return (EINVAL);
}
vpart++;
}
bzero(&un->un_label, sizeof (un->un_label));
un->un_label.dkl_vtoc.v_bootinfo[0] = (uint32_t)vtoc->v_bootinfo[0];
un->un_label.dkl_vtoc.v_bootinfo[1] = (uint32_t)vtoc->v_bootinfo[1];
un->un_label.dkl_vtoc.v_bootinfo[2] = (uint32_t)vtoc->v_bootinfo[2];
un->un_label.dkl_vtoc.v_sanity = vtoc->v_sanity;
un->un_label.dkl_vtoc.v_version = vtoc->v_version;
bcopy(vtoc->v_volume, un->un_label.dkl_vtoc.v_volume, LEN_DKL_VVOL);
un->un_label.dkl_vtoc.v_nparts = vtoc->v_nparts;
bcopy(vtoc->v_reserved, un->un_label.dkl_vtoc.v_reserved,
sizeof (un->un_label.dkl_vtoc.v_reserved));
lmap = un->un_label.dkl_map;
lpart = un->un_label.dkl_vtoc.v_part;
vpart = vtoc->v_part;
for (i = 0; i < (int)vtoc->v_nparts; i++) {
lpart->p_tag = vtoc->v_part[i].p_tag;
lpart->p_flag = vtoc->v_part[i].p_flag;
lmap->dkl_cylno = vpart->p_start / nblks;
lmap->dkl_nblk = vpart->p_size;
lmap++;
lpart++;
vpart++;
}
for (i = 0; i < NDKMAP; i++) {
un->un_label.dkl_vtoc.v_timestamp[i] = vtoc->timestamp[i];
}
bcopy(vtoc->v_asciilabel, un->un_label.dkl_asciilabel, LEN_DKL_ASCII);
FDERRPRINT(FDEP_L1, FDEM_IOCT,
(C, "fd_build_label: asciilabel %s\n",
un->un_label.dkl_asciilabel));
un->un_label.dkl_magic = DKL_MAGIC;
un->un_label.dkl_pcyl = un->un_chars->fdc_ncyl;
un->un_label.dkl_nsect = (un->un_chars->fdc_secptrack *
un->un_chars->fdc_sec_size) / DEV_BSIZE;
un->un_label.dkl_ncyl = un->un_label.dkl_pcyl;
un->un_label.dkl_nhead = un->un_chars->fdc_nhead;
un->un_label.dkl_rpm = un->un_chars->fdc_medium ? 360 : 300;
un->un_label.dkl_intrlv = 1;
sum = 0;
un->un_label.dkl_cksum = 0;
sp = (short *)&un->un_label;
i = sizeof (struct dk_label)/sizeof (short);
while (i--) {
sum ^= *sp++;
}
un->un_label.dkl_cksum = sum;
return (0);
}
int
fd_isauxiodip(dev_info_t *dip)
{
if (strcmp(ddi_get_name(dip), "auxio") == 0 ||
strcmp(ddi_get_name(dip), "auxiliary-io") == 0) {
return (1);
}
return (0);
}
caddr_t
fd_getauxiova(dev_info_t *dip)
{
dev_info_t *auxdip;
caddr_t addr;
auxdip = ddi_get_child(ddi_get_parent(dip));
while (auxdip) {
if (fd_isauxiodip(auxdip))
break;
auxdip = ddi_get_next_sibling(auxdip);
}
if (auxdip == NULL)
return (NULL);
addr = (caddr_t)(uintptr_t)(caddr32_t)ddi_getprop(DDI_DEV_T_ANY,
auxdip, DDI_PROP_DONTPASS, "address", 0);
return (addr);
}
static void
set_rotational_speed(struct fdctlr *fdc, int unit)
{
int check;
int is_medium;
ASSERT(fdc->c_un->un_unit_no == unit);
if (fdc->c_fdtype & FDCTYPE_MACHIO)
return;
is_medium = fdc->c_un->un_chars->fdc_medium;
if (fdc->c_un->un_flags & FDUNIT_SET_SPEED) {
check = 1;
} else {
check = is_medium ^
((fdc->c_un->un_flags & FDUNIT_MEDIUM) ? 1 : 0);
if (check)
fdc->c_un->un_flags ^= FDUNIT_MEDIUM;
}
fdc->c_un->un_flags &= ~FDUNIT_SET_SPEED;
if (check) {
fdselect(fdc, unit, 0);
drv_usecwait(5);
if ((fdc->c_fdtype & FDCTYPE_AUXIOMASK) == FDCTYPE_SLAVIO) {
Set_dor(fdc, MEDIUM_DENSITY, is_medium);
}
if ((fdc->c_fdtype & FDCTYPE_AUXIOMASK) == FDCTYPE_CHEERIO) {
if (is_medium) {
Set_auxio(fdc, AUX_MEDIUM_DENSITY);
} else {
Set_auxio(fdc, AUX_HIGH_DENSITY);
}
}
if (is_medium) {
drv_usecwait(5);
}
fdselect(fdc, unit, 1);
FDERRPRINT(FDEP_L1, FDEM_EXEC, (C, "rotation:medium\n"));
drv_usecwait(500000);
}
}
static void
fd_media_watch(void *arg)
{
dev_t dev;
struct fdunit *un;
struct fdctlr *fdc;
int unit;
dev = (dev_t)arg;
fdc = fd_getctlr(dev);
unit = fdc->c_un->un_unit_no;
un = fdc->c_un;
mutex_enter(&fdc->c_lolock);
if (un->un_media_timeout_id == 0) {
mutex_exit(&fdc->c_lolock);
return;
}
un->un_media_state = fd_get_media_state(fdc, unit);
cv_broadcast(&fdc->c_statecv);
mutex_exit(&fdc->c_lolock);
if (un->un_media_timeout) {
un->un_media_timeout_id = timeout(fd_media_watch,
(void *)(ulong_t)dev, un->un_media_timeout);
}
}
enum dkio_state
fd_get_media_state(struct fdctlr *fdc, int unit)
{
enum dkio_state state;
ASSERT(fdc->c_un->un_unit_no == unit);
if (fdsense_chng(fdc, unit)) {
if (fdcheckdisk(fdc, unit)) {
state = DKIO_EJECTED;
} else {
state = DKIO_INSERTED;
}
} else {
state = DKIO_INSERTED;
}
return (state);
}
static int
fd_check_media(dev_t dev, enum dkio_state state)
{
struct fdunit *un;
struct fdctlr *fdc;
int unit;
FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fd_check_media: start\n"));
fdc = fd_getctlr(dev);
unit = fdc->c_un->un_unit_no;
un = fdc->c_un;
mutex_enter(&fdc->c_lolock);
CHECK_AND_WAIT_FD_STATE_SUSPENDED(fdc);
if (fdc->c_un->un_state == FD_STATE_STOPPED) {
mutex_exit(&fdc->c_lolock);
if ((pm_raise_power(fdc->c_dip, 0, PM_LEVEL_ON))
!= DDI_SUCCESS) {
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "Power change \
failed. \n"));
(void) pm_idle_component(fdc->c_dip, 0);
return (EIO);
}
mutex_enter(&fdc->c_lolock);
}
un->un_media_state = fd_get_media_state(fdc, unit);
un->un_media_timeout = drv_usectohz(fd_check_media_time);
un->un_media_timeout_id = timeout(fd_media_watch,
(void *)(ulong_t)dev, un->un_media_timeout);
while (un->un_media_state == state) {
if (cv_wait_sig(&fdc->c_statecv, &fdc->c_lolock) == 0) {
un->un_media_timeout = 0;
mutex_exit(&fdc->c_lolock);
return (EINTR);
}
}
if (un->un_media_timeout_id) {
timeout_id_t timeid = un->un_media_timeout_id;
un->un_media_timeout_id = 0;
mutex_exit(&fdc->c_lolock);
(void) untimeout(timeid);
mutex_enter(&fdc->c_lolock);
}
if (un->un_media_state == DKIO_INSERTED) {
if (fdgetlabel(fdc, unit)) {
mutex_exit(&fdc->c_lolock);
return (EIO);
}
}
mutex_exit(&fdc->c_lolock);
FDERRPRINT(FDEP_L1, FDEM_RW, (C, "fd_check_media: end\n"));
return (0);
}
static int
fd_get_media_info(struct fdunit *un, caddr_t buf, int flag)
{
struct dk_minfo media_info;
int err = 0;
media_info.dki_media_type = DK_FLOPPY;
media_info.dki_lbsize = un->un_chars->fdc_sec_size;
media_info.dki_capacity = un->un_chars->fdc_ncyl *
un->un_chars->fdc_secptrack * un->un_chars->fdc_nhead;
if (ddi_copyout((caddr_t)&media_info, buf,
sizeof (struct dk_minfo), flag))
err = EFAULT;
return (err);
}
static int
fd_power(dev_info_t *dip, int component, int level)
{
struct fdctlr *fdc;
int instance;
int rval;
if ((level < PM_LEVEL_OFF) || (level > PM_LEVEL_ON) ||
(component != 0)) {
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
fdc = fd_getctlr(instance << FDINSTSHIFT);
if (fdc->c_un == NULL)
return (DDI_FAILURE);
if (level == PM_LEVEL_OFF) {
rval = fd_pm_lower_power(fdc);
}
if (level == PM_LEVEL_ON) {
rval = fd_pm_raise_power(fdc);
}
return (rval);
}
static int
fd_pm_lower_power(struct fdctlr *fdc)
{
mutex_enter(&fdc->c_lolock);
if ((fdc->c_un->un_state == FD_STATE_SUSPENDED) ||
(fdc->c_un->un_state == FD_STATE_STOPPED)) {
mutex_exit(&fdc->c_lolock);
return (DDI_SUCCESS);
}
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "fd_pm_lower_power called\n"));
if (fdc->c_flags & FDCFLG_BUSY) {
FDERRPRINT(FDEP_L2, FDEM_PWR, (C, "fd_pm_lower_power : \
controller is busy.\n"));
mutex_exit(&fdc->c_lolock);
return (DDI_FAILURE);
}
fdc->c_un->un_state = FD_STATE_STOPPED;
mutex_exit(&fdc->c_lolock);
return (DDI_SUCCESS);
}
static int
fd_pm_raise_power(struct fdctlr *fdc)
{
struct fdunit *un = fdc->c_un;
int unit;
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "fd_pm_raise_power called\n"));
mutex_enter(&fdc->c_lolock);
fdgetcsb(fdc);
if (fdc->c_fdtype & FDCTYPE_DMA) {
mutex_enter(&fdc->c_hilock);
reset_dma_controller(fdc);
set_dma_control_register(fdc, DCSR_INIT_BITS);
mutex_exit(&fdc->c_hilock);
}
fdc->c_un->un_flags |= FDUNIT_SET_SPEED;
(void) fdreset(fdc);
unit = fdc->c_un->un_unit_no;
if (fdrecalseek(fdc, unit, -1, 0) != 0) {
FDERRPRINT(FDEP_L1, FDEM_PWR, (C, "raise_power : recalibrate \
failed\n"));
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
return (DDI_FAILURE);
}
fdselect(fdc, unit, 0);
un->un_state = FD_STATE_NORMAL;
fdretcsb(fdc);
mutex_exit(&fdc->c_lolock);
return (DDI_SUCCESS);
}
static void
create_pm_components(dev_info_t *dip)
{
char *un_pm_comp[] = { "NAME=spindle-motor", "0=off", "1=on"};
if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
"pm-components", un_pm_comp, 3) == DDI_PROP_SUCCESS) {
(void) pm_raise_power(dip, 0, PM_LEVEL_ON);
}
}
static void
set_data_count_register(struct fdctlr *fdc, uint32_t count)
{
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
struct cheerio_dma_reg *dma_reg;
dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dbcr, count);
} else if (fdc->c_fdtype & FDCTYPE_SB) {
struct sb_dma_reg *dma_reg;
count = count - 1;
dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
switch (fdc->sb_dma_channel) {
case 0 :
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_0WCNT],
count & 0xFFFF);
break;
case 1 :
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_1WCNT],
count & 0xFFFF);
break;
case 2 :
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_2WCNT],
count & 0xFFFF);
break;
case 3 :
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_3WCNT],
count & 0xFFFF);
break;
default :
FDERRPRINT(FDEP_L3, FDEM_SDMA,
(C, "set_data_count: wrong channel %x\n",
fdc->sb_dma_channel));
break;
}
}
}
static uint32_t
get_data_count_register(struct fdctlr *fdc)
{
uint32_t retval = 0;
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
struct cheerio_dma_reg *dma_reg;
dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
retval = ddi_get32(fdc->c_handlep_dma, &dma_reg->fdc_dbcr);
} else if (fdc->c_fdtype & FDCTYPE_SB) {
struct sb_dma_reg *dma_reg;
dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
switch (fdc->sb_dma_channel) {
case 0 :
retval = ddi_get16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_0WCNT]);
break;
case 1 :
retval = ddi_get16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_1WCNT]);
break;
case 2 :
retval = ddi_get16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_2WCNT]);
break;
case 3 :
retval = ddi_get16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_3WCNT]);
break;
default :
FDERRPRINT(FDEP_L3, FDEM_SDMA,
(C, "get_data_count: wrong channel %x\n",
fdc->sb_dma_channel));
break;
}
retval = (uint32_t)((uint16_t)(retval +1));
}
return (retval);
}
static void
reset_dma_controller(struct fdctlr *fdc)
{
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
struct cheerio_dma_reg *dma_reg;
dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr, DCSR_RESET);
while (get_dma_control_register(fdc) & DCSR_CYC_PEND)
;
ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr, 0);
} else if (fdc->c_fdtype & FDCTYPE_SB) {
struct sb_dma_reg *dma_reg;
dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
ddi_put8(fdc->c_handlep_dma, &dma_reg->sb_dma_regs[DMAC1_MASK],
(fdc->sb_dma_channel & 0x3));
}
}
static uint32_t
get_dma_control_register(struct fdctlr *fdc)
{
uint32_t retval = 0;
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
struct cheerio_dma_reg *dma_reg;
dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
retval = ddi_get32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr);
}
return (retval);
}
static void
set_data_address_register(struct fdctlr *fdc, uint32_t address)
{
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
struct cheerio_dma_reg *dma_reg;
dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dacr, address);
} else if (fdc->c_fdtype & FDCTYPE_SB) {
struct sb_dma_reg *dma_reg;
dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
switch (fdc->sb_dma_channel) {
case 0 :
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_0PAGE],
(address & 0xFF0000) >>16);
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_0HPG],
(address & 0xFF000000) >>24);
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_0ADR],
address & 0xFFFF);
break;
case 1 :
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_1PAGE],
(address & 0xFF0000) >>16);
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_1HPG],
(address & 0xFF000000) >>24);
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_1ADR],
address & 0xFFFF);
break;
case 2 :
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_2PAGE],
(address & 0xFF0000) >>16);
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_2HPG],
(address & 0xFF000000) >>24);
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_2ADR],
address & 0xFFFF);
break;
case 3 :
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_3PAGE],
(address & 0xFF0000) >>16);
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMA_3HPG],
(address & 0xFF000000) >>24);
ddi_put16(fdc->c_handlep_dma,
(ushort_t *)&dma_reg->sb_dma_regs[DMA_3ADR],
address & 0xFFFF);
break;
default :
FDERRPRINT(FDEP_L3, FDEM_SDMA,
(C, "set_data_address: wrong channel %x\n",
fdc->sb_dma_channel));
break;
}
}
}
static void
set_dma_mode(struct fdctlr *fdc, int val)
{
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
struct cheerio_dma_reg *dma_reg;
dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
if (val == CSB_READ)
ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr,
DCSR_INIT_BITS|DCSR_WRITE);
else
ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr,
DCSR_INIT_BITS);
} else if (fdc->c_fdtype & FDCTYPE_SB) {
uint8_t mode_reg_val, chn_mask;
struct sb_dma_reg *dma_reg;
dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
if (val == CSB_READ) {
mode_reg_val = fdc->sb_dma_channel | DMAMODE_READ
| DMAMODE_SINGLE;
} else {
mode_reg_val = fdc->sb_dma_channel | DMAMODE_WRITE
| DMAMODE_SINGLE;
}
ddi_put8(fdc->c_handlep_dma, &dma_reg->sb_dma_regs[DMAC1_MODE],
mode_reg_val);
chn_mask = 1 << (fdc->sb_dma_channel & 0x3);
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMAC1_ALLMASK], ~chn_mask);
fdc->sb_dma_lock = 1;
}
}
static void
set_dma_control_register(struct fdctlr *fdc, uint32_t val)
{
if (fdc->c_fdtype & FDCTYPE_CHEERIO) {
struct cheerio_dma_reg *dma_reg;
dma_reg = (struct cheerio_dma_reg *)fdc->c_dma_regs;
ddi_put32(fdc->c_handlep_dma, &dma_reg->fdc_dcsr, val);
}
}
static void
release_sb_dma(struct fdctlr *fdc)
{
struct sb_dma_reg *dma_reg;
dma_reg = (struct sb_dma_reg *)fdc->c_dma_regs;
ddi_put8(fdc->c_handlep_dma,
&dma_reg->sb_dma_regs[DMAC1_ALLMASK], 0);
fdc->sb_dma_lock = 0;
}
static void
quiesce_fd_interrupt(struct fdctlr *fdc)
{
if (fdc->c_fdtype & FDCTYPE_SB) {
ddi_put8(fdc->c_handlep_cont, ((uint8_t *)fdc->c_dor),
0x0);
drv_usecwait(200);
ddi_put8(fdc->c_handlep_cont, ((uint8_t *)fdc->c_dor),
0xC);
drv_usecwait(200);
Set_Fifo(fdc, 0xE6);
drv_usecwait(200);
}
}