1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for IMS Passenger Control Unit Devices |
4 | * |
5 | * Copyright (C) 2013 The IMS Company |
6 | */ |
7 | |
8 | #include <linux/completion.h> |
9 | #include <linux/device.h> |
10 | #include <linux/firmware.h> |
11 | #include <linux/ihex.h> |
12 | #include <linux/input.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/leds.h> |
15 | #include <linux/module.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/types.h> |
18 | #include <linux/usb/input.h> |
19 | #include <linux/usb/cdc.h> |
20 | #include <asm/unaligned.h> |
21 | |
22 | #define IMS_PCU_KEYMAP_LEN 32 |
23 | |
24 | struct ims_pcu_buttons { |
25 | struct input_dev *input; |
26 | char name[32]; |
27 | char phys[32]; |
28 | unsigned short keymap[IMS_PCU_KEYMAP_LEN]; |
29 | }; |
30 | |
31 | struct ims_pcu_gamepad { |
32 | struct input_dev *input; |
33 | char name[32]; |
34 | char phys[32]; |
35 | }; |
36 | |
37 | struct ims_pcu_backlight { |
38 | struct led_classdev cdev; |
39 | char name[32]; |
40 | }; |
41 | |
42 | #define IMS_PCU_PART_NUMBER_LEN 15 |
43 | #define IMS_PCU_SERIAL_NUMBER_LEN 8 |
44 | #define IMS_PCU_DOM_LEN 8 |
45 | #define IMS_PCU_FW_VERSION_LEN (9 + 1) |
46 | #define IMS_PCU_BL_VERSION_LEN (9 + 1) |
47 | #define IMS_PCU_BL_RESET_REASON_LEN (2 + 1) |
48 | |
49 | #define IMS_PCU_PCU_B_DEVICE_ID 5 |
50 | |
51 | #define IMS_PCU_BUF_SIZE 128 |
52 | |
53 | struct ims_pcu { |
54 | struct usb_device *udev; |
55 | struct device *dev; /* control interface's device, used for logging */ |
56 | |
57 | unsigned int device_no; |
58 | |
59 | bool bootloader_mode; |
60 | |
61 | char part_number[IMS_PCU_PART_NUMBER_LEN]; |
62 | char serial_number[IMS_PCU_SERIAL_NUMBER_LEN]; |
63 | char date_of_manufacturing[IMS_PCU_DOM_LEN]; |
64 | char fw_version[IMS_PCU_FW_VERSION_LEN]; |
65 | char bl_version[IMS_PCU_BL_VERSION_LEN]; |
66 | char reset_reason[IMS_PCU_BL_RESET_REASON_LEN]; |
67 | int update_firmware_status; |
68 | u8 device_id; |
69 | |
70 | u8 ofn_reg_addr; |
71 | |
72 | struct usb_interface *ctrl_intf; |
73 | |
74 | struct usb_endpoint_descriptor *ep_ctrl; |
75 | struct urb *urb_ctrl; |
76 | u8 *urb_ctrl_buf; |
77 | dma_addr_t ctrl_dma; |
78 | size_t max_ctrl_size; |
79 | |
80 | struct usb_interface *data_intf; |
81 | |
82 | struct usb_endpoint_descriptor *ep_in; |
83 | struct urb *urb_in; |
84 | u8 *urb_in_buf; |
85 | dma_addr_t read_dma; |
86 | size_t max_in_size; |
87 | |
88 | struct usb_endpoint_descriptor *ep_out; |
89 | u8 *urb_out_buf; |
90 | size_t max_out_size; |
91 | |
92 | u8 read_buf[IMS_PCU_BUF_SIZE]; |
93 | u8 read_pos; |
94 | u8 check_sum; |
95 | bool have_stx; |
96 | bool have_dle; |
97 | |
98 | u8 cmd_buf[IMS_PCU_BUF_SIZE]; |
99 | u8 ack_id; |
100 | u8 expected_response; |
101 | u8 cmd_buf_len; |
102 | struct completion cmd_done; |
103 | struct mutex cmd_mutex; |
104 | |
105 | u32 fw_start_addr; |
106 | u32 fw_end_addr; |
107 | struct completion async_firmware_done; |
108 | |
109 | struct ims_pcu_buttons buttons; |
110 | struct ims_pcu_gamepad *gamepad; |
111 | struct ims_pcu_backlight backlight; |
112 | |
113 | bool setup_complete; /* Input and LED devices have been created */ |
114 | }; |
115 | |
116 | |
117 | /********************************************************************* |
118 | * Buttons Input device support * |
119 | *********************************************************************/ |
120 | |
121 | static const unsigned short ims_pcu_keymap_1[] = { |
122 | [1] = KEY_ATTENDANT_OFF, |
123 | [2] = KEY_ATTENDANT_ON, |
124 | [3] = KEY_LIGHTS_TOGGLE, |
125 | [4] = KEY_VOLUMEUP, |
126 | [5] = KEY_VOLUMEDOWN, |
127 | [6] = KEY_INFO, |
128 | }; |
129 | |
130 | static const unsigned short ims_pcu_keymap_2[] = { |
131 | [4] = KEY_VOLUMEUP, |
132 | [5] = KEY_VOLUMEDOWN, |
133 | [6] = KEY_INFO, |
134 | }; |
135 | |
136 | static const unsigned short ims_pcu_keymap_3[] = { |
137 | [1] = KEY_HOMEPAGE, |
138 | [2] = KEY_ATTENDANT_TOGGLE, |
139 | [3] = KEY_LIGHTS_TOGGLE, |
140 | [4] = KEY_VOLUMEUP, |
141 | [5] = KEY_VOLUMEDOWN, |
142 | [6] = KEY_DISPLAYTOGGLE, |
143 | [18] = KEY_PLAYPAUSE, |
144 | }; |
145 | |
146 | static const unsigned short ims_pcu_keymap_4[] = { |
147 | [1] = KEY_ATTENDANT_OFF, |
148 | [2] = KEY_ATTENDANT_ON, |
149 | [3] = KEY_LIGHTS_TOGGLE, |
150 | [4] = KEY_VOLUMEUP, |
151 | [5] = KEY_VOLUMEDOWN, |
152 | [6] = KEY_INFO, |
153 | [18] = KEY_PLAYPAUSE, |
154 | }; |
155 | |
156 | static const unsigned short ims_pcu_keymap_5[] = { |
157 | [1] = KEY_ATTENDANT_OFF, |
158 | [2] = KEY_ATTENDANT_ON, |
159 | [3] = KEY_LIGHTS_TOGGLE, |
160 | }; |
161 | |
162 | struct ims_pcu_device_info { |
163 | const unsigned short *keymap; |
164 | size_t keymap_len; |
165 | bool has_gamepad; |
166 | }; |
167 | |
168 | #define IMS_PCU_DEVINFO(_n, _gamepad) \ |
169 | [_n] = { \ |
170 | .keymap = ims_pcu_keymap_##_n, \ |
171 | .keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n), \ |
172 | .has_gamepad = _gamepad, \ |
173 | } |
174 | |
175 | static const struct ims_pcu_device_info ims_pcu_device_info[] = { |
176 | IMS_PCU_DEVINFO(1, true), |
177 | IMS_PCU_DEVINFO(2, true), |
178 | IMS_PCU_DEVINFO(3, true), |
179 | IMS_PCU_DEVINFO(4, true), |
180 | IMS_PCU_DEVINFO(5, false), |
181 | }; |
182 | |
183 | static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data) |
184 | { |
185 | struct ims_pcu_buttons *buttons = &pcu->buttons; |
186 | struct input_dev *input = buttons->input; |
187 | int i; |
188 | |
189 | for (i = 0; i < 32; i++) { |
190 | unsigned short keycode = buttons->keymap[i]; |
191 | |
192 | if (keycode != KEY_RESERVED) |
193 | input_report_key(dev: input, code: keycode, value: data & (1UL << i)); |
194 | } |
195 | |
196 | input_sync(dev: input); |
197 | } |
198 | |
199 | static int ims_pcu_setup_buttons(struct ims_pcu *pcu, |
200 | const unsigned short *keymap, |
201 | size_t keymap_len) |
202 | { |
203 | struct ims_pcu_buttons *buttons = &pcu->buttons; |
204 | struct input_dev *input; |
205 | int i; |
206 | int error; |
207 | |
208 | input = input_allocate_device(); |
209 | if (!input) { |
210 | dev_err(pcu->dev, |
211 | "Not enough memory for input input device\n" ); |
212 | return -ENOMEM; |
213 | } |
214 | |
215 | snprintf(buf: buttons->name, size: sizeof(buttons->name), |
216 | fmt: "IMS PCU#%d Button Interface" , pcu->device_no); |
217 | |
218 | usb_make_path(dev: pcu->udev, buf: buttons->phys, size: sizeof(buttons->phys)); |
219 | strlcat(p: buttons->phys, q: "/input0" , avail: sizeof(buttons->phys)); |
220 | |
221 | memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len); |
222 | |
223 | input->name = buttons->name; |
224 | input->phys = buttons->phys; |
225 | usb_to_input_id(dev: pcu->udev, id: &input->id); |
226 | input->dev.parent = &pcu->ctrl_intf->dev; |
227 | |
228 | input->keycode = buttons->keymap; |
229 | input->keycodemax = ARRAY_SIZE(buttons->keymap); |
230 | input->keycodesize = sizeof(buttons->keymap[0]); |
231 | |
232 | __set_bit(EV_KEY, input->evbit); |
233 | for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++) |
234 | __set_bit(buttons->keymap[i], input->keybit); |
235 | __clear_bit(KEY_RESERVED, input->keybit); |
236 | |
237 | error = input_register_device(input); |
238 | if (error) { |
239 | dev_err(pcu->dev, |
240 | "Failed to register buttons input device: %d\n" , |
241 | error); |
242 | input_free_device(dev: input); |
243 | return error; |
244 | } |
245 | |
246 | buttons->input = input; |
247 | return 0; |
248 | } |
249 | |
250 | static void ims_pcu_destroy_buttons(struct ims_pcu *pcu) |
251 | { |
252 | struct ims_pcu_buttons *buttons = &pcu->buttons; |
253 | |
254 | input_unregister_device(buttons->input); |
255 | } |
256 | |
257 | |
258 | /********************************************************************* |
259 | * Gamepad Input device support * |
260 | *********************************************************************/ |
261 | |
262 | static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data) |
263 | { |
264 | struct ims_pcu_gamepad *gamepad = pcu->gamepad; |
265 | struct input_dev *input = gamepad->input; |
266 | int x, y; |
267 | |
268 | x = !!(data & (1 << 14)) - !!(data & (1 << 13)); |
269 | y = !!(data & (1 << 12)) - !!(data & (1 << 11)); |
270 | |
271 | input_report_abs(dev: input, ABS_X, value: x); |
272 | input_report_abs(dev: input, ABS_Y, value: y); |
273 | |
274 | input_report_key(dev: input, BTN_A, value: data & (1 << 7)); |
275 | input_report_key(dev: input, BTN_B, value: data & (1 << 8)); |
276 | input_report_key(dev: input, BTN_X, value: data & (1 << 9)); |
277 | input_report_key(dev: input, BTN_Y, value: data & (1 << 10)); |
278 | input_report_key(dev: input, BTN_START, value: data & (1 << 15)); |
279 | input_report_key(dev: input, BTN_SELECT, value: data & (1 << 16)); |
280 | |
281 | input_sync(dev: input); |
282 | } |
283 | |
284 | static int ims_pcu_setup_gamepad(struct ims_pcu *pcu) |
285 | { |
286 | struct ims_pcu_gamepad *gamepad; |
287 | struct input_dev *input; |
288 | int error; |
289 | |
290 | gamepad = kzalloc(size: sizeof(struct ims_pcu_gamepad), GFP_KERNEL); |
291 | input = input_allocate_device(); |
292 | if (!gamepad || !input) { |
293 | dev_err(pcu->dev, |
294 | "Not enough memory for gamepad device\n" ); |
295 | error = -ENOMEM; |
296 | goto err_free_mem; |
297 | } |
298 | |
299 | gamepad->input = input; |
300 | |
301 | snprintf(buf: gamepad->name, size: sizeof(gamepad->name), |
302 | fmt: "IMS PCU#%d Gamepad Interface" , pcu->device_no); |
303 | |
304 | usb_make_path(dev: pcu->udev, buf: gamepad->phys, size: sizeof(gamepad->phys)); |
305 | strlcat(p: gamepad->phys, q: "/input1" , avail: sizeof(gamepad->phys)); |
306 | |
307 | input->name = gamepad->name; |
308 | input->phys = gamepad->phys; |
309 | usb_to_input_id(dev: pcu->udev, id: &input->id); |
310 | input->dev.parent = &pcu->ctrl_intf->dev; |
311 | |
312 | __set_bit(EV_KEY, input->evbit); |
313 | __set_bit(BTN_A, input->keybit); |
314 | __set_bit(BTN_B, input->keybit); |
315 | __set_bit(BTN_X, input->keybit); |
316 | __set_bit(BTN_Y, input->keybit); |
317 | __set_bit(BTN_START, input->keybit); |
318 | __set_bit(BTN_SELECT, input->keybit); |
319 | |
320 | __set_bit(EV_ABS, input->evbit); |
321 | input_set_abs_params(dev: input, ABS_X, min: -1, max: 1, fuzz: 0, flat: 0); |
322 | input_set_abs_params(dev: input, ABS_Y, min: -1, max: 1, fuzz: 0, flat: 0); |
323 | |
324 | error = input_register_device(input); |
325 | if (error) { |
326 | dev_err(pcu->dev, |
327 | "Failed to register gamepad input device: %d\n" , |
328 | error); |
329 | goto err_free_mem; |
330 | } |
331 | |
332 | pcu->gamepad = gamepad; |
333 | return 0; |
334 | |
335 | err_free_mem: |
336 | input_free_device(dev: input); |
337 | kfree(objp: gamepad); |
338 | return error; |
339 | } |
340 | |
341 | static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) |
342 | { |
343 | struct ims_pcu_gamepad *gamepad = pcu->gamepad; |
344 | |
345 | input_unregister_device(gamepad->input); |
346 | kfree(objp: gamepad); |
347 | } |
348 | |
349 | |
350 | /********************************************************************* |
351 | * PCU Communication protocol handling * |
352 | *********************************************************************/ |
353 | |
354 | #define IMS_PCU_PROTOCOL_STX 0x02 |
355 | #define IMS_PCU_PROTOCOL_ETX 0x03 |
356 | #define IMS_PCU_PROTOCOL_DLE 0x10 |
357 | |
358 | /* PCU commands */ |
359 | #define IMS_PCU_CMD_STATUS 0xa0 |
360 | #define IMS_PCU_CMD_PCU_RESET 0xa1 |
361 | #define IMS_PCU_CMD_RESET_REASON 0xa2 |
362 | #define IMS_PCU_CMD_SEND_BUTTONS 0xa3 |
363 | #define IMS_PCU_CMD_JUMP_TO_BTLDR 0xa4 |
364 | #define IMS_PCU_CMD_GET_INFO 0xa5 |
365 | #define IMS_PCU_CMD_SET_BRIGHTNESS 0xa6 |
366 | #define IMS_PCU_CMD_EEPROM 0xa7 |
367 | #define IMS_PCU_CMD_GET_FW_VERSION 0xa8 |
368 | #define IMS_PCU_CMD_GET_BL_VERSION 0xa9 |
369 | #define IMS_PCU_CMD_SET_INFO 0xab |
370 | #define IMS_PCU_CMD_GET_BRIGHTNESS 0xac |
371 | #define IMS_PCU_CMD_GET_DEVICE_ID 0xae |
372 | #define IMS_PCU_CMD_SPECIAL_INFO 0xb0 |
373 | #define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */ |
374 | #define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3 |
375 | #define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4 |
376 | |
377 | /* PCU responses */ |
378 | #define IMS_PCU_RSP_STATUS 0xc0 |
379 | #define IMS_PCU_RSP_PCU_RESET 0 /* Originally 0xc1 */ |
380 | #define IMS_PCU_RSP_RESET_REASON 0xc2 |
381 | #define IMS_PCU_RSP_SEND_BUTTONS 0xc3 |
382 | #define IMS_PCU_RSP_JUMP_TO_BTLDR 0 /* Originally 0xc4 */ |
383 | #define IMS_PCU_RSP_GET_INFO 0xc5 |
384 | #define IMS_PCU_RSP_SET_BRIGHTNESS 0xc6 |
385 | #define IMS_PCU_RSP_EEPROM 0xc7 |
386 | #define IMS_PCU_RSP_GET_FW_VERSION 0xc8 |
387 | #define IMS_PCU_RSP_GET_BL_VERSION 0xc9 |
388 | #define IMS_PCU_RSP_SET_INFO 0xcb |
389 | #define IMS_PCU_RSP_GET_BRIGHTNESS 0xcc |
390 | #define IMS_PCU_RSP_CMD_INVALID 0xcd |
391 | #define IMS_PCU_RSP_GET_DEVICE_ID 0xce |
392 | #define IMS_PCU_RSP_SPECIAL_INFO 0xd0 |
393 | #define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */ |
394 | #define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2 |
395 | #define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3 |
396 | |
397 | |
398 | #define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */ |
399 | #define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */ |
400 | |
401 | |
402 | #define IMS_PCU_MIN_PACKET_LEN 3 |
403 | #define IMS_PCU_DATA_OFFSET 2 |
404 | |
405 | #define IMS_PCU_CMD_WRITE_TIMEOUT 100 /* msec */ |
406 | #define IMS_PCU_CMD_RESPONSE_TIMEOUT 500 /* msec */ |
407 | |
408 | static void ims_pcu_report_events(struct ims_pcu *pcu) |
409 | { |
410 | u32 data = get_unaligned_be32(p: &pcu->read_buf[3]); |
411 | |
412 | ims_pcu_buttons_report(pcu, data: data & ~IMS_PCU_GAMEPAD_MASK); |
413 | if (pcu->gamepad) |
414 | ims_pcu_gamepad_report(pcu, data); |
415 | } |
416 | |
417 | static void ims_pcu_handle_response(struct ims_pcu *pcu) |
418 | { |
419 | switch (pcu->read_buf[0]) { |
420 | case IMS_PCU_RSP_EVNT_BUTTONS: |
421 | if (likely(pcu->setup_complete)) |
422 | ims_pcu_report_events(pcu); |
423 | break; |
424 | |
425 | default: |
426 | /* |
427 | * See if we got command completion. |
428 | * If both the sequence and response code match save |
429 | * the data and signal completion. |
430 | */ |
431 | if (pcu->read_buf[0] == pcu->expected_response && |
432 | pcu->read_buf[1] == pcu->ack_id - 1) { |
433 | |
434 | memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos); |
435 | pcu->cmd_buf_len = pcu->read_pos; |
436 | complete(&pcu->cmd_done); |
437 | } |
438 | break; |
439 | } |
440 | } |
441 | |
442 | static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) |
443 | { |
444 | int i; |
445 | |
446 | for (i = 0; i < urb->actual_length; i++) { |
447 | u8 data = pcu->urb_in_buf[i]; |
448 | |
449 | /* Skip everything until we get Start Xmit */ |
450 | if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX) |
451 | continue; |
452 | |
453 | if (pcu->have_dle) { |
454 | pcu->have_dle = false; |
455 | pcu->read_buf[pcu->read_pos++] = data; |
456 | pcu->check_sum += data; |
457 | continue; |
458 | } |
459 | |
460 | switch (data) { |
461 | case IMS_PCU_PROTOCOL_STX: |
462 | if (pcu->have_stx) |
463 | dev_warn(pcu->dev, |
464 | "Unexpected STX at byte %d, discarding old data\n" , |
465 | pcu->read_pos); |
466 | pcu->have_stx = true; |
467 | pcu->have_dle = false; |
468 | pcu->read_pos = 0; |
469 | pcu->check_sum = 0; |
470 | break; |
471 | |
472 | case IMS_PCU_PROTOCOL_DLE: |
473 | pcu->have_dle = true; |
474 | break; |
475 | |
476 | case IMS_PCU_PROTOCOL_ETX: |
477 | if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) { |
478 | dev_warn(pcu->dev, |
479 | "Short packet received (%d bytes), ignoring\n" , |
480 | pcu->read_pos); |
481 | } else if (pcu->check_sum != 0) { |
482 | dev_warn(pcu->dev, |
483 | "Invalid checksum in packet (%d bytes), ignoring\n" , |
484 | pcu->read_pos); |
485 | } else { |
486 | ims_pcu_handle_response(pcu); |
487 | } |
488 | |
489 | pcu->have_stx = false; |
490 | pcu->have_dle = false; |
491 | pcu->read_pos = 0; |
492 | break; |
493 | |
494 | default: |
495 | pcu->read_buf[pcu->read_pos++] = data; |
496 | pcu->check_sum += data; |
497 | break; |
498 | } |
499 | } |
500 | } |
501 | |
502 | static bool ims_pcu_byte_needs_escape(u8 byte) |
503 | { |
504 | return byte == IMS_PCU_PROTOCOL_STX || |
505 | byte == IMS_PCU_PROTOCOL_ETX || |
506 | byte == IMS_PCU_PROTOCOL_DLE; |
507 | } |
508 | |
509 | static int ims_pcu_send_cmd_chunk(struct ims_pcu *pcu, |
510 | u8 command, int chunk, int len) |
511 | { |
512 | int error; |
513 | |
514 | error = usb_bulk_msg(usb_dev: pcu->udev, |
515 | usb_sndbulkpipe(pcu->udev, |
516 | pcu->ep_out->bEndpointAddress), |
517 | data: pcu->urb_out_buf, len, |
518 | NULL, IMS_PCU_CMD_WRITE_TIMEOUT); |
519 | if (error < 0) { |
520 | dev_dbg(pcu->dev, |
521 | "Sending 0x%02x command failed at chunk %d: %d\n" , |
522 | command, chunk, error); |
523 | return error; |
524 | } |
525 | |
526 | return 0; |
527 | } |
528 | |
529 | static int ims_pcu_send_command(struct ims_pcu *pcu, |
530 | u8 command, const u8 *data, int len) |
531 | { |
532 | int count = 0; |
533 | int chunk = 0; |
534 | int delta; |
535 | int i; |
536 | int error; |
537 | u8 csum = 0; |
538 | u8 ack_id; |
539 | |
540 | pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_STX; |
541 | |
542 | /* We know the command need not be escaped */ |
543 | pcu->urb_out_buf[count++] = command; |
544 | csum += command; |
545 | |
546 | ack_id = pcu->ack_id++; |
547 | if (ack_id == 0xff) |
548 | ack_id = pcu->ack_id++; |
549 | |
550 | if (ims_pcu_byte_needs_escape(byte: ack_id)) |
551 | pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; |
552 | |
553 | pcu->urb_out_buf[count++] = ack_id; |
554 | csum += ack_id; |
555 | |
556 | for (i = 0; i < len; i++) { |
557 | |
558 | delta = ims_pcu_byte_needs_escape(byte: data[i]) ? 2 : 1; |
559 | if (count + delta >= pcu->max_out_size) { |
560 | error = ims_pcu_send_cmd_chunk(pcu, command, |
561 | chunk: ++chunk, len: count); |
562 | if (error) |
563 | return error; |
564 | |
565 | count = 0; |
566 | } |
567 | |
568 | if (delta == 2) |
569 | pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; |
570 | |
571 | pcu->urb_out_buf[count++] = data[i]; |
572 | csum += data[i]; |
573 | } |
574 | |
575 | csum = 1 + ~csum; |
576 | |
577 | delta = ims_pcu_byte_needs_escape(byte: csum) ? 3 : 2; |
578 | if (count + delta >= pcu->max_out_size) { |
579 | error = ims_pcu_send_cmd_chunk(pcu, command, chunk: ++chunk, len: count); |
580 | if (error) |
581 | return error; |
582 | |
583 | count = 0; |
584 | } |
585 | |
586 | if (delta == 3) |
587 | pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; |
588 | |
589 | pcu->urb_out_buf[count++] = csum; |
590 | pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_ETX; |
591 | |
592 | return ims_pcu_send_cmd_chunk(pcu, command, chunk: ++chunk, len: count); |
593 | } |
594 | |
595 | static int __ims_pcu_execute_command(struct ims_pcu *pcu, |
596 | u8 command, const void *data, size_t len, |
597 | u8 expected_response, int response_time) |
598 | { |
599 | int error; |
600 | |
601 | pcu->expected_response = expected_response; |
602 | init_completion(x: &pcu->cmd_done); |
603 | |
604 | error = ims_pcu_send_command(pcu, command, data, len); |
605 | if (error) |
606 | return error; |
607 | |
608 | if (expected_response && |
609 | !wait_for_completion_timeout(x: &pcu->cmd_done, |
610 | timeout: msecs_to_jiffies(m: response_time))) { |
611 | dev_dbg(pcu->dev, "Command 0x%02x timed out\n" , command); |
612 | return -ETIMEDOUT; |
613 | } |
614 | |
615 | return 0; |
616 | } |
617 | |
618 | #define ims_pcu_execute_command(pcu, code, data, len) \ |
619 | __ims_pcu_execute_command(pcu, \ |
620 | IMS_PCU_CMD_##code, data, len, \ |
621 | IMS_PCU_RSP_##code, \ |
622 | IMS_PCU_CMD_RESPONSE_TIMEOUT) |
623 | |
624 | #define ims_pcu_execute_query(pcu, code) \ |
625 | ims_pcu_execute_command(pcu, code, NULL, 0) |
626 | |
627 | /* Bootloader commands */ |
628 | #define IMS_PCU_BL_CMD_QUERY_DEVICE 0xa1 |
629 | #define IMS_PCU_BL_CMD_UNLOCK_CONFIG 0xa2 |
630 | #define IMS_PCU_BL_CMD_ERASE_APP 0xa3 |
631 | #define IMS_PCU_BL_CMD_PROGRAM_DEVICE 0xa4 |
632 | #define IMS_PCU_BL_CMD_PROGRAM_COMPLETE 0xa5 |
633 | #define IMS_PCU_BL_CMD_READ_APP 0xa6 |
634 | #define IMS_PCU_BL_CMD_RESET_DEVICE 0xa7 |
635 | #define IMS_PCU_BL_CMD_LAUNCH_APP 0xa8 |
636 | |
637 | /* Bootloader commands */ |
638 | #define IMS_PCU_BL_RSP_QUERY_DEVICE 0xc1 |
639 | #define IMS_PCU_BL_RSP_UNLOCK_CONFIG 0xc2 |
640 | #define IMS_PCU_BL_RSP_ERASE_APP 0xc3 |
641 | #define IMS_PCU_BL_RSP_PROGRAM_DEVICE 0xc4 |
642 | #define IMS_PCU_BL_RSP_PROGRAM_COMPLETE 0xc5 |
643 | #define IMS_PCU_BL_RSP_READ_APP 0xc6 |
644 | #define IMS_PCU_BL_RSP_RESET_DEVICE 0 /* originally 0xa7 */ |
645 | #define IMS_PCU_BL_RSP_LAUNCH_APP 0 /* originally 0xa8 */ |
646 | |
647 | #define IMS_PCU_BL_DATA_OFFSET 3 |
648 | |
649 | static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu, |
650 | u8 command, const void *data, size_t len, |
651 | u8 expected_response, int response_time) |
652 | { |
653 | int error; |
654 | |
655 | pcu->cmd_buf[0] = command; |
656 | if (data) |
657 | memcpy(&pcu->cmd_buf[1], data, len); |
658 | |
659 | error = __ims_pcu_execute_command(pcu, |
660 | IMS_PCU_CMD_BOOTLOADER, data: pcu->cmd_buf, len: len + 1, |
661 | expected_response: expected_response ? IMS_PCU_RSP_BOOTLOADER : 0, |
662 | response_time); |
663 | if (error) { |
664 | dev_err(pcu->dev, |
665 | "Failure when sending 0x%02x command to bootloader, error: %d\n" , |
666 | pcu->cmd_buf[0], error); |
667 | return error; |
668 | } |
669 | |
670 | if (expected_response && pcu->cmd_buf[2] != expected_response) { |
671 | dev_err(pcu->dev, |
672 | "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n" , |
673 | pcu->cmd_buf[2], expected_response); |
674 | return -EINVAL; |
675 | } |
676 | |
677 | return 0; |
678 | } |
679 | |
680 | #define ims_pcu_execute_bl_command(pcu, code, data, len, timeout) \ |
681 | __ims_pcu_execute_bl_command(pcu, \ |
682 | IMS_PCU_BL_CMD_##code, data, len, \ |
683 | IMS_PCU_BL_RSP_##code, timeout) \ |
684 | |
685 | #define IMS_PCU_INFO_PART_OFFSET 2 |
686 | #define IMS_PCU_INFO_DOM_OFFSET 17 |
687 | #define IMS_PCU_INFO_SERIAL_OFFSET 25 |
688 | |
689 | #define IMS_PCU_SET_INFO_SIZE 31 |
690 | |
691 | static int ims_pcu_get_info(struct ims_pcu *pcu) |
692 | { |
693 | int error; |
694 | |
695 | error = ims_pcu_execute_query(pcu, GET_INFO); |
696 | if (error) { |
697 | dev_err(pcu->dev, |
698 | "GET_INFO command failed, error: %d\n" , error); |
699 | return error; |
700 | } |
701 | |
702 | memcpy(pcu->part_number, |
703 | &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], |
704 | sizeof(pcu->part_number)); |
705 | memcpy(pcu->date_of_manufacturing, |
706 | &pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET], |
707 | sizeof(pcu->date_of_manufacturing)); |
708 | memcpy(pcu->serial_number, |
709 | &pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET], |
710 | sizeof(pcu->serial_number)); |
711 | |
712 | return 0; |
713 | } |
714 | |
715 | static int ims_pcu_set_info(struct ims_pcu *pcu) |
716 | { |
717 | int error; |
718 | |
719 | memcpy(&pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], |
720 | pcu->part_number, sizeof(pcu->part_number)); |
721 | memcpy(&pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET], |
722 | pcu->date_of_manufacturing, sizeof(pcu->date_of_manufacturing)); |
723 | memcpy(&pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET], |
724 | pcu->serial_number, sizeof(pcu->serial_number)); |
725 | |
726 | error = ims_pcu_execute_command(pcu, SET_INFO, |
727 | &pcu->cmd_buf[IMS_PCU_DATA_OFFSET], |
728 | IMS_PCU_SET_INFO_SIZE); |
729 | if (error) { |
730 | dev_err(pcu->dev, |
731 | "Failed to update device information, error: %d\n" , |
732 | error); |
733 | return error; |
734 | } |
735 | |
736 | return 0; |
737 | } |
738 | |
739 | static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu) |
740 | { |
741 | int error; |
742 | |
743 | /* Execute jump to the bootoloader */ |
744 | error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0); |
745 | if (error) { |
746 | dev_err(pcu->dev, |
747 | "Failure when sending JUMP TO BOOTLOADER command, error: %d\n" , |
748 | error); |
749 | return error; |
750 | } |
751 | |
752 | return 0; |
753 | } |
754 | |
755 | /********************************************************************* |
756 | * Firmware Update handling * |
757 | *********************************************************************/ |
758 | |
759 | #define IMS_PCU_FIRMWARE_NAME "imspcu.fw" |
760 | |
761 | struct ims_pcu_flash_fmt { |
762 | __le32 addr; |
763 | u8 len; |
764 | u8 data[]; |
765 | }; |
766 | |
767 | static unsigned int ims_pcu_count_fw_records(const struct firmware *fw) |
768 | { |
769 | const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data; |
770 | unsigned int count = 0; |
771 | |
772 | while (rec) { |
773 | count++; |
774 | rec = ihex_next_binrec(rec); |
775 | } |
776 | |
777 | return count; |
778 | } |
779 | |
780 | static int ims_pcu_verify_block(struct ims_pcu *pcu, |
781 | u32 addr, u8 len, const u8 *data) |
782 | { |
783 | struct ims_pcu_flash_fmt *fragment; |
784 | int error; |
785 | |
786 | fragment = (void *)&pcu->cmd_buf[1]; |
787 | put_unaligned_le32(val: addr, p: &fragment->addr); |
788 | fragment->len = len; |
789 | |
790 | error = ims_pcu_execute_bl_command(pcu, READ_APP, NULL, 5, |
791 | IMS_PCU_CMD_RESPONSE_TIMEOUT); |
792 | if (error) { |
793 | dev_err(pcu->dev, |
794 | "Failed to retrieve block at 0x%08x, len %d, error: %d\n" , |
795 | addr, len, error); |
796 | return error; |
797 | } |
798 | |
799 | fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET]; |
800 | if (get_unaligned_le32(p: &fragment->addr) != addr || |
801 | fragment->len != len) { |
802 | dev_err(pcu->dev, |
803 | "Wrong block when retrieving 0x%08x (0x%08x), len %d (%d)\n" , |
804 | addr, get_unaligned_le32(&fragment->addr), |
805 | len, fragment->len); |
806 | return -EINVAL; |
807 | } |
808 | |
809 | if (memcmp(p: fragment->data, q: data, size: len)) { |
810 | dev_err(pcu->dev, |
811 | "Mismatch in block at 0x%08x, len %d\n" , |
812 | addr, len); |
813 | return -EINVAL; |
814 | } |
815 | |
816 | return 0; |
817 | } |
818 | |
819 | static int ims_pcu_flash_firmware(struct ims_pcu *pcu, |
820 | const struct firmware *fw, |
821 | unsigned int n_fw_records) |
822 | { |
823 | const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data; |
824 | struct ims_pcu_flash_fmt *fragment; |
825 | unsigned int count = 0; |
826 | u32 addr; |
827 | u8 len; |
828 | int error; |
829 | |
830 | error = ims_pcu_execute_bl_command(pcu, ERASE_APP, NULL, 0, 2000); |
831 | if (error) { |
832 | dev_err(pcu->dev, |
833 | "Failed to erase application image, error: %d\n" , |
834 | error); |
835 | return error; |
836 | } |
837 | |
838 | while (rec) { |
839 | /* |
840 | * The firmware format is messed up for some reason. |
841 | * The address twice that of what is needed for some |
842 | * reason and we end up overwriting half of the data |
843 | * with the next record. |
844 | */ |
845 | addr = be32_to_cpu(rec->addr) / 2; |
846 | len = be16_to_cpu(rec->len); |
847 | |
848 | fragment = (void *)&pcu->cmd_buf[1]; |
849 | put_unaligned_le32(val: addr, p: &fragment->addr); |
850 | fragment->len = len; |
851 | memcpy(fragment->data, rec->data, len); |
852 | |
853 | error = ims_pcu_execute_bl_command(pcu, PROGRAM_DEVICE, |
854 | NULL, len + 5, |
855 | IMS_PCU_CMD_RESPONSE_TIMEOUT); |
856 | if (error) { |
857 | dev_err(pcu->dev, |
858 | "Failed to write block at 0x%08x, len %d, error: %d\n" , |
859 | addr, len, error); |
860 | return error; |
861 | } |
862 | |
863 | if (addr >= pcu->fw_start_addr && addr < pcu->fw_end_addr) { |
864 | error = ims_pcu_verify_block(pcu, addr, len, data: rec->data); |
865 | if (error) |
866 | return error; |
867 | } |
868 | |
869 | count++; |
870 | pcu->update_firmware_status = (count * 100) / n_fw_records; |
871 | |
872 | rec = ihex_next_binrec(rec); |
873 | } |
874 | |
875 | error = ims_pcu_execute_bl_command(pcu, PROGRAM_COMPLETE, |
876 | NULL, 0, 2000); |
877 | if (error) |
878 | dev_err(pcu->dev, |
879 | "Failed to send PROGRAM_COMPLETE, error: %d\n" , |
880 | error); |
881 | |
882 | return 0; |
883 | } |
884 | |
885 | static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu, |
886 | const struct firmware *fw) |
887 | { |
888 | unsigned int n_fw_records; |
889 | int retval; |
890 | |
891 | dev_info(pcu->dev, "Updating firmware %s, size: %zu\n" , |
892 | IMS_PCU_FIRMWARE_NAME, fw->size); |
893 | |
894 | n_fw_records = ims_pcu_count_fw_records(fw); |
895 | |
896 | retval = ims_pcu_flash_firmware(pcu, fw, n_fw_records); |
897 | if (retval) |
898 | goto out; |
899 | |
900 | retval = ims_pcu_execute_bl_command(pcu, LAUNCH_APP, NULL, 0, 0); |
901 | if (retval) |
902 | dev_err(pcu->dev, |
903 | "Failed to start application image, error: %d\n" , |
904 | retval); |
905 | |
906 | out: |
907 | pcu->update_firmware_status = retval; |
908 | sysfs_notify(kobj: &pcu->dev->kobj, NULL, attr: "update_firmware_status" ); |
909 | return retval; |
910 | } |
911 | |
912 | static void ims_pcu_process_async_firmware(const struct firmware *fw, |
913 | void *context) |
914 | { |
915 | struct ims_pcu *pcu = context; |
916 | int error; |
917 | |
918 | if (!fw) { |
919 | dev_err(pcu->dev, "Failed to get firmware %s\n" , |
920 | IMS_PCU_FIRMWARE_NAME); |
921 | goto out; |
922 | } |
923 | |
924 | error = ihex_validate_fw(fw); |
925 | if (error) { |
926 | dev_err(pcu->dev, "Firmware %s is invalid\n" , |
927 | IMS_PCU_FIRMWARE_NAME); |
928 | goto out; |
929 | } |
930 | |
931 | mutex_lock(&pcu->cmd_mutex); |
932 | ims_pcu_handle_firmware_update(pcu, fw); |
933 | mutex_unlock(lock: &pcu->cmd_mutex); |
934 | |
935 | release_firmware(fw); |
936 | |
937 | out: |
938 | complete(&pcu->async_firmware_done); |
939 | } |
940 | |
941 | /********************************************************************* |
942 | * Backlight LED device support * |
943 | *********************************************************************/ |
944 | |
945 | #define IMS_PCU_MAX_BRIGHTNESS 31998 |
946 | |
947 | static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev, |
948 | enum led_brightness value) |
949 | { |
950 | struct ims_pcu_backlight *backlight = |
951 | container_of(cdev, struct ims_pcu_backlight, cdev); |
952 | struct ims_pcu *pcu = |
953 | container_of(backlight, struct ims_pcu, backlight); |
954 | __le16 br_val = cpu_to_le16(value); |
955 | int error; |
956 | |
957 | mutex_lock(&pcu->cmd_mutex); |
958 | |
959 | error = ims_pcu_execute_command(pcu, SET_BRIGHTNESS, |
960 | &br_val, sizeof(br_val)); |
961 | if (error && error != -ENODEV) |
962 | dev_warn(pcu->dev, |
963 | "Failed to set desired brightness %u, error: %d\n" , |
964 | value, error); |
965 | |
966 | mutex_unlock(lock: &pcu->cmd_mutex); |
967 | |
968 | return error; |
969 | } |
970 | |
971 | static enum led_brightness |
972 | ims_pcu_backlight_get_brightness(struct led_classdev *cdev) |
973 | { |
974 | struct ims_pcu_backlight *backlight = |
975 | container_of(cdev, struct ims_pcu_backlight, cdev); |
976 | struct ims_pcu *pcu = |
977 | container_of(backlight, struct ims_pcu, backlight); |
978 | int brightness; |
979 | int error; |
980 | |
981 | mutex_lock(&pcu->cmd_mutex); |
982 | |
983 | error = ims_pcu_execute_query(pcu, GET_BRIGHTNESS); |
984 | if (error) { |
985 | dev_warn(pcu->dev, |
986 | "Failed to get current brightness, error: %d\n" , |
987 | error); |
988 | /* Assume the LED is OFF */ |
989 | brightness = LED_OFF; |
990 | } else { |
991 | brightness = |
992 | get_unaligned_le16(p: &pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); |
993 | } |
994 | |
995 | mutex_unlock(lock: &pcu->cmd_mutex); |
996 | |
997 | return brightness; |
998 | } |
999 | |
1000 | static int ims_pcu_setup_backlight(struct ims_pcu *pcu) |
1001 | { |
1002 | struct ims_pcu_backlight *backlight = &pcu->backlight; |
1003 | int error; |
1004 | |
1005 | snprintf(buf: backlight->name, size: sizeof(backlight->name), |
1006 | fmt: "pcu%d::kbd_backlight" , pcu->device_no); |
1007 | |
1008 | backlight->cdev.name = backlight->name; |
1009 | backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS; |
1010 | backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness; |
1011 | backlight->cdev.brightness_set_blocking = |
1012 | ims_pcu_backlight_set_brightness; |
1013 | |
1014 | error = led_classdev_register(parent: pcu->dev, led_cdev: &backlight->cdev); |
1015 | if (error) { |
1016 | dev_err(pcu->dev, |
1017 | "Failed to register backlight LED device, error: %d\n" , |
1018 | error); |
1019 | return error; |
1020 | } |
1021 | |
1022 | return 0; |
1023 | } |
1024 | |
1025 | static void ims_pcu_destroy_backlight(struct ims_pcu *pcu) |
1026 | { |
1027 | struct ims_pcu_backlight *backlight = &pcu->backlight; |
1028 | |
1029 | led_classdev_unregister(led_cdev: &backlight->cdev); |
1030 | } |
1031 | |
1032 | |
1033 | /********************************************************************* |
1034 | * Sysfs attributes handling * |
1035 | *********************************************************************/ |
1036 | |
1037 | struct ims_pcu_attribute { |
1038 | struct device_attribute dattr; |
1039 | size_t field_offset; |
1040 | int field_length; |
1041 | }; |
1042 | |
1043 | static ssize_t ims_pcu_attribute_show(struct device *dev, |
1044 | struct device_attribute *dattr, |
1045 | char *buf) |
1046 | { |
1047 | struct usb_interface *intf = to_usb_interface(dev); |
1048 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1049 | struct ims_pcu_attribute *attr = |
1050 | container_of(dattr, struct ims_pcu_attribute, dattr); |
1051 | char *field = (char *)pcu + attr->field_offset; |
1052 | |
1053 | return scnprintf(buf, PAGE_SIZE, fmt: "%.*s\n" , attr->field_length, field); |
1054 | } |
1055 | |
1056 | static ssize_t ims_pcu_attribute_store(struct device *dev, |
1057 | struct device_attribute *dattr, |
1058 | const char *buf, size_t count) |
1059 | { |
1060 | |
1061 | struct usb_interface *intf = to_usb_interface(dev); |
1062 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1063 | struct ims_pcu_attribute *attr = |
1064 | container_of(dattr, struct ims_pcu_attribute, dattr); |
1065 | char *field = (char *)pcu + attr->field_offset; |
1066 | size_t data_len; |
1067 | int error; |
1068 | |
1069 | if (count > attr->field_length) |
1070 | return -EINVAL; |
1071 | |
1072 | data_len = strnlen(p: buf, maxlen: attr->field_length); |
1073 | if (data_len > attr->field_length) |
1074 | return -EINVAL; |
1075 | |
1076 | error = mutex_lock_interruptible(&pcu->cmd_mutex); |
1077 | if (error) |
1078 | return error; |
1079 | |
1080 | memset(field, 0, attr->field_length); |
1081 | memcpy(field, buf, data_len); |
1082 | |
1083 | error = ims_pcu_set_info(pcu); |
1084 | |
1085 | /* |
1086 | * Even if update failed, let's fetch the info again as we just |
1087 | * clobbered one of the fields. |
1088 | */ |
1089 | ims_pcu_get_info(pcu); |
1090 | |
1091 | mutex_unlock(lock: &pcu->cmd_mutex); |
1092 | |
1093 | return error < 0 ? error : count; |
1094 | } |
1095 | |
1096 | #define IMS_PCU_ATTR(_field, _mode) \ |
1097 | struct ims_pcu_attribute ims_pcu_attr_##_field = { \ |
1098 | .dattr = __ATTR(_field, _mode, \ |
1099 | ims_pcu_attribute_show, \ |
1100 | ims_pcu_attribute_store), \ |
1101 | .field_offset = offsetof(struct ims_pcu, _field), \ |
1102 | .field_length = sizeof(((struct ims_pcu *)NULL)->_field), \ |
1103 | } |
1104 | |
1105 | #define IMS_PCU_RO_ATTR(_field) \ |
1106 | IMS_PCU_ATTR(_field, S_IRUGO) |
1107 | #define IMS_PCU_RW_ATTR(_field) \ |
1108 | IMS_PCU_ATTR(_field, S_IRUGO | S_IWUSR) |
1109 | |
1110 | static IMS_PCU_RW_ATTR(part_number); |
1111 | static IMS_PCU_RW_ATTR(serial_number); |
1112 | static IMS_PCU_RW_ATTR(date_of_manufacturing); |
1113 | |
1114 | static IMS_PCU_RO_ATTR(fw_version); |
1115 | static IMS_PCU_RO_ATTR(bl_version); |
1116 | static IMS_PCU_RO_ATTR(reset_reason); |
1117 | |
1118 | static ssize_t ims_pcu_reset_device(struct device *dev, |
1119 | struct device_attribute *dattr, |
1120 | const char *buf, size_t count) |
1121 | { |
1122 | static const u8 reset_byte = 1; |
1123 | struct usb_interface *intf = to_usb_interface(dev); |
1124 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1125 | int value; |
1126 | int error; |
1127 | |
1128 | error = kstrtoint(s: buf, base: 0, res: &value); |
1129 | if (error) |
1130 | return error; |
1131 | |
1132 | if (value != 1) |
1133 | return -EINVAL; |
1134 | |
1135 | dev_info(pcu->dev, "Attempting to reset device\n" ); |
1136 | |
1137 | error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1); |
1138 | if (error) { |
1139 | dev_info(pcu->dev, |
1140 | "Failed to reset device, error: %d\n" , |
1141 | error); |
1142 | return error; |
1143 | } |
1144 | |
1145 | return count; |
1146 | } |
1147 | |
1148 | static DEVICE_ATTR(reset_device, S_IWUSR, NULL, ims_pcu_reset_device); |
1149 | |
1150 | static ssize_t ims_pcu_update_firmware_store(struct device *dev, |
1151 | struct device_attribute *dattr, |
1152 | const char *buf, size_t count) |
1153 | { |
1154 | struct usb_interface *intf = to_usb_interface(dev); |
1155 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1156 | const struct firmware *fw = NULL; |
1157 | int value; |
1158 | int error; |
1159 | |
1160 | error = kstrtoint(s: buf, base: 0, res: &value); |
1161 | if (error) |
1162 | return error; |
1163 | |
1164 | if (value != 1) |
1165 | return -EINVAL; |
1166 | |
1167 | error = mutex_lock_interruptible(&pcu->cmd_mutex); |
1168 | if (error) |
1169 | return error; |
1170 | |
1171 | error = request_ihex_firmware(fw: &fw, IMS_PCU_FIRMWARE_NAME, dev: pcu->dev); |
1172 | if (error) { |
1173 | dev_err(pcu->dev, "Failed to request firmware %s, error: %d\n" , |
1174 | IMS_PCU_FIRMWARE_NAME, error); |
1175 | goto out; |
1176 | } |
1177 | |
1178 | /* |
1179 | * If we are already in bootloader mode we can proceed with |
1180 | * flashing the firmware. |
1181 | * |
1182 | * If we are in application mode, then we need to switch into |
1183 | * bootloader mode, which will cause the device to disconnect |
1184 | * and reconnect as different device. |
1185 | */ |
1186 | if (pcu->bootloader_mode) |
1187 | error = ims_pcu_handle_firmware_update(pcu, fw); |
1188 | else |
1189 | error = ims_pcu_switch_to_bootloader(pcu); |
1190 | |
1191 | release_firmware(fw); |
1192 | |
1193 | out: |
1194 | mutex_unlock(lock: &pcu->cmd_mutex); |
1195 | return error ?: count; |
1196 | } |
1197 | |
1198 | static DEVICE_ATTR(update_firmware, S_IWUSR, |
1199 | NULL, ims_pcu_update_firmware_store); |
1200 | |
1201 | static ssize_t |
1202 | ims_pcu_update_firmware_status_show(struct device *dev, |
1203 | struct device_attribute *dattr, |
1204 | char *buf) |
1205 | { |
1206 | struct usb_interface *intf = to_usb_interface(dev); |
1207 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1208 | |
1209 | return scnprintf(buf, PAGE_SIZE, fmt: "%d\n" , pcu->update_firmware_status); |
1210 | } |
1211 | |
1212 | static DEVICE_ATTR(update_firmware_status, S_IRUGO, |
1213 | ims_pcu_update_firmware_status_show, NULL); |
1214 | |
1215 | static struct attribute *ims_pcu_attrs[] = { |
1216 | &ims_pcu_attr_part_number.dattr.attr, |
1217 | &ims_pcu_attr_serial_number.dattr.attr, |
1218 | &ims_pcu_attr_date_of_manufacturing.dattr.attr, |
1219 | &ims_pcu_attr_fw_version.dattr.attr, |
1220 | &ims_pcu_attr_bl_version.dattr.attr, |
1221 | &ims_pcu_attr_reset_reason.dattr.attr, |
1222 | &dev_attr_reset_device.attr, |
1223 | &dev_attr_update_firmware.attr, |
1224 | &dev_attr_update_firmware_status.attr, |
1225 | NULL |
1226 | }; |
1227 | |
1228 | static umode_t ims_pcu_is_attr_visible(struct kobject *kobj, |
1229 | struct attribute *attr, int n) |
1230 | { |
1231 | struct device *dev = kobj_to_dev(kobj); |
1232 | struct usb_interface *intf = to_usb_interface(dev); |
1233 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1234 | umode_t mode = attr->mode; |
1235 | |
1236 | if (pcu->bootloader_mode) { |
1237 | if (attr != &dev_attr_update_firmware_status.attr && |
1238 | attr != &dev_attr_update_firmware.attr && |
1239 | attr != &dev_attr_reset_device.attr) { |
1240 | mode = 0; |
1241 | } |
1242 | } else { |
1243 | if (attr == &dev_attr_update_firmware_status.attr) |
1244 | mode = 0; |
1245 | } |
1246 | |
1247 | return mode; |
1248 | } |
1249 | |
1250 | static const struct attribute_group ims_pcu_attr_group = { |
1251 | .is_visible = ims_pcu_is_attr_visible, |
1252 | .attrs = ims_pcu_attrs, |
1253 | }; |
1254 | |
1255 | /* Support for a separate OFN attribute group */ |
1256 | |
1257 | #define OFN_REG_RESULT_OFFSET 2 |
1258 | |
1259 | static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data) |
1260 | { |
1261 | int error; |
1262 | s16 result; |
1263 | |
1264 | error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG, |
1265 | &addr, sizeof(addr)); |
1266 | if (error) |
1267 | return error; |
1268 | |
1269 | result = (s16)get_unaligned_le16(p: pcu->cmd_buf + OFN_REG_RESULT_OFFSET); |
1270 | if (result < 0) |
1271 | return -EIO; |
1272 | |
1273 | /* We only need LSB */ |
1274 | *data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET]; |
1275 | return 0; |
1276 | } |
1277 | |
1278 | static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data) |
1279 | { |
1280 | u8 buffer[] = { addr, data }; |
1281 | int error; |
1282 | s16 result; |
1283 | |
1284 | error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG, |
1285 | &buffer, sizeof(buffer)); |
1286 | if (error) |
1287 | return error; |
1288 | |
1289 | result = (s16)get_unaligned_le16(p: pcu->cmd_buf + OFN_REG_RESULT_OFFSET); |
1290 | if (result < 0) |
1291 | return -EIO; |
1292 | |
1293 | return 0; |
1294 | } |
1295 | |
1296 | static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev, |
1297 | struct device_attribute *dattr, |
1298 | char *buf) |
1299 | { |
1300 | struct usb_interface *intf = to_usb_interface(dev); |
1301 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1302 | int error; |
1303 | u8 data; |
1304 | |
1305 | mutex_lock(&pcu->cmd_mutex); |
1306 | error = ims_pcu_read_ofn_config(pcu, addr: pcu->ofn_reg_addr, data: &data); |
1307 | mutex_unlock(lock: &pcu->cmd_mutex); |
1308 | |
1309 | if (error) |
1310 | return error; |
1311 | |
1312 | return scnprintf(buf, PAGE_SIZE, fmt: "%x\n" , data); |
1313 | } |
1314 | |
1315 | static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev, |
1316 | struct device_attribute *dattr, |
1317 | const char *buf, size_t count) |
1318 | { |
1319 | struct usb_interface *intf = to_usb_interface(dev); |
1320 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1321 | int error; |
1322 | u8 value; |
1323 | |
1324 | error = kstrtou8(s: buf, base: 0, res: &value); |
1325 | if (error) |
1326 | return error; |
1327 | |
1328 | mutex_lock(&pcu->cmd_mutex); |
1329 | error = ims_pcu_write_ofn_config(pcu, addr: pcu->ofn_reg_addr, data: value); |
1330 | mutex_unlock(lock: &pcu->cmd_mutex); |
1331 | |
1332 | return error ?: count; |
1333 | } |
1334 | |
1335 | static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR, |
1336 | ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store); |
1337 | |
1338 | static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev, |
1339 | struct device_attribute *dattr, |
1340 | char *buf) |
1341 | { |
1342 | struct usb_interface *intf = to_usb_interface(dev); |
1343 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1344 | int error; |
1345 | |
1346 | mutex_lock(&pcu->cmd_mutex); |
1347 | error = scnprintf(buf, PAGE_SIZE, fmt: "%x\n" , pcu->ofn_reg_addr); |
1348 | mutex_unlock(lock: &pcu->cmd_mutex); |
1349 | |
1350 | return error; |
1351 | } |
1352 | |
1353 | static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev, |
1354 | struct device_attribute *dattr, |
1355 | const char *buf, size_t count) |
1356 | { |
1357 | struct usb_interface *intf = to_usb_interface(dev); |
1358 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1359 | int error; |
1360 | u8 value; |
1361 | |
1362 | error = kstrtou8(s: buf, base: 0, res: &value); |
1363 | if (error) |
1364 | return error; |
1365 | |
1366 | mutex_lock(&pcu->cmd_mutex); |
1367 | pcu->ofn_reg_addr = value; |
1368 | mutex_unlock(lock: &pcu->cmd_mutex); |
1369 | |
1370 | return count; |
1371 | } |
1372 | |
1373 | static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR, |
1374 | ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store); |
1375 | |
1376 | struct ims_pcu_ofn_bit_attribute { |
1377 | struct device_attribute dattr; |
1378 | u8 addr; |
1379 | u8 nr; |
1380 | }; |
1381 | |
1382 | static ssize_t ims_pcu_ofn_bit_show(struct device *dev, |
1383 | struct device_attribute *dattr, |
1384 | char *buf) |
1385 | { |
1386 | struct usb_interface *intf = to_usb_interface(dev); |
1387 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1388 | struct ims_pcu_ofn_bit_attribute *attr = |
1389 | container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); |
1390 | int error; |
1391 | u8 data; |
1392 | |
1393 | mutex_lock(&pcu->cmd_mutex); |
1394 | error = ims_pcu_read_ofn_config(pcu, addr: attr->addr, data: &data); |
1395 | mutex_unlock(lock: &pcu->cmd_mutex); |
1396 | |
1397 | if (error) |
1398 | return error; |
1399 | |
1400 | return scnprintf(buf, PAGE_SIZE, fmt: "%d\n" , !!(data & (1 << attr->nr))); |
1401 | } |
1402 | |
1403 | static ssize_t ims_pcu_ofn_bit_store(struct device *dev, |
1404 | struct device_attribute *dattr, |
1405 | const char *buf, size_t count) |
1406 | { |
1407 | struct usb_interface *intf = to_usb_interface(dev); |
1408 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
1409 | struct ims_pcu_ofn_bit_attribute *attr = |
1410 | container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); |
1411 | int error; |
1412 | int value; |
1413 | u8 data; |
1414 | |
1415 | error = kstrtoint(s: buf, base: 0, res: &value); |
1416 | if (error) |
1417 | return error; |
1418 | |
1419 | if (value > 1) |
1420 | return -EINVAL; |
1421 | |
1422 | mutex_lock(&pcu->cmd_mutex); |
1423 | |
1424 | error = ims_pcu_read_ofn_config(pcu, addr: attr->addr, data: &data); |
1425 | if (!error) { |
1426 | if (value) |
1427 | data |= 1U << attr->nr; |
1428 | else |
1429 | data &= ~(1U << attr->nr); |
1430 | |
1431 | error = ims_pcu_write_ofn_config(pcu, addr: attr->addr, data); |
1432 | } |
1433 | |
1434 | mutex_unlock(lock: &pcu->cmd_mutex); |
1435 | |
1436 | return error ?: count; |
1437 | } |
1438 | |
1439 | #define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \ |
1440 | struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = { \ |
1441 | .dattr = __ATTR(_field, S_IWUSR | S_IRUGO, \ |
1442 | ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store), \ |
1443 | .addr = _addr, \ |
1444 | .nr = _nr, \ |
1445 | } |
1446 | |
1447 | static IMS_PCU_OFN_BIT_ATTR(engine_enable, 0x60, 7); |
1448 | static IMS_PCU_OFN_BIT_ATTR(speed_enable, 0x60, 6); |
1449 | static IMS_PCU_OFN_BIT_ATTR(assert_enable, 0x60, 5); |
1450 | static IMS_PCU_OFN_BIT_ATTR(xyquant_enable, 0x60, 4); |
1451 | static IMS_PCU_OFN_BIT_ATTR(xyscale_enable, 0x60, 1); |
1452 | |
1453 | static IMS_PCU_OFN_BIT_ATTR(scale_x2, 0x63, 6); |
1454 | static IMS_PCU_OFN_BIT_ATTR(scale_y2, 0x63, 7); |
1455 | |
1456 | static struct attribute *ims_pcu_ofn_attrs[] = { |
1457 | &dev_attr_reg_data.attr, |
1458 | &dev_attr_reg_addr.attr, |
1459 | &ims_pcu_ofn_attr_engine_enable.dattr.attr, |
1460 | &ims_pcu_ofn_attr_speed_enable.dattr.attr, |
1461 | &ims_pcu_ofn_attr_assert_enable.dattr.attr, |
1462 | &ims_pcu_ofn_attr_xyquant_enable.dattr.attr, |
1463 | &ims_pcu_ofn_attr_xyscale_enable.dattr.attr, |
1464 | &ims_pcu_ofn_attr_scale_x2.dattr.attr, |
1465 | &ims_pcu_ofn_attr_scale_y2.dattr.attr, |
1466 | NULL |
1467 | }; |
1468 | |
1469 | static const struct attribute_group ims_pcu_ofn_attr_group = { |
1470 | .name = "ofn" , |
1471 | .attrs = ims_pcu_ofn_attrs, |
1472 | }; |
1473 | |
1474 | static void ims_pcu_irq(struct urb *urb) |
1475 | { |
1476 | struct ims_pcu *pcu = urb->context; |
1477 | int retval, status; |
1478 | |
1479 | status = urb->status; |
1480 | |
1481 | switch (status) { |
1482 | case 0: |
1483 | /* success */ |
1484 | break; |
1485 | case -ECONNRESET: |
1486 | case -ENOENT: |
1487 | case -ESHUTDOWN: |
1488 | /* this urb is terminated, clean up */ |
1489 | dev_dbg(pcu->dev, "%s - urb shutting down with status: %d\n" , |
1490 | __func__, status); |
1491 | return; |
1492 | default: |
1493 | dev_dbg(pcu->dev, "%s - nonzero urb status received: %d\n" , |
1494 | __func__, status); |
1495 | goto exit; |
1496 | } |
1497 | |
1498 | dev_dbg(pcu->dev, "%s: received %d: %*ph\n" , __func__, |
1499 | urb->actual_length, urb->actual_length, pcu->urb_in_buf); |
1500 | |
1501 | if (urb == pcu->urb_in) |
1502 | ims_pcu_process_data(pcu, urb); |
1503 | |
1504 | exit: |
1505 | retval = usb_submit_urb(urb, GFP_ATOMIC); |
1506 | if (retval && retval != -ENODEV) |
1507 | dev_err(pcu->dev, "%s - usb_submit_urb failed with result %d\n" , |
1508 | __func__, retval); |
1509 | } |
1510 | |
1511 | static int ims_pcu_buffers_alloc(struct ims_pcu *pcu) |
1512 | { |
1513 | int error; |
1514 | |
1515 | pcu->urb_in_buf = usb_alloc_coherent(dev: pcu->udev, size: pcu->max_in_size, |
1516 | GFP_KERNEL, dma: &pcu->read_dma); |
1517 | if (!pcu->urb_in_buf) { |
1518 | dev_err(pcu->dev, |
1519 | "Failed to allocate memory for read buffer\n" ); |
1520 | return -ENOMEM; |
1521 | } |
1522 | |
1523 | pcu->urb_in = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
1524 | if (!pcu->urb_in) { |
1525 | dev_err(pcu->dev, "Failed to allocate input URB\n" ); |
1526 | error = -ENOMEM; |
1527 | goto err_free_urb_in_buf; |
1528 | } |
1529 | |
1530 | pcu->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
1531 | pcu->urb_in->transfer_dma = pcu->read_dma; |
1532 | |
1533 | usb_fill_bulk_urb(urb: pcu->urb_in, dev: pcu->udev, |
1534 | usb_rcvbulkpipe(pcu->udev, |
1535 | pcu->ep_in->bEndpointAddress), |
1536 | transfer_buffer: pcu->urb_in_buf, buffer_length: pcu->max_in_size, |
1537 | complete_fn: ims_pcu_irq, context: pcu); |
1538 | |
1539 | /* |
1540 | * We are using usb_bulk_msg() for sending so there is no point |
1541 | * in allocating memory with usb_alloc_coherent(). |
1542 | */ |
1543 | pcu->urb_out_buf = kmalloc(size: pcu->max_out_size, GFP_KERNEL); |
1544 | if (!pcu->urb_out_buf) { |
1545 | dev_err(pcu->dev, "Failed to allocate memory for write buffer\n" ); |
1546 | error = -ENOMEM; |
1547 | goto err_free_in_urb; |
1548 | } |
1549 | |
1550 | pcu->urb_ctrl_buf = usb_alloc_coherent(dev: pcu->udev, size: pcu->max_ctrl_size, |
1551 | GFP_KERNEL, dma: &pcu->ctrl_dma); |
1552 | if (!pcu->urb_ctrl_buf) { |
1553 | dev_err(pcu->dev, |
1554 | "Failed to allocate memory for read buffer\n" ); |
1555 | error = -ENOMEM; |
1556 | goto err_free_urb_out_buf; |
1557 | } |
1558 | |
1559 | pcu->urb_ctrl = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
1560 | if (!pcu->urb_ctrl) { |
1561 | dev_err(pcu->dev, "Failed to allocate input URB\n" ); |
1562 | error = -ENOMEM; |
1563 | goto err_free_urb_ctrl_buf; |
1564 | } |
1565 | |
1566 | pcu->urb_ctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
1567 | pcu->urb_ctrl->transfer_dma = pcu->ctrl_dma; |
1568 | |
1569 | usb_fill_int_urb(urb: pcu->urb_ctrl, dev: pcu->udev, |
1570 | usb_rcvintpipe(pcu->udev, |
1571 | pcu->ep_ctrl->bEndpointAddress), |
1572 | transfer_buffer: pcu->urb_ctrl_buf, buffer_length: pcu->max_ctrl_size, |
1573 | complete_fn: ims_pcu_irq, context: pcu, interval: pcu->ep_ctrl->bInterval); |
1574 | |
1575 | return 0; |
1576 | |
1577 | err_free_urb_ctrl_buf: |
1578 | usb_free_coherent(dev: pcu->udev, size: pcu->max_ctrl_size, |
1579 | addr: pcu->urb_ctrl_buf, dma: pcu->ctrl_dma); |
1580 | err_free_urb_out_buf: |
1581 | kfree(objp: pcu->urb_out_buf); |
1582 | err_free_in_urb: |
1583 | usb_free_urb(urb: pcu->urb_in); |
1584 | err_free_urb_in_buf: |
1585 | usb_free_coherent(dev: pcu->udev, size: pcu->max_in_size, |
1586 | addr: pcu->urb_in_buf, dma: pcu->read_dma); |
1587 | return error; |
1588 | } |
1589 | |
1590 | static void ims_pcu_buffers_free(struct ims_pcu *pcu) |
1591 | { |
1592 | usb_kill_urb(urb: pcu->urb_in); |
1593 | usb_free_urb(urb: pcu->urb_in); |
1594 | |
1595 | usb_free_coherent(dev: pcu->udev, size: pcu->max_out_size, |
1596 | addr: pcu->urb_in_buf, dma: pcu->read_dma); |
1597 | |
1598 | kfree(objp: pcu->urb_out_buf); |
1599 | |
1600 | usb_kill_urb(urb: pcu->urb_ctrl); |
1601 | usb_free_urb(urb: pcu->urb_ctrl); |
1602 | |
1603 | usb_free_coherent(dev: pcu->udev, size: pcu->max_ctrl_size, |
1604 | addr: pcu->urb_ctrl_buf, dma: pcu->ctrl_dma); |
1605 | } |
1606 | |
1607 | static const struct usb_cdc_union_desc * |
1608 | ims_pcu_get_cdc_union_desc(struct usb_interface *intf) |
1609 | { |
1610 | const void *buf = intf->altsetting->extra; |
1611 | size_t buflen = intf->altsetting->extralen; |
1612 | struct usb_cdc_union_desc *union_desc; |
1613 | |
1614 | if (!buf) { |
1615 | dev_err(&intf->dev, "Missing descriptor data\n" ); |
1616 | return NULL; |
1617 | } |
1618 | |
1619 | if (!buflen) { |
1620 | dev_err(&intf->dev, "Zero length descriptor\n" ); |
1621 | return NULL; |
1622 | } |
1623 | |
1624 | while (buflen >= sizeof(*union_desc)) { |
1625 | union_desc = (struct usb_cdc_union_desc *)buf; |
1626 | |
1627 | if (union_desc->bLength > buflen) { |
1628 | dev_err(&intf->dev, "Too large descriptor\n" ); |
1629 | return NULL; |
1630 | } |
1631 | |
1632 | if (union_desc->bDescriptorType == USB_DT_CS_INTERFACE && |
1633 | union_desc->bDescriptorSubType == USB_CDC_UNION_TYPE) { |
1634 | dev_dbg(&intf->dev, "Found union header\n" ); |
1635 | |
1636 | if (union_desc->bLength >= sizeof(*union_desc)) |
1637 | return union_desc; |
1638 | |
1639 | dev_err(&intf->dev, |
1640 | "Union descriptor too short (%d vs %zd)\n" , |
1641 | union_desc->bLength, sizeof(*union_desc)); |
1642 | return NULL; |
1643 | } |
1644 | |
1645 | buflen -= union_desc->bLength; |
1646 | buf += union_desc->bLength; |
1647 | } |
1648 | |
1649 | dev_err(&intf->dev, "Missing CDC union descriptor\n" ); |
1650 | return NULL; |
1651 | } |
1652 | |
1653 | static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pcu) |
1654 | { |
1655 | const struct usb_cdc_union_desc *union_desc; |
1656 | struct usb_host_interface *alt; |
1657 | |
1658 | union_desc = ims_pcu_get_cdc_union_desc(intf); |
1659 | if (!union_desc) |
1660 | return -EINVAL; |
1661 | |
1662 | pcu->ctrl_intf = usb_ifnum_to_if(dev: pcu->udev, |
1663 | ifnum: union_desc->bMasterInterface0); |
1664 | if (!pcu->ctrl_intf) |
1665 | return -EINVAL; |
1666 | |
1667 | alt = pcu->ctrl_intf->cur_altsetting; |
1668 | |
1669 | if (alt->desc.bNumEndpoints < 1) |
1670 | return -ENODEV; |
1671 | |
1672 | pcu->ep_ctrl = &alt->endpoint[0].desc; |
1673 | pcu->max_ctrl_size = usb_endpoint_maxp(epd: pcu->ep_ctrl); |
1674 | |
1675 | pcu->data_intf = usb_ifnum_to_if(dev: pcu->udev, |
1676 | ifnum: union_desc->bSlaveInterface0); |
1677 | if (!pcu->data_intf) |
1678 | return -EINVAL; |
1679 | |
1680 | alt = pcu->data_intf->cur_altsetting; |
1681 | if (alt->desc.bNumEndpoints != 2) { |
1682 | dev_err(pcu->dev, |
1683 | "Incorrect number of endpoints on data interface (%d)\n" , |
1684 | alt->desc.bNumEndpoints); |
1685 | return -EINVAL; |
1686 | } |
1687 | |
1688 | pcu->ep_out = &alt->endpoint[0].desc; |
1689 | if (!usb_endpoint_is_bulk_out(epd: pcu->ep_out)) { |
1690 | dev_err(pcu->dev, |
1691 | "First endpoint on data interface is not BULK OUT\n" ); |
1692 | return -EINVAL; |
1693 | } |
1694 | |
1695 | pcu->max_out_size = usb_endpoint_maxp(epd: pcu->ep_out); |
1696 | if (pcu->max_out_size < 8) { |
1697 | dev_err(pcu->dev, |
1698 | "Max OUT packet size is too small (%zd)\n" , |
1699 | pcu->max_out_size); |
1700 | return -EINVAL; |
1701 | } |
1702 | |
1703 | pcu->ep_in = &alt->endpoint[1].desc; |
1704 | if (!usb_endpoint_is_bulk_in(epd: pcu->ep_in)) { |
1705 | dev_err(pcu->dev, |
1706 | "Second endpoint on data interface is not BULK IN\n" ); |
1707 | return -EINVAL; |
1708 | } |
1709 | |
1710 | pcu->max_in_size = usb_endpoint_maxp(epd: pcu->ep_in); |
1711 | if (pcu->max_in_size < 8) { |
1712 | dev_err(pcu->dev, |
1713 | "Max IN packet size is too small (%zd)\n" , |
1714 | pcu->max_in_size); |
1715 | return -EINVAL; |
1716 | } |
1717 | |
1718 | return 0; |
1719 | } |
1720 | |
1721 | static int ims_pcu_start_io(struct ims_pcu *pcu) |
1722 | { |
1723 | int error; |
1724 | |
1725 | error = usb_submit_urb(urb: pcu->urb_ctrl, GFP_KERNEL); |
1726 | if (error) { |
1727 | dev_err(pcu->dev, |
1728 | "Failed to start control IO - usb_submit_urb failed with result: %d\n" , |
1729 | error); |
1730 | return -EIO; |
1731 | } |
1732 | |
1733 | error = usb_submit_urb(urb: pcu->urb_in, GFP_KERNEL); |
1734 | if (error) { |
1735 | dev_err(pcu->dev, |
1736 | "Failed to start IO - usb_submit_urb failed with result: %d\n" , |
1737 | error); |
1738 | usb_kill_urb(urb: pcu->urb_ctrl); |
1739 | return -EIO; |
1740 | } |
1741 | |
1742 | return 0; |
1743 | } |
1744 | |
1745 | static void ims_pcu_stop_io(struct ims_pcu *pcu) |
1746 | { |
1747 | usb_kill_urb(urb: pcu->urb_in); |
1748 | usb_kill_urb(urb: pcu->urb_ctrl); |
1749 | } |
1750 | |
1751 | static int ims_pcu_line_setup(struct ims_pcu *pcu) |
1752 | { |
1753 | struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting; |
1754 | struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf; |
1755 | int error; |
1756 | |
1757 | memset(line, 0, sizeof(*line)); |
1758 | line->dwDTERate = cpu_to_le32(57600); |
1759 | line->bDataBits = 8; |
1760 | |
1761 | error = usb_control_msg(dev: pcu->udev, usb_sndctrlpipe(pcu->udev, 0), |
1762 | USB_CDC_REQ_SET_LINE_CODING, |
1763 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, |
1764 | value: 0, index: interface->desc.bInterfaceNumber, |
1765 | data: line, size: sizeof(struct usb_cdc_line_coding), |
1766 | timeout: 5000); |
1767 | if (error < 0) { |
1768 | dev_err(pcu->dev, "Failed to set line coding, error: %d\n" , |
1769 | error); |
1770 | return error; |
1771 | } |
1772 | |
1773 | error = usb_control_msg(dev: pcu->udev, usb_sndctrlpipe(pcu->udev, 0), |
1774 | USB_CDC_REQ_SET_CONTROL_LINE_STATE, |
1775 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, |
1776 | value: 0x03, index: interface->desc.bInterfaceNumber, |
1777 | NULL, size: 0, timeout: 5000); |
1778 | if (error < 0) { |
1779 | dev_err(pcu->dev, "Failed to set line state, error: %d\n" , |
1780 | error); |
1781 | return error; |
1782 | } |
1783 | |
1784 | return 0; |
1785 | } |
1786 | |
1787 | static int ims_pcu_get_device_info(struct ims_pcu *pcu) |
1788 | { |
1789 | int error; |
1790 | |
1791 | error = ims_pcu_get_info(pcu); |
1792 | if (error) |
1793 | return error; |
1794 | |
1795 | error = ims_pcu_execute_query(pcu, GET_FW_VERSION); |
1796 | if (error) { |
1797 | dev_err(pcu->dev, |
1798 | "GET_FW_VERSION command failed, error: %d\n" , error); |
1799 | return error; |
1800 | } |
1801 | |
1802 | snprintf(buf: pcu->fw_version, size: sizeof(pcu->fw_version), |
1803 | fmt: "%02d%02d%02d%02d.%c%c" , |
1804 | pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], |
1805 | pcu->cmd_buf[6], pcu->cmd_buf[7]); |
1806 | |
1807 | error = ims_pcu_execute_query(pcu, GET_BL_VERSION); |
1808 | if (error) { |
1809 | dev_err(pcu->dev, |
1810 | "GET_BL_VERSION command failed, error: %d\n" , error); |
1811 | return error; |
1812 | } |
1813 | |
1814 | snprintf(buf: pcu->bl_version, size: sizeof(pcu->bl_version), |
1815 | fmt: "%02d%02d%02d%02d.%c%c" , |
1816 | pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], |
1817 | pcu->cmd_buf[6], pcu->cmd_buf[7]); |
1818 | |
1819 | error = ims_pcu_execute_query(pcu, RESET_REASON); |
1820 | if (error) { |
1821 | dev_err(pcu->dev, |
1822 | "RESET_REASON command failed, error: %d\n" , error); |
1823 | return error; |
1824 | } |
1825 | |
1826 | snprintf(buf: pcu->reset_reason, size: sizeof(pcu->reset_reason), |
1827 | fmt: "%02x" , pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); |
1828 | |
1829 | dev_dbg(pcu->dev, |
1830 | "P/N: %s, MD: %s, S/N: %s, FW: %s, BL: %s, RR: %s\n" , |
1831 | pcu->part_number, |
1832 | pcu->date_of_manufacturing, |
1833 | pcu->serial_number, |
1834 | pcu->fw_version, |
1835 | pcu->bl_version, |
1836 | pcu->reset_reason); |
1837 | |
1838 | return 0; |
1839 | } |
1840 | |
1841 | static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id) |
1842 | { |
1843 | int error; |
1844 | |
1845 | error = ims_pcu_execute_query(pcu, GET_DEVICE_ID); |
1846 | if (error) { |
1847 | dev_err(pcu->dev, |
1848 | "GET_DEVICE_ID command failed, error: %d\n" , error); |
1849 | return error; |
1850 | } |
1851 | |
1852 | *device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET]; |
1853 | dev_dbg(pcu->dev, "Detected device ID: %d\n" , *device_id); |
1854 | |
1855 | return 0; |
1856 | } |
1857 | |
1858 | static int ims_pcu_init_application_mode(struct ims_pcu *pcu) |
1859 | { |
1860 | static atomic_t device_no = ATOMIC_INIT(-1); |
1861 | |
1862 | const struct ims_pcu_device_info *info; |
1863 | int error; |
1864 | |
1865 | error = ims_pcu_get_device_info(pcu); |
1866 | if (error) { |
1867 | /* Device does not respond to basic queries, hopeless */ |
1868 | return error; |
1869 | } |
1870 | |
1871 | error = ims_pcu_identify_type(pcu, device_id: &pcu->device_id); |
1872 | if (error) { |
1873 | dev_err(pcu->dev, |
1874 | "Failed to identify device, error: %d\n" , error); |
1875 | /* |
1876 | * Do not signal error, but do not create input nor |
1877 | * backlight devices either, let userspace figure this |
1878 | * out (flash a new firmware?). |
1879 | */ |
1880 | return 0; |
1881 | } |
1882 | |
1883 | if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) || |
1884 | !ims_pcu_device_info[pcu->device_id].keymap) { |
1885 | dev_err(pcu->dev, "Device ID %d is not valid\n" , pcu->device_id); |
1886 | /* Same as above, punt to userspace */ |
1887 | return 0; |
1888 | } |
1889 | |
1890 | /* Device appears to be operable, complete initialization */ |
1891 | pcu->device_no = atomic_inc_return(v: &device_no); |
1892 | |
1893 | /* |
1894 | * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor |
1895 | */ |
1896 | if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) { |
1897 | error = sysfs_create_group(kobj: &pcu->dev->kobj, |
1898 | grp: &ims_pcu_ofn_attr_group); |
1899 | if (error) |
1900 | return error; |
1901 | } |
1902 | |
1903 | error = ims_pcu_setup_backlight(pcu); |
1904 | if (error) |
1905 | return error; |
1906 | |
1907 | info = &ims_pcu_device_info[pcu->device_id]; |
1908 | error = ims_pcu_setup_buttons(pcu, keymap: info->keymap, keymap_len: info->keymap_len); |
1909 | if (error) |
1910 | goto err_destroy_backlight; |
1911 | |
1912 | if (info->has_gamepad) { |
1913 | error = ims_pcu_setup_gamepad(pcu); |
1914 | if (error) |
1915 | goto err_destroy_buttons; |
1916 | } |
1917 | |
1918 | pcu->setup_complete = true; |
1919 | |
1920 | return 0; |
1921 | |
1922 | err_destroy_buttons: |
1923 | ims_pcu_destroy_buttons(pcu); |
1924 | err_destroy_backlight: |
1925 | ims_pcu_destroy_backlight(pcu); |
1926 | return error; |
1927 | } |
1928 | |
1929 | static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu) |
1930 | { |
1931 | if (pcu->setup_complete) { |
1932 | pcu->setup_complete = false; |
1933 | mb(); /* make sure flag setting is not reordered */ |
1934 | |
1935 | if (pcu->gamepad) |
1936 | ims_pcu_destroy_gamepad(pcu); |
1937 | ims_pcu_destroy_buttons(pcu); |
1938 | ims_pcu_destroy_backlight(pcu); |
1939 | |
1940 | if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) |
1941 | sysfs_remove_group(kobj: &pcu->dev->kobj, |
1942 | grp: &ims_pcu_ofn_attr_group); |
1943 | } |
1944 | } |
1945 | |
1946 | static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu) |
1947 | { |
1948 | int error; |
1949 | |
1950 | error = ims_pcu_execute_bl_command(pcu, QUERY_DEVICE, NULL, 0, |
1951 | IMS_PCU_CMD_RESPONSE_TIMEOUT); |
1952 | if (error) { |
1953 | dev_err(pcu->dev, "Bootloader does not respond, aborting\n" ); |
1954 | return error; |
1955 | } |
1956 | |
1957 | pcu->fw_start_addr = |
1958 | get_unaligned_le32(p: &pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]); |
1959 | pcu->fw_end_addr = |
1960 | get_unaligned_le32(p: &pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 15]); |
1961 | |
1962 | dev_info(pcu->dev, |
1963 | "Device is in bootloader mode (addr 0x%08x-0x%08x), requesting firmware\n" , |
1964 | pcu->fw_start_addr, pcu->fw_end_addr); |
1965 | |
1966 | error = request_firmware_nowait(THIS_MODULE, uevent: true, |
1967 | IMS_PCU_FIRMWARE_NAME, |
1968 | device: pcu->dev, GFP_KERNEL, context: pcu, |
1969 | cont: ims_pcu_process_async_firmware); |
1970 | if (error) { |
1971 | /* This error is not fatal, let userspace have another chance */ |
1972 | complete(&pcu->async_firmware_done); |
1973 | } |
1974 | |
1975 | return 0; |
1976 | } |
1977 | |
1978 | static void ims_pcu_destroy_bootloader_mode(struct ims_pcu *pcu) |
1979 | { |
1980 | /* Make sure our initial firmware request has completed */ |
1981 | wait_for_completion(&pcu->async_firmware_done); |
1982 | } |
1983 | |
1984 | #define IMS_PCU_APPLICATION_MODE 0 |
1985 | #define IMS_PCU_BOOTLOADER_MODE 1 |
1986 | |
1987 | static struct usb_driver ims_pcu_driver; |
1988 | |
1989 | static int ims_pcu_probe(struct usb_interface *intf, |
1990 | const struct usb_device_id *id) |
1991 | { |
1992 | struct usb_device *udev = interface_to_usbdev(intf); |
1993 | struct ims_pcu *pcu; |
1994 | int error; |
1995 | |
1996 | pcu = kzalloc(size: sizeof(struct ims_pcu), GFP_KERNEL); |
1997 | if (!pcu) |
1998 | return -ENOMEM; |
1999 | |
2000 | pcu->dev = &intf->dev; |
2001 | pcu->udev = udev; |
2002 | pcu->bootloader_mode = id->driver_info == IMS_PCU_BOOTLOADER_MODE; |
2003 | mutex_init(&pcu->cmd_mutex); |
2004 | init_completion(x: &pcu->cmd_done); |
2005 | init_completion(x: &pcu->async_firmware_done); |
2006 | |
2007 | error = ims_pcu_parse_cdc_data(intf, pcu); |
2008 | if (error) |
2009 | goto err_free_mem; |
2010 | |
2011 | error = usb_driver_claim_interface(driver: &ims_pcu_driver, |
2012 | iface: pcu->data_intf, data: pcu); |
2013 | if (error) { |
2014 | dev_err(&intf->dev, |
2015 | "Unable to claim corresponding data interface: %d\n" , |
2016 | error); |
2017 | goto err_free_mem; |
2018 | } |
2019 | |
2020 | usb_set_intfdata(intf: pcu->ctrl_intf, data: pcu); |
2021 | |
2022 | error = ims_pcu_buffers_alloc(pcu); |
2023 | if (error) |
2024 | goto err_unclaim_intf; |
2025 | |
2026 | error = ims_pcu_start_io(pcu); |
2027 | if (error) |
2028 | goto err_free_buffers; |
2029 | |
2030 | error = ims_pcu_line_setup(pcu); |
2031 | if (error) |
2032 | goto err_stop_io; |
2033 | |
2034 | error = sysfs_create_group(kobj: &intf->dev.kobj, grp: &ims_pcu_attr_group); |
2035 | if (error) |
2036 | goto err_stop_io; |
2037 | |
2038 | error = pcu->bootloader_mode ? |
2039 | ims_pcu_init_bootloader_mode(pcu) : |
2040 | ims_pcu_init_application_mode(pcu); |
2041 | if (error) |
2042 | goto err_remove_sysfs; |
2043 | |
2044 | return 0; |
2045 | |
2046 | err_remove_sysfs: |
2047 | sysfs_remove_group(kobj: &intf->dev.kobj, grp: &ims_pcu_attr_group); |
2048 | err_stop_io: |
2049 | ims_pcu_stop_io(pcu); |
2050 | err_free_buffers: |
2051 | ims_pcu_buffers_free(pcu); |
2052 | err_unclaim_intf: |
2053 | usb_driver_release_interface(driver: &ims_pcu_driver, iface: pcu->data_intf); |
2054 | err_free_mem: |
2055 | kfree(objp: pcu); |
2056 | return error; |
2057 | } |
2058 | |
2059 | static void ims_pcu_disconnect(struct usb_interface *intf) |
2060 | { |
2061 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
2062 | struct usb_host_interface *alt = intf->cur_altsetting; |
2063 | |
2064 | usb_set_intfdata(intf, NULL); |
2065 | |
2066 | /* |
2067 | * See if we are dealing with control or data interface. The cleanup |
2068 | * happens when we unbind primary (control) interface. |
2069 | */ |
2070 | if (alt->desc.bInterfaceClass != USB_CLASS_COMM) |
2071 | return; |
2072 | |
2073 | sysfs_remove_group(kobj: &intf->dev.kobj, grp: &ims_pcu_attr_group); |
2074 | |
2075 | ims_pcu_stop_io(pcu); |
2076 | |
2077 | if (pcu->bootloader_mode) |
2078 | ims_pcu_destroy_bootloader_mode(pcu); |
2079 | else |
2080 | ims_pcu_destroy_application_mode(pcu); |
2081 | |
2082 | ims_pcu_buffers_free(pcu); |
2083 | kfree(objp: pcu); |
2084 | } |
2085 | |
2086 | #ifdef CONFIG_PM |
2087 | static int ims_pcu_suspend(struct usb_interface *intf, |
2088 | pm_message_t message) |
2089 | { |
2090 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
2091 | struct usb_host_interface *alt = intf->cur_altsetting; |
2092 | |
2093 | if (alt->desc.bInterfaceClass == USB_CLASS_COMM) |
2094 | ims_pcu_stop_io(pcu); |
2095 | |
2096 | return 0; |
2097 | } |
2098 | |
2099 | static int ims_pcu_resume(struct usb_interface *intf) |
2100 | { |
2101 | struct ims_pcu *pcu = usb_get_intfdata(intf); |
2102 | struct usb_host_interface *alt = intf->cur_altsetting; |
2103 | int retval = 0; |
2104 | |
2105 | if (alt->desc.bInterfaceClass == USB_CLASS_COMM) { |
2106 | retval = ims_pcu_start_io(pcu); |
2107 | if (retval == 0) |
2108 | retval = ims_pcu_line_setup(pcu); |
2109 | } |
2110 | |
2111 | return retval; |
2112 | } |
2113 | #endif |
2114 | |
2115 | static const struct usb_device_id ims_pcu_id_table[] = { |
2116 | { |
2117 | USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0082, |
2118 | USB_CLASS_COMM, |
2119 | USB_CDC_SUBCLASS_ACM, |
2120 | USB_CDC_ACM_PROTO_AT_V25TER), |
2121 | .driver_info = IMS_PCU_APPLICATION_MODE, |
2122 | }, |
2123 | { |
2124 | USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0083, |
2125 | USB_CLASS_COMM, |
2126 | USB_CDC_SUBCLASS_ACM, |
2127 | USB_CDC_ACM_PROTO_AT_V25TER), |
2128 | .driver_info = IMS_PCU_BOOTLOADER_MODE, |
2129 | }, |
2130 | { } |
2131 | }; |
2132 | |
2133 | static struct usb_driver ims_pcu_driver = { |
2134 | .name = "ims_pcu" , |
2135 | .id_table = ims_pcu_id_table, |
2136 | .probe = ims_pcu_probe, |
2137 | .disconnect = ims_pcu_disconnect, |
2138 | #ifdef CONFIG_PM |
2139 | .suspend = ims_pcu_suspend, |
2140 | .resume = ims_pcu_resume, |
2141 | .reset_resume = ims_pcu_resume, |
2142 | #endif |
2143 | }; |
2144 | |
2145 | module_usb_driver(ims_pcu_driver); |
2146 | |
2147 | MODULE_DESCRIPTION("IMS Passenger Control Unit driver" ); |
2148 | MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>" ); |
2149 | MODULE_LICENSE("GPL" ); |
2150 | |