#if defined(lint) && !defined(DEBUG)
#define DEBUG
#endif
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
#include <sys/usb/usba.h>
#include <sys/fcntl.h>
#include <sys/cmn_err.h>
#include <sys/usb/clients/video/usbvc/usbvc_var.h>
#include <sys/videodev2.h>
#include <sys/usb/clients/video/usbvc/usbvc.h>
static uint_t usbvc_errmask = (uint_t)PRINT_MASK_ALL;
static uint_t usbvc_errlevel = 4;
static uint_t usbvc_instance_debug = (uint_t)-1;
static char *name = "usbvc";
static int usbvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int usbvc_attach(dev_info_t *, ddi_attach_cmd_t);
static int usbvc_detach(dev_info_t *, ddi_detach_cmd_t);
static void usbvc_cleanup(dev_info_t *, usbvc_state_t *);
static int usbvc_open(dev_t *, int, int, cred_t *);
static int usbvc_close(dev_t, int, int, cred_t *);
static int usbvc_read(dev_t, struct uio *uip_p, cred_t *);
static int usbvc_strategy(struct buf *);
static void usbvc_minphys(struct buf *);
static int usbvc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int usbvc_devmap(dev_t, devmap_cookie_t, offset_t,
size_t, size_t *, uint_t);
static int usbvc_power(dev_info_t *, int, int);
static void usbvc_init_power_mgmt(usbvc_state_t *);
static void usbvc_destroy_power_mgmt(usbvc_state_t *);
static void usbvc_pm_busy_component(usbvc_state_t *);
static void usbvc_pm_idle_component(usbvc_state_t *);
static int usbvc_pwrlvl0(usbvc_state_t *);
static int usbvc_pwrlvl1(usbvc_state_t *);
static int usbvc_pwrlvl2(usbvc_state_t *);
static int usbvc_pwrlvl3(usbvc_state_t *);
static void usbvc_cpr_suspend(dev_info_t *);
static void usbvc_cpr_resume(dev_info_t *);
static void usbvc_restore_device_state(dev_info_t *, usbvc_state_t *);
static int usbvc_disconnect_event_cb(dev_info_t *);
static int usbvc_reconnect_event_cb(dev_info_t *);
static void usbvc_init_sync_objs(usbvc_state_t *);
static void usbvc_fini_sync_objs(usbvc_state_t *);
static void usbvc_init_lists(usbvc_state_t *);
static void usbvc_fini_lists(usbvc_state_t *);
static void usbvc_free_ctrl_descr(usbvc_state_t *);
static void usbvc_free_stream_descr(usbvc_state_t *);
static int usbvc_chk_descr_len(uint8_t, uint8_t, uint8_t,
usb_cvs_data_t *);
static usbvc_stream_if_t *usbvc_parse_stream_if(usbvc_state_t *, int);
static int usbvc_parse_ctrl_if(usbvc_state_t *);
static int usbvc_parse_stream_ifs(usbvc_state_t *);
static void usbvc_parse_color_still(usbvc_state_t *,
usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
static void usbvc_parse_frames(usbvc_state_t *, usbvc_format_group_t *,
usb_cvs_data_t *, uint_t, uint_t);
static int usbvc_parse_format_group(usbvc_state_t *,
usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
static int usbvc_parse_format_groups(usbvc_state_t *, usbvc_stream_if_t *);
static int usbvc_parse_stream_header(usbvc_state_t *, usbvc_stream_if_t *);
static int usbvc_alloc_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
static int usbvc_read_buf(usbvc_state_t *, struct buf *);
static void usbvc_free_read_buf(usbvc_buf_t *);
static void usbvc_free_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
static void usbvc_close_isoc_pipe(usbvc_state_t *, usbvc_stream_if_t *);
static void usbvc_isoc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
static void usbvc_isoc_exc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
static int usbvc_set_alt(usbvc_state_t *, usbvc_stream_if_t *);
static int usbvc_decode_stream_header(usbvc_state_t *, usbvc_buf_grp_t *,
mblk_t *, int);
static int usbvc_serialize_access(usbvc_state_t *, boolean_t);
static void usbvc_release_access(usbvc_state_t *);
static int usbvc_set_default_stream_fmt(usbvc_state_t *);
static usb_event_t usbvc_events = {
usbvc_disconnect_event_cb,
usbvc_reconnect_event_cb,
NULL, NULL
};
struct cb_ops usbvc_cb_ops = {
usbvc_open,
usbvc_close,
usbvc_strategy,
nulldev,
nulldev,
usbvc_read,
nodev,
usbvc_ioctl,
usbvc_devmap,
nodev,
ddi_devmap_segmap,
nochpoll,
ddi_prop_op,
NULL,
D_MP | D_DEVMAP
};
static struct dev_ops usbvc_ops = {
DEVO_REV,
0,
usbvc_info,
nulldev,
nulldev,
usbvc_attach,
usbvc_detach,
nodev,
&usbvc_cb_ops,
NULL,
usbvc_power,
ddi_quiesce_not_needed,
};
static struct modldrv usbvc_modldrv = {
&mod_driverops,
"USB video class driver",
&usbvc_ops
};
static struct modlinkage modlinkage = {
MODREV_1,
&usbvc_modldrv,
NULL
};
#define USBVC_INITIAL_SOFT_SPACE 1
static void *usbvc_statep;
int
_init(void)
{
int rval;
if ((rval = ddi_soft_state_init(&usbvc_statep,
sizeof (usbvc_state_t), USBVC_INITIAL_SOFT_SPACE)) != 0) {
return (rval);
}
if ((rval = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&usbvc_statep);
}
return (rval);
}
int
_fini(void)
{
int rval;
if ((rval = mod_remove(&modlinkage)) != 0) {
return (rval);
}
ddi_soft_state_fini(&usbvc_statep);
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
usbvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
void *arg, void **result)
{
usbvc_state_t *usbvcp;
int error = DDI_FAILURE;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if ((usbvcp = ddi_get_soft_state(usbvc_statep,
getminor((dev_t)arg))) != NULL) {
*result = usbvcp->usbvc_dip;
if (*result != NULL) {
error = DDI_SUCCESS;
}
} else {
*result = NULL;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(uintptr_t)getminor((dev_t)arg);
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
static int
usbvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
usbvc_state_t *usbvcp = NULL;
int status;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
usbvc_cpr_resume(dip);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(usbvc_statep, instance) == DDI_SUCCESS) {
usbvcp = ddi_get_soft_state(usbvc_statep, instance);
}
if (usbvcp == NULL) {
return (DDI_FAILURE);
}
usbvcp->usbvc_dip = dip;
usbvcp->usbvc_log_handle = usb_alloc_log_hdl(dip,
"usbvc", &usbvc_errlevel,
&usbvc_errmask, &usbvc_instance_debug, 0);
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_attach: enter");
if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_attach: usb_client_attach failed, error code:%d",
status);
goto fail;
}
if ((status = usb_get_dev_data(dip, &usbvcp->usbvc_reg,
USB_PARSE_LVL_ALL, 0)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_attach: usb_get_dev_data failed, error code:%d",
status);
goto fail;
}
usbvc_init_sync_objs(usbvcp);
if ((status = ddi_create_minor_node(dip, name, S_IFCHR, instance,
"usb_video", 0)) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_attach: Error creating minor node, error code:%d",
status);
goto fail;
}
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_init_lists(usbvcp);
usbvcp->usbvc_default_ph = usbvcp->usbvc_reg->dev_default_ph;
usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
mutex_exit(&usbvcp->usbvc_mutex);
usbvc_init_power_mgmt(usbvcp);
if ((status = usbvc_parse_ctrl_if(usbvcp)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_attach: parse ctrl interface fail, error code:%d",
status);
goto fail;
}
if ((status = usbvc_parse_stream_ifs(usbvcp)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_attach: parse stream interfaces fail, error code:%d",
status);
goto fail;
}
(void) usbvc_set_default_stream_fmt(usbvcp);
if ((status = usb_register_event_cbs(dip, &usbvc_events, 0)) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_attach: register_event_cbs failed, error code:%d",
status);
goto fail;
}
ddi_report_dev(dip);
return (DDI_SUCCESS);
fail:
if (usbvcp) {
usbvc_cleanup(dip, usbvcp);
}
return (DDI_FAILURE);
}
static int
usbvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
int rval = USB_FAILURE;
switch (cmd) {
case DDI_DETACH:
mutex_enter(&usbvcp->usbvc_mutex);
ASSERT((usbvcp->usbvc_drv_state & USBVC_OPEN) == 0);
mutex_exit(&usbvcp->usbvc_mutex);
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_detach: enter for detach");
usbvc_cleanup(dip, usbvcp);
rval = USB_SUCCESS;
break;
case DDI_SUSPEND:
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_detach: enter for suspend");
usbvc_cpr_suspend(dip);
rval = USB_SUCCESS;
break;
default:
break;
}
return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
}
static void
usbvc_cleanup(dev_info_t *dip, usbvc_state_t *usbvcp)
{
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"Cleanup: enter");
if (usbvcp->usbvc_locks_initialized) {
usb_unregister_event_cbs(dip, &usbvc_events);
mutex_enter(&usbvcp->usbvc_mutex);
(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
usbvc_destroy_power_mgmt(usbvcp);
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_fini_lists(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
ddi_remove_minor_node(dip, NULL);
usbvc_fini_sync_objs(usbvcp);
}
usb_client_detach(dip, usbvcp->usbvc_reg);
usb_free_log_hdl(usbvcp->usbvc_log_handle);
ddi_soft_state_free(usbvc_statep, ddi_get_instance(dip));
ddi_prop_remove_all(dip);
}
static int
usbvc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
{
usbvc_state_t *usbvcp =
ddi_get_soft_state(usbvc_statep, getminor(*devp));
if (usbvcp == NULL) {
return (ENXIO);
}
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_open: enter, dev_stat=%d", usbvcp->usbvc_dev_state);
if (usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) {
mutex_exit(&usbvcp->usbvc_mutex);
return (ENODEV);
}
if (usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED) {
mutex_exit(&usbvcp->usbvc_mutex);
return (EIO);
}
if ((usbvcp->usbvc_drv_state & USBVC_OPEN) != 0) {
mutex_exit(&usbvcp->usbvc_mutex);
return (EBUSY);
}
usbvcp->usbvc_drv_state |= USBVC_OPEN;
if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) == 0) {
usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
usbvcp->usbvc_serial_inuse = B_FALSE;
mutex_exit(&usbvcp->usbvc_mutex);
return (EINTR);
}
usbvc_pm_busy_component(usbvcp);
if (usbvcp->usbvc_pm->usbvc_current_power != USB_DEV_OS_FULL_PWR) {
usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
mutex_exit(&usbvcp->usbvc_mutex);
(void) pm_raise_power(usbvcp->usbvc_dip,
0, USB_DEV_OS_FULL_PWR);
mutex_enter(&usbvcp->usbvc_mutex);
usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
}
usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_open: end.");
return (0);
}
static int
usbvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
usbvc_stream_if_t *strm_if;
int if_num;
usbvc_state_t *usbvcp =
ddi_get_soft_state(usbvc_statep, getminor(dev));
USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"close: enter");
mutex_enter(&usbvcp->usbvc_mutex);
(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
mutex_exit(&usbvcp->usbvc_mutex);
USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"close: cleaning up...");
(void) usb_pipe_drain_reqs(usbvcp->usbvc_dip,
usbvcp->usbvc_reg->dev_default_ph, 0,
USB_FLAGS_SLEEP, NULL, 0);
mutex_enter(&usbvcp->usbvc_mutex);
strm_if = usbvcp->usbvc_curr_strm;
if (strm_if->start_polling == 1) {
mutex_exit(&usbvcp->usbvc_mutex);
usb_pipe_stop_isoc_polling(strm_if->datain_ph, USB_FLAGS_SLEEP);
mutex_enter(&usbvcp->usbvc_mutex);
strm_if->start_polling = 0;
}
strm_if->stream_on = 0;
usbvc_close_isoc_pipe(usbvcp, strm_if);
if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
mutex_exit(&usbvcp->usbvc_mutex);
(void) usb_set_alt_if(usbvcp->usbvc_dip, if_num, 0,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_free_read_bufs(usbvcp, strm_if);
strm_if->buf_read_num = USBVC_DEFAULT_READ_BUF_NUM;
usbvc_free_map_bufs(usbvcp, strm_if);
usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
usbvc_release_access(usbvcp);
usbvc_pm_idle_component(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
return (0);
}
static int
usbvc_read(dev_t dev, struct uio *uio_p, cred_t *cred_p)
{
int rval;
usbvc_stream_if_t *strm_if;
usbvc_state_t *usbvcp =
ddi_get_soft_state(usbvc_statep, getminor(dev));
USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_read: enter");
mutex_enter(&usbvcp->usbvc_mutex);
if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_read: Device is not available,"
" dev_stat=%d", usbvcp->usbvc_dev_state);
mutex_exit(&usbvcp->usbvc_mutex);
return (EFAULT);
}
if ((uio_p->uio_fmode & (FNDELAY|FNONBLOCK)) &&
(usbvcp->usbvc_serial_inuse != B_FALSE)) {
USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_read: non-blocking read, return fail.");
mutex_exit(&usbvcp->usbvc_mutex);
return (EAGAIN);
}
if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_read: serialize_access failed.");
rval = EFAULT;
goto fail;
}
strm_if = usbvcp->usbvc_curr_strm;
if (!strm_if) {
USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_read: no stream interfaces");
rval = EFAULT;
goto fail;
}
if (strm_if->datain_ph == NULL) {
if (usbvc_open_isoc_pipe(usbvcp, strm_if) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_READ,
usbvcp->usbvc_log_handle,
"usbvc_read: first read, open pipe fail");
rval = EFAULT;
goto fail;
}
if (usbvc_alloc_read_bufs(usbvcp, strm_if) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_READ,
usbvcp->usbvc_log_handle,
"usbvc_read: allocate rw bufs fail");
rval = EFAULT;
goto fail;
}
}
if (strm_if->start_polling != 1) {
if (usbvc_start_isoc_polling(usbvcp, strm_if, 0) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_READ,
usbvcp->usbvc_log_handle,
"usbvc_read: usbvc_start_isoc_polling fail");
rval = EFAULT;
goto fail;
}
strm_if->start_polling = 1;
}
if (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_read: full buf list is empty.");
if (uio_p->uio_fmode & (FNDELAY | FNONBLOCK)) {
USB_DPRINTF_L2(PRINT_MASK_READ,
usbvcp->usbvc_log_handle, "usbvc_read: fail, "
"non-blocking read, done buf is empty.");
rval = EAGAIN;
goto fail;
}
while (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
USB_DPRINTF_L3(PRINT_MASK_READ,
usbvcp->usbvc_log_handle,
"usbvc_read: wait for done buf");
if (cv_wait_sig(&usbvcp->usbvc_read_cv,
&usbvcp->usbvc_mutex) <= 0) {
rval = EINTR;
goto fail;
}
if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
rval = EINTR;
goto fail;
}
}
}
mutex_exit(&usbvcp->usbvc_mutex);
rval = physio(usbvc_strategy, NULL, dev, B_READ,
usbvc_minphys, uio_p);
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
return (rval);
fail:
usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
return (rval);
}
static int
usbvc_strategy(struct buf *bp)
{
usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep,
getminor(bp->b_edev));
USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_strategy: enter");
bp->b_resid = bp->b_bcount;
if (bp->b_flags & (B_PHYS | B_PAGEIO)) {
bp_mapin(bp);
}
mutex_enter(&usbvcp->usbvc_mutex);
if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_strategy: device can't be accessed");
mutex_exit(&usbvcp->usbvc_mutex);
goto fail;
}
if (usbvc_read_buf(usbvcp, bp) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_strategy: read full buf list fail");
mutex_exit(&usbvcp->usbvc_mutex);
goto fail;
}
mutex_exit(&usbvcp->usbvc_mutex);
biodone(bp);
return (0);
fail:
USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_strategy: strategy fail");
bp->b_private = NULL;
bioerror(bp, EIO);
biodone(bp);
return (0);
}
static void
usbvc_minphys(struct buf *bp)
{
dev_t dev = bp->b_edev;
usbvc_stream_if_t *strm_if;
uint32_t maxsize;
usbvc_state_t *usbvcp =
ddi_get_soft_state(usbvc_statep, getminor(dev));
mutex_enter(&usbvcp->usbvc_mutex);
strm_if = usbvcp->usbvc_curr_strm;
LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, maxsize);
USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"usbvc_minphys: max read size=%d", maxsize);
if (bp->b_bcount > maxsize) {
bp->b_bcount = maxsize;
}
mutex_exit(&usbvcp->usbvc_mutex);
}
static int
usbvc_ioctl(dev_t dev, int cmd, intptr_t arg,
int mode, cred_t *cred_p, int *rval_p)
{
int rv = 0;
usbvc_state_t *usbvcp =
ddi_get_soft_state(usbvc_statep, getminor(dev));
if (usbvcp == NULL) {
return (ENXIO);
}
USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"ioctl enter, cmd=%x", cmd);
mutex_enter(&usbvcp->usbvc_mutex);
if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"ioctl: Device is not online,"
" dev_stat=%d", usbvcp->usbvc_dev_state);
mutex_exit(&usbvcp->usbvc_mutex);
return (EFAULT);
}
if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
usbvcp->usbvc_serial_inuse = B_FALSE;
mutex_exit(&usbvcp->usbvc_mutex);
USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"serialize_access failed.");
return (EFAULT);
}
mutex_exit(&usbvcp->usbvc_mutex);
rv = usbvc_v4l2_ioctl(usbvcp, cmd, arg, mode);
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"usbvc_ioctl exit");
return (rv);
}
static int
usbvc_devmap(dev_t dev, devmap_cookie_t handle, offset_t off,
size_t len, size_t *maplen, uint_t model)
{
usbvc_state_t *usbvcp;
int error, i;
usbvc_buf_t *buf = NULL;
usbvc_stream_if_t *strm_if;
usbvc_buf_grp_t *bufgrp;
usbvcp = ddi_get_soft_state(usbvc_statep, getminor(dev));
if (usbvcp == NULL) {
USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
"usbvc_devmap: usbvcp == NULL");
return (ENXIO);
}
USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
"devmap: memory map for instance(%d), off=%llx,"
"len=%ld, maplen=%ld, model=%d", getminor(dev), off,
len, *maplen, model);
mutex_enter(&usbvcp->usbvc_mutex);
(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
strm_if = usbvcp->usbvc_curr_strm;
if (!strm_if) {
USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
"usbvc_devmap: No current strm if");
mutex_exit(&usbvcp->usbvc_mutex);
return (ENXIO);
}
bufgrp = &strm_if->buf_map;
for (i = 0; i < bufgrp->buf_cnt; i++) {
if (bufgrp->buf_head[i].v4l2_buf.m.offset == off) {
buf = &bufgrp->buf_head[i];
break;
}
}
USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
"usbvc_devmap: idx=%d", i);
if (buf == NULL) {
mutex_exit(&usbvcp->usbvc_mutex);
return (ENXIO);
}
len = ptob(btopr(len));
if (len > ptob(btopr(buf->len))) {
USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
"usbvc_devmap: len=0x%lx", len);
mutex_exit(&usbvcp->usbvc_mutex);
return (ENXIO);
}
mutex_exit(&usbvcp->usbvc_mutex);
error = devmap_umem_setup(handle, usbvcp->usbvc_dip, NULL,
buf->umem_cookie, off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
mutex_enter(&usbvcp->usbvc_mutex);
*maplen = len;
if (error == 0 && buf->status == USBVC_BUF_INIT) {
buf->status = USBVC_BUF_MAPPED;
} else {
USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
"usbvc_devmap: devmap_umem_setup, err=%d", error);
}
(void) usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
return (error);
}
static int
usbvc_power(dev_info_t *dip, int comp, int level)
{
usbvc_state_t *usbvcp;
usbvc_power_t *pm;
int rval = USB_FAILURE;
usbvcp = ddi_get_soft_state(usbvc_statep, ddi_get_instance(dip));
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_power: enter: level = %d, dev_state: %x",
level, usbvcp->usbvc_dev_state);
if (usbvcp->usbvc_pm == NULL) {
goto done;
}
pm = usbvcp->usbvc_pm;
if (USB_DEV_PWRSTATE_OK(pm->usbvc_pwr_states, level)) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_power: illegal power level = %d "
"pwr_states: %x", level, pm->usbvc_pwr_states);
goto done;
}
if (pm->usbvc_raise_power && (level < (int)pm->usbvc_current_power)) {
goto done;
}
switch (level) {
case USB_DEV_OS_PWR_OFF :
rval = usbvc_pwrlvl0(usbvcp);
break;
case USB_DEV_OS_PWR_1 :
rval = usbvc_pwrlvl1(usbvcp);
break;
case USB_DEV_OS_PWR_2 :
rval = usbvc_pwrlvl2(usbvcp);
break;
case USB_DEV_OS_FULL_PWR :
rval = usbvc_pwrlvl3(usbvcp);
break;
}
done:
mutex_exit(&usbvcp->usbvc_mutex);
return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
}
static void
usbvc_init_power_mgmt(usbvc_state_t *usbvcp)
{
usbvc_power_t *usbvcpm;
uint_t pwr_states;
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"init_power_mgmt enter");
usbvcpm = kmem_zalloc(sizeof (usbvc_power_t), KM_SLEEP);
mutex_enter(&usbvcp->usbvc_mutex);
usbvcp->usbvc_pm = usbvcpm;
usbvcpm->usbvc_state = usbvcp;
usbvcpm->usbvc_pm_capabilities = 0;
usbvcpm->usbvc_current_power = USB_DEV_OS_FULL_PWR;
mutex_exit(&usbvcp->usbvc_mutex);
if (usb_create_pm_components(usbvcp->usbvc_dip, &pwr_states) ==
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_init_power_mgmt: created PM components");
if (usb_handle_remote_wakeup(usbvcp->usbvc_dip,
USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
usbvcpm->usbvc_wakeup_enabled = 1;
} else {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_init_power_mgmt:"
" remote wakeup not supported");
}
mutex_enter(&usbvcp->usbvc_mutex);
usbvcpm->usbvc_pwr_states = (uint8_t)pwr_states;
usbvc_pm_busy_component(usbvcp);
usbvcpm->usbvc_raise_power = B_TRUE;
mutex_exit(&usbvcp->usbvc_mutex);
(void) pm_raise_power(
usbvcp->usbvc_dip, 0, USB_DEV_OS_FULL_PWR);
mutex_enter(&usbvcp->usbvc_mutex);
usbvcpm->usbvc_raise_power = B_FALSE;
usbvc_pm_idle_component(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
}
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_init_power_mgmt: end");
}
static void
usbvc_destroy_power_mgmt(usbvc_state_t *usbvcp)
{
usbvc_power_t *pm;
int rval;
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"destroy_power_mgmt enter");
mutex_enter(&usbvcp->usbvc_mutex);
pm = usbvcp->usbvc_pm;
if (pm && (usbvcp->usbvc_dev_state != USB_DEV_DISCONNECTED)) {
usbvc_pm_busy_component(usbvcp);
if (pm->usbvc_wakeup_enabled) {
pm->usbvc_raise_power = B_TRUE;
mutex_exit(&usbvcp->usbvc_mutex);
(void) pm_raise_power(usbvcp->usbvc_dip, 0,
USB_DEV_OS_FULL_PWR);
if ((rval = usb_handle_remote_wakeup(
usbvcp->usbvc_dip,
USB_REMOTE_WAKEUP_DISABLE)) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle,
"usbvc_destroy_power_mgmt: "
"Error disabling rmt wakeup: rval = %d",
rval);
}
mutex_enter(&usbvcp->usbvc_mutex);
pm->usbvc_raise_power = B_FALSE;
}
mutex_exit(&usbvcp->usbvc_mutex);
(void) pm_lower_power(usbvcp->usbvc_dip, 0, USB_DEV_OS_PWR_OFF);
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_pm_idle_component(usbvcp);
}
if (pm) {
kmem_free(pm, sizeof (usbvc_power_t));
usbvcp->usbvc_pm = NULL;
}
mutex_exit(&usbvcp->usbvc_mutex);
}
static void
usbvc_pm_busy_component(usbvc_state_t *usbvcp)
{
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pm_busy_component: enter");
usbvcp->usbvc_pm->usbvc_pm_busy++;
mutex_exit(&usbvcp->usbvc_mutex);
if (pm_busy_component(usbvcp->usbvc_dip, 0) !=
DDI_SUCCESS) {
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pm_busy_component: pm busy fail, usbvc_pm_busy=%d",
usbvcp->usbvc_pm->usbvc_pm_busy);
usbvcp->usbvc_pm->usbvc_pm_busy--;
mutex_exit(&usbvcp->usbvc_mutex);
}
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pm_busy_component: exit");
}
static void
usbvc_pm_idle_component(usbvc_state_t *usbvcp)
{
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pm_idle_component: enter");
if (usbvcp->usbvc_pm != NULL) {
mutex_exit(&usbvcp->usbvc_mutex);
if (pm_idle_component(usbvcp->usbvc_dip, 0) ==
DDI_SUCCESS) {
mutex_enter(&usbvcp->usbvc_mutex);
ASSERT(usbvcp->usbvc_pm->usbvc_pm_busy > 0);
usbvcp->usbvc_pm->usbvc_pm_busy--;
mutex_exit(&usbvcp->usbvc_mutex);
}
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pm_idle_component: %d",
usbvcp->usbvc_pm->usbvc_pm_busy);
}
}
static int
usbvc_pwrlvl0(usbvc_state_t *usbvcp)
{
int rval;
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pwrlvl0, dev_state: %x", usbvcp->usbvc_dev_state);
switch (usbvcp->usbvc_dev_state) {
case USB_DEV_ONLINE:
if (usbvcp->usbvc_pm->usbvc_pm_busy != 0) {
USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pwrlvl0: usbvc_pm_busy");
return (USB_FAILURE);
}
rval = usb_set_device_pwrlvl3(usbvcp->usbvc_dip);
ASSERT(rval == USB_SUCCESS);
usbvcp->usbvc_dev_state = USB_DEV_PWRED_DOWN;
usbvcp->usbvc_pm->usbvc_current_power = USB_DEV_OS_PWR_OFF;
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
return (USB_SUCCESS);
case USB_DEV_PWRED_DOWN:
default:
USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pwrlvl0: illegal dev state");
return (USB_FAILURE);
}
}
static int
usbvc_pwrlvl1(usbvc_state_t *usbvcp)
{
int rval;
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pwrlvl1");
rval = usb_set_device_pwrlvl2(usbvcp->usbvc_dip);
ASSERT(rval == USB_SUCCESS);
return (USB_FAILURE);
}
static int
usbvc_pwrlvl2(usbvc_state_t *usbvcp)
{
int rval;
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pwrlvl2");
rval = usb_set_device_pwrlvl1(usbvcp->usbvc_dip);
ASSERT(rval == USB_SUCCESS);
return (USB_FAILURE);
}
static int
usbvc_pwrlvl3(usbvc_state_t *usbvcp)
{
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pwrlvl3, dev_stat=%d", usbvcp->usbvc_dev_state);
switch (usbvcp->usbvc_dev_state) {
case USB_DEV_PWRED_DOWN:
(void) usb_set_device_pwrlvl0(usbvcp->usbvc_dip);
usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
usbvcp->usbvc_pm->usbvc_current_power =
USB_DEV_OS_FULL_PWR;
case USB_DEV_ONLINE:
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
return (USB_SUCCESS);
default:
USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_pwrlvl3: illegal dev state");
return (USB_FAILURE);
}
}
static void
usbvc_cpr_suspend(dev_info_t *dip)
{
int instance = ddi_get_instance(dip);
usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_cpr_suspend enter");
mutex_enter(&usbvcp->usbvc_mutex);
usbvcp->usbvc_dev_state = USB_DEV_SUSPENDED;
mutex_exit(&usbvcp->usbvc_mutex);
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_cpr_suspend: return");
}
static int
usbvc_resume_operation(usbvc_state_t *usbvcp)
{
usbvc_stream_if_t *strm_if;
int rv = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"usbvc_resume_operation: enter");
mutex_enter(&usbvcp->usbvc_mutex);
strm_if = usbvcp->usbvc_curr_strm;
if (!strm_if) {
mutex_exit(&usbvcp->usbvc_mutex);
rv = USB_FAILURE;
return (rv);
}
if (strm_if->stream_on == 0) {
mutex_exit(&usbvcp->usbvc_mutex);
return (rv);
}
if (!strm_if->datain_ph) {
mutex_exit(&usbvcp->usbvc_mutex);
rv = USB_FAILURE;
return (rv);
}
mutex_exit(&usbvcp->usbvc_mutex);
if ((rv = usbvc_vs_set_probe_commit(usbvcp, strm_if,
&strm_if->ctrl_pc, VS_COMMIT_CONTROL)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_IOCTL,
usbvcp->usbvc_log_handle,
"usbvc_resume_operation: set probe failed, rv=%d", rv);
return (rv);
}
mutex_enter(&usbvcp->usbvc_mutex);
if ((rv = usbvc_set_alt(usbvcp, strm_if)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_IOCTL,
usbvcp->usbvc_log_handle,
"usbvc_resume_operation: set alt failed");
mutex_exit(&usbvcp->usbvc_mutex);
return (rv);
}
if (usbvc_start_isoc_polling(usbvcp, strm_if, V4L2_MEMORY_MMAP)
!= USB_SUCCESS) {
rv = USB_FAILURE;
mutex_exit(&usbvcp->usbvc_mutex);
return (rv);
}
strm_if->start_polling = 1;
mutex_exit(&usbvcp->usbvc_mutex);
return (rv);
}
static void
usbvc_cpr_resume(dev_info_t *dip)
{
int instance = ddi_get_instance(dip);
usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"resume: enter");
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_restore_device_state(dip, usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
}
static void
usbvc_restore_device_state(dev_info_t *dip, usbvc_state_t *usbvcp)
{
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_restore_device_state: enter");
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
ASSERT((usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) ||
(usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED));
usbvc_pm_busy_component(usbvcp);
usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
mutex_exit(&usbvcp->usbvc_mutex);
(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
if (usb_check_same_device(dip, usbvcp->usbvc_log_handle,
USB_LOG_L0, PRINT_MASK_ALL,
USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
goto fail;
}
mutex_enter(&usbvcp->usbvc_mutex);
usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
mutex_exit(&usbvcp->usbvc_mutex);
if (usbvcp->usbvc_pm->usbvc_wakeup_enabled) {
if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle,
"device may or may not be accessible. "
"Please verify reconnection");
}
}
if (usbvc_resume_operation(usbvcp) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_restore_device_state: can't resume operation");
goto fail;
}
mutex_enter(&usbvcp->usbvc_mutex);
usbvc_pm_idle_component(usbvcp);
USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
"usbvc_restore_device_state: end");
return;
fail:
mutex_enter(&usbvcp->usbvc_mutex);
usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
usbvc_pm_idle_component(usbvcp);
}
static int
usbvc_disconnect_event_cb(dev_info_t *dip)
{
int instance = ddi_get_instance(dip);
usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
"disconnect: enter");
mutex_enter(&usbvcp->usbvc_mutex);
usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
cv_broadcast(&usbvcp->usbvc_mapio_cv);
} else {
cv_broadcast(&usbvcp->usbvc_read_cv);
}
(void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
return (USB_SUCCESS);
}
static int
usbvc_reconnect_event_cb(dev_info_t *dip)
{
int instance = ddi_get_instance(dip);
usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
"reconnect: enter");
mutex_enter(&usbvcp->usbvc_mutex);
(void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
usbvc_restore_device_state(dip, usbvcp);
usbvc_release_access(usbvcp);
mutex_exit(&usbvcp->usbvc_mutex);
return (USB_SUCCESS);
}
static void
usbvc_init_sync_objs(usbvc_state_t *usbvcp)
{
mutex_init(&usbvcp->usbvc_mutex, NULL, MUTEX_DRIVER,
usbvcp->usbvc_reg->dev_iblock_cookie);
cv_init(&usbvcp->usbvc_serial_cv, NULL, CV_DRIVER, NULL);
cv_init(&usbvcp->usbvc_read_cv, NULL, CV_DRIVER, NULL);
cv_init(&usbvcp->usbvc_mapio_cv, NULL, CV_DRIVER, NULL);
usbvcp->usbvc_serial_inuse = B_FALSE;
usbvcp->usbvc_locks_initialized = B_TRUE;
}
static void
usbvc_fini_sync_objs(usbvc_state_t *usbvcp)
{
cv_destroy(&usbvcp->usbvc_serial_cv);
cv_destroy(&usbvcp->usbvc_read_cv);
cv_destroy(&usbvcp->usbvc_mapio_cv);
mutex_destroy(&usbvcp->usbvc_mutex);
}
static void
usbvc_init_lists(usbvc_state_t *usbvcp)
{
list_create(&(usbvcp->usbvc_term_list), sizeof (usbvc_terms_t),
offsetof(usbvc_terms_t, term_node));
list_create(&(usbvcp->usbvc_unit_list), sizeof (usbvc_units_t),
offsetof(usbvc_units_t, unit_node));
list_create(&(usbvcp->usbvc_stream_list), sizeof (usbvc_stream_if_t),
offsetof(usbvc_stream_if_t, stream_if_node));
}
static void
usbvc_fini_lists(usbvc_state_t *usbvcp)
{
USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_fini_lists: enter");
usbvc_free_ctrl_descr(usbvcp);
usbvc_free_stream_descr(usbvcp);
USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_fini_lists: end");
}
static void
usbvc_free_ctrl_descr(usbvc_state_t *usbvcp)
{
usbvc_terms_t *term;
usbvc_units_t *unit;
USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_free_ctrl_descr: enter");
if (usbvcp->usbvc_vc_header) {
kmem_free(usbvcp->usbvc_vc_header, sizeof (usbvc_vc_header_t));
}
while (!list_is_empty(&usbvcp->usbvc_term_list)) {
term = list_head(&usbvcp->usbvc_term_list);
if (term != NULL) {
list_remove(&(usbvcp->usbvc_term_list), term);
kmem_free(term, sizeof (usbvc_terms_t));
}
}
while (!list_is_empty(&usbvcp->usbvc_unit_list)) {
unit = list_head(&usbvcp->usbvc_unit_list);
if (unit != NULL) {
list_remove(&(usbvcp->usbvc_unit_list), unit);
kmem_free(unit, sizeof (usbvc_units_t));
}
}
}
static void
usbvc_free_stream_descr(usbvc_state_t *usbvcp)
{
usbvc_stream_if_t *strm;
usbvc_input_header_t *in_hdr;
usbvc_output_header_t *out_hdr;
uint8_t fmt_cnt, frm_cnt;
while (!list_is_empty(&usbvcp->usbvc_stream_list)) {
USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_fini_lists: stream list not empty.");
strm = list_head(&usbvcp->usbvc_stream_list);
if (strm != NULL) {
list_remove(&(usbvcp->usbvc_stream_list), strm);
} else {
return;
}
in_hdr = strm->input_header;
out_hdr = strm->output_header;
if (in_hdr) {
fmt_cnt = in_hdr->descr->bNumFormats;
} else if (out_hdr) {
fmt_cnt = out_hdr->descr->bNumFormats;
}
USB_DPRINTF_L3(PRINT_MASK_CLOSE,
usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
" fmtgrp cnt=%d", fmt_cnt);
if (in_hdr) {
kmem_free(in_hdr, sizeof (usbvc_input_header_t));
}
if (out_hdr) {
kmem_free(out_hdr, sizeof (usbvc_output_header_t));
}
if (strm->format_group) {
int i;
usbvc_format_group_t *fmtgrp;
for (i = 0; i < fmt_cnt; i++) {
fmtgrp = &strm->format_group[i];
if (fmtgrp->format == NULL) {
break;
}
if (fmtgrp->still) {
kmem_free(fmtgrp->still,
sizeof (usbvc_still_image_frame_t));
}
frm_cnt = fmtgrp->format->bNumFrameDescriptors;
USB_DPRINTF_L3(PRINT_MASK_CLOSE,
usbvcp->usbvc_log_handle,
"usbvc_fini_lists:"
" frame cnt=%d", frm_cnt);
if (fmtgrp->frames) {
kmem_free(fmtgrp->frames,
sizeof (usbvc_frames_t) * frm_cnt);
}
}
kmem_free(strm->format_group,
sizeof (usbvc_format_group_t) * fmt_cnt);
}
USB_DPRINTF_L3(PRINT_MASK_CLOSE,
usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
" free stream_if_t");
kmem_free(strm, sizeof (usbvc_stream_if_t));
}
}
static int
usbvc_chk_descr_len(uint8_t off_num, uint8_t size, uint8_t off_size,
usb_cvs_data_t *cvs_data)
{
uchar_t *cvs_buf;
uint_t cvs_buf_len;
cvs_buf = cvs_data->cvs_buf;
cvs_buf_len = cvs_data->cvs_buf_len;
if (size == 0) {
if (cvs_buf_len > off_size) {
size = cvs_buf[off_size];
} else {
return (USB_FAILURE);
}
}
if (cvs_buf_len < (off_num + 1)) {
return (USB_FAILURE);
}
if (cvs_buf_len < (cvs_buf[off_num] * size + off_num +1)) {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static int
usbvc_parse_ctrl_if(usbvc_state_t *usbvcp)
{
int if_num;
int cvs_num;
usb_alt_if_data_t *if_alt_data;
usb_cvs_data_t *cvs_data;
uchar_t *cvs_buf;
uint_t cvs_buf_len;
uint16_t version;
if_num = usbvcp->usbvc_reg->dev_curr_if;
if_alt_data = usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num].if_alt;
cvs_data = if_alt_data->altif_cvs;
for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
cvs_buf = cvs_data[cvs_num].cvs_buf;
cvs_buf_len = cvs_data[cvs_num].cvs_buf_len;
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_ctrl_if: cvs_num= %d, cvs_buf_len=%d",
cvs_num, cvs_buf_len);
if (cvs_buf[1] != CS_INTERFACE) {
continue;
}
switch (cvs_buf[2]) {
case VC_HEADER:
if (usbvcp->usbvc_vc_header) {
return (USB_FAILURE);
}
if (usbvc_chk_descr_len(11, 1, 0, cvs_data) !=
USB_SUCCESS) {
return (USB_FAILURE);
}
usbvcp->usbvc_vc_header =
(usbvc_vc_header_t *)kmem_zalloc(
sizeof (usbvc_vc_header_t), KM_SLEEP);
usbvcp->usbvc_vc_header->descr =
(usbvc_vc_header_descr_t *)&cvs_buf[0];
LE_TO_UINT16(usbvcp->usbvc_vc_header->descr->bcdUVC,
0, version);
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
" VC header, bcdUVC=%x", version);
if (usbvcp->usbvc_vc_header->descr->bInCollection ==
0) {
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle,
"usbvc_parse_ctrl_if: no strm interfaces");
break;
}
usbvcp->usbvc_vc_header->baInterfaceNr = &cvs_buf[12];
break;
case VC_INPUT_TERMINAL:
{
usbvc_terms_t *term;
if (cvs_buf_len < USBVC_I_TERM_LEN_MIN) {
break;
}
term = (usbvc_terms_t *)
kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
term->descr = (usbvc_term_descr_t *)cvs_buf;
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
"input term type=%x", term->descr->wTerminalType);
if (term->descr->wTerminalType == ITT_CAMERA) {
if (usbvc_chk_descr_len(14, 1, 0, cvs_data) !=
USB_SUCCESS) {
kmem_free(term, sizeof (usbvc_terms_t));
break;
}
term->bmControls = &cvs_buf[15];
} else if (cvs_buf_len > 8) {
term->bSpecific = &cvs_buf[8];
}
list_insert_tail(&(usbvcp->usbvc_term_list), term);
break;
}
case VC_OUTPUT_TERMINAL:
{
usbvc_terms_t *term;
if (cvs_buf_len < USBVC_O_TERM_LEN_MIN) {
break;
}
term = (usbvc_terms_t *)
kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
term->descr = (usbvc_term_descr_t *)cvs_buf;
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
" output term id= %x", term->descr->bTerminalID);
if (cvs_buf_len > 9) {
term->bSpecific = &cvs_buf[9];
}
list_insert_tail(&(usbvcp->usbvc_term_list), term);
break;
}
case VC_PROCESSING_UNIT:
{
uint8_t sz;
usbvc_units_t *unit;
if (usbvc_chk_descr_len(7, 1, 0, cvs_data) !=
USB_SUCCESS) {
break;
}
sz = cvs_buf[7];
if ((sz + 8) >= cvs_buf_len) {
break;
}
unit = (usbvc_units_t *)
kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
unit->descr = (usbvc_unit_descr_t *)cvs_buf;
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
"unit type=%x", unit->descr->bDescriptorSubType);
if (sz != 0) {
unit->bmControls = &cvs_buf[8];
}
unit->iProcessing = cvs_buf[8 + sz];
if (cvs_buf_len > (9 + sz)) {
unit->bmVideoStandards = cvs_buf[9 + sz];
}
list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
break;
}
case VC_SELECTOR_UNIT:
{
uint8_t pins;
usbvc_units_t *unit;
if (usbvc_chk_descr_len(4, 1, 0, cvs_data) !=
USB_SUCCESS) {
break;
}
pins = cvs_buf[4];
if ((pins + 5) >= cvs_buf_len) {
break;
}
unit = (usbvc_units_t *)
kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
unit->descr = (usbvc_unit_descr_t *)cvs_buf;
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
"unit type=%x", unit->descr->bDescriptorSubType);
if (pins > 0) {
unit->baSourceID = &cvs_buf[5];
}
unit->iSelector = cvs_buf[5 + pins];
list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
break;
}
case VC_EXTENSION_UNIT:
{
uint8_t pins, n;
usbvc_units_t *unit;
if (usbvc_chk_descr_len(21, 1, 0, cvs_data) !=
USB_SUCCESS) {
break;
}
pins = cvs_buf[21];
if ((pins + 22) >= cvs_buf_len) {
break;
}
n = cvs_buf[pins + 22];
if (usbvc_chk_descr_len(pins + 22, 1, 0, cvs_data) !=
USB_SUCCESS) {
break;
}
if ((23 + pins + n) >= cvs_buf_len) {
break;
}
unit = (usbvc_units_t *)
kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
unit->descr = (usbvc_unit_descr_t *)cvs_buf;
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
"unit type=%x", unit->descr->bDescriptorSubType);
if (pins != 0) {
unit->baSourceID = &cvs_buf[22];
}
unit->bControlSize = cvs_buf[22 + pins];
if (unit->bControlSize != 0) {
unit->bmControls = &cvs_buf[23 + pins];
}
unit->iExtension = cvs_buf[23 + pins + n];
list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
break;
}
default:
break;
}
}
if (!usbvcp->usbvc_vc_header) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_ctrl_if: no header descriptor");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
usbvc_stream_if_t *
usbvc_parse_stream_if(usbvc_state_t *usbvcp, int if_num)
{
usb_alt_if_data_t *if_alt_data;
uint_t i, j;
usbvc_stream_if_t *strm_if;
uint16_t pktsize;
uint8_t ep_adr;
strm_if = (usbvc_stream_if_t *)kmem_zalloc(sizeof (usbvc_stream_if_t),
KM_SLEEP);
strm_if->if_descr = &usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num];
if_alt_data = strm_if->if_descr->if_alt;
if (usbvc_parse_stream_header(usbvcp, strm_if) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_stream_if: parse header fail");
kmem_free(strm_if, sizeof (usbvc_stream_if_t));
return (NULL);
}
if (usbvc_parse_format_groups(usbvcp, strm_if) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_stream_if: parse groups fail");
kmem_free(strm_if, sizeof (usbvc_stream_if_t));
return (NULL);
}
for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
if_alt_data = &strm_if->if_descr->if_alt[i];
for (j = 0; j < if_alt_data->altif_n_ep; j++) {
ep_adr =
if_alt_data->altif_ep[j].ep_descr.bEndpointAddress;
if (strm_if->input_header != NULL &&
ep_adr !=
strm_if->input_header->descr->bEndpointAddress) {
continue;
}
if (strm_if->output_header != NULL &&
ep_adr !=
strm_if->output_header->descr->bEndpointAddress) {
continue;
}
pktsize =
if_alt_data->altif_ep[j].ep_descr.wMaxPacketSize;
pktsize = HS_PKT_SIZE(pktsize);
if (pktsize > strm_if->max_isoc_payload) {
strm_if->max_isoc_payload = pktsize;
}
}
}
strm_if->fid = 0xff;
strm_if->buf_read_num = USBVC_DEFAULT_READ_BUF_NUM;
USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_stream_if: return. max_isoc_payload=%x",
strm_if->max_isoc_payload);
return (strm_if);
}
static int
usbvc_parse_stream_ifs(usbvc_state_t *usbvcp)
{
int i, if_cnt, if_num;
usbvc_stream_if_t *strm_if;
if_cnt = usbvcp->usbvc_vc_header->descr->bInCollection;
if (if_cnt == 0) {
ASSERT(list_is_empty(&usbvcp->usbvc_stream_list));
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_stream_ifs: no stream interfaces");
return (USB_SUCCESS);
}
for (i = 0; i < if_cnt; i++) {
if_num = usbvcp->usbvc_vc_header->baInterfaceNr[i];
strm_if = usbvc_parse_stream_if(usbvcp, if_num);
if (strm_if == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle, "usbvc_parse_stream_ifs:"
" parse stream interface %d failed.", if_num);
return (USB_FAILURE);
}
list_create(&(strm_if->buf_map.uv_buf_free),
sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
list_create(&(strm_if->buf_map.uv_buf_done),
sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
list_create(&(strm_if->buf_read.uv_buf_free),
sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
list_create(&(strm_if->buf_read.uv_buf_done),
sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
list_insert_tail(&(usbvcp->usbvc_stream_list), strm_if);
}
usbvcp->usbvc_curr_strm =
(usbvc_stream_if_t *)list_head(&usbvcp->usbvc_stream_list);
return (USB_SUCCESS);
}
static void
usbvc_parse_color_still(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
{
uint8_t frame_cnt;
uint_t last_frame, i;
uchar_t *cvs_buf;
uint_t cvs_buf_len;
frame_cnt = fmtgrp->format->bNumFrameDescriptors;
last_frame = frame_cnt + cvs_num;
for (i = 1; i <= 2; i++) {
if ((last_frame + i) >= altif_n_cvs) {
break;
}
cvs_buf = cvs_data[last_frame + i].cvs_buf;
cvs_buf_len = cvs_data[last_frame + i].cvs_buf_len;
if (cvs_buf[2] == VS_STILL_IMAGE_FRAME) {
uint8_t m, n, off;
usbvc_still_image_frame_t *st;
if (usbvc_chk_descr_len(4, 4, 0, cvs_data) !=
USB_SUCCESS) {
continue;
}
n = cvs_buf[4];
off = 9 + 4 * n -4;
if (off >= cvs_buf_len) {
continue;
}
m = cvs_buf[off];
if (usbvc_chk_descr_len(m, 1, 0, cvs_data) !=
USB_SUCCESS) {
continue;
}
fmtgrp->still = (usbvc_still_image_frame_t *)
kmem_zalloc(sizeof (usbvc_still_image_frame_t),
KM_SLEEP);
st = fmtgrp->still;
st->descr = (usbvc_still_image_frame_descr_t *)cvs_buf;
n = st->descr->bNumImageSizePatterns;
if (n > 0) {
st->width_height =
(width_height_t *)&cvs_buf[5];
}
st->bNumCompressionPattern = cvs_buf[off];
if (cvs_buf[off] > 0) {
st->bCompression = &cvs_buf[off + 1];
}
}
if (cvs_buf[2] == VS_COLORFORMAT) {
fmtgrp->color = (usbvc_color_matching_descr_t *)cvs_buf;
fmtgrp->v4l2_color = usbvc_v4l2_colorspace(
fmtgrp->color->bColorPrimaries);
}
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_color_still: still=%p, color=%p",
(void *)fmtgrp->still, (void *)fmtgrp->color);
}
static void
usbvc_parse_frames(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
{
uint_t last_frame;
usbvc_frames_t *frm;
usb_cvs_data_t *cvs;
uchar_t *cvs_buf;
uint_t cvs_buf_len;
uint8_t i;
uint8_t frame_cnt = fmtgrp->format->bNumFrameDescriptors;
USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_group: frame_cnt=%d", frame_cnt);
if (frame_cnt == 0) {
fmtgrp->frames = NULL;
return;
}
fmtgrp->frames = (usbvc_frames_t *)
kmem_zalloc(sizeof (usbvc_frames_t) * frame_cnt, KM_SLEEP);
last_frame = frame_cnt + cvs_num;
cvs_num++;
i = 0;
for (; cvs_num <= last_frame; cvs_num++) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_frames: cvs_num=%d, i=%d", cvs_num, i);
if (cvs_num >= altif_n_cvs) {
USB_DPRINTF_L3(PRINT_MASK_ATTA,
usbvcp->usbvc_log_handle,
"usbvc_parse_frames: less frames than "
"expected, cvs_num=%d, i=%d", cvs_num, i);
break;
}
cvs = &cvs_data[cvs_num];
cvs_buf = cvs->cvs_buf;
cvs_buf_len = cvs->cvs_buf_len;
if (cvs_buf_len < USBVC_FRAME_LEN_MIN) {
i++;
continue;
}
frm = &fmtgrp->frames[i];
frm->descr = (usbvc_frame_descr_t *)cvs->cvs_buf;
if (frm->descr->bFrameIntervalType > 0) {
if (usbvc_chk_descr_len(25, 4, 0, cvs) != USB_SUCCESS) {
frm->descr = NULL;
i++;
continue;
}
frm->dwFrameInterval = (uint8_t *)&cvs_buf[26];
} else {
if (cvs_buf_len < USBVC_FRAME_LEN_CON) {
frm->descr = NULL;
i++;
continue;
}
LE_TO_UINT32(cvs_buf, 26, frm->dwMinFrameInterval);
LE_TO_UINT32(cvs_buf, 30, frm->dwMaxFrameInterval);
LE_TO_UINT32(cvs_buf, 34, frm->dwFrameIntervalStep);
}
i++;
}
fmtgrp->frame_cnt = i;
USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_frames: %d frames are actually parsed",
fmtgrp->frame_cnt);
}
static int
usbvc_parse_format_group(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
{
usbvc_format_descr_t *fmt;
fmt = fmtgrp->format;
USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_group: frame_cnt=%d, cvs_num=%d",
fmt->bNumFrameDescriptors, cvs_num);
switch (fmt->bDescriptorSubType) {
case VS_FORMAT_UNCOMPRESSED:
usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
altif_n_cvs);
usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
altif_n_cvs);
fmtgrp->v4l2_bpp = fmt->fmt.uncompressed.bBitsPerPixel / 8;
fmtgrp->v4l2_pixelformat = usbvc_v4l2_guid2fcc(
(uint8_t *)&fmt->fmt.uncompressed.guidFormat);
break;
case VS_FORMAT_MJPEG:
usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
altif_n_cvs);
usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
altif_n_cvs);
fmtgrp->v4l2_bpp = 0;
fmtgrp->v4l2_pixelformat = V4L2_PIX_FMT_MJPEG;
break;
case VS_FORMAT_MPEG2TS:
case VS_FORMAT_DV:
case VS_FORMAT_FRAME_BASED:
case VS_FORMAT_STREAM_BASED:
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_group: format not supported yet.");
return (USB_FAILURE);
default:
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_group: unknown format.");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static int
usbvc_parse_format_groups(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
usb_alt_if_data_t *if_alt_data;
usb_cvs_data_t *cvs_data;
uint8_t fmtgrp_num, fmtgrp_cnt;
uchar_t *cvs_buf;
uint_t cvs_num = 0;
usbvc_format_group_t *fmtgrp;
fmtgrp_cnt = 0;
if (strm_if->input_header) {
fmtgrp_cnt = strm_if->input_header->descr->bNumFormats;
} else if (strm_if->output_header) {
fmtgrp_cnt = strm_if->output_header->descr->bNumFormats;
}
if (!fmtgrp_cnt) {
return (USB_FAILURE);
}
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_groups: fmtgrp_cnt=%d", fmtgrp_cnt);
fmtgrp = (usbvc_format_group_t *)
kmem_zalloc(sizeof (usbvc_format_group_t) * fmtgrp_cnt, KM_SLEEP);
if_alt_data = strm_if->if_descr->if_alt;
cvs_data = if_alt_data->altif_cvs;
for (fmtgrp_num = 0; fmtgrp_num < fmtgrp_cnt &&
cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
cvs_buf = cvs_data[cvs_num].cvs_buf;
switch (cvs_buf[2]) {
case VS_FORMAT_UNCOMPRESSED:
case VS_FORMAT_MJPEG:
case VS_FORMAT_MPEG2TS:
case VS_FORMAT_DV:
case VS_FORMAT_FRAME_BASED:
case VS_FORMAT_STREAM_BASED:
fmtgrp[fmtgrp_num].format =
(usbvc_format_descr_t *)cvs_buf;
(void) usbvc_parse_format_group(usbvcp,
&fmtgrp[fmtgrp_num], cvs_data, cvs_num,
if_alt_data->altif_n_cvs);
fmtgrp_num++;
break;
default:
break;
}
}
strm_if->fmtgrp_cnt = fmtgrp_num;
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_groups: acctually %d formats parsed",
fmtgrp_num);
if (!(fmtgrp[0].format)) {
kmem_free(fmtgrp, sizeof (usbvc_format_group_t) * fmtgrp_cnt);
strm_if->format_group = NULL;
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_groups: can't find any formats");
return (USB_FAILURE);
}
strm_if->format_group = fmtgrp;
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_format_groups: %d format groups parsed", fmtgrp_num);
return (USB_SUCCESS);
}
int
usbvc_parse_stream_header(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
usb_alt_if_data_t *if_alt_data;
usb_cvs_data_t *cvs_data;
int cvs_num;
uchar_t *cvs_buf;
usbvc_input_header_t *in_hdr;
usbvc_output_header_t *out_hdr;
if_alt_data = strm_if->if_descr->if_alt;
cvs_data = if_alt_data->altif_cvs;
for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
cvs_buf = cvs_data[cvs_num].cvs_buf;
USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_stream_header: cvs_num= %d", cvs_num);
if (cvs_buf[1] != CS_INTERFACE) {
continue;
}
if (cvs_buf[2] == VS_INPUT_HEADER) {
if (usbvc_chk_descr_len(3, 0, 12, cvs_data) !=
USB_SUCCESS) {
continue;
}
strm_if->input_header =
(usbvc_input_header_t *)
kmem_zalloc(sizeof (usbvc_input_header_t),
KM_SLEEP);
in_hdr = strm_if->input_header;
in_hdr->descr = (usbvc_input_header_descr_t *)cvs_buf;
if (in_hdr->descr->bNumFormats > 0) {
in_hdr->bmaControls = &cvs_buf[13];
}
return (USB_SUCCESS);
} else if (cvs_buf[2] == VS_OUTPUT_HEADER) {
if (usbvc_chk_descr_len(3, 0, 8, cvs_data) !=
USB_SUCCESS) {
continue;
}
strm_if->output_header =
(usbvc_output_header_t *)
kmem_zalloc(sizeof (usbvc_output_header_t),
KM_SLEEP);
out_hdr = strm_if->output_header;
out_hdr->descr =
(usbvc_output_header_descr_t *)cvs_buf;
if (out_hdr->descr->bNumFormats > 0) {
out_hdr->bmaControls = &cvs_buf[13];
}
return (USB_SUCCESS);
} else {
continue;
}
}
USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
"usbvc_parse_stream_header: FAIL");
return (USB_FAILURE);
}
static int
usbvc_alloc_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
usbvc_buf_t *buf;
uchar_t *data;
int i;
uint32_t len;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
if (!len) {
return (USB_FAILURE);
}
for (i = 0; i < strm_if->buf_read_num; i++) {
mutex_exit(&usbvcp->usbvc_mutex);
buf = (usbvc_buf_t *)kmem_zalloc(sizeof (usbvc_buf_t),
KM_SLEEP);
data = (uchar_t *)kmem_zalloc(len, KM_SLEEP);
mutex_enter(&usbvcp->usbvc_mutex);
buf->data = data;
buf->len = len;
list_insert_tail(&(strm_if->buf_read.uv_buf_free), buf);
}
strm_if->buf_read.buf_cnt = strm_if->buf_read_num;
USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
"read_bufs: %d bufs allocated", strm_if->buf_read.buf_cnt);
return (USB_SUCCESS);
}
static int
usbvc_read_buf(usbvc_state_t *usbvcp, struct buf *bp)
{
usbvc_buf_t *buf;
int buf_residue;
int len_to_copy;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
if (list_is_empty(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done)) {
USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_read_buf: empty list(uv_buf_done)!");
return (USB_FAILURE);
}
buf = list_head(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done);
USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_read_buf: buf=%p, buf->filled=%d, buf->len=%d,"
" buf->len_read=%d bp->b_bcount=%ld, bp->b_resid=%lu",
(void *)buf, buf->filled, buf->len, buf->len_read,
bp->b_bcount, bp->b_resid);
ASSERT(buf->len_read <= buf->filled);
buf_residue = buf->filled - buf->len_read;
len_to_copy = min(bp->b_bcount, buf_residue);
bcopy(buf->data + buf->len_read, bp->b_un.b_addr, len_to_copy);
bp->b_private = NULL;
buf->len_read += len_to_copy;
bp->b_resid = bp->b_bcount - len_to_copy;
if (len_to_copy == buf_residue) {
buf->len_read = 0;
list_remove(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done,
buf);
list_insert_tail(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_free,
buf);
}
return (USB_SUCCESS);
}
static void
usbvc_free_read_buf(usbvc_buf_t *buf)
{
if (buf != NULL) {
if (buf->data) {
kmem_free(buf->data, buf->len);
}
kmem_free(buf, sizeof (usbvc_buf_t));
}
}
static void
usbvc_free_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
usbvc_buf_t *buf;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
if (!strm_if) {
return;
}
buf = strm_if->buf_read.buf_filling;
usbvc_free_read_buf(buf);
strm_if->buf_read.buf_filling = NULL;
while (!list_is_empty(&strm_if->buf_read.uv_buf_free)) {
buf = list_head(&strm_if->buf_read.uv_buf_free);
if (buf != NULL) {
list_remove(&(strm_if->buf_read.uv_buf_free), buf);
usbvc_free_read_buf(buf);
}
}
while (!list_is_empty(&strm_if->buf_read.uv_buf_done)) {
buf = list_head(&strm_if->buf_read.uv_buf_done);
if (buf != NULL) {
list_remove(&(strm_if->buf_read.uv_buf_done), buf);
usbvc_free_read_buf(buf);
}
}
strm_if->buf_read.buf_cnt = 0;
USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_free_read_bufs: return");
}
int
usbvc_alloc_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
int buf_cnt, int buf_len)
{
int i = 0;
usbvc_buf_t *bufs;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_alloc_map_bufs: bufcnt=%d, buflen=%d", buf_cnt, buf_len);
if (buf_len <= 0 || buf_cnt <= 0) {
USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_alloc_map_bufs: len<=0, cnt<=0");
return (0);
}
mutex_exit(&usbvcp->usbvc_mutex);
bufs = (usbvc_buf_t *) kmem_zalloc(sizeof (usbvc_buf_t) * buf_cnt,
KM_SLEEP);
mutex_enter(&usbvcp->usbvc_mutex);
strm_if->buf_map.buf_head = bufs;
buf_len = ptob(btopr(buf_len));
mutex_exit(&usbvcp->usbvc_mutex);
bufs[0].data = ddi_umem_alloc(buf_len * buf_cnt, DDI_UMEM_SLEEP,
&bufs[0].umem_cookie);
mutex_enter(&usbvcp->usbvc_mutex);
for (i = 0; i < buf_cnt; i++) {
bufs[i].len = buf_len;
bufs[i].data = bufs[0].data + (buf_len * i);
bufs[i].umem_cookie = bufs[0].umem_cookie;
bufs[i].status = USBVC_BUF_INIT;
bufs[i].v4l2_buf.index = i;
bufs[i].v4l2_buf.m.offset = i * bufs[i].len;
bufs[i].v4l2_buf.length = bufs[i].len;
bufs[i].v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufs[i].v4l2_buf.sequence = 0;
bufs[i].v4l2_buf.field = V4L2_FIELD_NONE;
bufs[i].v4l2_buf.memory = V4L2_MEMORY_MMAP;
bufs[i].v4l2_buf.flags = V4L2_MEMORY_MMAP;
list_insert_tail(&strm_if->buf_map.uv_buf_free, &bufs[i]);
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_alloc_map_bufs: prepare %d buffers of %d bytes",
buf_cnt, bufs[i].len);
}
strm_if->buf_map.buf_cnt = buf_cnt;
strm_if->buf_map.buf_filling = NULL;
return (buf_cnt);
}
void
usbvc_free_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
usbvc_buf_t *buf;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
if (!strm_if) {
return;
}
strm_if->buf_map.buf_filling = NULL;
while (!list_is_empty(&strm_if->buf_map.uv_buf_free)) {
buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_free);
list_remove(&(strm_if->buf_map.uv_buf_free), buf);
}
while (!list_is_empty(&strm_if->buf_map.uv_buf_done)) {
buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_done);
list_remove(&(strm_if->buf_map.uv_buf_done), buf);
}
buf = strm_if->buf_map.buf_head;
if (!buf) {
USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_free_map_bufs: no data buf need be freed, return");
return;
}
if (buf->umem_cookie) {
ddi_umem_free(buf->umem_cookie);
}
kmem_free(buf, sizeof (usbvc_buf_t) * strm_if->buf_map.buf_cnt);
strm_if->buf_map.buf_cnt = 0;
strm_if->buf_map.buf_head = NULL;
USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_free_map_bufs: return");
}
int
usbvc_open_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
usb_pipe_policy_t policy;
int rval = USB_SUCCESS;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
if ((rval = usbvc_set_alt(usbvcp, strm_if)) != USB_SUCCESS) {
return (rval);
}
bzero(&policy, sizeof (usb_pipe_policy_t));
policy.pp_max_async_reqs = 2;
mutex_exit(&usbvcp->usbvc_mutex);
if ((rval = usb_pipe_open(usbvcp->usbvc_dip, strm_if->curr_ep, &policy,
USB_FLAGS_SLEEP, &strm_if->datain_ph)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_open_isoc_pipe: open pipe fail");
mutex_enter(&usbvcp->usbvc_mutex);
return (rval);
}
mutex_enter(&usbvcp->usbvc_mutex);
strm_if->start_polling = 0;
strm_if->stream_on = 0;
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_open_isoc_pipe: success, datain_ph=%p",
(void *)strm_if->datain_ph);
return (rval);
}
static void
usbvc_close_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
if (!strm_if) {
USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
"usbvc_close_isoc_pipe: stream interface is NULL");
return;
}
if (strm_if->datain_ph) {
mutex_exit(&usbvcp->usbvc_mutex);
usb_pipe_close(usbvcp->usbvc_dip, strm_if->datain_ph,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&usbvcp->usbvc_mutex);
}
strm_if->datain_ph = NULL;
}
int
usbvc_start_isoc_polling(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
uchar_t io_type)
{
int rval = USB_SUCCESS;
uint_t if_num;
usb_isoc_req_t *req;
ushort_t pkt_size;
ushort_t n_pkt, pkt;
uint32_t frame_size;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
pkt_size = HS_PKT_SIZE(strm_if->curr_ep->wMaxPacketSize);
if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, frame_size);
n_pkt = (frame_size + (pkt_size) - 1) / (pkt_size);
USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"usbvc_start_isoc_polling: if_num=%d, alt=%d, n_pkt=%d,"
" pkt_size=0x%x, MaxPacketSize=0x%x(Tsac#=%d), frame_size=0x%x",
if_num, strm_if->curr_alt, n_pkt, pkt_size,
strm_if->curr_ep->wMaxPacketSize,
(1 + ((strm_if->curr_ep->wMaxPacketSize>> 11) & 3)),
frame_size);
if (n_pkt > USBVC_MAX_PKTS) {
n_pkt = USBVC_MAX_PKTS;
}
USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"usbvc_start_isoc_polling: n_pkt=%d", n_pkt);
mutex_exit(&usbvcp->usbvc_mutex);
if ((req = usb_alloc_isoc_req(usbvcp->usbvc_dip, n_pkt,
n_pkt * pkt_size, USB_FLAGS_SLEEP)) != NULL) {
mutex_enter(&usbvcp->usbvc_mutex);
for (pkt = 0; pkt < n_pkt; pkt++) {
req->isoc_pkt_descr[pkt].isoc_pkt_length = pkt_size;
}
req->isoc_pkts_count = n_pkt;
req->isoc_pkts_length = 0;
req->isoc_attributes = USB_ATTRS_ISOC_XFER_ASAP |
USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
req->isoc_cb = usbvc_isoc_cb;
req->isoc_exc_cb = usbvc_isoc_exc_cb;
usbvcp->usbvc_io_type = io_type;
req->isoc_client_private = (usb_opaque_t)usbvcp;
mutex_exit(&usbvcp->usbvc_mutex);
rval = usb_pipe_isoc_xfer(strm_if->datain_ph, req, 0);
mutex_enter(&usbvcp->usbvc_mutex);
} else {
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"usbvc_start_isoc_polling: alloc_isoc_req fail");
return (USB_FAILURE);
}
if (rval != USB_SUCCESS) {
if (req) {
usb_free_isoc_req(req);
req = NULL;
}
}
USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
"usbvc_start_isoc_polling: return, rval=%d", rval);
return (rval);
}
static void
usbvc_isoc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
{
usbvc_state_t *usbvcp =
(usbvc_state_t *)isoc_req->isoc_client_private;
int i;
mblk_t *data = isoc_req->isoc_data;
usbvc_buf_grp_t *bufgrp;
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_isoc_cb: rq=0x%p, fno=%" PRId64 ", n_pkts=%u, flag=0x%x,"
" data=0x%p, cnt=%d",
(void *)isoc_req, isoc_req->isoc_frame_no,
isoc_req->isoc_pkts_count, isoc_req->isoc_attributes,
(void *)isoc_req->isoc_data, isoc_req->isoc_error_count);
ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) != 0);
for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"\tpkt%d: "
"pktsize=%d status=%d resid=%d",
i,
isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
isoc_req->isoc_pkt_descr[i].isoc_pkt_status,
isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length);
if (isoc_req->isoc_pkt_descr[i].isoc_pkt_status !=
USB_CR_OK) {
USB_DPRINTF_L3(PRINT_MASK_CB,
usbvcp->usbvc_log_handle,
"record: pkt=%d status=%s", i, usb_str_cr(
isoc_req->isoc_pkt_descr[i].isoc_pkt_status));
}
if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
bufgrp = &usbvcp->usbvc_curr_strm->buf_map;
} else {
bufgrp = &usbvcp->usbvc_curr_strm->buf_read;
}
if (isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length) {
if (usbvc_decode_stream_header(usbvcp, bufgrp, data,
isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length)
!= USB_SUCCESS) {
USB_DPRINTF_L3(PRINT_MASK_CB,
usbvcp->usbvc_log_handle, "decode error");
}
if (bufgrp->buf_filling &&
(bufgrp->buf_filling->status == USBVC_BUF_ERR ||
bufgrp->buf_filling->status == USBVC_BUF_DONE)) {
list_insert_tail(&bufgrp->uv_buf_done,
bufgrp->buf_filling);
bufgrp->buf_filling = NULL;
if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
cv_broadcast(&usbvcp->usbvc_mapio_cv);
} else {
cv_broadcast(&usbvcp->usbvc_read_cv);
}
}
}
data->b_rptr += isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
}
mutex_exit(&usbvcp->usbvc_mutex);
usb_free_isoc_req(isoc_req);
}
static void
usbvc_isoc_exc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
{
usbvc_state_t *usbvcp =
(usbvc_state_t *)isoc_req->isoc_client_private;
usb_cr_t completion_reason;
int rval;
usbvc_stream_if_t *strm_if;
ASSERT(!list_is_empty(&usbvcp->usbvc_stream_list));
mutex_enter(&usbvcp->usbvc_mutex);
strm_if = usbvcp->usbvc_curr_strm;
completion_reason = isoc_req->isoc_completion_reason;
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_isoc_exc_cb: ph=0x%p, isoc_req=0x%p, cr=%d",
(void *)ph, (void *)isoc_req, completion_reason);
ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) == 0);
switch (completion_reason) {
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_CLOSING:
case USB_CR_PIPE_RESET:
break;
case USB_CR_NO_RESOURCES:
rval = usb_pipe_isoc_xfer(strm_if->datain_ph, isoc_req,
USB_FLAGS_NOSLEEP);
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_isoc_exc_cb: restart capture rval=%d", rval);
mutex_exit(&usbvcp->usbvc_mutex);
return;
default:
mutex_exit(&usbvcp->usbvc_mutex);
usb_pipe_stop_isoc_polling(ph, USB_FLAGS_NOSLEEP);
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_isoc_exc_cb: stop polling");
mutex_enter(&usbvcp->usbvc_mutex);
}
usb_free_isoc_req(isoc_req);
strm_if->start_polling = 0;
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_isoc_exc_cb: start_polling=%d cr=%d",
strm_if->start_polling, completion_reason);
mutex_exit(&usbvcp->usbvc_mutex);
}
static int
usbvc_set_alt(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
{
usb_alt_if_data_t *alt;
uint_t i, j, if_num;
uint16_t pktsize, curr_pktsize;
uint32_t bandwidth;
int rval = USB_SUCCESS;
usbvc_input_header_t *ihd;
usbvc_output_header_t *ohd;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
if (!bandwidth) {
USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_set_alt: bandwidth is not set yet");
return (USB_FAILURE);
}
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_set_alt: bandwidth=%x", bandwidth);
strm_if->curr_ep = NULL;
curr_pktsize = 0xffff;
ohd = strm_if->output_header;
ihd = strm_if->input_header;
for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
alt = &strm_if->if_descr->if_alt[i];
for (j = 0; j < alt->altif_n_ep; j++) {
if (ihd != NULL &&
alt->altif_ep[j].ep_descr.bEndpointAddress !=
ihd->descr->bEndpointAddress) {
continue;
}
if (ohd != NULL &&
alt->altif_ep[j].ep_descr.bEndpointAddress !=
ohd->descr->bEndpointAddress) {
continue;
}
pktsize =
alt->altif_ep[j].ep_descr.wMaxPacketSize;
pktsize = HS_PKT_SIZE(pktsize);
if (pktsize >= bandwidth && pktsize < curr_pktsize) {
curr_pktsize = pktsize;
strm_if->curr_alt = i;
strm_if->curr_ep = &alt->altif_ep[j].ep_descr;
}
}
}
if (!strm_if->curr_ep) {
USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_set_alt: can't find a proper ep to satisfy"
" the given bandwidth");
return (USB_FAILURE);
}
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_set_alt: strm_if->curr_alt=%d", strm_if->curr_alt);
if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
mutex_exit(&usbvcp->usbvc_mutex);
if ((rval = usb_set_alt_if(usbvcp->usbvc_dip, if_num, strm_if->curr_alt,
USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_set_alt: usb_set_alt_if fail, if.alt=%d.%d, rval=%d",
if_num, strm_if->curr_alt, rval);
return (rval);
}
mutex_enter(&usbvcp->usbvc_mutex);
USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
"usbvc_set_alt: return, if_num=%d, alt=%d",
if_num, strm_if->curr_alt);
return (rval);
}
static int
usbvc_decode_stream_header(usbvc_state_t *usbvcp, usbvc_buf_grp_t *bufgrp,
mblk_t *data, int actual_len)
{
uint32_t len, buf_left, data_len;
usbvc_stream_if_t *strm_if;
uchar_t head_flag, head_len;
usbvc_buf_t *buf_filling;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: enter. actual_len=%x", actual_len);
if (actual_len < 2) {
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: header is not completed");
return (USB_FAILURE);
}
head_len = data->b_rptr[0];
head_flag = data->b_rptr[1];
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: headlen=%x", head_len);
if (actual_len < head_len) {
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: actual_len < head_len");
return (USB_FAILURE);
}
if ((actual_len == head_len) && !(head_flag & USBVC_STREAM_EOF)) {
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: only header, no data");
return (USB_FAILURE);
}
strm_if = usbvcp->usbvc_curr_strm;
LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: dwMaxVideoFrameSize=%x, head_flag=%x",
len, head_flag);
if (!bufgrp->buf_filling) {
if (list_is_empty(&bufgrp->uv_buf_free)) {
strm_if->fid = head_flag & USBVC_STREAM_FID;
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: free list are empty");
return (USB_FAILURE);
} else {
bufgrp->buf_filling =
(usbvc_buf_t *)list_head(&bufgrp->uv_buf_free);
list_remove(&bufgrp->uv_buf_free, bufgrp->buf_filling);
}
bufgrp->buf_filling->filled = 0;
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: status=%d",
bufgrp->buf_filling->status);
bufgrp->buf_filling->status = USBVC_BUF_EMPTY;
}
buf_filling = bufgrp->buf_filling;
ASSERT(buf_filling->len >= buf_filling->filled);
buf_left = buf_filling->len - buf_filling->filled;
if (buf_left == 0) {
if ((head_flag & USBVC_STREAM_EOF) &&
(actual_len == head_len)) {
buf_filling->status = USBVC_BUF_DONE;
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: got a EOF packet");
return (USB_SUCCESS);
}
buf_filling->status = USBVC_BUF_ERR;
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: frame buf full");
return (USB_FAILURE);
}
data_len = actual_len - head_len;
USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: fid=%x, len=%x, filled=%x",
strm_if->fid, buf_filling->len, buf_filling->filled);
if (buf_filling->filled == 0) {
if (strm_if->fid != 0xff && strm_if->fid ==
(head_flag & USBVC_STREAM_FID)) {
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: 1st sample of a frame,"
" fid is incorrect.");
return (USB_FAILURE);
}
strm_if->fid = head_flag & USBVC_STREAM_FID;
} else if (strm_if->fid != (head_flag & USBVC_STREAM_FID)) {
USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: fid is incorrect.");
strm_if->fid = head_flag & USBVC_STREAM_FID;
buf_filling->status = USBVC_BUF_ERR;
return (USB_FAILURE);
}
if (data_len) {
bcopy((void *)(data->b_rptr + head_len),
(void *)(buf_filling->data + buf_filling->filled),
min(data_len, buf_left));
buf_filling->filled += min(data_len, buf_left);
}
if (head_flag & USBVC_STREAM_EOF) {
buf_filling->status = USBVC_BUF_DONE;
}
if (data_len > buf_left) {
buf_filling->status = USBVC_BUF_ERR;
}
USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
"usbvc_decode_stream_header: buf_status=%d", buf_filling->status);
return (USB_SUCCESS);
}
static int
usbvc_serialize_access(usbvc_state_t *usbvcp, boolean_t waitsig)
{
int rval = 1;
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
while (usbvcp->usbvc_serial_inuse) {
if (waitsig == USBVC_SER_SIG) {
rval = cv_wait_sig(&usbvcp->usbvc_serial_cv,
&usbvcp->usbvc_mutex);
} else {
cv_wait(&usbvcp->usbvc_serial_cv,
&usbvcp->usbvc_mutex);
}
}
usbvcp->usbvc_serial_inuse = B_TRUE;
return (rval);
}
static void
usbvc_release_access(usbvc_state_t *usbvcp)
{
ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
usbvcp->usbvc_serial_inuse = B_FALSE;
cv_broadcast(&usbvcp->usbvc_serial_cv);
}
int
usbvc_vc_get_ctrl(usbvc_state_t *usbvcp, uint8_t req_code, uint8_t entity_id,
uint16_t cs, uint16_t wlength, mblk_t *data)
{
usb_cb_flags_t cb_flags;
usb_cr_t cr;
usb_ctrl_setup_t setup;
setup.bmRequestType = USBVC_GET_IF;
setup.bRequest = req_code;
setup.wValue = cs<<8;
setup.wIndex = entity_id<<8;
setup.wLength = wlength;
setup.attrs = 0;
if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
&cr, &cb_flags, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_vc_get_ctrl: cmd failed, cr=%d, cb_flags=%x",
cr, cb_flags);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
int
usbvc_vc_set_ctrl(usbvc_state_t *usbvcp, uint8_t req_code, uint8_t entity_id,
uint16_t cs, uint16_t wlength, mblk_t *data)
{
usb_cb_flags_t cb_flags;
usb_cr_t cr;
usb_ctrl_setup_t setup;
setup.bmRequestType = USBVC_SET_IF;
setup.bRequest = req_code;
setup.wValue = cs<<8;
setup.wIndex = entity_id<<8;
setup.wLength = wlength;
setup.attrs = 0;
if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
&cr, &cb_flags, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_vc_set_ctrl: cmd failed, cr=%d, cb_flags=%x",
cr, cb_flags);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
int
usbvc_vs_set_probe_commit(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
usbvc_vs_probe_commit_t *ctrl_pc, uchar_t cs)
{
mblk_t *data;
usb_cb_flags_t cb_flags;
usb_cr_t cr;
usb_ctrl_setup_t setup;
int rval;
setup.bmRequestType = USBVC_SET_IF;
setup.bRequest = SET_CUR;
setup.wValue = cs;
setup.wValue = setup.wValue << 8;
setup.wIndex = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
setup.attrs = 0;
USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_vs_set_probe_commit: wLength=%d", setup.wLength);
if ((data = allocb(setup.wLength, BPRI_HI)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_vs_set_probe_commit: allocb failed");
return (USB_FAILURE);
}
bcopy(ctrl_pc, data->b_rptr, setup.wLength);
data->b_wptr += setup.wLength;
if ((rval = usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup,
&data, &cr, &cb_flags, 0)) != USB_SUCCESS) {
if (data) {
freemsg(data);
}
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_vs_set_probe_commit: fail, rval=%d, cr=%d, "
"cb_flags=%x", rval, cr, cb_flags);
return (rval);
}
if (data) {
freemsg(data);
}
return (USB_SUCCESS);
}
int
usbvc_vs_get_probe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
usbvc_vs_probe_commit_t *ctrl_pc, uchar_t bRequest)
{
mblk_t *data = NULL;
usb_cb_flags_t cb_flags;
usb_cr_t cr;
usb_ctrl_setup_t setup;
setup.bmRequestType = USBVC_GET_IF;
setup.bRequest = bRequest;
setup.wValue = VS_PROBE_CONTROL;
setup.wValue = setup.wValue << 8;
setup.wIndex =
(uint16_t)strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
setup.attrs = 0;
if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
&cr, &cb_flags, 0) != USB_SUCCESS) {
if (data) {
freemsg(data);
}
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_vs_get_probe: cmd failed, cr=%d, cb_flags=%x",
cr, cb_flags);
return (USB_FAILURE);
}
bcopy(data->b_rptr, ctrl_pc, setup.wLength);
if (data) {
freemsg(data);
}
return (USB_SUCCESS);
}
static int
usbvc_set_default_stream_fmt(usbvc_state_t *usbvcp)
{
usbvc_vs_probe_commit_t ctrl, ctrl_get;
usbvc_stream_if_t *strm_if;
usbvc_format_group_t *curr_fmtgrp;
uint32_t bandwidth;
uint8_t index, i;
USB_DPRINTF_L4(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_set_default_stream_fmt: enter");
mutex_enter(&usbvcp->usbvc_mutex);
if (list_is_empty(&usbvcp->usbvc_stream_list)) {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_set_default_stream_fmt: no stream interface, fail");
mutex_exit(&usbvcp->usbvc_mutex);
return (USB_FAILURE);
}
bzero((void *)&ctrl, sizeof (usbvc_vs_probe_commit_t));
strm_if = usbvcp->usbvc_curr_strm;
ctrl.bmHint[0] = 0;
for (i = 0; i < strm_if->fmtgrp_cnt; i++) {
curr_fmtgrp = &strm_if->format_group[i];
if (!curr_fmtgrp || !curr_fmtgrp->v4l2_pixelformat ||
curr_fmtgrp->frame_cnt == 0) {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL,
usbvcp->usbvc_log_handle,
"usbvc_set_default_stream_fmt: no frame, fail");
continue;
} else {
break;
}
}
if (!curr_fmtgrp || curr_fmtgrp->frame_cnt == 0) {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_set_default_stream_fmt: can't find a fmtgrp"
"which has a frame, fail");
mutex_exit(&usbvcp->usbvc_mutex);
return (USB_FAILURE);
}
ctrl.bFormatIndex = curr_fmtgrp->format->bFormatIndex;
ctrl.bFrameIndex = curr_fmtgrp->frames[0].descr->bFrameIndex;
bcopy(&(curr_fmtgrp->frames[0].descr->dwDefaultFrameInterval[0]),
&(ctrl.dwFrameInterval[0]), 4);
mutex_exit(&usbvcp->usbvc_mutex);
if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl, VS_PROBE_CONTROL)
!= USB_SUCCESS) {
return (USB_FAILURE);
}
if (usbvc_vs_get_probe(usbvcp, strm_if, &ctrl_get, GET_CUR)
!= USB_SUCCESS) {
return (USB_FAILURE);
}
mutex_enter(&usbvcp->usbvc_mutex);
LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_set_default_stream_fmt: get bandwidth=%x", bandwidth);
mutex_exit(&usbvcp->usbvc_mutex);
if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl_get,
VS_COMMIT_CONTROL) != USB_SUCCESS) {
return (USB_FAILURE);
}
mutex_enter(&usbvcp->usbvc_mutex);
index = ctrl_get.bFormatIndex - curr_fmtgrp->format->bFormatIndex;
if (index < strm_if->fmtgrp_cnt) {
strm_if->cur_format_group = &strm_if->format_group[index];
} else {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_set_default_stream_fmt: format index out of range");
mutex_exit(&usbvcp->usbvc_mutex);
return (USB_FAILURE);
}
index = ctrl_get.bFrameIndex -
strm_if->cur_format_group->frames[0].descr->bFrameIndex;
if (index < strm_if->cur_format_group->frame_cnt) {
strm_if->cur_format_group->cur_frame =
&strm_if->cur_format_group->frames[index];
} else {
USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
"usbvc_set_default_stream: frame index out of range");
mutex_exit(&usbvcp->usbvc_mutex);
return (USB_FAILURE);
}
bcopy(&ctrl_get, &strm_if->ctrl_pc, sizeof (usbvc_vs_probe_commit_t));
mutex_exit(&usbvcp->usbvc_mutex);
return (USB_SUCCESS);
}