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