Ok i've been playing around with camera button support. Currently the
only camera that has any support for buttons is the one using the
soi968 sensor. The first patch will modify the driver so we create an
evdev input device and then send button press and release events to
that device. This should allow you to use something like gizmod to
perform an action whenever a button on your webcam is pressed such as
taking a snapshot for instance. The one minor issue is that since it
submits the input events during the v4l dqbuf api you must actually be
streaming video in order to receive button presses, the second patch
attempts to fix this by using a kthread to scan for and submit button
presses.

You can check to see if you are reciving button press events corectly
by first installing gizmod and then at the console type the follwing
# sudo gizmod -g
now with your webcam push one of its buttons and you should see
something like "onEvent: Standard -- /dev/input/event8 | [EV_KEY]
<BTN_2> c: 0x102 v: 0x1"

--~--~---------~--~----~------------~-------~--~----~
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 cd7fb7a57f70988e01268680765eb2dd0081127e Mon Sep 17 00:00:00 2001
From: Brian Johnson <[email protected]>
Date: Sat, 14 Mar 2009 17:28:56 -0400
Subject: [PATCH] Implement input event device for handling button presses

Signed-off-by: Brian Johnson <[email protected]>
---
 .config          |    2 ++
 Kconfig          |    6 ++++++
 Makefile         |    4 ++++
 omnivision.c     |   18 ------------------
 omnivision.h     |    1 -
 sn9c20x-bridge.c |   31 +++++++++++++++++++++++++++++++
 sn9c20x-bridge.h |    1 +
 sn9c20x-dev.c    |   19 +------------------
 sn9c20x-usb.c    |   44 ++++++++++++++++++++++++++++++++++++++++++++
 sn9c20x.h        |    9 +++++++--
 10 files changed, 96 insertions(+), 39 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 bb0f535..c0b40d4 100644
--- a/sn9c20x-bridge.c
+++ b/sn9c20x-bridge.c
@@ -228,6 +228,29 @@ int sn9c20x_set_LEDs(struct usb_sn9c20x *dev, int enable)
 }
 
 /**
+ * @brief submits appropriate input events
+ *
+ * @author Brian
+ *
+ * @param dev Pointer to the device
+ *
+ */
+void sn9c20x_handle_evdev(struct usb_sn9c20x *dev)
+{
+	int ret, i;
+	__u8 gpio;
+	ret = usb_sn9c20x_control_read(dev, 0x1005, &gpio, 1);
+	if (ret < 0)
+		return;
+	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);
+	}
+}
+
+/**
  * @brief Initializes Micro-Controller's I2C interface
  *
  * @author Neekhil
@@ -966,6 +989,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-bridge.h b/sn9c20x-bridge.h
index 029042c..2b0a1a2 100644
--- a/sn9c20x-bridge.h
+++ b/sn9c20x-bridge.h
@@ -47,6 +47,7 @@
 
 int sn9c20x_initialize(struct usb_sn9c20x *dev);
 int sn9c20x_set_LEDs(struct usb_sn9c20x *dev, int enable);
+void sn9c20x_handle_evdev(struct usb_sn9c20x *dev);
 int sn9c20x_set_camera_control(struct usb_sn9c20x *dev, __u32 control, __u32 value);
 int sn9c20x_enable_video(struct usb_sn9c20x *dev, int enable);
 int sn9c20x_i2c_initialize(struct usb_sn9c20x *dev);
diff --git a/sn9c20x-dev.c b/sn9c20x-dev.c
index dd1eabf..ac77365 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");
@@ -247,7 +246,7 @@ 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);
+	sn9c20x_handle_evdev(dev);
 	if (!dev->camera.set_auto_exposure &&
 		dev->vsettings.auto_exposure) {
 		dev_sn9c20x_perform_soft_ae(dev);
@@ -273,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 5094a5d..597da84 100644
--- a/sn9c20x-usb.c
+++ b/sn9c20x-usb.c
@@ -34,6 +34,9 @@
 #include <linux/stat.h>
 
 #include <linux/usb.h>
+#ifdef CONFIG_SN9C20X_EVDEV
+#include <linux/usb/input.h>
+#endif
 #include <media/v4l2-common.h>
 
 #include "sn9c20x.h"
@@ -835,6 +838,38 @@ static int usb_sn9c20x_probe(struct usb_interface *interface, const struct usb_d
 
 	sn9c20x_create_debugfs_files(dev);
 
+#ifdef CONFIG_SN9C20X_EVDEV
+	dev->input_dev = input_allocate_device();
+	if (!dev->input_dev) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+	dev->input_dev->name = "SN9C20X Webcam";
+	dev->input_dev->uniq = kasprintf(GFP_KERNEL, "Video%d",
+					 dev->vdev->minor);
+	dev->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s",
+					 dev->udev->bus->bus_name,
+					 dev->udev->devpath);
+	if (!dev->input_dev->phys || !dev->input_dev->uniq) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+	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)
+		goto free_dev;
+#endif
+
 	/* Save our data pointer in this interface device */
 	usb_set_intfdata(interface, dev);
 
@@ -858,6 +893,15 @@ void usb_sn9c20x_delete(struct kref *kref)
 		sn9c20x_remove_debugfs_files(dev);
 		v4l_sn9c20x_unregister_video_device(dev);
 	}
