142337b9dSAndrej Shadura // SPDX-License-Identifier: GPL-2.0 242337b9dSAndrej Shadura /* 342337b9dSAndrej Shadura * U2F Zero LED and RNG driver 442337b9dSAndrej Shadura * 542337b9dSAndrej Shadura * Copyright 2018 Andrej Shadura <andrew@shadura.me> 642337b9dSAndrej Shadura * Loosely based on drivers/hid/hid-led.c 742337b9dSAndrej Shadura * and drivers/usb/misc/chaoskey.c 842337b9dSAndrej Shadura * 942337b9dSAndrej Shadura * This program is free software; you can redistribute it and/or 1042337b9dSAndrej Shadura * modify it under the terms of the GNU General Public License as 1142337b9dSAndrej Shadura * published by the Free Software Foundation, version 2. 1242337b9dSAndrej Shadura */ 1342337b9dSAndrej Shadura 1442337b9dSAndrej Shadura #include <linux/hid.h> 1542337b9dSAndrej Shadura #include <linux/hidraw.h> 1642337b9dSAndrej Shadura #include <linux/hw_random.h> 1742337b9dSAndrej Shadura #include <linux/leds.h> 1842337b9dSAndrej Shadura #include <linux/module.h> 1942337b9dSAndrej Shadura #include <linux/mutex.h> 2042337b9dSAndrej Shadura #include <linux/usb.h> 2142337b9dSAndrej Shadura 2242337b9dSAndrej Shadura #include "usbhid/usbhid.h" 2342337b9dSAndrej Shadura #include "hid-ids.h" 2442337b9dSAndrej Shadura 2542337b9dSAndrej Shadura #define DRIVER_SHORT "u2fzero" 2642337b9dSAndrej Shadura 2742337b9dSAndrej Shadura #define HID_REPORT_SIZE 64 2842337b9dSAndrej Shadura 2942337b9dSAndrej Shadura /* We only use broadcast (CID-less) messages */ 3042337b9dSAndrej Shadura #define CID_BROADCAST 0xffffffff 3142337b9dSAndrej Shadura 3242337b9dSAndrej Shadura struct u2f_hid_msg { 3342337b9dSAndrej Shadura u32 cid; 3442337b9dSAndrej Shadura union { 3542337b9dSAndrej Shadura struct { 3642337b9dSAndrej Shadura u8 cmd; 3742337b9dSAndrej Shadura u8 bcnth; 3842337b9dSAndrej Shadura u8 bcntl; 3942337b9dSAndrej Shadura u8 data[HID_REPORT_SIZE - 7]; 4042337b9dSAndrej Shadura } init; 4142337b9dSAndrej Shadura struct { 4242337b9dSAndrej Shadura u8 seq; 4342337b9dSAndrej Shadura u8 data[HID_REPORT_SIZE - 5]; 4442337b9dSAndrej Shadura } cont; 4542337b9dSAndrej Shadura }; 4642337b9dSAndrej Shadura } __packed; 4742337b9dSAndrej Shadura 4842337b9dSAndrej Shadura struct u2f_hid_report { 4942337b9dSAndrej Shadura u8 report_type; 5042337b9dSAndrej Shadura struct u2f_hid_msg msg; 5142337b9dSAndrej Shadura } __packed; 5242337b9dSAndrej Shadura 5342337b9dSAndrej Shadura #define U2F_HID_MSG_LEN(f) (size_t)(((f).init.bcnth << 8) + (f).init.bcntl) 5442337b9dSAndrej Shadura 5542337b9dSAndrej Shadura /* Custom extensions to the U2FHID protocol */ 5642337b9dSAndrej Shadura #define U2F_CUSTOM_GET_RNG 0x21 5742337b9dSAndrej Shadura #define U2F_CUSTOM_WINK 0x24 5842337b9dSAndrej Shadura 5942337b9dSAndrej Shadura struct u2fzero_device { 6042337b9dSAndrej Shadura struct hid_device *hdev; 6142337b9dSAndrej Shadura struct urb *urb; /* URB for the RNG data */ 6242337b9dSAndrej Shadura struct led_classdev ldev; /* Embedded struct for led */ 6342337b9dSAndrej Shadura struct hwrng hwrng; /* Embedded struct for hwrng */ 6442337b9dSAndrej Shadura char *led_name; 6542337b9dSAndrej Shadura char *rng_name; 6642337b9dSAndrej Shadura u8 *buf_out; 6742337b9dSAndrej Shadura u8 *buf_in; 6842337b9dSAndrej Shadura struct mutex lock; 6942337b9dSAndrej Shadura bool present; 7042337b9dSAndrej Shadura }; 7142337b9dSAndrej Shadura 7242337b9dSAndrej Shadura static int u2fzero_send(struct u2fzero_device *dev, struct u2f_hid_report *req) 7342337b9dSAndrej Shadura { 7442337b9dSAndrej Shadura int ret; 7542337b9dSAndrej Shadura 7642337b9dSAndrej Shadura mutex_lock(&dev->lock); 7742337b9dSAndrej Shadura 7842337b9dSAndrej Shadura memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report)); 7942337b9dSAndrej Shadura 8042337b9dSAndrej Shadura ret = hid_hw_output_report(dev->hdev, dev->buf_out, 8142337b9dSAndrej Shadura sizeof(struct u2f_hid_msg)); 8242337b9dSAndrej Shadura 8342337b9dSAndrej Shadura mutex_unlock(&dev->lock); 8442337b9dSAndrej Shadura 8542337b9dSAndrej Shadura if (ret < 0) 8642337b9dSAndrej Shadura return ret; 8742337b9dSAndrej Shadura 8842337b9dSAndrej Shadura return ret == sizeof(struct u2f_hid_msg) ? 0 : -EMSGSIZE; 8942337b9dSAndrej Shadura } 9042337b9dSAndrej Shadura 9142337b9dSAndrej Shadura struct u2fzero_transfer_context { 9242337b9dSAndrej Shadura struct completion done; 9342337b9dSAndrej Shadura int status; 9442337b9dSAndrej Shadura }; 9542337b9dSAndrej Shadura 9642337b9dSAndrej Shadura static void u2fzero_read_callback(struct urb *urb) 9742337b9dSAndrej Shadura { 9842337b9dSAndrej Shadura struct u2fzero_transfer_context *ctx = urb->context; 9942337b9dSAndrej Shadura 10042337b9dSAndrej Shadura ctx->status = urb->status; 10142337b9dSAndrej Shadura complete(&ctx->done); 10242337b9dSAndrej Shadura } 10342337b9dSAndrej Shadura 10442337b9dSAndrej Shadura static int u2fzero_recv(struct u2fzero_device *dev, 10542337b9dSAndrej Shadura struct u2f_hid_report *req, 10642337b9dSAndrej Shadura struct u2f_hid_msg *resp) 10742337b9dSAndrej Shadura { 10842337b9dSAndrej Shadura int ret; 10942337b9dSAndrej Shadura struct hid_device *hdev = dev->hdev; 11042337b9dSAndrej Shadura struct u2fzero_transfer_context ctx; 11142337b9dSAndrej Shadura 11242337b9dSAndrej Shadura mutex_lock(&dev->lock); 11342337b9dSAndrej Shadura 11442337b9dSAndrej Shadura memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report)); 11542337b9dSAndrej Shadura 11642337b9dSAndrej Shadura dev->urb->context = &ctx; 11742337b9dSAndrej Shadura init_completion(&ctx.done); 11842337b9dSAndrej Shadura 11942337b9dSAndrej Shadura ret = usb_submit_urb(dev->urb, GFP_NOIO); 12042337b9dSAndrej Shadura if (unlikely(ret)) { 12142337b9dSAndrej Shadura hid_err(hdev, "usb_submit_urb failed: %d", ret); 12242337b9dSAndrej Shadura goto err; 12342337b9dSAndrej Shadura } 12442337b9dSAndrej Shadura 12542337b9dSAndrej Shadura ret = hid_hw_output_report(dev->hdev, dev->buf_out, 12642337b9dSAndrej Shadura sizeof(struct u2f_hid_msg)); 12742337b9dSAndrej Shadura 12842337b9dSAndrej Shadura if (ret < 0) { 12942337b9dSAndrej Shadura hid_err(hdev, "hid_hw_output_report failed: %d", ret); 13042337b9dSAndrej Shadura goto err; 13142337b9dSAndrej Shadura } 13242337b9dSAndrej Shadura 13342337b9dSAndrej Shadura ret = (wait_for_completion_timeout( 13442337b9dSAndrej Shadura &ctx.done, msecs_to_jiffies(USB_CTRL_SET_TIMEOUT))); 135*43775e62SAndrej Shadura if (ret == 0) { 13642337b9dSAndrej Shadura usb_kill_urb(dev->urb); 13742337b9dSAndrej Shadura hid_err(hdev, "urb submission timed out"); 13842337b9dSAndrej Shadura } else { 13942337b9dSAndrej Shadura ret = dev->urb->actual_length; 14042337b9dSAndrej Shadura memcpy(resp, dev->buf_in, ret); 14142337b9dSAndrej Shadura } 14242337b9dSAndrej Shadura 14342337b9dSAndrej Shadura err: 14442337b9dSAndrej Shadura mutex_unlock(&dev->lock); 14542337b9dSAndrej Shadura 14642337b9dSAndrej Shadura return ret; 14742337b9dSAndrej Shadura } 14842337b9dSAndrej Shadura 14942337b9dSAndrej Shadura static int u2fzero_blink(struct led_classdev *ldev) 15042337b9dSAndrej Shadura { 15142337b9dSAndrej Shadura struct u2fzero_device *dev = container_of(ldev, 15242337b9dSAndrej Shadura struct u2fzero_device, ldev); 15342337b9dSAndrej Shadura struct u2f_hid_report req = { 15442337b9dSAndrej Shadura .report_type = 0, 15542337b9dSAndrej Shadura .msg.cid = CID_BROADCAST, 15642337b9dSAndrej Shadura .msg.init = { 15742337b9dSAndrej Shadura .cmd = U2F_CUSTOM_WINK, 15842337b9dSAndrej Shadura .bcnth = 0, 15942337b9dSAndrej Shadura .bcntl = 0, 16042337b9dSAndrej Shadura .data = {0}, 16142337b9dSAndrej Shadura } 16242337b9dSAndrej Shadura }; 16342337b9dSAndrej Shadura return u2fzero_send(dev, &req); 16442337b9dSAndrej Shadura } 16542337b9dSAndrej Shadura 16642337b9dSAndrej Shadura static int u2fzero_brightness_set(struct led_classdev *ldev, 16742337b9dSAndrej Shadura enum led_brightness brightness) 16842337b9dSAndrej Shadura { 16942337b9dSAndrej Shadura ldev->brightness = LED_OFF; 17042337b9dSAndrej Shadura if (brightness) 17142337b9dSAndrej Shadura return u2fzero_blink(ldev); 17242337b9dSAndrej Shadura else 17342337b9dSAndrej Shadura return 0; 17442337b9dSAndrej Shadura } 17542337b9dSAndrej Shadura 17642337b9dSAndrej Shadura static int u2fzero_rng_read(struct hwrng *rng, void *data, 17742337b9dSAndrej Shadura size_t max, bool wait) 17842337b9dSAndrej Shadura { 17942337b9dSAndrej Shadura struct u2fzero_device *dev = container_of(rng, 18042337b9dSAndrej Shadura struct u2fzero_device, hwrng); 18142337b9dSAndrej Shadura struct u2f_hid_report req = { 18242337b9dSAndrej Shadura .report_type = 0, 18342337b9dSAndrej Shadura .msg.cid = CID_BROADCAST, 18442337b9dSAndrej Shadura .msg.init = { 18542337b9dSAndrej Shadura .cmd = U2F_CUSTOM_GET_RNG, 18642337b9dSAndrej Shadura .bcnth = 0, 18742337b9dSAndrej Shadura .bcntl = 0, 18842337b9dSAndrej Shadura .data = {0}, 18942337b9dSAndrej Shadura } 19042337b9dSAndrej Shadura }; 19142337b9dSAndrej Shadura struct u2f_hid_msg resp; 19242337b9dSAndrej Shadura int ret; 19342337b9dSAndrej Shadura size_t actual_length; 194b7abf78bSAndrej Shadura /* valid packets must have a correct header */ 195b7abf78bSAndrej Shadura int min_length = offsetof(struct u2f_hid_msg, init.data); 19642337b9dSAndrej Shadura 19742337b9dSAndrej Shadura if (!dev->present) { 19842337b9dSAndrej Shadura hid_dbg(dev->hdev, "device not present"); 19942337b9dSAndrej Shadura return 0; 20042337b9dSAndrej Shadura } 20142337b9dSAndrej Shadura 20242337b9dSAndrej Shadura ret = u2fzero_recv(dev, &req, &resp); 20322d65765SAndrej Shadura 20422d65765SAndrej Shadura /* ignore errors or packets without data */ 205b7abf78bSAndrej Shadura if (ret < min_length) 20642337b9dSAndrej Shadura return 0; 20742337b9dSAndrej Shadura 20842337b9dSAndrej Shadura /* only take the minimum amount of data it is safe to take */ 209b7abf78bSAndrej Shadura actual_length = min3((size_t)ret - min_length, 210b7abf78bSAndrej Shadura U2F_HID_MSG_LEN(resp), max); 21142337b9dSAndrej Shadura 21242337b9dSAndrej Shadura memcpy(data, resp.init.data, actual_length); 21342337b9dSAndrej Shadura 21442337b9dSAndrej Shadura return actual_length; 21542337b9dSAndrej Shadura } 21642337b9dSAndrej Shadura 21742337b9dSAndrej Shadura static int u2fzero_init_led(struct u2fzero_device *dev, 21842337b9dSAndrej Shadura unsigned int minor) 21942337b9dSAndrej Shadura { 22042337b9dSAndrej Shadura dev->led_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL, 22142337b9dSAndrej Shadura "%s%u", DRIVER_SHORT, minor); 22242337b9dSAndrej Shadura if (dev->led_name == NULL) 22342337b9dSAndrej Shadura return -ENOMEM; 22442337b9dSAndrej Shadura 22542337b9dSAndrej Shadura dev->ldev.name = dev->led_name; 22642337b9dSAndrej Shadura dev->ldev.max_brightness = LED_ON; 22742337b9dSAndrej Shadura dev->ldev.flags = LED_HW_PLUGGABLE; 22842337b9dSAndrej Shadura dev->ldev.brightness_set_blocking = u2fzero_brightness_set; 22942337b9dSAndrej Shadura 23042337b9dSAndrej Shadura return devm_led_classdev_register(&dev->hdev->dev, &dev->ldev); 23142337b9dSAndrej Shadura } 23242337b9dSAndrej Shadura 23342337b9dSAndrej Shadura static int u2fzero_init_hwrng(struct u2fzero_device *dev, 23442337b9dSAndrej Shadura unsigned int minor) 23542337b9dSAndrej Shadura { 23642337b9dSAndrej Shadura dev->rng_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL, 23742337b9dSAndrej Shadura "%s-rng%u", DRIVER_SHORT, minor); 23842337b9dSAndrej Shadura if (dev->rng_name == NULL) 23942337b9dSAndrej Shadura return -ENOMEM; 24042337b9dSAndrej Shadura 24142337b9dSAndrej Shadura dev->hwrng.name = dev->rng_name; 24242337b9dSAndrej Shadura dev->hwrng.read = u2fzero_rng_read; 24342337b9dSAndrej Shadura dev->hwrng.quality = 1; 24442337b9dSAndrej Shadura 24542337b9dSAndrej Shadura return devm_hwrng_register(&dev->hdev->dev, &dev->hwrng); 24642337b9dSAndrej Shadura } 24742337b9dSAndrej Shadura 24842337b9dSAndrej Shadura static int u2fzero_fill_in_urb(struct u2fzero_device *dev) 24942337b9dSAndrej Shadura { 25042337b9dSAndrej Shadura struct hid_device *hdev = dev->hdev; 25142337b9dSAndrej Shadura struct usb_device *udev; 25242337b9dSAndrej Shadura struct usbhid_device *usbhid = hdev->driver_data; 25342337b9dSAndrej Shadura unsigned int pipe_in; 25442337b9dSAndrej Shadura struct usb_host_endpoint *ep; 25542337b9dSAndrej Shadura 25642337b9dSAndrej Shadura if (dev->hdev->bus != BUS_USB) 25742337b9dSAndrej Shadura return -EINVAL; 25842337b9dSAndrej Shadura 25942337b9dSAndrej Shadura udev = hid_to_usb_dev(hdev); 26042337b9dSAndrej Shadura 26142337b9dSAndrej Shadura if (!usbhid->urbout || !usbhid->urbin) 26242337b9dSAndrej Shadura return -ENODEV; 26342337b9dSAndrej Shadura 26442337b9dSAndrej Shadura ep = usb_pipe_endpoint(udev, usbhid->urbin->pipe); 26542337b9dSAndrej Shadura if (!ep) 26642337b9dSAndrej Shadura return -ENODEV; 26742337b9dSAndrej Shadura 26842337b9dSAndrej Shadura dev->urb = usb_alloc_urb(0, GFP_KERNEL); 26942337b9dSAndrej Shadura if (!dev->urb) 27042337b9dSAndrej Shadura return -ENOMEM; 27142337b9dSAndrej Shadura 27242337b9dSAndrej Shadura pipe_in = (usbhid->urbin->pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); 27342337b9dSAndrej Shadura 27442337b9dSAndrej Shadura usb_fill_int_urb(dev->urb, 27542337b9dSAndrej Shadura udev, 27642337b9dSAndrej Shadura pipe_in, 27742337b9dSAndrej Shadura dev->buf_in, 27842337b9dSAndrej Shadura HID_REPORT_SIZE, 27942337b9dSAndrej Shadura u2fzero_read_callback, 28042337b9dSAndrej Shadura NULL, 28142337b9dSAndrej Shadura ep->desc.bInterval); 28242337b9dSAndrej Shadura 28342337b9dSAndrej Shadura return 0; 28442337b9dSAndrej Shadura } 28542337b9dSAndrej Shadura 28642337b9dSAndrej Shadura static int u2fzero_probe(struct hid_device *hdev, 28742337b9dSAndrej Shadura const struct hid_device_id *id) 28842337b9dSAndrej Shadura { 28942337b9dSAndrej Shadura struct u2fzero_device *dev; 29042337b9dSAndrej Shadura unsigned int minor; 29142337b9dSAndrej Shadura int ret; 29242337b9dSAndrej Shadura 29359579a8dSJiri Kosina if (!hid_is_using_ll_driver(hdev, &usb_hid_driver)) 29459579a8dSJiri Kosina return -EINVAL; 29559579a8dSJiri Kosina 29642337b9dSAndrej Shadura dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL); 29742337b9dSAndrej Shadura if (dev == NULL) 29842337b9dSAndrej Shadura return -ENOMEM; 29942337b9dSAndrej Shadura 30042337b9dSAndrej Shadura dev->buf_out = devm_kmalloc(&hdev->dev, 30142337b9dSAndrej Shadura sizeof(struct u2f_hid_report), GFP_KERNEL); 30242337b9dSAndrej Shadura if (dev->buf_out == NULL) 30342337b9dSAndrej Shadura return -ENOMEM; 30442337b9dSAndrej Shadura 30542337b9dSAndrej Shadura dev->buf_in = devm_kmalloc(&hdev->dev, 30642337b9dSAndrej Shadura sizeof(struct u2f_hid_msg), GFP_KERNEL); 30742337b9dSAndrej Shadura if (dev->buf_in == NULL) 30842337b9dSAndrej Shadura return -ENOMEM; 30942337b9dSAndrej Shadura 31042337b9dSAndrej Shadura ret = hid_parse(hdev); 31142337b9dSAndrej Shadura if (ret) 31242337b9dSAndrej Shadura return ret; 31342337b9dSAndrej Shadura 31442337b9dSAndrej Shadura dev->hdev = hdev; 31542337b9dSAndrej Shadura hid_set_drvdata(hdev, dev); 31642337b9dSAndrej Shadura mutex_init(&dev->lock); 31742337b9dSAndrej Shadura 31842337b9dSAndrej Shadura ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); 31942337b9dSAndrej Shadura if (ret) 32042337b9dSAndrej Shadura return ret; 32142337b9dSAndrej Shadura 32242337b9dSAndrej Shadura u2fzero_fill_in_urb(dev); 32342337b9dSAndrej Shadura 32442337b9dSAndrej Shadura dev->present = true; 32542337b9dSAndrej Shadura 32642337b9dSAndrej Shadura minor = ((struct hidraw *) hdev->hidraw)->minor; 32742337b9dSAndrej Shadura 32842337b9dSAndrej Shadura ret = u2fzero_init_led(dev, minor); 32942337b9dSAndrej Shadura if (ret) { 33042337b9dSAndrej Shadura hid_hw_stop(hdev); 33142337b9dSAndrej Shadura return ret; 33242337b9dSAndrej Shadura } 33342337b9dSAndrej Shadura 33442337b9dSAndrej Shadura hid_info(hdev, "U2F Zero LED initialised\n"); 33542337b9dSAndrej Shadura 33642337b9dSAndrej Shadura ret = u2fzero_init_hwrng(dev, minor); 33742337b9dSAndrej Shadura if (ret) { 33842337b9dSAndrej Shadura hid_hw_stop(hdev); 33942337b9dSAndrej Shadura return ret; 34042337b9dSAndrej Shadura } 34142337b9dSAndrej Shadura 34242337b9dSAndrej Shadura hid_info(hdev, "U2F Zero RNG initialised\n"); 34342337b9dSAndrej Shadura 34442337b9dSAndrej Shadura return 0; 34542337b9dSAndrej Shadura } 34642337b9dSAndrej Shadura 34742337b9dSAndrej Shadura static void u2fzero_remove(struct hid_device *hdev) 34842337b9dSAndrej Shadura { 34942337b9dSAndrej Shadura struct u2fzero_device *dev = hid_get_drvdata(hdev); 35042337b9dSAndrej Shadura 35142337b9dSAndrej Shadura mutex_lock(&dev->lock); 35242337b9dSAndrej Shadura dev->present = false; 35342337b9dSAndrej Shadura mutex_unlock(&dev->lock); 35442337b9dSAndrej Shadura 35542337b9dSAndrej Shadura hid_hw_stop(hdev); 35642337b9dSAndrej Shadura usb_poison_urb(dev->urb); 35742337b9dSAndrej Shadura usb_free_urb(dev->urb); 35842337b9dSAndrej Shadura } 35942337b9dSAndrej Shadura 36042337b9dSAndrej Shadura static const struct hid_device_id u2fzero_table[] = { 36142337b9dSAndrej Shadura { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, 36242337b9dSAndrej Shadura USB_DEVICE_ID_U2F_ZERO) }, 36342337b9dSAndrej Shadura { } 36442337b9dSAndrej Shadura }; 36542337b9dSAndrej Shadura MODULE_DEVICE_TABLE(hid, u2fzero_table); 36642337b9dSAndrej Shadura 36742337b9dSAndrej Shadura static struct hid_driver u2fzero_driver = { 36842337b9dSAndrej Shadura .name = "hid-" DRIVER_SHORT, 36942337b9dSAndrej Shadura .probe = u2fzero_probe, 37042337b9dSAndrej Shadura .remove = u2fzero_remove, 37142337b9dSAndrej Shadura .id_table = u2fzero_table, 37242337b9dSAndrej Shadura }; 37342337b9dSAndrej Shadura 37442337b9dSAndrej Shadura module_hid_driver(u2fzero_driver); 37542337b9dSAndrej Shadura 37642337b9dSAndrej Shadura MODULE_LICENSE("GPL"); 37742337b9dSAndrej Shadura MODULE_AUTHOR("Andrej Shadura <andrew@shadura.me>"); 37842337b9dSAndrej Shadura MODULE_DESCRIPTION("U2F Zero LED and RNG driver"); 379