canokey.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * CanoKey QEMU device implementation.
  3. *
  4. * Copyright (c) 2021-2022 Canokeys.org <contact@canokeys.org>
  5. * Written by Hongren (Zenithal) Zheng <i@zenithal.me>
  6. *
  7. * This code is licensed under the GPL v2 or later.
  8. */
  9. #include "qemu/osdep.h"
  10. #include <canokey-qemu.h>
  11. #include "qemu/module.h"
  12. #include "qapi/error.h"
  13. #include "hw/usb.h"
  14. #include "hw/qdev-properties.h"
  15. #include "trace.h"
  16. #include "desc.h"
  17. #include "canokey.h"
  18. #define CANOKEY_EP_IN(ep) ((ep) & 0x7F)
  19. #define CANOKEY_VENDOR_NUM 0x20a0
  20. #define CANOKEY_PRODUCT_NUM 0x42d2
  21. /*
  22. * placeholder, canokey-qemu implements its own usb desc
  23. * Namely we do not use usb_desc_handle_contorl
  24. */
  25. enum {
  26. STR_MANUFACTURER = 1,
  27. STR_PRODUCT,
  28. STR_SERIALNUMBER
  29. };
  30. static const USBDescStrings desc_strings = {
  31. [STR_MANUFACTURER] = "canokeys.org",
  32. [STR_PRODUCT] = "CanoKey QEMU",
  33. [STR_SERIALNUMBER] = "0"
  34. };
  35. static const USBDescDevice desc_device_canokey = {
  36. .bcdUSB = 0x0,
  37. .bMaxPacketSize0 = 16,
  38. .bNumConfigurations = 0,
  39. .confs = NULL,
  40. };
  41. static const USBDesc desc_canokey = {
  42. .id = {
  43. .idVendor = CANOKEY_VENDOR_NUM,
  44. .idProduct = CANOKEY_PRODUCT_NUM,
  45. .bcdDevice = 0x0100,
  46. .iManufacturer = STR_MANUFACTURER,
  47. .iProduct = STR_PRODUCT,
  48. .iSerialNumber = STR_SERIALNUMBER,
  49. },
  50. .full = &desc_device_canokey,
  51. .str = desc_strings,
  52. };
  53. /*
  54. * libcanokey-qemu.so side functions
  55. * All functions are called from canokey_emu_device_loop
  56. */
  57. int canokey_emu_stall_ep(void *base, uint8_t ep)
  58. {
  59. trace_canokey_emu_stall_ep(ep);
  60. CanoKeyState *key = base;
  61. uint8_t ep_in = CANOKEY_EP_IN(ep); /* INTR IN has ep 129 */
  62. key->ep_in_size[ep_in] = 0;
  63. key->ep_in_state[ep_in] = CANOKEY_EP_IN_STALL;
  64. return 0;
  65. }
  66. int canokey_emu_set_address(void *base, uint8_t addr)
  67. {
  68. trace_canokey_emu_set_address(addr);
  69. CanoKeyState *key = base;
  70. key->dev.addr = addr;
  71. return 0;
  72. }
  73. int canokey_emu_prepare_receive(
  74. void *base, uint8_t ep, uint8_t *pbuf, uint16_t size)
  75. {
  76. trace_canokey_emu_prepare_receive(ep, size);
  77. CanoKeyState *key = base;
  78. key->ep_out[ep] = pbuf;
  79. key->ep_out_size[ep] = size;
  80. return 0;
  81. }
  82. int canokey_emu_transmit(
  83. void *base, uint8_t ep, const uint8_t *pbuf, uint16_t size)
  84. {
  85. trace_canokey_emu_transmit(ep, size);
  86. CanoKeyState *key = base;
  87. uint8_t ep_in = CANOKEY_EP_IN(ep); /* INTR IN has ep 129 */
  88. memcpy(key->ep_in[ep_in] + key->ep_in_size[ep_in],
  89. pbuf, size);
  90. key->ep_in_size[ep_in] += size;
  91. key->ep_in_state[ep_in] = CANOKEY_EP_IN_READY;
  92. /*
  93. * wake up controller if we NAKed IN token before
  94. * Note: this is a quirk for CanoKey CTAPHID
  95. */
  96. if (ep_in == CANOKEY_EMU_EP_CTAPHID) {
  97. usb_wakeup(usb_ep_get(&key->dev, USB_TOKEN_IN, ep_in), 0);
  98. }
  99. /*
  100. * ready for more data in device loop
  101. *
  102. * Note: this is a quirk for CanoKey CTAPHID
  103. * because it calls multiple emu_transmit in one device_loop
  104. * but w/o data_in it would stuck in device_loop
  105. * This has side effect for CCID since CCID can send ZLP
  106. * This also has side effect for Control transfer
  107. */
  108. if (ep_in == CANOKEY_EMU_EP_CTAPHID) {
  109. canokey_emu_data_in(ep_in);
  110. }
  111. return 0;
  112. }
  113. uint32_t canokey_emu_get_rx_data_size(void *base, uint8_t ep)
  114. {
  115. CanoKeyState *key = base;
  116. return key->ep_out_size[ep];
  117. }
  118. /*
  119. * QEMU side functions
  120. */
  121. static void canokey_handle_reset(USBDevice *dev)
  122. {
  123. trace_canokey_handle_reset();
  124. CanoKeyState *key = CANOKEY(dev);
  125. for (int i = 0; i != CANOKEY_EP_NUM; ++i) {
  126. key->ep_in_state[i] = CANOKEY_EP_IN_WAIT;
  127. key->ep_in_pos[i] = 0;
  128. key->ep_in_size[i] = 0;
  129. }
  130. canokey_emu_reset();
  131. }
  132. static void canokey_handle_control(USBDevice *dev, USBPacket *p,
  133. int request, int value, int index, int length, uint8_t *data)
  134. {
  135. trace_canokey_handle_control_setup(request, value, index, length);
  136. CanoKeyState *key = CANOKEY(dev);
  137. canokey_emu_setup(request, value, index, length);
  138. uint32_t dir_in = request & DeviceRequest;
  139. if (!dir_in) {
  140. /* OUT */
  141. trace_canokey_handle_control_out();
  142. if (key->ep_out[0] != NULL) {
  143. memcpy(key->ep_out[0], data, length);
  144. }
  145. canokey_emu_data_out(p->ep->nr, data);
  146. }
  147. canokey_emu_device_loop();
  148. /* IN */
  149. switch (key->ep_in_state[0]) {
  150. case CANOKEY_EP_IN_WAIT:
  151. p->status = USB_RET_NAK;
  152. break;
  153. case CANOKEY_EP_IN_STALL:
  154. p->status = USB_RET_STALL;
  155. break;
  156. case CANOKEY_EP_IN_READY:
  157. memcpy(data, key->ep_in[0], key->ep_in_size[0]);
  158. p->actual_length = key->ep_in_size[0];
  159. trace_canokey_handle_control_in(p->actual_length);
  160. /* reset state */
  161. key->ep_in_state[0] = CANOKEY_EP_IN_WAIT;
  162. key->ep_in_size[0] = 0;
  163. key->ep_in_pos[0] = 0;
  164. break;
  165. }
  166. }
  167. static void canokey_handle_data(USBDevice *dev, USBPacket *p)
  168. {
  169. CanoKeyState *key = CANOKEY(dev);
  170. uint8_t ep_in = CANOKEY_EP_IN(p->ep->nr);
  171. uint8_t ep_out = p->ep->nr;
  172. uint32_t in_len;
  173. uint32_t out_pos;
  174. uint32_t out_len;
  175. switch (p->pid) {
  176. case USB_TOKEN_OUT:
  177. trace_canokey_handle_data_out(ep_out, p->iov.size);
  178. out_pos = 0;
  179. /* segment packet into (possibly multiple) ep_out */
  180. while (out_pos != p->iov.size) {
  181. /*
  182. * key->ep_out[ep_out] set by prepare_receive
  183. * to be a buffer inside libcanokey-qemu.so
  184. * key->ep_out_size[ep_out] set by prepare_receive
  185. * to be the buffer length
  186. */
  187. out_len = MIN(p->iov.size - out_pos, key->ep_out_size[ep_out]);
  188. /* usb_packet_copy would update the pos offset internally */
  189. usb_packet_copy(p, key->ep_out[ep_out], out_len);
  190. out_pos += out_len;
  191. /* update ep_out_size to actual len */
  192. key->ep_out_size[ep_out] = out_len;
  193. canokey_emu_data_out(ep_out, NULL);
  194. }
  195. /*
  196. * Note: this is a quirk for CanoKey CTAPHID
  197. *
  198. * There is one code path that uses this device loop
  199. * INTR IN -> useful data_in and useless device_loop -> NAKed
  200. * INTR OUT -> useful device loop -> transmit -> wakeup
  201. * (useful thanks to both data_in and data_out having been called)
  202. * the next INTR IN -> actual data to guest
  203. *
  204. * if there is no such device loop, there would be no further
  205. * INTR IN, no device loop, no transmit hence no usb_wakeup
  206. * then qemu would hang
  207. */
  208. if (ep_in == CANOKEY_EMU_EP_CTAPHID) {
  209. canokey_emu_device_loop(); /* may call transmit multiple times */
  210. }
  211. break;
  212. case USB_TOKEN_IN:
  213. if (key->ep_in_pos[ep_in] == 0) { /* first time IN */
  214. canokey_emu_data_in(ep_in);
  215. canokey_emu_device_loop(); /* may call transmit multiple times */
  216. }
  217. switch (key->ep_in_state[ep_in]) {
  218. case CANOKEY_EP_IN_WAIT:
  219. /* NAK for early INTR IN */
  220. p->status = USB_RET_NAK;
  221. break;
  222. case CANOKEY_EP_IN_STALL:
  223. p->status = USB_RET_STALL;
  224. break;
  225. case CANOKEY_EP_IN_READY:
  226. /* submit part of ep_in buffer to USBPacket */
  227. in_len = MIN(key->ep_in_size[ep_in] - key->ep_in_pos[ep_in],
  228. p->iov.size);
  229. usb_packet_copy(p,
  230. key->ep_in[ep_in] + key->ep_in_pos[ep_in], in_len);
  231. key->ep_in_pos[ep_in] += in_len;
  232. /* reset state if all data submitted */
  233. if (key->ep_in_pos[ep_in] == key->ep_in_size[ep_in]) {
  234. key->ep_in_state[ep_in] = CANOKEY_EP_IN_WAIT;
  235. key->ep_in_size[ep_in] = 0;
  236. key->ep_in_pos[ep_in] = 0;
  237. }
  238. trace_canokey_handle_data_in(ep_in, in_len);
  239. break;
  240. }
  241. break;
  242. default:
  243. p->status = USB_RET_STALL;
  244. break;
  245. }
  246. }
  247. static void canokey_realize(USBDevice *base, Error **errp)
  248. {
  249. trace_canokey_realize();
  250. CanoKeyState *key = CANOKEY(base);
  251. if (key->file == NULL) {
  252. error_setg(errp, "You must provide file=/path/to/canokey-file");
  253. return;
  254. }
  255. usb_desc_init(base);
  256. for (int i = 0; i != CANOKEY_EP_NUM; ++i) {
  257. key->ep_in_state[i] = CANOKEY_EP_IN_WAIT;
  258. key->ep_in_size[i] = 0;
  259. key->ep_in_pos[i] = 0;
  260. }
  261. if (canokey_emu_init(key, key->file)) {
  262. error_setg(errp, "canokey can not create or read %s", key->file);
  263. return;
  264. }
  265. }
  266. static void canokey_unrealize(USBDevice *base)
  267. {
  268. trace_canokey_unrealize();
  269. }
  270. static const Property canokey_properties[] = {
  271. DEFINE_PROP_STRING("file", CanoKeyState, file),
  272. };
  273. static void canokey_class_init(ObjectClass *klass, void *data)
  274. {
  275. DeviceClass *dc = DEVICE_CLASS(klass);
  276. USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
  277. uc->product_desc = "CanoKey QEMU";
  278. uc->usb_desc = &desc_canokey;
  279. uc->handle_reset = canokey_handle_reset;
  280. uc->handle_control = canokey_handle_control;
  281. uc->handle_data = canokey_handle_data;
  282. uc->handle_attach = usb_desc_attach;
  283. uc->realize = canokey_realize;
  284. uc->unrealize = canokey_unrealize;
  285. dc->desc = "CanoKey QEMU";
  286. device_class_set_props(dc, canokey_properties);
  287. set_bit(DEVICE_CATEGORY_MISC, dc->categories);
  288. }
  289. static const TypeInfo canokey_info = {
  290. .name = TYPE_CANOKEY,
  291. .parent = TYPE_USB_DEVICE,
  292. .instance_size = sizeof(CanoKeyState),
  293. .class_init = canokey_class_init
  294. };
  295. static void canokey_register_types(void)
  296. {
  297. type_register_static(&canokey_info);
  298. }
  299. type_init(canokey_register_types)