Hi Andrzej, On Thu, Jul 17, 2014 at 02:42:19PM +0200, Andrzej Pietrasiewicz wrote: > Add support for using uvc as a component of a composite gadget > set up with configfs. > > Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx> > --- > Documentation/ABI/testing/configfs-usb-gadget-uvc | 11 + > drivers/usb/gadget/Kconfig | 11 + > drivers/usb/gadget/function/Makefile | 2 +- > drivers/usb/gadget/function/f_uvc.c | 94 + > drivers/usb/gadget/function/u_uvc.h | 19 + > drivers/usb/gadget/function/uvc_configfs.c | 2928 +++++++++++++++++++++ > drivers/usb/gadget/function/uvc_configfs.h | 283 ++ > 7 files changed, 3347 insertions(+), 1 deletion(-) > create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-uvc > create mode 100644 drivers/usb/gadget/function/uvc_configfs.c > create mode 100644 drivers/usb/gadget/function/uvc_configfs.h > > diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc > new file mode 100644 > index 0000000..b3b4ba5 > --- /dev/null > +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc > @@ -0,0 +1,11 @@ > +What: /config/usb-gadget/gadget/functions/uvc.name > +Date: Oct 2014 > +KenelVersion: 3.18 > +Description: > + The attributes: > + > + streaming_interval - 1..16 > + streaming_maxpacket - 1..1023 (fs), 1..3072 (hs/ss) > + streaming_maxburst - 0..15 (ss only) > + trace - trace level bitmask, > + common for all uvc instances > diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig > index 4b3d4e9..ce55234 100644 > --- a/drivers/usb/gadget/Kconfig > +++ b/drivers/usb/gadget/Kconfig > @@ -356,6 +356,17 @@ config USB_CONFIGFS_F_FS > implemented in kernel space (for instance Ethernet, serial or > mass storage) and other are implemented in user space. > > +config USB_CONFIGFS_F_UVC > + boolean "USB Webcam function" > + depends on USB_CONFIGFS > + depends on VIDEO_DEV > + select VIDEOBUF2_VMALLOC > + select USB_F_UVC > + help > + The Webcam function acts as a composite USB Audio and Video Class > + device. It provides a userspace API to process UVC control requests > + and stream video data to the host. > + > source "drivers/usb/gadget/legacy/Kconfig" > > endchoice > diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile > index ad80f21..94391f3 100644 > --- a/drivers/usb/gadget/function/Makefile > +++ b/drivers/usb/gadget/function/Makefile > @@ -32,5 +32,5 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o > obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o > usb_f_fs-y := f_fs.o > obj-$(CONFIG_USB_F_FS) += usb_f_fs.o > -usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o > +usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o > obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o > diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c > index 9d22928..10ad916 100644 > --- a/drivers/usb/gadget/function/f_uvc.c > +++ b/drivers/usb/gadget/function/f_uvc.c > @@ -28,6 +28,7 @@ > #include <media/v4l2-event.h> > > #include "uvc.h" > +#include "uvc_configfs.h" > #include "uvc_v4l2.h" > #include "uvc_video.h" > #include "u_uvc.h" > @@ -467,6 +468,9 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) > break; > } > > + if (!uvc_control_desc || !uvc_streaming_cls) > + return ERR_PTR(-ENODEV); > + > /* Descriptors layout > * > * uvc_iad > @@ -642,6 +646,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) > uvc_streaming_intf_alt0.iInterface = ret; > uvc_streaming_intf_alt1.iInterface = ret; > > + > /* Allocate interface IDs. */ > if ((ret = usb_interface_id(c, f)) < 0) > goto error; > @@ -657,10 +662,25 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) > > /* Copy descriptors */ > f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); > + if (IS_ERR(f->fs_descriptors)) { > + ret = PTR_ERR(f->fs_descriptors); > + f->fs_descriptors = NULL; > + goto error; > + } > if (gadget_is_dualspeed(cdev->gadget)) > f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); > + if (IS_ERR(f->hs_descriptors)) { > + ret = PTR_ERR(f->hs_descriptors); > + f->hs_descriptors = NULL; > + goto error; > + } > if (gadget_is_superspeed(c->cdev->gadget)) > f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); > + if (IS_ERR(f->ss_descriptors)) { > + ret = PTR_ERR(f->ss_descriptors); > + f->ss_descriptors = NULL; > + goto error; > + } > > /* Preallocate control endpoint request. */ > uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); > @@ -735,16 +755,87 @@ static struct usb_function_instance *uvc_alloc_inst(void) > opts = kzalloc(sizeof(*opts), GFP_KERNEL); > if (!opts) > return ERR_PTR(-ENOMEM); > + mutex_init(&opts->lock); > opts->func_inst.free_func_inst = uvc_free_inst; > > + config_group_init_type_name(&f_uvc_header_group, "header", > + &f_uvc_header_type); > + config_group_init_type_name(&f_uvc_processing_group, "processing", > + &f_uvc_processing_type); > + config_group_init_type_name(&f_uvc_class_fs_group, "fs", > + &f_uvc_class_fs_type); > + config_group_init_type_name(&f_uvc_class_ss_group, "ss", > + &f_uvc_class_ss_type); > + f_uvc_class_group.default_groups = f_uvc_class_default_groups; > + config_group_init_type_name(&f_uvc_class_group, "class", > + &f_uvc_class_type); > + config_group_init_type_name(&f_uvc_camera_group, "camera", > + &f_uvc_camera_type); > + config_group_init_type_name(&f_uvc_output_group, "output", > + &f_uvc_output_type); > + f_uvc_terminal_group.default_groups = f_uvc_terminal_default_groups; > + config_group_init_type_name(&f_uvc_terminal_group, "terminal", > + &f_uvc_terminal_type); > + f_uvc_control_group.group.default_groups = f_uvc_control_default_groups; > + INIT_LIST_HEAD(&f_uvc_control_group.known_targets); > + config_group_init_type_name(&f_uvc_control_group.group, "control", > + &f_uvc_control_type); > + config_group_init_type_name(&f_uvc_input_header_group, "input_header", > + &f_uvc_input_header_type); > + config_group_init_type_name(&f_uvc_color_matching_group, "color_matching", > + &f_uvc_color_matching_type); > + config_group_init_type_name(&f_uvc_streaming_fs_group, "fs", > + &f_uvc_streaming_fs_type); > + config_group_init_type_name(&f_uvc_streaming_hs_group, "hs", > + &f_uvc_streaming_hs_type); > + config_group_init_type_name(&f_uvc_streaming_ss_group, "ss", > + &f_uvc_streaming_ss_type); > + f_uvc_streaming_class_group.default_groups = > + f_uvc_streaming_class_default_groups; > + config_group_init_type_name(&f_uvc_streaming_class_group, "class", > + &f_uvc_streaming_class_type); > + config_group_init_type_name(&f_uvc_frame_yuv_group, "yuv", > + &f_uvc_frame_yuv_type); > + config_group_init_type_name(&f_uvc_frame_mjpeg_group, "mjpeg", > + &f_uvc_frame_mjpeg_type); > + f_uvc_frame_group.default_groups = f_uvc_frame_default_groups; > + config_group_init_type_name(&f_uvc_frame_group, "frame", > + &f_uvc_frame_type); > + config_group_init_type_name(&f_uvc_format_yuv_group, "yuv", > + &f_uvc_format_yuv_type); > + config_group_init_type_name(&f_uvc_format_mjpeg_group, "mjpeg", > + &f_uvc_format_mjpeg_type); > + f_uvc_format_group.group.default_groups = f_uvc_format_default_groups; > + INIT_LIST_HEAD(&f_uvc_format_group.known_targets); > + config_group_init_type_name(&f_uvc_format_group.group, "format", > + &f_uvc_format_type); > + f_uvc_streaming_group.group.default_groups = f_uvc_streaming_default_groups; > + INIT_LIST_HEAD(&f_uvc_streaming_group.known_targets); > + config_group_init_type_name(&f_uvc_streaming_group.group, "streaming", > + &f_uvc_streaming_type); > + opts->func_inst.group.default_groups = f_uvc_default_groups; > + opts->fs_class = &f_uvc_class_fs_group.cg_item; > + opts->ss_class = &f_uvc_class_ss_group.cg_item; > + opts->fs_streaming_class = &f_uvc_streaming_fs_group.cg_item; > + opts->hs_streaming_class = &f_uvc_streaming_hs_group.cg_item; > + opts->ss_streaming_class = &f_uvc_streaming_ss_group.cg_item; > + INIT_LIST_HEAD(&opts->known_targets); > + config_group_init_type_name(&opts->func_inst.group, "", > + &uvc_func_type); > + > return &opts->func_inst; > } > > static void uvc_free(struct usb_function *f) > { > struct uvc_device *uvc = to_uvc(f); > + struct f_uvc_opts *opts; > > + opts = container_of(f->fi, struct f_uvc_opts, func_inst); > kfree(uvc); How about to_f_uvc_opts instead of container_of here? [snip] I am trying to work with your patches. It was possible to compile and run this together with your other f_uvc series. Now I tried the configfs instructions in the coverletter together with the dummy_udc.0. On this udc it was possible to load webcam.ko. But the configfs implementation does bailout on uvc_copy_descriptors: if (!uvc_control_desc || !uvc_streaming_cl) return ERR_PTR(-ENODEV); root@virtual:/cfg/usb_gadget/g1 echo dummy_udc.0 > UDC configfs-gadget gadget: uvc_function_bind configfs-gadget dummy_udc.0: failed to start g1: -19 sh: write error: No such device Regards, Michael -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html