in mainline
svn path=/dists/trunk/linux-2.6/; revision=12855
This commit is contained in:
parent
6ad66037fa
commit
4a1ac42d99
|
@ -1,79 +0,0 @@
|
|||
From: Sven Wegener <sven.wegener@stealer.net>
|
||||
Subject: [PATCH] leds-pca9532: Fix memory leak and properly handle errors
|
||||
|
||||
When the registration fails, we need to release the memory we allocated. Also
|
||||
we need to save the error from led_classdev_register and propagate it up, else
|
||||
we'll return success, even if we failed.
|
||||
|
||||
Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
|
||||
---
|
||||
drivers/leds/leds-pca9532.c | 22 +++++++++++++---------
|
||||
1 files changed, 13 insertions(+), 9 deletions(-)
|
||||
|
||||
I don't have the hardware. Riku, can you please test if it still works as
|
||||
expected.
|
||||
|
||||
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
|
||||
index 4064d4f..16af817 100644
|
||||
--- a/drivers/leds/leds-pca9532.c
|
||||
+++ b/drivers/leds/leds-pca9532.c
|
||||
@@ -204,8 +204,8 @@ static int pca9532_configure(struct i2c_client *client,
|
||||
led->ldev.brightness = LED_OFF;
|
||||
led->ldev.brightness_set = pca9532_set_brightness;
|
||||
led->ldev.blink_set = pca9532_set_blink;
|
||||
- if (led_classdev_register(&client->dev,
|
||||
- &led->ldev) < 0) {
|
||||
+ err = led_classdev_register(&client->dev, &led->ldev);
|
||||
+ if (err < 0) {
|
||||
dev_err(&client->dev,
|
||||
"couldn't register LED %s\n",
|
||||
led->name);
|
||||
@@ -263,7 +263,6 @@ exit:
|
||||
}
|
||||
|
||||
return err;
|
||||
-
|
||||
}
|
||||
|
||||
static int pca9532_probe(struct i2c_client *client,
|
||||
@@ -271,12 +270,16 @@ static int pca9532_probe(struct i2c_client *client,
|
||||
{
|
||||
struct pca9532_data *data = i2c_get_clientdata(client);
|
||||
struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
|
||||
+ int err;
|
||||
+
|
||||
+ if (!pca9532_pdata)
|
||||
+ return -EIO;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -EIO;
|
||||
|
||||
- data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL);
|
||||
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -285,12 +288,13 @@ static int pca9532_probe(struct i2c_client *client,
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
- if (pca9532_pdata == NULL)
|
||||
- return -EIO;
|
||||
-
|
||||
- pca9532_configure(client, data, pca9532_pdata);
|
||||
- return 0;
|
||||
+ err = pca9532_configure(client, data, pca9532_pdata);
|
||||
+ if (err) {
|
||||
+ kfree(data);
|
||||
+ i2c_set_clientdata(client, NULL);
|
||||
+ }
|
||||
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static int pca9532_remove(struct i2c_client *client)
|
||||
|
||||
|
||||
--
|
||||
"rm -rf" only sounds scary if you don't have backups
|
|
@ -1,199 +0,0 @@
|
|||
From: Riku Voipio <riku.voipio@iki.fi>
|
||||
Subject: [PATCH] Move i2c work to a workqueque
|
||||
|
||||
Apparently these might be called under atomic context,
|
||||
and i2c operations may sleep. BUG found by
|
||||
Ross Burton <ross@burtonini.com>
|
||||
|
||||
Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
|
||||
---
|
||||
drivers/leds/leds-pca9532.c | 51 +++++++++++++++++++++++++++++++++++------
|
||||
include/linux/leds-pca9532.h | 2 +
|
||||
2 files changed, 45 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
|
||||
index 62b60a0..cb8e517 100644
|
||||
--- a/drivers/leds/leds-pca9532.c
|
||||
+++ b/drivers/leds/leds-pca9532.c
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/workqueue.h>
|
||||
#include <linux/leds-pca9532.h>
|
||||
|
||||
static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END};
|
||||
@@ -34,6 +35,7 @@ struct pca9532_data {
|
||||
struct pca9532_led leds[16];
|
||||
struct mutex update_lock;
|
||||
struct input_dev *idev;
|
||||
+ struct work_struct work;
|
||||
u8 pwm[2];
|
||||
u8 psc[2];
|
||||
};
|
||||
@@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = {
|
||||
* as a compromise we average one pwm to the values requested by all
|
||||
* leds that are not ON/OFF.
|
||||
* */
|
||||
-static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink,
|
||||
+static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
|
||||
enum led_brightness value)
|
||||
{
|
||||
int a = 0, b = 0, i = 0;
|
||||
@@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink,
|
||||
b = b/a;
|
||||
if (b > 0xFF)
|
||||
return -EINVAL;
|
||||
- mutex_lock(&data->update_lock);
|
||||
data->pwm[pwm] = b;
|
||||
+ data->psc[pwm] = blink;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int pca9532_setpwm(struct i2c_client *client, int pwm)
|
||||
+{
|
||||
+ struct pca9532_data *data = i2c_get_clientdata(client);
|
||||
+ mutex_lock(&data->update_lock);
|
||||
i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
|
||||
data->pwm[pwm]);
|
||||
- data->psc[pwm] = blink;
|
||||
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
|
||||
data->psc[pwm]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
@@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev,
|
||||
led->state = PCA9532_ON;
|
||||
else {
|
||||
led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */
|
||||
- err = pca9532_setpwm(led->client, 0, 0, value);
|
||||
+ err = pca9532_calcpwm(led->client, 0, 0, value);
|
||||
if (err)
|
||||
return; /* XXX: led api doesn't allow error code? */
|
||||
}
|
||||
- pca9532_setled(led);
|
||||
+ schedule_work(&led->work);
|
||||
}
|
||||
|
||||
static int pca9532_set_blink(struct led_classdev *led_cdev,
|
||||
@@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
|
||||
struct pca9532_led *led = ldev_to_led(led_cdev);
|
||||
struct i2c_client *client = led->client;
|
||||
int psc;
|
||||
+ int err = 0;
|
||||
|
||||
if (*delay_on == 0 && *delay_off == 0) {
|
||||
/* led subsystem ask us for a blink rate */
|
||||
@@ -148,7 +157,11 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
|
||||
|
||||
/* Thecus specific: only use PSC/PWM 0 */
|
||||
psc = (*delay_on * 152-1)/1000;
|
||||
- return pca9532_setpwm(client, 0, psc, led_cdev->brightness);
|
||||
+ err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ schedule_work(&led->work);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int pca9532_event(struct input_dev *dev, unsigned int type,
|
||||
@@ -165,13 +178,28 @@ static int pca9532_event(struct input_dev *dev, unsigned int type,
|
||||
else
|
||||
data->pwm[1] = 0;
|
||||
|
||||
- dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]);
|
||||
+ schedule_work(&data->work);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void pca9532_input_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct pca9532_data *data;
|
||||
+ data = container_of(work, struct pca9532_data, work);
|
||||
mutex_lock(&data->update_lock);
|
||||
i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
|
||||
data->pwm[1]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
+}
|
||||
|
||||
- return 0;
|
||||
+static void pca9532_led_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct pca9532_led *led;
|
||||
+ led = container_of(work, struct pca9532_led, work);
|
||||
+ if (led->state == PCA9532_PWM0)
|
||||
+ pca9532_setpwm(led->client, 0);
|
||||
+ pca9532_setled(led);
|
||||
}
|
||||
|
||||
static int pca9532_configure(struct i2c_client *client,
|
||||
@@ -204,6 +232,7 @@ static int pca9532_configure(struct i2c_client *client,
|
||||
led->ldev.brightness = LED_OFF;
|
||||
led->ldev.brightness_set = pca9532_set_brightness;
|
||||
led->ldev.blink_set = pca9532_set_blink;
|
||||
+ INIT_WORK(&led->work, pca9532_led_work);
|
||||
err = led_classdev_register(&client->dev, &led->ldev);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev,
|
||||
@@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client,
|
||||
BIT_MASK(SND_TONE);
|
||||
data->idev->event = pca9532_event;
|
||||
input_set_drvdata(data->idev, data);
|
||||
+ INIT_WORK(&data->work, pca9532_input_work);
|
||||
err = input_register_device(data->idev);
|
||||
if (err) {
|
||||
input_free_device(data->idev);
|
||||
+ cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
goto exit;
|
||||
}
|
||||
@@ -252,11 +283,13 @@ exit:
|
||||
break;
|
||||
case PCA9532_TYPE_LED:
|
||||
led_classdev_unregister(&data->leds[i].ldev);
|
||||
+ cancel_work_sync(&data->leds[i].work);
|
||||
break;
|
||||
case PCA9532_TYPE_N2100_BEEP:
|
||||
if (data->idev != NULL) {
|
||||
input_unregister_device(data->idev);
|
||||
input_free_device(data->idev);
|
||||
+ cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
}
|
||||
break;
|
||||
@@ -307,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client)
|
||||
break;
|
||||
case PCA9532_TYPE_LED:
|
||||
led_classdev_unregister(&data->leds[i].ldev);
|
||||
+ cancel_work_sync(&data->leds[i].work);
|
||||
break;
|
||||
case PCA9532_TYPE_N2100_BEEP:
|
||||
if (data->idev != NULL) {
|
||||
input_unregister_device(data->idev);
|
||||
input_free_device(data->idev);
|
||||
+ cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
}
|
||||
break;
|
||||
diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h
|
||||
index 81b4207..f158eb1 100644
|
||||
--- a/include/linux/leds-pca9532.h
|
||||
+++ b/include/linux/leds-pca9532.h
|
||||
@@ -15,6 +15,7 @@
|
||||
#define __LINUX_PCA9532_H
|
||||
|
||||
#include <linux/leds.h>
|
||||
+#include <linux/workqueue.h>
|
||||
|
||||
enum pca9532_state {
|
||||
PCA9532_OFF = 0x0,
|
||||
@@ -31,6 +32,7 @@ struct pca9532_led {
|
||||
struct i2c_client *client;
|
||||
char *name;
|
||||
struct led_classdev ldev;
|
||||
+ struct work_struct work;
|
||||
enum pca9532_type type;
|
||||
enum pca9532_state state;
|
||||
};
|
||||
--
|
||||
1.5.6.5
|
||||
|
|
@ -22,6 +22,4 @@
|
|||
+ bugfix/powerpc/lpar-console.patch
|
||||
+ bugfix/all/sata_mv-clear_irq.patch
|
||||
#+ bugfix/all/wireless-regulatory-default-EU.patch
|
||||
#+ features/arm/led-pca9532-mem-leak.patch
|
||||
#+ features/arm/led-pca9532-workqueque.patch
|
||||
+ features/sparc/video-sunxvr500-intergraph.patch
|
||||
|
|
Loading…
Reference in New Issue