Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  leds-blinkm.c
0004  *  (c) Jan-Simon Möller (dl9pf@gmx.de)
0005  */
0006 
0007 #include <linux/module.h>
0008 #include <linux/slab.h>
0009 #include <linux/jiffies.h>
0010 #include <linux/i2c.h>
0011 #include <linux/err.h>
0012 #include <linux/mutex.h>
0013 #include <linux/sysfs.h>
0014 #include <linux/printk.h>
0015 #include <linux/pm_runtime.h>
0016 #include <linux/leds.h>
0017 #include <linux/delay.h>
0018 
0019 /* Addresses to scan - BlinkM is on 0x09 by default*/
0020 static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
0021 
0022 static int blinkm_transfer_hw(struct i2c_client *client, int cmd);
0023 static int blinkm_test_run(struct i2c_client *client);
0024 
0025 struct blinkm_led {
0026     struct i2c_client *i2c_client;
0027     struct led_classdev led_cdev;
0028     int id;
0029 };
0030 
0031 #define cdev_to_blmled(c)          container_of(c, struct blinkm_led, led_cdev)
0032 
0033 struct blinkm_data {
0034     struct i2c_client *i2c_client;
0035     struct mutex update_lock;
0036     /* used for led class interface */
0037     struct blinkm_led blinkm_leds[3];
0038     /* used for "blinkm" sysfs interface */
0039     u8 red;         /* color red */
0040     u8 green;       /* color green */
0041     u8 blue;        /* color blue */
0042     /* next values to use for transfer */
0043     u8 next_red;            /* color red */
0044     u8 next_green;      /* color green */
0045     u8 next_blue;       /* color blue */
0046     /* internal use */
0047     u8 args[7];     /* set of args for transmission */
0048     u8 i2c_addr;        /* i2c addr */
0049     u8 fw_ver;      /* firmware version */
0050     /* used, but not from userspace */
0051     u8 hue;         /* HSB  hue */
0052     u8 saturation;      /* HSB  saturation */
0053     u8 brightness;      /* HSB  brightness */
0054     u8 next_hue;            /* HSB  hue */
0055     u8 next_saturation;     /* HSB  saturation */
0056     u8 next_brightness;     /* HSB  brightness */
0057     /* currently unused / todo */
0058     u8 fade_speed;      /* fade speed     1 - 255 */
0059     s8 time_adjust;     /* time adjust -128 - 127 */
0060     u8 fade:1;      /* fade on = 1, off = 0 */
0061     u8 rand:1;      /* rand fade mode on = 1 */
0062     u8 script_id;       /* script ID */
0063     u8 script_repeats;  /* repeats of script */
0064     u8 script_startline;    /* line to start */
0065 };
0066 
0067 /* Colors */
0068 #define RED   0
0069 #define GREEN 1
0070 #define BLUE  2
0071 
0072 /* mapping command names to cmd chars - see datasheet */
0073 #define BLM_GO_RGB            0
0074 #define BLM_FADE_RGB          1
0075 #define BLM_FADE_HSB          2
0076 #define BLM_FADE_RAND_RGB     3
0077 #define BLM_FADE_RAND_HSB     4
0078 #define BLM_PLAY_SCRIPT       5
0079 #define BLM_STOP_SCRIPT       6
0080 #define BLM_SET_FADE_SPEED    7
0081 #define BLM_SET_TIME_ADJ      8
0082 #define BLM_GET_CUR_RGB       9
0083 #define BLM_WRITE_SCRIPT_LINE 10
0084 #define BLM_READ_SCRIPT_LINE  11
0085 #define BLM_SET_SCRIPT_LR     12    /* Length & Repeats */
0086 #define BLM_SET_ADDR          13
0087 #define BLM_GET_ADDR          14
0088 #define BLM_GET_FW_VER        15
0089 #define BLM_SET_STARTUP_PARAM 16
0090 
0091 /* BlinkM Commands
0092  *  as extracted out of the datasheet:
0093  *
0094  *  cmdchar = command (ascii)
0095  *  cmdbyte = command in hex
0096  *  nr_args = number of arguments (to send)
0097  *  nr_ret  = number of return values (to read)
0098  *  dir = direction (0 = read, 1 = write, 2 = both)
0099  *
0100  */
0101 static const struct {
0102     char cmdchar;
0103     u8 cmdbyte;
0104     u8 nr_args;
0105     u8 nr_ret;
0106     u8 dir:2;
0107 } blinkm_cmds[17] = {
0108   /* cmdchar, cmdbyte, nr_args, nr_ret,  dir */
0109     { 'n', 0x6e, 3, 0, 1},
0110     { 'c', 0x63, 3, 0, 1},
0111     { 'h', 0x68, 3, 0, 1},
0112     { 'C', 0x43, 3, 0, 1},
0113     { 'H', 0x48, 3, 0, 1},
0114     { 'p', 0x70, 3, 0, 1},
0115     { 'o', 0x6f, 0, 0, 1},
0116     { 'f', 0x66, 1, 0, 1},
0117     { 't', 0x74, 1, 0, 1},
0118     { 'g', 0x67, 0, 3, 0},
0119     { 'W', 0x57, 7, 0, 1},
0120     { 'R', 0x52, 2, 5, 2},
0121     { 'L', 0x4c, 3, 0, 1},
0122     { 'A', 0x41, 4, 0, 1},
0123     { 'a', 0x61, 0, 1, 0},
0124     { 'Z', 0x5a, 0, 1, 0},
0125     { 'B', 0x42, 5, 0, 1},
0126 };
0127 
0128 static ssize_t show_color_common(struct device *dev, char *buf, int color)
0129 {
0130     struct i2c_client *client;
0131     struct blinkm_data *data;
0132     int ret;
0133 
0134     client = to_i2c_client(dev);
0135     data = i2c_get_clientdata(client);
0136 
0137     ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB);
0138     if (ret < 0)
0139         return ret;
0140     switch (color) {
0141     case RED:
0142         return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red);
0143     case GREEN:
0144         return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green);
0145     case BLUE:
0146         return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue);
0147     default:
0148         return -EINVAL;
0149     }
0150     return -EINVAL;
0151 }
0152 
0153 static int store_color_common(struct device *dev, const char *buf, int color)
0154 {
0155     struct i2c_client *client;
0156     struct blinkm_data *data;
0157     int ret;
0158     u8 value;
0159 
0160     client = to_i2c_client(dev);
0161     data = i2c_get_clientdata(client);
0162 
0163     ret = kstrtou8(buf, 10, &value);
0164     if (ret < 0) {
0165         dev_err(dev, "BlinkM: value too large!\n");
0166         return ret;
0167     }
0168 
0169     switch (color) {
0170     case RED:
0171         data->next_red = value;
0172         break;
0173     case GREEN:
0174         data->next_green = value;
0175         break;
0176     case BLUE:
0177         data->next_blue = value;
0178         break;
0179     default:
0180         return -EINVAL;
0181     }
0182 
0183     dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n",
0184             data->next_red, data->next_green, data->next_blue);
0185 
0186     /* if mode ... */
0187     ret = blinkm_transfer_hw(client, BLM_GO_RGB);
0188     if (ret < 0) {
0189         dev_err(dev, "BlinkM: can't set RGB\n");
0190         return ret;
0191     }
0192     return 0;
0193 }
0194 
0195 static ssize_t red_show(struct device *dev, struct device_attribute *attr,
0196             char *buf)
0197 {
0198     return show_color_common(dev, buf, RED);
0199 }
0200 
0201 static ssize_t red_store(struct device *dev, struct device_attribute *attr,
0202              const char *buf, size_t count)
0203 {
0204     int ret;
0205 
0206     ret = store_color_common(dev, buf, RED);
0207     if (ret < 0)
0208         return ret;
0209     return count;
0210 }
0211 
0212 static DEVICE_ATTR_RW(red);
0213 
0214 static ssize_t green_show(struct device *dev, struct device_attribute *attr,
0215               char *buf)
0216 {
0217     return show_color_common(dev, buf, GREEN);
0218 }
0219 
0220 static ssize_t green_store(struct device *dev, struct device_attribute *attr,
0221                const char *buf, size_t count)
0222 {
0223 
0224     int ret;
0225 
0226     ret = store_color_common(dev, buf, GREEN);
0227     if (ret < 0)
0228         return ret;
0229     return count;
0230 }
0231 
0232 static DEVICE_ATTR_RW(green);
0233 
0234 static ssize_t blue_show(struct device *dev, struct device_attribute *attr,
0235              char *buf)
0236 {
0237     return show_color_common(dev, buf, BLUE);
0238 }
0239 
0240 static ssize_t blue_store(struct device *dev, struct device_attribute *attr,
0241               const char *buf, size_t count)
0242 {
0243     int ret;
0244 
0245     ret = store_color_common(dev, buf, BLUE);
0246     if (ret < 0)
0247         return ret;
0248     return count;
0249 }
0250 
0251 static DEVICE_ATTR_RW(blue);
0252 
0253 static ssize_t test_show(struct device *dev, struct device_attribute *attr,
0254              char *buf)
0255 {
0256     return scnprintf(buf, PAGE_SIZE,
0257              "#Write into test to start test sequence!#\n");
0258 }
0259 
0260 static ssize_t test_store(struct device *dev, struct device_attribute *attr,
0261               const char *buf, size_t count)
0262 {
0263 
0264     struct i2c_client *client;
0265     int ret;
0266     client = to_i2c_client(dev);
0267 
0268     /*test */
0269     ret = blinkm_test_run(client);
0270     if (ret < 0)
0271         return ret;
0272 
0273     return count;
0274 }
0275 
0276 static DEVICE_ATTR_RW(test);
0277 
0278 /* TODO: HSB, fade, timeadj, script ... */
0279 
0280 static struct attribute *blinkm_attrs[] = {
0281     &dev_attr_red.attr,
0282     &dev_attr_green.attr,
0283     &dev_attr_blue.attr,
0284     &dev_attr_test.attr,
0285     NULL,
0286 };
0287 
0288 static const struct attribute_group blinkm_group = {
0289     .name = "blinkm",
0290     .attrs = blinkm_attrs,
0291 };
0292 
0293 static int blinkm_write(struct i2c_client *client, int cmd, u8 *arg)
0294 {
0295     int result;
0296     int i;
0297     int arglen = blinkm_cmds[cmd].nr_args;
0298     /* write out cmd to blinkm - always / default step */
0299     result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte);
0300     if (result < 0)
0301         return result;
0302     /* no args to write out */
0303     if (arglen == 0)
0304         return 0;
0305 
0306     for (i = 0; i < arglen; i++) {
0307         /* repeat for arglen */
0308         result = i2c_smbus_write_byte(client, arg[i]);
0309         if (result < 0)
0310             return result;
0311     }
0312     return 0;
0313 }
0314 
0315 static int blinkm_read(struct i2c_client *client, int cmd, u8 *arg)
0316 {
0317     int result;
0318     int i;
0319     int retlen = blinkm_cmds[cmd].nr_ret;
0320     for (i = 0; i < retlen; i++) {
0321         /* repeat for retlen */
0322         result = i2c_smbus_read_byte(client);
0323         if (result < 0)
0324             return result;
0325         arg[i] = result;
0326     }
0327 
0328     return 0;
0329 }
0330 
0331 static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
0332 {
0333     /* the protocol is simple but non-standard:
0334      * e.g.  cmd 'g' (= 0x67) for "get device address"
0335      * - which defaults to 0x09 - would be the sequence:
0336      *   a) write 0x67 to the device (byte write)
0337      *   b) read the value (0x09) back right after (byte read)
0338      *
0339      * Watch out for "unfinished" sequences (i.e. not enough reads
0340      * or writes after a command. It will make the blinkM misbehave.
0341      * Sequence is key here.
0342      */
0343 
0344     /* args / return are in private data struct */
0345     struct blinkm_data *data = i2c_get_clientdata(client);
0346 
0347     /* We start hardware transfers which are not to be
0348      * mixed with other commands. Aquire a lock now. */
0349     if (mutex_lock_interruptible(&data->update_lock) < 0)
0350         return -EAGAIN;
0351 
0352     /* switch cmd - usually write before reads */
0353     switch (cmd) {
0354     case BLM_FADE_RAND_RGB:
0355     case BLM_GO_RGB:
0356     case BLM_FADE_RGB:
0357         data->args[0] = data->next_red;
0358         data->args[1] = data->next_green;
0359         data->args[2] = data->next_blue;
0360         blinkm_write(client, cmd, data->args);
0361         data->red = data->args[0];
0362         data->green = data->args[1];
0363         data->blue = data->args[2];
0364         break;
0365     case BLM_FADE_HSB:
0366     case BLM_FADE_RAND_HSB:
0367         data->args[0] = data->next_hue;
0368         data->args[1] = data->next_saturation;
0369         data->args[2] = data->next_brightness;
0370         blinkm_write(client, cmd, data->args);
0371         data->hue = data->next_hue;
0372         data->saturation = data->next_saturation;
0373         data->brightness = data->next_brightness;
0374         break;
0375     case BLM_PLAY_SCRIPT:
0376         data->args[0] = data->script_id;
0377         data->args[1] = data->script_repeats;
0378         data->args[2] = data->script_startline;
0379         blinkm_write(client, cmd, data->args);
0380         break;
0381     case BLM_STOP_SCRIPT:
0382         blinkm_write(client, cmd, NULL);
0383         break;
0384     case BLM_GET_CUR_RGB:
0385         data->args[0] = data->red;
0386         data->args[1] = data->green;
0387         data->args[2] = data->blue;
0388         blinkm_write(client, cmd, NULL);
0389         blinkm_read(client, cmd, data->args);
0390         data->red = data->args[0];
0391         data->green = data->args[1];
0392         data->blue = data->args[2];
0393         break;
0394     case BLM_GET_ADDR:
0395         data->args[0] = data->i2c_addr;
0396         blinkm_write(client, cmd, NULL);
0397         blinkm_read(client, cmd, data->args);
0398         data->i2c_addr = data->args[0];
0399         break;
0400     case BLM_SET_TIME_ADJ:
0401     case BLM_SET_FADE_SPEED:
0402     case BLM_READ_SCRIPT_LINE:
0403     case BLM_WRITE_SCRIPT_LINE:
0404     case BLM_SET_SCRIPT_LR:
0405     case BLM_SET_ADDR:
0406     case BLM_GET_FW_VER:
0407     case BLM_SET_STARTUP_PARAM:
0408         dev_err(&client->dev,
0409                 "BlinkM: cmd %d not implemented yet.\n", cmd);
0410         break;
0411     default:
0412         dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);
0413         mutex_unlock(&data->update_lock);
0414         return -EINVAL;
0415     }           /* end switch(cmd) */
0416 
0417     /* transfers done, unlock */
0418     mutex_unlock(&data->update_lock);
0419     return 0;
0420 }
0421 
0422 static int blinkm_led_common_set(struct led_classdev *led_cdev,
0423                  enum led_brightness value, int color)
0424 {
0425     /* led_brightness is 0, 127 or 255 - we just use it here as-is */
0426     struct blinkm_led *led = cdev_to_blmled(led_cdev);
0427     struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
0428 
0429     switch (color) {
0430     case RED:
0431         /* bail out if there's no change */
0432         if (data->next_red == (u8) value)
0433             return 0;
0434         data->next_red = (u8) value;
0435         break;
0436     case GREEN:
0437         /* bail out if there's no change */
0438         if (data->next_green == (u8) value)
0439             return 0;
0440         data->next_green = (u8) value;
0441         break;
0442     case BLUE:
0443         /* bail out if there's no change */
0444         if (data->next_blue == (u8) value)
0445             return 0;
0446         data->next_blue = (u8) value;
0447         break;
0448 
0449     default:
0450         dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n");
0451         return -EINVAL;
0452     }
0453 
0454     blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
0455     dev_dbg(&led->i2c_client->dev,
0456             "# DONE # next_red = %d, next_green = %d,"
0457             " next_blue = %d\n",
0458             data->next_red, data->next_green,
0459             data->next_blue);
0460     return 0;
0461 }
0462 
0463 static int blinkm_led_red_set(struct led_classdev *led_cdev,
0464                    enum led_brightness value)
0465 {
0466     return blinkm_led_common_set(led_cdev, value, RED);
0467 }
0468 
0469 static int blinkm_led_green_set(struct led_classdev *led_cdev,
0470                  enum led_brightness value)
0471 {
0472     return blinkm_led_common_set(led_cdev, value, GREEN);
0473 }
0474 
0475 static int blinkm_led_blue_set(struct led_classdev *led_cdev,
0476                 enum led_brightness value)
0477 {
0478     return blinkm_led_common_set(led_cdev, value, BLUE);
0479 }
0480 
0481 static void blinkm_init_hw(struct i2c_client *client)
0482 {
0483     blinkm_transfer_hw(client, BLM_STOP_SCRIPT);
0484     blinkm_transfer_hw(client, BLM_GO_RGB);
0485 }
0486 
0487 static int blinkm_test_run(struct i2c_client *client)
0488 {
0489     int ret;
0490     struct blinkm_data *data = i2c_get_clientdata(client);
0491 
0492     data->next_red = 0x01;
0493     data->next_green = 0x05;
0494     data->next_blue = 0x10;
0495     ret = blinkm_transfer_hw(client, BLM_GO_RGB);
0496     if (ret < 0)
0497         return ret;
0498     msleep(2000);
0499 
0500     data->next_red = 0x25;
0501     data->next_green = 0x10;
0502     data->next_blue = 0x31;
0503     ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
0504     if (ret < 0)
0505         return ret;
0506     msleep(2000);
0507 
0508     data->next_hue = 0x50;
0509     data->next_saturation = 0x10;
0510     data->next_brightness = 0x20;
0511     ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
0512     if (ret < 0)
0513         return ret;
0514     msleep(2000);
0515 
0516     return 0;
0517 }
0518 
0519 /* Return 0 if detection is successful, -ENODEV otherwise */
0520 static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
0521 {
0522     struct i2c_adapter *adapter = client->adapter;
0523     int ret;
0524     int count = 99;
0525     u8 tmpargs[7];
0526 
0527     if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
0528                      | I2C_FUNC_SMBUS_WORD_DATA
0529                      | I2C_FUNC_SMBUS_WRITE_BYTE))
0530         return -ENODEV;
0531 
0532     /* Now, we do the remaining detection. Simple for now. */
0533     /* We might need more guards to protect other i2c slaves */
0534 
0535     /* make sure the blinkM is balanced (read/writes) */
0536     while (count > 0) {
0537         ret = blinkm_write(client, BLM_GET_ADDR, NULL);
0538         if (ret)
0539             return ret;
0540         usleep_range(5000, 10000);
0541         ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
0542         if (ret)
0543             return ret;
0544         usleep_range(5000, 10000);
0545         if (tmpargs[0] == 0x09)
0546             count = 0;
0547         count--;
0548     }
0549 
0550     /* Step 1: Read BlinkM address back  -  cmd_char 'a' */
0551     ret = blinkm_write(client, BLM_GET_ADDR, NULL);
0552     if (ret < 0)
0553         return ret;
0554     usleep_range(20000, 30000); /* allow a small delay */
0555     ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
0556     if (ret < 0)
0557         return ret;
0558 
0559     if (tmpargs[0] != 0x09) {
0560         dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);
0561         return -ENODEV;
0562     }
0563 
0564     strlcpy(info->type, "blinkm", I2C_NAME_SIZE);
0565     return 0;
0566 }
0567 
0568 static int blinkm_probe(struct i2c_client *client,
0569             const struct i2c_device_id *id)
0570 {
0571     struct blinkm_data *data;
0572     struct blinkm_led *led[3];
0573     int err, i;
0574     char blinkm_led_name[28];
0575 
0576     data = devm_kzalloc(&client->dev,
0577             sizeof(struct blinkm_data), GFP_KERNEL);
0578     if (!data) {
0579         err = -ENOMEM;
0580         goto exit;
0581     }
0582 
0583     data->i2c_addr = 0x08;
0584     /* i2c addr  - use fake addr of 0x08 initially (real is 0x09) */
0585     data->fw_ver = 0xfe;
0586     /* firmware version - use fake until we read real value
0587      * (currently broken - BlinkM confused!) */
0588     data->script_id = 0x01;
0589     data->i2c_client = client;
0590 
0591     i2c_set_clientdata(client, data);
0592     mutex_init(&data->update_lock);
0593 
0594     /* Register sysfs hooks */
0595     err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
0596     if (err < 0) {
0597         dev_err(&client->dev, "couldn't register sysfs group\n");
0598         goto exit;
0599     }
0600 
0601     for (i = 0; i < 3; i++) {
0602         /* RED = 0, GREEN = 1, BLUE = 2 */
0603         led[i] = &data->blinkm_leds[i];
0604         led[i]->i2c_client = client;
0605         led[i]->id = i;
0606         led[i]->led_cdev.max_brightness = 255;
0607         led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
0608         switch (i) {
0609         case RED:
0610             snprintf(blinkm_led_name, sizeof(blinkm_led_name),
0611                      "blinkm-%d-%d-red",
0612                      client->adapter->nr,
0613                      client->addr);
0614             led[i]->led_cdev.name = blinkm_led_name;
0615             led[i]->led_cdev.brightness_set_blocking =
0616                             blinkm_led_red_set;
0617             err = led_classdev_register(&client->dev,
0618                             &led[i]->led_cdev);
0619             if (err < 0) {
0620                 dev_err(&client->dev,
0621                     "couldn't register LED %s\n",
0622                     led[i]->led_cdev.name);
0623                 goto failred;
0624             }
0625             break;
0626         case GREEN:
0627             snprintf(blinkm_led_name, sizeof(blinkm_led_name),
0628                      "blinkm-%d-%d-green",
0629                      client->adapter->nr,
0630                      client->addr);
0631             led[i]->led_cdev.name = blinkm_led_name;
0632             led[i]->led_cdev.brightness_set_blocking =
0633                             blinkm_led_green_set;
0634             err = led_classdev_register(&client->dev,
0635                             &led[i]->led_cdev);
0636             if (err < 0) {
0637                 dev_err(&client->dev,
0638                     "couldn't register LED %s\n",
0639                     led[i]->led_cdev.name);
0640                 goto failgreen;
0641             }
0642             break;
0643         case BLUE:
0644             snprintf(blinkm_led_name, sizeof(blinkm_led_name),
0645                      "blinkm-%d-%d-blue",
0646                      client->adapter->nr,
0647                      client->addr);
0648             led[i]->led_cdev.name = blinkm_led_name;
0649             led[i]->led_cdev.brightness_set_blocking =
0650                             blinkm_led_blue_set;
0651             err = led_classdev_register(&client->dev,
0652                             &led[i]->led_cdev);
0653             if (err < 0) {
0654                 dev_err(&client->dev,
0655                     "couldn't register LED %s\n",
0656                     led[i]->led_cdev.name);
0657                 goto failblue;
0658             }
0659             break;
0660         }       /* end switch */
0661     }           /* end for */
0662 
0663     /* Initialize the blinkm */
0664     blinkm_init_hw(client);
0665 
0666     return 0;
0667 
0668 failblue:
0669     led_classdev_unregister(&led[GREEN]->led_cdev);
0670 
0671 failgreen:
0672     led_classdev_unregister(&led[RED]->led_cdev);
0673 
0674 failred:
0675     sysfs_remove_group(&client->dev.kobj, &blinkm_group);
0676 exit:
0677     return err;
0678 }
0679 
0680 static int blinkm_remove(struct i2c_client *client)
0681 {
0682     struct blinkm_data *data = i2c_get_clientdata(client);
0683     int ret = 0;
0684     int i;
0685 
0686     /* make sure no workqueue entries are pending */
0687     for (i = 0; i < 3; i++)
0688         led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
0689 
0690     /* reset rgb */
0691     data->next_red = 0x00;
0692     data->next_green = 0x00;
0693     data->next_blue = 0x00;
0694     ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
0695     if (ret < 0)
0696         dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
0697 
0698     /* reset hsb */
0699     data->next_hue = 0x00;
0700     data->next_saturation = 0x00;
0701     data->next_brightness = 0x00;
0702     ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
0703     if (ret < 0)
0704         dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
0705 
0706     /* red fade to off */
0707     data->next_red = 0xff;
0708     ret = blinkm_transfer_hw(client, BLM_GO_RGB);
0709     if (ret < 0)
0710         dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
0711 
0712     /* off */
0713     data->next_red = 0x00;
0714     ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
0715     if (ret < 0)
0716         dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
0717 
0718     sysfs_remove_group(&client->dev.kobj, &blinkm_group);
0719     return 0;
0720 }
0721 
0722 static const struct i2c_device_id blinkm_id[] = {
0723     {"blinkm", 0},
0724     {}
0725 };
0726 
0727 MODULE_DEVICE_TABLE(i2c, blinkm_id);
0728 
0729   /* This is the driver that will be inserted */
0730 static struct i2c_driver blinkm_driver = {
0731     .class = I2C_CLASS_HWMON,
0732     .driver = {
0733            .name = "blinkm",
0734            },
0735     .probe = blinkm_probe,
0736     .remove = blinkm_remove,
0737     .id_table = blinkm_id,
0738     .detect = blinkm_detect,
0739     .address_list = normal_i2c,
0740 };
0741 
0742 module_i2c_driver(blinkm_driver);
0743 
0744 MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
0745 MODULE_DESCRIPTION("BlinkM RGB LED driver");
0746 MODULE_LICENSE("GPL");
0747