|
1 /* |
|
2 * FTDI FT232BM Device emulation |
|
3 * |
|
4 * Copyright (c) 2006 CodeSourcery. |
|
5 * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org> |
|
6 * Written by Paul Brook, reused for FTDI by Samuel Thibault |
|
7 * |
|
8 * This code is licenced under the LGPL. |
|
9 */ |
|
10 |
|
11 #include "qemu-common.h" |
|
12 #include "usb.h" |
|
13 #include "qemu-char.h" |
|
14 |
|
15 //#define DEBUG_Serial |
|
16 |
|
17 #ifdef DEBUG_Serial |
|
18 #define DPRINTF(fmt, args...) \ |
|
19 do { printf("usb-serial: " fmt , ##args); } while (0) |
|
20 #else |
|
21 #define DPRINTF(fmt, args...) do {} while(0) |
|
22 #endif |
|
23 |
|
24 #define RECV_BUF 384 |
|
25 |
|
26 /* Commands */ |
|
27 #define FTDI_RESET 0 |
|
28 #define FTDI_SET_MDM_CTRL 1 |
|
29 #define FTDI_SET_FLOW_CTRL 2 |
|
30 #define FTDI_SET_BAUD 3 |
|
31 #define FTDI_SET_DATA 4 |
|
32 #define FTDI_GET_MDM_ST 5 |
|
33 #define FTDI_SET_EVENT_CHR 6 |
|
34 #define FTDI_SET_ERROR_CHR 7 |
|
35 #define FTDI_SET_LATENCY 9 |
|
36 #define FTDI_GET_LATENCY 10 |
|
37 |
|
38 #define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) |
|
39 #define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) |
|
40 |
|
41 /* RESET */ |
|
42 |
|
43 #define FTDI_RESET_SIO 0 |
|
44 #define FTDI_RESET_RX 1 |
|
45 #define FTDI_RESET_TX 2 |
|
46 |
|
47 /* SET_MDM_CTRL */ |
|
48 |
|
49 #define FTDI_DTR 1 |
|
50 #define FTDI_SET_DTR (FTDI_DTR << 8) |
|
51 #define FTDI_RTS 2 |
|
52 #define FTDI_SET_RTS (FTDI_RTS << 8) |
|
53 |
|
54 /* SET_FLOW_CTRL */ |
|
55 |
|
56 #define FTDI_RTS_CTS_HS 1 |
|
57 #define FTDI_DTR_DSR_HS 2 |
|
58 #define FTDI_XON_XOFF_HS 4 |
|
59 |
|
60 /* SET_DATA */ |
|
61 |
|
62 #define FTDI_PARITY (0x7 << 8) |
|
63 #define FTDI_ODD (0x1 << 8) |
|
64 #define FTDI_EVEN (0x2 << 8) |
|
65 #define FTDI_MARK (0x3 << 8) |
|
66 #define FTDI_SPACE (0x4 << 8) |
|
67 |
|
68 #define FTDI_STOP (0x3 << 11) |
|
69 #define FTDI_STOP1 (0x0 << 11) |
|
70 #define FTDI_STOP15 (0x1 << 11) |
|
71 #define FTDI_STOP2 (0x2 << 11) |
|
72 |
|
73 /* GET_MDM_ST */ |
|
74 /* TODO: should be sent every 40ms */ |
|
75 #define FTDI_CTS (1<<4) // CTS line status |
|
76 #define FTDI_DSR (1<<5) // DSR line status |
|
77 #define FTDI_RI (1<<6) // RI line status |
|
78 #define FTDI_RLSD (1<<7) // Receive Line Signal Detect |
|
79 |
|
80 /* Status */ |
|
81 |
|
82 #define FTDI_DR (1<<0) // Data Ready |
|
83 #define FTDI_OE (1<<1) // Overrun Err |
|
84 #define FTDI_PE (1<<2) // Parity Err |
|
85 #define FTDI_FE (1<<3) // Framing Err |
|
86 #define FTDI_BI (1<<4) // Break Interrupt |
|
87 #define FTDI_THRE (1<<5) // Transmitter Holding Register |
|
88 #define FTDI_TEMT (1<<6) // Transmitter Empty |
|
89 #define FTDI_FIFO (1<<7) // Error in FIFO |
|
90 |
|
91 typedef struct { |
|
92 USBDevice dev; |
|
93 uint16_t vendorid; |
|
94 uint16_t productid; |
|
95 uint8_t recv_buf[RECV_BUF]; |
|
96 uint16_t recv_ptr; |
|
97 uint16_t recv_used; |
|
98 uint8_t event_chr; |
|
99 uint8_t error_chr; |
|
100 uint8_t event_trigger; |
|
101 QEMUSerialSetParams params; |
|
102 int latency; /* ms */ |
|
103 CharDriverState *cs; |
|
104 } USBSerialState; |
|
105 |
|
106 static const uint8_t qemu_serial_dev_descriptor[] = { |
|
107 0x12, /* u8 bLength; */ |
|
108 0x01, /* u8 bDescriptorType; Device */ |
|
109 0x00, 0x02, /* u16 bcdUSB; v2.0 */ |
|
110 |
|
111 0x00, /* u8 bDeviceClass; */ |
|
112 0x00, /* u8 bDeviceSubClass; */ |
|
113 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ |
|
114 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ |
|
115 |
|
116 /* Vendor and product id are arbitrary. */ |
|
117 0x03, 0x04, /* u16 idVendor; */ |
|
118 0x00, 0xFF, /* u16 idProduct; */ |
|
119 0x00, 0x04, /* u16 bcdDevice */ |
|
120 |
|
121 0x01, /* u8 iManufacturer; */ |
|
122 0x02, /* u8 iProduct; */ |
|
123 0x03, /* u8 iSerialNumber; */ |
|
124 0x01 /* u8 bNumConfigurations; */ |
|
125 }; |
|
126 |
|
127 static const uint8_t qemu_serial_config_descriptor[] = { |
|
128 |
|
129 /* one configuration */ |
|
130 0x09, /* u8 bLength; */ |
|
131 0x02, /* u8 bDescriptorType; Configuration */ |
|
132 0x20, 0x00, /* u16 wTotalLength; */ |
|
133 0x01, /* u8 bNumInterfaces; (1) */ |
|
134 0x01, /* u8 bConfigurationValue; */ |
|
135 0x00, /* u8 iConfiguration; */ |
|
136 0x80, /* u8 bmAttributes; |
|
137 Bit 7: must be set, |
|
138 6: Self-powered, |
|
139 5: Remote wakeup, |
|
140 4..0: resvd */ |
|
141 100/2, /* u8 MaxPower; */ |
|
142 |
|
143 /* one interface */ |
|
144 0x09, /* u8 if_bLength; */ |
|
145 0x04, /* u8 if_bDescriptorType; Interface */ |
|
146 0x00, /* u8 if_bInterfaceNumber; */ |
|
147 0x00, /* u8 if_bAlternateSetting; */ |
|
148 0x02, /* u8 if_bNumEndpoints; */ |
|
149 0xff, /* u8 if_bInterfaceClass; Vendor Specific */ |
|
150 0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */ |
|
151 0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */ |
|
152 0x02, /* u8 if_iInterface; */ |
|
153 |
|
154 /* Bulk-In endpoint */ |
|
155 0x07, /* u8 ep_bLength; */ |
|
156 0x05, /* u8 ep_bDescriptorType; Endpoint */ |
|
157 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ |
|
158 0x02, /* u8 ep_bmAttributes; Bulk */ |
|
159 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ |
|
160 0x00, /* u8 ep_bInterval; */ |
|
161 |
|
162 /* Bulk-Out endpoint */ |
|
163 0x07, /* u8 ep_bLength; */ |
|
164 0x05, /* u8 ep_bDescriptorType; Endpoint */ |
|
165 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ |
|
166 0x02, /* u8 ep_bmAttributes; Bulk */ |
|
167 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ |
|
168 0x00 /* u8 ep_bInterval; */ |
|
169 }; |
|
170 |
|
171 static void usb_serial_reset(USBSerialState *s) |
|
172 { |
|
173 /* TODO: Set flow control to none */ |
|
174 s->event_chr = 0x0d; |
|
175 s->event_trigger = 0; |
|
176 s->recv_ptr = 0; |
|
177 s->recv_used = 0; |
|
178 /* TODO: purge in char driver */ |
|
179 } |
|
180 |
|
181 static void usb_serial_handle_reset(USBDevice *dev) |
|
182 { |
|
183 USBSerialState *s = (USBSerialState *)dev; |
|
184 |
|
185 DPRINTF("Reset\n"); |
|
186 |
|
187 usb_serial_reset(s); |
|
188 /* TODO: Reset char device, send BREAK? */ |
|
189 } |
|
190 |
|
191 static uint8_t usb_get_modem_lines(USBSerialState *s) |
|
192 { |
|
193 int flags; |
|
194 uint8_t ret; |
|
195 |
|
196 if (qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) |
|
197 return FTDI_CTS|FTDI_DSR|FTDI_RLSD; |
|
198 |
|
199 ret = 0; |
|
200 if (flags & CHR_TIOCM_CTS) |
|
201 ret |= FTDI_CTS; |
|
202 if (flags & CHR_TIOCM_DSR) |
|
203 ret |= FTDI_DSR; |
|
204 if (flags & CHR_TIOCM_RI) |
|
205 ret |= FTDI_RI; |
|
206 if (flags & CHR_TIOCM_CAR) |
|
207 ret |= FTDI_RLSD; |
|
208 |
|
209 return ret; |
|
210 } |
|
211 |
|
212 static int usb_serial_handle_control(USBDevice *dev, int request, int value, |
|
213 int index, int length, uint8_t *data) |
|
214 { |
|
215 USBSerialState *s = (USBSerialState *)dev; |
|
216 int ret = 0; |
|
217 |
|
218 //DPRINTF("got control %x, value %x\n",request, value); |
|
219 switch (request) { |
|
220 case DeviceRequest | USB_REQ_GET_STATUS: |
|
221 data[0] = (0 << USB_DEVICE_SELF_POWERED) | |
|
222 (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); |
|
223 data[1] = 0x00; |
|
224 ret = 2; |
|
225 break; |
|
226 case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: |
|
227 if (value == USB_DEVICE_REMOTE_WAKEUP) { |
|
228 dev->remote_wakeup = 0; |
|
229 } else { |
|
230 goto fail; |
|
231 } |
|
232 ret = 0; |
|
233 break; |
|
234 case DeviceOutRequest | USB_REQ_SET_FEATURE: |
|
235 if (value == USB_DEVICE_REMOTE_WAKEUP) { |
|
236 dev->remote_wakeup = 1; |
|
237 } else { |
|
238 goto fail; |
|
239 } |
|
240 ret = 0; |
|
241 break; |
|
242 case DeviceOutRequest | USB_REQ_SET_ADDRESS: |
|
243 dev->addr = value; |
|
244 ret = 0; |
|
245 break; |
|
246 case DeviceRequest | USB_REQ_GET_DESCRIPTOR: |
|
247 switch(value >> 8) { |
|
248 case USB_DT_DEVICE: |
|
249 memcpy(data, qemu_serial_dev_descriptor, |
|
250 sizeof(qemu_serial_dev_descriptor)); |
|
251 data[8] = s->vendorid & 0xff; |
|
252 data[9] = ((s->vendorid) >> 8) & 0xff; |
|
253 data[10] = s->productid & 0xff; |
|
254 data[11] = ((s->productid) >> 8) & 0xff; |
|
255 ret = sizeof(qemu_serial_dev_descriptor); |
|
256 break; |
|
257 case USB_DT_CONFIG: |
|
258 memcpy(data, qemu_serial_config_descriptor, |
|
259 sizeof(qemu_serial_config_descriptor)); |
|
260 ret = sizeof(qemu_serial_config_descriptor); |
|
261 break; |
|
262 case USB_DT_STRING: |
|
263 switch(value & 0xff) { |
|
264 case 0: |
|
265 /* language ids */ |
|
266 data[0] = 4; |
|
267 data[1] = 3; |
|
268 data[2] = 0x09; |
|
269 data[3] = 0x04; |
|
270 ret = 4; |
|
271 break; |
|
272 case 1: |
|
273 /* vendor description */ |
|
274 ret = set_usb_string(data, "QEMU " QEMU_VERSION); |
|
275 break; |
|
276 case 2: |
|
277 /* product description */ |
|
278 ret = set_usb_string(data, "QEMU USB SERIAL"); |
|
279 break; |
|
280 case 3: |
|
281 /* serial number */ |
|
282 ret = set_usb_string(data, "1"); |
|
283 break; |
|
284 default: |
|
285 goto fail; |
|
286 } |
|
287 break; |
|
288 default: |
|
289 goto fail; |
|
290 } |
|
291 break; |
|
292 case DeviceRequest | USB_REQ_GET_CONFIGURATION: |
|
293 data[0] = 1; |
|
294 ret = 1; |
|
295 break; |
|
296 case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: |
|
297 ret = 0; |
|
298 break; |
|
299 case DeviceRequest | USB_REQ_GET_INTERFACE: |
|
300 data[0] = 0; |
|
301 ret = 1; |
|
302 break; |
|
303 case InterfaceOutRequest | USB_REQ_SET_INTERFACE: |
|
304 ret = 0; |
|
305 break; |
|
306 case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: |
|
307 ret = 0; |
|
308 break; |
|
309 |
|
310 /* Class specific requests. */ |
|
311 case DeviceOutVendor | FTDI_RESET: |
|
312 switch (value) { |
|
313 case FTDI_RESET_SIO: |
|
314 usb_serial_reset(s); |
|
315 break; |
|
316 case FTDI_RESET_RX: |
|
317 s->recv_ptr = 0; |
|
318 s->recv_used = 0; |
|
319 /* TODO: purge from char device */ |
|
320 break; |
|
321 case FTDI_RESET_TX: |
|
322 /* TODO: purge from char device */ |
|
323 break; |
|
324 } |
|
325 break; |
|
326 case DeviceOutVendor | FTDI_SET_MDM_CTRL: |
|
327 { |
|
328 static int flags; |
|
329 qemu_chr_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); |
|
330 if (value & FTDI_SET_RTS) { |
|
331 if (value & FTDI_RTS) |
|
332 flags |= CHR_TIOCM_RTS; |
|
333 else |
|
334 flags &= ~CHR_TIOCM_RTS; |
|
335 } |
|
336 if (value & FTDI_SET_DTR) { |
|
337 if (value & FTDI_DTR) |
|
338 flags |= CHR_TIOCM_DTR; |
|
339 else |
|
340 flags &= ~CHR_TIOCM_DTR; |
|
341 } |
|
342 qemu_chr_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); |
|
343 break; |
|
344 } |
|
345 case DeviceOutVendor | FTDI_SET_FLOW_CTRL: |
|
346 /* TODO: ioctl */ |
|
347 break; |
|
348 case DeviceOutVendor | FTDI_SET_BAUD: { |
|
349 static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; |
|
350 int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) |
|
351 | ((index & 1) << 2)]; |
|
352 int divisor = value & 0x3fff; |
|
353 |
|
354 /* chip special cases */ |
|
355 if (divisor == 1 && subdivisor8 == 0) |
|
356 subdivisor8 = 4; |
|
357 if (divisor == 0 && subdivisor8 == 0) |
|
358 divisor = 1; |
|
359 |
|
360 s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); |
|
361 qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); |
|
362 break; |
|
363 } |
|
364 case DeviceOutVendor | FTDI_SET_DATA: |
|
365 switch (value & FTDI_PARITY) { |
|
366 case 0: |
|
367 s->params.parity = 'N'; |
|
368 break; |
|
369 case FTDI_ODD: |
|
370 s->params.parity = 'O'; |
|
371 break; |
|
372 case FTDI_EVEN: |
|
373 s->params.parity = 'E'; |
|
374 break; |
|
375 default: |
|
376 DPRINTF("unsupported parity %d\n", value & FTDI_PARITY); |
|
377 goto fail; |
|
378 } |
|
379 switch (value & FTDI_STOP) { |
|
380 case FTDI_STOP1: |
|
381 s->params.stop_bits = 1; |
|
382 break; |
|
383 case FTDI_STOP2: |
|
384 s->params.stop_bits = 2; |
|
385 break; |
|
386 default: |
|
387 DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); |
|
388 goto fail; |
|
389 } |
|
390 qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); |
|
391 /* TODO: TX ON/OFF */ |
|
392 break; |
|
393 case DeviceInVendor | FTDI_GET_MDM_ST: |
|
394 data[0] = usb_get_modem_lines(s) | 1; |
|
395 data[1] = 0; |
|
396 ret = 2; |
|
397 break; |
|
398 case DeviceOutVendor | FTDI_SET_EVENT_CHR: |
|
399 /* TODO: handle it */ |
|
400 s->event_chr = value; |
|
401 break; |
|
402 case DeviceOutVendor | FTDI_SET_ERROR_CHR: |
|
403 /* TODO: handle it */ |
|
404 s->error_chr = value; |
|
405 break; |
|
406 case DeviceOutVendor | FTDI_SET_LATENCY: |
|
407 s->latency = value; |
|
408 break; |
|
409 case DeviceInVendor | FTDI_GET_LATENCY: |
|
410 data[0] = s->latency; |
|
411 ret = 1; |
|
412 break; |
|
413 default: |
|
414 fail: |
|
415 DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); |
|
416 ret = USB_RET_STALL; |
|
417 break; |
|
418 } |
|
419 return ret; |
|
420 } |
|
421 |
|
422 static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) |
|
423 { |
|
424 USBSerialState *s = (USBSerialState *)dev; |
|
425 int ret = 0; |
|
426 uint8_t devep = p->devep; |
|
427 uint8_t *data = p->data; |
|
428 int len = p->len; |
|
429 int first_len; |
|
430 |
|
431 switch (p->pid) { |
|
432 case USB_TOKEN_OUT: |
|
433 if (devep != 2) |
|
434 goto fail; |
|
435 qemu_chr_write(s->cs, data, len); |
|
436 break; |
|
437 |
|
438 case USB_TOKEN_IN: |
|
439 if (devep != 1) |
|
440 goto fail; |
|
441 first_len = RECV_BUF - s->recv_ptr; |
|
442 if (len <= 2) { |
|
443 ret = USB_RET_NAK; |
|
444 break; |
|
445 } |
|
446 *data++ = usb_get_modem_lines(s) | 1; |
|
447 /* We do not have the uart details */ |
|
448 *data++ = 0; |
|
449 len -= 2; |
|
450 if (len > s->recv_used) |
|
451 len = s->recv_used; |
|
452 if (!len) { |
|
453 ret = USB_RET_NAK; |
|
454 break; |
|
455 } |
|
456 if (first_len > len) |
|
457 first_len = len; |
|
458 memcpy(data, s->recv_buf + s->recv_ptr, first_len); |
|
459 if (len > first_len) |
|
460 memcpy(data + first_len, s->recv_buf, len - first_len); |
|
461 s->recv_used -= len; |
|
462 s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; |
|
463 ret = len + 2; |
|
464 break; |
|
465 |
|
466 default: |
|
467 DPRINTF("Bad token\n"); |
|
468 fail: |
|
469 ret = USB_RET_STALL; |
|
470 break; |
|
471 } |
|
472 |
|
473 return ret; |
|
474 } |
|
475 |
|
476 static void usb_serial_handle_destroy(USBDevice *dev) |
|
477 { |
|
478 USBSerialState *s = (USBSerialState *)dev; |
|
479 |
|
480 qemu_chr_close(s->cs); |
|
481 qemu_free(s); |
|
482 } |
|
483 |
|
484 static int usb_serial_can_read(void *opaque) |
|
485 { |
|
486 USBSerialState *s = opaque; |
|
487 return RECV_BUF - s->recv_used; |
|
488 } |
|
489 |
|
490 static void usb_serial_read(void *opaque, const uint8_t *buf, int size) |
|
491 { |
|
492 USBSerialState *s = opaque; |
|
493 int first_size = RECV_BUF - s->recv_ptr; |
|
494 if (first_size > size) |
|
495 first_size = size; |
|
496 memcpy(s->recv_buf + s->recv_ptr + s->recv_used, buf, first_size); |
|
497 if (size > first_size) |
|
498 memcpy(s->recv_buf, buf + first_size, size - first_size); |
|
499 s->recv_used += size; |
|
500 } |
|
501 |
|
502 static void usb_serial_event(void *opaque, int event) |
|
503 { |
|
504 USBSerialState *s = opaque; |
|
505 |
|
506 switch (event) { |
|
507 case CHR_EVENT_BREAK: |
|
508 /* TODO: Send Break to USB */ |
|
509 break; |
|
510 case CHR_EVENT_FOCUS: |
|
511 break; |
|
512 case CHR_EVENT_RESET: |
|
513 usb_serial_reset(s); |
|
514 /* TODO: Reset USB port */ |
|
515 break; |
|
516 } |
|
517 } |
|
518 |
|
519 USBDevice *usb_serial_init(const char *filename) |
|
520 { |
|
521 USBSerialState *s; |
|
522 CharDriverState *cdrv; |
|
523 unsigned short vendorid = 0x0403, productid = 0x6001; |
|
524 char label[32]; |
|
525 static int index; |
|
526 |
|
527 while (*filename && *filename != ':') { |
|
528 const char *p; |
|
529 char *e; |
|
530 if (strstart(filename, "vendorid=", &p)) { |
|
531 vendorid = strtol(p, &e, 16); |
|
532 if (e == p || (*e && *e != ',' && *e != ':')) { |
|
533 printf("bogus vendor ID %s\n", p); |
|
534 return NULL; |
|
535 } |
|
536 filename = e; |
|
537 } else if (strstart(filename, "productid=", &p)) { |
|
538 productid = strtol(p, &e, 16); |
|
539 if (e == p || (*e && *e != ',' && *e != ':')) { |
|
540 printf("bogus product ID %s\n", p); |
|
541 return NULL; |
|
542 } |
|
543 filename = e; |
|
544 } else { |
|
545 printf("unrecognized serial USB option %s\n", filename); |
|
546 return NULL; |
|
547 } |
|
548 while(*filename == ',') |
|
549 filename++; |
|
550 } |
|
551 if (!*filename) { |
|
552 printf("character device specification needed\n"); |
|
553 return NULL; |
|
554 } |
|
555 filename++; |
|
556 s = qemu_mallocz(sizeof(USBSerialState)); |
|
557 if (!s) |
|
558 return NULL; |
|
559 |
|
560 snprintf(label, sizeof(label), "usbserial%d", index++); |
|
561 cdrv = qemu_chr_open(label, filename); |
|
562 if (!cdrv) |
|
563 goto fail; |
|
564 s->cs = cdrv; |
|
565 qemu_chr_add_handlers(cdrv, usb_serial_can_read, usb_serial_read, usb_serial_event, s); |
|
566 |
|
567 s->dev.speed = USB_SPEED_FULL; |
|
568 s->dev.handle_packet = usb_generic_handle_packet; |
|
569 |
|
570 s->dev.handle_reset = usb_serial_handle_reset; |
|
571 s->dev.handle_control = usb_serial_handle_control; |
|
572 s->dev.handle_data = usb_serial_handle_data; |
|
573 s->dev.handle_destroy = usb_serial_handle_destroy; |
|
574 |
|
575 s->vendorid = vendorid; |
|
576 s->productid = productid; |
|
577 |
|
578 snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Serial(%.16s)", |
|
579 filename); |
|
580 |
|
581 usb_serial_handle_reset((USBDevice *)s); |
|
582 return (USBDevice *)s; |
|
583 fail: |
|
584 qemu_free(s); |
|
585 return NULL; |
|
586 } |