root / hw / usb-serial.c @ 079d0b7f
History | View | Annotate | Download (17.1 kB)
1 | a7954218 | balrog | /*
|
---|---|---|---|
2 | a7954218 | balrog | * FTDI FT232BM Device emulation
|
3 | a7954218 | balrog | *
|
4 | a7954218 | balrog | * Copyright (c) 2006 CodeSourcery.
|
5 | a7954218 | balrog | * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
|
6 | a7954218 | balrog | * Written by Paul Brook, reused for FTDI by Samuel Thibault
|
7 | a7954218 | balrog | *
|
8 | 8e31bf38 | Matthew Fernandez | * This code is licensed under the LGPL.
|
9 | a7954218 | balrog | */
|
10 | a7954218 | balrog | |
11 | a7954218 | balrog | #include "qemu-common.h" |
12 | 2f792016 | Markus Armbruster | #include "qemu-error.h" |
13 | a7954218 | balrog | #include "usb.h" |
14 | f29783f7 | Gerd Hoffmann | #include "usb-desc.h" |
15 | a7954218 | balrog | #include "qemu-char.h" |
16 | a7954218 | balrog | |
17 | a7954218 | balrog | //#define DEBUG_Serial
|
18 | a7954218 | balrog | |
19 | a7954218 | balrog | #ifdef DEBUG_Serial
|
20 | 001faf32 | Blue Swirl | #define DPRINTF(fmt, ...) \
|
21 | 001faf32 | Blue Swirl | do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) |
22 | a7954218 | balrog | #else
|
23 | 001faf32 | Blue Swirl | #define DPRINTF(fmt, ...) do {} while(0) |
24 | a7954218 | balrog | #endif
|
25 | a7954218 | balrog | |
26 | a7954218 | balrog | #define RECV_BUF 384 |
27 | a7954218 | balrog | |
28 | a7954218 | balrog | /* Commands */
|
29 | a7954218 | balrog | #define FTDI_RESET 0 |
30 | a7954218 | balrog | #define FTDI_SET_MDM_CTRL 1 |
31 | a7954218 | balrog | #define FTDI_SET_FLOW_CTRL 2 |
32 | a7954218 | balrog | #define FTDI_SET_BAUD 3 |
33 | a7954218 | balrog | #define FTDI_SET_DATA 4 |
34 | a7954218 | balrog | #define FTDI_GET_MDM_ST 5 |
35 | a7954218 | balrog | #define FTDI_SET_EVENT_CHR 6 |
36 | a7954218 | balrog | #define FTDI_SET_ERROR_CHR 7 |
37 | a7954218 | balrog | #define FTDI_SET_LATENCY 9 |
38 | a7954218 | balrog | #define FTDI_GET_LATENCY 10 |
39 | a7954218 | balrog | |
40 | a7954218 | balrog | #define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) |
41 | a7954218 | balrog | #define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) |
42 | a7954218 | balrog | |
43 | a7954218 | balrog | /* RESET */
|
44 | a7954218 | balrog | |
45 | a7954218 | balrog | #define FTDI_RESET_SIO 0 |
46 | a7954218 | balrog | #define FTDI_RESET_RX 1 |
47 | a7954218 | balrog | #define FTDI_RESET_TX 2 |
48 | a7954218 | balrog | |
49 | a7954218 | balrog | /* SET_MDM_CTRL */
|
50 | a7954218 | balrog | |
51 | a7954218 | balrog | #define FTDI_DTR 1 |
52 | abb8a139 | aurel32 | #define FTDI_SET_DTR (FTDI_DTR << 8) |
53 | a7954218 | balrog | #define FTDI_RTS 2 |
54 | abb8a139 | aurel32 | #define FTDI_SET_RTS (FTDI_RTS << 8) |
55 | a7954218 | balrog | |
56 | a7954218 | balrog | /* SET_FLOW_CTRL */
|
57 | a7954218 | balrog | |
58 | a7954218 | balrog | #define FTDI_RTS_CTS_HS 1 |
59 | a7954218 | balrog | #define FTDI_DTR_DSR_HS 2 |
60 | a7954218 | balrog | #define FTDI_XON_XOFF_HS 4 |
61 | a7954218 | balrog | |
62 | a7954218 | balrog | /* SET_DATA */
|
63 | a7954218 | balrog | |
64 | a7954218 | balrog | #define FTDI_PARITY (0x7 << 8) |
65 | a7954218 | balrog | #define FTDI_ODD (0x1 << 8) |
66 | a7954218 | balrog | #define FTDI_EVEN (0x2 << 8) |
67 | a7954218 | balrog | #define FTDI_MARK (0x3 << 8) |
68 | a7954218 | balrog | #define FTDI_SPACE (0x4 << 8) |
69 | a7954218 | balrog | |
70 | a7954218 | balrog | #define FTDI_STOP (0x3 << 11) |
71 | a7954218 | balrog | #define FTDI_STOP1 (0x0 << 11) |
72 | a7954218 | balrog | #define FTDI_STOP15 (0x1 << 11) |
73 | a7954218 | balrog | #define FTDI_STOP2 (0x2 << 11) |
74 | a7954218 | balrog | |
75 | a7954218 | balrog | /* GET_MDM_ST */
|
76 | a7954218 | balrog | /* TODO: should be sent every 40ms */
|
77 | a7954218 | balrog | #define FTDI_CTS (1<<4) // CTS line status |
78 | a7954218 | balrog | #define FTDI_DSR (1<<5) // DSR line status |
79 | a7954218 | balrog | #define FTDI_RI (1<<6) // RI line status |
80 | a7954218 | balrog | #define FTDI_RLSD (1<<7) // Receive Line Signal Detect |
81 | a7954218 | balrog | |
82 | a7954218 | balrog | /* Status */
|
83 | a7954218 | balrog | |
84 | a7954218 | balrog | #define FTDI_DR (1<<0) // Data Ready |
85 | a7954218 | balrog | #define FTDI_OE (1<<1) // Overrun Err |
86 | a7954218 | balrog | #define FTDI_PE (1<<2) // Parity Err |
87 | a7954218 | balrog | #define FTDI_FE (1<<3) // Framing Err |
88 | a7954218 | balrog | #define FTDI_BI (1<<4) // Break Interrupt |
89 | a7954218 | balrog | #define FTDI_THRE (1<<5) // Transmitter Holding Register |
90 | a7954218 | balrog | #define FTDI_TEMT (1<<6) // Transmitter Empty |
91 | a7954218 | balrog | #define FTDI_FIFO (1<<7) // Error in FIFO |
92 | a7954218 | balrog | |
93 | a7954218 | balrog | typedef struct { |
94 | a7954218 | balrog | USBDevice dev; |
95 | a7954218 | balrog | uint8_t recv_buf[RECV_BUF]; |
96 | 8109b9b6 | aurel32 | uint16_t recv_ptr; |
97 | 8109b9b6 | aurel32 | uint16_t recv_used; |
98 | a7954218 | balrog | uint8_t event_chr; |
99 | a7954218 | balrog | uint8_t error_chr; |
100 | a7954218 | balrog | uint8_t event_trigger; |
101 | a7954218 | balrog | QEMUSerialSetParams params; |
102 | a7954218 | balrog | int latency; /* ms */ |
103 | a7954218 | balrog | CharDriverState *cs; |
104 | a7954218 | balrog | } USBSerialState; |
105 | a7954218 | balrog | |
106 | f29783f7 | Gerd Hoffmann | enum {
|
107 | f29783f7 | Gerd Hoffmann | STR_MANUFACTURER = 1,
|
108 | f29783f7 | Gerd Hoffmann | STR_PRODUCT_SERIAL, |
109 | f29783f7 | Gerd Hoffmann | STR_PRODUCT_BRAILLE, |
110 | f29783f7 | Gerd Hoffmann | STR_SERIALNUMBER, |
111 | a7954218 | balrog | }; |
112 | a7954218 | balrog | |
113 | f29783f7 | Gerd Hoffmann | static const USBDescStrings desc_strings = { |
114 | f29783f7 | Gerd Hoffmann | [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
115 | f29783f7 | Gerd Hoffmann | [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL",
|
116 | f29783f7 | Gerd Hoffmann | [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
|
117 | f29783f7 | Gerd Hoffmann | [STR_SERIALNUMBER] = "1",
|
118 | f29783f7 | Gerd Hoffmann | }; |
119 | f29783f7 | Gerd Hoffmann | |
120 | f29783f7 | Gerd Hoffmann | static const USBDescIface desc_iface0 = { |
121 | f29783f7 | Gerd Hoffmann | .bInterfaceNumber = 0,
|
122 | f29783f7 | Gerd Hoffmann | .bNumEndpoints = 2,
|
123 | f29783f7 | Gerd Hoffmann | .bInterfaceClass = 0xff,
|
124 | f29783f7 | Gerd Hoffmann | .bInterfaceSubClass = 0xff,
|
125 | f29783f7 | Gerd Hoffmann | .bInterfaceProtocol = 0xff,
|
126 | f29783f7 | Gerd Hoffmann | .eps = (USBDescEndpoint[]) { |
127 | f29783f7 | Gerd Hoffmann | { |
128 | f29783f7 | Gerd Hoffmann | .bEndpointAddress = USB_DIR_IN | 0x01,
|
129 | f29783f7 | Gerd Hoffmann | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
130 | f29783f7 | Gerd Hoffmann | .wMaxPacketSize = 64,
|
131 | f29783f7 | Gerd Hoffmann | },{ |
132 | f29783f7 | Gerd Hoffmann | .bEndpointAddress = USB_DIR_OUT | 0x02,
|
133 | f29783f7 | Gerd Hoffmann | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
134 | f29783f7 | Gerd Hoffmann | .wMaxPacketSize = 64,
|
135 | f29783f7 | Gerd Hoffmann | }, |
136 | f29783f7 | Gerd Hoffmann | } |
137 | f29783f7 | Gerd Hoffmann | }; |
138 | f29783f7 | Gerd Hoffmann | |
139 | f29783f7 | Gerd Hoffmann | static const USBDescDevice desc_device = { |
140 | f29783f7 | Gerd Hoffmann | .bcdUSB = 0x0200,
|
141 | f29783f7 | Gerd Hoffmann | .bMaxPacketSize0 = 8,
|
142 | f29783f7 | Gerd Hoffmann | .bNumConfigurations = 1,
|
143 | f29783f7 | Gerd Hoffmann | .confs = (USBDescConfig[]) { |
144 | f29783f7 | Gerd Hoffmann | { |
145 | f29783f7 | Gerd Hoffmann | .bNumInterfaces = 1,
|
146 | f29783f7 | Gerd Hoffmann | .bConfigurationValue = 1,
|
147 | f29783f7 | Gerd Hoffmann | .bmAttributes = 0x80,
|
148 | f29783f7 | Gerd Hoffmann | .bMaxPower = 50,
|
149 | add75088 | Brad Hards | .nif = 1,
|
150 | f29783f7 | Gerd Hoffmann | .ifs = &desc_iface0, |
151 | f29783f7 | Gerd Hoffmann | }, |
152 | f29783f7 | Gerd Hoffmann | }, |
153 | f29783f7 | Gerd Hoffmann | }; |
154 | f29783f7 | Gerd Hoffmann | |
155 | f29783f7 | Gerd Hoffmann | static const USBDesc desc_serial = { |
156 | f29783f7 | Gerd Hoffmann | .id = { |
157 | f29783f7 | Gerd Hoffmann | .idVendor = 0x0403,
|
158 | f29783f7 | Gerd Hoffmann | .idProduct = 0x6001,
|
159 | f29783f7 | Gerd Hoffmann | .bcdDevice = 0x0400,
|
160 | f29783f7 | Gerd Hoffmann | .iManufacturer = STR_MANUFACTURER, |
161 | f29783f7 | Gerd Hoffmann | .iProduct = STR_PRODUCT_SERIAL, |
162 | f29783f7 | Gerd Hoffmann | .iSerialNumber = STR_SERIALNUMBER, |
163 | f29783f7 | Gerd Hoffmann | }, |
164 | f29783f7 | Gerd Hoffmann | .full = &desc_device, |
165 | f29783f7 | Gerd Hoffmann | .str = desc_strings, |
166 | f29783f7 | Gerd Hoffmann | }; |
167 | f29783f7 | Gerd Hoffmann | |
168 | f29783f7 | Gerd Hoffmann | static const USBDesc desc_braille = { |
169 | f29783f7 | Gerd Hoffmann | .id = { |
170 | f29783f7 | Gerd Hoffmann | .idVendor = 0x0403,
|
171 | f29783f7 | Gerd Hoffmann | .idProduct = 0xfe72,
|
172 | f29783f7 | Gerd Hoffmann | .bcdDevice = 0x0400,
|
173 | f29783f7 | Gerd Hoffmann | .iManufacturer = STR_MANUFACTURER, |
174 | f29783f7 | Gerd Hoffmann | .iProduct = STR_PRODUCT_BRAILLE, |
175 | f29783f7 | Gerd Hoffmann | .iSerialNumber = STR_SERIALNUMBER, |
176 | f29783f7 | Gerd Hoffmann | }, |
177 | f29783f7 | Gerd Hoffmann | .full = &desc_device, |
178 | f29783f7 | Gerd Hoffmann | .str = desc_strings, |
179 | a7954218 | balrog | }; |
180 | a7954218 | balrog | |
181 | a7954218 | balrog | static void usb_serial_reset(USBSerialState *s) |
182 | a7954218 | balrog | { |
183 | a7954218 | balrog | /* TODO: Set flow control to none */
|
184 | a7954218 | balrog | s->event_chr = 0x0d;
|
185 | a7954218 | balrog | s->event_trigger = 0;
|
186 | a7954218 | balrog | s->recv_ptr = 0;
|
187 | a7954218 | balrog | s->recv_used = 0;
|
188 | a7954218 | balrog | /* TODO: purge in char driver */
|
189 | a7954218 | balrog | } |
190 | a7954218 | balrog | |
191 | a7954218 | balrog | static void usb_serial_handle_reset(USBDevice *dev) |
192 | a7954218 | balrog | { |
193 | a7954218 | balrog | USBSerialState *s = (USBSerialState *)dev; |
194 | a7954218 | balrog | |
195 | a7954218 | balrog | DPRINTF("Reset\n");
|
196 | a7954218 | balrog | |
197 | a7954218 | balrog | usb_serial_reset(s); |
198 | a7954218 | balrog | /* TODO: Reset char device, send BREAK? */
|
199 | a7954218 | balrog | } |
200 | a7954218 | balrog | |
201 | abb8a139 | aurel32 | static uint8_t usb_get_modem_lines(USBSerialState *s)
|
202 | abb8a139 | aurel32 | { |
203 | abb8a139 | aurel32 | int flags;
|
204 | abb8a139 | aurel32 | uint8_t ret; |
205 | abb8a139 | aurel32 | |
206 | 41084f1b | Anthony Liguori | if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP)
|
207 | abb8a139 | aurel32 | return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
|
208 | abb8a139 | aurel32 | |
209 | abb8a139 | aurel32 | ret = 0;
|
210 | abb8a139 | aurel32 | if (flags & CHR_TIOCM_CTS)
|
211 | abb8a139 | aurel32 | ret |= FTDI_CTS; |
212 | abb8a139 | aurel32 | if (flags & CHR_TIOCM_DSR)
|
213 | abb8a139 | aurel32 | ret |= FTDI_DSR; |
214 | abb8a139 | aurel32 | if (flags & CHR_TIOCM_RI)
|
215 | abb8a139 | aurel32 | ret |= FTDI_RI; |
216 | abb8a139 | aurel32 | if (flags & CHR_TIOCM_CAR)
|
217 | abb8a139 | aurel32 | ret |= FTDI_RLSD; |
218 | abb8a139 | aurel32 | |
219 | abb8a139 | aurel32 | return ret;
|
220 | abb8a139 | aurel32 | } |
221 | abb8a139 | aurel32 | |
222 | 007fd62f | Hans de Goede | static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, |
223 | 007fd62f | Hans de Goede | int request, int value, int index, int length, uint8_t *data) |
224 | a7954218 | balrog | { |
225 | a7954218 | balrog | USBSerialState *s = (USBSerialState *)dev; |
226 | f29783f7 | Gerd Hoffmann | int ret;
|
227 | f29783f7 | Gerd Hoffmann | |
228 | f29783f7 | Gerd Hoffmann | DPRINTF("got control %x, value %x\n",request, value);
|
229 | 007fd62f | Hans de Goede | ret = usb_desc_handle_control(dev, p, request, value, index, length, data); |
230 | f29783f7 | Gerd Hoffmann | if (ret >= 0) { |
231 | f29783f7 | Gerd Hoffmann | return ret;
|
232 | f29783f7 | Gerd Hoffmann | } |
233 | a7954218 | balrog | |
234 | f29783f7 | Gerd Hoffmann | ret = 0;
|
235 | a7954218 | balrog | switch (request) {
|
236 | a7954218 | balrog | case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
237 | a7954218 | balrog | ret = 0;
|
238 | a7954218 | balrog | break;
|
239 | a7954218 | balrog | |
240 | a7954218 | balrog | /* Class specific requests. */
|
241 | a7954218 | balrog | case DeviceOutVendor | FTDI_RESET:
|
242 | a7954218 | balrog | switch (value) {
|
243 | a7954218 | balrog | case FTDI_RESET_SIO:
|
244 | a7954218 | balrog | usb_serial_reset(s); |
245 | a7954218 | balrog | break;
|
246 | a7954218 | balrog | case FTDI_RESET_RX:
|
247 | a7954218 | balrog | s->recv_ptr = 0;
|
248 | a7954218 | balrog | s->recv_used = 0;
|
249 | a7954218 | balrog | /* TODO: purge from char device */
|
250 | a7954218 | balrog | break;
|
251 | a7954218 | balrog | case FTDI_RESET_TX:
|
252 | a7954218 | balrog | /* TODO: purge from char device */
|
253 | a7954218 | balrog | break;
|
254 | a7954218 | balrog | } |
255 | a7954218 | balrog | break;
|
256 | a7954218 | balrog | case DeviceOutVendor | FTDI_SET_MDM_CTRL:
|
257 | abb8a139 | aurel32 | { |
258 | abb8a139 | aurel32 | static int flags; |
259 | 41084f1b | Anthony Liguori | qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); |
260 | abb8a139 | aurel32 | if (value & FTDI_SET_RTS) {
|
261 | abb8a139 | aurel32 | if (value & FTDI_RTS)
|
262 | abb8a139 | aurel32 | flags |= CHR_TIOCM_RTS; |
263 | abb8a139 | aurel32 | else
|
264 | abb8a139 | aurel32 | flags &= ~CHR_TIOCM_RTS; |
265 | abb8a139 | aurel32 | } |
266 | abb8a139 | aurel32 | if (value & FTDI_SET_DTR) {
|
267 | abb8a139 | aurel32 | if (value & FTDI_DTR)
|
268 | abb8a139 | aurel32 | flags |= CHR_TIOCM_DTR; |
269 | abb8a139 | aurel32 | else
|
270 | abb8a139 | aurel32 | flags &= ~CHR_TIOCM_DTR; |
271 | abb8a139 | aurel32 | } |
272 | 41084f1b | Anthony Liguori | qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); |
273 | a7954218 | balrog | break;
|
274 | abb8a139 | aurel32 | } |
275 | a7954218 | balrog | case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
|
276 | a7954218 | balrog | /* TODO: ioctl */
|
277 | a7954218 | balrog | break;
|
278 | a7954218 | balrog | case DeviceOutVendor | FTDI_SET_BAUD: {
|
279 | a7954218 | balrog | static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; |
280 | a7954218 | balrog | int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) |
281 | a7954218 | balrog | | ((index & 1) << 2)]; |
282 | a7954218 | balrog | int divisor = value & 0x3fff; |
283 | a7954218 | balrog | |
284 | a7954218 | balrog | /* chip special cases */
|
285 | a7954218 | balrog | if (divisor == 1 && subdivisor8 == 0) |
286 | a7954218 | balrog | subdivisor8 = 4;
|
287 | a7954218 | balrog | if (divisor == 0 && subdivisor8 == 0) |
288 | a7954218 | balrog | divisor = 1;
|
289 | a7954218 | balrog | |
290 | a7954218 | balrog | s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); |
291 | 41084f1b | Anthony Liguori | qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); |
292 | a7954218 | balrog | break;
|
293 | a7954218 | balrog | } |
294 | a7954218 | balrog | case DeviceOutVendor | FTDI_SET_DATA:
|
295 | a7954218 | balrog | switch (value & FTDI_PARITY) {
|
296 | a7954218 | balrog | case 0: |
297 | a7954218 | balrog | s->params.parity = 'N';
|
298 | a7954218 | balrog | break;
|
299 | a7954218 | balrog | case FTDI_ODD:
|
300 | a7954218 | balrog | s->params.parity = 'O';
|
301 | a7954218 | balrog | break;
|
302 | a7954218 | balrog | case FTDI_EVEN:
|
303 | a7954218 | balrog | s->params.parity = 'E';
|
304 | a7954218 | balrog | break;
|
305 | a7954218 | balrog | default:
|
306 | a7954218 | balrog | DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
|
307 | a7954218 | balrog | goto fail;
|
308 | a7954218 | balrog | } |
309 | a7954218 | balrog | switch (value & FTDI_STOP) {
|
310 | a7954218 | balrog | case FTDI_STOP1:
|
311 | a7954218 | balrog | s->params.stop_bits = 1;
|
312 | a7954218 | balrog | break;
|
313 | a7954218 | balrog | case FTDI_STOP2:
|
314 | a7954218 | balrog | s->params.stop_bits = 2;
|
315 | a7954218 | balrog | break;
|
316 | a7954218 | balrog | default:
|
317 | a7954218 | balrog | DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
|
318 | a7954218 | balrog | goto fail;
|
319 | a7954218 | balrog | } |
320 | 41084f1b | Anthony Liguori | qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); |
321 | a7954218 | balrog | /* TODO: TX ON/OFF */
|
322 | a7954218 | balrog | break;
|
323 | a7954218 | balrog | case DeviceInVendor | FTDI_GET_MDM_ST:
|
324 | abb8a139 | aurel32 | data[0] = usb_get_modem_lines(s) | 1; |
325 | abb8a139 | aurel32 | data[1] = 0; |
326 | abb8a139 | aurel32 | ret = 2;
|
327 | a7954218 | balrog | break;
|
328 | a7954218 | balrog | case DeviceOutVendor | FTDI_SET_EVENT_CHR:
|
329 | a7954218 | balrog | /* TODO: handle it */
|
330 | a7954218 | balrog | s->event_chr = value; |
331 | a7954218 | balrog | break;
|
332 | a7954218 | balrog | case DeviceOutVendor | FTDI_SET_ERROR_CHR:
|
333 | a7954218 | balrog | /* TODO: handle it */
|
334 | a7954218 | balrog | s->error_chr = value; |
335 | a7954218 | balrog | break;
|
336 | a7954218 | balrog | case DeviceOutVendor | FTDI_SET_LATENCY:
|
337 | a7954218 | balrog | s->latency = value; |
338 | a7954218 | balrog | break;
|
339 | a7954218 | balrog | case DeviceInVendor | FTDI_GET_LATENCY:
|
340 | a7954218 | balrog | data[0] = s->latency;
|
341 | a7954218 | balrog | ret = 1;
|
342 | a7954218 | balrog | break;
|
343 | a7954218 | balrog | default:
|
344 | a7954218 | balrog | fail:
|
345 | a7954218 | balrog | DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
|
346 | a7954218 | balrog | ret = USB_RET_STALL; |
347 | a7954218 | balrog | break;
|
348 | a7954218 | balrog | } |
349 | a7954218 | balrog | return ret;
|
350 | a7954218 | balrog | } |
351 | a7954218 | balrog | |
352 | a7954218 | balrog | static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) |
353 | a7954218 | balrog | { |
354 | a7954218 | balrog | USBSerialState *s = (USBSerialState *)dev; |
355 | 9440b7e5 | Gerd Hoffmann | int i, ret = 0; |
356 | 079d0b7f | Gerd Hoffmann | uint8_t devep = p->ep->nr; |
357 | 9440b7e5 | Gerd Hoffmann | struct iovec *iov;
|
358 | 9440b7e5 | Gerd Hoffmann | uint8_t header[2];
|
359 | 9440b7e5 | Gerd Hoffmann | int first_len, len;
|
360 | a7954218 | balrog | |
361 | a7954218 | balrog | switch (p->pid) {
|
362 | a7954218 | balrog | case USB_TOKEN_OUT:
|
363 | a7954218 | balrog | if (devep != 2) |
364 | a7954218 | balrog | goto fail;
|
365 | 9440b7e5 | Gerd Hoffmann | for (i = 0; i < p->iov.niov; i++) { |
366 | 9440b7e5 | Gerd Hoffmann | iov = p->iov.iov + i; |
367 | 2cc6e0a1 | Anthony Liguori | qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len); |
368 | 9440b7e5 | Gerd Hoffmann | } |
369 | a7954218 | balrog | break;
|
370 | a7954218 | balrog | |
371 | a7954218 | balrog | case USB_TOKEN_IN:
|
372 | a7954218 | balrog | if (devep != 1) |
373 | a7954218 | balrog | goto fail;
|
374 | a7954218 | balrog | first_len = RECV_BUF - s->recv_ptr; |
375 | 9440b7e5 | Gerd Hoffmann | len = p->iov.size; |
376 | a7954218 | balrog | if (len <= 2) { |
377 | a7954218 | balrog | ret = USB_RET_NAK; |
378 | a7954218 | balrog | break;
|
379 | a7954218 | balrog | } |
380 | 9440b7e5 | Gerd Hoffmann | header[0] = usb_get_modem_lines(s) | 1; |
381 | abb8a139 | aurel32 | /* We do not have the uart details */
|
382 | 7e57f049 | Jason Wessel | /* handle serial break */
|
383 | 7e57f049 | Jason Wessel | if (s->event_trigger && s->event_trigger & FTDI_BI) {
|
384 | 7e57f049 | Jason Wessel | s->event_trigger &= ~FTDI_BI; |
385 | 9440b7e5 | Gerd Hoffmann | header[1] = FTDI_BI;
|
386 | 9440b7e5 | Gerd Hoffmann | usb_packet_copy(p, header, 2);
|
387 | 7e57f049 | Jason Wessel | ret = 2;
|
388 | 7e57f049 | Jason Wessel | break;
|
389 | 7e57f049 | Jason Wessel | } else {
|
390 | 9440b7e5 | Gerd Hoffmann | header[1] = 0; |
391 | 7e57f049 | Jason Wessel | } |
392 | a7954218 | balrog | len -= 2;
|
393 | a7954218 | balrog | if (len > s->recv_used)
|
394 | a7954218 | balrog | len = s->recv_used; |
395 | a7954218 | balrog | if (!len) {
|
396 | a7954218 | balrog | ret = USB_RET_NAK; |
397 | a7954218 | balrog | break;
|
398 | a7954218 | balrog | } |
399 | a7954218 | balrog | if (first_len > len)
|
400 | a7954218 | balrog | first_len = len; |
401 | 9440b7e5 | Gerd Hoffmann | usb_packet_copy(p, header, 2);
|
402 | 9440b7e5 | Gerd Hoffmann | usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); |
403 | a7954218 | balrog | if (len > first_len)
|
404 | 9440b7e5 | Gerd Hoffmann | usb_packet_copy(p, s->recv_buf, len - first_len); |
405 | a7954218 | balrog | s->recv_used -= len; |
406 | a7954218 | balrog | s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; |
407 | a7954218 | balrog | ret = len + 2;
|
408 | a7954218 | balrog | break;
|
409 | a7954218 | balrog | |
410 | a7954218 | balrog | default:
|
411 | a7954218 | balrog | DPRINTF("Bad token\n");
|
412 | a7954218 | balrog | fail:
|
413 | a7954218 | balrog | ret = USB_RET_STALL; |
414 | a7954218 | balrog | break;
|
415 | a7954218 | balrog | } |
416 | a7954218 | balrog | |
417 | a7954218 | balrog | return ret;
|
418 | a7954218 | balrog | } |
419 | a7954218 | balrog | |
420 | a7954218 | balrog | static void usb_serial_handle_destroy(USBDevice *dev) |
421 | a7954218 | balrog | { |
422 | a7954218 | balrog | USBSerialState *s = (USBSerialState *)dev; |
423 | a7954218 | balrog | |
424 | 70f24fb6 | Anthony Liguori | qemu_chr_delete(s->cs); |
425 | a7954218 | balrog | } |
426 | a7954218 | balrog | |
427 | 8fcd3692 | blueswir1 | static int usb_serial_can_read(void *opaque) |
428 | a7954218 | balrog | { |
429 | a7954218 | balrog | USBSerialState *s = opaque; |
430 | a7954218 | balrog | return RECV_BUF - s->recv_used;
|
431 | a7954218 | balrog | } |
432 | a7954218 | balrog | |
433 | 8fcd3692 | blueswir1 | static void usb_serial_read(void *opaque, const uint8_t *buf, int size) |
434 | a7954218 | balrog | { |
435 | a7954218 | balrog | USBSerialState *s = opaque; |
436 | 4ab4183d | David S. Ahern | int first_size, start;
|
437 | 4ab4183d | David S. Ahern | |
438 | 4ab4183d | David S. Ahern | /* room in the buffer? */
|
439 | 4ab4183d | David S. Ahern | if (size > (RECV_BUF - s->recv_used))
|
440 | 4ab4183d | David S. Ahern | size = RECV_BUF - s->recv_used; |
441 | 4ab4183d | David S. Ahern | |
442 | 4ab4183d | David S. Ahern | start = s->recv_ptr + s->recv_used; |
443 | 4ab4183d | David S. Ahern | if (start < RECV_BUF) {
|
444 | 4ab4183d | David S. Ahern | /* copy data to end of buffer */
|
445 | 4ab4183d | David S. Ahern | first_size = RECV_BUF - start; |
446 | 4ab4183d | David S. Ahern | if (first_size > size)
|
447 | 4ab4183d | David S. Ahern | first_size = size; |
448 | 4ab4183d | David S. Ahern | |
449 | 4ab4183d | David S. Ahern | memcpy(s->recv_buf + start, buf, first_size); |
450 | 4ab4183d | David S. Ahern | |
451 | 4ab4183d | David S. Ahern | /* wrap around to front if needed */
|
452 | 4ab4183d | David S. Ahern | if (size > first_size)
|
453 | 4ab4183d | David S. Ahern | memcpy(s->recv_buf, buf + first_size, size - first_size); |
454 | 4ab4183d | David S. Ahern | } else {
|
455 | 4ab4183d | David S. Ahern | start -= RECV_BUF; |
456 | 4ab4183d | David S. Ahern | memcpy(s->recv_buf + start, buf, size); |
457 | 4ab4183d | David S. Ahern | } |
458 | a7954218 | balrog | s->recv_used += size; |
459 | a7954218 | balrog | } |
460 | a7954218 | balrog | |
461 | 8fcd3692 | blueswir1 | static void usb_serial_event(void *opaque, int event) |
462 | a7954218 | balrog | { |
463 | a7954218 | balrog | USBSerialState *s = opaque; |
464 | a7954218 | balrog | |
465 | a7954218 | balrog | switch (event) {
|
466 | a7954218 | balrog | case CHR_EVENT_BREAK:
|
467 | 7e57f049 | Jason Wessel | s->event_trigger |= FTDI_BI; |
468 | a7954218 | balrog | break;
|
469 | a7954218 | balrog | case CHR_EVENT_FOCUS:
|
470 | a7954218 | balrog | break;
|
471 | b6b8df56 | Amit Shah | case CHR_EVENT_OPENED:
|
472 | a7954218 | balrog | usb_serial_reset(s); |
473 | a7954218 | balrog | /* TODO: Reset USB port */
|
474 | a7954218 | balrog | break;
|
475 | a7954218 | balrog | } |
476 | a7954218 | balrog | } |
477 | a7954218 | balrog | |
478 | 806b6024 | Gerd Hoffmann | static int usb_serial_initfn(USBDevice *dev) |
479 | 806b6024 | Gerd Hoffmann | { |
480 | 806b6024 | Gerd Hoffmann | USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev); |
481 | a980a065 | Gerd Hoffmann | |
482 | a980a065 | Gerd Hoffmann | usb_desc_init(dev); |
483 | 2b0efdc3 | Gerd Hoffmann | |
484 | 81bf96d3 | Markus Armbruster | if (!s->cs) {
|
485 | 81bf96d3 | Markus Armbruster | error_report("Property chardev is required");
|
486 | 81bf96d3 | Markus Armbruster | return -1; |
487 | 81bf96d3 | Markus Armbruster | } |
488 | 81bf96d3 | Markus Armbruster | |
489 | 2b0efdc3 | Gerd Hoffmann | qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, |
490 | 2b0efdc3 | Gerd Hoffmann | usb_serial_event, s); |
491 | 2b0efdc3 | Gerd Hoffmann | usb_serial_handle_reset(dev); |
492 | 806b6024 | Gerd Hoffmann | return 0; |
493 | 806b6024 | Gerd Hoffmann | } |
494 | 806b6024 | Gerd Hoffmann | |
495 | 2b0efdc3 | Gerd Hoffmann | static USBDevice *usb_serial_init(const char *filename) |
496 | a7954218 | balrog | { |
497 | 806b6024 | Gerd Hoffmann | USBDevice *dev; |
498 | a7954218 | balrog | CharDriverState *cdrv; |
499 | 2b0efdc3 | Gerd Hoffmann | uint32_t vendorid = 0, productid = 0; |
500 | 5ccfae10 | aliguori | char label[32]; |
501 | 5ccfae10 | aliguori | static int index; |
502 | a7954218 | balrog | |
503 | a7954218 | balrog | while (*filename && *filename != ':') { |
504 | a7954218 | balrog | const char *p; |
505 | a7954218 | balrog | char *e;
|
506 | a7954218 | balrog | if (strstart(filename, "vendorid=", &p)) { |
507 | a7954218 | balrog | vendorid = strtol(p, &e, 16);
|
508 | a7954218 | balrog | if (e == p || (*e && *e != ',' && *e != ':')) { |
509 | 1ecda02b | Markus Armbruster | error_report("bogus vendor ID %s", p);
|
510 | a7954218 | balrog | return NULL; |
511 | a7954218 | balrog | } |
512 | a7954218 | balrog | filename = e; |
513 | a7954218 | balrog | } else if (strstart(filename, "productid=", &p)) { |
514 | a7954218 | balrog | productid = strtol(p, &e, 16);
|
515 | a7954218 | balrog | if (e == p || (*e && *e != ',' && *e != ':')) { |
516 | 1ecda02b | Markus Armbruster | error_report("bogus product ID %s", p);
|
517 | a7954218 | balrog | return NULL; |
518 | a7954218 | balrog | } |
519 | a7954218 | balrog | filename = e; |
520 | a7954218 | balrog | } else {
|
521 | 1ecda02b | Markus Armbruster | error_report("unrecognized serial USB option %s", filename);
|
522 | a7954218 | balrog | return NULL; |
523 | a7954218 | balrog | } |
524 | a7954218 | balrog | while(*filename == ',') |
525 | a7954218 | balrog | filename++; |
526 | a7954218 | balrog | } |
527 | a7954218 | balrog | if (!*filename) {
|
528 | 1ecda02b | Markus Armbruster | error_report("character device specification needed");
|
529 | a7954218 | balrog | return NULL; |
530 | a7954218 | balrog | } |
531 | a7954218 | balrog | filename++; |
532 | a7954218 | balrog | |
533 | 5ccfae10 | aliguori | snprintf(label, sizeof(label), "usbserial%d", index++); |
534 | 27143a44 | Anthony Liguori | cdrv = qemu_chr_new(label, filename, NULL);
|
535 | a7954218 | balrog | if (!cdrv)
|
536 | 806b6024 | Gerd Hoffmann | return NULL; |
537 | a7954218 | balrog | |
538 | 556cd098 | Markus Armbruster | dev = usb_create(NULL /* FIXME */, "usb-serial"); |
539 | d44168ff | Paul Brook | if (!dev) {
|
540 | d44168ff | Paul Brook | return NULL; |
541 | d44168ff | Paul Brook | } |
542 | 2b0efdc3 | Gerd Hoffmann | qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
|
543 | 2b0efdc3 | Gerd Hoffmann | if (vendorid)
|
544 | 2b0efdc3 | Gerd Hoffmann | qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid);
|
545 | 2b0efdc3 | Gerd Hoffmann | if (productid)
|
546 | 2b0efdc3 | Gerd Hoffmann | qdev_prop_set_uint16(&dev->qdev, "productid", productid);
|
547 | beb6f0de | Kevin Wolf | qdev_init_nofail(&dev->qdev); |
548 | a7954218 | balrog | |
549 | 2b0efdc3 | Gerd Hoffmann | return dev;
|
550 | 2b0efdc3 | Gerd Hoffmann | } |
551 | 2b0efdc3 | Gerd Hoffmann | |
552 | 2b0efdc3 | Gerd Hoffmann | static USBDevice *usb_braille_init(const char *unused) |
553 | 2b0efdc3 | Gerd Hoffmann | { |
554 | 2b0efdc3 | Gerd Hoffmann | USBDevice *dev; |
555 | 2b0efdc3 | Gerd Hoffmann | CharDriverState *cdrv; |
556 | 2b0efdc3 | Gerd Hoffmann | |
557 | 27143a44 | Anthony Liguori | cdrv = qemu_chr_new("braille", "braille", NULL); |
558 | 2b0efdc3 | Gerd Hoffmann | if (!cdrv)
|
559 | 2b0efdc3 | Gerd Hoffmann | return NULL; |
560 | 806b6024 | Gerd Hoffmann | |
561 | 556cd098 | Markus Armbruster | dev = usb_create(NULL /* FIXME */, "usb-braille"); |
562 | 2b0efdc3 | Gerd Hoffmann | qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
|
563 | beb6f0de | Kevin Wolf | qdev_init_nofail(&dev->qdev); |
564 | 2b0efdc3 | Gerd Hoffmann | |
565 | 2b0efdc3 | Gerd Hoffmann | return dev;
|
566 | a7954218 | balrog | } |
567 | 806b6024 | Gerd Hoffmann | |
568 | 98e51ec9 | Gerd Hoffmann | static const VMStateDescription vmstate_usb_serial = { |
569 | 98e51ec9 | Gerd Hoffmann | .name = "usb-serial",
|
570 | 98e51ec9 | Gerd Hoffmann | .unmigratable = 1,
|
571 | 98e51ec9 | Gerd Hoffmann | }; |
572 | 98e51ec9 | Gerd Hoffmann | |
573 | 39bffca2 | Anthony Liguori | static Property serial_properties[] = {
|
574 | 39bffca2 | Anthony Liguori | DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
575 | 39bffca2 | Anthony Liguori | DEFINE_PROP_END_OF_LIST(), |
576 | 39bffca2 | Anthony Liguori | }; |
577 | 39bffca2 | Anthony Liguori | |
578 | 62aed765 | Anthony Liguori | static void usb_serial_class_initfn(ObjectClass *klass, void *data) |
579 | 62aed765 | Anthony Liguori | { |
580 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
581 | 62aed765 | Anthony Liguori | USBDeviceClass *uc = USB_DEVICE_CLASS(klass); |
582 | 62aed765 | Anthony Liguori | |
583 | 62aed765 | Anthony Liguori | uc->init = usb_serial_initfn; |
584 | 62aed765 | Anthony Liguori | uc->product_desc = "QEMU USB Serial";
|
585 | 62aed765 | Anthony Liguori | uc->usb_desc = &desc_serial; |
586 | 62aed765 | Anthony Liguori | uc->handle_reset = usb_serial_handle_reset; |
587 | 62aed765 | Anthony Liguori | uc->handle_control = usb_serial_handle_control; |
588 | 62aed765 | Anthony Liguori | uc->handle_data = usb_serial_handle_data; |
589 | 62aed765 | Anthony Liguori | uc->handle_destroy = usb_serial_handle_destroy; |
590 | 39bffca2 | Anthony Liguori | dc->vmsd = &vmstate_usb_serial; |
591 | 39bffca2 | Anthony Liguori | dc->props = serial_properties; |
592 | 62aed765 | Anthony Liguori | } |
593 | 62aed765 | Anthony Liguori | |
594 | 39bffca2 | Anthony Liguori | static TypeInfo serial_info = {
|
595 | 39bffca2 | Anthony Liguori | .name = "usb-serial",
|
596 | 39bffca2 | Anthony Liguori | .parent = TYPE_USB_DEVICE, |
597 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(USBSerialState),
|
598 | 39bffca2 | Anthony Liguori | .class_init = usb_serial_class_initfn, |
599 | 39bffca2 | Anthony Liguori | }; |
600 | 39bffca2 | Anthony Liguori | |
601 | 39bffca2 | Anthony Liguori | static Property braille_properties[] = {
|
602 | 39bffca2 | Anthony Liguori | DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
603 | 39bffca2 | Anthony Liguori | DEFINE_PROP_END_OF_LIST(), |
604 | 2b0efdc3 | Gerd Hoffmann | }; |
605 | 2b0efdc3 | Gerd Hoffmann | |
606 | 62aed765 | Anthony Liguori | static void usb_braille_class_initfn(ObjectClass *klass, void *data) |
607 | 62aed765 | Anthony Liguori | { |
608 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
609 | 62aed765 | Anthony Liguori | USBDeviceClass *uc = USB_DEVICE_CLASS(klass); |
610 | 62aed765 | Anthony Liguori | |
611 | 62aed765 | Anthony Liguori | uc->init = usb_serial_initfn; |
612 | 62aed765 | Anthony Liguori | uc->product_desc = "QEMU USB Braille";
|
613 | 62aed765 | Anthony Liguori | uc->usb_desc = &desc_braille; |
614 | 62aed765 | Anthony Liguori | uc->handle_reset = usb_serial_handle_reset; |
615 | 62aed765 | Anthony Liguori | uc->handle_control = usb_serial_handle_control; |
616 | 62aed765 | Anthony Liguori | uc->handle_data = usb_serial_handle_data; |
617 | 62aed765 | Anthony Liguori | uc->handle_destroy = usb_serial_handle_destroy; |
618 | 39bffca2 | Anthony Liguori | dc->vmsd = &vmstate_usb_serial; |
619 | 39bffca2 | Anthony Liguori | dc->props = braille_properties; |
620 | 62aed765 | Anthony Liguori | } |
621 | 62aed765 | Anthony Liguori | |
622 | 39bffca2 | Anthony Liguori | static TypeInfo braille_info = {
|
623 | 39bffca2 | Anthony Liguori | .name = "usb-braille",
|
624 | 39bffca2 | Anthony Liguori | .parent = TYPE_USB_DEVICE, |
625 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(USBSerialState),
|
626 | 39bffca2 | Anthony Liguori | .class_init = usb_braille_class_initfn, |
627 | 806b6024 | Gerd Hoffmann | }; |
628 | 806b6024 | Gerd Hoffmann | |
629 | 806b6024 | Gerd Hoffmann | static void usb_serial_register_devices(void) |
630 | 806b6024 | Gerd Hoffmann | { |
631 | 39bffca2 | Anthony Liguori | type_register_static(&serial_info); |
632 | ba02430f | Anthony Liguori | usb_legacy_register("usb-serial", "serial", usb_serial_init); |
633 | 39bffca2 | Anthony Liguori | type_register_static(&braille_info); |
634 | ba02430f | Anthony Liguori | usb_legacy_register("usb-braille", "braille", usb_braille_init); |
635 | 806b6024 | Gerd Hoffmann | } |
636 | 806b6024 | Gerd Hoffmann | device_init(usb_serial_register_devices) |