From 91b228107da3e41558449a62cd93816b47b3b4b5 Mon Sep 17 00:00:00 2001 From: Rushikesh S Kadam Date: Tue, 2 Apr 2019 11:17:41 +0530 Subject: HID: intel-ish-hid: ISH firmware loader client driver This driver adds support for loading Intel Integrated Sensor Hub (ISH) firmware from host file system to ISH SRAM and start execution. At power-on, the ISH subsystem shall boot to an interim Shim loader-firmware, which shall expose an ISHTP loader device. The driver implements an ISHTP client that communicates with the Shim ISHTP loader device over the intel-ish-hid stack, to download the main ISH firmware. Signed-off-by: Rushikesh S Kadam Acked-by: Srinivas Pandruvada Acked-by: Nick Crews Tested-by: Jett Rink Signed-off-by: Jiri Kosina --- drivers/hid/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid/Makefile') diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 170163b41303..d8d393e7a1f9 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -134,3 +134,4 @@ obj-$(CONFIG_USB_KBD) += usbhid/ obj-$(CONFIG_I2C_HID) += i2c-hid/ obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/ +obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/ -- cgit v1.2.3 From 161f62cd07fde123fd52bf6d5b6fd6513cca968e Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 2 Apr 2019 21:18:17 -0600 Subject: HID: macally: Add support for Macally ikey keyboard This enables the power and equals keys on the Macally ikey keyboard. Based on the Cougar gaming keyboard HID driver, which uses the same vendor ID. Signed-off-by: Alex Henrie Reviewed-by: Hans de Goede Signed-off-by: Benjamin Tissoires --- drivers/hid/Kconfig | 10 ++++++++++ drivers/hid/Makefile | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-macally.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 drivers/hid/hid-macally.c (limited to 'drivers/hid/Makefile') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 6ca8d322b487..aef4a2a690e1 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -230,6 +230,16 @@ config HID_COUGAR Supported devices: - Cougar 500k Gaming Keyboard +config HID_MACALLY + tristate "Macally devices" + depends on HID + help + Support for Macally devices that are not fully compliant with the + HID standard. + + supported devices: + - Macally ikey keyboard + config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" depends on HID && SND diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 170163b41303..44b9caea46a7 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o +obj-$(CONFIG_HID_MACALLY) += hid-macally.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MALTRON) += hid-maltron.o obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b6d93f4ad037..aacc7534b076 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1034,6 +1034,7 @@ #define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008 #define USB_VENDOR_ID_SOLID_YEAR 0x060b +#define USB_DEVICE_ID_MACALLY_IKEY_KEYBOARD 0x0001 #define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a #define USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD 0x700a diff --git a/drivers/hid/hid-macally.c b/drivers/hid/hid-macally.c new file mode 100644 index 000000000000..9a4fc7dffb14 --- /dev/null +++ b/drivers/hid/hid-macally.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for quirky Macally devices + * + * Copyright (c) 2019 Alex Henrie + */ + +#include +#include + +#include "hid-ids.h" + +MODULE_AUTHOR("Alex Henrie "); +MODULE_DESCRIPTION("Macally devices"); +MODULE_LICENSE("GPL"); + +/* + * The Macally ikey keyboard says that its logical and usage maximums are both + * 101, but the power key is 102 and the equals key is 103 + */ +static __u8 *macally_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (*rsize >= 60 && rdesc[53] == 0x65 && rdesc[59] == 0x65) { + hid_info(hdev, + "fixing up Macally ikey keyboard report descriptor\n"); + rdesc[53] = rdesc[59] = 0x67; + } + return rdesc; +} + +static struct hid_device_id macally_id_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, + USB_DEVICE_ID_MACALLY_IKEY_KEYBOARD) }, + { } +}; +MODULE_DEVICE_TABLE(hid, macally_id_table); + +static struct hid_driver macally_driver = { + .name = "macally", + .id_table = macally_id_table, + .report_fixup = macally_report_fixup, +}; + +module_hid_driver(macally_driver); -- cgit v1.2.3 From 42337b9d4d958daae266dc455a54e3b9f157862a Mon Sep 17 00:00:00 2001 From: Andrej Shadura Date: Mon, 1 Apr 2019 14:42:00 +0200 Subject: HID: add driver for U2F Zero built-in LED and RNG U2F Zero supports custom commands for blinking the LED and getting data from the internal hardware RNG. Expose the blinking function as a LED device, and the internal hardware RNG as an HWRNG so that it can be used to feed the enthropy pool. Signed-off-by: Andrej Shadura Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 15 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-u2fzero.c | 371 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 388 insertions(+) create mode 100644 drivers/hid/hid-u2fzero.c (limited to 'drivers/hid/Makefile') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4ca0cdfa6b33..b090d425ff22 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1003,6 +1003,21 @@ config HID_UDRAW_PS3 Say Y here if you want to use the THQ uDraw gaming tablet for the PS3. +config HID_U2FZERO + tristate "U2F Zero LED and RNG support" + depends on USB_HID + depends on LEDS_CLASS + help + Support for the LED of the U2F Zero device. + + U2F Zero supports custom commands for blinking the LED + and getting data from the internal hardware RNG. + The internal hardware can be used to feed the enthropy pool. + + U2F Zero only supports blinking its LED, so this driver doesn't + allow setting the brightness to anything but 1, which will + trigger a single blink and immediately reset to back 0. + config HID_WACOM tristate "Wacom Intuos/Graphire tablet support (USB)" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 170163b41303..338d93f0c139 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -109,6 +109,7 @@ obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o obj-$(CONFIG_HID_TIVO) += hid-tivo.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o +obj-$(CONFIG_HID_U2FZERO) += hid-u2fzero.o hid-uclogic-objs := hid-uclogic-core.o \ hid-uclogic-rdesc.o \ hid-uclogic-params.o diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index adce58f24f76..03d86cace074 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -323,6 +323,7 @@ #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a #define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH 0x81b9 #define USB_DEVICE_ID_CYGNAL_CP2112 0xea90 +#define USB_DEVICE_ID_U2F_ZERO 0x8acf #define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244 diff --git a/drivers/hid/hid-u2fzero.c b/drivers/hid/hid-u2fzero.c new file mode 100644 index 000000000000..d11a5cb56a0d --- /dev/null +++ b/drivers/hid/hid-u2fzero.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * U2F Zero LED and RNG driver + * + * Copyright 2018 Andrej Shadura + * Loosely based on drivers/hid/hid-led.c + * and drivers/usb/misc/chaoskey.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "usbhid/usbhid.h" +#include "hid-ids.h" + +#define DRIVER_SHORT "u2fzero" + +#define HID_REPORT_SIZE 64 + +/* We only use broadcast (CID-less) messages */ +#define CID_BROADCAST 0xffffffff + +struct u2f_hid_msg { + u32 cid; + union { + struct { + u8 cmd; + u8 bcnth; + u8 bcntl; + u8 data[HID_REPORT_SIZE - 7]; + } init; + struct { + u8 seq; + u8 data[HID_REPORT_SIZE - 5]; + } cont; + }; +} __packed; + +struct u2f_hid_report { + u8 report_type; + struct u2f_hid_msg msg; +} __packed; + +#define U2F_HID_MSG_LEN(f) (size_t)(((f).init.bcnth << 8) + (f).init.bcntl) + +/* Custom extensions to the U2FHID protocol */ +#define U2F_CUSTOM_GET_RNG 0x21 +#define U2F_CUSTOM_WINK 0x24 + +struct u2fzero_device { + struct hid_device *hdev; + struct urb *urb; /* URB for the RNG data */ + struct led_classdev ldev; /* Embedded struct for led */ + struct hwrng hwrng; /* Embedded struct for hwrng */ + char *led_name; + char *rng_name; + u8 *buf_out; + u8 *buf_in; + struct mutex lock; + bool present; +}; + +static int u2fzero_send(struct u2fzero_device *dev, struct u2f_hid_report *req) +{ + int ret; + + mutex_lock(&dev->lock); + + memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report)); + + ret = hid_hw_output_report(dev->hdev, dev->buf_out, + sizeof(struct u2f_hid_msg)); + + mutex_unlock(&dev->lock); + + if (ret < 0) + return ret; + + return ret == sizeof(struct u2f_hid_msg) ? 0 : -EMSGSIZE; +} + +struct u2fzero_transfer_context { + struct completion done; + int status; +}; + +static void u2fzero_read_callback(struct urb *urb) +{ + struct u2fzero_transfer_context *ctx = urb->context; + + ctx->status = urb->status; + complete(&ctx->done); +} + +static int u2fzero_recv(struct u2fzero_device *dev, + struct u2f_hid_report *req, + struct u2f_hid_msg *resp) +{ + int ret; + struct hid_device *hdev = dev->hdev; + struct u2fzero_transfer_context ctx; + + mutex_lock(&dev->lock); + + memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report)); + + dev->urb->context = &ctx; + init_completion(&ctx.done); + + ret = usb_submit_urb(dev->urb, GFP_NOIO); + if (unlikely(ret)) { + hid_err(hdev, "usb_submit_urb failed: %d", ret); + goto err; + } + + ret = hid_hw_output_report(dev->hdev, dev->buf_out, + sizeof(struct u2f_hid_msg)); + + if (ret < 0) { + hid_err(hdev, "hid_hw_output_report failed: %d", ret); + goto err; + } + + ret = (wait_for_completion_timeout( + &ctx.done, msecs_to_jiffies(USB_CTRL_SET_TIMEOUT))); + if (ret < 0) { + usb_kill_urb(dev->urb); + hid_err(hdev, "urb submission timed out"); + } else { + ret = dev->urb->actual_length; + memcpy(resp, dev->buf_in, ret); + } + +err: + mutex_unlock(&dev->lock); + + return ret; +} + +static int u2fzero_blink(struct led_classdev *ldev) +{ + struct u2fzero_device *dev = container_of(ldev, + struct u2fzero_device, ldev); + struct u2f_hid_report req = { + .report_type = 0, + .msg.cid = CID_BROADCAST, + .msg.init = { + .cmd = U2F_CUSTOM_WINK, + .bcnth = 0, + .bcntl = 0, + .data = {0}, + } + }; + return u2fzero_send(dev, &req); +} + +static int u2fzero_brightness_set(struct led_classdev *ldev, + enum led_brightness brightness) +{ + ldev->brightness = LED_OFF; + if (brightness) + return u2fzero_blink(ldev); + else + return 0; +} + +static int u2fzero_rng_read(struct hwrng *rng, void *data, + size_t max, bool wait) +{ + struct u2fzero_device *dev = container_of(rng, + struct u2fzero_device, hwrng); + struct u2f_hid_report req = { + .report_type = 0, + .msg.cid = CID_BROADCAST, + .msg.init = { + .cmd = U2F_CUSTOM_GET_RNG, + .bcnth = 0, + .bcntl = 0, + .data = {0}, + } + }; + struct u2f_hid_msg resp; + int ret; + size_t actual_length; + + if (!dev->present) { + hid_dbg(dev->hdev, "device not present"); + return 0; + } + + ret = u2fzero_recv(dev, &req, &resp); + if (ret < 0) + return 0; + + /* only take the minimum amount of data it is safe to take */ + actual_length = min3((size_t)ret - offsetof(struct u2f_hid_msg, + init.data), U2F_HID_MSG_LEN(resp), max); + + memcpy(data, resp.init.data, actual_length); + + return actual_length; +} + +static int u2fzero_init_led(struct u2fzero_device *dev, + unsigned int minor) +{ + dev->led_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL, + "%s%u", DRIVER_SHORT, minor); + if (dev->led_name == NULL) + return -ENOMEM; + + dev->ldev.name = dev->led_name; + dev->ldev.max_brightness = LED_ON; + dev->ldev.flags = LED_HW_PLUGGABLE; + dev->ldev.brightness_set_blocking = u2fzero_brightness_set; + + return devm_led_classdev_register(&dev->hdev->dev, &dev->ldev); +} + +static int u2fzero_init_hwrng(struct u2fzero_device *dev, + unsigned int minor) +{ + dev->rng_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL, + "%s-rng%u", DRIVER_SHORT, minor); + if (dev->rng_name == NULL) + return -ENOMEM; + + dev->hwrng.name = dev->rng_name; + dev->hwrng.read = u2fzero_rng_read; + dev->hwrng.quality = 1; + + return devm_hwrng_register(&dev->hdev->dev, &dev->hwrng); +} + +static int u2fzero_fill_in_urb(struct u2fzero_device *dev) +{ + struct hid_device *hdev = dev->hdev; + struct usb_device *udev; + struct usbhid_device *usbhid = hdev->driver_data; + unsigned int pipe_in; + struct usb_host_endpoint *ep; + + if (dev->hdev->bus != BUS_USB) + return -EINVAL; + + udev = hid_to_usb_dev(hdev); + + if (!usbhid->urbout || !usbhid->urbin) + return -ENODEV; + + ep = usb_pipe_endpoint(udev, usbhid->urbin->pipe); + if (!ep) + return -ENODEV; + + dev->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb) + return -ENOMEM; + + pipe_in = (usbhid->urbin->pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); + + usb_fill_int_urb(dev->urb, + udev, + pipe_in, + dev->buf_in, + HID_REPORT_SIZE, + u2fzero_read_callback, + NULL, + ep->desc.bInterval); + + return 0; +} + +static int u2fzero_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct u2fzero_device *dev; + unsigned int minor; + int ret; + + dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + + dev->buf_out = devm_kmalloc(&hdev->dev, + sizeof(struct u2f_hid_report), GFP_KERNEL); + if (dev->buf_out == NULL) + return -ENOMEM; + + dev->buf_in = devm_kmalloc(&hdev->dev, + sizeof(struct u2f_hid_msg), GFP_KERNEL); + if (dev->buf_in == NULL) + return -ENOMEM; + + ret = hid_parse(hdev); + if (ret) + return ret; + + dev->hdev = hdev; + hid_set_drvdata(hdev, dev); + mutex_init(&dev->lock); + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + return ret; + + u2fzero_fill_in_urb(dev); + + dev->present = true; + + minor = ((struct hidraw *) hdev->hidraw)->minor; + + ret = u2fzero_init_led(dev, minor); + if (ret) { + hid_hw_stop(hdev); + return ret; + } + + hid_info(hdev, "U2F Zero LED initialised\n"); + + ret = u2fzero_init_hwrng(dev, minor); + if (ret) { + hid_hw_stop(hdev); + return ret; + } + + hid_info(hdev, "U2F Zero RNG initialised\n"); + + return 0; +} + +static void u2fzero_remove(struct hid_device *hdev) +{ + struct u2fzero_device *dev = hid_get_drvdata(hdev); + + mutex_lock(&dev->lock); + dev->present = false; + mutex_unlock(&dev->lock); + + hid_hw_stop(hdev); + usb_poison_urb(dev->urb); + usb_free_urb(dev->urb); +} + +static const struct hid_device_id u2fzero_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, + USB_DEVICE_ID_U2F_ZERO) }, + { } +}; +MODULE_DEVICE_TABLE(hid, u2fzero_table); + +static struct hid_driver u2fzero_driver = { + .name = "hid-" DRIVER_SHORT, + .probe = u2fzero_probe, + .remove = u2fzero_remove, + .id_table = u2fzero_table, +}; + +module_hid_driver(u2fzero_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrej Shadura "); +MODULE_DESCRIPTION("U2F Zero LED and RNG driver"); -- cgit v1.2.3