Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 087caba6 authored by Manu Gautam's avatar Manu Gautam Committed by Sriharsha Allenki
Browse files

usb: android: f_uvc: Add support for UVC function



Integrate USB video class function driver with android
gadget. User can run below sample commands to enumerate
UVC as webcam on hosts:

->setprop sys.usb.config none
->echo 0 > /sys/class/android_usb/android0/enable
->echo $PID > /sys/class/android_usb/android0/idProduct
->echo video > /sys/class/android_usb/android0/functions
->echo 1 > /sys/class/android_usb/android0/enable

After above commands user should start userspace application
to open video device and handle v4l2 control requests. USB
enumeration is disabled until then.

CRs-fixed: 1097684
Change-Id: Ib147959dd8ca4bf6230b1a6adbed67198e0c56bb
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
Signed-off-by: default avatarSriharsha Allenki <sallenki@codeaurora.org>
parent 67ca95e6
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -188,6 +188,7 @@ config USB_F_UAC2
	tristate
	tristate


config USB_F_UVC
config USB_F_UVC
	select VIDEOBUF2_VMALLOC
	tristate
	tristate


config USB_F_MTP
config USB_F_MTP
@@ -422,6 +423,7 @@ config USB_G_ANDROID
	select USB_F_NCM
	select USB_F_NCM
	select USB_F_MASS_STORAGE
	select USB_F_MASS_STORAGE
	select USB_F_UAC1 if SND_PCM
	select USB_F_UAC1 if SND_PCM
	select USB_F_UVC if MEDIA_SUPPORT
	help
	help
	  The Android Composite Gadget supports multiple USB
	  The Android Composite Gadget supports multiple USB
	  functions: adb, acm, mass storage, mtp, accessory
	  functions: adb, acm, mass storage, mtp, accessory
+138 −1
Original line number Original line Diff line number Diff line
@@ -36,6 +36,10 @@


#include "gadget_chips.h"
#include "gadget_chips.h"


#ifdef CONFIG_MEDIA_SUPPORT
#include "f_uvc.h"
#include "u_uvc.h"
#endif
#include "u_fs.h"
#include "u_fs.h"
#include "u_ecm.h"
#include "u_ecm.h"
#include "u_ncm.h"
#include "u_ncm.h"
@@ -71,6 +75,9 @@
#include "f_mass_storage.h"
#include "f_mass_storage.h"


USB_ETHERNET_MODULE_PARAMETERS();
USB_ETHERNET_MODULE_PARAMETERS();
#ifdef CONFIG_MEDIA_SUPPORT
USB_VIDEO_MODULE_PARAMETERS();
#endif
#include "debug.h"
#include "debug.h"


MODULE_AUTHOR("Mike Lockwood");
MODULE_AUTHOR("Mike Lockwood");
@@ -238,6 +245,8 @@ static struct android_configuration *alloc_android_config
static void free_android_config(struct android_dev *dev,
static void free_android_config(struct android_dev *dev,
				struct android_configuration *conf);
				struct android_configuration *conf);


static bool video_enabled;

/* string IDs are assigned dynamically */
/* string IDs are assigned dynamically */
#define STRING_MANUFACTURER_IDX		0
#define STRING_MANUFACTURER_IDX		0
#define STRING_PRODUCT_IDX		1
#define STRING_PRODUCT_IDX		1
@@ -520,7 +529,11 @@ static int android_enable(struct android_dev *dev)
		if (ktime_to_ms(diff) < MIN_DISCONNECT_DELAY_MS)
		if (ktime_to_ms(diff) < MIN_DISCONNECT_DELAY_MS)
			msleep(MIN_DISCONNECT_DELAY_MS - ktime_to_ms(diff));
			msleep(MIN_DISCONNECT_DELAY_MS - ktime_to_ms(diff));


		/* Userspace UVC driver will trigger connect for video */
		if (!video_enabled)
			usb_gadget_connect(cdev->gadget);
			usb_gadget_connect(cdev->gadget);
		else
			pr_debug("defer gadget connect until usersapce opens video device\n");
	}
	}


	return err;
	return err;
@@ -1501,6 +1514,127 @@ static struct android_usb_function audio_function = {
};
};
#endif
#endif


#ifdef CONFIG_MEDIA_SUPPORT
/* PERIPHERAL VIDEO */
struct video_function_config {
	struct usb_function *func;
	struct usb_function_instance *fi;
};

static int video_function_init(struct android_usb_function *f,
			       struct usb_composite_dev *cdev)
{
	struct f_uvc_opts *uvc_opts;
	struct video_function_config *config;

	f->config = kzalloc(sizeof(*config), GFP_KERNEL);
	if (!f->config)
		return -ENOMEM;

	config = f->config;

	config->fi = usb_get_function_instance("uvc");
	if (IS_ERR(config->fi))
		return PTR_ERR(config->fi);

	uvc_opts = container_of(config->fi, 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;

	config->func = usb_get_function(config->fi);
	if (IS_ERR(config->func)) {
		usb_put_function_instance(config->fi);
		return PTR_ERR(config->func);
	}

	return 0;
}

static void video_function_cleanup(struct android_usb_function *f)
{
	struct video_function_config *config = f->config;

	if (config) {
		usb_put_function(config->func);
		usb_put_function_instance(config->fi);
	}

	kfree(f->config);
	f->config = NULL;
}

static int video_function_bind_config(struct android_usb_function *f,
					  struct usb_configuration *c)
{
	struct video_function_config *config = f->config;

