Linux-Media Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl
@ 2020-12-21 16:48 Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 01/12] media: uvcvideo: Fix race condition handling events Ricardo Ribalda
                   ` (10 more replies)
  0 siblings, 11 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Some devices can implement a physical switch to disable the input of the
camera on demand. Think of it like an elegant privacy sticker.

The system can read the status of the privacy switch via a GPIO.

The ACPI table maps this GPIO to the USB device via _CRS and _DSD
descriptors, so the kernel can find it.

The userspace applications need to know if the privacy pin is enabled
or not.

The obvious way to show it to userspace is via the V4L2_CID_PRIVACY control.

This patchset implement this functionality.

v5: Thanks to all the comments from Laurent!
  - Allow multiple async_ctrls
  - Use dev_dbg() for uvc_trace
  - Major redesing of "Implement UVC_EXT_GPIO_UNIT"
  - Major redesing of "Implement UVC_QUIRK_PRIVACY_DURING_STREAM"

v4: Implement UVC_QUIRK_PRIVACY_DURING_STREAM

v3: Thanks to all the comments from Joe Perches
  - Rework of printk macros

v2: Thanks to all the comments from Laurent!
  - move guid to unit
  - support entities with no pads
  - CodeStyle
  - Irq handling
  - pr_cont
  - new ids

Ricardo Ribalda (12):
  media: uvcvideo: Fix race condition handling events
  media: uvcvideo: Allow more that one asyc_ctrl
  media: uvcvideo: Move guid to entity
  media: uvcvideo: Allow extra entities
  media: uvcvideo: Allow entities with no pads
  media: uvcvideo: Allow entity-defined get_info and get_cur
  media: uvcvideo: Implement UVC_EXT_GPIO_UNIT
  media: uvcvideo: Add Privacy control based on EXT_GPIO
  media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  media: uvcvideo: Use dev_ printk aliases
  media: uvcvideo: New macro uvc_trace_cont
  media: uvcvideo: use dev_dbg() for uvc_trace()

 drivers/media/usb/uvc/uvc_ctrl.c   | 204 +++++----
 drivers/media/usb/uvc/uvc_driver.c | 644 +++++++++++++++++++----------
 drivers/media/usb/uvc/uvc_entity.c |  11 +-
 drivers/media/usb/uvc/uvc_isight.c |  16 +-
 drivers/media/usb/uvc/uvc_queue.c  |  12 +-
 drivers/media/usb/uvc/uvc_status.c |  32 +-
 drivers/media/usb/uvc/uvc_v4l2.c   |  53 ++-
 drivers/media/usb/uvc/uvc_video.c  | 120 +++---
 drivers/media/usb/uvc/uvcvideo.h   |  91 ++--
 9 files changed, 767 insertions(+), 416 deletions(-)

-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 01/12] media: uvcvideo: Fix race condition handling events
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-22  8:04   ` Laurent Pinchart
  2020-12-21 16:48 ` [PATCH v5 02/12] media: uvcvideo: Allow more that one asyc_ctrl Ricardo Ribalda
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

The control and its data needs to be copied to the workqueue at the same
time to avoid half-updates of the events.
This is, events reported to userspace were the control id does not match
its value.

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 2 +-
 drivers/media/usb/uvc/uvcvideo.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 011e69427b7c..aa18dcdf8165 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1332,7 +1332,7 @@ bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain,
 		return false;
 	}
 
-	w->data = data;
+	memcpy(w->data, data, ctrl->info.size);
 	w->urb = urb;
 	w->chain = chain;
 	w->ctrl = ctrl;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index a3dfacf069c4..0db6c2e0bd98 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -678,7 +678,7 @@ struct uvc_device {
 		struct urb *urb;
 		struct uvc_video_chain *chain;
 		struct uvc_control *ctrl;
-		const void *data;
+		u8 data[UVC_MAX_STATUS_SIZE];
 	} async_ctrl;
 };
 
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 02/12] media: uvcvideo: Allow more that one asyc_ctrl
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 01/12] media: uvcvideo: Fix race condition handling events Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-22  8:07   ` Laurent Pinchart
  2020-12-21 16:48 ` [PATCH v5 03/12] media: uvcvideo: Move guid to entity Ricardo Ribalda
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

The current implementation allocates memory for only one async_control.
If we get a second event before we have processed the previous one, the
old one gets lost.

Introduce a dynamic memory allocation and a list to handle the
async_controls.

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 49 ++++++++++++++++++++++++++------
 drivers/media/usb/uvc/uvcvideo.h | 19 ++++++++-----
 2 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index aa18dcdf8165..69b2fc6ce12c 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1275,11 +1275,9 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
 	uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
 }
 
-static void uvc_ctrl_status_event_work(struct work_struct *work)
+static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
+					 struct uvc_ctrl_work *w)
 {
-	struct uvc_device *dev = container_of(work, struct uvc_device,
-					      async_ctrl.work);
-	struct uvc_ctrl_work *w = &dev->async_ctrl;
 	struct uvc_video_chain *chain = w->chain;
 	struct uvc_control_mapping *mapping;
 	struct uvc_control *ctrl = w->ctrl;
@@ -1321,23 +1319,54 @@ static void uvc_ctrl_status_event_work(struct work_struct *work)
 			   ret);
 }
 
+static void uvc_ctrl_status_event_work(struct work_struct *work)
+{
+	struct uvc_device *dev = container_of(work, struct uvc_device,
+					      async_ctrl_work);
+	struct uvc_ctrl_work *w;
+
+	do {
+		mutex_lock(&dev->async_ctrl_lock);
+		w = list_first_entry_or_null(&dev->async_ctrl_list,
+					     struct uvc_ctrl_work,
+					     list);
+		if (w)
+			list_del(&w->list);
+		mutex_unlock(&dev->async_ctrl_lock);
+
+		if (!w)
+			return;
+
+		__uvc_ctrl_status_event_work(dev, w);
+		kfree(w);
+	} while (w);
+}
+
 bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain,
 			   struct uvc_control *ctrl, const u8 *data)
 {
 	struct uvc_device *dev = chain->dev;
-	struct uvc_ctrl_work *w = &dev->async_ctrl;
+	struct uvc_ctrl_work *w;
 
 	if (list_empty(&ctrl->info.mappings)) {
 		ctrl->handle = NULL;
 		return false;
 	}
 
+	w = kzalloc(sizeof(*w), GFP_KERNEL);
+	if (WARN(!w, "Not enough memory to trigger uvc event"))
+		return false;
+
 	memcpy(w->data, data, ctrl->info.size);
 	w->urb = urb;
 	w->chain = chain;
 	w->ctrl = ctrl;
 
-	schedule_work(&w->work);
+	mutex_lock(&dev->async_ctrl_lock);
+	list_add_tail(&w->list, &dev->async_ctrl_list);
+	mutex_unlock(&dev->async_ctrl_lock);
+
+	schedule_work(&dev->async_ctrl_work);
 
 	return true;
 }
@@ -2277,7 +2306,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
 	struct uvc_entity *entity;
 	unsigned int i;
 
-	INIT_WORK(&dev->async_ctrl.work, uvc_ctrl_status_event_work);
+	INIT_WORK(&dev->async_ctrl_work, uvc_ctrl_status_event_work);
+	mutex_init(&dev->async_ctrl_lock);
+	INIT_LIST_HEAD(&dev->async_ctrl_list);
 
 	/* Walk the entities list and instantiate controls */
 	list_for_each_entry(entity, &dev->entities, list) {
@@ -2348,8 +2379,8 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev)
 	unsigned int i;
 
 	/* Can be uninitialized if we are aborting on probe error. */
-	if (dev->async_ctrl.work.func)
-		cancel_work_sync(&dev->async_ctrl.work);
+	if (dev->async_ctrl_work.func)
+		cancel_work_sync(&dev->async_ctrl_work);
 
 	/* Free controls and control mappings for all entities. */
 	list_for_each_entry(entity, &dev->entities, list) {
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 0db6c2e0bd98..afcaf49fad1a 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -637,6 +637,14 @@ struct uvc_device_info {
 	u32	meta_format;
 };
 
+struct uvc_ctrl_work {
+	struct list_head list;
+	struct urb *urb;
+	struct uvc_video_chain *chain;
+	struct uvc_control *ctrl;
+	u8 data[UVC_MAX_STATUS_SIZE];
+};
+
 struct uvc_device {
 	struct usb_device *udev;
 	struct usb_interface *intf;
@@ -673,13 +681,10 @@ struct uvc_device {
 	struct input_dev *input;
 	char input_phys[64];
 
-	struct uvc_ctrl_work {
-		struct work_struct work;
-		struct urb *urb;
-		struct uvc_video_chain *chain;
-		struct uvc_control *ctrl;
-		u8 data[UVC_MAX_STATUS_SIZE];
-	} async_ctrl;
+	/* Async control */
+	struct work_struct async_ctrl_work;
+	struct list_head async_ctrl_list;
+	struct mutex async_ctrl_lock;
 };
 
 enum uvc_handle_state {
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 03/12] media: uvcvideo: Move guid to entity
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 01/12] media: uvcvideo: Fix race condition handling events Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 02/12] media: uvcvideo: Allow more that one asyc_ctrl Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 05/12] media: uvcvideo: Allow entities with no pads Ricardo Ribalda
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Instead of having multiple copies of the entity guid on the code, move
it to the entity structure.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c   | 30 ++++--------------------------
 drivers/media/usb/uvc/uvc_driver.c | 25 +++++++++++++++++++++++--
 drivers/media/usb/uvc/uvcvideo.h   |  2 +-
 3 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 69b2fc6ce12c..d7ccd0fcd88d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -826,31 +826,10 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
  * Terminal and unit management
  */
 
-static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
-static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
-static const u8 uvc_media_transport_input_guid[16] =
-	UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
-
 static int uvc_entity_match_guid(const struct uvc_entity *entity,
-	const u8 guid[16])
+				 const u8 guid[16])
 {
-	switch (UVC_ENTITY_TYPE(entity)) {
-	case UVC_ITT_CAMERA:
-		return memcmp(uvc_camera_guid, guid, 16) == 0;
-
-	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
-		return memcmp(uvc_media_transport_input_guid, guid, 16) == 0;
-
-	case UVC_VC_PROCESSING_UNIT:
-		return memcmp(uvc_processing_guid, guid, 16) == 0;
-
-	case UVC_VC_EXTENSION_UNIT:
-		return memcmp(entity->extension.guidExtensionCode,
-			      guid, 16) == 0;
-
-	default:
-		return 0;
-	}
+	return memcmp(entity->guid, guid, sizeof(entity->guid)) == 0;
 }
 
 /* ------------------------------------------------------------------------
@@ -1805,8 +1784,7 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
 	if (data == NULL)
 		return -ENOMEM;
 
-	memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
-	       sizeof(info->entity));
+	memcpy(info->entity, ctrl->entity->guid, sizeof(info->entity));
 	info->index = ctrl->index;
 	info->selector = ctrl->index + 1;
 
@@ -1912,7 +1890,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 
 	if (!found) {
 		uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n",
-			entity->extension.guidExtensionCode, xqry->selector);
+			entity->guid, xqry->selector);
 		return -ENOENT;
 	}
 
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index ddb9eaa11be7..bfbc5a4d4ca6 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1019,6 +1019,11 @@ static int uvc_parse_streaming(struct uvc_device *dev,
 	return ret;
 }
 
+static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
+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, u8 id,
 		unsigned int num_pads, unsigned int extra_size)
 {
@@ -1038,6 +1043,22 @@ 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_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;
@@ -1109,7 +1130,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];
@@ -1368,7 +1389,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];
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index afcaf49fad1a..0a3404091665 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -304,6 +304,7 @@ struct uvc_entity {
 	u8 id;
 	u16 type;
 	char name[64];
+	u8 guid[16];
 
 	/* Media controller-related fields. */
 	struct video_device *vdev;
@@ -342,7 +343,6 @@ struct uvc_entity {
 		} selector;
 
 		struct {
-			u8  guidExtensionCode[16];
 			u8  bNumControls;
 			u8  bControlSize;
 			u8  *bmControls;
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 05/12] media: uvcvideo: Allow entities with no pads
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (2 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 03/12] media: uvcvideo: Move guid to entity Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 06/12] media: uvcvideo: Allow entity-defined get_info and get_cur Ricardo Ribalda
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Avoid an underflow while calculating the number of inputs for entities
with zero pads.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_driver.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 82cdd1bb28dc..c0c5f75ade40 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1033,7 +1033,10 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 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);
@@ -1065,7 +1068,7 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
 
 	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;
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 06/12] media: uvcvideo: Allow entity-defined get_info and get_cur
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (3 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 05/12] media: uvcvideo: Allow entities with no pads Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT Ricardo Ribalda
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Allows controls to get their properties and current value
from an entity-defined function instead of via a query to the USB
device.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 22 ++++++++++++++++++----
 drivers/media/usb/uvc/uvcvideo.h |  5 +++++
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index d7ccd0fcd88d..528254230535 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -980,10 +980,20 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
 		return -EACCES;
 
 	if (!ctrl->loaded) {
-		ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
-				chain->dev->intfnum, ctrl->info.selector,
+		if (ctrl->entity->get_cur) {
+			ret = ctrl->entity->get_cur(chain->dev,
+				ctrl->entity,
+				ctrl->info.selector,
 				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
 				ctrl->info.size);
+		} else {
+			ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+				ctrl->entity->id,
+				chain->dev->intfnum,
+				ctrl->info.selector,
+				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+				ctrl->info.size);
+		}
 		if (ret < 0)
 			return ret;
 
@@ -1716,8 +1726,12 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev,
 	if (data == NULL)
 		return -ENOMEM;
 
-	ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
-			     info->selector, data, 1);
+	if (ctrl->entity->get_info)
+		ret = ctrl->entity->get_info(dev, ctrl->entity,
+					     ctrl->info.selector, data);
+	else
+		ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
+				     dev->intfnum, info->selector, data, 1);
 	if (!ret)
 		info->flags |= (data[0] & UVC_CONTROL_CAP_GET ?
 				UVC_CTRL_FLAG_GET_CUR : 0)
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 5d4da54dfd13..6edbf79b2ff1 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -358,6 +358,11 @@ struct uvc_entity {
 	u8 bNrInPins;
 	u8 *baSourceID;
 
+	int (*get_info)(struct uvc_device *dev, struct uvc_entity *entity,
+			u8 cs, u8 *caps);
+	int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity,
+		       u8 cs, void *data, u16 size);
+
 	unsigned int ncontrols;
 	struct uvc_control *controls;
 };
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (4 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 06/12] media: uvcvideo: Allow entity-defined get_info and get_cur Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-22  8:33   ` Laurent Pinchart
  2020-12-21 16:48 ` [PATCH v5 08/12] media: uvcvideo: Add Privacy control based on EXT_GPIO Ricardo Ribalda
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Some devices can implement a physical switch to disable the input of the
camera on demand. Think of it like an elegant privacy sticker.

The system can read the status of the privacy switch via a GPIO.

It is important to know the status of the switch, e.g. to notify the
user when the camera will produce black frames and a videochat
application is used.

In some systems, the GPIO is connected to main SoC instead of the
camera controller, with the connected reported by the system firmware
(ACPI or DT). In that case, the UVC device isn't aware of the GPIO. We
need to implement a virtual entity to handle the GPIO fully on the
driver side.

For example, for ACPI-based systems, the GPIO is reported in the USB
device object:

  Scope (\_SB.PCI0.XHCI.RHUB.HS07)
  {

	  /.../

    Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
    {
        GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
            "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, ,
            )
            {   // Pin list
                0x0064
            }
    })
    Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
    {
        ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
        Package (0x01)
        {
            Package (0x02)
            {
                "privacy-gpio",
                Package (0x04)
                {
                    \_SB.PCI0.XHCI.RHUB.HS07,
                    Zero,
                    Zero,
                    One
                }
            }
        }
    })
  }

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c   |   7 ++
 drivers/media/usb/uvc/uvc_driver.c | 156 +++++++++++++++++++++++++++++
 drivers/media/usb/uvc/uvc_entity.c |   1 +
 drivers/media/usb/uvc/uvcvideo.h   |  16 +++
 4 files changed, 180 insertions(+)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 528254230535..a430fa666897 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1300,6 +1300,10 @@ static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
 
 	mutex_unlock(&chain->ctrl_mutex);
 
+	/* Events not started by the UVC device. E.g. the GPIO unit */
+	if (!w->urb)
+		return;
+
 	/* Resubmit the URB. */
 	w->urb->interval = dev->int_ep->desc.bInterval;
 	ret = usb_submit_urb(w->urb, GFP_KERNEL);
@@ -2317,6 +2321,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
 		} else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
 			bmControls = entity->camera.bmControls;
 			bControlSize = entity->camera.bControlSize;
+		} else if (UVC_ENTITY_TYPE(entity) == UVC_EXT_GPIO_UNIT) {
+			bmControls = entity->gpio.bmControls;
+			bControlSize = entity->gpio.bControlSize;
 		}
 
 		/* Remove bogus/blacklisted controls */
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index c0c5f75ade40..72516101fdd0 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>
@@ -1020,6 +1021,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
 }
 
 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;
@@ -1051,6 +1053,9 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
 	 * 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;
@@ -1464,6 +1469,137 @@ static int uvc_parse_control(struct uvc_device *dev)
 	return 0;
 }
 
+/* -----------------------------------------------------------------------------
+ * Privacy GPIO
+ */
+
+
+static u8 uvc_gpio_update_value(struct uvc_device *dev,
+				struct uvc_entity *unit)
+{
+	struct uvc_video_chain *chain;
+	u8 gpio_val, old_val, new_val;
+
+	gpio_val = new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
+
+	old_val = atomic_xchg(&unit->gpio.gpio_privacy_value, gpio_val);
+	if (new_val == old_val)
+		return new_val;
+
+	/* GPIO entities are always on the first chain. */
+	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
+	uvc_ctrl_status_event(NULL, chain, unit->controls, &new_val);
+
+	return 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;
+
+	*(uint8_t *)data = uvc_gpio_update_value(dev, entity);
+	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 struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
+{
+	struct uvc_video_chain *chain;
+	struct uvc_entity *unit;
+
+	/* GPIO entities are always on the first chain. */
+	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
+	list_for_each_entry(unit, &chain->entities, chain) {
+		if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
+			return unit;
+	}
+
+	return NULL;
+}
+
+static irqreturn_t uvc_gpio_irq(int irq, void *data)
+{
+	struct uvc_device *dev = data;
+	struct uvc_entity *unit;
+
+	unit = uvc_gpio_find_entity(dev);
+	if (!unit)
+		return IRQ_HANDLED;
+
+	uvc_gpio_update_value(dev, unit);
+	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 == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	if (irq < 0)
+		dev_warn(&dev->udev->dev,
+		       "Unable to find privacy_gpio: %d\n", irq);
+
+	atomic_set(&unit->gpio.gpio_privacy_value, -1);
+	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;
+
+	sprintf(unit->name, "GPIO");
+
+	list_add_tail(&unit->list, &dev->entities);
+
+	return 0;
+}
+
+static int uvc_gpio_init_irq(struct uvc_device *dev)
+{
+	int ret;
+	struct uvc_entity *unit;
+
+	unit = uvc_gpio_find_entity(dev);
+	if (!unit)
+		return 0;
+
+	if (unit->gpio.irq < 0)
+		return 0;
+
+	ret = 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);
+	return ret;
+}
+
 /* ------------------------------------------------------------------------
  * UVC device scan
  */
@@ -1915,6 +2051,7 @@ static int uvc_scan_device(struct uvc_device *dev)
 {
 	struct uvc_video_chain *chain;
 	struct uvc_entity *term;
+	struct uvc_entity *unit;
 
 	list_for_each_entry(term, &dev->entities, list) {
 		if (!UVC_ENTITY_IS_OTERM(term))
@@ -1953,6 +2090,13 @@ static int uvc_scan_device(struct uvc_device *dev)
 		return -1;
 	}
 
+	/* Add GPIO entities to the first chain. */
+	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
+	list_for_each_entry(unit, &dev->entities, list) {
+		if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
+			list_add_tail(&unit->chain, &chain->entities);
+	}
+
 	return 0;
 }
 
@@ -2285,6 +2429,12 @@ static int uvc_probe(struct usb_interface *intf,
 		goto error;
 	}
 
+	/* Parse the associated GPIOs. */
+	if (uvc_gpio_parse(dev) < 0) {
+		uvc_trace(UVC_TRACE_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>",
@@ -2329,6 +2479,12 @@ static int uvc_probe(struct usb_interface *intf,
 			"supported.\n", ret);
 	}
 
+	ret = uvc_gpio_init_irq(dev);
+	if (ret < 0)
+		dev_warn(&dev->udev->dev,
+			 "Unable to request uvc_privacy_gpio irq %d. Continuing wihtout privacy events\n",
+			 ret);
+
 	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
 	usb_enable_autosuspend(udev);
 	return 0;
diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
index ca3a9c2eec27..6a9ba5b498db 100644
--- a/drivers/media/usb/uvc/uvc_entity.c
+++ b/drivers/media/usb/uvc/uvc_entity.c
@@ -105,6 +105,7 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain,
 		case UVC_OTT_DISPLAY:
 		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
 		case UVC_EXTERNAL_VENDOR_SPECIFIC:
+		case UVC_EXT_GPIO_UNIT:
 		default:
 			function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
 			break;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 6edbf79b2ff1..079a407ebba5 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -6,6 +6,7 @@
 #error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
 #endif /* __KERNEL__ */
 
+#include <linux/atomic.h>
 #include <linux/kernel.h>
 #include <linux/poll.h>
 #include <linux/usb.h>
@@ -37,6 +38,8 @@
 	(UVC_ENTITY_IS_TERM(entity) && \
 	((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
 
+#define UVC_EXT_GPIO_UNIT		0x7ffe
+#define UVC_EXT_GPIO_UNIT_ID		0x100
 
 /* ------------------------------------------------------------------------
  * GUIDs
@@ -56,6 +59,9 @@
 #define UVC_GUID_UVC_SELECTOR \
 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
 	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
+#define UVC_GUID_EXT_GPIO_CONTROLLER \
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03}
 
 #define UVC_GUID_FORMAT_MJPEG \
 	{ 'M',  'J',  'P',  'G', 0x00, 0x00, 0x10, 0x00, \
@@ -278,6 +284,8 @@ struct uvc_format_desc {
 	u32 fcc;
 };
 
+struct gpio_desc;
+
 /* The term 'entity' refers to both UVC units and UVC terminals.
  *
  * The type field is either the terminal type (wTerminalType in the terminal
@@ -353,6 +361,14 @@ struct uvc_entity {
 			u8  *bmControls;
 			u8  *bmControlsType;
 		} extension;
+
+		struct {
+			u8  bControlSize;
+			u8  *bmControls;
+			struct gpio_desc *gpio_privacy;
+			int irq;
+			atomic_t gpio_privacy_value;
+		} gpio;
 	};
 
 	u8 bNrInPins;
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 08/12] media: uvcvideo: Add Privacy control based on EXT_GPIO
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (5 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM Ricardo Ribalda
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Add a new control and mapping for Privacy controls connected to
UVC_GUID_EXT_GPIO_CONTROLLERs.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index a430fa666897..374822b1ee94 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -347,6 +347,14 @@ static const struct uvc_control_info uvc_ctrls[] = {
 				| UVC_CTRL_FLAG_RESTORE
 				| UVC_CTRL_FLAG_AUTO_UPDATE,
 	},
+	{
+		.entity		= UVC_GUID_EXT_GPIO_CONTROLLER,
+		.selector	= UVC_CT_PRIVACY_CONTROL,
+		.index		= 0,
+		.size		= 1,
+		.flags		= UVC_CTRL_FLAG_GET_CUR
+				| UVC_CTRL_FLAG_AUTO_UPDATE,
+	},
 };
 
 static const struct uvc_menu_info power_line_frequency_controls[] = {
@@ -735,6 +743,16 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
 	},
+	{
+		.id		= V4L2_CID_PRIVACY,
+		.name		= "Privacy",
+		.entity		= UVC_GUID_EXT_GPIO_CONTROLLER,
+		.selector	= UVC_CT_PRIVACY_CONTROL,
+		.size		= 1,
+		.offset		= 0,
+		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
+	},
 };
 
 /* ------------------------------------------------------------------------
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (6 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 08/12] media: uvcvideo: Add Privacy control based on EXT_GPIO Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-22 10:29   ` Laurent Pinchart
  2020-12-21 16:48 ` [PATCH v5 10/12] media: uvcvideo: Use dev_ printk aliases Ricardo Ribalda
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Some devices, can only read the privacy_pin if the device is
streaming.

This patch implement a quirk for such devices, in order to avoid invalid
reads and/or spurious events.

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++++++++++++++++--
 drivers/media/usb/uvc/uvc_queue.c  |  3 ++
 drivers/media/usb/uvc/uvcvideo.h   |  4 +++
 3 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 72516101fdd0..7af37d4bd60a 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/dmi.h>
 #include <linux/gpio/consumer.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
@@ -1472,6 +1473,17 @@ static int uvc_parse_control(struct uvc_device *dev)
 /* -----------------------------------------------------------------------------
  * Privacy GPIO
  */
+static bool uvc_gpio_is_streaming(struct uvc_device *dev)
+{
+	struct uvc_streaming *streaming;
+
+	list_for_each_entry(streaming, &dev->streams, list) {
+		if (uvc_queue_streaming(&streaming->queue))
+			return true;
+	}
+
+	return false;
+}
 
 
 static u8 uvc_gpio_update_value(struct uvc_device *dev,
@@ -1499,7 +1511,12 @@ static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
 	if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
 		return -EINVAL;
 
+	if ((dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM) &&
+	    !uvc_gpio_is_streaming(dev))
+		return -EBUSY;
+
 	*(uint8_t *)data = uvc_gpio_update_value(dev, entity);
+
 	return 0;
 }
 
@@ -1528,19 +1545,50 @@ static struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
 	return NULL;
 }
 
-static irqreturn_t uvc_gpio_irq(int irq, void *data)
+void uvc_privacy_gpio_event(struct uvc_device *dev)
 {
-	struct uvc_device *dev = data;
 	struct uvc_entity *unit;
 
+
 	unit = uvc_gpio_find_entity(dev);
 	if (!unit)
-		return IRQ_HANDLED;
+		return;
 
 	uvc_gpio_update_value(dev, unit);
+}
+
+static irqreturn_t uvc_gpio_irq(int irq, void *data)
+{
+	struct uvc_device *dev = data;
+
+	/* Ignore privacy events during streamoff */
+	if (dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
+		if (!uvc_gpio_is_streaming(dev))
+			return IRQ_HANDLED;
+
+	uvc_privacy_gpio_event(dev);
+
 	return IRQ_HANDLED;
 }
 
+static const struct dmi_system_id privacy_valid_during_streamon[] = {
+	{
+		.ident = "HP Elite c1030 Chromebook",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Jinlon"),
+		},
+	},
+	{
+		.ident = "HP Pro c640 Chromebook",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Dratini"),
+		},
+	},
+	{ } /* terminate list */
+};
+
 static int uvc_gpio_parse(struct uvc_device *dev)
 {
 	struct uvc_entity *unit;
@@ -1577,6 +1625,9 @@ static int uvc_gpio_parse(struct uvc_device *dev)
 
 	list_add_tail(&unit->list, &dev->entities);
 
+	if (dmi_check_system(privacy_valid_during_streamon))
+		dev->quirks |= UVC_QUIRK_PRIVACY_DURING_STREAM;
+
 	return 0;
 }
 
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cd60c6c1749e..e800d491303f 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -337,9 +337,12 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
 int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
 {
 	int ret;
+	struct uvc_streaming *stream = uvc_queue_to_stream(queue);
 
 	mutex_lock(&queue->mutex);
 	ret = vb2_streamon(&queue->queue, type);
+	if (stream->dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
+		uvc_privacy_gpio_event(stream->dev);
 	mutex_unlock(&queue->mutex);
 
 	return ret;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 079a407ebba5..32c1ba246d97 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -209,6 +209,7 @@
 #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT	0x00000400
 #define UVC_QUIRK_FORCE_Y8		0x00000800
 #define UVC_QUIRK_FORCE_BPP		0x00001000
+#define UVC_QUIRK_PRIVACY_DURING_STREAM	0x00002000
 
 /* Format flags */
 #define UVC_FMT_FLAG_COMPRESSED		0x00000001
@@ -826,6 +827,9 @@ extern const struct v4l2_file_operations uvc_fops;
 int uvc_mc_register_entities(struct uvc_video_chain *chain);
 void uvc_mc_cleanup_entity(struct uvc_entity *entity);
 
+/* Privacy gpio */
+void uvc_privacy_gpio_event(struct uvc_device *dev);
+
 /* Video */
 int uvc_video_init(struct uvc_streaming *stream);
 int uvc_video_suspend(struct uvc_streaming *stream);
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 10/12] media: uvcvideo: Use dev_ printk aliases
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (7 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 11/12] media: uvcvideo: New macro uvc_trace_cont Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 12/12] media: uvcvideo: use dev_dbg() for uvc_trace() Ricardo Ribalda
  10 siblings, 0 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda, Joe Perches

Replace all the uses of printk() and uvc_printk() with its
equivalent dev_ alias macros.

Modify uvc_warn_once() macro to use dev_info instead printk().

They are more standard across the kernel tree and provide
more context about the error.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Suggested-by: Joe Perches <joe@perches.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c   | 12 +++----
 drivers/media/usb/uvc/uvc_driver.c | 50 ++++++++++++++++--------------
 drivers/media/usb/uvc/uvc_entity.c | 10 +++---
 drivers/media/usb/uvc/uvc_status.c | 13 ++++----
 drivers/media/usb/uvc/uvc_video.c  | 48 ++++++++++++++--------------
 drivers/media/usb/uvc/uvcvideo.h   | 25 +++++++--------
 6 files changed, 81 insertions(+), 77 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 374822b1ee94..1f1323a649fb 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1326,8 +1326,8 @@ static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
 	w->urb->interval = dev->int_ep->desc.bInterval;
 	ret = usb_submit_urb(w->urb, GFP_KERNEL);
 	if (ret < 0)
-		uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
-			   ret);
+		dev_err(&dev->udev->dev,
+			"Failed to resubmit status URB (%d).\n", ret);
 }
 
 static void uvc_ctrl_status_event_work(struct work_struct *work)
@@ -2038,10 +2038,10 @@ int uvc_ctrl_restore_values(struct uvc_device *dev)
 			if (!ctrl->initialized || !ctrl->modified ||
 			    (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0)
 				continue;
-
-			printk(KERN_INFO "restoring control %pUl/%u/%u\n",
-				ctrl->info.entity, ctrl->info.index,
-				ctrl->info.selector);
+			dev_info(&dev->udev->dev,
+				 "restoring control %pUl/%u/%u\n",
+				 ctrl->info.entity, ctrl->info.index,
+				 ctrl->info.selector);
 			ctrl->dirty = 1;
 		}
 
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 7af37d4bd60a..c98d57680e5c 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -536,8 +536,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;
@@ -2137,7 +2137,7 @@ 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;
 	}
 
@@ -2296,8 +2296,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;
 	}
 
@@ -2313,8 +2314,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;
 	}
 
@@ -2348,8 +2349,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;
 		}
 
@@ -2382,8 +2384,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
 	}
 
@@ -2486,17 +2488,18 @@ static int uvc_probe(struct usb_interface *intf,
 		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");
 	}
 
 	/* Register the V4L2 device. */
@@ -2525,9 +2528,9 @@ 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);
 	}
 
 	ret = uvc_gpio_init_irq(dev);
@@ -3233,7 +3236,6 @@ static int __init uvc_init(void)
 		return ret;
 	}
 
-	printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
 	return 0;
 }
 
diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
index 6a9ba5b498db..7c4d2f93d351 100644
--- a/drivers/media/usb/uvc/uvc_entity.c
+++ b/drivers/media/usb/uvc/uvc_entity.c
@@ -140,8 +140,9 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain)
 	list_for_each_entry(entity, &chain->entities, chain) {
 		ret = uvc_mc_init_entity(chain, entity);
 		if (ret < 0) {
-			uvc_printk(KERN_INFO, "Failed to initialize entity for "
-				   "entity %u\n", entity->id);
+			dev_info(&chain->dev->udev->dev,
+				 "Failed to initialize entity for entity %u\n",
+				 entity->id);
 			return ret;
 		}
 	}
@@ -149,8 +150,9 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain)
 	list_for_each_entry(entity, &chain->entities, chain) {
 		ret = uvc_mc_create_links(chain, entity);
 		if (ret < 0) {
-			uvc_printk(KERN_INFO, "Failed to create links for "
-				   "entity %u\n", entity->id);
+			dev_info(&chain->dev->udev->dev,
+				 "Failed to create links for entity %u\n",
+				 entity->id);
 			return ret;
 		}
 	}
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index 2bdb0ff203f8..36fa196a9258 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -208,8 +208,9 @@ static void uvc_status_complete(struct urb *urb)
 		return;
 
 	default:
-		uvc_printk(KERN_WARNING, "Non-zero status (%d) in status "
-			"completion handler.\n", urb->status);
+		dev_warn(&dev->udev->dev,
+			 "Non-zero status (%d) in status completion handler.\n",
+			 urb->status);
 		return;
 	}
 
@@ -243,10 +244,10 @@ static void uvc_status_complete(struct urb *urb)
 
 	/* Resubmit the URB. */
 	urb->interval = dev->int_ep->desc.bInterval;
-	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-		uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
-			ret);
-	}
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret < 0)
+		dev_err(&dev->udev->dev,
+			"Failed to resubmit status URB (%d).\n", ret);
 }
 
 int uvc_status_init(struct uvc_device *dev)
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index a6a441d92b94..71e643e6d006 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -76,9 +76,9 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
 	if (likely(ret == size))
 		return 0;
 
-	uvc_printk(KERN_ERR,
-		   "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n",
-		   uvc_query_name(query), cs, unit, ret, size);
+	dev_err(&dev->udev->dev,
+		"Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n",
+		uvc_query_name(query), cs, unit, ret, size);
 
 	if (ret != -EPIPE)
 		return ret;
@@ -254,9 +254,9 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
 		ret = -EIO;
 		goto out;
 	} else if (ret != size) {
-		uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : "
-			"%d (exp. %u).\n", query, probe ? "probe" : "commit",
-			ret, size);
+		dev_err(&stream->intf->dev,
+			"Failed to query (%u) UVC %s control : %d (exp. %u).\n",
+			query, probe ? "probe" : "commit", ret, size);
 		ret = -EIO;
 		goto out;
 	}
@@ -334,9 +334,9 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream,
 		probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
 		size, uvc_timeout_param);
 	if (ret != size) {
-		uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
-			"%d (exp. %u).\n", probe ? "probe" : "commit",
-			ret, size);
+		dev_err(&stream->intf->dev,
+			"Failed to set UVC %s control : %d (exp. %u).\n",
+			probe ? "probe" : "commit", ret, size);
 		ret = -EIO;
 	}
 
@@ -1120,8 +1120,8 @@ static void uvc_video_copy_data_work(struct work_struct *work)
 
 	ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL);
 	if (ret < 0)
-		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
-			   ret);
+		dev_err(&uvc_urb->stream->intf->dev,
+			"Failed to resubmit video URB (%d).\n", ret);
 }
 
 static void uvc_video_decode_data(struct uvc_urb *uvc_urb,
@@ -1507,8 +1507,9 @@ static void uvc_video_complete(struct urb *urb)
 		break;
 
 	default:
-		uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
-			"completion handler.\n", urb->status);
+		dev_warn(&stream->intf->dev,
+			 "Non-zero status (%d) in video completion handler.\n",
+			 urb->status);
 		fallthrough;
 	case -ENOENT:		/* usb_poison_urb() called. */
 		if (stream->frozen)
@@ -1545,9 +1546,8 @@ static void uvc_video_complete(struct urb *urb)
 	if (!uvc_urb->async_operations) {
 		ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC);
 		if (ret < 0)
-			uvc_printk(KERN_ERR,
-				   "Failed to resubmit video URB (%d).\n",
-				   ret);
+			dev_err(&stream->intf->dev,
+				"Failed to resubmit video URB (%d).\n", ret);
 		return;
 	}
 
@@ -1893,8 +1893,9 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
 	for_each_uvc_urb(uvc_urb, stream) {
 		ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
 		if (ret < 0) {
-			uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n",
-				   uvc_urb_index(uvc_urb), ret);
+			dev_err(&stream->intf->dev,
+				"Failed to submit URB %u (%d).\n",
+				uvc_urb_index(uvc_urb), ret);
 			uvc_video_stop_transfer(stream, 1);
 			return ret;
 		}
@@ -1989,7 +1990,8 @@ int uvc_video_init(struct uvc_streaming *stream)
 	int ret;
 
 	if (stream->nformats == 0) {
-		uvc_printk(KERN_INFO, "No supported video formats found.\n");
+		dev_info(&stream->intf->dev,
+			 "No supported video formats found.\n");
 		return -EINVAL;
 	}
 
@@ -2029,8 +2031,8 @@ int uvc_video_init(struct uvc_streaming *stream)
 	}
 
 	if (format->nframes == 0) {
-		uvc_printk(KERN_INFO, "No frame descriptor found for the "
-			"default format.\n");
+		dev_info(&stream->intf->dev,
+			 "No frame descriptor found for the default format.\n");
 		return -EINVAL;
 	}
 
@@ -2064,8 +2066,8 @@ int uvc_video_init(struct uvc_streaming *stream)
 		if (stream->intf->num_altsetting == 1)
 			stream->decode = uvc_video_encode_bulk;
 		else {
-			uvc_printk(KERN_INFO, "Isochronous endpoints are not "
-				"supported for video output devices.\n");
+			dev_info(&stream->intf->dev,
+				 "Isochronous endpoints are not supported for video output devices.\n");
 			return -EINVAL;
 		}
 	}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 32c1ba246d97..a4ebd56bb987 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -757,20 +757,17 @@ extern unsigned int uvc_trace_param;
 extern unsigned int uvc_timeout_param;
 extern unsigned int uvc_hw_timestamps_param;
 
-#define uvc_trace(flag, msg...) \
-	do { \
-		if (uvc_trace_param & flag) \
-			printk(KERN_DEBUG "uvcvideo: " msg); \
-	} while (0)
-
-#define uvc_warn_once(dev, warn, msg...) \
-	do { \
-		if (!test_and_set_bit(warn, &dev->warnings)) \
-			printk(KERN_INFO "uvcvideo: " msg); \
-	} while (0)
-
-#define uvc_printk(level, msg...) \
-	printk(level "uvcvideo: " msg)
+#define uvc_trace(flag, fmt, ...)					\
+do {									\
+	if (uvc_trace_param & flag)					\
+		printk(KERN_DEBUG "uvcvideo: " fmt, ##__VA_ARGS__);	\
+} while (0)
+
+#define uvc_warn_once(_dev, warn, fmt, ...)				\
+do {									\
+	if (!test_and_set_bit(warn, &(_dev)->warnings))			\
+		dev_info(&(_dev)->udev->dev, fmt, ##__VA_ARGS__);	\
+} while (0)
 
 /* --------------------------------------------------------------------------
  * Internal functions.
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 11/12] media: uvcvideo: New macro uvc_trace_cont
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (8 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 10/12] media: uvcvideo: Use dev_ printk aliases Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-21 16:48 ` [PATCH v5 12/12] media: uvcvideo: use dev_dbg() for uvc_trace() Ricardo Ribalda
  10 siblings, 0 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda, Joe Perches

