@@ -6,6 +6,7 @@
*
* Separated from Pixart PAC7311 library by Márton Németh
* Camera button input handling by Márton Németh <nm127@freemail.hu>
+ * LED control by Márton Németh <nm127@freemail.hu>
* Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
@@ -62,6 +63,7 @@
0 | 0xc6 | setwhitebalance()
0 | 0xc7 | setbluebalance()
0 | 0xdc | setbrightcont(), setcolors()
+ 1 | 0x78 | set_streaming_led()
3 | 0x02 | setexposure()
3 | 0x10 | setgain()
3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
@@ -72,6 +74,8 @@
#include <linux/input.h>
#include <media/v4l2-chip-ident.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
#include "gspca.h"
MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
@@ -91,6 +95,7 @@
unsigned char gain;
unsigned char exposure;
unsigned char autogain;
+ unsigned char led;
__u8 hflip;
__u8 vflip;
u8 flags;
@@ -101,6 +106,16 @@
u8 autogain_ignore_frames;
atomic_t avg_lum;
+
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+ struct led_classdev led_cdev;
+ struct work_struct led_work;
+ char name[32];
+#ifdef CONFIG_LEDS_TRIGGERS
+ struct led_trigger led_trigger;
+ char trigger_name[32];
+#endif
+#endif
};
/* V4L2 controls supported by the driver */
@@ -572,6 +587,7 @@
sd->gain = GAIN_DEF;
sd->exposure = EXPOSURE_DEF;
sd->autogain = AUTOGAIN_DEF;
+ sd->led = 0;
sd->hflip = HFLIP_DEF;
sd->vflip = VFLIP_DEF;
sd->flags = id->driver_info;
@@ -716,6 +732,58 @@
reg_w(gspca_dev, 0x11, 0x01);
}
+static void set_streaming_led(struct gspca_dev *gspca_dev, u8 streaming)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = 0;
+
+ if (sd->led) {
+ if (streaming)
+ data = 0x01;
+ else
+ data = 0x00;
+ } else {
+ if (streaming)
+ data = 0x41;
+ else
+ data = 0x40;
+ }
+
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, data);
+}
+
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+/* Set the LED, may sleep */
+static void led_work(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, led_work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ mutex_lock(&gspca_dev->usb_lock);
+ set_streaming_led(gspca_dev, gspca_dev->streaming);
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
+/* LED state set request, must not sleep */
+static void led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ u8 new_value;
+ struct sd *sd = container_of(led_cdev, struct sd, led_cdev);
+
+ if (value == LED_OFF)
+ new_value = 0;
+ else
+ new_value = 1;
+
+ if (sd->led != new_value) {
+ sd->led = new_value;
+ schedule_work(&sd->led_work);
+ }
+}
+#endif
+
/* this function is called at probe and resume time for pac7302 */
static int sd_init(struct gspca_dev *gspca_dev)
{
@@ -747,27 +815,60 @@
atomic_set(&sd->avg_lum, -1);
/* start stream */
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x01);
+
+#if (defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)) && defined(CONFIG_LEDS_TRIGGERS)
+ led_trigger_event(&sd->led_trigger, LED_FULL);
+#elif defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+ sd->led_cdev.brightness = sd->led_cdev.max_brightness;
+ if (!(sd->led_cdev.flags & LED_SUSPENDED))
+ sd->led_cdev.brightness_set(&sd->led_cdev,
+ sd->led_cdev.brightness);
+#else
+ sd->led = 1;
+#endif
+ set_streaming_led(gspca_dev, 1);
return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev);
+#if !defined(CONFIG_LEDS_CLASS) && !defined(CONFIG_LEDS_CLASS_MODULE)
+ sd->led = 0;
+#endif
/* stop stream */
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x00);
+ set_streaming_led(gspca_dev, 0);
+#if (defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)) && defined(CONFIG_LEDS_TRIGGERS)
+ led_trigger_event(&sd->led_trigger, LED_OFF);
+#elif defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+ sd->led_cdev.brightness = LED_OFF;
+ if (!(sd->led_cdev.flags & LED_SUSPENDED))
+ sd->led_cdev.brightness_set(&sd->led_cdev,
+ sd->led_cdev.brightness);
+#endif
}
/* called on streamoff with alt 0 and on disconnect for pac7302 */
static void sd_stop0(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev);
+
if (!gspca_dev->present)
return;
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x40);
+#if !defined(CONFIG_LEDS_CLASS) && !defined(CONFIG_LEDS_CLASS_MODULE)
+ sd->led = 0;
+#endif
+ set_streaming_led(gspca_dev, 0);
+#if (defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)) && defined(CONFIG_LEDS_TRIGGERS)
+ led_trigger_event(&sd->led_trigger, LED_OFF);
+#elif defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+ sd->led_cdev.brightness = LED_OFF;
+ if (!(sd->led_cdev.flags & LED_SUSPENDED))
+ sd->led_cdev.brightness_set(&sd->led_cdev,
+ sd->led_cdev.brightness);
+#endif
}
/* Include pac common sof detection functions */
@@ -1239,15 +1340,65 @@
static int __devinit sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ int ret;
+
+ ret = gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
THIS_MODULE);
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+ if (ret == 0) {
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev);
+
+#ifdef CONFIG_LEDS_TRIGGERS
+ snprintf(sd->trigger_name, sizeof(sd->trigger_name),
+ "pac7302-%u", gspca_dev->vdev.num);
+ sd->led_trigger.name = sd->trigger_name;
+ sd->led_cdev.default_trigger = sd->trigger_name;
+#endif
+ snprintf(sd->name, sizeof(sd->name),
+ "pac7302-%u::camera", gspca_dev->vdev.num);
+ sd->led_cdev.name = sd->name;
+ sd->led_cdev.brightness_set = led_set;
+ sd->led_cdev.blink_set = NULL;
+ sd->led_cdev.flags = LED_CORE_SUSPENDRESUME;
+ INIT_WORK(&sd->led_work, led_work);
+ ret = led_classdev_register(&gspca_dev->dev->dev,
+ &sd->led_cdev);
+ if (ret)
+ gspca_disconnect(intf);
+ else {
+#ifdef CONFIG_LEDS_TRIGGERS
+ led_trigger_register(&sd->led_trigger);
+#endif
+ }
+ }
+#endif
+
+ return ret;
}
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+static void sd_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = container_of(gspca_dev, struct sd, gspca_dev);
+
+#ifdef CONFIG_LEDS_TRIGGERS
+ led_trigger_unregister(&sd->led_trigger);
+#endif
+ led_classdev_unregister(&sd->led_cdev);
+ cancel_work_sync(&sd->led_work);
+ gspca_disconnect(intf);
+}
+#else
+#define sd_disconnect gspca_disconnect
+#endif
+
static struct usb_driver sd_driver = {
.name = MODULE_NAME,
.id_table = device_table,
.probe = sd_probe,
- .disconnect = gspca_disconnect,
+ .disconnect = sd_disconnect,
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,