leds-blinkm.c (18490B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * leds-blinkm.c 4 * (c) Jan-Simon Möller (dl9pf@gmx.de) 5 */ 6 7#include <linux/module.h> 8#include <linux/slab.h> 9#include <linux/jiffies.h> 10#include <linux/i2c.h> 11#include <linux/err.h> 12#include <linux/mutex.h> 13#include <linux/sysfs.h> 14#include <linux/printk.h> 15#include <linux/pm_runtime.h> 16#include <linux/leds.h> 17#include <linux/delay.h> 18 19/* Addresses to scan - BlinkM is on 0x09 by default*/ 20static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END }; 21 22static int blinkm_transfer_hw(struct i2c_client *client, int cmd); 23static int blinkm_test_run(struct i2c_client *client); 24 25struct blinkm_led { 26 struct i2c_client *i2c_client; 27 struct led_classdev led_cdev; 28 int id; 29}; 30 31#define cdev_to_blmled(c) container_of(c, struct blinkm_led, led_cdev) 32 33struct blinkm_data { 34 struct i2c_client *i2c_client; 35 struct mutex update_lock; 36 /* used for led class interface */ 37 struct blinkm_led blinkm_leds[3]; 38 /* used for "blinkm" sysfs interface */ 39 u8 red; /* color red */ 40 u8 green; /* color green */ 41 u8 blue; /* color blue */ 42 /* next values to use for transfer */ 43 u8 next_red; /* color red */ 44 u8 next_green; /* color green */ 45 u8 next_blue; /* color blue */ 46 /* internal use */ 47 u8 args[7]; /* set of args for transmission */ 48 u8 i2c_addr; /* i2c addr */ 49 u8 fw_ver; /* firmware version */ 50 /* used, but not from userspace */ 51 u8 hue; /* HSB hue */ 52 u8 saturation; /* HSB saturation */ 53 u8 brightness; /* HSB brightness */ 54 u8 next_hue; /* HSB hue */ 55 u8 next_saturation; /* HSB saturation */ 56 u8 next_brightness; /* HSB brightness */ 57 /* currently unused / todo */ 58 u8 fade_speed; /* fade speed 1 - 255 */ 59 s8 time_adjust; /* time adjust -128 - 127 */ 60 u8 fade:1; /* fade on = 1, off = 0 */ 61 u8 rand:1; /* rand fade mode on = 1 */ 62 u8 script_id; /* script ID */ 63 u8 script_repeats; /* repeats of script */ 64 u8 script_startline; /* line to start */ 65}; 66 67/* Colors */ 68#define RED 0 69#define GREEN 1 70#define BLUE 2 71 72/* mapping command names to cmd chars - see datasheet */ 73#define BLM_GO_RGB 0 74#define BLM_FADE_RGB 1 75#define BLM_FADE_HSB 2 76#define BLM_FADE_RAND_RGB 3 77#define BLM_FADE_RAND_HSB 4 78#define BLM_PLAY_SCRIPT 5 79#define BLM_STOP_SCRIPT 6 80#define BLM_SET_FADE_SPEED 7 81#define BLM_SET_TIME_ADJ 8 82#define BLM_GET_CUR_RGB 9 83#define BLM_WRITE_SCRIPT_LINE 10 84#define BLM_READ_SCRIPT_LINE 11 85#define BLM_SET_SCRIPT_LR 12 /* Length & Repeats */ 86#define BLM_SET_ADDR 13 87#define BLM_GET_ADDR 14 88#define BLM_GET_FW_VER 15 89#define BLM_SET_STARTUP_PARAM 16 90 91/* BlinkM Commands 92 * as extracted out of the datasheet: 93 * 94 * cmdchar = command (ascii) 95 * cmdbyte = command in hex 96 * nr_args = number of arguments (to send) 97 * nr_ret = number of return values (to read) 98 * dir = direction (0 = read, 1 = write, 2 = both) 99 * 100 */ 101static const struct { 102 char cmdchar; 103 u8 cmdbyte; 104 u8 nr_args; 105 u8 nr_ret; 106 u8 dir:2; 107} blinkm_cmds[17] = { 108 /* cmdchar, cmdbyte, nr_args, nr_ret, dir */ 109 { 'n', 0x6e, 3, 0, 1}, 110 { 'c', 0x63, 3, 0, 1}, 111 { 'h', 0x68, 3, 0, 1}, 112 { 'C', 0x43, 3, 0, 1}, 113 { 'H', 0x48, 3, 0, 1}, 114 { 'p', 0x70, 3, 0, 1}, 115 { 'o', 0x6f, 0, 0, 1}, 116 { 'f', 0x66, 1, 0, 1}, 117 { 't', 0x74, 1, 0, 1}, 118 { 'g', 0x67, 0, 3, 0}, 119 { 'W', 0x57, 7, 0, 1}, 120 { 'R', 0x52, 2, 5, 2}, 121 { 'L', 0x4c, 3, 0, 1}, 122 { 'A', 0x41, 4, 0, 1}, 123 { 'a', 0x61, 0, 1, 0}, 124 { 'Z', 0x5a, 0, 1, 0}, 125 { 'B', 0x42, 5, 0, 1}, 126}; 127 128static ssize_t show_color_common(struct device *dev, char *buf, int color) 129{ 130 struct i2c_client *client; 131 struct blinkm_data *data; 132 int ret; 133 134 client = to_i2c_client(dev); 135 data = i2c_get_clientdata(client); 136 137 ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB); 138 if (ret < 0) 139 return ret; 140 switch (color) { 141 case RED: 142 return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red); 143 case GREEN: 144 return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green); 145 case BLUE: 146 return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue); 147 default: 148 return -EINVAL; 149 } 150 return -EINVAL; 151} 152 153static int store_color_common(struct device *dev, const char *buf, int color) 154{ 155 struct i2c_client *client; 156 struct blinkm_data *data; 157 int ret; 158 u8 value; 159 160 client = to_i2c_client(dev); 161 data = i2c_get_clientdata(client); 162 163 ret = kstrtou8(buf, 10, &value); 164 if (ret < 0) { 165 dev_err(dev, "BlinkM: value too large!\n"); 166 return ret; 167 } 168 169 switch (color) { 170 case RED: 171 data->next_red = value; 172 break; 173 case GREEN: 174 data->next_green = value; 175 break; 176 case BLUE: 177 data->next_blue = value; 178 break; 179 default: 180 return -EINVAL; 181 } 182 183 dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n", 184 data->next_red, data->next_green, data->next_blue); 185 186 /* if mode ... */ 187 ret = blinkm_transfer_hw(client, BLM_GO_RGB); 188 if (ret < 0) { 189 dev_err(dev, "BlinkM: can't set RGB\n"); 190 return ret; 191 } 192 return 0; 193} 194 195static ssize_t red_show(struct device *dev, struct device_attribute *attr, 196 char *buf) 197{ 198 return show_color_common(dev, buf, RED); 199} 200 201static ssize_t red_store(struct device *dev, struct device_attribute *attr, 202 const char *buf, size_t count) 203{ 204 int ret; 205 206 ret = store_color_common(dev, buf, RED); 207 if (ret < 0) 208 return ret; 209 return count; 210} 211 212static DEVICE_ATTR_RW(red); 213 214static ssize_t green_show(struct device *dev, struct device_attribute *attr, 215 char *buf) 216{ 217 return show_color_common(dev, buf, GREEN); 218} 219 220static ssize_t green_store(struct device *dev, struct device_attribute *attr, 221 const char *buf, size_t count) 222{ 223 224 int ret; 225 226 ret = store_color_common(dev, buf, GREEN); 227 if (ret < 0) 228 return ret; 229 return count; 230} 231 232static DEVICE_ATTR_RW(green); 233 234static ssize_t blue_show(struct device *dev, struct device_attribute *attr, 235 char *buf) 236{ 237 return show_color_common(dev, buf, BLUE); 238} 239 240static ssize_t blue_store(struct device *dev, struct device_attribute *attr, 241 const char *buf, size_t count) 242{ 243 int ret; 244 245 ret = store_color_common(dev, buf, BLUE); 246 if (ret < 0) 247 return ret; 248 return count; 249} 250 251static DEVICE_ATTR_RW(blue); 252 253static ssize_t test_show(struct device *dev, struct device_attribute *attr, 254 char *buf) 255{ 256 return scnprintf(buf, PAGE_SIZE, 257 "#Write into test to start test sequence!#\n"); 258} 259 260static ssize_t test_store(struct device *dev, struct device_attribute *attr, 261 const char *buf, size_t count) 262{ 263 264 struct i2c_client *client; 265 int ret; 266 client = to_i2c_client(dev); 267 268 /*test */ 269 ret = blinkm_test_run(client); 270 if (ret < 0) 271 return ret; 272 273 return count; 274} 275 276static DEVICE_ATTR_RW(test); 277 278/* TODO: HSB, fade, timeadj, script ... */ 279 280static struct attribute *blinkm_attrs[] = { 281 &dev_attr_red.attr, 282 &dev_attr_green.attr, 283 &dev_attr_blue.attr, 284 &dev_attr_test.attr, 285 NULL, 286}; 287 288static const struct attribute_group blinkm_group = { 289 .name = "blinkm", 290 .attrs = blinkm_attrs, 291}; 292 293static int blinkm_write(struct i2c_client *client, int cmd, u8 *arg) 294{ 295 int result; 296 int i; 297 int arglen = blinkm_cmds[cmd].nr_args; 298 /* write out cmd to blinkm - always / default step */ 299 result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte); 300 if (result < 0) 301 return result; 302 /* no args to write out */ 303 if (arglen == 0) 304 return 0; 305 306 for (i = 0; i < arglen; i++) { 307 /* repeat for arglen */ 308 result = i2c_smbus_write_byte(client, arg[i]); 309 if (result < 0) 310 return result; 311 } 312 return 0; 313} 314 315static int blinkm_read(struct i2c_client *client, int cmd, u8 *arg) 316{ 317 int result; 318 int i; 319 int retlen = blinkm_cmds[cmd].nr_ret; 320 for (i = 0; i < retlen; i++) { 321 /* repeat for retlen */ 322 result = i2c_smbus_read_byte(client); 323 if (result < 0) 324 return result; 325 arg[i] = result; 326 } 327 328 return 0; 329} 330 331static int blinkm_transfer_hw(struct i2c_client *client, int cmd) 332{ 333 /* the protocol is simple but non-standard: 334 * e.g. cmd 'g' (= 0x67) for "get device address" 335 * - which defaults to 0x09 - would be the sequence: 336 * a) write 0x67 to the device (byte write) 337 * b) read the value (0x09) back right after (byte read) 338 * 339 * Watch out for "unfinished" sequences (i.e. not enough reads 340 * or writes after a command. It will make the blinkM misbehave. 341 * Sequence is key here. 342 */ 343 344 /* args / return are in private data struct */ 345 struct blinkm_data *data = i2c_get_clientdata(client); 346 347 /* We start hardware transfers which are not to be 348 * mixed with other commands. Aquire a lock now. */ 349 if (mutex_lock_interruptible(&data->update_lock) < 0) 350 return -EAGAIN; 351 352 /* switch cmd - usually write before reads */ 353 switch (cmd) { 354 case BLM_FADE_RAND_RGB: 355 case BLM_GO_RGB: 356 case BLM_FADE_RGB: 357 data->args[0] = data->next_red; 358 data->args[1] = data->next_green; 359 data->args[2] = data->next_blue; 360 blinkm_write(client, cmd, data->args); 361 data->red = data->args[0]; 362 data->green = data->args[1]; 363 data->blue = data->args[2]; 364 break; 365 case BLM_FADE_HSB: 366 case BLM_FADE_RAND_HSB: 367 data->args[0] = data->next_hue; 368 data->args[1] = data->next_saturation; 369 data->args[2] = data->next_brightness; 370 blinkm_write(client, cmd, data->args); 371 data->hue = data->next_hue; 372 data->saturation = data->next_saturation; 373 data->brightness = data->next_brightness; 374 break; 375 case BLM_PLAY_SCRIPT: 376 data->args[0] = data->script_id; 377 data->args[1] = data->script_repeats; 378 data->args[2] = data->script_startline; 379 blinkm_write(client, cmd, data->args); 380 break; 381 case BLM_STOP_SCRIPT: 382 blinkm_write(client, cmd, NULL); 383 break; 384 case BLM_GET_CUR_RGB: 385 data->args[0] = data->red; 386 data->args[1] = data->green; 387 data->args[2] = data->blue; 388 blinkm_write(client, cmd, NULL); 389 blinkm_read(client, cmd, data->args); 390 data->red = data->args[0]; 391 data->green = data->args[1]; 392 data->blue = data->args[2]; 393 break; 394 case BLM_GET_ADDR: 395 data->args[0] = data->i2c_addr; 396 blinkm_write(client, cmd, NULL); 397 blinkm_read(client, cmd, data->args); 398 data->i2c_addr = data->args[0]; 399 break; 400 case BLM_SET_TIME_ADJ: 401 case BLM_SET_FADE_SPEED: 402 case BLM_READ_SCRIPT_LINE: 403 case BLM_WRITE_SCRIPT_LINE: 404 case BLM_SET_SCRIPT_LR: 405 case BLM_SET_ADDR: 406 case BLM_GET_FW_VER: 407 case BLM_SET_STARTUP_PARAM: 408 dev_err(&client->dev, 409 "BlinkM: cmd %d not implemented yet.\n", cmd); 410 break; 411 default: 412 dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd); 413 mutex_unlock(&data->update_lock); 414 return -EINVAL; 415 } /* end switch(cmd) */ 416 417 /* transfers done, unlock */ 418 mutex_unlock(&data->update_lock); 419 return 0; 420} 421 422static int blinkm_led_common_set(struct led_classdev *led_cdev, 423 enum led_brightness value, int color) 424{ 425 /* led_brightness is 0, 127 or 255 - we just use it here as-is */ 426 struct blinkm_led *led = cdev_to_blmled(led_cdev); 427 struct blinkm_data *data = i2c_get_clientdata(led->i2c_client); 428 429 switch (color) { 430 case RED: 431 /* bail out if there's no change */ 432 if (data->next_red == (u8) value) 433 return 0; 434 data->next_red = (u8) value; 435 break; 436 case GREEN: 437 /* bail out if there's no change */ 438 if (data->next_green == (u8) value) 439 return 0; 440 data->next_green = (u8) value; 441 break; 442 case BLUE: 443 /* bail out if there's no change */ 444 if (data->next_blue == (u8) value) 445 return 0; 446 data->next_blue = (u8) value; 447 break; 448 449 default: 450 dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n"); 451 return -EINVAL; 452 } 453 454 blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB); 455 dev_dbg(&led->i2c_client->dev, 456 "# DONE # next_red = %d, next_green = %d," 457 " next_blue = %d\n", 458 data->next_red, data->next_green, 459 data->next_blue); 460 return 0; 461} 462 463static int blinkm_led_red_set(struct led_classdev *led_cdev, 464 enum led_brightness value) 465{ 466 return blinkm_led_common_set(led_cdev, value, RED); 467} 468 469static int blinkm_led_green_set(struct led_classdev *led_cdev, 470 enum led_brightness value) 471{ 472 return blinkm_led_common_set(led_cdev, value, GREEN); 473} 474 475static int blinkm_led_blue_set(struct led_classdev *led_cdev, 476 enum led_brightness value) 477{ 478 return blinkm_led_common_set(led_cdev, value, BLUE); 479} 480 481static void blinkm_init_hw(struct i2c_client *client) 482{ 483 blinkm_transfer_hw(client, BLM_STOP_SCRIPT); 484 blinkm_transfer_hw(client, BLM_GO_RGB); 485} 486 487static int blinkm_test_run(struct i2c_client *client) 488{ 489 int ret; 490 struct blinkm_data *data = i2c_get_clientdata(client); 491 492 data->next_red = 0x01; 493 data->next_green = 0x05; 494 data->next_blue = 0x10; 495 ret = blinkm_transfer_hw(client, BLM_GO_RGB); 496 if (ret < 0) 497 return ret; 498 msleep(2000); 499 500 data->next_red = 0x25; 501 data->next_green = 0x10; 502 data->next_blue = 0x31; 503 ret = blinkm_transfer_hw(client, BLM_FADE_RGB); 504 if (ret < 0) 505 return ret; 506 msleep(2000); 507 508 data->next_hue = 0x50; 509 data->next_saturation = 0x10; 510 data->next_brightness = 0x20; 511 ret = blinkm_transfer_hw(client, BLM_FADE_HSB); 512 if (ret < 0) 513 return ret; 514 msleep(2000); 515 516 return 0; 517} 518 519/* Return 0 if detection is successful, -ENODEV otherwise */ 520static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info) 521{ 522 struct i2c_adapter *adapter = client->adapter; 523 int ret; 524 int count = 99; 525 u8 tmpargs[7]; 526 527 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA 528 | I2C_FUNC_SMBUS_WORD_DATA 529 | I2C_FUNC_SMBUS_WRITE_BYTE)) 530 return -ENODEV; 531 532 /* Now, we do the remaining detection. Simple for now. */ 533 /* We might need more guards to protect other i2c slaves */ 534 535 /* make sure the blinkM is balanced (read/writes) */ 536 while (count > 0) { 537 ret = blinkm_write(client, BLM_GET_ADDR, NULL); 538 if (ret) 539 return ret; 540 usleep_range(5000, 10000); 541 ret = blinkm_read(client, BLM_GET_ADDR, tmpargs); 542 if (ret) 543 return ret; 544 usleep_range(5000, 10000); 545 if (tmpargs[0] == 0x09) 546 count = 0; 547 count--; 548 } 549 550 /* Step 1: Read BlinkM address back - cmd_char 'a' */ 551 ret = blinkm_write(client, BLM_GET_ADDR, NULL); 552 if (ret < 0) 553 return ret; 554 usleep_range(20000, 30000); /* allow a small delay */ 555 ret = blinkm_read(client, BLM_GET_ADDR, tmpargs); 556 if (ret < 0) 557 return ret; 558 559 if (tmpargs[0] != 0x09) { 560 dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]); 561 return -ENODEV; 562 } 563 564 strlcpy(info->type, "blinkm", I2C_NAME_SIZE); 565 return 0; 566} 567 568static int blinkm_probe(struct i2c_client *client, 569 const struct i2c_device_id *id) 570{ 571 struct blinkm_data *data; 572 struct blinkm_led *led[3]; 573 int err, i; 574 char blinkm_led_name[28]; 575 576 data = devm_kzalloc(&client->dev, 577 sizeof(struct blinkm_data), GFP_KERNEL); 578 if (!data) { 579 err = -ENOMEM; 580 goto exit; 581 } 582 583 data->i2c_addr = 0x08; 584 /* i2c addr - use fake addr of 0x08 initially (real is 0x09) */ 585 data->fw_ver = 0xfe; 586 /* firmware version - use fake until we read real value 587 * (currently broken - BlinkM confused!) */ 588 data->script_id = 0x01; 589 data->i2c_client = client; 590 591 i2c_set_clientdata(client, data); 592 mutex_init(&data->update_lock); 593 594 /* Register sysfs hooks */ 595 err = sysfs_create_group(&client->dev.kobj, &blinkm_group); 596 if (err < 0) { 597 dev_err(&client->dev, "couldn't register sysfs group\n"); 598 goto exit; 599 } 600 601 for (i = 0; i < 3; i++) { 602 /* RED = 0, GREEN = 1, BLUE = 2 */ 603 led[i] = &data->blinkm_leds[i]; 604 led[i]->i2c_client = client; 605 led[i]->id = i; 606 led[i]->led_cdev.max_brightness = 255; 607 led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME; 608 switch (i) { 609 case RED: 610 snprintf(blinkm_led_name, sizeof(blinkm_led_name), 611 "blinkm-%d-%d-red", 612 client->adapter->nr, 613 client->addr); 614 led[i]->led_cdev.name = blinkm_led_name; 615 led[i]->led_cdev.brightness_set_blocking = 616 blinkm_led_red_set; 617 err = led_classdev_register(&client->dev, 618 &led[i]->led_cdev); 619 if (err < 0) { 620 dev_err(&client->dev, 621 "couldn't register LED %s\n", 622 led[i]->led_cdev.name); 623 goto failred; 624 } 625 break; 626 case GREEN: 627 snprintf(blinkm_led_name, sizeof(blinkm_led_name), 628 "blinkm-%d-%d-green", 629 client->adapter->nr, 630 client->addr); 631 led[i]->led_cdev.name = blinkm_led_name; 632 led[i]->led_cdev.brightness_set_blocking = 633 blinkm_led_green_set; 634 err = led_classdev_register(&client->dev, 635 &led[i]->led_cdev); 636 if (err < 0) { 637 dev_err(&client->dev, 638 "couldn't register LED %s\n", 639 led[i]->led_cdev.name); 640 goto failgreen; 641 } 642 break; 643 case BLUE: 644 snprintf(blinkm_led_name, sizeof(blinkm_led_name), 645 "blinkm-%d-%d-blue", 646 client->adapter->nr, 647 client->addr); 648 led[i]->led_cdev.name = blinkm_led_name; 649 led[i]->led_cdev.brightness_set_blocking = 650 blinkm_led_blue_set; 651 err = led_classdev_register(&client->dev, 652 &led[i]->led_cdev); 653 if (err < 0) { 654 dev_err(&client->dev, 655 "couldn't register LED %s\n", 656 led[i]->led_cdev.name); 657 goto failblue; 658 } 659 break; 660 } /* end switch */ 661 } /* end for */ 662 663 /* Initialize the blinkm */ 664 blinkm_init_hw(client); 665 666 return 0; 667 668failblue: 669 led_classdev_unregister(&led[GREEN]->led_cdev); 670 671failgreen: 672 led_classdev_unregister(&led[RED]->led_cdev); 673 674failred: 675 sysfs_remove_group(&client->dev.kobj, &blinkm_group); 676exit: 677 return err; 678} 679 680static int blinkm_remove(struct i2c_client *client) 681{ 682 struct blinkm_data *data = i2c_get_clientdata(client); 683 int ret = 0; 684 int i; 685 686 /* make sure no workqueue entries are pending */ 687 for (i = 0; i < 3; i++) 688 led_classdev_unregister(&data->blinkm_leds[i].led_cdev); 689 690 /* reset rgb */ 691 data->next_red = 0x00; 692 data->next_green = 0x00; 693 data->next_blue = 0x00; 694 ret = blinkm_transfer_hw(client, BLM_FADE_RGB); 695 if (ret < 0) 696 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n"); 697 698 /* reset hsb */ 699 data->next_hue = 0x00; 700 data->next_saturation = 0x00; 701 data->next_brightness = 0x00; 702 ret = blinkm_transfer_hw(client, BLM_FADE_HSB); 703 if (ret < 0) 704 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n"); 705 706 /* red fade to off */ 707 data->next_red = 0xff; 708 ret = blinkm_transfer_hw(client, BLM_GO_RGB); 709 if (ret < 0) 710 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n"); 711 712 /* off */ 713 data->next_red = 0x00; 714 ret = blinkm_transfer_hw(client, BLM_FADE_RGB); 715 if (ret < 0) 716 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n"); 717 718 sysfs_remove_group(&client->dev.kobj, &blinkm_group); 719 return 0; 720} 721 722static const struct i2c_device_id blinkm_id[] = { 723 {"blinkm", 0}, 724 {} 725}; 726 727MODULE_DEVICE_TABLE(i2c, blinkm_id); 728 729 /* This is the driver that will be inserted */ 730static struct i2c_driver blinkm_driver = { 731 .class = I2C_CLASS_HWMON, 732 .driver = { 733 .name = "blinkm", 734 }, 735 .probe = blinkm_probe, 736 .remove = blinkm_remove, 737 .id_table = blinkm_id, 738 .detect = blinkm_detect, 739 .address_list = normal_i2c, 740}; 741 742module_i2c_driver(blinkm_driver); 743 744MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>"); 745MODULE_DESCRIPTION("BlinkM RGB LED driver"); 746MODULE_LICENSE("GPL"); 747