Remove all the duplicated code around printk(KERN_CONT, with a new macro.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Suggested-by: Joe Perches <joe@perches.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_driver.c | 57 +++++++++++-------------------
 drivers/media/usb/uvc/uvcvideo.h   |  6 ++++
 2 files changed, 27 insertions(+), 36 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index c98d57680e5c..ea64c716f1f3 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1686,8 +1686,7 @@ 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_trace_cont(UVC_TRACE_PROBE, " <- XU %d", entity->id);
 
 		if (entity->bNrInPins != 1) {
 			uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
@@ -1698,8 +1697,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
 		break;
 
 	case UVC_VC_PROCESSING_UNIT:
-		if (uvc_trace_param & UVC_TRACE_PROBE)
-			printk(KERN_CONT " <- PU %d", entity->id);
+		uvc_trace_cont(UVC_TRACE_PROBE, " <- PU %d", entity->id);
 
 		if (chain->processing != NULL) {
 			uvc_trace(UVC_TRACE_DESCR, "Found multiple "
@@ -1711,8 +1709,7 @@ 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_trace_cont(UVC_TRACE_PROBE, " <- SU %d", entity->id);
 
 		/* Single-input selector units are ignored. */
 		if (entity->bNrInPins == 1)
@@ -1730,27 +1727,22 @@ 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_trace_cont(UVC_TRACE_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_trace_cont(UVC_TRACE_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_trace_cont(UVC_TRACE_PROBE, " <- IT %d\n", entity->id);
+		else
+			uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", entity->id);
 
 		break;
 
@@ -1797,13 +1789,11 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
 			}
 
 			list_add_tail(&forward->chain, &chain->entities);
-			if (uvc_trace_param & UVC_TRACE_PROBE) {
-				if (!found)
-					printk(KERN_CONT " (->");
+			if (!found)
+				uvc_trace_cont(UVC_TRACE_PROBE, " (->");
 
-				printk(KERN_CONT " XU %d", forward->id);
-				found = 1;
-			}
+			uvc_trace_cont(UVC_TRACE_PROBE, " XU %d", forward->id);
+			found = 1;
 			break;
 
 		case UVC_OTT_VENDOR_SPECIFIC:
@@ -1817,18 +1807,16 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
 			}
 
 			list_add_tail(&forward->chain, &chain->entities);
-			if (uvc_trace_param & UVC_TRACE_PROBE) {
-				if (!found)
-					printk(KERN_CONT " (->");
+			if (!found)
+				uvc_trace_cont(UVC_TRACE_PROBE, " (->");
 
-				printk(KERN_CONT " OT %d", forward->id);
-				found = 1;
-			}
+			uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", forward->id);
+			found = 1;
 			break;
 		}
 	}
 	if (found)
-		printk(KERN_CONT ")");
+		uvc_trace_cont(UVC_TRACE_PROBE, ")");
 
 	return 0;
 }
@@ -1853,8 +1841,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
 			break;
 		}
 
-		if (uvc_trace_param & UVC_TRACE_PROBE)
-			printk(KERN_CONT " <- IT");
+		uvc_trace_cont(UVC_TRACE_PROBE, " <- IT");
 
 		chain->selector = entity;
 		for (i = 0; i < entity->bNrInPins; ++i) {
@@ -1874,15 +1861,13 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
 				return -EINVAL;
 			}
 
-			if (uvc_trace_param & UVC_TRACE_PROBE)
-				printk(KERN_CONT " %d", term->id);
+			uvc_trace_cont(UVC_TRACE_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_trace_cont(UVC_TRACE_PROBE, "\n");
 
 		id = 0;
 		break;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index a4ebd56bb987..4a7d1fb76eee 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -763,6 +763,12 @@ do {									\
 		printk(KERN_DEBUG "uvcvideo: " fmt, ##__VA_ARGS__);	\
 } while (0)
 
+#define uvc_trace_cont(flag, fmt, ...)					\
+do {									\
+	if (uvc_trace_param & flag)					\
+		pr_cont(fmt, ##__VA_ARGS__);				\
+} while (0)
+
 #define uvc_warn_once(_dev, warn, fmt, ...)				\
 do {									\
 	if (!test_and_set_bit(warn, &(_dev)->warnings))			\
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 12/12] media: uvcvideo: use dev_dbg() for uvc_trace()
  2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
                   ` (9 preceding siblings ...)
  2020-12-21 16:48 ` [PATCH v5 11/12] media: uvcvideo: New macro uvc_trace_cont Ricardo Ribalda
@ 2020-12-21 16:48 ` Ricardo Ribalda
  2020-12-22  8:51   ` Laurent Pinchart
  10 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 16:48 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Instead of calling prink() inside uvc_trace, use dev_dbg(), which add
context to the output.

Now that we are at it, regroup the strings so the messages can be easily
"grepable".

Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c   |  66 ++++---
 drivers/media/usb/uvc/uvc_driver.c | 298 +++++++++++++++--------------
 drivers/media/usb/uvc/uvc_isight.c |  16 +-
 drivers/media/usb/uvc/uvc_queue.c  |   9 +-
 drivers/media/usb/uvc/uvc_status.c |  19 +-
 drivers/media/usb/uvc/uvc_v4l2.c   |  53 +++--
 drivers/media/usb/uvc/uvc_video.c  |  72 +++----
 drivers/media/usb/uvc/uvcvideo.h   |  11 +-
 8 files changed, 291 insertions(+), 253 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 1f1323a649fb..febe20e2887d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -906,8 +906,8 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
 	}
 
 	if (ctrl == NULL && !next)
-		uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n",
-				v4l2_id);
+		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
+			  "Control 0x%08x not found.\n", v4l2_id);
 
 	return ctrl;
 }
@@ -1828,9 +1828,9 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
 	ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
 			     info->selector, data, 2);
 	if (ret < 0) {
-		uvc_trace(UVC_TRACE_CONTROL,
+		uvc_trace(dev, UVC_TRACE_CONTROL,
 			  "GET_LEN failed on control %pUl/%u (%d).\n",
-			   info->entity, info->selector, ret);
+			  info->entity, info->selector, ret);
 		goto done;
 	}
 
@@ -1841,7 +1841,7 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
 
 	ret = uvc_ctrl_get_flags(dev, ctrl, info);
 	if (ret < 0) {
-		uvc_trace(UVC_TRACE_CONTROL,
+		uvc_trace(dev, UVC_TRACE_CONTROL,
 			  "Failed to get flags for control %pUl/%u (%d).\n",
 			  info->entity, info->selector, ret);
 		goto done;
@@ -1849,8 +1849,8 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
 
 	uvc_ctrl_fixup_xu_info(dev, ctrl, info);
 
-	uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
-		  "flags { get %u set %u auto %u }.\n",
+	uvc_trace(dev, UVC_TRACE_CONTROL,
+		  "XU control %pUl/%u queried: len %u, flags { get %u set %u auto %u }.\n",
 		  info->entity, info->selector, info->size,
 		  (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0,
 		  (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0,
@@ -1879,9 +1879,10 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
 
 	ret = uvc_ctrl_add_info(dev, ctrl, &info);
 	if (ret < 0)
-		uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control "
-			  "%pUl/%u on device %s entity %u\n", info.entity,
-			  info.selector, dev->udev->devpath, ctrl->entity->id);
+		uvc_trace(dev, UVC_TRACE_CONTROL,
+			  "Failed to initialize control %pUl/%u on device %s entity %u\n",
+			  info.entity, info.selector, dev->udev->devpath,
+			  ctrl->entity->id);
 
 	return ret;
 }
@@ -1909,8 +1910,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 	}
 
 	if (!found) {
-		uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
-			xqry->unit);
+		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
+			  "Extension unit %u not found.\n", xqry->unit);
 		return -ENOENT;
 	}
 
@@ -1925,8 +1926,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 	}
 
 	if (!found) {
-		uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n",
-			entity->guid, xqry->selector);
+		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
+			  "Control %pUl/%u not found.\n", entity->guid,
+			  xqry->selector);
 		return -ENOENT;
 	}
 
@@ -2074,9 +2076,10 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
 
 	ctrl->initialized = 1;
 
-	uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
-		"entity %u\n", ctrl->info.entity, ctrl->info.selector,
-		dev->udev->devpath, ctrl->entity->id);
+	uvc_trace(dev, UVC_TRACE_CONTROL,
+		  "Added control %pUl/%u to device %s entity %u\n",
+		  ctrl->info.entity, ctrl->info.selector, dev->udev->devpath,
+		  ctrl->entity->id);
 
 	return 0;
 }
@@ -2113,9 +2116,9 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
 		map->set = uvc_set_le_value;
 
 	list_add_tail(&map->list, &ctrl->info.mappings);
-	uvc_trace(UVC_TRACE_CONTROL,
-		"Adding mapping '%s' to control %pUl/%u.\n",
-		map->name, ctrl->info.entity, ctrl->info.selector);
+	uvc_trace(dev, UVC_TRACE_CONTROL,
+		  "Adding mapping '%s' to control %pUl/%u.\n",
+		  map->name, ctrl->info.entity, ctrl->info.selector);
 
 	return 0;
 }
@@ -2131,9 +2134,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 	int ret;
 
 	if (mapping->id & ~V4L2_CTRL_ID_MASK) {
-		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
-			"id 0x%08x is invalid.\n", mapping->name,
-			mapping->id);
+		uvc_trace(dev, UVC_TRACE_CONTROL,
+			  "Can't add mapping '%s', control id 0x%08x is invalid.\n",
+			  mapping->name, mapping->id);
 		return -EINVAL;
 	}
 
@@ -2178,9 +2181,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 
 	list_for_each_entry(map, &ctrl->info.mappings, list) {
 		if (mapping->id == map->id) {
-			uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
-				"control id 0x%08x already exists.\n",
-				mapping->name, mapping->id);
+			uvc_trace(dev, UVC_TRACE_CONTROL,
+				  "Can't add mapping '%s', control id 0x%08x already exists.\n",
+				  mapping->name, mapping->id);
 			ret = -EEXIST;
 			goto done;
 		}
@@ -2189,9 +2192,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 	/* Prevent excess memory consumption */
 	if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) {
 		atomic_dec(&dev->nmappings);
-		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', maximum "
-			"mappings count (%u) exceeded.\n", mapping->name,
-			UVC_MAX_CONTROL_MAPPINGS);
+		uvc_trace(dev, UVC_TRACE_CONTROL,
+			  "Can't add mapping '%s', maximum mappings count (%u) exceeded.\n",
+			  mapping->name, UVC_MAX_CONTROL_MAPPINGS);
 		ret = -ENOMEM;
 		goto done;
 	}
@@ -2260,8 +2263,9 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
 		    !uvc_test_bit(controls, blacklist[i].index))
 			continue;
 
-		uvc_trace(UVC_TRACE_CONTROL, "%u/%u control is black listed, "
-			"removing it.\n", entity->id, blacklist[i].index);
+		uvc_trace(dev, UVC_TRACE_CONTROL,
+			  "%u/%u control is black listed, removing it.\n",
+			  entity->id, blacklist[i].index);
 
 		uvc_clear_bit(controls, blacklist[i].index);
 	}
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index ea64c716f1f3..f857bb9b7755 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -521,10 +521,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_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videostreaming interface %d FORMAT error\n",
+				  dev->udev->devnum,
+				  alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -585,10 +585,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_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videostreaming interface %d FORMAT error\n",
+				  dev->udev->devnum,
+				  alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -601,10 +601,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_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videostreaming interface %d FORMAT error\n",
+				  dev->udev->devnum,
+				  alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -619,9 +619,9 @@ 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,
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videostreaming interface %d: unknown DV format %u\n",
+				  dev->udev->devnum,
 			       alts->desc.bInterfaceNumber, buffer[8]);
 			return -EINVAL;
 		}
@@ -648,14 +648,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_trace(dev, UVC_TRACE_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_trace(dev, UVC_TRACE_DESCR, "Found format %s.\n", format->name);
 
 	buflen -= buffer[0];
 	buffer += buffer[0];
@@ -674,9 +674,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_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videostreaming interface %d FRAME error\n",
+				  dev->udev->devnum,
+				  alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -738,10 +739,10 @@ static int uvc_parse_format(struct uvc_device *dev,
 				frame->dwDefaultFrameInterval;
 		}
 
-		uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
-			frame->wWidth, frame->wHeight,
-			10000000/frame->dwDefaultFrameInterval,
-			(100000000/frame->dwDefaultFrameInterval)%10);
+		uvc_trace(dev, UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
+			  frame->wWidth, frame->wHeight,
+			  10000000 / frame->dwDefaultFrameInterval,
+			  (100000000 / frame->dwDefaultFrameInterval) % 10);
 
 		format->nframes++;
 		buflen -= buffer[0];
@@ -757,10 +758,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_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videostreaming interface %d COLORFORMAT error\n",
+				  dev->udev->devnum,
+				  alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -792,16 +793,18 @@ 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,
-			intf->altsetting[0].desc.bInterfaceNumber);
+		uvc_trace(dev, UVC_TRACE_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,
-			intf->altsetting[0].desc.bInterfaceNumber);
+		uvc_trace(dev, UVC_TRACE_DESCR,
+			  "device %d interface %d is already claimed\n",
+			  dev->udev->devnum,
+			  intf->altsetting[0].desc.bInterfaceNumber);
 		return -EINVAL;
 	}
 
@@ -823,8 +826,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_trace(dev, UVC_TRACE_DESCR,
+					  "trying extra data from endpoint %u.\n",
+					  i);
 				buffer = alts->endpoint[i].extra;
 				buflen = alts->endpoint[i].extralen;
 				break;
@@ -839,8 +843,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_trace(dev, UVC_TRACE_DESCR,
+			  "no class-specific streaming interface descriptors found.\n");
 		goto error;
 	}
 
@@ -857,9 +861,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_trace(dev, UVC_TRACE_DESCR,
+			  "device %d videostreaming interface %d HEADER descriptor not found.\n",
+			  dev->udev->devnum, alts->desc.bInterfaceNumber);
 		goto error;
 	}
 
@@ -867,9 +871,9 @@ 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",
-			dev->udev->devnum, alts->desc.bInterfaceNumber);
+		uvc_trace(dev, UVC_TRACE_DESCR,
+			  "device %d videostreaming interface %d HEADER descriptor is invalid.\n",
+			  dev->udev->devnum, alts->desc.bInterfaceNumber);
 		goto error;
 	}
 
@@ -919,10 +923,10 @@ 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",
-				dev->udev->devnum,
-				alts->desc.bInterfaceNumber, _buffer[2]);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videostreaming interface %d FORMAT %u is not supported.\n",
+				  dev->udev->devnum,
+				  alts->desc.bInterfaceNumber, _buffer[2]);
 			break;
 
 		case UVC_VS_FRAME_UNCOMPRESSED:
@@ -944,9 +948,9 @@ 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",
-			dev->udev->devnum, alts->desc.bInterfaceNumber);
+		uvc_trace(dev, UVC_TRACE_DESCR,
+			  "device %d videostreaming interface %d has no supported formats defined.\n",
+			  dev->udev->devnum, alts->desc.bInterfaceNumber);
 		goto error;
 	}
 
@@ -993,9 +997,10 @@ 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",
-			dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
+		uvc_trace(dev, UVC_TRACE_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. */
 	for (i = 0; i < intf->num_altsetting; ++i) {
@@ -1128,9 +1133,9 @@ 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",
-				udev->devnum, alts->desc.bInterfaceNumber);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d EXTENSION_UNIT error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			break;
 		}
 
@@ -1177,9 +1182,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_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d HEADER error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -1190,9 +1195,9 @@ 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",
-					udev->devnum, i);
+				uvc_trace(dev, UVC_TRACE_DESCR,
+					  "device %d interface %d doesn't exists\n",
+					  udev->devnum, i);
 				continue;
 			}
 
@@ -1202,9 +1207,9 @@ 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",
-				udev->devnum, alts->desc.bInterfaceNumber);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d INPUT_TERMINAL error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -1221,11 +1226,10 @@ 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,
-				buffer[3], type);
+			uvc_trace(dev, UVC_TRACE_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;
 		}
 
@@ -1244,9 +1248,9 @@ 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",
-				udev->devnum, alts->desc.bInterfaceNumber);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d INPUT_TERMINAL error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -1291,9 +1295,9 @@ 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",
-				udev->devnum, alts->desc.bInterfaceNumber);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d OUTPUT_TERMINAL error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -1302,10 +1306,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_trace(dev, 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);
 			return 0;
 		}
 
@@ -1329,9 +1333,9 @@ 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",
-				udev->devnum, alts->desc.bInterfaceNumber);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d SELECTOR_UNIT error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -1355,9 +1359,9 @@ 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",
-				udev->devnum, alts->desc.bInterfaceNumber);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d PROCESSING_UNIT error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -1388,9 +1392,9 @@ 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",
-				udev->devnum, alts->desc.bInterfaceNumber);
+			uvc_trace(dev, UVC_TRACE_DESCR,
+				  "device %d videocontrol interface %d EXTENSION_UNIT error\n",
+				  udev->devnum, alts->desc.bInterfaceNumber);
 			return -EINVAL;
 		}
 
@@ -1415,8 +1419,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_trace(dev, UVC_TRACE_DESCR,
+			  "Found an unknown CS_INTERFACE descriptor (%u)\n",
+			  buffer[2]);
 		break;
 	}
 
@@ -1461,8 +1466,9 @@ static int uvc_parse_control(struct uvc_device *dev)
 		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_trace(dev, UVC_TRACE_DESCR,
+				  "Found a Status endpoint (addr %02x).\n",
+				  desc->bEndpointAddress);
 			dev->int_ep = ep;
 		}
 	}
@@ -1689,8 +1695,9 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
 		uvc_trace_cont(UVC_TRACE_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_trace(chain->dev, UVC_TRACE_DESCR,
+				  "Extension unit %d has more than 1 input pin.\n",
+				  entity->id);
 			return -1;
 		}
 
@@ -1700,8 +1707,8 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
 		uvc_trace_cont(UVC_TRACE_PROBE, " <- PU %d", entity->id);
 
 		if (chain->processing != NULL) {
-			uvc_trace(UVC_TRACE_DESCR, "Found multiple "
-				"Processing Units in chain.\n");
+			uvc_trace(chain->dev, UVC_TRACE_DESCR,
+				  "Found multiple Processing Units in chain.\n");
 			return -1;
 		}
 
