2262 lines · 55.1 KB · C
RawDownload
// 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.