#include <sys/note.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/cred.h>
#include <sys/poll.h>
#include <sys/mman.h>
#include <sys/kmem.h>
#include <sys/model.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/open.h>
#include <sys/user.h>
#include <sys/t_lock.h>
#include <sys/vm.h>
#include <sys/stat.h>
#include <vm/hat.h>
#include <vm/seg.h>
#include <vm/seg_vn.h>
#include <vm/seg_dev.h>
#include <vm/as.h>
#include <sys/cmn_err.h>
#include <sys/cpuvar.h>
#include <sys/debug.h>
#include <sys/autoconf.h>
#include <sys/sunddi.h>
#include <sys/esunddi.h>
#include <sys/sunndi.h>
#include <sys/kstat.h>
#include <sys/conf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/ddi_periodic.h>
#include <sys/hwconf.h>
#include <sys/pathname.h>
#include <sys/modctl.h>
#include <sys/epm.h>
#include <sys/devctl.h>
#include <sys/callb.h>
#include <sys/cladm.h>
#include <sys/sysevent.h>
#include <sys/dacf_impl.h>
#include <sys/ddidevmap.h>
#include <sys/bootconf.h>
#include <sys/disp.h>
#include <sys/atomic.h>
#include <sys/promif.h>
#include <sys/instance.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/task.h>
#include <sys/project.h>
#include <sys/taskq.h>
#include <sys/devpolicy.h>
#include <sys/ctype.h>
#include <net/if.h>
#include <sys/rctl.h>
#include <sys/zone.h>
#include <sys/clock_impl.h>
#include <sys/ddi.h>
#include <sys/modhash.h>
#include <sys/sunldi_impl.h>
#include <sys/fs/dv_node.h>
#include <sys/fs/snode.h>
extern pri_t minclsyspri;
extern rctl_hndl_t rc_project_locked_mem;
extern rctl_hndl_t rc_zone_locked_mem;
#ifdef DEBUG
static int sunddi_debug = 0;
#endif
static void i_ddi_umem_unlock_thread_start(void);
static kmutex_t ddi_umem_unlock_mutex;
static kcondvar_t ddi_umem_unlock_cv;
static kthread_t *ddi_umem_unlock_thread;
static struct ddi_umem_cookie *ddi_umem_unlock_head = NULL;
static struct ddi_umem_cookie *ddi_umem_unlock_tail = NULL;
#if defined(__x86)
char *chosen_reg = "chosen-reg";
#endif
void (*ddi_console_bell_func)(clock_t duration);
int
ddi_map(dev_info_t *dp, ddi_map_req_t *mp, off_t offset,
off_t len, caddr_t *addrp)
{
dev_info_t *pdip;
ASSERT(dp);
pdip = (dev_info_t *)DEVI(dp)->devi_parent;
return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
dp, mp, offset, len, addrp));
}
int
ddi_apply_range(dev_info_t *dp, dev_info_t *rdip, struct regspec *rp)
{
return (i_ddi_apply_range(dp, rdip, rp));
}
int
ddi_map_regs(dev_info_t *dip, uint_t rnumber, caddr_t *kaddrp, off_t offset,
off_t len)
{
ddi_map_req_t mr;
#if defined(__x86)
struct {
int bus;
int addr;
int size;
} reg, *reglist;
uint_t length;
int rc;
rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "registers", (int **)®list, &length);
if (rc != DDI_PROP_SUCCESS)
rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "reg", (int **)®list, &length);
if (rc == DDI_PROP_SUCCESS) {
reg = reglist[rnumber];
reg.addr += offset;
if (len != 0)
reg.size = len;
if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
chosen_reg, (int *)®, (sizeof (reg)/sizeof (int)))
!= DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "%s%d: cannot create '%s' "
"property", DEVI(dip)->devi_name,
DEVI(dip)->devi_instance, chosen_reg);
}
ddi_prop_free((void *)reglist);
}
#endif
mr.map_op = DDI_MO_MAP_LOCKED;
mr.map_type = DDI_MT_RNUMBER;
mr.map_obj.rnumber = rnumber;
mr.map_prot = PROT_READ | PROT_WRITE;
mr.map_flags = DDI_MF_KERNEL_MAPPING;
mr.map_handlep = NULL;
mr.map_vers = DDI_MAP_VERSION;
return (ddi_map(dip, &mr, offset, len, kaddrp));
}
void
ddi_unmap_regs(dev_info_t *dip, uint_t rnumber, caddr_t *kaddrp, off_t offset,
off_t len)
{
ddi_map_req_t mr;
mr.map_op = DDI_MO_UNMAP;
mr.map_type = DDI_MT_RNUMBER;
mr.map_flags = DDI_MF_KERNEL_MAPPING;
mr.map_prot = PROT_READ | PROT_WRITE;
mr.map_obj.rnumber = rnumber;
mr.map_handlep = NULL;
mr.map_vers = DDI_MAP_VERSION;
(void) ddi_map(dip, &mr, offset, len, kaddrp);
*kaddrp = (caddr_t)0;
#if defined(__x86)
(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, chosen_reg);
#endif
}
int
ddi_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
off_t offset, off_t len, caddr_t *vaddrp)
{
return (i_ddi_bus_map(dip, rdip, mp, offset, len, vaddrp));
}
int
nullbusmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
off_t offset, off_t len, caddr_t *vaddrp)
{
_NOTE(ARGUNUSED(rdip))
if (mp->map_type == DDI_MT_RNUMBER)
return (DDI_ME_UNSUPPORTED);
return (ddi_map(dip, mp, offset, len, vaddrp));
}
struct regspec *
ddi_rnumber_to_regspec(dev_info_t *dip, int rnumber)
{
return (i_ddi_rnumber_to_regspec(dip, rnumber));
}
static int
i_ddi_peekpoke(dev_info_t *devi, ddi_ctl_enum_t cmd, size_t size,
void *addr, void *value_p)
{
union {
uint64_t u64;
uint32_t u32;
uint16_t u16;
uint8_t u8;
} peekpoke_value;
peekpoke_ctlops_t peekpoke_args;
uint64_t dummy_result;
int rval;
peekpoke_args.size = size;
peekpoke_args.dev_addr = (uintptr_t)addr;
peekpoke_args.handle = NULL;
peekpoke_args.repcount = 1;
peekpoke_args.flags = 0;
if (cmd == DDI_CTLOPS_POKE) {
switch (size) {
case sizeof (uint8_t):
peekpoke_value.u8 = *(uint8_t *)value_p;
break;
case sizeof (uint16_t):
peekpoke_value.u16 = *(uint16_t *)value_p;
break;
case sizeof (uint32_t):
peekpoke_value.u32 = *(uint32_t *)value_p;
break;
case sizeof (uint64_t):
peekpoke_value.u64 = *(uint64_t *)value_p;
break;
}
}
peekpoke_args.host_addr = (uintptr_t)&peekpoke_value.u64;
if (devi != NULL)
rval = ddi_ctlops(devi, devi, cmd, &peekpoke_args,
&dummy_result);
else
rval = peekpoke_mem(cmd, &peekpoke_args);
if ((cmd == DDI_CTLOPS_PEEK) & (value_p != NULL)) {
switch (size) {
case sizeof (uint8_t):
*(uint8_t *)value_p = peekpoke_value.u8;
break;
case sizeof (uint16_t):
*(uint16_t *)value_p = peekpoke_value.u16;
break;
case sizeof (uint32_t):
*(uint32_t *)value_p = peekpoke_value.u32;
break;
case sizeof (uint64_t):
*(uint64_t *)value_p = peekpoke_value.u64;
break;
}
}
return (rval);
}
int
ddi_peek(dev_info_t *devi, size_t size, void *addr, void *value_p)
{
switch (size) {
case sizeof (uint8_t):
case sizeof (uint16_t):
case sizeof (uint32_t):
case sizeof (uint64_t):
break;
default:
return (DDI_FAILURE);
}
return (i_ddi_peekpoke(devi, DDI_CTLOPS_PEEK, size, addr, value_p));
}
int
ddi_poke(dev_info_t *devi, size_t size, void *addr, void *value_p)
{
switch (size) {
case sizeof (uint8_t):
case sizeof (uint16_t):
case sizeof (uint32_t):
case sizeof (uint64_t):
break;
default:
return (DDI_FAILURE);
}
return (i_ddi_peekpoke(devi, DDI_CTLOPS_POKE, size, addr, value_p));
}
int
ddi_peek8(dev_info_t *dip, int8_t *addr, int8_t *val_p)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_PEEK, sizeof (*val_p), addr,
val_p));
}
int
ddi_peek16(dev_info_t *dip, int16_t *addr, int16_t *val_p)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_PEEK, sizeof (*val_p), addr,
val_p));
}
int
ddi_peek32(dev_info_t *dip, int32_t *addr, int32_t *val_p)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_PEEK, sizeof (*val_p), addr,
val_p));
}
int
ddi_peek64(dev_info_t *dip, int64_t *addr, int64_t *val_p)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_PEEK, sizeof (*val_p), addr,
val_p));
}
int
ddi_poke8(dev_info_t *dip, int8_t *addr, int8_t val)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_POKE, sizeof (val), addr, &val));
}
int
ddi_poke16(dev_info_t *dip, int16_t *addr, int16_t val)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_POKE, sizeof (val), addr, &val));
}
int
ddi_poke32(dev_info_t *dip, int32_t *addr, int32_t val)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_POKE, sizeof (val), addr, &val));
}
int
ddi_poke64(dev_info_t *dip, int64_t *addr, int64_t val)
{
return (i_ddi_peekpoke(dip, DDI_CTLOPS_POKE, sizeof (val), addr, &val));
}
int
ddi_peekpokeio(dev_info_t *devi, struct uio *uio, enum uio_rw rw,
caddr_t addr, size_t len, uint_t xfersize)
{
int64_t ibuffer;
int8_t w8;
size_t sz;
int o;
if (xfersize > sizeof (long))
xfersize = sizeof (long);
while (len != 0) {
if ((len | (uintptr_t)addr) & 1) {
sz = sizeof (int8_t);
if (rw == UIO_WRITE) {
if ((o = uwritec(uio)) == -1)
return (DDI_FAILURE);
if (ddi_poke8(devi, (int8_t *)addr,
(int8_t)o) != DDI_SUCCESS)
return (DDI_FAILURE);
} else {
if (i_ddi_peekpoke(devi, DDI_CTLOPS_PEEK, sz,
(int8_t *)addr, &w8) != DDI_SUCCESS)
return (DDI_FAILURE);
if (ureadc(w8, uio))
return (DDI_FAILURE);
}
} else {
switch (xfersize) {
case sizeof (int64_t):
if (((len | (uintptr_t)addr) &
(sizeof (int64_t) - 1)) == 0) {
sz = xfersize;
break;
}
case sizeof (int32_t):
if (((len | (uintptr_t)addr) &
(sizeof (int32_t) - 1)) == 0) {
sz = xfersize;
break;
}
default:
sz = sizeof (int16_t);
break;
}
if (rw == UIO_READ) {
if (i_ddi_peekpoke(devi, DDI_CTLOPS_PEEK, sz,
addr, &ibuffer) != DDI_SUCCESS)
return (DDI_FAILURE);
}
if (uiomove(&ibuffer, sz, rw, uio))
return (DDI_FAILURE);
if (rw == UIO_WRITE) {
if (i_ddi_peekpoke(devi, DDI_CTLOPS_POKE, sz,
addr, &ibuffer) != DDI_SUCCESS)
return (DDI_FAILURE);
}
}
addr += sz;
len -= sz;
}
return (DDI_SUCCESS);
}
#if !defined(__sparc)
int
ddi_copyin(const void *buf, void *kernbuf, size_t size, int flags)
{
if (flags & FKIOCTL)
return (kcopy(buf, kernbuf, size) ? -1 : 0);
return (copyin(buf, kernbuf, size));
}
int
ddi_copyout(const void *buf, void *kernbuf, size_t size, int flags)
{
if (flags & FKIOCTL)
return (kcopy(buf, kernbuf, size) ? -1 : 0);
return (copyout(buf, kernbuf, size));
}
#endif
unsigned long
ddi_btop(dev_info_t *dip, unsigned long bytes)
{
unsigned long pages;
(void) ddi_ctlops(dip, dip, DDI_CTLOPS_BTOP, &bytes, &pages);
return (pages);
}
unsigned long
ddi_btopr(dev_info_t *dip, unsigned long bytes)
{
unsigned long pages;
(void) ddi_ctlops(dip, dip, DDI_CTLOPS_BTOPR, &bytes, &pages);
return (pages);
}
unsigned long
ddi_ptob(dev_info_t *dip, unsigned long pages)
{
unsigned long bytes;
(void) ddi_ctlops(dip, dip, DDI_CTLOPS_PTOB, &pages, &bytes);
return (bytes);
}
unsigned int
ddi_enter_critical(void)
{
return ((uint_t)spl7());
}
void
ddi_exit_critical(unsigned int spl)
{
splx((int)spl);
}
#if !defined(__sparc)
int
ddi_ctlops(dev_info_t *d, dev_info_t *r, ddi_ctl_enum_t op, void *a, void *v)
{
int (*fp)();
if (!d || !r)
return (DDI_FAILURE);
if ((d = (dev_info_t *)DEVI(d)->devi_bus_ctl) == NULL)
return (DDI_FAILURE);
fp = DEVI(d)->devi_ops->devo_bus_ops->bus_ctl;
return ((*fp)(d, r, op, a, v));
}
#endif
#if !defined(__sparc)
int
ddi_dma_mctl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, enum ddi_dma_ctlops request,
off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags)
{
int (*fp)();
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_ctl;
fp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_ctl;
return ((*fp) (dip, rdip, handle, request, offp, lenp, objp, flags));
}
#endif
#define HD ((ddi_dma_impl_t *)h)->dmai_rdip
int
ddi_dma_map(dev_info_t *dip, dev_info_t *rdip,
struct ddi_dma_req *dmareqp, ddi_dma_handle_t *handlep)
{
return (DDI_FAILURE);
}
#if !defined(__sparc)
int
ddi_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr,
int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep)
{
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_attr_t *,
int (*)(caddr_t), caddr_t, ddi_dma_handle_t *);
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_allochdl;
funcp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_allochdl;
return ((*funcp)(dip, rdip, attr, waitfp, arg, handlep));
}
int
ddi_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handlep)
{
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_handle_t);
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_allochdl;
funcp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_freehdl;
return ((*funcp)(dip, rdip, handlep));
}
int
ddi_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, struct ddi_dma_req *dmareq,
ddi_dma_cookie_t *cp, uint_t *ccountp)
{
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_handle_t,
struct ddi_dma_req *, ddi_dma_cookie_t *, uint_t *);
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_bindhdl;
funcp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_bindhdl;
return ((*funcp)(dip, rdip, handle, dmareq, cp, ccountp));
}
int
ddi_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle)
{
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_handle_t);
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_unbindhdl;
funcp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_unbindhdl;
return ((*funcp)(dip, rdip, handle));
}
int
ddi_dma_flush(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, off_t off, size_t len,
uint_t cache_flags)
{
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_handle_t,
off_t, size_t, uint_t);
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_flush;
funcp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_flush;
return ((*funcp)(dip, rdip, handle, off, len, cache_flags));
}
int
ddi_dma_win(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, uint_t win, off_t *offp,
size_t *lenp, ddi_dma_cookie_t *cookiep, uint_t *ccountp)
{
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_handle_t,
uint_t, off_t *, size_t *, ddi_dma_cookie_t *, uint_t *);
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_win;
funcp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_win;
return ((*funcp)(dip, rdip, handle, win, offp, lenp,
cookiep, ccountp));
}
int
ddi_dma_sync(ddi_dma_handle_t h, off_t o, size_t l, uint_t whom)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)h;
dev_info_t *dip, *rdip;
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_handle_t, off_t,
size_t, uint_t);
if ((hp->dmai_rflags & DMP_NOSYNC) == DMP_NOSYNC)
return (DDI_SUCCESS);
dip = rdip = hp->dmai_rdip;
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_flush;
funcp = DEVI(dip)->devi_ops->devo_bus_ops->bus_dma_flush;
return ((*funcp)(dip, rdip, h, o, l, whom));
}
int
ddi_dma_unbind_handle(ddi_dma_handle_t h)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)h;
dev_info_t *dip, *rdip;
int (*funcp)(dev_info_t *, dev_info_t *, ddi_dma_handle_t);
dip = rdip = hp->dmai_rdip;
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_unbindhdl;
funcp = DEVI(rdip)->devi_bus_dma_unbindfunc;
return ((*funcp)(dip, rdip, h));
}
#endif
int
ddi_dma_burstsizes(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *dimp = (ddi_dma_impl_t *)handle;
if (!dimp)
return (0);
else
return (dimp->dmai_burstsizes);
}
void
ddi_dma_attr_merge(ddi_dma_attr_t *attr, const ddi_dma_attr_t *mod)
{
attr->dma_attr_addr_lo =
MAX(attr->dma_attr_addr_lo, mod->dma_attr_addr_lo);
attr->dma_attr_addr_hi =
MIN(attr->dma_attr_addr_hi, mod->dma_attr_addr_hi);
attr->dma_attr_count_max =
MIN(attr->dma_attr_count_max, mod->dma_attr_count_max);
attr->dma_attr_align =
MAX(attr->dma_attr_align, mod->dma_attr_align);
attr->dma_attr_burstsizes =
(uint_t)(attr->dma_attr_burstsizes & mod->dma_attr_burstsizes);
attr->dma_attr_minxfer =
maxbit(attr->dma_attr_minxfer, mod->dma_attr_minxfer);
attr->dma_attr_maxxfer =
MIN(attr->dma_attr_maxxfer, mod->dma_attr_maxxfer);
attr->dma_attr_seg = MIN(attr->dma_attr_seg, mod->dma_attr_seg);
attr->dma_attr_sgllen = MIN((uint_t)attr->dma_attr_sgllen,
(uint_t)mod->dma_attr_sgllen);
attr->dma_attr_granular =
MAX(attr->dma_attr_granular, mod->dma_attr_granular);
}
int
ddi_segmap(dev_t dev, off_t offset, struct as *asp, caddr_t *addrp, off_t len,
uint_t prot, uint_t maxprot, uint_t flags, cred_t *credp)
{
extern int spec_segmap(dev_t, off_t, struct as *, caddr_t *,
off_t, uint_t, uint_t, uint_t, struct cred *);
return (spec_segmap(dev, offset, asp, addrp, len,
prot, maxprot, flags, credp));
}
int
ddi_map_fault(dev_info_t *dip, struct hat *hat, struct seg *seg,
caddr_t addr, struct devpage *dp, pfn_t pfn, uint_t prot, uint_t lock)
{
return (i_ddi_map_fault(dip, dip, hat, seg, addr, dp, pfn, prot, lock));
}
int
ddi_device_mapping_check(dev_t dev, const ddi_device_acc_attr_t *accattrp,
uint_t rnumber, uint_t *hat_flags)
{
ddi_acc_handle_t handle;
ddi_map_req_t mr;
ddi_acc_hdl_t *hp;
int result;
dev_info_t *dip;
if ((dip =
e_ddi_hold_devi_by_dev(dev, E_DDI_HOLD_DEVI_NOATTACH)) == NULL)
return (-1);
ddi_release_devi(dip);
handle = impl_acc_hdl_alloc(KM_SLEEP, NULL);
if (handle == NULL)
return (-1);
hp = impl_acc_hdl_get(handle);
hp->ah_vers = VERS_ACCHDL;
hp->ah_dip = dip;
hp->ah_rnumber = rnumber;
hp->ah_offset = 0;
hp->ah_len = 0;
hp->ah_acc = *accattrp;
mr.map_op = DDI_MO_MAP_HANDLE;
mr.map_type = DDI_MT_RNUMBER;
mr.map_obj.rnumber = rnumber;
mr.map_prot = PROT_READ | PROT_WRITE;
mr.map_flags = DDI_MF_KERNEL_MAPPING;
mr.map_handlep = hp;
mr.map_vers = DDI_MAP_VERSION;
result = ddi_map(dip, &mr, 0, 0, NULL);
*hat_flags = hp->ah_hat_flags;
impl_acc_hdl_free(handle);
if (result != DDI_SUCCESS)
return (-1);
return (0);
}
int
nopropop(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
char *name, caddr_t valuep, int *lengthp)
{
_NOTE(ARGUNUSED(dev, dip, prop_op, mod_flags, name, valuep, lengthp))
return (DDI_PROP_NOT_FOUND);
}
#ifdef DDI_PROP_DEBUG
int ddi_prop_debug_flag = 0;
int
ddi_prop_debug(int enable)
{
int prev = ddi_prop_debug_flag;
if ((enable != 0) || (prev != 0))
printf("ddi_prop_debug: debugging %s\n",
enable ? "enabled" : "disabled");
ddi_prop_debug_flag = enable;
return (prev);
}
#endif
ddi_prop_t *
i_ddi_prop_search(dev_t dev, char *name, uint_t flags, ddi_prop_t **list_head)
{
ddi_prop_t *propp;
if (dev == DDI_DEV_T_NONE)
dev = DDI_DEV_T_ANY;
for (propp = *list_head; propp != NULL; propp = propp->prop_next) {
if (!DDI_STRSAME(propp->prop_name, name))
continue;
if ((dev != DDI_DEV_T_ANY) && (propp->prop_dev != dev))
continue;
if (((propp->prop_flags & flags) & DDI_PROP_TYPE_MASK) == 0)
continue;
return (propp);
}
return ((ddi_prop_t *)0);
}
ddi_prop_t *
i_ddi_search_global_prop(dev_t dev, char *name, uint_t flags)
{
major_t major;
struct devnames *dnp;
ddi_prop_t *propp;
ASSERT(dev != DDI_DEV_T_ANY);
if (dev == DDI_DEV_T_ANY) {
return ((ddi_prop_t *)0);
}
major = getmajor(dev);
dnp = &(devnamesp[major]);
if (dnp->dn_global_prop_ptr == NULL)
return ((ddi_prop_t *)0);
LOCK_DEV_OPS(&dnp->dn_lock);
for (propp = dnp->dn_global_prop_ptr->prop_list;
propp != NULL;
propp = (ddi_prop_t *)propp->prop_next) {
if (!DDI_STRSAME(propp->prop_name, name))
continue;
if ((!(flags & DDI_PROP_ROOTNEX_GLOBAL)) &&
(!(flags & LDI_DEV_T_ANY)) && (propp->prop_dev != dev))
continue;
if (((propp->prop_flags & flags) & DDI_PROP_TYPE_MASK) == 0)
continue;
UNLOCK_DEV_OPS(&dnp->dn_lock);
return (propp);
}
UNLOCK_DEV_OPS(&dnp->dn_lock);
return ((ddi_prop_t *)0);
}
static char prop_no_mem_msg[] = "can't allocate memory for ddi property <%s>";
static int
i_ddi_prop_search_global(dev_t dev, uint_t flags, char *name,
void *valuep, uint_t *lengthp)
{
ddi_prop_t *propp;
caddr_t buffer;
propp = i_ddi_search_global_prop(dev, name, flags);
if (propp == (ddi_prop_t *)0)
return (DDI_PROP_NOT_FOUND);
if (propp->prop_flags & DDI_PROP_UNDEF_IT)
return (DDI_PROP_UNDEFINED);
if ((buffer = kmem_alloc(propp->prop_len,
(flags & DDI_PROP_CANSLEEP) ? KM_SLEEP : KM_NOSLEEP)) == NULL) {
cmn_err(CE_CONT, prop_no_mem_msg, name);
return (DDI_PROP_NO_MEMORY);
}
*(caddr_t *)valuep = buffer;
*lengthp = propp->prop_len;
bcopy(propp->prop_val, buffer, propp->prop_len);
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_search_common(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
uint_t flags, char *name, void *valuep, uint_t *lengthp)
{
ddi_prop_t *propp;
int i;
caddr_t buffer = NULL;
caddr_t prealloc = NULL;
int plength = 0;
dev_info_t *pdip;
int (*bop)();
while (1) {
mutex_enter(&(DEVI(dip)->devi_lock));
propp = i_ddi_prop_search(dev, name, flags,
&(DEVI(dip)->devi_drv_prop_ptr));
if (propp == NULL) {
propp = i_ddi_prop_search(dev, name, flags,
&(DEVI(dip)->devi_sys_prop_ptr));
}
if ((propp == NULL) && DEVI(dip)->devi_global_prop_list) {
propp = i_ddi_prop_search(dev, name, flags,
&DEVI(dip)->devi_global_prop_list->prop_list);
}
if (propp == NULL) {
propp = i_ddi_prop_search(dev, name, flags,
&(DEVI(dip)->devi_hw_prop_ptr));
}
if (propp != (ddi_prop_t *)0) {
if (propp->prop_flags & DDI_PROP_UNDEF_IT) {
mutex_exit(&(DEVI(dip)->devi_lock));
if (prealloc)
kmem_free(prealloc, plength);
return (DDI_PROP_UNDEFINED);
}
if (prop_op == PROP_EXISTS) {
mutex_exit(&(DEVI(dip)->devi_lock));
ASSERT(prealloc == NULL);
return (DDI_PROP_SUCCESS);
}
if ((prop_op == PROP_LEN) ||(propp->prop_len == 0)) {
*lengthp = propp->prop_len;
if (prop_op == PROP_LEN_AND_VAL_ALLOC)
*(caddr_t *)valuep = NULL;
mutex_exit(&(DEVI(dip)->devi_lock));
if (prealloc)
kmem_free(prealloc, plength);
return (DDI_PROP_SUCCESS);
}
if ((prop_op == PROP_LEN_AND_VAL_ALLOC) &&
(flags & DDI_PROP_CANSLEEP)) {
if (prealloc && (propp->prop_len != plength)) {
kmem_free(prealloc, plength);
prealloc = NULL;
}
if (prealloc == NULL) {
plength = propp->prop_len;
mutex_exit(&(DEVI(dip)->devi_lock));
prealloc = kmem_alloc(plength,
KM_SLEEP);
continue;
}
}
i = *lengthp;
*lengthp = propp->prop_len;
switch (prop_op) {
case PROP_LEN_AND_VAL_ALLOC:
if (prealloc == NULL) {
buffer = kmem_alloc(propp->prop_len,
KM_NOSLEEP);
} else {
buffer = prealloc;
}
if (buffer == NULL) {
mutex_exit(&(DEVI(dip)->devi_lock));
cmn_err(CE_CONT, prop_no_mem_msg, name);
return (DDI_PROP_NO_MEMORY);
}
*(caddr_t *)valuep = buffer;
break;
case PROP_LEN_AND_VAL_BUF:
if (propp->prop_len > (i)) {
mutex_exit(&(DEVI(dip)->devi_lock));
return (DDI_PROP_BUF_TOO_SMALL);
}
buffer = valuep;
break;
default:
break;
}
if (buffer != NULL)
bcopy(propp->prop_val, buffer, propp->prop_len);
mutex_exit(&(DEVI(dip)->devi_lock));
return (DDI_PROP_SUCCESS);
}
mutex_exit(&(DEVI(dip)->devi_lock));
if (prealloc)
kmem_free(prealloc, plength);
prealloc = NULL;
pdip = (dev_info_t *)DEVI(dip)->devi_parent;
if (dip == ddi_root_node()) {
return (ddi_bus_prop_op(dev, dip, dip, prop_op,
flags, name, valuep, (int *)lengthp));
}
if (pdip == NULL) {
ASSERT((flags &
(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM)) ==
(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM));
return (DDI_PROP_NOT_FOUND);
}
bop = ddi_bus_prop_op;
if (i_ddi_devi_attached(pdip) &&
(i_ddi_node_state(dip) >= DS_INITIALIZED))
bop = DEVI(pdip)->devi_ops->devo_bus_ops->bus_prop_op;
i = DDI_PROP_NOT_FOUND;
if ((bop != ddi_bus_prop_op) || ndi_dev_is_prom_node(dip)) {
i = (*bop)(dev, pdip, dip, prop_op,
flags | DDI_PROP_DONTPASS,
name, valuep, lengthp);
}
if ((flags & DDI_PROP_DONTPASS) ||
(i != DDI_PROP_NOT_FOUND))
return (i);
dip = pdip;
}
}
int
ddi_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)
{
int i;
ASSERT((mod_flags & DDI_PROP_TYPE_MASK) == 0);
if (mod_flags & DDI_PROP_DYNAMIC)
return (DDI_PROP_NOT_FOUND);
if (mod_flags & DDI_PROP_CONSUMER_TYPED)
mod_flags |= DDI_PROP_TYPE_INT64;
mod_flags |= DDI_PROP_TYPE_ANY;
i = ddi_prop_search_common(dev, dip, prop_op,
mod_flags, name, valuep, (uint_t *)lengthp);
if (i == DDI_PROP_FOUND_1275)
return (DDI_PROP_SUCCESS);
return (i);
}
int
ddi_prop_op_nblocks_blksize(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int mod_flags, char *name, caddr_t valuep, int *lengthp,
uint64_t nblocks64, uint_t blksize)
{
uint64_t size64;
int blkshift;
ASSERT(BIT_ONLYONESET(blksize));
blkshift = highbit(blksize) - 1;
if (nblocks64 >= (UINT64_MAX >> blkshift))
return (ddi_prop_op(dev, dip, prop_op, mod_flags,
name, valuep, lengthp));
size64 = nblocks64 << blkshift;
return (ddi_prop_op_size_blksize(dev, dip, prop_op, mod_flags,
name, valuep, lengthp, size64, blksize));
}
int
ddi_prop_op_nblocks(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int mod_flags, char *name, caddr_t valuep, int *lengthp, uint64_t nblocks64)
{
return (ddi_prop_op_nblocks_blksize(dev, dip, prop_op,
mod_flags, name, valuep, lengthp, nblocks64, DEV_BSIZE));
}
int
ddi_prop_op_size_blksize(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int mod_flags, char *name, caddr_t valuep, int *lengthp, uint64_t size64,
uint_t blksize)
{
uint64_t nblocks64;
int callers_length;
caddr_t buffer;
int blkshift;
if (i_ddi_prop_dyn_driver_get(dip) == NULL) {
static i_ddi_prop_dyn_t prop_dyn_size[] = {
{"Size", DDI_PROP_TYPE_INT64, S_IFCHR},
{"Nblocks", DDI_PROP_TYPE_INT64, S_IFBLK},
{NULL}
};
i_ddi_prop_dyn_driver_set(dip, prop_dyn_size);
}
ASSERT(BIT_ONLYONESET(blksize));
blkshift = highbit(blksize) - 1;
nblocks64 = size64 >> blkshift;
callers_length = *lengthp;
if (strcmp(name, "Nblocks") == 0)
*lengthp = sizeof (uint64_t);
else if (strcmp(name, "Size") == 0)
*lengthp = sizeof (uint64_t);
else if ((strcmp(name, "nblocks") == 0) && (nblocks64 < UINT_MAX))
*lengthp = sizeof (uint32_t);
else if ((strcmp(name, "size") == 0) && (size64 < UINT_MAX))
*lengthp = sizeof (uint32_t);
else if ((strcmp(name, "blksize") == 0) && (blksize < UINT_MAX))
*lengthp = sizeof (uint32_t);
else {
return (ddi_prop_op(dev, dip, prop_op, mod_flags,
name, valuep, lengthp));
}
if (prop_op == PROP_LEN)
return (DDI_PROP_SUCCESS);
switch (prop_op) {
case PROP_LEN_AND_VAL_ALLOC:
if ((buffer = kmem_alloc(*lengthp,
(mod_flags & DDI_PROP_CANSLEEP) ?
KM_SLEEP : KM_NOSLEEP)) == NULL)
return (DDI_PROP_NO_MEMORY);
*(caddr_t *)valuep = buffer;
break;
case PROP_LEN_AND_VAL_BUF:
if (callers_length != *lengthp)
return (DDI_PROP_INVAL_ARG);
buffer = valuep;
break;
default:
return (DDI_PROP_INVAL_ARG);
}
if (strcmp(name, "Nblocks") == 0)
*((uint64_t *)buffer) = nblocks64;
else if (strcmp(name, "Size") == 0)
*((uint64_t *)buffer) = size64;
else if (strcmp(name, "nblocks") == 0)
*((uint32_t *)buffer) = (uint32_t)nblocks64;
else if (strcmp(name, "size") == 0)
*((uint32_t *)buffer) = (uint32_t)size64;
else if (strcmp(name, "blksize") == 0)
*((uint32_t *)buffer) = (uint32_t)blksize;
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_op_size(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int mod_flags, char *name, caddr_t valuep, int *lengthp, uint64_t size64)
{
return (ddi_prop_op_size_blksize(dev, dip, prop_op,
mod_flags, name, valuep, lengthp, size64, DEV_BSIZE));
}
int
ddi_getlongprop(dev_t dev, dev_info_t *dip, int flags,
char *name, caddr_t valuep, int *lengthp)
{
return (ddi_prop_op(dev, dip, PROP_LEN_AND_VAL_ALLOC,
flags, name, valuep, lengthp));
}
int
ddi_getlongprop_buf(dev_t dev, dev_info_t *dip, int flags,
char *name, caddr_t valuep, int *lengthp)
{
return (ddi_prop_op(dev, dip, PROP_LEN_AND_VAL_BUF,
flags, name, valuep, lengthp));
}
int
ddi_getprop(dev_t dev, dev_info_t *dip, int flags, char *name, int defvalue)
{
int propvalue = defvalue;
int proplength = sizeof (int);
int error;
error = ddi_prop_op(dev, dip, PROP_LEN_AND_VAL_BUF,
flags, name, (caddr_t)&propvalue, &proplength);
if ((error == DDI_PROP_SUCCESS) && (proplength == 0))
propvalue = 1;
return (propvalue);
}
int
ddi_getproplen(dev_t dev, dev_info_t *dip, int flags, char *name, int *lengthp)
{
return (ddi_prop_op(dev, dip, PROP_LEN, flags, name, NULL, lengthp));
}
static void *
ddi_prop_decode_alloc(size_t size, void (*prop_free)(struct prop_driver_data *))
{
struct prop_driver_data *pdd;
pdd = kmem_zalloc(sizeof (struct prop_driver_data) + size, KM_SLEEP);
pdd->pdd_size = (sizeof (struct prop_driver_data) + size);
pdd->pdd_prop_free = prop_free;
return ((void *)((caddr_t)pdd + sizeof (struct prop_driver_data)));
}
static int
ddi_prop_encode_alloc(prop_handle_t *ph, size_t size)
{
if (size == 0) {
ph->ph_size = 0;
ph->ph_data = NULL;
ph->ph_cur_pos = NULL;
ph->ph_save_pos = NULL;
} else {
if (ph->ph_flags == DDI_PROP_DONTSLEEP) {
ph->ph_data = kmem_zalloc(size, KM_NOSLEEP);
if (ph->ph_data == NULL)
return (DDI_PROP_NO_MEMORY);
} else
ph->ph_data = kmem_zalloc(size, KM_SLEEP);
ph->ph_size = size;
ph->ph_cur_pos = ph->ph_data;
ph->ph_save_pos = ph->ph_data;
}
return (DDI_PROP_SUCCESS);
}
void
ddi_prop_free(void *datap)
{
struct prop_driver_data *pdd;
pdd = (struct prop_driver_data *)
((caddr_t)datap - sizeof (struct prop_driver_data));
(*pdd->pdd_prop_free)(pdd);
}
static void
ddi_prop_free_ints(struct prop_driver_data *pdd)
{
kmem_free(pdd, pdd->pdd_size);
}
static void
ddi_prop_free_string(struct prop_driver_data *pdd)
{
kmem_free(pdd, pdd->pdd_size);
}
static void
ddi_prop_free_strings(struct prop_driver_data *pdd)
{
kmem_free(pdd, pdd->pdd_size);
}
static void
ddi_prop_free_bytes(struct prop_driver_data *pdd)
{
kmem_free(pdd, pdd->pdd_size);
}
void
ddi_prop_reset_pos(prop_handle_t *ph)
{
ph->ph_cur_pos = ph->ph_data;
ph->ph_save_pos = ph->ph_data;
}
void
ddi_prop_save_pos(prop_handle_t *ph)
{
ph->ph_save_pos = ph->ph_cur_pos;
}
void
ddi_prop_restore_pos(prop_handle_t *ph)
{
ph->ph_cur_pos = ph->ph_save_pos;
}
static int
ddi_prop_fm_decode_int(prop_handle_t *ph, void *data, uint_t *nelements)
{
int i;
int tmp;
if (ph->ph_size == 0)
return (DDI_PROP_END_OF_DATA);
i = DDI_PROP_INT(ph, DDI_PROP_CMD_DECODE, &tmp);
if (i < DDI_PROP_RESULT_OK) {
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
*(int *)data = tmp;
*nelements = 1;
return (DDI_PROP_SUCCESS);
}
static int
ddi_prop_fm_decode_int64(prop_handle_t *ph, void *data, uint_t *nelements)
{
int i;
int64_t tmp;
if (ph->ph_size == 0)
return (DDI_PROP_END_OF_DATA);
i = DDI_PROP_INT64(ph, DDI_PROP_CMD_DECODE, &tmp);
if (i < DDI_PROP_RESULT_OK) {
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
*(int64_t *)data = tmp;
*nelements = 1;
return (DDI_PROP_SUCCESS);
}
static int
ddi_prop_fm_decode_ints(prop_handle_t *ph, void *data, uint_t *nelements)
{
int i;
int cnt = 0;
int *tmp;
int *intp;
int n;
for (;;) {
i = DDI_PROP_INT(ph, DDI_PROP_CMD_SKIP, NULL);
if (i < 0)
break;
cnt++;
}
if (cnt == 0)
return (DDI_PROP_END_OF_DATA);
if (i == DDI_PROP_RESULT_ERROR)
return (DDI_PROP_CANNOT_DECODE);
ddi_prop_reset_pos(ph);
intp = ddi_prop_decode_alloc((cnt * sizeof (int)),
ddi_prop_free_ints);
tmp = intp;
for (n = 0; n < cnt; n++, tmp++) {
i = DDI_PROP_INT(ph, DDI_PROP_CMD_DECODE, tmp);
if (i < DDI_PROP_RESULT_OK) {
ddi_prop_free(intp);
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
}
*nelements = cnt;
*(int **)data = intp;
return (DDI_PROP_SUCCESS);
}
static int
ddi_prop_fm_decode_int64_array(prop_handle_t *ph, void *data, uint_t *nelements)
{
int i;
int n;
int cnt = 0;
int64_t *tmp;
int64_t *intp;
for (;;) {
i = DDI_PROP_INT64(ph, DDI_PROP_CMD_SKIP, NULL);
if (i < 0)
break;
cnt++;
}
if (cnt == 0)
return (DDI_PROP_END_OF_DATA);
if (i == DDI_PROP_RESULT_ERROR)
return (DDI_PROP_CANNOT_DECODE);
ddi_prop_reset_pos(ph);
intp = ddi_prop_decode_alloc((cnt * sizeof (int64_t)),
ddi_prop_free_ints);
tmp = intp;
for (n = 0; n < cnt; n++, tmp++) {
i = DDI_PROP_INT64(ph, DDI_PROP_CMD_DECODE, tmp);
if (i < DDI_PROP_RESULT_OK) {
ddi_prop_free(intp);
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
}
*nelements = cnt;
*(int64_t **)data = intp;
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_fm_encode_ints(prop_handle_t *ph, void *data, uint_t nelements)
{
int i;
int *tmp;
int cnt;
int size;
if (nelements == 0)
return (DDI_PROP_CANNOT_ENCODE);
size = DDI_PROP_INT(ph, DDI_PROP_CMD_GET_ESIZE, NULL);
if (size < DDI_PROP_RESULT_OK) {
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
if (ddi_prop_encode_alloc(ph, size * nelements) !=
DDI_PROP_SUCCESS)
return (DDI_PROP_NO_MEMORY);
tmp = (int *)data;
for (cnt = 0; cnt < nelements; cnt++, tmp++) {
i = DDI_PROP_INT(ph, DDI_PROP_CMD_ENCODE, tmp);
if (i < DDI_PROP_RESULT_OK) {
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
}
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_fm_encode_int64(prop_handle_t *ph, void *data, uint_t nelements)
{
int i;
int cnt;
int size;
int64_t *tmp;
if (nelements == 0)
return (DDI_PROP_CANNOT_ENCODE);
size = DDI_PROP_INT64(ph, DDI_PROP_CMD_GET_ESIZE, NULL);
if (size < DDI_PROP_RESULT_OK) {
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
if (ddi_prop_encode_alloc(ph, size * nelements) !=
DDI_PROP_SUCCESS)
return (DDI_PROP_NO_MEMORY);
tmp = (int64_t *)data;
for (cnt = 0; cnt < nelements; cnt++, tmp++) {
i = DDI_PROP_INT64(ph, DDI_PROP_CMD_ENCODE, tmp);
if (i < DDI_PROP_RESULT_OK) {
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
}
return (DDI_PROP_SUCCESS);
}
static int
ddi_prop_fm_decode_string(prop_handle_t *ph, void *data, uint_t *nelements)
{
char *tmp;
char *str;
int i;
int size;
if (ph->ph_size == 0)
return (DDI_PROP_END_OF_DATA);
size = DDI_PROP_STR(ph, DDI_PROP_CMD_GET_DSIZE, NULL);
if (size < DDI_PROP_RESULT_OK) {
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
str = ddi_prop_decode_alloc((size_t)size, ddi_prop_free_string);
ddi_prop_reset_pos(ph);
tmp = str;
i = DDI_PROP_STR(ph, DDI_PROP_CMD_DECODE, tmp);
if (i < DDI_PROP_RESULT_OK) {
ddi_prop_free(str);
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
*(char **)data = str;
*nelements = 1;
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_fm_decode_strings(prop_handle_t *ph, void *data, uint_t *nelements)
{
int cnt = 0;
char **strs;
char **tmp;
char *ptr;
int i;
int n;
int size;
size_t nbytes;
for (;;) {
i = DDI_PROP_STR(ph, DDI_PROP_CMD_SKIP, NULL);
if (i < 0)
break;
cnt++;
}
if (cnt == 0)
return (DDI_PROP_END_OF_DATA);
if (i == DDI_PROP_RESULT_ERROR)
return (DDI_PROP_CANNOT_DECODE);
ddi_prop_reset_pos(ph);
nbytes = (cnt + 1) * sizeof (char *);
for (n = 0; n < cnt; n++) {
size = DDI_PROP_STR(ph, DDI_PROP_CMD_GET_DSIZE, NULL);
if (size < DDI_PROP_RESULT_OK) {
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
nbytes += size;
}
strs = ddi_prop_decode_alloc(nbytes, ddi_prop_free_strings);
ddi_prop_reset_pos(ph);
ptr = (caddr_t)strs + ((cnt + 1) * sizeof (char *));
for (tmp = strs, n = 0; n < cnt; n++, tmp++) {
size = DDI_PROP_STR(ph, DDI_PROP_CMD_GET_DSIZE, NULL);
if (size < DDI_PROP_RESULT_OK) {
ddi_prop_free(strs);
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
*tmp = ptr;
ptr += size;
}
*tmp = NULL;
ddi_prop_reset_pos(ph);
for (tmp = strs, n = 0; n < cnt; n++, tmp++) {
i = DDI_PROP_STR(ph, DDI_PROP_CMD_DECODE, *tmp);
if (i < DDI_PROP_RESULT_OK) {
ddi_prop_free(strs);
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
}
*(char ***)data = strs;
*nelements = cnt;
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_fm_encode_string(prop_handle_t *ph, void *data, uint_t nelements)
{
char **tmp;
int size;
int i;
if (nelements == 0)
return (DDI_PROP_CANNOT_ENCODE);
tmp = (char **)data;
size = DDI_PROP_STR(ph, DDI_PROP_CMD_GET_ESIZE, *tmp);
if (size < DDI_PROP_RESULT_OK) {
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
if (ddi_prop_encode_alloc(ph, size) != DDI_PROP_SUCCESS)
return (DDI_PROP_NO_MEMORY);
ddi_prop_reset_pos(ph);
tmp = (char **)data;
i = DDI_PROP_STR(ph, DDI_PROP_CMD_ENCODE, *tmp);
if (i < DDI_PROP_RESULT_OK) {
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_fm_encode_strings(prop_handle_t *ph, void *data, uint_t nelements)
{
int cnt = 0;
char **tmp;
int size;
uint_t total_size;
int i;
if (nelements == 0)
return (DDI_PROP_CANNOT_ENCODE);
total_size = 0;
tmp = (char **)data;
for (cnt = 0; cnt < nelements; cnt++, tmp++) {
size = DDI_PROP_STR(ph, DDI_PROP_CMD_GET_ESIZE, *tmp);
if (size < DDI_PROP_RESULT_OK) {
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
total_size += (uint_t)size;
}
if (ddi_prop_encode_alloc(ph, total_size) != DDI_PROP_SUCCESS)
return (DDI_PROP_NO_MEMORY);
ddi_prop_reset_pos(ph);
tmp = (char **)data;
for (cnt = 0; cnt < nelements; cnt++, tmp++) {
i = DDI_PROP_STR(ph, DDI_PROP_CMD_ENCODE, *tmp);
if (i < DDI_PROP_RESULT_OK) {
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
}
return (DDI_PROP_SUCCESS);
}
static int
ddi_prop_fm_decode_bytes(prop_handle_t *ph, void *data, uint_t *nelements)
{
uchar_t *tmp;
int nbytes;
int i;
if (ph->ph_size == 0)
return (DDI_PROP_END_OF_DATA);
nbytes = DDI_PROP_BYTES(ph, DDI_PROP_CMD_GET_DSIZE,
data, ph->ph_size);
if (nbytes < DDI_PROP_RESULT_OK) {
switch (nbytes) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
tmp = ddi_prop_decode_alloc(nbytes, ddi_prop_free_bytes);
i = DDI_PROP_BYTES(ph, DDI_PROP_CMD_DECODE, tmp, nbytes);
if (i < DDI_PROP_RESULT_OK) {
ddi_prop_free(tmp);
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
*(uchar_t **)data = tmp;
*nelements = nbytes;
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_fm_encode_bytes(prop_handle_t *ph, void *data, uint_t nelements)
{
int size;
int i;
if (nelements == 0) {
(void) ddi_prop_encode_alloc(ph, 0);
return (DDI_PROP_SUCCESS);
}
size = DDI_PROP_BYTES(ph, DDI_PROP_CMD_GET_ESIZE, (uchar_t *)data,
nelements);
if (size < DDI_PROP_RESULT_OK) {
switch (size) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_DECODE);
}
}
if (ddi_prop_encode_alloc(ph, (uint_t)size) != DDI_PROP_SUCCESS)
return (DDI_PROP_NO_MEMORY);
i = DDI_PROP_BYTES(ph, DDI_PROP_CMD_ENCODE, (uchar_t *)data,
nelements);
if (i < DDI_PROP_RESULT_OK) {
switch (i) {
case DDI_PROP_RESULT_EOF:
return (DDI_PROP_END_OF_DATA);
case DDI_PROP_RESULT_ERROR:
return (DDI_PROP_CANNOT_ENCODE);
}
}
return (DDI_PROP_SUCCESS);
}
int
ddi_prop_1275_int(prop_handle_t *ph, uint_t cmd, int *data)
{
int i;
switch (cmd) {
case DDI_PROP_CMD_DECODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0)
return (DDI_PROP_RESULT_ERROR);
if (ph->ph_flags & PH_FROM_PROM) {
i = MIN(ph->ph_size, PROP_1275_INT_SIZE);
if ((int *)ph->ph_cur_pos > ((int *)ph->ph_data +
ph->ph_size - i))
return (DDI_PROP_RESULT_ERROR);
} else {
if (ph->ph_size < sizeof (int) ||
((int *)ph->ph_cur_pos > ((int *)ph->ph_data +
ph->ph_size - sizeof (int))))
return (DDI_PROP_RESULT_ERROR);
}
if (ph->ph_flags & PH_FROM_PROM) {
*data = impl_ddi_prop_int_from_prom(
(uchar_t *)ph->ph_cur_pos,
(ph->ph_size < PROP_1275_INT_SIZE) ?
ph->ph_size : PROP_1275_INT_SIZE);
} else {
bcopy(ph->ph_cur_pos, data, sizeof (int));
}
ph->ph_cur_pos = (uchar_t *)ph->ph_cur_pos +
PROP_1275_INT_SIZE;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_ENCODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0 ||
ph->ph_size < PROP_1275_INT_SIZE ||
((int *)ph->ph_cur_pos > ((int *)ph->ph_data +
ph->ph_size - sizeof (int))))
return (DDI_PROP_RESULT_ERROR);
bcopy(data, ph->ph_cur_pos, sizeof (int));
ph->ph_cur_pos = (uchar_t *)ph->ph_cur_pos + PROP_1275_INT_SIZE;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_SKIP:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0 ||
ph->ph_size < PROP_1275_INT_SIZE)
return (DDI_PROP_RESULT_ERROR);
if ((caddr_t)ph->ph_cur_pos ==
(caddr_t)ph->ph_data + ph->ph_size) {
return (DDI_PROP_RESULT_EOF);
} else if ((caddr_t)ph->ph_cur_pos >
(caddr_t)ph->ph_data + ph->ph_size) {
return (DDI_PROP_RESULT_EOF);
}
ph->ph_cur_pos = (uchar_t *)ph->ph_cur_pos + PROP_1275_INT_SIZE;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_GET_ESIZE:
return (PROP_1275_INT_SIZE);
case DDI_PROP_CMD_GET_DSIZE:
return (sizeof (int));
default:
#ifdef DEBUG
panic("ddi_prop_1275_int: %x impossible", cmd);
#else
return (DDI_PROP_RESULT_ERROR);
#endif
}
}
int
ddi_prop_int64_op(prop_handle_t *ph, uint_t cmd, int64_t *data)
{
switch (cmd) {
case DDI_PROP_CMD_DECODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0)
return (DDI_PROP_RESULT_ERROR);
if (ph->ph_flags & PH_FROM_PROM) {
return (DDI_PROP_RESULT_ERROR);
} else {
if (ph->ph_size < sizeof (int64_t) ||
((int64_t *)ph->ph_cur_pos >
((int64_t *)ph->ph_data +
ph->ph_size - sizeof (int64_t))))
return (DDI_PROP_RESULT_ERROR);
}
if (ph->ph_flags & PH_FROM_PROM) {
return (DDI_PROP_RESULT_ERROR);
} else {
bcopy(ph->ph_cur_pos, data, sizeof (int64_t));
}
ph->ph_cur_pos = (uchar_t *)ph->ph_cur_pos +
sizeof (int64_t);
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_ENCODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0 ||
ph->ph_size < sizeof (int64_t) ||
((int64_t *)ph->ph_cur_pos > ((int64_t *)ph->ph_data +
ph->ph_size - sizeof (int64_t))))
return (DDI_PROP_RESULT_ERROR);
bcopy(data, ph->ph_cur_pos, sizeof (int64_t));
ph->ph_cur_pos = (uchar_t *)ph->ph_cur_pos +
sizeof (int64_t);
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_SKIP:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0 ||
ph->ph_size < sizeof (int64_t))
return (DDI_PROP_RESULT_ERROR);
if ((caddr_t)ph->ph_cur_pos ==
(caddr_t)ph->ph_data + ph->ph_size) {
return (DDI_PROP_RESULT_EOF);
} else if ((caddr_t)ph->ph_cur_pos >
(caddr_t)ph->ph_data + ph->ph_size) {
return (DDI_PROP_RESULT_EOF);
}
ph->ph_cur_pos = (uchar_t *)ph->ph_cur_pos +
sizeof (int64_t);
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_GET_ESIZE:
return (sizeof (int64_t));
case DDI_PROP_CMD_GET_DSIZE:
return (sizeof (int64_t));
default:
#ifdef DEBUG
panic("ddi_prop_int64_op: %x impossible", cmd);
#else
return (DDI_PROP_RESULT_ERROR);
#endif
}
}
int
ddi_prop_1275_string(prop_handle_t *ph, uint_t cmd, char *data)
{
int n;
char *p;
char *end;
switch (cmd) {
case DDI_PROP_CMD_DECODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0) {
return (DDI_PROP_RESULT_ERROR);
}
p = (char *)ph->ph_cur_pos;
end = (char *)ph->ph_data + ph->ph_size;
if (p >= end)
return (DDI_PROP_RESULT_EOF);
while (p < end) {
*data++ = *p;
if (*p++ == 0) {
ph->ph_cur_pos = p;
return (DDI_PROP_RESULT_OK);
}
}
ph->ph_cur_pos = p;
*data = 0;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_ENCODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0) {
return (DDI_PROP_RESULT_ERROR);
}
n = strlen(data) + 1;
if ((char *)ph->ph_cur_pos > ((char *)ph->ph_data +
ph->ph_size - n)) {
return (DDI_PROP_RESULT_ERROR);
}
bcopy(data, ph->ph_cur_pos, n);
ph->ph_cur_pos = (char *)ph->ph_cur_pos + n;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_SKIP:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0) {
return (DDI_PROP_RESULT_ERROR);
}
p = (char *)ph->ph_cur_pos;
end = (char *)ph->ph_data + ph->ph_size;
if (p >= end)
return (DDI_PROP_RESULT_EOF);
while (p < end) {
if (*p++ == 0) {
ph->ph_cur_pos = p;
return (DDI_PROP_RESULT_OK);
}
}
ph->ph_cur_pos = p;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_GET_ESIZE:
return (strlen(data) + 1);
case DDI_PROP_CMD_GET_DSIZE:
p = (char *)ph->ph_cur_pos;
end = (char *)ph->ph_data + ph->ph_size;
if (p >= end)
return (DDI_PROP_RESULT_EOF);
for (n = 0; p < end; n++) {
if (*p++ == 0) {
ph->ph_cur_pos = p;
return (n + 1);
}
}
ph->ph_cur_pos = p;
return (n + 1);
default:
#ifdef DEBUG
panic("ddi_prop_1275_string: %x impossible", cmd);
#else
return (DDI_PROP_RESULT_ERROR);
#endif
}
}
int
ddi_prop_1275_bytes(prop_handle_t *ph, uint_t cmd, uchar_t *data,
uint_t nelements)
{
switch (cmd) {
case DDI_PROP_CMD_DECODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0 ||
ph->ph_size < nelements ||
((char *)ph->ph_cur_pos > ((char *)ph->ph_data +
ph->ph_size - nelements)))
return (DDI_PROP_RESULT_ERROR);
bcopy(ph->ph_cur_pos, data, nelements);
ph->ph_cur_pos = (char *)ph->ph_cur_pos + nelements;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_ENCODE:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0 ||
ph->ph_size < nelements ||
((char *)ph->ph_cur_pos > ((char *)ph->ph_data +
ph->ph_size - nelements)))
return (DDI_PROP_RESULT_ERROR);
bcopy(data, ph->ph_cur_pos, nelements);
ph->ph_cur_pos = (char *)ph->ph_cur_pos + nelements;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_SKIP:
if (ph->ph_cur_pos == NULL || ph->ph_size == 0 ||
ph->ph_size < nelements)
return (DDI_PROP_RESULT_ERROR);
if ((char *)ph->ph_cur_pos > ((char *)ph->ph_data +
ph->ph_size - nelements))
return (DDI_PROP_RESULT_EOF);
ph->ph_cur_pos = (char *)ph->ph_cur_pos + nelements;
return (DDI_PROP_RESULT_OK);
case DDI_PROP_CMD_GET_ESIZE:
return (nelements);
case DDI_PROP_CMD_GET_DSIZE:
return (nelements);
default:
#ifdef DEBUG
panic("ddi_prop_1275_bytes: %x impossible", cmd);
#else
return (DDI_PROP_RESULT_ERROR);
#endif
}
}
static struct prop_handle_ops prop_1275_ops = {
ddi_prop_1275_int,
ddi_prop_1275_string,
ddi_prop_1275_bytes,
ddi_prop_int64_op
};
#define DDI_NEW_PROP_T(km_flags) \
(kmem_zalloc(sizeof (ddi_prop_t), km_flags))
static int
ddi_prop_add(dev_t dev, dev_info_t *dip, int flags,
char *name, caddr_t value, int length)
{
ddi_prop_t *new_propp, *propp;
ddi_prop_t **list_head = &(DEVI(dip)->devi_drv_prop_ptr);
int km_flags = KM_NOSLEEP;
int name_buf_len;
if (dev == DDI_DEV_T_ANY || name == (char *)0 || strlen(name) == 0)
return (DDI_PROP_INVAL_ARG);
if (flags & DDI_PROP_CANSLEEP)
km_flags = KM_SLEEP;
if (flags & DDI_PROP_SYSTEM_DEF)
list_head = &(DEVI(dip)->devi_sys_prop_ptr);
else if (flags & DDI_PROP_HW_DEF)
list_head = &(DEVI(dip)->devi_hw_prop_ptr);
if ((new_propp = DDI_NEW_PROP_T(km_flags)) == NULL) {
cmn_err(CE_CONT, prop_no_mem_msg, name);
return (DDI_PROP_NO_MEMORY);
}
if (getmajor(dev) == DDI_MAJOR_T_UNKNOWN) {
new_propp->prop_dev =
makedevice(ddi_name_to_major(DEVI(dip)->devi_binding_name),
getminor(dev));
} else
new_propp->prop_dev = dev;
name_buf_len = strlen(name) + 1;
new_propp->prop_name = kmem_alloc(name_buf_len, km_flags);
if (new_propp->prop_name == 0) {
kmem_free(new_propp, sizeof (ddi_prop_t));
cmn_err(CE_CONT, prop_no_mem_msg, name);
return (DDI_PROP_NO_MEMORY);
}
bcopy(name, new_propp->prop_name, name_buf_len);
new_propp->prop_flags = flags & DDI_PROP_TYPE_MASK;
if (flags & DDI_PROP_UNDEF_IT) {
new_propp->prop_flags |= DDI_PROP_UNDEF_IT;
} else {
if ((new_propp->prop_len = length) != 0) {
new_propp->prop_val = kmem_alloc(length, km_flags);
if (new_propp->prop_val == 0) {
kmem_free(new_propp->prop_name, name_buf_len);
kmem_free(new_propp, sizeof (ddi_prop_t));
cmn_err(CE_CONT, prop_no_mem_msg, name);
return (DDI_PROP_NO_MEMORY);
}
bcopy(value, new_propp->prop_val, length);
}
}
mutex_enter(&(DEVI(dip)->devi_lock));
propp = *list_head;
new_propp->prop_next = propp;
*list_head = new_propp;
mutex_exit(&(DEVI(dip)->devi_lock));
return (DDI_PROP_SUCCESS);
}
static int
ddi_prop_change(dev_t dev, dev_info_t *dip, int flags,
char *name, caddr_t value, int length)
{
ddi_prop_t *propp;
ddi_prop_t **ppropp;
caddr_t p = NULL;
if ((dev == DDI_DEV_T_ANY) || (name == NULL) || (strlen(name) == 0))
return (DDI_PROP_INVAL_ARG);
if (length != 0) {
p = kmem_alloc(length, (flags & DDI_PROP_CANSLEEP) ?
KM_SLEEP : KM_NOSLEEP);
if (p == NULL) {
cmn_err(CE_CONT, prop_no_mem_msg, name);
return (DDI_PROP_NO_MEMORY);
}
}
if (getmajor(dev) == DDI_MAJOR_T_UNKNOWN)
dev = makedevice(
ddi_name_to_major(DEVI(dip)->devi_binding_name),
getminor(dev));
mutex_enter(&(DEVI(dip)->devi_lock));
ppropp = &DEVI(dip)->devi_drv_prop_ptr;
if (flags & DDI_PROP_SYSTEM_DEF)
ppropp = &DEVI(dip)->devi_sys_prop_ptr;
else if (flags & DDI_PROP_HW_DEF)
ppropp = &DEVI(dip)->devi_hw_prop_ptr;
if ((propp = i_ddi_prop_search(dev, name, flags, ppropp)) != NULL) {
if (length != 0)
bcopy(value, p, length);
if (propp->prop_len != 0)
kmem_free(propp->prop_val, propp->prop_len);
propp->prop_len = length;
propp->prop_val = p;
propp->prop_flags &= ~DDI_PROP_UNDEF_IT;
mutex_exit(&(DEVI(dip)->devi_lock));
return (DDI_PROP_SUCCESS);
}
mutex_exit(&(DEVI(dip)->devi_lock));
if (length != 0)
kmem_free(p, length);
return (ddi_prop_add(dev, dip, flags, name, value, length));
}
int
ddi_prop_update_common(dev_t match_dev, dev_info_t *dip, int flags,
char *name, void *data, uint_t nelements,
int (*prop_create)(prop_handle_t *, void *data, uint_t nelements))
{
prop_handle_t ph;
int rval;
uint_t ourflags;
if (match_dev == DDI_DEV_T_ANY || name == NULL || strlen(name) == 0)
return (DDI_PROP_INVAL_ARG);
ph.ph_data = NULL;
ph.ph_cur_pos = NULL;
ph.ph_save_pos = NULL;
ph.ph_size = 0;
ph.ph_ops = &prop_1275_ops;
if (flags & DDI_PROP_DONTSLEEP) {
ourflags = flags;
ph.ph_flags = DDI_PROP_DONTSLEEP;
} else {
ourflags = flags | DDI_PROP_CANSLEEP;
ph.ph_flags = DDI_PROP_CANSLEEP;
}
if ((rval = (*prop_create)(&ph, data, nelements)) !=
DDI_PROP_SUCCESS) {
if (rval == DDI_PROP_NO_MEMORY)
cmn_err(CE_CONT, prop_no_mem_msg, name);
if (ph.ph_size != 0)
kmem_free(ph.ph_data, ph.ph_size);
return (rval);
}
if (flags & DDI_PROP_STACK_CREATE) {
rval = ddi_prop_add(match_dev, dip,
ourflags, name, ph.ph_data, ph.ph_size);
} else {
rval = ddi_prop_change(match_dev, dip,
ourflags, name, ph.ph_data, ph.ph_size);
}
if (ph.ph_size != 0)
kmem_free(ph.ph_data, ph.ph_size);
return (rval);
}
int
ddi_prop_create(dev_t dev, dev_info_t *dip, int flag,
char *name, caddr_t value, int length)
{
if (!(flag & DDI_PROP_CANSLEEP)) {
flag |= DDI_PROP_DONTSLEEP;
#ifdef DDI_PROP_DEBUG
if (length != 0)
cmn_err(CE_NOTE, "!ddi_prop_create: interface obsolete,"
"use ddi_prop_update (prop = %s, node = %s%d)",
name, ddi_driver_name(dip), ddi_get_instance(dip));
#endif
}
flag &= ~DDI_PROP_SYSTEM_DEF;
flag |= DDI_PROP_STACK_CREATE | DDI_PROP_TYPE_ANY;
return (ddi_prop_update_common(dev, dip, flag, name,
value, length, ddi_prop_fm_encode_bytes));
}
int
e_ddi_prop_create(dev_t dev, dev_info_t *dip, int flag,
char *name, caddr_t value, int length)
{
if (!(flag & DDI_PROP_CANSLEEP))
flag |= DDI_PROP_DONTSLEEP;
flag |= DDI_PROP_SYSTEM_DEF | DDI_PROP_STACK_CREATE | DDI_PROP_TYPE_ANY;
return (ddi_prop_update_common(dev, dip, flag,
name, value, length, ddi_prop_fm_encode_bytes));
}
int
ddi_prop_modify(dev_t dev, dev_info_t *dip, int flag,
char *name, caddr_t value, int length)
{
ASSERT((flag & DDI_PROP_TYPE_MASK) == 0);
if (dev == DDI_DEV_T_ANY || name == NULL || strlen(name) == 0)
return (DDI_PROP_INVAL_ARG);
if (!(flag & DDI_PROP_CANSLEEP))
flag |= DDI_PROP_DONTSLEEP;
flag &= ~DDI_PROP_SYSTEM_DEF;
if (ddi_prop_exists(dev, dip, (flag | DDI_PROP_NOTPROM), name) == 0)
return (DDI_PROP_NOT_FOUND);
return (ddi_prop_update_common(dev, dip,
(flag | DDI_PROP_TYPE_BYTE), name,
value, length, ddi_prop_fm_encode_bytes));
}
int
e_ddi_prop_modify(dev_t dev, dev_info_t *dip, int flag,
char *name, caddr_t value, int length)
{
ASSERT((flag & DDI_PROP_TYPE_MASK) == 0);
if (dev == DDI_DEV_T_ANY || name == NULL || strlen(name) == 0)
return (DDI_PROP_INVAL_ARG);
if (ddi_prop_exists(dev, dip, (flag | DDI_PROP_SYSTEM_DEF), name) == 0)
return (DDI_PROP_NOT_FOUND);
if (!(flag & DDI_PROP_CANSLEEP))
flag |= DDI_PROP_DONTSLEEP;
return (ddi_prop_update_common(dev, dip,
(flag | DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_BYTE),
name, value, length, ddi_prop_fm_encode_bytes));
}
int
ddi_prop_lookup_common(dev_t match_dev, dev_info_t *dip,
uint_t flags, char *name, void *data, uint_t *nelements,
int (*prop_decoder)(prop_handle_t *, void *data, uint_t *nelements))
{
int rval;
uint_t ourflags;
prop_handle_t ph;
if ((match_dev == DDI_DEV_T_NONE) ||
(name == NULL) || (strlen(name) == 0))
return (DDI_PROP_INVAL_ARG);
ourflags = (flags & DDI_PROP_DONTSLEEP) ? flags :
flags | DDI_PROP_CANSLEEP;
bzero(&ph, sizeof (prop_handle_t));
if ((flags & DDI_UNBND_DLPI2) || (flags & DDI_PROP_ROOTNEX_GLOBAL)) {
ourflags &= ~DDI_UNBND_DLPI2;
rval = i_ddi_prop_search_global(match_dev,
ourflags, name, &ph.ph_data, &ph.ph_size);
} else {
rval = ddi_prop_search_common(match_dev, dip,
PROP_LEN_AND_VAL_ALLOC, ourflags, name,
&ph.ph_data, &ph.ph_size);
}
if (rval != DDI_PROP_SUCCESS && rval != DDI_PROP_FOUND_1275) {
ASSERT(ph.ph_data == NULL);
ASSERT(ph.ph_size == 0);
return (rval);
}
ph.ph_cur_pos = ph.ph_data;
ph.ph_save_pos = ph.ph_data;
ph.ph_ops = &prop_1275_ops;
ph.ph_flags = (rval == DDI_PROP_FOUND_1275) ? PH_FROM_PROM : 0;
rval = (*prop_decoder)(&ph, data, nelements);
if (ph.ph_size != 0)
kmem_free(ph.ph_data, ph.ph_size);
return (rval);
}
int
ddi_prop_lookup(dev_t match_dev, dev_info_t *dip,
uint_t flags, char *name, void *data, uint_t *nelements,
int (*prop_decoder)(prop_handle_t *, void *data, uint_t *nelements))
{
return (ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_COMPOSITE), name,
data, nelements, prop_decoder));
}
int
ddi_prop_exists(dev_t match_dev, dev_info_t *dip, uint_t flags, char *name)
{
int i;
uint_t x = 0;
i = ddi_prop_search_common(match_dev, dip, PROP_EXISTS,
flags | DDI_PROP_TYPE_MASK, name, NULL, &x);
return (i == DDI_PROP_SUCCESS || i == DDI_PROP_FOUND_1275);
}
int
ddi_prop_update(dev_t match_dev, dev_info_t *dip,
char *name, void *data, uint_t nelements,
int (*prop_create)(prop_handle_t *, void *data, uint_t nelements))
{
return (ddi_prop_update_common(match_dev, dip, DDI_PROP_TYPE_COMPOSITE,
name, data, nelements, prop_create));
}
int
ddi_prop_get_int(dev_t match_dev, dev_info_t *dip, uint_t flags,
char *name, int defvalue)
{
int data;
uint_t nelements;
int rval;
if (flags & ~(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2 | DDI_PROP_ROOTNEX_GLOBAL)) {
#ifdef DEBUG
if (dip != NULL) {
cmn_err(CE_WARN, "ddi_prop_get_int: invalid flag"
" 0x%x (prop = %s, node = %s%d)", flags,
name, ddi_driver_name(dip), ddi_get_instance(dip));
}
#endif
flags &= DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2;
}
if ((rval = ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_INT), name, &data, &nelements,
ddi_prop_fm_decode_int)) != DDI_PROP_SUCCESS) {
if (rval == DDI_PROP_END_OF_DATA)
data = 1;
else
data = defvalue;
}
return (data);
}
int64_t
ddi_prop_get_int64(dev_t match_dev, dev_info_t *dip, uint_t flags,
char *name, int64_t defvalue)
{
int64_t data;
uint_t nelements;
int rval;
if (flags & ~(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2 | DDI_PROP_ROOTNEX_GLOBAL)) {
#ifdef DEBUG
if (dip != NULL) {
cmn_err(CE_WARN, "ddi_prop_get_int64: invalid flag"
" 0x%x (prop = %s, node = %s%d)", flags,
name, ddi_driver_name(dip), ddi_get_instance(dip));
}
#endif
return (DDI_PROP_INVAL_ARG);
}
if ((rval = ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_INT64 | DDI_PROP_NOTPROM),
name, &data, &nelements, ddi_prop_fm_decode_int64))
!= DDI_PROP_SUCCESS) {
if (rval == DDI_PROP_END_OF_DATA)
data = 1;
else
data = defvalue;
}
return (data);
}
int
ddi_prop_lookup_int_array(dev_t match_dev, dev_info_t *dip, uint_t flags,
char *name, int **data, uint_t *nelements)
{
if (flags & ~(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2 | DDI_PROP_ROOTNEX_GLOBAL)) {
#ifdef DEBUG
if (dip != NULL) {
cmn_err(CE_WARN, "ddi_prop_lookup_int_array: "
"invalid flag 0x%x (prop = %s, node = %s%d)",
flags, name, ddi_driver_name(dip),
ddi_get_instance(dip));
}
#endif
flags &= DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2;
}
return (ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_INT), name, data,
nelements, ddi_prop_fm_decode_ints));
}
int
ddi_prop_lookup_int64_array(dev_t match_dev, dev_info_t *dip, uint_t flags,
char *name, int64_t **data, uint_t *nelements)
{
if (flags & ~(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2 | DDI_PROP_ROOTNEX_GLOBAL)) {
#ifdef DEBUG
if (dip != NULL) {
cmn_err(CE_WARN, "ddi_prop_lookup_int64_array: "
"invalid flag 0x%x (prop = %s, node = %s%d)",
flags, name, ddi_driver_name(dip),
ddi_get_instance(dip));
}
#endif
return (DDI_PROP_INVAL_ARG);
}
return (ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_INT64 | DDI_PROP_NOTPROM),
name, data, nelements, ddi_prop_fm_decode_int64_array));
}
int
ddi_prop_update_int(dev_t match_dev, dev_info_t *dip,
char *name, int data)
{
return (ddi_prop_update_common(match_dev, dip, DDI_PROP_TYPE_INT,
name, &data, 1, ddi_prop_fm_encode_ints));
}
int
ddi_prop_update_int64(dev_t match_dev, dev_info_t *dip,
char *name, int64_t data)
{
return (ddi_prop_update_common(match_dev, dip, DDI_PROP_TYPE_INT64,
name, &data, 1, ddi_prop_fm_encode_int64));
}
int
e_ddi_prop_update_int(dev_t match_dev, dev_info_t *dip,
char *name, int data)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_INT,
name, &data, 1, ddi_prop_fm_encode_ints));
}
int
e_ddi_prop_update_int64(dev_t match_dev, dev_info_t *dip,
char *name, int64_t data)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_INT64,
name, &data, 1, ddi_prop_fm_encode_int64));
}
int
ddi_prop_update_int_array(dev_t match_dev, dev_info_t *dip,
char *name, int *data, uint_t nelements)
{
return (ddi_prop_update_common(match_dev, dip, DDI_PROP_TYPE_INT,
name, data, nelements, ddi_prop_fm_encode_ints));
}
int
ddi_prop_update_int64_array(dev_t match_dev, dev_info_t *dip,
char *name, int64_t *data, uint_t nelements)
{
return (ddi_prop_update_common(match_dev, dip, DDI_PROP_TYPE_INT64,
name, data, nelements, ddi_prop_fm_encode_int64));
}
int
e_ddi_prop_update_int64_array(dev_t match_dev, dev_info_t *dip,
char *name, int64_t *data, uint_t nelements)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_INT64,
name, data, nelements, ddi_prop_fm_encode_int64));
}
int
e_ddi_prop_update_int_array(dev_t match_dev, dev_info_t *dip,
char *name, int *data, uint_t nelements)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_INT,
name, data, nelements, ddi_prop_fm_encode_ints));
}
int
ddi_prop_lookup_string(dev_t match_dev, dev_info_t *dip, uint_t flags,
char *name, char **data)
{
uint_t x;
if (flags & ~(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2 | DDI_PROP_ROOTNEX_GLOBAL)) {
#ifdef DEBUG
if (dip != NULL) {
cmn_err(CE_WARN, "%s: invalid flag 0x%x "
"(prop = %s, node = %s%d); invalid bits ignored",
"ddi_prop_lookup_string", flags, name,
ddi_driver_name(dip), ddi_get_instance(dip));
}
#endif
flags &= DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2;
}
return (ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_STRING), name, data,
&x, ddi_prop_fm_decode_string));
}
int
ddi_prop_lookup_string_array(dev_t match_dev, dev_info_t *dip, uint_t flags,
char *name, char ***data, uint_t *nelements)
{
if (flags & ~(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2 | DDI_PROP_ROOTNEX_GLOBAL)) {
#ifdef DEBUG
if (dip != NULL) {
cmn_err(CE_WARN, "ddi_prop_lookup_string_array: "
"invalid flag 0x%x (prop = %s, node = %s%d)",
flags, name, ddi_driver_name(dip),
ddi_get_instance(dip));
}
#endif
flags &= DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2;
}
return (ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_STRING), name, data,
nelements, ddi_prop_fm_decode_strings));
}
int
ddi_prop_update_string(dev_t match_dev, dev_info_t *dip,
char *name, char *data)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_TYPE_STRING, name, &data, 1,
ddi_prop_fm_encode_string));
}
int
e_ddi_prop_update_string(dev_t match_dev, dev_info_t *dip,
char *name, char *data)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_STRING,
name, &data, 1, ddi_prop_fm_encode_string));
}
int
ddi_prop_update_string_array(dev_t match_dev, dev_info_t *dip,
char *name, char **data, uint_t nelements)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_TYPE_STRING, name, data, nelements,
ddi_prop_fm_encode_strings));
}
int
e_ddi_prop_update_string_array(dev_t match_dev, dev_info_t *dip,
char *name, char **data, uint_t nelements)
{
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_STRING,
name, data, nelements,
ddi_prop_fm_encode_strings));
}
int
ddi_prop_lookup_byte_array(dev_t match_dev, dev_info_t *dip, uint_t flags,
char *name, uchar_t **data, uint_t *nelements)
{
if (flags & ~(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2 | DDI_PROP_ROOTNEX_GLOBAL)) {
#ifdef DEBUG
if (dip != NULL) {
cmn_err(CE_WARN, "ddi_prop_lookup_byte_array: "
" invalid flag 0x%x (prop = %s, node = %s%d)",
flags, name, ddi_driver_name(dip),
ddi_get_instance(dip));
}
#endif
flags &= DDI_PROP_DONTPASS | DDI_PROP_NOTPROM |
LDI_DEV_T_ANY | DDI_UNBND_DLPI2;
}
return (ddi_prop_lookup_common(match_dev, dip,
(flags | DDI_PROP_TYPE_BYTE), name, data,
nelements, ddi_prop_fm_decode_bytes));
}
int
ddi_prop_update_byte_array(dev_t match_dev, dev_info_t *dip,
char *name, uchar_t *data, uint_t nelements)
{
if (nelements == 0)
return (DDI_PROP_INVAL_ARG);
return (ddi_prop_update_common(match_dev, dip, DDI_PROP_TYPE_BYTE,
name, data, nelements, ddi_prop_fm_encode_bytes));
}
int
e_ddi_prop_update_byte_array(dev_t match_dev, dev_info_t *dip,
char *name, uchar_t *data, uint_t nelements)
{
if (nelements == 0)
return (DDI_PROP_INVAL_ARG);
return (ddi_prop_update_common(match_dev, dip,
DDI_PROP_SYSTEM_DEF | DDI_PROP_TYPE_BYTE,
name, data, nelements, ddi_prop_fm_encode_bytes));
}
int
ddi_prop_remove_common(dev_t dev, dev_info_t *dip, char *name, int flag)
{
ddi_prop_t **list_head = &(DEVI(dip)->devi_drv_prop_ptr);
ddi_prop_t *propp;
ddi_prop_t *lastpropp = NULL;
if ((dev == DDI_DEV_T_ANY) || (name == (char *)0) ||
(strlen(name) == 0)) {
return (DDI_PROP_INVAL_ARG);
}
if (flag & DDI_PROP_SYSTEM_DEF)
list_head = &(DEVI(dip)->devi_sys_prop_ptr);
else if (flag & DDI_PROP_HW_DEF)
list_head = &(DEVI(dip)->devi_hw_prop_ptr);
mutex_enter(&(DEVI(dip)->devi_lock));
for (propp = *list_head; propp != NULL; propp = propp->prop_next) {
if (DDI_STRSAME(propp->prop_name, name) &&
(dev == propp->prop_dev)) {
if (lastpropp == NULL)
*list_head = propp->prop_next;
else
lastpropp->prop_next = propp->prop_next;
mutex_exit(&(DEVI(dip)->devi_lock));
kmem_free(propp->prop_name,
strlen(propp->prop_name) + 1);
if (propp->prop_len != 0)
kmem_free(propp->prop_val, propp->prop_len);
kmem_free(propp, sizeof (ddi_prop_t));
return (DDI_PROP_SUCCESS);
}
lastpropp = propp;
}
mutex_exit(&(DEVI(dip)->devi_lock));
return (DDI_PROP_NOT_FOUND);
}
int
ddi_prop_remove(dev_t dev, dev_info_t *dip, char *name)
{
return (ddi_prop_remove_common(dev, dip, name, 0));
}
int
e_ddi_prop_remove(dev_t dev, dev_info_t *dip, char *name)
{
return (ddi_prop_remove_common(dev, dip, name, DDI_PROP_SYSTEM_DEF));
}
void
e_ddi_prop_list_delete(ddi_prop_t *props)
{
i_ddi_prop_list_delete(props);
}
void
ddi_prop_remove_all_common(dev_info_t *dip, int flag)
{
ddi_prop_t **list_head;
mutex_enter(&(DEVI(dip)->devi_lock));
if (flag & DDI_PROP_SYSTEM_DEF) {
list_head = &(DEVI(dip)->devi_sys_prop_ptr);
} else if (flag & DDI_PROP_HW_DEF) {
list_head = &(DEVI(dip)->devi_hw_prop_ptr);
} else {
list_head = &(DEVI(dip)->devi_drv_prop_ptr);
}
i_ddi_prop_list_delete(*list_head);
*list_head = NULL;
mutex_exit(&(DEVI(dip)->devi_lock));
}
void
ddi_prop_remove_all(dev_info_t *dip)
{
i_ddi_prop_dyn_driver_set(dip, NULL);
ddi_prop_remove_all_common(dip, 0);
}
void
e_ddi_prop_remove_all(dev_info_t *dip)
{
ddi_prop_remove_all_common(dip, (int)DDI_PROP_SYSTEM_DEF);
}
int
ddi_prop_undefine(dev_t dev, dev_info_t *dip, int flag, char *name)
{
if (!(flag & DDI_PROP_CANSLEEP))
flag |= DDI_PROP_DONTSLEEP;
flag |= DDI_PROP_STACK_CREATE | DDI_PROP_UNDEF_IT | DDI_PROP_TYPE_ANY;
return (ddi_prop_update_common(dev, dip, flag,
name, NULL, 0, ddi_prop_fm_encode_bytes));
}
int
e_ddi_prop_undefine(dev_t dev, dev_info_t *dip, int flag, char *name)
{
if (!(flag & DDI_PROP_CANSLEEP))
flag |= DDI_PROP_DONTSLEEP;
flag |= DDI_PROP_SYSTEM_DEF | DDI_PROP_STACK_CREATE |
DDI_PROP_UNDEF_IT | DDI_PROP_TYPE_ANY;
return (ddi_prop_update_common(dev, dip, flag,
name, NULL, 0, ddi_prop_fm_encode_bytes));
}
void
i_ddi_prop_dyn_driver_set(dev_info_t *dip, i_ddi_prop_dyn_t *dp)
{
DEVI(dip)->devi_prop_dyn_driver = dp;
}
i_ddi_prop_dyn_t *
i_ddi_prop_dyn_driver_get(dev_info_t *dip)
{
return (DEVI(dip)->devi_prop_dyn_driver);
}
void
i_ddi_prop_dyn_parent_set(dev_info_t *dip, i_ddi_prop_dyn_t *dp)
{
DEVI(dip)->devi_prop_dyn_parent = dp;
}
i_ddi_prop_dyn_t *
i_ddi_prop_dyn_parent_get(dev_info_t *dip)
{
return (DEVI(dip)->devi_prop_dyn_parent);
}
void
i_ddi_prop_dyn_cache_invalidate(dev_info_t *dip, i_ddi_prop_dyn_t *dp)
{
if (dip && dp)
i_ddi_di_cache_invalidate();
}
void
ddi_prop_cache_invalidate(dev_t dev, dev_info_t *dip, char *name, int flags)
{
i_ddi_di_cache_invalidate();
}
int
impl_ddi_bus_prop_op(dev_t dev, dev_info_t *dip, dev_info_t *ch_dip,
ddi_prop_op_t prop_op, int mod_flags,
char *name, caddr_t valuep, int *lengthp)
{
int len;
caddr_t buffer = NULL;
if (((dev == DDI_DEV_T_NONE) || (dev == DDI_DEV_T_ANY)) &&
ndi_dev_is_prom_node(ch_dip) &&
((mod_flags & DDI_PROP_NOTPROM) == 0)) {
len = prom_getproplen((pnode_t)DEVI(ch_dip)->devi_nodeid, name);
if (len == -1) {
return (DDI_PROP_NOT_FOUND);
}
if (prop_op == PROP_EXISTS) {
return (DDI_PROP_FOUND_1275);
}
if ((prop_op == PROP_LEN) || (len == 0)) {
*lengthp = len;
return (DDI_PROP_FOUND_1275);
}
switch (prop_op) {
case PROP_LEN_AND_VAL_ALLOC:
buffer = kmem_alloc((size_t)len,
mod_flags & DDI_PROP_CANSLEEP ?
KM_SLEEP : KM_NOSLEEP);
if (buffer == NULL) {
return (DDI_PROP_NO_MEMORY);
}
*(caddr_t *)valuep = buffer;
break;
case PROP_LEN_AND_VAL_BUF:
if (len > (*lengthp)) {
*lengthp = len;
return (DDI_PROP_BUF_TOO_SMALL);
}
buffer = valuep;
break;
default:
break;
}
(void) prom_getprop((pnode_t)DEVI(ch_dip)->devi_nodeid,
name, buffer);
*lengthp = len;
(void) impl_fix_props(dip, ch_dip, name, len, buffer);
return (DDI_PROP_FOUND_1275);
}
return (DDI_PROP_NOT_FOUND);
}
int
ddi_bus_prop_op(dev_t dev, dev_info_t *dip, dev_info_t *ch_dip,
ddi_prop_op_t prop_op, int mod_flags,
char *name, caddr_t valuep, int *lengthp)
{
int error;
error = impl_ddi_bus_prop_op(dev, dip, ch_dip, prop_op, mod_flags,
name, valuep, lengthp);
if (error == DDI_PROP_SUCCESS || error == DDI_PROP_FOUND_1275 ||
error == DDI_PROP_BUF_TOO_SMALL)
return (error);
if (error == DDI_PROP_NO_MEMORY) {
cmn_err(CE_CONT, prop_no_mem_msg, name);
return (DDI_PROP_NO_MEMORY);
}
if ((mod_flags & DDI_PROP_DONTPASS) != 0)
return (DDI_PROP_NOT_FOUND);
if (ch_dip == ddi_root_node()) {
ASSERT(options_dip != NULL);
return (ddi_prop_search_common(dev, options_dip, prop_op,
mod_flags|DDI_PROP_DONTPASS, name, valuep,
(uint_t *)lengthp));
}
return (ddi_prop_search_common(dev, dip, prop_op, mod_flags,
name, valuep, (uint_t *)lengthp));
}
int
e_ddi_getlongprop(dev_t dev, vtype_t type, char *name, int flags,
caddr_t valuep, int *lengthp)
{
_NOTE(ARGUNUSED(type))
dev_info_t *devi;
ddi_prop_op_t prop_op = PROP_LEN_AND_VAL_ALLOC;
int error;
if ((devi = e_ddi_hold_devi_by_dev(dev, 0)) == NULL)
return (DDI_PROP_NOT_FOUND);
error = cdev_prop_op(dev, devi, prop_op, flags, name, valuep, lengthp);
ddi_release_devi(devi);
return (error);
}
int
e_ddi_getlongprop_buf(dev_t dev, vtype_t type, char *name, int flags,
caddr_t valuep, int *lengthp)
{
_NOTE(ARGUNUSED(type))
dev_info_t *devi;
ddi_prop_op_t prop_op = PROP_LEN_AND_VAL_BUF;
int error;
if ((devi = e_ddi_hold_devi_by_dev(dev, 0)) == NULL)
return (DDI_PROP_NOT_FOUND);
error = cdev_prop_op(dev, devi, prop_op, flags, name, valuep, lengthp);
ddi_release_devi(devi);
return (error);
}
int
e_ddi_getprop(dev_t dev, vtype_t type, char *name, int flags, int defvalue)
{
_NOTE(ARGUNUSED(type))
dev_info_t *devi;
ddi_prop_op_t prop_op = PROP_LEN_AND_VAL_BUF;
int propvalue = defvalue;
int proplength = sizeof (int);
int error;
if ((devi = e_ddi_hold_devi_by_dev(dev, 0)) == NULL)
return (defvalue);
error = cdev_prop_op(dev, devi, prop_op,
flags, name, (caddr_t)&propvalue, &proplength);
ddi_release_devi(devi);
if ((error == DDI_PROP_SUCCESS) && (proplength == 0))
propvalue = 1;
return (propvalue);
}
int64_t
e_ddi_getprop_int64(dev_t dev, vtype_t type, char *name,
int flags, int64_t defvalue)
{
_NOTE(ARGUNUSED(type))
dev_info_t *devi;
ddi_prop_op_t prop_op = PROP_LEN_AND_VAL_BUF;
int64_t propvalue = defvalue;
int proplength = sizeof (propvalue);
int error;
if ((devi = e_ddi_hold_devi_by_dev(dev, 0)) == NULL)
return (defvalue);
error = cdev_prop_op(dev, devi, prop_op, flags |
DDI_PROP_CONSUMER_TYPED, name, (caddr_t)&propvalue, &proplength);
ddi_release_devi(devi);
if ((error == DDI_PROP_SUCCESS) && (proplength == 0))
propvalue = 1;
return (propvalue);
}
int
e_ddi_getproplen(dev_t dev, vtype_t type, char *name, int flags, int *lengthp)
{
_NOTE(ARGUNUSED(type))
dev_info_t *devi;
ddi_prop_op_t prop_op = PROP_LEN;
int error;
if ((devi = e_ddi_hold_devi_by_dev(dev, 0)) == NULL)
return (DDI_PROP_NOT_FOUND);
error = cdev_prop_op(dev, devi, prop_op, flags, name, NULL, lengthp);
ddi_release_devi(devi);
return (error);
}
char *
ddi_binding_name(dev_info_t *dip)
{
return (DEVI(dip)->devi_binding_name);
}
major_t
ddi_driver_major(dev_info_t *devi)
{
return (DEVI(devi)->devi_major);
}
const char *
ddi_driver_name(dev_info_t *devi)
{
major_t major;
if ((major = ddi_driver_major(devi)) != DDI_MAJOR_T_NONE)
return (ddi_major_to_name(major));
return (ddi_node_name(devi));
}
void
i_ddi_set_binding_name(dev_info_t *dip, char *name)
{
DEVI(dip)->devi_binding_name = name;
}
char *
ddi_get_name(dev_info_t *dip)
{
return (DEVI(dip)->devi_binding_name);
}
char *
ddi_node_name(dev_info_t *dip)
{
return (DEVI(dip)->devi_node_name);
}
int
ddi_get_nodeid(dev_info_t *dip)
{
return (DEVI(dip)->devi_nodeid);
}
int
ddi_get_instance(dev_info_t *dip)
{
return (DEVI(dip)->devi_instance);
}
struct dev_ops *
ddi_get_driver(dev_info_t *dip)
{
return (DEVI(dip)->devi_ops);
}
void
ddi_set_driver(dev_info_t *dip, struct dev_ops *devo)
{
DEVI(dip)->devi_ops = devo;
}
void
ddi_set_driver_private(dev_info_t *dip, void *data)
{
DEVI(dip)->devi_driver_data = data;
}
void *
ddi_get_driver_private(dev_info_t *dip)
{
return (DEVI(dip)->devi_driver_data);
}
dev_info_t *
ddi_get_parent(dev_info_t *dip)
{
return ((dev_info_t *)DEVI(dip)->devi_parent);
}
dev_info_t *
ddi_get_child(dev_info_t *dip)
{
return ((dev_info_t *)DEVI(dip)->devi_child);
}
dev_info_t *
ddi_get_next_sibling(dev_info_t *dip)
{
return ((dev_info_t *)DEVI(dip)->devi_sibling);
}
dev_info_t *
ddi_get_next(dev_info_t *dip)
{
return ((dev_info_t *)DEVI(dip)->devi_next);
}
void
ddi_set_next(dev_info_t *dip, dev_info_t *nextdip)
{
DEVI(dip)->devi_next = DEVI(nextdip);
}
dev_info_t *
ddi_root_node(void)
{
extern dev_info_t *top_devinfo;
return (top_devinfo);
}
void
ddi_report_dev(dev_info_t *d)
{
char *b;
(void) ddi_ctlops(d, d, DDI_CTLOPS_REPORTDEV, (void *)0, (void *)0);
if (DEVI(d)->devi_ops->devo_cb_ops != (struct cb_ops *)0 &&
(b = kmem_zalloc(MAXPATHLEN, KM_NOSLEEP))) {
cmn_err(CE_CONT, "?%s%d is %s\n",
ddi_driver_name(d), ddi_get_instance(d),
ddi_pathname(d, b));
kmem_free(b, MAXPATHLEN);
}
}
int
ddi_dev_regsize(dev_info_t *dev, uint_t rnumber, off_t *result)
{
int ret;
ret = ddi_ctlops(dev, dev, DDI_CTLOPS_REGSIZE,
(void *)&rnumber, (void *)result);
return (ret == DDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
}
int
ddi_dev_nregs(dev_info_t *dev, int *result)
{
return (ddi_ctlops(dev, dev, DDI_CTLOPS_NREGS, 0, (void *)result));
}
int
ddi_dev_is_sid(dev_info_t *d)
{
return (ddi_ctlops(d, d, DDI_CTLOPS_SIDDEV, (void *)0, (void *)0));
}
int
ddi_slaveonly(dev_info_t *d)
{
return (ddi_ctlops(d, d, DDI_CTLOPS_SLAVEONLY, (void *)0, (void *)0));
}
int
ddi_dev_affinity(dev_info_t *a, dev_info_t *b)
{
return (ddi_ctlops(a, a, DDI_CTLOPS_AFFINITY, (void *)b, (void *)0));
}
int
ddi_streams_driver(dev_info_t *dip)
{
if (i_ddi_devi_attached(dip) &&
(DEVI(dip)->devi_ops->devo_cb_ops != NULL) &&
(DEVI(dip)->devi_ops->devo_cb_ops->cb_str != NULL))
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int ncallbacks;
static int nc_low = 170;
static int nc_med = 512;
static int nc_high = 2048;
static struct ddi_callback *callbackq;
static struct ddi_callback *callbackqfree;
struct cbstats {
kstat_named_t cb_asked;
kstat_named_t cb_new;
kstat_named_t cb_run;
kstat_named_t cb_delete;
kstat_named_t cb_maxreq;
kstat_named_t cb_maxlist;
kstat_named_t cb_alloc;
kstat_named_t cb_runouts;
kstat_named_t cb_L2;
kstat_named_t cb_grow;
} cbstats = {
{"asked", KSTAT_DATA_UINT32},
{"new", KSTAT_DATA_UINT32},
{"run", KSTAT_DATA_UINT32},
{"delete", KSTAT_DATA_UINT32},
{"maxreq", KSTAT_DATA_UINT32},
{"maxlist", KSTAT_DATA_UINT32},
{"alloc", KSTAT_DATA_UINT32},
{"runouts", KSTAT_DATA_UINT32},
{"L2", KSTAT_DATA_UINT32},
{"grow", KSTAT_DATA_UINT32},
};
#define nc_asked cb_asked.value.ui32
#define nc_new cb_new.value.ui32
#define nc_run cb_run.value.ui32
#define nc_delete cb_delete.value.ui32
#define nc_maxreq cb_maxreq.value.ui32
#define nc_maxlist cb_maxlist.value.ui32
#define nc_alloc cb_alloc.value.ui32
#define nc_runouts cb_runouts.value.ui32
#define nc_L2 cb_L2.value.ui32
#define nc_grow cb_grow.value.ui32
static kmutex_t ddi_callback_mutex;
void
impl_ddi_callback_init(void)
{
int i;
uint_t physmegs;
kstat_t *ksp;
physmegs = physmem >> (20 - PAGESHIFT);
if (physmegs < 48) {
ncallbacks = nc_low;
} else if (physmegs < 128) {
ncallbacks = nc_med;
} else {
ncallbacks = nc_high;
}
callbackq = kmem_zalloc(
ncallbacks * sizeof (struct ddi_callback), KM_SLEEP);
for (i = 0; i < ncallbacks-1; i++)
callbackq[i].c_nfree = &callbackq[i+1];
callbackqfree = callbackq;
if (ksp = kstat_create("unix", 0, "cbstats", "misc", KSTAT_TYPE_NAMED,
sizeof (cbstats) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) {
ksp->ks_data = (void *) &cbstats;
kstat_install(ksp);
}
}
static void
callback_insert(int (*funcp)(caddr_t), caddr_t arg, uintptr_t *listid,
int count)
{
struct ddi_callback *list, *marker, *new;
size_t size = sizeof (struct ddi_callback);
list = marker = (struct ddi_callback *)*listid;
while (list != NULL) {
if (list->c_call == funcp && list->c_arg == arg) {
list->c_count += count;
return;
}
marker = list;
list = list->c_nlist;
}
new = kmem_alloc(size, KM_NOSLEEP);
if (new == NULL) {
new = callbackqfree;
if (new == NULL) {
new = kmem_alloc_tryhard(sizeof (struct ddi_callback),
&size, KM_NOSLEEP | KM_PANIC);
cbstats.nc_grow++;
} else {
callbackqfree = new->c_nfree;
cbstats.nc_L2++;
}
}
if (marker != NULL) {
marker->c_nlist = new;
} else {
*listid = (uintptr_t)new;
}
new->c_size = size;
new->c_nlist = NULL;
new->c_call = funcp;
new->c_arg = arg;
new->c_count = count;
cbstats.nc_new++;
cbstats.nc_alloc++;
if (cbstats.nc_alloc > cbstats.nc_maxlist)
cbstats.nc_maxlist = cbstats.nc_alloc;
}
void
ddi_set_callback(int (*funcp)(caddr_t), caddr_t arg, uintptr_t *listid)
{
mutex_enter(&ddi_callback_mutex);
cbstats.nc_asked++;
if ((cbstats.nc_asked - cbstats.nc_run) > cbstats.nc_maxreq)
cbstats.nc_maxreq = (cbstats.nc_asked - cbstats.nc_run);
(void) callback_insert(funcp, arg, listid, 1);
mutex_exit(&ddi_callback_mutex);
}
static void
real_callback_run(void *Queue)
{
int (*funcp)(caddr_t);
caddr_t arg;
int count, rval;
uintptr_t *listid;
struct ddi_callback *list, *marker;
int check_pending = 1;
int pending = 0;
do {
mutex_enter(&ddi_callback_mutex);
listid = Queue;
list = (struct ddi_callback *)*listid;
if (list == NULL) {
mutex_exit(&ddi_callback_mutex);
return;
}
if (check_pending) {
marker = list;
while (marker != NULL) {
pending += marker->c_count;
marker = marker->c_nlist;
}
check_pending = 0;
}
ASSERT(pending > 0);
ASSERT(list->c_count > 0);
funcp = list->c_call;
arg = list->c_arg;
count = list->c_count;
*(uintptr_t *)Queue = (uintptr_t)list->c_nlist;
if (list >= &callbackq[0] &&
list <= &callbackq[ncallbacks-1]) {
list->c_nfree = callbackqfree;
callbackqfree = list;
} else
kmem_free(list, list->c_size);
cbstats.nc_delete++;
cbstats.nc_alloc--;
mutex_exit(&ddi_callback_mutex);
do {
if ((rval = (*funcp)(arg)) == 0) {
pending -= count;
mutex_enter(&ddi_callback_mutex);
(void) callback_insert(funcp, arg, listid,
count);
cbstats.nc_runouts++;
} else {
pending--;
mutex_enter(&ddi_callback_mutex);
cbstats.nc_run++;
}
mutex_exit(&ddi_callback_mutex);
} while (rval != 0 && (--count > 0));
} while (pending > 0);
}
void
ddi_run_callback(uintptr_t *listid)
{
softcall(real_callback_run, listid);
}
ddi_periodic_t
ddi_periodic_add(void (*func)(void *), void *arg, hrtime_t interval, int level)
{
if (level < DDI_IPL_0 || level > DDI_IPL_10)
cmn_err(CE_PANIC,
"ddi_periodic_add: invalid interrupt level (%d).", level);
if (servicing_interrupt())
cmn_err(CE_PANIC,
"ddi_periodic_add: called in (high) interrupt context.");
return ((ddi_periodic_t)i_timeout(func, arg, interval, level));
}
void
ddi_periodic_delete(ddi_periodic_t req)
{
if (servicing_interrupt())
cmn_err(CE_PANIC,
"ddi_periodic_delete: called in (high) interrupt context.");
i_untimeout((timeout_t)req);
}
dev_info_t *
nodevinfo(dev_t dev, int otyp)
{
_NOTE(ARGUNUSED(dev, otyp))
return ((dev_info_t *)0);
}
int
ddi_no_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
_NOTE(ARGUNUSED(dip, infocmd, arg, result))
return (DDI_FAILURE);
}
int
ddi_getinfo_1to1(dev_info_t *dip, ddi_info_cmd_t infocmd,
void *arg, void **result)
{
_NOTE(ARGUNUSED(dip))
int instance;
if (infocmd != DDI_INFO_DEVT2INSTANCE)
return (DDI_FAILURE);
instance = getminor((dev_t)(uintptr_t)arg);
*result = (void *)(uintptr_t)instance;
return (DDI_SUCCESS);
}
int
ddifail(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
_NOTE(ARGUNUSED(devi, cmd))
return (DDI_FAILURE);
}
int
ddi_no_dma_map(dev_info_t *dip, dev_info_t *rdip,
struct ddi_dma_req *dmareqp, ddi_dma_handle_t *handlep)
{
_NOTE(ARGUNUSED(dip, rdip, dmareqp, handlep))
return (DDI_DMA_NOMAPPING);
}
int
ddi_no_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr,
int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep)
{
_NOTE(ARGUNUSED(dip, rdip, attr, waitfp, arg, handlep))
return (DDI_DMA_BADATTR);
}
int
ddi_no_dma_freehdl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle)
{
_NOTE(ARGUNUSED(dip, rdip, handle))
return (DDI_FAILURE);
}
int
ddi_no_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, struct ddi_dma_req *dmareq,
ddi_dma_cookie_t *cp, uint_t *ccountp)
{
_NOTE(ARGUNUSED(dip, rdip, handle, dmareq, cp, ccountp))
return (DDI_DMA_NOMAPPING);
}
int
ddi_no_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle)
{
_NOTE(ARGUNUSED(dip, rdip, handle))
return (DDI_FAILURE);
}
int
ddi_no_dma_flush(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, off_t off, size_t len,
uint_t cache_flags)
{
_NOTE(ARGUNUSED(dip, rdip, handle, off, len, cache_flags))
return (DDI_FAILURE);
}
int
ddi_no_dma_win(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, uint_t win, off_t *offp,
size_t *lenp, ddi_dma_cookie_t *cookiep, uint_t *ccountp)
{
_NOTE(ARGUNUSED(dip, rdip, handle, win, offp, lenp, cookiep, ccountp))
return (DDI_FAILURE);
}
int
ddi_no_dma_mctl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_handle_t handle, enum ddi_dma_ctlops request,
off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags)
{
_NOTE(ARGUNUSED(dip, rdip, handle, request, offp, lenp, objp, flags))
return (DDI_FAILURE);
}
void
ddivoid(void)
{}
int
nochpoll(dev_t dev, short events, int anyyet, short *reventsp,
struct pollhead **pollhdrp)
{
_NOTE(ARGUNUSED(dev, events, anyyet, reventsp, pollhdrp))
return (ENXIO);
}
cred_t *
ddi_get_cred(void)
{
return (CRED());
}
clock_t
ddi_get_lbolt(void)
{
return ((clock_t)lbolt_hybrid());
}
int64_t
ddi_get_lbolt64(void)
{
return (lbolt_hybrid());
}
time_t
ddi_get_time(void)
{
time_t now;
if ((now = gethrestime_sec()) == 0) {
timestruc_t ts;
mutex_enter(&tod_lock);
ts = tod_get();
mutex_exit(&tod_lock);
return (ts.tv_sec);
} else {
return (now);
}
}
pid_t
ddi_get_pid(void)
{
return (ttoproc(curthread)->p_pid);
}
kt_did_t
ddi_get_kt_did(void)
{
return (curthread->t_did);
}
boolean_t
ddi_can_receive_sig(void)
{
proc_t *pp;
if (curthread->t_proc_flag & TP_LWPEXIT)
return (B_FALSE);
if ((pp = ttoproc(curthread)) == NULL)
return (B_FALSE);
return (pp->p_as != &kas);
}
void
swab(void *src, void *dst, size_t nbytes)
{
uchar_t *pf = (uchar_t *)src;
uchar_t *pt = (uchar_t *)dst;
uchar_t tmp;
int nshorts;
nshorts = nbytes >> 1;
while (--nshorts >= 0) {
tmp = *pf++;
*pt++ = *pf++;
*pt++ = tmp;
}
}
static void
ddi_append_minor_node(dev_info_t *ddip, struct ddi_minor_data *dmdp)
{
struct ddi_minor_data *dp;
ndi_devi_enter(ddip);
if ((dp = DEVI(ddip)->devi_minor) == (struct ddi_minor_data *)NULL) {
DEVI(ddip)->devi_minor = dmdp;
} else {
while (dp->next != (struct ddi_minor_data *)NULL)
dp = dp->next;
dp->next = dmdp;
}
ndi_devi_exit(ddip);
}
static int
i_log_devfs_minor_create(dev_info_t *dip, char *minor_name)
{
int se_flag;
int kmem_flag;
int se_err;
char *pathname, *class_name;
sysevent_t *ev = NULL;
sysevent_id_t eid;
sysevent_value_t se_val;
sysevent_attr_list_t *ev_attr_list = NULL;
se_flag = (servicing_interrupt()) ? SE_NOSLEEP : SE_SLEEP;
kmem_flag = (se_flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
i_ddi_di_cache_invalidate();
#ifdef DEBUG
if ((se_flag == SE_NOSLEEP) && sunddi_debug) {
cmn_err(CE_CONT, "ddi_create_minor_node: called from "
"interrupt level by driver %s",
ddi_driver_name(dip));
}
#endif
ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_MINOR_CREATE, EP_DDI, se_flag);
if (ev == NULL) {
goto fail;
}
pathname = kmem_alloc(MAXPATHLEN, kmem_flag);
if (pathname == NULL) {
sysevent_free(ev);
goto fail;
}
(void) ddi_pathname(dip, pathname);
ASSERT(strlen(pathname));
se_val.value_type = SE_DATA_TYPE_STRING;
se_val.value.sv_string = pathname;
if (sysevent_add_attr(&ev_attr_list, DEVFS_PATHNAME,
&se_val, se_flag) != 0) {
kmem_free(pathname, MAXPATHLEN);
sysevent_free(ev);
goto fail;
}
kmem_free(pathname, MAXPATHLEN);
if ((class_name = i_ddi_devi_class(dip)) != NULL) {
se_val.value_type = SE_DATA_TYPE_STRING;
se_val.value.sv_string = class_name;
if (sysevent_add_attr(&ev_attr_list,
DEVFS_DEVI_CLASS, &se_val, SE_SLEEP) != 0) {
sysevent_free_attr(ev_attr_list);
goto fail;
}
}
if (minor_name != NULL) {
se_val.value.sv_string = minor_name;
if (sysevent_add_attr(&ev_attr_list, DEVFS_MINOR_NAME,
&se_val, se_flag) != 0) {
sysevent_free_attr(ev_attr_list);
sysevent_free(ev);
goto fail;
}
}
if (sysevent_attach_attributes(ev, ev_attr_list) != 0) {
sysevent_free_attr(ev_attr_list);
sysevent_free(ev);
goto fail;
}
if ((se_err = log_sysevent(ev, se_flag, &eid)) != 0) {
if (se_err == SE_NO_TRANSPORT) {
cmn_err(CE_WARN, "/devices or /dev may not be current "
"for driver %s (%s). Run devfsadm -i %s",
ddi_driver_name(dip), "syseventd not responding",
ddi_driver_name(dip));
} else {
sysevent_free(ev);
goto fail;
}
}
sysevent_free(ev);
return (DDI_SUCCESS);
fail:
cmn_err(CE_WARN, "/devices or /dev may not be current "
"for driver %s. Run devfsadm -i %s",
ddi_driver_name(dip), ddi_driver_name(dip));
return (DDI_SUCCESS);
}
static int
i_log_devfs_minor_remove(dev_info_t *dip, char *minor_name)
{
char *pathname, *class_name;
sysevent_t *ev;
sysevent_id_t eid;
sysevent_value_t se_val;
sysevent_attr_list_t *ev_attr_list = NULL;
if (DEVI_IS_ATTACHING(dip) || DEVI_IS_DETACHING(dip) ||
(i_ddi_node_state(dip) < DS_INITIALIZED)) {
return (DDI_SUCCESS);
}
i_ddi_di_cache_invalidate();
ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_MINOR_REMOVE, EP_DDI, SE_SLEEP);
if (ev == NULL) {
return (DDI_SUCCESS);
}
pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
if (pathname == NULL) {
sysevent_free(ev);
return (DDI_SUCCESS);
}
(void) ddi_pathname(dip, pathname);
ASSERT(strlen(pathname));
se_val.value_type = SE_DATA_TYPE_STRING;
se_val.value.sv_string = pathname;
if (sysevent_add_attr(&ev_attr_list, DEVFS_PATHNAME,
&se_val, SE_SLEEP) != 0) {
kmem_free(pathname, MAXPATHLEN);
sysevent_free(ev);
return (DDI_SUCCESS);
}
kmem_free(pathname, MAXPATHLEN);
if (minor_name != NULL) {
se_val.value.sv_string = minor_name;
if (sysevent_add_attr(&ev_attr_list, DEVFS_MINOR_NAME,
&se_val, SE_SLEEP) != 0) {
sysevent_free_attr(ev_attr_list);
goto fail;
}
}
if ((class_name = i_ddi_devi_class(dip)) != NULL) {
se_val.value_type = SE_DATA_TYPE_STRING;
se_val.value.sv_string = class_name;
if (sysevent_add_attr(&ev_attr_list,
DEVFS_DEVI_CLASS, &se_val, SE_SLEEP) != 0) {
sysevent_free_attr(ev_attr_list);
goto fail;
}
se_val.value_type = SE_DATA_TYPE_STRING;
se_val.value.sv_string = (char *)ddi_driver_name(dip);
if (sysevent_add_attr(&ev_attr_list,
DEVFS_DRIVER_NAME, &se_val, SE_SLEEP) != 0) {
sysevent_free_attr(ev_attr_list);
goto fail;
}
se_val.value_type = SE_DATA_TYPE_INT32;
se_val.value.sv_int32 = ddi_get_instance(dip);
if (sysevent_add_attr(&ev_attr_list,
DEVFS_INSTANCE, &se_val, SE_SLEEP) != 0) {
sysevent_free_attr(ev_attr_list);
goto fail;
}
}
if (sysevent_attach_attributes(ev, ev_attr_list) != 0) {
sysevent_free_attr(ev_attr_list);
} else {
(void) log_sysevent(ev, SE_SLEEP, &eid);
}
fail:
sysevent_free(ev);
return (DDI_SUCCESS);
}
static int
derive_devi_class(dev_info_t *dip, const char *node_type, int flag)
{
int rv = DDI_SUCCESS;
if (i_ddi_devi_class(dip) == NULL) {
if (strncmp(node_type, DDI_NT_BLOCK,
sizeof (DDI_NT_BLOCK) - 1) == 0 &&
(node_type[sizeof (DDI_NT_BLOCK) - 1] == '\0' ||
node_type[sizeof (DDI_NT_BLOCK) - 1] == ':') &&
strcmp(node_type, DDI_NT_FD) != 0) {
rv = i_ddi_set_devi_class(dip, ESC_DISK, flag);
} else if (strncmp(node_type, DDI_NT_NET,
sizeof (DDI_NT_NET) - 1) == 0 &&
(node_type[sizeof (DDI_NT_NET) - 1] == '\0' ||
node_type[sizeof (DDI_NT_NET) - 1] == ':')) {
rv = i_ddi_set_devi_class(dip, ESC_NETWORK, flag);
} else if (strncmp(node_type, DDI_NT_PRINTER,
sizeof (DDI_NT_PRINTER) - 1) == 0 &&
(node_type[sizeof (DDI_NT_PRINTER) - 1] == '\0' ||
node_type[sizeof (DDI_NT_PRINTER) - 1] == ':')) {
rv = i_ddi_set_devi_class(dip, ESC_PRINTER, flag);
} else if (strncmp(node_type, DDI_PSEUDO,
sizeof (DDI_PSEUDO) -1) == 0 &&
(strncmp(ESC_LOFI, ddi_node_name(dip),
sizeof (ESC_LOFI) -1) == 0)) {
rv = i_ddi_set_devi_class(dip, ESC_LOFI, flag);
}
}
return (rv);
}
static boolean_t
verify_name(const char *name)
{
size_t len = strlen(name);
const char *cp;
if (len == 0 || len > IFNAMSIZ)
return (B_FALSE);
for (cp = name; *cp != '\0'; cp++) {
if (!isalnum(*cp) && *cp != '_')
return (B_FALSE);
}
return (B_TRUE);
}
static int
ddi_create_minor_common(dev_info_t *dip, const char *name, int spec_type,
minor_t minor_num, const char *node_type, int flag, ddi_minor_type mtype,
const char *read_priv, const char *write_priv, mode_t priv_mode)
{
struct ddi_minor_data *dmdp;
major_t major;
if (spec_type != S_IFCHR && spec_type != S_IFBLK)
return (DDI_FAILURE);
if (name == NULL)
return (DDI_FAILURE);
if (minor_num > L_MAXMIN32) {
cmn_err(CE_WARN,
"%s%d:%s minor 0x%x too big for 32-bit applications",
ddi_driver_name(dip), ddi_get_instance(dip),
name, minor_num);
return (DDI_FAILURE);
}
major = ddi_driver_major(dip);
ASSERT(major != DDI_MAJOR_T_NONE);
if (node_type == NULL) {
node_type = DDI_PSEUDO;
NDI_CONFIG_DEBUG((CE_NOTE, "!illegal node_type NULL for %s%d "
" minor node %s; default to DDI_PSEUDO",
ddi_driver_name(dip), ddi_get_instance(dip), name));
}
if (strcmp(node_type, DDI_NT_NET) == 0) {
if (!verify_name(name))
return (DDI_FAILURE);
if (mtype == DDM_MINOR) {
struct devnames *dnp = &devnamesp[major];
LOCK_DEV_OPS(&dnp->dn_lock);
dnp->dn_flags |= DN_NETWORK_DRIVER;
if (DEVI_IS_ATTACHING(dip))
dnp->dn_flags |= DN_NETWORK_PHYSDRIVER;
UNLOCK_DEV_OPS(&dnp->dn_lock);
}
}
if (mtype == DDM_MINOR) {
if (derive_devi_class(dip, node_type, KM_NOSLEEP) !=
DDI_SUCCESS)
return (DDI_FAILURE);
}
if ((dmdp = kmem_zalloc(sizeof (struct ddi_minor_data),
KM_NOSLEEP)) == NULL) {
return (DDI_FAILURE);
}
if ((dmdp->ddm_name = i_ddi_strdup(name, KM_NOSLEEP)) == NULL) {
kmem_free(dmdp, sizeof (struct ddi_minor_data));
return (DDI_FAILURE);
}
dmdp->dip = dip;
dmdp->ddm_dev = makedevice(major, minor_num);
dmdp->ddm_spec_type = spec_type;
dmdp->ddm_node_type = node_type;
dmdp->type = mtype;
if (flag & CLONE_DEV) {
dmdp->type = DDM_ALIAS;
dmdp->ddm_dev = makedevice(ddi_driver_major(clone_dip), major);
}
if (flag & PRIVONLY_DEV) {
dmdp->ddm_flags |= DM_NO_FSPERM;
}
if (read_priv || write_priv) {
dmdp->ddm_node_priv =
devpolicy_priv_by_name(read_priv, write_priv);
}
dmdp->ddm_priv_mode = priv_mode;
ddi_append_minor_node(dip, dmdp);
if (!(DEVI_IS_ATTACHING(dip) || DEVI_IS_DETACHING(dip)) &&
mtype != DDM_INTERNAL_PATH) {
(void) i_log_devfs_minor_create(dip, dmdp->ddm_name);
}
dacfc_match_create_minor(name, node_type, dip, dmdp, flag);
return (DDI_SUCCESS);
}
int
ddi_create_minor_node(dev_info_t *dip, const char *name, int spec_type,
minor_t minor_num, const char *node_type, int flag)
{
return (ddi_create_minor_common(dip, name, spec_type, minor_num,
node_type, flag, DDM_MINOR, NULL, NULL, 0));
}
int
ddi_create_priv_minor_node(dev_info_t *dip, const char *name, int spec_type,
minor_t minor_num, const char *node_type, int flag,
const char *rdpriv, const char *wrpriv, mode_t priv_mode)
{
return (ddi_create_minor_common(dip, name, spec_type, minor_num,
node_type, flag, DDM_MINOR, rdpriv, wrpriv, priv_mode));
}
int
ddi_create_default_minor_node(dev_info_t *dip, const char *name, int spec_type,
minor_t minor_num, const char *node_type, int flag)
{
return (ddi_create_minor_common(dip, name, spec_type, minor_num,
node_type, flag, DDM_DEFAULT, NULL, NULL, 0));
}
int
ddi_create_internal_pathname(dev_info_t *dip, char *name, int spec_type,
minor_t minor_num)
{
return (ddi_create_minor_common(dip, name, spec_type, minor_num,
"internal", 0, DDM_INTERNAL_PATH, NULL, NULL, 0));
}
void
ddi_remove_minor_node(dev_info_t *dip, const char *name)
{
struct ddi_minor_data *dmdp, *dmdp1;
struct ddi_minor_data **dmdp_prev;
ndi_devi_enter(dip);
dmdp_prev = &DEVI(dip)->devi_minor;
dmdp = DEVI(dip)->devi_minor;
while (dmdp != NULL) {
dmdp1 = dmdp->next;
if ((name == NULL || (dmdp->ddm_name != NULL &&
strcmp(name, dmdp->ddm_name) == 0))) {
if (dmdp->ddm_name != NULL) {
if (dmdp->type != DDM_INTERNAL_PATH)
(void) i_log_devfs_minor_remove(dip,
dmdp->ddm_name);
kmem_free(dmdp->ddm_name,
strlen(dmdp->ddm_name) + 1);
}
if (dmdp->ddm_node_priv)
dpfree(dmdp->ddm_node_priv);
dacf_store_info((dacf_infohdl_t)dmdp, NULL);
kmem_free(dmdp, sizeof (struct ddi_minor_data));
*dmdp_prev = dmdp1;
if (name != NULL)
break;
} else {
dmdp_prev = &dmdp->next;
}
dmdp = dmdp1;
}
ndi_devi_exit(dip);
}
int
ddi_in_panic()
{
return (panicstr != NULL);
}
int
ddi_ffs(long mask)
{
return (ffs(mask));
}
int
ddi_fls(long mask)
{
while (mask) {
long nx;
if ((nx = (mask & (mask - 1))) == 0)
break;
mask = nx;
}
return (ffs(mask));
}
#define MIN_N_ITEMS 8
int
ddi_soft_state_init(void **state_p, size_t size, size_t n_items)
{
i_ddi_soft_state *ss;
if (state_p == NULL || size == 0)
return (EINVAL);
ss = kmem_zalloc(sizeof (*ss), KM_SLEEP);
mutex_init(&ss->lock, NULL, MUTEX_DRIVER, NULL);
ss->size = size;
if (n_items < MIN_N_ITEMS)
ss->n_items = MIN_N_ITEMS;
else {
int bitlog;
if ((bitlog = ddi_fls(n_items)) == ddi_ffs(n_items))
bitlog--;
ss->n_items = 1 << bitlog;
}
ASSERT(ss->n_items >= n_items);
ss->array = kmem_zalloc(ss->n_items * sizeof (void *), KM_SLEEP);
*state_p = ss;
return (0);
}
int
ddi_soft_state_zalloc(void *state, int item)
{
i_ddi_soft_state *ss = (i_ddi_soft_state *)state;
void **array;
void *new_element;
if ((state == NULL) || (item < 0))
return (DDI_FAILURE);
mutex_enter(&ss->lock);
if (ss->size == 0) {
mutex_exit(&ss->lock);
cmn_err(CE_WARN, "ddi_soft_state_zalloc: bad handle: %s",
mod_containing_pc(caller()));
return (DDI_FAILURE);
}
array = ss->array;
ASSERT(ss->n_items != 0 && array != NULL);
if (item < ss->n_items && array[item] != NULL) {
mutex_exit(&ss->lock);
return (DDI_FAILURE);
}
new_element = kmem_zalloc(ss->size, KM_SLEEP);
if (item >= ss->n_items) {
void **new_array;
size_t new_n_items;
struct i_ddi_soft_state *dirty;
new_n_items = ss->n_items;
while (new_n_items < (1 + item))
new_n_items <<= 1;
ASSERT(new_n_items >= (1 + item));
new_array = kmem_zalloc(new_n_items * sizeof (void *),
KM_SLEEP);
bcopy(array, new_array, ss->n_items * sizeof (void *));
dirty = kmem_zalloc(sizeof (*dirty), KM_SLEEP);
dirty->array = ss->array;
dirty->n_items = ss->n_items;
dirty->next = ss->next;
ss->next = dirty;
ss->array = (array = new_array);
ss->n_items = new_n_items;
}
ASSERT(array != NULL && item < ss->n_items && array[item] == NULL);
array[item] = new_element;
mutex_exit(&ss->lock);
return (DDI_SUCCESS);
}
void *
ddi_get_soft_state(void *state, int item)
{
i_ddi_soft_state *ss = (i_ddi_soft_state *)state;
ASSERT((ss != NULL) && (item >= 0));
if (item < ss->n_items && ss->array != NULL)
return (ss->array[item]);
return (NULL);
}
void
ddi_soft_state_free(void *state, int item)
{
i_ddi_soft_state *ss = (i_ddi_soft_state *)state;
void **array;
void *element;
static char msg[] = "ddi_soft_state_free:";
if (ss == NULL) {
cmn_err(CE_WARN, "%s null handle: %s",
msg, mod_containing_pc(caller()));
return;
}
element = NULL;
mutex_enter(&ss->lock);
if ((array = ss->array) == NULL || ss->size == 0) {
cmn_err(CE_WARN, "%s bad handle: %s",
msg, mod_containing_pc(caller()));
} else if (item < 0 || item >= ss->n_items) {
cmn_err(CE_WARN, "%s item %d not in range [0..%lu]: %s",
msg, item, ss->n_items - 1, mod_containing_pc(caller()));
} else if (array[item] != NULL) {
element = array[item];
array[item] = NULL;
}
mutex_exit(&ss->lock);
if (element)
kmem_free(element, ss->size);
}
void
ddi_soft_state_fini(void **state_p)
{
i_ddi_soft_state *ss, *dirty;
int item;
static char msg[] = "ddi_soft_state_fini:";
if (state_p == NULL ||
(ss = (i_ddi_soft_state *)(*state_p)) == NULL) {
cmn_err(CE_WARN, "%s null handle: %s",
msg, mod_containing_pc(caller()));
return;
}
if (ss->size == 0) {
cmn_err(CE_WARN, "%s bad handle: %s",
msg, mod_containing_pc(caller()));
return;
}
if (ss->n_items > 0) {
for (item = 0; item < ss->n_items; item++)
ddi_soft_state_free(ss, item);
kmem_free(ss->array, ss->n_items * sizeof (void *));
}
for (dirty = ss->next; dirty; dirty = ss->next) {
ss->next = dirty->next;
kmem_free(dirty->array, dirty->n_items * sizeof (void *));
kmem_free(dirty, sizeof (*dirty));
}
mutex_destroy(&ss->lock);
kmem_free(ss, sizeof (*ss));
*state_p = NULL;
}
#define SS_N_ITEMS_PER_HASH 16
#define SS_MIN_HASH_SZ 16
#define SS_MAX_HASH_SZ 4096
int
ddi_soft_state_bystr_init(ddi_soft_state_bystr **state_p, size_t size,
int n_items)
{
i_ddi_soft_state_bystr *sss;
int hash_sz;
ASSERT(state_p && size && n_items);
if ((state_p == NULL) || (size == 0) || (n_items == 0))
return (EINVAL);
hash_sz = n_items / SS_N_ITEMS_PER_HASH;
if (hash_sz < SS_MIN_HASH_SZ)
hash_sz = SS_MIN_HASH_SZ;
else if (hash_sz > SS_MAX_HASH_SZ)
hash_sz = SS_MAX_HASH_SZ;
sss = kmem_zalloc(sizeof (*sss), KM_SLEEP);
sss->ss_size = size;
sss->ss_mod_hash = mod_hash_create_strhash("soft_state_bystr",
hash_sz, mod_hash_null_valdtor);
*state_p = (ddi_soft_state_bystr *)sss;
return (0);
}
int
ddi_soft_state_bystr_zalloc(ddi_soft_state_bystr *state, const char *str)
{
i_ddi_soft_state_bystr *sss = (i_ddi_soft_state_bystr *)state;
void *sso;
char *dup_str;
ASSERT(sss && str && sss->ss_mod_hash);
if ((sss == NULL) || (str == NULL) || (sss->ss_mod_hash == NULL))
return (DDI_FAILURE);
sso = kmem_zalloc(sss->ss_size, KM_SLEEP);
dup_str = i_ddi_strdup((char *)str, KM_SLEEP);
if (mod_hash_insert(sss->ss_mod_hash,
(mod_hash_key_t)dup_str, (mod_hash_val_t)sso) == 0)
return (DDI_SUCCESS);
kmem_free(dup_str, strlen(dup_str) + 1);
kmem_free(sso, sss->ss_size);
return (DDI_FAILURE);
}
void *
ddi_soft_state_bystr_get(ddi_soft_state_bystr *state, const char *str)
{
i_ddi_soft_state_bystr *sss = (i_ddi_soft_state_bystr *)state;
void *sso;
ASSERT(sss && str && sss->ss_mod_hash);
if ((sss == NULL) || (str == NULL) || (sss->ss_mod_hash == NULL))
return (NULL);
if (mod_hash_find(sss->ss_mod_hash,
(mod_hash_key_t)str, (mod_hash_val_t *)&sso) == 0)
return (sso);
return (NULL);
}
void
ddi_soft_state_bystr_free(ddi_soft_state_bystr *state, const char *str)
{
i_ddi_soft_state_bystr *sss = (i_ddi_soft_state_bystr *)state;
void *sso;
ASSERT(sss && str && sss->ss_mod_hash);
if ((sss == NULL) || (str == NULL) || (sss->ss_mod_hash == NULL))
return;
(void) mod_hash_remove(sss->ss_mod_hash,
(mod_hash_key_t)str, (mod_hash_val_t *)&sso);
kmem_free(sso, sss->ss_size);
}
void
ddi_soft_state_bystr_fini(ddi_soft_state_bystr **state_p)
{
i_ddi_soft_state_bystr *sss;
ASSERT(state_p);
if (state_p == NULL)
return;
sss = (i_ddi_soft_state_bystr *)(*state_p);
if (sss == NULL)
return;
ASSERT(sss->ss_mod_hash);
if (sss->ss_mod_hash) {
mod_hash_destroy_strhash(sss->ss_mod_hash);
sss->ss_mod_hash = NULL;
}
kmem_free(sss, sizeof (*sss));
*state_p = NULL;
}
int
ddi_strid_init(ddi_strid **strid_p, int n_items)
{
i_ddi_strid *ss;
int hash_sz;
if (strid_p == NULL)
return (DDI_FAILURE);
hash_sz = n_items / SS_N_ITEMS_PER_HASH;
if (hash_sz < SS_MIN_HASH_SZ)
hash_sz = SS_MIN_HASH_SZ;
else if (hash_sz > SS_MAX_HASH_SZ)
hash_sz = SS_MAX_HASH_SZ;
ss = kmem_alloc(sizeof (*ss), KM_SLEEP);
ss->strid_chunksz = n_items;
ss->strid_spacesz = n_items;
ss->strid_space = id_space_create("strid", 1, n_items);
ss->strid_bystr = mod_hash_create_strhash("strid_bystr", hash_sz,
mod_hash_null_valdtor);
ss->strid_byid = mod_hash_create_idhash("strid_byid", hash_sz,
mod_hash_null_valdtor);
*strid_p = (ddi_strid *)ss;
return (DDI_SUCCESS);
}
static id_t
i_ddi_strid_alloc(ddi_strid *strid, char *str)
{
i_ddi_strid *ss = (i_ddi_strid *)strid;
id_t id;
char *s;
ASSERT(ss && str);
if ((ss == NULL) || (str == NULL))
return (0);
if ((id = id_allocff_nosleep(ss->strid_space)) == (id_t)-1) {
id_space_extend(ss->strid_space, ss->strid_spacesz,
ss->strid_spacesz + ss->strid_chunksz);
ss->strid_spacesz += ss->strid_chunksz;
if ((id = id_allocff_nosleep(ss->strid_space)) == (id_t)-1)
return (0);
}
s = i_ddi_strdup(str, KM_SLEEP);
if (mod_hash_insert(ss->strid_bystr, (mod_hash_key_t)s,
(mod_hash_val_t)(intptr_t)id) != 0) {
ddi_strid_free(strid, id);
return (0);
}
if (mod_hash_insert(ss->strid_byid, (mod_hash_key_t)(intptr_t)id,
(mod_hash_val_t)s) != 0) {
ddi_strid_free(strid, id);
return (0);
}
return (id);
}
id_t
ddi_strid_alloc(ddi_strid *strid, char *str)
{
return (i_ddi_strid_alloc(strid, str));
}
id_t
ddi_strid_str2id(ddi_strid *strid, char *str)
{
i_ddi_strid *ss = (i_ddi_strid *)strid;
id_t id = 0;
mod_hash_val_t hv;
ASSERT(ss && str);
if (ss && str && (mod_hash_find(ss->strid_bystr,
(mod_hash_key_t)str, &hv) == 0))
id = (int)(intptr_t)hv;
return (id);
}
char *
ddi_strid_id2str(ddi_strid *strid, id_t id)
{
i_ddi_strid *ss = (i_ddi_strid *)strid;
char *str = NULL;
mod_hash_val_t hv;
ASSERT(ss && id > 0);
if (ss && (id > 0) && (mod_hash_find(ss->strid_byid,
(mod_hash_key_t)(uintptr_t)id, &hv) == 0))
str = (char *)hv;
return (str);
}
void
ddi_strid_free(ddi_strid *strid, id_t id)
{
i_ddi_strid *ss = (i_ddi_strid *)strid;
char *str;
ASSERT(ss && id > 0);
if ((ss == NULL) || (id <= 0))
return;
str = ddi_strid_id2str(strid, id);
(void) mod_hash_destroy(ss->strid_byid, (mod_hash_key_t)(uintptr_t)id);
id_free(ss->strid_space, id);
if (str)
(void) mod_hash_destroy(ss->strid_bystr, (mod_hash_key_t)str);
}
void
ddi_strid_fini(ddi_strid **strid_p)
{
i_ddi_strid *ss;
ASSERT(strid_p);
if (strid_p == NULL)
return;
ss = (i_ddi_strid *)(*strid_p);
if (ss == NULL)
return;
if (ss->strid_byid)
mod_hash_destroy_hash(ss->strid_byid);
if (ss->strid_byid)
mod_hash_destroy_hash(ss->strid_bystr);
if (ss->strid_space)
id_space_destroy(ss->strid_space);
kmem_free(ss, sizeof (*ss));
*strid_p = NULL;
}
void
ddi_set_name_addr(dev_info_t *dip, char *name)
{
char *buf = DEVI(dip)->devi_addr_buf;
char *newaddr;
if (buf == NULL) {
buf = kmem_zalloc(2 * MAXNAMELEN, KM_SLEEP);
DEVI(dip)->devi_addr_buf = buf;
}
if (name) {
ASSERT(strlen(name) < MAXNAMELEN);
newaddr = (DEVI(dip)->devi_addr == buf) ?
(buf + MAXNAMELEN) : buf;
(void) strlcpy(newaddr, name, MAXNAMELEN);
} else
newaddr = NULL;
DEVI(dip)->devi_addr = newaddr;
}
char *
ddi_get_name_addr(dev_info_t *dip)
{
return (DEVI(dip)->devi_addr);
}
void
ddi_set_parent_data(dev_info_t *dip, void *pd)
{
DEVI(dip)->devi_parent_data = pd;
}
void *
ddi_get_parent_data(dev_info_t *dip)
{
return (DEVI(dip)->devi_parent_data);
}
major_t
ddi_name_to_major(const char *name)
{
return (mod_name_to_major(name));
}
char *
ddi_major_to_name(major_t major)
{
return (mod_major_to_name(major));
}
char *
ddi_deviname(dev_info_t *dip, char *name)
{
char *addrname;
char none = '\0';
if (dip == ddi_root_node()) {
*name = '\0';
return (name);
}
if (i_ddi_node_state(dip) < DS_BOUND) {
addrname = &none;
} else {
addrname = ddi_get_name_addr(dip);
if (addrname == NULL)
addrname = &none;
}
if (*addrname == '\0') {
(void) sprintf(name, "/%s", ddi_node_name(dip));
} else {
(void) sprintf(name, "/%s@%s", ddi_node_name(dip), addrname);
}
return (name);
}
char *
i_ddi_parname(dev_info_t *dip, char *name)
{
char *addrname;
if (dip == ddi_root_node()) {
*name = '\0';
return (name);
}
ASSERT(i_ddi_node_state(dip) >= DS_INITIALIZED);
if (*(addrname = ddi_get_name_addr(dip)) == '\0')
(void) sprintf(name, "%s", ddi_binding_name(dip));
else
(void) sprintf(name, "%s@%s", ddi_binding_name(dip), addrname);
return (name);
}
static char *
pathname_work(dev_info_t *dip, char *path)
{
char *bp;
if (dip == ddi_root_node()) {
*path = '\0';
return (path);
}
(void) pathname_work(ddi_get_parent(dip), path);
bp = path + strlen(path);
(void) ddi_deviname(dip, bp);
return (path);
}
char *
ddi_pathname(dev_info_t *dip, char *path)
{
return (pathname_work(dip, path));
}
char *
ddi_pathname_minor(struct ddi_minor_data *dmdp, char *path)
{
if (dmdp->dip == NULL)
*path = '\0';
else {
(void) ddi_pathname(dmdp->dip, path);
if (dmdp->ddm_name) {
(void) strcat(path, ":");
(void) strcat(path, dmdp->ddm_name);
}
}
return (path);
}
static char *
pathname_work_obp(dev_info_t *dip, char *path)
{
char *bp;
char *obp_path;
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"obp-path", &obp_path) == DDI_PROP_SUCCESS) {
(void) strcpy(path, obp_path);
ddi_prop_free(obp_path);
return (path);
}
if (dip == ddi_root_node()) {
return (NULL);
}
obp_path = pathname_work_obp(ddi_get_parent(dip), path);
if (obp_path == NULL)
return (NULL);
bp = path + strlen(path);
if (*(bp - 1) != '/')
(void) strcat(bp++, "/");
(void) ddi_deviname(dip, bp);
return (path);
}
char *
ddi_pathname_obp(dev_info_t *dip, char *path)
{
ASSERT(!servicing_interrupt());
if (dip == NULL || path == NULL)
return (NULL);
return (pathname_work_obp(dip, path));
}
int
ddi_pathname_obp_set(dev_info_t *dip, char *component)
{
dev_info_t *pdip;
char *obp_path = NULL;
int rc = DDI_FAILURE;
if (dip == NULL)
return (DDI_FAILURE);
obp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
pdip = ddi_get_parent(dip);
if (ddi_pathname_obp(pdip, obp_path) == NULL) {
(void) ddi_pathname(pdip, obp_path);
}
if (component) {
(void) strncat(obp_path, "/", MAXPATHLEN);
(void) strncat(obp_path, component, MAXPATHLEN);
}
rc = ndi_prop_update_string(DDI_DEV_T_NONE, dip, "obp-path",
obp_path);
if (obp_path)
kmem_free(obp_path, MAXPATHLEN);
return (rc);
}
int
ddi_dev_pathname(dev_t devt, int spec_type, char *path)
{
major_t major = getmajor(devt);
int instance;
dev_info_t *dip;
char *minorname;
char *drvname;
if (major >= devcnt)
goto fail;
if (major == clone_major) {
if ((drvname = ddi_major_to_name(getminor(devt))) == NULL)
goto fail;
(void) snprintf(path, MAXPATHLEN, "%s:%s", CLONE_PATH, drvname);
return (DDI_SUCCESS);
}
if ((instance = dev_to_instance(devt)) == -1)
goto fail;
if (e_ddi_majorinstance_to_path(major, instance, path) != DDI_SUCCESS)
goto fail;
if ((spec_type == S_IFCHR) || (spec_type == S_IFBLK)) {
if ((dip = e_ddi_hold_devi_by_path(path, 0)) == NULL)
goto fail;
ndi_devi_enter(dip);
minorname = i_ddi_devtspectype_to_minorname(dip,
devt, spec_type);
if (minorname) {
(void) strcat(path, ":");
(void) strcat(path, minorname);
}
ndi_devi_exit(dip);
ddi_release_devi(dip);
if (minorname == NULL)
goto fail;
}
ASSERT(strlen(path) < MAXPATHLEN);
return (DDI_SUCCESS);
fail: *path = 0;
return (DDI_FAILURE);
}
int
e_ddi_majorinstance_to_path(major_t major, int instance, char *path)
{
struct devnames *dnp;
dev_info_t *dip;
if ((major >= devcnt) || (instance == -1)) {
*path = 0;
return (DDI_FAILURE);
}
if (e_ddi_instance_majorinstance_to_path(major, instance,
path) == DDI_SUCCESS) {
ASSERT(strlen(path) < MAXPATHLEN);
return (DDI_SUCCESS);
}
dnp = &(devnamesp[major]);
LOCK_DEV_OPS(&(dnp->dn_lock));
for (dip = dnp->dn_head; dip;
dip = (dev_info_t *)DEVI(dip)->devi_next) {
if (DEVI(dip)->devi_instance != instance)
continue;
if (i_ddi_node_state(dip) >= DS_INITIALIZED) {
(void) ddi_pathname(dip, path);
if (i_ddi_node_state(dip) < DS_INITIALIZED)
continue;
UNLOCK_DEV_OPS(&(dnp->dn_lock));
ASSERT(strlen(path) < MAXPATHLEN);
return (DDI_SUCCESS);
}
}
UNLOCK_DEV_OPS(&(dnp->dn_lock));
*path = 0;
return (DDI_FAILURE);
}
#define GLD_DRIVER_PPA "SUNW,gld_v0_ppa"
int
i_ddi_devi_get_ppa(dev_info_t *dip)
{
return (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
GLD_DRIVER_PPA, ddi_get_instance(dip)));
}
void
i_ddi_devi_set_ppa(dev_info_t *dip, int ppa)
{
(void) e_ddi_prop_update_int(DDI_DEV_T_NONE, dip, GLD_DRIVER_PPA, ppa);
}
void
ddi_ring_console_bell(clock_t duration)
{
if (ddi_console_bell_func != NULL)
(*ddi_console_bell_func)(duration);
}
void
ddi_set_console_bell(void (*bellfunc)(clock_t duration))
{
ddi_console_bell_func = bellfunc;
}
int
ddi_dma_alloc_handle(dev_info_t *dip, const ddi_dma_attr_t *attr,
int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep)
{
int (*funcp)() = ddi_dma_allochdl;
ddi_dma_attr_t dma_attr;
struct bus_ops *bop;
if (attr == NULL)
return (DDI_DMA_BADATTR);
dma_attr = *attr;
bop = DEVI(dip)->devi_ops->devo_bus_ops;
if (bop && bop->bus_dma_allochdl)
funcp = bop->bus_dma_allochdl;
return ((*funcp)(dip, dip, &dma_attr, waitfp, arg, handlep));
}
void
ddi_dma_free_handle(ddi_dma_handle_t *handlep)
{
ddi_dma_handle_t h = *handlep;
(void) ddi_dma_freehdl(HD, HD, h);
}
static uintptr_t dma_mem_list_id = 0;
int
ddi_dma_mem_alloc(ddi_dma_handle_t handle, size_t length,
const ddi_device_acc_attr_t *accattrp, uint_t flags,
int (*waitfp)(caddr_t), caddr_t arg, caddr_t *kaddrp,
size_t *real_length, ddi_acc_handle_t *handlep)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
dev_info_t *dip = hp->dmai_rdip;
ddi_acc_hdl_t *ap;
ddi_dma_attr_t *attrp = &hp->dmai_attr;
uint_t sleepflag, xfermodes;
int (*fp)(caddr_t);
int rval;
if (waitfp == DDI_DMA_SLEEP)
fp = (int (*)())KM_SLEEP;
else if (waitfp == DDI_DMA_DONTWAIT)
fp = (int (*)())KM_NOSLEEP;
else
fp = waitfp;
*handlep = impl_acc_hdl_alloc(fp, arg);
if (*handlep == NULL)
return (DDI_FAILURE);
if (i_ddi_check_cache_attr(flags) == B_FALSE)
return (DDI_FAILURE);
xfermodes = flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING);
if (xfermodes == 0) {
xfermodes = DDI_DMA_STREAMING;
}
ap = impl_acc_hdl_get(*handlep);
ap->ah_vers = VERS_ACCHDL;
ap->ah_dip = dip;
ap->ah_offset = 0;
ap->ah_len = 0;
ap->ah_xfermodes = flags;
ap->ah_acc = *accattrp;
sleepflag = ((waitfp == DDI_DMA_SLEEP) ? 1 : 0);
if (xfermodes == DDI_DMA_CONSISTENT) {
rval = i_ddi_mem_alloc(dip, attrp, length, sleepflag,
flags, accattrp, kaddrp, NULL, ap);
*real_length = length;
} else {
rval = i_ddi_mem_alloc(dip, attrp, length, sleepflag,
flags, accattrp, kaddrp, real_length, ap);
}
if (rval == DDI_SUCCESS) {
ap->ah_len = (off_t)(*real_length);
ap->ah_addr = *kaddrp;
} else {
impl_acc_hdl_free(*handlep);
*handlep = (ddi_acc_handle_t)NULL;
if (waitfp != DDI_DMA_SLEEP && waitfp != DDI_DMA_DONTWAIT) {
ddi_set_callback(waitfp, arg, &dma_mem_list_id);
}
rval = DDI_FAILURE;
}
return (rval);
}
void
ddi_dma_mem_free(ddi_acc_handle_t *handlep)
{
ddi_acc_hdl_t *ap;
ap = impl_acc_hdl_get(*handlep);
ASSERT(ap);
i_ddi_mem_free((caddr_t)ap->ah_addr, ap);
impl_acc_hdl_free(*handlep);
*handlep = (ddi_acc_handle_t)NULL;
if (dma_mem_list_id != 0) {
ddi_run_callback(&dma_mem_list_id);
}
}
int
ddi_dma_buf_bind_handle(ddi_dma_handle_t handle, struct buf *bp,
uint_t flags, int (*waitfp)(caddr_t), caddr_t arg,
ddi_dma_cookie_t *cookiep, uint_t *ccountp)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
dev_info_t *dip, *rdip;
struct ddi_dma_req dmareq;
int (*funcp)();
ddi_dma_cookie_t cookie;
uint_t count;
if (cookiep == NULL)
cookiep = &cookie;
if (ccountp == NULL)
ccountp = &count;
dmareq.dmar_flags = flags;
dmareq.dmar_fp = waitfp;
dmareq.dmar_arg = arg;
dmareq.dmar_object.dmao_size = (uint_t)bp->b_bcount;
if (bp->b_flags & B_PAGEIO) {
dmareq.dmar_object.dmao_type = DMA_OTYP_PAGES;
dmareq.dmar_object.dmao_obj.pp_obj.pp_pp = bp->b_pages;
dmareq.dmar_object.dmao_obj.pp_obj.pp_offset =
(uint_t)(((uintptr_t)bp->b_un.b_addr) & MMU_PAGEOFFSET);
} else {
dmareq.dmar_object.dmao_obj.virt_obj.v_addr = bp->b_un.b_addr;
if (bp->b_flags & B_SHADOW) {
dmareq.dmar_object.dmao_obj.virt_obj.v_priv =
bp->b_shadow;
dmareq.dmar_object.dmao_type = DMA_OTYP_BUFVADDR;
} else {
dmareq.dmar_object.dmao_type =
(bp->b_flags & (B_PHYS | B_REMAPPED)) ?
DMA_OTYP_BUFVADDR : DMA_OTYP_VADDR;
dmareq.dmar_object.dmao_obj.virt_obj.v_priv = NULL;
}
if ((bp->b_proc == NULL) || (bp->b_proc->p_as == &kas) ||
(bp->b_flags & B_REMAPPED)) {
dmareq.dmar_object.dmao_obj.virt_obj.v_as = 0;
} else {
dmareq.dmar_object.dmao_obj.virt_obj.v_as =
bp->b_proc->p_as;
}
}
dip = rdip = hp->dmai_rdip;
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_bindhdl;
funcp = DEVI(rdip)->devi_bus_dma_bindfunc;
return ((*funcp)(dip, rdip, handle, &dmareq, cookiep, ccountp));
}
int
ddi_dma_addr_bind_handle(ddi_dma_handle_t handle, struct as *as,
caddr_t addr, size_t len, uint_t flags, int (*waitfp)(caddr_t),
caddr_t arg, ddi_dma_cookie_t *cookiep, uint_t *ccountp)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
dev_info_t *dip, *rdip;
struct ddi_dma_req dmareq;
int (*funcp)();
ddi_dma_cookie_t cookie;
uint_t count;
if (len == (uint_t)0) {
return (DDI_DMA_NOMAPPING);
}
if (cookiep == NULL)
cookiep = &cookie;
if (ccountp == NULL)
ccountp = &count;
dmareq.dmar_flags = flags;
dmareq.dmar_fp = waitfp;
dmareq.dmar_arg = arg;
dmareq.dmar_object.dmao_size = len;
dmareq.dmar_object.dmao_type = DMA_OTYP_VADDR;
dmareq.dmar_object.dmao_obj.virt_obj.v_as = as;
dmareq.dmar_object.dmao_obj.virt_obj.v_addr = addr;
dmareq.dmar_object.dmao_obj.virt_obj.v_priv = NULL;
dip = rdip = hp->dmai_rdip;
if (dip != ddi_root_node())
dip = (dev_info_t *)DEVI(dip)->devi_bus_dma_bindhdl;
funcp = DEVI(rdip)->devi_bus_dma_bindfunc;
return ((*funcp)(dip, rdip, handle, &dmareq, cookiep, ccountp));
}
void
ddi_dma_nextcookie(ddi_dma_handle_t handle, ddi_dma_cookie_t *cookiep)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
ddi_dma_cookie_t *cp;
if (hp->dmai_curcookie >= hp->dmai_ncookies) {
panic("ddi_dma_nextcookie() called too many times on handle %p",
hp);
}
cp = hp->dmai_cookie;
ASSERT(cp);
cookiep->dmac_notused = cp->dmac_notused;
cookiep->dmac_type = cp->dmac_type;
cookiep->dmac_address = cp->dmac_address;
cookiep->dmac_size = cp->dmac_size;
hp->dmai_cookie++;
hp->dmai_curcookie++;
}
int
ddi_dma_ncookies(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
return (hp->dmai_ncookies);
}
const ddi_dma_cookie_t *
ddi_dma_cookie_iter(ddi_dma_handle_t handle, const ddi_dma_cookie_t *iter)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
const ddi_dma_cookie_t *base, *end;
if (hp->dmai_ncookies == 0) {
return (NULL);
}
base = hp->dmai_cookie - hp->dmai_curcookie;
end = base + hp->dmai_ncookies;
if (iter == NULL) {
return (base);
}
if ((uintptr_t)iter < (uintptr_t)base ||
(uintptr_t)iter >= (uintptr_t)end) {
return (NULL);
}
iter++;
if (iter == end) {
return (NULL);
}
return (iter);
}
const ddi_dma_cookie_t *
ddi_dma_cookie_get(ddi_dma_handle_t handle, uint_t index)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
const ddi_dma_cookie_t *base;
if (index >= hp->dmai_ncookies) {
return (NULL);
}
base = hp->dmai_cookie - hp->dmai_curcookie;
return (base + index);
}
const ddi_dma_cookie_t *
ddi_dma_cookie_one(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
const ddi_dma_cookie_t *base;
if (hp->dmai_ncookies != 1) {
panic("ddi_dma_cookie_one() called with improper handle %p",
hp);
}
ASSERT3P(hp->dmai_cookie, !=, NULL);
base = hp->dmai_cookie - hp->dmai_curcookie;
return (base);
}
int
ddi_dma_numwin(ddi_dma_handle_t handle, uint_t *nwinp)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
if ((hp->dmai_rflags & DDI_DMA_PARTIAL) == 0) {
return (DDI_FAILURE);
} else {
*nwinp = hp->dmai_nwin;
return (DDI_SUCCESS);
}
}
int
ddi_dma_getwin(ddi_dma_handle_t h, uint_t win, off_t *offp,
size_t *lenp, ddi_dma_cookie_t *cookiep, uint_t *ccountp)
{
int (*funcp)() = ddi_dma_win;
struct bus_ops *bop;
ddi_dma_cookie_t cookie;
uint_t count;
bop = DEVI(HD)->devi_ops->devo_bus_ops;
if (bop && bop->bus_dma_win)
funcp = bop->bus_dma_win;
if (cookiep == NULL)
cookiep = &cookie;
if (ccountp == NULL)
ccountp = &count;
return ((*funcp)(HD, HD, h, win, offp, lenp, cookiep, ccountp));
}
int
ddi_dma_set_sbus64(ddi_dma_handle_t h, ulong_t burstsizes)
{
return (ddi_dma_mctl(HD, HD, h, DDI_DMA_SET_SBUS64, 0,
&burstsizes, 0, 0));
}
int
i_ddi_dma_fault_check(ddi_dma_impl_t *hp)
{
return (hp->dmai_fault);
}
int
ddi_check_dma_handle(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
int (*check)(ddi_dma_impl_t *);
if ((check = hp->dmai_fault_check) == NULL)
check = i_ddi_dma_fault_check;
return (((*check)(hp) == DDI_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
}
void
i_ddi_dma_set_fault(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
void (*notify)(ddi_dma_impl_t *);
if (!hp->dmai_fault) {
hp->dmai_fault = 1;
if ((notify = hp->dmai_fault_notify) != NULL)
(*notify)(hp);
}
}
void
i_ddi_dma_clr_fault(ddi_dma_handle_t handle)
{
ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle;
void (*notify)(ddi_dma_impl_t *);
if (hp->dmai_fault) {
hp->dmai_fault = 0;
if ((notify = hp->dmai_fault_notify) != NULL)
(*notify)(hp);
}
}
int
ddi_regs_map_setup(dev_info_t *dip, uint_t rnumber, caddr_t *addrp,
offset_t offset, offset_t len, const ddi_device_acc_attr_t *accattrp,
ddi_acc_handle_t *handle)
{
ddi_map_req_t mr;
ddi_acc_hdl_t *hp;
int result;
*handle = impl_acc_hdl_alloc(KM_SLEEP, NULL);
hp = impl_acc_hdl_get(*handle);
hp->ah_vers = VERS_ACCHDL;
hp->ah_dip = dip;
hp->ah_rnumber = rnumber;
hp->ah_offset = offset;
hp->ah_len = len;
hp->ah_acc = *accattrp;
mr.map_op = DDI_MO_MAP_LOCKED;
mr.map_type = DDI_MT_RNUMBER;
mr.map_obj.rnumber = rnumber;
mr.map_prot = PROT_READ | PROT_WRITE;
mr.map_flags = DDI_MF_KERNEL_MAPPING;
mr.map_handlep = hp;
mr.map_vers = DDI_MAP_VERSION;
result = ddi_map(dip, &mr, offset, len, addrp);
if (result != DDI_SUCCESS) {
impl_acc_hdl_free(*handle);
*handle = (ddi_acc_handle_t)NULL;
} else {
hp->ah_addr = *addrp;
}
return (result);
}
void
ddi_regs_map_free(ddi_acc_handle_t *handlep)
{
ddi_map_req_t mr;
ddi_acc_hdl_t *hp;
hp = impl_acc_hdl_get(*handlep);
ASSERT(hp);
mr.map_op = DDI_MO_UNMAP;
mr.map_type = DDI_MT_RNUMBER;
mr.map_obj.rnumber = hp->ah_rnumber;
mr.map_prot = PROT_READ | PROT_WRITE;
mr.map_flags = DDI_MF_KERNEL_MAPPING;
mr.map_handlep = hp;
mr.map_vers = DDI_MAP_VERSION;
(void) ddi_map(hp->ah_dip, &mr, hp->ah_offset,
hp->ah_len, &hp->ah_addr);
impl_acc_hdl_free(*handlep);
*handlep = (ddi_acc_handle_t)NULL;
}
int
ddi_device_zero(ddi_acc_handle_t handle, caddr_t dev_addr, size_t bytecount,
ssize_t dev_advcnt, uint_t dev_datasz)
{
uint8_t *b;
uint16_t *w;
uint32_t *l;
uint64_t *ll;
if (bytecount != ((bytecount / dev_datasz) * dev_datasz))
return (DDI_FAILURE);
switch (dev_datasz) {
case DDI_DATA_SZ01_ACC:
for (b = (uint8_t *)dev_addr;
bytecount != 0; bytecount -= 1, b += dev_advcnt)
ddi_put8(handle, b, 0);
break;
case DDI_DATA_SZ02_ACC:
for (w = (uint16_t *)dev_addr;
bytecount != 0; bytecount -= 2, w += dev_advcnt)
ddi_put16(handle, w, 0);
break;
case DDI_DATA_SZ04_ACC:
for (l = (uint32_t *)dev_addr;
bytecount != 0; bytecount -= 4, l += dev_advcnt)
ddi_put32(handle, l, 0);
break;
case DDI_DATA_SZ08_ACC:
for (ll = (uint64_t *)dev_addr;
bytecount != 0; bytecount -= 8, ll += dev_advcnt)
ddi_put64(handle, ll, 0x0ll);
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
ddi_device_copy(
ddi_acc_handle_t src_handle, caddr_t src_addr, ssize_t src_advcnt,
ddi_acc_handle_t dest_handle, caddr_t dest_addr, ssize_t dest_advcnt,
size_t bytecount, uint_t dev_datasz)
{
uint8_t *b_src, *b_dst;
uint16_t *w_src, *w_dst;
uint32_t *l_src, *l_dst;
uint64_t *ll_src, *ll_dst;
if (bytecount != ((bytecount / dev_datasz) * dev_datasz))
return (DDI_FAILURE);
switch (dev_datasz) {
case DDI_DATA_SZ01_ACC:
b_src = (uint8_t *)src_addr;
b_dst = (uint8_t *)dest_addr;
for (; bytecount != 0; bytecount -= 1) {
ddi_put8(dest_handle, b_dst,
ddi_get8(src_handle, b_src));
b_dst += dest_advcnt;
b_src += src_advcnt;
}
break;
case DDI_DATA_SZ02_ACC:
w_src = (uint16_t *)src_addr;
w_dst = (uint16_t *)dest_addr;
for (; bytecount != 0; bytecount -= 2) {
ddi_put16(dest_handle, w_dst,
ddi_get16(src_handle, w_src));
w_dst += dest_advcnt;
w_src += src_advcnt;
}
break;
case DDI_DATA_SZ04_ACC:
l_src = (uint32_t *)src_addr;
l_dst = (uint32_t *)dest_addr;
for (; bytecount != 0; bytecount -= 4) {
ddi_put32(dest_handle, l_dst,
ddi_get32(src_handle, l_src));
l_dst += dest_advcnt;
l_src += src_advcnt;
}
break;
case DDI_DATA_SZ08_ACC:
ll_src = (uint64_t *)src_addr;
ll_dst = (uint64_t *)dest_addr;
for (; bytecount != 0; bytecount -= 8) {
ddi_put64(dest_handle, ll_dst,
ddi_get64(src_handle, ll_src));
ll_dst += dest_advcnt;
ll_src += src_advcnt;
}
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
#define swap16(value) \
((((value) & 0xff) << 8) | ((value) >> 8))
#define swap32(value) \
(((uint32_t)swap16((uint16_t)((value) & 0xffff)) << 16) | \
(uint32_t)swap16((uint16_t)((value) >> 16)))
#define swap64(value) \
(((uint64_t)swap32((uint32_t)((value) & 0xffffffff)) \
<< 32) | \
(uint64_t)swap32((uint32_t)((value) >> 32)))
uint16_t
ddi_swap16(uint16_t value)
{
return (swap16(value));
}
uint32_t
ddi_swap32(uint32_t value)
{
return (swap32(value));
}
uint64_t
ddi_swap64(uint64_t value)
{
return (swap64(value));
}
char *
i_binding_to_drv_name(char *bname)
{
major_t major_no;
ASSERT(bname != NULL);
if ((major_no = ddi_name_to_major(bname)) == -1)
return (NULL);
return (ddi_major_to_name(major_no));
}
char *
i_ddi_devtspectype_to_minorname(dev_info_t *dip, dev_t dev, int spec_type)
{
struct ddi_minor_data *dmdp;
ASSERT((ddi_driver_major(dip) == getmajor(dev)) ||
(strcmp(ddi_major_to_name(getmajor(dev)), "did") == 0));
ASSERT(DEVI_BUSY_OWNED(dip));
for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) {
if (((dmdp->type == DDM_MINOR) ||
(dmdp->type == DDM_INTERNAL_PATH) ||
(dmdp->type == DDM_DEFAULT)) &&
(dmdp->ddm_dev == dev) &&
((((spec_type & (S_IFCHR|S_IFBLK))) == 0) ||
(dmdp->ddm_spec_type == spec_type)))
return (dmdp->ddm_name);
}
return (NULL);
}
int
i_ddi_minorname_to_devtspectype(dev_info_t *dip, const char *minor_name,
dev_t *devtp, int *spectypep)
{
struct ddi_minor_data *dmdp;
if (dip == clone_dip) {
major_t major;
major = ddi_name_to_major(minor_name);
if (major == DDI_MAJOR_T_NONE)
return (DDI_FAILURE);
if (ddi_hold_driver(major) == NULL)
return (DDI_FAILURE);
if (STREAMSTAB(major) == NULL) {
ddi_rele_driver(major);
return (DDI_FAILURE);
}
ddi_rele_driver(major);
if (devtp)
*devtp = makedevice(clone_major, (minor_t)major);
if (spectypep)
*spectypep = S_IFCHR;
return (DDI_SUCCESS);
}
ndi_devi_enter(dip);
for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) {
if (((dmdp->type != DDM_MINOR) &&
(dmdp->type != DDM_INTERNAL_PATH) &&
(dmdp->type != DDM_DEFAULT)) ||
strcmp(minor_name, dmdp->ddm_name))
continue;
if (devtp)
*devtp = dmdp->ddm_dev;
if (spectypep)
*spectypep = dmdp->ddm_spec_type;
ndi_devi_exit(dip);
return (DDI_SUCCESS);
}
ndi_devi_exit(dip);
return (DDI_FAILURE);
}
static kmutex_t devid_gen_mutex;
static short devid_gen_number;
#ifdef DEBUG
static int devid_register_corrupt = 0;
static int devid_register_corrupt_major = 0;
static int devid_register_corrupt_hint = 0;
static int devid_register_corrupt_hint_major = 0;
static int devid_lyr_debug = 0;
#define DDI_DEBUG_DEVID_DEVTS(msg, ndevs, devs) \
if (devid_lyr_debug) \
ddi_debug_devid_devts(msg, ndevs, devs)
#else
#define DDI_DEBUG_DEVID_DEVTS(msg, ndevs, devs)
#endif
#ifdef DEBUG
static void
ddi_debug_devid_devts(char *msg, int ndevs, dev_t *devs)
{
int i;
cmn_err(CE_CONT, "%s:\n", msg);
for (i = 0; i < ndevs; i++) {
cmn_err(CE_CONT, " 0x%lx\n", devs[i]);
}
}
static void
ddi_debug_devid_paths(char *msg, int npaths, char **paths)
{
int i;
cmn_err(CE_CONT, "%s:\n", msg);
for (i = 0; i < npaths; i++) {
cmn_err(CE_CONT, " %s\n", paths[i]);
}
}
static void
ddi_debug_devid_devts_per_path(char *path, int ndevs, dev_t *devs)
{
int i;
cmn_err(CE_CONT, "dev_ts per path %s\n", path);
for (i = 0; i < ndevs; i++) {
cmn_err(CE_CONT, " 0x%lx\n", devs[i]);
}
}
#endif
static int
i_ddi_devid_register(dev_info_t *dip, ddi_devid_t devid)
{
impl_devid_t *i_devid = (impl_devid_t *)devid;
size_t driver_len;
const char *driver_name;
char *devid_str;
major_t major;
if ((dip == NULL) ||
((major = ddi_driver_major(dip)) == DDI_MAJOR_T_NONE))
return (DDI_FAILURE);
if (ddi_devid_valid(devid) != DDI_SUCCESS)
return (DDI_FAILURE);
driver_name = ddi_driver_name(dip);
driver_len = strlen(driver_name);
if (driver_len > DEVID_HINT_SIZE) {
driver_name += driver_len - DEVID_HINT_SIZE;
driver_len = DEVID_HINT_SIZE;
}
bzero(i_devid->did_driver, DEVID_HINT_SIZE);
bcopy(driver_name, i_devid->did_driver, driver_len);
#ifdef DEBUG
if (devid_register_corrupt)
i_devid->did_id[0] += devid_register_corrupt;
if (devid_register_corrupt_major &&
(major == devid_register_corrupt_major))
i_devid->did_id[0] += 1;
if (devid_register_corrupt_hint)
i_devid->did_driver[0] += devid_register_corrupt_hint;
if (devid_register_corrupt_hint_major &&
(major == devid_register_corrupt_hint_major))
i_devid->did_driver[0] += 1;
#endif
if ((devid_str = ddi_devid_str_encode(devid, NULL)) == NULL)
return (DDI_FAILURE);
if (ndi_prop_update_string(DDI_DEV_T_NONE, dip,
DEVID_PROP_NAME, devid_str) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: devid property update failed",
ddi_driver_name(dip), ddi_get_instance(dip));
ddi_devid_str_free(devid_str);
return (DDI_FAILURE);
}
if (DEVI(dip)->devi_devid_str)
ddi_devid_str_free(DEVI(dip)->devi_devid_str);
DEVI(dip)->devi_devid_str = devid_str;
return (DDI_SUCCESS);
}
int
ddi_devid_register(dev_info_t *dip, ddi_devid_t devid)
{
int rval;
rval = i_ddi_devid_register(dip, devid);
if (rval == DDI_SUCCESS) {
if (e_devid_cache_register(dip, devid) == DDI_SUCCESS) {
mutex_enter(&DEVI(dip)->devi_lock);
DEVI(dip)->devi_flags |= DEVI_CACHED_DEVID;
mutex_exit(&DEVI(dip)->devi_lock);
} else if (ddi_get_name_addr(dip)) {
cmn_err(CE_WARN, "%s%d: failed to cache devid",
ddi_driver_name(dip), ddi_get_instance(dip));
}
} else {
cmn_err(CE_WARN, "%s%d: failed to register devid",
ddi_driver_name(dip), ddi_get_instance(dip));
}
return (rval);
}
static void
i_ddi_devid_unregister(dev_info_t *dip)
{
if (DEVI(dip)->devi_devid_str) {
ddi_devid_str_free(DEVI(dip)->devi_devid_str);
DEVI(dip)->devi_devid_str = NULL;
}
(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, DEVID_PROP_NAME);
}
void
ddi_devid_unregister(dev_info_t *dip)
{
mutex_enter(&DEVI(dip)->devi_lock);
DEVI(dip)->devi_flags &= ~DEVI_CACHED_DEVID;
mutex_exit(&DEVI(dip)->devi_lock);
e_devid_cache_unregister(dip);
i_ddi_devid_unregister(dip);
}
int
ddi_devid_init(
dev_info_t *dip,
ushort_t devid_type,
ushort_t nbytes,
void *id,
ddi_devid_t *ret_devid)
{
impl_devid_t *i_devid;
int sz = sizeof (*i_devid) + nbytes - sizeof (char);
int driver_len;
const char *driver_name;
switch (devid_type) {
case DEVID_SCSI3_WWN:
case DEVID_SCSI_SERIAL:
case DEVID_ATA_SERIAL:
case DEVID_NVME_NSID:
case DEVID_NVME_EUI64:
case DEVID_NVME_NGUID:
case DEVID_ENCAP:
if (nbytes == 0)
return (DDI_FAILURE);
if (id == NULL)
return (DDI_FAILURE);
break;
case DEVID_FAB:
if (nbytes != 0)
return (DDI_FAILURE);
if (id != NULL)
return (DDI_FAILURE);
nbytes = sizeof (int) +
sizeof (struct timeval32) + sizeof (short);
sz += nbytes;
break;
default:
return (DDI_FAILURE);
}
if ((i_devid = kmem_zalloc(sz, KM_SLEEP)) == NULL)
return (DDI_FAILURE);
i_devid->did_magic_hi = DEVID_MAGIC_MSB;
i_devid->did_magic_lo = DEVID_MAGIC_LSB;
i_devid->did_rev_hi = DEVID_REV_MSB;
i_devid->did_rev_lo = DEVID_REV_LSB;
DEVID_FORMTYPE(i_devid, devid_type);
DEVID_FORMLEN(i_devid, nbytes);
driver_name = ddi_driver_name(dip);
driver_len = strlen(driver_name);
if (driver_len > DEVID_HINT_SIZE) {
driver_name += driver_len - DEVID_HINT_SIZE;
driver_len = DEVID_HINT_SIZE;
}
bcopy(driver_name, i_devid->did_driver, driver_len);
if (devid_type == DEVID_FAB) {
char *cp;
uint32_t hostid;
struct timeval32 timestamp32;
int i;
int *ip;
short gen;
mutex_enter(&devid_gen_mutex);
gen = devid_gen_number++;
mutex_exit(&devid_gen_mutex);
cp = i_devid->did_id;
hostid = zone_get_hostid(NULL);
*cp++ = hibyte(hiword(hostid));
*cp++ = lobyte(hiword(hostid));
*cp++ = hibyte(loword(hostid));
*cp++ = lobyte(loword(hostid));
uniqtime32(×tamp32);
ip = (int *)×tamp32;
for (i = 0;
i < sizeof (timestamp32) / sizeof (int); i++, ip++) {
int val;
val = *ip;
*cp++ = hibyte(hiword(val));
*cp++ = lobyte(hiword(val));
*cp++ = hibyte(loword(val));
*cp++ = lobyte(loword(val));
}
*cp++ = hibyte(gen);
*cp++ = lobyte(gen);
} else
bcopy(id, i_devid->did_id, nbytes);
*ret_devid = (ddi_devid_t)i_devid;
return (DDI_SUCCESS);
}
int
ddi_devid_get(dev_info_t *dip, ddi_devid_t *ret_devid)
{
return (i_ddi_devi_get_devid(DDI_DEV_T_ANY, dip, ret_devid));
}
int
i_ddi_devi_get_devid(dev_t dev, dev_info_t *dip, ddi_devid_t *ret_devid)
{
char *devidstr;
ASSERT(dev != DDI_DEV_T_NONE);
if (ddi_prop_lookup_string(dev, dip, DDI_PROP_DONTPASS,
DEVID_PROP_NAME, &devidstr) != DDI_PROP_SUCCESS) {
if ((dev == DDI_DEV_T_ANY) ||
(ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devidstr) !=
DDI_PROP_SUCCESS)) {
return (DDI_FAILURE);
}
}
if (ddi_devid_str_decode(devidstr, ret_devid, NULL) == -1) {
ddi_prop_free(devidstr);
return (DDI_FAILURE);
}
ddi_prop_free(devidstr);
return (DDI_SUCCESS);
}
int
ddi_lyr_get_devid(dev_t dev, ddi_devid_t *ret_devid)
{
dev_info_t *dip;
int rval;
if ((dip = e_ddi_hold_devi_by_dev(dev, 0)) == NULL)
return (DDI_FAILURE);
rval = i_ddi_devi_get_devid(dev, dip, ret_devid);
ddi_release_devi(dip);
return (rval);
}
int
ddi_lyr_get_minor_name(dev_t dev, int spec_type, char **minor_name)
{
char *buf;
dev_info_t *dip;
char *nm;
int rval;
if ((dip = e_ddi_hold_devi_by_dev(dev, 0)) == NULL) {
*minor_name = NULL;
return (DDI_FAILURE);
}
buf = kmem_alloc(MAXNAMELEN, KM_SLEEP);
ndi_devi_enter(dip);
nm = i_ddi_devtspectype_to_minorname(dip, dev, spec_type);
if (nm)
(void) strcpy(buf, nm);
ndi_devi_exit(dip);
ddi_release_devi(dip);
if (nm) {
*minor_name = i_ddi_strdup(buf, KM_SLEEP);
rval = DDI_SUCCESS;
} else {
*minor_name = NULL;
rval = DDI_FAILURE;
}
kmem_free(buf, MAXNAMELEN);
return (rval);
}
int
ddi_lyr_devid_to_devlist(
ddi_devid_t devid,
const char *minor_name,
int *retndevs,
dev_t **retdevs)
{
ASSERT(ddi_devid_valid(devid) == DDI_SUCCESS);
if (e_devid_cache_to_devt_list(devid, minor_name,
retndevs, retdevs) == DDI_SUCCESS) {
ASSERT(*retndevs > 0);
DDI_DEBUG_DEVID_DEVTS("ddi_lyr_devid_to_devlist",
*retndevs, *retdevs);
return (DDI_SUCCESS);
}
if (e_ddi_devid_discovery(devid) == DDI_FAILURE) {
return (DDI_FAILURE);
}
if (e_devid_cache_to_devt_list(devid, minor_name,
retndevs, retdevs) == DDI_SUCCESS) {
ASSERT(*retndevs > 0);
DDI_DEBUG_DEVID_DEVTS("ddi_lyr_devid_to_devlist",
*retndevs, *retdevs);
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
void
ddi_lyr_free_devlist(dev_t *devlist, int ndevs)
{
kmem_free(devlist, sizeof (dev_t) * ndevs);
}
model_t
ddi_mmap_get_model(void)
{
return (get_udatamodel());
}
model_t
ddi_model_convert_from(model_t model)
{
return ((model & DDI_MODEL_MASK) & ~DDI_MODEL_NATIVE);
}
int
ddi_remove_event_handler(ddi_callback_id_t id)
{
ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)id;
dev_info_t *ddip;
ASSERT(cb);
if (!cb) {
return (DDI_FAILURE);
}
ddip = NDI_EVENT_DDIP(cb->ndi_evtcb_cookie);
return (ndi_busop_remove_eventcall(ddip, id));
}
int
ddi_add_event_handler(dev_info_t *dip, ddi_eventcookie_t event,
void (*handler)(dev_info_t *, ddi_eventcookie_t, void *, void *),
void *arg, ddi_callback_id_t *id)
{
return (ndi_busop_add_eventcall(dip, dip, event, handler, arg, id));
}
int
ddi_get_eventcookie(dev_info_t *dip, char *name,
ddi_eventcookie_t *event_cookiep)
{
return (ndi_busop_get_eventcookie(dip, dip,
name, event_cookiep));
}
void
umem_lock_undo(struct as *as, void *arg, uint_t event)
{
_NOTE(ARGUNUSED(as, event))
struct ddi_umem_cookie *cp = (struct ddi_umem_cookie *)arg;
(*cp->callbacks.cbo_umem_lock_cleanup)((ddi_umem_cookie_t)cp);
if (atomic_dec_ulong_nv((ulong_t *)(&(cp->cook_refcnt))) == 0) {
kmem_free(cp, sizeof (struct ddi_umem_cookie));
}
}
int
i_ddi_incr_locked_memory(proc_t *procp, rctl_qty_t inc)
{
ASSERT(procp != NULL);
mutex_enter(&procp->p_lock);
if (rctl_incr_locked_mem(procp, NULL, inc, 1)) {
mutex_exit(&procp->p_lock);
return (ENOMEM);
}
mutex_exit(&procp->p_lock);
return (0);
}
void
i_ddi_decr_locked_memory(proc_t *procp, rctl_qty_t dec)
{
ASSERT(procp != NULL);
mutex_enter(&procp->p_lock);
rctl_decr_locked_mem(procp, NULL, dec, 1);
mutex_exit(&procp->p_lock);
}
static void
init_lockedmem_rctl_flag(struct ddi_umem_cookie *cookie)
{
proc_t *p;
kproject_t *projp;
zone_t *zonep;
ASSERT(cookie);
p = cookie->procp;
ASSERT(p);
zonep = p->p_zone;
projp = p->p_task->tk_proj;
ASSERT(zonep);
ASSERT(projp);
if (zonep->zone_locked_mem_ctl == UINT64_MAX &&
projp->kpj_data.kpd_locked_mem_ctl == UINT64_MAX)
cookie->upd_max_lock_rctl = 0;
else
cookie->upd_max_lock_rctl = 1;
}
static int
umem_incr_devlockmem(struct ddi_umem_cookie *cookie)
{
proc_t *procp;
int ret;
ASSERT(cookie);
if (cookie->upd_max_lock_rctl == 0)
return (0);
procp = cookie->procp;
ASSERT(procp);
if ((ret = i_ddi_incr_locked_memory(procp,
cookie->size)) != 0) {
return (ret);
}
return (0);
}
static void
umem_decr_devlockmem(struct ddi_umem_cookie *cookie)
{
proc_t *proc;
if (cookie->upd_max_lock_rctl == 0)
return;
proc = (proc_t *)cookie->procp;
if (!proc)
return;
i_ddi_decr_locked_memory(proc, cookie->size);
}
int
umem_lockmemory(caddr_t addr, size_t len, int flags, ddi_umem_cookie_t *cookie,
struct umem_callback_ops *ops_vector,
proc_t *procp)
{
int error;
struct ddi_umem_cookie *p;
void (*driver_callback)() = NULL;
struct as *as;
struct seg *seg;
vnode_t *vp;
if (procp == NULL)
procp = curproc;
as = procp->p_as;
*cookie = NULL;
if ((flags & ~(DDI_UMEMLOCK_READ | DDI_UMEMLOCK_WRITE |
DDI_UMEMLOCK_LONGTERM)) != 0)
return (EINVAL);
if ((flags & (DDI_UMEMLOCK_READ | DDI_UMEMLOCK_WRITE)) == 0)
return (EINVAL);
if (((uintptr_t)addr & PAGEOFFSET) != 0)
return (EINVAL);
if ((len & PAGEOFFSET) != 0)
return (EINVAL);
if (ops_vector != NULL) {
if (ops_vector->cbo_umem_callback_version !=
UMEM_CALLBACK_VERSION)
return (EINVAL);
else
driver_callback = ops_vector->cbo_umem_lock_cleanup;
}
if ((driver_callback == NULL) && (flags & DDI_UMEMLOCK_LONGTERM))
return (EINVAL);
if (ddi_umem_unlock_thread == NULL)
i_ddi_umem_unlock_thread_start();
p = kmem_zalloc(sizeof (struct ddi_umem_cookie), KM_SLEEP);
if (flags & DDI_UMEMLOCK_WRITE) {
p->s_flags = S_WRITE;
} else {
p->s_flags = S_READ;
}
p->procp = (void *)procp;
p->asp = as;
p->size = len;
init_lockedmem_rctl_flag(p);
if (umem_incr_devlockmem(p) != 0) {
kmem_free(p, sizeof (struct ddi_umem_cookie));
*cookie = (ddi_umem_cookie_t)NULL;
return (ENOMEM);
}
error = as_pagelock(as, &(p->pparray), addr, len, p->s_flags);
if (error != 0) {
umem_decr_devlockmem(p);
kmem_free(p, sizeof (struct ddi_umem_cookie));
*cookie = (ddi_umem_cookie_t)NULL;
return (error);
}
if (flags & DDI_UMEMLOCK_LONGTERM) {
extern struct seg_ops segspt_shmops;
extern struct seg_ops segdev_ops;
AS_LOCK_ENTER(as, RW_READER);
for (seg = as_segat(as, addr); ; seg = AS_SEGNEXT(as, seg)) {
if (seg == NULL || seg->s_base > addr + len)
break;
if (seg->s_ops == &segdev_ops)
continue;
if (((seg->s_ops != &segvn_ops) &&
(seg->s_ops != &segspt_shmops)) ||
((SEGOP_GETVP(seg, addr, &vp) == 0 &&
vp != NULL && vp->v_type == VREG) &&
(SEGOP_GETTYPE(seg, addr) & MAP_SHARED))) {
as_pageunlock(as, p->pparray,
addr, len, p->s_flags);
AS_LOCK_EXIT(as);
umem_decr_devlockmem(p);
kmem_free(p, sizeof (struct ddi_umem_cookie));
*cookie = (ddi_umem_cookie_t)NULL;
return (EFAULT);
}
}
AS_LOCK_EXIT(as);
}
p->cvaddr = addr;
p->type = UMEM_LOCKED;
if (driver_callback != NULL) {
p->cook_refcnt = 2;
p->callbacks = *ops_vector;
} else {
p->cook_refcnt = 1;
}
*cookie = (ddi_umem_cookie_t)p;
if (driver_callback) {
error = as_add_callback(as, umem_lock_undo, p, AS_ALL_EVENT,
addr, len, KM_SLEEP);
if (error != 0) {
as_pageunlock(as, p->pparray,
addr, len, p->s_flags);
umem_decr_devlockmem(p);
kmem_free(p, sizeof (struct ddi_umem_cookie));
*cookie = (ddi_umem_cookie_t)NULL;
}
}
return (error);
}
static void
i_ddi_umem_unlock(struct ddi_umem_cookie *p)
{
uint_t rc;
rc = as_delete_callback(p->asp, p);
as_pageunlock(p->asp, p->pparray, p->cvaddr, p->size, p->s_flags);
umem_decr_devlockmem(p);
if (rc == AS_CALLBACK_DELETED) {
ASSERT(p->cook_refcnt == 2);
kmem_free(p, sizeof (struct ddi_umem_cookie));
} else {
if ((rc != AS_CALLBACK_DELETE_DEFERRED) ||
(atomic_dec_ulong_nv((ulong_t *)(&(p->cook_refcnt)))
== 0)) {
kmem_free(p, sizeof (struct ddi_umem_cookie));
}
}
}
static void
i_ddi_umem_unlock_thread(void)
{
struct ddi_umem_cookie *ret_cookie;
callb_cpr_t cprinfo;
CALLB_CPR_INIT(&cprinfo, &ddi_umem_unlock_mutex,
callb_generic_cpr, "unlock_thread");
for (;;) {
mutex_enter(&ddi_umem_unlock_mutex);
if (ddi_umem_unlock_head != NULL) {
ret_cookie = ddi_umem_unlock_head;
if ((ddi_umem_unlock_head =
ddi_umem_unlock_head->unl_forw) == NULL) {
ddi_umem_unlock_tail = NULL;
}
mutex_exit(&ddi_umem_unlock_mutex);
(void) i_ddi_umem_unlock(ret_cookie);
} else {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&ddi_umem_unlock_cv, &ddi_umem_unlock_mutex);
CALLB_CPR_SAFE_END(&cprinfo, &ddi_umem_unlock_mutex);
mutex_exit(&ddi_umem_unlock_mutex);
}
}
}
static void
i_ddi_umem_unlock_thread_start(void)
{
mutex_enter(&ddi_umem_unlock_mutex);
if (ddi_umem_unlock_thread == NULL) {
ddi_umem_unlock_thread = thread_create(NULL, 0,
i_ddi_umem_unlock_thread, NULL, 0, &p0,
TS_RUN, minclsyspri);
}
mutex_exit(&ddi_umem_unlock_mutex);
}
int
ddi_umem_lock(caddr_t addr, size_t len, int flags, ddi_umem_cookie_t *cookie)
{
int error;
struct ddi_umem_cookie *p;
*cookie = NULL;
if ((flags & ~(DDI_UMEMLOCK_READ | DDI_UMEMLOCK_WRITE)) != 0) {
return (EINVAL);
}
if ((flags & (DDI_UMEMLOCK_READ | DDI_UMEMLOCK_WRITE)) == 0) {
return (EINVAL);
}
if (((uintptr_t)addr & PAGEOFFSET) != 0) {
return (EINVAL);
}
if ((len & PAGEOFFSET) != 0) {
return (EINVAL);
}
if (ddi_umem_unlock_thread == NULL)
i_ddi_umem_unlock_thread_start();
p = kmem_zalloc(sizeof (struct ddi_umem_cookie), KM_SLEEP);
if (flags & DDI_UMEMLOCK_WRITE) {
p->s_flags = S_WRITE;
} else {
p->s_flags = S_READ;
}
p->procp = (void *)curproc;
p->asp = curproc->p_as;
p->size = len;
init_lockedmem_rctl_flag(p);
if (umem_incr_devlockmem(p) != 0) {
kmem_free(p, sizeof (struct ddi_umem_cookie));
*cookie = (ddi_umem_cookie_t)NULL;
return (ENOMEM);
}
error = as_pagelock(((proc_t *)p->procp)->p_as, &(p->pparray),
addr, len, p->s_flags);
if (error != 0) {
umem_decr_devlockmem(p);
kmem_free(p, sizeof (struct ddi_umem_cookie));
*cookie = (ddi_umem_cookie_t)NULL;
return (error);
}
p->cvaddr = addr;
p->type = UMEM_LOCKED;
p->cook_refcnt = 1;
*cookie = (ddi_umem_cookie_t)p;
return (error);
}
void
ddi_umem_unlock(ddi_umem_cookie_t cookie)
{
struct ddi_umem_cookie *p = (struct ddi_umem_cookie *)cookie;
ASSERT(p->type == UMEM_LOCKED);
ASSERT(CPU_ON_INTR(CPU) == 0);
ASSERT(ddi_umem_unlock_thread != NULL);
p->unl_forw = (struct ddi_umem_cookie *)NULL;
if (servicing_interrupt()) {
mutex_enter(&ddi_umem_unlock_mutex);
if (ddi_umem_unlock_head == NULL) {
ddi_umem_unlock_head = ddi_umem_unlock_tail = p;
cv_broadcast(&ddi_umem_unlock_cv);
} else {
ddi_umem_unlock_tail->unl_forw = p;
ddi_umem_unlock_tail = p;
}
mutex_exit(&ddi_umem_unlock_mutex);
} else {
(void) i_ddi_umem_unlock(p);
}
}
struct buf *
ddi_umem_iosetup(ddi_umem_cookie_t cookie, off_t off, size_t len,
int direction, dev_t dev, daddr_t blkno,
int (*iodone)(struct buf *), int sleepflag)
{
struct ddi_umem_cookie *p = (struct ddi_umem_cookie *)cookie;
struct buf *bp;
if ((off + len) > p->size) {
return (NULL);
}
if (len > p->size) {
return (NULL);
}
if ((direction != B_READ) && (direction != B_WRITE)) {
return (NULL);
}
if ((sleepflag != DDI_UMEM_SLEEP) && (sleepflag != DDI_UMEM_NOSLEEP)) {
return (NULL);
}
if ((p->type != UMEM_LOCKED) && (p->type != KMEM_NON_PAGEABLE)) {
return (NULL);
}
ASSERT((p->type == KMEM_NON_PAGEABLE) ?
(p->procp == NULL) : (p->procp != NULL));
bp = kmem_alloc(sizeof (struct buf), sleepflag);
if (bp == NULL) {
return (NULL);
}
bioinit(bp);
bp->b_flags = B_BUSY | B_PHYS | direction;
bp->b_edev = dev;
bp->b_lblkno = blkno;
bp->b_iodone = iodone;
bp->b_bcount = len;
bp->b_proc = (proc_t *)p->procp;
ASSERT(((uintptr_t)(p->cvaddr) & PAGEOFFSET) == 0);
bp->b_un.b_addr = (caddr_t)((uintptr_t)(p->cvaddr) + off);
if (p->pparray != NULL) {
bp->b_flags |= B_SHADOW;
ASSERT(((uintptr_t)(p->cvaddr) & PAGEOFFSET) == 0);
bp->b_shadow = p->pparray + btop(off);
}
return (bp);
}
ddi_devstate_t
ddi_get_devstate(dev_info_t *dip)
{
if (DEVI_IS_DEVICE_OFFLINE(dip))
return (DDI_DEVSTATE_OFFLINE);
else if (DEVI_IS_DEVICE_DOWN(dip) || DEVI_IS_BUS_DOWN(dip))
return (DDI_DEVSTATE_DOWN);
else if (DEVI_IS_BUS_QUIESCED(dip))
return (DDI_DEVSTATE_QUIESCED);
else if (DEVI_IS_DEVICE_DEGRADED(dip))
return (DDI_DEVSTATE_DEGRADED);
else
return (DDI_DEVSTATE_UP);
}
void
ddi_dev_report_fault(dev_info_t *dip, ddi_fault_impact_t impact,
ddi_fault_location_t location, const char *message)
{
struct ddi_fault_event_data fd;
ddi_eventcookie_t ec;
fd.f_dip = dip;
fd.f_impact = impact;
fd.f_location = location;
fd.f_message = message;
fd.f_oldstate = ddi_get_devstate(dip);
if (ddi_get_eventcookie(dip, DDI_DEVI_FAULT_EVENT, &ec) !=
DDI_SUCCESS)
return;
(void) ndi_post_event(dip, dip, ec, &fd);
}
char *
i_ddi_devi_class(dev_info_t *dip)
{
return (DEVI(dip)->devi_device_class);
}
int
i_ddi_set_devi_class(dev_info_t *dip, const char *devi_class, int flag)
{
struct dev_info *devi = DEVI(dip);
mutex_enter(&devi->devi_lock);
if (devi->devi_device_class)
kmem_free(devi->devi_device_class,
strlen(devi->devi_device_class) + 1);
if ((devi->devi_device_class = i_ddi_strdup(devi_class, flag))
!= NULL) {
mutex_exit(&devi->devi_lock);
return (DDI_SUCCESS);
}
mutex_exit(&devi->devi_lock);
return (DDI_FAILURE);
}
ddi_taskq_t *
ddi_taskq_create(dev_info_t *dip, const char *name, int nthreads,
pri_t pri, uint_t cflags)
{
char full_name[TASKQ_NAMELEN];
const char *tq_name;
int nodeid = 0;
if (dip == NULL)
tq_name = name;
else {
nodeid = ddi_get_instance(dip);
if (name == NULL)
name = "tq";
(void) snprintf(full_name, sizeof (full_name), "%s_%s",
ddi_driver_name(dip), name);
tq_name = full_name;
}
return ((ddi_taskq_t *)taskq_create_instance(tq_name, nodeid, nthreads,
pri == TASKQ_DEFAULTPRI ? minclsyspri : pri,
nthreads, INT_MAX, TASKQ_PREPOPULATE));
}
void
ddi_taskq_destroy(ddi_taskq_t *tq)
{
taskq_destroy((taskq_t *)tq);
}
int
ddi_taskq_dispatch(ddi_taskq_t *tq, void (* func)(void *),
void *arg, uint_t dflags)
{
taskqid_t id = taskq_dispatch((taskq_t *)tq, func, arg,
dflags == DDI_SLEEP ? TQ_SLEEP : TQ_NOSLEEP);
return (id != TASKQID_INVALID ? DDI_SUCCESS : DDI_FAILURE);
}
void
ddi_taskq_wait(ddi_taskq_t *tq)
{
taskq_wait((taskq_t *)tq);
}
void
ddi_taskq_suspend(ddi_taskq_t *tq)
{
taskq_suspend((taskq_t *)tq);
}
boolean_t
ddi_taskq_suspended(ddi_taskq_t *tq)
{
return (taskq_suspended((taskq_t *)tq));
}
void
ddi_taskq_resume(ddi_taskq_t *tq)
{
taskq_resume((taskq_t *)tq);
}
int
ddi_parse(const char *ifname, char *alnum, uint_t *nump)
{
return (ddi_parse_dlen(ifname, alnum, LIFNAMSIZ, nump));
}
int
ddi_parse_dlen(const char *ifname, char *alnum, size_t alnumsize, uint_t *nump)
{
const char *p;
int copy_len;
ulong_t num;
boolean_t nonum = B_TRUE;
char c;
copy_len = strlen(ifname);
for (p = ifname + copy_len; p != ifname; copy_len--) {
c = *--p;
if (!isdigit(c)) {
int copy_len_nul = copy_len + 1;
if (nonum || alnumsize < copy_len_nul)
return (DDI_FAILURE);
(void) strlcpy(alnum, ifname, copy_len_nul);
if (ddi_strtoul(p + 1, NULL, 10, &num) != 0)
return (DDI_FAILURE);
break;
}
nonum = B_FALSE;
}
if (copy_len == 0)
return (DDI_FAILURE);
*nump = num;
return (DDI_SUCCESS);
}
int
ddi_quiesce_not_needed(dev_info_t *dip)
{
return (DDI_SUCCESS);
}
int
ddi_quiesce_not_supported(dev_info_t *dip)
{
return (DDI_FAILURE);
}
char *
ddi_strdup(const char *str, int flag)
{
int n;
char *ptr;
ASSERT(str != NULL);
ASSERT((flag == KM_SLEEP) || (flag == KM_NOSLEEP));
n = strlen(str);
if ((ptr = kmem_alloc(n + 1, flag)) == NULL)
return (NULL);
bcopy(str, ptr, n + 1);
return (ptr);
}
char *
strdup(const char *str)
{
return (ddi_strdup(str, KM_SLEEP));
}
void
strfree(char *str)
{
ASSERT(str != NULL);
kmem_free(str, strlen(str) + 1);
}
int
ddi_cb_register(dev_info_t *dip, ddi_cb_flags_t flags, ddi_cb_func_t cbfunc,
void *arg1, void *arg2, ddi_cb_handle_t *ret_hdlp)
{
ddi_cb_t *cbp;
ASSERT(dip != NULL);
ASSERT(DDI_CB_FLAG_VALID(flags));
ASSERT(cbfunc != NULL);
ASSERT(ret_hdlp != NULL);
ASSERT(!servicing_interrupt());
if (servicing_interrupt())
return (DDI_FAILURE);
if ((dip == NULL) || !DDI_CB_FLAG_VALID(flags) ||
(cbfunc == NULL) || (ret_hdlp == NULL))
return (DDI_EINVAL);
if (DEVI(dip)->devi_cb_p != NULL)
return (DDI_EALREADY);
cbp = kmem_zalloc(sizeof (ddi_cb_t), KM_SLEEP);
cbp->cb_dip = dip;
cbp->cb_func = cbfunc;
cbp->cb_arg1 = arg1;
cbp->cb_arg2 = arg2;
cbp->cb_flags = flags;
DEVI(dip)->devi_cb_p = cbp;
if (flags & DDI_CB_FLAG_INTR)
i_ddi_irm_set_cb(dip, B_TRUE);
*ret_hdlp = (ddi_cb_handle_t)&(DEVI(dip)->devi_cb_p);
return (DDI_SUCCESS);
}
int
ddi_cb_unregister(ddi_cb_handle_t hdl)
{
ddi_cb_t *cbp;
dev_info_t *dip;
ASSERT(hdl != NULL);
ASSERT(!servicing_interrupt());
if (servicing_interrupt())
return (DDI_FAILURE);
if ((hdl == NULL) || ((cbp = *(ddi_cb_t **)hdl) == NULL) ||
((dip = cbp->cb_dip) == NULL))
return (DDI_EINVAL);
if (cbp->cb_flags & DDI_CB_FLAG_INTR)
i_ddi_irm_set_cb(dip, B_FALSE);
kmem_free(cbp, sizeof (ddi_cb_t));
DEVI(dip)->devi_cb_p = NULL;
return (DDI_SUCCESS);
}
static int
ndi2errno(int n)
{
int err = 0;
switch (n) {
case NDI_NOMEM:
err = ENOMEM;
break;
case NDI_BUSY:
err = EBUSY;
break;
case NDI_FAULT:
err = EFAULT;
break;
case NDI_FAILURE:
err = EIO;
break;
case NDI_SUCCESS:
break;
case NDI_BADHANDLE:
default:
err = EINVAL;
break;
}
return (err);
}
struct ptnode {
pnode_t nodeid;
struct ptnode *next;
};
struct pta {
dev_info_t *pdip;
devi_branch_t *bp;
uint_t flags;
dev_info_t *fdip;
struct ptnode *head;
};
static void
visit_node(pnode_t nodeid, struct pta *ap)
{
struct ptnode **nextp;
int (*select)(pnode_t, void *, uint_t);
ASSERT(nodeid != OBP_NONODE && nodeid != OBP_BADNODE);
select = ap->bp->create.prom_branch_select;
ASSERT(select);
if (select(nodeid, ap->bp->arg, 0) == DDI_SUCCESS) {
for (nextp = &ap->head; *nextp; nextp = &(*nextp)->next)
;
*nextp = kmem_zalloc(sizeof (struct ptnode), KM_SLEEP);
(*nextp)->nodeid = nodeid;
}
if ((ap->flags & DEVI_BRANCH_CHILD) == DEVI_BRANCH_CHILD)
return;
nodeid = prom_childnode(nodeid);
while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
visit_node(nodeid, ap);
nodeid = prom_nextnode(nodeid);
}
}
static int
set_infant_dip_offline(dev_info_t *dip, void *arg)
{
char *path = (char *)arg;
ASSERT(dip);
ASSERT(arg);
if (i_ddi_node_state(dip) >= DS_ATTACHED) {
(void) ddi_pathname(dip, path);
cmn_err(CE_WARN, "Attempt to set offline flag on attached "
"node: %s", path);
return (DDI_FAILURE);
}
mutex_enter(&(DEVI(dip)->devi_lock));
if (!DEVI_IS_DEVICE_OFFLINE(dip))
DEVI_SET_DEVICE_OFFLINE(dip);
mutex_exit(&(DEVI(dip)->devi_lock));
return (DDI_SUCCESS);
}
typedef struct result {
char *path;
int result;
} result_t;
static int
dip_set_offline(dev_info_t *dip, void *arg)
{
int end;
result_t *resp = (result_t *)arg;
ASSERT(dip);
ASSERT(resp);
if (e_ddi_offline_notify(dip) == DDI_FAILURE) {
resp->result = DDI_FAILURE;
return (DDI_WALK_TERMINATE);
}
if (set_infant_dip_offline(dip, resp->path) == DDI_SUCCESS)
end = DDI_SUCCESS;
else
end = DDI_FAILURE;
e_ddi_offline_finalize(dip, end);
return (DDI_WALK_CONTINUE);
}
static int
branch_set_offline(dev_info_t *dip, char *path)
{
int end;
result_t res;
if (e_ddi_offline_notify(dip) == DDI_FAILURE) {
return (DDI_FAILURE);
}
if (set_infant_dip_offline(dip, path) == DDI_SUCCESS)
end = DDI_SUCCESS;
else
end = DDI_FAILURE;
e_ddi_offline_finalize(dip, end);
if (end == DDI_FAILURE)
return (DDI_FAILURE);
res.result = DDI_SUCCESS;
res.path = path;
ndi_devi_enter(dip);
ddi_walk_devs(ddi_get_child(dip), dip_set_offline, &res);
ndi_devi_exit(dip);
return (res.result);
}
static int
create_prom_branch(void *arg, int has_changed)
{
int exists, rv;
pnode_t nodeid;
struct ptnode *tnp;
dev_info_t *dip;
struct pta *ap = arg;
devi_branch_t *bp;
char *path;
ASSERT(ap);
ASSERT(ap->fdip == NULL);
ASSERT(ap->pdip && ndi_dev_is_prom_node(ap->pdip));
bp = ap->bp;
nodeid = ddi_get_nodeid(ap->pdip);
if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) {
cmn_err(CE_WARN, "create_prom_branch: invalid "
"nodeid: 0x%x", nodeid);
return (EINVAL);
}
ap->head = NULL;
nodeid = prom_childnode(nodeid);
while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) {
visit_node(nodeid, ap);
nodeid = prom_nextnode(nodeid);
}
if (ap->head == NULL)
return (ENODEV);
path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
rv = 0;
while ((tnp = ap->head) != NULL) {
ap->head = tnp->next;
ndi_devi_enter(ap->pdip);
exists = 0;
dip = e_ddi_nodeid_to_dip(tnp->nodeid);
if (dip != NULL) {
exists = 1;
ndi_rele_devi(dip);
#ifdef DEBUG
cmn_err(CE_WARN, "create_prom_branch: dip(%p) exists"
" for nodeid 0x%x", (void *)dip, tnp->nodeid);
#endif
} else {
dip = i_ddi_create_branch(ap->pdip, tnp->nodeid);
}
kmem_free(tnp, sizeof (struct ptnode));
if (dip && !exists) {
e_ddi_branch_hold(dip);
}
ASSERT(dip == NULL || e_ddi_branch_held(dip));
if (dip == NULL || branch_set_offline(dip, path)
== DDI_FAILURE) {
ndi_devi_exit(ap->pdip);
rv = EIO;
continue;
}
ASSERT(ddi_get_parent(dip) == ap->pdip);
ndi_devi_exit(ap->pdip);
if (ap->flags & DEVI_BRANCH_CONFIGURE) {
int error = e_ddi_branch_configure(dip, &ap->fdip, 0);
if (error && rv == 0)
rv = error;
}
if (bp->devi_branch_callback && !exists)
bp->devi_branch_callback(dip, bp->arg, 0);
}
kmem_free(path, MAXPATHLEN);
return (rv);
}
static int
sid_node_create(dev_info_t *pdip, devi_branch_t *bp, dev_info_t **rdipp)
{
int rv, len;
int i, flags, ret;
dev_info_t *dip;
char *nbuf;
char *path;
static const char *noname = "<none>";
ASSERT(pdip);
ASSERT(DEVI_BUSY_OWNED(pdip));
flags = 0;
if (rdipp) {
*rdipp = NULL;
flags = DEVI_BRANCH_ROOT;
}
ndi_devi_alloc_sleep(pdip, (char *)noname, DEVI_SID_NODEID, &dip);
rv = bp->create.sid_branch_create(dip, bp->arg, flags);
nbuf = kmem_alloc(OBP_MAXDRVNAME, KM_SLEEP);
if (rv == DDI_WALK_ERROR) {
cmn_err(CE_WARN, "e_ddi_branch_create: Error setting"
" properties on devinfo node %p", (void *)dip);
goto fail;
}
len = OBP_MAXDRVNAME;
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "name", nbuf, &len)
!= DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "e_ddi_branch_create: devinfo node %p has"
"no name property", (void *)dip);
goto fail;
}
ASSERT(i_ddi_node_state(dip) == DS_PROTO);
if (ndi_devi_set_nodename(dip, nbuf, 0) != NDI_SUCCESS) {
cmn_err(CE_WARN, "e_ddi_branch_create: cannot set name (%s)"
" for devinfo node %p", nbuf, (void *)dip);
goto fail;
}
kmem_free(nbuf, OBP_MAXDRVNAME);
(void) ndi_devi_bind_driver(dip, 0);
switch (rv) {
case DDI_WALK_CONTINUE:
case DDI_WALK_PRUNESIB:
ndi_devi_enter(dip);
i = DDI_WALK_CONTINUE;
for (; i == DDI_WALK_CONTINUE; ) {
i = sid_node_create(dip, bp, NULL);
}
ASSERT(i == DDI_WALK_ERROR || i == DDI_WALK_PRUNESIB);
if (i == DDI_WALK_ERROR)
rv = i;
ndi_devi_exit(dip);
break;
case DDI_WALK_TERMINATE:
rv = DDI_WALK_PRUNESIB;
break;
case DDI_WALK_PRUNECHILD:
rv = DDI_WALK_CONTINUE;
break;
default:
ASSERT(0);
break;
}
if (rdipp)
*rdipp = dip;
ASSERT(i_ddi_node_state(dip) < DS_ATTACHED);
path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
ret = set_infant_dip_offline(dip, path);
ASSERT(ret == DDI_SUCCESS);
kmem_free(path, MAXPATHLEN);
return (rv);
fail:
(void) ndi_devi_free(dip);
kmem_free(nbuf, OBP_MAXDRVNAME);
return (DDI_WALK_ERROR);
}
static int
create_sid_branch(
dev_info_t *pdip,
devi_branch_t *bp,
dev_info_t **dipp,
uint_t flags)
{
int rv = 0, state = DDI_WALK_CONTINUE;
dev_info_t *rdip;
while (state == DDI_WALK_CONTINUE) {
ndi_devi_enter(pdip);
state = sid_node_create(pdip, bp, &rdip);
if (rdip == NULL) {
ndi_devi_exit(pdip);
ASSERT(state == DDI_WALK_ERROR);
break;
}
e_ddi_branch_hold(rdip);
ndi_devi_exit(pdip);
if (flags & DEVI_BRANCH_CONFIGURE) {
int error = e_ddi_branch_configure(rdip, dipp, 0);
if (error && rv == 0)
rv = error;
}
if (bp->devi_branch_callback)
bp->devi_branch_callback(rdip, bp->arg, 0);
}
ASSERT(state == DDI_WALK_ERROR || state == DDI_WALK_PRUNESIB);
return (state == DDI_WALK_ERROR ? EIO : rv);
}
int
e_ddi_branch_create(
dev_info_t *pdip,
devi_branch_t *bp,
dev_info_t **dipp,
uint_t flags)
{
int prom_devi, sid_devi, error;
if (pdip == NULL || bp == NULL || bp->type == 0)
return (EINVAL);
prom_devi = (bp->type == DEVI_BRANCH_PROM) ? 1 : 0;
sid_devi = (bp->type == DEVI_BRANCH_SID) ? 1 : 0;
if (prom_devi && bp->create.prom_branch_select == NULL)
return (EINVAL);
else if (sid_devi && bp->create.sid_branch_create == NULL)
return (EINVAL);
else if (!prom_devi && !sid_devi)
return (EINVAL);
if (flags & DEVI_BRANCH_EVENT)
return (EINVAL);
if (prom_devi) {
struct pta pta = {0};
pta.pdip = pdip;
pta.bp = bp;
pta.flags = flags;
error = prom_tree_access(create_prom_branch, &pta, NULL);
if (dipp)
*dipp = pta.fdip;
else if (pta.fdip)
ndi_rele_devi(pta.fdip);
} else {
error = create_sid_branch(pdip, bp, dipp, flags);
}
return (error);
}
int
e_ddi_branch_configure(dev_info_t *rdip, dev_info_t **dipp, uint_t flags)
{
int rv;
char *devnm;
dev_info_t *pdip;
if (dipp)
*dipp = NULL;
if (rdip == NULL || flags != 0 || (flags & DEVI_BRANCH_EVENT))
return (EINVAL);
pdip = ddi_get_parent(rdip);
ndi_hold_devi(pdip);
if (!e_ddi_branch_held(rdip)) {
ndi_rele_devi(pdip);
cmn_err(CE_WARN, "e_ddi_branch_configure: "
"dip(%p) not held", (void *)rdip);
return (EINVAL);
}
if (i_ddi_node_state(rdip) < DS_INITIALIZED) {
if (ndi_devi_bind_driver(rdip, 0) != NDI_SUCCESS) {
ndi_rele_devi(pdip);
return (0);
}
if (ddi_initchild(pdip, rdip) != DDI_SUCCESS) {
rv = NDI_FAILURE;
goto out;
}
}
ASSERT(i_ddi_node_state(rdip) >= DS_INITIALIZED);
devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
(void) ddi_deviname(rdip, devnm);
if ((rv = ndi_devi_config_one(pdip, devnm+1, &rdip,
NDI_DEVI_ONLINE | NDI_CONFIG)) == NDI_SUCCESS) {
ndi_rele_devi(rdip);
}
kmem_free(devnm, MAXNAMELEN + 1);
out:
if (rv != NDI_SUCCESS && dipp && rdip) {
ndi_hold_devi(rdip);
*dipp = rdip;
}
ndi_rele_devi(pdip);
return (ndi2errno(rv));
}
void
e_ddi_branch_hold(dev_info_t *rdip)
{
if (e_ddi_branch_held(rdip)) {
cmn_err(CE_WARN, "e_ddi_branch_hold: branch already held");
return;
}
mutex_enter(&DEVI(rdip)->devi_lock);
if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) == 0) {
DEVI(rdip)->devi_flags |= DEVI_BRANCH_HELD;
DEVI(rdip)->devi_ref++;
}
ASSERT(DEVI(rdip)->devi_ref > 0);
mutex_exit(&DEVI(rdip)->devi_lock);
}
int
e_ddi_branch_held(dev_info_t *rdip)
{
int rv = 0;
mutex_enter(&DEVI(rdip)->devi_lock);
if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) &&
DEVI(rdip)->devi_ref > 0) {
rv = 1;
}
mutex_exit(&DEVI(rdip)->devi_lock);
return (rv);
}
void
e_ddi_branch_rele(dev_info_t *rdip)
{
mutex_enter(&DEVI(rdip)->devi_lock);
DEVI(rdip)->devi_flags &= ~DEVI_BRANCH_HELD;
DEVI(rdip)->devi_ref--;
mutex_exit(&DEVI(rdip)->devi_lock);
}
int
e_ddi_branch_unconfigure(
dev_info_t *rdip,
dev_info_t **dipp,
uint_t flags)
{
int rv;
int destroy;
char *devnm;
uint_t nflags;
dev_info_t *pdip;
if (dipp)
*dipp = NULL;
if (rdip == NULL)
return (EINVAL);
pdip = ddi_get_parent(rdip);
ASSERT(pdip);
if (DEVI_BUSY_OWNED(pdip)) {
cmn_err(CE_WARN, "e_ddi_branch_unconfigure: failed: parent"
" devinfo node(%p) is busy held", (void *)pdip);
return (EINVAL);
}
destroy = (flags & DEVI_BRANCH_DESTROY) ? 1 : 0;
devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
ndi_devi_enter(pdip);
(void) ddi_deviname(rdip, devnm);
ndi_devi_exit(pdip);
(void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE);
ndi_devi_enter(pdip);
(void) ddi_deviname(rdip, devnm);
if (!e_ddi_branch_held(rdip)) {
kmem_free(devnm, MAXNAMELEN + 1);
ndi_devi_exit(pdip);
cmn_err(CE_WARN, "e_ddi_%s_branch: dip(%p) not held",
destroy ? "destroy" : "unconfigure", (void *)rdip);
return (EINVAL);
}
e_ddi_branch_rele(rdip);
nflags = NDI_DEVI_OFFLINE;
if (destroy || (flags & DEVI_BRANCH_DESTROY)) {
nflags |= NDI_DEVI_REMOVE;
destroy = 1;
} else {
nflags |= NDI_UNCONFIG;
}
if (flags & DEVI_BRANCH_EVENT)
nflags |= NDI_POST_EVENT;
if (i_ddi_devi_attached(pdip) &&
(i_ddi_node_state(rdip) >= DS_INITIALIZED)) {
rv = ndi_devi_unconfig_one(pdip, devnm+1, dipp, nflags);
} else {
rv = e_ddi_devi_unconfig(rdip, dipp, nflags);
if (rv == NDI_SUCCESS) {
ASSERT(!destroy || ddi_get_child(rdip) == NULL);
rv = ndi_devi_offline(rdip, nflags);
}
}
if (!destroy || rv != NDI_SUCCESS) {
e_ddi_branch_hold(rdip);
}
kmem_free(devnm, MAXNAMELEN + 1);
ndi_devi_exit(pdip);
return (ndi2errno(rv));
}
int
e_ddi_branch_destroy(dev_info_t *rdip, dev_info_t **dipp, uint_t flag)
{
return (e_ddi_branch_unconfigure(rdip, dipp,
flag|DEVI_BRANCH_DESTROY));
}
#define NUMCHAINS 17
struct devi_busy {
int dv_total;
int s_total;
mod_hash_t *dv_hash;
mod_hash_t *s_hash;
int (*callback)(dev_info_t *, void *, uint_t);
void *arg;
};
static int
visit_dip(dev_info_t *dip, void *arg)
{
uintptr_t sbusy, dvbusy, ref;
struct devi_busy *bsp = arg;
ASSERT(bsp->callback);
if ((ref = e_ddi_devi_holdcnt(dip)) == 0) {
return (bsp->callback(dip, bsp->arg, 0));
}
if (mod_hash_find(bsp->dv_hash, dip, (mod_hash_val_t *)&dvbusy))
dvbusy = 0;
if (mod_hash_find(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
sbusy = 0;
#ifdef DEBUG
if (ref < sbusy || ref < dvbusy) {
cmn_err(CE_WARN, "dip(%p): sopen = %lu, dvopen = %lu "
"dip ref = %lu\n", (void *)dip, sbusy, dvbusy, ref);
}
#endif
dvbusy = (sbusy > dvbusy) ? sbusy : dvbusy;
return (bsp->callback(dip, bsp->arg, dvbusy));
}
static int
visit_snode(struct snode *sp, void *arg)
{
uintptr_t sbusy;
dev_info_t *dip;
int count;
struct devi_busy *bsp = arg;
ASSERT(sp);
dip = NULL;
count = spec_devi_open_count(sp, &dip);
if (count <= 0)
return (DDI_WALK_CONTINUE);
ASSERT(dip);
if (mod_hash_remove(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy))
sbusy = count;
else
sbusy += count;
if (mod_hash_insert(bsp->s_hash, dip, (mod_hash_val_t)sbusy)) {
cmn_err(CE_WARN, "%s: s_hash insert failed: dip=0x%p, "
"sbusy = %lu", "e_ddi_branch_referenced",
(void *)dip, sbusy);
}
bsp->s_total += count;
return (DDI_WALK_CONTINUE);
}
static void
visit_dvnode(struct dv_node *dv, void *arg)
{
uintptr_t dvbusy;
uint_t count;
struct vnode *vp;
struct devi_busy *bsp = arg;
ASSERT(dv && dv->dv_devi);
vp = DVTOV(dv);
mutex_enter(&vp->v_lock);
count = vp->v_count;
mutex_exit(&vp->v_lock);
if (!count)
return;
if (mod_hash_remove(bsp->dv_hash, dv->dv_devi,
(mod_hash_val_t *)&dvbusy))
dvbusy = count;
else
dvbusy += count;
if (mod_hash_insert(bsp->dv_hash, dv->dv_devi,
(mod_hash_val_t)dvbusy)) {
cmn_err(CE_WARN, "%s: dv_hash insert failed: dip=0x%p, "
"dvbusy=%lu", "e_ddi_branch_referenced",
(void *)dv->dv_devi, dvbusy);
}
bsp->dv_total += count;
}
int
e_ddi_branch_referenced(
dev_info_t *rdip,
int (*callback)(dev_info_t *dip, void *arg, uint_t ref),
void *arg)
{
char *path;
dev_info_t *pdip;
struct devi_busy bsa = {0};
ASSERT(rdip);
path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
ndi_hold_devi(rdip);
pdip = ddi_get_parent(rdip);
ASSERT(pdip);
if (!e_ddi_branch_held(rdip) || DEVI_BUSY_OWNED(pdip)) {
cmn_err(CE_WARN, "e_ddi_branch_referenced: failed: "
"devinfo branch(%p) not held or parent busy held",
(void *)rdip);
ndi_rele_devi(rdip);
kmem_free(path, MAXPATHLEN);
return (-1);
}
ndi_devi_enter(pdip);
(void) ddi_pathname(rdip, path);
ndi_devi_exit(pdip);
bsa.dv_hash = mod_hash_create_ptrhash("dv_node busy hash", NUMCHAINS,
mod_hash_null_valdtor, sizeof (struct dev_info));
bsa.s_hash = mod_hash_create_ptrhash("snode busy hash", NUMCHAINS,
mod_hash_null_valdtor, sizeof (struct snode));
if (devfs_walk(path, visit_dvnode, &bsa)) {
cmn_err(CE_WARN, "e_ddi_branch_referenced: "
"devfs walk failed for: %s", path);
kmem_free(path, MAXPATHLEN);
bsa.s_total = bsa.dv_total = -1;
goto out;
}
kmem_free(path, MAXPATHLEN);
spec_snode_walk(visit_snode, &bsa);
if (callback == NULL)
goto out;
bsa.callback = callback;
bsa.arg = arg;
if (visit_dip(rdip, &bsa) == DDI_WALK_CONTINUE) {
ndi_devi_enter(rdip);
ddi_walk_devs(ddi_get_child(rdip), visit_dip, &bsa);
ndi_devi_exit(rdip);
}
out:
ndi_rele_devi(rdip);
mod_hash_destroy_ptrhash(bsa.s_hash);
mod_hash_destroy_ptrhash(bsa.dv_hash);
return (bsa.s_total > bsa.dv_total ? bsa.s_total : bsa.dv_total);
}