/* Addresses to scan - BlinkM is on 0x09 by default*/ staticconstunsignedshort normal_i2c[] = { 0x09, I2C_CLIENT_END };
staticint blinkm_transfer_hw(struct i2c_client *client, int cmd); staticint blinkm_test_run(struct i2c_client *client);
/* Contains structs for both the color-separated sysfs classes, and the new multicolor class */ struct blinkm_led { struct i2c_client *i2c_client; union { /* used when multicolor support is disabled */ struct led_classdev led_cdev; struct led_classdev_mc mcled_cdev;
} cdev; int id;
};
struct blinkm_data { struct i2c_client *i2c_client; struct mutex update_lock; /* used for led class interface */ struct blinkm_led blinkm_leds[NUM_LEDS]; /* used for "blinkm" sysfs interface */
u8 red; /* color red */
u8 green; /* color green */
u8 blue; /* color blue */ /* next values to use for transfer */
u8 next_red; /* color red */
u8 next_green; /* color green */
u8 next_blue; /* color blue */ /* internal use */
u8 args[7]; /* set of args for transmission */
u8 i2c_addr; /* i2c addr */
u8 fw_ver; /* firmware version */ /* used, but not from userspace */
u8 hue; /* HSB hue */
u8 saturation; /* HSB saturation */
u8 brightness; /* HSB brightness */
u8 next_hue; /* HSB hue */
u8 next_saturation; /* HSB saturation */
u8 next_brightness; /* HSB brightness */ /* currently unused / todo */
u8 fade_speed; /* fade speed 1 - 255 */
s8 time_adjust; /* time adjust -128 - 127 */
u8 fade:1; /* fade on = 1, off = 0 */
u8 rand:1; /* rand fade mode on = 1 */
u8 script_id; /* script ID */
u8 script_repeats; /* repeats of script */
u8 script_startline; /* line to start */
};
/* Colors */ #define RED 0 #define GREEN 1 #define BLUE 2
staticint blinkm_write(struct i2c_client *client, int cmd, u8 *arg)
{ int result; int i; int arglen = blinkm_cmds[cmd].nr_args; /* write out cmd to blinkm - always / default step */
result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte); if (result < 0) return result; /* no args to write out */ if (arglen == 0) return 0;
for (i = 0; i < arglen; i++) { /* repeat for arglen */
result = i2c_smbus_write_byte(client, arg[i]); if (result < 0) return result;
} return 0;
}
staticint blinkm_read(struct i2c_client *client, int cmd, u8 *arg)
{ int result; int i; int retlen = blinkm_cmds[cmd].nr_ret; for (i = 0; i < retlen; i++) { /* repeat for retlen */
result = i2c_smbus_read_byte(client); if (result < 0) return result;
arg[i] = result;
}
return 0;
}
staticint blinkm_transfer_hw(struct i2c_client *client, int cmd)
{ /* the protocol is simple but non-standard: * e.g. cmd 'g' (= 0x67) for "get device address" * - which defaults to 0x09 - would be the sequence: * a) write 0x67 to the device (byte write) * b) read the value (0x09) back right after (byte read) * * Watch out for "unfinished" sequences (i.e. not enough reads * or writes after a command. It will make the blinkM misbehave. * Sequence is key here.
*/
/* args / return are in private data struct */ struct blinkm_data *data = i2c_get_clientdata(client);
/* We start hardware transfers which are not to be
* mixed with other commands. Aquire a lock now. */ if (mutex_lock_interruptible(&data->update_lock) < 0) return -EAGAIN;
/* switch cmd - usually write before reads */ switch (cmd) { case BLM_FADE_RAND_RGB: case BLM_GO_RGB: case BLM_FADE_RGB:
data->args[0] = data->next_red;
data->args[1] = data->next_green;
data->args[2] = data->next_blue;
blinkm_write(client, cmd, data->args);
data->red = data->args[0];
data->green = data->args[1];
data->blue = data->args[2]; break; case BLM_FADE_HSB: case BLM_FADE_RAND_HSB:
data->args[0] = data->next_hue;
data->args[1] = data->next_saturation;
data->args[2] = data->next_brightness;
blinkm_write(client, cmd, data->args);
data->hue = data->next_hue;
data->saturation = data->next_saturation;
data->brightness = data->next_brightness; break; case BLM_PLAY_SCRIPT:
data->args[0] = data->script_id;
data->args[1] = data->script_repeats;
data->args[2] = data->script_startline;
blinkm_write(client, cmd, data->args); break; case BLM_STOP_SCRIPT:
blinkm_write(client, cmd, NULL); break; case BLM_GET_CUR_RGB:
data->args[0] = data->red;
data->args[1] = data->green;
data->args[2] = data->blue;
blinkm_write(client, cmd, NULL);
blinkm_read(client, cmd, data->args);
data->red = data->args[0];
data->green = data->args[1];
data->blue = data->args[2]; break; case BLM_GET_ADDR:
data->args[0] = data->i2c_addr;
blinkm_write(client, cmd, NULL);
blinkm_read(client, cmd, data->args);
data->i2c_addr = data->args[0]; break; case BLM_SET_TIME_ADJ: case BLM_SET_FADE_SPEED: case BLM_READ_SCRIPT_LINE: case BLM_WRITE_SCRIPT_LINE: case BLM_SET_SCRIPT_LR: case BLM_SET_ADDR: case BLM_GET_FW_VER: case BLM_SET_STARTUP_PARAM:
dev_err(&client->dev, "BlinkM: cmd %d not implemented yet.\n", cmd); break; default:
dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);
mutex_unlock(&data->update_lock); return -EINVAL;
} /* end switch(cmd) */
staticint blinkm_led_common_set(struct led_classdev *led_cdev, enum led_brightness value, int color)
{ /* led_brightness is 0, 127 or 255 - we just use it here as-is */ struct blinkm_led *led = led_cdev_to_blmled(led_cdev); struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
switch (color) { case RED: /* bail out if there's no change */ if (data->next_red == (u8) value) return 0;
data->next_red = (u8) value; break; case GREEN: /* bail out if there's no change */ if (data->next_green == (u8) value) return 0;
data->next_green = (u8) value; break; case BLUE: /* bail out if there's no change */ if (data->next_blue == (u8) value) return 0;
data->next_blue = (u8) value; break;
data->next_red = 0x01;
data->next_green = 0x05;
data->next_blue = 0x10;
ret = blinkm_transfer_hw(client, BLM_GO_RGB); if (ret < 0) return ret;
msleep(2000);
data->next_red = 0x25;
data->next_green = 0x10;
data->next_blue = 0x31;
ret = blinkm_transfer_hw(client, BLM_FADE_RGB); if (ret < 0) return ret;
msleep(2000);
data->next_hue = 0x50;
data->next_saturation = 0x10;
data->next_brightness = 0x20;
ret = blinkm_transfer_hw(client, BLM_FADE_HSB); if (ret < 0) return ret;
msleep(2000);
return 0;
}
/* Return 0 if detection is successful, -ENODEV otherwise */ staticint blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
{ struct i2c_adapter *adapter = client->adapter; int ret; int count = 99;
u8 tmpargs[7];
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE)) return -ENODEV;
/* Now, we do the remaining detection. Simple for now. */ /* We might need more guards to protect other i2c slaves */
/* make sure the blinkM is balanced (read/writes) */ while (count > 0) {
ret = blinkm_write(client, BLM_GET_ADDR, NULL); if (ret) return ret;
usleep_range(5000, 10000);
ret = blinkm_read(client, BLM_GET_ADDR, tmpargs); if (ret) return ret;
usleep_range(5000, 10000); if (tmpargs[0] == 0x09)
count = 0;
count--;
}
/* Step 1: Read BlinkM address back - cmd_char 'a' */
ret = blinkm_write(client, BLM_GET_ADDR, NULL); if (ret < 0) return ret;
usleep_range(20000, 30000); /* allow a small delay */
ret = blinkm_read(client, BLM_GET_ADDR, tmpargs); if (ret < 0) return ret;
if (tmpargs[0] != 0x09) {
dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]); return -ENODEV;
}
/* Register multicolor sysfs class */ /* The first element of leds is used for multicolor facilities */
mc_led = &data->blinkm_leds[RED];
mc_led->i2c_client = client;
mc_led_info = devm_kcalloc(&client->dev, NUM_LEDS, sizeof(*mc_led_info),
GFP_KERNEL); if (!mc_led_info) return -ENOMEM;
staticint blinkm_probe(struct i2c_client *client)
{ struct blinkm_data *data; int err;
data = devm_kzalloc(&client->dev, sizeof(struct blinkm_data), GFP_KERNEL); if (!data) return -ENOMEM;
data->i2c_addr = 0x08; /* i2c addr - use fake addr of 0x08 initially (real is 0x09) */
data->fw_ver = 0xfe; /* firmware version - use fake until we read real value * (currently broken - BlinkM confused!)
*/
data->script_id = 0x01;
data->i2c_client = client;
if (!IS_ENABLED(CONFIG_LEDS_BLINKM_MULTICOLOR)) {
err = register_separate_colors(client, data); if (err < 0) return err;
} else {
err = register_multicolor(client, data); if (err < 0) return err;
}
blinkm_init_hw(client);
return 0;
}
staticvoid blinkm_remove(struct i2c_client *client)
{ struct blinkm_data *data = i2c_get_clientdata(client); int ret = 0; int i;
/* make sure no workqueue entries are pending */ for (i = 0; i < NUM_LEDS; i++)
led_classdev_unregister(&data->blinkm_leds[i].cdev.led_cdev);
/* reset rgb */
data->next_red = 0x00;
data->next_green = 0x00;
data->next_blue = 0x00;
ret = blinkm_transfer_hw(client, BLM_FADE_RGB); if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
/* reset hsb */
data->next_hue = 0x00;
data->next_saturation = 0x00;
data->next_brightness = 0x00;
ret = blinkm_transfer_hw(client, BLM_FADE_HSB); if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
/* red fade to off */
data->next_red = 0xff;
ret = blinkm_transfer_hw(client, BLM_GO_RGB); if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
/* off */
data->next_red = 0x00;
ret = blinkm_transfer_hw(client, BLM_FADE_RGB); if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.