/* * MHL3 HID Tunneling implementation * * Copyright (c) 2013-2014 Lee Mulcahy * Copyright (c) 2013-2014 Silicon Image, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. * This program is distributed AS-IS WITHOUT ANY WARRANTY of any * kind, whether express or implied; INCLUDING without the implied warranty * of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE or NON-INFRINGEMENT. * See the GNU General Public License for more details at * http://www.gnu.org/licenses/gpl-2.0.html. * * This code is inspired by the "HID over I2C protocol implementation" * * Copyright (c) 2012 Benjamin Tissoires * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France * Copyright (c) 2012 Red Hat, Inc * * and the "USB HID support for Linux" * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik * Copyright (c) 2005 Michael Haboustak for Concept2, Inc * Copyright (c) 2007-2008 Oliver Neukum * Copyright (c) 2006-2010 Jiri Kosina * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. */ #include #include #include #include #include #include #include #include #include #include #include #include "si_fw_macros.h" #include "si_app_devcap.h" #include "si_infoframe.h" #include "si_edid.h" #include "si_mhl_defs.h" #include "si_mhl2_edid_3d_api.h" #include "si_8620_internal_api.h" #include "si_mhl_tx_hw_drv_api.h" #ifdef MEDIA_DATA_TUNNEL_SUPPORT #include "si_mdt_inputdev.h" #endif #include "si_mhl_callback_api.h" #include "si_8620_drv.h" #include "mhl_linux_tx.h" #include "si_8620_regs.h" #include "mhl_supp.h" #include "platform.h" #include "si_emsc_hid.h" #define EMSC_RCV_MSG_START 0 /* First fragment of message. */ #define EMSC_RCV_MSG_NEXT 1 /* Subsequent fragment of message. */ void dump_array(int level, char *ptitle, uint8_t *pdata, int count) { int i, buf_offset; int bufsize = 128; char *buf; if (level > debug_level) return; if (count > 1) { bufsize += count * 3; bufsize += ((count / 16) + 1) * 8; } buf = kmalloc(bufsize, GFP_KERNEL); if (!buf) return; buf_offset = 0; if (ptitle) buf_offset = scnprintf(&buf[0], bufsize, "%s (%d bytes):", ptitle, count); for (i = 0; i < count; i++) { if ((i & 0x0F) == 0) buf_offset += scnprintf(&buf[buf_offset], bufsize - buf_offset, "\n%04X: ", i); buf_offset += scnprintf(&buf[buf_offset], bufsize - buf_offset, "%02X ", pdata[i]); } buf_offset += scnprintf(&buf[buf_offset], bufsize - buf_offset, "\n"); print_formatted_debug_msg(NULL, NULL, -1, buf); kfree(buf); } struct cbus_req *hid_host_role_request_done(struct mhl_dev_context *mdev, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * Send Host role request or relinquish it. This should be called with * MHL_RHID_REQUEST_HOST for a source during MHL connection and when the * want_host flag is set (after the sink has relinquished the Host role that * we let them have by sending MHL_RHID_RELIQUISH_HOST). */ void mhl_tx_hid_host_role_request(struct mhl_dev_context *context, int request) { MHL3_HID_DBG_INFO("RHID: Sending HID Host role %s message\n", (request == MHL_RHID_REQUEST_HOST) ? "REQUEST" : "RELINQUISH"); /* During a request, we are in limbo */ context->mhl_ghid.is_host = false; context->mhl_ghid.is_device = false; context->mhl_ghid.want_host = false; si_mhl_tx_send_msc_msg(context, MHL_MSC_MSG_RHID, request, hid_host_role_request_done); } struct cbus_req *rhidk_done(struct mhl_dev_context *mdev, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * Handle host-device negotiation request messages received from peer. * * A sink requesting Host role is typically just trying to determine * if the source has already requested the Host role, which is required * of the sink before starting the Device role (14.3.1.1). * As a source, we should have sent a host role request to the sink * before this (dev_context->mhl_ghid.is_host == true), so host * requests should be refused. * * When the sink relinquishes the Host role (assuming we let the sink have * it for some reason), we immediately want it back. */ void mhl_tx_hid_host_negotiation(struct mhl_dev_context *mdev) { uint8_t rhidk_status = MHL_RHID_NO_ERR; if (mdev->msc_msg_sub_command == MHL_MSC_MSG_RHID) { if (mdev->msc_msg_data == MHL_RHID_REQUEST_HOST) { if (mdev->mhl_ghid.is_host) rhidk_status = MHL_RHID_DENY; } else if (mdev->msc_msg_data == MHL_RHID_RELINQUISH_HOST) { mdev->mhl_ghid.want_host = true; } else { rhidk_status = MHL_RHID_INVALID; } MHL3_HID_DBG_INFO( "RHID: Received HID Host role %s result: %d\n", (mdev->msc_msg_data == MHL_RHID_REQUEST_HOST) ? "REQUEST" : "RELINQUISH", rhidk_status); /* Always RHIDK to the peer */ si_mhl_tx_send_msc_msg(mdev, MHL_MSC_MSG_RHIDK, rhidk_status, rhidk_done); } else if (mdev->msc_msg_sub_command == MHL_MSC_MSG_RHIDK) { if (mdev->msc_msg_data == MHL_RHID_NO_ERR) { if (mdev->msc_msg_last_data == MHL_RHID_REQUEST_HOST) { mdev->mhl_ghid.is_host = true; mdev->mhl_ghid.is_device = false; MHL3_HID_DBG_INFO("HID HOST role granted\n"); } else { mdev->mhl_ghid.is_host = false; mdev->mhl_ghid.is_device = true; MHL3_HID_DBG_INFO("HID DEVICE role granted\n"); } } else if (mdev->msc_msg_last_data == MHL_RHID_REQUEST_HOST) { mdev->mhl_ghid.is_host = false; mdev->mhl_ghid.is_device = true; MHL3_HID_DBG_INFO("HID HOST role DENIED\n"); } } } /* * Add a HID message to the eMSC output queue using one or more * eMSC BLOCK commands. */ static int si_mhl_tx_emsc_add_hid_message(struct mhl_dev_context *mdev, uint8_t hb0, uint8_t hb1, uint8_t *msg, int msg_len) { int i, cmd_size, fragment_count, msg_index, index; uint8_t *payload; uint8_t payload_size; uint16_t accum; uint16_t *pchksum; bool first_fragment; /* If message exceeds one (empty) BLOCK command buffer, we must * break it into fragments. Since the first fragment will be * the full command buffer size, the current request (if any) will be * sent before starting to add this one. */ i = msg_len + HID_MSG_HEADER_LEN + HID_MSG_CHKSUM_LEN; fragment_count = i / HID_FRAG_LEN_MAX; if ((fragment_count * HID_FRAG_LEN_MAX) != i) fragment_count++; /* This time don't include the standard header in the message length. */ cmd_size = HID_BURST_ID_LEN + HID_FRAG_HEADER_LEN + HID_MSG_HEADER_LEN + HID_MSG_CHKSUM_LEN + msg_len; first_fragment = true; msg_index = 0; index = 0; accum = 0; /* * TODO: Need to make sure there will be enough buffers * for the fragments. */ index = 0; while (fragment_count > 0) { payload_size = (cmd_size > EMSC_BLK_CMD_MAX_LEN) ? EMSC_BLK_CMD_MAX_LEN : cmd_size; /* TODO: Need up to 17 buffers (do we have them?) */ payload = (uint8_t *)si_mhl_tx_get_sub_payload_buffer( mdev, payload_size); if (payload == NULL) { MHL3_HID_DBG_ERR( "%ssi_mhl_tx_get_sub_payload_buffer failed%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); /* * TODO: Should be handled with an error code and * exit, but need to clean up any buffers that may * have been successfully allocated. */ } else { payload[index++] = (uint8_t)(burst_id_HID_PAYLOAD >> 8); payload[index++] = (uint8_t)(burst_id_HID_PAYLOAD); payload[index++] = payload_size - HID_BURST_ID_LEN; payload[index++] = fragment_count - 1; payload_size -= (HID_BURST_ID_LEN + HID_FRAG_HEADER_LEN); if (fragment_count == 1) payload_size -= HID_MSG_CHKSUM_LEN; if (first_fragment) { first_fragment = false; payload[index++] = hb0; payload[index++] = hb1; payload_size -= HID_MSG_HEADER_LEN; accum = ((((uint16_t)hb1) << 8) | hb0); pchksum = (uint16_t *)&msg[0]; for (i = 0; i < (msg_len / 2); i++) accum += pchksum[i]; if (msg_len & 0x01) accum += ((uint16_t)msg[msg_len - 1]); accum += (msg_len + HID_MSG_HEADER_LEN); } memcpy(&payload[index], &msg[msg_index], payload_size); msg_index += payload_size; index += payload_size; if (fragment_count == 1) { payload[index++] = (uint8_t)accum; payload[index++] = (uint8_t)(accum >> 8); } } fragment_count--; } return 0; } /* * Build a HID tunneling message and send it. * Returns non-zero if an error occurred, such as the device was * disconnected. * Always use the CTRL channel to send messages from the HOST. * * This function should be called wrapped in an isr_lock semaphore pair UNLESS * it is being called from the Titan interrupt handler. */ static int send_hid_msg(struct mhl3_hid_data *mhid, int outlen, bool want_ack) { struct mhl_dev_context *mdev = mhid->mdev; uint8_t hb0, hb1; int ret; hb0 = mhid->id << 4; if (want_ack) { hb1 = mhid->msg_count[0] | EMSC_HID_HB1_ACK; mhid->msg_count[0] = (mhid->msg_count[0] + 1) & EMSC_HID_HB1_MSG_CNT_FLD; } hb1 = mhid->msg_count[0] | (want_ack ? EMSC_HID_HB1_ACK : 0x00); mhid->msg_count[0] = (mhid->msg_count[0] + 1) & EMSC_HID_HB1_MSG_CNT_FLD; ret = si_mhl_tx_emsc_add_hid_message(mdev, hb0, hb1, mhid->out_data, outlen); if (ret == 0) si_mhl_tx_push_block_transactions(mdev); return ret; } /* * Send an MHL3 HID Command and wait for a response. * called only from a WORK QUEUE function; Do NOT call from * the Titan interrupt context. * Returns negative if an error occurred, such as the device was * disconnected, otherwise returns the number of bytes * received. */ static int send_hid_wait(struct mhl3_hid_data *mhid, int outlen, uint8_t *pin, int inlen, bool want_ack) { struct mhl_dev_context *mdev = mhid->mdev; int count, ret; /* * Take a hold of the wait lock. It will be released * when the response is received. This down call will be * released by an up call in the hid_message_processor() * function when the response message has been received. Not * conventional, but until I think of a better way, * this is it. */ if (down_timeout(&mhid->data_wait_lock, 1*HZ)) { MHL3_HID_DBG_ERR("Could not acquire data_wait lock !!!\n"); return -ENODEV; } MHL3_HID_DBG_INFO("Acquired data_wait_lock\n"); /* * Send the message when the Titan ISR is not active so * that we don't deadlock with eMsc block buffer allocation. */ if (down_interruptible(&mdev->isr_lock)) { MHL3_HID_DBG_ERR( "Could not acquire isr_lock for HID work queue\n"); ret = -ERESTARTSYS; goto done_release; } /* If canceling, get out. */ if (mhid->flags & HID_FLAGS_WQ_CANCEL) { ret = -ENODEV; up(&mdev->isr_lock); goto done_release; } ret = send_hid_msg(mhid, outlen, want_ack); up(&mdev->isr_lock); if (ret == 0) { /* Wait until a message is ready (when the driver unblocks). */ if (down_timeout(&mhid->data_wait_lock, 15*HZ)) { MHL3_HID_DBG_WARN("Timed out waiting for HID msg!\n"); ret = -EBUSY; /* * As odd as it may seem, we must release the semaphore * that we were waiting for because something * apparently has gone wrong with the communications * protocol and we need to start over. */ goto done_release; } /* Intercept HID_ACK{NO_DEV} messages. */ if ((mhid->in_data[0] == MHL3_HID_ACK) && (mhid->in_data[1] == HID_ACK_NODEV)) { ret = -ENODEV; goto done_release; } /* Message has been placed in our input buffer. */ count = (mhid->in_data_length > inlen) ? inlen : mhid->in_data_length; memcpy(pin, mhid->in_data, count); ret = count; } done_release: up(&mhid->data_wait_lock); return ret; } /* * Message protocol level ACK packet, as opposed to the HID_ACK message. */ static int send_ack_packet(struct mhl_dev_context *mdev, uint8_t hb0, uint8_t hb1) { uint8_t *payload; payload = (uint8_t *)si_mhl_tx_get_sub_payload_buffer( mdev, HID_BURST_ID_LEN + HID_ACK_PACKET_LEN); if (payload == NULL) { MHL3_HID_DBG_ERR( "%ssi_mhl_tx_get_sub_payload_buffer failed%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); /* * TODO: Should be handled with an error code and * exit, but need to clean up any buffers that may * have been successfully allocated. */ } else { payload[0] = (uint8_t)(burst_id_HID_PAYLOAD >> 8); payload[1] = (uint8_t)(burst_id_HID_PAYLOAD); payload[2] = HID_ACK_PACKET_LEN; payload[3] = hb1 | 0x80; payload[4] = hb0; } return 0; } /* * Send a HID_ACK message with the passed error code from the interrupt * context without using the mhid device structure (in case it's not there). */ static int mhl3_int_send_ack(struct mhl_dev_context *mdev, int reason, uint8_t hb0) { int status; uint8_t out_data[2]; MHL3_HID_DBG_WARN("HID_ACK reason code: %02X\n", reason); out_data[0] = MHL3_HID_ACK; out_data[1] = (reason < 0) ? HID_ACK_NODEV : reason; status = si_mhl_tx_emsc_add_hid_message(mdev, hb0, 0, out_data, 2); if (status < 0) MHL3_HID_DBG_ERR("MHID: Failed to send HID_ACK to device.\n"); return status; } /* * Send a HID_ACK message with the passed error code. */ static int mhl3_send_ack(struct mhl3_hid_data *mhid, uint8_t reason) { int status; struct mhl_dev_context *mdev = mhid->mdev; if (mhid == 0) return -ENODEV; MHL3_HID_DBG_WARN("%s - HID_ACK reason code: %02X\n", __func__, reason); MHL3_HID_DBG_ERR("mhid->mdev: %p\n", mhid->mdev); mhid->out_data[0] = MHL3_HID_ACK; mhid->out_data[1] = reason; /* * Send the message when the Titan ISR is not active so * that we don't deadlock with eMsc block buffer allocation. */ if (down_interruptible(&mdev->isr_lock)) { MHL3_HID_DBG_ERR("Could not acquire isr_lock for HID work\n"); return -ERESTARTSYS; } status = send_hid_msg(mhid, 2, false); up(&mdev->isr_lock); if (status < 0) MHL3_HID_DBG_ERR("Failed to send a HID_ACK to device.\n"); return status; } /* * Called from mhl3_hid_get_raw_report() and mhl3_hid_init_report() * to get report data from the device. * * It CANNOT be called from the Titan driver interrupt context, because * it blocks until it gets a response FROM the Titan interrupt. */ static int mhl3_hid_get_report(struct mhl3_hid_data *mhid, u8 report_type, u8 report_id, unsigned char *pin, int inlen) { int ret = 0; /* Build MHL3_GET_REPORT message and add it to the out queue. */ mhid->out_data[0] = MHL3_GET_REPORT; mhid->out_data[1] = report_type; mhid->out_data[2] = report_id; ret = send_hid_wait(mhid, 3, pin, inlen, false); if (ret < 0) MHL3_HID_DBG_ERR("Failed to retrieve report from device.\n"); return ret; } /* * Called from mhl3_hid_output_raw_report() to send report data to * the device. */ static int mhl3_hid_set_report(struct mhl3_hid_data *mhid, u8 report_type, u8 report_id, unsigned char *pout, size_t outlen) { struct mhl_dev_context *mdev = mhid->mdev; int ret = 0; mhid->out_data[0] = MHL3_SET_REPORT; mhid->out_data[1] = report_type; mhid->out_data[2] = report_id; memcpy(&mhid->out_data[3], pout, outlen); /* TODO: Need to make sure that this function is never called from * the Titan ISR (through linkage from a HID function called * from a HID report response or some such). Just needs some * research.... */ /* * Send the message when the Titan ISR is not active so * that we don't deadlock with eMsc block buffer allocation. */ if (down_interruptible(&mdev->isr_lock)) { MHL3_HID_DBG_ERR("Could not acquire isr_lock for HID work\n"); return -ERESTARTSYS; } ret = send_hid_msg(mhid, outlen + 3, false); up(&mdev->isr_lock); if (ret) { MHL3_HID_DBG_ERR("Failed to set a report to device.\n"); return ret; } return outlen + 3; } /* * TODO: Doesn't do anything yet */ static int mhl3_hid_set_power(struct mhl_dev_context *context, int power_state) { int ret = 0; return ret; } static int mhl3_hid_alloc_buffers(struct mhl3_hid_data *mhid, size_t report_size) { mhid->in_report_buf = kzalloc(report_size, GFP_KERNEL); if (mhid->in_report_buf == NULL) { kfree(mhid->hdesc); mhid->hdesc = NULL; kfree(mhid->in_report_buf); mhid->in_report_buf = NULL; return -ENOMEM; } mhid->bufsize = report_size; return 0; } /* * Called from the HID driver to obtain raw report data from the * device. * * It is not called from an interrupt context, so it is OK to block * until a reply is given. */ #if (LINUX_KERNEL_VER < 315) static int mhl3_hid_get_raw_report(struct hid_device *hid, unsigned char report_number, __u8 *buf, size_t count, unsigned char report_type) { struct mhl3_hid_data *mhid = hid->driver_data; int ret; if (report_type == HID_OUTPUT_REPORT) return -EINVAL; ret = mhl3_hid_get_report(mhid, report_type == HID_FEATURE_REPORT ? 0x03 : 0x01, report_number, buf, count); return ret; } /* * Called from the HID driver to send report data to the device. */ static int mhl3_hid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { struct mhl3_hid_data *mhid = hid->driver_data; int report_id = buf[0]; int ret; if (report_type == HID_INPUT_REPORT) return -EINVAL; if (report_id) { buf++; count--; } ret = mhl3_hid_set_report(mhid, report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, report_id, buf, count); if (report_id && ret >= 0) ret++; /* add report_id to the number of transfered bytes */ return ret; } #endif static int mhl3_hid_get_report_length(struct hid_report *report) { return ((report->size - 1) >> 3) + 1 + report->device->report_enum[report->type].numbered + 2; } /* * Traverse the supplied list of reports and find the longest */ static void mhl3_hid_find_max_report(struct hid_device *hid, unsigned int type, unsigned int *max) { struct hid_report *report; unsigned int size; /* * We should not rely on wMaxInputLength, as some devices may set * it to a wrong length. */ list_for_each_entry(report, &hid->report_enum[type].report_list, list) { size = mhl3_hid_get_report_length(report); if (*max < size) *max = size; } } /* * */ #if 0 static void mhl3_hid_init_report(struct hid_report *report, u8 *buffer, size_t bufsize) { struct hid_device *hid = report->device; struct mhl3_hid_data *mhid = hid->driver_data; unsigned int size, ret_size; size = mhl3_hid_get_report_length(report); if (mhl3_hid_get_report(mhid, report->type == HID_FEATURE_REPORT ? 0x03 : 0x01, report->id, buffer, size)) return; ret_size = buffer[0] | (buffer[1] << 8); if (ret_size != size) { MHL3_HID_DBG_ERR("Error in %s size:%d / ret_size:%d\n", __func__, size, ret_size); return; } /* * hid->driver_lock is held as we are in probe function, * we just need to setup the input fields, so using * hid_report_raw_event is safe. */ hid_report_raw_event(hid, report->type, buffer + 2, size - 2, 1); } #endif #if 0 /* * Initialize all reports. This gets the current value of all * input/feature reports for the device so that the HID-core can keep * them in internal structures. The structure is updated as further * device reports occur. */ static void mhl3_hid_init_reports(struct hid_device *hid) { struct mhl3_hid_data *mhid = hid->driver_data; struct hid_report *report; MHL3_HID_DBG_INFO("%s\n", __func__); list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) mhl3_hid_init_report( report, mhid->in_report_buf, mhid->bufsize); list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) mhl3_hid_init_report( report, mhid->in_report_buf, mhid->bufsize); } #endif /* * TODO: Doesn't do anything yet except manage the open count */ static int mhl3_hid_open(struct hid_device *hid) { struct mhl_dev_context *mdev = 0; struct mhl3_hid_data *mhid = 0; int ret = 0; if (hid) mhid = hid->driver_data; if (mhid == 0) return 0; mdev = mhid->mdev; mutex_lock(&mhl3_hid_open_mutex); if (!hid->open++) { ret = mhl3_hid_set_power(mdev, MHL3_HID_PWR_ON); if (ret) { hid->open--; goto done; } mhid->flags |= MHL3_HID_STARTED; } done: mutex_unlock(&mhl3_hid_open_mutex); return ret; } /* * TODO: Doesn't do anything yet except manage the open count */ static void mhl3_hid_close(struct hid_device *hid) { struct mhl_dev_context *mdev = 0; struct mhl3_hid_data *mhid = 0; if (hid) mhid = hid->driver_data; if (mhid == 0) return; mdev = mhid->mdev; /* * Protecting hid->open to make sure we don't restart * data acquisition due to a resumption we no longer * care about */ mutex_lock(&mhl3_hid_open_mutex); if (!--hid->open) { mhid->flags &= ~MHL3_HID_STARTED; mhl3_hid_set_power(mdev, MHL3_HID_PWR_SLEEP); } mutex_unlock(&mhl3_hid_open_mutex); } /* * TODO: Doesn't do anything yet */ static int mhl3_hid_power(struct hid_device *hid, int lvl) { int ret = 0; MHL3_HID_DBG_ERR("level: %d\n", lvl); switch (lvl) { case PM_HINT_FULLON: break; case PM_HINT_NORMAL: break; } return ret; } static struct hid_ll_driver mhl3_hid_ll_driver = { .open = mhl3_hid_open, .close = mhl3_hid_close, .power = mhl3_hid_power, }; /* * Use the MHL3 GET_MHID_DSCRPT message to request the HID Device * Descriptor from the device on the control channel. The device * should reply with an MHL3_MHID_DSCRPT message * * This function MUST be called from a work queue function. * */ static int mhid_fetch_hid_descriptor(struct mhl3_hid_data *mhid) { struct mhl3_hid_desc *hdesc; uint8_t *pdesc_raw; int ret = -ENODEV; int desc_len, raw_offset; /* The actual length of the data will likely not be this much. */ desc_len = sizeof(struct mhl3_hid_desc) + sizeof(mhid->desc_product_name) + sizeof(mhid->desc_mfg_name) + sizeof(mhid->desc_serial_number); pdesc_raw = kmalloc(desc_len, GFP_KERNEL); if (!pdesc_raw) { MHL3_HID_DBG_ERR("Couldn't allocate raw descriptor memory\n"); return -ENOMEM; } MHL3_HID_DBG_INFO("Fetching the HID descriptor\n"); /* * Build GET_MHID_DSCRPT message and add it to the out queue. * By setting the LANG_ID field bytes to 0, we allow the * device to send any language it chooses. */ mhid->out_data[0] = MHL3_GET_MHID_DSCRPT; mhid->out_data[1] = 0; mhid->out_data[2] = 0; mhid->opState = OP_STATE_WAIT_MHID_DSCRPT; ret = send_hid_wait(mhid, 3, pdesc_raw, desc_len, false); if ((ret < 0) || (mhid->opState == OP_STATE_IDLE)) { MHL3_HID_DBG_ERR( "Failed to get MHID descriptor: %d.\n", ret); goto raw_cleanup; } hdesc = kmalloc(desc_len, GFP_KERNEL); if (!hdesc) { MHL3_HID_DBG_ERR("Couldn't allocate hdesc descriptor memory\n"); ret = -ENOMEM; goto raw_cleanup; } /* dump_array(DBG_MSG_LEVEL_INFO, "HID descriptor", pdesc_raw, ret); */ /* Get the fixed length part and verify. */ memcpy(hdesc, pdesc_raw, sizeof(struct mhl3_hid_desc)); raw_offset = sizeof(struct mhl3_hid_desc); /* Do some simple checks. */ if (hdesc->bMHL3HIDmessageID != 0x05) { MHL3_HID_DBG_ERR("Invalid MHID_DSCRPT data\n"); ret = -EINVAL; goto hdesc_cleanup; } if (ret < (sizeof(struct mhl3_hid_desc) + hdesc->bProductNameSize + hdesc->bManufacturerNameSize + hdesc->bSerialNumberSize)) { memcpy(mhid->desc_product_name, &pdesc_raw[raw_offset], hdesc->bProductNameSize); raw_offset += hdesc->bProductNameSize; memcpy(mhid->desc_mfg_name, &pdesc_raw[raw_offset], hdesc->bManufacturerNameSize); raw_offset += hdesc->bManufacturerNameSize; memcpy(mhid->desc_serial_number, &pdesc_raw[raw_offset], hdesc->bSerialNumberSize); } /* * If this was an existing mhid being updated, free the previous * descriptor and reassign with the new one. */ kfree(mhid->hdesc); mhid->hdesc = hdesc; ret = 0; goto raw_cleanup; hdesc_cleanup: kfree(hdesc); raw_cleanup: kfree(pdesc_raw); return ret; } /* TODO: Eliminated ACPI stuff; i2c_hid_acpi_pdata. etc. */ /* * The users of the hid_device structure don't always check that * the hid_device structure has a valid hid_driver before trying to access * its members. Creating an empty structure at least avoids a * kernel panic. */ static struct hid_driver mhl3_hid_driver = { .name = "mhl3_hid", }; static struct hid_driver mhl3_mt3_hid_driver = { .name = "mhl3_hid-mt", .event = mhl3_mt_event, .input_mapping = mhl3_mt_input_mapping, .input_mapped = mhl3_mt_input_mapped, .feature_mapping = mt_feature_mapping, #if (LINUX_KERNEL_VER >= 311) .input_configured = mt_input_configured, .report = mt_report, #endif }; /* * Get report descriptors from the device and parse them. * The Report Descriptor describes the report ID and the type(s) * of data it contains. During operation, when the device returns * a report with a specific ReportID, the HID core will know how * to parse it. */ int mhl3_hid_report_desc_parse(struct mhl3_hid_data *mhid) { unsigned int report_len; int ret; uint8_t *report_desc_raw = NULL; struct hid_device *hdev; hdev = mhid->hid; report_desc_raw = kmalloc(HID_MAX_DESCRIPTOR_SIZE, GFP_KERNEL); if (!report_desc_raw) { MHL3_HID_DBG_ERR("Allocate raw report descriptor mem failed\n"); ret = -ENOMEM; goto err; } mhid->out_data[0] = MHL3_GET_REPORT_DSCRPT; mhid->opState = OP_STATE_WAIT_REPORT_DSCRPT; ret = send_hid_wait(mhid, 1, report_desc_raw, HID_MAX_DESCRIPTOR_SIZE, false); if (ret < 0) { MHL3_HID_DBG_ERR( "Failed to get descriptor from device: %d.\n", ret); goto err; } if (mhid->opState == OP_STATE_IDLE) goto done; if (report_desc_raw[0] != MHL3_REPORT_DSCRPT) { MHL3_HID_DBG_ERR( "Invalid response to MHL3_GET_REPORT_DSCRPT\n"); ret = -EINVAL; goto err; } report_len = report_desc_raw[2]; report_len <<= 8; report_len |= report_desc_raw[1]; report_len = (report_len < (ret-3)) ? report_len : (ret-3); ret = hid_parse_report(hdev, &report_desc_raw[3], report_len); if (ret) goto err; #if (LINUX_KERNEL_VER >= 305) ret = hid_open_report(hdev); if (ret) { MHL3_HID_DBG_ERR("hid_open_report failed\n"); goto err; } #endif goto done; err: MHL3_HID_DBG_ERR("WORK QUEUE mhl3_hid_report_desc_parse() FAIL\n"); done: kfree(report_desc_raw); return ret; } static void mhl3_disconnect_and_destroy_hid_device(struct mhl3_hid_data *mhid) { if (mhid->hid) { if (mhid->hid->claimed & HID_CLAIMED_INPUT) { hidinput_disconnect(mhid->hid); mhid->hid->claimed &= ~HID_CLAIMED_INPUT; } hid_destroy_device(mhid->hid); mhid->hid = 0; } } /* * The second part of the mhl3_hid_add() function, implemented as * a work queue. It is entered at opState == OP_STATE_IDLE * A failure here deletes the mhid device. */ static void mhid_add_work(struct work_struct *workdata) { struct mhl3_hid_data *mhid = ((struct hid_add_work_struct *)workdata)->mhid; struct mhl_dev_context *mdev = mhid->mdev; struct hid_device *hdev; struct hid_device_id id = {0}; unsigned int bufsize = HID_MIN_BUFFER_SIZE; int ret; mhid->flags |= HID_FLAGS_WQ_ACTIVE; MHL3_HID_DBG_ERR("WORK QUEUE function executing\n"); mdev->mhl_hid[mhid->id] = mhid; ret = mhid_fetch_hid_descriptor(mhid); /* Allocates hdesc */ if ((ret < 0) || (mhid->opState == OP_STATE_IDLE)) goto mhid_cleanup; /* Get a HID device if this is not an update of the existing HID. */ if (mhid->hid == NULL) { hdev = hid_allocate_device(); if (IS_ERR(hdev)) { ret = PTR_ERR(hdev); goto mhid_cleanup; } mhid->hid = hdev; hdev->driver = &mhl3_mt3_hid_driver; hdev->driver_data = mhid; id.vendor = mhid->hdesc->wHIDVendorID; id.product = mhid->hdesc->wHIDProductID; MHL3_HID_DBG_ERR("Allocated HID\n"); } hdev = mhid->hid; hdev->ll_driver = &mhl3_hid_ll_driver; #if (LINUX_KERNEL_VER < 315) hdev->hid_get_raw_report = mhl3_hid_get_raw_report; hdev->hid_output_raw_report = mhl3_hid_output_raw_report; #endif hdev->dev.parent = NULL; hdev->bus = BUS_VIRTUAL; hdev->version = mhid->hdesc->wBcdHID; hdev->vendor = mhid->hdesc->wHIDVendorID; hdev->product = mhid->hdesc->wHIDProductID; snprintf(hdev->name, sizeof(hdev->name), "MHL3 HID %04hX:%04hX", hdev->vendor, hdev->product); /* Check for multitouch device first. */ ret = mhl3_mt_add(mhid, &id); if (ret) { MHL3_HID_DBG_INFO("NOT Multitouch, trying generic HID\n"); hdev->driver = &mhl3_hid_driver; ret = mhl3_hid_report_desc_parse(mhid); } if ((ret < 0) || (mhid->opState == OP_STATE_IDLE)) goto mhid_cleanup; if ((mhid->flags & MHL3_HID_CONNECTED) == 0) { if (!hidinput_connect(hdev, 0)) { MHL3_HID_DBG_INFO( "%s hidinput_connect succeeded\n", __func__); hdev->claimed |= HID_CLAIMED_INPUT; mhid->flags |= MHL3_HID_CONNECTED; } else { MHL3_HID_DBG_ERR( "%s hidinput_connect FAILED\n", __func__); goto mhid_cleanup; } } /* * Allocate some report buffers and read the initial state of * the INPUT and FEATURE reports. */ mhl3_hid_find_max_report(hdev, HID_INPUT_REPORT, &bufsize); mhl3_hid_find_max_report(hdev, HID_OUTPUT_REPORT, &bufsize); mhl3_hid_find_max_report(hdev, HID_FEATURE_REPORT, &bufsize); if (bufsize > mhid->bufsize) { kfree(mhid->in_report_buf); mhid->in_report_buf = NULL; ret = mhl3_hid_alloc_buffers(mhid, bufsize); if (ret) goto mhid_cleanup; } /* if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS)) mhl3_hid_init_reports(hdev); */ mhid->opState = OP_STATE_CONNECTED; MHL3_HID_DBG_ERR("WORK QUEUE function SUCCESS\n"); mhid->flags &= ~HID_FLAGS_WQ_ACTIVE; return; mhid_cleanup: mhl3_send_ack(mhid, HID_ACK_NODEV); mhid->flags |= HID_FLAGS_WQ_CANCEL; MHL3_HID_DBG_ERR("WORK QUEUE function FAIL - mhid: %p\n", mhid); mhl3_disconnect_and_destroy_hid_device(mhid); /* * Don't destroy the mhid while an interrupt is in progress on the * off chance that the message we were waiting for came in after the * timeout. */ if (down_interruptible(&mdev->isr_lock)) { MHL3_HID_DBG_ERR("Could not acquire isr_lock\n"); return; } kfree(mhid->hdesc); kfree(mhid->in_report_buf); kfree(mhid); mdev->mhl_hid[mhid->id] = 0; up(&mdev->isr_lock); MHL3_HID_DBG_ERR("WORK QUEUE function exit\n"); } /* * Create and initialize the device control structures and get added to * the system HID device list. This is the equivalent to the probe * function of normal HID-type drivers, but it is called when the * MHL transport receives the eMSC Block transfer HID Tunneling request * message MHL3_DSCRPT_UPDATE * * Called from a Titan interrupt handler. All events in the MHL driver * are handled in interrupt handlers, so the real work here is performed on * a work queue so the function can wait for responses from the driver. */ static int mhid_add(struct mhl_dev_context *mdev, int dev_id) { struct mhl3_hid_data *mhid; int status; MHL3_HID_DBG_ERR("Adding device %d\n", dev_id); if (dev_id >= MAX_HID_MESSAGE_CHANNELS) return -EINVAL; mhid = kzalloc(sizeof(struct mhl3_hid_data), GFP_KERNEL); if (!mhid) return -ENOMEM; sema_init(&mhid->data_wait_lock, 1); mhid->mhl3_work.mhid = mhid; mhid->id = dev_id; mhid->mdev = mdev; INIT_WORK((struct work_struct *)&mhid->mhl3_work, mhid_add_work); status = queue_work( mdev->hid_work_queue, (struct work_struct *)&mhid->mhl3_work); return 0; } /* * This is the reverse of the mhl3_hid_add() function. * The device_id parameter is for when we support multiple HID devices. */ static int mhid_remove(struct mhl_dev_context *mdev, int dev_id) { struct mhl3_hid_data *mhid; if (dev_id >= MAX_HID_MESSAGE_CHANNELS) return -EINVAL; mhid = mdev->mhl_hid[dev_id]; if (mhid == NULL) return 0; /* * If work queue is not active, we need to free the mhid * here, otherwise let the WQ do it. */ if ((mhid->flags & HID_FLAGS_WQ_ACTIVE) == 0) { mhl3_disconnect_and_destroy_hid_device(mhid); kfree(mhid->hdesc); kfree(mhid->in_report_buf); kfree(mhid); mdev->mhl_hid[dev_id] = 0; } else { mhid->flags |= HID_FLAGS_WQ_CANCEL; /* Release the data wait to let the work queue finish. */ if (down_trylock(&mhid->data_wait_lock)) MHL3_HID_DBG_ERR("Waiting for data when HID removed\n"); up(&mhid->data_wait_lock); } return 0; } /* * Remove all MHID devices. */ void mhl3_hid_remove_all(struct mhl_dev_context *mdev) { int dev_id; for (dev_id = 0; dev_id < MAX_HID_MESSAGE_CHANNELS; dev_id++) { if (mdev->mhl_hid[dev_id]) mhid_remove(mdev, dev_id); } } /* * We have received a completed MHL3 HID Tunneling message. Parse the * header to decide what to do with it. * Called indirectly from the Titan interrupt handler. */ static void hid_message_processor(struct mhl_dev_context *mdev, uint8_t hb0, uint8_t hb1, uint8_t *pmsg, int length) { struct mhl3_hid_data *mhid; uint8_t msg_id; bool want_ack; bool matched_expected_message; int opState, dev_id, hid_msg_count, header_len, ret; int msg_channel; dev_id = (hb0 >> 4) & 0x0F; msg_channel = (hb0 & 0x01); want_ack = ((hb1 & 0x80) != 0); hid_msg_count = hb1 & 0x7F; msg_id = pmsg[0]; matched_expected_message = false; if (msg_id != 1) { MHL3_HID_DBG_WARN( "Received message: msg_id: %02X, deviceID: %02X\n", msg_id, dev_id); } mhid = mdev->mhl_hid[dev_id]; opState = OP_STATE_IDLE; if (mhid == 0) { MHL3_HID_DBG_WARN("Message received with mhid == 0\n"); if (msg_id != MHL3_DSCRPT_UPDATE) { MHL3_HID_DBG_ERR( "Message NOT MHL3_DSCRPT_UPDATE: %02X\n", msg_id); mhl3_int_send_ack(mdev, HID_ACK_TIMEOUT, hb0); return; } } else { if (want_ack) { if (hid_msg_count != (mhid->msg_count[msg_channel] + 1)) { send_ack_packet( mdev, hb0, mhid->msg_count[msg_channel]); return; } else { mhid->msg_count[msg_channel] += 1; } } opState = mhid->opState; } /* if (want_ack) send_ack_packet(mdev, hb0, hb1); */ /* * If a DSCRPT_UPDATE we need to queue a new device add, no matter * if the device already exists or not. If it already exists, * destroy it first. */ if ((msg_id == MHL3_DSCRPT_UPDATE) && (mhid != 0)) { opState = OP_STATE_IDLE; mhid_remove(mdev, dev_id); } switch (opState) { case OP_STATE_IDLE: if (msg_id == MHL3_DSCRPT_UPDATE) { MHL3_HID_DBG_ERR("call mhid_add from OP_STATE_IDLE\n"); ret = mhid_add(mdev, dev_id); if (ret < 0) mhl3_int_send_ack(mdev, ret, hb0); } else { mhl3_int_send_ack(mdev, HID_ACK_PROTV, hb0); } return; case OP_STATE_WAIT_MHID_DSCRPT: MHL3_HID_DBG_INFO("OP_STATE_WAIT_MHID_DSCRPT\n"); if ((msg_id == MHL3_MHID_DSCRPT) || (msg_id == MHL3_DSCRPT_UPDATE)) { matched_expected_message = true; } break; case OP_STATE_WAIT_REPORT_DSCRPT: MHL3_HID_DBG_INFO("OP_STATE_WAIT_REPORT_DSCRPT\n"); if ((msg_id == MHL3_REPORT_DSCRPT) || (msg_id == MHL3_DSCRPT_UPDATE)) matched_expected_message = true; break; case OP_STATE_WAIT_REPORT: MHL3_HID_DBG_INFO("OP_STATE_WAIT_REPORT\n"); if ((msg_id == MHL3_REPORT) || (msg_id == MHL3_DSCRPT_UPDATE)) matched_expected_message = true; break; case OP_STATE_CONNECTED: /* * If sent by the interrupt channel, the message is NOT from a * MHL3_GET_REPORT request by the host, and we send it * directly to the HID core. Otherwise, we return it via the * WORK QUEUE. */ switch (msg_id) { case MHL3_REPORT: if (msg_channel == HID_ACHID_INT) { /* Send the report directly to the HID core, * minus the MSG_ID, REPORT_TYPE, REPORT_ID * (if present), and LENGTH (2 bytes). */ /* * According to MHL spec 3.2, the * mhl_hid_report_msg.id member should reflect * the report ID for numbered reports, and not * be present for non-numbered reports. * However, for numbered reports, the report * number is already in the data being passed, * and we don't need it for anything. Since * the HID device side driver does not * parse the HID data, it has no way to tell * if the report is numbered. Therefore, it * ALWAYS includes this byte and sets it to 0. */ header_len = sizeof(struct mhl_hid_report_msg) - 1; ret = hid_input_report( mhid->hid, HID_INPUT_REPORT, pmsg + header_len, length - header_len, 1); return; } matched_expected_message = true; break; case MHL3_DSCRPT_UPDATE: MHL3_HID_DBG_ERR( "call mhid_add from OP_STATE_CONNECTED\n"); ret = mhid_add(mdev, dev_id); if (ret < 0) mhl3_int_send_ack(mdev, ret, hb0); return; break; default: if (msg_id != MHL3_HID_ACK) matched_expected_message = true; break; } break; } if (!matched_expected_message && (msg_id != MHL3_HID_ACK)) { mhl3_int_send_ack(mdev, HID_ACK_PROTV, hb0); return; } else if (matched_expected_message && (msg_id == MHL3_DSCRPT_UPDATE)) { /* * opstate is OP_STATE_WAIT_MHID_DSCRPT, * OP_STATE_WAIT_REPORT_DSCRPT, or OP_STATE_WAIT_REPORT, and * we're still running the work queue function, so we have to * restart it. */ mhid->opState = OP_STATE_IDLE; } /* If someone was waiting for this data, let them know it's here. */ if (down_trylock(&mhid->data_wait_lock)) { memcpy(mhid->in_data, pmsg, length); mhid->in_data_length = length; } else { /* TODO: If not on a WORK QUEUE, where does it go? */ MHL3_HID_DBG_ERR("Sending received msg into the ether!!!\n"); } /* * Either the try was successful, in which case no one was waiting * for the message, or it wasn't, meaning someone WAS waiting. In * either case we need to release the semaphore. */ up(&mhid->data_wait_lock); } static void validate_ack(struct mhl_dev_context *mdev, uint8_t *pmsg, int length) { /* uint8_t ack_msg, channel; */ int dev_id; struct mhl3_hid_data *mhid; if (length != HID_ACK_PACKET_LEN) return; dev_id = (pmsg[1] & HID_HB0_DEV_ID_MSK) >> 4; mhid = mdev->mhl_hid[dev_id]; if (mhid == 0) return; /* ack_msg = pmsg[0] & HID_FRAG_HB0_CNT_MSK; channel = pmsg[1] & HID_HB0_ACHID_MSK; if (ack_msg == mhid->msg_count[channel]) resend_last_message(); */ } /* * Accumulate fragments of a HID message until the entire message has * been received, then dispatch it properly. * Called from the Titan interrupt */ void build_received_hid_message(struct mhl_dev_context *mdev, uint8_t *pmsg, int length) { int i, fragment_count = 0; uint16_t *pchksum; uint16_t accum, msg_chksum; struct mhl3_hid_global_data *emsc = &mdev->mhl_ghid; if (length == 0) return; switch (emsc->hid_receive_state) { case EMSC_RCV_MSG_START: if ((pmsg[0] & HID_FRAG_HB0_TYPE) == HID_FRAG_HB0_TYPE_ACK) { validate_ack(mdev, pmsg, length); return; } fragment_count = pmsg[0]; if (length >= (HID_FRAG_HEADER_LEN + HID_MSG_HEADER_LEN + HID_MSG_CHKSUM_LEN + 1)) { emsc->hb0 = pmsg[1]; emsc->hb1 = pmsg[2]; length -= 3; memcpy(&emsc->in_buf[0], &pmsg[3], length); } emsc->msg_length = length; emsc->hid_receive_state = EMSC_RCV_MSG_NEXT; break; case EMSC_RCV_MSG_NEXT: fragment_count = pmsg[0]; if (length >= (HID_FRAG_HEADER_LEN + 1)) { length--; memcpy(&emsc->in_buf[emsc->msg_length], &pmsg[1], length); emsc->msg_length += length; } break; default: /* This is an error, so don't dispatch anything. */ fragment_count = 1; break; } /* If the end of the message, dispatch it. */ if (fragment_count == 0) { emsc->hid_receive_state = EMSC_RCV_MSG_START; accum = 0; pchksum = (uint16_t *)&emsc->in_buf[0]; emsc->msg_length -= HID_MSG_CHKSUM_LEN; for (i = 0; i < (emsc->msg_length / 2); i++) accum += pchksum[i]; accum += ((uint16_t)emsc->hb0 | (((uint16_t)emsc->hb1) << 8)); if (emsc->msg_length & 0x01) accum += ((uint16_t)emsc->in_buf[emsc->msg_length - 1]); /* Add in length of message including HB0/HB1. */ accum += emsc->msg_length + HID_MSG_HEADER_LEN; msg_chksum = *((uint16_t *)&emsc->in_buf[emsc->msg_length]); if (accum != msg_chksum) { MHL3_HID_DBG_ERR( "HID MSG CKSM fail, ignoring message: " "Act: %04X Exp: %04X\n", accum, msg_chksum); /* Request a re-try. */ if (emsc->hb1 & EMSC_HID_HB1_ACK) { send_ack_packet( mdev, emsc->hb0, (emsc->hb1 & EMSC_HID_HB1_MSG_CNT_FLD) - 1); } return; } hid_message_processor(mdev, emsc->hb0, emsc->hb1, emsc->in_buf, emsc->msg_length); } } #ifdef SI_CONFIG_PM_SLEEP /* this was originally CONFIG_PM_SLEEP, * but we don't want the compiler warnings */ /* * TODO: This is used during system suspend and hibernation as well * as normal runtime PM. Needs work. */ static int mhl3_hid_suspend(struct device *dev) { /* struct i2c_client *client = to_i2c_client(dev); if (device_may_wakeup(&client->dev)) enable_irq_wake(client->irq); mhl3_hid_set_power(client, I2C_HID_PWR_SLEEP); */ return 0; } /* * TODO: This is used during system suspend and hibernation as well * as normal runtime PM. Needs work. */ static int mhl3_hid_resume(struct device *dev) { /* int ret; struct i2c_client *client = to_i2c_client(dev); ret = i2c_hid_hwreset(client); if (ret) return ret; if (device_may_wakeup(&client->dev)) disable_irq_wake(client->irq); */ return 0; } #endif