@@ -1716,8 +1723,8 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
 			break;
 
 		if (chain->selector != NULL) {
-			uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
-				"Units in chain.\n");
+			uvc_trace(chain->dev, UVC_TRACE_DESCR,
+				  "Found multiple Selector Units in chain.\n");
 			return -1;
 		}
 
@@ -1747,8 +1754,9 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
 		break;
 
 	default:
-		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
-			"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
+		uvc_trace(chain->dev, UVC_TRACE_DESCR,
+			  "Unsupported entity type 0x%04x found in chain.\n",
+			  UVC_ENTITY_TYPE(entity));
 		return -1;
 	}
 
@@ -1774,16 +1782,17 @@ 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_trace(chain->dev, UVC_TRACE_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",
+				uvc_trace(chain->dev, UVC_TRACE_DESCR,
+					  "Extension unit %d has more than 1 input pin.\n",
 					  entity->id);
 				return -EINVAL;
 			}
@@ -1801,8 +1810,9 @@ 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_trace(chain->dev, UVC_TRACE_DESCR,
+					  "Unsupported input terminal %u.\n",
+					  forward->id);
 				return -EINVAL;
 			}
 
@@ -1848,16 +1858,16 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
 			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_trace(chain->dev, UVC_TRACE_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",
-					term->id);
+				uvc_trace(chain->dev, UVC_TRACE_DESCR,
+					  "Found reference to entity %d already in chain.\n",
+					  term->id);
 				return -EINVAL;
 			}
 
@@ -1890,8 +1900,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_trace(chain->dev, UVC_TRACE_DESCR,
+			  "Found reference to unknown entity %d.\n", id);
 		return -EINVAL;
 	}
 
@@ -1904,7 +1914,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_trace(chain->dev, UVC_TRACE_PROBE, "Scanning UVC chain:");
 
 	entity = term;
 	prev = NULL;
@@ -1912,8 +1922,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_trace(chain->dev, UVC_TRACE_DESCR,
+				  "Found reference to entity %d already in chain.\n",
+				  entity->id);
 			return -EINVAL;
 		}
 
@@ -2067,7 +2078,7 @@ static int uvc_scan_fallback(struct uvc_device *dev)
 
 	list_add_tail(&chain->list, &dev->chains);
 
-	uvc_trace(UVC_TRACE_PROBE,
+	uvc_trace(dev, UVC_TRACE_PROBE,
 		  "Found a video chain by fallback heuristic (%s).\n",
 		  uvc_print_chain(chain));
 
@@ -2112,7 +2123,8 @@ static int uvc_scan_device(struct uvc_device *dev)
 			continue;
 		}
 
-		uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
+		uvc_trace(dev, UVC_TRACE_PROBE,
+			  "Found a valid video chain (%s).\n",
 			  uvc_print_chain(chain));
 
 		list_add_tail(&chain->list, &dev->chains);
@@ -2393,14 +2405,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)
@@ -2420,6 +2424,14 @@ 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_trace(dev, UVC_TRACE_PROBE,
+			  "Probing known UVC device %s (%04x:%04x)\n",
+			  udev->devpath, id->idVendor, id->idProduct);
+	else
+		uvc_trace(dev, UVC_TRACE_PROBE,
+			  "Probing generic UVC device %s\n", udev->devpath);
+
 	if (udev->product != NULL)
 		strscpy(dev->name, udev->product, sizeof(dev->name));
 	else
@@ -2462,14 +2474,14 @@ 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_trace(dev, UVC_TRACE_PROBE,
+			  "Unable to parse UVC descriptors.\n");
 		goto error;
 	}
 
 	/* Parse the associated GPIOs. */
 	if (uvc_gpio_parse(dev) < 0) {
-		uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n");
+		uvc_trace(dev, UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n");
 		goto error;
 	}
 
@@ -2524,7 +2536,7 @@ static int uvc_probe(struct usb_interface *intf,
 			 "Unable to request uvc_privacy_gpio irq %d. Continuing wihtout privacy events\n",
 			 ret);
 
-	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+	uvc_trace(dev, UVC_TRACE_PROBE, "UVC device initialized.\n");
 	usb_enable_autosuspend(udev);
 	return 0;
 
@@ -2556,7 +2568,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_trace(dev, UVC_TRACE_SUSPEND, "Suspending interface %u\n",
 		intf->cur_altsetting->desc.bInterfaceNumber);
 
 	/* Controls are cached on the fly so they don't need to be saved. */
@@ -2574,8 +2586,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_trace(dev, UVC_TRACE_SUSPEND,
+		  "Suspend: video streaming USB interface mismatch.\n");
 	return -EINVAL;
 }
 
@@ -2585,8 +2597,8 @@ 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",
-		intf->cur_altsetting->desc.bInterfaceNumber);
+	uvc_trace(dev, UVC_TRACE_SUSPEND, "Resuming interface %u\n",
+		  intf->cur_altsetting->desc.bInterfaceNumber);
 
 	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
 	    UVC_SC_VIDEOCONTROL) {
@@ -2614,8 +2626,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
 		}
 	}
 
-	uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
-			"mismatch.\n");
+	uvc_trace(dev, UVC_TRACE_SUSPEND,
+		  "Resume: video streaming USB interface mismatch.\n");
 	return -EINVAL;
 }
 
diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c
index 135fd7fe6852..d06fa2ea9420 100644
--- a/drivers/media/usb/uvc/uvc_isight.c
+++ b/drivers/media/usb/uvc/uvc_isight.c
@@ -43,21 +43,23 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
 	unsigned int maxlen, nbytes;
 	u8 *mem;
 	int is_header = 0;
+	struct uvc_streaming *stream = uvc_queue_to_stream(queue);
 
 	if (buf == NULL)
 		return 0;
 
 	if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) ||
 	    (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) {
-		uvc_trace(UVC_TRACE_FRAME, "iSight header found\n");
+		uvc_trace(stream->dev, UVC_TRACE_FRAME,
+			  "iSight header found\n");
 		is_header = 1;
 	}
 
 	/* Synchronize to the input stream by waiting for a header packet. */
 	if (buf->state != UVC_BUF_STATE_ACTIVE) {
 		if (!is_header) {
-			uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of "
-				  "sync).\n");
+			uvc_trace(stream->dev, UVC_TRACE_FRAME,
+				  "Dropping packet (out of sync).\n");
 			return 0;
 		}
 
@@ -85,8 +87,8 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
 		buf->bytesused += nbytes;
 
 		if (len > maxlen || buf->bytesused == buf->length) {
-			uvc_trace(UVC_TRACE_FRAME, "Frame complete "
-				  "(overflow).\n");
+			uvc_trace(stream->dev, UVC_TRACE_FRAME,
+				  "Frame complete (overflow).\n");
 			buf->state = UVC_BUF_STATE_DONE;
 		}
 	}
@@ -103,8 +105,8 @@ void uvc_video_decode_isight(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
 
 	for (i = 0; i < urb->number_of_packets; ++i) {
 		if (urb->iso_frame_desc[i].status < 0) {
-			uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
-				  "lost (%d).\n",
+			uvc_trace(stream->dev, UVC_TRACE_FRAME,
+				  "USB isochronous frame lost (%d).\n",
 				  urb->iso_frame_desc[i].status);
 		}
 
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index e800d491303f..66f2b2f19d65 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -32,12 +32,6 @@
  * the driver.
  */
 
-static inline struct uvc_streaming *
-uvc_queue_to_stream(struct uvc_video_queue *queue)
-{
-	return container_of(queue, struct uvc_streaming, queue);
-}
-
 static inline struct uvc_buffer *uvc_vbuf_to_buffer(struct vb2_v4l2_buffer *buf)
 {
 	return container_of(buf, struct uvc_buffer, buf);
@@ -109,7 +103,8 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
 
 	if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
 	    vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
-		uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+		uvc_trace(uvc_queue_to_stream(queue)->dev, UVC_TRACE_CAPTURE,
+			  "[E] Bytes used out of bounds.\n");
 		return -EINVAL;
 	}
 
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index 36fa196a9258..b5f493216fcd 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -93,20 +93,20 @@ static void uvc_event_streaming(struct uvc_device *dev,
 				struct uvc_streaming_status *status, int len)
 {
 	if (len < 3) {
-		uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event "
-				"received.\n");
+		uvc_trace(dev, UVC_TRACE_STATUS,
+			  "Invalid streaming status event received.\n");
 		return;
 	}
 
 	if (status->bEvent == 0) {
 		if (len < 4)
 			return;
-		uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
+		uvc_trace(dev, UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
 			  status->bOriginator,
 			  status->bValue[0] ? "pressed" : "released", len);
 		uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]);
 	} else {
-		uvc_trace(UVC_TRACE_STATUS,
+		uvc_trace(dev, UVC_TRACE_STATUS,
 			  "Stream %u error event %02x len %d.\n",
 			  status->bOriginator, status->bEvent, len);
 	}
@@ -163,12 +163,12 @@ static bool uvc_event_control(struct urb *urb,
 
 	if (len < 6 || status->bEvent != 0 ||
 	    status->bAttribute >= ARRAY_SIZE(attrs)) {
-		uvc_trace(UVC_TRACE_STATUS, "Invalid control status event "
-				"received.\n");
+		uvc_trace(dev, UVC_TRACE_STATUS,
+			  "Invalid control status event received.\n");
 		return false;
 	}
 
-	uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
+	uvc_trace(dev, UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
 		  status->bOriginator, status->bSelector,
 		  attrs[status->bAttribute], len);
 
@@ -236,8 +236,9 @@ static void uvc_status_complete(struct urb *urb)
 		}
 
 		default:
-			uvc_trace(UVC_TRACE_STATUS, "Unknown status event "
-				"type %u.\n", dev->status[0]);
+			uvc_trace(dev, UVC_TRACE_STATUS,
+				  "Unknown status event type %u.\n",
+				  dev->status[0]);
 			break;
 		}
 	}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index fa06bfa174ad..3a2d17bc766b 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -75,8 +75,9 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
 		break;
 
 	default:
-		uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
-			  "%u.\n", xmap->v4l2_type);
+		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
+			  "Unsupported V4L2 control type %u.\n",
+			  xmap->v4l2_type);
 		ret = -ENOTTY;
 		goto free_map;
 	}
@@ -164,10 +165,11 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
 		return -EINVAL;
 
 	fcc = (u8 *)&fmt->fmt.pix.pixelformat;
-	uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
-			fmt->fmt.pix.pixelformat,
-			fcc[0], fcc[1], fcc[2], fcc[3],
-			fmt->fmt.pix.width, fmt->fmt.pix.height);
+	uvc_trace(stream->dev, UVC_TRACE_FORMAT,
+		  "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
+		  fmt->fmt.pix.pixelformat,
+		  fcc[0], fcc[1], fcc[2], fcc[3],
+		  fmt->fmt.pix.width, fmt->fmt.pix.height);
 
 	/* Check if the hardware supports the requested format, use the default
 	 * format otherwise.
@@ -207,16 +209,18 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
 	}
 
 	if (frame == NULL) {
-		uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n",
-				fmt->fmt.pix.width, fmt->fmt.pix.height);
+		uvc_trace(stream->dev, UVC_TRACE_FORMAT,
+			  "Unsupported size %ux%u.\n", fmt->fmt.pix.width,
+			  fmt->fmt.pix.height);
 		return -EINVAL;
 	}
 
 	/* Use the default frame interval. */
 	interval = frame->dwDefaultFrameInterval;
-	uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us "
-		"(%u.%u fps).\n", interval/10, interval%10, 10000000/interval,
-		(100000000/interval)%10);
+	uvc_trace(stream->dev, UVC_TRACE_FORMAT,
+		  "Using default frame interval %u.%u us (%u.%u fps).\n",
+		  interval / 10, interval % 10, 10000000 / interval,
+		 (100000000 / interval) % 10);
 
 	/* Set the format index, frame index and frame interval. */
 	memset(probe, 0, sizeof(*probe));
@@ -258,7 +262,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
 	}
 
 	if (i == stream->nformats) {
-		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFormatIndex %u\n",
+		uvc_trace(stream->dev, UVC_TRACE_FORMAT,
+			  "Unknown bFormatIndex %u\n",
 			  probe->bFormatIndex);
 		return -EINVAL;
 	}
@@ -271,7 +276,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
 	}
 
 	if (i == format->nframes) {
-		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFrameIndex %u\n",
+		uvc_trace(stream->dev, UVC_TRACE_FORMAT,
+			  "Unknown bFrameIndex %u\n",
 			  probe->bFrameIndex);
 		return -EINVAL;
 	}
@@ -416,8 +422,9 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
 
 	interval = uvc_fraction_to_interval(timeperframe.numerator,
 		timeperframe.denominator);
-	uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
-		timeperframe.numerator, timeperframe.denominator, interval);
+	uvc_trace(stream->dev, UVC_TRACE_FORMAT,
+		  "Setting frame interval to %u/%u (%u).\n",
+		  timeperframe.numerator, timeperframe.denominator, interval);
 
 	mutex_lock(&stream->mutex);
 
@@ -545,8 +552,8 @@ static int uvc_v4l2_open(struct file *file)
 	struct uvc_fh *handle;
 	int ret = 0;
 
-	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
 	stream = video_drvdata(file);
+	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
 
 	ret = usb_autopm_get_interface(stream->dev->intf);
 	if (ret < 0)
@@ -588,7 +595,7 @@ static int uvc_v4l2_release(struct file *file)
 	struct uvc_fh *handle = file->private_data;
 	struct uvc_streaming *stream = handle->stream;
 
-	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
+	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
 
 	/* Only free resources if this is a privileged handle. */
 	if (uvc_has_privileges(handle))
@@ -1461,7 +1468,11 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
 static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
 		    size_t count, loff_t *ppos)
 {
-	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
+	struct uvc_fh *handle = file->private_data;
+	struct uvc_streaming *stream = handle->stream;
+
+	uvc_trace(stream->dev, UVC_TRACE_CALLS,
+		  "uvc_v4l2_read: not implemented.\n");
 	return -EINVAL;
 }
 
@@ -1470,7 +1481,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
 	struct uvc_fh *handle = file->private_data;
 	struct uvc_streaming *stream = handle->stream;
 
-	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n");
+	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
 
 	return uvc_queue_mmap(&stream->queue, vma);
 }
@@ -1480,7 +1491,7 @@ static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait)
 	struct uvc_fh *handle = file->private_data;
 	struct uvc_streaming *stream = handle->stream;
 
-	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
+	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
 
 	return uvc_queue_poll(&stream->queue, file, wait);
 }
@@ -1493,7 +1504,7 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
 	struct uvc_fh *handle = file->private_data;
 	struct uvc_streaming *stream = handle->stream;
 
-	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n");
+	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
 
 	return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
 }
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 71e643e6d006..26251ed0c3fd 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -95,7 +95,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
 	if (ret != 1)
 		return ret < 0 ? ret : -EPIPE;
 
-	uvc_trace(UVC_TRACE_CONTROL, "Control error %u\n", error);
+	uvc_trace(dev, UVC_TRACE_CONTROL, "Control error %u\n", error);
 
 	switch (error) {
 	case 0:
@@ -705,8 +705,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
 
 	sof = y;
 
-	uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu "
-		  "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
+	uvc_trace(stream->dev, UVC_TRACE_CLOCK,
+		  "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
 		  stream->dev->name, buf->pts,
 		  y >> 16, div_u64((y & 0xffff) * 1000000, 65536),
 		  sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
@@ -740,8 +740,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
 
 	timestamp = ktime_to_ns(first->host_time) + y - y1;
 
-	uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %llu "
-		  "buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
+	uvc_trace(stream->dev, UVC_TRACE_CLOCK,
+		  "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
 		  stream->dev->name,
 		  sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
 		  y, timestamp, vbuf->vb2_buf.timestamp,
@@ -875,9 +875,8 @@ static void uvc_video_stats_update(struct uvc_streaming *stream)
 {
 	struct uvc_stats_frame *frame = &stream->stats.frame;
 
-	uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
-		  "%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
-		  "last pts/stc/sof %u/%u/%u\n",
+	uvc_trace(stream->dev, UVC_TRACE_STATS,
+		  "frame %u stats: %u/%u/%u packets, %u/%u/%u pts (%searly %sinitial), %u/%u scr, last pts/stc/sof %u/%u/%u\n",
 		  stream->sequence, frame->first_data,
 		  frame->nb_packets - frame->nb_empty, frame->nb_packets,
 		  frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
@@ -1039,8 +1038,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
 
 	/* Mark the buffer as bad if the error bit is set. */
 	if (data[1] & UVC_STREAM_ERR) {
-		uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit "
-			  "set).\n");
+		uvc_trace(stream->dev, UVC_TRACE_FRAME,
+			  "Marking buffer as bad (error bit set).\n");
 		buf->error = 1;
 	}
 
@@ -1054,8 +1053,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
 	 */
 	if (buf->state != UVC_BUF_STATE_ACTIVE) {
 		if (fid == stream->last_fid) {
-			uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
-				"sync).\n");
+			uvc_trace(stream->dev, UVC_TRACE_FRAME,
+				  "Dropping payload (out of sync).\n");
 			if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
 			    (data[1] & UVC_STREAM_EOF))
 				stream->last_fid ^= UVC_STREAM_FID;
@@ -1086,8 +1085,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
 	 * previous payload had the EOF bit set.
 	 */
 	if (fid != stream->last_fid && buf->bytesused != 0) {
-		uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
-				"toggled).\n");
+		uvc_trace(stream->dev, UVC_TRACE_FRAME,
+			  "Frame complete (FID bit toggled).\n");
 		buf->state = UVC_BUF_STATE_READY;
 		return -EAGAIN;
 	}
@@ -1148,7 +1147,8 @@ static void uvc_video_decode_data(struct uvc_urb *uvc_urb,
 
 	/* Complete the current frame if the buffer size was exceeded. */
 	if (len > maxlen) {
-		uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
+		uvc_trace(uvc_urb->stream->dev, UVC_TRACE_FRAME,
+			  "Frame complete (overflow).\n");
 		buf->error = 1;
 		buf->state = UVC_BUF_STATE_READY;
 	}
@@ -1161,9 +1161,11 @@ static void uvc_video_decode_end(struct uvc_streaming *stream,
 {
 	/* Mark the buffer as done if the EOF marker is set. */
 	if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
-		uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
+		uvc_trace(stream->dev, UVC_TRACE_FRAME,
+			  "Frame complete (EOF found).\n");
 		if (data[0] == len)
-			uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
+			uvc_trace(stream->dev, UVC_TRACE_FRAME,
+				  "EOF in empty payload.\n");
 		buf->state = UVC_BUF_STATE_READY;
 		if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
 			stream->last_fid ^= UVC_STREAM_FID;
@@ -1279,7 +1281,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
 	memcpy(&meta->length, mem, length);
 	meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof);
 
-	uvc_trace(UVC_TRACE_FRAME,
+	uvc_trace(stream->dev, UVC_TRACE_FRAME,
 		  "%s(): t-sys %lluns, SOF %u, len %u, flags 0x%x, PTS %u, STC %u frame SOF %u\n",
 		  __func__, ktime_to_ns(time), meta->sof, meta->length,
 		  meta->flags,
@@ -1339,8 +1341,9 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
 
 	for (i = 0; i < urb->number_of_packets; ++i) {
 		if (urb->iso_frame_desc[i].status < 0) {
-			uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
-				"lost (%d).\n", urb->iso_frame_desc[i].status);
+			uvc_trace(stream->dev, UVC_TRACE_FRAME,
+				  "USB isochronous frame lost (%d).\n",
+				  urb->iso_frame_desc[i].status);
 			/* Mark the buffer as faulty. */
 			if (buf != NULL)
 				buf->error = 1;
@@ -1628,15 +1631,16 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
 		}
 
 		if (i == UVC_URBS) {
-			uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
-				"of %ux%u bytes each.\n", UVC_URBS, npackets,
-				psize);
+			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
+				  "Allocated %u URB buffers of %ux%u bytes each.\n",
+				  UVC_URBS, npackets, psize);
 			return npackets;
 		}
 	}
 
-	uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
-		"per packet).\n", psize);
+	uvc_trace(stream->dev, UVC_TRACE_VIDEO,
+		  "Failed to allocate URB buffers (%u bytes per packet).\n",
+		  psize);
 	return 0;
 }
 
@@ -1835,12 +1839,13 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
 		bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
 
 		if (bandwidth == 0) {
-			uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
-				"bandwidth, defaulting to lowest.\n");
+			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
+				  "Device requested null bandwidth, defaulting to lowest.\n");
 			bandwidth = 1;
 		} else {
-			uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
-				"B/frame bandwidth.\n", bandwidth);
+			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
+				  "Device requested %u B/frame bandwidth.\n",
+				  bandwidth);
 		}
 
 		for (i = 0; i < intf->num_altsetting; ++i) {
@@ -1863,13 +1868,14 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
 		}
 
 		if (best_ep == NULL) {
-			uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
-				"for requested bandwidth.\n");
+			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
+				  "No fast enough alt setting for requested bandwidth.\n");
 			return -EIO;
 		}
 
-		uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
-			"(%u B/frame bandwidth).\n", altsetting, best_psize);
+		uvc_trace(stream->dev, UVC_TRACE_VIDEO,
+			  "Selecting alternate setting %u (%u B/frame bandwidth).\n",
+			  altsetting, best_psize);
 
 		ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
 		if (ret < 0)
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 4a7d1fb76eee..60626feeee27 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -757,10 +757,11 @@ extern unsigned int uvc_trace_param;
 extern unsigned int uvc_timeout_param;
 extern unsigned int uvc_hw_timestamps_param;
 
-#define uvc_trace(flag, fmt, ...)					\
+#define uvc_trace(_dev, flag, fmt, ...)					\
 do {									\
 	if (uvc_trace_param & flag)					\
-		printk(KERN_DEBUG "uvcvideo: " fmt, ##__VA_ARGS__);	\
+		dev_printk(KERN_DEBUG, &(_dev)->udev->dev, fmt,		\
+			   ##__VA_ARGS__);				\
 } while (0)
 
 #define uvc_trace_cont(flag, fmt, ...)					\
@@ -822,6 +823,12 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
 	return vb2_is_streaming(&queue->queue);
 }
 
+static inline struct uvc_streaming *
+uvc_queue_to_stream(struct uvc_video_queue *queue)
+{
+	return container_of(queue, struct uvc_streaming, queue);
+}
+
 /* V4L2 interface */
 extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
 extern const struct v4l2_file_operations uvc_fops;
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v5 04/12] media: uvcvideo: Allow extra entities
  2020-12-21 16:48 [PATCH v5 04/12] media: uvcvideo: Allow extra entities Ricardo Ribalda
@ 2020-12-21 18:17 ` Ricardo Ribalda
  0 siblings, 0 replies; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-21 18:17 UTC (permalink / raw)
  To: Laurent Pinchart, Mauro Carvalho Chehab, linux-media, linux-kernel
  Cc: Ricardo Ribalda

Increase the size of the id, to avoid collisions with entities
implemented by the driver that are not part of the UVC device.

Entities exposed by the UVC device use IDs 0-255, extra entities
implemented by the driver (such as the GPIO entity) use IDs 256 and
up.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
Sorry for a duplicated patch, the previous one the spacing was wrong :(

 drivers/media/usb/uvc/uvc_driver.c | 2 +-
 drivers/media/usb/uvc/uvcvideo.h   | 7 ++++++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index bfbc5a4d4ca6..82cdd1bb28dc 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1024,7 +1024,7 @@ 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, u8 id,
+static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
 		unsigned int num_pads, unsigned int extra_size)
 {
 	struct uvc_entity *entity;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 0a3404091665..aa96e54b8896 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -301,7 +301,12 @@ struct uvc_entity {
 					 * chain. */
 	unsigned int flags;
 
-	u8 id;
+	/*
+	 * Entities exposed by the UVC device use IDs 0-255, extra entities
+	 * implemented by the driver (such as the GPIO entity) use IDs 256 and
+	 * up.
+	 */
+	u16 id;
 	u16 type;
 	char name[64];
 	u8 guid[16];
-- 
2.29.2.684.gfbc64c5ab5-goog


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 01/12] media: uvcvideo: Fix race condition handling events
  2020-12-21 16:48 ` [PATCH v5 01/12] media: uvcvideo: Fix race condition handling events Ricardo Ribalda
@ 2020-12-22  8:04   ` Laurent Pinchart
  0 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-22  8:04 UTC (permalink / raw)
  To: Ricardo Ribalda; +Cc: Mauro Carvalho Chehab, linux-media, linux-kernel

Hi Ricardo,

Thank you for the patch.

On Mon, Dec 21, 2020 at 05:48:08PM +0100, Ricardo Ribalda wrote:
> The control and its data needs to be copied to the workqueue at the same
> time to avoid half-updates of the events.
> This is, events reported to userspace were the control id does not match
> its value.

Actually, after discussing this with you on IRC, I'm not sure there's a
problem. The URB is resubmitted by uvc_ctrl_status_event_work(), so the
data shouldn't be overwritten before it is processed.

> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 2 +-
>  drivers/media/usb/uvc/uvcvideo.h | 2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 011e69427b7c..aa18dcdf8165 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1332,7 +1332,7 @@ bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain,
>  		return false;
>  	}
>  
> -	w->data = data;
> +	memcpy(w->data, data, ctrl->info.size);
>  	w->urb = urb;
>  	w->chain = chain;
>  	w->ctrl = ctrl;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index a3dfacf069c4..0db6c2e0bd98 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -678,7 +678,7 @@ struct uvc_device {
>  		struct urb *urb;
>  		struct uvc_video_chain *chain;
>  		struct uvc_control *ctrl;
> -		const void *data;
> +		u8 data[UVC_MAX_STATUS_SIZE];
>  	} async_ctrl;
>  };
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 02/12] media: uvcvideo: Allow more that one asyc_ctrl
  2020-12-21 16:48 ` [PATCH v5 02/12] media: uvcvideo: Allow more that one asyc_ctrl Ricardo Ribalda
