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