// SPDX-License-Identifier: GPL-2.0-only/* * Driver for IMS Passenger Control Unit Devices * * Copyright (C) 2013 The IMS Company */ #include <linux/completion.h>#include <linux/device.h>#include <linux/firmware.h>#include <linux/ihex.h>#include <linux/input.h>#include <linux/kernel.h>#include <linux/leds.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/sysfs.h>#include <linux/types.h>#include <linux/usb/input.h>#include <linux/usb/cdc.h>#include <linux/unaligned.h> #define IMS_PCU_KEYMAP_LEN 32 struct ims_pcu_buttons { struct input_dev *input; char name[32]; char phys[32]; unsigned short keymap[IMS_PCU_KEYMAP_LEN];}; struct ims_pcu_gamepad { struct input_dev *input; char name[32]; char phys[32];}; struct ims_pcu_backlight { struct led_classdev cdev; char name[32];}; #define IMS_PCU_PART_NUMBER_LEN 15#define IMS_PCU_SERIAL_NUMBER_LEN 8#define IMS_PCU_DOM_LEN 8#define IMS_PCU_FW_VERSION_LEN 16#define IMS_PCU_BL_VERSION_LEN 16#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1) #define IMS_PCU_PCU_B_DEVICE_ID 5 #define IMS_PCU_BUF_SIZE 128 struct ims_pcu { struct usb_device *udev; struct device *dev; /* control interface's device, used for logging */ unsigned int device_no; bool bootloader_mode; char part_number[IMS_PCU_PART_NUMBER_LEN]; char serial_number[IMS_PCU_SERIAL_NUMBER_LEN]; char date_of_manufacturing[IMS_PCU_DOM_LEN]; char fw_version[IMS_PCU_FW_VERSION_LEN]; char bl_version[IMS_PCU_BL_VERSION_LEN]; char reset_reason[IMS_PCU_BL_RESET_REASON_LEN]; int update_firmware_status; u8 device_id; u8 ofn_reg_addr; struct usb_interface *ctrl_intf; struct usb_endpoint_descriptor *ep_ctrl; struct urb *urb_ctrl; u8 *urb_ctrl_buf; dma_addr_t ctrl_dma; size_t max_ctrl_size; struct usb_interface *data_intf; struct usb_endpoint_descriptor *ep_in; struct urb *urb_in; u8 *urb_in_buf; dma_addr_t read_dma; size_t max_in_size; struct usb_endpoint_descriptor *ep_out; u8 *urb_out_buf; size_t max_out_size; u8 read_buf[IMS_PCU_BUF_SIZE]; u8 read_pos; u8 check_sum; bool have_stx; bool have_dle; u8 cmd_buf[IMS_PCU_BUF_SIZE]; u8 ack_id; u8 expected_response; u8 cmd_buf_len; struct completion cmd_done; struct mutex cmd_mutex; u32 fw_start_addr; u32 fw_end_addr; struct completion async_firmware_done; struct ims_pcu_buttons buttons; struct ims_pcu_gamepad *gamepad; struct ims_pcu_backlight backlight; bool setup_complete; /* Input and LED devices have been created */}; /********************************************************************* * Buttons Input device support * *********************************************************************/ static const unsigned short ims_pcu_keymap_1[] = { [1] = KEY_ATTENDANT_OFF, [2] = KEY_ATTENDANT_ON, [3] = KEY_LIGHTS_TOGGLE, [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_INFO,}; static const unsigned short ims_pcu_keymap_2[] = { [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_INFO,}; static const unsigned short ims_pcu_keymap_3[] = { [1] = KEY_HOMEPAGE, [2] = KEY_ATTENDANT_TOGGLE, [3] = KEY_LIGHTS_TOGGLE, [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_DISPLAYTOGGLE, [18] = KEY_PLAYPAUSE,}; static const unsigned short ims_pcu_keymap_4[] = { [1] = KEY_ATTENDANT_OFF, [2] = KEY_ATTENDANT_ON, [3] = KEY_LIGHTS_TOGGLE, [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_INFO, [18] = KEY_PLAYPAUSE,}; static const unsigned short ims_pcu_keymap_5[] = { [1] = KEY_ATTENDANT_OFF, [2] = KEY_ATTENDANT_ON, [3] = KEY_LIGHTS_TOGGLE,}; struct ims_pcu_device_info { const unsigned short *keymap; size_t keymap_len; bool has_gamepad;}; #define IMS_PCU_DEVINFO(_n, _gamepad) \ [_n] = { \ .keymap = ims_pcu_keymap_##_n, \ .keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n), \ .has_gamepad = _gamepad, \ } static const struct ims_pcu_device_info ims_pcu_device_info[] = { IMS_PCU_DEVINFO(1, true), IMS_PCU_DEVINFO(2, true), IMS_PCU_DEVINFO(3, true), IMS_PCU_DEVINFO(4, true), IMS_PCU_DEVINFO(5, false),}; static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data){ struct ims_pcu_buttons *buttons = &pcu->buttons; struct input_dev *input = buttons->input; int i; for (i = 0; i < 32; i++) { unsigned short keycode = buttons->keymap[i]; if (keycode != KEY_RESERVED) input_report_key(input, keycode, data & (1UL << i)); } input_sync(input);} static int ims_pcu_setup_buttons(struct ims_pcu *pcu, const unsigned short *keymap, size_t keymap_len){ struct ims_pcu_buttons *buttons = &pcu->buttons; struct input_dev *input; int i; int error; input = input_allocate_device(); if (!input) { dev_err(pcu->dev, "Not enough memory for input device\n"); return -ENOMEM; } snprintf(buttons->name, sizeof(buttons->name), "IMS PCU#%d Button Interface", pcu->device_no); usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys)); strlcat(buttons->phys, "/input0", sizeof(buttons->phys)); memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len); input->name = buttons->name; input->phys = buttons->phys; usb_to_input_id(pcu->udev, &input->id); input->dev.parent = &pcu->ctrl_intf->dev; input->keycode = buttons->keymap; input->keycodemax = ARRAY_SIZE(buttons->keymap); input->keycodesize = sizeof(buttons->keymap[0]); __set_bit(EV_KEY, input->evbit); for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++) __set_bit(buttons->keymap[i], input->keybit); __clear_bit(KEY_RESERVED, input->keybit); error = input_register_device(input); if (error) { dev_err(pcu->dev, "Failed to register buttons input device: %d\n", error); input_free_device(input); return error; } buttons->input = input; return 0;} static void ims_pcu_destroy_buttons(struct ims_pcu *pcu){ struct ims_pcu_buttons *buttons = &pcu->buttons; input_unregister_device(buttons->input);} /********************************************************************* * Gamepad Input device support * *********************************************************************/ static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data){ struct ims_pcu_gamepad *gamepad = pcu->gamepad; struct input_dev *input = gamepad->input; int x, y; x = !!(data & (1 << 14)) - !!(data & (1 << 13)); y = !!(data & (1 << 12)) - !!(data & (1 << 11)); input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); input_report_key(input, BTN_A, data & (1 << 7)); input_report_key(input, BTN_B, data & (1 << 8)); input_report_key(input, BTN_X, data & (1 << 9)); input_report_key(input, BTN_Y, data & (1 << 10)); input_report_key(input, BTN_START, data & (1 << 15)); input_report_key(input, BTN_SELECT, data & (1 << 16)); input_sync(input);} static int ims_pcu_setup_gamepad(struct ims_pcu *pcu){ struct ims_pcu_gamepad *gamepad; struct input_dev *input; int error; gamepad = kzalloc_obj(*gamepad); input = input_allocate_device(); if (!gamepad || !input) { dev_err(pcu->dev, "Not enough memory for gamepad device\n"); error = -ENOMEM; goto err_free_mem; } gamepad->input = input; snprintf(gamepad->name, sizeof(gamepad->name), "IMS PCU#%d Gamepad Interface", pcu->device_no); usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys)); strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys)); input->name = gamepad->name; input->phys = gamepad->phys; usb_to_input_id(pcu->udev, &input->id); input->dev.parent = &pcu->ctrl_intf->dev; __set_bit(EV_KEY, input->evbit); __set_bit(BTN_A, input->keybit); __set_bit(BTN_B, input->keybit); __set_bit(BTN_X, input->keybit); __set_bit(BTN_Y, input->keybit); __set_bit(BTN_START, input->keybit); __set_bit(BTN_SELECT, input->keybit); __set_bit(EV_ABS, input->evbit); input_set_abs_params(input, ABS_X, -1, 1, 0, 0); input_set_abs_params(input, ABS_Y, -1, 1, 0, 0); error = input_register_device(input); if (error) { dev_err(pcu->dev, "Failed to register gamepad input device: %d\n", error); goto err_free_mem; } pcu->gamepad = gamepad; return 0; err_free_mem: input_free_device(input); kfree(gamepad); return error;} static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu){ struct ims_pcu_gamepad *gamepad = pcu->gamepad; input_unregister_device(gamepad->input); kfree(gamepad);} /********************************************************************* * PCU Communication protocol handling * *********************************************************************/ #define IMS_PCU_PROTOCOL_STX 0x02#define IMS_PCU_PROTOCOL_ETX 0x03#define IMS_PCU_PROTOCOL_DLE 0x10 /* PCU commands */#define IMS_PCU_CMD_STATUS 0xa0#define IMS_PCU_CMD_PCU_RESET 0xa1#define IMS_PCU_CMD_RESET_REASON 0xa2#define IMS_PCU_CMD_SEND_BUTTONS 0xa3#define IMS_PCU_CMD_JUMP_TO_BTLDR 0xa4#define IMS_PCU_CMD_GET_INFO 0xa5#define IMS_PCU_CMD_SET_BRIGHTNESS 0xa6#define IMS_PCU_CMD_EEPROM 0xa7#define IMS_PCU_CMD_GET_FW_VERSION 0xa8#define IMS_PCU_CMD_GET_BL_VERSION 0xa9#define IMS_PCU_CMD_SET_INFO 0xab#define IMS_PCU_CMD_GET_BRIGHTNESS 0xac#define IMS_PCU_CMD_GET_DEVICE_ID 0xae#define IMS_PCU_CMD_SPECIAL_INFO 0xb0#define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */#define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3#define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4 /* PCU responses */#define IMS_PCU_RSP_STATUS 0xc0#define IMS_PCU_RSP_PCU_RESET 0 /* Originally 0xc1 */#define IMS_PCU_RSP_RESET_REASON 0xc2#define IMS_PCU_RSP_SEND_BUTTONS 0xc3#define IMS_PCU_RSP_JUMP_TO_BTLDR 0 /* Originally 0xc4 */#define IMS_PCU_RSP_GET_INFO 0xc5#define IMS_PCU_RSP_SET_BRIGHTNESS 0xc6#define IMS_PCU_RSP_EEPROM 0xc7#define IMS_PCU_RSP_GET_FW_VERSION 0xc8#define IMS_PCU_RSP_GET_BL_VERSION 0xc9#define IMS_PCU_RSP_SET_INFO 0xcb#define IMS_PCU_RSP_GET_BRIGHTNESS 0xcc#define IMS_PCU_RSP_CMD_INVALID 0xcd#define IMS_PCU_RSP_GET_DEVICE_ID 0xce#define IMS_PCU_RSP_SPECIAL_INFO 0xd0#define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */#define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2#define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3 #define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */#define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */ #define IMS_PCU_MIN_PACKET_LEN 3#define IMS_PCU_DATA_OFFSET 2 #define IMS_PCU_CMD_WRITE_TIMEOUT 100 /* msec */#define IMS_PCU_CMD_RESPONSE_TIMEOUT 500 /* msec */ static void ims_pcu_report_events(struct ims_pcu *pcu){ u32 data; /* 6-axis setting (1 byte) + button data + checksum */ if (pcu->read_pos < IMS_PCU_DATA_OFFSET + 1 + sizeof(data) + 1) { dev_warn(pcu->dev, "Short buttons report: %d bytes\n", pcu->read_pos); return; } data = get_unaligned_be32(&pcu->read_buf[IMS_PCU_DATA_OFFSET + 1]); ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK); if (pcu->gamepad) ims_pcu_gamepad_report(pcu, data);} static void ims_pcu_handle_response(struct ims_pcu *pcu){ switch (pcu->read_buf[0]) { case IMS_PCU_RSP_EVNT_BUTTONS: if (likely(pcu->setup_complete)) ims_pcu_report_events(pcu); break; default: /* * See if we got command completion. * If both the sequence and response code match save * the data and signal completion. */ if (pcu->read_buf[0] == pcu->expected_response && pcu->read_buf[1] == pcu->ack_id - 1) { memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos); pcu->cmd_buf_len = pcu->read_pos; complete(&pcu->cmd_done); } break; }} static void ims_pcu_reset_packet(struct ims_pcu *pcu){ pcu->have_stx = false; pcu->have_dle = false; pcu->read_pos = 0; pcu->check_sum = 0;} static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb){ int i; for (i = 0; i < urb->actual_length; i++) { u8 data = pcu->urb_in_buf[i]; /* Skip everything until we get Start Xmit */ if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX) continue; if (pcu->have_dle) { if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { dev_warn(pcu->dev, "Packet too long (%d bytes), discarding\n", pcu->read_pos); ims_pcu_reset_packet(pcu); continue; } pcu->have_dle = false; pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; continue; } switch (data) { case IMS_PCU_PROTOCOL_STX: if (pcu->have_stx) dev_warn(pcu->dev, "Unexpected STX at byte %d, discarding old data\n", pcu->read_pos); ims_pcu_reset_packet(pcu); pcu->have_stx = true; break; case IMS_PCU_PROTOCOL_DLE: pcu->have_dle = true; break; case IMS_PCU_PROTOCOL_ETX: if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) {Lines 1–500 of 2262. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Driver for IMS Passenger Control Unit Devices * * Copyright (C) 2013 The IMS Company */ #include <linux/completion.h> #include <linux/device.h> #include <linux/firmware.h> #include <linux/ihex.h> #include <linux/input.h> #include <linux/kernel.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/types.h> #include <linux/usb/input.h> #include <linux/usb/cdc.h> #include <linux/unaligned.h> #define IMS_PCU_KEYMAP_LEN 32 struct ims_pcu_buttons { struct input_dev *input; char name[32]; char phys[32]; unsigned short keymap[IMS_PCU_KEYMAP_LEN]; }; struct ims_pcu_gamepad { struct input_dev *input; char name[32]; char phys[32]; }; struct ims_pcu_backlight { struct led_classdev cdev; char name[32]; }; #define IMS_PCU_PART_NUMBER_LEN 15 #define IMS_PCU_SERIAL_NUMBER_LEN 8 #define IMS_PCU_DOM_LEN 8 #define IMS_PCU_FW_VERSION_LEN 16 #define IMS_PCU_BL_VERSION_LEN 16 #define IMS_PCU_BL_RESET_REASON_LEN (2 + 1) #define IMS_PCU_PCU_B_DEVICE_ID 5 #define IMS_PCU_BUF_SIZE 128 struct ims_pcu { struct usb_device *udev; struct device *dev; /* control interface's device, used for logging */ unsigned int device_no; bool bootloader_mode; char part_number[IMS_PCU_PART_NUMBER_LEN]; char serial_number[IMS_PCU_SERIAL_NUMBER_LEN]; char date_of_manufacturing[IMS_PCU_DOM_LEN]; char fw_version[IMS_PCU_FW_VERSION_LEN]; char bl_version[IMS_PCU_BL_VERSION_LEN]; char reset_reason[IMS_PCU_BL_RESET_REASON_LEN]; int update_firmware_status; u8 device_id; u8 ofn_reg_addr; struct usb_interface *ctrl_intf; struct usb_endpoint_descriptor *ep_ctrl; struct urb *urb_ctrl; u8 *urb_ctrl_buf; dma_addr_t ctrl_dma; size_t max_ctrl_size; struct usb_interface *data_intf; struct usb_endpoint_descriptor *ep_in; struct urb *urb_in; u8 *urb_in_buf; dma_addr_t read_dma; size_t max_in_size; struct usb_endpoint_descriptor *ep_out; u8 *urb_out_buf; size_t max_out_size; u8 read_buf[IMS_PCU_BUF_SIZE]; u8 read_pos; u8 check_sum; bool have_stx; bool have_dle; u8 cmd_buf[IMS_PCU_BUF_SIZE]; u8 ack_id; u8 expected_response; u8 cmd_buf_len; struct completion cmd_done; struct mutex cmd_mutex; u32 fw_start_addr; u32 fw_end_addr; struct completion async_firmware_done; struct ims_pcu_buttons buttons; struct ims_pcu_gamepad *gamepad; struct ims_pcu_backlight backlight; bool setup_complete; /* Input and LED devices have been created */ }; /********************************************************************* * Buttons Input device support * *********************************************************************/ static const unsigned short ims_pcu_keymap_1[] = { [1] = KEY_ATTENDANT_OFF, [2] = KEY_ATTENDANT_ON, [3] = KEY_LIGHTS_TOGGLE, [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_INFO, }; static const unsigned short ims_pcu_keymap_2[] = { [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_INFO, }; static const unsigned short ims_pcu_keymap_3[] = { [1] = KEY_HOMEPAGE, [2] = KEY_ATTENDANT_TOGGLE, [3] = KEY_LIGHTS_TOGGLE, [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_DISPLAYTOGGLE, [18] = KEY_PLAYPAUSE, }; static const unsigned short ims_pcu_keymap_4[] = { [1] = KEY_ATTENDANT_OFF, [2] = KEY_ATTENDANT_ON, [3] = KEY_LIGHTS_TOGGLE, [4] = KEY_VOLUMEUP, [5] = KEY_VOLUMEDOWN, [6] = KEY_INFO, [18] = KEY_PLAYPAUSE, }; static const unsigned short ims_pcu_keymap_5[] = { [1] = KEY_ATTENDANT_OFF, [2] = KEY_ATTENDANT_ON, [3] = KEY_LIGHTS_TOGGLE, }; struct ims_pcu_device_info { const unsigned short *keymap; size_t keymap_len; bool has_gamepad; }; #define IMS_PCU_DEVINFO(_n, _gamepad) \ [_n] = { \ .keymap = ims_pcu_keymap_##_n, \ .keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n), \ .has_gamepad = _gamepad, \ } static const struct ims_pcu_device_info ims_pcu_device_info[] = { IMS_PCU_DEVINFO(1, true), IMS_PCU_DEVINFO(2, true), IMS_PCU_DEVINFO(3, true), IMS_PCU_DEVINFO(4, true), IMS_PCU_DEVINFO(5, false), }; static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data) { struct ims_pcu_buttons *buttons = &pcu->buttons; struct input_dev *input = buttons->input; int i; for (i = 0; i < 32; i++) { unsigned short keycode = buttons->keymap[i]; if (keycode != KEY_RESERVED) input_report_key(input, keycode, data & (1UL << i)); } input_sync(input); } static int ims_pcu_setup_buttons(struct ims_pcu *pcu, const unsigned short *keymap, size_t keymap_len) { struct ims_pcu_buttons *buttons = &pcu->buttons; struct input_dev *input; int i; int error; input = input_allocate_device(); if (!input) { dev_err(pcu->dev, "Not enough memory for input device\n"); return -ENOMEM; } snprintf(buttons->name, sizeof(buttons->name), "IMS PCU#%d Button Interface", pcu->device_no); usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys)); strlcat(buttons->phys, "/input0", sizeof(buttons->phys)); memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len); input->name = buttons->name; input->phys = buttons->phys; usb_to_input_id(pcu->udev, &input->id); input->dev.parent = &pcu->ctrl_intf->dev; input->keycode = buttons->keymap; input->keycodemax = ARRAY_SIZE(buttons->keymap); input->keycodesize = sizeof(buttons->keymap[0]); __set_bit(EV_KEY, input->evbit); for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++) __set_bit(buttons->keymap[i], input->keybit); __clear_bit(KEY_RESERVED, input->keybit); error = input_register_device(input); if (error) { dev_err(pcu->dev, "Failed to register buttons input device: %d\n", error); input_free_device(input); return error; } buttons->input = input; return 0; } static void ims_pcu_destroy_buttons(struct ims_pcu *pcu) { struct ims_pcu_buttons *buttons = &pcu->buttons; input_unregister_device(buttons->input); } /********************************************************************* * Gamepad Input device support * *********************************************************************/ static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data) { struct ims_pcu_gamepad *gamepad = pcu->gamepad; struct input_dev *input = gamepad->input; int x, y; x = !!(data & (1 << 14)) - !!(data & (1 << 13)); y = !!(data & (1 << 12)) - !!(data & (1 << 11)); input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); input_report_key(input, BTN_A, data & (1 << 7)); input_report_key(input, BTN_B, data & (1 << 8)); input_report_key(input, BTN_X, data & (1 << 9)); input_report_key(input, BTN_Y, data & (1 << 10)); input_report_key(input, BTN_START, data & (1 << 15)); input_report_key(input, BTN_SELECT, data & (1 << 16)); input_sync(input); } static int ims_pcu_setup_gamepad(struct ims_pcu *pcu) { struct ims_pcu_gamepad *gamepad; struct input_dev *input; int error; gamepad = kzalloc_obj(*gamepad); input = input_allocate_device(); if (!gamepad || !input) { dev_err(pcu->dev, "Not enough memory for gamepad device\n"); error = -ENOMEM; goto err_free_mem; } gamepad->input = input; snprintf(gamepad->name, sizeof(gamepad->name), "IMS PCU#%d Gamepad Interface", pcu->device_no); usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys)); strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys)); input->name = gamepad->name; input->phys = gamepad->phys; usb_to_input_id(pcu->udev, &input->id); input->dev.parent = &pcu->ctrl_intf->dev; __set_bit(EV_KEY, input->evbit); __set_bit(BTN_A, input->keybit); __set_bit(BTN_B, input->keybit); __set_bit(BTN_X, input->keybit); __set_bit(BTN_Y, input->keybit); __set_bit(BTN_START, input->keybit); __set_bit(BTN_SELECT, input->keybit); __set_bit(EV_ABS, input->evbit); input_set_abs_params(input, ABS_X, -1, 1, 0, 0); input_set_abs_params(input, ABS_Y, -1, 1, 0, 0); error = input_register_device(input); if (error) { dev_err(pcu->dev, "Failed to register gamepad input device: %d\n", error); goto err_free_mem; } pcu->gamepad = gamepad; return 0; err_free_mem: input_free_device(input); kfree(gamepad); return error; } static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) { struct ims_pcu_gamepad *gamepad = pcu->gamepad; input_unregister_device(gamepad->input); kfree(gamepad); } /********************************************************************* * PCU Communication protocol handling * *********************************************************************/ #define IMS_PCU_PROTOCOL_STX 0x02 #define IMS_PCU_PROTOCOL_ETX 0x03 #define IMS_PCU_PROTOCOL_DLE 0x10 /* PCU commands */ #define IMS_PCU_CMD_STATUS 0xa0 #define IMS_PCU_CMD_PCU_RESET 0xa1 #define IMS_PCU_CMD_RESET_REASON 0xa2 #define IMS_PCU_CMD_SEND_BUTTONS 0xa3 #define IMS_PCU_CMD_JUMP_TO_BTLDR 0xa4 #define IMS_PCU_CMD_GET_INFO 0xa5 #define IMS_PCU_CMD_SET_BRIGHTNESS 0xa6 #define IMS_PCU_CMD_EEPROM 0xa7 #define IMS_PCU_CMD_GET_FW_VERSION 0xa8 #define IMS_PCU_CMD_GET_BL_VERSION 0xa9 #define IMS_PCU_CMD_SET_INFO 0xab #define IMS_PCU_CMD_GET_BRIGHTNESS 0xac #define IMS_PCU_CMD_GET_DEVICE_ID 0xae #define IMS_PCU_CMD_SPECIAL_INFO 0xb0 #define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */ #define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3 #define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4 /* PCU responses */ #define IMS_PCU_RSP_STATUS 0xc0 #define IMS_PCU_RSP_PCU_RESET 0 /* Originally 0xc1 */ #define IMS_PCU_RSP_RESET_REASON 0xc2 #define IMS_PCU_RSP_SEND_BUTTONS 0xc3 #define IMS_PCU_RSP_JUMP_TO_BTLDR 0 /* Originally 0xc4 */ #define IMS_PCU_RSP_GET_INFO 0xc5 #define IMS_PCU_RSP_SET_BRIGHTNESS 0xc6 #define IMS_PCU_RSP_EEPROM 0xc7 #define IMS_PCU_RSP_GET_FW_VERSION 0xc8 #define IMS_PCU_RSP_GET_BL_VERSION 0xc9 #define IMS_PCU_RSP_SET_INFO 0xcb #define IMS_PCU_RSP_GET_BRIGHTNESS 0xcc #define IMS_PCU_RSP_CMD_INVALID 0xcd #define IMS_PCU_RSP_GET_DEVICE_ID 0xce #define IMS_PCU_RSP_SPECIAL_INFO 0xd0 #define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */ #define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2 #define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3 #define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */ #define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */ #define IMS_PCU_MIN_PACKET_LEN 3 #define IMS_PCU_DATA_OFFSET 2 #define IMS_PCU_CMD_WRITE_TIMEOUT 100 /* msec */ #define IMS_PCU_CMD_RESPONSE_TIMEOUT 500 /* msec */ static void ims_pcu_report_events(struct ims_pcu *pcu) { u32 data; /* 6-axis setting (1 byte) + button data + checksum */ if (pcu->read_pos < IMS_PCU_DATA_OFFSET + 1 + sizeof(data) + 1) { dev_warn(pcu->dev, "Short buttons report: %d bytes\n", pcu->read_pos); return; } data = get_unaligned_be32(&pcu->read_buf[IMS_PCU_DATA_OFFSET + 1]); ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK); if (pcu->gamepad) ims_pcu_gamepad_report(pcu, data); } static void ims_pcu_handle_response(struct ims_pcu *pcu) { switch (pcu->read_buf[0]) { case IMS_PCU_RSP_EVNT_BUTTONS: if (likely(pcu->setup_complete)) ims_pcu_report_events(pcu); break; default: /* * See if we got command completion. * If both the sequence and response code match save * the data and signal completion. */ if (pcu->read_buf[0] == pcu->expected_response && pcu->read_buf[1] == pcu->ack_id - 1) { memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos); pcu->cmd_buf_len = pcu->read_pos; complete(&pcu->cmd_done); } break; } } static void ims_pcu_reset_packet(struct ims_pcu *pcu) { pcu->have_stx = false; pcu->have_dle = false; pcu->read_pos = 0; pcu->check_sum = 0; } static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) { int i; for (i = 0; i < urb->actual_length; i++) { u8 data = pcu->urb_in_buf[i]; /* Skip everything until we get Start Xmit */ if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX) continue; if (pcu->have_dle) { if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { dev_warn(pcu->dev, "Packet too long (%d bytes), discarding\n", pcu->read_pos); ims_pcu_reset_packet(pcu); continue; } pcu->have_dle = false; pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; continue; } switch (data) { case IMS_PCU_PROTOCOL_STX: if (pcu->have_stx) dev_warn(pcu->dev, "Unexpected STX at byte %d, discarding old data\n", pcu->read_pos); ims_pcu_reset_packet(pcu); pcu->have_stx = true; break; case IMS_PCU_PROTOCOL_DLE: pcu->have_dle = true; break; case IMS_PCU_PROTOCOL_ETX: if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) {