diff --git a/src/driver-steelseries.c b/src/driver-steelseries.c index 49a41e6c..f03a4ea5 100644 --- a/src/driver-steelseries.c +++ b/src/driver-steelseries.c @@ -76,6 +76,21 @@ struct steelseries_data { int firmware_minor; }; +struct steelseries_point { + struct list link; + + struct ratbag_color color; /* point color */ + uint8_t pos; /* relative position in the cycle */ +}; + +struct steelseries_led_cycle { + uint8_t led_id; /* led id */ + uint16_t duration; /* cycle duration */ + bool repeat; /* if the cycle restarts automatically */ + uint8_t trigger_buttons; /* trigger button combination */ + struct list points; /* colors in the cycle */ +}; + static int steelseries_test_hidraw(struct ratbag_device *device) { @@ -571,103 +586,134 @@ steelseries_write_led_v1(struct ratbag_led *led) return 0; } +static void +create_cycle(struct steelseries_led_cycle *cycle) +{ + cycle->led_id = 0x00; + cycle->duration = 5000; + cycle->repeat = true; + cycle->trigger_buttons = 0x00; + + list_init(&cycle->points); +} + +static void +construct_cycle_buffer(struct steelseries_led_cycle *cycle, uint8_t *buf, uint8_t buf_size) +{ + struct steelseries_point *point; + uint16_t duration; + uint8_t npoints = 0; + uint16_t cycle_size = 0; + + buf[0] = STEELSERIES_ID_LED; + buf[2] = cycle->led_id; + + if (!cycle->repeat) + buf[19] = 0x01; + + buf[23] = cycle->trigger_buttons; + + list_for_each(point, &cycle->points, link) { + if(npoints == 0){ + /* this is not a mistake, + we need to write the first + point to 28,29,30 */ + buf[28] = point->color.red; + buf[29] = point->color.green; + buf[30] = point->color.blue; + } + + cycle_size += point->pos; + assert(cycle_size < 256); + assert(34 + npoints*4 <= buf_size); + + buf[31 + npoints*4] = point->color.red; + buf[32 + npoints*4] = point->color.green; + buf[33 + npoints*4] = point->color.blue; + buf[34 + npoints*4] = point->pos; + + npoints++; + } + + buf[27] = npoints; + + /* this seems to be the minimum allowed */ + duration = max(buf[27] * 330, cycle->duration); + + hidpp_set_unaligned_le_u16(&buf[3], duration); +} + static int steelseries_write_led_v2(struct ratbag_led *led) { struct ratbag_device *device = led->profile->device; uint8_t buf[STEELSERIES_REPORT_SIZE] = {0}; - uint16_t duration, min_duration; int ret; - buf[0] = STEELSERIES_ID_LED; - buf[2] = led->index; + struct steelseries_led_cycle cycle; + struct steelseries_point point[4]; - /* not sure if these two are needed */ - buf[15] = 0x01; - buf[17] = 0x01; + const struct ratbag_color black = { 0x00 }; + const struct ratbag_color red = { 0xFF, 0x00, 0x00 }; + const struct ratbag_color green = { 0x00, 0xFF, 0x00 }; + const struct ratbag_color blue = { 0x00, 0x00, 0xFF }; + + create_cycle(&cycle); + cycle.led_id = led->index; switch(led->mode) { case RATBAG_LED_OFF: - buf[19] = 0x01; - buf[27] = 0x01; - buf[28] = buf[31] = 0x00; - buf[29] = buf[32] = 0x00; - buf[30] = buf[33] = 0x00; - /* not sure why the duration is set for the steady color or why - it is different for the two LEDs */ - duration = led->index == 0 ? 10000 : 5000; /* 0x1027 or 0x8813 */ + point[0].color = black; + point[0].pos = 0x00; + + list_append(&cycle.points, &point[0].link); break; case RATBAG_LED_ON: - buf[19] = 0x01; - buf[27] = 0x01; - buf[28] = buf[31] = led->color.red; - buf[29] = buf[32] = led->color.green; - buf[30] = buf[33] = led->color.blue; + point[0].color = led->color; - /* not sure why the duration is set for the steady color or why - it is different for the two LEDs */ - duration = led->index == 0 ? 10000 : 5000; /* 0x1027 or 0x8813 */ + list_append(&cycle.points, &point[0].link); break; - case RATBAG_LED_CYCLE: - buf[27] = 0x04; /* number of steps in cycle */ + case RATBAG_LED_CYCLE: + point[0].color = red; + point[0].pos = 0x00; - /* start color */ - buf[28] = buf[31] = 0xFF; - buf[29] = buf[32] = 0x00; - buf[30] = buf[33] = 0x00; + point[1].color = green; + point[1].pos = 0x55; - /* Cycle to green */ - buf[35] = 0x00; - buf[36] = 0xFF; - buf[37] = 0x00; - buf[38] = 0x54; /* normalized time share of animation */ + point[2].color = blue; + point[2].pos = 0x55; - /* Cycle to blue */ - buf[39] = 0x00; - buf[40] = 0x00; - buf[41] = 0xFF; - buf[42] = 0x54; /* normalized time share of animation */ + point[3].color = red; + point[3].pos = 0x55; - /* Cycle to red */ - buf[43] = 0xFF; - buf[44] = 0x00; - buf[45] = 0x00; - buf[46] = 0x56; /* normalized time share of animation */ + list_append(&cycle.points, &point[0].link); + list_append(&cycle.points, &point[1].link); + list_append(&cycle.points, &point[2].link); + list_append(&cycle.points, &point[3].link); - duration = led->ms; + cycle.duration = led->ms; break; case RATBAG_LED_BREATHING: - buf[27] = 0x03; /* number of steps in cycle */ + point[0].color = black; + point[0].pos = 0x00; - /* start color */ - buf[28] = buf[31] = led->color.red; - buf[29] = buf[32] = led->color.green; - buf[30] = buf[33] = led->color.blue; + point[1].color = led->color; + point[1].pos = 0x7F; - /* Cycle to black */ - buf[35] = 0x00; - buf[36] = 0x00; - buf[37] = 0x00; - buf[38] = 0x7F; /* normalized time share of animation */ + point[2].color = black; + point[2].pos = 0x7F; + + list_append(&cycle.points, &point[0].link); + list_append(&cycle.points, &point[1].link); + list_append(&cycle.points, &point[2].link); - /* Cycle to selected color */ - buf[39] = led->color.red; - buf[40] = led->color.green; - buf[41] = led->color.blue; - buf[42] = 0x7F; /* normalized time share of animation */ - - duration = led->ms; + cycle.duration = led->ms; break; default: return -EINVAL; } - /* this seems to be the minimum allowed */ - min_duration = buf[27] * 330; - if (duration < min_duration) - duration = min_duration; - - hidpp_set_unaligned_le_u16(&buf[3], duration); + construct_cycle_buffer(&cycle, buf, sizeof(buf)); msleep(10); ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));