@ 2020-12-22  8:07   ` Laurent Pinchart
  0 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-22  8:07 UTC (permalink / raw)
  To: Ricardo Ribalda; +Cc: Mauro Carvalho Chehab, linux-media, linux-kernel

Hi Ricardo,

Thank you for the patch.

On Mon, Dec 21, 2020 at 05:48:09PM +0100, Ricardo Ribalda wrote:
> The current implementation allocates memory for only one async_control.
> If we get a second event before we have processed the previous one, the
> old one gets lost.
> 
> Introduce a dynamic memory allocation and a list to handle the
> async_controls.

Thinking some more about this, do we need to go through the work queue
at all for GPIO-based events ? Could the part of
uvc_ctrl_status_event_work() before the URB resubmission be moved to
another function, which would be called directly by the GPIO threaded
IRQ handler ?

> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 49 ++++++++++++++++++++++++++------
>  drivers/media/usb/uvc/uvcvideo.h | 19 ++++++++-----
>  2 files changed, 52 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index aa18dcdf8165..69b2fc6ce12c 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1275,11 +1275,9 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
>  	uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
>  }
>  
> -static void uvc_ctrl_status_event_work(struct work_struct *work)
> +static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
> +					 struct uvc_ctrl_work *w)
>  {
> -	struct uvc_device *dev = container_of(work, struct uvc_device,
> -					      async_ctrl.work);
> -	struct uvc_ctrl_work *w = &dev->async_ctrl;
>  	struct uvc_video_chain *chain = w->chain;
>  	struct uvc_control_mapping *mapping;
>  	struct uvc_control *ctrl = w->ctrl;
> @@ -1321,23 +1319,54 @@ static void uvc_ctrl_status_event_work(struct work_struct *work)
>  			   ret);
>  }
>  
> +static void uvc_ctrl_status_event_work(struct work_struct *work)
> +{
> +	struct uvc_device *dev = container_of(work, struct uvc_device,
> +					      async_ctrl_work);
> +	struct uvc_ctrl_work *w;
> +
> +	do {
> +		mutex_lock(&dev->async_ctrl_lock);
> +		w = list_first_entry_or_null(&dev->async_ctrl_list,
> +					     struct uvc_ctrl_work,
> +					     list);
> +		if (w)
> +			list_del(&w->list);
> +		mutex_unlock(&dev->async_ctrl_lock);
> +
> +		if (!w)
> +			return;
> +
> +		__uvc_ctrl_status_event_work(dev, w);
> +		kfree(w);
> +	} while (w);
> +}
> +
>  bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain,
>  			   struct uvc_control *ctrl, const u8 *data)
>  {
>  	struct uvc_device *dev = chain->dev;
> -	struct uvc_ctrl_work *w = &dev->async_ctrl;
> +	struct uvc_ctrl_work *w;
>  
>  	if (list_empty(&ctrl->info.mappings)) {
>  		ctrl->handle = NULL;
>  		return false;
>  	}
>  
> +	w = kzalloc(sizeof(*w), GFP_KERNEL);
> +	if (WARN(!w, "Not enough memory to trigger uvc event"))
> +		return false;
> +
>  	memcpy(w->data, data, ctrl->info.size);
>  	w->urb = urb;
>  	w->chain = chain;
>  	w->ctrl = ctrl;
>  
> -	schedule_work(&w->work);
> +	mutex_lock(&dev->async_ctrl_lock);
> +	list_add_tail(&w->list, &dev->async_ctrl_list);
> +	mutex_unlock(&dev->async_ctrl_lock);
> +
> +	schedule_work(&dev->async_ctrl_work);
>  
>  	return true;
>  }
> @@ -2277,7 +2306,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
>  	struct uvc_entity *entity;
>  	unsigned int i;
>  
> -	INIT_WORK(&dev->async_ctrl.work, uvc_ctrl_status_event_work);
> +	INIT_WORK(&dev->async_ctrl_work, uvc_ctrl_status_event_work);
> +	mutex_init(&dev->async_ctrl_lock);
> +	INIT_LIST_HEAD(&dev->async_ctrl_list);
>  
>  	/* Walk the entities list and instantiate controls */
>  	list_for_each_entry(entity, &dev->entities, list) {
> @@ -2348,8 +2379,8 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev)
>  	unsigned int i;
>  
>  	/* Can be uninitialized if we are aborting on probe error. */
> -	if (dev->async_ctrl.work.func)
> -		cancel_work_sync(&dev->async_ctrl.work);
> +	if (dev->async_ctrl_work.func)
> +		cancel_work_sync(&dev->async_ctrl_work);
>  
>  	/* Free controls and control mappings for all entities. */
>  	list_for_each_entry(entity, &dev->entities, list) {
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 0db6c2e0bd98..afcaf49fad1a 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -637,6 +637,14 @@ struct uvc_device_info {
>  	u32	meta_format;
>  };
>  
> +struct uvc_ctrl_work {
> +	struct list_head list;
> +	struct urb *urb;
> +	struct uvc_video_chain *chain;
> +	struct uvc_control *ctrl;
> +	u8 data[UVC_MAX_STATUS_SIZE];
> +};
> +
>  struct uvc_device {
>  	struct usb_device *udev;
>  	struct usb_interface *intf;
> @@ -673,13 +681,10 @@ struct uvc_device {
>  	struct input_dev *input;
>  	char input_phys[64];
>  
> -	struct uvc_ctrl_work {
> -		struct work_struct work;
> -		struct urb *urb;
> -		struct uvc_video_chain *chain;
> -		struct uvc_control *ctrl;
> -		u8 data[UVC_MAX_STATUS_SIZE];
> -	} async_ctrl;
> +	/* Async control */
> +	struct work_struct async_ctrl_work;
> +	struct list_head async_ctrl_list;
> +	struct mutex async_ctrl_lock;
>  };
>  
>  enum uvc_handle_state {

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT
  2020-12-21 16:48 ` [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT Ricardo Ribalda
@ 2020-12-22  8:33   ` Laurent Pinchart
  2020-12-22  8:37     ` Laurent Pinchart
  2020-12-22 18:36     ` Ricardo Ribalda
  0 siblings, 2 replies; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-22  8:33 UTC (permalink / raw)
  To: Ricardo Ribalda; +Cc: Mauro Carvalho Chehab, linux-media, linux-kernel

Hi Ricardo,

Thank you for the patch.

On Mon, Dec 21, 2020 at 05:48:14PM +0100, Ricardo Ribalda wrote:
> Some devices can implement a physical switch to disable the input of the
> camera on demand. Think of it like an elegant privacy sticker.
> 
> The system can read the status of the privacy switch via a GPIO.
> 
> It is important to know the status of the switch, e.g. to notify the
> user when the camera will produce black frames and a videochat
> application is used.
> 
> In some systems, the GPIO is connected to main SoC instead of the
> camera controller, with the connected reported by the system firmware

s/connected/connection/

> (ACPI or DT). In that case, the UVC device isn't aware of the GPIO. We
> need to implement a virtual entity to handle the GPIO fully on the
> driver side.
> 
> For example, for ACPI-based systems, the GPIO is reported in the USB
> device object:
> 
>   Scope (\_SB.PCI0.XHCI.RHUB.HS07)
>   {
> 
> 	  /.../
> 
>     Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
>     {
>         GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
>             "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, ,
>             )
>             {   // Pin list
>                 0x0064
>             }
>     })
>     Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
>     {
>         ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
>         Package (0x01)
>         {
>             Package (0x02)
>             {
>                 "privacy-gpio",
>                 Package (0x04)
>                 {
>                     \_SB.PCI0.XHCI.RHUB.HS07,
>                     Zero,
>                     Zero,
>                     One
>                 }
>             }
>         }
>     })
>   }
> 
> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   |   7 ++
>  drivers/media/usb/uvc/uvc_driver.c | 156 +++++++++++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_entity.c |   1 +
>  drivers/media/usb/uvc/uvcvideo.h   |  16 +++
>  4 files changed, 180 insertions(+)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 528254230535..a430fa666897 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1300,6 +1300,10 @@ static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
>  
>  	mutex_unlock(&chain->ctrl_mutex);
>  
> +	/* Events not started by the UVC device. E.g. the GPIO unit */
> +	if (!w->urb)
> +		return;
> +
>  	/* Resubmit the URB. */
>  	w->urb->interval = dev->int_ep->desc.bInterval;
>  	ret = usb_submit_urb(w->urb, GFP_KERNEL);
> @@ -2317,6 +2321,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
>  		} else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
>  			bmControls = entity->camera.bmControls;
>  			bControlSize = entity->camera.bControlSize;
> +		} else if (UVC_ENTITY_TYPE(entity) == UVC_EXT_GPIO_UNIT) {
> +			bmControls = entity->gpio.bmControls;
> +			bControlSize = entity->gpio.bControlSize;
>  		}
>  
>  		/* Remove bogus/blacklisted controls */
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index c0c5f75ade40..72516101fdd0 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>
> @@ -1020,6 +1021,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
>  }
>  
>  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;
> @@ -1051,6 +1053,9 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
>  	 * 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;
> @@ -1464,6 +1469,137 @@ static int uvc_parse_control(struct uvc_device *dev)
>  	return 0;
>  }
>  
> +/* -----------------------------------------------------------------------------
> + * Privacy GPIO
> + */
> +
> +
> +static u8 uvc_gpio_update_value(struct uvc_device *dev,
> +				struct uvc_entity *unit)
> +{
> +	struct uvc_video_chain *chain;
> +	u8 gpio_val, old_val, new_val;
> +
> +	gpio_val = new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
> +
> +	old_val = atomic_xchg(&unit->gpio.gpio_privacy_value, gpio_val);
> +	if (new_val == old_val)
> +		return new_val;
> +
> +	/* GPIO entities are always on the first chain. */
> +	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> +	uvc_ctrl_status_event(NULL, chain, unit->controls, &new_val);
> +
> +	return 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;
> +
> +	*(uint8_t *)data = uvc_gpio_update_value(dev, entity);

s/uint8_t/u8/

Do we really need to generate an event when the GPIO is read, if there's
no IRQ support ?

> +	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 struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> +{
> +	struct uvc_video_chain *chain;
> +	struct uvc_entity *unit;
> +
> +	/* GPIO entities are always on the first chain. */
> +	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> +	list_for_each_entry(unit, &chain->entities, chain) {
> +		if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> +			return unit;
> +	}

As there can only be a single GPIO entity, would it make sense to store
a pointer to it in struct uvc_device to avoid walking the chain every
time ?

> +
> +	return NULL;
> +}
> +
> +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> +{
> +	struct uvc_device *dev = data;
> +	struct uvc_entity *unit;
> +
> +	unit = uvc_gpio_find_entity(dev);
> +	if (!unit)
> +		return IRQ_HANDLED;
> +
> +	uvc_gpio_update_value(dev, unit);
> +	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 == -EPROBE_DEFER)
> +		return -EPROBE_DEFER;
> +
> +	if (irq < 0)
> +		dev_warn(&dev->udev->dev,
> +		       "Unable to find privacy_gpio: %d\n", irq);

That's not right, the GPIO is found, it's only the IRQ that is
unsupported.

		dev_warn(&dev->udev->dev,
			 "No IRQ for privacy GPIO (%d)\n", irq);

> +
> +	atomic_set(&unit->gpio.gpio_privacy_value, -1);
> +	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;
> +
> +	sprintf(unit->name, "GPIO");

strncpy() would be simpler.

> +
> +	list_add_tail(&unit->list, &dev->entities);
> +
> +	return 0;
> +}
> +
> +static int uvc_gpio_init_irq(struct uvc_device *dev)
> +{
> +	int ret;
> +	struct uvc_entity *unit;

Could you please swap those two lines ?

> +
> +	unit = uvc_gpio_find_entity(dev);
> +	if (!unit)
> +		return 0;
> +
> +	if (unit->gpio.irq < 0)
> +		return 0;
> +
> +	ret = devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
> +					uvc_gpio_irq,
> +					IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
> +					 IRQF_TRIGGER_RISING,

Extra space in the indentation.

> +					"uvc_privacy_gpio", dev);
> +	return ret;

No need for a ret variable, you can write

	return devm_request_threaded_irq(...);

(unless you want to move the error message here, see below).

> +}
> +
>  /* ------------------------------------------------------------------------
>   * UVC device scan
>   */
> @@ -1915,6 +2051,7 @@ static int uvc_scan_device(struct uvc_device *dev)
>  {
>  	struct uvc_video_chain *chain;
>  	struct uvc_entity *term;
> +	struct uvc_entity *unit;
>  
>  	list_for_each_entry(term, &dev->entities, list) {
>  		if (!UVC_ENTITY_IS_OTERM(term))
> @@ -1953,6 +2090,13 @@ static int uvc_scan_device(struct uvc_device *dev)
>  		return -1;
>  	}
>  
> +	/* Add GPIO entities to the first chain. */
> +	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> +	list_for_each_entry(unit, &dev->entities, list) {
> +		if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> +			list_add_tail(&unit->chain, &chain->entities);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -2285,6 +2429,12 @@ static int uvc_probe(struct usb_interface *intf,
>  		goto error;
>  	}
>  
> +	/* Parse the associated GPIOs. */
> +	if (uvc_gpio_parse(dev) < 0) {
> +		uvc_trace(UVC_TRACE_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>",
> @@ -2329,6 +2479,12 @@ static int uvc_probe(struct usb_interface *intf,
>  			"supported.\n", ret);
>  	}
>  
> +	ret = uvc_gpio_init_irq(dev);
> +	if (ret < 0)
> +		dev_warn(&dev->udev->dev,
> +			 "Unable to request uvc_privacy_gpio irq %d. Continuing wihtout privacy events\n",

s/uvc_privacy_gpio irq/privacy GPIO IRQ/ ?

> +			 ret);

This could be moved to uvc_gpio_init_irq(), up to you.

> +
>  	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
>  	usb_enable_autosuspend(udev);
>  	return 0;
> diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
> index ca3a9c2eec27..6a9ba5b498db 100644
> --- a/drivers/media/usb/uvc/uvc_entity.c
> +++ b/drivers/media/usb/uvc/uvc_entity.c
> @@ -105,6 +105,7 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain,
>  		case UVC_OTT_DISPLAY:
>  		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
>  		case UVC_EXTERNAL_VENDOR_SPECIFIC:
> +		case UVC_EXT_GPIO_UNIT:
>  		default:
>  			function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
>  			break;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 6edbf79b2ff1..079a407ebba5 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -6,6 +6,7 @@
>  #error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
>  #endif /* __KERNEL__ */
>  
> +#include <linux/atomic.h>
>  #include <linux/kernel.h>
>  #include <linux/poll.h>
>  #include <linux/usb.h>
> @@ -37,6 +38,8 @@
>  	(UVC_ENTITY_IS_TERM(entity) && \
>  	((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
>  
> +#define UVC_EXT_GPIO_UNIT		0x7ffe
> +#define UVC_EXT_GPIO_UNIT_ID		0x100
>  
>  /* ------------------------------------------------------------------------
>   * GUIDs
> @@ -56,6 +59,9 @@
>  #define UVC_GUID_UVC_SELECTOR \
>  	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
>  	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
> +#define UVC_GUID_EXT_GPIO_CONTROLLER \
> +	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> +	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03}
>  
>  #define UVC_GUID_FORMAT_MJPEG \
>  	{ 'M',  'J',  'P',  'G', 0x00, 0x00, 0x10, 0x00, \
> @@ -278,6 +284,8 @@ struct uvc_format_desc {
>  	u32 fcc;
>  };
>  
> +struct gpio_desc;
> +

This could be moved a bit up, with the other forward declaration.

>  /* The term 'entity' refers to both UVC units and UVC terminals.
>   *
>   * The type field is either the terminal type (wTerminalType in the terminal
> @@ -353,6 +361,14 @@ struct uvc_entity {
>  			u8  *bmControls;
>  			u8  *bmControlsType;
>  		} extension;
> +
> +		struct {
> +			u8  bControlSize;
> +			u8  *bmControls;
> +			struct gpio_desc *gpio_privacy;
> +			int irq;
> +			atomic_t gpio_privacy_value;
> +		} gpio;
>  	};
>  
>  	u8 bNrInPins;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT
  2020-12-22  8:33   ` Laurent Pinchart
@ 2020-12-22  8:37     ` Laurent Pinchart
  2020-12-22 18:36     ` Ricardo Ribalda
  1 sibling, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-22  8:37 UTC (permalink / raw)
  To: Ricardo Ribalda; +Cc: Mauro Carvalho Chehab, linux-media, linux-kernel

On Tue, Dec 22, 2020 at 10:33:52AM +0200, Laurent Pinchart wrote:
> Hi Ricardo,
> 
> Thank you for the patch.
> 
> On Mon, Dec 21, 2020 at 05:48:14PM +0100, Ricardo Ribalda wrote:
> > Some devices can implement a physical switch to disable the input of the
> > camera on demand. Think of it like an elegant privacy sticker.
> > 
> > The system can read the status of the privacy switch via a GPIO.
> > 
> > It is important to know the status of the switch, e.g. to notify the
> > user when the camera will produce black frames and a videochat
> > application is used.
> > 
> > In some systems, the GPIO is connected to main SoC instead of the
> > camera controller, with the connected reported by the system firmware
> 
> s/connected/connection/
> 
> > (ACPI or DT). In that case, the UVC device isn't aware of the GPIO. We
> > need to implement a virtual entity to handle the GPIO fully on the
> > driver side.
> > 
> > For example, for ACPI-based systems, the GPIO is reported in the USB
> > device object:
> > 
> >   Scope (\_SB.PCI0.XHCI.RHUB.HS07)
> >   {
> > 
> > 	  /.../
> > 
> >     Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
> >     {
> >         GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
> >             "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, ,
> >             )
> >             {   // Pin list
> >                 0x0064
> >             }
> >     })
> >     Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
> >     {
> >         ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
> >         Package (0x01)
> >         {
> >             Package (0x02)
> >             {
> >                 "privacy-gpio",
> >                 Package (0x04)
> >                 {
> >                     \_SB.PCI0.XHCI.RHUB.HS07,
> >                     Zero,
> >                     Zero,
> >                     One
> >                 }
> >             }
> >         }
> >     })
> >   }
> > 
> > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > ---
> >  drivers/media/usb/uvc/uvc_ctrl.c   |   7 ++
> >  drivers/media/usb/uvc/uvc_driver.c | 156 +++++++++++++++++++++++++++++
> >  drivers/media/usb/uvc/uvc_entity.c |   1 +
> >  drivers/media/usb/uvc/uvcvideo.h   |  16 +++
> >  4 files changed, 180 insertions(+)
> > 
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 528254230535..a430fa666897 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -1300,6 +1300,10 @@ static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
> >  
> >  	mutex_unlock(&chain->ctrl_mutex);
> >  
> > +	/* Events not started by the UVC device. E.g. the GPIO unit */
> > +	if (!w->urb)
> > +		return;
> > +
> >  	/* Resubmit the URB. */
> >  	w->urb->interval = dev->int_ep->desc.bInterval;
> >  	ret = usb_submit_urb(w->urb, GFP_KERNEL);
> > @@ -2317,6 +2321,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
> >  		} else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
> >  			bmControls = entity->camera.bmControls;
> >  			bControlSize = entity->camera.bControlSize;
> > +		} else if (UVC_ENTITY_TYPE(entity) == UVC_EXT_GPIO_UNIT) {
> > +			bmControls = entity->gpio.bmControls;
> > +			bControlSize = entity->gpio.bControlSize;
> >  		}
> >  
> >  		/* Remove bogus/blacklisted controls */
> > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > index c0c5f75ade40..72516101fdd0 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>
> > @@ -1020,6 +1021,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
> >  }
> >  
> >  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;
> > @@ -1051,6 +1053,9 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
> >  	 * 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;
> > @@ -1464,6 +1469,137 @@ static int uvc_parse_control(struct uvc_device *dev)
> >  	return 0;
> >  }
> >  
> > +/* -----------------------------------------------------------------------------
> > + * Privacy GPIO
> > + */
> > +
> > +

And there's an extra blank line here.

