/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* USB video class driver: V4L2 interface implementation.
*/
#include <sys/videodev2.h>
struct v4l2_buffer *);
struct v4l2_buffer *);
static int usbvc_v4l2_dequeue_buffer(usbvc_state_t *,
struct v4l2_buffer *, int);
/* Video controls that supported by usbvc driver */
{
"Brightness",
2,
0,
},
{
"Contrast",
2,
1,
},
{
"Saturation",
2,
3,
},
{
"Hue",
2,
2,
},
{
"Gamma",
2,
5,
}
};
/*
* V4L2 colorspaces.
*/
0,
};
/* V4L2 ioctls */
int
{
int rv = 0;
switch (cmd) {
case VIDIOC_QUERYCAP: /* Query capabilities */
{
"V4L2 ioctl: VIDIOC_QUERYCAP");
} else {
}
break;
}
case VIDIOC_ENUM_FMT:
{
"V4L2 ioctl: VIDIOC_ENUM_FMT");
break;
}
"V4L2 ioctl: VIDIOC_ENUM_FMT, idx=%d, grpcnt=%d",
case VS_FORMAT_MJPEG:
sizeof (fmtdesc.description));
break;
case VS_FORMAT_UNCOMPRESSED:
sizeof (fmtdesc.description));
sizeof (fmtdesc.description));
} else {
"Unknown format",
sizeof (fmtdesc.description));
}
break;
default:
sizeof (fmtdesc.description));
}
break;
}
case VIDIOC_S_FMT:
{
"V4L2 ioctl: VIDIOC_S_FMT");
/* If data I/O is in progress */
break;
}
"V4L2 ioctl VIDIOC_S_FMT fail");
}
break;
}
case VIDIOC_G_FMT:
{
"V4L2 ioctl: VIDIOC_G_FMT");
break;
}
break;
}
case VIDIOC_REQBUFS: /* for memory mapping IO method */
{
"V4L2 ioctl: VIDIOC_REQBUFS");
break;
}
if (!strm_if) {
break;
}
"V4L2 ioctl: req too many buffers, fail");
break;
}
/* If some bufs were already allocated */
/*
* According to v4l2 spec, application can change the
* buffer number and also free all buffers if set
* count to 0
*/
strm_if->start_polling = 0;
}
break;
}
"v4l2 ioctls: req the same buffers"
" as we already have, just return success");
break;
} else {
/*
* req different number of bufs, according to
* v4l2 spec, this is not allowed when there
* are some bufs still mapped.
*/
"v4l2 ioctls: req different number bufs"
"than the exist ones, fail");
break;
}
}
break;
}
"V4L2 ioctl: VIDIOC_REQBUFS: alloc fail");
break;
}
/*
* return buf number that acctually allocated to application
*/
break;
}
case VIDIOC_QUERYBUF: /* for memory mapping IO method */
{
break;
}
"V4L2 ioctl: VIDIOC_QUERYBUF: len=%d",
&buf);
"V4L2 ioctl: VIDIOC_QUERYBUF,(index=%d)len=%d",
break;
}
case VIDIOC_QBUF:
{
"V4L2 ioctl: VIDIOC_QBUF");
"VIDIOC_QBUF error:index=%d,type=%d,memory=%d",
break;
}
if (rv < 0) {
break;
}
break;
}
case VIDIOC_DQBUF:
{
"V4L2 ioctl: VIDIOC_DQBUF");
"VIDIOC_DQBUF: fail, rv=%d", rv);
break;
}
break;
}
case VIDIOC_STREAMON:
{
"V4L2 ioctl: VIDIOC_STREAMON");
if (!strm_if) {
break;
}
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
"VIDIOC_STREAMON: fail. Only capture type is"
" supported by now.");
break;
}
/* if the first read, open isoc pipe */
USB_SUCCESS) {
" first read, open pipe fail");
break;
}
}
/* If it is already started */
break;
}
/* At present, VIDIOC_STREAMON supports mmap io only. */
V4L2_MEMORY_MMAP) != USB_SUCCESS) {
break;
}
break;
}
case VIDIOC_STREAMOFF:
{
"V4L2 ioctl: VIDIOC_STREAMOFF");
if (!strm_if) {
break;
}
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
"VIDIOC_STREAMON: fail. Only capture type is "
"supported by now.");
break;
}
/* Need close the isoc data pipe if any reads are performed. */
strm_if->start_polling = 0;
}
break;
}
case VIDIOC_ENUMINPUT:
{
"V4L2 ioctl: ENUMINPUT");
break;
}
break;
}
case VIDIOC_G_INPUT:
{
"V4L2 ioctl: G_INPUT");
break;
}
case VIDIOC_S_INPUT:
{
int input_idx;
"V4L2 ioctl: S_INPUT");
if (input_idx != 0) { /* Support only one input now */
}
break;
}
/* Query the device that what kinds of video ctrls are supported */
case VIDIOC_QUERYCTRL:
{
"V4L2 ioctl: QUERYCTRL");
break;
}
break;
}
case VIDIOC_G_CTRL:
{
"V4L2 ioctl: G_CTRL");
break;
}
break;
}
case VIDIOC_S_CTRL:
{
"V4L2 ioctl: S_CTRL");
break;
}
break;
}
case VIDIOC_S_PARM:
{
"V4L2 ioctl: VIDIOC_S_PARM");
/* If data I/O is in progress */
break;
}
/* Support capture only, so far. */
break;
}
"V4L2 ioctl VIDIOC_S_PARM fail");
}
break;
}
case VIDIOC_G_PARM:
{
"V4L2 ioctl: VIDIOC_G_PARM");
/* Support capture only, so far. */
break;
}
break;
}
break;
}
/* These ioctls are for analog video standards. */
case VIDIOC_G_STD:
case VIDIOC_S_STD:
case VIDIOC_ENUMSTD:
case VIDIOC_QUERYSTD:
"usbvc_v4l2_ioctl: not a supported cmd, cmd=%x", cmd);
break;
default:
"usbvc_v4l2_ioctl: not a valid cmd value, cmd=%x", cmd);
}
"usbvc_v4l2_ioctl: exit, rv=%d", rv);
return (rv);
}
/*
* Convert GUID in uncompressed format descriptor to the pixelformat element
* in struct v4l2_pix_format
*/
{
return (ret);
}
return (ret);
}
return (0);
}
/*
* Find a frame which has the closest image size as the input args
* (width, height)
*/
static usbvc_frames_t *
{
diff = 0xffffffff;
continue;
}
}
if (diff == 0) {
return (frame);
}
}
return (frame);
}
/* Implement ioctl VIDIOC_S_FMT, set a video format */
static int
{
/*
* Get the first stream interface. Todo: deal with multi stream
* interfaces.
*/
"usbvc_v4l2_set_format: strm_if->fmtgrp_cnt=%d",
/* Find the proper format group according to compress type and guid */
for (i = 0; i < strm_if->fmtgrp_cnt; i++) {
/*
* If v4l2_pixelformat is NULL, then that means there is not
* a parsed format in format_group[i].
*/
"usbvc_set_default_stream_fmt: no frame, fail");
continue;
}
"usbvc_v4l2_set_format: type =%x, i =%d", type, i);
if ((type == VS_FORMAT_MJPEG) ||
(type == VS_FORMAT_UNCOMPRESSED)) {
break;
}
}
}
if (i >= strm_if->fmtgrp_cnt) {
"usbvc_v4l2_set_format: can't find a proper format, "
return (USB_FAILURE);
}
"usbvc_v4l2_set_format: can't find a proper frame, rw=%d, "
return (USB_FAILURE);
}
/* frame interval */
"usbvc_v4l2_set_format: Default Frame Interval=%x", interval);
/*
* Begin negotiate formats.
*/
/* dwFrameInterval is fixed */
/* Probe, just a test before the real try */
!= USB_SUCCESS) {
"usbvc_v4l2_set_format: set probe failed");
return (USB_FAILURE);
}
/* Get max values */
USB_SUCCESS) {
"usbvc_v4l2_set_format: get probe MAX failed");
return (USB_FAILURE);
}
/* Use the best quality first */
/*
* By now, we've get some parametres of ctrl req, next try to set ctrl.
*/
for (i = 0; i < 2; i++) {
/* Probe */
VS_PROBE_CONTROL) != USB_SUCCESS) {
return (USB_FAILURE);
}
/* Get current value after probe */
!= USB_SUCCESS) {
return (USB_FAILURE);
}
"usbvc_v4l2_set_format: bandwidth=%x", bandwidth);
/*
* If the bandwidth does not exceed the max value of all the
* alternatives in this interface, we done.
*/
break;
}
if (i >= 1) {
return (USB_FAILURE);
}
/* Get minimum values since the bandwidth is not enough */
USB_SUCCESS) {
"usbvc_v4l2_set_format: get probe MIN failed");
return (USB_FAILURE);
}
/* To keep simple, just use some minimum values to try again */
sizeof (usbvc_vs_probe_commit_t));
}
/* commit the values we negotiated above */
VS_COMMIT_CONTROL) != USB_SUCCESS) {
"usbvc_v4l2_set_format: set probe failed, i=%d", i);
return (USB_FAILURE);
}
/*
* It's good to check index here before use it. bFormatIndex is based
* on 1, and format_group[i] is based on 0, so minus 1
*/
if (i < strm_if->fmtgrp_cnt) {
} else {
"usbvc_v4l2_set_format: format index out of range");
return (USB_FAILURE);
}
/* bFrameIndex is based on 1, and frames[i] is based on 0, so minus 1 */
} else {
"usbvc_v4l2_set_format: frame index out of range");
return (USB_FAILURE);
}
/*
* by now, the video format is set successfully. record the current
* setting to strm_if->ctrl_pc
*/
"usbvc_v4l2_set_format: dwMaxVideoFrameSize=%x, w=%x, h=%x",
return (USB_SUCCESS);
}
/* Implement ioctl VIDIOC_G_FMT, get the current video format */
static int
{
uint16_t w, h;
return (EINVAL);
}
/* get the current interface. */
"usbvc_v4l2_get_format: fail, no current format or frame,"
"fmtgrp=%p", (void *)fmtgrp);
return (EINVAL);
}
"v4l2 ioctl get format ");
return (0);
}
/*
* Convert color space descriptor's bColorPrimaries to the colorspace element
* in struct v4l2_pix_format
*/
{
return (color_primaries[color_prim]);
}
return (0);
}
/* Implement ioctl VIDIOC_QUERYBUF, get the buf status */
static void
struct v4l2_buffer *v4l2_buf)
{
"usbvc_v4l2_query_buf: uv_buf_len=%d, len=%d",
}
case USBVC_BUF_DONE:
case USBVC_BUF_ERR:
break;
case USBVC_BUF_EMPTY:
break;
case USBVC_BUF_INIT:
default:
break;
}
}
/* Implement ioctl VIDIOC_QBUF, queue a empty buf to the free list */
static int
struct v4l2_buffer *buf)
{
"enqueue_buffer(%d) , want to queue buf_filling, "
return (0);
}
while (donebuf) {
break;
}
donebuf);
}
}
if (queued) {
"enqueue_buffer(%d), still in done list, don't insert to"
return (0);
}
return (0);
}
return (EINVAL);
}
/*
* The buf is put to the buf free list when allocated, so, if the buf
* is the first time to enqueue, just change the state to empty is
* enough.
*/
} else {
}
return (0);
}
/* Implement ioctl VIDIOC_DQBUF, pick a buf from done list */
static int
int mode)
{
/* v4l2 spec: app just set type and memory field */
return (EINVAL);
}
/* non-blocking */
return (EAGAIN);
}
/* no available buffers, block here */
"usbvc_v4l2_dequeue_buffer: wait for done buf");
<= 0) {
/* no done buf and is signaled */
return (EINTR);
}
/* Device is disconnected. */
return (EINTR);
}
}
/*
* just copy the v4l2_buf structure because app need only the index
* value to locate the mapped memory
*/
"usbvc_v4l2_dequeue_buffer: bytesused=%d, idx=%x, status=%d",
return (0);
}
/*
* Check if a ctrl_id is supported by the device, if yes, find the
* corresponding processing unit and fill usbvc_v4l2_ctrl_t
*/
static int
{
"usbvc_v4l2_match_ctrl: ctrl_id=%x", ctrl_id);
if (ctrl_id >= V4L2_CID_PRIVATE_BASE) {
return (USB_FAILURE);
}
if (ctrl_id < V4L2_CID_BASE) {
return (USB_FAILURE);
}
/* get the idx of ctrl array usbvc_v4l2_ctrl */
if (ctrl_id == V4L2_CID_GAMMA) {
/* The 4th one is for Gamma ctrl */
} else if ((ctrl_id >= V4L2_CID_BRIGHTNESS) &&
(ctrl_id <= V4L2_CID_HUE)) {
/* The idxth one is for this ctrl */
} else {
return (USB_FAILURE);
}
/*
* Check if there is a processing unit supportting this ctrl.
* Todo: check if the ctrl and the unit is really for the right
* stream interface in case of multi stream interfaces.
*/
if (bit >=
/*
* If this unit's bmControls size is smaller
* than bit, then next
*/
unit = (usbvc_units_t *)
continue;
} else {
/*
* The first two bytes of bmControls are
* for ctrls
*/
if ((bit < 8) &&
break;
}
break;
}
}
}
unit);
}
return (USB_FAILURE);
}
if (ctrl_id == V4L2_CID_GAMMA) {
} else {
}
return (USB_SUCCESS);
}
/*
* Implement ioctl VIDIOC_QUERYCTRL, query the ctrl types that the device
* supports
*/
static int
{
USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_FAILURE);
}
USB_SUCCESS) {
goto fail;
}
goto fail;
}
goto fail;
}
goto fail;
}
if (data) {
}
return (USB_SUCCESS);
fail:
if (data) {
}
"usbvc_v4l2_query_ctrl: fail when %s", req);
return (USB_FAILURE);
}
/* Implement ioctl VIDIOC_G_CTRL, get current ctrl */
static int
{
USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_FAILURE);
}
if (data) {
}
"usbvc_v4l2_get_ctrl: fail");
return (USB_FAILURE);
}
if (data) {
}
return (USB_SUCCESS);
}
/* Implement ioctl VIDIOC_S_CTRL */
static int
{
USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_FAILURE);
}
USB_SUCCESS) {
if (data) {
}
"usbvc_v4l2_set_ctrl: fail");
return (USB_FAILURE);
}
if (data) {
}
return (USB_SUCCESS);
}
/* For the given interval, find the closest frame interval to it. */
static uint32_t
{
/*
* for continuous case, there is a min and a max, and also a step
* value. The available intervals are those between min and max
* values.
*/
if (step == 0) {
/* a malfunction device */
return (0);
/* return the most possible interval we can handle */
return (frame->dwMinFrameInterval);
/* return the most possible interval we can handle */
return (frame->dwMaxFrameInterval);
}
return (closest);
}
/*
* for discrete case, search all the available intervals, find the
* closest one.
*/
closest = 0;
if (approx1 == 0) {
/* find the matched one, return it immediately */
return (i);
}
closest = i;
}
}
return (closest);
}
/* Implement ioctl VIDIOC_S_PARM. Support capture only, so far. */
static int
{
uint32_t n, d, c, i;
if (!strm_if->cur_format_group ||
"usbvc_v4l2_set_parm: current format or"
" frame is not set. cur_fmt=%p",
(void *)strm_if->cur_format_group);
return (USB_FAILURE);
}
"usbvc_v4l2_set_parm: ask too many read buffers,"
" readbuffers=%d",
return (USB_FAILURE);
}
/* check the values passed in, in case of zero devide */
if (d == 0) {
"usbvc_v4l2_set_parm: invalid denominator=%d", d);
return (USB_FAILURE);
}
/*
* UVC frame intervals are in 100ns units, need convert from
* 1s unit to 100ns unit
*/
/* check the values passed in, in case of overflow */
if (n / d >= ((uint32_t)-1) / c) {
"usbvc_v4l2_set_parm: overflow, numerator=%d,"
" denominator=%d", n, d);
return (USB_FAILURE);
}
"usbvc_v4l2_set_parm: numerator=%d, denominator=%d", n, d);
/* compute the interval in 100ns unit */
if (n <= ((uint32_t)-1) / c) {
i = (n * c) / d;
} else {
do {
n >>= 1;
d >>= 1;
/* decrease both n and d, in case overflow */
} while (n && d && n > ((uint32_t)-1) / c);
if (!d) {
"usbvc_v4l2_set_parm: can't compute interval,"
" denominator=%d", d);
return (USB_FAILURE);
}
i = (n * c) / d;
}
"usbvc_v4l2_set_parm: want interval=%d, n=%d, d=%d, c=%d",
i, n, d, c);
/*
* Begin negotiate frame intervals.
*/
i = usbvc_find_interval(cur_frame, i);
if (i == 0) {
"usbvc_v4l2_set_parm: can not find an proper interval."
" i=%d, n=%d, d=%d", i, n, d);
return (USB_FAILURE);
}
"usbvc_v4l2_set_parm: get interval=%d", i);
/* Probe, just a test before the real try */
!= USB_SUCCESS) {
"usbvc_v4l2_set_parm: set probe failed");
return (USB_FAILURE);
}
/* Commit the frame interval. */
!= USB_SUCCESS) {
"usbvc_v4l2_set_parm: set commit failed");
return (USB_FAILURE);
}
/*
* According to ioctl VIDIOC_S_PARM, zero value of readbuffers will not
* be set. And the current value is expected to return to application.
*/
} else {
}
return (USB_SUCCESS);
}
/* Implement ioctl VIDIOC_G_PARM. */
static int
{
uint32_t n, d;
/* return the actual number of buffers allocated for read() I/O */
/* in 100ns units */
/*
* According to UVC payload specs, the dwFrameInterval in frame
* descriptors is in 100ns unit.
*/
/* Support capture only, so far. */
/* Always success for current support of this command */
return (USB_SUCCESS);
}