From d0d480cce8f522b37c2c1de38230fc9ad15fa506 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:44 -0700 Subject: leds: add led-class attribute-group support Allow led-class devices to be created with optional attribute groups. This is needed in order to allow led drivers to create custom device attributes in a race-free manner. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index f37d63cf726b..aa29198fca3e 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -210,8 +210,9 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = { */ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { - led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, - "%s", led_cdev->name); + led_cdev->dev = device_create_with_groups(leds_class, parent, 0, + led_cdev, led_cdev->groups, + "%s", led_cdev->name); if (IS_ERR(led_cdev->dev)) return PTR_ERR(led_cdev->dev); -- cgit v1.2.3 From 74c850394394efc0905c22ab9686e8d20e3921eb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:45 -0700 Subject: leds: lm3550: fix attribute-creation race Use the attribute groups of the led-class to create the mode attribute during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3530.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 652368c2ea9a..91325de3cd33 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -400,6 +400,12 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute } static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set); +static struct attribute *lm3530_attrs[] = { + &dev_attr_mode.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm3530); + static int lm3530_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -436,6 +442,7 @@ static int lm3530_probe(struct i2c_client *client, drvdata->led_dev.name = LM3530_LED_DEV; drvdata->led_dev.brightness_set = lm3530_brightness_set; drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; + drvdata->led_dev.groups = lm3530_groups; i2c_set_clientdata(client, drvdata); @@ -461,26 +468,13 @@ static int lm3530_probe(struct i2c_client *client, return err; } - err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode); - if (err < 0) { - dev_err(&client->dev, "File device creation failed: %d\n", err); - err = -ENODEV; - goto err_create_file; - } - return 0; - -err_create_file: - led_classdev_unregister(&drvdata->led_dev); - return err; } static int lm3530_remove(struct i2c_client *client) { struct lm3530_data *drvdata = i2c_get_clientdata(client); - device_remove_file(drvdata->led_dev.dev, &dev_attr_mode); - lm3530_led_disable(drvdata); led_classdev_unregister(&drvdata->led_dev); return 0; -- cgit v1.2.3 From 9db9386016c6cfc6a6af3bacc7043cab8ff04ecf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:46 -0700 Subject: leds: lm3533: fix attribute-creation race Use the attribute groups of the led-class to create the attributes during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3533.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index e2c642c1169b..cbf61a40137d 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -645,6 +645,11 @@ static struct attribute_group lm3533_led_attribute_group = { .attrs = lm3533_led_attributes }; +static const struct attribute_group *lm3533_led_attribute_groups[] = { + &lm3533_led_attribute_group, + NULL +}; + static int lm3533_led_setup(struct lm3533_led *led, struct lm3533_led_platform_data *pdata) { @@ -692,6 +697,7 @@ static int lm3533_led_probe(struct platform_device *pdev) led->cdev.brightness_get = lm3533_led_get; led->cdev.blink_set = lm3533_led_blink_set; led->cdev.brightness = LED_OFF; + led->cdev.groups = lm3533_led_attribute_groups, led->id = pdev->id; mutex_init(&led->mutex); @@ -715,25 +721,16 @@ static int lm3533_led_probe(struct platform_device *pdev) led->cb.dev = led->cdev.dev; - ret = sysfs_create_group(&led->cdev.dev->kobj, - &lm3533_led_attribute_group); - if (ret < 0) { - dev_err(&pdev->dev, "failed to create sysfs attributes\n"); - goto err_unregister; - } - ret = lm3533_led_setup(led, pdata); if (ret) - goto err_sysfs_remove; + goto err_unregister; ret = lm3533_ctrlbank_enable(&led->cb); if (ret) - goto err_sysfs_remove; + goto err_unregister; return 0; -err_sysfs_remove: - sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); err_unregister: led_classdev_unregister(&led->cdev); flush_work(&led->work); @@ -748,7 +745,6 @@ static int lm3533_led_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s\n", __func__); lm3533_ctrlbank_disable(&led->cb); - sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); led_classdev_unregister(&led->cdev); flush_work(&led->work); -- cgit v1.2.3 From 0d25376c326220687a2dccb5c40b785a7499326c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:47 -0700 Subject: leds: lm355x: fix attribute-creation race Use the attribute groups of the led-class to create the indicator-LED attributes during probe in order to avoid racing with userspace. Note that the comment about the pattern attribute only being for LM3554 was incorrect and did not match the code (the original leds-lm3556 driver had the attribute before LM3554 support was added). Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-lm355x.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c index 591eb5e58ae3..f5112cb2d991 100644 --- a/drivers/leds/leds-lm355x.c +++ b/drivers/leds/leds-lm355x.c @@ -413,6 +413,12 @@ out: static DEVICE_ATTR(pattern, S_IWUSR, NULL, lm3556_indicator_pattern_store); +static struct attribute *lm355x_indicator_attrs[] = { + &dev_attr_pattern.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm355x_indicator); + static const struct regmap_config lm355x_regmap = { .reg_bits = 8, .val_bits = 8, @@ -501,25 +507,18 @@ static int lm355x_probe(struct i2c_client *client, else chip->cdev_indicator.max_brightness = 8; chip->cdev_indicator.brightness_set = lm355x_indicator_brightness_set; + /* indicator pattern control only for LM3556 */ + if (id->driver_data == CHIP_LM3556) + chip->cdev_indicator.groups = lm355x_indicator_groups; err = led_classdev_register((struct device *) &client->dev, &chip->cdev_indicator); if (err < 0) goto err_create_indicator_file; - /* indicator pattern control only for LM3554 */ - if (id->driver_data == CHIP_LM3556) { - err = - device_create_file(chip->cdev_indicator.dev, - &dev_attr_pattern); - if (err < 0) - goto err_create_pattern_file; - } dev_info(&client->dev, "%s is initialized\n", lm355x_name[id->driver_data]); return 0; -err_create_pattern_file: - led_classdev_unregister(&chip->cdev_indicator); err_create_indicator_file: led_classdev_unregister(&chip->cdev_torch); err_create_torch_file: @@ -534,8 +533,6 @@ static int lm355x_remove(struct i2c_client *client) struct lm355x_reg_data *preg = chip->regs; regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); - if (chip->type == CHIP_LM3556) - device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern); led_classdev_unregister(&chip->cdev_indicator); flush_work(&chip->work_indicator); led_classdev_unregister(&chip->cdev_torch); -- cgit v1.2.3 From fe29a3b0bb375aeac1c995904369553e62d03d75 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:48 -0700 Subject: leds: lm3642: fix attribute-creation race Use the attribute groups of the led-class to create the flash and torch-LED attributes during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-lm3642.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index ceb6b3cde6fe..d3dec0132769 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -313,6 +313,18 @@ static const struct regmap_config lm3642_regmap = { .max_register = REG_MAX, }; +static struct attribute *lm3642_flash_attrs[] = { + &dev_attr_strobe_pin.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm3642_flash); + +static struct attribute *lm3642_torch_attrs[] = { + &dev_attr_torch_pin.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm3642_torch); + static int lm3642_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -364,17 +376,13 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_flash.max_brightness = 16; chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set; chip->cdev_flash.default_trigger = "flash"; + chip->cdev_flash.groups = lm3642_flash_groups, err = led_classdev_register((struct device *) &client->dev, &chip->cdev_flash); if (err < 0) { dev_err(chip->dev, "failed to register flash\n"); goto err_out; } - err = device_create_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); - if (err < 0) { - dev_err(chip->dev, "failed to create strobe-pin file\n"); - goto err_create_flash_pin_file; - } /* torch */ INIT_WORK(&chip->work_torch, lm3642_deferred_torch_brightness_set); @@ -382,17 +390,13 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_torch.max_brightness = 8; chip->cdev_torch.brightness_set = lm3642_torch_brightness_set; chip->cdev_torch.default_trigger = "torch"; + chip->cdev_torch.groups = lm3642_torch_groups, err = led_classdev_register((struct device *) &client->dev, &chip->cdev_torch); if (err < 0) { dev_err(chip->dev, "failed to register torch\n"); goto err_create_torch_file; } - err = device_create_file(chip->cdev_torch.dev, &dev_attr_torch_pin); - if (err < 0) { - dev_err(chip->dev, "failed to create torch-pin file\n"); - goto err_create_torch_pin_file; - } /* indicator */ INIT_WORK(&chip->work_indicator, @@ -411,12 +415,8 @@ static int lm3642_probe(struct i2c_client *client, return 0; err_create_indicator_file: - device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); -err_create_torch_pin_file: led_classdev_unregister(&chip->cdev_torch); err_create_torch_file: - device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); -err_create_flash_pin_file: led_classdev_unregister(&chip->cdev_flash); err_out: return err; @@ -428,10 +428,8 @@ static int lm3642_remove(struct i2c_client *client) led_classdev_unregister(&chip->cdev_indicator); flush_work(&chip->work_indicator); - device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); led_classdev_unregister(&chip->cdev_torch); flush_work(&chip->work_torch); - device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); led_classdev_unregister(&chip->cdev_flash); flush_work(&chip->work_flash); regmap_write(chip->regmap, REG_ENABLE, 0); -- cgit v1.2.3 From 35c164998eede9e3d49f51541fcf2e639670fe72 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:49 -0700 Subject: leds: max8997: fix attribute-creation race Use the attribute groups of the led-class to create the mode attribute during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-max8997.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c index f449a8bdddc7..607bc2755aba 100644 --- a/drivers/leds/leds-max8997.c +++ b/drivers/leds/leds-max8997.c @@ -229,6 +229,12 @@ static ssize_t max8997_led_store_mode(struct device *dev, static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode); +static struct attribute *max8997_attrs[] = { + &dev_attr_mode.attr, + NULL +}; +ATTRIBUTE_GROUPS(max8997); + static int max8997_led_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); @@ -253,6 +259,7 @@ static int max8997_led_probe(struct platform_device *pdev) led->cdev.brightness_set = max8997_led_brightness_set; led->cdev.flags |= LED_CORE_SUSPENDRESUME; led->cdev.brightness = 0; + led->cdev.groups = max8997_groups; led->iodev = iodev; /* initialize mode and brightness according to platform_data */ @@ -281,14 +288,6 @@ static int max8997_led_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = device_create_file(led->cdev.dev, &dev_attr_mode); - if (ret != 0) { - dev_err(&pdev->dev, - "failed to create file: %d\n", ret); - led_classdev_unregister(&led->cdev); - return ret; - } - return 0; } @@ -296,7 +295,6 @@ static int max8997_led_remove(struct platform_device *pdev) { struct max8997_led *led = platform_get_drvdata(pdev); - device_remove_file(led->cdev.dev, &dev_attr_mode); led_classdev_unregister(&led->cdev); return 0; -- cgit v1.2.3 From 588a6a99286ae30afb1339d8bc2163517b1b7dd1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:50 -0700 Subject: leds: netxbig: fix attribute-creation race Use the attribute groups of the led-class to create the sata attribute during probe in order to avoid racing with userspace. [cooloney@gmail.com: clean up return led_classdev_register()] Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-netxbig.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index e97f443a6e07..64fde485dcaa 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -293,10 +293,14 @@ static ssize_t netxbig_led_sata_show(struct device *dev, static DEVICE_ATTR(sata, 0644, netxbig_led_sata_show, netxbig_led_sata_store); +static struct attribute *netxbig_led_attrs[] = { + &dev_attr_sata.attr, + NULL +}; +ATTRIBUTE_GROUPS(netxbig_led); + static void delete_netxbig_led(struct netxbig_led_data *led_dat) { - if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) - device_remove_file(led_dat->cdev.dev, &dev_attr_sata); led_classdev_unregister(&led_dat->cdev); } @@ -306,7 +310,6 @@ create_netxbig_led(struct platform_device *pdev, const struct netxbig_led *template) { struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); - int ret; spin_lock_init(&led_dat->lock); led_dat->gpio_ext = pdata->gpio_ext; @@ -327,6 +330,12 @@ create_netxbig_led(struct platform_device *pdev, led_dat->sata = 0; led_dat->cdev.brightness = LED_OFF; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + /* + * If available, expose the SATA activity blink capability through + * a "sata" sysfs attribute. + */ + if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) + led_dat->cdev.groups = netxbig_led_groups; led_dat->mode_addr = template->mode_addr; led_dat->mode_val = template->mode_val; led_dat->bright_addr = template->bright_addr; @@ -334,21 +343,7 @@ create_netxbig_led(struct platform_device *pdev, led_dat->timer = pdata->timer; led_dat->num_timer = pdata->num_timer; - ret = led_classdev_register(&pdev->dev, &led_dat->cdev); - if (ret < 0) - return ret; - - /* - * If available, expose the SATA activity blink capability through - * a "sata" sysfs attribute. - */ - if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) { - ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); - if (ret) - led_classdev_unregister(&led_dat->cdev); - } - - return ret; + return led_classdev_register(&pdev->dev, &led_dat->cdev); } static int netxbig_led_probe(struct platform_device *pdev) -- cgit v1.2.3 From 475f85482663985550e589496bc3c1e09521838a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:51 -0700 Subject: leds: ns2: fix attribute-creation race Use the attribute groups of the led-class to create the sata attribute during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-ns2.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index efa625883c83..231993d1fe21 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -185,6 +185,12 @@ static ssize_t ns2_led_sata_show(struct device *dev, static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); +static struct attribute *ns2_led_attrs[] = { + &dev_attr_sata.attr, + NULL +}; +ATTRIBUTE_GROUPS(ns2_led); + static int create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, const struct ns2_led *template) @@ -219,6 +225,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, led_dat->cdev.blink_set = NULL; led_dat->cdev.brightness_set = ns2_led_set; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + led_dat->cdev.groups = ns2_led_groups; led_dat->cmd = template->cmd; led_dat->slow = template->slow; @@ -235,20 +242,11 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, if (ret < 0) return ret; - ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); - if (ret < 0) - goto err_free_cdev; - return 0; - -err_free_cdev: - led_classdev_unregister(&led_dat->cdev); - return ret; } static void delete_ns2_led(struct ns2_led_data *led_dat) { - device_remove_file(led_dat->cdev.dev, &dev_attr_sata); led_classdev_unregister(&led_dat->cdev); } -- cgit v1.2.3 From 67bd8eb07eb873fe0b189ee2a9a9ca67f9023270 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:52 -0700 Subject: leds: ss4200: fix attribute-creation race Use the attribute groups of the led-class to create the blink attribute during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-ss4200.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 2eb3ef62962b..046cb7008745 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -469,6 +469,12 @@ static ssize_t nas_led_blink_store(struct device *dev, static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store); +static struct attribute *nasgpio_led_attrs[] = { + &dev_attr_blink.attr, + NULL +}; +ATTRIBUTE_GROUPS(nasgpio_led); + static int register_nasgpio_led(int led_nr) { int ret; @@ -481,20 +487,18 @@ static int register_nasgpio_led(int led_nr) led->brightness = LED_FULL; led->brightness_set = nasgpio_led_set_brightness; led->blink_set = nasgpio_led_set_blink; + led->groups = nasgpio_led_groups; ret = led_classdev_register(&nas_gpio_pci_dev->dev, led); if (ret) return ret; - ret = device_create_file(led->dev, &dev_attr_blink); - if (ret) - led_classdev_unregister(led); - return ret; + + return 0; } static void unregister_nasgpio_led(int led_nr) { struct led_classdev *led = get_classdev_for_led_nr(led_nr); led_classdev_unregister(led); - device_remove_file(led->dev, &dev_attr_blink); } /* * module load/initialization -- cgit v1.2.3 From b2015ed5eae165372b6762b2d967829ba0254956 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:53 -0700 Subject: leds: wm831x-status: fix attribute-creation race Use the attribute groups of the led-class to create the src attribute during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-wm831x-status.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index e72c974142d0..1b71e0701002 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -219,6 +219,12 @@ static ssize_t wm831x_status_src_store(struct device *dev, static DEVICE_ATTR(src, 0644, wm831x_status_src_show, wm831x_status_src_store); +static struct attribute *wm831x_status_attrs[] = { + &dev_attr_src.attr, + NULL +}; +ATTRIBUTE_GROUPS(wm831x_status); + static int wm831x_status_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); @@ -232,8 +238,7 @@ static int wm831x_status_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_REG, 0); if (res == NULL) { dev_err(&pdev->dev, "No register resource\n"); - ret = -EINVAL; - goto err; + return -EINVAL; } drvdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_status), @@ -284,31 +289,21 @@ static int wm831x_status_probe(struct platform_device *pdev) drvdata->cdev.default_trigger = pdata.default_trigger; drvdata->cdev.brightness_set = wm831x_status_set; drvdata->cdev.blink_set = wm831x_status_blink_set; + drvdata->cdev.groups = wm831x_status_groups; ret = led_classdev_register(wm831x->dev, &drvdata->cdev); if (ret < 0) { dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); - goto err_led; + return ret; } - ret = device_create_file(drvdata->cdev.dev, &dev_attr_src); - if (ret != 0) - dev_err(&pdev->dev, - "No source control for LED: %d\n", ret); - return 0; - -err_led: - led_classdev_unregister(&drvdata->cdev); -err: - return ret; } static int wm831x_status_remove(struct platform_device *pdev) { struct wm831x_status *drvdata = platform_get_drvdata(pdev); - device_remove_file(drvdata->cdev.dev, &dev_attr_src); led_classdev_unregister(&drvdata->cdev); return 0; -- cgit v1.2.3 From f254bea49d05ee80b09d577947bd3b937e3a304a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:54 -0700 Subject: input: lm8323: fix attribute-creation race Use the attribute groups of the led-class to create the time attribute during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Acked-by: Dmitry Torokhov Signed-off-by: Bryan Wu --- drivers/input/keyboard/lm8323.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 0b42118cbf8f..cb32e2b506b7 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -558,6 +558,12 @@ static ssize_t lm8323_pwm_store_time(struct device *dev, } static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time); +static struct attribute *lm8323_pwm_attrs[] = { + &dev_attr_time.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm8323_pwm); + static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, const char *name) { @@ -580,16 +586,11 @@ static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, if (name) { pwm->cdev.name = name; pwm->cdev.brightness_set = lm8323_pwm_set_brightness; + pwm->cdev.groups = lm8323_pwm_groups; if (led_classdev_register(dev, &pwm->cdev) < 0) { dev_err(dev, "couldn't register PWM %d\n", id); return -1; } - if (device_create_file(pwm->cdev.dev, - &dev_attr_time) < 0) { - dev_err(dev, "couldn't register time attribute\n"); - led_classdev_unregister(&pwm->cdev); - return -1; - } pwm->enabled = true; } @@ -753,11 +754,8 @@ fail3: device_remove_file(&client->dev, &dev_attr_disable_kp); fail2: while (--pwm >= 0) - if (lm->pwm[pwm].enabled) { - device_remove_file(lm->pwm[pwm].cdev.dev, - &dev_attr_time); + if (lm->pwm[pwm].enabled) led_classdev_unregister(&lm->pwm[pwm].cdev); - } fail1: input_free_device(idev); kfree(lm); @@ -777,10 +775,8 @@ static int lm8323_remove(struct i2c_client *client) device_remove_file(&lm->client->dev, &dev_attr_disable_kp); for (i = 0; i < 3; i++) - if (lm->pwm[i].enabled) { - device_remove_file(lm->pwm[i].cdev.dev, &dev_attr_time); + if (lm->pwm[i].enabled) led_classdev_unregister(&lm->pwm[i].cdev); - } kfree(lm); -- cgit v1.2.3 From 660216bf82e0cd65603c0935193c1f7c4aca04fb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:55 -0700 Subject: leds: lp55xx-common: fix sysfs entry leak Make sure the sysfs group is removed when the LEDs are unregistered. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 88317b4f7bf3..3fbfb31602c7 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -520,6 +520,8 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) for (i = 0; i < chip->num_leds; i++) { each = led + i; + sysfs_remove_group(&each->cdev.dev->kobj, + &lp55xx_led_attr_group); led_classdev_unregister(&each->cdev); flush_work(&each->brightness_work); } -- cgit v1.2.3 From 44a1255b03aeedb56cbe7cf27461458bd4513049 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:56 -0700 Subject: leds: lp55xx-common: fix attribute-creation race Use the attribute groups of the led-class to create the LED attributes during probe in order to avoid racing with userspace. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 3fbfb31602c7..77c26bc32eed 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -127,15 +127,12 @@ static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current, lp55xx_store_current); static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL); -static struct attribute *lp55xx_led_attributes[] = { +static struct attribute *lp55xx_led_attrs[] = { &dev_attr_led_current.attr, &dev_attr_max_current.attr, NULL, }; - -static struct attribute_group lp55xx_led_attr_group = { - .attrs = lp55xx_led_attributes -}; +ATTRIBUTE_GROUPS(lp55xx_led); static void lp55xx_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) @@ -176,6 +173,7 @@ static int lp55xx_init_led(struct lp55xx_led *led, } led->cdev.brightness_set = lp55xx_set_brightness; + led->cdev.groups = lp55xx_led_groups; if (pdata->led_config[chan].name) { led->cdev.name = pdata->led_config[chan].name; @@ -185,24 +183,12 @@ static int lp55xx_init_led(struct lp55xx_led *led, led->cdev.name = name; } - /* - * register led class device for each channel and - * add device attributes - */ - ret = led_classdev_register(dev, &led->cdev); if (ret) { dev_err(dev, "led register err: %d\n", ret); return ret; } - ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group); - if (ret) { - dev_err(dev, "led sysfs err: %d\n", ret); - led_classdev_unregister(&led->cdev); - return ret; - } - return 0; } @@ -520,8 +506,6 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) for (i = 0; i < chip->num_leds; i++) { each = led + i; - sysfs_remove_group(&each->cdev.dev->kobj, - &lp55xx_led_attr_group); led_classdev_unregister(&each->cdev); flush_work(&each->brightness_work); } -- cgit v1.2.3 From f471d9480275796dea2ac7ec249b050e70a2888d Mon Sep 17 00:00:00 2001 From: Janne Kanniainen Date: Wed, 18 Jun 2014 19:05:02 +0300 Subject: HID: add support for MSI GT683R led panels This driver adds support for USB controlled led panels that exists in MSI GT683R laptop Signed-off-by: Janne Kanniainen Reviewed-by: Johan Hovold Signed-off-by: Jiri Kosina Signed-off-by: Bryan Wu --- .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 + drivers/hid/Kconfig | 14 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-gt683r.c | 309 +++++++++++++++++++++ drivers/hid/hid-ids.h | 2 +- drivers/hid/usbhid/hid-quirks.c | 2 +- 7 files changed, 341 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r create mode 100644 drivers/hid/hid-gt683r.c (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r new file mode 100644 index 000000000000..317e9d5170f3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r @@ -0,0 +1,14 @@ +What: /sys/class/hidraw//device/leds_mode +Date: Jun 2014 +KernelVersion: 3.17 +Contact: Janne Kanniainen +Description: + Set the mode of LEDs + + 0 - normal + 1 - audio + 2 - breathing + + Normal: LEDs are fully on when enabled + Audio: LEDs brightness depends on sound level + Breathing: LEDs brightness varies at human breathing rate \ No newline at end of file diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 800c8b60f7a2..bc7ecbdd03da 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -261,6 +261,20 @@ config HOLTEK_FF Say Y here if you have a Holtek On Line Grip based game controller and want to have force feedback support for it. +config HID_GT683R + tristate "MSI GT68xR LED support" + depends on LEDS_CLASS && USB_HID + ---help--- + Say Y here if you want to enable support for the three MSI GT68xR LEDs + + This driver support following modes: + - Normal: LEDs are fully on when enabled + - Audio: LEDs brightness depends on sound level + - Breathing: LEDs brightness varies at human breathing rate + + Currently the following devices are know to be supported: + - MSI GT683R + config HID_HUION tristate "Huion tablets" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index a6fa6baf368e..058b5f0249e1 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd1ea87..2ce3f7a88002 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1845,6 +1845,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) }, diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c new file mode 100644 index 000000000000..077f7a19c9d5 --- /dev/null +++ b/drivers/hid/hid-gt683r.c @@ -0,0 +1,309 @@ +/* + * MSI GT683R led driver + * + * Copyright (c) 2014 Janne Kanniainen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "hid-ids.h" + +#define GT683R_BUFFER_SIZE 8 + +/* + * GT683R_LED_OFF: all LEDs are off + * GT683R_LED_AUDIO: LEDs brightness depends on sound level + * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate + * GT683R_LED_NORMAL: LEDs are fully on when enabled + */ +enum gt683r_led_mode { + GT683R_LED_OFF = 0, + GT683R_LED_AUDIO = 2, + GT683R_LED_BREATHING = 3, + GT683R_LED_NORMAL = 5 +}; + +enum gt683r_panels { + GT683R_LED_BACK = 0, + GT683R_LED_SIDE = 1, + GT683R_LED_FRONT = 2, + GT683R_LED_COUNT, +}; + +static const char * const gt683r_panel_names[] = { + "back", + "side", + "front", +}; + +struct gt683r_led { + struct hid_device *hdev; + struct led_classdev led_devs[GT683R_LED_COUNT]; + struct mutex lock; + struct work_struct work; + enum led_brightness brightnesses[GT683R_LED_COUNT]; + enum gt683r_led_mode mode; +}; + +static const struct hid_device_id gt683r_led_id[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) }, + { } +}; + +static void gt683r_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int i; + struct device *dev = led_cdev->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct gt683r_led *led = hid_get_drvdata(hdev); + + for (i = 0; i < GT683R_LED_COUNT; i++) { + if (led_cdev == &led->led_devs[i]) + break; + } + + if (i < GT683R_LED_COUNT) { + led->brightnesses[i] = brightness; + schedule_work(&led->work); + } +} + +static ssize_t leds_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 sysfs_mode; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct gt683r_led *led = hid_get_drvdata(hdev); + + if (led->mode == GT683R_LED_NORMAL) + sysfs_mode = 0; + else if (led->mode == GT683R_LED_AUDIO) + sysfs_mode = 1; + else + sysfs_mode = 2; + + return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode); +} + +static ssize_t leds_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 sysfs_mode; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct gt683r_led *led = hid_get_drvdata(hdev); + + + if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2) + return -EINVAL; + + mutex_lock(&led->lock); + + if (sysfs_mode == 0) + led->mode = GT683R_LED_NORMAL; + else if (sysfs_mode == 1) + led->mode = GT683R_LED_AUDIO; + else + led->mode = GT683R_LED_BREATHING; + + mutex_unlock(&led->lock); + schedule_work(&led->work); + + return count; +} + +static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg) +{ + int ret; + + ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret != GT683R_BUFFER_SIZE) { + hid_err(led->hdev, + "failed to send set report request: %i\n", ret); + if (ret < 0) + return ret; + return -EIO; + } + + return 0; +} + +static int gt683r_leds_set(struct gt683r_led *led, u8 leds) +{ + int ret; + u8 *buffer; + + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 0x01; + buffer[1] = 0x02; + buffer[2] = 0x30; + buffer[3] = leds; + ret = gt683r_led_snd_msg(led, buffer); + + kfree(buffer); + return ret; +} + +static int gt683r_mode_set(struct gt683r_led *led, u8 mode) +{ + int ret; + u8 *buffer; + + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 0x01; + buffer[1] = 0x02; + buffer[2] = 0x20; + buffer[3] = mode; + buffer[4] = 0x01; + ret = gt683r_led_snd_msg(led, buffer); + + kfree(buffer); + return ret; +} + +static void gt683r_led_work(struct work_struct *work) +{ + int i; + u8 leds = 0; + u8 mode; + struct gt683r_led *led = container_of(work, struct gt683r_led, work); + + mutex_lock(&led->lock); + + for (i = 0; i < GT683R_LED_COUNT; i++) { + if (led->brightnesses[i]) + leds |= BIT(i); + } + + if (gt683r_leds_set(led, leds)) + goto fail; + + if (leds) + mode = led->mode; + else + mode = GT683R_LED_OFF; + + gt683r_mode_set(led, mode); +fail: + mutex_unlock(&led->lock); +} + +static DEVICE_ATTR_RW(leds_mode); + +static int gt683r_led_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int i; + int ret; + int name_sz; + char *name; + struct gt683r_led *led; + + led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->mode = GT683R_LED_NORMAL; + led->hdev = hdev; + hid_set_drvdata(hdev, led); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "hid parsing failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + for (i = 0; i < GT683R_LED_COUNT; i++) { + name_sz = strlen(dev_name(&hdev->dev)) + + strlen(gt683r_panel_names[i]) + 3; + + name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto fail; + } + + snprintf(name, name_sz, "%s::%s", + dev_name(&hdev->dev), gt683r_panel_names[i]); + led->led_devs[i].name = name; + led->led_devs[i].max_brightness = 1; + led->led_devs[i].brightness_set = gt683r_brightness_set; + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]); + if (ret) { + hid_err(hdev, "could not register led device\n"); + goto fail; + } + } + + ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode); + if (ret) { + hid_err(hdev, "could not make mode attribute file\n"); + goto fail; + } + + mutex_init(&led->lock); + INIT_WORK(&led->work, gt683r_led_work); + + return 0; + +fail: + for (i = i - 1; i >= 0; i--) + led_classdev_unregister(&led->led_devs[i]); + hid_hw_stop(hdev); + return ret; +} + +static void gt683r_led_remove(struct hid_device *hdev) +{ + int i; + struct gt683r_led *led = hid_get_drvdata(hdev); + + device_remove_file(&hdev->dev, &dev_attr_leds_mode); + for (i = 0; i < GT683R_LED_COUNT; i++) + led_classdev_unregister(&led->led_devs[i]); + flush_work(&led->work); + hid_hw_stop(hdev); +} + +static struct hid_driver gt683r_led_driver = { + .probe = gt683r_led_probe, + .remove = gt683r_led_remove, + .name = "gt683r_led", + .id_table = gt683r_led_id, +}; + +module_hid_driver(gt683r_led_driver); + +MODULE_AUTHOR("Janne Kanniainen"); +MODULE_DESCRIPTION("MSI GT683R led driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6d00bb9366fa..7370ae301975 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -645,7 +645,7 @@ #define USB_DEVICE_ID_GENIUS_KB29E 0x3004 #define USB_VENDOR_ID_MSI 0x1770 -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00 +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00 #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 #define USB_DEVICE_ID_N_S_HARMONY 0xc359 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 59badc10a08c..13ca19262e0c 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -73,7 +73,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, -- cgit v1.2.3 From c3883ae918c8754f7c251564d63211e1d5648b08 Mon Sep 17 00:00:00 2001 From: Janne Kanniainen Date: Thu, 3 Jul 2014 10:17:08 -0700 Subject: HID: gt683r: fix race condition This will fix race condition noticed by Oliver Neukum. Sysfs files are created before mutex and work are initialized. Signed-off-by: Janne Kanniainen Reviewed-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/hid/hid-gt683r.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c index 077f7a19c9d5..073bd80ec839 100644 --- a/drivers/hid/hid-gt683r.c +++ b/drivers/hid/hid-gt683r.c @@ -227,6 +227,9 @@ static int gt683r_led_probe(struct hid_device *hdev, if (!led) return -ENOMEM; + mutex_init(&led->lock); + INIT_WORK(&led->work, gt683r_led_work); + led->mode = GT683R_LED_NORMAL; led->hdev = hdev; hid_set_drvdata(hdev, led); @@ -271,9 +274,6 @@ static int gt683r_led_probe(struct hid_device *hdev, goto fail; } - mutex_init(&led->lock); - INIT_WORK(&led->work, gt683r_led_work); - return 0; fail: -- cgit v1.2.3 From 6522fe1c39a79d4753e37d912876315720119f82 Mon Sep 17 00:00:00 2001 From: Janne Kanniainen Date: Thu, 3 Jul 2014 10:17:09 -0700 Subject: HID: gt683r: move mode attribute to led-class devices Move led_mode attribute from HID device to led-class devices and rename it mode. This will also fix race condition by using attribute-groups. (cooloney@gmai.com: fix a typo in commit message) Signed-off-by: Janne Kanniainen Reviewed-by: Johan Hovold Signed-off-by: Bryan Wu --- .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 --------- Documentation/ABI/testing/sysfs-class-leds-gt683r | 16 ++++++++++ drivers/hid/hid-gt683r.c | 36 ++++++++++++++-------- 3 files changed, 40 insertions(+), 26 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r create mode 100644 Documentation/ABI/testing/sysfs-class-leds-gt683r (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r deleted file mode 100644 index 317e9d5170f3..000000000000 --- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r +++ /dev/null @@ -1,14 +0,0 @@ -What: /sys/class/hidraw//device/leds_mode -Date: Jun 2014 -KernelVersion: 3.17 -Contact: Janne Kanniainen -Description: - Set the mode of LEDs - - 0 - normal - 1 - audio - 2 - breathing - - Normal: LEDs are fully on when enabled - Audio: LEDs brightness depends on sound level - Breathing: LEDs brightness varies at human breathing rate \ No newline at end of file diff --git a/Documentation/ABI/testing/sysfs-class-leds-gt683r b/Documentation/ABI/testing/sysfs-class-leds-gt683r new file mode 100644 index 000000000000..e4fae6026e79 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-leds-gt683r @@ -0,0 +1,16 @@ +What: /sys/class/leds//gt683r/mode +Date: Jun 2014 +KernelVersion: 3.17 +Contact: Janne Kanniainen +Description: + Set the mode of LEDs. You should notice that changing the mode + of one LED will update the mode of its two sibling devices as + well. + + 0 - normal + 1 - audio + 2 - breathing + + Normal: LEDs are fully on when enabled + Audio: LEDs brightness depends on sound level + Breathing: LEDs brightness varies at human breathing rate \ No newline at end of file diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c index 073bd80ec839..0d6f135e266c 100644 --- a/drivers/hid/hid-gt683r.c +++ b/drivers/hid/hid-gt683r.c @@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev, } } -static ssize_t leds_mode_show(struct device *dev, +static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 sysfs_mode; - struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct hid_device *hdev = container_of(dev->parent, + struct hid_device, dev); struct gt683r_led *led = hid_get_drvdata(hdev); if (led->mode == GT683R_LED_NORMAL) @@ -102,12 +103,13 @@ static ssize_t leds_mode_show(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode); } -static ssize_t leds_mode_store(struct device *dev, +static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 sysfs_mode; - struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct hid_device *hdev = container_of(dev->parent, + struct hid_device, dev); struct gt683r_led *led = hid_get_drvdata(hdev); @@ -212,7 +214,22 @@ fail: mutex_unlock(&led->lock); } -static DEVICE_ATTR_RW(leds_mode); +static DEVICE_ATTR_RW(mode); + +static struct attribute *gt683r_led_attrs[] = { + &dev_attr_mode.attr, + NULL +}; + +static const struct attribute_group gt683r_led_group = { + .name = "gt683r", + .attrs = gt683r_led_attrs, +}; + +static const struct attribute_group *gt683r_led_groups[] = { + >683r_led_group, + NULL +}; static int gt683r_led_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -261,6 +278,8 @@ static int gt683r_led_probe(struct hid_device *hdev, led->led_devs[i].name = name; led->led_devs[i].max_brightness = 1; led->led_devs[i].brightness_set = gt683r_brightness_set; + led->led_devs[i].groups = gt683r_led_groups; + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]); if (ret) { hid_err(hdev, "could not register led device\n"); @@ -268,12 +287,6 @@ static int gt683r_led_probe(struct hid_device *hdev, } } - ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode); - if (ret) { - hid_err(hdev, "could not make mode attribute file\n"); - goto fail; - } - return 0; fail: @@ -288,7 +301,6 @@ static void gt683r_led_remove(struct hid_device *hdev) int i; struct gt683r_led *led = hid_get_drvdata(hdev); - device_remove_file(&hdev->dev, &dev_attr_leds_mode); for (i = 0; i < GT683R_LED_COUNT; i++) led_classdev_unregister(&led->led_devs[i]); flush_work(&led->work); -- cgit v1.2.3 From 3dfedb9d545346958311b8d73f90b8522d1fc759 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Wed, 2 Jul 2014 22:50:34 -0700 Subject: leds:pca963x: Add support for PCA9635 LED driver chip supports 16 PWM-controlled LEDs Signed-off-by: Peter Meerwald Signed-off-by: Bryan Wu --- drivers/leds/leds-pca963x.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 82589c0a5689..4438be9980a6 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -12,7 +12,7 @@ * directory of this archive for more details. * * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) - * LED driver for the PCA9634 I2C LED driver (7-bit slave address set by hw.) + * LED driver for the PCA9634/5 I2C LED driver (7-bit slave address set by hw.) * * Note that hardware blinking violates the leds infrastructure driver * interface since the hardware only supports blinking all LEDs with the @@ -52,6 +52,7 @@ enum pca963x_type { pca9633, pca9634, + pca9635, }; struct pca963x_chipdef { @@ -74,6 +75,12 @@ static struct pca963x_chipdef pca963x_chipdefs[] = { .ledout_base = 0xc, .n_leds = 8, }, + [pca9635] = { + .grppwm = 0x12, + .grpfreq = 0x13, + .ledout_base = 0x14, + .n_leds = 16, + }, }; /* Total blink period in milliseconds */ @@ -84,6 +91,7 @@ static const struct i2c_device_id pca963x_id[] = { { "pca9632", pca9633 }, { "pca9633", pca9633 }, { "pca9634", pca9634 }, + { "pca9635", pca9635 }, { } }; MODULE_DEVICE_TABLE(i2c, pca963x_id); @@ -107,7 +115,7 @@ struct pca963x_led { struct work_struct work; enum led_brightness brightness; struct led_classdev led_cdev; - int led_num; /* 0 .. 7 potentially */ + int led_num; /* 0 .. 15 potentially */ enum pca963x_cmd cmd; char name[32]; u8 gdc; @@ -321,6 +329,7 @@ static const struct of_device_id of_pca963x_match[] = { { .compatible = "nxp,pca9632", }, { .compatible = "nxp,pca9633", }, { .compatible = "nxp,pca9634", }, + { .compatible = "nxp,pca9635", }, {}, }; #else @@ -375,9 +384,8 @@ static int pca963x_probe(struct i2c_client *client, pca963x_chip->leds = pca963x; /* Turn off LEDs by default*/ - i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00); - if (chip->n_leds > 4) - i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00); + for (i = 0; i < chip->n_leds / 4; i++) + i2c_smbus_write_byte_data(client, chip->ledout_base + i, 0x00); for (i = 0; i < chip->n_leds; i++) { pca963x[i].led_num = i; -- cgit v1.2.3 From 0c62f42dabb5d43d880262d7f1f85e6464acd601 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Wed, 2 Jul 2014 22:50:35 -0700 Subject: leds:pca963x: Always initialize MODE2 register PCA9632 defaults to open-drain PCA9633/4/5 defaults to totem-pole the driver assumed that totem-pole default and didn't actively set the value; the MODE2 register is now written if platform data indicating the mode is given Signed-off-by: Peter Meerwald Signed-off-by: Bryan Wu --- drivers/leds/leds-pca963x.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 4438be9980a6..f110b4c456ba 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -423,9 +423,13 @@ static int pca963x_probe(struct i2c_client *client, /* Disable LED all-call address and set normal mode */ i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00); - /* Configure output: open-drain or totem pole (push-pull) */ - if (pdata && pdata->outdrv == PCA963X_OPEN_DRAIN) - i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x01); + if (pdata) { + /* Configure output: open-drain or totem pole (push-pull) */ + if (pdata->outdrv == PCA963X_OPEN_DRAIN) + i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x01); + else + i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x05); + } return 0; -- cgit v1.2.3 From 8b37e1bef5a6b60e949e28a4db3006e4b00bd758 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Sat, 14 Jun 2014 02:21:40 -0700 Subject: leds: convert blink timer to workqueue This patch converts the blink timer from led-core to workqueue which is more suitable for this kind of non-priority operations. Moreover, timer may lead to errors when a LED setting function use a scheduling function such as pinctrl which is using mutex. Signed-off-by: Vincent Donnefort Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 14 +++++++------- drivers/leds/led-core.c | 11 ++++++----- include/linux/leds.h | 3 +-- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index aa29198fca3e..129729d35478 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -15,10 +15,10 @@ #include #include #include -#include #include #include #include +#include #include "leds.h" static struct class *leds_class; @@ -97,9 +97,10 @@ static const struct attribute_group *led_groups[] = { NULL, }; -static void led_timer_function(unsigned long data) +static void led_work_function(struct work_struct *ws) { - struct led_classdev *led_cdev = (void *)data; + struct led_classdev *led_cdev = + container_of(ws, struct led_classdev, blink_work.work); unsigned long brightness; unsigned long delay; @@ -143,7 +144,8 @@ static void led_timer_function(unsigned long data) } } - mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); + queue_delayed_work(system_wq, &led_cdev->blink_work, + msecs_to_jiffies(delay)); } static void set_brightness_delayed(struct work_struct *ws) @@ -231,9 +233,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); - init_timer(&led_cdev->blink_timer); - led_cdev->blink_timer.function = led_timer_function; - led_cdev->blink_timer.data = (unsigned long)led_cdev; + INIT_DELAYED_WORK(&led_cdev->blink_work, led_work_function); #ifdef CONFIG_LEDS_TRIGGERS led_trigger_set_default(led_cdev); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 71b40d3bf776..4bb116867b88 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "leds.h" DECLARE_RWSEM(leds_list_lock); @@ -51,7 +52,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev, return; } - mod_timer(&led_cdev->blink_timer, jiffies + 1); + queue_delayed_work(system_wq, &led_cdev->blink_work, 1); } @@ -75,7 +76,7 @@ void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { - del_timer_sync(&led_cdev->blink_timer); + cancel_delayed_work_sync(&led_cdev->blink_work); led_cdev->flags &= ~LED_BLINK_ONESHOT; led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; @@ -90,7 +91,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev, int invert) { if ((led_cdev->flags & LED_BLINK_ONESHOT) && - timer_pending(&led_cdev->blink_timer)) + delayed_work_pending(&led_cdev->blink_work)) return; led_cdev->flags |= LED_BLINK_ONESHOT; @@ -107,7 +108,7 @@ EXPORT_SYMBOL(led_blink_set_oneshot); void led_stop_software_blink(struct led_classdev *led_cdev) { - del_timer_sync(&led_cdev->blink_timer); + cancel_delayed_work_sync(&led_cdev->blink_work); led_cdev->blink_delay_on = 0; led_cdev->blink_delay_off = 0; } @@ -116,7 +117,7 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink); void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { - /* delay brightness setting if need to stop soft-blink timer */ + /* delay brightness setting if need to stop soft-blink work */ if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { led_cdev->delayed_set_value = brightness; schedule_work(&led_cdev->set_brightness_work); diff --git a/include/linux/leds.h b/include/linux/leds.h index e43686472197..6a599dce7f9d 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -15,7 +15,6 @@ #include #include #include -#include #include struct device; @@ -69,7 +68,7 @@ struct led_classdev { const char *default_trigger; /* Trigger to use */ unsigned long blink_delay_on, blink_delay_off; - struct timer_list blink_timer; + struct delayed_work blink_work; int blink_brightness; struct work_struct set_brightness_work; -- cgit v1.2.3 From 4da498fc65de321682396401393340fef5b9c2f6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 23 Jul 2014 05:36:00 -0700 Subject: leds: add driver for the iPAQ micro This adds a driver for the iPAQ microcontroller LED. Signed-off-by: Linus Walleij Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 7 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-ipaq-micro.c | 141 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 drivers/leds/leds-ipaq-micro.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a1b044e7eaad..30e82860567a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -143,6 +143,13 @@ config LEDS_SUNFIRE This option enables support for the Left, Middle, and Right LEDs on the I/O and CPU boards of SunFire UltraSPARC servers. +config LEDS_IPAQ_MICRO + tristate "LED Support for the Compaq iPAQ h3xxx" + depends on MFD_IPAQ_MICRO + help + Choose this option if you want to use the notification LED on + Compaq/HP iPAQ h3100 and h3600. + config LEDS_HP6XX tristate "LED Support for the HP Jornada 6xx" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 79c5155199a7..b2a62959ff8f 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o +obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o diff --git a/drivers/leds/leds-ipaq-micro.c b/drivers/leds/leds-ipaq-micro.c new file mode 100644 index 000000000000..3210a47f0812 --- /dev/null +++ b/drivers/leds/leds-ipaq-micro.c @@ -0,0 +1,141 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * h3xxx atmel micro companion support, notification LED subdevice + * + * Author : Linus Walleij + */ + +#include +#include +#include +#include + +#define LED_YELLOW 0x00 +#define LED_GREEN 0x01 + +#define LED_EN (1 << 4) /* LED ON/OFF 0:off, 1:on */ +#define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */ +#define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ + +static void micro_leds_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent); + /* + * In this message: + * Byte 0 = LED color: 0 = yellow, 1 = green + * yellow LED is always ~30 blinks per minute + * Byte 1 = duration (flags?) appears to be ignored + * Byte 2 = green ontime in 1/10 sec (deciseconds) + * 1 = 1/10 second + * 0 = 256/10 second + * Byte 3 = green offtime in 1/10 sec (deciseconds) + * 1 = 1/10 second + * 0 = 256/10 seconds + */ + struct ipaq_micro_msg msg = { + .id = MSG_NOTIFY_LED, + .tx_len = 4, + }; + + msg.tx_data[0] = LED_GREEN; + msg.tx_data[1] = 0; + if (value) { + msg.tx_data[2] = 0; /* Duty cycle 256 */ + msg.tx_data[3] = 1; + } else { + msg.tx_data[2] = 1; + msg.tx_data[3] = 0; /* Duty cycle 256 */ + } + ipaq_micro_tx_msg_sync(micro, &msg); +} + +/* Maximum duty cycle in ms 256/10 sec = 25600 ms */ +#define IPAQ_LED_MAX_DUTY 25600 + +static int micro_leds_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent); + /* + * In this message: + * Byte 0 = LED color: 0 = yellow, 1 = green + * yellow LED is always ~30 blinks per minute + * Byte 1 = duration (flags?) appears to be ignored + * Byte 2 = green ontime in 1/10 sec (deciseconds) + * 1 = 1/10 second + * 0 = 256/10 second + * Byte 3 = green offtime in 1/10 sec (deciseconds) + * 1 = 1/10 second + * 0 = 256/10 seconds + */ + struct ipaq_micro_msg msg = { + .id = MSG_NOTIFY_LED, + .tx_len = 4, + }; + + msg.tx_data[0] = LED_GREEN; + if (*delay_on > IPAQ_LED_MAX_DUTY || + *delay_off > IPAQ_LED_MAX_DUTY) + return -EINVAL; + + if (*delay_on == 0 && *delay_off == 0) { + *delay_on = 100; + *delay_off = 100; + } + + msg.tx_data[1] = 0; + if (*delay_on >= IPAQ_LED_MAX_DUTY) + msg.tx_data[2] = 0; + else + msg.tx_data[2] = (u8) DIV_ROUND_CLOSEST(*delay_on, 100); + if (*delay_off >= IPAQ_LED_MAX_DUTY) + msg.tx_data[3] = 0; + else + msg.tx_data[3] = (u8) DIV_ROUND_CLOSEST(*delay_off, 100); + return ipaq_micro_tx_msg_sync(micro, &msg); +} + +static struct led_classdev micro_led = { + .name = "led-ipaq-micro", + .brightness_set = micro_leds_brightness_set, + .blink_set = micro_leds_blink_set, + .flags = LED_CORE_SUSPENDRESUME, +}; + +static int micro_leds_probe(struct platform_device *pdev) +{ + int ret; + + ret = led_classdev_register(&pdev->dev, µ_led); + if (ret) { + dev_err(&pdev->dev, "registering led failed: %d\n", ret); + return ret; + } + dev_info(&pdev->dev, "iPAQ micro notification LED driver\n"); + + return 0; +} + +static int micro_leds_remove(struct platform_device *pdev) +{ + led_classdev_unregister(µ_led); + return 0; +} + +struct platform_driver micro_leds_device_driver = { + .driver = { + .name = "ipaq-micro-leds", + }, + .probe = micro_leds_probe, + .remove = micro_leds_remove, +}; +module_platform_driver(micro_leds_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("driver for iPAQ Atmel micro leds"); +MODULE_ALIAS("platform:ipaq-micro-leds"); -- cgit v1.2.3 From e661c8978e4833d4148d08b405a2f3175d6f97d9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 28 Jul 2014 06:15:24 -0700 Subject: leds: ipaq-micro: fix sparse non static symbol warning Fixes the following sparse warnings: drivers/leds/leds-ipaq-micro.c:130:24: warning: symbol 'micro_leds_device_driver' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Bryan Wu --- drivers/leds/leds-ipaq-micro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/leds-ipaq-micro.c b/drivers/leds/leds-ipaq-micro.c index 3210a47f0812..3776f516cd88 100644 --- a/drivers/leds/leds-ipaq-micro.c +++ b/drivers/leds/leds-ipaq-micro.c @@ -127,7 +127,7 @@ static int micro_leds_remove(struct platform_device *pdev) return 0; } -struct platform_driver micro_leds_device_driver = { +static struct platform_driver micro_leds_device_driver = { .driver = { .name = "ipaq-micro-leds", }, -- cgit v1.2.3