root/drivers/hid/hid-u2fzero.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. u2fzero_send
  2. u2fzero_read_callback
  3. u2fzero_recv
  4. u2fzero_blink
  5. u2fzero_brightness_set
  6. u2fzero_rng_read
  7. u2fzero_init_led
  8. u2fzero_init_hwrng
  9. u2fzero_fill_in_urb
  10. u2fzero_probe
  11. u2fzero_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * U2F Zero LED and RNG driver
   4  *
   5  * Copyright 2018 Andrej Shadura <andrew@shadura.me>
   6  * Loosely based on drivers/hid/hid-led.c
   7  *              and drivers/usb/misc/chaoskey.c
   8  *
   9  * This program is free software; you can redistribute it and/or
  10  * modify it under the terms of the GNU General Public License as
  11  * published by the Free Software Foundation, version 2.
  12  */
  13 
  14 #include <linux/hid.h>
  15 #include <linux/hidraw.h>
  16 #include <linux/hw_random.h>
  17 #include <linux/leds.h>
  18 #include <linux/module.h>
  19 #include <linux/mutex.h>
  20 #include <linux/usb.h>
  21 
  22 #include "usbhid/usbhid.h"
  23 #include "hid-ids.h"
  24 
  25 #define DRIVER_SHORT            "u2fzero"
  26 
  27 #define HID_REPORT_SIZE         64
  28 
  29 /* We only use broadcast (CID-less) messages */
  30 #define CID_BROADCAST           0xffffffff
  31 
  32 struct u2f_hid_msg {
  33         u32 cid;
  34         union {
  35                 struct {
  36                         u8 cmd;
  37                         u8 bcnth;
  38                         u8 bcntl;
  39                         u8 data[HID_REPORT_SIZE - 7];
  40                 } init;
  41                 struct {
  42                         u8 seq;
  43                         u8 data[HID_REPORT_SIZE - 5];
  44                 } cont;
  45         };
  46 } __packed;
  47 
  48 struct u2f_hid_report {
  49         u8 report_type;
  50         struct u2f_hid_msg msg;
  51 } __packed;
  52 
  53 #define U2F_HID_MSG_LEN(f)      (size_t)(((f).init.bcnth << 8) + (f).init.bcntl)
  54 
  55 /* Custom extensions to the U2FHID protocol */
  56 #define U2F_CUSTOM_GET_RNG      0x21
  57 #define U2F_CUSTOM_WINK         0x24
  58 
  59 struct u2fzero_device {
  60         struct hid_device       *hdev;
  61         struct urb              *urb;       /* URB for the RNG data */
  62         struct led_classdev     ldev;       /* Embedded struct for led */
  63         struct hwrng            hwrng;      /* Embedded struct for hwrng */
  64         char                    *led_name;
  65         char                    *rng_name;
  66         u8                      *buf_out;
  67         u8                      *buf_in;
  68         struct mutex            lock;
  69         bool                    present;
  70 };
  71 
  72 static int u2fzero_send(struct u2fzero_device *dev, struct u2f_hid_report *req)
  73 {
  74         int ret;
  75 
  76         mutex_lock(&dev->lock);
  77 
  78         memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report));
  79 
  80         ret = hid_hw_output_report(dev->hdev, dev->buf_out,
  81                                    sizeof(struct u2f_hid_msg));
  82 
  83         mutex_unlock(&dev->lock);
  84 
  85         if (ret < 0)
  86                 return ret;
  87 
  88         return ret == sizeof(struct u2f_hid_msg) ? 0 : -EMSGSIZE;
  89 }
  90 
  91 struct u2fzero_transfer_context {
  92         struct completion done;
  93         int status;
  94 };
  95 
  96 static void u2fzero_read_callback(struct urb *urb)
  97 {
  98         struct u2fzero_transfer_context *ctx = urb->context;
  99 
 100         ctx->status = urb->status;
 101         complete(&ctx->done);
 102 }
 103 
 104 static int u2fzero_recv(struct u2fzero_device *dev,
 105                         struct u2f_hid_report *req,
 106                         struct u2f_hid_msg *resp)
 107 {
 108         int ret;
 109         struct hid_device *hdev = dev->hdev;
 110         struct u2fzero_transfer_context ctx;
 111 
 112         mutex_lock(&dev->lock);
 113 
 114         memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report));
 115 
 116         dev->urb->context = &ctx;
 117         init_completion(&ctx.done);
 118 
 119         ret = usb_submit_urb(dev->urb, GFP_NOIO);
 120         if (unlikely(ret)) {
 121                 hid_err(hdev, "usb_submit_urb failed: %d", ret);
 122                 goto err;
 123         }
 124 
 125         ret = hid_hw_output_report(dev->hdev, dev->buf_out,
 126                                    sizeof(struct u2f_hid_msg));
 127 
 128         if (ret < 0) {
 129                 hid_err(hdev, "hid_hw_output_report failed: %d", ret);
 130                 goto err;
 131         }
 132 
 133         ret = (wait_for_completion_timeout(
 134                 &ctx.done, msecs_to_jiffies(USB_CTRL_SET_TIMEOUT)));
 135         if (ret < 0) {
 136                 usb_kill_urb(dev->urb);
 137                 hid_err(hdev, "urb submission timed out");
 138         } else {
 139                 ret = dev->urb->actual_length;
 140                 memcpy(resp, dev->buf_in, ret);
 141         }
 142 
 143 err:
 144         mutex_unlock(&dev->lock);
 145 
 146         return ret;
 147 }
 148 
 149 static int u2fzero_blink(struct led_classdev *ldev)
 150 {
 151         struct u2fzero_device *dev = container_of(ldev,
 152                 struct u2fzero_device, ldev);
 153         struct u2f_hid_report req = {
 154                 .report_type = 0,
 155                 .msg.cid = CID_BROADCAST,
 156                 .msg.init = {
 157                         .cmd = U2F_CUSTOM_WINK,
 158                         .bcnth = 0,
 159                         .bcntl = 0,
 160                         .data  = {0},
 161                 }
 162         };
 163         return u2fzero_send(dev, &req);
 164 }
 165 
 166 static int u2fzero_brightness_set(struct led_classdev *ldev,
 167                                   enum led_brightness brightness)
 168 {
 169         ldev->brightness = LED_OFF;
 170         if (brightness)
 171                 return u2fzero_blink(ldev);
 172         else
 173                 return 0;
 174 }
 175 
 176 static int u2fzero_rng_read(struct hwrng *rng, void *data,
 177                             size_t max, bool wait)
 178 {
 179         struct u2fzero_device *dev = container_of(rng,
 180                 struct u2fzero_device, hwrng);
 181         struct u2f_hid_report req = {
 182                 .report_type = 0,
 183                 .msg.cid = CID_BROADCAST,
 184                 .msg.init = {
 185                         .cmd = U2F_CUSTOM_GET_RNG,
 186                         .bcnth = 0,
 187                         .bcntl = 0,
 188                         .data  = {0},
 189                 }
 190         };
 191         struct u2f_hid_msg resp;
 192         int ret;
 193         size_t actual_length;
 194 
 195         if (!dev->present) {
 196                 hid_dbg(dev->hdev, "device not present");
 197                 return 0;
 198         }
 199 
 200         ret = u2fzero_recv(dev, &req, &resp);
 201         if (ret < 0)
 202                 return 0;
 203 
 204         /* only take the minimum amount of data it is safe to take */
 205         actual_length = min3((size_t)ret - offsetof(struct u2f_hid_msg,
 206                 init.data), U2F_HID_MSG_LEN(resp), max);
 207 
 208         memcpy(data, resp.init.data, actual_length);
 209 
 210         return actual_length;
 211 }
 212 
 213 static int u2fzero_init_led(struct u2fzero_device *dev,
 214                             unsigned int minor)
 215 {
 216         dev->led_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL,
 217                 "%s%u", DRIVER_SHORT, minor);
 218         if (dev->led_name == NULL)
 219                 return -ENOMEM;
 220 
 221         dev->ldev.name = dev->led_name;
 222         dev->ldev.max_brightness = LED_ON;
 223         dev->ldev.flags = LED_HW_PLUGGABLE;
 224         dev->ldev.brightness_set_blocking = u2fzero_brightness_set;
 225 
 226         return devm_led_classdev_register(&dev->hdev->dev, &dev->ldev);
 227 }
 228 
 229 static int u2fzero_init_hwrng(struct u2fzero_device *dev,
 230                               unsigned int minor)
 231 {
 232         dev->rng_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL,
 233                 "%s-rng%u", DRIVER_SHORT, minor);
 234         if (dev->rng_name == NULL)
 235                 return -ENOMEM;
 236 
 237         dev->hwrng.name = dev->rng_name;
 238         dev->hwrng.read = u2fzero_rng_read;
 239         dev->hwrng.quality = 1;
 240 
 241         return devm_hwrng_register(&dev->hdev->dev, &dev->hwrng);
 242 }
 243 
 244 static int u2fzero_fill_in_urb(struct u2fzero_device *dev)
 245 {
 246         struct hid_device *hdev = dev->hdev;
 247         struct usb_device *udev;
 248         struct usbhid_device *usbhid = hdev->driver_data;
 249         unsigned int pipe_in;
 250         struct usb_host_endpoint *ep;
 251 
 252         if (dev->hdev->bus != BUS_USB)
 253                 return -EINVAL;
 254 
 255         udev = hid_to_usb_dev(hdev);
 256 
 257         if (!usbhid->urbout || !usbhid->urbin)
 258                 return -ENODEV;
 259 
 260         ep = usb_pipe_endpoint(udev, usbhid->urbin->pipe);
 261         if (!ep)
 262                 return -ENODEV;
 263 
 264         dev->urb = usb_alloc_urb(0, GFP_KERNEL);
 265         if (!dev->urb)
 266                 return -ENOMEM;
 267 
 268         pipe_in = (usbhid->urbin->pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
 269 
 270         usb_fill_int_urb(dev->urb,
 271                 udev,
 272                 pipe_in,
 273                 dev->buf_in,
 274                 HID_REPORT_SIZE,
 275                 u2fzero_read_callback,
 276                 NULL,
 277                 ep->desc.bInterval);
 278 
 279         return 0;
 280 }
 281 
 282 static int u2fzero_probe(struct hid_device *hdev,
 283                          const struct hid_device_id *id)
 284 {
 285         struct u2fzero_device *dev;
 286         unsigned int minor;
 287         int ret;
 288 
 289         if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
 290                 return -EINVAL;
 291 
 292         dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL);
 293         if (dev == NULL)
 294                 return -ENOMEM;
 295 
 296         dev->buf_out = devm_kmalloc(&hdev->dev,
 297                 sizeof(struct u2f_hid_report), GFP_KERNEL);
 298         if (dev->buf_out == NULL)
 299                 return -ENOMEM;
 300 
 301         dev->buf_in = devm_kmalloc(&hdev->dev,
 302                 sizeof(struct u2f_hid_msg), GFP_KERNEL);
 303         if (dev->buf_in == NULL)
 304                 return -ENOMEM;
 305 
 306         ret = hid_parse(hdev);
 307         if (ret)
 308                 return ret;
 309 
 310         dev->hdev = hdev;
 311         hid_set_drvdata(hdev, dev);
 312         mutex_init(&dev->lock);
 313 
 314         ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
 315         if (ret)
 316                 return ret;
 317 
 318         u2fzero_fill_in_urb(dev);
 319 
 320         dev->present = true;
 321 
 322         minor = ((struct hidraw *) hdev->hidraw)->minor;
 323 
 324         ret = u2fzero_init_led(dev, minor);
 325         if (ret) {
 326                 hid_hw_stop(hdev);
 327                 return ret;
 328         }
 329 
 330         hid_info(hdev, "U2F Zero LED initialised\n");
 331 
 332         ret = u2fzero_init_hwrng(dev, minor);
 333         if (ret) {
 334                 hid_hw_stop(hdev);
 335                 return ret;
 336         }
 337 
 338         hid_info(hdev, "U2F Zero RNG initialised\n");
 339 
 340         return 0;
 341 }
 342 
 343 static void u2fzero_remove(struct hid_device *hdev)
 344 {
 345         struct u2fzero_device *dev = hid_get_drvdata(hdev);
 346 
 347         mutex_lock(&dev->lock);
 348         dev->present = false;
 349         mutex_unlock(&dev->lock);
 350 
 351         hid_hw_stop(hdev);
 352         usb_poison_urb(dev->urb);
 353         usb_free_urb(dev->urb);
 354 }
 355 
 356 static const struct hid_device_id u2fzero_table[] = {
 357         { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL,
 358           USB_DEVICE_ID_U2F_ZERO) },
 359         { }
 360 };
 361 MODULE_DEVICE_TABLE(hid, u2fzero_table);
 362 
 363 static struct hid_driver u2fzero_driver = {
 364         .name = "hid-" DRIVER_SHORT,
 365         .probe = u2fzero_probe,
 366         .remove = u2fzero_remove,
 367         .id_table = u2fzero_table,
 368 };
 369 
 370 module_hid_driver(u2fzero_driver);
 371 
 372 MODULE_LICENSE("GPL");
 373 MODULE_AUTHOR("Andrej Shadura <andrew@shadura.me>");
 374 MODULE_DESCRIPTION("U2F Zero LED and RNG driver");

/* [<][>][^][v][top][bottom][index][help] */