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 ); } |