17
17
18
18
#define LG_G15_TRANSFER_BUF_SIZE 20
19
19
20
+ #define LG_G15_FEATURE_REPORT 0x02
21
+
20
22
enum lg_g15_model {
21
23
LG_G15 ,
22
24
LG_G15_V2 ,
23
25
};
24
26
27
+ enum lg_g15_led_type {
28
+ LG_G15_KBD_BRIGHTNESS ,
29
+ LG_G15_LCD_BRIGHTNESS ,
30
+ LG_G15_BRIGHTNESS_MAX ,
31
+ };
32
+
33
+ struct lg_g15_led {
34
+ struct led_classdev cdev ;
35
+ enum led_brightness brightness ;
36
+ enum lg_g15_led_type led ;
37
+ };
38
+
25
39
struct lg_g15_data {
26
40
/* Must be first for proper dma alignment */
27
41
u8 transfer_buf [LG_G15_TRANSFER_BUF_SIZE ];
42
+ /* Protects the transfer_buf and led brightness */
43
+ struct mutex mutex ;
44
+ struct work_struct work ;
28
45
struct input_dev * input ;
29
46
struct hid_device * hdev ;
30
47
enum lg_g15_model model ;
48
+ struct lg_g15_led leds [LG_G15_BRIGHTNESS_MAX ];
31
49
};
32
50
51
+ static int lg_g15_update_led_brightness (struct lg_g15_data * g15 )
52
+ {
53
+ int ret ;
54
+
55
+ ret = hid_hw_raw_request (g15 -> hdev , LG_G15_FEATURE_REPORT ,
56
+ g15 -> transfer_buf , 4 ,
57
+ HID_FEATURE_REPORT , HID_REQ_GET_REPORT );
58
+ if (ret != 4 ) {
59
+ hid_err (g15 -> hdev , "Error getting LED brightness: %d\n" , ret );
60
+ return (ret < 0 ) ? ret : - EIO ;
61
+ }
62
+
63
+ g15 -> leds [LG_G15_KBD_BRIGHTNESS ].brightness = g15 -> transfer_buf [1 ];
64
+ g15 -> leds [LG_G15_LCD_BRIGHTNESS ].brightness = g15 -> transfer_buf [2 ];
65
+ return 0 ;
66
+ }
67
+
68
+ static enum led_brightness lg_g15_led_get (struct led_classdev * led_cdev )
69
+ {
70
+ struct lg_g15_led * g15_led =
71
+ container_of (led_cdev , struct lg_g15_led , cdev );
72
+ struct lg_g15_data * g15 = dev_get_drvdata (led_cdev -> dev -> parent );
73
+ enum led_brightness brightness ;
74
+
75
+ mutex_lock (& g15 -> mutex );
76
+ lg_g15_update_led_brightness (g15 );
77
+ brightness = g15 -> leds [g15_led -> led ].brightness ;
78
+ mutex_unlock (& g15 -> mutex );
79
+
80
+ return brightness ;
81
+ }
82
+
83
+ static int lg_g15_led_set (struct led_classdev * led_cdev ,
84
+ enum led_brightness brightness )
85
+ {
86
+ struct lg_g15_led * g15_led =
87
+ container_of (led_cdev , struct lg_g15_led , cdev );
88
+ struct lg_g15_data * g15 = dev_get_drvdata (led_cdev -> dev -> parent );
89
+ int ret ;
90
+
91
+ /* Ignore LED off on unregister / keyboard unplug */
92
+ if (led_cdev -> flags & LED_UNREGISTERING )
93
+ return 0 ;
94
+
95
+ mutex_lock (& g15 -> mutex );
96
+
97
+ g15 -> transfer_buf [0 ] = LG_G15_FEATURE_REPORT ;
98
+ g15 -> transfer_buf [1 ] = g15_led -> led + 1 ;
99
+ g15 -> transfer_buf [2 ] = brightness << (g15_led -> led * 4 );
100
+ g15 -> transfer_buf [3 ] = 0 ;
101
+
102
+ ret = hid_hw_raw_request (g15 -> hdev , LG_G15_FEATURE_REPORT ,
103
+ g15 -> transfer_buf , 4 ,
104
+ HID_FEATURE_REPORT , HID_REQ_SET_REPORT );
105
+ if (ret == 4 ) {
106
+ /* Success */
107
+ g15_led -> brightness = brightness ;
108
+ ret = 0 ;
109
+ } else {
110
+ hid_err (g15 -> hdev , "Error setting LED brightness: %d\n" , ret );
111
+ ret = (ret < 0 ) ? ret : - EIO ;
112
+ }
113
+
114
+ mutex_unlock (& g15 -> mutex );
115
+
116
+ return ret ;
117
+ }
118
+
119
+ static void lg_g15_leds_changed_work (struct work_struct * work )
120
+ {
121
+ struct lg_g15_data * g15 = container_of (work , struct lg_g15_data , work );
122
+ enum led_brightness old_brightness [LG_G15_BRIGHTNESS_MAX ];
123
+ enum led_brightness brightness [LG_G15_BRIGHTNESS_MAX ];
124
+ int i , ret ;
125
+
126
+ mutex_lock (& g15 -> mutex );
127
+ for (i = 0 ; i < LG_G15_BRIGHTNESS_MAX ; i ++ )
128
+ old_brightness [i ] = g15 -> leds [i ].brightness ;
129
+
130
+ ret = lg_g15_update_led_brightness (g15 );
131
+
132
+ for (i = 0 ; i < LG_G15_BRIGHTNESS_MAX ; i ++ )
133
+ brightness [i ] = g15 -> leds [i ].brightness ;
134
+ mutex_unlock (& g15 -> mutex );
135
+
136
+ if (ret )
137
+ return ;
138
+
139
+ for (i = 0 ; i < LG_G15_BRIGHTNESS_MAX ; i ++ ) {
140
+ if (brightness [i ] == old_brightness [i ])
141
+ continue ;
142
+
143
+ led_classdev_notify_brightness_hw_changed (& g15 -> leds [i ].cdev ,
144
+ brightness [i ]);
145
+ }
146
+ }
147
+
33
148
/* On the G15 Mark I Logitech has been quite creative with which bit is what */
34
149
static int lg_g15_event (struct lg_g15_data * g15 , u8 * data , int size )
35
150
{
@@ -69,6 +184,10 @@ static int lg_g15_event(struct lg_g15_data *g15, u8 *data, int size)
69
184
input_report_key (g15 -> input , KEY_KBD_LCD_MENU2 + i , val );
70
185
}
71
186
187
+ /* Backlight cycle button pressed? */
188
+ if (data [1 ] & 0x80 )
189
+ schedule_work (& g15 -> work );
190
+
72
191
input_sync (g15 -> input );
73
192
return 0 ;
74
193
}
@@ -97,6 +216,10 @@ static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data, int size)
97
216
input_report_key (g15 -> input , KEY_KBD_LCD_MENU2 + i , val );
98
217
}
99
218
219
+ /* Backlight cycle button pressed? */
220
+ if (data [2 ] & 0x01 )
221
+ schedule_work (& g15 -> work );
222
+
100
223
input_sync (g15 -> input );
101
224
return 0 ;
102
225
}
@@ -129,6 +252,23 @@ static void lg_g15_input_close(struct input_dev *dev)
129
252
hid_hw_close (hdev );
130
253
}
131
254
255
+ static int lg_g15_register_led (struct lg_g15_data * g15 , int i )
256
+ {
257
+ const char * const led_names [] = {
258
+ "g15::kbd_backlight" ,
259
+ "g15::lcd_backlight" ,
260
+ };
261
+
262
+ g15 -> leds [i ].led = i ;
263
+ g15 -> leds [i ].cdev .name = led_names [i ];
264
+ g15 -> leds [i ].cdev .brightness_set_blocking = lg_g15_led_set ;
265
+ g15 -> leds [i ].cdev .brightness_get = lg_g15_led_get ;
266
+ g15 -> leds [i ].cdev .flags = LED_BRIGHT_HW_CHANGED ;
267
+ g15 -> leds [i ].cdev .max_brightness = 2 ;
268
+
269
+ return devm_led_classdev_register (& g15 -> hdev -> dev , & g15 -> leds [i ].cdev );
270
+ }
271
+
132
272
static int lg_g15_probe (struct hid_device * hdev , const struct hid_device_id * id )
133
273
{
134
274
u8 gkeys_settings_output_report = 0 ;
@@ -145,12 +285,15 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
145
285
if (!g15 )
146
286
return - ENOMEM ;
147
287
288
+ mutex_init (& g15 -> mutex );
289
+
148
290
input = devm_input_allocate_device (& hdev -> dev );
149
291
if (!input )
150
292
return - ENOMEM ;
151
293
152
294
g15 -> hdev = hdev ;
153
295
g15 -> model = id -> driver_data ;
296
+ INIT_WORK (& g15 -> work , lg_g15_leds_changed_work );
154
297
hid_set_drvdata (hdev , (void * )g15 );
155
298
156
299
switch (g15 -> model ) {
@@ -196,6 +339,12 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
196
339
goto error_hw_stop ;
197
340
}
198
341
342
+ /* Get initial brightness levels */
343
+ ret = lg_g15_update_led_brightness (g15 );
344
+ if (ret )
345
+ goto error_hw_stop ;
346
+
347
+ /* Setup and register input device */
199
348
input -> name = "Logitech Gaming Keyboard Gaming Keys" ;
200
349
input -> phys = hdev -> phys ;
201
350
input -> uniq = hdev -> uniq ;
@@ -227,6 +376,13 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
227
376
if (ret )
228
377
goto error_hw_stop ;
229
378
379
+ /* Register LED devices */
380
+ for (i = 0 ; i < LG_G15_BRIGHTNESS_MAX ; i ++ ) {
381
+ ret = lg_g15_register_led (g15 , i );
382
+ if (ret )
383
+ goto error_hw_stop ;
384
+ }
385
+
230
386
return 0 ;
231
387
232
388
error_hw_stop :
0 commit comments