mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-16 23:45:31 +08:00
c94e289f19
A recent change introduced a link error for the composite printer gadget driver: `printer_unbind' referenced in section `.ref.data' of drivers/built-in.o: defined in discarded section `.exit.text' of drivers/built-in.o Evidently the unbind function should not be marked __exit here, because it is called through a callback pointer that is not necessarily discarded, __composite_unbind() is indeed called from the error path of composite_bind(), which can never work for a built-in driver. Looking at the surrounding code, I found the same problem in all other composite gadget drivers in both the bind and unbind functions, as well as the udc platform driver 'remove' functions. Those will break if anyone uses the 'unbind' sysfs attribute to detach a device from a built-in driver. This patch removes the incorrect annotations from all the gadget drivers. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Felipe Balbi <balbi@ti.com>
441 lines
14 KiB
C
441 lines
14 KiB
C
/*
|
|
* webcam.c -- USB webcam gadget driver
|
|
*
|
|
* Copyright (C) 2009-2010
|
|
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/usb/video.h>
|
|
|
|
#include "u_uvc.h"
|
|
|
|
USB_GADGET_COMPOSITE_OPTIONS();
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
/* module parameters specific to the Video streaming endpoint */
|
|
static unsigned int streaming_interval = 1;
|
|
module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(streaming_interval, "1 - 16");
|
|
|
|
static unsigned int streaming_maxpacket = 1024;
|
|
module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
|
|
|
|
static unsigned int streaming_maxburst;
|
|
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
|
|
|
|
static unsigned int trace;
|
|
module_param(trace, uint, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(trace, "Trace level bitmask");
|
|
/* --------------------------------------------------------------------------
|
|
* Device descriptor
|
|
*/
|
|
|
|
#define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */
|
|
#define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */
|
|
#define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */
|
|
|
|
static char webcam_vendor_label[] = "Linux Foundation";
|
|
static char webcam_product_label[] = "Webcam gadget";
|
|
static char webcam_config_label[] = "Video";
|
|
|
|
/* string IDs are assigned dynamically */
|
|
|
|
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
|
|
|
|
static struct usb_string webcam_strings[] = {
|
|
[USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
|
|
[USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
|
|
[USB_GADGET_SERIAL_IDX].s = "",
|
|
[STRING_DESCRIPTION_IDX].s = webcam_config_label,
|
|
{ }
|
|
};
|
|
|
|
static struct usb_gadget_strings webcam_stringtab = {
|
|
.language = 0x0409, /* en-us */
|
|
.strings = webcam_strings,
|
|
};
|
|
|
|
static struct usb_gadget_strings *webcam_device_strings[] = {
|
|
&webcam_stringtab,
|
|
NULL,
|
|
};
|
|
|
|
static struct usb_function_instance *fi_uvc;
|
|
static struct usb_function *f_uvc;
|
|
|
|
static struct usb_device_descriptor webcam_device_descriptor = {
|
|
.bLength = USB_DT_DEVICE_SIZE,
|
|
.bDescriptorType = USB_DT_DEVICE,
|
|
.bcdUSB = cpu_to_le16(0x0200),
|
|
.bDeviceClass = USB_CLASS_MISC,
|
|
.bDeviceSubClass = 0x02,
|
|
.bDeviceProtocol = 0x01,
|
|
.bMaxPacketSize0 = 0, /* dynamic */
|
|
.idVendor = cpu_to_le16(WEBCAM_VENDOR_ID),
|
|
.idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID),
|
|
.bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD),
|
|
.iManufacturer = 0, /* dynamic */
|
|
.iProduct = 0, /* dynamic */
|
|
.iSerialNumber = 0, /* dynamic */
|
|
.bNumConfigurations = 0, /* dynamic */
|
|
};
|
|
|
|
DECLARE_UVC_HEADER_DESCRIPTOR(1);
|
|
|
|
static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
|
|
.bLength = UVC_DT_HEADER_SIZE(1),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VC_HEADER,
|
|
.bcdUVC = cpu_to_le16(0x0100),
|
|
.wTotalLength = 0, /* dynamic */
|
|
.dwClockFrequency = cpu_to_le32(48000000),
|
|
.bInCollection = 0, /* dynamic */
|
|
.baInterfaceNr[0] = 0, /* dynamic */
|
|
};
|
|
|
|
static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
|
|
.bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VC_INPUT_TERMINAL,
|
|
.bTerminalID = 1,
|
|
.wTerminalType = cpu_to_le16(0x0201),
|
|
.bAssocTerminal = 0,
|
|
.iTerminal = 0,
|
|
.wObjectiveFocalLengthMin = cpu_to_le16(0),
|
|
.wObjectiveFocalLengthMax = cpu_to_le16(0),
|
|
.wOcularFocalLength = cpu_to_le16(0),
|
|
.bControlSize = 3,
|
|
.bmControls[0] = 2,
|
|
.bmControls[1] = 0,
|
|
.bmControls[2] = 0,
|
|
};
|
|
|
|
static const struct uvc_processing_unit_descriptor uvc_processing = {
|
|
.bLength = UVC_DT_PROCESSING_UNIT_SIZE(2),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VC_PROCESSING_UNIT,
|
|
.bUnitID = 2,
|
|
.bSourceID = 1,
|
|
.wMaxMultiplier = cpu_to_le16(16*1024),
|
|
.bControlSize = 2,
|
|
.bmControls[0] = 1,
|
|
.bmControls[1] = 0,
|
|
.iProcessing = 0,
|
|
};
|
|
|
|
static const struct uvc_output_terminal_descriptor uvc_output_terminal = {
|
|
.bLength = UVC_DT_OUTPUT_TERMINAL_SIZE,
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL,
|
|
.bTerminalID = 3,
|
|
.wTerminalType = cpu_to_le16(0x0101),
|
|
.bAssocTerminal = 0,
|
|
.bSourceID = 2,
|
|
.iTerminal = 0,
|
|
};
|
|
|
|
DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2);
|
|
|
|
static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
|
|
.bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_INPUT_HEADER,
|
|
.bNumFormats = 2,
|
|
.wTotalLength = 0, /* dynamic */
|
|
.bEndpointAddress = 0, /* dynamic */
|
|
.bmInfo = 0,
|
|
.bTerminalLink = 3,
|
|
.bStillCaptureMethod = 0,
|
|
.bTriggerSupport = 0,
|
|
.bTriggerUsage = 0,
|
|
.bControlSize = 1,
|
|
.bmaControls[0][0] = 0,
|
|
.bmaControls[1][0] = 4,
|
|
};
|
|
|
|
static const struct uvc_format_uncompressed uvc_format_yuv = {
|
|
.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
|
|
.bFormatIndex = 1,
|
|
.bNumFrameDescriptors = 2,
|
|
.guidFormat =
|
|
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
|
|
.bBitsPerPixel = 16,
|
|
.bDefaultFrameIndex = 1,
|
|
.bAspectRatioX = 0,
|
|
.bAspectRatioY = 0,
|
|
.bmInterfaceFlags = 0,
|
|
.bCopyProtect = 0,
|
|
};
|
|
|
|
DECLARE_UVC_FRAME_UNCOMPRESSED(1);
|
|
DECLARE_UVC_FRAME_UNCOMPRESSED(3);
|
|
|
|
static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
|
|
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
|
|
.bFrameIndex = 1,
|
|
.bmCapabilities = 0,
|
|
.wWidth = cpu_to_le16(640),
|
|
.wHeight = cpu_to_le16(360),
|
|
.dwMinBitRate = cpu_to_le32(18432000),
|
|
.dwMaxBitRate = cpu_to_le32(55296000),
|
|
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
|
|
.dwDefaultFrameInterval = cpu_to_le32(666666),
|
|
.bFrameIntervalType = 3,
|
|
.dwFrameInterval[0] = cpu_to_le32(666666),
|
|
.dwFrameInterval[1] = cpu_to_le32(1000000),
|
|
.dwFrameInterval[2] = cpu_to_le32(5000000),
|
|
};
|
|
|
|
static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
|
|
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
|
|
.bFrameIndex = 2,
|
|
.bmCapabilities = 0,
|
|
.wWidth = cpu_to_le16(1280),
|
|
.wHeight = cpu_to_le16(720),
|
|
.dwMinBitRate = cpu_to_le32(29491200),
|
|
.dwMaxBitRate = cpu_to_le32(29491200),
|
|
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
|
|
.dwDefaultFrameInterval = cpu_to_le32(5000000),
|
|
.bFrameIntervalType = 1,
|
|
.dwFrameInterval[0] = cpu_to_le32(5000000),
|
|
};
|
|
|
|
static const struct uvc_format_mjpeg uvc_format_mjpg = {
|
|
.bLength = UVC_DT_FORMAT_MJPEG_SIZE,
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
|
|
.bFormatIndex = 2,
|
|
.bNumFrameDescriptors = 2,
|
|
.bmFlags = 0,
|
|
.bDefaultFrameIndex = 1,
|
|
.bAspectRatioX = 0,
|
|
.bAspectRatioY = 0,
|
|
.bmInterfaceFlags = 0,
|
|
.bCopyProtect = 0,
|
|
};
|
|
|
|
DECLARE_UVC_FRAME_MJPEG(1);
|
|
DECLARE_UVC_FRAME_MJPEG(3);
|
|
|
|
static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
|
|
.bLength = UVC_DT_FRAME_MJPEG_SIZE(3),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
|
|
.bFrameIndex = 1,
|
|
.bmCapabilities = 0,
|
|
.wWidth = cpu_to_le16(640),
|
|
.wHeight = cpu_to_le16(360),
|
|
.dwMinBitRate = cpu_to_le32(18432000),
|
|
.dwMaxBitRate = cpu_to_le32(55296000),
|
|
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
|
|
.dwDefaultFrameInterval = cpu_to_le32(666666),
|
|
.bFrameIntervalType = 3,
|
|
.dwFrameInterval[0] = cpu_to_le32(666666),
|
|
.dwFrameInterval[1] = cpu_to_le32(1000000),
|
|
.dwFrameInterval[2] = cpu_to_le32(5000000),
|
|
};
|
|
|
|
static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
|
|
.bLength = UVC_DT_FRAME_MJPEG_SIZE(1),
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
|
|
.bFrameIndex = 2,
|
|
.bmCapabilities = 0,
|
|
.wWidth = cpu_to_le16(1280),
|
|
.wHeight = cpu_to_le16(720),
|
|
.dwMinBitRate = cpu_to_le32(29491200),
|
|
.dwMaxBitRate = cpu_to_le32(29491200),
|
|
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
|
|
.dwDefaultFrameInterval = cpu_to_le32(5000000),
|
|
.bFrameIntervalType = 1,
|
|
.dwFrameInterval[0] = cpu_to_le32(5000000),
|
|
};
|
|
|
|
static const struct uvc_color_matching_descriptor uvc_color_matching = {
|
|
.bLength = UVC_DT_COLOR_MATCHING_SIZE,
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
.bDescriptorSubType = UVC_VS_COLORFORMAT,
|
|
.bColorPrimaries = 1,
|
|
.bTransferCharacteristics = 1,
|
|
.bMatrixCoefficients = 4,
|
|
};
|
|
|
|
static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
|
|
(const struct uvc_descriptor_header *) &uvc_control_header,
|
|
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
|
|
(const struct uvc_descriptor_header *) &uvc_processing,
|
|
(const struct uvc_descriptor_header *) &uvc_output_terminal,
|
|
NULL,
|
|
};
|
|
|
|
static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
|
|
(const struct uvc_descriptor_header *) &uvc_control_header,
|
|
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
|
|
(const struct uvc_descriptor_header *) &uvc_processing,
|
|
(const struct uvc_descriptor_header *) &uvc_output_terminal,
|
|
NULL,
|
|
};
|
|
|
|
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
|
|
(const struct uvc_descriptor_header *) &uvc_input_header,
|
|
(const struct uvc_descriptor_header *) &uvc_format_yuv,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
|
|
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
|
|
(const struct uvc_descriptor_header *) &uvc_color_matching,
|
|
NULL,
|
|
};
|
|
|
|
static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
|
|
(const struct uvc_descriptor_header *) &uvc_input_header,
|
|
(const struct uvc_descriptor_header *) &uvc_format_yuv,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
|
|
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
|
|
(const struct uvc_descriptor_header *) &uvc_color_matching,
|
|
NULL,
|
|
};
|
|
|
|
static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
|
|
(const struct uvc_descriptor_header *) &uvc_input_header,
|
|
(const struct uvc_descriptor_header *) &uvc_format_yuv,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
|
|
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
|
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
|
|
(const struct uvc_descriptor_header *) &uvc_color_matching,
|
|
NULL,
|
|
};
|
|
|
|
/* --------------------------------------------------------------------------
|
|
* USB configuration
|
|
*/
|
|
|
|
static int
|
|
webcam_config_bind(struct usb_configuration *c)
|
|
{
|
|
int status = 0;
|
|
|
|
f_uvc = usb_get_function(fi_uvc);
|
|
if (IS_ERR(f_uvc))
|
|
return PTR_ERR(f_uvc);
|
|
|
|
status = usb_add_function(c, f_uvc);
|
|
if (status < 0)
|
|
usb_put_function(f_uvc);
|
|
|
|
return status;
|
|
}
|
|
|
|
static struct usb_configuration webcam_config_driver = {
|
|
.label = webcam_config_label,
|
|
.bConfigurationValue = 1,
|
|
.iConfiguration = 0, /* dynamic */
|
|
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
|
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
|
|
};
|
|
|
|
static int
|
|
webcam_unbind(struct usb_composite_dev *cdev)
|
|
{
|
|
if (!IS_ERR_OR_NULL(f_uvc))
|
|
usb_put_function(f_uvc);
|
|
if (!IS_ERR_OR_NULL(fi_uvc))
|
|
usb_put_function_instance(fi_uvc);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
webcam_bind(struct usb_composite_dev *cdev)
|
|
{
|
|
struct f_uvc_opts *uvc_opts;
|
|
int ret;
|
|
|
|
fi_uvc = usb_get_function_instance("uvc");
|
|
if (IS_ERR(fi_uvc))
|
|
return PTR_ERR(fi_uvc);
|
|
|
|
uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
|
|
|
|
uvc_opts->streaming_interval = streaming_interval;
|
|
uvc_opts->streaming_maxpacket = streaming_maxpacket;
|
|
uvc_opts->streaming_maxburst = streaming_maxburst;
|
|
uvc_set_trace_param(trace);
|
|
|
|
uvc_opts->fs_control = uvc_fs_control_cls;
|
|
uvc_opts->ss_control = uvc_ss_control_cls;
|
|
uvc_opts->fs_streaming = uvc_fs_streaming_cls;
|
|
uvc_opts->hs_streaming = uvc_hs_streaming_cls;
|
|
uvc_opts->ss_streaming = uvc_ss_streaming_cls;
|
|
|
|
/* Allocate string descriptor numbers ... note that string contents
|
|
* can be overridden by the composite_dev glue.
|
|
*/
|
|
ret = usb_string_ids_tab(cdev, webcam_strings);
|
|
if (ret < 0)
|
|
goto error;
|
|
webcam_device_descriptor.iManufacturer =
|
|
webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
|
|
webcam_device_descriptor.iProduct =
|
|
webcam_strings[USB_GADGET_PRODUCT_IDX].id;
|
|
webcam_config_driver.iConfiguration =
|
|
webcam_strings[STRING_DESCRIPTION_IDX].id;
|
|
|
|
/* Register our configuration. */
|
|
if ((ret = usb_add_config(cdev, &webcam_config_driver,
|
|
webcam_config_bind)) < 0)
|
|
goto error;
|
|
|
|
usb_composite_overwrite_options(cdev, &coverwrite);
|
|
INFO(cdev, "Webcam Video Gadget\n");
|
|
return 0;
|
|
|
|
error:
|
|
usb_put_function_instance(fi_uvc);
|
|
return ret;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------
|
|
* Driver
|
|
*/
|
|
|
|
static struct usb_composite_driver webcam_driver = {
|
|
.name = "g_webcam",
|
|
.dev = &webcam_device_descriptor,
|
|
.strings = webcam_device_strings,
|
|
.max_speed = USB_SPEED_SUPER,
|
|
.bind = webcam_bind,
|
|
.unbind = webcam_unbind,
|
|
};
|
|
|
|
module_usb_composite_driver(webcam_driver);
|
|
|
|
MODULE_AUTHOR("Laurent Pinchart");
|
|
MODULE_DESCRIPTION("Webcam Video Gadget");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION("0.1.0");
|
|
|