/* * HID over I2C protocol implementation * * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France * Copyright (c) 2012 Red Hat, Inc * * This code is partly based on "USB HID support for Linux": * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> 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.
*/
staticint i2c_hid_probe_address(struct i2c_hid *ihid)
{ int ret;
/* * Some STM-based devices need 400µs after a rising clock edge to wake * from deep sleep, in which case the first read will fail. Try after a * short sleep to see if the device came alive on the bus. Certain * Weida Tech devices also need this.
*/
ret = i2c_smbus_read_byte(ihid->client); if (ret < 0) {
usleep_range(400, 500);
ret = i2c_smbus_read_byte(ihid->client);
} return ret < 0 ? ret : 0;
}
staticint i2c_hid_xfer(struct i2c_hid *ihid,
u8 *send_buf, int send_len, u8 *recv_buf, int recv_len)
{ struct i2c_client *client = ihid->client; struct i2c_msg msgs[2] = { 0 }; int n = 0; int ret;
if (send_len) {
i2c_hid_dbg(ihid, "%s: cmd=%*ph\n",
__func__, send_len, send_buf);
/* Command register goes first */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
length += sizeof(__le16); /* Next is GET_REPORT command */
length += i2c_hid_encode_command(ihid->cmdbuf + length,
I2C_HID_OPCODE_GET_REPORT,
report_type, report_id); /* * Device will send report data through data register. Because * command can be either 2 or 3 bytes destination for the data * register may be not aligned.
*/
put_unaligned_le16(le16_to_cpu(ihid->hdesc.wDataRegister),
ihid->cmdbuf + length);
length += sizeof(__le16);
/* * In addition to report data device will supply data length * in the first 2 bytes of the response, so adjust .
*/
error = i2c_hid_xfer(ihid, ihid->cmdbuf, length,
ihid->rawbuf, recv_len + sizeof(__le16)); if (error) {
dev_err(&ihid->client->dev, "failed to get a report from device: %d\n", error); return error;
}
/* The buffer is sufficiently aligned */
ret_count = le16_to_cpup((__le16 *)ihid->rawbuf);
/* Check for empty report response */ if (ret_count <= sizeof(__le16)) return 0;
if (report_id && recv_len != 0 && recv_buf[0] != report_id) {
dev_err(&ihid->client->dev, "device returned incorrect report (%d vs %d expected)\n",
recv_buf[0], report_id); return -EINVAL;
}
return recv_len;
}
static size_t i2c_hid_format_report(u8 *buf, int report_id, const u8 *data, size_t size)
{
size_t length = sizeof(__le16); /* reserve space to store size */
if (report_id)
buf[length++] = report_id;
memcpy(buf + length, data, size);
length += size;
/* Store overall size in the beginning of the buffer */
put_unaligned_le16(length, buf);
return length;
}
/** * i2c_hid_set_or_send_report: forward an incoming report to the device * @ihid: the i2c hid device * @report_type: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT * @report_id: the report ID * @buf: the actual data to transfer, without the report ID * @data_len: size of buf * @do_set: true: use SET_REPORT HID command, false: send plain OUTPUT report
*/ staticint i2c_hid_set_or_send_report(struct i2c_hid *ihid,
u8 report_type, u8 report_id, const u8 *buf, size_t data_len, bool do_set)
{
size_t length = 0; int error;
i2c_hid_dbg(ihid, "%s\n", __func__);
if (data_len > ihid->bufsize) return -EINVAL;
if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0) return -ENOSYS;
guard(mutex)(&ihid->cmd_lock);
if (do_set) { /* Command register goes first */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
length += sizeof(__le16); /* Next is SET_REPORT command */
length += i2c_hid_encode_command(ihid->cmdbuf + length,
I2C_HID_OPCODE_SET_REPORT,
report_type, report_id); /* * Report data will go into the data register. Because * command can be either 2 or 3 bytes destination for * the data register may be not aligned.
*/
put_unaligned_le16(le16_to_cpu(ihid->hdesc.wDataRegister),
ihid->cmdbuf + length);
length += sizeof(__le16);
} else { /* * With simple "send report" all data goes into the output * register.
*/
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wOutputRegister;
length += sizeof(__le16);
}
error = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0); if (error) {
dev_err(&ihid->client->dev, "failed to set a report to device: %d\n", error); return error;
}
return data_len;
}
staticint i2c_hid_set_power_command(struct i2c_hid *ihid, int power_state)
{
size_t length;
staticint i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
{ int ret;
i2c_hid_dbg(ihid, "%s\n", __func__);
/* * Some STM-based devices need 400µs after a rising clock edge to wake * from deep sleep, in which case the first request will fail due to * the address not being acknowledged. Try after a short sleep to see * if the device came alive on the bus. Certain Weida Tech devices also * need this.
*/
ret = i2c_hid_set_power_command(ihid, power_state); if (ret && power_state == I2C_HID_PWR_ON) {
usleep_range(400, 500);
ret = i2c_hid_set_power_command(ihid, I2C_HID_PWR_ON);
}
if (ret)
dev_err(&ihid->client->dev, "failed to change power setting.\n");
/* * The HID over I2C specification states that if a DEVICE needs time * after the PWR_ON request, it should utilise CLOCK stretching. * However, it has been observered that the Windows driver provides a * 1ms sleep between the PWR_ON and RESET requests. * According to Goodix Windows even waits 60 ms after (other?) * PWR_ON requests. Testing has confirmed that several devices * will not work properly without a delay after a PWR_ON request.
*/ if (!ret && power_state == I2C_HID_PWR_ON)
msleep(60);
/* * This prevents sending feature reports while the device is * being reset. Otherwise we may lose the reset complete * interrupt.
*/
lockdep_assert_held(&ihid->reset_lock);
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON); if (ret) return ret;
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0); if (ret) {
dev_err(&ihid->client->dev, "failed to reset device: %d\n", ret); break;
}
return 0;
}
/* Clean up if sending reset command failed */
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP); return ret;
}
staticint i2c_hid_finish_hwreset(struct i2c_hid *ihid)
{ int ret = 0;
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
msleep(100);
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
} elseif (!wait_event_timeout(ihid->wait,
!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
msecs_to_jiffies(1000))) {
dev_warn(&ihid->client->dev, "device did not ack reset within 1000 ms\n");
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
}
i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
/* At least some SIS devices need this after reset */ if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET))
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
/* * Traverse the supplied list of reports and find the longest
*/ staticvoid i2c_hid_find_max_report(struct hid_device *hid, unsignedint type, unsignedint *max)
{ struct hid_report *report; unsignedint 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 = i2c_hid_get_report_length(report); if (*max < size)
*max = size;
}
}
if (report_type == HID_OUTPUT_REPORT) return -EINVAL;
/* * In case of unnumbered reports the response from the device will * not have the report ID that the upper layers expect, so we need * to stash it the buffer ourselves and adjust the data size.
*/ if (!report_id) {
buf[0] = 0;
buf++;
count--;
}
if (report_type == HID_INPUT_REPORT) return -EINVAL;
mutex_lock(&ihid->reset_lock);
/* * Note that both numbered and unnumbered reports passed here * are supposed to have report ID stored in the 1st byte of the * buffer, so we strip it off unconditionally before passing payload * to i2c_hid_set_or_send_report which takes care of encoding * everything properly.
*/
ret = i2c_hid_set_or_send_report(ihid,
report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
report_id, buf + 1, count - 1, do_set);
if (ret >= 0)
ret++; /* add report_id to the number of transferred bytes */
rsize = le16_to_cpu(hdesc->wReportDescLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
dbg_hid("weird size of report descriptor (%u)\n", rsize); return -EINVAL;
}
mutex_lock(&ihid->reset_lock); do {
ret = i2c_hid_start_hwreset(ihid); if (ret == 0)
ret = i2c_hid_finish_hwreset(ihid); else
msleep(1000);
} while (tries-- > 0 && ret);
mutex_unlock(&ihid->reset_lock);
if (!device_may_wakeup(&client->dev))
i2c_hid_core_power_up(ihid);
enable_irq(client->irq);
/* On Goodix 27c6:0d42 wait extra time before device wakeup. * It's not clear why but if we send wakeup too early, the device will * never trigger input interrupts.
*/ if (ihid->quirks & I2C_HID_QUIRK_DELAY_WAKEUP_AFTER_RESUME)
msleep(1500);
/* Instead of resetting device, simply powers the device on. This * solves "incomplete reports" on Raydium devices 2386:3118 and * 2386:4B33 and fixes various SIS touchscreens no longer sending * data after a suspend/resume. * * However some ALPS touchpads generate IRQ storm without reset, so * let's still reset them here.
*/ if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) {
mutex_lock(&ihid->reset_lock);
ret = i2c_hid_start_hwreset(ihid); if (ret == 0)
ret = i2c_hid_finish_hwreset(ihid);
mutex_unlock(&ihid->reset_lock);
} else {
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
}
if (ret) return ret;
return hid_driver_reset_resume(hid);
}
/* * Check that the device exists and parse the HID descriptor.
*/ staticint __i2c_hid_core_probe(struct i2c_hid *ihid)
{ struct i2c_client *client = ihid->client; struct hid_device *hid = ihid->hid; int ret;
ret = i2c_hid_probe_address(ihid); if (ret < 0) {
i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret); return -ENXIO;
}
ret = i2c_hid_fetch_hid_descriptor(ihid); if (ret < 0) {
dev_err(&client->dev, "Failed to fetch the HID Descriptor\n"); return ret;
}
ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV)
hid_err(client, "can't add hid device: %d\n", ret);
disable_irq(client->irq); return ret;
}
/* At least some QTEC devices need this after initialization */ if (ihid->quirks & I2C_HID_QUIRK_RE_POWER_ON)
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
return ret;
}
staticint i2c_hid_core_probe_panel_follower(struct i2c_hid *ihid)
{ int ret;
ret = i2c_hid_core_power_up(ihid); if (ret) return ret;
ret = __i2c_hid_core_probe(ihid); if (ret) goto err_power_down;
ret = i2c_hid_core_register_hid(ihid); if (ret) goto err_power_down;
/* * hid->version is set on the first power up. If it's still zero then * this is the first power on so we should perform initial power up * steps.
*/ if (!hid->version)
ret = i2c_hid_core_probe_panel_follower(ihid); else
ret = i2c_hid_core_resume(ihid);
if (ret)
dev_warn(&ihid->client->dev, "Power on failed: %d\n", ret); else
WRITE_ONCE(ihid->panel_follower_work_finished, true);
/* * The work APIs provide a number of memory ordering guarantees * including one that says that memory writes before schedule_work() * are always visible to the work function, but they don't appear to * guarantee that a write that happened in the work is visible after * cancel_work_sync(). We'll add a write memory barrier here to match * with i2c_hid_core_panel_unpreparing() to ensure that our write to * panel_follower_work_finished is visible there.
*/
smp_wmb();
}
/* * Powering on a touchscreen can be a slow process. Queue the work to * the system workqueue so we don't block the panel's power up.
*/
WRITE_ONCE(ihid->panel_follower_work_finished, false);
schedule_work(&ihid->panel_follower_work);
/* * If we're not in control of our own power up/power down then we can't * do the logic to manage wakeups. Give a warning if a user thought * that was possible then force the capability off.
*/ if (device_can_wakeup(dev)) {
dev_warn(dev, "Can't wakeup if following panel\n");
device_set_wakeup_capable(dev, false);
}
ret = drm_panel_add_follower(dev, &ihid->panel_follower); if (ret) return ret;
return 0;
}
int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
u16 hid_descriptor_address, u32 quirks)
{ int ret; struct i2c_hid *ihid; struct hid_device *hid;
dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);
if (!client->irq) {
dev_err(&client->dev, "HID over i2c has not been provided an Int IRQ\n"); return -EINVAL;
}
if (client->irq < 0) { if (client->irq != -EPROBE_DEFER)
dev_err(&client->dev, "HID over i2c doesn't have a valid IRQ\n"); return client->irq;
}
ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL); if (!ihid) return -ENOMEM;
/* we need to allocate the command buffer without knowing the maximum * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the
* real computation later. */
ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE); if (ret < 0) return ret;
device_enable_async_suspend(&client->dev);
hid = hid_allocate_device(); if (IS_ERR(hid)) {
ret = PTR_ERR(hid); goto err_free_buffers;
}
/* Power on and probe unless device is a panel follower. */ if (!ihid->is_panel_follower) {
ret = i2c_hid_core_power_up(ihid); if (ret < 0) goto err_destroy_device;
ret = __i2c_hid_core_probe(ihid); if (ret < 0) goto err_power_down;
}
ret = i2c_hid_init_irq(client); if (ret < 0) goto err_power_down;
/* * If we're a panel follower, we'll register when the panel turns on; * otherwise we do it right away.
*/ if (ihid->is_panel_follower)
ret = i2c_hid_core_register_panel_follower(ihid); else
ret = i2c_hid_core_register_hid(ihid); if (ret) goto err_free_irq;
/* * If we're a follower, the act of unfollowing will cause us to be * powered down. Otherwise we need to manually do it.
*/ if (ihid->is_panel_follower)
drm_panel_remove_follower(&ihid->panel_follower); else
i2c_hid_core_suspend(ihid, true);
hid = ihid->hid;
hid_destroy_device(hid);
free_irq(client->irq, ihid);
if (ihid->bufsize)
i2c_hid_free_buffers(ihid);
}
EXPORT_SYMBOL_GPL(i2c_hid_core_remove);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.