2 * CanoKey QEMU device implementation.
4 * Copyright (c) 2021-2022 Canokeys.org <contact@canokeys.org>
5 * Written by Hongren (Zenithal) Zheng <i@zenithal.me>
7 * This code is licensed under the GPL v2 or later.
10 #include "qemu/osdep.h"
11 #include <canokey-qemu.h>
13 #include "qemu/module.h"
14 #include "qapi/error.h"
16 #include "hw/qdev-properties.h"
21 #define CANOKEY_EP_IN(ep) ((ep) & 0x7F)
23 #define CANOKEY_VENDOR_NUM 0x20a0
24 #define CANOKEY_PRODUCT_NUM 0x42d2
27 * placeholder, canokey-qemu implements its own usb desc
28 * Namely we do not use usb_desc_handle_contorl
36 static const USBDescStrings desc_strings
= {
37 [STR_MANUFACTURER
] = "canokeys.org",
38 [STR_PRODUCT
] = "CanoKey QEMU",
39 [STR_SERIALNUMBER
] = "0"
42 static const USBDescDevice desc_device_canokey
= {
44 .bMaxPacketSize0
= 16,
45 .bNumConfigurations
= 0,
49 static const USBDesc desc_canokey
= {
51 .idVendor
= CANOKEY_VENDOR_NUM
,
52 .idProduct
= CANOKEY_PRODUCT_NUM
,
54 .iManufacturer
= STR_MANUFACTURER
,
55 .iProduct
= STR_PRODUCT
,
56 .iSerialNumber
= STR_SERIALNUMBER
,
58 .full
= &desc_device_canokey
,
64 * libcanokey-qemu.so side functions
65 * All functions are called from canokey_emu_device_loop
67 int canokey_emu_stall_ep(void *base
, uint8_t ep
)
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
;
77 int canokey_emu_set_address(void *base
, uint8_t addr
)
79 trace_canokey_emu_set_address(addr
);
80 CanoKeyState
*key
= base
;
85 int canokey_emu_prepare_receive(
86 void *base
, uint8_t ep
, uint8_t *pbuf
, uint16_t size
)
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
;
95 int canokey_emu_transmit(
96 void *base
, uint8_t ep
, const uint8_t *pbuf
, uint16_t size
)
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
],
103 key
->ep_in_size
[ep_in
] += size
;
104 key
->ep_in_state
[ep_in
] = CANOKEY_EP_IN_READY
;
106 * wake up controller if we NAKed IN token before
107 * Note: this is a quirk for CanoKey CTAPHID
109 if (ep_in
== CANOKEY_EMU_EP_CTAPHID
) {
110 usb_wakeup(usb_ep_get(&key
->dev
, USB_TOKEN_IN
, ep_in
), 0);
113 * ready for more data in device loop
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
121 if (ep_in
== CANOKEY_EMU_EP_CTAPHID
) {
122 canokey_emu_data_in(ep_in
);
127 uint32_t canokey_emu_get_rx_data_size(void *base
, uint8_t ep
)
129 CanoKeyState
*key
= base
;
130 return key
->ep_out_size
[ep
];
134 * QEMU side functions
136 static void canokey_handle_reset(USBDevice
*dev
)
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;
148 static void canokey_handle_control(USBDevice
*dev
, USBPacket
*p
,
149 int request
, int value
, int index
, int length
, uint8_t *data
)
151 trace_canokey_handle_control_setup(request
, value
, index
, length
);
152 CanoKeyState
*key
= CANOKEY(dev
);
154 canokey_emu_setup(request
, value
, index
, length
);
156 uint32_t dir_in
= request
& DeviceRequest
;
159 trace_canokey_handle_control_out();
160 if (key
->ep_out
[0] != NULL
) {
161 memcpy(key
->ep_out
[0], data
, length
);
163 canokey_emu_data_out(p
->ep
->nr
, data
);
166 canokey_emu_device_loop();
169 switch (key
->ep_in_state
[0]) {
170 case CANOKEY_EP_IN_WAIT
:
171 p
->status
= USB_RET_NAK
;
173 case CANOKEY_EP_IN_STALL
:
174 p
->status
= USB_RET_STALL
;
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
);
181 key
->ep_in_state
[0] = CANOKEY_EP_IN_WAIT
;
182 key
->ep_in_size
[0] = 0;
183 key
->ep_in_pos
[0] = 0;
188 static void canokey_handle_data(USBDevice
*dev
, USBPacket
*p
)
190 CanoKeyState
*key
= CANOKEY(dev
);
192 uint8_t ep_in
= CANOKEY_EP_IN(p
->ep
->nr
);
193 uint8_t ep_out
= p
->ep
->nr
;
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
);
202 while (out_pos
!= p
->iov
.size
) {
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
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
);
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
);
218 * Note: this is a quirk for CanoKey CTAPHID
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
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
230 if (ep_in
== CANOKEY_EMU_EP_CTAPHID
) {
231 canokey_emu_device_loop(); /* may call transmit multiple times */
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 */
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
;
244 case CANOKEY_EP_IN_STALL
:
245 p
->status
= USB_RET_STALL
;
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
],
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;
260 trace_canokey_handle_data_in(ep_in
, in_len
);
265 p
->status
= USB_RET_STALL
;
270 static void canokey_realize(USBDevice
*base
, Error
**errp
)
272 trace_canokey_realize();
273 CanoKeyState
*key
= CANOKEY(base
);
275 if (key
->file
== NULL
) {
276 error_setg(errp
, "You must provide file=/path/to/canokey-file");
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;
288 if (canokey_emu_init(key
, key
->file
)) {
289 error_setg(errp
, "canokey can not create or read %s", key
->file
);
294 static void canokey_unrealize(USBDevice
*base
)
296 trace_canokey_unrealize();
299 static Property canokey_properties
[] = {
300 DEFINE_PROP_STRING("file", CanoKeyState
, file
),
301 DEFINE_PROP_END_OF_LIST(),
304 static void canokey_class_init(ObjectClass
*klass
, void *data
)
306 DeviceClass
*dc
= DEVICE_CLASS(klass
);
307 USBDeviceClass
*uc
= USB_DEVICE_CLASS(klass
);
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
);
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
329 static void canokey_register_types(void)
331 type_register_static(&canokey_info
);
334 type_init(canokey_register_types
)