Received: by 2002:a05:6a10:f347:0:0:0:0 with SMTP id d7csp3611728pxu; Sun, 20 Dec 2020 08:51:32 -0800 (PST) X-Google-Smtp-Source: ABdhPJxctm9X4bGpZByDD8tiEct1SWnbwqB+5CvyjsjKA+0x2zDao7Ab2h7yBZNAE8s7sNN1SQT5 X-Received: by 2002:a50:9310:: with SMTP id m16mr13027573eda.94.1608483092654; Sun, 20 Dec 2020 08:51:32 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1608483092; cv=none; d=google.com; s=arc-20160816; b=bs94mhEuGWb+niPPRRRmPoTzM0QCgprisUFR5qtIGMzykgkBFXIzYPR1kj2CmBpwah 3GJJ4HWk7X3eU8lxJYIFxj2bT9uLaJgCmC5VOCulFKn1UN9Ku3NZkGn+YNaX5Z1WJvzU MkzZnzec07lJEAeWXYVp0DQO4vYP0C0Mx35XodKAgMGNJ3Xhlj8Q8O0g8s9PnDhDAA6f dvLmn3RJtOqWuxUi5D2/6bsiZJjeMes0RSOM42nFTkT05VWN0eA3jZIoAskrBYekAW/j q61SPFykPI+SezrSXBZVnOXyP64ujgQniC1j4jKEyDLms98xwFMRHye5WeLV8uMpHL/b q3FA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=qyr8YHojws7IyIT0IkmpLHfeGpJ798yxgKxazFM5Ukc=; b=vGTaEMhUn2X3swm3QIgP/hxHpTLsqjuMF8HwC/Zp1NGX338aOa1JqFsfovXr1PttoO 7taJ2us4gpE+/PviZsA6SaG76bEDPGsY1zJoqNkE1pyVpgn/+WYumXw86atu9mKtc5K+ yDH1nve36ZGXXbuVKMe2vQHhOn5aUJLruZciXfaYQ/YRr2R9Im24AH//nslncyqGWRkg 3rMEBjLDAvukPkQsdwlP70E/QeCmjjBPHbc/6YBVR2hDO3td/J+R/0WkyzXmY9dJ5pjT djThc9IhH0/dH4Fh4MCfhGifVRDJtR2jgJJ1hjsVOOIuEqxSvptomU94uL1qJkJG8E2q oAjw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass (test mode) header.i=@ideasonboard.com header.s=mail header.b=rTTSHO3N; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id e17si7631281ejx.663.2020.12.20.08.51.09; Sun, 20 Dec 2020 08:51:32 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass (test mode) header.i=@ideasonboard.com header.s=mail header.b=rTTSHO3N; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727736AbgLTQug (ORCPT + 99 others); Sun, 20 Dec 2020 11:50:36 -0500 Received: from perceval.ideasonboard.com ([213.167.242.64]:50522 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727700AbgLTQug (ORCPT ); Sun, 20 Dec 2020 11:50:36 -0500 Received: from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8D399593; Sun, 20 Dec 2020 17:49:52 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1608482992; bh=5vuEXinV5yH4e3LVKMG8YvZTOgaZXBk7XbaEYNsS4fo=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=rTTSHO3NFzxjgoqFV6huda1083Qy1N7B3wkSlNO6FukFfBjta2G6r0R+cLcPbUNZg bRynINBu4oLwMqKGL+XxS4rwV2DUfNQUmK7fJVUozfzszlDZVINpGWeNo7Y9tVL38g Dhelo30r79hns19NvS3UcDH4A/Bk3giu5+lOg174= Date: Sun, 20 Dec 2020 18:49:45 +0200 From: Laurent Pinchart To: Ricardo Ribalda Cc: Mauro Carvalho Chehab , linux-media@vger.kernel.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH v4 5/9] media: uvcvideo: Implement UVC_EXT_GPIO_UNIT Message-ID: References: <20201215154439.69062-1-ribalda@chromium.org> <20201215154439.69062-6-ribalda@chromium.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <20201215154439.69062-6-ribalda@chromium.org> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Ricardo, Thank you for the patch. On Tue, Dec 15, 2020 at 04:44:35PM +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. > > Since the uvc device is not aware of this pin (and it should't), we need s/should't/shouldn't/ But I don't agree, if this is part of the camera, it should be implemented in the camera firmware. I understand that it's not possible (or desirable) with the hardware architecture you're dealing with though. How about "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:" > to implement a virtual entity that can interact with such pin. > > The location of the GPIO is specified via acpi or DT. on the usb device Eg: > > 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 > --- > drivers/media/usb/uvc/uvc_ctrl.c | 6 ++ > drivers/media/usb/uvc/uvc_driver.c | 106 +++++++++++++++++++++++++++++ > drivers/media/usb/uvc/uvcvideo.h | 12 ++++ > 3 files changed, 124 insertions(+) > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c > index 531816762892..53da1d984883 100644 > --- a/drivers/media/usb/uvc/uvc_ctrl.c > +++ b/drivers/media/usb/uvc/uvc_ctrl.c > @@ -1302,6 +1302,9 @@ static void uvc_ctrl_status_event_work(struct work_struct *work) > > mutex_unlock(&chain->ctrl_mutex); > > + if (!w->urb) > + return; A small comment to explain why would be useful. > + > /* Resubmit the URB. */ > w->urb->interval = dev->int_ep->desc.bInterval; > ret = usb_submit_urb(w->urb, GFP_KERNEL); > @@ -2286,6 +2289,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 534629ecd60d..498de09da07e 100644 > --- a/drivers/media/usb/uvc/uvc_driver.c > +++ b/drivers/media/usb/uvc/uvc_driver.c > @@ -7,6 +7,7 @@ > */ > > #include > +#include > #include > #include > #include > @@ -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; > @@ -1053,6 +1055,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; > @@ -1466,6 +1471,93 @@ static int uvc_parse_control(struct uvc_device *dev) > return 0; > } Can we add /* ----------------------------------------------------------------------------- * Privacy GPIO */ here ? > +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 = gpiod_get_value(entity->gpio.gpio_privacy); > + return 0; > +} > + > +static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity, > + u8 cs, u8 *caps) > +{ > + if (cs != UVC_CT_PRIVACY_CONTROL) > + return -EINVAL; > + > + *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE; > + return 0; > +} > + > +static irqreturn_t uvc_privacy_gpio_irq(int irq, void *data) > +{ > + struct uvc_device *dev = data; > + struct uvc_video_chain *chain; > + struct uvc_entity *unit; > + u8 value; > + > + /* GPIO entities are always on the first chain */ s/chain/chain./ > + chain = list_first_entry(&dev->chains, struct uvc_video_chain, list); > + list_for_each_entry(unit, &dev->entities, list) { Can we iterate over entities in the chain instead ? I'd be actually tempted to pass the pointer to the entity as data. We would however need to add a chain pointer to uvc_entity. Not sure if it's worth it, up to you. > + if (UVC_ENTITY_TYPE(unit) != UVC_EXT_GPIO_UNIT) > + continue; > + value = gpiod_get_value(unit->gpio.gpio_privacy); Could this sleep ? Should we use a threaded IRQ ? > + uvc_ctrl_status_event(NULL, chain, unit->controls, &value); > + return IRQ_HANDLED; > + } > + > + return IRQ_HANDLED; > +} > + > +static int uvc_parse_gpio(struct uvc_device *dev) How about making the naming scheme consistent, by using a uvc_gpio_ prefix for all four functions ? > +{ > + struct uvc_entity *unit; > + struct gpio_desc *gpio_privacy; > + int irq; > + int ret; > + > + gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", > + GPIOD_IN); > + if (IS_ERR(gpio_privacy)) > + return PTR_ERR(gpio_privacy); > + > + if (!gpio_privacy) > + return 0; This could be folded in if (IS_ERR_OR_NULL(gpio_privacy)) return PTR_ERR_OR_ZERO(gpio_privacy); Up to you. > + > + unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); > + if (!unit) > + return -ENOMEM; > + > + unit->gpio.gpio_privacy = gpio_privacy; > + 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 Unit"); Other unit names don't contain "Unit", should this be just "GPIO" ? > + > + list_add_tail(&unit->list, &dev->entities); > + > + irq = gpiod_to_irq(gpio_privacy); > + if (irq == -EPROBE_DEFER) > + return -EPROBE_DEFER; Can this happen in practice ? > + > + if (irq < 0) > + return 0; So this will succeed, with the GPIO unit created, but it won't be operational. Is this desired ? Shouldn't we at least print a warning ? I also wonder if we should create the GPIO unit in this cases. Wouldn't it be more confusing for userspace to see the privacy control being exposed, but without it being functional ? Maybe the call to gpiod_to_irq should be moved to before uvc_alloc_entity() ? > + > + ret = devm_request_irq(&dev->udev->dev, irq, uvc_privacy_gpio_irq, > + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, > + "uvc_privacy_gpio", dev); > + if (ret < 0) > + dev_warn(&dev->udev->dev, > + "Unable to request uvc_privacy_gpio irq. Continuing\n"); s/uvc_privacy_gpio irq/privacy GPIO IRQ/ ? There's also a race condition, as soon as the IRQ is requested, it could fire, and the driver isn't fully initialized. Walking the chain in the IRQ handler while entities are added to the chain could lead to a crash. I see two options, either adding a flag to tell that initialization is complete and returning from the interrupt handler when the flag isn't set, or moving IRQ registration to a separate function, called later during the initialization. I think I have a preference for the latter. > + > + return 0; > +} > + > /* ------------------------------------------------------------------------ > * UVC device scan > */ > @@ -1917,6 +2009,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)) > @@ -1955,6 +2048,13 @@ static int uvc_scan_device(struct uvc_device *dev) > return -1; > } > > + /* Add GPIO entities to the first chain */ s/chain/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; > } > > @@ -2287,6 +2387,12 @@ static int uvc_probe(struct usb_interface *intf, > goto error; > } > > + /* Parse the associated GPIOs */ s/GPIOs/GPIOs./ > + if (uvc_parse_gpio(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 : "", > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h > index ae464ba83f4f..f87d14fb3f56 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 We can use a forward declaration of struct gpio_desc in this file. > #include > #include > #include > @@ -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 Maybe a couple of tabs to align this with the other defines above ? > > /* ------------------------------------------------------------------------ > * 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, \ > @@ -348,6 +354,12 @@ struct uvc_entity { > u8 *bmControls; > u8 *bmControlsType; > } extension; > + > + struct { > + u8 bControlSize; > + u8 *bmControls; > + struct gpio_desc *gpio_privacy; > + } gpio; > }; > > u8 bNrInPins; -- Regards, Laurent Pinchart