> > +static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > +				struct uvc_entity *unit)
> > +{
> > +	struct uvc_video_chain *chain;
> > +	u8 gpio_val, old_val, new_val;
> > +
> > +	gpio_val = new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
> > +
> > +	old_val = atomic_xchg(&unit->gpio.gpio_privacy_value, gpio_val);
> > +	if (new_val == old_val)
> > +		return new_val;
> > +
> > +	/* GPIO entities are always on the first chain. */
> > +	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > +	uvc_ctrl_status_event(NULL, chain, unit->controls, &new_val);
> > +
> > +	return 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;
> > +
> > +	*(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> 
> s/uint8_t/u8/
> 
> Do we really need to generate an event when the GPIO is read, if there's
> no IRQ support ?
> 
> > +	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 struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> > +{
> > +	struct uvc_video_chain *chain;
> > +	struct uvc_entity *unit;
> > +
> > +	/* GPIO entities are always on the first chain. */
> > +	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > +	list_for_each_entry(unit, &chain->entities, chain) {
> > +		if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> > +			return unit;
> > +	}
> 
> As there can only be a single GPIO entity, would it make sense to store
> a pointer to it in struct uvc_device to avoid walking the chain every
> time ?
> 
> > +
> > +	return NULL;
> > +}
> > +
> > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > +{
> > +	struct uvc_device *dev = data;
> > +	struct uvc_entity *unit;
> > +
> > +	unit = uvc_gpio_find_entity(dev);
> > +	if (!unit)
> > +		return IRQ_HANDLED;
> > +
> > +	uvc_gpio_update_value(dev, unit);
> > +	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 == -EPROBE_DEFER)
> > +		return -EPROBE_DEFER;
> > +
> > +	if (irq < 0)
> > +		dev_warn(&dev->udev->dev,
> > +		       "Unable to find privacy_gpio: %d\n", irq);
> 
> That's not right, the GPIO is found, it's only the IRQ that is
> unsupported.
> 
> 		dev_warn(&dev->udev->dev,
> 			 "No IRQ for privacy GPIO (%d)\n", irq);
> 
> > +
> > +	atomic_set(&unit->gpio.gpio_privacy_value, -1);
> > +	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;
> > +
> > +	sprintf(unit->name, "GPIO");
> 
> strncpy() would be simpler.
> 
> > +
> > +	list_add_tail(&unit->list, &dev->entities);
> > +
> > +	return 0;
> > +}
> > +
> > +static int uvc_gpio_init_irq(struct uvc_device *dev)
> > +{
> > +	int ret;
> > +	struct uvc_entity *unit;
> 
> Could you please swap those two lines ?
> 
> > +
> > +	unit = uvc_gpio_find_entity(dev);
> > +	if (!unit)
> > +		return 0;
> > +
> > +	if (unit->gpio.irq < 0)
> > +		return 0;
> > +
> > +	ret = devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
> > +					uvc_gpio_irq,
> > +					IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
> > +					 IRQF_TRIGGER_RISING,
> 
> Extra space in the indentation.
> 
> > +					"uvc_privacy_gpio", dev);
> > +	return ret;
> 
> No need for a ret variable, you can write
> 
> 	return devm_request_threaded_irq(...);
> 
> (unless you want to move the error message here, see below).
> 
> > +}
> > +
> >  /* ------------------------------------------------------------------------
> >   * UVC device scan
> >   */
> > @@ -1915,6 +2051,7 @@ static int uvc_scan_device(struct uvc_device *dev)
> >  {
> >  	struct uvc_video_chain *chain;
> >  	struct uvc_entity *term;
> > +	struct uvc_entity *unit;
> >  
> >  	list_for_each_entry(term, &dev->entities, list) {
> >  		if (!UVC_ENTITY_IS_OTERM(term))
> > @@ -1953,6 +2090,13 @@ static int uvc_scan_device(struct uvc_device *dev)
> >  		return -1;
> >  	}
> >  
> > +	/* Add GPIO entities to the first chain. */
> > +	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > +	list_for_each_entry(unit, &dev->entities, list) {
> > +		if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> > +			list_add_tail(&unit->chain, &chain->entities);
> > +	}
> > +
> >  	return 0;
> >  }
> >  
> > @@ -2285,6 +2429,12 @@ static int uvc_probe(struct usb_interface *intf,
> >  		goto error;
> >  	}
> >  
> > +	/* Parse the associated GPIOs. */
> > +	if (uvc_gpio_parse(dev) < 0) {
> > +		uvc_trace(UVC_TRACE_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>",
> > @@ -2329,6 +2479,12 @@ static int uvc_probe(struct usb_interface *intf,
> >  			"supported.\n", ret);
> >  	}
> >  
> > +	ret = uvc_gpio_init_irq(dev);
> > +	if (ret < 0)
> > +		dev_warn(&dev->udev->dev,
> > +			 "Unable to request uvc_privacy_gpio irq %d. Continuing wihtout privacy events\n",
> 
> s/uvc_privacy_gpio irq/privacy GPIO IRQ/ ?
> 
> > +			 ret);
> 
> This could be moved to uvc_gpio_init_irq(), up to you.
> 
> > +
> >  	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
> >  	usb_enable_autosuspend(udev);
> >  	return 0;
> > diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
> > index ca3a9c2eec27..6a9ba5b498db 100644
> > --- a/drivers/media/usb/uvc/uvc_entity.c
> > +++ b/drivers/media/usb/uvc/uvc_entity.c
> > @@ -105,6 +105,7 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain,
> >  		case UVC_OTT_DISPLAY:
> >  		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
> >  		case UVC_EXTERNAL_VENDOR_SPECIFIC:
> > +		case UVC_EXT_GPIO_UNIT:
> >  		default:
> >  			function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
> >  			break;
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 6edbf79b2ff1..079a407ebba5 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -6,6 +6,7 @@
> >  #error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
> >  #endif /* __KERNEL__ */
> >  
> > +#include <linux/atomic.h>
> >  #include <linux/kernel.h>
> >  #include <linux/poll.h>
> >  #include <linux/usb.h>
> > @@ -37,6 +38,8 @@
> >  	(UVC_ENTITY_IS_TERM(entity) && \
> >  	((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
> >  
> > +#define UVC_EXT_GPIO_UNIT		0x7ffe
> > +#define UVC_EXT_GPIO_UNIT_ID		0x100
> >  
> >  /* ------------------------------------------------------------------------
> >   * GUIDs
> > @@ -56,6 +59,9 @@
> >  #define UVC_GUID_UVC_SELECTOR \
> >  	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> >  	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
> > +#define UVC_GUID_EXT_GPIO_CONTROLLER \
> > +	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> > +	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03}
> >  
> >  #define UVC_GUID_FORMAT_MJPEG \
> >  	{ 'M',  'J',  'P',  'G', 0x00, 0x00, 0x10, 0x00, \
> > @@ -278,6 +284,8 @@ struct uvc_format_desc {
> >  	u32 fcc;
> >  };
> >  
> > +struct gpio_desc;
> > +
> 
> This could be moved a bit up, with the other forward declaration.
> 
> >  /* The term 'entity' refers to both UVC units and UVC terminals.
> >   *
> >   * The type field is either the terminal type (wTerminalType in the terminal
> > @@ -353,6 +361,14 @@ struct uvc_entity {
> >  			u8  *bmControls;
> >  			u8  *bmControlsType;
> >  		} extension;
> > +
> > +		struct {
> > +			u8  bControlSize;
> > +			u8  *bmControls;
> > +			struct gpio_desc *gpio_privacy;
> > +			int irq;
> > +			atomic_t gpio_privacy_value;
> > +		} gpio;
> >  	};
> >  
> >  	u8 bNrInPins;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 12/12] media: uvcvideo: use dev_dbg() for uvc_trace()
  2020-12-21 16:48 ` [PATCH v5 12/12] media: uvcvideo: use dev_dbg() for uvc_trace() Ricardo Ribalda
@ 2020-12-22  8:51   ` Laurent Pinchart
  0 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-22  8:51 UTC (permalink / raw)
  To: Ricardo Ribalda; +Cc: Mauro Carvalho Chehab, linux-media, linux-kernel

Hi Ricardo,

Thank you for the patch.

On Mon, Dec 21, 2020 at 05:48:19PM +0100, Ricardo Ribalda wrote:
> Instead of calling prink() inside uvc_trace, use dev_dbg(), which add

You're using dev_printk(KERN_DEBUG), which is right, but doesn't match
the commit message.

> context to the output.
> 
> Now that we are at it, regroup the strings so the messages can be easily
> "grepable".
> 
> Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   |  66 ++++---
>  drivers/media/usb/uvc/uvc_driver.c | 298 +++++++++++++++--------------
>  drivers/media/usb/uvc/uvc_isight.c |  16 +-
>  drivers/media/usb/uvc/uvc_queue.c  |   9 +-
>  drivers/media/usb/uvc/uvc_status.c |  19 +-
>  drivers/media/usb/uvc/uvc_v4l2.c   |  53 +++--
>  drivers/media/usb/uvc/uvc_video.c  |  72 +++----
>  drivers/media/usb/uvc/uvcvideo.h   |  11 +-
>  8 files changed, 291 insertions(+), 253 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 1f1323a649fb..febe20e2887d 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -906,8 +906,8 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>  	}
>  
>  	if (ctrl == NULL && !next)
> -		uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n",
> -				v4l2_id);
> +		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
> +			  "Control 0x%08x not found.\n", v4l2_id);
>  
>  	return ctrl;
>  }
> @@ -1828,9 +1828,9 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
>  	ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
>  			     info->selector, data, 2);
>  	if (ret < 0) {
> -		uvc_trace(UVC_TRACE_CONTROL,
> +		uvc_trace(dev, UVC_TRACE_CONTROL,
>  			  "GET_LEN failed on control %pUl/%u (%d).\n",
> -			   info->entity, info->selector, ret);
> +			  info->entity, info->selector, ret);
>  		goto done;
>  	}
>  
> @@ -1841,7 +1841,7 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
>  
>  	ret = uvc_ctrl_get_flags(dev, ctrl, info);
>  	if (ret < 0) {
> -		uvc_trace(UVC_TRACE_CONTROL,
> +		uvc_trace(dev, UVC_TRACE_CONTROL,
>  			  "Failed to get flags for control %pUl/%u (%d).\n",
>  			  info->entity, info->selector, ret);
>  		goto done;
> @@ -1849,8 +1849,8 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
>  
>  	uvc_ctrl_fixup_xu_info(dev, ctrl, info);
>  
> -	uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
> -		  "flags { get %u set %u auto %u }.\n",
> +	uvc_trace(dev, UVC_TRACE_CONTROL,
> +		  "XU control %pUl/%u queried: len %u, flags { get %u set %u auto %u }.\n",
>  		  info->entity, info->selector, info->size,
>  		  (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0,
>  		  (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0,
> @@ -1879,9 +1879,10 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
>  
>  	ret = uvc_ctrl_add_info(dev, ctrl, &info);
>  	if (ret < 0)
> -		uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control "
> -			  "%pUl/%u on device %s entity %u\n", info.entity,
> -			  info.selector, dev->udev->devpath, ctrl->entity->id);
> +		uvc_trace(dev, UVC_TRACE_CONTROL,
> +			  "Failed to initialize control %pUl/%u on device %s entity %u\n",
> +			  info.entity, info.selector, dev->udev->devpath,
> +			  ctrl->entity->id);
>  
>  	return ret;
>  }
> @@ -1909,8 +1910,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  	}
>  
>  	if (!found) {
> -		uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
> -			xqry->unit);
> +		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
> +			  "Extension unit %u not found.\n", xqry->unit);
>  		return -ENOENT;
>  	}
>  
> @@ -1925,8 +1926,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  	}
>  
>  	if (!found) {
> -		uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n",
> -			entity->guid, xqry->selector);
> +		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
> +			  "Control %pUl/%u not found.\n", entity->guid,
> +			  xqry->selector);
>  		return -ENOENT;
>  	}
>  
> @@ -2074,9 +2076,10 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
>  
>  	ctrl->initialized = 1;
>  
> -	uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
> -		"entity %u\n", ctrl->info.entity, ctrl->info.selector,
> -		dev->udev->devpath, ctrl->entity->id);
> +	uvc_trace(dev, UVC_TRACE_CONTROL,
> +		  "Added control %pUl/%u to device %s entity %u\n",
> +		  ctrl->info.entity, ctrl->info.selector, dev->udev->devpath,
> +		  ctrl->entity->id);
>  
>  	return 0;
>  }
> @@ -2113,9 +2116,9 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
>  		map->set = uvc_set_le_value;
>  
>  	list_add_tail(&map->list, &ctrl->info.mappings);
> -	uvc_trace(UVC_TRACE_CONTROL,
> -		"Adding mapping '%s' to control %pUl/%u.\n",
> -		map->name, ctrl->info.entity, ctrl->info.selector);
> +	uvc_trace(dev, UVC_TRACE_CONTROL,
> +		  "Adding mapping '%s' to control %pUl/%u.\n",
> +		  map->name, ctrl->info.entity, ctrl->info.selector);
>  
>  	return 0;
>  }
> @@ -2131,9 +2134,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>  	int ret;
>  
>  	if (mapping->id & ~V4L2_CTRL_ID_MASK) {
> -		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
> -			"id 0x%08x is invalid.\n", mapping->name,
> -			mapping->id);
> +		uvc_trace(dev, UVC_TRACE_CONTROL,
> +			  "Can't add mapping '%s', control id 0x%08x is invalid.\n",
> +			  mapping->name, mapping->id);
>  		return -EINVAL;
>  	}
>  
> @@ -2178,9 +2181,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>  
>  	list_for_each_entry(map, &ctrl->info.mappings, list) {
>  		if (mapping->id == map->id) {
> -			uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
> -				"control id 0x%08x already exists.\n",
> -				mapping->name, mapping->id);
> +			uvc_trace(dev, UVC_TRACE_CONTROL,
> +				  "Can't add mapping '%s', control id 0x%08x already exists.\n",
> +				  mapping->name, mapping->id);
>  			ret = -EEXIST;
>  			goto done;
>  		}
> @@ -2189,9 +2192,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>  	/* Prevent excess memory consumption */
>  	if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) {
>  		atomic_dec(&dev->nmappings);
> -		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', maximum "
> -			"mappings count (%u) exceeded.\n", mapping->name,
> -			UVC_MAX_CONTROL_MAPPINGS);
> +		uvc_trace(dev, UVC_TRACE_CONTROL,
> +			  "Can't add mapping '%s', maximum mappings count (%u) exceeded.\n",
> +			  mapping->name, UVC_MAX_CONTROL_MAPPINGS);
>  		ret = -ENOMEM;
>  		goto done;
>  	}
> @@ -2260,8 +2263,9 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
>  		    !uvc_test_bit(controls, blacklist[i].index))
>  			continue;
>  
> -		uvc_trace(UVC_TRACE_CONTROL, "%u/%u control is black listed, "
> -			"removing it.\n", entity->id, blacklist[i].index);
> +		uvc_trace(dev, UVC_TRACE_CONTROL,
> +			  "%u/%u control is black listed, removing it.\n",
> +			  entity->id, blacklist[i].index);
>  
>  		uvc_clear_bit(controls, blacklist[i].index);
>  	}
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index ea64c716f1f3..f857bb9b7755 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -521,10 +521,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_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videostreaming interface %d FORMAT error\n",
> +				  dev->udev->devnum,
> +				  alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -585,10 +585,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_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videostreaming interface %d FORMAT error\n",
> +				  dev->udev->devnum,
> +				  alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -601,10 +601,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_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videostreaming interface %d FORMAT error\n",
> +				  dev->udev->devnum,
> +				  alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -619,9 +619,9 @@ 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,
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videostreaming interface %d: unknown DV format %u\n",
> +				  dev->udev->devnum,
>  			       alts->desc.bInterfaceNumber, buffer[8]);
>  			return -EINVAL;
>  		}
> @@ -648,14 +648,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_trace(dev, UVC_TRACE_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_trace(dev, UVC_TRACE_DESCR, "Found format %s.\n", format->name);
>  
>  	buflen -= buffer[0];
>  	buffer += buffer[0];
> @@ -674,9 +674,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_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videostreaming interface %d FRAME error\n",
> +				  dev->udev->devnum,
> +				  alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -738,10 +739,10 @@ static int uvc_parse_format(struct uvc_device *dev,
>  				frame->dwDefaultFrameInterval;
>  		}
>  
> -		uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
> -			frame->wWidth, frame->wHeight,
> -			10000000/frame->dwDefaultFrameInterval,
> -			(100000000/frame->dwDefaultFrameInterval)%10);
> +		uvc_trace(dev, UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
> +			  frame->wWidth, frame->wHeight,
> +			  10000000 / frame->dwDefaultFrameInterval,
> +			  (100000000 / frame->dwDefaultFrameInterval) % 10);
>  
>  		format->nframes++;
>  		buflen -= buffer[0];
> @@ -757,10 +758,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_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videostreaming interface %d COLORFORMAT error\n",
> +				  dev->udev->devnum,
> +				  alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -792,16 +793,18 @@ 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,
> -			intf->altsetting[0].desc.bInterfaceNumber);
> +		uvc_trace(dev, UVC_TRACE_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,
> -			intf->altsetting[0].desc.bInterfaceNumber);
> +		uvc_trace(dev, UVC_TRACE_DESCR,
> +			  "device %d interface %d is already claimed\n",
> +			  dev->udev->devnum,
> +			  intf->altsetting[0].desc.bInterfaceNumber);
>  		return -EINVAL;
>  	}
>  
> @@ -823,8 +826,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_trace(dev, UVC_TRACE_DESCR,
> +					  "trying extra data from endpoint %u.\n",
> +					  i);
>  				buffer = alts->endpoint[i].extra;
>  				buflen = alts->endpoint[i].extralen;
>  				break;
> @@ -839,8 +843,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_trace(dev, UVC_TRACE_DESCR,
> +			  "no class-specific streaming interface descriptors found.\n");
>  		goto error;
>  	}
>  
> @@ -857,9 +861,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_trace(dev, UVC_TRACE_DESCR,
> +			  "device %d videostreaming interface %d HEADER descriptor not found.\n",
> +			  dev->udev->devnum, alts->desc.bInterfaceNumber);
>  		goto error;
>  	}
>  
> @@ -867,9 +871,9 @@ 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",
> -			dev->udev->devnum, alts->desc.bInterfaceNumber);
> +		uvc_trace(dev, UVC_TRACE_DESCR,
> +			  "device %d videostreaming interface %d HEADER descriptor is invalid.\n",
> +			  dev->udev->devnum, alts->desc.bInterfaceNumber);
>  		goto error;
>  	}
>  
> @@ -919,10 +923,10 @@ 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",
> -				dev->udev->devnum,
> -				alts->desc.bInterfaceNumber, _buffer[2]);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videostreaming interface %d FORMAT %u is not supported.\n",
> +				  dev->udev->devnum,
> +				  alts->desc.bInterfaceNumber, _buffer[2]);
>  			break;
>  
>  		case UVC_VS_FRAME_UNCOMPRESSED:
> @@ -944,9 +948,9 @@ 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",
> -			dev->udev->devnum, alts->desc.bInterfaceNumber);
> +		uvc_trace(dev, UVC_TRACE_DESCR,
> +			  "device %d videostreaming interface %d has no supported formats defined.\n",
> +			  dev->udev->devnum, alts->desc.bInterfaceNumber);
>  		goto error;
>  	}
>  
> @@ -993,9 +997,10 @@ 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",
> -			dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
> +		uvc_trace(dev, UVC_TRACE_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. */
>  	for (i = 0; i < intf->num_altsetting; ++i) {
> @@ -1128,9 +1133,9 @@ 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",
> -				udev->devnum, alts->desc.bInterfaceNumber);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d EXTENSION_UNIT error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			break;
>  		}
>  
> @@ -1177,9 +1182,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_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d HEADER error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -1190,9 +1195,9 @@ 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",
> -					udev->devnum, i);
> +				uvc_trace(dev, UVC_TRACE_DESCR,
> +					  "device %d interface %d doesn't exists\n",
> +					  udev->devnum, i);
>  				continue;
>  			}
>  
> @@ -1202,9 +1207,9 @@ 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",
> -				udev->devnum, alts->desc.bInterfaceNumber);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d INPUT_TERMINAL error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -1221,11 +1226,10 @@ 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,
> -				buffer[3], type);
> +			uvc_trace(dev, UVC_TRACE_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;
>  		}
>  
> @@ -1244,9 +1248,9 @@ 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",
> -				udev->devnum, alts->desc.bInterfaceNumber);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d INPUT_TERMINAL error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -1291,9 +1295,9 @@ 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",
> -				udev->devnum, alts->desc.bInterfaceNumber);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d OUTPUT_TERMINAL error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -1302,10 +1306,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_trace(dev, 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);
>  			return 0;
>  		}
>  
> @@ -1329,9 +1333,9 @@ 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",
> -				udev->devnum, alts->desc.bInterfaceNumber);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d SELECTOR_UNIT error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -1355,9 +1359,9 @@ 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",
> -				udev->devnum, alts->desc.bInterfaceNumber);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d PROCESSING_UNIT error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -1388,9 +1392,9 @@ 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",
> -				udev->devnum, alts->desc.bInterfaceNumber);
> +			uvc_trace(dev, UVC_TRACE_DESCR,
> +				  "device %d videocontrol interface %d EXTENSION_UNIT error\n",
> +				  udev->devnum, alts->desc.bInterfaceNumber);
>  			return -EINVAL;
>  		}
>  
> @@ -1415,8 +1419,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_trace(dev, UVC_TRACE_DESCR,
> +			  "Found an unknown CS_INTERFACE descriptor (%u)\n",
> +			  buffer[2]);
>  		break;
>  	}
>  
> @@ -1461,8 +1466,9 @@ static int uvc_parse_control(struct uvc_device *dev)
>  		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_trace(dev, UVC_TRACE_DESCR,
> +				  "Found a Status endpoint (addr %02x).\n",
> +				  desc->bEndpointAddress);
>  			dev->int_ep = ep;
>  		}
>  	}
> @@ -1689,8 +1695,9 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
>  		uvc_trace_cont(UVC_TRACE_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_trace(chain->dev, UVC_TRACE_DESCR,
> +				  "Extension unit %d has more than 1 input pin.\n",
> +				  entity->id);
>  			return -1;
>  		}
>  
> @@ -1700,8 +1707,8 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
>  		uvc_trace_cont(UVC_TRACE_PROBE, " <- PU %d", entity->id);
>  
>  		if (chain->processing != NULL) {
> -			uvc_trace(UVC_TRACE_DESCR, "Found multiple "
> -				"Processing Units in chain.\n");
> +			uvc_trace(chain->dev, UVC_TRACE_DESCR,
> +				  "Found multiple Processing Units in chain.\n");
>  			return -1;
>  		}
>  
> @@ -1716,8 +1723,8 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
>  			break;
>  
>  		if (chain->selector != NULL) {
> -			uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
> -				"Units in chain.\n");
> +			uvc_trace(chain->dev, UVC_TRACE_DESCR,
> +				  "Found multiple Selector Units in chain.\n");
>  			return -1;
>  		}
>  
> @@ -1747,8 +1754,9 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
>  		break;
>  
>  	default:
> -		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
> -			"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
> +		uvc_trace(chain->dev, UVC_TRACE_DESCR,
> +			  "Unsupported entity type 0x%04x found in chain.\n",
> +			  UVC_ENTITY_TYPE(entity));
>  		return -1;
>  	}
>  
> @@ -1774,16 +1782,17 @@ 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_trace(chain->dev, UVC_TRACE_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",
> +				uvc_trace(chain->dev, UVC_TRACE_DESCR,
> +					  "Extension unit %d has more than 1 input pin.\n",
>  					  entity->id);
>  				return -EINVAL;
>  			}
> @@ -1801,8 +1810,9 @@ 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_trace(chain->dev, UVC_TRACE_DESCR,
> +					  "Unsupported input terminal %u.\n",
> +					  forward->id);
>  				return -EINVAL;
>  			}
>  
> @@ -1848,16 +1858,16 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
>  			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_trace(chain->dev, UVC_TRACE_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",
> -					term->id);
> +				uvc_trace(chain->dev, UVC_TRACE_DESCR,
> +					  "Found reference to entity %d already in chain.\n",
> +					  term->id);
>  				return -EINVAL;
>  			}
>  
> @@ -1890,8 +1900,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_trace(chain->dev, UVC_TRACE_DESCR,
> +			  "Found reference to unknown entity %d.\n", id);
>  		return -EINVAL;
>  	}
>  
> @@ -1904,7 +1914,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_trace(chain->dev, UVC_TRACE_PROBE, "Scanning UVC chain:");
>  
>  	entity = term;
>  	prev = NULL;
> @@ -1912,8 +1922,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_trace(chain->dev, UVC_TRACE_DESCR,
> +				  "Found reference to entity %d already in chain.\n",
> +				  entity->id);
>  			return -EINVAL;
>  		}
>  
> @@ -2067,7 +2078,7 @@ static int uvc_scan_fallback(struct uvc_device *dev)
>  
>  	list_add_tail(&chain->list, &dev->chains);
>  
> -	uvc_trace(UVC_TRACE_PROBE,
> +	uvc_trace(dev, UVC_TRACE_PROBE,
>  		  "Found a video chain by fallback heuristic (%s).\n",
>  		  uvc_print_chain(chain));
>  
> @@ -2112,7 +2123,8 @@ static int uvc_scan_device(struct uvc_device *dev)
>  			continue;
>  		}
>  
> -		uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
> +		uvc_trace(dev, UVC_TRACE_PROBE,
> +			  "Found a valid video chain (%s).\n",
>  			  uvc_print_chain(chain));
>  
>  		list_add_tail(&chain->list, &dev->chains);
> @@ -2393,14 +2405,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)
> @@ -2420,6 +2424,14 @@ 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_trace(dev, UVC_TRACE_PROBE,
> +			  "Probing known UVC device %s (%04x:%04x)\n",
> +			  udev->devpath, id->idVendor, id->idProduct);
> +	else
> +		uvc_trace(dev, UVC_TRACE_PROBE,
> +			  "Probing generic UVC device %s\n", udev->devpath);
> +
>  	if (udev->product != NULL)
>  		strscpy(dev->name, udev->product, sizeof(dev->name));
>  	else
> @@ -2462,14 +2474,14 @@ 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_trace(dev, UVC_TRACE_PROBE,
> +			  "Unable to parse UVC descriptors.\n");
>  		goto error;
>  	}
>  
>  	/* Parse the associated GPIOs. */
>  	if (uvc_gpio_parse(dev) < 0) {
> -		uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n");
> +		uvc_trace(dev, UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n");
>  		goto error;
>  	}
>  
> @@ -2524,7 +2536,7 @@ static int uvc_probe(struct usb_interface *intf,
>  			 "Unable to request uvc_privacy_gpio irq %d. Continuing wihtout privacy events\n",
>  			 ret);
>  
> -	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
> +	uvc_trace(dev, UVC_TRACE_PROBE, "UVC device initialized.\n");
>  	usb_enable_autosuspend(udev);
>  	return 0;
>  
> @@ -2556,7 +2568,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_trace(dev, UVC_TRACE_SUSPEND, "Suspending interface %u\n",
>  		intf->cur_altsetting->desc.bInterfaceNumber);
>  
>  	/* Controls are cached on the fly so they don't need to be saved. */
> @@ -2574,8 +2586,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_trace(dev, UVC_TRACE_SUSPEND,
> +		  "Suspend: video streaming USB interface mismatch.\n");
>  	return -EINVAL;
>  }
>  
> @@ -2585,8 +2597,8 @@ 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",
> -		intf->cur_altsetting->desc.bInterfaceNumber);
> +	uvc_trace(dev, UVC_TRACE_SUSPEND, "Resuming interface %u\n",
> +		  intf->cur_altsetting->desc.bInterfaceNumber);
>  
>  	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
>  	    UVC_SC_VIDEOCONTROL) {
> @@ -2614,8 +2626,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
>  		}
>  	}
>  
> -	uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
> -			"mismatch.\n");
> +	uvc_trace(dev, UVC_TRACE_SUSPEND,
> +		  "Resume: video streaming USB interface mismatch.\n");
>  	return -EINVAL;
>  }
>  
> diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c
> index 135fd7fe6852..d06fa2ea9420 100644
> --- a/drivers/media/usb/uvc/uvc_isight.c
> +++ b/drivers/media/usb/uvc/uvc_isight.c
> @@ -43,21 +43,23 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
>  	unsigned int maxlen, nbytes;
>  	u8 *mem;
>  	int is_header = 0;
> +	struct uvc_streaming *stream = uvc_queue_to_stream(queue);