+#ifdef CONFIG_SN9C20X_EVDEV
+	if (dev->input_dev != NULL) {
+		input_unregister_device(dev->input_dev);
+		kfree(dev->input_dev->phys);
+		kfree(dev->input_dev->uniq);
+		input_free_device(dev->input_dev);
+		dev->input_dev = NULL;
+	}
+#endif
 	kfree(dev);
 }
 
diff --git a/sn9c20x.h b/sn9c20x.h
index 306b1fb..4b1b02b 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);
@@ -336,6 +338,10 @@ struct sn9c20x_camera {
  * @struct usb_sn9c20x
  */
 struct usb_sn9c20x {
+#ifdef CONFIG_SN9C20X_EVDEV
+	struct input_dev *input_dev;
+	__u8 input_gpio;
+#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 */
@@ -388,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

From a3bd5762a07fdff1be7e82477c80d5a3ec2e8fac Mon Sep 17 00:00:00 2001
From: Brian Johnson <[email protected]>
Date: Sat, 14 Mar 2009 17:30:55 -0400
Subject: [PATCH] Move button press checking to kthread

This allows us to genreate button events on the webcam
even when the webcam is not open for streaming.

Signed-off-by: Brian Johnson <[email protected]>
---
 sn9c20x-bridge.c |   23 -----------------------
 sn9c20x-dev.c    |    1 -
 sn9c20x-usb.c    |   31 +++++++++++++++++++++++++++++++
 sn9c20x.h        |    1 +
 4 files changed, 32 insertions(+), 24 deletions(-)

diff --git a/sn9c20x-bridge.c b/sn9c20x-bridge.c
index c0b40d4..5275e74 100644
--- a/sn9c20x-bridge.c
+++ b/sn9c20x-bridge.c
@@ -228,29 +228,6 @@ int sn9c20x_set_LEDs(struct usb_sn9c20x *dev, int enable)
 }
 
 /**
- * @brief submits appropriate input events
- *
- * @author Brian
- *
- * @param dev Pointer to the device
- *
- */
-void sn9c20x_handle_evdev(struct usb_sn9c20x *dev)
-{
-	int ret, i;
-	__u8 gpio;
-	ret = usb_sn9c20x_control_read(dev, 0x1005, &gpio, 1);
-	if (ret < 0)
-		return;
-	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);
-	}
-}
-
-/**
  * @brief Initializes Micro-Controller's I2C interface
  *
  * @author Neekhil
diff --git a/sn9c20x-dev.c b/sn9c20x-dev.c
index ac77365..29f9217 100644
--- a/sn9c20x-dev.c
+++ b/sn9c20x-dev.c
@@ -246,7 +246,6 @@ int dev_sn9c20x_call_constantly(struct usb_sn9c20x *dev)
 
 	/* Know to be broken, temporarely disabled */
 	/*dev_sn9c20x_flip_detection(dev);*/
-	sn9c20x_handle_evdev(dev);
 	if (!dev->camera.set_auto_exposure &&
 		dev->vsettings.auto_exposure) {
 		dev_sn9c20x_perform_soft_ae(dev);
diff --git a/sn9c20x-usb.c b/sn9c20x-usb.c
index 597da84..5310e28 100644
--- a/sn9c20x-usb.c
+++ b/sn9c20x-usb.c
@@ -31,6 +31,7 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/kref.h>
+#include <linux/kthread.h>
 #include <linux/stat.h>
 
 #include <linux/usb.h>
@@ -752,6 +753,26 @@ 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;
+	while (!kthread_should_stop()) {
+		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);
+		}
+	}
+	return 0;
+}
+
 /**
  * @brief Load the driver
  *
@@ -868,6 +889,13 @@ static int usb_sn9c20x_probe(struct usb_interface *interface, const struct usb_d
 	ret = input_register_device(dev->input_dev);
 	if (ret)
 		goto free_dev;
+
+	dev->input_task = kthread_run(input_thread, dev, "sn9c20x/%d", dev->vdev->minor);
+
+	if (IS_ERR(dev->input_task)) {
+		ret = -EINVAL;
+		goto free_dev;
+	}
 #endif
 
 	/* Save our data pointer in this interface device */
@@ -875,6 +903,7 @@ static int usb_sn9c20x_probe(struct usb_interface *interface, const struct usb_d
 
 	usb_sn9c20x_default_settings(dev);
 
+
 	return 0;
 
 free_dev:
@@ -894,6 +923,8 @@ void usb_sn9c20x_delete(struct kref *kref)
 		v4l_sn9c20x_unregister_video_device(dev);
 	}
 #ifdef CONFIG_SN9C20X_EVDEV
+	kthread_stop(dev->input_task);
+
 	if (dev->input_dev != NULL) {
 		input_unregister_device(dev->input_dev);
 		kfree(dev->input_dev->phys);
diff --git a/sn9c20x.h b/sn9c20x.h
index 4b1b02b..7fd0b06 100644
--- a/sn9c20x.h
+++ b/sn9c20x.h
@@ -341,6 +341,7 @@ 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 */
-- 
1.5.6.3

Reply via email to