From patchwork Sun Jul 16 14:53:03 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Zabel X-Patchwork-Id: 9843425 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 8250A602BD for ; Sun, 16 Jul 2017 14:53:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 78B1128419 for ; Sun, 16 Jul 2017 14:53:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6DA23284FF; Sun, 16 Jul 2017 14:53:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E19E728419 for ; Sun, 16 Jul 2017 14:53:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751434AbdGPOxQ (ORCPT ); Sun, 16 Jul 2017 10:53:16 -0400 Received: from mail-wm0-f67.google.com ([74.125.82.67]:32818 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751326AbdGPOxO (ORCPT ); Sun, 16 Jul 2017 10:53:14 -0400 Received: by mail-wm0-f67.google.com with SMTP id j85so18791060wmj.0 for ; Sun, 16 Jul 2017 07:53:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=vNU55x/XVw+QikvrbltBKfXJhjPgdktXxH5P4cR78OU=; b=M3P/Z41ctSgqgOTT5qTrBKauQcDvkxlAuEKC34RGtnpxMCYAw5KZ8i9TXSXYj1wVIT GuekvhIpU/jzO2/xQ1YnvW7vzDonmvTWM1IV281Yi6bRGQDwaeINCAyAskZeoC6D5cAB Cyfz7tELArbpzvTVUihN9VL492ZTfuJawT4CEpRY4P/jz6DnqwZRh56ZHR2z0tQte6V0 U5Hkp/hYZeKq6vVZ3LKTHLjBvnBpVGvOiwL/jGDJ3GZMAeni5pf2FBQ7kshQiyJnqMug ztTh+1oBJDKPE//2MaLTujkVTzJDlj3GlHd5D8frwxwcyjVpmcksxXmREJoUIYXzYkRF dp6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=vNU55x/XVw+QikvrbltBKfXJhjPgdktXxH5P4cR78OU=; b=JzYi5JpG8astttu46QzWojGJDFyqjwIRX5oSsv52wxEG7iHUdEQjOB+vpn14QiVdUy Brc+2VrxzreW5kxdI8YnaaoyG6H37ZQzRHZGpw9RjTH1Gn0zMxxnX8y4jKRq46lp3UTf GIWU0K4HUewfGRY0h7m1FFQCELhDPtKqvsWb5P3HEPfZprPBdMguh2pvwgDJjeCjAA2D EalAptxlhkInx9LgyYnBBjGq/UNHYBx/B1dkDk9pwe8r/IfnD0+Ewos3E+cRlv5bz31t IQ6+FS4uihpOz9tbSJQERGnREmo9hNHBRzLYQgJi0vEeOZXXvSimyRdstubpsCaYMb79 EWvQ== X-Gm-Message-State: AIVw1133fHJi2em+sNDuGOOpKcfyayvVA/+H+Gs2yPu6MV7MkaJKn/15 /Sa+o7iK4L3256nw X-Received: by 10.80.165.164 with SMTP id a33mr14318663edc.53.1500216792716; Sun, 16 Jul 2017 07:53:12 -0700 (PDT) Received: from ped.lan (ip1f12fb64.dynamic.kabel-deutschland.de. [31.18.251.100]) by smtp.googlemail.com with ESMTPSA id c11sm7865922eda.0.2017.07.16.07.53.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 16 Jul 2017 07:53:11 -0700 (PDT) From: Philipp Zabel To: linux-media@vger.kernel.org Cc: Laurent Pinchart , Philipp Zabel Subject: [PATCH v2 1/3] [media] uvcvideo: variable size controls Date: Sun, 16 Jul 2017 16:53:03 +0200 Message-Id: <20170716145305.19934-1-philipp.zabel@gmail.com> X-Mailer: git-send-email 2.13.2 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Some USB webcam controllers have extension unit controls that report different lengths via GET_LEN, depending on internal state. Add a flag to mark these controls as variable length and issue GET_LEN before GET/SET_CUR transfers to verify the current length. Signed-off-by: Philipp Zabel --- Changes since v1: - Split uvc_ctrl_fill_xu_info_size and uvc_ctrl_fill_xu_info_flags out of uvc_ctrl_fill_xu_info. - Add uvc_ctrl_update_xu_info_size and reuse uvc_ctrl_fill_xu_info_size. - Call uvc_ctrl_update_xu_info_size from uvc_xu_ctrl_query instead of open coding the size update, thereby fixing a double kfree. --- drivers/media/usb/uvc/uvc_ctrl.c | 98 ++++++++++++++++++++++++++++++++++------ include/uapi/linux/uvcvideo.h | 2 + 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index c2ee6e39fd0c..43e8851cc381 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1597,7 +1597,7 @@ static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev, struct usb_device_id id; u8 entity; u8 selector; - u8 flags; + u16 flags; }; static const struct uvc_ctrl_fixup fixups[] = { @@ -1629,10 +1629,7 @@ static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev, } } -/* - * Query control information (size and flags) for XU controls. - */ -static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, +static int uvc_ctrl_fill_xu_info_size(struct uvc_device *dev, const struct uvc_control *ctrl, struct uvc_control_info *info) { u8 *data; @@ -1642,11 +1639,6 @@ 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)); - info->index = ctrl->index; - info->selector = ctrl->index + 1; - /* Query and verify the control length (GET_LEN) */ ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, info->selector, data, 2); @@ -1659,6 +1651,21 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, info->size = le16_to_cpup((__le16 *)data); +done: + kfree(data); + return ret; +} + +static int uvc_ctrl_fill_xu_info_flags(struct uvc_device *dev, + const struct uvc_control *ctrl, struct uvc_control_info *info) +{ + u8 *data; + int ret; + + data = kmalloc(1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + /* Query the control information (GET_INFO) */ ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, info->selector, data, 1); @@ -1678,6 +1685,32 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? UVC_CTRL_FLAG_AUTO_UPDATE : 0); +done: + kfree(data); + return ret; +} + +/* + * Query control information (size and flags) for XU controls. + */ +static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, + const struct uvc_control *ctrl, struct uvc_control_info *info) +{ + int ret; + + memcpy(info->entity, ctrl->entity->extension.guidExtensionCode, + sizeof(info->entity)); + info->index = ctrl->index; + info->selector = ctrl->index + 1; + + ret = uvc_ctrl_fill_xu_info_size(dev, ctrl, info); + if (ret < 0) + return ret; + + ret = uvc_ctrl_fill_xu_info_flags(dev, ctrl, info); + if (ret < 0) + return ret; + uvc_ctrl_fixup_xu_info(dev, ctrl, info); uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " @@ -1687,9 +1720,7 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0, (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0); -done: - kfree(data); - return ret; + return 0; } static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, @@ -1717,6 +1748,40 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, return ret; } +/* + * Update control size for variable length XU controls. + */ +static int uvc_ctrl_update_xu_info_size(struct uvc_device *dev, + struct uvc_control *ctrl) +{ + u16 size = ctrl->info.size; + int ret; + + if (!(ctrl->info.flags & UVC_CTRL_FLAG_VARIABLE_LEN)) + return 0; + + /* Check if the control size has changed */ + ret = uvc_ctrl_fill_xu_info_size(dev, ctrl, &ctrl->info); + if (ret < 0) + return ret; + + if (ctrl->info.size != size) { + uvc_trace(UVC_TRACE_CONTROL, + "XU control %pUl/%u queried: len %u -> %u\n", + ctrl->info.entity, ctrl->info.selector, + size, ctrl->info.size); + + /* Resize array for saved control values */ + kfree(ctrl->uvc_data); + ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + + 1, GFP_KERNEL); + if (ctrl->uvc_data == NULL) + return -ENOMEM; + } + + return 0; +} + int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry) { @@ -1799,6 +1864,13 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, goto done; } + if (reqflags) { + ret = uvc_ctrl_update_xu_info_size(chain->dev, ctrl); + if (ret < 0) + goto done; + size = ctrl->info.size; + } + if (size != xqry->size) { ret = -ENOBUFS; goto done; diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h index 3b081862b9e8..0f0d63e79045 100644 --- a/include/uapi/linux/uvcvideo.h +++ b/include/uapi/linux/uvcvideo.h @@ -27,6 +27,8 @@ #define UVC_CTRL_FLAG_RESTORE (1 << 6) /* Control can be updated by the camera. */ #define UVC_CTRL_FLAG_AUTO_UPDATE (1 << 7) +/* Control can change LEN */ +#define UVC_CTRL_FLAG_VARIABLE_LEN (1 << 8) #define UVC_CTRL_FLAG_GET_RANGE \ (UVC_CTRL_FLAG_GET_CUR | UVC_CTRL_FLAG_GET_MIN | \