Hello.

The leds driver used on N900 - ledslp5523.c has a bug. The function lp5523_set_brightness() which might be called in interrupt context, or with interrupts disabled calls (eventually) i2c_smbus_xfer(), which might sleep, which is not allowed. The function should use a work queue, as in this patch.

The bug is not noticeable in usual working, since the leds brightness is only set from userspace, so the function is only called in process context. But trying to change one of the leds' trigger, results in immediate "scheduling while atomic" oops.

--
Matan.
--- orig/kernel-source/drivers/leds/leds-lp5523.c       2009-12-17 
09:29:39.000000000 +0200
+++ kernel-source/drivers/leds/leds-lp5523.c    2010-02-10 19:23:41.000000000 
+0200
@@ -120,6 +120,8 @@
        u8                      led_nr;
        u8                      led_current;
        struct led_classdev     cdev;
+       struct work_struct      work;
+       u8 brightness;
 };
 
 struct lp5523_chip {
@@ -472,10 +474,10 @@
        return pos;
 }
 
-static void lp5523_set_brightness(struct led_classdev *cdev,
-                            enum led_brightness brightness)
+static void lp5523_set_brightness_work(struct work_struct  *work)
 {
-       struct lp5523_led *led = cdev_to_led(cdev);
+        struct lp5523_led *led =
+                container_of(work, struct lp5523_led, work);
        struct lp5523_chip *chip = led_to_lp5523(led);
        struct i2c_client *client = chip->client;
 
@@ -483,10 +485,20 @@
 
        lp5523_write(client,
                     LP5523_REG_LED_PWM_BASE + led->led_nr,
-                    (u8)brightness);
+                    led->brightness);
 
        mutex_unlock(&chip->lock);
 }
+static void lp5523_set_brightness(struct led_classdev *cdev,
+                            enum led_brightness brightness)
+{
+       struct lp5523_led *led = cdev_to_led(cdev);
+       struct lp5523_chip *chip = led_to_lp5523(led);
+       struct i2c_client *client = chip->client;
+
+       led->brightness = (u8)brightness;
+       schedule_work(&led->work);
+}
 
 static int lp5523_do_store_load(struct lp5523_engine *engine,
                                const char *buf, size_t len)
@@ -792,6 +804,7 @@
 
        led->cdev.name = name;
        led->cdev.brightness_set = lp5523_set_brightness;
+       INIT_WORK( &led->work, lp5523_set_brightness_work); 
        if (led_classdev_register(dev, &led->cdev) < 0) {
                dev_err(dev, "couldn't register led %d\n", id);
                return -1;
_______________________________________________
maemo-developers mailing list
[email protected]
https://lists.maemo.org/mailman/listinfo/maemo-developers

Reply via email to