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