Alright here is a thread to post any patches to test this weekend. I've attached my wo patches i've been working on to this message 1. adds support for USB suspend/resume 2. exposes webcam button presses via the linux evdev input system
Suspend/Resume support seems to work reasonably well on my own machine, with me being able to open up mplayer suspend the machine and when i resume mplayer is still delivering a valid video stream. My second patch is is a combination of the two patches related to webcam buttons in a previous post that i have combined into one as well as modified my thread function to support suspend/resume. This patch probably needs a bit of testing with different web cams that have buttons. It should work ok as it is now, however i would like some more information about which webcams are known to have buttons, if there are any that have more then one and which bits in the general purpose input registers(1005 and 1009) are set when a button is pressed. You can find out that information using debugfs. Testing the current button events cn be done using gizmod. # sudo gizmod -g Thsi will start gizmod in debug mode and you should see information about BTN_0 - BTN_7 show up when pressing your webcam buttons --~--~---------~--~----~------------~-------~--~----~ Lets make microdia webcams plug'n play, (currently plug'n pray) To post to this group, send email to [email protected] Visit us online https://groups.google.com/group/microdia -~----------~----~----~----~------~----~------~--~---
From a69b2c321eafb64fdf7789b5eb73805591960dbc Mon Sep 17 00:00:00 2001 From: Brian Johnson <[email protected]> Date: Wed, 18 Mar 2009 12:24:40 -0400 Subject: [PATCH] Add Suspend/Resume support Signed-off-by: Brian Johnson <[email protected]> --- sn9c20x-bridge.c | 40 +++++++++++++++++++++++ sn9c20x-bridge.h | 1 + sn9c20x-usb.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++------ sn9c20x-v4l2.c | 2 +- sn9c20x.h | 2 +- 5 files changed, 123 insertions(+), 13 deletions(-) diff --git a/sn9c20x-bridge.c b/sn9c20x-bridge.c index dfbf87f..97cb507 100644 --- a/sn9c20x-bridge.c +++ b/sn9c20x-bridge.c @@ -921,3 +921,43 @@ err: UDIA_ERROR("Device Init failed (%d)!\n", ret); return ret; } + +int sn9c20x_reset_device(struct usb_sn9c20x *dev) +{ + if (sn9c20x_initialize(dev) < 0) + return -EINVAL; + + sn9c20x_set_camera_control(dev, V4L2_CID_HFLIP, + dev->vsettings.hflip); + sn9c20x_set_camera_control(dev, V4L2_CID_VFLIP, + dev->vsettings.vflip); + sn9c20x_set_camera_control(dev, V4L2_CID_GAIN, + dev->vsettings.gain); + sn9c20x_set_camera_control(dev, V4L2_CID_BRIGHTNESS, + dev->vsettings.brightness); + sn9c20x_set_camera_control(dev, V4L2_CID_CONTRAST, + dev->vsettings.contrast); + sn9c20x_set_camera_control(dev, V4L2_CID_GAMMA, + dev->vsettings.gamma); + sn9c20x_set_camera_control(dev, V4L2_CID_SHARPNESS, + dev->vsettings.sharpness); + sn9c20x_set_camera_control(dev, V4L2_CID_RED_BALANCE, + dev->vsettings.red_gain); + sn9c20x_set_camera_control(dev, V4L2_CID_BLUE_BALANCE, + dev->vsettings.blue_gain); + sn9c20x_set_camera_control(dev, V4L2_CID_EXPOSURE_AUTO, + dev->vsettings.auto_exposure); + sn9c20x_set_camera_control(dev, V4L2_CID_AUTOGAIN, + dev->vsettings.auto_gain); + sn9c20x_set_camera_control(dev, V4L2_CID_AUTO_WHITE_BALANCE, + dev->vsettings.auto_whitebalance); + sn9c20x_set_camera_control(dev, V4L2_CID_EXPOSURE, + dev->vsettings.exposure); + + sn9c20x_set_resolution(dev, dev->vsettings.format.width, + dev->vsettings.format.height); + + sn9c20x_set_format(dev, dev->vsettings.format.pixelformat); + + return 0; +} diff --git a/sn9c20x-bridge.h b/sn9c20x-bridge.h index 029042c..00e7875 100644 --- a/sn9c20x-bridge.h +++ b/sn9c20x-bridge.h @@ -46,6 +46,7 @@ #define SN9C20X_1_4_SCALE 0x20 int sn9c20x_initialize(struct usb_sn9c20x *dev); +int sn9c20x_reset_device(struct usb_sn9c20x *dev); int sn9c20x_set_LEDs(struct usb_sn9c20x *dev, int enable); int sn9c20x_set_camera_control(struct usb_sn9c20x *dev, __u32 control, __u32 value); int sn9c20x_enable_video(struct usb_sn9c20x *dev, int enable); diff --git a/sn9c20x-usb.c b/sn9c20x-usb.c index 17d8c2e..156dcdd 100644 --- a/sn9c20x-usb.c +++ b/sn9c20x-usb.c @@ -304,7 +304,7 @@ int usb_sn9c20x_isoc_init(struct usb_sn9c20x *dev, if (urb == NULL) { UDIA_ERROR("Failed to allocate URB %d\n", i); - usb_sn9c20x_uninit_urbs(dev); + usb_sn9c20x_uninit_urbs(dev, 1); return -ENOMEM; } @@ -323,13 +323,14 @@ int usb_sn9c20x_isoc_init(struct usb_sn9c20x *dev, urb->iso_frame_desc[j].length = iso_max_frame_size; } - dev->urbs[i].data = kzalloc(urb->transfer_buffer_length, - GFP_KERNEL); if (dev->urbs[i].data == NULL) { - usb_sn9c20x_uninit_urbs(dev); - return -ENOMEM; + dev->urbs[i].data = kzalloc(urb->transfer_buffer_length, + GFP_KERNEL); + if (dev->urbs[i].data == NULL) { + usb_sn9c20x_uninit_urbs(dev, 1); + return -ENOMEM; + } } - urb->transfer_buffer = dev->urbs[i].data; dev->urbs[i].urb = urb; } @@ -351,11 +352,16 @@ int usb_sn9c20x_bulk_init(struct usb_sn9c20x *dev, for (i = 0; i < MAX_URBS; ++i) { urb = usb_alloc_urb(0, GFP_KERNEL); if (urb == NULL) { - usb_sn9c20x_uninit_urbs(dev); + usb_sn9c20x_uninit_urbs(dev, 1); return -ENOMEM; } - - dev->urbs[i].data = kzalloc(size, GFP_KERNEL); + if (dev->urbs[i].data == NULL) { + dev->urbs[i].data = kzalloc(size, GFP_KERNEL); + if (dev->urbs[i].data == NULL) { + usb_sn9c20x_uninit_urbs(dev, 1); + return -ENOMEM; + } + } usb_fill_bulk_urb(urb, dev->udev, pipe, dev->urbs[i].data, size, @@ -433,7 +439,7 @@ int usb_sn9c20x_init_urbs(struct usb_sn9c20x *dev) * * This function permits to clean-up all the ISOC buffers. */ -void usb_sn9c20x_uninit_urbs(struct usb_sn9c20x *dev) +void usb_sn9c20x_uninit_urbs(struct usb_sn9c20x *dev, int free_buffers) { int i; struct urb *urb; @@ -448,9 +454,12 @@ void usb_sn9c20x_uninit_urbs(struct usb_sn9c20x *dev) if (urb == NULL) continue; usb_kill_urb(urb); - kfree(dev->urbs[i].data); usb_free_urb(urb); dev->urbs[i].urb = NULL; + if (free_buffers) { + kfree(dev->urbs[i].data); + dev->urbs[i].data = NULL; + } } } @@ -873,6 +882,63 @@ static void usb_sn9c20x_disconnect(struct usb_interface *interface) mutex_unlock(&open_lock); } +static int usb_sn9c20x_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_sn9c20x *dev = usb_get_intfdata(intf); + + UDIA_INFO("Suspending interface: %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + if (dev->interface != intf) + return -EINVAL; + + if (!sn9c20x_queue_streaming(&dev->queue)) + return 0; + + dev->frozen = 1; + usb_sn9c20x_uninit_urbs(dev, 0); + usb_set_interface(dev->udev, 0, 0); + return 0; +} + + + +static int _usb_sn9c20x_resume(struct usb_interface *intf, int reset) +{ + struct usb_sn9c20x *dev = usb_get_intfdata(intf); + + UDIA_INFO("Resuming interface: %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + if (dev->interface != intf) + return -EINVAL; + + if (reset && sn9c20x_reset_device(dev) < 0) + return -EINVAL; + + if (!sn9c20x_queue_streaming(&dev->queue)) + return 0; + + dev->frozen = 0; + if (usb_sn9c20x_init_urbs(dev) < 0) + sn9c20x_queue_enable(&dev->queue, 0); + + sn9c20x_enable_video(dev, 1); + return 0; +} + + +static int usb_sn9c20x_resume(struct usb_interface *intf) +{ + UDIA_DEBUG("usb_sn9c20x_resume()\n"); + return _usb_sn9c20x_resume(intf, 0); +} + +static int usb_sn9c20x_reset_resume(struct usb_interface *intf) +{ + UDIA_DEBUG("usb_sn9c20x_reset_resume()\n"); + return _usb_sn9c20x_resume(intf, 1); +} /** * @var usb_sn9c20x_driver @@ -883,6 +949,9 @@ static struct usb_driver usb_sn9c20x_driver = { .name = "sn9c20x", .probe = usb_sn9c20x_probe, .disconnect = usb_sn9c20x_disconnect, + .suspend = usb_sn9c20x_suspend, + .resume = usb_sn9c20x_resume, + .reset_resume = usb_sn9c20x_reset_resume, .id_table = sn9c20x_table, }; diff --git a/sn9c20x-v4l2.c b/sn9c20x-v4l2.c index b762678..6eada10 100644 --- a/sn9c20x-v4l2.c +++ b/sn9c20x-v4l2.c @@ -363,7 +363,7 @@ int v4l2_enable_video(struct usb_sn9c20x *dev, int mode) if (mode == SN9C20X_MODE_IDLE) { sn9c20x_enable_video(dev, 0); - usb_sn9c20x_uninit_urbs(dev); + usb_sn9c20x_uninit_urbs(dev, 1); sn9c20x_queue_enable(&dev->queue, 0); dev->mode = mode; return 0; diff --git a/sn9c20x.h b/sn9c20x.h index 6a846a4..8da4265 100644 --- a/sn9c20x.h +++ b/sn9c20x.h @@ -378,7 +378,7 @@ int usb_sn9c20x_isoc_init(struct usb_sn9c20x *, struct usb_endpoint_descriptor *); void usb_sn9c20x_completion_handler(struct urb *); int usb_sn9c20x_init_urbs(struct usb_sn9c20x *); -void usb_sn9c20x_uninit_urbs(struct usb_sn9c20x *); +void usb_sn9c20x_uninit_urbs(struct usb_sn9c20x *, int); void usb_sn9c20x_delete(struct kref *); int sn9c20x_initialize(struct usb_sn9c20x *dev); -- 1.5.6.3
From fc57a23f6e5b6d330e0e2a11e9545fc634b3582b Mon Sep 17 00:00:00 2001 From: Brian Johnson <[email protected]> Date: Wed, 18 Mar 2009 14:32:05 -0400 Subject: [PATCH] Implement input device for webcam buttons Signed-off-by: Brian Johnson <[email protected]> --- .config | 2 + Kconfig | 6 +++ Makefile | 4 ++ omnivision.c | 18 --------- omnivision.h | 1 - sn9c20x-bridge.c | 8 ++++ sn9c20x-dev.c | 18 --------- sn9c20x-usb.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++- sn9c20x.h | 10 ++++- 9 files changed, 135 insertions(+), 40 deletions(-) diff --git a/.config b/.config index abd30c6..b86f4e2 100644 --- a/.config +++ b/.config @@ -1,3 +1,5 @@ CONFIG_SN9C20X=m CONFIG_SN9C20X_DEBUGFS=y + +CONFIG_SN9C20X_EVDEV=y diff --git a/Kconfig b/Kconfig index e07a058..2a6d408 100644 --- a/Kconfig +++ b/Kconfig @@ -13,3 +13,9 @@ config SN9C20X_DEBUGFS depends on SN9C20X ---help--- Say Y here in order to enable debugfs for sn9c20x webcams + +config SN9C20X_EVDEV + bool "enable evdev for sn9c20x webcams" + depends on SN9C20X + ---help--- + Say Y here in order to evdev support for webcam button presses diff --git a/Makefile b/Makefile index 45a0506..968de56 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,10 @@ sn9c20x-objs += sn9c20x-debugfs.o EXTRA_CFLAGS = -DCONFIG_SN9C20X_DEBUGFS endif +ifeq ($(CONFIG_SN9C20X_EVDEV),y) +EXTRA_CFLAGS += -DCONFIG_SN9C20X_EVDEV +endif + obj-$(CONFIG_SN9C20X) += sn9c20x.o else diff --git a/omnivision.c b/omnivision.c index 24c7b97..2454f68 100644 --- a/omnivision.c +++ b/omnivision.c @@ -829,21 +829,3 @@ int ov965x_flip_detect(struct usb_sn9c20x *dev) dev->vsettings.vflip = 0; return ret; } - -int soi968_button_detect(struct usb_sn9c20x *dev) -{ - __u8 buf; - - usb_sn9c20x_control_read(dev, 0x1005, &buf, 1); - - if (unlikely(buf & 0x10)) { - /* button pushed */ - if (dev->vsettings.auto_gain == 0) - dev->vsettings.auto_gain = 1; - else - dev->vsettings.auto_gain = 0; - ov_set_autogain(dev); - } - - return 0; -} diff --git a/omnivision.h b/omnivision.h index 86c57a2..42bcf5f 100644 --- a/omnivision.h +++ b/omnivision.h @@ -541,7 +541,6 @@ int ov7670_auto_flip(struct usb_sn9c20x *, __u8); int ov7670_flip_detect(struct usb_sn9c20x *dev); int soi968_set_exposure(struct usb_sn9c20x *dev); -int soi968_button_detect(struct usb_sn9c20x *dev); int soi968_set_gain(struct usb_sn9c20x *dev); int soi968_set_autoexposure(struct usb_sn9c20x *dev); int soi968_set_autowhitebalance(struct usb_sn9c20x *dev); diff --git a/sn9c20x-bridge.c b/sn9c20x-bridge.c index 97cb507..f5d77b8 100644 --- a/sn9c20x-bridge.c +++ b/sn9c20x-bridge.c @@ -903,6 +903,14 @@ int sn9c20x_initialize(struct usb_sn9c20x *dev) if (ret < 0) goto err; +#ifdef CONFIG_SN9C20X_EVDEV + ret = usb_sn9c20x_control_read(dev, 0x1005, &dev->input_gpio, 1); + if (ret < 0) + goto err; + + dev->input_gpio = ~dev->input_gpio; +#endif + dev->camera.set_contrast = sn9c20x_set_contrast; dev->camera.set_brightness = sn9c20x_set_brightness; dev->camera.set_gamma = sn9c20x_set_gamma; diff --git a/sn9c20x-dev.c b/sn9c20x-dev.c index 11ac1f3..b1eb4d3 100644 --- a/sn9c20x-dev.c +++ b/sn9c20x-dev.c @@ -136,7 +136,6 @@ int sn9c20x_initialize_sensor(struct usb_sn9c20x *dev) dev->camera.set_gain = soi968_set_gain; dev->camera.set_auto_gain = ov_set_autogain; dev->camera.set_auto_whitebalance = soi968_set_autowhitebalance; - dev->camera.button_detect = soi968_button_detect; dev->camera.hstart = 60; dev->camera.vstart = 11; UDIA_INFO("Detected SOI968 Sensor.\n"); @@ -248,7 +247,6 @@ int dev_sn9c20x_call_constantly(struct usb_sn9c20x *dev) /* Know to be broken, temporarely disabled */ /*dev_sn9c20x_flip_detection(dev);*/ - dev_sn9c20x_button_detection(dev); if (!dev->camera.set_auto_exposure && dev->vsettings.auto_exposure) { dev_sn9c20x_perform_soft_ae(dev); @@ -274,22 +272,6 @@ int dev_sn9c20x_flip_detection(struct usb_sn9c20x *dev) } /** - * @brief Wrapper function to detect a pushed button - * - * @param dev Pointer to device structure - * - * @returns 0 or negative error value - * - */ -int dev_sn9c20x_button_detection(struct usb_sn9c20x *dev) -{ - int ret = -ENODEV; - if (dev && dev->camera.button_detect) - ret = dev->camera.button_detect(dev); - return ret; -} - -/** * @brief Perform software autoexposure * * @param dev diff --git a/sn9c20x-usb.c b/sn9c20x-usb.c index 156dcdd..149664f 100644 --- a/sn9c20x-usb.c +++ b/sn9c20x-usb.c @@ -32,10 +32,15 @@ #include <linux/slab.h> #include <linux/kref.h> #include <linux/stat.h> - #include <linux/usb.h> #include <media/v4l2-common.h> +#ifdef CONFIG_SN9C20X_EVDEV +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/usb/input.h> +#endif + #include "sn9c20x.h" #include "sn9c20x-bridge.h" #include "micron.h" @@ -751,6 +756,97 @@ static int usb_sn9c20x_default_settings(struct usb_sn9c20x *dev) return 0; } + +static int input_thread(void *data) +{ + struct usb_sn9c20x *dev = (struct usb_sn9c20x *)data; + __u8 gpio; + int ret, i; + + DECLARE_WAIT_QUEUE_HEAD(wait); + set_freezable(); + for (;;) { + if (kthread_should_stop()) + break; + UDIA_INFO("sn9c20x status thread\n"); + ret = usb_sn9c20x_control_read(dev, 0x1005, &gpio, 1); + if (ret < 0) + continue; + + for (i = 0; i < 8; i++) { + if (!(dev->input_gpio & (1 << i))) + continue; + + input_report_key(dev->input_dev, + BTN_0 + i, + gpio & (1 << i)); + input_sync(dev->input_dev); + } + + wait_event_freezable_timeout(wait, + kthread_should_stop(), + msecs_to_jiffies(100)); + } + return 0; +} + + +static int sn9c20x_input_init(struct usb_sn9c20x *dev) +{ + int ret; + + dev->input_dev = input_allocate_device(); + if (!dev->input_dev) + return -ENOMEM; + + dev->input_dev->name = "SN9C20X Webcam"; + + dev->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s", + dev->udev->bus->bus_name, + dev->udev->devpath); + + if (!dev->input_dev->phys) + return -ENOMEM; + + usb_to_input_id(dev->udev, &dev->input_dev->id); + dev->input_dev->dev.parent = &dev->udev->dev; + + set_bit(EV_KEY, dev->input_dev->evbit); + set_bit(BTN_0, dev->input_dev->keybit); + set_bit(BTN_1, dev->input_dev->keybit); + set_bit(BTN_2, dev->input_dev->keybit); + set_bit(BTN_3, dev->input_dev->keybit); + set_bit(BTN_4, dev->input_dev->keybit); + set_bit(BTN_5, dev->input_dev->keybit); + set_bit(BTN_6, dev->input_dev->keybit); + set_bit(BTN_7, dev->input_dev->keybit); + + ret = input_register_device(dev->input_dev); + if (ret) + return ret; + + dev->input_task = kthread_run(input_thread, dev, "sn9c20x/%d", + dev->vdev->minor); + + if (IS_ERR(dev->input_task)) + return -EINVAL; + + return 0; +} + +static void sn9c20x_input_cleanup(struct usb_sn9c20x *dev) +{ + if (!IS_ERR(dev->input_task)) + kthread_stop(dev->input_task); + + if (dev->input_dev != NULL) { + input_unregister_device(dev->input_dev); + kfree(dev->input_dev->phys); + input_free_device(dev->input_dev); + dev->input_dev = NULL; + } +} + /** * @brief Load the driver * @@ -837,11 +933,18 @@ static int usb_sn9c20x_probe(struct usb_interface *interface, const struct usb_d sn9c20x_create_debugfs_files(dev); +#ifdef CONFIG_SN9C20X_EVDEV + ret = sn9c20x_input_init(dev); + if (ret < 0) + goto free_dev; +#endif + /* Save our data pointer in this interface device */ usb_set_intfdata(interface, dev); usb_sn9c20x_default_settings(dev); + return 0; free_dev: @@ -860,6 +963,9 @@ void usb_sn9c20x_delete(struct kref *kref) sn9c20x_remove_debugfs_files(dev); v4l_sn9c20x_unregister_video_device(dev); } +#ifdef CONFIG_SN9C20X_EVDEV + sn9c20x_input_cleanup(dev); +#endif kfree(dev); } diff --git a/sn9c20x.h b/sn9c20x.h index 8da4265..a9aa513 100644 --- a/sn9c20x.h +++ b/sn9c20x.h @@ -27,6 +27,9 @@ #include <linux/kernel.h> #include <linux/version.h> #include <linux/usb.h> +#ifdef CONFIG_SN9C20X_EVDEV +#include <linux/input.h> +#endif #include <media/v4l2-common.h> #ifndef SN9C20X_H @@ -315,7 +318,6 @@ struct sn9c20x_camera { int hstart; int (*flip_detect) (struct usb_sn9c20x *dev); - int (*button_detect) (struct usb_sn9c20x *dev); int (*set_hvflip) (struct usb_sn9c20x *dev); /* image quality functions */ int (*set_exposure) (struct usb_sn9c20x *dev); @@ -335,6 +337,11 @@ struct sn9c20x_camera { * @struct usb_sn9c20x */ struct usb_sn9c20x { +#ifdef CONFIG_SN9C20X_EVDEV + struct input_dev *input_dev; + __u8 input_gpio; + struct task_struct *input_task; +#endif struct video_device *vdev; /**< Pointer on a V4L2 video device */ struct usb_device *udev; /**< Pointer on a USB device */ struct usb_interface *interface;/**< Pointer on a USB interface */ @@ -387,7 +394,6 @@ int sn9c20x_enable_video(struct usb_sn9c20x *dev, int enable); int dev_sn9c20x_call_constantly(struct usb_sn9c20x *dev); int dev_sn9c20x_flip_detection(struct usb_sn9c20x *dev); -int dev_sn9c20x_button_detection(struct usb_sn9c20x *dev); int dev_sn9c20x_camera_set_exposure(struct usb_sn9c20x *); int dev_sn9c20x_camera_set_gain(struct usb_sn9c20x *); int dev_sn9c20x_camera_set_hvflip(struct usb_sn9c20x *); -- 1.5.6.3
