| File: | hw/usb/dev-smartcard-reader.c |
| Location: | line 811, column 5 |
| Description: | Null pointer passed as an argument to a 'nonnull' parameter |
| 1 | /* | |||
| 2 | * Copyright (C) 2011 Red Hat, Inc. | |||
| 3 | * | |||
| 4 | * CCID Device emulation | |||
| 5 | * | |||
| 6 | * Written by Alon Levy, with contributions from Robert Relyea. | |||
| 7 | * | |||
| 8 | * Based on usb-serial.c, see its copyright and attributions below. | |||
| 9 | * | |||
| 10 | * This work is licensed under the terms of the GNU GPL, version 2.1 or later. | |||
| 11 | * See the COPYING file in the top-level directory. | |||
| 12 | * ------- (original copyright & attribution for usb-serial.c below) -------- | |||
| 13 | * Copyright (c) 2006 CodeSourcery. | |||
| 14 | * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org> | |||
| 15 | * Written by Paul Brook, reused for FTDI by Samuel Thibault, | |||
| 16 | */ | |||
| 17 | ||||
| 18 | /* | |||
| 19 | * References: | |||
| 20 | * | |||
| 21 | * CCID Specification Revision 1.1 April 22nd 2005 | |||
| 22 | * "Universal Serial Bus, Device Class: Smart Card" | |||
| 23 | * Specification for Integrated Circuit(s) Cards Interface Devices | |||
| 24 | * | |||
| 25 | * Endianness note: from the spec (1.3) | |||
| 26 | * "Fields that are larger than a byte are stored in little endian" | |||
| 27 | * | |||
| 28 | * KNOWN BUGS | |||
| 29 | * 1. remove/insert can sometimes result in removed state instead of inserted. | |||
| 30 | * This is a result of the following: | |||
| 31 | * symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This can happen | |||
| 32 | * when a short packet is sent, as seen in uhci-usb.c, resulting from a urb | |||
| 33 | * from the guest requesting SPD and us returning a smaller packet. | |||
| 34 | * Not sure which messages trigger this. | |||
| 35 | */ | |||
| 36 | ||||
| 37 | #include "qemu-common.h" | |||
| 38 | #include "qemu/error-report.h" | |||
| 39 | #include "hw/usb.h" | |||
| 40 | #include "hw/usb/desc.h" | |||
| 41 | #include "monitor/monitor.h" | |||
| 42 | ||||
| 43 | #include "ccid.h" | |||
| 44 | ||||
| 45 | #define DPRINTF(s, lvl, fmt, ...)do { if (lvl <= s->debug) { printf("usb-ccid: " fmt , ... ); } } while (0) \ | |||
| 46 | do { \ | |||
| 47 | if (lvl <= s->debug) { \ | |||
| 48 | printf("usb-ccid: " fmt , ## __VA_ARGS__); \ | |||
| 49 | } \ | |||
| 50 | } while (0) | |||
| 51 | ||||
| 52 | #define D_WARN1 1 | |||
| 53 | #define D_INFO2 2 | |||
| 54 | #define D_MORE_INFO3 3 | |||
| 55 | #define D_VERBOSE4 4 | |||
| 56 | ||||
| 57 | #define CCID_DEV_NAME"usb-ccid" "usb-ccid" | |||
| 58 | ||||
| 59 | /* | |||
| 60 | * The two options for variable sized buffers: | |||
| 61 | * make them constant size, for large enough constant, | |||
| 62 | * or handle the migration complexity - VMState doesn't handle this case. | |||
| 63 | * sizes are expected never to be exceeded, unless guest misbehaves. | |||
| 64 | */ | |||
| 65 | #define BULK_OUT_DATA_SIZE65536 65536 | |||
| 66 | #define PENDING_ANSWERS_NUM128 128 | |||
| 67 | ||||
| 68 | #define BULK_IN_BUF_SIZE384 384 | |||
| 69 | #define BULK_IN_PENDING_NUM8 8 | |||
| 70 | ||||
| 71 | #define CCID_MAX_PACKET_SIZE64 64 | |||
| 72 | ||||
| 73 | #define CCID_CONTROL_ABORT0x1 0x1 | |||
| 74 | #define CCID_CONTROL_GET_CLOCK_FREQUENCIES0x2 0x2 | |||
| 75 | #define CCID_CONTROL_GET_DATA_RATES0x3 0x3 | |||
| 76 | ||||
| 77 | #define CCID_PRODUCT_DESCRIPTION"QEMU USB CCID" "QEMU USB CCID" | |||
| 78 | #define CCID_VENDOR_DESCRIPTION"QEMU" "QEMU" | |||
| 79 | #define CCID_INTERFACE_NAME"CCID Interface" "CCID Interface" | |||
| 80 | #define CCID_SERIAL_NUMBER_STRING"1" "1" | |||
| 81 | /* | |||
| 82 | * Using Gemplus Vendor and Product id | |||
| 83 | * Effect on various drivers: | |||
| 84 | * usbccid.sys (winxp, others untested) is a class driver so it doesn't care. | |||
| 85 | * linux has a number of class drivers, but openct filters based on | |||
| 86 | * vendor/product (/etc/openct.conf under fedora), hence Gemplus. | |||
| 87 | */ | |||
| 88 | #define CCID_VENDOR_ID0x08e6 0x08e6 | |||
| 89 | #define CCID_PRODUCT_ID0x4433 0x4433 | |||
| 90 | #define CCID_DEVICE_VERSION0x0000 0x0000 | |||
| 91 | ||||
| 92 | /* | |||
| 93 | * BULK_OUT messages from PC to Reader | |||
| 94 | * Defined in CCID Rev 1.1 6.1 (page 26) | |||
| 95 | */ | |||
| 96 | #define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn0x62 0x62 | |||
| 97 | #define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff0x63 0x63 | |||
| 98 | #define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus0x65 0x65 | |||
| 99 | #define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock0x6f 0x6f | |||
| 100 | #define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters0x6c 0x6c | |||
| 101 | #define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters0x6d 0x6d | |||
| 102 | #define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters0x61 0x61 | |||
| 103 | #define CCID_MESSAGE_TYPE_PC_to_RDR_Escape0x6b 0x6b | |||
| 104 | #define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock0x6e 0x6e | |||
| 105 | #define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU0x6a 0x6a | |||
| 106 | #define CCID_MESSAGE_TYPE_PC_to_RDR_Secure0x69 0x69 | |||
| 107 | #define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical0x71 0x71 | |||
| 108 | #define CCID_MESSAGE_TYPE_PC_to_RDR_Abort0x72 0x72 | |||
| 109 | #define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency0x73 0x73 | |||
| 110 | ||||
| 111 | /* | |||
| 112 | * BULK_IN messages from Reader to PC | |||
| 113 | * Defined in CCID Rev 1.1 6.2 (page 48) | |||
| 114 | */ | |||
| 115 | #define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock0x80 0x80 | |||
| 116 | #define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus0x81 0x81 | |||
| 117 | #define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters0x82 0x82 | |||
| 118 | #define CCID_MESSAGE_TYPE_RDR_to_PC_Escape0x83 0x83 | |||
| 119 | #define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency0x84 0x84 | |||
| 120 | ||||
| 121 | /* | |||
| 122 | * INTERRUPT_IN messages from Reader to PC | |||
| 123 | * Defined in CCID Rev 1.1 6.3 (page 56) | |||
| 124 | */ | |||
| 125 | #define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange0x50 0x50 | |||
| 126 | #define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError0x51 0x51 | |||
| 127 | ||||
| 128 | /* | |||
| 129 | * Endpoints for CCID - addresses are up to us to decide. | |||
| 130 | * To support slot insertion and removal we must have an interrupt in ep | |||
| 131 | * in addition we need a bulk in and bulk out ep | |||
| 132 | * 5.2, page 20 | |||
| 133 | */ | |||
| 134 | #define CCID_INT_IN_EP1 1 | |||
| 135 | #define CCID_BULK_IN_EP2 2 | |||
| 136 | #define CCID_BULK_OUT_EP3 3 | |||
| 137 | ||||
| 138 | /* bmSlotICCState masks */ | |||
| 139 | #define SLOT_0_STATE_MASK1 1 | |||
| 140 | #define SLOT_0_CHANGED_MASK2 2 | |||
| 141 | ||||
| 142 | /* Status codes that go in bStatus (see 6.2.6) */ | |||
| 143 | enum { | |||
| 144 | ICC_STATUS_PRESENT_ACTIVE = 0, | |||
| 145 | ICC_STATUS_PRESENT_INACTIVE, | |||
| 146 | ICC_STATUS_NOT_PRESENT | |||
| 147 | }; | |||
| 148 | ||||
| 149 | enum { | |||
| 150 | COMMAND_STATUS_NO_ERROR = 0, | |||
| 151 | COMMAND_STATUS_FAILED, | |||
| 152 | COMMAND_STATUS_TIME_EXTENSION_REQUIRED | |||
| 153 | }; | |||
| 154 | ||||
| 155 | /* Error codes that go in bError (see 6.2.6) */ | |||
| 156 | enum { | |||
| 157 | ERROR_CMD_NOT_SUPPORTED = 0, | |||
| 158 | ERROR_CMD_ABORTED = -1, | |||
| 159 | ERROR_ICC_MUTE = -2, | |||
| 160 | ERROR_XFR_PARITY_ERROR = -3, | |||
| 161 | ERROR_XFR_OVERRUN = -4, | |||
| 162 | ERROR_HW_ERROR = -5, | |||
| 163 | }; | |||
| 164 | ||||
| 165 | /* 6.2.6 RDR_to_PC_SlotStatus definitions */ | |||
| 166 | enum { | |||
| 167 | CLOCK_STATUS_RUNNING = 0, | |||
| 168 | /* | |||
| 169 | * 0 - Clock Running, 1 - Clock stopped in State L, 2 - H, | |||
| 170 | * 3 - unknown state. rest are RFU | |||
| 171 | */ | |||
| 172 | }; | |||
| 173 | ||||
| 174 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_Header { | |||
| 175 | uint8_t bMessageType; | |||
| 176 | uint32_t dwLength; | |||
| 177 | uint8_t bSlot; | |||
| 178 | uint8_t bSeq; | |||
| 179 | } CCID_Header; | |||
| 180 | ||||
| 181 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_BULK_IN { | |||
| 182 | CCID_Header hdr; | |||
| 183 | uint8_t bStatus; /* Only used in BULK_IN */ | |||
| 184 | uint8_t bError; /* Only used in BULK_IN */ | |||
| 185 | } CCID_BULK_IN; | |||
| 186 | ||||
| 187 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_SlotStatus { | |||
| 188 | CCID_BULK_IN b; | |||
| 189 | uint8_t bClockStatus; | |||
| 190 | } CCID_SlotStatus; | |||
| 191 | ||||
| 192 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_T0ProtocolDataStructure { | |||
| 193 | uint8_t bmFindexDindex; | |||
| 194 | uint8_t bmTCCKST0; | |||
| 195 | uint8_t bGuardTimeT0; | |||
| 196 | uint8_t bWaitingIntegerT0; | |||
| 197 | uint8_t bClockStop; | |||
| 198 | } CCID_T0ProtocolDataStructure; | |||
| 199 | ||||
| 200 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_T1ProtocolDataStructure { | |||
| 201 | uint8_t bmFindexDindex; | |||
| 202 | uint8_t bmTCCKST1; | |||
| 203 | uint8_t bGuardTimeT1; | |||
| 204 | uint8_t bWaitingIntegerT1; | |||
| 205 | uint8_t bClockStop; | |||
| 206 | uint8_t bIFSC; | |||
| 207 | uint8_t bNadValue; | |||
| 208 | } CCID_T1ProtocolDataStructure; | |||
| 209 | ||||
| 210 | typedef union CCID_ProtocolDataStructure { | |||
| 211 | CCID_T0ProtocolDataStructure t0; | |||
| 212 | CCID_T1ProtocolDataStructure t1; | |||
| 213 | uint8_t data[7]; /* must be = max(sizeof(t0), sizeof(t1)) */ | |||
| 214 | } CCID_ProtocolDataStructure; | |||
| 215 | ||||
| 216 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_Parameter { | |||
| 217 | CCID_BULK_IN b; | |||
| 218 | uint8_t bProtocolNum; | |||
| 219 | CCID_ProtocolDataStructure abProtocolDataStructure; | |||
| 220 | } CCID_Parameter; | |||
| 221 | ||||
| 222 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_DataBlock { | |||
| 223 | CCID_BULK_IN b; | |||
| 224 | uint8_t bChainParameter; | |||
| 225 | uint8_t abData[0]; | |||
| 226 | } CCID_DataBlock; | |||
| 227 | ||||
| 228 | /* 6.1.4 PC_to_RDR_XfrBlock */ | |||
| 229 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_XferBlock { | |||
| 230 | CCID_Header hdr; | |||
| 231 | uint8_t bBWI; /* Block Waiting Timeout */ | |||
| 232 | uint16_t wLevelParameter; /* XXX currently unused */ | |||
| 233 | uint8_t abData[0]; | |||
| 234 | } CCID_XferBlock; | |||
| 235 | ||||
| 236 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_IccPowerOn { | |||
| 237 | CCID_Header hdr; | |||
| 238 | uint8_t bPowerSelect; | |||
| 239 | uint16_t abRFU; | |||
| 240 | } CCID_IccPowerOn; | |||
| 241 | ||||
| 242 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_IccPowerOff { | |||
| 243 | CCID_Header hdr; | |||
| 244 | uint16_t abRFU; | |||
| 245 | } CCID_IccPowerOff; | |||
| 246 | ||||
| 247 | typedef struct QEMU_PACKED__attribute__((packed)) CCID_SetParameters { | |||
| 248 | CCID_Header hdr; | |||
| 249 | uint8_t bProtocolNum; | |||
| 250 | uint16_t abRFU; | |||
| 251 | CCID_ProtocolDataStructure abProtocolDataStructure; | |||
| 252 | } CCID_SetParameters; | |||
| 253 | ||||
| 254 | typedef struct CCID_Notify_Slot_Change { | |||
| 255 | uint8_t bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */ | |||
| 256 | uint8_t bmSlotICCState; | |||
| 257 | } CCID_Notify_Slot_Change; | |||
| 258 | ||||
| 259 | /* used for DataBlock response to XferBlock */ | |||
| 260 | typedef struct Answer { | |||
| 261 | uint8_t slot; | |||
| 262 | uint8_t seq; | |||
| 263 | } Answer; | |||
| 264 | ||||
| 265 | /* pending BULK_IN messages */ | |||
| 266 | typedef struct BulkIn { | |||
| 267 | uint8_t data[BULK_IN_BUF_SIZE384]; | |||
| 268 | uint32_t len; | |||
| 269 | uint32_t pos; | |||
| 270 | } BulkIn; | |||
| 271 | ||||
| 272 | enum { | |||
| 273 | MIGRATION_NONE, | |||
| 274 | MIGRATION_MIGRATED, | |||
| 275 | }; | |||
| 276 | ||||
| 277 | typedef struct CCIDBus { | |||
| 278 | BusState qbus; | |||
| 279 | } CCIDBus; | |||
| 280 | ||||
| 281 | /* | |||
| 282 | * powered - defaults to true, changed by PowerOn/PowerOff messages | |||
| 283 | */ | |||
| 284 | typedef struct USBCCIDState { | |||
| 285 | USBDevice dev; | |||
| 286 | USBEndpoint *intr; | |||
| 287 | CCIDBus bus; | |||
| 288 | CCIDCardState *card; | |||
| 289 | BulkIn bulk_in_pending[BULK_IN_PENDING_NUM8]; /* circular */ | |||
| 290 | uint32_t bulk_in_pending_start; | |||
| 291 | uint32_t bulk_in_pending_end; /* first free */ | |||
| 292 | uint32_t bulk_in_pending_num; | |||
| 293 | BulkIn *current_bulk_in; | |||
| 294 | uint8_t bulk_out_data[BULK_OUT_DATA_SIZE65536]; | |||
| 295 | uint32_t bulk_out_pos; | |||
| 296 | uint64_t last_answer_error; | |||
| 297 | Answer pending_answers[PENDING_ANSWERS_NUM128]; | |||
| 298 | uint32_t pending_answers_start; | |||
| 299 | uint32_t pending_answers_end; | |||
| 300 | uint32_t pending_answers_num; | |||
| 301 | uint8_t bError; | |||
| 302 | uint8_t bmCommandStatus; | |||
| 303 | uint8_t bProtocolNum; | |||
| 304 | CCID_ProtocolDataStructure abProtocolDataStructure; | |||
| 305 | uint32_t ulProtocolDataStructureSize; | |||
| 306 | uint32_t state_vmstate; | |||
| 307 | uint32_t migration_target_ip; | |||
| 308 | uint16_t migration_target_port; | |||
| 309 | uint8_t migration_state; | |||
| 310 | uint8_t bmSlotICCState; | |||
| 311 | uint8_t powered; | |||
| 312 | uint8_t notify_slot_change; | |||
| 313 | uint8_t debug; | |||
| 314 | } USBCCIDState; | |||
| 315 | ||||
| 316 | /* | |||
| 317 | * CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9, | |||
| 318 | * "USB Device Framework", section 9.6.1, in the Universal Serial Bus | |||
| 319 | * Specification. | |||
| 320 | * | |||
| 321 | * This device implemented based on the spec and with an Athena Smart Card | |||
| 322 | * Reader as reference: | |||
| 323 | * 0dc3:1004 Athena Smartcard Solutions, Inc. | |||
| 324 | */ | |||
| 325 | ||||
| 326 | static const uint8_t qemu_ccid_descriptor[] = { | |||
| 327 | /* Smart Card Device Class Descriptor */ | |||
| 328 | 0x36, /* u8 bLength; */ | |||
| 329 | 0x21, /* u8 bDescriptorType; Functional */ | |||
| 330 | 0x10, 0x01, /* u16 bcdCCID; CCID Specification Release Number. */ | |||
| 331 | 0x00, /* | |||
| 332 | * u8 bMaxSlotIndex; The index of the highest available | |||
| 333 | * slot on this device. All slots are consecutive starting | |||
| 334 | * at 00h. | |||
| 335 | */ | |||
| 336 | 0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ | |||
| 337 | ||||
| 338 | 0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ | |||
| 339 | 0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ | |||
| 340 | /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ | |||
| 341 | 0xa0, 0x0f, 0x00, 0x00, | |||
| 342 | /* u32 dwMaximumClock; */ | |||
| 343 | 0x00, 0x00, 0x01, 0x00, | |||
| 344 | 0x00, /* u8 bNumClockSupported; * | |||
| 345 | * 0 means just the default and max. */ | |||
| 346 | /* u32 dwDataRate ;bps. 9600 == 00002580h */ | |||
| 347 | 0x80, 0x25, 0x00, 0x00, | |||
| 348 | /* u32 dwMaxDataRate ; 11520 bps == 0001C200h */ | |||
| 349 | 0x00, 0xC2, 0x01, 0x00, | |||
| 350 | 0x00, /* u8 bNumDataRatesSupported; 00 means all rates between | |||
| 351 | * default and max */ | |||
| 352 | /* u32 dwMaxIFSD; * | |||
| 353 | * maximum IFSD supported by CCID for protocol * | |||
| 354 | * T=1 (Maximum seen from various cards) */ | |||
| 355 | 0xfe, 0x00, 0x00, 0x00, | |||
| 356 | /* u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */ | |||
| 357 | 0x00, 0x00, 0x00, 0x00, | |||
| 358 | /* u32 dwMechanical; 0 - no special characteristics. */ | |||
| 359 | 0x00, 0x00, 0x00, 0x00, | |||
| 360 | /* | |||
| 361 | * u32 dwFeatures; | |||
| 362 | * 0 - No special characteristics | |||
| 363 | * + 2 Automatic parameter configuration based on ATR data | |||
| 364 | * + 4 Automatic activation of ICC on inserting | |||
| 365 | * + 8 Automatic ICC voltage selection | |||
| 366 | * + 10 Automatic ICC clock frequency change | |||
| 367 | * + 20 Automatic baud rate change | |||
| 368 | * + 40 Automatic parameters negotiation made by the CCID | |||
| 369 | * + 80 automatic PPS made by the CCID | |||
| 370 | * 100 CCID can set ICC in clock stop mode | |||
| 371 | * 200 NAD value other then 00 accepted (T=1 protocol) | |||
| 372 | * + 400 Automatic IFSD exchange as first exchange (T=1) | |||
| 373 | * One of the following only: | |||
| 374 | * + 10000 TPDU level exchanges with CCID | |||
| 375 | * 20000 Short APDU level exchange with CCID | |||
| 376 | * 40000 Short and Extended APDU level exchange with CCID | |||
| 377 | * | |||
| 378 | * 100000 USB Wake up signaling supported on card | |||
| 379 | * insertion and removal. Must set bit 5 in bmAttributes | |||
| 380 | * in Configuration descriptor if 100000 is set. | |||
| 381 | */ | |||
| 382 | 0xfe, 0x04, 0x01, 0x00, | |||
| 383 | /* | |||
| 384 | * u32 dwMaxCCIDMessageLength; For extended APDU in | |||
| 385 | * [261 + 10 , 65544 + 10]. Otherwise the minimum is | |||
| 386 | * wMaxPacketSize of the Bulk-OUT endpoint | |||
| 387 | */ | |||
| 388 | 0x12, 0x00, 0x01, 0x00, | |||
| 389 | 0xFF, /* | |||
| 390 | * u8 bClassGetResponse; Significant only for CCID that | |||
| 391 | * offers an APDU level for exchanges. Indicates the | |||
| 392 | * default class value used by the CCID when it sends a | |||
| 393 | * Get Response command to perform the transportation of | |||
| 394 | * an APDU by T=0 protocol | |||
| 395 | * FFh indicates that the CCID echos the class of the APDU. | |||
| 396 | */ | |||
| 397 | 0xFF, /* | |||
| 398 | * u8 bClassEnvelope; EAPDU only. Envelope command for | |||
| 399 | * T=0 | |||
| 400 | */ | |||
| 401 | 0x00, 0x00, /* | |||
| 402 | * u16 wLcdLayout; XXYY Number of lines (XX) and chars per | |||
| 403 | * line for LCD display used for PIN entry. 0000 - no LCD | |||
| 404 | */ | |||
| 405 | 0x01, /* | |||
| 406 | * u8 bPINSupport; 01h PIN Verification, | |||
| 407 | * 02h PIN Modification | |||
| 408 | */ | |||
| 409 | 0x01, /* u8 bMaxCCIDBusySlots; */ | |||
| 410 | }; | |||
| 411 | ||||
| 412 | enum { | |||
| 413 | STR_MANUFACTURER = 1, | |||
| 414 | STR_PRODUCT, | |||
| 415 | STR_SERIALNUMBER, | |||
| 416 | STR_INTERFACE, | |||
| 417 | }; | |||
| 418 | ||||
| 419 | static const USBDescStrings desc_strings = { | |||
| 420 | [STR_MANUFACTURER] = "QEMU", | |||
| 421 | [STR_PRODUCT] = "QEMU USB CCID", | |||
| 422 | [STR_SERIALNUMBER] = "1", | |||
| 423 | [STR_INTERFACE] = "CCID Interface", | |||
| 424 | }; | |||
| 425 | ||||
| 426 | static const USBDescIface desc_iface0 = { | |||
| 427 | .bInterfaceNumber = 0, | |||
| 428 | .bNumEndpoints = 3, | |||
| 429 | .bInterfaceClass = USB_CLASS_CSCID0x0b, | |||
| 430 | .bInterfaceSubClass = USB_SUBCLASS_UNDEFINED0, | |||
| 431 | .bInterfaceProtocol = 0x00, | |||
| 432 | .iInterface = STR_INTERFACE, | |||
| 433 | .ndesc = 1, | |||
| 434 | .descs = (USBDescOther[]) { | |||
| 435 | { | |||
| 436 | /* smartcard descriptor */ | |||
| 437 | .data = qemu_ccid_descriptor, | |||
| 438 | }, | |||
| 439 | }, | |||
| 440 | .eps = (USBDescEndpoint[]) { | |||
| 441 | { | |||
| 442 | .bEndpointAddress = USB_DIR_IN0x80 | CCID_INT_IN_EP1, | |||
| 443 | .bmAttributes = USB_ENDPOINT_XFER_INT3, | |||
| 444 | .bInterval = 255, | |||
| 445 | .wMaxPacketSize = 64, | |||
| 446 | },{ | |||
| 447 | .bEndpointAddress = USB_DIR_IN0x80 | CCID_BULK_IN_EP2, | |||
| 448 | .bmAttributes = USB_ENDPOINT_XFER_BULK2, | |||
| 449 | .wMaxPacketSize = 64, | |||
| 450 | },{ | |||
| 451 | .bEndpointAddress = USB_DIR_OUT0 | CCID_BULK_OUT_EP3, | |||
| 452 | .bmAttributes = USB_ENDPOINT_XFER_BULK2, | |||
| 453 | .wMaxPacketSize = 64, | |||
| 454 | }, | |||
| 455 | } | |||
| 456 | }; | |||
| 457 | ||||
| 458 | static const USBDescDevice desc_device = { | |||
| 459 | .bcdUSB = 0x0110, | |||
| 460 | .bMaxPacketSize0 = 64, | |||
| 461 | .bNumConfigurations = 1, | |||
| 462 | .confs = (USBDescConfig[]) { | |||
| 463 | { | |||
| 464 | .bNumInterfaces = 1, | |||
| 465 | .bConfigurationValue = 1, | |||
| 466 | .bmAttributes = 0xe0, | |||
| 467 | .bMaxPower = 50, | |||
| 468 | .nif = 1, | |||
| 469 | .ifs = &desc_iface0, | |||
| 470 | }, | |||
| 471 | }, | |||
| 472 | }; | |||
| 473 | ||||
| 474 | static const USBDesc desc_ccid = { | |||
| 475 | .id = { | |||
| 476 | .idVendor = CCID_VENDOR_ID0x08e6, | |||
| 477 | .idProduct = CCID_PRODUCT_ID0x4433, | |||
| 478 | .bcdDevice = CCID_DEVICE_VERSION0x0000, | |||
| 479 | .iManufacturer = STR_MANUFACTURER, | |||
| 480 | .iProduct = STR_PRODUCT, | |||
| 481 | .iSerialNumber = STR_SERIALNUMBER, | |||
| 482 | }, | |||
| 483 | .full = &desc_device, | |||
| 484 | .str = desc_strings, | |||
| 485 | }; | |||
| 486 | ||||
| 487 | static const uint8_t *ccid_card_get_atr(CCIDCardState *card, uint32_t *len) | |||
| 488 | { | |||
| 489 | CCIDCardClass *cc = CCID_CARD_GET_CLASS(card)((CCIDCardClass *)object_class_dynamic_cast_assert(((ObjectClass *)(object_get_class(((Object *)((card)))))), ("ccid-card"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 489, __func__)); | |||
| 490 | ||||
| 491 | if (cc->get_atr) { | |||
| 492 | return cc->get_atr(card, len); | |||
| 493 | } | |||
| 494 | return NULL((void*)0); | |||
| 495 | } | |||
| 496 | ||||
| 497 | static void ccid_card_apdu_from_guest(CCIDCardState *card, | |||
| 498 | const uint8_t *apdu, | |||
| 499 | uint32_t len) | |||
| 500 | { | |||
| 501 | CCIDCardClass *cc = CCID_CARD_GET_CLASS(card)((CCIDCardClass *)object_class_dynamic_cast_assert(((ObjectClass *)(object_get_class(((Object *)((card)))))), ("ccid-card"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 501, __func__)); | |||
| 502 | ||||
| 503 | if (cc->apdu_from_guest) { | |||
| 504 | cc->apdu_from_guest(card, apdu, len); | |||
| 505 | } | |||
| 506 | } | |||
| 507 | ||||
| 508 | static int ccid_card_exitfn(CCIDCardState *card) | |||
| 509 | { | |||
| 510 | CCIDCardClass *cc = CCID_CARD_GET_CLASS(card)((CCIDCardClass *)object_class_dynamic_cast_assert(((ObjectClass *)(object_get_class(((Object *)((card)))))), ("ccid-card"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 510, __func__)); | |||
| 511 | ||||
| 512 | if (cc->exitfn) { | |||
| 513 | return cc->exitfn(card); | |||
| 514 | } | |||
| 515 | return 0; | |||
| 516 | } | |||
| 517 | ||||
| 518 | static int ccid_card_initfn(CCIDCardState *card) | |||
| 519 | { | |||
| 520 | CCIDCardClass *cc = CCID_CARD_GET_CLASS(card)((CCIDCardClass *)object_class_dynamic_cast_assert(((ObjectClass *)(object_get_class(((Object *)((card)))))), ("ccid-card"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 520, __func__)); | |||
| 521 | ||||
| 522 | if (cc->initfn) { | |||
| 523 | return cc->initfn(card); | |||
| 524 | } | |||
| 525 | return 0; | |||
| 526 | } | |||
| 527 | ||||
| 528 | static bool_Bool ccid_has_pending_answers(USBCCIDState *s) | |||
| 529 | { | |||
| 530 | return s->pending_answers_num > 0; | |||
| 531 | } | |||
| 532 | ||||
| 533 | static void ccid_clear_pending_answers(USBCCIDState *s) | |||
| 534 | { | |||
| 535 | s->pending_answers_num = 0; | |||
| 536 | s->pending_answers_start = 0; | |||
| 537 | s->pending_answers_end = 0; | |||
| 538 | } | |||
| 539 | ||||
| 540 | static void ccid_print_pending_answers(USBCCIDState *s) | |||
| 541 | { | |||
| 542 | Answer *answer; | |||
| 543 | int i, count; | |||
| 544 | ||||
| 545 | DPRINTF(s, D_VERBOSE, "usb-ccid: pending answers:")do { if (4 <= s->debug) { printf("usb-ccid: " "usb-ccid: pending answers:" ); } } while (0); | |||
| 546 | if (!ccid_has_pending_answers(s)) { | |||
| 547 | DPRINTF(s, D_VERBOSE, " empty\n")do { if (4 <= s->debug) { printf("usb-ccid: " " empty\n" ); } } while (0); | |||
| 548 | return; | |||
| 549 | } | |||
| 550 | for (i = s->pending_answers_start, count = s->pending_answers_num ; | |||
| 551 | count > 0; count--, i++) { | |||
| 552 | answer = &s->pending_answers[i % PENDING_ANSWERS_NUM128]; | |||
| 553 | if (count == 1) { | |||
| 554 | DPRINTF(s, D_VERBOSE, "%d:%d\n", answer->slot, answer->seq)do { if (4 <= s->debug) { printf("usb-ccid: " "%d:%d\n" , answer->slot, answer->seq); } } while (0); | |||
| 555 | } else { | |||
| 556 | DPRINTF(s, D_VERBOSE, "%d:%d,", answer->slot, answer->seq)do { if (4 <= s->debug) { printf("usb-ccid: " "%d:%d," , answer->slot, answer->seq); } } while (0); | |||
| 557 | } | |||
| 558 | } | |||
| 559 | } | |||
| 560 | ||||
| 561 | static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr) | |||
| 562 | { | |||
| 563 | Answer *answer; | |||
| 564 | ||||
| 565 | assert(s->pending_answers_num < PENDING_ANSWERS_NUM)((s->pending_answers_num < 128) ? (void) (0) : __assert_fail ("s->pending_answers_num < 128", "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 565, __PRETTY_FUNCTION__)); | |||
| 566 | s->pending_answers_num++; | |||
| 567 | answer = | |||
| 568 | &s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM128]; | |||
| 569 | answer->slot = hdr->bSlot; | |||
| 570 | answer->seq = hdr->bSeq; | |||
| 571 | ccid_print_pending_answers(s); | |||
| 572 | } | |||
| 573 | ||||
| 574 | static void ccid_remove_pending_answer(USBCCIDState *s, | |||
| 575 | uint8_t *slot, uint8_t *seq) | |||
| 576 | { | |||
| 577 | Answer *answer; | |||
| 578 | ||||
| 579 | assert(s->pending_answers_num > 0)((s->pending_answers_num > 0) ? (void) (0) : __assert_fail ("s->pending_answers_num > 0", "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 579, __PRETTY_FUNCTION__)); | |||
| 580 | s->pending_answers_num--; | |||
| 581 | answer = | |||
| 582 | &s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM128]; | |||
| 583 | *slot = answer->slot; | |||
| 584 | *seq = answer->seq; | |||
| 585 | ccid_print_pending_answers(s); | |||
| 586 | } | |||
| 587 | ||||
| 588 | static void ccid_bulk_in_clear(USBCCIDState *s) | |||
| 589 | { | |||
| 590 | s->bulk_in_pending_start = 0; | |||
| 591 | s->bulk_in_pending_end = 0; | |||
| 592 | s->bulk_in_pending_num = 0; | |||
| 593 | } | |||
| 594 | ||||
| 595 | static void ccid_bulk_in_release(USBCCIDState *s) | |||
| 596 | { | |||
| 597 | assert(s->current_bulk_in != NULL)((s->current_bulk_in != ((void*)0)) ? (void) (0) : __assert_fail ("s->current_bulk_in != ((void*)0)", "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 597, __PRETTY_FUNCTION__)); | |||
| 598 | s->current_bulk_in->pos = 0; | |||
| 599 | s->current_bulk_in = NULL((void*)0); | |||
| 600 | } | |||
| 601 | ||||
| 602 | static void ccid_bulk_in_get(USBCCIDState *s) | |||
| 603 | { | |||
| 604 | if (s->current_bulk_in != NULL((void*)0) || s->bulk_in_pending_num == 0) { | |||
| 605 | return; | |||
| 606 | } | |||
| 607 | assert(s->bulk_in_pending_num > 0)((s->bulk_in_pending_num > 0) ? (void) (0) : __assert_fail ("s->bulk_in_pending_num > 0", "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 607, __PRETTY_FUNCTION__)); | |||
| 608 | s->bulk_in_pending_num--; | |||
| 609 | s->current_bulk_in = | |||
| 610 | &s->bulk_in_pending[(s->bulk_in_pending_start++) % BULK_IN_PENDING_NUM8]; | |||
| 611 | } | |||
| 612 | ||||
| 613 | static void *ccid_reserve_recv_buf(USBCCIDState *s, uint16_t len) | |||
| 614 | { | |||
| 615 | BulkIn *bulk_in; | |||
| 616 | ||||
| 617 | DPRINTF(s, D_VERBOSE, "%s: QUEUE: reserve %d bytes\n", __func__, len)do { if (4 <= s->debug) { printf("usb-ccid: " "%s: QUEUE: reserve %d bytes\n" , __func__, len); } } while (0); | |||
| 618 | ||||
| 619 | /* look for an existing element */ | |||
| 620 | if (len > BULK_IN_BUF_SIZE384) { | |||
| 621 | DPRINTF(s, D_WARN, "usb-ccid.c: %s: len larger then max (%d>%d). "do { if (1 <= s->debug) { printf("usb-ccid: " "usb-ccid.c: %s: len larger then max (%d>%d). " "discarding message.\n" , __func__, len, 384); } } while (0) | |||
| 622 | "discarding message.\n",do { if (1 <= s->debug) { printf("usb-ccid: " "usb-ccid.c: %s: len larger then max (%d>%d). " "discarding message.\n" , __func__, len, 384); } } while (0) | |||
| 623 | __func__, len, BULK_IN_BUF_SIZE)do { if (1 <= s->debug) { printf("usb-ccid: " "usb-ccid.c: %s: len larger then max (%d>%d). " "discarding message.\n" , __func__, len, 384); } } while (0); | |||
| 624 | return NULL((void*)0); | |||
| 625 | } | |||
| 626 | if (s->bulk_in_pending_num >= BULK_IN_PENDING_NUM8) { | |||
| 627 | DPRINTF(s, D_WARN, "usb-ccid.c: %s: No free bulk_in buffers. "do { if (1 <= s->debug) { printf("usb-ccid: " "usb-ccid.c: %s: No free bulk_in buffers. " "discarding message.\n" , __func__); } } while (0) | |||
| 628 | "discarding message.\n", __func__)do { if (1 <= s->debug) { printf("usb-ccid: " "usb-ccid.c: %s: No free bulk_in buffers. " "discarding message.\n" , __func__); } } while (0); | |||
| 629 | return NULL((void*)0); | |||
| 630 | } | |||
| 631 | bulk_in = | |||
| 632 | &s->bulk_in_pending[(s->bulk_in_pending_end++) % BULK_IN_PENDING_NUM8]; | |||
| 633 | s->bulk_in_pending_num++; | |||
| 634 | bulk_in->len = len; | |||
| 635 | return bulk_in->data; | |||
| 636 | } | |||
| 637 | ||||
| 638 | static void ccid_reset(USBCCIDState *s) | |||
| 639 | { | |||
| 640 | ccid_bulk_in_clear(s); | |||
| 641 | ccid_clear_pending_answers(s); | |||
| 642 | } | |||
| 643 | ||||
| 644 | static void ccid_detach(USBCCIDState *s) | |||
| 645 | { | |||
| 646 | ccid_reset(s); | |||
| 647 | } | |||
| 648 | ||||
| 649 | static void ccid_handle_reset(USBDevice *dev) | |||
| 650 | { | |||
| 651 | USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev)]; ({ const typeof((( USBCCIDState *) 0)->dev) *__mptr = (dev); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof(USBCCIDState, dev));}) ;})); | |||
| 652 | ||||
| 653 | DPRINTF(s, 1, "Reset\n")do { if (1 <= s->debug) { printf("usb-ccid: " "Reset\n" ); } } while (0); | |||
| 654 | ||||
| 655 | ccid_reset(s); | |||
| 656 | } | |||
| 657 | ||||
| 658 | static const char *ccid_control_to_str(USBCCIDState *s, int request) | |||
| 659 | { | |||
| 660 | switch (request) { | |||
| 661 | /* generic - should be factored out if there are other debugees */ | |||
| 662 | case DeviceOutRequest((0|(0x00 << 5)|0x00)<<8) | USB_REQ_SET_ADDRESS0x05: | |||
| 663 | return "(generic) set address"; | |||
| 664 | case DeviceRequest((0x80|(0x00 << 5)|0x00)<<8) | USB_REQ_GET_DESCRIPTOR0x06: | |||
| 665 | return "(generic) get descriptor"; | |||
| 666 | case DeviceRequest((0x80|(0x00 << 5)|0x00)<<8) | USB_REQ_GET_CONFIGURATION0x08: | |||
| 667 | return "(generic) get configuration"; | |||
| 668 | case DeviceOutRequest((0|(0x00 << 5)|0x00)<<8) | USB_REQ_SET_CONFIGURATION0x09: | |||
| 669 | return "(generic) set configuration"; | |||
| 670 | case DeviceRequest((0x80|(0x00 << 5)|0x00)<<8) | USB_REQ_GET_STATUS0x00: | |||
| 671 | return "(generic) get status"; | |||
| 672 | case DeviceOutRequest((0|(0x00 << 5)|0x00)<<8) | USB_REQ_CLEAR_FEATURE0x01: | |||
| 673 | return "(generic) clear feature"; | |||
| 674 | case DeviceOutRequest((0|(0x00 << 5)|0x00)<<8) | USB_REQ_SET_FEATURE0x03: | |||
| 675 | return "(generic) set_feature"; | |||
| 676 | case InterfaceRequest((0x80|(0x00 << 5)|0x01)<<8) | USB_REQ_GET_INTERFACE0x0A: | |||
| 677 | return "(generic) get interface"; | |||
| 678 | case InterfaceOutRequest((0|(0x00 << 5)|0x01)<<8) | USB_REQ_SET_INTERFACE0x0B: | |||
| 679 | return "(generic) set interface"; | |||
| 680 | /* class requests */ | |||
| 681 | case ClassInterfaceOutRequest((0|(0x01 << 5)|0x01)<<8) | CCID_CONTROL_ABORT0x1: | |||
| 682 | return "ABORT"; | |||
| 683 | case ClassInterfaceRequest((0x80|(0x01 << 5)|0x01)<<8) | CCID_CONTROL_GET_CLOCK_FREQUENCIES0x2: | |||
| 684 | return "GET_CLOCK_FREQUENCIES"; | |||
| 685 | case ClassInterfaceRequest((0x80|(0x01 << 5)|0x01)<<8) | CCID_CONTROL_GET_DATA_RATES0x3: | |||
| 686 | return "GET_DATA_RATES"; | |||
| 687 | } | |||
| 688 | return "unknown"; | |||
| 689 | } | |||
| 690 | ||||
| 691 | static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request, | |||
| 692 | int value, int index, int length, uint8_t *data) | |||
| 693 | { | |||
| 694 | USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev)]; ({ const typeof((( USBCCIDState *) 0)->dev) *__mptr = (dev); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof(USBCCIDState, dev));}) ;})); | |||
| 695 | int ret; | |||
| 696 | ||||
| 697 | DPRINTF(s, 1, "%s: got control %s (%x), value %x\n", __func__,do { if (1 <= s->debug) { printf("usb-ccid: " "%s: got control %s (%x), value %x\n" , __func__, ccid_control_to_str(s, request), request, value) ; } } while (0) | |||
| 698 | ccid_control_to_str(s, request), request, value)do { if (1 <= s->debug) { printf("usb-ccid: " "%s: got control %s (%x), value %x\n" , __func__, ccid_control_to_str(s, request), request, value) ; } } while (0); | |||
| 699 | ret = usb_desc_handle_control(dev, p, request, value, index, length, data); | |||
| 700 | if (ret >= 0) { | |||
| 701 | return; | |||
| 702 | } | |||
| 703 | ||||
| 704 | switch (request) { | |||
| 705 | /* Class specific requests. */ | |||
| 706 | case ClassInterfaceOutRequest((0|(0x01 << 5)|0x01)<<8) | CCID_CONTROL_ABORT0x1: | |||
| 707 | DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n")do { if (1 <= s->debug) { printf("usb-ccid: " "ccid_control abort UNIMPLEMENTED\n" ); } } while (0); | |||
| 708 | p->status = USB_RET_STALL(-3); | |||
| 709 | break; | |||
| 710 | case ClassInterfaceRequest((0x80|(0x01 << 5)|0x01)<<8) | CCID_CONTROL_GET_CLOCK_FREQUENCIES0x2: | |||
| 711 | DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n")do { if (1 <= s->debug) { printf("usb-ccid: " "ccid_control get clock frequencies UNIMPLEMENTED\n" ); } } while (0); | |||
| 712 | p->status = USB_RET_STALL(-3); | |||
| 713 | break; | |||
| 714 | case ClassInterfaceRequest((0x80|(0x01 << 5)|0x01)<<8) | CCID_CONTROL_GET_DATA_RATES0x3: | |||
| 715 | DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n")do { if (1 <= s->debug) { printf("usb-ccid: " "ccid_control get data rates UNIMPLEMENTED\n" ); } } while (0); | |||
| 716 | p->status = USB_RET_STALL(-3); | |||
| 717 | break; | |||
| 718 | default: | |||
| 719 | DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",do { if (1 <= s->debug) { printf("usb-ccid: " "got unsupported/bogus control %x, value %x\n" , request, value); } } while (0) | |||
| 720 | request, value)do { if (1 <= s->debug) { printf("usb-ccid: " "got unsupported/bogus control %x, value %x\n" , request, value); } } while (0); | |||
| 721 | p->status = USB_RET_STALL(-3); | |||
| 722 | break; | |||
| 723 | } | |||
| 724 | } | |||
| 725 | ||||
| 726 | static bool_Bool ccid_card_inserted(USBCCIDState *s) | |||
| 727 | { | |||
| 728 | return s->bmSlotICCState & SLOT_0_STATE_MASK1; | |||
| 729 | } | |||
| 730 | ||||
| 731 | static uint8_t ccid_card_status(USBCCIDState *s) | |||
| 732 | { | |||
| 733 | return ccid_card_inserted(s) | |||
| 734 | ? (s->powered ? | |||
| 735 | ICC_STATUS_PRESENT_ACTIVE | |||
| 736 | : ICC_STATUS_PRESENT_INACTIVE | |||
| 737 | ) | |||
| 738 | : ICC_STATUS_NOT_PRESENT; | |||
| 739 | } | |||
| 740 | ||||
| 741 | static uint8_t ccid_calc_status(USBCCIDState *s) | |||
| 742 | { | |||
| 743 | /* | |||
| 744 | * page 55, 6.2.6, calculation of bStatus from bmICCStatus and | |||
| 745 | * bmCommandStatus | |||
| 746 | */ | |||
| 747 | uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6); | |||
| 748 | DPRINTF(s, D_VERBOSE, "%s: status = %d\n", __func__, ret)do { if (4 <= s->debug) { printf("usb-ccid: " "%s: status = %d\n" , __func__, ret); } } while (0); | |||
| 749 | return ret; | |||
| 750 | } | |||
| 751 | ||||
| 752 | static void ccid_reset_error_status(USBCCIDState *s) | |||
| 753 | { | |||
| 754 | s->bError = ERROR_CMD_NOT_SUPPORTED; | |||
| 755 | s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; | |||
| 756 | } | |||
| 757 | ||||
| 758 | static void ccid_write_slot_status(USBCCIDState *s, CCID_Header *recv) | |||
| 759 | { | |||
| 760 | CCID_SlotStatus *h = ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus)); | |||
| 761 | if (h == NULL((void*)0)) { | |||
| 762 | return; | |||
| 763 | } | |||
| 764 | h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus0x81; | |||
| 765 | h->b.hdr.dwLength = 0; | |||
| 766 | h->b.hdr.bSlot = recv->bSlot; | |||
| 767 | h->b.hdr.bSeq = recv->bSeq; | |||
| 768 | h->b.bStatus = ccid_calc_status(s); | |||
| 769 | h->b.bError = s->bError; | |||
| 770 | h->bClockStatus = CLOCK_STATUS_RUNNING; | |||
| 771 | ccid_reset_error_status(s); | |||
| 772 | } | |||
| 773 | ||||
| 774 | static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv) | |||
| 775 | { | |||
| 776 | CCID_Parameter *h; | |||
| 777 | uint32_t len = s->ulProtocolDataStructureSize; | |||
| 778 | ||||
| 779 | h = ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len); | |||
| 780 | if (h == NULL((void*)0)) { | |||
| 781 | return; | |||
| 782 | } | |||
| 783 | h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters0x82; | |||
| 784 | h->b.hdr.dwLength = 0; | |||
| 785 | h->b.hdr.bSlot = recv->bSlot; | |||
| 786 | h->b.hdr.bSeq = recv->bSeq; | |||
| 787 | h->b.bStatus = ccid_calc_status(s); | |||
| 788 | h->b.bError = s->bError; | |||
| 789 | h->bProtocolNum = s->bProtocolNum; | |||
| 790 | h->abProtocolDataStructure = s->abProtocolDataStructure; | |||
| 791 | ccid_reset_error_status(s); | |||
| 792 | } | |||
| 793 | ||||
| 794 | static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq, | |||
| 795 | const uint8_t *data, uint32_t len) | |||
| 796 | { | |||
| 797 | CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len); | |||
| 798 | ||||
| 799 | if (p == NULL((void*)0)) { | |||
| 800 | return; | |||
| 801 | } | |||
| 802 | p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock0x80; | |||
| 803 | p->b.hdr.dwLength = cpu_to_le32(len); | |||
| 804 | p->b.hdr.bSlot = slot; | |||
| 805 | p->b.hdr.bSeq = seq; | |||
| 806 | p->b.bStatus = ccid_calc_status(s); | |||
| 807 | p->b.bError = s->bError; | |||
| 808 | if (p->b.bError) { | |||
| 809 | DPRINTF(s, D_VERBOSE, "error %d\n", p->b.bError)do { if (4 <= s->debug) { printf("usb-ccid: " "error %d\n" , p->b.bError); } } while (0); | |||
| 810 | } | |||
| 811 | memcpy(p->abData, data, len); | |||
| ||||
| 812 | ccid_reset_error_status(s); | |||
| 813 | } | |||
| 814 | ||||
| 815 | static void ccid_report_error_failed(USBCCIDState *s, uint8_t error) | |||
| 816 | { | |||
| 817 | s->bmCommandStatus = COMMAND_STATUS_FAILED; | |||
| 818 | s->bError = error; | |||
| 819 | } | |||
| 820 | ||||
| 821 | static void ccid_write_data_block_answer(USBCCIDState *s, | |||
| 822 | const uint8_t *data, uint32_t len) | |||
| 823 | { | |||
| 824 | uint8_t seq; | |||
| 825 | uint8_t slot; | |||
| 826 | ||||
| 827 | if (!ccid_has_pending_answers(s)) { | |||
| 828 | DPRINTF(s, D_WARN, "error: no pending answer to return to guest\n")do { if (1 <= s->debug) { printf("usb-ccid: " "error: no pending answer to return to guest\n" ); } } while (0); | |||
| 829 | ccid_report_error_failed(s, ERROR_ICC_MUTE); | |||
| 830 | return; | |||
| 831 | } | |||
| 832 | ccid_remove_pending_answer(s, &slot, &seq); | |||
| 833 | ccid_write_data_block(s, slot, seq, data, len); | |||
| 834 | } | |||
| 835 | ||||
| 836 | static uint8_t atr_get_protocol_num(const uint8_t *atr, uint32_t len) | |||
| 837 | { | |||
| 838 | int i; | |||
| 839 | ||||
| 840 | if (len < 2 || !(atr[1] & 0x80)) { | |||
| 841 | /* too short or TD1 not included */ | |||
| 842 | return 0; /* T=0, default */ | |||
| 843 | } | |||
| 844 | i = 1 + !!(atr[1] & 0x10) + !!(atr[1] & 0x20) + !!(atr[1] & 0x40); | |||
| 845 | i += !!(atr[1] & 0x80); | |||
| 846 | return atr[i] & 0x0f; | |||
| 847 | } | |||
| 848 | ||||
| 849 | static void ccid_write_data_block_atr(USBCCIDState *s, CCID_Header *recv) | |||
| 850 | { | |||
| 851 | const uint8_t *atr = NULL((void*)0); | |||
| 852 | uint32_t len = 0; | |||
| 853 | uint8_t atr_protocol_num; | |||
| 854 | CCID_T0ProtocolDataStructure *t0 = &s->abProtocolDataStructure.t0; | |||
| 855 | CCID_T1ProtocolDataStructure *t1 = &s->abProtocolDataStructure.t1; | |||
| 856 | ||||
| 857 | if (s->card) { | |||
| 858 | atr = ccid_card_get_atr(s->card, &len); | |||
| 859 | } | |||
| 860 | atr_protocol_num = atr_get_protocol_num(atr, len); | |||
| 861 | DPRINTF(s, D_VERBOSE, "%s: atr contains protocol=%d\n", __func__,do { if (4 <= s->debug) { printf("usb-ccid: " "%s: atr contains protocol=%d\n" , __func__, atr_protocol_num); } } while (0) | |||
| 862 | atr_protocol_num)do { if (4 <= s->debug) { printf("usb-ccid: " "%s: atr contains protocol=%d\n" , __func__, atr_protocol_num); } } while (0); | |||
| 863 | /* set parameters from ATR - see spec page 109 */ | |||
| 864 | s->bProtocolNum = (atr_protocol_num <= 1 ? atr_protocol_num | |||
| 865 | : s->bProtocolNum); | |||
| 866 | switch (atr_protocol_num) { | |||
| 867 | case 0: | |||
| 868 | /* TODO: unimplemented ATR T0 parameters */ | |||
| 869 | t0->bmFindexDindex = 0; | |||
| 870 | t0->bmTCCKST0 = 0; | |||
| 871 | t0->bGuardTimeT0 = 0; | |||
| 872 | t0->bWaitingIntegerT0 = 0; | |||
| 873 | t0->bClockStop = 0; | |||
| 874 | break; | |||
| 875 | case 1: | |||
| 876 | /* TODO: unimplemented ATR T1 parameters */ | |||
| 877 | t1->bmFindexDindex = 0; | |||
| 878 | t1->bmTCCKST1 = 0; | |||
| 879 | t1->bGuardTimeT1 = 0; | |||
| 880 | t1->bWaitingIntegerT1 = 0; | |||
| 881 | t1->bClockStop = 0; | |||
| 882 | t1->bIFSC = 0; | |||
| 883 | t1->bNadValue = 0; | |||
| 884 | break; | |||
| 885 | default: | |||
| 886 | DPRINTF(s, D_WARN, "%s: error: unsupported ATR protocol %d\n",do { if (1 <= s->debug) { printf("usb-ccid: " "%s: error: unsupported ATR protocol %d\n" , __func__, atr_protocol_num); } } while (0) | |||
| 887 | __func__, atr_protocol_num)do { if (1 <= s->debug) { printf("usb-ccid: " "%s: error: unsupported ATR protocol %d\n" , __func__, atr_protocol_num); } } while (0); | |||
| 888 | } | |||
| 889 | ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len); | |||
| 890 | } | |||
| 891 | ||||
| 892 | static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv) | |||
| 893 | { | |||
| 894 | CCID_SetParameters *ph = (CCID_SetParameters *) recv; | |||
| 895 | uint32_t protocol_num = ph->bProtocolNum & 3; | |||
| 896 | ||||
| 897 | if (protocol_num != 0 && protocol_num != 1) { | |||
| 898 | ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED); | |||
| 899 | return; | |||
| 900 | } | |||
| 901 | s->bProtocolNum = protocol_num; | |||
| 902 | s->abProtocolDataStructure = ph->abProtocolDataStructure; | |||
| 903 | } | |||
| 904 | ||||
| 905 | /* | |||
| 906 | * must be 5 bytes for T=0, 7 bytes for T=1 | |||
| 907 | * See page 52 | |||
| 908 | */ | |||
| 909 | static const CCID_ProtocolDataStructure defaultProtocolDataStructure = { | |||
| 910 | .t1 = { | |||
| 911 | .bmFindexDindex = 0x77, | |||
| 912 | .bmTCCKST1 = 0x00, | |||
| 913 | .bGuardTimeT1 = 0x00, | |||
| 914 | .bWaitingIntegerT1 = 0x00, | |||
| 915 | .bClockStop = 0x00, | |||
| 916 | .bIFSC = 0xfe, | |||
| 917 | .bNadValue = 0x00, | |||
| 918 | } | |||
| 919 | }; | |||
| 920 | ||||
| 921 | static void ccid_reset_parameters(USBCCIDState *s) | |||
| 922 | { | |||
| 923 | s->bProtocolNum = 0; /* T=0 */ | |||
| 924 | s->abProtocolDataStructure = defaultProtocolDataStructure; | |||
| 925 | } | |||
| 926 | ||||
| 927 | /* NOTE: only a single slot is supported (SLOT_0) */ | |||
| 928 | static void ccid_on_slot_change(USBCCIDState *s, bool_Bool full) | |||
| 929 | { | |||
| 930 | /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56 */ | |||
| 931 | uint8_t current = s->bmSlotICCState; | |||
| 932 | if (full) { | |||
| 933 | s->bmSlotICCState |= SLOT_0_STATE_MASK1; | |||
| 934 | } else { | |||
| 935 | s->bmSlotICCState &= ~SLOT_0_STATE_MASK1; | |||
| 936 | } | |||
| 937 | if (current != s->bmSlotICCState) { | |||
| 938 | s->bmSlotICCState |= SLOT_0_CHANGED_MASK2; | |||
| 939 | } | |||
| 940 | s->notify_slot_change = true1; | |||
| 941 | usb_wakeup(s->intr, 0); | |||
| 942 | } | |||
| 943 | ||||
| 944 | static void ccid_write_data_block_error( | |||
| 945 | USBCCIDState *s, uint8_t slot, uint8_t seq) | |||
| 946 | { | |||
| 947 | ccid_write_data_block(s, slot, seq, NULL((void*)0), 0); | |||
| 948 | } | |||
| 949 | ||||
| 950 | static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv) | |||
| 951 | { | |||
| 952 | uint32_t len; | |||
| 953 | ||||
| 954 | if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) { | |||
| 955 | DPRINTF(s, 1,do { if (1 <= s->debug) { printf("usb-ccid: " "usb-ccid: not sending apdu to client, no card connected\n" ); } } while (0) | |||
| 956 | "usb-ccid: not sending apdu to client, no card connected\n")do { if (1 <= s->debug) { printf("usb-ccid: " "usb-ccid: not sending apdu to client, no card connected\n" ); } } while (0); | |||
| 957 | ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq); | |||
| 958 | return; | |||
| 959 | } | |||
| 960 | len = le32_to_cpu(recv->hdr.dwLength); | |||
| 961 | DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__,do { if (1 <= s->debug) { printf("usb-ccid: " "%s: seq %d, len %d\n" , __func__, recv->hdr.bSeq, len); } } while (0) | |||
| 962 | recv->hdr.bSeq, len)do { if (1 <= s->debug) { printf("usb-ccid: " "%s: seq %d, len %d\n" , __func__, recv->hdr.bSeq, len); } } while (0); | |||
| 963 | ccid_add_pending_answer(s, (CCID_Header *)recv); | |||
| 964 | if (s->card) { | |||
| 965 | ccid_card_apdu_from_guest(s->card, recv->abData, len); | |||
| 966 | } else { | |||
| 967 | DPRINTF(s, D_WARN, "warning: discarded apdu\n")do { if (1 <= s->debug) { printf("usb-ccid: " "warning: discarded apdu\n" ); } } while (0); | |||
| 968 | } | |||
| 969 | } | |||
| 970 | ||||
| 971 | static const char *ccid_message_type_to_str(uint8_t type) | |||
| 972 | { | |||
| 973 | switch (type) { | |||
| 974 | case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn0x62: return "IccPowerOn"; | |||
| 975 | case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff0x63: return "IccPowerOff"; | |||
| 976 | case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus0x65: return "GetSlotStatus"; | |||
| 977 | case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock0x6f: return "XfrBlock"; | |||
| 978 | case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters0x6c: return "GetParameters"; | |||
| 979 | case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters0x6d: return "ResetParameters"; | |||
| 980 | case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters0x61: return "SetParameters"; | |||
| 981 | case CCID_MESSAGE_TYPE_PC_to_RDR_Escape0x6b: return "Escape"; | |||
| 982 | case CCID_MESSAGE_TYPE_PC_to_RDR_IccClock0x6e: return "IccClock"; | |||
| 983 | case CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU0x6a: return "T0APDU"; | |||
| 984 | case CCID_MESSAGE_TYPE_PC_to_RDR_Secure0x69: return "Secure"; | |||
| 985 | case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical0x71: return "Mechanical"; | |||
| 986 | case CCID_MESSAGE_TYPE_PC_to_RDR_Abort0x72: return "Abort"; | |||
| 987 | case CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency0x73: | |||
| 988 | return "SetDataRateAndClockFrequency"; | |||
| 989 | } | |||
| 990 | return "unknown"; | |||
| 991 | } | |||
| 992 | ||||
| 993 | static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) | |||
| 994 | { | |||
| 995 | CCID_Header *ccid_header; | |||
| 996 | ||||
| 997 | if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE65536) { | |||
| 998 | p->status = USB_RET_STALL(-3); | |||
| 999 | return; | |||
| 1000 | } | |||
| 1001 | ccid_header = (CCID_Header *)s->bulk_out_data; | |||
| 1002 | usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size); | |||
| 1003 | s->bulk_out_pos += p->iov.size; | |||
| 1004 | if (p->iov.size == CCID_MAX_PACKET_SIZE64) { | |||
| 1005 | DPRINTF(s, D_VERBOSE,do { if (4 <= s->debug) { printf("usb-ccid: " "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n" , p->iov.size, ccid_header->dwLength); } } while (0) | |||
| 1006 | "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",do { if (4 <= s->debug) { printf("usb-ccid: " "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n" , p->iov.size, ccid_header->dwLength); } } while (0) | |||
| 1007 | p->iov.size, ccid_header->dwLength)do { if (4 <= s->debug) { printf("usb-ccid: " "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n" , p->iov.size, ccid_header->dwLength); } } while (0); | |||
| 1008 | return; | |||
| 1009 | } | |||
| 1010 | if (s->bulk_out_pos < 10) { | |||
| 1011 | DPRINTF(s, 1,do { if (1 <= s->debug) { printf("usb-ccid: " "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n" , __func__); } } while (0) | |||
| 1012 | "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n",do { if (1 <= s->debug) { printf("usb-ccid: " "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n" , __func__); } } while (0) | |||
| 1013 | __func__)do { if (1 <= s->debug) { printf("usb-ccid: " "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n" , __func__); } } while (0); | |||
| 1014 | } else { | |||
| 1015 | DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__,do { if (3 <= s->debug) { printf("usb-ccid: " "%s %x %s\n" , __func__, ccid_header->bMessageType, ccid_message_type_to_str (ccid_header->bMessageType)); } } while (0) | |||
| 1016 | ccid_header->bMessageType,do { if (3 <= s->debug) { printf("usb-ccid: " "%s %x %s\n" , __func__, ccid_header->bMessageType, ccid_message_type_to_str (ccid_header->bMessageType)); } } while (0) | |||
| 1017 | ccid_message_type_to_str(ccid_header->bMessageType))do { if (3 <= s->debug) { printf("usb-ccid: " "%s %x %s\n" , __func__, ccid_header->bMessageType, ccid_message_type_to_str (ccid_header->bMessageType)); } } while (0); | |||
| 1018 | switch (ccid_header->bMessageType) { | |||
| 1019 | case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus0x65: | |||
| 1020 | ccid_write_slot_status(s, ccid_header); | |||
| 1021 | break; | |||
| 1022 | case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn0x62: | |||
| 1023 | DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__,do { if (1 <= s->debug) { printf("usb-ccid: " "%s: PowerOn: %d\n" , __func__, ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect ); } } while (0) | |||
| 1024 | ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect)do { if (1 <= s->debug) { printf("usb-ccid: " "%s: PowerOn: %d\n" , __func__, ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect ); } } while (0); | |||
| 1025 | s->powered = true1; | |||
| 1026 | if (!ccid_card_inserted(s)) { | |||
| 1027 | ccid_report_error_failed(s, ERROR_ICC_MUTE); | |||
| 1028 | } | |||
| 1029 | /* atr is written regardless of error. */ | |||
| 1030 | ccid_write_data_block_atr(s, ccid_header); | |||
| 1031 | break; | |||
| 1032 | case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff0x63: | |||
| 1033 | ccid_reset_error_status(s); | |||
| 1034 | s->powered = false0; | |||
| 1035 | ccid_write_slot_status(s, ccid_header); | |||
| 1036 | break; | |||
| 1037 | case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock0x6f: | |||
| 1038 | ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data); | |||
| 1039 | break; | |||
| 1040 | case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters0x61: | |||
| 1041 | ccid_reset_error_status(s); | |||
| 1042 | ccid_set_parameters(s, ccid_header); | |||
| 1043 | ccid_write_parameters(s, ccid_header); | |||
| 1044 | break; | |||
| 1045 | case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters0x6d: | |||
| 1046 | ccid_reset_error_status(s); | |||
| 1047 | ccid_reset_parameters(s); | |||
| 1048 | ccid_write_parameters(s, ccid_header); | |||
| 1049 | break; | |||
| 1050 | case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters0x6c: | |||
| 1051 | ccid_reset_error_status(s); | |||
| 1052 | ccid_write_parameters(s, ccid_header); | |||
| 1053 | break; | |||
| 1054 | case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical0x71: | |||
| 1055 | ccid_report_error_failed(s, 0); | |||
| 1056 | ccid_write_slot_status(s, ccid_header); | |||
| 1057 | break; | |||
| 1058 | default: | |||
| 1059 | DPRINTF(s, 1,do { if (1 <= s->debug) { printf("usb-ccid: " "handle_data: ERROR: unhandled message type %Xh\n" , ccid_header->bMessageType); } } while (0) | |||
| 1060 | "handle_data: ERROR: unhandled message type %Xh\n",do { if (1 <= s->debug) { printf("usb-ccid: " "handle_data: ERROR: unhandled message type %Xh\n" , ccid_header->bMessageType); } } while (0) | |||
| 1061 | ccid_header->bMessageType)do { if (1 <= s->debug) { printf("usb-ccid: " "handle_data: ERROR: unhandled message type %Xh\n" , ccid_header->bMessageType); } } while (0); | |||
| 1062 | /* | |||
| 1063 | * The caller is expecting the device to respond, tell it we | |||
| 1064 | * don't support the operation. | |||
| 1065 | */ | |||
| 1066 | ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED); | |||
| 1067 | ccid_write_slot_status(s, ccid_header); | |||
| 1068 | break; | |||
| 1069 | } | |||
| 1070 | } | |||
| 1071 | s->bulk_out_pos = 0; | |||
| 1072 | } | |||
| 1073 | ||||
| 1074 | static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) | |||
| 1075 | { | |||
| 1076 | int len = 0; | |||
| 1077 | ||||
| 1078 | ccid_bulk_in_get(s); | |||
| 1079 | if (s->current_bulk_in != NULL((void*)0)) { | |||
| 1080 | len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,(((s->current_bulk_in->len - s->current_bulk_in-> pos) < (p->iov.size)) ? (s->current_bulk_in->len - s->current_bulk_in->pos) : (p->iov.size)) | |||
| 1081 | p->iov.size)(((s->current_bulk_in->len - s->current_bulk_in-> pos) < (p->iov.size)) ? (s->current_bulk_in->len - s->current_bulk_in->pos) : (p->iov.size)); | |||
| 1082 | usb_packet_copy(p, s->current_bulk_in->data + | |||
| 1083 | s->current_bulk_in->pos, len); | |||
| 1084 | s->current_bulk_in->pos += len; | |||
| 1085 | if (s->current_bulk_in->pos == s->current_bulk_in->len) { | |||
| 1086 | ccid_bulk_in_release(s); | |||
| 1087 | } | |||
| 1088 | } else { | |||
| 1089 | /* return when device has no data - usb 2.0 spec Table 8-4 */ | |||
| 1090 | p->status = USB_RET_NAK(-2); | |||
| 1091 | } | |||
| 1092 | if (len) { | |||
| 1093 | DPRINTF(s, D_MORE_INFO,do { if (3 <= s->debug) { printf("usb-ccid: " "%s: %zd/%d req/act to guest (BULK_IN)\n" , __func__, p->iov.size, len); } } while (0) | |||
| 1094 | "%s: %zd/%d req/act to guest (BULK_IN)\n",do { if (3 <= s->debug) { printf("usb-ccid: " "%s: %zd/%d req/act to guest (BULK_IN)\n" , __func__, p->iov.size, len); } } while (0) | |||
| 1095 | __func__, p->iov.size, len)do { if (3 <= s->debug) { printf("usb-ccid: " "%s: %zd/%d req/act to guest (BULK_IN)\n" , __func__, p->iov.size, len); } } while (0); | |||
| 1096 | } | |||
| 1097 | if (len < p->iov.size) { | |||
| 1098 | DPRINTF(s, 1,do { if (1 <= s->debug) { printf("usb-ccid: " "%s: returning short (EREMOTEIO) %d < %zd\n" , __func__, len, p->iov.size); } } while (0) | |||
| 1099 | "%s: returning short (EREMOTEIO) %d < %zd\n",do { if (1 <= s->debug) { printf("usb-ccid: " "%s: returning short (EREMOTEIO) %d < %zd\n" , __func__, len, p->iov.size); } } while (0) | |||
| 1100 | __func__, len, p->iov.size)do { if (1 <= s->debug) { printf("usb-ccid: " "%s: returning short (EREMOTEIO) %d < %zd\n" , __func__, len, p->iov.size); } } while (0); | |||
| 1101 | } | |||
| 1102 | } | |||
| 1103 | ||||
| 1104 | static void ccid_handle_data(USBDevice *dev, USBPacket *p) | |||
| 1105 | { | |||
| 1106 | USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev)]; ({ const typeof((( USBCCIDState *) 0)->dev) *__mptr = (dev); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof(USBCCIDState, dev));}) ;})); | |||
| 1107 | uint8_t buf[2]; | |||
| 1108 | ||||
| 1109 | switch (p->pid) { | |||
| 1110 | case USB_TOKEN_OUT0xe1: | |||
| 1111 | ccid_handle_bulk_out(s, p); | |||
| 1112 | break; | |||
| 1113 | ||||
| 1114 | case USB_TOKEN_IN0x69: | |||
| 1115 | switch (p->ep->nr) { | |||
| 1116 | case CCID_BULK_IN_EP2: | |||
| 1117 | ccid_bulk_in_copy_to_guest(s, p); | |||
| 1118 | break; | |||
| 1119 | case CCID_INT_IN_EP1: | |||
| 1120 | if (s->notify_slot_change) { | |||
| 1121 | /* page 56, RDR_to_PC_NotifySlotChange */ | |||
| 1122 | buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange0x50; | |||
| 1123 | buf[1] = s->bmSlotICCState; | |||
| 1124 | usb_packet_copy(p, buf, 2); | |||
| 1125 | s->notify_slot_change = false0; | |||
| 1126 | s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK2; | |||
| 1127 | DPRINTF(s, D_INFO,do { if (2 <= s->debug) { printf("usb-ccid: " "handle_data: int_in: notify_slot_change %X, " "requested len %zd\n" , s->bmSlotICCState, p->iov.size ); } } while (0) | |||
| 1128 | "handle_data: int_in: notify_slot_change %X, "do { if (2 <= s->debug) { printf("usb-ccid: " "handle_data: int_in: notify_slot_change %X, " "requested len %zd\n" , s->bmSlotICCState, p->iov.size ); } } while (0) | |||
| 1129 | "requested len %zd\n",do { if (2 <= s->debug) { printf("usb-ccid: " "handle_data: int_in: notify_slot_change %X, " "requested len %zd\n" , s->bmSlotICCState, p->iov.size ); } } while (0) | |||
| 1130 | s->bmSlotICCState, p->iov.size)do { if (2 <= s->debug) { printf("usb-ccid: " "handle_data: int_in: notify_slot_change %X, " "requested len %zd\n" , s->bmSlotICCState, p->iov.size ); } } while (0); | |||
| 1131 | } else { | |||
| 1132 | p->status = USB_RET_NAK(-2); | |||
| 1133 | } | |||
| 1134 | break; | |||
| 1135 | default: | |||
| 1136 | DPRINTF(s, 1, "Bad endpoint\n")do { if (1 <= s->debug) { printf("usb-ccid: " "Bad endpoint\n" ); } } while (0); | |||
| 1137 | p->status = USB_RET_STALL(-3); | |||
| 1138 | break; | |||
| 1139 | } | |||
| 1140 | break; | |||
| 1141 | default: | |||
| 1142 | DPRINTF(s, 1, "Bad token\n")do { if (1 <= s->debug) { printf("usb-ccid: " "Bad token\n" ); } } while (0); | |||
| 1143 | p->status = USB_RET_STALL(-3); | |||
| 1144 | break; | |||
| 1145 | } | |||
| 1146 | } | |||
| 1147 | ||||
| 1148 | static void ccid_handle_destroy(USBDevice *dev) | |||
| 1149 | { | |||
| 1150 | USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev)]; ({ const typeof((( USBCCIDState *) 0)->dev) *__mptr = (dev); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof(USBCCIDState, dev));}) ;})); | |||
| 1151 | ||||
| 1152 | ccid_bulk_in_clear(s); | |||
| 1153 | } | |||
| 1154 | ||||
| 1155 | static void ccid_flush_pending_answers(USBCCIDState *s) | |||
| 1156 | { | |||
| 1157 | while (ccid_has_pending_answers(s)) { | |||
| 1158 | ccid_write_data_block_answer(s, NULL((void*)0), 0); | |||
| 1159 | } | |||
| 1160 | } | |||
| 1161 | ||||
| 1162 | static Answer *ccid_peek_next_answer(USBCCIDState *s) | |||
| 1163 | { | |||
| 1164 | return s->pending_answers_num == 0 | |||
| 1165 | ? NULL((void*)0) | |||
| 1166 | : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM128]; | |||
| 1167 | } | |||
| 1168 | ||||
| 1169 | static Property ccid_props[] = { | |||
| 1170 | DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0){ .name = ("slot"), .info = &(qdev_prop_uint32), .offset = __builtin_offsetof(struct CCIDCardState, slot) + ((uint32_t* )0 - (typeof(((struct CCIDCardState *)0)->slot)*)0), .qtype = QTYPE_QINT, .defval = (uint32_t)0, }, | |||
| 1171 | DEFINE_PROP_END_OF_LIST(){}, | |||
| 1172 | }; | |||
| 1173 | ||||
| 1174 | #define TYPE_CCID_BUS"ccid-bus" "ccid-bus" | |||
| 1175 | #define CCID_BUS(obj)((CCIDBus *)object_dynamic_cast_assert(((Object *)((obj))), ( "ccid-bus"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1175, __func__)) OBJECT_CHECK(CCIDBus, (obj), TYPE_CCID_BUS)((CCIDBus *)object_dynamic_cast_assert(((Object *)((obj))), ( "ccid-bus"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1175, __func__)) | |||
| 1176 | ||||
| 1177 | static const TypeInfo ccid_bus_info = { | |||
| 1178 | .name = TYPE_CCID_BUS"ccid-bus", | |||
| 1179 | .parent = TYPE_BUS"bus", | |||
| 1180 | .instance_size = sizeof(CCIDBus), | |||
| 1181 | }; | |||
| 1182 | ||||
| 1183 | void ccid_card_send_apdu_to_guest(CCIDCardState *card, | |||
| 1184 | uint8_t *apdu, uint32_t len) | |||
| 1185 | { | |||
| 1186 | USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev,( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})) | |||
| 1187 | card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1188 | Answer *answer; | |||
| 1189 | ||||
| 1190 | if (!ccid_has_pending_answers(s)) { | |||
| 1191 | DPRINTF(s, 1, "CCID ERROR: got an APDU without pending answers\n")do { if (1 <= s->debug) { printf("usb-ccid: " "CCID ERROR: got an APDU without pending answers\n" ); } } while (0); | |||
| 1192 | return; | |||
| 1193 | } | |||
| 1194 | s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; | |||
| 1195 | answer = ccid_peek_next_answer(s); | |||
| 1196 | if (answer == NULL((void*)0)) { | |||
| 1197 | DPRINTF(s, D_WARN, "%s: error: unexpected lack of answer\n", __func__)do { if (1 <= s->debug) { printf("usb-ccid: " "%s: error: unexpected lack of answer\n" , __func__); } } while (0); | |||
| 1198 | ccid_report_error_failed(s, ERROR_HW_ERROR); | |||
| 1199 | return; | |||
| 1200 | } | |||
| 1201 | DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n",do { if (1 <= s->debug) { printf("usb-ccid: " "APDU returned to guest %d (answer seq %d, slot %d)\n" , len, answer->seq, answer->slot); } } while (0) | |||
| 1202 | len, answer->seq, answer->slot)do { if (1 <= s->debug) { printf("usb-ccid: " "APDU returned to guest %d (answer seq %d, slot %d)\n" , len, answer->seq, answer->slot); } } while (0); | |||
| 1203 | ccid_write_data_block_answer(s, apdu, len); | |||
| 1204 | } | |||
| 1205 | ||||
| 1206 | void ccid_card_card_removed(CCIDCardState *card) | |||
| 1207 | { | |||
| 1208 | USBCCIDState *s = | |||
| 1209 | DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1210 | ||||
| 1211 | ccid_on_slot_change(s, false0); | |||
| 1212 | ccid_flush_pending_answers(s); | |||
| 1213 | ccid_reset(s); | |||
| 1214 | } | |||
| 1215 | ||||
| 1216 | int ccid_card_ccid_attach(CCIDCardState *card) | |||
| 1217 | { | |||
| 1218 | USBCCIDState *s = | |||
| 1219 | DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1220 | ||||
| 1221 | DPRINTF(s, 1, "CCID Attach\n")do { if (1 <= s->debug) { printf("usb-ccid: " "CCID Attach\n" ); } } while (0); | |||
| 1222 | if (s->migration_state == MIGRATION_MIGRATED) { | |||
| 1223 | s->migration_state = MIGRATION_NONE; | |||
| 1224 | } | |||
| 1225 | return 0; | |||
| 1226 | } | |||
| 1227 | ||||
| 1228 | void ccid_card_ccid_detach(CCIDCardState *card) | |||
| 1229 | { | |||
| 1230 | USBCCIDState *s = | |||
| 1231 | DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1232 | ||||
| 1233 | DPRINTF(s, 1, "CCID Detach\n")do { if (1 <= s->debug) { printf("usb-ccid: " "CCID Detach\n" ); } } while (0); | |||
| 1234 | if (ccid_card_inserted(s)) { | |||
| 1235 | ccid_on_slot_change(s, false0); | |||
| 1236 | } | |||
| 1237 | ccid_detach(s); | |||
| 1238 | } | |||
| 1239 | ||||
| 1240 | void ccid_card_card_error(CCIDCardState *card, uint64_t error) | |||
| 1241 | { | |||
| 1242 | USBCCIDState *s = | |||
| 1243 | DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1244 | ||||
| 1245 | s->bmCommandStatus = COMMAND_STATUS_FAILED; | |||
| 1246 | s->last_answer_error = error; | |||
| 1247 | DPRINTF(s, 1, "VSC_Error: %" PRIX64 "\n", s->last_answer_error)do { if (1 <= s->debug) { printf("usb-ccid: " "VSC_Error: %" "l" "X" "\n" , s->last_answer_error); } } while (0); | |||
| 1248 | /* TODO: these errors should be more verbose and propagated to the guest.*/ | |||
| 1249 | /* | |||
| 1250 | * We flush all pending answers on CardRemove message in ccid-card-passthru, | |||
| 1251 | * so check that first to not trigger abort | |||
| 1252 | */ | |||
| 1253 | if (ccid_has_pending_answers(s)) { | |||
| ||||
| 1254 | ccid_write_data_block_answer(s, NULL((void*)0), 0); | |||
| 1255 | } | |||
| 1256 | } | |||
| 1257 | ||||
| 1258 | void ccid_card_card_inserted(CCIDCardState *card) | |||
| 1259 | { | |||
| 1260 | USBCCIDState *s = | |||
| 1261 | DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1262 | ||||
| 1263 | s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; | |||
| 1264 | ccid_flush_pending_answers(s); | |||
| 1265 | ccid_on_slot_change(s, true1); | |||
| 1266 | } | |||
| 1267 | ||||
| 1268 | static int ccid_card_exit(DeviceState *qdev) | |||
| 1269 | { | |||
| 1270 | int ret = 0; | |||
| 1271 | CCIDCardState *card = CCID_CARD(qdev)((CCIDCardState *)object_dynamic_cast_assert(((Object *)((qdev ))), ("ccid-card"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1271, __func__)); | |||
| 1272 | USBCCIDState *s = | |||
| 1273 | DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1274 | ||||
| 1275 | if (ccid_card_inserted(s)) { | |||
| 1276 | ccid_card_card_removed(card); | |||
| 1277 | } | |||
| 1278 | ret = ccid_card_exitfn(card); | |||
| 1279 | s->card = NULL((void*)0); | |||
| 1280 | return ret; | |||
| 1281 | } | |||
| 1282 | ||||
| 1283 | static int ccid_card_init(DeviceState *qdev) | |||
| 1284 | { | |||
| 1285 | CCIDCardState *card = CCID_CARD(qdev)((CCIDCardState *)object_dynamic_cast_assert(((Object *)((qdev ))), ("ccid-card"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1285, __func__)); | |||
| 1286 | USBCCIDState *s = | |||
| 1287 | DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev.qdev)]; ({ const typeof (((USBCCIDState *) 0)->dev.qdev) *__mptr = (card->qdev. parent_bus->parent); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof (USBCCIDState, dev.qdev));});})); | |||
| 1288 | int ret = 0; | |||
| 1289 | ||||
| 1290 | if (card->slot != 0) { | |||
| 1291 | error_report("Warning: usb-ccid supports one slot, can't add %d", | |||
| 1292 | card->slot); | |||
| 1293 | return -1; | |||
| 1294 | } | |||
| 1295 | if (s->card != NULL((void*)0)) { | |||
| 1296 | error_report("Warning: usb-ccid card already full, not adding"); | |||
| 1297 | return -1; | |||
| 1298 | } | |||
| 1299 | ret = ccid_card_initfn(card); | |||
| 1300 | if (ret == 0) { | |||
| 1301 | s->card = card; | |||
| 1302 | } | |||
| 1303 | return ret; | |||
| 1304 | } | |||
| 1305 | ||||
| 1306 | static int ccid_initfn(USBDevice *dev) | |||
| 1307 | { | |||
| 1308 | USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev)( __extension__ ( { char __attribute__((unused)) offset_must_be_zero [ -__builtin_offsetof(USBCCIDState, dev)]; ({ const typeof((( USBCCIDState *) 0)->dev) *__mptr = (dev); (USBCCIDState *) ((char *) __mptr - __builtin_offsetof(USBCCIDState, dev));}) ;})); | |||
| 1309 | ||||
| 1310 | usb_desc_create_serial(dev); | |||
| 1311 | usb_desc_init(dev); | |||
| 1312 | qbus_create_inplace(&s->bus, sizeof(s->bus), TYPE_CCID_BUS"ccid-bus", DEVICE(dev)((DeviceState *)object_dynamic_cast_assert(((Object *)((dev)) ), ("device"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1312, __func__)), | |||
| 1313 | NULL((void*)0)); | |||
| 1314 | s->intr = usb_ep_get(dev, USB_TOKEN_IN0x69, CCID_INT_IN_EP1); | |||
| 1315 | s->bus.qbus.allow_hotplug = 1; | |||
| 1316 | s->card = NULL((void*)0); | |||
| 1317 | s->migration_state = MIGRATION_NONE; | |||
| 1318 | s->migration_target_ip = 0; | |||
| 1319 | s->migration_target_port = 0; | |||
| 1320 | s->dev.speed = USB_SPEED_FULL1; | |||
| 1321 | s->dev.speedmask = USB_SPEED_MASK_FULL(1 << 1); | |||
| 1322 | s->notify_slot_change = false0; | |||
| 1323 | s->powered = true1; | |||
| 1324 | s->pending_answers_num = 0; | |||
| 1325 | s->last_answer_error = 0; | |||
| 1326 | s->bulk_in_pending_start = 0; | |||
| 1327 | s->bulk_in_pending_end = 0; | |||
| 1328 | s->current_bulk_in = NULL((void*)0); | |||
| 1329 | ccid_reset_error_status(s); | |||
| 1330 | s->bulk_out_pos = 0; | |||
| 1331 | ccid_reset_parameters(s); | |||
| 1332 | ccid_reset(s); | |||
| 1333 | s->debug = parse_debug_env("QEMU_CCID_DEBUG", D_VERBOSE4, s->debug); | |||
| 1334 | return 0; | |||
| 1335 | } | |||
| 1336 | ||||
| 1337 | static int ccid_post_load(void *opaque, int version_id) | |||
| 1338 | { | |||
| 1339 | USBCCIDState *s = opaque; | |||
| 1340 | ||||
| 1341 | /* | |||
| 1342 | * This must be done after usb_device_attach, which sets state to ATTACHED, | |||
| 1343 | * while it must be DEFAULT in order to accept packets (like it is after | |||
| 1344 | * reset, but reset will reset our addr and call our reset handler which | |||
| 1345 | * may change state, and we don't want to do that when migrating). | |||
| 1346 | */ | |||
| 1347 | s->dev.state = s->state_vmstate; | |||
| 1348 | return 0; | |||
| 1349 | } | |||
| 1350 | ||||
| 1351 | static void ccid_pre_save(void *opaque) | |||
| 1352 | { | |||
| 1353 | USBCCIDState *s = opaque; | |||
| 1354 | ||||
| 1355 | s->state_vmstate = s->dev.state; | |||
| 1356 | if (s->dev.attached) { | |||
| 1357 | /* | |||
| 1358 | * Migrating an open device, ignore reconnection CHR_EVENT to avoid an | |||
| 1359 | * erroneous detach. | |||
| 1360 | */ | |||
| 1361 | s->migration_state = MIGRATION_MIGRATED; | |||
| 1362 | } | |||
| 1363 | } | |||
| 1364 | ||||
| 1365 | static VMStateDescription bulk_in_vmstate = { | |||
| 1366 | .name = "CCID BulkIn state", | |||
| 1367 | .version_id = 1, | |||
| 1368 | .minimum_version_id = 1, | |||
| 1369 | .fields = (VMStateField[]) { | |||
| 1370 | VMSTATE_BUFFER(data, BulkIn){ .name = ("data"), .version_id = (0), .field_exists = (((void *)0)), .size = (sizeof(typeof(((BulkIn *)0)->data)) - 0), . info = &vmstate_info_buffer, .flags = VMS_BUFFER, .offset = (__builtin_offsetof(BulkIn, data) + ((uint8_t(*)[sizeof(typeof (((BulkIn *)0)->data))])0 - (typeof(((BulkIn *)0)->data )*)0)) + 0, }, | |||
| 1371 | VMSTATE_UINT32(len, BulkIn){ .name = ("len"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(BulkIn, len) + ((uint32_t*)0 - (typeof(((BulkIn *)0)->len)*)0)), }, | |||
| 1372 | VMSTATE_UINT32(pos, BulkIn){ .name = ("pos"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(BulkIn, pos) + ((uint32_t*)0 - (typeof(((BulkIn *)0)->pos)*)0)), }, | |||
| 1373 | VMSTATE_END_OF_LIST(){} | |||
| 1374 | } | |||
| 1375 | }; | |||
| 1376 | ||||
| 1377 | static VMStateDescription answer_vmstate = { | |||
| 1378 | .name = "CCID Answer state", | |||
| 1379 | .version_id = 1, | |||
| 1380 | .minimum_version_id = 1, | |||
| 1381 | .fields = (VMStateField[]) { | |||
| 1382 | VMSTATE_UINT8(slot, Answer){ .name = ("slot"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(Answer, slot) + ((uint8_t*)0 - (typeof(((Answer *)0)->slot)*)0)), }, | |||
| 1383 | VMSTATE_UINT8(seq, Answer){ .name = ("seq"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(Answer, seq) + ((uint8_t*)0 - (typeof(((Answer *)0)->seq)*)0)), }, | |||
| 1384 | VMSTATE_END_OF_LIST(){} | |||
| 1385 | } | |||
| 1386 | }; | |||
| 1387 | ||||
| 1388 | static VMStateDescription usb_device_vmstate = { | |||
| 1389 | .name = "usb_device", | |||
| 1390 | .version_id = 1, | |||
| 1391 | .minimum_version_id = 1, | |||
| 1392 | .fields = (VMStateField[]) { | |||
| 1393 | VMSTATE_UINT8(addr, USBDevice){ .name = ("addr"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBDevice , addr) + ((uint8_t*)0 - (typeof(((USBDevice *)0)->addr)*) 0)), }, | |||
| 1394 | VMSTATE_BUFFER(setup_buf, USBDevice){ .name = ("setup_buf"), .version_id = (0), .field_exists = ( ((void*)0)), .size = (sizeof(typeof(((USBDevice *)0)->setup_buf )) - 0), .info = &vmstate_info_buffer, .flags = VMS_BUFFER , .offset = (__builtin_offsetof(USBDevice, setup_buf) + ((uint8_t (*)[sizeof(typeof(((USBDevice *)0)->setup_buf))])0 - (typeof (((USBDevice *)0)->setup_buf)*)0)) + 0, }, | |||
| 1395 | VMSTATE_BUFFER(data_buf, USBDevice){ .name = ("data_buf"), .version_id = (0), .field_exists = (( (void*)0)), .size = (sizeof(typeof(((USBDevice *)0)->data_buf )) - 0), .info = &vmstate_info_buffer, .flags = VMS_BUFFER , .offset = (__builtin_offsetof(USBDevice, data_buf) + ((uint8_t (*)[sizeof(typeof(((USBDevice *)0)->data_buf))])0 - (typeof (((USBDevice *)0)->data_buf)*)0)) + 0, }, | |||
| 1396 | VMSTATE_END_OF_LIST(){} | |||
| 1397 | } | |||
| 1398 | }; | |||
| 1399 | ||||
| 1400 | static VMStateDescription ccid_vmstate = { | |||
| 1401 | .name = "usb-ccid", | |||
| 1402 | .version_id = 1, | |||
| 1403 | .minimum_version_id = 1, | |||
| 1404 | .post_load = ccid_post_load, | |||
| 1405 | .pre_save = ccid_pre_save, | |||
| 1406 | .fields = (VMStateField[]) { | |||
| 1407 | VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice){ .name = ("dev"), .version_id = (1), .field_exists = (((void *)0)), .vmsd = &(usb_device_vmstate), .size = sizeof(USBDevice ), .flags = VMS_STRUCT, .offset = (__builtin_offsetof(USBCCIDState , dev) + ((USBDevice*)0 - (typeof(((USBCCIDState *)0)->dev )*)0)), }, | |||
| 1408 | VMSTATE_UINT8(debug, USBCCIDState){ .name = ("debug"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , debug) + ((uint8_t*)0 - (typeof(((USBCCIDState *)0)->debug )*)0)), }, | |||
| 1409 | VMSTATE_BUFFER(bulk_out_data, USBCCIDState){ .name = ("bulk_out_data"), .version_id = (0), .field_exists = (((void*)0)), .size = (sizeof(typeof(((USBCCIDState *)0)-> bulk_out_data)) - 0), .info = &vmstate_info_buffer, .flags = VMS_BUFFER, .offset = (__builtin_offsetof(USBCCIDState, bulk_out_data ) + ((uint8_t(*)[sizeof(typeof(((USBCCIDState *)0)->bulk_out_data ))])0 - (typeof(((USBCCIDState *)0)->bulk_out_data)*)0)) + 0, }, | |||
| 1410 | VMSTATE_UINT32(bulk_out_pos, USBCCIDState){ .name = ("bulk_out_pos"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , bulk_out_pos) + ((uint32_t*)0 - (typeof(((USBCCIDState *)0) ->bulk_out_pos)*)0)), }, | |||
| 1411 | VMSTATE_UINT8(bmSlotICCState, USBCCIDState){ .name = ("bmSlotICCState"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , bmSlotICCState) + ((uint8_t*)0 - (typeof(((USBCCIDState *)0 )->bmSlotICCState)*)0)), }, | |||
| 1412 | VMSTATE_UINT8(powered, USBCCIDState){ .name = ("powered"), .version_id = (0), .field_exists = ((( void*)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , powered) + ((uint8_t*)0 - (typeof(((USBCCIDState *)0)->powered )*)0)), }, | |||
| 1413 | VMSTATE_UINT8(notify_slot_change, USBCCIDState){ .name = ("notify_slot_change"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , notify_slot_change) + ((uint8_t*)0 - (typeof(((USBCCIDState *)0)->notify_slot_change)*)0)), }, | |||
| 1414 | VMSTATE_UINT64(last_answer_error, USBCCIDState){ .name = ("last_answer_error"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint64_t), .info = &(vmstate_info_uint64 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , last_answer_error) + ((uint64_t*)0 - (typeof(((USBCCIDState *)0)->last_answer_error)*)0)), }, | |||
| 1415 | VMSTATE_UINT8(bError, USBCCIDState){ .name = ("bError"), .version_id = (0), .field_exists = (((void *)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , bError) + ((uint8_t*)0 - (typeof(((USBCCIDState *)0)->bError )*)0)), }, | |||
| 1416 | VMSTATE_UINT8(bmCommandStatus, USBCCIDState){ .name = ("bmCommandStatus"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , bmCommandStatus) + ((uint8_t*)0 - (typeof(((USBCCIDState *) 0)->bmCommandStatus)*)0)), }, | |||
| 1417 | VMSTATE_UINT8(bProtocolNum, USBCCIDState){ .name = ("bProtocolNum"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , bProtocolNum) + ((uint8_t*)0 - (typeof(((USBCCIDState *)0)-> bProtocolNum)*)0)), }, | |||
| 1418 | VMSTATE_BUFFER(abProtocolDataStructure.data, USBCCIDState){ .name = ("abProtocolDataStructure.data"), .version_id = (0) , .field_exists = (((void*)0)), .size = (sizeof(typeof(((USBCCIDState *)0)->abProtocolDataStructure.data)) - 0), .info = &vmstate_info_buffer , .flags = VMS_BUFFER, .offset = (__builtin_offsetof(USBCCIDState , abProtocolDataStructure.data) + ((uint8_t(*)[sizeof(typeof( ((USBCCIDState *)0)->abProtocolDataStructure.data))])0 - ( typeof(((USBCCIDState *)0)->abProtocolDataStructure.data)* )0)) + 0, }, | |||
| 1419 | VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState){ .name = ("ulProtocolDataStructureSize"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState, ulProtocolDataStructureSize ) + ((uint32_t*)0 - (typeof(((USBCCIDState *)0)->ulProtocolDataStructureSize )*)0)), }, | |||
| 1420 | VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState,{ .name = ("bulk_in_pending"), .num = (8), .field_exists = (( (void*)0)), .version_id = (1), .vmsd = &(bulk_in_vmstate) , .size = sizeof(BulkIn), .flags = VMS_STRUCT|VMS_ARRAY, .offset = (__builtin_offsetof(USBCCIDState, bulk_in_pending) + ((BulkIn (*)[8])0 - (typeof(((USBCCIDState *)0)->bulk_in_pending)*) 0)),} | |||
| 1421 | BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn){ .name = ("bulk_in_pending"), .num = (8), .field_exists = (( (void*)0)), .version_id = (1), .vmsd = &(bulk_in_vmstate) , .size = sizeof(BulkIn), .flags = VMS_STRUCT|VMS_ARRAY, .offset = (__builtin_offsetof(USBCCIDState, bulk_in_pending) + ((BulkIn (*)[8])0 - (typeof(((USBCCIDState *)0)->bulk_in_pending)*) 0)),}, | |||
| 1422 | VMSTATE_UINT32(bulk_in_pending_start, USBCCIDState){ .name = ("bulk_in_pending_start"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , bulk_in_pending_start) + ((uint32_t*)0 - (typeof(((USBCCIDState *)0)->bulk_in_pending_start)*)0)), }, | |||
| 1423 | VMSTATE_UINT32(bulk_in_pending_end, USBCCIDState){ .name = ("bulk_in_pending_end"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , bulk_in_pending_end) + ((uint32_t*)0 - (typeof(((USBCCIDState *)0)->bulk_in_pending_end)*)0)), }, | |||
| 1424 | VMSTATE_STRUCT_ARRAY(pending_answers, USBCCIDState,{ .name = ("pending_answers"), .num = (128), .field_exists = ( ((void*)0)), .version_id = (1), .vmsd = &(answer_vmstate) , .size = sizeof(Answer), .flags = VMS_STRUCT|VMS_ARRAY, .offset = (__builtin_offsetof(USBCCIDState, pending_answers) + ((Answer (*)[128])0 - (typeof(((USBCCIDState *)0)->pending_answers) *)0)),} | |||
| 1425 | PENDING_ANSWERS_NUM, 1, answer_vmstate, Answer){ .name = ("pending_answers"), .num = (128), .field_exists = ( ((void*)0)), .version_id = (1), .vmsd = &(answer_vmstate) , .size = sizeof(Answer), .flags = VMS_STRUCT|VMS_ARRAY, .offset = (__builtin_offsetof(USBCCIDState, pending_answers) + ((Answer (*)[128])0 - (typeof(((USBCCIDState *)0)->pending_answers) *)0)),}, | |||
| 1426 | VMSTATE_UINT32(pending_answers_num, USBCCIDState){ .name = ("pending_answers_num"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , pending_answers_num) + ((uint32_t*)0 - (typeof(((USBCCIDState *)0)->pending_answers_num)*)0)), }, | |||
| 1427 | VMSTATE_UINT8(migration_state, USBCCIDState){ .name = ("migration_state"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint8_t), .info = &(vmstate_info_uint8 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , migration_state) + ((uint8_t*)0 - (typeof(((USBCCIDState *) 0)->migration_state)*)0)), }, | |||
| 1428 | VMSTATE_UINT32(state_vmstate, USBCCIDState){ .name = ("state_vmstate"), .version_id = (0), .field_exists = (((void*)0)), .size = sizeof(uint32_t), .info = &(vmstate_info_uint32 ), .flags = VMS_SINGLE, .offset = (__builtin_offsetof(USBCCIDState , state_vmstate) + ((uint32_t*)0 - (typeof(((USBCCIDState *)0 )->state_vmstate)*)0)), }, | |||
| 1429 | VMSTATE_END_OF_LIST(){} | |||
| 1430 | } | |||
| 1431 | }; | |||
| 1432 | ||||
| 1433 | static Property ccid_properties[] = { | |||
| 1434 | DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0){ .name = ("debug"), .info = &(qdev_prop_uint8), .offset = __builtin_offsetof(USBCCIDState, debug) + ((uint8_t*)0 - (typeof (((USBCCIDState *)0)->debug)*)0), .qtype = QTYPE_QINT, .defval = (uint8_t)0, }, | |||
| 1435 | DEFINE_PROP_END_OF_LIST(){}, | |||
| 1436 | }; | |||
| 1437 | ||||
| 1438 | static void ccid_class_initfn(ObjectClass *klass, void *data) | |||
| 1439 | { | |||
| 1440 | DeviceClass *dc = DEVICE_CLASS(klass)((DeviceClass *)object_class_dynamic_cast_assert(((ObjectClass *)((klass))), ("device"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1440, __func__)); | |||
| 1441 | USBDeviceClass *uc = USB_DEVICE_CLASS(klass)((USBDeviceClass *)object_class_dynamic_cast_assert(((ObjectClass *)((klass))), ("usb-device"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1441, __func__)); | |||
| 1442 | ||||
| 1443 | uc->init = ccid_initfn; | |||
| 1444 | uc->product_desc = "QEMU USB CCID"; | |||
| 1445 | uc->usb_desc = &desc_ccid; | |||
| 1446 | uc->handle_reset = ccid_handle_reset; | |||
| 1447 | uc->handle_control = ccid_handle_control; | |||
| 1448 | uc->handle_data = ccid_handle_data; | |||
| 1449 | uc->handle_destroy = ccid_handle_destroy; | |||
| 1450 | dc->desc = "CCID Rev 1.1 smartcard reader"; | |||
| 1451 | dc->vmsd = &ccid_vmstate; | |||
| 1452 | dc->props = ccid_properties; | |||
| 1453 | set_bit(DEVICE_CATEGORY_INPUT, dc->categories); | |||
| 1454 | } | |||
| 1455 | ||||
| 1456 | static const TypeInfo ccid_info = { | |||
| 1457 | .name = CCID_DEV_NAME"usb-ccid", | |||
| 1458 | .parent = TYPE_USB_DEVICE"usb-device", | |||
| 1459 | .instance_size = sizeof(USBCCIDState), | |||
| 1460 | .class_init = ccid_class_initfn, | |||
| 1461 | }; | |||
| 1462 | ||||
| 1463 | static void ccid_card_class_init(ObjectClass *klass, void *data) | |||
| 1464 | { | |||
| 1465 | DeviceClass *k = DEVICE_CLASS(klass)((DeviceClass *)object_class_dynamic_cast_assert(((ObjectClass *)((klass))), ("device"), "/home/stefan/src/qemu/qemu.org/qemu/hw/usb/dev-smartcard-reader.c" , 1465, __func__)); | |||
| 1466 | k->bus_type = TYPE_CCID_BUS"ccid-bus"; | |||
| 1467 | k->init = ccid_card_init; | |||
| 1468 | k->exit = ccid_card_exit; | |||
| 1469 | k->props = ccid_props; | |||
| 1470 | } | |||
| 1471 | ||||
| 1472 | static const TypeInfo ccid_card_type_info = { | |||
| 1473 | .name = TYPE_CCID_CARD"ccid-card", | |||
| 1474 | .parent = TYPE_DEVICE"device", | |||
| 1475 | .instance_size = sizeof(CCIDCardState), | |||
| 1476 | .abstract = true1, | |||
| 1477 | .class_size = sizeof(CCIDCardClass), | |||
| 1478 | .class_init = ccid_card_class_init, | |||
| 1479 | }; | |||
| 1480 | ||||
| 1481 | static void ccid_register_types(void) | |||
| 1482 | { | |||
| 1483 | type_register_static(&ccid_bus_info); | |||
| 1484 | type_register_static(&ccid_card_type_info); | |||
| 1485 | type_register_static(&ccid_info); | |||
| 1486 | usb_legacy_register(CCID_DEV_NAME"usb-ccid", "ccid", NULL((void*)0)); | |||
| 1487 | } | |||
| 1488 | ||||
| 1489 | type_init(ccid_register_types)static void __attribute__((constructor)) do_qemu_init_ccid_register_types (void) { register_module_init(ccid_register_types, MODULE_INIT_QOM ); } |