Commit 8c2f421c authored by Jiri Kosina's avatar Jiri Kosina

Merge branch 'for-4.8/hid-led' into for-linus

Conflicts:
	drivers/hid/hid-thingm.c
parents e82a82c1 f4c109b6
......@@ -11314,11 +11314,6 @@ F: Documentation/thermal/cpu-cooling-api.txt
F: drivers/thermal/cpu_cooling.c
F: include/linux/cpu_cooling.h
THINGM BLINK(1) USB RGB LED DRIVER
M: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
S: Maintained
F: drivers/hid/hid-thingm.c
THINKPAD ACPI EXTRAS DRIVER
M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
L: ibm-acpi-devel@lists.sourceforge.net
......
......@@ -388,6 +388,21 @@ config HID_LCPOWER
---help---
Support for LC-Power RC1000MCE RF remote control.
config HID_LED
tristate "Simple RGB LED support"
depends on HID
depends on LEDS_CLASS
---help---
Support for simple RGB LED devices. Currently supported are:
- Riso Kagaku Webmail Notifier
- Dream Cheeky Webmail Notifier and Friends Alert
- ThingM blink(1)
- Delcom Visual Signal Indicator Generation 2
- Greynut Luxafor
To compile this driver as a module, choose M here: the
module will be called hid-led.
config HID_LENOVO
tristate "Lenovo / Thinkpad devices"
depends on HID
......@@ -819,11 +834,11 @@ config HID_THINGM
tristate "ThingM blink(1) USB RGB LED"
depends on HID
depends on LEDS_CLASS
select HID_LED
---help---
Support for the ThingM blink(1) USB RGB LED. This driver registers a
Linux LED class instance, plus additional sysfs attributes to control
RGB colors, fade time and playing. The device is exposed through hidraw
to access other functions.
Support for the ThingM blink(1) USB RGB LED. This driver has been
merged into the generic hid led driver. Config symbol HID_THINGM
just selects HID_LED and will be removed soon.
config HID_THRUSTMASTER
tristate "ThrustMaster devices support"
......
......@@ -91,12 +91,12 @@ obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
obj-$(CONFIG_HID_THINGM) += hid-thingm.o
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TIVO) += hid-tivo.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
obj-$(CONFIG_HID_LED) += hid-led.o
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
......
......@@ -1879,8 +1879,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, USB_DEVICE_ID_DELCOM_VISUAL_IND) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) },
......@@ -1964,6 +1967,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_LUXAFOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
......@@ -2010,6 +2014,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
#if IS_ENABLED(CONFIG_HID_ROCCAT)
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
......@@ -2350,8 +2355,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
......@@ -2488,7 +2491,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
{ }
};
......
......@@ -300,6 +300,9 @@
#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a
#define USB_VENDOR_ID_DELCOM 0x0fc5
#define USB_DEVICE_ID_DELCOM_VISUAL_IND 0xb080
#define USB_VENDOR_ID_DELORME 0x1163
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
......@@ -338,6 +341,8 @@
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34
#define USB_DEVICE_ID_DREAM_CHEEKY_WN 0x0004
#define USB_DEVICE_ID_DREAM_CHEEKY_FA 0x000a
#define USB_VENDOR_ID_ELITEGROUP 0x03fc
#define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8
......@@ -684,6 +689,7 @@
#define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002
#define USB_DEVICE_ID_PICK16F1454 0x0042
#define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7
#define USB_DEVICE_ID_LUXAFOR 0xf372
#define USB_VENDOR_ID_MICROSOFT 0x045e
#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b
......
This diff is collapsed.
/*
* ThingM blink(1) USB RGB LED driver
*
* Copyright 2013-2014 Savoir-faire Linux Inc.
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* 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, version 2.
*/
#include <linux/hid.h>
#include <linux/hidraw.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include "hid-ids.h"
#define REPORT_ID 1
#define REPORT_SIZE 9
/* Firmware major number of supported devices */
#define THINGM_MAJOR_MK1 '1'
#define THINGM_MAJOR_MK2 '2'
struct thingm_fwinfo {
char major;
unsigned numrgb;
unsigned first;
};
static const struct thingm_fwinfo thingm_fwinfo[] = {
{
.major = THINGM_MAJOR_MK1,
.numrgb = 1,
.first = 0,
}, {
.major = THINGM_MAJOR_MK2,
.numrgb = 2,
.first = 1,
}
};
/* A red, green or blue channel, part of an RGB chip */
struct thingm_led {
struct thingm_rgb *rgb;
struct led_classdev ldev;
char name[32];
};
/* Basically a WS2812 5050 RGB LED chip */
struct thingm_rgb {
struct thingm_device *tdev;
struct thingm_led red;
struct thingm_led green;
struct thingm_led blue;
u8 num;
};
struct thingm_device {
struct hid_device *hdev;
struct {
char major;
char minor;
} version;
const struct thingm_fwinfo *fwinfo;
struct mutex lock;
struct thingm_rgb *rgb;
};
static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
{
int ret;
hid_dbg(tdev->hdev, "-> %d %c %7ph\n", buf[0], buf[1], &buf[2]);
mutex_lock(&tdev->lock);
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
mutex_unlock(&tdev->lock);
return ret < 0 ? ret : 0;
}
static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
{
int ret;
/*
* A read consists of two operations: sending the read command
* and the actual read from the device. Use the mutex to protect
* the full sequence of both operations.
*/
mutex_lock(&tdev->lock);
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0)
goto err;
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
goto err;
ret = 0;
hid_dbg(tdev->hdev, "<- %d %c %7ph\n", buf[0], buf[1], &buf[2]);
err:
mutex_unlock(&tdev->lock);
return ret;
}
static int thingm_version(struct thingm_device *tdev)
{
u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
int err;
err = thingm_recv(tdev, buf);
if (err)
return err;
tdev->version.major = buf[3];
tdev->version.minor = buf[4];
return 0;
}
static int thingm_write_color(struct thingm_rgb *rgb)
{
u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 };
buf[2] = rgb->red.ldev.brightness;
buf[3] = rgb->green.ldev.brightness;
buf[4] = rgb->blue.ldev.brightness;
return thingm_send(rgb->tdev, buf);
}
static int thingm_led_set(struct led_classdev *ldev,
enum led_brightness brightness)
{
struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
return thingm_write_color(led->rgb);
}
static int thingm_init_led(struct thingm_led *led, const char *color_name,
struct thingm_rgb *rgb, int minor)
{
snprintf(led->name, sizeof(led->name), "thingm%d:%s:led%d",
minor, color_name, rgb->num);
led->ldev.name = led->name;
led->ldev.max_brightness = 255;
led->ldev.brightness_set_blocking = thingm_led_set;
led->ldev.flags = LED_HW_PLUGGABLE;
led->rgb = rgb;
return devm_led_classdev_register(&rgb->tdev->hdev->dev, &led->ldev);
}
static int thingm_init_rgb(struct thingm_rgb *rgb)
{
const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor;
int err;
/* Register the red diode */
err = thingm_init_led(&rgb->red, "red", rgb, minor);
if (err)
return err;
/* Register the green diode */
err = thingm_init_led(&rgb->green, "green", rgb, minor);
if (err)
return err;
/* Register the blue diode */
return thingm_init_led(&rgb->blue, "blue", rgb, minor);
}
static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct thingm_device *tdev;
int i, err;
tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device),
GFP_KERNEL);
if (!tdev)
return -ENOMEM;
tdev->hdev = hdev;
hid_set_drvdata(hdev, tdev);
err = hid_parse(hdev);
if (err)
return err;
mutex_init(&tdev->lock);
err = thingm_version(tdev);
if (err)
return err;
hid_dbg(hdev, "firmware version: %c.%c\n",
tdev->version.major, tdev->version.minor);
for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i)
if (thingm_fwinfo[i].major == tdev->version.major)
tdev->fwinfo = &thingm_fwinfo[i];
if (!tdev->fwinfo) {
hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
return -ENODEV;
}
tdev->rgb = devm_kzalloc(&hdev->dev,
sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
GFP_KERNEL);
if (!tdev->rgb)
return -ENOMEM;
err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (err)
return err;
for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
struct thingm_rgb *rgb = tdev->rgb + i;
rgb->tdev = tdev;
rgb->num = tdev->fwinfo->first + i;
err = thingm_init_rgb(rgb);
if (err) {
hid_hw_stop(hdev);
return err;
}
}
return 0;
}
static const struct hid_device_id thingm_table[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
{ }
};
MODULE_DEVICE_TABLE(hid, thingm_table);
static struct hid_driver thingm_driver = {
.name = "thingm",
.probe = thingm_probe,
.id_table = thingm_table,
};
module_hid_driver(thingm_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vivien Didelot <vivien.didelot@savoirfairelinux.com>");
MODULE_DESCRIPTION("ThingM blink(1) USB RGB LED driver");
......@@ -79,15 +79,6 @@ config USB_LCD
To compile this driver as a module, choose M here: the
module will be called usblcd.
config USB_LED
tristate "USB LED driver support"
help
Say Y here if you want to connect an USBLED device to your
computer's USB port.
To compile this driver as a module, choose M here: the
module will be called usbled.
config USB_CYPRESS_CY7C63
tristate "Cypress CY7C63xxx USB driver support"
help
......
......@@ -15,7 +15,6 @@ obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_TEST) += usbtest.o
......
/*
* USB LED driver
*
* Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)
*
* 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, version 2.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
#define DRIVER_DESC "USB LED Driver"
enum led_type {
DELCOM_VISUAL_SIGNAL_INDICATOR,
DREAM_CHEEKY_WEBMAIL_NOTIFIER,
RISO_KAGAKU_LED
};
/* the Webmail LED made by RISO KAGAKU CORP. decodes a color index
internally, we want to keep the red+green+blue sysfs api, so we decode
from 1-bit RGB to the riso kagaku color index according to this table... */
static unsigned const char riso_kagaku_tbl[] = {
/* R+2G+4B -> riso kagaku color index */
[0] = 0, /* black */
[1] = 2, /* red */
[2] = 1, /* green */
[3] = 5, /* yellow */
[4] = 3, /* blue */
[5] = 6, /* magenta */
[6] = 4, /* cyan */
[7] = 7 /* white */
};
#define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
/* table of devices that work with this driver */
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0fc5, 0x1223),
.driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
{ USB_DEVICE(0x1d34, 0x0004),
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
{ USB_DEVICE(0x1d34, 0x000a),
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
{ USB_DEVICE(0x1294, 0x1320),
.driver_info = RISO_KAGAKU_LED },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
struct usb_led {
struct usb_device *udev;
unsigned char blue;
unsigned char red;
unsigned char green;
enum led_type type;
};
static void change_color(struct usb_led *led)
{
int retval = 0;
unsigned char *buffer;
int actlength;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&led->udev->dev, "out of memory\n");
return;
}
switch (led->type) {
case DELCOM_VISUAL_SIGNAL_INDICATOR: {
unsigned char color = 0x07;
if (led->blue)
color &= ~0x04;
if (led->red)
color &= ~0x02;
if (led->green)
color &= ~0x01;
dev_dbg(&led->udev->dev,
"blue = %d, red = %d, green = %d, color = %.2x\n",
led->blue, led->red, led->green, color);
retval = usb_control_msg(led->udev,
usb_sndctrlpipe(led->udev, 0),
0x12,
0xc8,
(0x02 * 0x100) + 0x0a,
(0x00 * 0x100) + color,
buffer,
8,
2000);
break;
}
case DREAM_CHEEKY_WEBMAIL_NOTIFIER:
dev_dbg(&led->udev->dev,
"red = %d, green = %d, blue = %d\n",
led->red, led->green, led->blue);
buffer[0] = led->red;
buffer[1] = led->green;
buffer[2] = led->blue;
buffer[3] = buffer[4] = buffer[5] = 0;
buffer[6] = 0x1a;
buffer[7] = 0x05;
retval = usb_control_msg(led->udev,
usb_sndctrlpipe(led->udev, 0),
0x09,
0x21,
0x200,
0,
buffer,
8,
2000);
break;
case RISO_KAGAKU_LED:
buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue);
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
buffer[4] = 0;
retval = usb_interrupt_msg(led->udev,
usb_sndctrlpipe(led->udev, 2),
buffer, 5, &actlength, 1000 /*ms timeout*/);
break;
default:
dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
}
if (retval)
dev_dbg(&led->udev->dev, "retval = %d\n", retval);
kfree(buffer);
}
#define show_set(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\
char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
\
return sprintf(buf, "%d\n", led->value); \
} \
static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\
const char *buf, size_t count) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
int temp = simple_strtoul(buf, NULL, 10); \
\
led->value = temp; \
change_color(led); \
return count; \
} \
static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value);
show_set(blue);
show_set(red);
show_set(green);
static int led_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_led *dev = NULL;
int retval = -ENOMEM;
dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "out of memory\n");
goto error_mem;
}
dev->udev = usb_get_dev(udev);
dev->type = id->driver_info;
usb_set_intfdata(interface, dev);
retval = device_create_file(&interface->dev, &dev_attr_blue);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_red);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_green);
if (retval)
goto error;
if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
unsigned char *enable;
enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
if (!enable) {
dev_err(&interface->dev, "out of memory\n");
retval = -ENOMEM;
goto error;
}
retval = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
0x09,
0x21,
0x200,
0,
enable,
8,
2000);
kfree(enable);
if (retval != 8)
goto error;
}
dev_info(&interface->dev, "USB LED device now attached\n");
return 0;
error:
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
error_mem:
return retval;
}
static void led_disconnect(struct usb_interface *interface)
{
struct usb_led *dev;
dev = usb_get_intfdata(interface);
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
/* first remove the files, then set the pointer to NULL */
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
dev_info(&interface->dev, "USB LED now disconnected\n");
}
static struct usb_driver led_driver = {
.name = "usbled",
.probe = led_probe,
.disconnect = led_disconnect,
.id_table = id_table,
};
module_usb_driver(led_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment