summaryrefslogtreecommitdiff
path: root/drivers/media/usb/uvc/uvc_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/uvc/uvc_driver.c')
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c552
1 files changed, 357 insertions, 195 deletions
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index ddb9eaa11be7..30ef2a3110f7 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -7,6 +7,7 @@
*/
#include <linux/atomic.h>
+#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -31,7 +32,7 @@ unsigned int uvc_clock_param = CLOCK_MONOTONIC;
unsigned int uvc_hw_timestamps_param;
unsigned int uvc_no_drop_param;
static unsigned int uvc_quirks_param = -1;
-unsigned int uvc_trace_param;
+unsigned int uvc_dbg_param;
unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
/* ------------------------------------------------------------------------
@@ -519,10 +520,10 @@ static int uvc_parse_format(struct uvc_device *dev,
case UVC_VS_FORMAT_FRAME_BASED:
n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
if (buflen < n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -534,8 +535,8 @@ static int uvc_parse_format(struct uvc_device *dev,
sizeof(format->name));
format->fcc = fmtdesc->fcc;
} else {
- uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
- &buffer[5]);
+ dev_info(&streaming->intf->dev,
+ "Unknown video format %pUl\n", &buffer[5]);
snprintf(format->name, sizeof(format->name), "%pUl\n",
&buffer[5]);
format->fcc = 0;
@@ -583,10 +584,10 @@ static int uvc_parse_format(struct uvc_device *dev,
case UVC_VS_FORMAT_MJPEG:
if (buflen < 11) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -599,10 +600,10 @@ static int uvc_parse_format(struct uvc_device *dev,
case UVC_VS_FORMAT_DV:
if (buflen < 9) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -617,10 +618,10 @@ static int uvc_parse_format(struct uvc_device *dev,
strscpy(format->name, "HD-DV", sizeof(format->name));
break;
default:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d: unknown DV format %u\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber, buffer[8]);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d: unknown DV format %u\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber, buffer[8]);
return -EINVAL;
}
@@ -646,14 +647,14 @@ static int uvc_parse_format(struct uvc_device *dev,
case UVC_VS_FORMAT_STREAM_BASED:
/* Not supported yet. */
default:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d unsupported format %u\n",
- dev->udev->devnum, alts->desc.bInterfaceNumber,
- buffer[2]);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d unsupported format %u\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber,
+ buffer[2]);
return -EINVAL;
}
- uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
+ uvc_dbg(dev, DESCR, "Found format %s\n", format->name);
buflen -= buffer[0];
buffer += buffer[0];
@@ -672,9 +673,10 @@ static int uvc_parse_format(struct uvc_device *dev,
n = n ? n : 3;
if (buflen < 26 + 4*n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FRAME error\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d FRAME error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -736,10 +738,10 @@ static int uvc_parse_format(struct uvc_device *dev,
frame->dwDefaultFrameInterval;
}
- uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
+ uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n",
frame->wWidth, frame->wHeight,
- 10000000/frame->dwDefaultFrameInterval,
- (100000000/frame->dwDefaultFrameInterval)%10);
+ 10000000 / frame->dwDefaultFrameInterval,
+ (100000000 / frame->dwDefaultFrameInterval) % 10);
format->nframes++;
buflen -= buffer[0];
@@ -755,10 +757,10 @@ static int uvc_parse_format(struct uvc_device *dev,
if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
buffer[2] == UVC_VS_COLORFORMAT) {
if (buflen < 6) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d COLORFORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d COLORFORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -790,15 +792,17 @@ static int uvc_parse_streaming(struct uvc_device *dev,
if (intf->cur_altsetting->desc.bInterfaceSubClass
!= UVC_SC_VIDEOSTREAMING) {
- uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
- "video streaming interface\n", dev->udev->devnum,
+ uvc_dbg(dev, DESCR,
+ "device %d interface %d isn't a video streaming interface\n",
+ dev->udev->devnum,
intf->altsetting[0].desc.bInterfaceNumber);
return -EINVAL;
}
if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
- uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
- "claimed\n", dev->udev->devnum,
+ uvc_dbg(dev, DESCR,
+ "device %d interface %d is already claimed\n",
+ dev->udev->devnum,
intf->altsetting[0].desc.bInterfaceNumber);
return -EINVAL;
}
@@ -821,8 +825,9 @@ static int uvc_parse_streaming(struct uvc_device *dev,
if (ep->extralen > 2 &&
ep->extra[1] == USB_DT_CS_INTERFACE) {
- uvc_trace(UVC_TRACE_DESCR, "trying extra data "
- "from endpoint %u.\n", i);
+ uvc_dbg(dev, DESCR,
+ "trying extra data from endpoint %u\n",
+ i);
buffer = alts->endpoint[i].extra;
buflen = alts->endpoint[i].extralen;
break;
@@ -837,8 +842,8 @@ static int uvc_parse_streaming(struct uvc_device *dev,
}
if (buflen <= 2) {
- uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
- "interface descriptors found.\n");
+ uvc_dbg(dev, DESCR,
+ "no class-specific streaming interface descriptors found\n");
goto error;
}
@@ -855,9 +860,9 @@ static int uvc_parse_streaming(struct uvc_device *dev,
break;
default:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d HEADER descriptor not found.\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d HEADER descriptor not found\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
goto error;
}
@@ -865,8 +870,8 @@ static int uvc_parse_streaming(struct uvc_device *dev,
n = buflen >= size ? buffer[size-1] : 0;
if (buflen < size + p*n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d HEADER descriptor is invalid.\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d HEADER descriptor is invalid\n",
dev->udev->devnum, alts->desc.bInterfaceNumber);
goto error;
}
@@ -917,8 +922,8 @@ static int uvc_parse_streaming(struct uvc_device *dev,
case UVC_VS_FORMAT_MPEG2TS:
case UVC_VS_FORMAT_STREAM_BASED:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT %u is not supported.\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d FORMAT %u is not supported\n",
dev->udev->devnum,
alts->desc.bInterfaceNumber, _buffer[2]);
break;
@@ -942,8 +947,8 @@ static int uvc_parse_streaming(struct uvc_device *dev,
}
if (nformats == 0) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d has no supported formats defined.\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d has no supported formats defined\n",
dev->udev->devnum, alts->desc.bInterfaceNumber);
goto error;
}
@@ -991,8 +996,8 @@ static int uvc_parse_streaming(struct uvc_device *dev,
}
if (buflen)
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d has %u bytes of trailing descriptor garbage.\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n",
dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
/* Parse the alternate settings to find the maximum bandwidth. */
@@ -1019,7 +1024,13 @@ error:
return ret;
}
-static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
+static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
+static const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER;
+static const u8 uvc_media_transport_input_guid[16] =
+ UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
+static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
+
+static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
unsigned int num_pads, unsigned int extra_size)
{
struct uvc_entity *entity;
@@ -1028,7 +1039,10 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
unsigned int i;
extra_size = roundup(extra_size, sizeof(*entity->pads));
- num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
+ if (num_pads)
+ num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1;
+ else
+ num_inputs = 0;
size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
+ num_inputs;
entity = kzalloc(size, GFP_KERNEL);
@@ -1038,13 +1052,32 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
entity->id = id;
entity->type = type;
+ /*
+ * Set the GUID for standard entity types. For extension units, the GUID
+ * is initialized by the caller.
+ */
+ switch (type) {
+ case UVC_EXT_GPIO_UNIT:
+ memcpy(entity->guid, uvc_gpio_guid, 16);
+ break;
+ case UVC_ITT_CAMERA:
+ memcpy(entity->guid, uvc_camera_guid, 16);
+ break;
+ case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+ memcpy(entity->guid, uvc_media_transport_input_guid, 16);
+ break;
+ case UVC_VC_PROCESSING_UNIT:
+ memcpy(entity->guid, uvc_processing_guid, 16);
+ break;
+ }
+
entity->num_links = 0;
entity->num_pads = num_pads;
entity->pads = ((void *)(entity + 1)) + extra_size;
for (i = 0; i < num_inputs; ++i)
entity->pads[i].flags = MEDIA_PAD_FL_SINK;
- if (!UVC_ENTITY_IS_OTERM(entity))
+ if (!UVC_ENTITY_IS_OTERM(entity) && num_pads)
entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
entity->bNrInPins = num_inputs;
@@ -1098,8 +1131,8 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
n = buflen >= 25 + p ? buffer[22+p] : 0;
if (buflen < 25 + p + 2*n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d EXTENSION_UNIT error\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d EXTENSION_UNIT error\n",
udev->devnum, alts->desc.bInterfaceNumber);
break;
}
@@ -1109,7 +1142,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
if (unit == NULL)
return -ENOMEM;
- memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ memcpy(unit->guid, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
memcpy(unit->baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
@@ -1147,9 +1180,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
n = buflen >= 12 ? buffer[11] : 0;
if (buflen < 12 + n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d HEADER error\n", udev->devnum,
- alts->desc.bInterfaceNumber);
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d HEADER error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -1160,8 +1193,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
for (i = 0; i < n; ++i) {
intf = usb_ifnum_to_if(udev, buffer[12+i]);
if (intf == NULL) {
- uvc_trace(UVC_TRACE_DESCR, "device %d "
- "interface %d doesn't exists\n",
+ uvc_dbg(dev, DESCR,
+ "device %d interface %d doesn't exists\n",
udev->devnum, i);
continue;
}
@@ -1172,8 +1205,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
case UVC_VC_INPUT_TERMINAL:
if (buflen < 8) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d INPUT_TERMINAL error\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d INPUT_TERMINAL error\n",
udev->devnum, alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -1191,10 +1224,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
*/
type = get_unaligned_le16(&buffer[4]);
if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d INPUT_TERMINAL %d has invalid "
- "type 0x%04x, skipping\n", udev->devnum,
- alts->desc.bInterfaceNumber,
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n",
+ udev->devnum, alts->desc.bInterfaceNumber,
buffer[3], type);
return 0;
}
@@ -1214,8 +1246,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
}
if (buflen < len + n + p) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d INPUT_TERMINAL error\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d INPUT_TERMINAL error\n",
udev->devnum, alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -1261,8 +1293,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
case UVC_VC_OUTPUT_TERMINAL:
if (buflen < 9) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d OUTPUT_TERMINAL error\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d OUTPUT_TERMINAL error\n",
udev->devnum, alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -1272,10 +1304,10 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
*/
type = get_unaligned_le16(&buffer[4]);
if ((type & 0xff00) == 0) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d OUTPUT_TERMINAL %d has invalid "
- "type 0x%04x, skipping\n", udev->devnum,
- alts->desc.bInterfaceNumber, buffer[3], type);
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n",
+ udev->devnum, alts->desc.bInterfaceNumber,
+ buffer[3], type);
return 0;
}
@@ -1299,8 +1331,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
p = buflen >= 5 ? buffer[4] : 0;
if (buflen < 5 || buflen < 6 + p) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d SELECTOR_UNIT error\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d SELECTOR_UNIT error\n",
udev->devnum, alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -1325,8 +1357,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
p = dev->uvc_version >= 0x0110 ? 10 : 9;
if (buflen < p + n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d PROCESSING_UNIT error\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d PROCESSING_UNIT error\n",
udev->devnum, alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -1358,8 +1390,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
n = buflen >= 24 + p ? buffer[22+p] : 0;
if (buflen < 24 + p + n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d EXTENSION_UNIT error\n",
+ uvc_dbg(dev, DESCR,
+ "device %d videocontrol interface %d EXTENSION_UNIT error\n",
udev->devnum, alts->desc.bInterfaceNumber);
return -EINVAL;
}
@@ -1368,7 +1400,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
if (unit == NULL)
return -ENOMEM;
- memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ memcpy(unit->guid, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
memcpy(unit->baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
@@ -1385,8 +1417,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
break;
default:
- uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
- "descriptor (%u)\n", buffer[2]);
+ uvc_dbg(dev, DESCR,
+ "Found an unknown CS_INTERFACE descriptor (%u)\n",
+ buffer[2]);
break;
}
@@ -1431,8 +1464,9 @@ next_descriptor:
if (usb_endpoint_is_int_in(desc) &&
le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
desc->bInterval != 0) {
- uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
- "(addr %02x).\n", desc->bEndpointAddress);
+ uvc_dbg(dev, DESCR,
+ "Found a Status endpoint (addr %02x)\n",
+ desc->bEndpointAddress);
dev->int_ep = ep;
}
}
@@ -1440,6 +1474,108 @@ next_descriptor:
return 0;
}
+/* -----------------------------------------------------------------------------
+ * Privacy GPIO
+ */
+
+static void uvc_gpio_event(struct uvc_device *dev)
+{
+ struct uvc_entity *unit = dev->gpio_unit;
+ struct uvc_video_chain *chain;
+ u8 new_val;
+
+ if (!unit)
+ return;
+
+ new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
+
+ /* GPIO entities are always on the first chain. */
+ chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
+ uvc_ctrl_status_event(chain, unit->controls, &new_val);
+}
+
+static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, void *data, u16 size)
+{
+ if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
+ return -EINVAL;
+
+ *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy);
+
+ return 0;
+}
+
+static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity,
+ u8 cs, u8 *caps)
+{
+ if (cs != UVC_CT_PRIVACY_CONTROL)
+ return -EINVAL;
+
+ *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE;
+ return 0;
+}
+
+static irqreturn_t uvc_gpio_irq(int irq, void *data)
+{
+ struct uvc_device *dev = data;
+
+ uvc_gpio_event(dev);
+ return IRQ_HANDLED;
+}
+
+static int uvc_gpio_parse(struct uvc_device *dev)
+{
+ struct uvc_entity *unit;
+ struct gpio_desc *gpio_privacy;
+ int irq;
+
+ gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy",
+ GPIOD_IN);
+ if (IS_ERR_OR_NULL(gpio_privacy))
+ return PTR_ERR_OR_ZERO(gpio_privacy);
+
+ unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1);
+ if (!unit)
+ return -ENOMEM;
+
+ irq = gpiod_to_irq(gpio_privacy);
+ if (irq < 0) {
+ if (irq != EPROBE_DEFER)
+ dev_err(&dev->udev->dev,
+ "No IRQ for privacy GPIO (%d)\n", irq);
+ return irq;
+ }
+
+ unit->gpio.gpio_privacy = gpio_privacy;
+ unit->gpio.irq = irq;
+ unit->gpio.bControlSize = 1;
+ unit->gpio.bmControls = (u8 *)unit + sizeof(*unit);
+ unit->gpio.bmControls[0] = 1;
+ unit->get_cur = uvc_gpio_get_cur;
+ unit->get_info = uvc_gpio_get_info;
+ strscpy(unit->name, "GPIO", sizeof(unit->name));
+
+ list_add_tail(&unit->list, &dev->entities);
+
+ dev->gpio_unit = unit;
+
+ return 0;
+}
+
+static int uvc_gpio_init_irq(struct uvc_device *dev)
+{
+ struct uvc_entity *unit = dev->gpio_unit;
+
+ if (!unit || unit->gpio.irq < 0)
+ return 0;
+
+ return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
+ uvc_gpio_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING,
+ "uvc_privacy_gpio", dev);
+}
+
/* ------------------------------------------------------------------------
* UVC device scan
*/
@@ -1475,24 +1611,23 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
{
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_EXTENSION_UNIT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " <- XU %d", entity->id);
+ uvc_dbg_cont(PROBE, " <- XU %d", entity->id);
if (entity->bNrInPins != 1) {
- uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
- "than 1 input pin.\n", entity->id);
+ uvc_dbg(chain->dev, DESCR,
+ "Extension unit %d has more than 1 input pin\n",
+ entity->id);
return -1;
}
break;
case UVC_VC_PROCESSING_UNIT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " <- PU %d", entity->id);
+ uvc_dbg_cont(PROBE, " <- PU %d", entity->id);
if (chain->processing != NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found multiple "
- "Processing Units in chain.\n");
+ uvc_dbg(chain->dev, DESCR,
+ "Found multiple Processing Units in chain\n");
return -1;
}
@@ -1500,16 +1635,15 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
break;
case UVC_VC_SELECTOR_UNIT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " <- SU %d", entity->id);
+ uvc_dbg_cont(PROBE, " <- SU %d", entity->id);
/* Single-input selector units are ignored. */
if (entity->bNrInPins == 1)
break;
if (chain->selector != NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
- "Units in chain.\n");
+ uvc_dbg(chain->dev, DESCR,
+ "Found multiple Selector Units in chain\n");
return -1;
}
@@ -1519,33 +1653,29 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
case UVC_ITT_VENDOR_SPECIFIC:
case UVC_ITT_CAMERA:
case UVC_ITT_MEDIA_TRANSPORT_INPUT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " <- IT %d\n", entity->id);
+ uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id);
break;
case UVC_OTT_VENDOR_SPECIFIC:
case UVC_OTT_DISPLAY:
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " OT %d", entity->id);
+ uvc_dbg_cont(PROBE, " OT %d", entity->id);
break;
case UVC_TT_STREAMING:
- if (UVC_ENTITY_IS_ITERM(entity)) {
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " <- IT %d\n", entity->id);
- } else {
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " OT %d", entity->id);
- }
+ if (UVC_ENTITY_IS_ITERM(entity))
+ uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id);
+ else
+ uvc_dbg_cont(PROBE, " OT %d", entity->id);
break;
default:
- uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
- "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
+ uvc_dbg(chain->dev, DESCR,
+ "Unsupported entity type 0x%04x found in chain\n",
+ UVC_ENTITY_TYPE(entity));
return -1;
}
@@ -1571,28 +1701,27 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
if (forward == prev)
continue;
if (forward->chain.next || forward->chain.prev) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "entity %d already in chain.\n", forward->id);
+ uvc_dbg(chain->dev, DESCR,
+ "Found reference to entity %d already in chain\n",
+ forward->id);
return -EINVAL;
}
switch (UVC_ENTITY_TYPE(forward)) {
case UVC_VC_EXTENSION_UNIT:
if (forward->bNrInPins != 1) {
- uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
- "has more than 1 input pin.\n",
- entity->id);
+ uvc_dbg(chain->dev, DESCR,
+ "Extension unit %d has more than 1 input pin\n",
+ entity->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &chain->entities);
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- if (!found)
- printk(KERN_CONT " (->");
+ if (!found)
+ uvc_dbg_cont(PROBE, " (->");
- printk(KERN_CONT " XU %d", forward->id);
- found = 1;
- }
+ uvc_dbg_cont(PROBE, " XU %d", forward->id);
+ found = 1;
break;
case UVC_OTT_VENDOR_SPECIFIC:
@@ -1600,24 +1729,23 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
case UVC_TT_STREAMING:
if (UVC_ENTITY_IS_ITERM(forward)) {
- uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
- "terminal %u.\n", forward->id);
+ uvc_dbg(chain->dev, DESCR,
+ "Unsupported input terminal %u\n",
+ forward->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &chain->entities);
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- if (!found)
- printk(KERN_CONT " (->");
+ if (!found)
+ uvc_dbg_cont(PROBE, " (->");
- printk(KERN_CONT " OT %d", forward->id);
- found = 1;
- }
+ uvc_dbg_cont(PROBE, " OT %d", forward->id);
+ found = 1;
break;
}
}
if (found)
- printk(KERN_CONT ")");
+ uvc_dbg_cont(PROBE, ")");
return 0;
}
@@ -1642,36 +1770,33 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
break;
}
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " <- IT");
+ uvc_dbg_cont(PROBE, " <- IT");
chain->selector = entity;
for (i = 0; i < entity->bNrInPins; ++i) {
id = entity->baSourceID[i];
term = uvc_entity_by_id(chain->dev, id);
if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
- uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
- "input %d isn't connected to an "
- "input terminal\n", entity->id, i);
+ uvc_dbg(chain->dev, DESCR,
+ "Selector unit %d input %d isn't connected to an input terminal\n",
+ entity->id, i);
return -1;
}
if (term->chain.next || term->chain.prev) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "entity %d already in chain.\n",
+ uvc_dbg(chain->dev, DESCR,
+ "Found reference to entity %d already in chain\n",
term->id);
return -EINVAL;
}
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT " %d", term->id);
+ uvc_dbg_cont(PROBE, " %d", term->id);
list_add_tail(&term->chain, &chain->entities);
uvc_scan_chain_forward(chain, term, entity);
}
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(KERN_CONT "\n");
+ uvc_dbg_cont(PROBE, "\n");
id = 0;
break;
@@ -1694,8 +1819,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
entity = uvc_entity_by_id(chain->dev, id);
if (entity == NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "unknown entity %d.\n", id);
+ uvc_dbg(chain->dev, DESCR,
+ "Found reference to unknown entity %d\n", id);
return -EINVAL;
}
@@ -1708,7 +1833,7 @@ static int uvc_scan_chain(struct uvc_video_chain *chain,
{
struct uvc_entity *entity, *prev;
- uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
+ uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:");
entity = term;
prev = NULL;
@@ -1716,8 +1841,9 @@ static int uvc_scan_chain(struct uvc_video_chain *chain,
while (entity != NULL) {
/* Entity must not be part of an existing chain */
if (entity->chain.next || entity->chain.prev) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "entity %d already in chain.\n", entity->id);
+ uvc_dbg(chain->dev, DESCR,
+ "Found reference to entity %d already in chain\n",
+ entity->id);
return -EINVAL;
}
@@ -1871,9 +1997,8 @@ static int uvc_scan_fallback(struct uvc_device *dev)
list_add_tail(&chain->list, &dev->chains);
- uvc_trace(UVC_TRACE_PROBE,
- "Found a video chain by fallback heuristic (%s).\n",
- uvc_print_chain(chain));
+ uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n",
+ uvc_print_chain(chain));
return 0;
@@ -1915,8 +2040,8 @@ static int uvc_scan_device(struct uvc_device *dev)
continue;
}
- uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
- uvc_print_chain(chain));
+ uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n",
+ uvc_print_chain(chain));
list_add_tail(&chain->list, &dev->chains);
}
@@ -1925,10 +2050,17 @@ static int uvc_scan_device(struct uvc_device *dev)
uvc_scan_fallback(dev);
if (list_empty(&dev->chains)) {
- uvc_printk(KERN_INFO, "No valid video chain found.\n");
+ dev_info(&dev->udev->dev, "No valid video chain found.\n");
return -1;
}
+ /* Add GPIO entity to the first chain. */
+ if (dev->gpio_unit) {
+ chain = list_first_entry(&dev->chains,
+ struct uvc_video_chain, list);
+ list_add_tail(&dev->gpio_unit->chain, &chain->entities);
+ }
+
return 0;
}
@@ -2077,8 +2209,9 @@ int uvc_register_video_device(struct uvc_device *dev,
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
- uvc_printk(KERN_ERR, "Failed to register %s device (%d).\n",
- v4l2_type_names[type], ret);
+ dev_err(&stream->intf->dev,
+ "Failed to register %s device (%d).\n",
+ v4l2_type_names[type], ret);
return ret;
}
@@ -2094,8 +2227,8 @@ static int uvc_register_video(struct uvc_device *dev,
/* Initialize the streaming interface with default parameters. */
ret = uvc_video_init(stream);
if (ret < 0) {
- uvc_printk(KERN_ERR, "Failed to initialize the device (%d).\n",
- ret);
+ dev_err(&stream->intf->dev,
+ "Failed to initialize the device (%d).\n", ret);
return ret;
}
@@ -2129,8 +2262,9 @@ static int uvc_register_terms(struct uvc_device *dev,
stream = uvc_stream_by_id(dev, term->id);
if (stream == NULL) {
- uvc_printk(KERN_INFO, "No streaming interface found "
- "for terminal %u.", term->id);
+ dev_info(&dev->udev->dev,
+ "No streaming interface found for terminal %u.",
+ term->id);
continue;
}
@@ -2163,8 +2297,8 @@ static int uvc_register_chains(struct uvc_device *dev)
#ifdef CONFIG_MEDIA_CONTROLLER
ret = uvc_mc_register_entities(chain);
if (ret < 0)
- uvc_printk(KERN_INFO,
- "Failed to register entities (%d).\n", ret);
+ dev_info(&dev->udev->dev,
+ "Failed to register entities (%d).\n", ret);
#endif
}
@@ -2187,14 +2321,6 @@ static int uvc_probe(struct usb_interface *intf,
int function;
int ret;
- if (id->idVendor && id->idProduct)
- uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
- "(%04x:%04x)\n", udev->devpath, id->idVendor,
- id->idProduct);
- else
- uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
- udev->devpath);
-
/* Allocate memory for the device and initialize it. */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL)
@@ -2214,6 +2340,13 @@ static int uvc_probe(struct usb_interface *intf,
dev->quirks = uvc_quirks_param == -1
? dev->info->quirks : uvc_quirks_param;
+ if (id->idVendor && id->idProduct)
+ uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n",
+ udev->devpath, id->idVendor, id->idProduct);
+ else
+ uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n",
+ udev->devpath);
+
if (udev->product != NULL)
strscpy(dev->name, udev->product, sizeof(dev->name));
else
@@ -2256,22 +2389,34 @@ static int uvc_probe(struct usb_interface *intf,
/* Parse the Video Class control descriptor. */
if (uvc_parse_control(dev) < 0) {
- uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
- "descriptors.\n");
+ uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n");
+ goto error;
+ }
+
+ /* Parse the associated GPIOs. */
+ if (uvc_gpio_parse(dev) < 0) {
+ uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n");
goto error;
}
- uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
- dev->uvc_version >> 8, dev->uvc_version & 0xff,
- udev->product ? udev->product : "<unnamed>",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
+ dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n",
+ dev->uvc_version >> 8, dev->uvc_version & 0xff,
+ udev->product ? udev->product : "<unnamed>",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
if (dev->quirks != dev->info->quirks) {
- uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
- "parameter for testing purpose.\n", dev->quirks);
- uvc_printk(KERN_INFO, "Please report required quirks to the "
- "linux-uvc-devel mailing list.\n");
+ dev_info(&dev->udev->dev,
+ "Forcing device quirks to 0x%x by module parameter for testing purpose.\n",
+ dev->quirks);
+ dev_info(&dev->udev->dev,
+ "Please report required quirks to the linux-uvc-devel mailing list.\n");
+ }
+
+ if (dev->info->uvc_version) {
+ dev->uvc_version = dev->info->uvc_version;
+ dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n",
+ dev->uvc_version >> 8, dev->uvc_version & 0xff);
}
/* Register the V4L2 device. */
@@ -2300,12 +2445,19 @@ static int uvc_probe(struct usb_interface *intf,
/* Initialize the interrupt URB. */
if ((ret = uvc_status_init(dev)) < 0) {
- uvc_printk(KERN_INFO, "Unable to initialize the status "
- "endpoint (%d), status interrupt will not be "
- "supported.\n", ret);
+ dev_info(&dev->udev->dev,
+ "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n",
+ ret);
}
- uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+ ret = uvc_gpio_init_irq(dev);
+ if (ret < 0) {
+ dev_err(&dev->udev->dev,
+ "Unable to request privacy GPIO IRQ (%d)\n", ret);
+ goto error;
+ }
+
+ uvc_dbg(dev, PROBE, "UVC device initialized\n");
usb_enable_autosuspend(udev);
return 0;
@@ -2337,7 +2489,7 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
struct uvc_device *dev = usb_get_intfdata(intf);
struct uvc_streaming *stream;
- uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
+ uvc_dbg(dev, SUSPEND, "Suspending interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber);
/* Controls are cached on the fly so they don't need to be saved. */
@@ -2355,8 +2507,8 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
return uvc_video_suspend(stream);
}
- uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
- "mismatch.\n");
+ uvc_dbg(dev, SUSPEND,
+ "Suspend: video streaming USB interface mismatch\n");
return -EINVAL;
}
@@ -2366,7 +2518,7 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
struct uvc_streaming *stream;
int ret = 0;
- uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
+ uvc_dbg(dev, SUSPEND, "Resuming interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber);
if (intf->cur_altsetting->desc.bInterfaceSubClass ==
@@ -2395,8 +2547,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
}
}
- uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
- "mismatch.\n");
+ uvc_dbg(dev, SUSPEND,
+ "Resume: video streaming USB interface mismatch\n");
return -EINVAL;
}
@@ -2446,7 +2598,7 @@ module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(quirks, "Forced device quirks");
-module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
+module_param_named(trace, uvc_dbg_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(trace, "Trace level bitmask");
module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
@@ -2923,6 +3075,17 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
+ /* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1bcf,
+ .idProduct = 0x0b40,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){
+ .uvc_version = 0x010a,
+ } },
/* SiGma Micro USB Web Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -3002,7 +3165,6 @@ static int __init uvc_init(void)
return ret;
}
- printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
return 0;
}