#include <sys/scsi/scsi.h>
#include <sys/dktp/cm.h>
#include <sys/dktp/quetypes.h>
#include <sys/dktp/queue.h>
#include <sys/dktp/fctypes.h>
#include <sys/dktp/flowctrl.h>
#include <sys/dktp/cmdev.h>
#include <sys/dkio.h>
#include <sys/dktp/tgdk.h>
#include <sys/dktp/dadk.h>
#include <sys/dktp/bbh.h>
#include <sys/dktp/altsctr.h>
#include <sys/dktp/cmdk.h>
#include <sys/stat.h>
#include <sys/vtoc.h>
#include <sys/file.h>
#include <sys/dktp/dadkio.h>
#include <sys/aio_req.h>
#include <sys/cmlb.h>
#ifdef CMDK_DEBUG
#define DENT 0x0001
#define DIO 0x0002
static int cmdk_debug = DIO;
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define PARTITION0_INDEX (NDKMAP + 0)
#define DKTP_DATA (dkp->dk_tgobjp)->tg_data
#define DKTP_EXT (dkp->dk_tgobjp)->tg_ext
void *cmdk_state;
static kmutex_t cmdk_attach_mutex;
static int cmdk_max_instance = 0;
static int cmdk_indump;
static int cmdk_create_obj(dev_info_t *dip, struct cmdk *dkp);
static void cmdk_destroy_obj(dev_info_t *dip, struct cmdk *dkp);
static void cmdkmin(struct buf *bp);
static int cmdkrw(dev_t dev, struct uio *uio, int flag);
static int cmdkarw(dev_t dev, struct aio_req *aio, int flag);
static void cmdk_bbh_reopen(struct cmdk *dkp);
static opaque_t cmdk_bbh_gethandle(opaque_t bbh_data, struct buf *bp);
static bbh_cookie_t cmdk_bbh_htoc(opaque_t bbh_data, opaque_t handle);
static void cmdk_bbh_freehandle(opaque_t bbh_data, opaque_t handle);
static void cmdk_bbh_close(struct cmdk *dkp);
static void cmdk_bbh_setalts_idx(struct cmdk *dkp);
static int cmdk_bbh_bsearch(struct alts_ent *buf, int cnt, daddr32_t key);
static struct bbh_objops cmdk_bbh_ops = {
nulldev,
nulldev,
cmdk_bbh_gethandle,
cmdk_bbh_htoc,
cmdk_bbh_freehandle,
0, 0
};
static int cmdkopen(dev_t *dev_p, int flag, int otyp, cred_t *credp);
static int cmdkclose(dev_t dev, int flag, int otyp, cred_t *credp);
static int cmdkstrategy(struct buf *bp);
static int cmdkdump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk);
static int cmdkioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int cmdkread(dev_t dev, struct uio *uio, cred_t *credp);
static int cmdkwrite(dev_t dev, struct uio *uio, cred_t *credp);
static int cmdk_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);
static int cmdkaread(dev_t dev, struct aio_req *aio, cred_t *credp);
static int cmdkawrite(dev_t dev, struct aio_req *aio, cred_t *credp);
static struct cb_ops cmdk_cb_ops = {
cmdkopen,
cmdkclose,
cmdkstrategy,
nodev,
cmdkdump,
cmdkread,
cmdkwrite,
cmdkioctl,
nodev,
nodev,
nodev,
nochpoll,
cmdk_prop_op,
0,
D_64BIT | D_MP | D_NEW,
CB_REV,
cmdkaread,
cmdkawrite
};
static int cmdkinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result);
static int cmdkprobe(dev_info_t *dip);
static int cmdkattach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int cmdkdetach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static void cmdk_setup_pm(dev_info_t *dip, struct cmdk *dkp);
static int cmdkresume(dev_info_t *dip);
static int cmdksuspend(dev_info_t *dip);
static int cmdkpower(dev_info_t *dip, int component, int level);
struct dev_ops cmdk_ops = {
DEVO_REV,
0,
cmdkinfo,
nulldev,
cmdkprobe,
cmdkattach,
cmdkdetach,
nodev,
&cmdk_cb_ops,
(struct bus_ops *)0,
cmdkpower,
ddi_quiesce_not_needed,
};
#include <sys/modctl.h>
static struct modldrv modldrv = {
&mod_driverops,
"Common Direct Access Disk",
&cmdk_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
static int cmdk_lb_rdwr(dev_info_t *dip, uchar_t cmd, void *bufaddr,
diskaddr_t start, size_t length, void *tg_cookie);
static int cmdk_lb_getinfo(dev_info_t *dip, int cmd, void *arg,
void *tg_cookie);
static void cmdk_devid_setup(struct cmdk *dkp);
static int cmdk_devid_modser(struct cmdk *dkp);
static int cmdk_get_modser(struct cmdk *dkp, int ioccmd, char *buf, int len);
static int cmdk_devid_fabricate(struct cmdk *dkp);
static int cmdk_devid_read(struct cmdk *dkp);
static cmlb_tg_ops_t cmdk_lb_ops = {
TG_DK_OPS_VERSION_1,
cmdk_lb_rdwr,
cmdk_lb_getinfo
};
static boolean_t
cmdk_isopen(struct cmdk *dkp, dev_t dev)
{
int part, otyp;
ulong_t partbit;
ASSERT(MUTEX_HELD((&dkp->dk_mutex)));
part = CMDKPART(dev);
partbit = 1 << part;
if (dkp->dk_open_lyr[part] != 0)
return (B_TRUE);
for (otyp = 0; otyp < OTYPCNT; otyp++)
if (dkp->dk_open_reg[otyp] & partbit)
return (B_TRUE);
return (B_FALSE);
}
int
_init(void)
{
int rval;
if (rval = ddi_soft_state_init(&cmdk_state, sizeof (struct cmdk), 7))
return (rval);
mutex_init(&cmdk_attach_mutex, NULL, MUTEX_DRIVER, NULL);
if ((rval = mod_install(&modlinkage)) != 0) {
mutex_destroy(&cmdk_attach_mutex);
ddi_soft_state_fini(&cmdk_state);
}
return (rval);
}
int
_fini(void)
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
cmdkprobe(dev_info_t *dip)
{
int instance;
int status;
struct cmdk *dkp;
instance = ddi_get_instance(dip);
if (ddi_get_soft_state(cmdk_state, instance))
return (DDI_PROBE_PARTIAL);
if (ddi_soft_state_zalloc(cmdk_state, instance) != DDI_SUCCESS)
return (DDI_PROBE_PARTIAL);
if ((dkp = ddi_get_soft_state(cmdk_state, instance)) == NULL)
return (DDI_PROBE_PARTIAL);
mutex_init(&dkp->dk_mutex, NULL, MUTEX_DRIVER, NULL);
rw_init(&dkp->dk_bbh_mutex, NULL, RW_DRIVER, NULL);
dkp->dk_dip = dip;
mutex_enter(&dkp->dk_mutex);
dkp->dk_dev = makedevice(ddi_driver_major(dip),
ddi_get_instance(dip) << CMDK_UNITSHF);
if (cmdk_create_obj(dip, dkp) != DDI_SUCCESS) {
mutex_exit(&dkp->dk_mutex);
mutex_destroy(&dkp->dk_mutex);
rw_destroy(&dkp->dk_bbh_mutex);
ddi_soft_state_free(cmdk_state, instance);
return (DDI_PROBE_PARTIAL);
}
status = dadk_probe(DKTP_DATA, KM_NOSLEEP);
if (status != DDI_PROBE_SUCCESS) {
cmdk_destroy_obj(dip, dkp);
mutex_exit(&dkp->dk_mutex);
mutex_destroy(&dkp->dk_mutex);
rw_destroy(&dkp->dk_bbh_mutex);
ddi_soft_state_free(cmdk_state, instance);
return (status);
}
mutex_exit(&dkp->dk_mutex);
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdkprobe: instance= %d name= `%s`\n",
instance, ddi_get_name_addr(dip));
#endif
return (status);
}
static int
cmdkattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
struct cmdk *dkp;
char *node_type;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (cmdkresume(dip));
default:
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (DDI_FAILURE);
dkp->dk_pm_level = CMDK_SPINDLE_UNINIT;
mutex_init(&dkp->dk_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_enter(&dkp->dk_mutex);
(void) dadk_attach(DKTP_DATA);
node_type = (DKTP_EXT->tg_nodetype);
if (dadk_open(DKTP_DATA, 0) != DDI_SUCCESS)
goto fail2;
dkp->dk_flag |= CMDK_TGDK_OPEN;
cmlb_alloc_handle((cmlb_handle_t *)&dkp->dk_cmlbhandle);
if (cmlb_attach(dip,
&cmdk_lb_ops,
DTYPE_DIRECT,
B_FALSE,
B_FALSE,
node_type,
CMLB_CREATE_ALTSLICE_VTOC_16_DTYPE_DIRECT,
dkp->dk_cmlbhandle,
0) != 0)
goto fail1;
(void) cmlb_validate(dkp->dk_cmlbhandle, 0, 0);
cmdk_bbh_reopen(dkp);
cmdk_devid_setup(dkp);
mutex_enter(&cmdk_attach_mutex);
if (instance > cmdk_max_instance)
cmdk_max_instance = instance;
mutex_exit(&cmdk_attach_mutex);
mutex_exit(&dkp->dk_mutex);
(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
DDI_KERNEL_IOCTL, NULL, 0);
ddi_report_dev(dip);
mutex_init(&dkp->dk_pm_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&dkp->dk_suspend_cv, NULL, CV_DRIVER, NULL);
cmdk_setup_pm(dip, dkp);
return (DDI_SUCCESS);
fail1:
cmlb_free_handle(&dkp->dk_cmlbhandle);
(void) dadk_close(DKTP_DATA);
fail2:
cmdk_destroy_obj(dip, dkp);
rw_destroy(&dkp->dk_bbh_mutex);
mutex_exit(&dkp->dk_mutex);
mutex_destroy(&dkp->dk_mutex);
ddi_soft_state_free(cmdk_state, instance);
return (DDI_FAILURE);
}
static int
cmdkdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
struct cmdk *dkp;
int instance;
int max_instance;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (cmdksuspend(dip));
default:
#ifdef CMDK_DEBUG
if (cmdk_debug & DIO) {
PRF("cmdkdetach: cmd = %d unknown\n", cmd);
}
#endif
return (DDI_FAILURE);
}
mutex_enter(&cmdk_attach_mutex);
max_instance = cmdk_max_instance;
mutex_exit(&cmdk_attach_mutex);
for (instance = 0; instance < max_instance; instance++) {
dkp = ddi_get_soft_state(cmdk_state, instance);
if (!dkp)
continue;
if (dkp->dk_flag & CMDK_OPEN)
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (DDI_SUCCESS);
mutex_enter(&dkp->dk_mutex);
if (dkp->dk_flag & CMDK_TGDK_OPEN) {
dkp->dk_flag &= ~CMDK_TGDK_OPEN;
(void) dadk_close(DKTP_DATA);
}
cmlb_detach(dkp->dk_cmlbhandle, 0);
cmlb_free_handle(&dkp->dk_cmlbhandle);
ddi_prop_remove_all(dip);
cmdk_destroy_obj(dip, dkp);
if (dkp->dk_devid) {
ddi_devid_free(dkp->dk_devid);
dkp->dk_devid = NULL;
}
mutex_exit(&dkp->dk_mutex);
mutex_destroy(&dkp->dk_mutex);
rw_destroy(&dkp->dk_bbh_mutex);
mutex_destroy(&dkp->dk_pm_mutex);
cv_destroy(&dkp->dk_suspend_cv);
ddi_soft_state_free(cmdk_state, instance);
return (DDI_SUCCESS);
}
static int
cmdkinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev = (dev_t)arg;
int instance;
struct cmdk *dkp;
#ifdef lint
dip = dip;
#endif
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdkinfo: call\n");
#endif
instance = CMDKUNIT(dev);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (DDI_FAILURE);
*result = (void *) dkp->dk_dip;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(intptr_t)instance;
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
cmdk_setup_pm(dev_info_t *dip, struct cmdk *dkp)
{
char *pm_comp[] = { "NAME=cmdk", "0=off", "1=on", NULL };
(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
"pm-hardware-state", "needs-suspend-resume");
if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
"pm-components", pm_comp, 3) == DDI_PROP_SUCCESS) {
if (pm_raise_power(dip, 0, CMDK_SPINDLE_ON) == DDI_SUCCESS) {
mutex_enter(&dkp->dk_pm_mutex);
dkp->dk_pm_level = CMDK_SPINDLE_ON;
dkp->dk_pm_is_enabled = 1;
mutex_exit(&dkp->dk_pm_mutex);
} else {
mutex_enter(&dkp->dk_pm_mutex);
dkp->dk_pm_level = CMDK_SPINDLE_OFF;
dkp->dk_pm_is_enabled = 0;
mutex_exit(&dkp->dk_pm_mutex);
}
} else {
mutex_enter(&dkp->dk_pm_mutex);
dkp->dk_pm_level = CMDK_SPINDLE_UNINIT;
dkp->dk_pm_is_enabled = 0;
mutex_exit(&dkp->dk_pm_mutex);
}
}
static int
cmdksuspend(dev_info_t *dip)
{
struct cmdk *dkp;
int instance;
clock_t count = 0;
instance = ddi_get_instance(dip);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (DDI_FAILURE);
mutex_enter(&dkp->dk_mutex);
if (dkp->dk_flag & CMDK_SUSPEND) {
mutex_exit(&dkp->dk_mutex);
return (DDI_SUCCESS);
}
dkp->dk_flag |= CMDK_SUSPEND;
while (dadk_getcmds(DKTP_DATA) != 0) {
delay(drv_usectohz(1000000));
if (count > 60) {
dkp->dk_flag &= ~CMDK_SUSPEND;
cv_broadcast(&dkp->dk_suspend_cv);
mutex_exit(&dkp->dk_mutex);
return (DDI_FAILURE);
}
count++;
}
mutex_exit(&dkp->dk_mutex);
return (DDI_SUCCESS);
}
static int
cmdkresume(dev_info_t *dip)
{
struct cmdk *dkp;
int instance;
instance = ddi_get_instance(dip);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (DDI_FAILURE);
mutex_enter(&dkp->dk_mutex);
if (!(dkp->dk_flag & CMDK_SUSPEND)) {
mutex_exit(&dkp->dk_mutex);
return (DDI_FAILURE);
}
dkp->dk_pm_level = CMDK_SPINDLE_ON;
dkp->dk_flag &= ~CMDK_SUSPEND;
cv_broadcast(&dkp->dk_suspend_cv);
mutex_exit(&dkp->dk_mutex);
return (DDI_SUCCESS);
}
static int
cmdkpower(dev_info_t *dip, int component, int level)
{
struct cmdk *dkp;
int instance;
instance = ddi_get_instance(dip);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)) ||
component != 0 || level > CMDK_SPINDLE_ON ||
level < CMDK_SPINDLE_OFF) {
return (DDI_FAILURE);
}
mutex_enter(&dkp->dk_pm_mutex);
if (dkp->dk_pm_is_enabled && dkp->dk_pm_level == level) {
mutex_exit(&dkp->dk_pm_mutex);
return (DDI_SUCCESS);
}
mutex_exit(&dkp->dk_pm_mutex);
if ((level == CMDK_SPINDLE_OFF) &&
(dadk_getcmds(DKTP_DATA) != 0)) {
return (DDI_FAILURE);
}
mutex_enter(&dkp->dk_pm_mutex);
dkp->dk_pm_level = level;
mutex_exit(&dkp->dk_pm_mutex);
return (DDI_SUCCESS);
}
static int
cmdk_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 cmdk *dkp;
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdk_prop_op: call\n");
#endif
dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
if (dkp == NULL)
return (ddi_prop_op(dev, dip, prop_op, mod_flags,
name, valuep, lengthp));
return (cmlb_prop_op(dkp->dk_cmlbhandle,
dev, dip, prop_op, mod_flags, name, valuep, lengthp,
CMDKPART(dev), NULL));
}
static int
cmdkdump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk)
{
int instance;
struct cmdk *dkp;
diskaddr_t p_lblksrt;
diskaddr_t p_lblkcnt;
struct buf local;
struct buf *bp;
#ifdef CMDK_DEBUG
if (cmdk_debug & DENT)
PRF("cmdkdump: call\n");
#endif
instance = CMDKUNIT(dev);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)) || (blkno < 0))
return (ENXIO);
if (cmlb_partinfo(
dkp->dk_cmlbhandle,
CMDKPART(dev),
&p_lblkcnt,
&p_lblksrt,
NULL,
NULL,
0)) {
return (ENXIO);
}
if ((blkno+nblk) > p_lblkcnt)
return (EINVAL);
cmdk_indump = 1;
bp = &local;
bzero(bp, sizeof (*bp));
bp->b_flags = B_BUSY;
bp->b_un.b_addr = addr;
bp->b_bcount = nblk << SCTRSHFT;
SET_BP_SEC(bp, ((ulong_t)(p_lblksrt + blkno)));
(void) dadk_dump(DKTP_DATA, bp);
return (bp->b_error);
}
static int
rwcmd_copyin(struct dadkio_rwcmd *rwcmdp, caddr_t inaddr, int flag)
{
switch (ddi_model_convert_from(flag)) {
case DDI_MODEL_ILP32: {
struct dadkio_rwcmd32 cmd32;
if (ddi_copyin(inaddr, &cmd32,
sizeof (struct dadkio_rwcmd32), flag)) {
return (EFAULT);
}
rwcmdp->cmd = cmd32.cmd;
rwcmdp->flags = cmd32.flags;
rwcmdp->blkaddr = (blkaddr_t)cmd32.blkaddr;
rwcmdp->buflen = cmd32.buflen;
rwcmdp->bufaddr = (caddr_t)(intptr_t)cmd32.bufaddr;
bzero(&rwcmdp->status, sizeof (rwcmdp->status));
break;
}
case DDI_MODEL_NONE: {
if (ddi_copyin(inaddr, rwcmdp,
sizeof (struct dadkio_rwcmd), flag)) {
return (EFAULT);
}
}
}
return (0);
}
static int
rwcmd_copyout(struct dadkio_rwcmd *rwcmdp, caddr_t outaddr, int flag)
{
switch (ddi_model_convert_from(flag)) {
case DDI_MODEL_ILP32: {
struct dadkio_rwcmd32 cmd32;
cmd32.cmd = rwcmdp->cmd;
cmd32.flags = rwcmdp->flags;
cmd32.blkaddr = rwcmdp->blkaddr;
cmd32.buflen = rwcmdp->buflen;
ASSERT64(((uintptr_t)rwcmdp->bufaddr >> 32) == 0);
cmd32.bufaddr = (caddr32_t)(uintptr_t)rwcmdp->bufaddr;
cmd32.status.status = rwcmdp->status.status;
cmd32.status.resid = rwcmdp->status.resid;
cmd32.status.failed_blk_is_valid =
rwcmdp->status.failed_blk_is_valid;
cmd32.status.failed_blk = rwcmdp->status.failed_blk;
cmd32.status.fru_code_is_valid =
rwcmdp->status.fru_code_is_valid;
cmd32.status.fru_code = rwcmdp->status.fru_code;
bcopy(rwcmdp->status.add_error_info,
cmd32.status.add_error_info, DADKIO_ERROR_INFO_LEN);
if (ddi_copyout(&cmd32, outaddr,
sizeof (struct dadkio_rwcmd32), flag))
return (EFAULT);
break;
}
case DDI_MODEL_NONE: {
if (ddi_copyout(rwcmdp, outaddr,
sizeof (struct dadkio_rwcmd), flag))
return (EFAULT);
}
}
return (0);
}
static int
cmdkioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp)
{
int instance;
struct scsi_device *devp;
struct cmdk *dkp;
char data[NBPSCTR];
instance = CMDKUNIT(dev);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (ENXIO);
mutex_enter(&dkp->dk_mutex);
while (dkp->dk_flag & CMDK_SUSPEND) {
cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
}
mutex_exit(&dkp->dk_mutex);
bzero(data, sizeof (data));
switch (cmd) {
case DKIOCGMEDIAINFO: {
struct dk_minfo media_info;
struct tgdk_geom phyg;
(void) dadk_getphygeom(DKTP_DATA, &phyg);
media_info.dki_lbsize = phyg.g_secsiz;
media_info.dki_capacity = phyg.g_cap;
media_info.dki_media_type = DK_FIXED_DISK;
if (ddi_copyout(&media_info, (void *)arg,
sizeof (struct dk_minfo), flag)) {
return (EFAULT);
} else {
return (0);
}
}
case DKIOCINFO: {
struct dk_cinfo *info = (struct dk_cinfo *)data;
info->dki_ctype = (DKTP_EXT->tg_ctype);
info->dki_cnum = ddi_get_instance(ddi_get_parent(dkp->dk_dip));
(void) strcpy(info->dki_cname,
ddi_get_name(ddi_get_parent(dkp->dk_dip)));
info->dki_unit = ddi_get_instance(dkp->dk_dip);
devp = ddi_get_driver_private(dkp->dk_dip);
info->dki_slave = (CMDEV_TARG(devp)<<3) | CMDEV_LUN(devp);
(void) strcpy(info->dki_dname, ddi_driver_name(dkp->dk_dip));
info->dki_flags = DKI_FMTVOL;
info->dki_partition = CMDKPART(dev);
info->dki_maxtransfer = maxphys / DEV_BSIZE;
info->dki_addr = 1;
info->dki_space = 0;
info->dki_prio = 0;
info->dki_vec = 0;
if (ddi_copyout(data, (void *)arg, sizeof (*info), flag))
return (EFAULT);
else
return (0);
}
case DKIOCSTATE: {
int state;
int rval;
diskaddr_t p_lblksrt;
diskaddr_t p_lblkcnt;
if (ddi_copyin((void *)arg, &state, sizeof (int), flag))
return (EFAULT);
if (rval = dadk_check_media(DKTP_DATA, &state))
return (rval);
if (state == DKIO_INSERTED) {
if (cmlb_validate(dkp->dk_cmlbhandle, 0, 0) != 0)
return (ENXIO);
if (cmlb_partinfo(dkp->dk_cmlbhandle, CMDKPART(dev),
&p_lblkcnt, &p_lblksrt, NULL, NULL, 0))
return (ENXIO);
if (p_lblkcnt <= 0)
return (ENXIO);
}
if (ddi_copyout(&state, (caddr_t)arg, sizeof (int), flag))
return (EFAULT);
return (0);
}
case DKIOCREMOVABLE: {
int i;
i = (DKTP_EXT->tg_rmb) ? 1 : 0;
if (ddi_copyout(&i, (caddr_t)arg, sizeof (int), flag))
return (EFAULT);
return (0);
}
case DKIOCADDBAD:
cmdk_bbh_reopen(dkp);
return (0);
case DKIOCG_PHYGEOM:
case DKIOCG_VIRTGEOM:
case DKIOCGGEOM:
case DKIOCSGEOM:
case DKIOCGAPART:
case DKIOCSAPART:
case DKIOCGVTOC:
case DKIOCSVTOC:
case DKIOCPARTINFO:
case DKIOCGEXTVTOC:
case DKIOCSEXTVTOC:
case DKIOCEXTPARTINFO:
case DKIOCGMBOOT:
case DKIOCSMBOOT:
case DKIOCGETEFI:
case DKIOCSETEFI:
case DKIOCPARTITION:
case DKIOCSETEXTPART:
{
int rc;
rc = cmlb_ioctl(dkp->dk_cmlbhandle, dev, cmd, arg, flag,
credp, rvalp, 0);
if (cmd == DKIOCSVTOC || cmd == DKIOCSEXTVTOC)
cmdk_devid_setup(dkp);
return (rc);
}
case DIOCTL_RWCMD: {
struct dadkio_rwcmd *rwcmdp;
int status;
rwcmdp = kmem_alloc(sizeof (struct dadkio_rwcmd), KM_SLEEP);
status = rwcmd_copyin(rwcmdp, (caddr_t)arg, flag);
if (status == 0) {
bzero(&(rwcmdp->status), sizeof (struct dadkio_status));
status = dadk_ioctl(DKTP_DATA,
dev,
cmd,
(uintptr_t)rwcmdp,
flag,
credp,
rvalp);
}
if (status == 0)
status = rwcmd_copyout(rwcmdp, (caddr_t)arg, flag);
kmem_free(rwcmdp, sizeof (struct dadkio_rwcmd));
return (status);
}
default:
return (dadk_ioctl(DKTP_DATA,
dev,
cmd,
arg,
flag,
credp,
rvalp));
}
}
static int
cmdkclose(dev_t dev, int flag, int otyp, cred_t *credp)
{
int part;
ulong_t partbit;
int instance;
struct cmdk *dkp;
int lastclose = 1;
int i;
instance = CMDKUNIT(dev);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)) ||
(otyp >= OTYPCNT))
return (ENXIO);
mutex_enter(&dkp->dk_mutex);
ASSERT(cmdk_isopen(dkp, dev));
if (!(dkp->dk_flag & CMDK_OPEN)) {
mutex_exit(&dkp->dk_mutex);
return (ENXIO);
}
while (dkp->dk_flag & CMDK_SUSPEND) {
cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
}
part = CMDKPART(dev);
partbit = 1 << part;
if (otyp == OTYP_LYR) {
ASSERT(dkp->dk_open_lyr[part] > 0);
if (dkp->dk_open_lyr[part])
dkp->dk_open_lyr[part]--;
} else {
ASSERT((dkp->dk_open_reg[otyp] & partbit) != 0);
dkp->dk_open_reg[otyp] &= ~partbit;
}
dkp->dk_open_exl &= ~partbit;
for (i = 0; i < CMDK_MAXPART; i++)
if (dkp->dk_open_lyr[i] != 0) {
lastclose = 0;
break;
}
if (lastclose)
for (i = 0; i < OTYPCNT; i++)
if (dkp->dk_open_reg[i] != 0) {
lastclose = 0;
break;
}
mutex_exit(&dkp->dk_mutex);
if (lastclose)
cmlb_invalidate(dkp->dk_cmlbhandle, 0);
return (DDI_SUCCESS);
}
static int
cmdkopen(dev_t *dev_p, int flag, int otyp, cred_t *credp)
{
dev_t dev = *dev_p;
int part;
ulong_t partbit;
int instance;
struct cmdk *dkp;
diskaddr_t p_lblksrt;
diskaddr_t p_lblkcnt;
int i;
int nodelay;
instance = CMDKUNIT(dev);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (ENXIO);
if (otyp >= OTYPCNT)
return (EINVAL);
mutex_enter(&dkp->dk_mutex);
while (dkp->dk_flag & CMDK_SUSPEND) {
cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
}
mutex_exit(&dkp->dk_mutex);
part = CMDKPART(dev);
partbit = 1 << part;
nodelay = (flag & (FNDELAY | FNONBLOCK));
mutex_enter(&dkp->dk_mutex);
if (cmlb_validate(dkp->dk_cmlbhandle, 0, 0) != 0) {
if (!nodelay) {
mutex_exit(&dkp->dk_mutex);
return (ENXIO);
}
} else if (cmlb_partinfo(dkp->dk_cmlbhandle, part, &p_lblkcnt,
&p_lblksrt, NULL, NULL, 0) == 0) {
if (p_lblkcnt <= 0 && (!nodelay || otyp != OTYP_CHR)) {
mutex_exit(&dkp->dk_mutex);
return (ENXIO);
}
} else {
if (!nodelay) {
mutex_exit(&dkp->dk_mutex);
return (ENXIO);
}
}
if ((DKTP_EXT->tg_rdonly) && (flag & FWRITE)) {
mutex_exit(&dkp->dk_mutex);
return (EROFS);
}
if (dkp->dk_open_exl & partbit)
goto excl_open_fail;
if (flag & FEXCL) {
if (dkp->dk_open_lyr[part])
goto excl_open_fail;
for (i = 0; i < OTYPCNT; i++) {
if (dkp->dk_open_reg[i] & partbit)
goto excl_open_fail;
}
}
dkp->dk_flag |= CMDK_OPEN;
if (otyp == OTYP_LYR)
dkp->dk_open_lyr[part]++;
else
dkp->dk_open_reg[otyp] |= partbit;
if (flag & FEXCL)
dkp->dk_open_exl |= partbit;
mutex_exit(&dkp->dk_mutex);
return (DDI_SUCCESS);
excl_open_fail:
mutex_exit(&dkp->dk_mutex);
return (EBUSY);
}
static int
cmdkread(dev_t dev, struct uio *uio, cred_t *credp)
{
return (cmdkrw(dev, uio, B_READ));
}
static int
cmdkaread(dev_t dev, struct aio_req *aio, cred_t *credp)
{
return (cmdkarw(dev, aio, B_READ));
}
static int
cmdkwrite(dev_t dev, struct uio *uio, cred_t *credp)
{
return (cmdkrw(dev, uio, B_WRITE));
}
static int
cmdkawrite(dev_t dev, struct aio_req *aio, cred_t *credp)
{
return (cmdkarw(dev, aio, B_WRITE));
}
static void
cmdkmin(struct buf *bp)
{
if (bp->b_bcount > DK_MAXRECSIZE)
bp->b_bcount = DK_MAXRECSIZE;
}
static int
cmdkrw(dev_t dev, struct uio *uio, int flag)
{
int instance;
struct cmdk *dkp;
instance = CMDKUNIT(dev);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (ENXIO);
mutex_enter(&dkp->dk_mutex);
while (dkp->dk_flag & CMDK_SUSPEND) {
cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
}
mutex_exit(&dkp->dk_mutex);
return (physio(cmdkstrategy, (struct buf *)0, dev, flag, cmdkmin, uio));
}
static int
cmdkarw(dev_t dev, struct aio_req *aio, int flag)
{
int instance;
struct cmdk *dkp;
instance = CMDKUNIT(dev);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (ENXIO);
mutex_enter(&dkp->dk_mutex);
while (dkp->dk_flag & CMDK_SUSPEND) {
cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
}
mutex_exit(&dkp->dk_mutex);
return (aphysio(cmdkstrategy, anocancel, dev, flag, cmdkmin, aio));
}
static int
cmdkstrategy(struct buf *bp)
{
int instance;
struct cmdk *dkp;
long d_cnt;
diskaddr_t p_lblksrt;
diskaddr_t p_lblkcnt;
instance = CMDKUNIT(bp->b_edev);
if (cmdk_indump || !(dkp = ddi_get_soft_state(cmdk_state, instance)) ||
(dkblock(bp) < 0)) {
bp->b_resid = bp->b_bcount;
SETBPERR(bp, ENXIO);
biodone(bp);
return (0);
}
mutex_enter(&dkp->dk_mutex);
ASSERT(cmdk_isopen(dkp, bp->b_edev));
while (dkp->dk_flag & CMDK_SUSPEND) {
cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
}
mutex_exit(&dkp->dk_mutex);
bp->b_flags &= ~(B_DONE|B_ERROR);
bp->b_resid = 0;
bp->av_back = NULL;
if (cmlb_partinfo(dkp->dk_cmlbhandle, CMDKPART(bp->b_edev),
&p_lblkcnt, &p_lblksrt, NULL, NULL, 0)) {
SETBPERR(bp, ENXIO);
}
if ((bp->b_bcount & (NBPSCTR-1)) || (dkblock(bp) > p_lblkcnt))
SETBPERR(bp, ENXIO);
if ((bp->b_flags & B_ERROR) || (dkblock(bp) == p_lblkcnt)) {
bp->b_resid = bp->b_bcount;
biodone(bp);
return (0);
}
d_cnt = bp->b_bcount >> SCTRSHFT;
if ((dkblock(bp) + d_cnt) > p_lblkcnt) {
bp->b_resid = ((dkblock(bp) + d_cnt) - p_lblkcnt) << SCTRSHFT;
bp->b_bcount -= bp->b_resid;
}
SET_BP_SEC(bp, ((ulong_t)(p_lblksrt + dkblock(bp))));
if (dadk_strategy(DKTP_DATA, bp) != DDI_SUCCESS) {
bp->b_resid += bp->b_bcount;
biodone(bp);
}
return (0);
}
static int
cmdk_create_obj(dev_info_t *dip, struct cmdk *dkp)
{
struct scsi_device *devp;
opaque_t queobjp = NULL;
opaque_t flcobjp = NULL;
char que_keyvalp[64];
int que_keylen;
char flc_keyvalp[64];
int flc_keylen;
ASSERT(mutex_owned(&dkp->dk_mutex));
que_keylen = sizeof (que_keyvalp);
if (ddi_prop_op(DDI_DEV_T_NONE, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_CANSLEEP, "queue", que_keyvalp, &que_keylen) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "cmdk_create_obj: queue property undefined");
return (DDI_FAILURE);
}
que_keyvalp[que_keylen] = (char)0;
if (strcmp(que_keyvalp, "qfifo") == 0) {
queobjp = (opaque_t)qfifo_create();
} else if (strcmp(que_keyvalp, "qsort") == 0) {
queobjp = (opaque_t)qsort_create();
} else {
return (DDI_FAILURE);
}
flc_keylen = sizeof (flc_keyvalp);
if (ddi_prop_op(DDI_DEV_T_NONE, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_CANSLEEP, "flow_control", flc_keyvalp, &flc_keylen) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN,
"cmdk_create_obj: flow-control property undefined");
return (DDI_FAILURE);
}
flc_keyvalp[flc_keylen] = (char)0;
if (strcmp(flc_keyvalp, "dsngl") == 0) {
flcobjp = (opaque_t)dsngl_create();
} else if (strcmp(flc_keyvalp, "dmult") == 0) {
flcobjp = (opaque_t)dmult_create();
} else {
return (DDI_FAILURE);
}
dkp->dk_bbh_obj.bbh_data = dkp;
dkp->dk_bbh_obj.bbh_ops = &cmdk_bbh_ops;
dkp->dk_tgobjp = (opaque_t)dadk_create();
devp = ddi_get_driver_private(dip);
(void) dadk_init(DKTP_DATA, devp, flcobjp, queobjp, &dkp->dk_bbh_obj,
NULL);
return (DDI_SUCCESS);
}
static void
cmdk_destroy_obj(dev_info_t *dip, struct cmdk *dkp)
{
char que_keyvalp[64];
int que_keylen;
char flc_keyvalp[64];
int flc_keylen;
ASSERT(mutex_owned(&dkp->dk_mutex));
(void) dadk_free((dkp->dk_tgobjp));
dkp->dk_tgobjp = NULL;
que_keylen = sizeof (que_keyvalp);
if (ddi_prop_op(DDI_DEV_T_NONE, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_CANSLEEP, "queue", que_keyvalp, &que_keylen) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "cmdk_destroy_obj: queue property undefined");
return;
}
que_keyvalp[que_keylen] = (char)0;
flc_keylen = sizeof (flc_keyvalp);
if (ddi_prop_op(DDI_DEV_T_NONE, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_CANSLEEP, "flow_control", flc_keyvalp, &flc_keylen) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN,
"cmdk_destroy_obj: flow-control property undefined");
return;
}
flc_keyvalp[flc_keylen] = (char)0;
}
static int
cmdk_lb_rdwr(dev_info_t *dip, uchar_t cmd, void *bufaddr,
diskaddr_t start, size_t count, void *tg_cookie)
{
struct cmdk *dkp;
opaque_t handle;
int rc = 0;
char *bufa;
size_t buflen;
dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
if (dkp == NULL)
return (ENXIO);
if (cmd != TG_READ && cmd != TG_WRITE)
return (EINVAL);
buflen = (count + NBPSCTR - 1) & -NBPSCTR;
handle = dadk_iob_alloc(DKTP_DATA, start, buflen, KM_SLEEP);
if (!handle)
return (ENOMEM);
if (cmd == TG_READ) {
bufa = dadk_iob_xfer(DKTP_DATA, handle, B_READ);
if (!bufa)
rc = EIO;
else
bcopy(bufa, bufaddr, count);
} else {
bufa = dadk_iob_htoc(DKTP_DATA, handle);
bcopy(bufaddr, bufa, count);
bufa = dadk_iob_xfer(DKTP_DATA, handle, B_WRITE);
if (!bufa)
rc = EIO;
}
(void) dadk_iob_free(DKTP_DATA, handle);
return (rc);
}
static int
cmdk_lb_getinfo(dev_info_t *dip, int cmd, void *arg, void *tg_cookie)
{
struct cmdk *dkp;
struct tgdk_geom phyg;
dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
if (dkp == NULL)
return (ENXIO);
switch (cmd) {
case TG_GETPHYGEOM: {
cmlb_geom_t *phygeomp = (cmlb_geom_t *)arg;
(void) dadk_getphygeom(DKTP_DATA, &phyg);
phygeomp->g_capacity = phyg.g_cap;
phygeomp->g_nsect = phyg.g_sec;
phygeomp->g_nhead = phyg.g_head;
phygeomp->g_acyl = phyg.g_acyl;
phygeomp->g_ncyl = phyg.g_cyl;
phygeomp->g_secsize = phyg.g_secsiz;
phygeomp->g_intrlv = 1;
phygeomp->g_rpm = 3600;
return (0);
}
case TG_GETVIRTGEOM: {
cmlb_geom_t *virtgeomp = (cmlb_geom_t *)arg;
diskaddr_t capacity;
(void) dadk_getgeom(DKTP_DATA, &phyg);
capacity = phyg.g_cap;
if (capacity < 0 || capacity >= 63 * 254 * 1024)
return (EINVAL);
virtgeomp->g_capacity = capacity;
virtgeomp->g_nsect = 63;
virtgeomp->g_nhead = 254;
virtgeomp->g_ncyl = capacity / (63 * 254);
virtgeomp->g_acyl = 0;
virtgeomp->g_secsize = 512;
virtgeomp->g_intrlv = 1;
virtgeomp->g_rpm = 3600;
return (0);
}
case TG_GETCAPACITY:
case TG_GETBLOCKSIZE:
{
(void) dadk_getphygeom(DKTP_DATA, &phyg);
if (cmd == TG_GETCAPACITY)
*(diskaddr_t *)arg = phyg.g_cap;
else
*(uint32_t *)arg = (uint32_t)phyg.g_secsiz;
return (0);
}
case TG_GETATTR: {
tg_attribute_t *tgattribute = (tg_attribute_t *)arg;
if ((DKTP_EXT->tg_rdonly))
tgattribute->media_is_writable = FALSE;
else
tgattribute->media_is_writable = TRUE;
tgattribute->media_is_rotational = TRUE;
return (0);
}
default:
return (ENOTTY);
}
}
static void
cmdk_devid_setup(struct cmdk *dkp)
{
int rc;
if (dkp->dk_devid != NULL)
return;
rc = cmdk_devid_modser(dkp);
if (rc != DDI_SUCCESS) {
rc = cmdk_devid_read(dkp);
if (rc != DDI_SUCCESS)
rc = cmdk_devid_fabricate(dkp);
}
if (rc == DDI_SUCCESS)
(void) ddi_devid_register(dkp->dk_dip, dkp->dk_devid);
}
static int
cmdk_devid_modser(struct cmdk *dkp)
{
int rc = DDI_FAILURE;
char *hwid;
int modlen;
int serlen;
hwid = kmem_alloc(CMDK_HWIDLEN, KM_SLEEP);
modlen = cmdk_get_modser(dkp, DIOCTL_GETMODEL, hwid, CMDK_HWIDLEN);
if (modlen == 0) {
rc = DDI_FAILURE;
goto err;
}
hwid[modlen++] = '=';
serlen = cmdk_get_modser(dkp, DIOCTL_GETSERIAL,
hwid + modlen, CMDK_HWIDLEN - modlen);
if (serlen == 0) {
rc = DDI_FAILURE;
goto err;
}
hwid[modlen + serlen] = 0;
rc = ddi_devid_init(dkp->dk_dip, DEVID_ATA_SERIAL, modlen + serlen,
hwid, &dkp->dk_devid);
if (rc != DDI_SUCCESS) {
rc = DDI_FAILURE;
goto err;
}
rc = DDI_SUCCESS;
err:
kmem_free(hwid, CMDK_HWIDLEN);
return (rc);
}
static int
cmdk_get_modser(struct cmdk *dkp, int ioccmd, char *buf, int len)
{
dadk_ioc_string_t strarg;
int rval;
char *s;
char ch;
boolean_t ret;
int i;
int tb;
strarg.is_buf = buf;
strarg.is_size = len;
if (dadk_ioctl(DKTP_DATA,
dkp->dk_dev,
ioccmd,
(uintptr_t)&strarg,
FNATIVE | FKIOCTL,
NULL,
&rval) != 0)
return (0);
ret = B_FALSE;
s = buf;
for (i = 0; i < strarg.is_size; i++) {
ch = *s++;
if (ch != ' ' && ch != '\0')
tb = i + 1;
if (ch != ' ' && ch != '\0' && ch != '0')
ret = B_TRUE;
}
if (ret == B_FALSE)
return (0);
return (tb);
}
static int
cmdk_devid_read(struct cmdk *dkp)
{
diskaddr_t blk;
struct dk_devid *dkdevidp;
uint_t *ip;
int chksum;
int i, sz;
tgdk_iob_handle handle = NULL;
int rc = DDI_FAILURE;
if (cmlb_get_devid_block(dkp->dk_cmlbhandle, &blk, 0))
goto err;
handle = dadk_iob_alloc(DKTP_DATA, blk, NBPSCTR, KM_SLEEP);
if (handle == NULL)
goto err;
dkdevidp = (struct dk_devid *)dadk_iob_xfer(DKTP_DATA, handle, B_READ);
if (dkdevidp == NULL)
goto err;
if ((dkdevidp->dkd_rev_hi != DK_DEVID_REV_MSB) ||
(dkdevidp->dkd_rev_lo != DK_DEVID_REV_LSB))
goto err;
chksum = 0;
ip = (uint_t *)dkdevidp;
for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
chksum ^= ip[i];
if (DKD_GETCHKSUM(dkdevidp) != chksum)
goto err;
if (ddi_devid_valid((ddi_devid_t)dkdevidp->dkd_devid) != DDI_SUCCESS)
goto err;
sz = ddi_devid_sizeof((ddi_devid_t)dkdevidp->dkd_devid);
dkp->dk_devid = kmem_alloc(sz, KM_SLEEP);
bcopy(dkdevidp->dkd_devid, dkp->dk_devid, sz);
rc = DDI_SUCCESS;
err:
if (handle != NULL)
(void) dadk_iob_free(DKTP_DATA, handle);
return (rc);
}
static int
cmdk_devid_fabricate(struct cmdk *dkp)
{
ddi_devid_t devid = NULL;
struct dk_devid *dkdevidp;
diskaddr_t blk;
tgdk_iob_handle handle = NULL;
uint_t *ip, chksum;
int i;
int rc = DDI_FAILURE;
if (ddi_devid_init(dkp->dk_dip, DEVID_FAB, 0, NULL, &devid) !=
DDI_SUCCESS)
goto err;
if (cmlb_get_devid_block(dkp->dk_cmlbhandle, &blk, 0)) {
goto err;
}
handle = dadk_iob_alloc(DKTP_DATA, blk, NBPSCTR, KM_SLEEP);
if (!handle)
goto err;
dkdevidp = (struct dk_devid *)dadk_iob_htoc(DKTP_DATA, handle);
bzero(dkdevidp, NBPSCTR);
dkdevidp->dkd_rev_hi = DK_DEVID_REV_MSB;
dkdevidp->dkd_rev_lo = DK_DEVID_REV_LSB;
i = ddi_devid_sizeof(devid);
if (i > DK_DEVID_SIZE)
goto err;
bcopy(devid, dkdevidp->dkd_devid, i);
chksum = 0;
ip = (uint_t *)dkdevidp;
for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
chksum ^= ip[i];
DKD_FORMCHKSUM(chksum, dkdevidp);
(void) dadk_iob_xfer(DKTP_DATA, handle, B_WRITE);
dkp->dk_devid = devid;
rc = DDI_SUCCESS;
err:
if (handle != NULL)
(void) dadk_iob_free(DKTP_DATA, handle);
if (rc != DDI_SUCCESS && devid != NULL)
ddi_devid_free(devid);
return (rc);
}
static void
cmdk_bbh_free_alts(struct cmdk *dkp)
{
if (dkp->dk_alts_hdl) {
(void) dadk_iob_free(DKTP_DATA, dkp->dk_alts_hdl);
kmem_free(dkp->dk_slc_cnt,
NDKMAP * (sizeof (uint32_t) + sizeof (struct alts_ent *)));
dkp->dk_alts_hdl = NULL;
}
}
static void
cmdk_bbh_reopen(struct cmdk *dkp)
{
tgdk_iob_handle handle = NULL;
diskaddr_t slcb, slcn, slce;
struct alts_parttbl *ap;
struct alts_ent *enttblp;
uint32_t altused;
uint32_t altbase;
uint32_t altlast;
int alts;
uint16_t vtoctag;
int i, j;
for (alts = 0; alts < NDKMAP; alts++) {
if (cmlb_partinfo(
dkp->dk_cmlbhandle,
alts,
&slcn,
&slcb,
NULL,
&vtoctag,
0)) {
goto empty;
}
if (vtoctag == V_ALTSCTR && slcn > 1)
break;
}
if (alts >= NDKMAP) {
goto empty;
}
handle = dadk_iob_alloc(DKTP_DATA, slcb, NBPSCTR, KM_SLEEP);
if (!handle) {
goto empty;
}
ap = (struct alts_parttbl *)dadk_iob_xfer(DKTP_DATA, handle, B_READ);
if (!ap || (ap->alts_sanity != ALTS_SANITY)) {
goto empty;
}
altused = ap->alts_ent_used;
altbase = ap->alts_ent_base;
altlast = ap->alts_ent_end;
if (altused == 0 ||
altbase < 1 ||
altbase > altlast ||
altlast >= slcn) {
goto empty;
}
(void) dadk_iob_free(DKTP_DATA, handle);
handle = dadk_iob_alloc(DKTP_DATA,
slcb + altbase,
(altlast - altbase + 1) << SCTRSHFT, KM_SLEEP);
if (!handle) {
goto empty;
}
enttblp = (struct alts_ent *)dadk_iob_xfer(DKTP_DATA, handle, B_READ);
if (!enttblp) {
goto empty;
}
rw_enter(&dkp->dk_bbh_mutex, RW_WRITER);
if (dkp->dk_slc_cnt == NULL) {
dkp->dk_slc_cnt = kmem_alloc(NDKMAP *
(sizeof (long) + sizeof (struct alts_ent *)), KM_SLEEP);
}
dkp->dk_slc_ent = (struct alts_ent **)(dkp->dk_slc_cnt + NDKMAP);
if (dkp->dk_alts_hdl) {
(void) dadk_iob_free(DKTP_DATA, dkp->dk_alts_hdl);
dkp->dk_alts_hdl = NULL;
dkp->dk_altused = 0;
}
dkp->dk_alts_hdl = handle;
dkp->dk_altused = altused;
for (i = 0; i < NDKMAP; i++) {
if (cmlb_partinfo(
dkp->dk_cmlbhandle,
i,
&slcn,
&slcb,
NULL,
NULL,
0)) {
goto empty1;
}
dkp->dk_slc_cnt[i] = 0;
if (slcn == 0)
continue;
slce = slcb + slcn - 1;
for (j = 0; j < altused; j++) {
if (enttblp[j].bad_start + enttblp[j].bad_end >= slcb)
break;
}
dkp->dk_slc_ent[i] = enttblp + j;
for (; j < altused && enttblp[j].bad_start <= slce; j++) {
dkp->dk_slc_cnt[i] += 1;
}
}
rw_exit(&dkp->dk_bbh_mutex);
return;
empty:
rw_enter(&dkp->dk_bbh_mutex, RW_WRITER);
empty1:
if (handle && handle != dkp->dk_alts_hdl)
(void) dadk_iob_free(DKTP_DATA, handle);
if (dkp->dk_alts_hdl) {
(void) dadk_iob_free(DKTP_DATA, dkp->dk_alts_hdl);
dkp->dk_alts_hdl = NULL;
}
rw_exit(&dkp->dk_bbh_mutex);
}
static bbh_cookie_t
cmdk_bbh_htoc(opaque_t bbh_data, opaque_t handle)
{
struct bbh_handle *hp;
bbh_cookie_t ckp;
hp = (struct bbh_handle *)handle;
ckp = hp->h_cktab + hp->h_idx;
hp->h_idx++;
return (ckp);
}
static void
cmdk_bbh_freehandle(opaque_t bbh_data, opaque_t handle)
{
struct bbh_handle *hp;
hp = (struct bbh_handle *)handle;
kmem_free(handle, (sizeof (struct bbh_handle) +
(hp->h_totck * (sizeof (struct bbh_cookie)))));
}
static opaque_t
cmdk_bbh_gethandle(opaque_t bbh_data, struct buf *bp)
{
struct cmdk *dkp = (struct cmdk *)bbh_data;
struct bbh_handle *hp;
struct bbh_cookie *ckp;
struct alts_ent *altp;
uint32_t alts_used;
uint32_t part = CMDKPART(bp->b_edev);
daddr32_t lastsec;
long d_count;
int i;
int idx;
int cnt;
if (part >= V_NUMPAR)
return (NULL);
if (!dkp->dk_alts_hdl) {
return (NULL);
}
rw_enter(&dkp->dk_bbh_mutex, RW_READER);
if (dkp->dk_alts_hdl == NULL) {
rw_exit(&dkp->dk_bbh_mutex);
return (NULL);
}
alts_used = dkp->dk_slc_cnt[part];
if (alts_used == 0) {
rw_exit(&dkp->dk_bbh_mutex);
return (NULL);
}
altp = dkp->dk_slc_ent[part];
i = cmdk_bbh_bsearch(altp, alts_used, GET_BP_SEC(bp));
if (i == -1) {
rw_exit(&dkp->dk_bbh_mutex);
return (NULL);
}
altp += i;
d_count = bp->b_bcount >> SCTRSHFT;
lastsec = GET_BP_SEC(bp) + d_count - 1;
for (idx = i, cnt = 0; idx < alts_used; idx++, altp++, cnt++) {
if (lastsec < altp->bad_start)
break;
}
if (!cnt) {
rw_exit(&dkp->dk_bbh_mutex);
return (NULL);
}
cnt <<= 1;
cnt++;
hp = (struct bbh_handle *)kmem_zalloc((sizeof (*hp) +
(cnt * sizeof (*ckp))), KM_SLEEP);
hp->h_idx = 0;
hp->h_totck = cnt;
ckp = hp->h_cktab = (struct bbh_cookie *)(hp + 1);
ckp[0].ck_sector = GET_BP_SEC(bp);
ckp[0].ck_seclen = d_count;
altp = dkp->dk_slc_ent[part];
altp += i;
for (idx = 0; i < alts_used; i++, altp++) {
if (lastsec < altp->bad_start)
break;
if (ckp[idx].ck_sector > altp->bad_end)
continue;
if ((ckp[idx].ck_sector >= altp->bad_start) &&
(lastsec <= altp->bad_end)) {
ckp[idx].ck_sector = altp->good_start +
ckp[idx].ck_sector - altp->bad_start;
break;
}
if ((lastsec >= altp->bad_start) &&
(lastsec <= altp->bad_end)) {
ckp[idx+1].ck_seclen = lastsec - altp->bad_start + 1;
ckp[idx].ck_seclen -= ckp[idx+1].ck_seclen;
ckp[idx+1].ck_sector = altp->good_start;
break;
}
if ((ckp[idx].ck_sector <= altp->bad_end) &&
(ckp[idx].ck_sector >= altp->bad_start)) {
ckp[idx+1].ck_seclen = ckp[idx].ck_seclen;
ckp[idx].ck_seclen = altp->bad_end -
ckp[idx].ck_sector + 1;
ckp[idx+1].ck_seclen -= ckp[idx].ck_seclen;
ckp[idx].ck_sector = altp->good_start +
ckp[idx].ck_sector - altp->bad_start;
idx++;
ckp[idx].ck_sector = altp->bad_end + 1;
continue;
}
ckp[idx].ck_seclen = altp->bad_start - ckp[idx].ck_sector;
ckp[idx+1].ck_sector = altp->good_start;
ckp[idx+1].ck_seclen = altp->bad_end - altp->bad_start + 1;
idx += 2;
ckp[idx].ck_sector = altp->bad_end + 1;
ckp[idx].ck_seclen = lastsec - altp->bad_end;
}
rw_exit(&dkp->dk_bbh_mutex);
return ((opaque_t)hp);
}
static int
cmdk_bbh_bsearch(struct alts_ent *buf, int cnt, daddr32_t key)
{
int i;
int ind;
int interval;
int mystatus = -1;
if (!cnt)
return (mystatus);
ind = 1;
for (i = 1; i <= cnt; i <<= 1)
ind = i;
for (interval = ind; interval; ) {
if ((key >= buf[ind-1].bad_start) &&
(key <= buf[ind-1].bad_end)) {
return (ind-1);
} else {
interval >>= 1;
if (key < buf[ind-1].bad_start) {
mystatus = ind-1;
if (!interval)
break;
ind = ind - interval;
} else {
if ((ind == cnt) || !interval)
break;
if ((ind+interval) <= cnt)
ind += interval;
}
}
}
return (mystatus);
}