	return usb_add_function(c, config->func);
}

static void video_function_enable(struct android_usb_function *f)
{
	video_enabled = true;
}

static void video_function_disable(struct android_usb_function *f)
{
	video_enabled = false;
}

static struct android_usb_function video_function = {
	.name		= "video",
	.init		= video_function_init,
	.cleanup	= video_function_cleanup,
	.bind_config	= video_function_bind_config,
	.enable		= video_function_enable,
	.disable	= video_function_disable,
};

int video_ready_callback(struct usb_function *function)
{
	struct android_dev *dev = video_function.android_dev;
	struct usb_composite_dev *cdev;

	if (!dev) {
		pr_err("%s: dev is NULL\n", __func__);
		return -ENODEV;
	}

	cdev = dev->cdev;

	pr_debug("%s: connect\n", __func__);
	usb_gadget_connect(cdev->gadget);

	return 0;
}

int video_closed_callback(struct usb_function *function)
{
	struct android_dev *dev = video_function.android_dev;
	struct usb_composite_dev *cdev;

	if (!dev) {
		pr_err("%s: dev is NULL\n", __func__);
		return -ENODEV;
	}

	cdev = dev->cdev;

	pr_debug("%s: disconnect\n", __func__);
	usb_gadget_disconnect(cdev->gadget);

	return 0;
}
#endif


/* DIAG */
/* DIAG */
static char diag_clients[32];	    /*enabled DIAG clients- "diag[,diag_mdm]" */
static char diag_clients[32];	    /*enabled DIAG clients- "diag[,diag_mdm]" */
@@ -3088,6 +3222,9 @@ static struct android_usb_function *default_functions[] = {
	&ecm_qc_function,
	&ecm_qc_function,
#ifdef CONFIG_SND_PCM
#ifdef CONFIG_SND_PCM
	&audio_function,
	&audio_function,
#endif
#ifdef CONFIG_MEDIA_SUPPORT
	&video_function,
#endif
#endif
	&rmnet_function,
	&rmnet_function,
	&gps_function,
	&gps_function,
+7 −6
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@
#include "uvc_v4l2.h"
#include "uvc_v4l2.h"
#include "uvc_video.h"
#include "uvc_video.h"
#include "u_uvc.h"
#include "u_uvc.h"
#include "f_uvc.h"


unsigned int uvc_gadget_trace_param;
unsigned int uvc_gadget_trace_param;


@@ -412,7 +413,8 @@ uvc_function_connect(struct uvc_device *uvc)
	struct usb_composite_dev *cdev = uvc->func.config->cdev;
	struct usb_composite_dev *cdev = uvc->func.config->cdev;
	int ret;
	int ret;


	if ((ret = usb_function_activate(&uvc->func)) < 0)
	ret = video_ready_callback(&uvc->func);
	if (ret < 0)
		INFO(cdev, "UVC connect failed with %d\n", ret);
		INFO(cdev, "UVC connect failed with %d\n", ret);
}
}


@@ -422,7 +424,8 @@ uvc_function_disconnect(struct uvc_device *uvc)
	struct usb_composite_dev *cdev = uvc->func.config->cdev;
	struct usb_composite_dev *cdev = uvc->func.config->cdev;
	int ret;
	int ret;


	if ((ret = usb_function_deactivate(&uvc->func)) < 0)
	ret = video_closed_callback(&uvc->func);
	if (ret < 0)
		INFO(cdev, "UVC disconnect failed with %d\n", ret);
		INFO(cdev, "UVC disconnect failed with %d\n", ret);
}
}


@@ -717,11 +720,9 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
	uvc->control_req->complete = uvc_function_ep0_complete;
	uvc->control_req->complete = uvc_function_ep0_complete;
	uvc->control_req->context = uvc;
	uvc->control_req->context = uvc;


	/* Avoid letting this gadget enumerate until the userspace server is
	/* Gadget drivers avoids enumerattion until the userspace server is
	 * active.
	 * active - when it opens uvc video device node.
	 */
	 */
	if ((ret = usb_function_deactivate(f)) < 0)
		goto error;


	if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
	if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
		printk(KERN_INFO "v4l2_device_register failed\n");
		printk(KERN_INFO "v4l2_device_register failed\n");
+242 −0
Original line number Original line Diff line number Diff line
@@ -24,5 +24,247 @@ void uvc_function_connect(struct uvc_device *uvc);


void uvc_function_disconnect(struct uvc_device *uvc);
void uvc_function_disconnect(struct uvc_device *uvc);


int video_ready_callback(struct usb_function *function);
int video_closed_callback(struct usb_function *function);

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,
};

#endif /* _F_UVC_H_ */
#endif /* _F_UVC_H_ */
+18 −0
Original line number Original line Diff line number Diff line
@@ -18,6 +18,24 @@


#include <linux/usb/composite.h>
#include <linux/usb/composite.h>


/* module parameters specific to the Video streaming endpoint */
#define USB_VIDEO_MODULE_PARAMETERS()					\
	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")

#define to_f_uvc_opts(f)	container_of(f, struct f_uvc_opts, func_inst)
#define to_f_uvc_opts(f)	container_of(f, struct f_uvc_opts, func_inst)


struct f_uvc_opts {
struct f_uvc_opts {