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 * ready for more data in device loop 108 * 109 * Note: this is a quirk for CanoKey CTAPHID 110 * because it calls multiple emu_transmit in one device_loop 111 * but w/o data_in it would stuck in device_loop 112 * This has no side effect for CCID as it is strictly 113 * OUT then IN transfer 114 * However it has side effect for Control transfer 115 */ 116 if (ep_in != 0) { 117 canokey_emu_data_in(ep_in); 118 } 119 return 0; 120 } 121 122 uint32_t canokey_emu_get_rx_data_size(void *base, uint8_t ep) 123 { 124 CanoKeyState *key = base; 125 return key->ep_out_size[ep]; 126 } 127 128 /* 129 * QEMU side functions 130 */ 131 static void canokey_handle_reset(USBDevice *dev) 132 { 133 trace_canokey_handle_reset(); 134 CanoKeyState *key = CANOKEY(dev); 135 for (int i = 0; i != CANOKEY_EP_NUM; ++i) { 136 key->ep_in_state[i] = CANOKEY_EP_IN_WAIT; 137 key->ep_in_pos[i] = 0; 138 key->ep_in_size[i] = 0; 139 } 140 canokey_emu_reset(); 141 } 142 143 static void canokey_handle_control(USBDevice *dev, USBPacket *p, 144 int request, int value, int index, int length, uint8_t *data) 145 { 146 trace_canokey_handle_control_setup(request, value, index, length); 147 CanoKeyState *key = CANOKEY(dev); 148 149 canokey_emu_setup(request, value, index, length); 150 151 uint32_t dir_in = request & DeviceRequest; 152 if (!dir_in) { 153 /* OUT */ 154 trace_canokey_handle_control_out(); 155 if (key->ep_out[0] != NULL) { 156 memcpy(key->ep_out[0], data, length); 157 } 158 canokey_emu_data_out(p->ep->nr, data); 159 } 160 161 canokey_emu_device_loop(); 162 163 /* IN */ 164 switch (key->ep_in_state[0]) { 165 case CANOKEY_EP_IN_WAIT: 166 p->status = USB_RET_NAK; 167 break; 168 case CANOKEY_EP_IN_STALL: 169 p->status = USB_RET_STALL; 170 break; 171 case CANOKEY_EP_IN_READY: 172 memcpy(data, key->ep_in[0], key->ep_in_size[0]); 173 p->actual_length = key->ep_in_size[0]; 174 trace_canokey_handle_control_in(p->actual_length); 175 /* reset state */ 176 key->ep_in_state[0] = CANOKEY_EP_IN_WAIT; 177 key->ep_in_size[0] = 0; 178 key->ep_in_pos[0] = 0; 179 break; 180 } 181 } 182 183 static void canokey_handle_data(USBDevice *dev, USBPacket *p) 184 { 185 CanoKeyState *key = CANOKEY(dev); 186 187 uint8_t ep_in = CANOKEY_EP_IN(p->ep->nr); 188 uint8_t ep_out = p->ep->nr; 189 uint32_t in_len; 190 uint32_t out_pos; 191 uint32_t out_len; 192 switch (p->pid) { 193 case USB_TOKEN_OUT: 194 trace_canokey_handle_data_out(ep_out, p->iov.size); 195 usb_packet_copy(p, key->ep_out_buffer[ep_out], p->iov.size); 196 out_pos = 0; 197 while (out_pos != p->iov.size) { 198 /* 199 * key->ep_out[ep_out] set by prepare_receive 200 * to be a buffer inside libcanokey-qemu.so 201 * key->ep_out_size[ep_out] set by prepare_receive 202 * to be the buffer length 203 */ 204 out_len = MIN(p->iov.size - out_pos, key->ep_out_size[ep_out]); 205 memcpy(key->ep_out[ep_out], 206 key->ep_out_buffer[ep_out] + out_pos, out_len); 207 out_pos += out_len; 208 /* update ep_out_size to actual len */ 209 key->ep_out_size[ep_out] = out_len; 210 canokey_emu_data_out(ep_out, NULL); 211 } 212 break; 213 case USB_TOKEN_IN: 214 if (key->ep_in_pos[ep_in] == 0) { /* first time IN */ 215 canokey_emu_data_in(ep_in); 216 canokey_emu_device_loop(); /* may call transmit multiple times */ 217 } 218 switch (key->ep_in_state[ep_in]) { 219 case CANOKEY_EP_IN_WAIT: 220 /* NAK for early INTR IN */ 221 p->status = USB_RET_NAK; 222 break; 223 case CANOKEY_EP_IN_STALL: 224 p->status = USB_RET_STALL; 225 break; 226 case CANOKEY_EP_IN_READY: 227 /* submit part of ep_in buffer to USBPacket */ 228 in_len = MIN(key->ep_in_size[ep_in] - key->ep_in_pos[ep_in], 229 p->iov.size); 230 usb_packet_copy(p, 231 key->ep_in[ep_in] + key->ep_in_pos[ep_in], in_len); 232 key->ep_in_pos[ep_in] += in_len; 233 /* reset state if all data submitted */ 234 if (key->ep_in_pos[ep_in] == key->ep_in_size[ep_in]) { 235 key->ep_in_state[ep_in] = CANOKEY_EP_IN_WAIT; 236 key->ep_in_size[ep_in] = 0; 237 key->ep_in_pos[ep_in] = 0; 238 } 239 trace_canokey_handle_data_in(ep_in, in_len); 240 break; 241 } 242 break; 243 default: 244 p->status = USB_RET_STALL; 245 break; 246 } 247 } 248 249 static void canokey_realize(USBDevice *base, Error **errp) 250 { 251 trace_canokey_realize(); 252 CanoKeyState *key = CANOKEY(base); 253 254 if (key->file == NULL) { 255 error_setg(errp, "You must provide file=/path/to/canokey-file"); 256 return; 257 } 258 259 usb_desc_init(base); 260 261 for (int i = 0; i != CANOKEY_EP_NUM; ++i) { 262 key->ep_in_state[i] = CANOKEY_EP_IN_WAIT; 263 key->ep_in_size[i] = 0; 264 key->ep_in_pos[i] = 0; 265 } 266 267 if (canokey_emu_init(key, key->file)) { 268 error_setg(errp, "canokey can not create or read %s", key->file); 269 return; 270 } 271 } 272 273 static void canokey_unrealize(USBDevice *base) 274 { 275 trace_canokey_unrealize(); 276 } 277 278 static Property canokey_properties[] = { 279 DEFINE_PROP_STRING("file", CanoKeyState, file), 280 DEFINE_PROP_END_OF_LIST(), 281 }; 282 283 static void canokey_class_init(ObjectClass *klass, void *data) 284 { 285 DeviceClass *dc = DEVICE_CLASS(klass); 286 USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 287 288 uc->product_desc = "CanoKey QEMU"; 289 uc->usb_desc = &desc_canokey; 290 uc->handle_reset = canokey_handle_reset; 291 uc->handle_control = canokey_handle_control; 292 uc->handle_data = canokey_handle_data; 293 uc->handle_attach = usb_desc_attach; 294 uc->realize = canokey_realize; 295 uc->unrealize = canokey_unrealize; 296 dc->desc = "CanoKey QEMU"; 297 device_class_set_props(dc, canokey_properties); 298 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 299 } 300 301 static const TypeInfo canokey_info = { 302 .name = TYPE_CANOKEY, 303 .parent = TYPE_USB_DEVICE, 304 .instance_size = sizeof(CanoKeyState), 305 .class_init = canokey_class_init 306 }; 307 308 static void canokey_register_types(void) 309 { 310 type_register_static(&canokey_info); 311 } 312 313 type_init(canokey_register_types) 314