xref: /openbmc/qemu/hw/usb/u2f-passthru.c (revision 299976b0)
1*299976b0SCésar Belley /*
2*299976b0SCésar Belley  * U2F USB Passthru device.
3*299976b0SCésar Belley  *
4*299976b0SCésar Belley  * Copyright (c) 2020 César Belley <cesar.belley@lse.epita.fr>
5*299976b0SCésar Belley  * Written by César Belley <cesar.belley@lse.epita.fr>
6*299976b0SCésar Belley  *
7*299976b0SCésar Belley  * Permission is hereby granted, free of charge, to any person obtaining a copy
8*299976b0SCésar Belley  * of this software and associated documentation files (the "Software"), to deal
9*299976b0SCésar Belley  * in the Software without restriction, including without limitation the rights
10*299976b0SCésar Belley  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11*299976b0SCésar Belley  * copies of the Software, and to permit persons to whom the Software is
12*299976b0SCésar Belley  * furnished to do so, subject to the following conditions:
13*299976b0SCésar Belley  *
14*299976b0SCésar Belley  * The above copyright notice and this permission notice shall be included in
15*299976b0SCésar Belley  * all copies or substantial portions of the Software.
16*299976b0SCésar Belley  *
17*299976b0SCésar Belley  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18*299976b0SCésar Belley  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19*299976b0SCésar Belley  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20*299976b0SCésar Belley  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21*299976b0SCésar Belley  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22*299976b0SCésar Belley  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23*299976b0SCésar Belley  * THE SOFTWARE.
24*299976b0SCésar Belley  */
25*299976b0SCésar Belley 
26*299976b0SCésar Belley #include "qemu/osdep.h"
27*299976b0SCésar Belley #include "qemu/module.h"
28*299976b0SCésar Belley #include "qemu/main-loop.h"
29*299976b0SCésar Belley #include "qemu/error-report.h"
30*299976b0SCésar Belley #include "qapi/error.h"
31*299976b0SCésar Belley #include "hw/qdev-properties.h"
32*299976b0SCésar Belley #include "hw/usb.h"
33*299976b0SCésar Belley #include "migration/vmstate.h"
34*299976b0SCésar Belley 
35*299976b0SCésar Belley #include "u2f.h"
36*299976b0SCésar Belley 
37*299976b0SCésar Belley #define NONCE_SIZE 8
38*299976b0SCésar Belley #define BROADCAST_CID 0xFFFFFFFF
39*299976b0SCésar Belley #define TRANSACTION_TIMEOUT 120000
40*299976b0SCésar Belley 
41*299976b0SCésar Belley struct transaction {
42*299976b0SCésar Belley     uint32_t cid;
43*299976b0SCésar Belley     uint16_t resp_bcnt;
44*299976b0SCésar Belley     uint16_t resp_size;
45*299976b0SCésar Belley 
46*299976b0SCésar Belley     /* Nonce for broadcast isolation */
47*299976b0SCésar Belley     uint8_t nonce[NONCE_SIZE];
48*299976b0SCésar Belley };
49*299976b0SCésar Belley 
50*299976b0SCésar Belley typedef struct U2FPassthruState U2FPassthruState;
51*299976b0SCésar Belley 
52*299976b0SCésar Belley #define CURRENT_TRANSACTIONS_NUM 4
53*299976b0SCésar Belley 
54*299976b0SCésar Belley struct U2FPassthruState {
55*299976b0SCésar Belley     U2FKeyState base;
56*299976b0SCésar Belley 
57*299976b0SCésar Belley     /* Host device */
58*299976b0SCésar Belley     char *hidraw;
59*299976b0SCésar Belley     int hidraw_fd;
60*299976b0SCésar Belley 
61*299976b0SCésar Belley     /* Current Transactions */
62*299976b0SCésar Belley     struct transaction current_transactions[CURRENT_TRANSACTIONS_NUM];
63*299976b0SCésar Belley     uint8_t current_transactions_start;
64*299976b0SCésar Belley     uint8_t current_transactions_end;
65*299976b0SCésar Belley     uint8_t current_transactions_num;
66*299976b0SCésar Belley 
67*299976b0SCésar Belley     /* Transaction time checking */
68*299976b0SCésar Belley     int64_t last_transaction_time;
69*299976b0SCésar Belley     QEMUTimer timer;
70*299976b0SCésar Belley };
71*299976b0SCésar Belley 
72*299976b0SCésar Belley #define TYPE_U2F_PASSTHRU "u2f-passthru"
73*299976b0SCésar Belley #define PASSTHRU_U2F_KEY(obj) \
74*299976b0SCésar Belley     OBJECT_CHECK(U2FPassthruState, (obj), TYPE_U2F_PASSTHRU)
75*299976b0SCésar Belley 
76*299976b0SCésar Belley /* Init packet sizes */
77*299976b0SCésar Belley #define PACKET_INIT_HEADER_SIZE 7
78*299976b0SCésar Belley #define PACKET_INIT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_INIT_HEADER_SIZE)
79*299976b0SCésar Belley 
80*299976b0SCésar Belley /* Cont packet sizes */
81*299976b0SCésar Belley #define PACKET_CONT_HEADER_SIZE 5
82*299976b0SCésar Belley #define PACKET_CONT_DATA_SIZE (U2FHID_PACKET_SIZE - PACKET_CONT_HEADER_SIZE)
83*299976b0SCésar Belley 
84*299976b0SCésar Belley struct packet_init {
85*299976b0SCésar Belley     uint32_t cid;
86*299976b0SCésar Belley     uint8_t cmd;
87*299976b0SCésar Belley     uint8_t bcnth;
88*299976b0SCésar Belley     uint8_t bcntl;
89*299976b0SCésar Belley     uint8_t data[PACKET_INIT_DATA_SIZE];
90*299976b0SCésar Belley } QEMU_PACKED;
91*299976b0SCésar Belley 
92*299976b0SCésar Belley static inline uint32_t packet_get_cid(const void *packet)
93*299976b0SCésar Belley {
94*299976b0SCésar Belley     return *((uint32_t *)packet);
95*299976b0SCésar Belley }
96*299976b0SCésar Belley 
97*299976b0SCésar Belley static inline bool packet_is_init(const void *packet)
98*299976b0SCésar Belley {
99*299976b0SCésar Belley     return ((uint8_t *)packet)[4] & (1 << 7);
100*299976b0SCésar Belley }
101*299976b0SCésar Belley 
102*299976b0SCésar Belley static inline uint16_t packet_init_get_bcnt(
103*299976b0SCésar Belley         const struct packet_init *packet_init)
104*299976b0SCésar Belley {
105*299976b0SCésar Belley     uint16_t bcnt = 0;
106*299976b0SCésar Belley     bcnt |= packet_init->bcnth << 8;
107*299976b0SCésar Belley     bcnt |= packet_init->bcntl;
108*299976b0SCésar Belley 
109*299976b0SCésar Belley     return bcnt;
110*299976b0SCésar Belley }
111*299976b0SCésar Belley 
112*299976b0SCésar Belley static void u2f_passthru_reset(U2FPassthruState *key)
113*299976b0SCésar Belley {
114*299976b0SCésar Belley     timer_del(&key->timer);
115*299976b0SCésar Belley     qemu_set_fd_handler(key->hidraw_fd, NULL, NULL, key);
116*299976b0SCésar Belley     key->last_transaction_time = 0;
117*299976b0SCésar Belley     key->current_transactions_start = 0;
118*299976b0SCésar Belley     key->current_transactions_end = 0;
119*299976b0SCésar Belley     key->current_transactions_num = 0;
120*299976b0SCésar Belley }
121*299976b0SCésar Belley 
122*299976b0SCésar Belley static void u2f_timeout_check(void *opaque)
123*299976b0SCésar Belley {
124*299976b0SCésar Belley     U2FPassthruState *key = opaque;
125*299976b0SCésar Belley     int64_t time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
126*299976b0SCésar Belley 
127*299976b0SCésar Belley     if (time > key->last_transaction_time + TRANSACTION_TIMEOUT) {
128*299976b0SCésar Belley         u2f_passthru_reset(key);
129*299976b0SCésar Belley     } else {
130*299976b0SCésar Belley         timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
131*299976b0SCésar Belley     }
132*299976b0SCésar Belley }
133*299976b0SCésar Belley 
134*299976b0SCésar Belley static int u2f_transaction_get_index(U2FPassthruState *key, uint32_t cid)
135*299976b0SCésar Belley {
136*299976b0SCésar Belley     for (int i = 0; i < key->current_transactions_num; ++i) {
137*299976b0SCésar Belley         int index = (key->current_transactions_start + i)
138*299976b0SCésar Belley             % CURRENT_TRANSACTIONS_NUM;
139*299976b0SCésar Belley         if (cid == key->current_transactions[index].cid) {
140*299976b0SCésar Belley             return index;
141*299976b0SCésar Belley         }
142*299976b0SCésar Belley     }
143*299976b0SCésar Belley     return -1;
144*299976b0SCésar Belley }
145*299976b0SCésar Belley 
146*299976b0SCésar Belley static struct transaction *u2f_transaction_get(U2FPassthruState *key,
147*299976b0SCésar Belley                                                uint32_t cid)
148*299976b0SCésar Belley {
149*299976b0SCésar Belley     int index = u2f_transaction_get_index(key, cid);
150*299976b0SCésar Belley     if (index < 0) {
151*299976b0SCésar Belley         return NULL;
152*299976b0SCésar Belley     }
153*299976b0SCésar Belley     return &key->current_transactions[index];
154*299976b0SCésar Belley }
155*299976b0SCésar Belley 
156*299976b0SCésar Belley static struct transaction *u2f_transaction_get_from_nonce(U2FPassthruState *key,
157*299976b0SCésar Belley                                 const uint8_t nonce[NONCE_SIZE])
158*299976b0SCésar Belley {
159*299976b0SCésar Belley     for (int i = 0; i < key->current_transactions_num; ++i) {
160*299976b0SCésar Belley         int index = (key->current_transactions_start + i)
161*299976b0SCésar Belley             % CURRENT_TRANSACTIONS_NUM;
162*299976b0SCésar Belley         if (key->current_transactions[index].cid == BROADCAST_CID
163*299976b0SCésar Belley             && memcmp(nonce, key->current_transactions[index].nonce,
164*299976b0SCésar Belley                       NONCE_SIZE) == 0) {
165*299976b0SCésar Belley             return &key->current_transactions[index];
166*299976b0SCésar Belley         }
167*299976b0SCésar Belley     }
168*299976b0SCésar Belley     return NULL;
169*299976b0SCésar Belley }
170*299976b0SCésar Belley 
171*299976b0SCésar Belley static void u2f_transaction_close(U2FPassthruState *key, uint32_t cid)
172*299976b0SCésar Belley {
173*299976b0SCésar Belley     int index, next_index;
174*299976b0SCésar Belley     index = u2f_transaction_get_index(key, cid);
175*299976b0SCésar Belley     if (index < 0) {
176*299976b0SCésar Belley         return;
177*299976b0SCésar Belley     }
178*299976b0SCésar Belley     next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
179*299976b0SCésar Belley 
180*299976b0SCésar Belley     /* Rearrange to ensure the oldest is at the start position */
181*299976b0SCésar Belley     while (next_index != key->current_transactions_end) {
182*299976b0SCésar Belley         memcpy(&key->current_transactions[index],
183*299976b0SCésar Belley                &key->current_transactions[next_index],
184*299976b0SCésar Belley                sizeof(struct transaction));
185*299976b0SCésar Belley 
186*299976b0SCésar Belley         index = next_index;
187*299976b0SCésar Belley         next_index = (index + 1) % CURRENT_TRANSACTIONS_NUM;
188*299976b0SCésar Belley     }
189*299976b0SCésar Belley 
190*299976b0SCésar Belley     key->current_transactions_end = index;
191*299976b0SCésar Belley     --key->current_transactions_num;
192*299976b0SCésar Belley 
193*299976b0SCésar Belley     if (key->current_transactions_num == 0) {
194*299976b0SCésar Belley         u2f_passthru_reset(key);
195*299976b0SCésar Belley     }
196*299976b0SCésar Belley }
197*299976b0SCésar Belley 
198*299976b0SCésar Belley static void u2f_transaction_add(U2FPassthruState *key, uint32_t cid,
199*299976b0SCésar Belley                                 const uint8_t nonce[NONCE_SIZE])
200*299976b0SCésar Belley {
201*299976b0SCésar Belley     uint8_t index;
202*299976b0SCésar Belley     struct transaction *transaction;
203*299976b0SCésar Belley 
204*299976b0SCésar Belley     if (key->current_transactions_num >= CURRENT_TRANSACTIONS_NUM) {
205*299976b0SCésar Belley         /* Close the oldest transaction */
206*299976b0SCésar Belley         index = key->current_transactions_start;
207*299976b0SCésar Belley         transaction = &key->current_transactions[index];
208*299976b0SCésar Belley         u2f_transaction_close(key, transaction->cid);
209*299976b0SCésar Belley     }
210*299976b0SCésar Belley 
211*299976b0SCésar Belley     /* Index */
212*299976b0SCésar Belley     index = key->current_transactions_end;
213*299976b0SCésar Belley     key->current_transactions_end = (index + 1) % CURRENT_TRANSACTIONS_NUM;
214*299976b0SCésar Belley     ++key->current_transactions_num;
215*299976b0SCésar Belley 
216*299976b0SCésar Belley     /* Transaction */
217*299976b0SCésar Belley     transaction = &key->current_transactions[index];
218*299976b0SCésar Belley     transaction->cid = cid;
219*299976b0SCésar Belley     transaction->resp_bcnt = 0;
220*299976b0SCésar Belley     transaction->resp_size = 0;
221*299976b0SCésar Belley 
222*299976b0SCésar Belley     /* Nonce */
223*299976b0SCésar Belley     if (nonce != NULL) {
224*299976b0SCésar Belley         memcpy(transaction->nonce, nonce, NONCE_SIZE);
225*299976b0SCésar Belley     }
226*299976b0SCésar Belley }
227*299976b0SCésar Belley 
228*299976b0SCésar Belley static void u2f_passthru_read(void *opaque);
229*299976b0SCésar Belley 
230*299976b0SCésar Belley static void u2f_transaction_start(U2FPassthruState *key,
231*299976b0SCésar Belley                                   const struct packet_init *packet_init)
232*299976b0SCésar Belley {
233*299976b0SCésar Belley     int64_t time;
234*299976b0SCésar Belley 
235*299976b0SCésar Belley     /* Transaction */
236*299976b0SCésar Belley     if (packet_init->cid == BROADCAST_CID) {
237*299976b0SCésar Belley         u2f_transaction_add(key, packet_init->cid, packet_init->data);
238*299976b0SCésar Belley     } else {
239*299976b0SCésar Belley         u2f_transaction_add(key, packet_init->cid, NULL);
240*299976b0SCésar Belley     }
241*299976b0SCésar Belley 
242*299976b0SCésar Belley     /* Time */
243*299976b0SCésar Belley     time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
244*299976b0SCésar Belley     if (key->last_transaction_time == 0) {
245*299976b0SCésar Belley         qemu_set_fd_handler(key->hidraw_fd, u2f_passthru_read, NULL, key);
246*299976b0SCésar Belley         timer_init_ms(&key->timer, QEMU_CLOCK_VIRTUAL, u2f_timeout_check, key);
247*299976b0SCésar Belley         timer_mod(&key->timer, time + TRANSACTION_TIMEOUT / 4);
248*299976b0SCésar Belley     }
249*299976b0SCésar Belley     key->last_transaction_time = time;
250*299976b0SCésar Belley }
251*299976b0SCésar Belley 
252*299976b0SCésar Belley static void u2f_passthru_recv_from_host(U2FPassthruState *key,
253*299976b0SCésar Belley                                     const uint8_t packet[U2FHID_PACKET_SIZE])
254*299976b0SCésar Belley {
255*299976b0SCésar Belley     struct transaction *transaction;
256*299976b0SCésar Belley     uint32_t cid;
257*299976b0SCésar Belley 
258*299976b0SCésar Belley     /* Retrieve transaction */
259*299976b0SCésar Belley     cid = packet_get_cid(packet);
260*299976b0SCésar Belley     if (cid == BROADCAST_CID) {
261*299976b0SCésar Belley         struct packet_init *packet_init;
262*299976b0SCésar Belley         if (!packet_is_init(packet)) {
263*299976b0SCésar Belley             return;
264*299976b0SCésar Belley         }
265*299976b0SCésar Belley         packet_init = (struct packet_init *)packet;
266*299976b0SCésar Belley         transaction = u2f_transaction_get_from_nonce(key, packet_init->data);
267*299976b0SCésar Belley     } else {
268*299976b0SCésar Belley         transaction = u2f_transaction_get(key, cid);
269*299976b0SCésar Belley     }
270*299976b0SCésar Belley 
271*299976b0SCésar Belley     /* Ignore no started transaction */
272*299976b0SCésar Belley     if (transaction == NULL) {
273*299976b0SCésar Belley         return;
274*299976b0SCésar Belley     }
275*299976b0SCésar Belley 
276*299976b0SCésar Belley     if (packet_is_init(packet)) {
277*299976b0SCésar Belley         struct packet_init *packet_init = (struct packet_init *)packet;
278*299976b0SCésar Belley         transaction->resp_bcnt = packet_init_get_bcnt(packet_init);
279*299976b0SCésar Belley         transaction->resp_size = PACKET_INIT_DATA_SIZE;
280*299976b0SCésar Belley 
281*299976b0SCésar Belley         if (packet_init->cid == BROADCAST_CID) {
282*299976b0SCésar Belley             /* Nonce checking for legitimate response */
283*299976b0SCésar Belley             if (memcmp(transaction->nonce, packet_init->data, NONCE_SIZE)
284*299976b0SCésar Belley                 != 0) {
285*299976b0SCésar Belley                 return;
286*299976b0SCésar Belley             }
287*299976b0SCésar Belley         }
288*299976b0SCésar Belley     } else {
289*299976b0SCésar Belley         transaction->resp_size += PACKET_CONT_DATA_SIZE;
290*299976b0SCésar Belley     }
291*299976b0SCésar Belley 
292*299976b0SCésar Belley     /* Transaction end check */
293*299976b0SCésar Belley     if (transaction->resp_size >= transaction->resp_bcnt) {
294*299976b0SCésar Belley         u2f_transaction_close(key, cid);
295*299976b0SCésar Belley     }
296*299976b0SCésar Belley     u2f_send_to_guest(&key->base, packet);
297*299976b0SCésar Belley }
298*299976b0SCésar Belley 
299*299976b0SCésar Belley static void u2f_passthru_read(void *opaque)
300*299976b0SCésar Belley {
301*299976b0SCésar Belley     U2FPassthruState *key = opaque;
302*299976b0SCésar Belley     U2FKeyState *base = &key->base;
303*299976b0SCésar Belley     uint8_t packet[2 * U2FHID_PACKET_SIZE];
304*299976b0SCésar Belley     int ret;
305*299976b0SCésar Belley 
306*299976b0SCésar Belley     /* Full size base queue check */
307*299976b0SCésar Belley     if (base->pending_in_num >= U2FHID_PENDING_IN_NUM) {
308*299976b0SCésar Belley         return;
309*299976b0SCésar Belley     }
310*299976b0SCésar Belley 
311*299976b0SCésar Belley     ret = read(key->hidraw_fd, packet, sizeof(packet));
312*299976b0SCésar Belley     if (ret < 0) {
313*299976b0SCésar Belley         /* Detach */
314*299976b0SCésar Belley         if (base->dev.attached) {
315*299976b0SCésar Belley             usb_device_detach(&base->dev);
316*299976b0SCésar Belley             u2f_passthru_reset(key);
317*299976b0SCésar Belley         }
318*299976b0SCésar Belley         return;
319*299976b0SCésar Belley     }
320*299976b0SCésar Belley     if (ret != U2FHID_PACKET_SIZE) {
321*299976b0SCésar Belley         return;
322*299976b0SCésar Belley     }
323*299976b0SCésar Belley     u2f_passthru_recv_from_host(key, packet);
324*299976b0SCésar Belley }
325*299976b0SCésar Belley 
326*299976b0SCésar Belley static void u2f_passthru_recv_from_guest(U2FKeyState *base,
327*299976b0SCésar Belley                                     const uint8_t packet[U2FHID_PACKET_SIZE])
328*299976b0SCésar Belley {
329*299976b0SCésar Belley     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
330*299976b0SCésar Belley     uint8_t host_packet[U2FHID_PACKET_SIZE + 1];
331*299976b0SCésar Belley     ssize_t written;
332*299976b0SCésar Belley 
333*299976b0SCésar Belley     if (packet_is_init(packet)) {
334*299976b0SCésar Belley         u2f_transaction_start(key, (struct packet_init *)packet);
335*299976b0SCésar Belley     }
336*299976b0SCésar Belley 
337*299976b0SCésar Belley     host_packet[0] = 0;
338*299976b0SCésar Belley     memcpy(host_packet + 1, packet, U2FHID_PACKET_SIZE);
339*299976b0SCésar Belley 
340*299976b0SCésar Belley     written = write(key->hidraw_fd, host_packet, sizeof(host_packet));
341*299976b0SCésar Belley     if (written != sizeof(host_packet)) {
342*299976b0SCésar Belley         error_report("%s: Bad written size (req 0x%zu, val 0x%zd)",
343*299976b0SCésar Belley                      TYPE_U2F_PASSTHRU, sizeof(host_packet), written);
344*299976b0SCésar Belley     }
345*299976b0SCésar Belley }
346*299976b0SCésar Belley 
347*299976b0SCésar Belley static void u2f_passthru_unrealize(U2FKeyState *base)
348*299976b0SCésar Belley {
349*299976b0SCésar Belley     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
350*299976b0SCésar Belley 
351*299976b0SCésar Belley     u2f_passthru_reset(key);
352*299976b0SCésar Belley     qemu_close(key->hidraw_fd);
353*299976b0SCésar Belley }
354*299976b0SCésar Belley 
355*299976b0SCésar Belley static void u2f_passthru_realize(U2FKeyState *base, Error **errp)
356*299976b0SCésar Belley {
357*299976b0SCésar Belley     U2FPassthruState *key = PASSTHRU_U2F_KEY(base);
358*299976b0SCésar Belley     int fd;
359*299976b0SCésar Belley 
360*299976b0SCésar Belley     if (key->hidraw == NULL) {
361*299976b0SCésar Belley         error_setg(errp, "%s: Missing hidraw", TYPE_U2F_PASSTHRU);
362*299976b0SCésar Belley         return;
363*299976b0SCésar Belley     }
364*299976b0SCésar Belley 
365*299976b0SCésar Belley     fd = qemu_open(key->hidraw, O_RDWR);
366*299976b0SCésar Belley     if (fd < 0) {
367*299976b0SCésar Belley         error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU,
368*299976b0SCésar Belley                    key->hidraw);
369*299976b0SCésar Belley         return;
370*299976b0SCésar Belley     }
371*299976b0SCésar Belley     key->hidraw_fd = fd;
372*299976b0SCésar Belley     u2f_passthru_reset(key);
373*299976b0SCésar Belley }
374*299976b0SCésar Belley 
375*299976b0SCésar Belley static int u2f_passthru_post_load(void *opaque, int version_id)
376*299976b0SCésar Belley {
377*299976b0SCésar Belley     U2FPassthruState *key = opaque;
378*299976b0SCésar Belley     u2f_passthru_reset(key);
379*299976b0SCésar Belley     return 0;
380*299976b0SCésar Belley }
381*299976b0SCésar Belley 
382*299976b0SCésar Belley static const VMStateDescription u2f_passthru_vmstate = {
383*299976b0SCésar Belley     .name = "u2f-key-passthru",
384*299976b0SCésar Belley     .version_id = 1,
385*299976b0SCésar Belley     .minimum_version_id = 1,
386*299976b0SCésar Belley     .post_load = u2f_passthru_post_load,
387*299976b0SCésar Belley     .fields = (VMStateField[]) {
388*299976b0SCésar Belley         VMSTATE_U2F_KEY(base, U2FPassthruState),
389*299976b0SCésar Belley         VMSTATE_END_OF_LIST()
390*299976b0SCésar Belley     }
391*299976b0SCésar Belley };
392*299976b0SCésar Belley 
393*299976b0SCésar Belley static Property u2f_passthru_properties[] = {
394*299976b0SCésar Belley     DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw),
395*299976b0SCésar Belley     DEFINE_PROP_END_OF_LIST(),
396*299976b0SCésar Belley };
397*299976b0SCésar Belley 
398*299976b0SCésar Belley static void u2f_passthru_class_init(ObjectClass *klass, void *data)
399*299976b0SCésar Belley {
400*299976b0SCésar Belley     DeviceClass *dc = DEVICE_CLASS(klass);
401*299976b0SCésar Belley     U2FKeyClass *kc = U2F_KEY_CLASS(klass);
402*299976b0SCésar Belley 
403*299976b0SCésar Belley     kc->realize = u2f_passthru_realize;
404*299976b0SCésar Belley     kc->unrealize = u2f_passthru_unrealize;
405*299976b0SCésar Belley     kc->recv_from_guest = u2f_passthru_recv_from_guest;
406*299976b0SCésar Belley     dc->desc = "QEMU U2F passthrough key";
407*299976b0SCésar Belley     dc->vmsd = &u2f_passthru_vmstate;
408*299976b0SCésar Belley     device_class_set_props(dc, u2f_passthru_properties);
409*299976b0SCésar Belley }
410*299976b0SCésar Belley 
411*299976b0SCésar Belley static const TypeInfo u2f_key_passthru_info = {
412*299976b0SCésar Belley     .name = TYPE_U2F_PASSTHRU,
413*299976b0SCésar Belley     .parent = TYPE_U2F_KEY,
414*299976b0SCésar Belley     .instance_size = sizeof(U2FPassthruState),
415*299976b0SCésar Belley     .class_init = u2f_passthru_class_init
416*299976b0SCésar Belley };
417*299976b0SCésar Belley 
418*299976b0SCésar Belley static void u2f_key_passthru_register_types(void)
419*299976b0SCésar Belley {
420*299976b0SCésar Belley     type_register_static(&u2f_key_passthru_info);
421*299976b0SCésar Belley }
422*299976b0SCésar Belley 
423*299976b0SCésar Belley type_init(u2f_key_passthru_register_types)
424