The driver more or less orders variable declarations by line size, could
you please move this up ?

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  
>  	if (buf == NULL)
>  		return 0;
>  
>  	if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) ||
>  	    (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) {
> -		uvc_trace(UVC_TRACE_FRAME, "iSight header found\n");
> +		uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +			  "iSight header found\n");
>  		is_header = 1;
>  	}
>  
>  	/* Synchronize to the input stream by waiting for a header packet. */
>  	if (buf->state != UVC_BUF_STATE_ACTIVE) {
>  		if (!is_header) {
> -			uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of "
> -				  "sync).\n");
> +			uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +				  "Dropping packet (out of sync).\n");
>  			return 0;
>  		}
>  
> @@ -85,8 +87,8 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
>  		buf->bytesused += nbytes;
>  
>  		if (len > maxlen || buf->bytesused == buf->length) {
> -			uvc_trace(UVC_TRACE_FRAME, "Frame complete "
> -				  "(overflow).\n");
> +			uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +				  "Frame complete (overflow).\n");
>  			buf->state = UVC_BUF_STATE_DONE;
>  		}
>  	}
> @@ -103,8 +105,8 @@ void uvc_video_decode_isight(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
>  
>  	for (i = 0; i < urb->number_of_packets; ++i) {
>  		if (urb->iso_frame_desc[i].status < 0) {
> -			uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
> -				  "lost (%d).\n",
> +			uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +				  "USB isochronous frame lost (%d).\n",
>  				  urb->iso_frame_desc[i].status);
>  		}
>  
> diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> index e800d491303f..66f2b2f19d65 100644
> --- a/drivers/media/usb/uvc/uvc_queue.c
> +++ b/drivers/media/usb/uvc/uvc_queue.c
> @@ -32,12 +32,6 @@
>   * the driver.
>   */
>  
> -static inline struct uvc_streaming *
> -uvc_queue_to_stream(struct uvc_video_queue *queue)
> -{
> -	return container_of(queue, struct uvc_streaming, queue);
> -}
> -
>  static inline struct uvc_buffer *uvc_vbuf_to_buffer(struct vb2_v4l2_buffer *buf)
>  {
>  	return container_of(buf, struct uvc_buffer, buf);
> @@ -109,7 +103,8 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
>  
>  	if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
>  	    vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
> +		uvc_trace(uvc_queue_to_stream(queue)->dev, UVC_TRACE_CAPTURE,
> +			  "[E] Bytes used out of bounds.\n");
>  		return -EINVAL;
>  	}
>  
> diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
> index 36fa196a9258..b5f493216fcd 100644
> --- a/drivers/media/usb/uvc/uvc_status.c
> +++ b/drivers/media/usb/uvc/uvc_status.c
> @@ -93,20 +93,20 @@ static void uvc_event_streaming(struct uvc_device *dev,
>  				struct uvc_streaming_status *status, int len)
>  {
>  	if (len < 3) {
> -		uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event "
> -				"received.\n");
> +		uvc_trace(dev, UVC_TRACE_STATUS,
> +			  "Invalid streaming status event received.\n");
>  		return;
>  	}
>  
>  	if (status->bEvent == 0) {
>  		if (len < 4)
>  			return;
> -		uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
> +		uvc_trace(dev, UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
>  			  status->bOriginator,
>  			  status->bValue[0] ? "pressed" : "released", len);
>  		uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]);
>  	} else {
> -		uvc_trace(UVC_TRACE_STATUS,
> +		uvc_trace(dev, UVC_TRACE_STATUS,
>  			  "Stream %u error event %02x len %d.\n",
>  			  status->bOriginator, status->bEvent, len);
>  	}
> @@ -163,12 +163,12 @@ static bool uvc_event_control(struct urb *urb,
>  
>  	if (len < 6 || status->bEvent != 0 ||
>  	    status->bAttribute >= ARRAY_SIZE(attrs)) {
> -		uvc_trace(UVC_TRACE_STATUS, "Invalid control status event "
> -				"received.\n");
> +		uvc_trace(dev, UVC_TRACE_STATUS,
> +			  "Invalid control status event received.\n");
>  		return false;
>  	}
>  
> -	uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
> +	uvc_trace(dev, UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
>  		  status->bOriginator, status->bSelector,
>  		  attrs[status->bAttribute], len);
>  
> @@ -236,8 +236,9 @@ static void uvc_status_complete(struct urb *urb)
>  		}
>  
>  		default:
> -			uvc_trace(UVC_TRACE_STATUS, "Unknown status event "
> -				"type %u.\n", dev->status[0]);
> +			uvc_trace(dev, UVC_TRACE_STATUS,
> +				  "Unknown status event type %u.\n",
> +				  dev->status[0]);
>  			break;
>  		}
>  	}
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index fa06bfa174ad..3a2d17bc766b 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -75,8 +75,9 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
>  		break;
>  
>  	default:
> -		uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
> -			  "%u.\n", xmap->v4l2_type);
> +		uvc_trace(chain->dev, UVC_TRACE_CONTROL,
> +			  "Unsupported V4L2 control type %u.\n",
> +			  xmap->v4l2_type);
>  		ret = -ENOTTY;
>  		goto free_map;
>  	}
> @@ -164,10 +165,11 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
>  		return -EINVAL;
>  
>  	fcc = (u8 *)&fmt->fmt.pix.pixelformat;
> -	uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
> -			fmt->fmt.pix.pixelformat,
> -			fcc[0], fcc[1], fcc[2], fcc[3],
> -			fmt->fmt.pix.width, fmt->fmt.pix.height);
> +	uvc_trace(stream->dev, UVC_TRACE_FORMAT,
> +		  "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
> +		  fmt->fmt.pix.pixelformat,
> +		  fcc[0], fcc[1], fcc[2], fcc[3],
> +		  fmt->fmt.pix.width, fmt->fmt.pix.height);
>  
>  	/* Check if the hardware supports the requested format, use the default
>  	 * format otherwise.
> @@ -207,16 +209,18 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
>  	}
>  
>  	if (frame == NULL) {
> -		uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n",
> -				fmt->fmt.pix.width, fmt->fmt.pix.height);
> +		uvc_trace(stream->dev, UVC_TRACE_FORMAT,
> +			  "Unsupported size %ux%u.\n", fmt->fmt.pix.width,
> +			  fmt->fmt.pix.height);
>  		return -EINVAL;
>  	}
>  
>  	/* Use the default frame interval. */
>  	interval = frame->dwDefaultFrameInterval;
> -	uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us "
> -		"(%u.%u fps).\n", interval/10, interval%10, 10000000/interval,
> -		(100000000/interval)%10);
> +	uvc_trace(stream->dev, UVC_TRACE_FORMAT,
> +		  "Using default frame interval %u.%u us (%u.%u fps).\n",
> +		  interval / 10, interval % 10, 10000000 / interval,
> +		 (100000000 / interval) % 10);
>  
>  	/* Set the format index, frame index and frame interval. */
>  	memset(probe, 0, sizeof(*probe));
> @@ -258,7 +262,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
>  	}
>  
>  	if (i == stream->nformats) {
> -		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFormatIndex %u\n",
> +		uvc_trace(stream->dev, UVC_TRACE_FORMAT,
> +			  "Unknown bFormatIndex %u\n",
>  			  probe->bFormatIndex);
>  		return -EINVAL;
>  	}
> @@ -271,7 +276,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
>  	}
>  
>  	if (i == format->nframes) {
> -		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFrameIndex %u\n",
> +		uvc_trace(stream->dev, UVC_TRACE_FORMAT,
> +			  "Unknown bFrameIndex %u\n",
>  			  probe->bFrameIndex);
>  		return -EINVAL;
>  	}
> @@ -416,8 +422,9 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
>  
>  	interval = uvc_fraction_to_interval(timeperframe.numerator,
>  		timeperframe.denominator);
> -	uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
> -		timeperframe.numerator, timeperframe.denominator, interval);
> +	uvc_trace(stream->dev, UVC_TRACE_FORMAT,
> +		  "Setting frame interval to %u/%u (%u).\n",
> +		  timeperframe.numerator, timeperframe.denominator, interval);
>  
>  	mutex_lock(&stream->mutex);
>  
> @@ -545,8 +552,8 @@ static int uvc_v4l2_open(struct file *file)
>  	struct uvc_fh *handle;
>  	int ret = 0;
>  
> -	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
>  	stream = video_drvdata(file);
> +	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
>  
>  	ret = usb_autopm_get_interface(stream->dev->intf);
>  	if (ret < 0)
> @@ -588,7 +595,7 @@ static int uvc_v4l2_release(struct file *file)
>  	struct uvc_fh *handle = file->private_data;
>  	struct uvc_streaming *stream = handle->stream;
>  
> -	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
> +	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
>  
>  	/* Only free resources if this is a privileged handle. */
>  	if (uvc_has_privileges(handle))
> @@ -1461,7 +1468,11 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
>  static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
>  		    size_t count, loff_t *ppos)
>  {
> -	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
> +	struct uvc_fh *handle = file->private_data;
> +	struct uvc_streaming *stream = handle->stream;
> +
> +	uvc_trace(stream->dev, UVC_TRACE_CALLS,
> +		  "uvc_v4l2_read: not implemented.\n");
>  	return -EINVAL;
>  }
>  
> @@ -1470,7 +1481,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
>  	struct uvc_fh *handle = file->private_data;
>  	struct uvc_streaming *stream = handle->stream;
>  
> -	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n");
> +	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
>  
>  	return uvc_queue_mmap(&stream->queue, vma);
>  }
> @@ -1480,7 +1491,7 @@ static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait)
>  	struct uvc_fh *handle = file->private_data;
>  	struct uvc_streaming *stream = handle->stream;
>  
> -	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
> +	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
>  
>  	return uvc_queue_poll(&stream->queue, file, wait);
>  }
> @@ -1493,7 +1504,7 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
>  	struct uvc_fh *handle = file->private_data;
>  	struct uvc_streaming *stream = handle->stream;
>  
> -	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n");
> +	uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__);
>  
>  	return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
>  }
> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> index 71e643e6d006..26251ed0c3fd 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -95,7 +95,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
>  	if (ret != 1)
>  		return ret < 0 ? ret : -EPIPE;
>  
> -	uvc_trace(UVC_TRACE_CONTROL, "Control error %u\n", error);
> +	uvc_trace(dev, UVC_TRACE_CONTROL, "Control error %u\n", error);
>  
>  	switch (error) {
>  	case 0:
> @@ -705,8 +705,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
>  
>  	sof = y;
>  
> -	uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu "
> -		  "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
> +	uvc_trace(stream->dev, UVC_TRACE_CLOCK,
> +		  "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
>  		  stream->dev->name, buf->pts,
>  		  y >> 16, div_u64((y & 0xffff) * 1000000, 65536),
>  		  sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
> @@ -740,8 +740,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
>  
>  	timestamp = ktime_to_ns(first->host_time) + y - y1;
>  
> -	uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %llu "
> -		  "buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
> +	uvc_trace(stream->dev, UVC_TRACE_CLOCK,
> +		  "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
>  		  stream->dev->name,
>  		  sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
>  		  y, timestamp, vbuf->vb2_buf.timestamp,
> @@ -875,9 +875,8 @@ static void uvc_video_stats_update(struct uvc_streaming *stream)
>  {
>  	struct uvc_stats_frame *frame = &stream->stats.frame;
>  
> -	uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
> -		  "%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
> -		  "last pts/stc/sof %u/%u/%u\n",
> +	uvc_trace(stream->dev, UVC_TRACE_STATS,
> +		  "frame %u stats: %u/%u/%u packets, %u/%u/%u pts (%searly %sinitial), %u/%u scr, last pts/stc/sof %u/%u/%u\n",
>  		  stream->sequence, frame->first_data,
>  		  frame->nb_packets - frame->nb_empty, frame->nb_packets,
>  		  frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
> @@ -1039,8 +1038,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
>  
>  	/* Mark the buffer as bad if the error bit is set. */
>  	if (data[1] & UVC_STREAM_ERR) {
> -		uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit "
> -			  "set).\n");
> +		uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +			  "Marking buffer as bad (error bit set).\n");
>  		buf->error = 1;
>  	}
>  
> @@ -1054,8 +1053,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
>  	 */
>  	if (buf->state != UVC_BUF_STATE_ACTIVE) {
>  		if (fid == stream->last_fid) {
> -			uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
> -				"sync).\n");
> +			uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +				  "Dropping payload (out of sync).\n");
>  			if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
>  			    (data[1] & UVC_STREAM_EOF))
>  				stream->last_fid ^= UVC_STREAM_FID;
> @@ -1086,8 +1085,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
>  	 * previous payload had the EOF bit set.
>  	 */
>  	if (fid != stream->last_fid && buf->bytesused != 0) {
> -		uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
> -				"toggled).\n");
> +		uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +			  "Frame complete (FID bit toggled).\n");
>  		buf->state = UVC_BUF_STATE_READY;
>  		return -EAGAIN;
>  	}
> @@ -1148,7 +1147,8 @@ static void uvc_video_decode_data(struct uvc_urb *uvc_urb,
>  
>  	/* Complete the current frame if the buffer size was exceeded. */
>  	if (len > maxlen) {
> -		uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
> +		uvc_trace(uvc_urb->stream->dev, UVC_TRACE_FRAME,
> +			  "Frame complete (overflow).\n");
>  		buf->error = 1;
>  		buf->state = UVC_BUF_STATE_READY;
>  	}
> @@ -1161,9 +1161,11 @@ static void uvc_video_decode_end(struct uvc_streaming *stream,
>  {
>  	/* Mark the buffer as done if the EOF marker is set. */
>  	if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
> -		uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
> +		uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +			  "Frame complete (EOF found).\n");
>  		if (data[0] == len)
> -			uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
> +			uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +				  "EOF in empty payload.\n");
>  		buf->state = UVC_BUF_STATE_READY;
>  		if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
>  			stream->last_fid ^= UVC_STREAM_FID;
> @@ -1279,7 +1281,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
>  	memcpy(&meta->length, mem, length);
>  	meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof);
>  
> -	uvc_trace(UVC_TRACE_FRAME,
> +	uvc_trace(stream->dev, UVC_TRACE_FRAME,
>  		  "%s(): t-sys %lluns, SOF %u, len %u, flags 0x%x, PTS %u, STC %u frame SOF %u\n",
>  		  __func__, ktime_to_ns(time), meta->sof, meta->length,
>  		  meta->flags,
> @@ -1339,8 +1341,9 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
>  
>  	for (i = 0; i < urb->number_of_packets; ++i) {
>  		if (urb->iso_frame_desc[i].status < 0) {
> -			uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
> -				"lost (%d).\n", urb->iso_frame_desc[i].status);
> +			uvc_trace(stream->dev, UVC_TRACE_FRAME,
> +				  "USB isochronous frame lost (%d).\n",
> +				  urb->iso_frame_desc[i].status);
>  			/* Mark the buffer as faulty. */
>  			if (buf != NULL)
>  				buf->error = 1;
> @@ -1628,15 +1631,16 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
>  		}
>  
>  		if (i == UVC_URBS) {
> -			uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
> -				"of %ux%u bytes each.\n", UVC_URBS, npackets,
> -				psize);
> +			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
> +				  "Allocated %u URB buffers of %ux%u bytes each.\n",
> +				  UVC_URBS, npackets, psize);
>  			return npackets;
>  		}
>  	}
>  
> -	uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
> -		"per packet).\n", psize);
> +	uvc_trace(stream->dev, UVC_TRACE_VIDEO,
> +		  "Failed to allocate URB buffers (%u bytes per packet).\n",
> +		  psize);
>  	return 0;
>  }
>  
> @@ -1835,12 +1839,13 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
>  		bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
>  
>  		if (bandwidth == 0) {
> -			uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
> -				"bandwidth, defaulting to lowest.\n");
> +			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
> +				  "Device requested null bandwidth, defaulting to lowest.\n");
>  			bandwidth = 1;
>  		} else {
> -			uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
> -				"B/frame bandwidth.\n", bandwidth);
> +			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
> +				  "Device requested %u B/frame bandwidth.\n",
> +				  bandwidth);
>  		}
>  
>  		for (i = 0; i < intf->num_altsetting; ++i) {
> @@ -1863,13 +1868,14 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
>  		}
>  
>  		if (best_ep == NULL) {
> -			uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
> -				"for requested bandwidth.\n");
> +			uvc_trace(stream->dev, UVC_TRACE_VIDEO,
> +				  "No fast enough alt setting for requested bandwidth.\n");
>  			return -EIO;
>  		}
>  
> -		uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
> -			"(%u B/frame bandwidth).\n", altsetting, best_psize);
> +		uvc_trace(stream->dev, UVC_TRACE_VIDEO,
> +			  "Selecting alternate setting %u (%u B/frame bandwidth).\n",
> +			  altsetting, best_psize);
>  
>  		ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
>  		if (ret < 0)
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 4a7d1fb76eee..60626feeee27 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -757,10 +757,11 @@ extern unsigned int uvc_trace_param;
>  extern unsigned int uvc_timeout_param;
>  extern unsigned int uvc_hw_timestamps_param;
>  
> -#define uvc_trace(flag, fmt, ...)					\
> +#define uvc_trace(_dev, flag, fmt, ...)					\
>  do {									\
>  	if (uvc_trace_param & flag)					\
> -		printk(KERN_DEBUG "uvcvideo: " fmt, ##__VA_ARGS__);	\
> +		dev_printk(KERN_DEBUG, &(_dev)->udev->dev, fmt,		\
> +			   ##__VA_ARGS__);				\
>  } while (0)
>  
>  #define uvc_trace_cont(flag, fmt, ...)					\
> @@ -822,6 +823,12 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
>  	return vb2_is_streaming(&queue->queue);
>  }
>  
> +static inline struct uvc_streaming *
> +uvc_queue_to_stream(struct uvc_video_queue *queue)
> +{
> +	return container_of(queue, struct uvc_streaming, queue);
> +}
> +
>  /* V4L2 interface */
>  extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
>  extern const struct v4l2_file_operations uvc_fops;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  2020-12-21 16:48 ` [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM Ricardo Ribalda
@ 2020-12-22 10:29   ` Laurent Pinchart
  2020-12-22 20:04     ` Ricardo Ribalda
  0 siblings, 1 reply; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-22 10:29 UTC (permalink / raw)
  To: Ricardo Ribalda; +Cc: Mauro Carvalho Chehab, linux-media, linux-kernel

Hi Ricardo,

Thank you for the patch.

On Mon, Dec 21, 2020 at 05:48:16PM +0100, Ricardo Ribalda wrote:
> Some devices, can only read the privacy_pin if the device is

s/devices,/devices/

> streaming.
> 
> This patch implement a quirk for such devices, in order to avoid invalid
> reads and/or spurious events.
> 
> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++++++++++++++++--
>  drivers/media/usb/uvc/uvc_queue.c  |  3 ++
>  drivers/media/usb/uvc/uvcvideo.h   |  4 +++
>  3 files changed, 61 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index 72516101fdd0..7af37d4bd60a 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/dmi.h>
>  #include <linux/gpio/consumer.h>
>  #include <linux/kernel.h>
>  #include <linux/list.h>
> @@ -1472,6 +1473,17 @@ static int uvc_parse_control(struct uvc_device *dev)
>  /* -----------------------------------------------------------------------------
>   * Privacy GPIO
>   */

There should be a blank line here.

> +static bool uvc_gpio_is_streaming(struct uvc_device *dev)
> +{
> +	struct uvc_streaming *streaming;
> +
> +	list_for_each_entry(streaming, &dev->streams, list) {
> +		if (uvc_queue_streaming(&streaming->queue))
> +			return true;
> +	}
> +
> +	return false;
> +}
>  
>  

But not too blank lines here.

>  static u8 uvc_gpio_update_value(struct uvc_device *dev,
> @@ -1499,7 +1511,12 @@ static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
>  	if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
>  		return -EINVAL;
>  
> +	if ((dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM) &&
> +	    !uvc_gpio_is_streaming(dev))
> +		return -EBUSY;
> +
>  	*(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> +
>  	return 0;
>  }
>  
> @@ -1528,19 +1545,50 @@ static struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
>  	return NULL;
>  }
>  
> -static irqreturn_t uvc_gpio_irq(int irq, void *data)
> +void uvc_privacy_gpio_event(struct uvc_device *dev)
>  {
> -	struct uvc_device *dev = data;
>  	struct uvc_entity *unit;
>  
> +
>  	unit = uvc_gpio_find_entity(dev);
>  	if (!unit)
> -		return IRQ_HANDLED;
> +		return;
>  
>  	uvc_gpio_update_value(dev, unit);
> +}
> +
> +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> +{
> +	struct uvc_device *dev = data;
> +
> +	/* Ignore privacy events during streamoff */
> +	if (dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> +		if (!uvc_gpio_is_streaming(dev))
> +			return IRQ_HANDLED;

I'm still a bit concerned of race conditions. When stopping the stream,
vb2_queue.streaming is set to 0 after calling the driver's .stop_stream()
handler. This means that the device will cut power before
uvc_gpio_is_streaming() can detect that streaming has stopped, and the
GPIO could thus trigger an IRQ.

You mentioned that devices have a pull-up or pull-down on the GPIO line.
As there are only two devices affected, do you know if it's a pull-up or
pull-down ? Would it be worse to expose that state to userspace than to
return -EBUSY when reading the control ?

> +
> +	uvc_privacy_gpio_event(dev);
> +
>  	return IRQ_HANDLED;
>  }
>  
> +static const struct dmi_system_id privacy_valid_during_streamon[] = {
> +	{
> +		.ident = "HP Elite c1030 Chromebook",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Jinlon"),
> +		},
> +	},
> +	{
> +		.ident = "HP Pro c640 Chromebook",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Dratini"),
> +		},
> +	},
> +	{ } /* terminate list */
> +};
> +
>  static int uvc_gpio_parse(struct uvc_device *dev)
>  {
>  	struct uvc_entity *unit;
> @@ -1577,6 +1625,9 @@ static int uvc_gpio_parse(struct uvc_device *dev)
>  
>  	list_add_tail(&unit->list, &dev->entities);
>  
> +	if (dmi_check_system(privacy_valid_during_streamon))
> +		dev->quirks |= UVC_QUIRK_PRIVACY_DURING_STREAM;

This will also match any external UVC camera plugged to one of the
affected systems, right ? It shouldn't matter in practice as those
devices won't have a GPIO entity.

I suppose we can't match on VID:PID instead because the same VID:PID is
used in both devices affected by this issue, and devices immune to it ?

> +
>  	return 0;
>  }
>  
> diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> index cd60c6c1749e..e800d491303f 100644
> --- a/drivers/media/usb/uvc/uvc_queue.c
> +++ b/drivers/media/usb/uvc/uvc_queue.c
> @@ -337,9 +337,12 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
>  int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
>  {
>  	int ret;
> +	struct uvc_streaming *stream = uvc_queue_to_stream(queue);

Please swap the two lines.

>  
>  	mutex_lock(&queue->mutex);
>  	ret = vb2_streamon(&queue->queue, type);
> +	if (stream->dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> +		uvc_privacy_gpio_event(stream->dev);

Even when vb2_streamon() failed ?

>  	mutex_unlock(&queue->mutex);
>  
>  	return ret;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 079a407ebba5..32c1ba246d97 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -209,6 +209,7 @@
>  #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT	0x00000400
>  #define UVC_QUIRK_FORCE_Y8		0x00000800
>  #define UVC_QUIRK_FORCE_BPP		0x00001000
> +#define UVC_QUIRK_PRIVACY_DURING_STREAM	0x00002000
>  
>  /* Format flags */
>  #define UVC_FMT_FLAG_COMPRESSED		0x00000001
> @@ -826,6 +827,9 @@ extern const struct v4l2_file_operations uvc_fops;
>  int uvc_mc_register_entities(struct uvc_video_chain *chain);
>  void uvc_mc_cleanup_entity(struct uvc_entity *entity);
>  
> +/* Privacy gpio */
> +void uvc_privacy_gpio_event(struct uvc_device *dev);
> +
>  /* Video */
>  int uvc_video_init(struct uvc_streaming *stream);
>  int uvc_video_suspend(struct uvc_streaming *stream);

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT
  2020-12-22  8:33   ` Laurent Pinchart
  2020-12-22  8:37     ` Laurent Pinchart
@ 2020-12-22 18:36     ` Ricardo Ribalda
  2020-12-23  7:54       ` Laurent Pinchart
  1 sibling, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-22 18:36 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List,
	Linux Kernel Mailing List

Hi Laurent

Thanks for your review!

On Tue, Dec 22, 2020 at 9:34 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Ricardo,
>
> Thank you for the patch.
>
> On Mon, Dec 21, 2020 at 05:48:14PM +0100, Ricardo Ribalda wrote:
> > Some devices can implement a physical switch to disable the input of the
> > camera on demand. Think of it like an elegant privacy sticker.
> >
> > The system can read the status of the privacy switch via a GPIO.
> >
> > It is important to know the status of the switch, e.g. to notify the
> > user when the camera will produce black frames and a videochat
> > application is used.
> >
> > In some systems, the GPIO is connected to main SoC instead of the
> > camera controller, with the connected reported by the system firmware
>
> s/connected/connection/
>
> > (ACPI or DT). In that case, the UVC device isn't aware of the GPIO. We
> > need to implement a virtual entity to handle the GPIO fully on the
> > driver side.
> >
> > For example, for ACPI-based systems, the GPIO is reported in the USB
> > device object:
> >
> >   Scope (\_SB.PCI0.XHCI.RHUB.HS07)
> >   {
> >
> >         /.../
> >
> >     Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
> >     {
> >         GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
> >             "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, ,
> >             )
> >             {   // Pin list
> >                 0x0064
> >             }
> >     })
> >     Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
> >     {
> >         ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
> >         Package (0x01)
> >         {
> >             Package (0x02)
> >             {
> >                 "privacy-gpio",
> >                 Package (0x04)
> >                 {
> >                     \_SB.PCI0.XHCI.RHUB.HS07,
> >                     Zero,
> >                     Zero,
> >                     One
> >                 }
> >             }
> >         }
> >     })
> >   }
> >
> > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > ---
> >  drivers/media/usb/uvc/uvc_ctrl.c   |   7 ++
> >  drivers/media/usb/uvc/uvc_driver.c | 156 +++++++++++++++++++++++++++++
> >  drivers/media/usb/uvc/uvc_entity.c |   1 +
> >  drivers/media/usb/uvc/uvcvideo.h   |  16 +++
> >  4 files changed, 180 insertions(+)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 528254230535..a430fa666897 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -1300,6 +1300,10 @@ static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
> >
> >       mutex_unlock(&chain->ctrl_mutex);
> >
> > +     /* Events not started by the UVC device. E.g. the GPIO unit */
> > +     if (!w->urb)
> > +             return;
> > +
> >       /* Resubmit the URB. */
> >       w->urb->interval = dev->int_ep->desc.bInterval;
> >       ret = usb_submit_urb(w->urb, GFP_KERNEL);
> > @@ -2317,6 +2321,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
> >               } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
> >                       bmControls = entity->camera.bmControls;
> >                       bControlSize = entity->camera.bControlSize;
> > +             } else if (UVC_ENTITY_TYPE(entity) == UVC_EXT_GPIO_UNIT) {
> > +                     bmControls = entity->gpio.bmControls;
> > +                     bControlSize = entity->gpio.bControlSize;
> >               }
> >
> >               /* Remove bogus/blacklisted controls */
> > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > index c0c5f75ade40..72516101fdd0 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>
> > @@ -1020,6 +1021,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
> >  }
> >
> >  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;
> > @@ -1051,6 +1053,9 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
> >        * 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;
> > @@ -1464,6 +1469,137 @@ static int uvc_parse_control(struct uvc_device *dev)
> >       return 0;
> >  }
> >
> > +/* -----------------------------------------------------------------------------
> > + * Privacy GPIO
> > + */
> > +
> > +
> > +static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > +                             struct uvc_entity *unit)
> > +{
> > +     struct uvc_video_chain *chain;
> > +     u8 gpio_val, old_val, new_val;
> > +
> > +     gpio_val = new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
> > +
> > +     old_val = atomic_xchg(&unit->gpio.gpio_privacy_value, gpio_val);
> > +     if (new_val == old_val)
> > +             return new_val;
> > +
> > +     /* GPIO entities are always on the first chain. */
> > +     chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > +     uvc_ctrl_status_event(NULL, chain, unit->controls, &new_val);
> > +
> > +     return 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;
> > +
> > +     *(uint8_t *)data = uvc_gpio_update_value(dev, entity);
>
> s/uint8_t/u8/
>
> Do we really need to generate an event when the GPIO is read, if there's
> no IRQ support ?

I think so. Imagine a scenario where you have one process checking for
events and notifying the user about changes, and another process
handling the camera.
Also I think that is the way the v4l2-ctrl behaves.



>
> > +     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 struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> > +{
> > +     struct uvc_video_chain *chain;
> > +     struct uvc_entity *unit;
> > +
> > +     /* GPIO entities are always on the first chain. */
> > +     chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > +     list_for_each_entry(unit, &chain->entities, chain) {
> > +             if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> > +                     return unit;
> > +     }
>
> As there can only be a single GPIO entity, would it make sense to store
> a pointer to it in struct uvc_device to avoid walking the chain every
> time ?
>
> > +
> > +     return NULL;
> > +}
> > +
> > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > +{
> > +     struct uvc_device *dev = data;
> > +     struct uvc_entity *unit;
> > +
> > +     unit = uvc_gpio_find_entity(dev);
> > +     if (!unit)
> > +             return IRQ_HANDLED;
> > +
> > +     uvc_gpio_update_value(dev, unit);
> > +     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 == -EPROBE_DEFER)
> > +             return -EPROBE_DEFER;
> > +
> > +     if (irq < 0)
> > +             dev_warn(&dev->udev->dev,
> > +                    "Unable to find privacy_gpio: %d\n", irq);
>
> That's not right, the GPIO is found, it's only the IRQ that is
> unsupported.
>
>                 dev_warn(&dev->udev->dev,
>                          "No IRQ for privacy GPIO (%d)\n", irq);
>
> > +
> > +     atomic_set(&unit->gpio.gpio_privacy_value, -1);
> > +     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;
> > +
> > +     sprintf(unit->name, "GPIO");
>
> strncpy() would be simpler.
>
> > +
> > +     list_add_tail(&unit->list, &dev->entities);
> > +
> > +     return 0;
> > +}
> > +
> > +static int uvc_gpio_init_irq(struct uvc_device *dev)
> > +{
> > +     int ret;
> > +     struct uvc_entity *unit;
>
> Could you please swap those two lines ?
>
> > +
> > +     unit = uvc_gpio_find_entity(dev);
> > +     if (!unit)
> > +             return 0;
> > +
> > +     if (unit->gpio.irq < 0)
> > +             return 0;
> > +
> > +     ret = devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
> > +                                     uvc_gpio_irq,
> > +                                     IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
> > +                                      IRQF_TRIGGER_RISING,
>
> Extra space in the indentation.
>
> > +                                     "uvc_privacy_gpio", dev);
> > +     return ret;
>
> No need for a ret variable, you can write
>
>         return devm_request_threaded_irq(...);
>
> (unless you want to move the error message here, see below).
>
> > +}
> > +
> >  /* ------------------------------------------------------------------------
> >   * UVC device scan
> >   */
> > @@ -1915,6 +2051,7 @@ static int uvc_scan_device(struct uvc_device *dev)
> >  {
> >       struct uvc_video_chain *chain;
> >       struct uvc_entity *term;
> > +     struct uvc_entity *unit;
> >
> >       list_for_each_entry(term, &dev->entities, list) {
> >               if (!UVC_ENTITY_IS_OTERM(term))
> > @@ -1953,6 +2090,13 @@ static int uvc_scan_device(struct uvc_device *dev)
> >               return -1;
> >       }
> >
> > +     /* Add GPIO entities to the first chain. */
> > +     chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > +     list_for_each_entry(unit, &dev->entities, list) {
> > +             if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> > +                     list_add_tail(&unit->chain, &chain->entities);
> > +     }
> > +
> >       return 0;
> >  }
> >
> > @@ -2285,6 +2429,12 @@ static int uvc_probe(struct usb_interface *intf,
> >               goto error;
> >       }
> >
> > +     /* Parse the associated GPIOs. */
> > +     if (uvc_gpio_parse(dev) < 0) {
> > +             uvc_trace(UVC_TRACE_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>",
> > @@ -2329,6 +2479,12 @@ static int uvc_probe(struct usb_interface *intf,
> >                       "supported.\n", ret);
> >       }
> >
> > +     ret = uvc_gpio_init_irq(dev);
> > +     if (ret < 0)
> > +             dev_warn(&dev->udev->dev,
> > +                      "Unable to request uvc_privacy_gpio irq %d. Continuing wihtout privacy events\n",
>
> s/uvc_privacy_gpio irq/privacy GPIO IRQ/ ?
>
> > +                      ret);
>
> This could be moved to uvc_gpio_init_irq(), up to you.
>
> > +
> >       uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
> >       usb_enable_autosuspend(udev);
> >       return 0;
> > diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
> > index ca3a9c2eec27..6a9ba5b498db 100644
> > --- a/drivers/media/usb/uvc/uvc_entity.c
> > +++ b/drivers/media/usb/uvc/uvc_entity.c
> > @@ -105,6 +105,7 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain,
> >               case UVC_OTT_DISPLAY:
> >               case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
> >               case UVC_EXTERNAL_VENDOR_SPECIFIC:
> > +             case UVC_EXT_GPIO_UNIT:
> >               default:
> >                       function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
> >                       break;
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 6edbf79b2ff1..079a407ebba5 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -6,6 +6,7 @@
> >  #error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
> >  #endif /* __KERNEL__ */
> >
> > +#include <linux/atomic.h>
> >  #include <linux/kernel.h>
> >  #include <linux/poll.h>
> >  #include <linux/usb.h>
> > @@ -37,6 +38,8 @@
> >       (UVC_ENTITY_IS_TERM(entity) && \
> >       ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
> >
> > +#define UVC_EXT_GPIO_UNIT            0x7ffe
> > +#define UVC_EXT_GPIO_UNIT_ID         0x100
> >
> >  /* ------------------------------------------------------------------------
> >   * GUIDs
> > @@ -56,6 +59,9 @@
> >  #define UVC_GUID_UVC_SELECTOR \
> >       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> >        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
> > +#define UVC_GUID_EXT_GPIO_CONTROLLER \
> > +     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> > +      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03}
> >
> >  #define UVC_GUID_FORMAT_MJPEG \
> >       { 'M',  'J',  'P',  'G', 0x00, 0x00, 0x10, 0x00, \
> > @@ -278,6 +284,8 @@ struct uvc_format_desc {
> >       u32 fcc;
> >  };
> >
> > +struct gpio_desc;
> > +
>
> This could be moved a bit up, with the other forward declaration.
>
> >  /* The term 'entity' refers to both UVC units and UVC terminals.
> >   *
> >   * The type field is either the terminal type (wTerminalType in the terminal
> > @@ -353,6 +361,14 @@ struct uvc_entity {
> >                       u8  *bmControls;
> >                       u8  *bmControlsType;
> >               } extension;
> > +
> > +             struct {
> > +                     u8  bControlSize;
> > +                     u8  *bmControls;
> > +                     struct gpio_desc *gpio_privacy;
> > +                     int irq;
> > +                     atomic_t gpio_privacy_value;
> > +             } gpio;
> >       };
> >
> >       u8 bNrInPins;
>
> --
> Regards,
>
> Laurent Pinchart



--
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  2020-12-22 10:29   ` Laurent Pinchart
@ 2020-12-22 20:04     ` Ricardo Ribalda
  2020-12-23  8:04       ` Laurent Pinchart
  0 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-22 20:04 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List,
	Linux Kernel Mailing List

HI Laurent

Thanks for your review!

On Tue, Dec 22, 2020 at 11:30 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Ricardo,
>
> Thank you for the patch.
>
> On Mon, Dec 21, 2020 at 05:48:16PM +0100, Ricardo Ribalda wrote:
> > Some devices, can only read the privacy_pin if the device is
>
> s/devices,/devices/
>
> > streaming.
> >
> > This patch implement a quirk for such devices, in order to avoid invalid
> > reads and/or spurious events.
> >
> > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > ---
> >  drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++++++++++++++++--
> >  drivers/media/usb/uvc/uvc_queue.c  |  3 ++
> >  drivers/media/usb/uvc/uvcvideo.h   |  4 +++
> >  3 files changed, 61 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > index 72516101fdd0..7af37d4bd60a 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/dmi.h>
> >  #include <linux/gpio/consumer.h>
> >  #include <linux/kernel.h>
> >  #include <linux/list.h>
> > @@ -1472,6 +1473,17 @@ static int uvc_parse_control(struct uvc_device *dev)
> >  /* -----------------------------------------------------------------------------
> >   * Privacy GPIO
> >   */
>
> There should be a blank line here.
>
> > +static bool uvc_gpio_is_streaming(struct uvc_device *dev)
> > +{
> > +     struct uvc_streaming *streaming;
> > +
> > +     list_for_each_entry(streaming, &dev->streams, list) {
> > +             if (uvc_queue_streaming(&streaming->queue))
> > +                     return true;
> > +     }
> > +
> > +     return false;
> > +}
> >
> >
>
> But not too blank lines here.
>
> >  static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > @@ -1499,7 +1511,12 @@ static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
> >       if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
> >               return -EINVAL;
> >
> > +     if ((dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM) &&
> > +         !uvc_gpio_is_streaming(dev))
> > +             return -EBUSY;
> > +
> >       *(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> > +
> >       return 0;
> >  }
> >
> > @@ -1528,19 +1545,50 @@ static struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> >       return NULL;
> >  }
> >
> > -static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > +void uvc_privacy_gpio_event(struct uvc_device *dev)
> >  {
> > -     struct uvc_device *dev = data;
> >       struct uvc_entity *unit;
> >
> > +
> >       unit = uvc_gpio_find_entity(dev);
> >       if (!unit)
> > -             return IRQ_HANDLED;
> > +             return;
> >
> >       uvc_gpio_update_value(dev, unit);
> > +}
> > +
> > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > +{
> > +     struct uvc_device *dev = data;
> > +
> > +     /* Ignore privacy events during streamoff */
> > +     if (dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > +             if (!uvc_gpio_is_streaming(dev))
> > +                     return IRQ_HANDLED;
>
> I'm still a bit concerned of race conditions. When stopping the stream,
> vb2_queue.streaming is set to 0 after calling the driver's .stop_stream()
> handler. This means that the device will cut power before
> uvc_gpio_is_streaming() can detect that streaming has stopped, and the
> GPIO could thus trigger an IRQ.

On the affected devices I have not seen this. I guess it takes some
time to discharge. Anyway I am implementing a workaround. Tell me if
it is too ugly.

>
> You mentioned that devices have a pull-up or pull-down on the GPIO line.
> As there are only two devices affected, do you know if it's a pull-up or
> pull-down ? Would it be worse to expose that state to userspace than to
> return -EBUSY when reading the control ?

The module has a 100K pull up. This is, it will return "Privacy = 0".

We cannot return the default value, as it would make the user believe
that the privacy is in a different state that currently is.
In other words, userspace needs to know at all times if the privacy is
in : unknow_state, on, off.

>
> > +
> > +     uvc_privacy_gpio_event(dev);
> > +
> >       return IRQ_HANDLED;
> >  }
> >
> > +static const struct dmi_system_id privacy_valid_during_streamon[] = {
> > +     {
> > +             .ident = "HP Elite c1030 Chromebook",
> > +             .matches = {
> > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Jinlon"),
> > +             },
> > +     },
> > +     {
> > +             .ident = "HP Pro c640 Chromebook",
> > +             .matches = {
> > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Dratini"),
> > +             },
> > +     },
> > +     { } /* terminate list */
> > +};
> > +
> >  static int uvc_gpio_parse(struct uvc_device *dev)
> >  {
> >       struct uvc_entity *unit;
> > @@ -1577,6 +1625,9 @@ static int uvc_gpio_parse(struct uvc_device *dev)
> >
> >       list_add_tail(&unit->list, &dev->entities);
> >
> > +     if (dmi_check_system(privacy_valid_during_streamon))
> > +             dev->quirks |= UVC_QUIRK_PRIVACY_DURING_STREAM;
>
> This will also match any external UVC camera plugged to one of the
> affected systems, right ? It shouldn't matter in practice as those
> devices won't have a GPIO entity.

I did think about that but did not make it explicit in the code.
Adding a comment.

>
> I suppose we can't match on VID:PID instead because the same VID:PID is
> used in both devices affected by this issue, and devices immune to it ?

The problem with VID:PID, is that the manufacturer can decide to
change the camera module and then the quirk will not work.

We cannot rely ONLY in VID:PID as these modules are also used in other
models not affected by the quirk.

I believe that it is also correct to rely on the dmi, as the quirk is
caused for the way the camera module is wired, which is on the
motherboard.


>
> > +
> >       return 0;
> >  }
> >
> > diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> > index cd60c6c1749e..e800d491303f 100644
> > --- a/drivers/media/usb/uvc/uvc_queue.c
> > +++ b/drivers/media/usb/uvc/uvc_queue.c
> > @@ -337,9 +337,12 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
> >  int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
> >  {
> >       int ret;
> > +     struct uvc_streaming *stream = uvc_queue_to_stream(queue);
>
> Please swap the two lines.
>
> >
> >       mutex_lock(&queue->mutex);
> >       ret = vb2_streamon(&queue->queue, type);
> > +     if (stream->dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > +             uvc_privacy_gpio_event(stream->dev);
>
> Even when vb2_streamon() failed ?
>
> >       mutex_unlock(&queue->mutex);
> >
> >       return ret;
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 079a407ebba5..32c1ba246d97 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -209,6 +209,7 @@
> >  #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT      0x00000400
> >  #define UVC_QUIRK_FORCE_Y8           0x00000800
> >  #define UVC_QUIRK_FORCE_BPP          0x00001000
> > +#define UVC_QUIRK_PRIVACY_DURING_STREAM      0x00002000
> >
> >  /* Format flags */
> >  #define UVC_FMT_FLAG_COMPRESSED              0x00000001
> > @@ -826,6 +827,9 @@ extern const struct v4l2_file_operations uvc_fops;
> >  int uvc_mc_register_entities(struct uvc_video_chain *chain);
> >  void uvc_mc_cleanup_entity(struct uvc_entity *entity);
> >
> > +/* Privacy gpio */
> > +void uvc_privacy_gpio_event(struct uvc_device *dev);
> > +
> >  /* Video */
> >  int uvc_video_init(struct uvc_streaming *stream);
> >  int uvc_video_suspend(struct uvc_streaming *stream);
>
> --
> Regards,
>
> Laurent Pinchart



-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT
  2020-12-22 18:36     ` Ricardo Ribalda
@ 2020-12-23  7:54       ` Laurent Pinchart
  0 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-23  7:54 UTC (permalink / raw)
  To: Ricardo Ribalda
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List,
	Linux Kernel Mailing List

Hi Ricardo,

On Tue, Dec 22, 2020 at 07:36:52PM +0100, Ricardo Ribalda wrote:
> On Tue, Dec 22, 2020 at 9:34 AM Laurent Pinchart wrote:
> > On Mon, Dec 21, 2020 at 05:48:14PM +0100, Ricardo Ribalda wrote:
> > > Some devices can implement a physical switch to disable the input of the
> > > camera on demand. Think of it like an elegant privacy sticker.
> > >
> > > The system can read the status of the privacy switch via a GPIO.
> > >
> > > It is important to know the status of the switch, e.g. to notify the
> > > user when the camera will produce black frames and a videochat
> > > application is used.
> > >
> > > In some systems, the GPIO is connected to main SoC instead of the
> > > camera controller, with the connected reported by the system firmware
> >
> > s/connected/connection/
> >
> > > (ACPI or DT). In that case, the UVC device isn't aware of the GPIO. We
> > > need to implement a virtual entity to handle the GPIO fully on the
> > > driver side.
> > >
> > > For example, for ACPI-based systems, the GPIO is reported in the USB
> > > device object:
> > >
> > >   Scope (\_SB.PCI0.XHCI.RHUB.HS07)
> > >   {
> > >
> > >         /.../
> > >
> > >     Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
> > >     {
> > >         GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
> > >             "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, ,
> > >             )
> > >             {   // Pin list
> > >                 0x0064
> > >             }
> > >     })
> > >     Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
> > >     {
> > >         ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
> > >         Package (0x01)
> > >         {
> > >             Package (0x02)
> > >             {
> > >                 "privacy-gpio",
> > >                 Package (0x04)
> > >                 {
> > >                     \_SB.PCI0.XHCI.RHUB.HS07,
> > >                     Zero,
> > >                     Zero,
> > >                     One
> > >                 }
> > >             }
> > >         }
> > >     })
> > >   }
> > >
> > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > ---
> > >  drivers/media/usb/uvc/uvc_ctrl.c   |   7 ++
> > >  drivers/media/usb/uvc/uvc_driver.c | 156 +++++++++++++++++++++++++++++
> > >  drivers/media/usb/uvc/uvc_entity.c |   1 +
> > >  drivers/media/usb/uvc/uvcvideo.h   |  16 +++
> > >  4 files changed, 180 insertions(+)
> > >
> > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > index 528254230535..a430fa666897 100644
> > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > @@ -1300,6 +1300,10 @@ static void __uvc_ctrl_status_event_work(struct uvc_device *dev,
> > >
> > >       mutex_unlock(&chain->ctrl_mutex);
> > >
> > > +     /* Events not started by the UVC device. E.g. the GPIO unit */
> > > +     if (!w->urb)
> > > +             return;
> > > +
> > >       /* Resubmit the URB. */
> > >       w->urb->interval = dev->int_ep->desc.bInterval;
> > >       ret = usb_submit_urb(w->urb, GFP_KERNEL);
> > > @@ -2317,6 +2321,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
> > >               } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
> > >                       bmControls = entity->camera.bmControls;
> > >                       bControlSize = entity->camera.bControlSize;
> > > +             } else if (UVC_ENTITY_TYPE(entity) == UVC_EXT_GPIO_UNIT) {
> > > +                     bmControls = entity->gpio.bmControls;
> > > +                     bControlSize = entity->gpio.bControlSize;
> > >               }
> > >
> > >               /* Remove bogus/blacklisted controls */
> > > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > > index c0c5f75ade40..72516101fdd0 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>
> > > @@ -1020,6 +1021,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
> > >  }
> > >
> > >  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;
> > > @@ -1051,6 +1053,9 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
> > >        * 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;
> > > @@ -1464,6 +1469,137 @@ static int uvc_parse_control(struct uvc_device *dev)
> > >       return 0;
> > >  }
> > >
> > > +/* -----------------------------------------------------------------------------
> > > + * Privacy GPIO
> > > + */
> > > +
> > > +
> > > +static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > > +                             struct uvc_entity *unit)
> > > +{
> > > +     struct uvc_video_chain *chain;
> > > +     u8 gpio_val, old_val, new_val;
> > > +
> > > +     gpio_val = new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
> > > +
> > > +     old_val = atomic_xchg(&unit->gpio.gpio_privacy_value, gpio_val);
> > > +     if (new_val == old_val)
> > > +             return new_val;
> > > +
> > > +     /* GPIO entities are always on the first chain. */
> > > +     chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > > +     uvc_ctrl_status_event(NULL, chain, unit->controls, &new_val);
> > > +
> > > +     return 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;
> > > +
> > > +     *(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> >
> > s/uint8_t/u8/
> >
> > Do we really need to generate an event when the GPIO is read, if there's
> > no IRQ support ?
> 
> I think so. Imagine a scenario where you have one process checking for
> events and notifying the user about changes, and another process
> handling the camera.

But the event will only be generated when the control is read, which the
process checking for events won't do (otherwise, it wouldn't need events
in the first place), and the process handling the video stream won't do
either. If the hardware can't generate events and polling is required,
I'm not sure trying to fake it would be useful.

> Also I think that is the way the v4l2-ctrl behaves.

v4l2-ctl is just a test tool, I'm more interested in real use cases :-)

> > > +     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 struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> > > +{
> > > +     struct uvc_video_chain *chain;
> > > +     struct uvc_entity *unit;
> > > +
> > > +     /* GPIO entities are always on the first chain. */
> > > +     chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > > +     list_for_each_entry(unit, &chain->entities, chain) {
> > > +             if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> > > +                     return unit;
> > > +     }
> >
> > As there can only be a single GPIO entity, would it make sense to store
> > a pointer to it in struct uvc_device to avoid walking the chain every
> > time ?
> >
> > > +
> > > +     return NULL;
> > > +}
> > > +
> > > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > +{
> > > +     struct uvc_device *dev = data;
> > > +     struct uvc_entity *unit;
> > > +
> > > +     unit = uvc_gpio_find_entity(dev);
> > > +     if (!unit)
> > > +             return IRQ_HANDLED;
> > > +
> > > +     uvc_gpio_update_value(dev, unit);
> > > +     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 == -EPROBE_DEFER)
> > > +             return -EPROBE_DEFER;
> > > +
> > > +     if (irq < 0)
> > > +             dev_warn(&dev->udev->dev,
> > > +                    "Unable to find privacy_gpio: %d\n", irq);
> >
> > That's not right, the GPIO is found, it's only the IRQ that is
> > unsupported.
> >
> >                 dev_warn(&dev->udev->dev,
> >                          "No IRQ for privacy GPIO (%d)\n", irq);
> >
> > > +
> > > +     atomic_set(&unit->gpio.gpio_privacy_value, -1);
> > > +     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;
> > > +
> > > +     sprintf(unit->name, "GPIO");
> >
> > strncpy() would be simpler.
> >
> > > +
> > > +     list_add_tail(&unit->list, &dev->entities);
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int uvc_gpio_init_irq(struct uvc_device *dev)
> > > +{
> > > +     int ret;
> > > +     struct uvc_entity *unit;
> >
> > Could you please swap those two lines ?
> >
> > > +
> > > +     unit = uvc_gpio_find_entity(dev);
> > > +     if (!unit)
> > > +             return 0;
> > > +
> > > +     if (unit->gpio.irq < 0)
> > > +             return 0;
> > > +
> > > +     ret = devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
> > > +                                     uvc_gpio_irq,
> > > +                                     IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
> > > +                                      IRQF_TRIGGER_RISING,
> >
> > Extra space in the indentation.
> >
> > > +                                     "uvc_privacy_gpio", dev);
> > > +     return ret;
> >
> > No need for a ret variable, you can write
> >
> >         return devm_request_threaded_irq(...);
> >
> > (unless you want to move the error message here, see below).
> >
> > > +}
> > > +
> > >  /* ------------------------------------------------------------------------
> > >   * UVC device scan
> > >   */
> > > @@ -1915,6 +2051,7 @@ static int uvc_scan_device(struct uvc_device *dev)
> > >  {
> > >       struct uvc_video_chain *chain;
> > >       struct uvc_entity *term;
> > > +     struct uvc_entity *unit;
> > >
> > >       list_for_each_entry(term, &dev->entities, list) {
> > >               if (!UVC_ENTITY_IS_OTERM(term))
> > > @@ -1953,6 +2090,13 @@ static int uvc_scan_device(struct uvc_device *dev)
> > >               return -1;
> > >       }
> > >
> > > +     /* Add GPIO entities to the first chain. */
> > > +     chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
> > > +     list_for_each_entry(unit, &dev->entities, list) {
> > > +             if (UVC_ENTITY_TYPE(unit) == UVC_EXT_GPIO_UNIT)
> > > +                     list_add_tail(&unit->chain, &chain->entities);
> > > +     }
> > > +
> > >       return 0;
> > >  }
> > >
> > > @@ -2285,6 +2429,12 @@ static int uvc_probe(struct usb_interface *intf,
> > >               goto error;
> > >       }
> > >
> > > +     /* Parse the associated GPIOs. */
> > > +     if (uvc_gpio_parse(dev) < 0) {
> > > +             uvc_trace(UVC_TRACE_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>",
> > > @@ -2329,6 +2479,12 @@ static int uvc_probe(struct usb_interface *intf,
> > >                       "supported.\n", ret);
> > >       }
> > >
> > > +     ret = uvc_gpio_init_irq(dev);
> > > +     if (ret < 0)
> > > +             dev_warn(&dev->udev->dev,
> > > +                      "Unable to request uvc_privacy_gpio irq %d. Continuing wihtout privacy events\n",
> >
> > s/uvc_privacy_gpio irq/privacy GPIO IRQ/ ?
> >
> > > +                      ret);
> >
> > This could be moved to uvc_gpio_init_irq(), up to you.
> >
> > > +
> > >       uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
> > >       usb_enable_autosuspend(udev);
> > >       return 0;
> > > diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
> > > index ca3a9c2eec27..6a9ba5b498db 100644
> > > --- a/drivers/media/usb/uvc/uvc_entity.c
> > > +++ b/drivers/media/usb/uvc/uvc_entity.c
> > > @@ -105,6 +105,7 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain,
> > >               case UVC_OTT_DISPLAY:
> > >               case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
> > >               case UVC_EXTERNAL_VENDOR_SPECIFIC:
> > > +             case UVC_EXT_GPIO_UNIT:
> > >               default:
> > >                       function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
> > >                       break;
> > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > index 6edbf79b2ff1..079a407ebba5 100644
> > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > @@ -6,6 +6,7 @@
> > >  #error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
> > >  #endif /* __KERNEL__ */
> > >
> > > +#include <linux/atomic.h>
> > >  #include <linux/kernel.h>
> > >  #include <linux/poll.h>
> > >  #include <linux/usb.h>
> > > @@ -37,6 +38,8 @@
> > >       (UVC_ENTITY_IS_TERM(entity) && \
> > >       ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
> > >
> > > +#define UVC_EXT_GPIO_UNIT            0x7ffe
> > > +#define UVC_EXT_GPIO_UNIT_ID         0x100
> > >
> > >  /* ------------------------------------------------------------------------
> > >   * GUIDs
> > > @@ -56,6 +59,9 @@
> > >  #define UVC_GUID_UVC_SELECTOR \
> > >       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> > >        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
> > > +#define UVC_GUID_EXT_GPIO_CONTROLLER \
> > > +     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> > > +      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03}
> > >
> > >  #define UVC_GUID_FORMAT_MJPEG \
> > >       { 'M',  'J',  'P',  'G', 0x00, 0x00, 0x10, 0x00, \
> > > @@ -278,6 +284,8 @@ struct uvc_format_desc {
> > >       u32 fcc;
> > >  };
> > >
> > > +struct gpio_desc;
> > > +
> >
> > This could be moved a bit up, with the other forward declaration.
> >
> > >  /* The term 'entity' refers to both UVC units and UVC terminals.
> > >   *
> > >   * The type field is either the terminal type (wTerminalType in the terminal
> > > @@ -353,6 +361,14 @@ struct uvc_entity {
> > >                       u8  *bmControls;
> > >                       u8  *bmControlsType;
> > >               } extension;
> > > +
> > > +             struct {
> > > +                     u8  bControlSize;
> > > +                     u8  *bmControls;
> > > +                     struct gpio_desc *gpio_privacy;
> > > +                     int irq;
> > > +                     atomic_t gpio_privacy_value;
> > > +             } gpio;
> > >       };
> > >
> > >       u8 bNrInPins;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  2020-12-22 20:04     ` Ricardo Ribalda
@ 2020-12-23  8:04       ` Laurent Pinchart
  2020-12-23  8:31         ` Ricardo Ribalda
  0 siblings, 1 reply; 26+ messages in thread
From: Laurent Pinchart @ 2020-12-23  8:04 UTC (permalink / raw)
  To: Ricardo Ribalda
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List,
	Linux Kernel Mailing List

Hi Ricardo,

On Tue, Dec 22, 2020 at 09:04:19PM +0100, Ricardo Ribalda wrote:
> On Tue, Dec 22, 2020 at 11:30 AM Laurent Pinchart wrote:
> > On Mon, Dec 21, 2020 at 05:48:16PM +0100, Ricardo Ribalda wrote:
> > > Some devices, can only read the privacy_pin if the device is
> >
> > s/devices,/devices/
> >
> > > streaming.
> > >
> > > This patch implement a quirk for such devices, in order to avoid invalid
> > > reads and/or spurious events.
> > >
> > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > ---
> > >  drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++++++++++++++++--
> > >  drivers/media/usb/uvc/uvc_queue.c  |  3 ++
> > >  drivers/media/usb/uvc/uvcvideo.h   |  4 +++
> > >  3 files changed, 61 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > > index 72516101fdd0..7af37d4bd60a 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/dmi.h>
> > >  #include <linux/gpio/consumer.h>
> > >  #include <linux/kernel.h>
> > >  #include <linux/list.h>
> > > @@ -1472,6 +1473,17 @@ static int uvc_parse_control(struct uvc_device *dev)
> > >  /* -----------------------------------------------------------------------------
> > >   * Privacy GPIO
> > >   */
> >
> > There should be a blank line here.
> >
> > > +static bool uvc_gpio_is_streaming(struct uvc_device *dev)
> > > +{
> > > +     struct uvc_streaming *streaming;
> > > +
> > > +     list_for_each_entry(streaming, &dev->streams, list) {
> > > +             if (uvc_queue_streaming(&streaming->queue))
> > > +                     return true;
> > > +     }
> > > +
> > > +     return false;
> > > +}
> > >
> > >
> >
> > But not too blank lines here.
> >
> > >  static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > > @@ -1499,7 +1511,12 @@ static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
> > >       if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
> > >               return -EINVAL;
> > >
> > > +     if ((dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM) &&
> > > +         !uvc_gpio_is_streaming(dev))
> > > +             return -EBUSY;
> > > +
> > >       *(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> > > +
> > >       return 0;
> > >  }
> > >
> > > @@ -1528,19 +1545,50 @@ static struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> > >       return NULL;
> > >  }
> > >
> > > -static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > +void uvc_privacy_gpio_event(struct uvc_device *dev)
> > >  {
> > > -     struct uvc_device *dev = data;
> > >       struct uvc_entity *unit;
> > >
> > > +
> > >       unit = uvc_gpio_find_entity(dev);
> > >       if (!unit)
> > > -             return IRQ_HANDLED;
> > > +             return;
> > >
> > >       uvc_gpio_update_value(dev, unit);
> > > +}
> > > +
> > > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > +{
> > > +     struct uvc_device *dev = data;
> > > +
> > > +     /* Ignore privacy events during streamoff */
> > > +     if (dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > +             if (!uvc_gpio_is_streaming(dev))
> > > +                     return IRQ_HANDLED;
> >
> > I'm still a bit concerned of race conditions. When stopping the stream,
> > vb2_queue.streaming is set to 0 after calling the driver's .stop_stream()
> > handler. This means that the device will cut power before
> > uvc_gpio_is_streaming() can detect that streaming has stopped, and the
> > GPIO could thus trigger an IRQ.
> 
> On the affected devices I have not seen this. I guess it takes some
> time to discharge. Anyway I am implementing a workaround. Tell me if
> it is too ugly.
> 
> > You mentioned that devices have a pull-up or pull-down on the GPIO line.
> > As there are only two devices affected, do you know if it's a pull-up or
> > pull-down ? Would it be worse to expose that state to userspace than to
> > return -EBUSY when reading the control ?
> 
> The module has a 100K pull up. This is, it will return "Privacy = 0".
> 
> We cannot return the default value, as it would make the user believe
> that the privacy is in a different state that currently is.
> In other words, userspace needs to know at all times if the privacy is
> in : unknow_state, on, off.

This seems to be the core of the issue: we're trying to shove 3 states
into a boolean. Would this call for turning the V4L2_CID_PRIVACY control
into a menu ? Or maybe setting V4L2_CTRL_FLAG_INACTIVE ? Returning
-EBUSY when the control is read while not streaming, and not generating
an event that tells the control value becomes unknown, seems like a hack
designed to work with a single userspace implementation.

As the rest of the series is getting ready, I'd propose merging it
without this patch until we figure out what should be done to support
these lovely devices. Would that work for you ?

> > > +
> > > +     uvc_privacy_gpio_event(dev);
> > > +
> > >       return IRQ_HANDLED;
> > >  }
> > >
> > > +static const struct dmi_system_id privacy_valid_during_streamon[] = {
> > > +     {
> > > +             .ident = "HP Elite c1030 Chromebook",
> > > +             .matches = {
> > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Jinlon"),
> > > +             },
> > > +     },
> > > +     {
> > > +             .ident = "HP Pro c640 Chromebook",
> > > +             .matches = {
> > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Dratini"),
> > > +             },
> > > +     },
> > > +     { } /* terminate list */
> > > +};
> > > +
> > >  static int uvc_gpio_parse(struct uvc_device *dev)
> > >  {
> > >       struct uvc_entity *unit;
> > > @@ -1577,6 +1625,9 @@ static int uvc_gpio_parse(struct uvc_device *dev)
> > >
> > >       list_add_tail(&unit->list, &dev->entities);
> > >
> > > +     if (dmi_check_system(privacy_valid_during_streamon))
> > > +             dev->quirks |= UVC_QUIRK_PRIVACY_DURING_STREAM;
> >
> > This will also match any external UVC camera plugged to one of the
> > affected systems, right ? It shouldn't matter in practice as those
> > devices won't have a GPIO entity.
> 
> I did think about that but did not make it explicit in the code.
> Adding a comment.
> 
> > I suppose we can't match on VID:PID instead because the same VID:PID is
> > used in both devices affected by this issue, and devices immune to it ?
> 
> The problem with VID:PID, is that the manufacturer can decide to
> change the camera module and then the quirk will not work.
> 
> We cannot rely ONLY in VID:PID as these modules are also used in other
> models not affected by the quirk.
> 
> I believe that it is also correct to rely on the dmi, as the quirk is
> caused for the way the camera module is wired, which is on the
> motherboard.

I can't comment on the hardware side as I lack details. Using the
VID:PID only seems a problem indeed. We could combine DMI and VID:PID,
but that wouldn't make a difference in practice, so I suppose this is
good enough.

> > > +
> > >       return 0;
> > >  }
> > >
> > > diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> > > index cd60c6c1749e..e800d491303f 100644
> > > --- a/drivers/media/usb/uvc/uvc_queue.c
> > > +++ b/drivers/media/usb/uvc/uvc_queue.c
> > > @@ -337,9 +337,12 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
> > >  int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
> > >  {
> > >       int ret;
> > > +     struct uvc_streaming *stream = uvc_queue_to_stream(queue);
> >
> > Please swap the two lines.
> >
> > >
> > >       mutex_lock(&queue->mutex);
> > >       ret = vb2_streamon(&queue->queue, type);
> > > +     if (stream->dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > +             uvc_privacy_gpio_event(stream->dev);
> >
> > Even when vb2_streamon() failed ?
> >
> > >       mutex_unlock(&queue->mutex);
> > >
> > >       return ret;
> > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > index 079a407ebba5..32c1ba246d97 100644
> > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > @@ -209,6 +209,7 @@
> > >  #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT      0x00000400
> > >  #define UVC_QUIRK_FORCE_Y8           0x00000800
> > >  #define UVC_QUIRK_FORCE_BPP          0x00001000
> > > +#define UVC_QUIRK_PRIVACY_DURING_STREAM      0x00002000
> > >
> > >  /* Format flags */
> > >  #define UVC_FMT_FLAG_COMPRESSED              0x00000001
> > > @@ -826,6 +827,9 @@ extern const struct v4l2_file_operations uvc_fops;
> > >  int uvc_mc_register_entities(struct uvc_video_chain *chain);
> > >  void uvc_mc_cleanup_entity(struct uvc_entity *entity);
> > >
> > > +/* Privacy gpio */
> > > +void uvc_privacy_gpio_event(struct uvc_device *dev);
> > > +
> > >  /* Video */
> > >  int uvc_video_init(struct uvc_streaming *stream);
> > >  int uvc_video_suspend(struct uvc_streaming *stream);

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  2020-12-23  8:04       ` Laurent Pinchart
@ 2020-12-23  8:31         ` Ricardo Ribalda
  2020-12-23 12:55           ` Ricardo Ribalda
  0 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-23  8:31 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List,
	Linux Kernel Mailing List

Hi Laurent

On Wed, Dec 23, 2020 at 9:05 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Ricardo,
>
> On Tue, Dec 22, 2020 at 09:04:19PM +0100, Ricardo Ribalda wrote:
> > On Tue, Dec 22, 2020 at 11:30 AM Laurent Pinchart wrote:
> > > On Mon, Dec 21, 2020 at 05:48:16PM +0100, Ricardo Ribalda wrote:
> > > > Some devices, can only read the privacy_pin if the device is
> > >
> > > s/devices,/devices/
> > >
> > > > streaming.
> > > >
> > > > This patch implement a quirk for such devices, in order to avoid invalid
> > > > reads and/or spurious events.
> > > >
> > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > ---
> > > >  drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++++++++++++++++--
> > > >  drivers/media/usb/uvc/uvc_queue.c  |  3 ++
> > > >  drivers/media/usb/uvc/uvcvideo.h   |  4 +++
> > > >  3 files changed, 61 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > > > index 72516101fdd0..7af37d4bd60a 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/dmi.h>
> > > >  #include <linux/gpio/consumer.h>
> > > >  #include <linux/kernel.h>
> > > >  #include <linux/list.h>
> > > > @@ -1472,6 +1473,17 @@ static int uvc_parse_control(struct uvc_device *dev)
> > > >  /* -----------------------------------------------------------------------------
> > > >   * Privacy GPIO
> > > >   */
> > >
> > > There should be a blank line here.
> > >
> > > > +static bool uvc_gpio_is_streaming(struct uvc_device *dev)
> > > > +{
> > > > +     struct uvc_streaming *streaming;
> > > > +
> > > > +     list_for_each_entry(streaming, &dev->streams, list) {
> > > > +             if (uvc_queue_streaming(&streaming->queue))
> > > > +                     return true;
> > > > +     }
> > > > +
> > > > +     return false;
> > > > +}
> > > >
> > > >
> > >
> > > But not too blank lines here.
> > >
> > > >  static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > > > @@ -1499,7 +1511,12 @@ static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
> > > >       if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
> > > >               return -EINVAL;
> > > >
> > > > +     if ((dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM) &&
> > > > +         !uvc_gpio_is_streaming(dev))
> > > > +             return -EBUSY;
> > > > +
> > > >       *(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> > > > +
> > > >       return 0;
> > > >  }
> > > >
> > > > @@ -1528,19 +1545,50 @@ static struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> > > >       return NULL;
> > > >  }
> > > >
> > > > -static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > > +void uvc_privacy_gpio_event(struct uvc_device *dev)
> > > >  {
> > > > -     struct uvc_device *dev = data;
> > > >       struct uvc_entity *unit;
> > > >
> > > > +
> > > >       unit = uvc_gpio_find_entity(dev);
> > > >       if (!unit)
> > > > -             return IRQ_HANDLED;
> > > > +             return;
> > > >
> > > >       uvc_gpio_update_value(dev, unit);
> > > > +}
> > > > +
> > > > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > > +{
> > > > +     struct uvc_device *dev = data;
> > > > +
> > > > +     /* Ignore privacy events during streamoff */
> > > > +     if (dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > > +             if (!uvc_gpio_is_streaming(dev))
> > > > +                     return IRQ_HANDLED;
> > >
> > > I'm still a bit concerned of race conditions. When stopping the stream,
> > > vb2_queue.streaming is set to 0 after calling the driver's .stop_stream()
> > > handler. This means that the device will cut power before
> > > uvc_gpio_is_streaming() can detect that streaming has stopped, and the
> > > GPIO could thus trigger an IRQ.
> >
> > On the affected devices I have not seen this. I guess it takes some
> > time to discharge. Anyway I am implementing a workaround. Tell me if
> > it is too ugly.
> >
> > > You mentioned that devices have a pull-up or pull-down on the GPIO line.
> > > As there are only two devices affected, do you know if it's a pull-up or
> > > pull-down ? Would it be worse to expose that state to userspace than to
> > > return -EBUSY when reading the control ?
> >
> > The module has a 100K pull up. This is, it will return "Privacy = 0".
> >
> > We cannot return the default value, as it would make the user believe
> > that the privacy is in a different state that currently is.
> > In other words, userspace needs to know at all times if the privacy is
> > in : unknow_state, on, off.
>
> This seems to be the core of the issue: we're trying to shove 3 states
> into a boolean. Would this call for turning the V4L2_CID_PRIVACY control
> into a menu ? Or maybe setting V4L2_CTRL_FLAG_INACTIVE ? Returning
> -EBUSY when the control is read while not streaming, and not generating
> an event that tells the control value becomes unknown, seems like a hack
> designed to work with a single userspace implementation.
>

I think that the V4L2_CTRL_FLAG_INACTIVE seems more correct. I will
see how can I wire that up.


> As the rest of the series is getting ready, I'd propose merging it
> without this patch until we figure out what should be done to support
> these lovely devices. Would that work for you ?

Yes that sounds good to me. Thanks!


>
> > > > +
> > > > +     uvc_privacy_gpio_event(dev);
> > > > +
> > > >       return IRQ_HANDLED;
> > > >  }
> > > >
> > > > +static const struct dmi_system_id privacy_valid_during_streamon[] = {
> > > > +     {
> > > > +             .ident = "HP Elite c1030 Chromebook",
> > > > +             .matches = {
> > > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Jinlon"),
> > > > +             },
> > > > +     },
> > > > +     {
> > > > +             .ident = "HP Pro c640 Chromebook",
> > > > +             .matches = {
> > > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Dratini"),
> > > > +             },
> > > > +     },
> > > > +     { } /* terminate list */
> > > > +};
> > > > +
> > > >  static int uvc_gpio_parse(struct uvc_device *dev)
> > > >  {
> > > >       struct uvc_entity *unit;
> > > > @@ -1577,6 +1625,9 @@ static int uvc_gpio_parse(struct uvc_device *dev)
> > > >
> > > >       list_add_tail(&unit->list, &dev->entities);
> > > >
> > > > +     if (dmi_check_system(privacy_valid_during_streamon))
> > > > +             dev->quirks |= UVC_QUIRK_PRIVACY_DURING_STREAM;
> > >
> > > This will also match any external UVC camera plugged to one of the
> > > affected systems, right ? It shouldn't matter in practice as those
> > > devices won't have a GPIO entity.
> >
> > I did think about that but did not make it explicit in the code.
> > Adding a comment.
> >
> > > I suppose we can't match on VID:PID instead because the same VID:PID is
> > > used in both devices affected by this issue, and devices immune to it ?
> >
> > The problem with VID:PID, is that the manufacturer can decide to
> > change the camera module and then the quirk will not work.
> >
> > We cannot rely ONLY in VID:PID as these modules are also used in other
> > models not affected by the quirk.
> >
> > I believe that it is also correct to rely on the dmi, as the quirk is
> > caused for the way the camera module is wired, which is on the
> > motherboard.
>
> I can't comment on the hardware side as I lack details. Using the
> VID:PID only seems a problem indeed. We could combine DMI and VID:PID,
> but that wouldn't make a difference in practice, so I suppose this is
> good enough.
>
> > > > +
> > > >       return 0;
> > > >  }
> > > >
> > > > diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> > > > index cd60c6c1749e..e800d491303f 100644
> > > > --- a/drivers/media/usb/uvc/uvc_queue.c
> > > > +++ b/drivers/media/usb/uvc/uvc_queue.c
> > > > @@ -337,9 +337,12 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
> > > >  int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
> > > >  {
> > > >       int ret;
> > > > +     struct uvc_streaming *stream = uvc_queue_to_stream(queue);
> > >
> > > Please swap the two lines.
> > >
> > > >
> > > >       mutex_lock(&queue->mutex);
> > > >       ret = vb2_streamon(&queue->queue, type);
> > > > +     if (stream->dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > > +             uvc_privacy_gpio_event(stream->dev);
> > >
> > > Even when vb2_streamon() failed ?
> > >
> > > >       mutex_unlock(&queue->mutex);
> > > >
> > > >       return ret;
> > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > index 079a407ebba5..32c1ba246d97 100644
> > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > @@ -209,6 +209,7 @@
> > > >  #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT      0x00000400
> > > >  #define UVC_QUIRK_FORCE_Y8           0x00000800
> > > >  #define UVC_QUIRK_FORCE_BPP          0x00001000
> > > > +#define UVC_QUIRK_PRIVACY_DURING_STREAM      0x00002000
> > > >
> > > >  /* Format flags */
> > > >  #define UVC_FMT_FLAG_COMPRESSED              0x00000001
> > > > @@ -826,6 +827,9 @@ extern const struct v4l2_file_operations uvc_fops;
> > > >  int uvc_mc_register_entities(struct uvc_video_chain *chain);
> > > >  void uvc_mc_cleanup_entity(struct uvc_entity *entity);
> > > >
> > > > +/* Privacy gpio */
> > > > +void uvc_privacy_gpio_event(struct uvc_device *dev);
> > > > +
> > > >  /* Video */
> > > >  int uvc_video_init(struct uvc_streaming *stream);
> > > >  int uvc_video_suspend(struct uvc_streaming *stream);
>
> --
> Regards,
>
> Laurent Pinchart



-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  2020-12-23  8:31         ` Ricardo Ribalda
@ 2020-12-23 12:55           ` Ricardo Ribalda
  2021-04-20  4:54             ` Tomasz Figa
  0 siblings, 1 reply; 26+ messages in thread
From: Ricardo Ribalda @ 2020-12-23 12:55 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List,
	Linux Kernel Mailing List

Hi again

On Wed, Dec 23, 2020 at 9:31 AM Ricardo Ribalda <ribalda@chromium.org> wrote:
>
> Hi Laurent
>
> On Wed, Dec 23, 2020 at 9:05 AM Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
> >
> > Hi Ricardo,
> >
> > On Tue, Dec 22, 2020 at 09:04:19PM +0100, Ricardo Ribalda wrote:
> > > On Tue, Dec 22, 2020 at 11:30 AM Laurent Pinchart wrote:
> > > > On Mon, Dec 21, 2020 at 05:48:16PM +0100, Ricardo Ribalda wrote:
> > > > > Some devices, can only read the privacy_pin if the device is
> > > >
> > > > s/devices,/devices/
> > > >
> > > > > streaming.
> > > > >
> > > > > This patch implement a quirk for such devices, in order to avoid invalid
> > > > > reads and/or spurious events.
> > > > >
> > > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > > ---
> > > > >  drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++++++++++++++++--
> > > > >  drivers/media/usb/uvc/uvc_queue.c  |  3 ++
> > > > >  drivers/media/usb/uvc/uvcvideo.h   |  4 +++
> > > > >  3 files changed, 61 insertions(+), 3 deletions(-)
> > > > >
> > > > > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > > > > index 72516101fdd0..7af37d4bd60a 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/dmi.h>
> > > > >  #include <linux/gpio/consumer.h>
> > > > >  #include <linux/kernel.h>
> > > > >  #include <linux/list.h>
> > > > > @@ -1472,6 +1473,17 @@ static int uvc_parse_control(struct uvc_device *dev)
> > > > >  /* -----------------------------------------------------------------------------
> > > > >   * Privacy GPIO
> > > > >   */
> > > >
> > > > There should be a blank line here.
> > > >
> > > > > +static bool uvc_gpio_is_streaming(struct uvc_device *dev)
> > > > > +{
> > > > > +     struct uvc_streaming *streaming;
> > > > > +
> > > > > +     list_for_each_entry(streaming, &dev->streams, list) {
> > > > > +             if (uvc_queue_streaming(&streaming->queue))
> > > > > +                     return true;
> > > > > +     }
> > > > > +
> > > > > +     return false;
> > > > > +}
> > > > >
> > > > >
> > > >
> > > > But not too blank lines here.
> > > >
> > > > >  static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > > > > @@ -1499,7 +1511,12 @@ static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
> > > > >       if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
> > > > >               return -EINVAL;
> > > > >
> > > > > +     if ((dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM) &&
> > > > > +         !uvc_gpio_is_streaming(dev))
> > > > > +             return -EBUSY;
> > > > > +
> > > > >       *(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> > > > > +
> > > > >       return 0;
> > > > >  }
> > > > >
> > > > > @@ -1528,19 +1545,50 @@ static struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> > > > >       return NULL;
> > > > >  }
> > > > >
> > > > > -static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > > > +void uvc_privacy_gpio_event(struct uvc_device *dev)
> > > > >  {
> > > > > -     struct uvc_device *dev = data;
> > > > >       struct uvc_entity *unit;
> > > > >
> > > > > +
> > > > >       unit = uvc_gpio_find_entity(dev);
> > > > >       if (!unit)
> > > > > -             return IRQ_HANDLED;
> > > > > +             return;
> > > > >
> > > > >       uvc_gpio_update_value(dev, unit);
> > > > > +}
> > > > > +
> > > > > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > > > +{
> > > > > +     struct uvc_device *dev = data;
> > > > > +
> > > > > +     /* Ignore privacy events during streamoff */
> > > > > +     if (dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > > > +             if (!uvc_gpio_is_streaming(dev))
> > > > > +                     return IRQ_HANDLED;
> > > >
> > > > I'm still a bit concerned of race conditions. When stopping the stream,
> > > > vb2_queue.streaming is set to 0 after calling the driver's .stop_stream()
> > > > handler. This means that the device will cut power before
> > > > uvc_gpio_is_streaming() can detect that streaming has stopped, and the
> > > > GPIO could thus trigger an IRQ.
> > >
> > > On the affected devices I have not seen this. I guess it takes some
> > > time to discharge. Anyway I am implementing a workaround. Tell me if
> > > it is too ugly.
> > >
> > > > You mentioned that devices have a pull-up or pull-down on the GPIO line.
> > > > As there are only two devices affected, do you know if it's a pull-up or
> > > > pull-down ? Would it be worse to expose that state to userspace than to
> > > > return -EBUSY when reading the control ?
> > >
> > > The module has a 100K pull up. This is, it will return "Privacy = 0".
> > >
> > > We cannot return the default value, as it would make the user believe
> > > that the privacy is in a different state that currently is.
> > > In other words, userspace needs to know at all times if the privacy is
> > > in : unknow_state, on, off.
> >
> > This seems to be the core of the issue: we're trying to shove 3 states
> > into a boolean. Would this call for turning the V4L2_CID_PRIVACY control
> > into a menu ? Or maybe setting V4L2_CTRL_FLAG_INACTIVE ? Returning
> > -EBUSY when the control is read while not streaming, and not generating
> > an event that tells the control value becomes unknown, seems like a hack
> > designed to work with a single userspace implementation.
> >
>
> I think that the V4L2_CTRL_FLAG_INACTIVE seems more correct. I will
> see how can I wire that up.

Let me correct myself here. FLAG_INACTIVE is also not a good idea.

you need two ioctls to read the value:
-queryctrl()
-g_ctrl()
and you cannot send both at the same time.

I guess we need to keep the -EBUSY or move to a menu. Since we will
probably delay this patch for  a while. I will resend based on -EBUSY
at the end of my patchset, so it can be easily ignored if we find a
better solution.

Thanks!

>
>
> > As the rest of the series is getting ready, I'd propose merging it
> > without this patch until we figure out what should be done to support
> > these lovely devices. Would that work for you ?
>
> Yes that sounds good to me. Thanks!
>
>
> >
> > > > > +
> > > > > +     uvc_privacy_gpio_event(dev);
> > > > > +
> > > > >       return IRQ_HANDLED;
> > > > >  }
> > > > >
> > > > > +static const struct dmi_system_id privacy_valid_during_streamon[] = {
> > > > > +     {
> > > > > +             .ident = "HP Elite c1030 Chromebook",
> > > > > +             .matches = {
> > > > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Jinlon"),
> > > > > +             },
> > > > > +     },
> > > > > +     {
> > > > > +             .ident = "HP Pro c640 Chromebook",
> > > > > +             .matches = {
> > > > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Dratini"),
> > > > > +             },
> > > > > +     },
> > > > > +     { } /* terminate list */
> > > > > +};
> > > > > +
> > > > >  static int uvc_gpio_parse(struct uvc_device *dev)
> > > > >  {
> > > > >       struct uvc_entity *unit;
> > > > > @@ -1577,6 +1625,9 @@ static int uvc_gpio_parse(struct uvc_device *dev)
> > > > >
> > > > >       list_add_tail(&unit->list, &dev->entities);
> > > > >
> > > > > +     if (dmi_check_system(privacy_valid_during_streamon))
> > > > > +             dev->quirks |= UVC_QUIRK_PRIVACY_DURING_STREAM;
> > > >
> > > > This will also match any external UVC camera plugged to one of the
> > > > affected systems, right ? It shouldn't matter in practice as those
> > > > devices won't have a GPIO entity.
> > >
> > > I did think about that but did not make it explicit in the code.
> > > Adding a comment.
> > >
> > > > I suppose we can't match on VID:PID instead because the same VID:PID is
> > > > used in both devices affected by this issue, and devices immune to it ?
> > >
> > > The problem with VID:PID, is that the manufacturer can decide to
> > > change the camera module and then the quirk will not work.
> > >
> > > We cannot rely ONLY in VID:PID as these modules are also used in other
> > > models not affected by the quirk.
> > >
> > > I believe that it is also correct to rely on the dmi, as the quirk is
> > > caused for the way the camera module is wired, which is on the
> > > motherboard.
> >
> > I can't comment on the hardware side as I lack details. Using the
> > VID:PID only seems a problem indeed. We could combine DMI and VID:PID,
> > but that wouldn't make a difference in practice, so I suppose this is
> > good enough.
> >
> > > > > +
> > > > >       return 0;
> > > > >  }
> > > > >
> > > > > diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> > > > > index cd60c6c1749e..e800d491303f 100644
> > > > > --- a/drivers/media/usb/uvc/uvc_queue.c
> > > > > +++ b/drivers/media/usb/uvc/uvc_queue.c
> > > > > @@ -337,9 +337,12 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
> > > > >  int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
> > > > >  {
> > > > >       int ret;
> > > > > +     struct uvc_streaming *stream = uvc_queue_to_stream(queue);
> > > >
> > > > Please swap the two lines.
> > > >
> > > > >
> > > > >       mutex_lock(&queue->mutex);
> > > > >       ret = vb2_streamon(&queue->queue, type);
> > > > > +     if (stream->dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > > > +             uvc_privacy_gpio_event(stream->dev);
> > > >
> > > > Even when vb2_streamon() failed ?
> > > >
> > > > >       mutex_unlock(&queue->mutex);
> > > > >
> > > > >       return ret;
> > > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > > index 079a407ebba5..32c1ba246d97 100644
> > > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > > @@ -209,6 +209,7 @@
> > > > >  #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT      0x00000400
> > > > >  #define UVC_QUIRK_FORCE_Y8           0x00000800
> > > > >  #define UVC_QUIRK_FORCE_BPP          0x00001000
> > > > > +#define UVC_QUIRK_PRIVACY_DURING_STREAM      0x00002000
> > > > >
> > > > >  /* Format flags */
> > > > >  #define UVC_FMT_FLAG_COMPRESSED              0x00000001
> > > > > @@ -826,6 +827,9 @@ extern const struct v4l2_file_operations uvc_fops;
> > > > >  int uvc_mc_register_entities(struct uvc_video_chain *chain);
> > > > >  void uvc_mc_cleanup_entity(struct uvc_entity *entity);
> > > > >
> > > > > +/* Privacy gpio */
> > > > > +void uvc_privacy_gpio_event(struct uvc_device *dev);
> > > > > +
> > > > >  /* Video */
> > > > >  int uvc_video_init(struct uvc_streaming *stream);
> > > > >  int uvc_video_suspend(struct uvc_streaming *stream);
> >
> > --
> > Regards,
> >
> > Laurent Pinchart
>
>
>
> --
> Ricardo Ribalda



-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM
  2020-12-23 12:55           ` Ricardo Ribalda
@ 2021-04-20  4:54             ` Tomasz Figa
  0 siblings, 0 replies; 26+ messages in thread
From: Tomasz Figa @ 2021-04-20  4:54 UTC (permalink / raw)
  To: Ricardo Ribalda, Laurent Pinchart, Kieran Bingham
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List,
	Linux Kernel Mailing List

On Wed, Dec 23, 2020 at 9:56 PM Ricardo Ribalda <ribalda@chromium.org> wrote:
>
> Hi again
>
> On Wed, Dec 23, 2020 at 9:31 AM Ricardo Ribalda <ribalda@chromium.org> wrote:
> >
> > Hi Laurent
> >
> > On Wed, Dec 23, 2020 at 9:05 AM Laurent Pinchart
> > <laurent.pinchart@ideasonboard.com> wrote:
> > >
> > > Hi Ricardo,
> > >
> > > On Tue, Dec 22, 2020 at 09:04:19PM +0100, Ricardo Ribalda wrote:
> > > > On Tue, Dec 22, 2020 at 11:30 AM Laurent Pinchart wrote:
> > > > > On Mon, Dec 21, 2020 at 05:48:16PM +0100, Ricardo Ribalda wrote:
> > > > > > Some devices, can only read the privacy_pin if the device is
> > > > >
> > > > > s/devices,/devices/
> > > > >
> > > > > > streaming.
> > > > > >
> > > > > > This patch implement a quirk for such devices, in order to avoid invalid
> > > > > > reads and/or spurious events.
> > > > > >
> > > > > > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > > > ---
> > > > > >  drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++++++++++++++++--
> > > > > >  drivers/media/usb/uvc/uvc_queue.c  |  3 ++
> > > > > >  drivers/media/usb/uvc/uvcvideo.h   |  4 +++
> > > > > >  3 files changed, 61 insertions(+), 3 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > > > > > index 72516101fdd0..7af37d4bd60a 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/dmi.h>
> > > > > >  #include <linux/gpio/consumer.h>
> > > > > >  #include <linux/kernel.h>
> > > > > >  #include <linux/list.h>
> > > > > > @@ -1472,6 +1473,17 @@ static int uvc_parse_control(struct uvc_device *dev)
> > > > > >  /* -----------------------------------------------------------------------------
> > > > > >   * Privacy GPIO
> > > > > >   */
> > > > >
> > > > > There should be a blank line here.
> > > > >
> > > > > > +static bool uvc_gpio_is_streaming(struct uvc_device *dev)
> > > > > > +{
> > > > > > +     struct uvc_streaming *streaming;
> > > > > > +
> > > > > > +     list_for_each_entry(streaming, &dev->streams, list) {
> > > > > > +             if (uvc_queue_streaming(&streaming->queue))
> > > > > > +                     return true;
> > > > > > +     }
> > > > > > +
> > > > > > +     return false;
> > > > > > +}
> > > > > >
> > > > > >
> > > > >
> > > > > But not too blank lines here.
> > > > >
> > > > > >  static u8 uvc_gpio_update_value(struct uvc_device *dev,
> > > > > > @@ -1499,7 +1511,12 @@ static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
> > > > > >       if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
> > > > > >               return -EINVAL;
> > > > > >
> > > > > > +     if ((dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM) &&
> > > > > > +         !uvc_gpio_is_streaming(dev))
> > > > > > +             return -EBUSY;
> > > > > > +
> > > > > >       *(uint8_t *)data = uvc_gpio_update_value(dev, entity);
> > > > > > +
> > > > > >       return 0;
> > > > > >  }
> > > > > >
> > > > > > @@ -1528,19 +1545,50 @@ static struct uvc_entity *uvc_gpio_find_entity(struct uvc_device *dev)
> > > > > >       return NULL;
> > > > > >  }
> > > > > >
> > > > > > -static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > > > > +void uvc_privacy_gpio_event(struct uvc_device *dev)
> > > > > >  {
> > > > > > -     struct uvc_device *dev = data;
> > > > > >       struct uvc_entity *unit;
> > > > > >
> > > > > > +
> > > > > >       unit = uvc_gpio_find_entity(dev);
> > > > > >       if (!unit)
> > > > > > -             return IRQ_HANDLED;
> > > > > > +             return;
> > > > > >
> > > > > >       uvc_gpio_update_value(dev, unit);
> > > > > > +}
> > > > > > +
> > > > > > +static irqreturn_t uvc_gpio_irq(int irq, void *data)
> > > > > > +{
> > > > > > +     struct uvc_device *dev = data;
> > > > > > +
> > > > > > +     /* Ignore privacy events during streamoff */
> > > > > > +     if (dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > > > > +             if (!uvc_gpio_is_streaming(dev))
> > > > > > +                     return IRQ_HANDLED;
> > > > >
> > > > > I'm still a bit concerned of race conditions. When stopping the stream,
> > > > > vb2_queue.streaming is set to 0 after calling the driver's .stop_stream()
> > > > > handler. This means that the device will cut power before
> > > > > uvc_gpio_is_streaming() can detect that streaming has stopped, and the
> > > > > GPIO could thus trigger an IRQ.
> > > >
> > > > On the affected devices I have not seen this. I guess it takes some
> > > > time to discharge. Anyway I am implementing a workaround. Tell me if
> > > > it is too ugly.
> > > >
> > > > > You mentioned that devices have a pull-up or pull-down on the GPIO line.
> > > > > As there are only two devices affected, do you know if it's a pull-up or
> > > > > pull-down ? Would it be worse to expose that state to userspace than to
> > > > > return -EBUSY when reading the control ?
> > > >
> > > > The module has a 100K pull up. This is, it will return "Privacy = 0".
> > > >
> > > > We cannot return the default value, as it would make the user believe
> > > > that the privacy is in a different state that currently is.
> > > > In other words, userspace needs to know at all times if the privacy is
> > > > in : unknow_state, on, off.
> > >
> > > This seems to be the core of the issue: we're trying to shove 3 states
> > > into a boolean. Would this call for turning the V4L2_CID_PRIVACY control
> > > into a menu ? Or maybe setting V4L2_CTRL_FLAG_INACTIVE ? Returning
> > > -EBUSY when the control is read while not streaming, and not generating
> > > an event that tells the control value becomes unknown, seems like a hack
> > > designed to work with a single userspace implementation.
> > >
> >
> > I think that the V4L2_CTRL_FLAG_INACTIVE seems more correct. I will
> > see how can I wire that up.
>
> Let me correct myself here. FLAG_INACTIVE is also not a good idea.
>
> you need two ioctls to read the value:
> -queryctrl()
> -g_ctrl()
> and you cannot send both at the same time.
>
> I guess we need to keep the -EBUSY or move to a menu. Since we will
> probably delay this patch for  a while. I will resend based on -EBUSY
> at the end of my patchset, so it can be easily ignored if we find a
> better solution.

Hi Laurent, Kieran,

Would it be okay to keep the behavior as suggested by Ricardo?

Best regards,
Tomasz

>
> Thanks!
>
> >
> >
> > > As the rest of the series is getting ready, I'd propose merging it
> > > without this patch until we figure out what should be done to support
> > > these lovely devices. Would that work for you ?
> >
> > Yes that sounds good to me. Thanks!
> >
> >
> > >
> > > > > > +
> > > > > > +     uvc_privacy_gpio_event(dev);
> > > > > > +
> > > > > >       return IRQ_HANDLED;
> > > > > >  }
> > > > > >
> > > > > > +static const struct dmi_system_id privacy_valid_during_streamon[] = {
> > > > > > +     {
> > > > > > +             .ident = "HP Elite c1030 Chromebook",
> > > > > > +             .matches = {
> > > > > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > > > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Jinlon"),
> > > > > > +             },
> > > > > > +     },
> > > > > > +     {
> > > > > > +             .ident = "HP Pro c640 Chromebook",
> > > > > > +             .matches = {
> > > > > > +                     DMI_MATCH(DMI_SYS_VENDOR, "HP"),
> > > > > > +                     DMI_MATCH(DMI_PRODUCT_NAME, "Dratini"),
> > > > > > +             },
> > > > > > +     },
> > > > > > +     { } /* terminate list */
> > > > > > +};
> > > > > > +
> > > > > >  static int uvc_gpio_parse(struct uvc_device *dev)
> > > > > >  {
> > > > > >       struct uvc_entity *unit;
> > > > > > @@ -1577,6 +1625,9 @@ static int uvc_gpio_parse(struct uvc_device *dev)
> > > > > >
> > > > > >       list_add_tail(&unit->list, &dev->entities);
> > > > > >
> > > > > > +     if (dmi_check_system(privacy_valid_during_streamon))
> > > > > > +             dev->quirks |= UVC_QUIRK_PRIVACY_DURING_STREAM;
> > > > >
> > > > > This will also match any external UVC camera plugged to one of the
> > > > > affected systems, right ? It shouldn't matter in practice as those
> > > > > devices won't have a GPIO entity.
> > > >
> > > > I did think about that but did not make it explicit in the code.
> > > > Adding a comment.
> > > >
> > > > > I suppose we can't match on VID:PID instead because the same VID:PID is
> > > > > used in both devices affected by this issue, and devices immune to it ?
> > > >
> > > > The problem with VID:PID, is that the manufacturer can decide to
> > > > change the camera module and then the quirk will not work.
> > > >
> > > > We cannot rely ONLY in VID:PID as these modules are also used in other
> > > > models not affected by the quirk.
> > > >
> > > > I believe that it is also correct to rely on the dmi, as the quirk is
> > > > caused for the way the camera module is wired, which is on the
> > > > motherboard.
> > >
> > > I can't comment on the hardware side as I lack details. Using the
> > > VID:PID only seems a problem indeed. We could combine DMI and VID:PID,
> > > but that wouldn't make a difference in practice, so I suppose this is
> > > good enough.
> > >
> > > > > > +
> > > > > >       return 0;
> > > > > >  }
> > > > > >
> > > > > > diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> > > > > > index cd60c6c1749e..e800d491303f 100644
> > > > > > --- a/drivers/media/usb/uvc/uvc_queue.c
> > > > > > +++ b/drivers/media/usb/uvc/uvc_queue.c
> > > > > > @@ -337,9 +337,12 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
> > > > > >  int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
> > > > > >  {
> > > > > >       int ret;
> > > > > > +     struct uvc_streaming *stream = uvc_queue_to_stream(queue);
> > > > >
> > > > > Please swap the two lines.
> > > > >
> > > > > >
> > > > > >       mutex_lock(&queue->mutex);
> > > > > >       ret = vb2_streamon(&queue->queue, type);
> > > > > > +     if (stream->dev->quirks & UVC_QUIRK_PRIVACY_DURING_STREAM)
> > > > > > +             uvc_privacy_gpio_event(stream->dev);
> > > > >
> > > > > Even when vb2_streamon() failed ?
> > > > >
> > > > > >       mutex_unlock(&queue->mutex);
> > > > > >
> > > > > >       return ret;
> > > > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > index 079a407ebba5..32c1ba246d97 100644
> > > > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > > > @@ -209,6 +209,7 @@
> > > > > >  #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT      0x00000400
> > > > > >  #define UVC_QUIRK_FORCE_Y8           0x00000800
> > > > > >  #define UVC_QUIRK_FORCE_BPP          0x00001000
> > > > > > +#define UVC_QUIRK_PRIVACY_DURING_STREAM      0x00002000
> > > > > >
> > > > > >  /* Format flags */
> > > > > >  #define UVC_FMT_FLAG_COMPRESSED              0x00000001
> > > > > > @@ -826,6 +827,9 @@ extern const struct v4l2_file_operations uvc_fops;
> > > > > >  int uvc_mc_register_entities(struct uvc_video_chain *chain);
> > > > > >  void uvc_mc_cleanup_entity(struct uvc_entity *entity);
> > > > > >
> > > > > > +/* Privacy gpio */
> > > > > > +void uvc_privacy_gpio_event(struct uvc_device *dev);
> > > > > > +
> > > > > >  /* Video */
> > > > > >  int uvc_video_init(struct uvc_streaming *stream);
> > > > > >  int uvc_video_suspend(struct uvc_streaming *stream);
> > >
> > > --
> > > Regards,
> > >
> > > Laurent Pinchart
> >
> >
> >
> > --
> > Ricardo Ribalda
>
>
>
> --
> Ricardo Ribalda

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, back to index

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-21 16:48 [PATCH v5 00/12]Show privacy_gpio as a v4l2_ctrl Ricardo Ribalda
2020-12-21 16:48 ` [PATCH v5 01/12] media: uvcvideo: Fix race condition handling events Ricardo Ribalda
2020-12-22  8:04   ` Laurent Pinchart
2020-12-21 16:48 ` [PATCH v5 02/12] media: uvcvideo: Allow more that one asyc_ctrl Ricardo Ribalda
2020-12-22  8:07   ` Laurent Pinchart
2020-12-21 16:48 ` [PATCH v5 03/12] media: uvcvideo: Move guid to entity Ricardo Ribalda
2020-12-21 16:48 ` [PATCH v5 05/12] media: uvcvideo: Allow entities with no pads Ricardo Ribalda
2020-12-21 16:48 ` [PATCH v5 06/12] media: uvcvideo: Allow entity-defined get_info and get_cur Ricardo Ribalda
2020-12-21 16:48 ` [PATCH v5 07/12] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT Ricardo Ribalda
2020-12-22  8:33   ` Laurent Pinchart
2020-12-22  8:37     ` Laurent Pinchart
2020-12-22 18:36     ` Ricardo Ribalda
2020-12-23  7:54       ` Laurent Pinchart
2020-12-21 16:48 ` [PATCH v5 08/12] media: uvcvideo: Add Privacy control based on EXT_GPIO Ricardo Ribalda
2020-12-21 16:48 ` [PATCH v5 09/12] media: uvcvideo: Implement UVC_QUIRK_PRIVACY_DURING_STREAM Ricardo Ribalda
2020-12-22 10:29   ` Laurent Pinchart
2020-12-22 20:04     ` Ricardo Ribalda
2020-12-23  8:04       ` Laurent Pinchart
2020-12-23  8:31         ` Ricardo Ribalda
2020-12-23 12:55           ` Ricardo Ribalda
2021-04-20  4:54             ` Tomasz Figa
2020-12-21 16:48 ` [PATCH v5 10/12] media: uvcvideo: Use dev_ printk aliases Ricardo Ribalda
2020-12-21 16:48 ` [PATCH v5 11/12] media: uvcvideo: New macro uvc_trace_cont Ricardo Ribalda
2020-12-21 16:48 ` [PATCH v5 12/12] media: uvcvideo: use dev_dbg() for uvc_trace() Ricardo Ribalda
2020-12-22  8:51   ` Laurent Pinchart
2020-12-21 16:48 [PATCH v5 04/12] media: uvcvideo: Allow extra entities Ricardo Ribalda
2020-12-21 18:17 ` Ricardo Ribalda

Linux-Media Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-media/0 linux-media/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-media linux-media/ https://lore.kernel.org/linux-media \
		linux-media@vger.kernel.org
	public-inbox-index linux-media

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-media


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git