diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 687705c5079422a82b9977e5694d0cd45b2a47bc..d575e1852d8c6666cc5e10e77019f7f9811df46b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -388,6 +388,13 @@ config HID_ICADE To compile this driver as a module, choose M here: the module will be called hid-icade. +config HID_ITE + tristate "ITE devices" + depends on HID + default !EXPERT + ---help--- + Support for ITE devices not fully compliant with HID standard. + config HID_TWINHAN tristate "Twinhan IR remote control" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fef027bc7fa3b17b6cd337c7d2460bd3245314d9..05ac8d375aeb40fcbbcd711d0279b18e7773a7c3 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_ICADE) += hid-icade.o +obj-$(CONFIG_HID_ITE) += hid-ite.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index dfd34d7f32123b5c338a4a733f1b8a9bacb22ec3..09f12236e23f261e2d32040fe4d4807d878f48e6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1985,6 +1985,9 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_ICADE) { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, #endif +#if IS_ENABLED(CONFIG_HID_ITE) + { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, +#endif #if IS_ENABLED(CONFIG_HID_KENSINGTON) { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, #endif diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index bba051cb63699db2dcd7bd3442d71c54c8b1459d..613d755ce22be40305278f8abe979c3c559ccc6f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -565,6 +565,7 @@ #define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386 #define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 +#define USB_DEVICE_ID_ITE8595 0x8595 #define USB_VENDOR_ID_JABRA 0x0b0e #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a1ebdd7d4d4d01c7942beb9e988a6fb36dd94714..ccdff1ee1f0c827ab5333ff37414545a37df63ed 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -656,6 +656,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_GD_START: map_key_clear(BTN_START); break; case HID_GD_SELECT: map_key_clear(BTN_SELECT); break; + case HID_GD_RFKILL_BTN: + /* MS wireless radio ctl extension, also check CA */ + if (field->application == HID_GD_WIRELESS_RADIO_CTLS) { + map_key_clear(KEY_RFKILL); + /* We need to simulate the btn release */ + field->flags |= HID_MAIN_ITEM_RELATIVE; + break; + } + default: goto unknown; } diff --git a/drivers/hid/hid-ite.c b/drivers/hid/hid-ite.c new file mode 100644 index 0000000000000000000000000000000000000000..1882a4ab0f29f48c8bed3dffa3cecc61a22d1661 --- /dev/null +++ b/drivers/hid/hid-ite.c @@ -0,0 +1,56 @@ +/* + * HID driver for some ITE "special" devices + * Copyright (c) 2017 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +static int ite_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct input_dev *input; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput) + return 0; + + input = field->hidinput->input; + + /* + * The ITE8595 always reports 0 as value for the rfkill button. Luckily + * it is the only button in its report, and it sends a report on + * release only, so receiving a report means the button was pressed. + */ + if (usage->hid == HID_GD_RFKILL_BTN) { + input_event(input, EV_KEY, KEY_RFKILL, 1); + input_sync(input); + input_event(input, EV_KEY, KEY_RFKILL, 0); + input_sync(input); + return 1; + } + + return 0; +} + +static const struct hid_device_id ite_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ite_devices); + +static struct hid_driver ite_driver = { + .name = "itetech", + .id_table = ite_devices, + .event = ite_event, +}; +module_hid_driver(ite_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/intel-ish-hid/Kconfig b/drivers/hid/intel-ish-hid/Kconfig index ea065b3684a208695b1566986eef091a1b9ec1bc..519e4c8b53c4fefef0941ca1a77809bb819973e9 100644 --- a/drivers/hid/intel-ish-hid/Kconfig +++ b/drivers/hid/intel-ish-hid/Kconfig @@ -1,5 +1,5 @@ menu "Intel ISH HID support" - depends on X86_64 && PCI + depends on (X86_64 || COMPILE_TEST) && PCI config INTEL_ISH_HID tristate "Intel Integrated Sensor Hub" diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h index fd34307a7a705591dc05db23d2fb8e1daaa02be8..2aac097c3f7034d4a654fbcbc1bc04566dfcfde0 100644 --- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h @@ -26,6 +26,8 @@ #define BXT_Bx_DEVICE_ID 0x1AA2 #define APL_Ax_DEVICE_ID 0x5AA2 #define SPT_Ax_DEVICE_ID 0x9D35 +#define CNL_Ax_DEVICE_ID 0x9DFC +#define GLK_Ax_DEVICE_ID 0x31A2 #define REVISION_ID_CHT_A0 0x6 #define REVISION_ID_CHT_Ax_SI 0x0 diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 842d8416a7a6f0c8d585eacece02aa319ea20ce3..9a60ec13cb10967a20aa560033a90ac4abe9d65c 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -296,17 +296,12 @@ static int write_ipc_from_queue(struct ishtp_device *dev) /* If sending MNG_SYNC_FW_CLOCK, update clock again */ if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG && IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) { - struct timespec ts_system; - struct timeval tv_utc; - uint64_t usec_system, usec_utc; + uint64_t usec_system, usec_utc; struct ipc_time_update_msg time_update; struct time_sync_format ts_format; - get_monotonic_boottime(&ts_system); - do_gettimeofday(&tv_utc); - usec_system = (timespec_to_ns(&ts_system)) / NSEC_PER_USEC; - usec_utc = (uint64_t)tv_utc.tv_sec * 1000000 + - ((uint32_t)tv_utc.tv_usec); + usec_system = ktime_to_us(ktime_get_boottime()); + usec_utc = ktime_to_us(ktime_get_real()); ts_format.ts1_source = HOST_SYSTEM_TIME_USEC; ts_format.ts2_source = HOST_UTC_TIME_USEC; ts_format.reserved = 0; @@ -575,15 +570,13 @@ static void fw_reset_work_fn(struct work_struct *unused) static void _ish_sync_fw_clock(struct ishtp_device *dev) { static unsigned long prev_sync; - struct timespec ts; uint64_t usec; if (prev_sync && jiffies - prev_sync < 20 * HZ) return; prev_sync = jiffies; - get_monotonic_boottime(&ts); - usec = (timespec_to_ns(&ts)) / NSEC_PER_USEC; + usec = ktime_to_us(ktime_get_boottime()); ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t)); } diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 8df81dc845295962a2e3b04b16e65ff990162176..20d824f74f99e6fd6809e68f9811dda8ec14fddf 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -35,6 +35,8 @@ static const struct pci_device_id ish_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)}, {0, } }; MODULE_DEVICE_TABLE(pci, ish_pci_tbl); diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c index 5c643d7a07b2b777c391c25acf76a44299ae1ab2..157b44aacdffb60203cb154680f9d02a94115fdc 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c @@ -136,10 +136,9 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, if (1 + sizeof(struct device_info) * i >= payload_len) { dev_err(&client_data->cl_device->dev, - "[hid-ish]: [ENUM_DEVICES]: content size %lu is bigger than payload_len %u\n", + "[hid-ish]: [ENUM_DEVICES]: content size %zu is bigger than payload_len %zu\n", 1 + sizeof(struct device_info) - * i, - (unsigned int)payload_len); + * i, payload_len); } if (1 + sizeof(struct device_info) * i >= diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c index aad61328f2822dc19b04090e6eca26d863b562b7..007443ef5fca44afea6df14267f12cbe24978990 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.c +++ b/drivers/hid/intel-ish-hid/ishtp/client.c @@ -803,7 +803,7 @@ void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl) * @ishtp_hdr: Pointer to message header * * Receive and dispatch ISHTP client messages. This function executes in ISR - * context + * or work queue context */ void recv_ishtp_cl_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr) @@ -813,7 +813,6 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, struct ishtp_cl_rb *new_rb; unsigned char *buffer = NULL; struct ishtp_cl_rb *complete_rb = NULL; - unsigned long dev_flags; unsigned long flags; int rb_count; @@ -828,7 +827,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, goto eoi; } - spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); + spin_lock_irqsave(&dev->read_list_spinlock, flags); rb_count = -1; list_for_each_entry(rb, &dev->read_list.list, list) { ++rb_count; @@ -840,8 +839,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, /* If no Rx buffer is allocated, disband the rb */ if (rb->buffer.size == 0 || rb->buffer.data == NULL) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "Rx buffer is not allocated.\n"); list_del(&rb->list); @@ -857,8 +855,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, * back FC, so communication will be stuck anyway) */ if (rb->buffer.size < ishtp_hdr->length + rb->buf_idx) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "message overflow. size %d len %d idx %ld\n", rb->buffer.size, ishtp_hdr->length, @@ -884,14 +881,13 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, * the whole msg arrived, send a new FC, and add a new * rb buffer for the next coming msg */ - spin_lock_irqsave(&cl->free_list_spinlock, flags); + spin_lock(&cl->free_list_spinlock); if (!list_empty(&cl->free_rb_list.list)) { new_rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, list); list_del_init(&new_rb->list); - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); new_rb->cl = cl; new_rb->buf_idx = 0; INIT_LIST_HEAD(&new_rb->list); @@ -900,8 +896,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, ishtp_hbm_cl_flow_control_req(dev, cl); } else { - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); } } /* One more fragment in message (even if this was last) */ @@ -914,7 +909,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, break; } - spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); /* If it's nobody's message, just read and discard it */ if (!buffer) { uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; @@ -925,7 +920,8 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, } if (complete_rb) { - getnstimeofday(&cl->ts_rx); + cl = complete_rb->cl; + cl->ts_rx = ktime_get(); ++cl->recv_msg_cnt_ipc; ishtp_cl_read_complete(complete_rb); } @@ -940,7 +936,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev, * @hbm: hbm buffer * * Receive and dispatch ISHTP client messages using DMA. This function executes - * in ISR context + * in ISR or work queue context */ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, struct dma_xfer_hbm *hbm) @@ -950,10 +946,10 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, struct ishtp_cl_rb *new_rb; unsigned char *buffer = NULL; struct ishtp_cl_rb *complete_rb = NULL; - unsigned long dev_flags; unsigned long flags; - spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); + spin_lock_irqsave(&dev->read_list_spinlock, flags); + list_for_each_entry(rb, &dev->read_list.list, list) { cl = rb->cl; if (!cl || !(cl->host_client_id == hbm->host_client_id && @@ -965,8 +961,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, * If no Rx buffer is allocated, disband the rb */ if (rb->buffer.size == 0 || rb->buffer.data == NULL) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "response buffer is not allocated.\n"); list_del(&rb->list); @@ -982,8 +977,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, * back FC, so communication will be stuck anyway) */ if (rb->buffer.size < hbm->msg_length) { - spin_unlock_irqrestore(&dev->read_list_spinlock, - dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); dev_err(&cl->device->dev, "message overflow. size %d len %d idx %ld\n", rb->buffer.size, hbm->msg_length, rb->buf_idx); @@ -1007,14 +1001,13 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, * the whole msg arrived, send a new FC, and add a new * rb buffer for the next coming msg */ - spin_lock_irqsave(&cl->free_list_spinlock, flags); + spin_lock(&cl->free_list_spinlock); if (!list_empty(&cl->free_rb_list.list)) { new_rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, list); list_del_init(&new_rb->list); - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); new_rb->cl = cl; new_rb->buf_idx = 0; INIT_LIST_HEAD(&new_rb->list); @@ -1023,8 +1016,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, ishtp_hbm_cl_flow_control_req(dev, cl); } else { - spin_unlock_irqrestore(&cl->free_list_spinlock, - flags); + spin_unlock(&cl->free_list_spinlock); } /* One more fragment in message (this is always last) */ @@ -1037,7 +1029,7 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, break; } - spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); + spin_unlock_irqrestore(&dev->read_list_spinlock, flags); /* If it's nobody's message, just read and discard it */ if (!buffer) { dev_err(dev->devc, "Dropped Rx (DMA) msg - no request\n"); @@ -1045,7 +1037,8 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, } if (complete_rb) { - getnstimeofday(&cl->ts_rx); + cl = complete_rb->cl; + cl->ts_rx = ktime_get(); ++cl->recv_msg_cnt_dma; ishtp_cl_read_complete(complete_rb); } diff --git a/drivers/hid/intel-ish-hid/ishtp/client.h b/drivers/hid/intel-ish-hid/ishtp/client.h index 444d069c2ed443fdc43cde06ac5dafd64a2e3442..79eade547f5db5663a2c8d7dc0d5b3fc0c96690a 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.h +++ b/drivers/hid/intel-ish-hid/ishtp/client.h @@ -118,9 +118,9 @@ struct ishtp_cl { unsigned int out_flow_ctrl_cnt; /* Rx msg ... out FC timing */ - struct timespec ts_rx; - struct timespec ts_out_fc; - struct timespec ts_max_fc_delay; + ktime_t ts_rx; + ktime_t ts_out_fc; + ktime_t ts_max_fc_delay; void *client_data; }; diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c index b7213608ce43ac111e608d866dc3307a3f059b4f..ae4a69f7f2f48d23b50aa639ac69cc68b04d8f3a 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.c +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c @@ -321,13 +321,10 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, if (!rv) { ++cl->out_flow_ctrl_creds; ++cl->out_flow_ctrl_cnt; - getnstimeofday(&cl->ts_out_fc); - if (cl->ts_rx.tv_sec && cl->ts_rx.tv_nsec) { - struct timespec ts_diff; - - ts_diff = timespec_sub(cl->ts_out_fc, cl->ts_rx); - if (timespec_compare(&ts_diff, &cl->ts_max_fc_delay) - > 0) + cl->ts_out_fc = ktime_get(); + if (cl->ts_rx) { + ktime_t ts_diff = ktime_sub(cl->ts_out_fc, cl->ts_rx); + if (ktime_after(ts_diff, cl->ts_max_fc_delay)) cl->ts_max_fc_delay = ts_diff; } } else { diff --git a/include/linux/hid.h b/include/linux/hid.h index 5be325d890d96f9d823753e92296c379e3751076..bebbf4893448a0a8792f72cad00adad262b4e28b 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -182,6 +182,11 @@ struct hid_item { #define HID_GD_KEYBOARD 0x00010006 #define HID_GD_KEYPAD 0x00010007 #define HID_GD_MULTIAXIS 0x00010008 +/* + * Microsoft Win8 Wireless Radio Controls extensions CA, see: + * http://www.usb.org/developers/hidpage/HUTRR40RadioHIDUsagesFinal.pdf + */ +#define HID_GD_WIRELESS_RADIO_CTLS 0x0001000c #define HID_GD_X 0x00010030 #define HID_GD_Y 0x00010031 #define HID_GD_Z 0x00010032 @@ -210,6 +215,10 @@ struct hid_item { #define HID_GD_DOWN 0x00010091 #define HID_GD_RIGHT 0x00010092 #define HID_GD_LEFT 0x00010093 +/* Microsoft Win8 Wireless Radio Controls CA usage codes */ +#define HID_GD_RFKILL_BTN 0x000100c6 +#define HID_GD_RFKILL_LED 0x000100c7 +#define HID_GD_RFKILL_SWITCH 0x000100c8 #define HID_DC_BATTERYSTRENGTH 0x00060020