root / hw / usb / hcd-uhci.c @ afd347ab
History | View | Annotate | Download (38.3 kB)
1 | bb36d470 | bellard | /*
|
---|---|---|---|
2 | bb36d470 | bellard | * USB UHCI controller emulation
|
3 | 5fafdf24 | ths | *
|
4 | bb36d470 | bellard | * Copyright (c) 2005 Fabrice Bellard
|
5 | 5fafdf24 | ths | *
|
6 | 54f254f9 | aliguori | * Copyright (c) 2008 Max Krasnyansky
|
7 | 54f254f9 | aliguori | * Magor rewrite of the UHCI data structures parser and frame processor
|
8 | 54f254f9 | aliguori | * Support for fully async operation and multiple outstanding transactions
|
9 | 54f254f9 | aliguori | *
|
10 | bb36d470 | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
11 | bb36d470 | bellard | * of this software and associated documentation files (the "Software"), to deal
|
12 | bb36d470 | bellard | * in the Software without restriction, including without limitation the rights
|
13 | bb36d470 | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14 | bb36d470 | bellard | * copies of the Software, and to permit persons to whom the Software is
|
15 | bb36d470 | bellard | * furnished to do so, subject to the following conditions:
|
16 | bb36d470 | bellard | *
|
17 | bb36d470 | bellard | * The above copyright notice and this permission notice shall be included in
|
18 | bb36d470 | bellard | * all copies or substantial portions of the Software.
|
19 | bb36d470 | bellard | *
|
20 | bb36d470 | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21 | bb36d470 | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22 | bb36d470 | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
23 | bb36d470 | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24 | bb36d470 | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25 | bb36d470 | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
26 | bb36d470 | bellard | * THE SOFTWARE.
|
27 | bb36d470 | bellard | */
|
28 | f1ae32a1 | Gerd Hoffmann | #include "hw/hw.h" |
29 | f1ae32a1 | Gerd Hoffmann | #include "hw/usb.h" |
30 | a2cb15b0 | Michael S. Tsirkin | #include "hw/pci/pci.h" |
31 | 1de7afc9 | Paolo Bonzini | #include "qemu/timer.h" |
32 | 1de7afc9 | Paolo Bonzini | #include "qemu/iov.h" |
33 | 9c17d615 | Paolo Bonzini | #include "sysemu/dma.h" |
34 | 50dcc0f8 | Gerd Hoffmann | #include "trace.h" |
35 | bb36d470 | bellard | |
36 | bb36d470 | bellard | //#define DEBUG
|
37 | 54f254f9 | aliguori | //#define DEBUG_DUMP_DATA
|
38 | bb36d470 | bellard | |
39 | 96217e31 | ths | #define UHCI_CMD_FGR (1 << 4) |
40 | 96217e31 | ths | #define UHCI_CMD_EGSM (1 << 3) |
41 | bb36d470 | bellard | #define UHCI_CMD_GRESET (1 << 2) |
42 | bb36d470 | bellard | #define UHCI_CMD_HCRESET (1 << 1) |
43 | bb36d470 | bellard | #define UHCI_CMD_RS (1 << 0) |
44 | bb36d470 | bellard | |
45 | bb36d470 | bellard | #define UHCI_STS_HCHALTED (1 << 5) |
46 | bb36d470 | bellard | #define UHCI_STS_HCPERR (1 << 4) |
47 | bb36d470 | bellard | #define UHCI_STS_HSERR (1 << 3) |
48 | bb36d470 | bellard | #define UHCI_STS_RD (1 << 2) |
49 | bb36d470 | bellard | #define UHCI_STS_USBERR (1 << 1) |
50 | bb36d470 | bellard | #define UHCI_STS_USBINT (1 << 0) |
51 | bb36d470 | bellard | |
52 | bb36d470 | bellard | #define TD_CTRL_SPD (1 << 29) |
53 | bb36d470 | bellard | #define TD_CTRL_ERROR_SHIFT 27 |
54 | bb36d470 | bellard | #define TD_CTRL_IOS (1 << 25) |
55 | bb36d470 | bellard | #define TD_CTRL_IOC (1 << 24) |
56 | bb36d470 | bellard | #define TD_CTRL_ACTIVE (1 << 23) |
57 | bb36d470 | bellard | #define TD_CTRL_STALL (1 << 22) |
58 | bb36d470 | bellard | #define TD_CTRL_BABBLE (1 << 20) |
59 | bb36d470 | bellard | #define TD_CTRL_NAK (1 << 19) |
60 | bb36d470 | bellard | #define TD_CTRL_TIMEOUT (1 << 18) |
61 | bb36d470 | bellard | |
62 | 9159f679 | Gerd Hoffmann | #define UHCI_PORT_SUSPEND (1 << 12) |
63 | bb36d470 | bellard | #define UHCI_PORT_RESET (1 << 9) |
64 | bb36d470 | bellard | #define UHCI_PORT_LSDA (1 << 8) |
65 | 9159f679 | Gerd Hoffmann | #define UHCI_PORT_RD (1 << 6) |
66 | bb36d470 | bellard | #define UHCI_PORT_ENC (1 << 3) |
67 | bb36d470 | bellard | #define UHCI_PORT_EN (1 << 2) |
68 | bb36d470 | bellard | #define UHCI_PORT_CSC (1 << 1) |
69 | bb36d470 | bellard | #define UHCI_PORT_CCS (1 << 0) |
70 | bb36d470 | bellard | |
71 | 9159f679 | Gerd Hoffmann | #define UHCI_PORT_READ_ONLY (0x1bb) |
72 | 9159f679 | Gerd Hoffmann | #define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC)
|
73 | 9159f679 | Gerd Hoffmann | |
74 | bb36d470 | bellard | #define FRAME_TIMER_FREQ 1000 |
75 | bb36d470 | bellard | |
76 | 3200d108 | Gerd Hoffmann | #define FRAME_MAX_LOOPS 256 |
77 | bb36d470 | bellard | |
78 | 475443cf | Hans de Goede | /* Must be large enough to handle 10 frame delay for initial isoc requests */
|
79 | 475443cf | Hans de Goede | #define QH_VALID 32 |
80 | 475443cf | Hans de Goede | |
81 | f8f48b69 | Hans de Goede | #define MAX_FRAMES_PER_TICK (QH_VALID / 2) |
82 | f8f48b69 | Hans de Goede | |
83 | bb36d470 | bellard | #define NB_PORTS 2 |
84 | bb36d470 | bellard | |
85 | 60e1b2a6 | Gerd Hoffmann | enum {
|
86 | 0cd178ca | Gerd Hoffmann | TD_RESULT_STOP_FRAME = 10,
|
87 | 0cd178ca | Gerd Hoffmann | TD_RESULT_COMPLETE, |
88 | 0cd178ca | Gerd Hoffmann | TD_RESULT_NEXT_QH, |
89 | 4efe4ef3 | Gerd Hoffmann | TD_RESULT_ASYNC_START, |
90 | 4efe4ef3 | Gerd Hoffmann | TD_RESULT_ASYNC_CONT, |
91 | 60e1b2a6 | Gerd Hoffmann | }; |
92 | 60e1b2a6 | Gerd Hoffmann | |
93 | 7b5a44c5 | Gerd Hoffmann | typedef struct UHCIState UHCIState; |
94 | f8af1e88 | Gerd Hoffmann | typedef struct UHCIAsync UHCIAsync; |
95 | f8af1e88 | Gerd Hoffmann | typedef struct UHCIQueue UHCIQueue; |
96 | 2c2e8525 | Gerd Hoffmann | typedef struct UHCIInfo UHCIInfo; |
97 | 8f3f90b0 | Gerd Hoffmann | typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; |
98 | 2c2e8525 | Gerd Hoffmann | |
99 | 2c2e8525 | Gerd Hoffmann | struct UHCIInfo {
|
100 | 2c2e8525 | Gerd Hoffmann | const char *name; |
101 | 2c2e8525 | Gerd Hoffmann | uint16_t vendor_id; |
102 | 2c2e8525 | Gerd Hoffmann | uint16_t device_id; |
103 | 2c2e8525 | Gerd Hoffmann | uint8_t revision; |
104 | 8f3f90b0 | Gerd Hoffmann | uint8_t irq_pin; |
105 | 2c2e8525 | Gerd Hoffmann | int (*initfn)(PCIDevice *dev);
|
106 | 2c2e8525 | Gerd Hoffmann | bool unplug;
|
107 | 2c2e8525 | Gerd Hoffmann | }; |
108 | 7b5a44c5 | Gerd Hoffmann | |
109 | 8f3f90b0 | Gerd Hoffmann | struct UHCIPCIDeviceClass {
|
110 | 8f3f90b0 | Gerd Hoffmann | PCIDeviceClass parent_class; |
111 | 8f3f90b0 | Gerd Hoffmann | UHCIInfo info; |
112 | 8f3f90b0 | Gerd Hoffmann | }; |
113 | 8f3f90b0 | Gerd Hoffmann | |
114 | 54f254f9 | aliguori | /*
|
115 | 54f254f9 | aliguori | * Pending async transaction.
|
116 | 54f254f9 | aliguori | * 'packet' must be the first field because completion
|
117 | 54f254f9 | aliguori | * handler does "(UHCIAsync *) pkt" cast.
|
118 | 54f254f9 | aliguori | */
|
119 | f8af1e88 | Gerd Hoffmann | |
120 | f8af1e88 | Gerd Hoffmann | struct UHCIAsync {
|
121 | 54f254f9 | aliguori | USBPacket packet; |
122 | df5e66ee | Gerd Hoffmann | QEMUSGList sgl; |
123 | f8af1e88 | Gerd Hoffmann | UHCIQueue *queue; |
124 | ddf6583f | Gerd Hoffmann | QTAILQ_ENTRY(UHCIAsync) next; |
125 | 1f250cc7 | Hans de Goede | uint32_t td_addr; |
126 | 54f254f9 | aliguori | uint8_t done; |
127 | f8af1e88 | Gerd Hoffmann | }; |
128 | f8af1e88 | Gerd Hoffmann | |
129 | f8af1e88 | Gerd Hoffmann | struct UHCIQueue {
|
130 | 66a08cbe | Hans de Goede | uint32_t qh_addr; |
131 | f8af1e88 | Gerd Hoffmann | uint32_t token; |
132 | f8af1e88 | Gerd Hoffmann | UHCIState *uhci; |
133 | 11d15e40 | Hans de Goede | USBEndpoint *ep; |
134 | f8af1e88 | Gerd Hoffmann | QTAILQ_ENTRY(UHCIQueue) next; |
135 | 8928c9c4 | Hans de Goede | QTAILQ_HEAD(asyncs_head, UHCIAsync) asyncs; |
136 | f8af1e88 | Gerd Hoffmann | int8_t valid; |
137 | f8af1e88 | Gerd Hoffmann | }; |
138 | 54f254f9 | aliguori | |
139 | bb36d470 | bellard | typedef struct UHCIPort { |
140 | bb36d470 | bellard | USBPort port; |
141 | bb36d470 | bellard | uint16_t ctrl; |
142 | bb36d470 | bellard | } UHCIPort; |
143 | bb36d470 | bellard | |
144 | 7b5a44c5 | Gerd Hoffmann | struct UHCIState {
|
145 | bb36d470 | bellard | PCIDevice dev; |
146 | a03f66e4 | Avi Kivity | MemoryRegion io_bar; |
147 | 35e4977f | Hans de Goede | USBBus bus; /* Note unused when we're a companion controller */
|
148 | bb36d470 | bellard | uint16_t cmd; /* cmd register */
|
149 | bb36d470 | bellard | uint16_t status; |
150 | bb36d470 | bellard | uint16_t intr; /* interrupt enable register */
|
151 | bb36d470 | bellard | uint16_t frnum; /* frame number */
|
152 | bb36d470 | bellard | uint32_t fl_base_addr; /* frame list base address */
|
153 | bb36d470 | bellard | uint8_t sof_timing; |
154 | bb36d470 | bellard | uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
|
155 | 8e65b7c0 | David S. Ahern | int64_t expire_time; |
156 | bb36d470 | bellard | QEMUTimer *frame_timer; |
157 | 9a16c595 | Gerd Hoffmann | QEMUBH *bh; |
158 | 4aed20e2 | Gerd Hoffmann | uint32_t frame_bytes; |
159 | 40141d12 | Gerd Hoffmann | uint32_t frame_bandwidth; |
160 | 88793816 | Hans de Goede | bool completions_only;
|
161 | bb36d470 | bellard | UHCIPort ports[NB_PORTS]; |
162 | 4d611c9a | pbrook | |
163 | 4d611c9a | pbrook | /* Interrupts that should be raised at the end of the current frame. */
|
164 | 4d611c9a | pbrook | uint32_t pending_int_mask; |
165 | 973002c1 | Gerd Hoffmann | int irq_pin;
|
166 | 54f254f9 | aliguori | |
167 | 54f254f9 | aliguori | /* Active packets */
|
168 | f8af1e88 | Gerd Hoffmann | QTAILQ_HEAD(, UHCIQueue) queues; |
169 | 64e58fe5 | Juan Quintela | uint8_t num_ports_vmstate; |
170 | 35e4977f | Hans de Goede | |
171 | 35e4977f | Hans de Goede | /* Properties */
|
172 | 35e4977f | Hans de Goede | char *masterbus;
|
173 | 35e4977f | Hans de Goede | uint32_t firstport; |
174 | 9fdf7027 | Hans de Goede | uint32_t maxframes; |
175 | 7b5a44c5 | Gerd Hoffmann | }; |
176 | bb36d470 | bellard | |
177 | bb36d470 | bellard | typedef struct UHCI_TD { |
178 | bb36d470 | bellard | uint32_t link; |
179 | bb36d470 | bellard | uint32_t ctrl; /* see TD_CTRL_xxx */
|
180 | bb36d470 | bellard | uint32_t token; |
181 | bb36d470 | bellard | uint32_t buffer; |
182 | bb36d470 | bellard | } UHCI_TD; |
183 | bb36d470 | bellard | |
184 | bb36d470 | bellard | typedef struct UHCI_QH { |
185 | bb36d470 | bellard | uint32_t link; |
186 | bb36d470 | bellard | uint32_t el_link; |
187 | bb36d470 | bellard | } UHCI_QH; |
188 | bb36d470 | bellard | |
189 | 40507377 | Hans de Goede | static void uhci_async_cancel(UHCIAsync *async); |
190 | 11d15e40 | Hans de Goede | static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td); |
191 | 40507377 | Hans de Goede | |
192 | f8af1e88 | Gerd Hoffmann | static inline int32_t uhci_queue_token(UHCI_TD *td) |
193 | f8af1e88 | Gerd Hoffmann | { |
194 | 6fe30910 | Hans de Goede | if ((td->token & (0xf << 15)) == 0) { |
195 | 6fe30910 | Hans de Goede | /* ctrl ep, cover ep and dev, not pid! */
|
196 | 6fe30910 | Hans de Goede | return td->token & 0x7ff00; |
197 | 6fe30910 | Hans de Goede | } else {
|
198 | 6fe30910 | Hans de Goede | /* covers ep, dev, pid -> identifies the endpoint */
|
199 | 6fe30910 | Hans de Goede | return td->token & 0x7ffff; |
200 | 6fe30910 | Hans de Goede | } |
201 | f8af1e88 | Gerd Hoffmann | } |
202 | f8af1e88 | Gerd Hoffmann | |
203 | 66a08cbe | Hans de Goede | static UHCIQueue *uhci_queue_new(UHCIState *s, uint32_t qh_addr, UHCI_TD *td,
|
204 | 66a08cbe | Hans de Goede | USBEndpoint *ep) |
205 | f8af1e88 | Gerd Hoffmann | { |
206 | f8af1e88 | Gerd Hoffmann | UHCIQueue *queue; |
207 | f8af1e88 | Gerd Hoffmann | |
208 | f8af1e88 | Gerd Hoffmann | queue = g_new0(UHCIQueue, 1);
|
209 | f8af1e88 | Gerd Hoffmann | queue->uhci = s; |
210 | 66a08cbe | Hans de Goede | queue->qh_addr = qh_addr; |
211 | 66a08cbe | Hans de Goede | queue->token = uhci_queue_token(td); |
212 | 11d15e40 | Hans de Goede | queue->ep = ep; |
213 | f8af1e88 | Gerd Hoffmann | QTAILQ_INIT(&queue->asyncs); |
214 | f8af1e88 | Gerd Hoffmann | QTAILQ_INSERT_HEAD(&s->queues, queue, next); |
215 | 475443cf | Hans de Goede | queue->valid = QH_VALID; |
216 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_queue_add(queue->token); |
217 | f8af1e88 | Gerd Hoffmann | return queue;
|
218 | f8af1e88 | Gerd Hoffmann | } |
219 | f8af1e88 | Gerd Hoffmann | |
220 | 66a08cbe | Hans de Goede | static void uhci_queue_free(UHCIQueue *queue, const char *reason) |
221 | f8af1e88 | Gerd Hoffmann | { |
222 | f8af1e88 | Gerd Hoffmann | UHCIState *s = queue->uhci; |
223 | 40507377 | Hans de Goede | UHCIAsync *async; |
224 | 40507377 | Hans de Goede | |
225 | 40507377 | Hans de Goede | while (!QTAILQ_EMPTY(&queue->asyncs)) {
|
226 | 40507377 | Hans de Goede | async = QTAILQ_FIRST(&queue->asyncs); |
227 | 40507377 | Hans de Goede | uhci_async_cancel(async); |
228 | 40507377 | Hans de Goede | } |
229 | f79738b0 | Hans de Goede | usb_device_ep_stopped(queue->ep->dev, queue->ep); |
230 | f8af1e88 | Gerd Hoffmann | |
231 | 66a08cbe | Hans de Goede | trace_usb_uhci_queue_del(queue->token, reason); |
232 | f8af1e88 | Gerd Hoffmann | QTAILQ_REMOVE(&s->queues, queue, next); |
233 | f8af1e88 | Gerd Hoffmann | g_free(queue); |
234 | f8af1e88 | Gerd Hoffmann | } |
235 | f8af1e88 | Gerd Hoffmann | |
236 | 66a08cbe | Hans de Goede | static UHCIQueue *uhci_queue_find(UHCIState *s, UHCI_TD *td)
|
237 | 66a08cbe | Hans de Goede | { |
238 | 66a08cbe | Hans de Goede | uint32_t token = uhci_queue_token(td); |
239 | 66a08cbe | Hans de Goede | UHCIQueue *queue; |
240 | 66a08cbe | Hans de Goede | |
241 | 66a08cbe | Hans de Goede | QTAILQ_FOREACH(queue, &s->queues, next) { |
242 | 66a08cbe | Hans de Goede | if (queue->token == token) {
|
243 | 66a08cbe | Hans de Goede | return queue;
|
244 | 66a08cbe | Hans de Goede | } |
245 | 66a08cbe | Hans de Goede | } |
246 | 66a08cbe | Hans de Goede | return NULL; |
247 | 66a08cbe | Hans de Goede | } |
248 | 66a08cbe | Hans de Goede | |
249 | 66a08cbe | Hans de Goede | static bool uhci_queue_verify(UHCIQueue *queue, uint32_t qh_addr, UHCI_TD *td, |
250 | 66a08cbe | Hans de Goede | uint32_t td_addr, bool queuing)
|
251 | 66a08cbe | Hans de Goede | { |
252 | 66a08cbe | Hans de Goede | UHCIAsync *first = QTAILQ_FIRST(&queue->asyncs); |
253 | 66a08cbe | Hans de Goede | |
254 | 66a08cbe | Hans de Goede | return queue->qh_addr == qh_addr &&
|
255 | 66a08cbe | Hans de Goede | queue->token == uhci_queue_token(td) && |
256 | 66a08cbe | Hans de Goede | (queuing || !(td->ctrl & TD_CTRL_ACTIVE) || first == NULL ||
|
257 | 66a08cbe | Hans de Goede | first->td_addr == td_addr); |
258 | 66a08cbe | Hans de Goede | } |
259 | 66a08cbe | Hans de Goede | |
260 | 1f250cc7 | Hans de Goede | static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t td_addr)
|
261 | 54f254f9 | aliguori | { |
262 | 326700e3 | Gerd Hoffmann | UHCIAsync *async = g_new0(UHCIAsync, 1);
|
263 | 487414f1 | aliguori | |
264 | f8af1e88 | Gerd Hoffmann | async->queue = queue; |
265 | 1f250cc7 | Hans de Goede | async->td_addr = td_addr; |
266 | 4f4321c1 | Gerd Hoffmann | usb_packet_init(&async->packet); |
267 | f8af1e88 | Gerd Hoffmann | pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
|
268 | 1f250cc7 | Hans de Goede | trace_usb_uhci_packet_add(async->queue->token, async->td_addr); |
269 | 54f254f9 | aliguori | |
270 | 54f254f9 | aliguori | return async;
|
271 | 54f254f9 | aliguori | } |
272 | 54f254f9 | aliguori | |
273 | f8af1e88 | Gerd Hoffmann | static void uhci_async_free(UHCIAsync *async) |
274 | 54f254f9 | aliguori | { |
275 | 1f250cc7 | Hans de Goede | trace_usb_uhci_packet_del(async->queue->token, async->td_addr); |
276 | 4f4321c1 | Gerd Hoffmann | usb_packet_cleanup(&async->packet); |
277 | df5e66ee | Gerd Hoffmann | qemu_sglist_destroy(&async->sgl); |
278 | 7267c094 | Anthony Liguori | g_free(async); |
279 | 54f254f9 | aliguori | } |
280 | 54f254f9 | aliguori | |
281 | f8af1e88 | Gerd Hoffmann | static void uhci_async_link(UHCIAsync *async) |
282 | 54f254f9 | aliguori | { |
283 | f8af1e88 | Gerd Hoffmann | UHCIQueue *queue = async->queue; |
284 | f8af1e88 | Gerd Hoffmann | QTAILQ_INSERT_TAIL(&queue->asyncs, async, next); |
285 | 1f250cc7 | Hans de Goede | trace_usb_uhci_packet_link_async(async->queue->token, async->td_addr); |
286 | 54f254f9 | aliguori | } |
287 | 54f254f9 | aliguori | |
288 | f8af1e88 | Gerd Hoffmann | static void uhci_async_unlink(UHCIAsync *async) |
289 | 54f254f9 | aliguori | { |
290 | f8af1e88 | Gerd Hoffmann | UHCIQueue *queue = async->queue; |
291 | f8af1e88 | Gerd Hoffmann | QTAILQ_REMOVE(&queue->asyncs, async, next); |
292 | 1f250cc7 | Hans de Goede | trace_usb_uhci_packet_unlink_async(async->queue->token, async->td_addr); |
293 | 54f254f9 | aliguori | } |
294 | 54f254f9 | aliguori | |
295 | f8af1e88 | Gerd Hoffmann | static void uhci_async_cancel(UHCIAsync *async) |
296 | 54f254f9 | aliguori | { |
297 | 2f2ee268 | Hans de Goede | uhci_async_unlink(async); |
298 | 1f250cc7 | Hans de Goede | trace_usb_uhci_packet_cancel(async->queue->token, async->td_addr, |
299 | 1f250cc7 | Hans de Goede | async->done); |
300 | 54f254f9 | aliguori | if (!async->done)
|
301 | 54f254f9 | aliguori | usb_cancel_packet(&async->packet); |
302 | 00a0770d | Hans de Goede | usb_packet_unmap(&async->packet, &async->sgl); |
303 | f8af1e88 | Gerd Hoffmann | uhci_async_free(async); |
304 | 54f254f9 | aliguori | } |
305 | 54f254f9 | aliguori | |
306 | 54f254f9 | aliguori | /*
|
307 | 54f254f9 | aliguori | * Mark all outstanding async packets as invalid.
|
308 | 54f254f9 | aliguori | * This is used for canceling them when TDs are removed by the HCD.
|
309 | 54f254f9 | aliguori | */
|
310 | f8af1e88 | Gerd Hoffmann | static void uhci_async_validate_begin(UHCIState *s) |
311 | 54f254f9 | aliguori | { |
312 | f8af1e88 | Gerd Hoffmann | UHCIQueue *queue; |
313 | 54f254f9 | aliguori | |
314 | f8af1e88 | Gerd Hoffmann | QTAILQ_FOREACH(queue, &s->queues, next) { |
315 | f8af1e88 | Gerd Hoffmann | queue->valid--; |
316 | 54f254f9 | aliguori | } |
317 | 54f254f9 | aliguori | } |
318 | 54f254f9 | aliguori | |
319 | 54f254f9 | aliguori | /*
|
320 | 54f254f9 | aliguori | * Cancel async packets that are no longer valid
|
321 | 54f254f9 | aliguori | */
|
322 | 54f254f9 | aliguori | static void uhci_async_validate_end(UHCIState *s) |
323 | 54f254f9 | aliguori | { |
324 | f8af1e88 | Gerd Hoffmann | UHCIQueue *queue, *n; |
325 | 54f254f9 | aliguori | |
326 | f8af1e88 | Gerd Hoffmann | QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { |
327 | 40507377 | Hans de Goede | if (!queue->valid) {
|
328 | 66a08cbe | Hans de Goede | uhci_queue_free(queue, "validate-end");
|
329 | f8af1e88 | Gerd Hoffmann | } |
330 | 54f254f9 | aliguori | } |
331 | 54f254f9 | aliguori | } |
332 | 54f254f9 | aliguori | |
333 | 07771f6f | Gerd Hoffmann | static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) |
334 | 07771f6f | Gerd Hoffmann | { |
335 | 5ad23e87 | Hans de Goede | UHCIQueue *queue, *n; |
336 | 07771f6f | Gerd Hoffmann | |
337 | 5ad23e87 | Hans de Goede | QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { |
338 | 5ad23e87 | Hans de Goede | if (queue->ep->dev == dev) {
|
339 | 5ad23e87 | Hans de Goede | uhci_queue_free(queue, "cancel-device");
|
340 | 07771f6f | Gerd Hoffmann | } |
341 | 07771f6f | Gerd Hoffmann | } |
342 | 07771f6f | Gerd Hoffmann | } |
343 | 07771f6f | Gerd Hoffmann | |
344 | 54f254f9 | aliguori | static void uhci_async_cancel_all(UHCIState *s) |
345 | 54f254f9 | aliguori | { |
346 | 77fa9aee | Gerd Hoffmann | UHCIQueue *queue, *nq; |
347 | 54f254f9 | aliguori | |
348 | 77fa9aee | Gerd Hoffmann | QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) { |
349 | 66a08cbe | Hans de Goede | uhci_queue_free(queue, "cancel-all");
|
350 | 54f254f9 | aliguori | } |
351 | 54f254f9 | aliguori | } |
352 | 54f254f9 | aliguori | |
353 | 8c75a899 | Hans de Goede | static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t td_addr)
|
354 | 54f254f9 | aliguori | { |
355 | f8af1e88 | Gerd Hoffmann | UHCIQueue *queue; |
356 | ddf6583f | Gerd Hoffmann | UHCIAsync *async; |
357 | e8ee3c72 | aurel32 | |
358 | f8af1e88 | Gerd Hoffmann | QTAILQ_FOREACH(queue, &s->queues, next) { |
359 | 8c75a899 | Hans de Goede | QTAILQ_FOREACH(async, &queue->asyncs, next) { |
360 | 8c75a899 | Hans de Goede | if (async->td_addr == td_addr) {
|
361 | 8c75a899 | Hans de Goede | return async;
|
362 | 8c75a899 | Hans de Goede | } |
363 | f8af1e88 | Gerd Hoffmann | } |
364 | f8af1e88 | Gerd Hoffmann | } |
365 | f8af1e88 | Gerd Hoffmann | return NULL; |
366 | 54f254f9 | aliguori | } |
367 | 54f254f9 | aliguori | |
368 | bb36d470 | bellard | static void uhci_update_irq(UHCIState *s) |
369 | bb36d470 | bellard | { |
370 | bb36d470 | bellard | int level;
|
371 | bb36d470 | bellard | if (((s->status2 & 1) && (s->intr & (1 << 2))) || |
372 | bb36d470 | bellard | ((s->status2 & 2) && (s->intr & (1 << 3))) || |
373 | bb36d470 | bellard | ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) || |
374 | bb36d470 | bellard | ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) || |
375 | bb36d470 | bellard | (s->status & UHCI_STS_HSERR) || |
376 | bb36d470 | bellard | (s->status & UHCI_STS_HCPERR)) { |
377 | bb36d470 | bellard | level = 1;
|
378 | bb36d470 | bellard | } else {
|
379 | bb36d470 | bellard | level = 0;
|
380 | bb36d470 | bellard | } |
381 | 973002c1 | Gerd Hoffmann | qemu_set_irq(s->dev.irq[s->irq_pin], level); |
382 | bb36d470 | bellard | } |
383 | bb36d470 | bellard | |
384 | c8075ac3 | Gleb Natapov | static void uhci_reset(void *opaque) |
385 | bb36d470 | bellard | { |
386 | c8075ac3 | Gleb Natapov | UHCIState *s = opaque; |
387 | bb36d470 | bellard | uint8_t *pci_conf; |
388 | bb36d470 | bellard | int i;
|
389 | bb36d470 | bellard | UHCIPort *port; |
390 | bb36d470 | bellard | |
391 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_reset(); |
392 | 6f382b5e | aliguori | |
393 | bb36d470 | bellard | pci_conf = s->dev.config; |
394 | bb36d470 | bellard | |
395 | bb36d470 | bellard | pci_conf[0x6a] = 0x01; /* usb clock */ |
396 | bb36d470 | bellard | pci_conf[0x6b] = 0x00; |
397 | bb36d470 | bellard | s->cmd = 0;
|
398 | bb36d470 | bellard | s->status = 0;
|
399 | bb36d470 | bellard | s->status2 = 0;
|
400 | bb36d470 | bellard | s->intr = 0;
|
401 | bb36d470 | bellard | s->fl_base_addr = 0;
|
402 | bb36d470 | bellard | s->sof_timing = 64;
|
403 | 54f254f9 | aliguori | |
404 | bb36d470 | bellard | for(i = 0; i < NB_PORTS; i++) { |
405 | bb36d470 | bellard | port = &s->ports[i]; |
406 | bb36d470 | bellard | port->ctrl = 0x0080;
|
407 | 891fb2cd | Gerd Hoffmann | if (port->port.dev && port->port.dev->attached) {
|
408 | d28f4e2d | Gerd Hoffmann | usb_port_reset(&port->port); |
409 | 618c169b | Gerd Hoffmann | } |
410 | bb36d470 | bellard | } |
411 | 54f254f9 | aliguori | |
412 | 54f254f9 | aliguori | uhci_async_cancel_all(s); |
413 | 9a16c595 | Gerd Hoffmann | qemu_bh_cancel(s->bh); |
414 | aba1f242 | Gerd Hoffmann | uhci_update_irq(s); |
415 | bb36d470 | bellard | } |
416 | bb36d470 | bellard | |
417 | 817afc61 | Juan Quintela | static const VMStateDescription vmstate_uhci_port = { |
418 | 817afc61 | Juan Quintela | .name = "uhci port",
|
419 | 817afc61 | Juan Quintela | .version_id = 1,
|
420 | 817afc61 | Juan Quintela | .minimum_version_id = 1,
|
421 | 817afc61 | Juan Quintela | .minimum_version_id_old = 1,
|
422 | 817afc61 | Juan Quintela | .fields = (VMStateField []) { |
423 | 817afc61 | Juan Quintela | VMSTATE_UINT16(ctrl, UHCIPort), |
424 | 817afc61 | Juan Quintela | VMSTATE_END_OF_LIST() |
425 | 817afc61 | Juan Quintela | } |
426 | 817afc61 | Juan Quintela | }; |
427 | 817afc61 | Juan Quintela | |
428 | 75f151cd | Gerd Hoffmann | static int uhci_post_load(void *opaque, int version_id) |
429 | 75f151cd | Gerd Hoffmann | { |
430 | 75f151cd | Gerd Hoffmann | UHCIState *s = opaque; |
431 | 75f151cd | Gerd Hoffmann | |
432 | 75f151cd | Gerd Hoffmann | if (version_id < 2) { |
433 | 75f151cd | Gerd Hoffmann | s->expire_time = qemu_get_clock_ns(vm_clock) + |
434 | 75f151cd | Gerd Hoffmann | (get_ticks_per_sec() / FRAME_TIMER_FREQ); |
435 | 75f151cd | Gerd Hoffmann | } |
436 | 75f151cd | Gerd Hoffmann | return 0; |
437 | 75f151cd | Gerd Hoffmann | } |
438 | 75f151cd | Gerd Hoffmann | |
439 | 817afc61 | Juan Quintela | static const VMStateDescription vmstate_uhci = { |
440 | 817afc61 | Juan Quintela | .name = "uhci",
|
441 | ecfdc15f | Hans de Goede | .version_id = 3,
|
442 | 817afc61 | Juan Quintela | .minimum_version_id = 1,
|
443 | 817afc61 | Juan Quintela | .minimum_version_id_old = 1,
|
444 | 75f151cd | Gerd Hoffmann | .post_load = uhci_post_load, |
445 | 817afc61 | Juan Quintela | .fields = (VMStateField []) { |
446 | 817afc61 | Juan Quintela | VMSTATE_PCI_DEVICE(dev, UHCIState), |
447 | 817afc61 | Juan Quintela | VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState), |
448 | 817afc61 | Juan Quintela | VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1,
|
449 | 817afc61 | Juan Quintela | vmstate_uhci_port, UHCIPort), |
450 | 817afc61 | Juan Quintela | VMSTATE_UINT16(cmd, UHCIState), |
451 | 817afc61 | Juan Quintela | VMSTATE_UINT16(status, UHCIState), |
452 | 817afc61 | Juan Quintela | VMSTATE_UINT16(intr, UHCIState), |
453 | 817afc61 | Juan Quintela | VMSTATE_UINT16(frnum, UHCIState), |
454 | 817afc61 | Juan Quintela | VMSTATE_UINT32(fl_base_addr, UHCIState), |
455 | 817afc61 | Juan Quintela | VMSTATE_UINT8(sof_timing, UHCIState), |
456 | 817afc61 | Juan Quintela | VMSTATE_UINT8(status2, UHCIState), |
457 | 817afc61 | Juan Quintela | VMSTATE_TIMER(frame_timer, UHCIState), |
458 | 6881dd5f | TeLeMan | VMSTATE_INT64_V(expire_time, UHCIState, 2),
|
459 | ecfdc15f | Hans de Goede | VMSTATE_UINT32_V(pending_int_mask, UHCIState, 3),
|
460 | 817afc61 | Juan Quintela | VMSTATE_END_OF_LIST() |
461 | 817afc61 | Juan Quintela | } |
462 | 817afc61 | Juan Quintela | }; |
463 | b9dc033c | balrog | |
464 | 89eb147c | Gerd Hoffmann | static void uhci_port_write(void *opaque, hwaddr addr, |
465 | 89eb147c | Gerd Hoffmann | uint64_t val, unsigned size)
|
466 | bb36d470 | bellard | { |
467 | bb36d470 | bellard | UHCIState *s = opaque; |
468 | 3b46e624 | ths | |
469 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_mmio_writew(addr, val); |
470 | 54f254f9 | aliguori | |
471 | bb36d470 | bellard | switch(addr) {
|
472 | bb36d470 | bellard | case 0x00: |
473 | bb36d470 | bellard | if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
|
474 | bb36d470 | bellard | /* start frame processing */
|
475 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_schedule_start(); |
476 | 94cc916a | Gerd Hoffmann | s->expire_time = qemu_get_clock_ns(vm_clock) + |
477 | 94cc916a | Gerd Hoffmann | (get_ticks_per_sec() / FRAME_TIMER_FREQ); |
478 | f8f48b69 | Hans de Goede | qemu_mod_timer(s->frame_timer, s->expire_time); |
479 | 52328140 | bellard | s->status &= ~UHCI_STS_HCHALTED; |
480 | 467d409f | bellard | } else if (!(val & UHCI_CMD_RS)) { |
481 | 52328140 | bellard | s->status |= UHCI_STS_HCHALTED; |
482 | bb36d470 | bellard | } |
483 | bb36d470 | bellard | if (val & UHCI_CMD_GRESET) {
|
484 | bb36d470 | bellard | UHCIPort *port; |
485 | bb36d470 | bellard | int i;
|
486 | bb36d470 | bellard | |
487 | bb36d470 | bellard | /* send reset on the USB bus */
|
488 | bb36d470 | bellard | for(i = 0; i < NB_PORTS; i++) { |
489 | bb36d470 | bellard | port = &s->ports[i]; |
490 | d28f4e2d | Gerd Hoffmann | usb_device_reset(port->port.dev); |
491 | bb36d470 | bellard | } |
492 | bb36d470 | bellard | uhci_reset(s); |
493 | bb36d470 | bellard | return;
|
494 | bb36d470 | bellard | } |
495 | 5e9ab4c4 | bellard | if (val & UHCI_CMD_HCRESET) {
|
496 | bb36d470 | bellard | uhci_reset(s); |
497 | bb36d470 | bellard | return;
|
498 | bb36d470 | bellard | } |
499 | bb36d470 | bellard | s->cmd = val; |
500 | bb36d470 | bellard | break;
|
501 | bb36d470 | bellard | case 0x02: |
502 | bb36d470 | bellard | s->status &= ~val; |
503 | bb36d470 | bellard | /* XXX: the chip spec is not coherent, so we add a hidden
|
504 | bb36d470 | bellard | register to distinguish between IOC and SPD */
|
505 | bb36d470 | bellard | if (val & UHCI_STS_USBINT)
|
506 | bb36d470 | bellard | s->status2 = 0;
|
507 | bb36d470 | bellard | uhci_update_irq(s); |
508 | bb36d470 | bellard | break;
|
509 | bb36d470 | bellard | case 0x04: |
510 | bb36d470 | bellard | s->intr = val; |
511 | bb36d470 | bellard | uhci_update_irq(s); |
512 | bb36d470 | bellard | break;
|
513 | bb36d470 | bellard | case 0x06: |
514 | bb36d470 | bellard | if (s->status & UHCI_STS_HCHALTED)
|
515 | bb36d470 | bellard | s->frnum = val & 0x7ff;
|
516 | bb36d470 | bellard | break;
|
517 | 89eb147c | Gerd Hoffmann | case 0x08: |
518 | 89eb147c | Gerd Hoffmann | s->fl_base_addr &= 0xffff0000;
|
519 | 89eb147c | Gerd Hoffmann | s->fl_base_addr |= val & ~0xfff;
|
520 | 89eb147c | Gerd Hoffmann | break;
|
521 | 89eb147c | Gerd Hoffmann | case 0x0a: |
522 | 89eb147c | Gerd Hoffmann | s->fl_base_addr &= 0x0000ffff;
|
523 | 89eb147c | Gerd Hoffmann | s->fl_base_addr |= (val << 16);
|
524 | 89eb147c | Gerd Hoffmann | break;
|
525 | 89eb147c | Gerd Hoffmann | case 0x0c: |
526 | 89eb147c | Gerd Hoffmann | s->sof_timing = val & 0xff;
|
527 | 89eb147c | Gerd Hoffmann | break;
|
528 | bb36d470 | bellard | case 0x10 ... 0x1f: |
529 | bb36d470 | bellard | { |
530 | bb36d470 | bellard | UHCIPort *port; |
531 | bb36d470 | bellard | USBDevice *dev; |
532 | bb36d470 | bellard | int n;
|
533 | bb36d470 | bellard | |
534 | bb36d470 | bellard | n = (addr >> 1) & 7; |
535 | bb36d470 | bellard | if (n >= NB_PORTS)
|
536 | bb36d470 | bellard | return;
|
537 | bb36d470 | bellard | port = &s->ports[n]; |
538 | a594cfbf | bellard | dev = port->port.dev; |
539 | 891fb2cd | Gerd Hoffmann | if (dev && dev->attached) {
|
540 | bb36d470 | bellard | /* port reset */
|
541 | 5fafdf24 | ths | if ( (val & UHCI_PORT_RESET) &&
|
542 | bb36d470 | bellard | !(port->ctrl & UHCI_PORT_RESET) ) { |
543 | d28f4e2d | Gerd Hoffmann | usb_device_reset(dev); |
544 | bb36d470 | bellard | } |
545 | bb36d470 | bellard | } |
546 | 9159f679 | Gerd Hoffmann | port->ctrl &= UHCI_PORT_READ_ONLY; |
547 | 1cbdde90 | Hans de Goede | /* enabled may only be set if a device is connected */
|
548 | 1cbdde90 | Hans de Goede | if (!(port->ctrl & UHCI_PORT_CCS)) {
|
549 | 1cbdde90 | Hans de Goede | val &= ~UHCI_PORT_EN; |
550 | 1cbdde90 | Hans de Goede | } |
551 | 9159f679 | Gerd Hoffmann | port->ctrl |= (val & ~UHCI_PORT_READ_ONLY); |
552 | bb36d470 | bellard | /* some bits are reset when a '1' is written to them */
|
553 | 9159f679 | Gerd Hoffmann | port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR); |
554 | bb36d470 | bellard | } |
555 | bb36d470 | bellard | break;
|
556 | bb36d470 | bellard | } |
557 | bb36d470 | bellard | } |
558 | bb36d470 | bellard | |
559 | 89eb147c | Gerd Hoffmann | static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) |
560 | bb36d470 | bellard | { |
561 | bb36d470 | bellard | UHCIState *s = opaque; |
562 | bb36d470 | bellard | uint32_t val; |
563 | bb36d470 | bellard | |
564 | bb36d470 | bellard | switch(addr) {
|
565 | bb36d470 | bellard | case 0x00: |
566 | bb36d470 | bellard | val = s->cmd; |
567 | bb36d470 | bellard | break;
|
568 | bb36d470 | bellard | case 0x02: |
569 | bb36d470 | bellard | val = s->status; |
570 | bb36d470 | bellard | break;
|
571 | bb36d470 | bellard | case 0x04: |
572 | bb36d470 | bellard | val = s->intr; |
573 | bb36d470 | bellard | break;
|
574 | bb36d470 | bellard | case 0x06: |
575 | bb36d470 | bellard | val = s->frnum; |
576 | bb36d470 | bellard | break;
|
577 | 89eb147c | Gerd Hoffmann | case 0x08: |
578 | 89eb147c | Gerd Hoffmann | val = s->fl_base_addr & 0xffff;
|
579 | 89eb147c | Gerd Hoffmann | break;
|
580 | 89eb147c | Gerd Hoffmann | case 0x0a: |
581 | 89eb147c | Gerd Hoffmann | val = (s->fl_base_addr >> 16) & 0xffff; |
582 | 89eb147c | Gerd Hoffmann | break;
|
583 | 89eb147c | Gerd Hoffmann | case 0x0c: |
584 | 89eb147c | Gerd Hoffmann | val = s->sof_timing; |
585 | 89eb147c | Gerd Hoffmann | break;
|
586 | bb36d470 | bellard | case 0x10 ... 0x1f: |
587 | bb36d470 | bellard | { |
588 | bb36d470 | bellard | UHCIPort *port; |
589 | bb36d470 | bellard | int n;
|
590 | bb36d470 | bellard | n = (addr >> 1) & 7; |
591 | 5fafdf24 | ths | if (n >= NB_PORTS)
|
592 | bb36d470 | bellard | goto read_default;
|
593 | bb36d470 | bellard | port = &s->ports[n]; |
594 | bb36d470 | bellard | val = port->ctrl; |
595 | bb36d470 | bellard | } |
596 | bb36d470 | bellard | break;
|
597 | bb36d470 | bellard | default:
|
598 | bb36d470 | bellard | read_default:
|
599 | bb36d470 | bellard | val = 0xff7f; /* disabled port */ |
600 | bb36d470 | bellard | break;
|
601 | bb36d470 | bellard | } |
602 | 54f254f9 | aliguori | |
603 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_mmio_readw(addr, val); |
604 | 54f254f9 | aliguori | |
605 | bb36d470 | bellard | return val;
|
606 | bb36d470 | bellard | } |
607 | bb36d470 | bellard | |
608 | 96217e31 | ths | /* signal resume if controller suspended */
|
609 | 96217e31 | ths | static void uhci_resume (void *opaque) |
610 | 96217e31 | ths | { |
611 | 96217e31 | ths | UHCIState *s = (UHCIState *)opaque; |
612 | 96217e31 | ths | |
613 | 96217e31 | ths | if (!s)
|
614 | 96217e31 | ths | return;
|
615 | 96217e31 | ths | |
616 | 96217e31 | ths | if (s->cmd & UHCI_CMD_EGSM) {
|
617 | 96217e31 | ths | s->cmd |= UHCI_CMD_FGR; |
618 | 96217e31 | ths | s->status |= UHCI_STS_RD; |
619 | 96217e31 | ths | uhci_update_irq(s); |
620 | 96217e31 | ths | } |
621 | 96217e31 | ths | } |
622 | 96217e31 | ths | |
623 | 618c169b | Gerd Hoffmann | static void uhci_attach(USBPort *port1) |
624 | bb36d470 | bellard | { |
625 | bb36d470 | bellard | UHCIState *s = port1->opaque; |
626 | bb36d470 | bellard | UHCIPort *port = &s->ports[port1->index]; |
627 | bb36d470 | bellard | |
628 | 618c169b | Gerd Hoffmann | /* set connect status */
|
629 | 618c169b | Gerd Hoffmann | port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; |
630 | 61064870 | pbrook | |
631 | 618c169b | Gerd Hoffmann | /* update speed */
|
632 | 618c169b | Gerd Hoffmann | if (port->port.dev->speed == USB_SPEED_LOW) {
|
633 | 618c169b | Gerd Hoffmann | port->ctrl |= UHCI_PORT_LSDA; |
634 | bb36d470 | bellard | } else {
|
635 | 618c169b | Gerd Hoffmann | port->ctrl &= ~UHCI_PORT_LSDA; |
636 | 618c169b | Gerd Hoffmann | } |
637 | 96217e31 | ths | |
638 | 618c169b | Gerd Hoffmann | uhci_resume(s); |
639 | 618c169b | Gerd Hoffmann | } |
640 | 96217e31 | ths | |
641 | 618c169b | Gerd Hoffmann | static void uhci_detach(USBPort *port1) |
642 | 618c169b | Gerd Hoffmann | { |
643 | 618c169b | Gerd Hoffmann | UHCIState *s = port1->opaque; |
644 | 618c169b | Gerd Hoffmann | UHCIPort *port = &s->ports[port1->index]; |
645 | 618c169b | Gerd Hoffmann | |
646 | 4706ab6c | Hans de Goede | uhci_async_cancel_device(s, port1->dev); |
647 | 4706ab6c | Hans de Goede | |
648 | 618c169b | Gerd Hoffmann | /* set connect status */
|
649 | 618c169b | Gerd Hoffmann | if (port->ctrl & UHCI_PORT_CCS) {
|
650 | 618c169b | Gerd Hoffmann | port->ctrl &= ~UHCI_PORT_CCS; |
651 | 618c169b | Gerd Hoffmann | port->ctrl |= UHCI_PORT_CSC; |
652 | bb36d470 | bellard | } |
653 | 618c169b | Gerd Hoffmann | /* disable port */
|
654 | 618c169b | Gerd Hoffmann | if (port->ctrl & UHCI_PORT_EN) {
|
655 | 618c169b | Gerd Hoffmann | port->ctrl &= ~UHCI_PORT_EN; |
656 | 618c169b | Gerd Hoffmann | port->ctrl |= UHCI_PORT_ENC; |
657 | 618c169b | Gerd Hoffmann | } |
658 | 618c169b | Gerd Hoffmann | |
659 | 618c169b | Gerd Hoffmann | uhci_resume(s); |
660 | bb36d470 | bellard | } |
661 | bb36d470 | bellard | |
662 | 4706ab6c | Hans de Goede | static void uhci_child_detach(USBPort *port1, USBDevice *child) |
663 | 4706ab6c | Hans de Goede | { |
664 | 4706ab6c | Hans de Goede | UHCIState *s = port1->opaque; |
665 | 4706ab6c | Hans de Goede | |
666 | 4706ab6c | Hans de Goede | uhci_async_cancel_device(s, child); |
667 | 4706ab6c | Hans de Goede | } |
668 | 4706ab6c | Hans de Goede | |
669 | d47e59b8 | Hans de Goede | static void uhci_wakeup(USBPort *port1) |
670 | 9159f679 | Gerd Hoffmann | { |
671 | d47e59b8 | Hans de Goede | UHCIState *s = port1->opaque; |
672 | d47e59b8 | Hans de Goede | UHCIPort *port = &s->ports[port1->index]; |
673 | 9159f679 | Gerd Hoffmann | |
674 | 9159f679 | Gerd Hoffmann | if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) {
|
675 | 9159f679 | Gerd Hoffmann | port->ctrl |= UHCI_PORT_RD; |
676 | 9159f679 | Gerd Hoffmann | uhci_resume(s); |
677 | 9159f679 | Gerd Hoffmann | } |
678 | 9159f679 | Gerd Hoffmann | } |
679 | 9159f679 | Gerd Hoffmann | |
680 | 461700c1 | Gerd Hoffmann | static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr)
|
681 | bb36d470 | bellard | { |
682 | 461700c1 | Gerd Hoffmann | USBDevice *dev; |
683 | 461700c1 | Gerd Hoffmann | int i;
|
684 | 54f254f9 | aliguori | |
685 | 461700c1 | Gerd Hoffmann | for (i = 0; i < NB_PORTS; i++) { |
686 | 54f254f9 | aliguori | UHCIPort *port = &s->ports[i]; |
687 | 461700c1 | Gerd Hoffmann | if (!(port->ctrl & UHCI_PORT_EN)) {
|
688 | 461700c1 | Gerd Hoffmann | continue;
|
689 | 461700c1 | Gerd Hoffmann | } |
690 | 461700c1 | Gerd Hoffmann | dev = usb_find_device(&port->port, addr); |
691 | 461700c1 | Gerd Hoffmann | if (dev != NULL) { |
692 | 461700c1 | Gerd Hoffmann | return dev;
|
693 | 891fb2cd | Gerd Hoffmann | } |
694 | bb36d470 | bellard | } |
695 | 461700c1 | Gerd Hoffmann | return NULL; |
696 | bb36d470 | bellard | } |
697 | bb36d470 | bellard | |
698 | 963a68b5 | Hans de Goede | static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link) |
699 | 963a68b5 | Hans de Goede | { |
700 | 963a68b5 | Hans de Goede | pci_dma_read(&s->dev, link & ~0xf, td, sizeof(*td)); |
701 | 963a68b5 | Hans de Goede | le32_to_cpus(&td->link); |
702 | 963a68b5 | Hans de Goede | le32_to_cpus(&td->ctrl); |
703 | 963a68b5 | Hans de Goede | le32_to_cpus(&td->token); |
704 | 963a68b5 | Hans de Goede | le32_to_cpus(&td->buffer); |
705 | 963a68b5 | Hans de Goede | } |
706 | 963a68b5 | Hans de Goede | |
707 | faccca00 | Hans de Goede | static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr, |
708 | faccca00 | Hans de Goede | int status, uint32_t *int_mask)
|
709 | faccca00 | Hans de Goede | { |
710 | faccca00 | Hans de Goede | uint32_t queue_token = uhci_queue_token(td); |
711 | faccca00 | Hans de Goede | int ret;
|
712 | faccca00 | Hans de Goede | |
713 | faccca00 | Hans de Goede | switch (status) {
|
714 | faccca00 | Hans de Goede | case USB_RET_NAK:
|
715 | faccca00 | Hans de Goede | td->ctrl |= TD_CTRL_NAK; |
716 | faccca00 | Hans de Goede | return TD_RESULT_NEXT_QH;
|
717 | faccca00 | Hans de Goede | |
718 | faccca00 | Hans de Goede | case USB_RET_STALL:
|
719 | faccca00 | Hans de Goede | td->ctrl |= TD_CTRL_STALL; |
720 | faccca00 | Hans de Goede | trace_usb_uhci_packet_complete_stall(queue_token, td_addr); |
721 | faccca00 | Hans de Goede | ret = TD_RESULT_NEXT_QH; |
722 | faccca00 | Hans de Goede | break;
|
723 | faccca00 | Hans de Goede | |
724 | faccca00 | Hans de Goede | case USB_RET_BABBLE:
|
725 | faccca00 | Hans de Goede | td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; |
726 | faccca00 | Hans de Goede | /* frame interrupted */
|
727 | faccca00 | Hans de Goede | trace_usb_uhci_packet_complete_babble(queue_token, td_addr); |
728 | faccca00 | Hans de Goede | ret = TD_RESULT_STOP_FRAME; |
729 | faccca00 | Hans de Goede | break;
|
730 | faccca00 | Hans de Goede | |
731 | faccca00 | Hans de Goede | case USB_RET_IOERROR:
|
732 | faccca00 | Hans de Goede | case USB_RET_NODEV:
|
733 | faccca00 | Hans de Goede | default:
|
734 | faccca00 | Hans de Goede | td->ctrl |= TD_CTRL_TIMEOUT; |
735 | faccca00 | Hans de Goede | td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT);
|
736 | faccca00 | Hans de Goede | trace_usb_uhci_packet_complete_error(queue_token, td_addr); |
737 | faccca00 | Hans de Goede | ret = TD_RESULT_NEXT_QH; |
738 | faccca00 | Hans de Goede | break;
|
739 | faccca00 | Hans de Goede | } |
740 | faccca00 | Hans de Goede | |
741 | faccca00 | Hans de Goede | td->ctrl &= ~TD_CTRL_ACTIVE; |
742 | faccca00 | Hans de Goede | s->status |= UHCI_STS_USBERR; |
743 | faccca00 | Hans de Goede | if (td->ctrl & TD_CTRL_IOC) {
|
744 | faccca00 | Hans de Goede | *int_mask |= 0x01;
|
745 | faccca00 | Hans de Goede | } |
746 | faccca00 | Hans de Goede | uhci_update_irq(s); |
747 | faccca00 | Hans de Goede | return ret;
|
748 | faccca00 | Hans de Goede | } |
749 | faccca00 | Hans de Goede | |
750 | 54f254f9 | aliguori | static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) |
751 | bb36d470 | bellard | { |
752 | 9a77a0f5 | Hans de Goede | int len = 0, max_len; |
753 | bb36d470 | bellard | uint8_t pid; |
754 | bb36d470 | bellard | |
755 | 54f254f9 | aliguori | max_len = ((td->token >> 21) + 1) & 0x7ff; |
756 | 54f254f9 | aliguori | pid = td->token & 0xff;
|
757 | 54f254f9 | aliguori | |
758 | 54f254f9 | aliguori | if (td->ctrl & TD_CTRL_IOS)
|
759 | 54f254f9 | aliguori | td->ctrl &= ~TD_CTRL_ACTIVE; |
760 | bb36d470 | bellard | |
761 | 9a77a0f5 | Hans de Goede | if (async->packet.status != USB_RET_SUCCESS) {
|
762 | 9a77a0f5 | Hans de Goede | return uhci_handle_td_error(s, td, async->td_addr,
|
763 | 9a77a0f5 | Hans de Goede | async->packet.status, int_mask); |
764 | faccca00 | Hans de Goede | } |
765 | b9dc033c | balrog | |
766 | 9a77a0f5 | Hans de Goede | len = async->packet.actual_length; |
767 | 54f254f9 | aliguori | td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); |
768 | 54f254f9 | aliguori | |
769 | 54f254f9 | aliguori | /* The NAK bit may have been set by a previous frame, so clear it
|
770 | 54f254f9 | aliguori | here. The docs are somewhat unclear, but win2k relies on this
|
771 | 54f254f9 | aliguori | behavior. */
|
772 | 54f254f9 | aliguori | td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK); |
773 | 5bd2c0d7 | Paul Brook | if (td->ctrl & TD_CTRL_IOC)
|
774 | 5bd2c0d7 | Paul Brook | *int_mask |= 0x01;
|
775 | 54f254f9 | aliguori | |
776 | 54f254f9 | aliguori | if (pid == USB_TOKEN_IN) {
|
777 | 54f254f9 | aliguori | if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
|
778 | bb36d470 | bellard | *int_mask |= 0x02;
|
779 | bb36d470 | bellard | /* short packet: do not update QH */
|
780 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_packet_complete_shortxfer(async->queue->token, |
781 | 1f250cc7 | Hans de Goede | async->td_addr); |
782 | 60e1b2a6 | Gerd Hoffmann | return TD_RESULT_NEXT_QH;
|
783 | bb36d470 | bellard | } |
784 | 54f254f9 | aliguori | } |
785 | 54f254f9 | aliguori | |
786 | 54f254f9 | aliguori | /* success */
|
787 | 1f250cc7 | Hans de Goede | trace_usb_uhci_packet_complete_success(async->queue->token, |
788 | 1f250cc7 | Hans de Goede | async->td_addr); |
789 | 60e1b2a6 | Gerd Hoffmann | return TD_RESULT_COMPLETE;
|
790 | bb36d470 | bellard | } |
791 | bb36d470 | bellard | |
792 | 66a08cbe | Hans de Goede | static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, |
793 | a4f30cd7 | Hans de Goede | UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask) |
794 | 54f254f9 | aliguori | { |
795 | 9a77a0f5 | Hans de Goede | int ret, max_len;
|
796 | 6ba43f1f | Hans de Goede | bool spd;
|
797 | a4f30cd7 | Hans de Goede | bool queuing = (q != NULL); |
798 | 11d15e40 | Hans de Goede | uint8_t pid = td->token & 0xff;
|
799 | 8c75a899 | Hans de Goede | UHCIAsync *async = uhci_async_find_td(s, td_addr); |
800 | 8c75a899 | Hans de Goede | |
801 | 8c75a899 | Hans de Goede | if (async) {
|
802 | 8c75a899 | Hans de Goede | if (uhci_queue_verify(async->queue, qh_addr, td, td_addr, queuing)) {
|
803 | 8c75a899 | Hans de Goede | assert(q == NULL || q == async->queue);
|
804 | 8c75a899 | Hans de Goede | q = async->queue; |
805 | 8c75a899 | Hans de Goede | } else {
|
806 | 8c75a899 | Hans de Goede | uhci_queue_free(async->queue, "guest re-used pending td");
|
807 | 8c75a899 | Hans de Goede | async = NULL;
|
808 | 8c75a899 | Hans de Goede | } |
809 | 8c75a899 | Hans de Goede | } |
810 | 54f254f9 | aliguori | |
811 | 66a08cbe | Hans de Goede | if (q == NULL) { |
812 | 66a08cbe | Hans de Goede | q = uhci_queue_find(s, td); |
813 | 66a08cbe | Hans de Goede | if (q && !uhci_queue_verify(q, qh_addr, td, td_addr, queuing)) {
|
814 | 66a08cbe | Hans de Goede | uhci_queue_free(q, "guest re-used qh");
|
815 | 66a08cbe | Hans de Goede | q = NULL;
|
816 | 66a08cbe | Hans de Goede | } |
817 | 66a08cbe | Hans de Goede | } |
818 | 66a08cbe | Hans de Goede | |
819 | 3905097e | Hans de Goede | if (q) {
|
820 | 475443cf | Hans de Goede | q->valid = QH_VALID; |
821 | 3905097e | Hans de Goede | } |
822 | 3905097e | Hans de Goede | |
823 | 54f254f9 | aliguori | /* Is active ? */
|
824 | 883bca77 | Hans de Goede | if (!(td->ctrl & TD_CTRL_ACTIVE)) {
|
825 | 420ca987 | Hans de Goede | if (async) {
|
826 | 420ca987 | Hans de Goede | /* Guest marked a pending td non-active, cancel the queue */
|
827 | 420ca987 | Hans de Goede | uhci_queue_free(async->queue, "pending td non-active");
|
828 | 420ca987 | Hans de Goede | } |
829 | 883bca77 | Hans de Goede | /*
|
830 | 883bca77 | Hans de Goede | * ehci11d spec page 22: "Even if the Active bit in the TD is already
|
831 | 883bca77 | Hans de Goede | * cleared when the TD is fetched ... an IOC interrupt is generated"
|
832 | 883bca77 | Hans de Goede | */
|
833 | 883bca77 | Hans de Goede | if (td->ctrl & TD_CTRL_IOC) {
|
834 | 883bca77 | Hans de Goede | *int_mask |= 0x01;
|
835 | 883bca77 | Hans de Goede | } |
836 | 60e1b2a6 | Gerd Hoffmann | return TD_RESULT_NEXT_QH;
|
837 | 883bca77 | Hans de Goede | } |
838 | 54f254f9 | aliguori | |
839 | 54f254f9 | aliguori | if (async) {
|
840 | ee008ba6 | Gerd Hoffmann | if (queuing) {
|
841 | ee008ba6 | Gerd Hoffmann | /* we are busy filling the queue, we are not prepared
|
842 | ee008ba6 | Gerd Hoffmann | to consume completed packages then, just leave them
|
843 | ee008ba6 | Gerd Hoffmann | in async state */
|
844 | ee008ba6 | Gerd Hoffmann | return TD_RESULT_ASYNC_CONT;
|
845 | ee008ba6 | Gerd Hoffmann | } |
846 | 8928c9c4 | Hans de Goede | if (!async->done) {
|
847 | 8928c9c4 | Hans de Goede | UHCI_TD last_td; |
848 | 8928c9c4 | Hans de Goede | UHCIAsync *last = QTAILQ_LAST(&async->queue->asyncs, asyncs_head); |
849 | 8928c9c4 | Hans de Goede | /*
|
850 | 8928c9c4 | Hans de Goede | * While we are waiting for the current td to complete, the guest
|
851 | 8928c9c4 | Hans de Goede | * may have added more tds to the queue. Note we re-read the td
|
852 | 8928c9c4 | Hans de Goede | * rather then caching it, as we want to see guest made changes!
|
853 | 8928c9c4 | Hans de Goede | */
|
854 | 8928c9c4 | Hans de Goede | uhci_read_td(s, &last_td, last->td_addr); |
855 | 8928c9c4 | Hans de Goede | uhci_queue_fill(async->queue, &last_td); |
856 | 54f254f9 | aliguori | |
857 | 8928c9c4 | Hans de Goede | return TD_RESULT_ASYNC_CONT;
|
858 | 8928c9c4 | Hans de Goede | } |
859 | f8af1e88 | Gerd Hoffmann | uhci_async_unlink(async); |
860 | 54f254f9 | aliguori | goto done;
|
861 | 54f254f9 | aliguori | } |
862 | 54f254f9 | aliguori | |
863 | 88793816 | Hans de Goede | if (s->completions_only) {
|
864 | 88793816 | Hans de Goede | return TD_RESULT_ASYNC_CONT;
|
865 | 88793816 | Hans de Goede | } |
866 | 88793816 | Hans de Goede | |
867 | 54f254f9 | aliguori | /* Allocate new packet */
|
868 | a4f30cd7 | Hans de Goede | if (q == NULL) { |
869 | 11d15e40 | Hans de Goede | USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f); |
870 | 11d15e40 | Hans de Goede | USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); |
871 | 7f102ebe | Hans de Goede | |
872 | 7f102ebe | Hans de Goede | if (ep == NULL) { |
873 | 7f102ebe | Hans de Goede | return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV,
|
874 | 7f102ebe | Hans de Goede | int_mask); |
875 | 7f102ebe | Hans de Goede | } |
876 | 66a08cbe | Hans de Goede | q = uhci_queue_new(s, qh_addr, td, ep); |
877 | a4f30cd7 | Hans de Goede | } |
878 | a4f30cd7 | Hans de Goede | async = uhci_async_alloc(q, td_addr); |
879 | 54f254f9 | aliguori | |
880 | 54f254f9 | aliguori | max_len = ((td->token >> 21) + 1) & 0x7ff; |
881 | 6ba43f1f | Hans de Goede | spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
|
882 | 11d15e40 | Hans de Goede | usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd, |
883 | a6fb2ddb | Hans de Goede | (td->ctrl & TD_CTRL_IOC) != 0);
|
884 | df5e66ee | Gerd Hoffmann | qemu_sglist_add(&async->sgl, td->buffer, max_len); |
885 | df5e66ee | Gerd Hoffmann | usb_packet_map(&async->packet, &async->sgl); |
886 | 54f254f9 | aliguori | |
887 | 54f254f9 | aliguori | switch(pid) {
|
888 | 54f254f9 | aliguori | case USB_TOKEN_OUT:
|
889 | 54f254f9 | aliguori | case USB_TOKEN_SETUP:
|
890 | 9a77a0f5 | Hans de Goede | usb_handle_packet(q->ep->dev, &async->packet); |
891 | 9a77a0f5 | Hans de Goede | if (async->packet.status == USB_RET_SUCCESS) {
|
892 | 9a77a0f5 | Hans de Goede | async->packet.actual_length = max_len; |
893 | 9a77a0f5 | Hans de Goede | } |
894 | 54f254f9 | aliguori | break;
|
895 | 54f254f9 | aliguori | |
896 | 54f254f9 | aliguori | case USB_TOKEN_IN:
|
897 | 9a77a0f5 | Hans de Goede | usb_handle_packet(q->ep->dev, &async->packet); |
898 | 54f254f9 | aliguori | break;
|
899 | 54f254f9 | aliguori | |
900 | 54f254f9 | aliguori | default:
|
901 | 54f254f9 | aliguori | /* invalid pid : frame interrupted */
|
902 | 00a0770d | Hans de Goede | usb_packet_unmap(&async->packet, &async->sgl); |
903 | f8af1e88 | Gerd Hoffmann | uhci_async_free(async); |
904 | 54f254f9 | aliguori | s->status |= UHCI_STS_HCPERR; |
905 | 54f254f9 | aliguori | uhci_update_irq(s); |
906 | 60e1b2a6 | Gerd Hoffmann | return TD_RESULT_STOP_FRAME;
|
907 | 54f254f9 | aliguori | } |
908 | 9a77a0f5 | Hans de Goede | |
909 | 9a77a0f5 | Hans de Goede | if (async->packet.status == USB_RET_ASYNC) {
|
910 | f8af1e88 | Gerd Hoffmann | uhci_async_link(async); |
911 | a4f30cd7 | Hans de Goede | if (!queuing) {
|
912 | 11d15e40 | Hans de Goede | uhci_queue_fill(q, td); |
913 | a4f30cd7 | Hans de Goede | } |
914 | 4efe4ef3 | Gerd Hoffmann | return TD_RESULT_ASYNC_START;
|
915 | 54f254f9 | aliguori | } |
916 | 54f254f9 | aliguori | |
917 | 54f254f9 | aliguori | done:
|
918 | 9a77a0f5 | Hans de Goede | ret = uhci_complete_td(s, td, async, int_mask); |
919 | e2f89926 | David Gibson | usb_packet_unmap(&async->packet, &async->sgl); |
920 | f8af1e88 | Gerd Hoffmann | uhci_async_free(async); |
921 | 9a77a0f5 | Hans de Goede | return ret;
|
922 | 54f254f9 | aliguori | } |
923 | 54f254f9 | aliguori | |
924 | d47e59b8 | Hans de Goede | static void uhci_async_complete(USBPort *port, USBPacket *packet) |
925 | 4d611c9a | pbrook | { |
926 | 7b5a44c5 | Gerd Hoffmann | UHCIAsync *async = container_of(packet, UHCIAsync, packet); |
927 | f8af1e88 | Gerd Hoffmann | UHCIState *s = async->queue->uhci; |
928 | 54f254f9 | aliguori | |
929 | 9a77a0f5 | Hans de Goede | if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
|
930 | 0cae7b1a | Hans de Goede | uhci_async_cancel(async); |
931 | 0cae7b1a | Hans de Goede | return;
|
932 | 0cae7b1a | Hans de Goede | } |
933 | 0cae7b1a | Hans de Goede | |
934 | 5b352ed5 | Hans de Goede | async->done = 1;
|
935 | 88793816 | Hans de Goede | /* Force processing of this packet *now*, needed for migration */
|
936 | 88793816 | Hans de Goede | s->completions_only = true;
|
937 | 88793816 | Hans de Goede | qemu_bh_schedule(s->bh); |
938 | 54f254f9 | aliguori | } |
939 | 54f254f9 | aliguori | |
940 | 54f254f9 | aliguori | static int is_valid(uint32_t link) |
941 | 54f254f9 | aliguori | { |
942 | 54f254f9 | aliguori | return (link & 1) == 0; |
943 | 54f254f9 | aliguori | } |
944 | 54f254f9 | aliguori | |
945 | 54f254f9 | aliguori | static int is_qh(uint32_t link) |
946 | 54f254f9 | aliguori | { |
947 | 54f254f9 | aliguori | return (link & 2) != 0; |
948 | 54f254f9 | aliguori | } |
949 | 54f254f9 | aliguori | |
950 | 54f254f9 | aliguori | static int depth_first(uint32_t link) |
951 | 54f254f9 | aliguori | { |
952 | 54f254f9 | aliguori | return (link & 4) != 0; |
953 | 54f254f9 | aliguori | } |
954 | 54f254f9 | aliguori | |
955 | 54f254f9 | aliguori | /* QH DB used for detecting QH loops */
|
956 | 54f254f9 | aliguori | #define UHCI_MAX_QUEUES 128 |
957 | 54f254f9 | aliguori | typedef struct { |
958 | 54f254f9 | aliguori | uint32_t addr[UHCI_MAX_QUEUES]; |
959 | 54f254f9 | aliguori | int count;
|
960 | 54f254f9 | aliguori | } QhDb; |
961 | 54f254f9 | aliguori | |
962 | 54f254f9 | aliguori | static void qhdb_reset(QhDb *db) |
963 | 54f254f9 | aliguori | { |
964 | 54f254f9 | aliguori | db->count = 0;
|
965 | 54f254f9 | aliguori | } |
966 | 54f254f9 | aliguori | |
967 | 54f254f9 | aliguori | /* Add QH to DB. Returns 1 if already present or DB is full. */
|
968 | 54f254f9 | aliguori | static int qhdb_insert(QhDb *db, uint32_t addr) |
969 | 54f254f9 | aliguori | { |
970 | 54f254f9 | aliguori | int i;
|
971 | 54f254f9 | aliguori | for (i = 0; i < db->count; i++) |
972 | 54f254f9 | aliguori | if (db->addr[i] == addr)
|
973 | 54f254f9 | aliguori | return 1; |
974 | 54f254f9 | aliguori | |
975 | 54f254f9 | aliguori | if (db->count >= UHCI_MAX_QUEUES)
|
976 | 54f254f9 | aliguori | return 1; |
977 | 54f254f9 | aliguori | |
978 | 54f254f9 | aliguori | db->addr[db->count++] = addr; |
979 | 54f254f9 | aliguori | return 0; |
980 | 54f254f9 | aliguori | } |
981 | 54f254f9 | aliguori | |
982 | 11d15e40 | Hans de Goede | static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td) |
983 | 5a248289 | Gerd Hoffmann | { |
984 | 5a248289 | Gerd Hoffmann | uint32_t int_mask = 0;
|
985 | 5a248289 | Gerd Hoffmann | uint32_t plink = td->link; |
986 | 5a248289 | Gerd Hoffmann | UHCI_TD ptd; |
987 | 5a248289 | Gerd Hoffmann | int ret;
|
988 | 5a248289 | Gerd Hoffmann | |
989 | 6ba43f1f | Hans de Goede | while (is_valid(plink)) {
|
990 | a4f30cd7 | Hans de Goede | uhci_read_td(q->uhci, &ptd, plink); |
991 | 5a248289 | Gerd Hoffmann | if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
|
992 | 5a248289 | Gerd Hoffmann | break;
|
993 | 5a248289 | Gerd Hoffmann | } |
994 | a4f30cd7 | Hans de Goede | if (uhci_queue_token(&ptd) != q->token) {
|
995 | 5a248289 | Gerd Hoffmann | break;
|
996 | 5a248289 | Gerd Hoffmann | } |
997 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
|
998 | 66a08cbe | Hans de Goede | ret = uhci_handle_td(q->uhci, q, q->qh_addr, &ptd, plink, &int_mask); |
999 | 52b0fecd | Gerd Hoffmann | if (ret == TD_RESULT_ASYNC_CONT) {
|
1000 | 52b0fecd | Gerd Hoffmann | break;
|
1001 | 52b0fecd | Gerd Hoffmann | } |
1002 | 4efe4ef3 | Gerd Hoffmann | assert(ret == TD_RESULT_ASYNC_START); |
1003 | 5a248289 | Gerd Hoffmann | assert(int_mask == 0);
|
1004 | 5a248289 | Gerd Hoffmann | plink = ptd.link; |
1005 | 5a248289 | Gerd Hoffmann | } |
1006 | 11d15e40 | Hans de Goede | usb_device_flush_ep_queue(q->ep->dev, q->ep); |
1007 | 5a248289 | Gerd Hoffmann | } |
1008 | 5a248289 | Gerd Hoffmann | |
1009 | 54f254f9 | aliguori | static void uhci_process_frame(UHCIState *s) |
1010 | 54f254f9 | aliguori | { |
1011 | 54f254f9 | aliguori | uint32_t frame_addr, link, old_td_ctrl, val, int_mask; |
1012 | 4aed20e2 | Gerd Hoffmann | uint32_t curr_qh, td_count = 0;
|
1013 | 54f254f9 | aliguori | int cnt, ret;
|
1014 | 4d611c9a | pbrook | UHCI_TD td; |
1015 | 54f254f9 | aliguori | UHCI_QH qh; |
1016 | 54f254f9 | aliguori | QhDb qhdb; |
1017 | 4d611c9a | pbrook | |
1018 | 54f254f9 | aliguori | frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); |
1019 | 54f254f9 | aliguori | |
1020 | 9fe2fd67 | David Gibson | pci_dma_read(&s->dev, frame_addr, &link, 4);
|
1021 | 54f254f9 | aliguori | le32_to_cpus(&link); |
1022 | b9dc033c | balrog | |
1023 | 54f254f9 | aliguori | int_mask = 0;
|
1024 | 54f254f9 | aliguori | curr_qh = 0;
|
1025 | 54f254f9 | aliguori | |
1026 | 54f254f9 | aliguori | qhdb_reset(&qhdb); |
1027 | 54f254f9 | aliguori | |
1028 | 54f254f9 | aliguori | for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
|
1029 | 88793816 | Hans de Goede | if (!s->completions_only && s->frame_bytes >= s->frame_bandwidth) {
|
1030 | 4aed20e2 | Gerd Hoffmann | /* We've reached the usb 1.1 bandwidth, which is
|
1031 | 4aed20e2 | Gerd Hoffmann | 1280 bytes/frame, stop processing */
|
1032 | 4aed20e2 | Gerd Hoffmann | trace_usb_uhci_frame_stop_bandwidth(); |
1033 | 4aed20e2 | Gerd Hoffmann | break;
|
1034 | 4aed20e2 | Gerd Hoffmann | } |
1035 | 54f254f9 | aliguori | if (is_qh(link)) {
|
1036 | 54f254f9 | aliguori | /* QH */
|
1037 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_qh_load(link & ~0xf);
|
1038 | 54f254f9 | aliguori | |
1039 | 54f254f9 | aliguori | if (qhdb_insert(&qhdb, link)) {
|
1040 | 54f254f9 | aliguori | /*
|
1041 | 54f254f9 | aliguori | * We're going in circles. Which is not a bug because
|
1042 | 3200d108 | Gerd Hoffmann | * HCD is allowed to do that as part of the BW management.
|
1043 | 3200d108 | Gerd Hoffmann | *
|
1044 | 4aed20e2 | Gerd Hoffmann | * Stop processing here if no transaction has been done
|
1045 | 4aed20e2 | Gerd Hoffmann | * since we've been here last time.
|
1046 | 54f254f9 | aliguori | */
|
1047 | 3200d108 | Gerd Hoffmann | if (td_count == 0) { |
1048 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_frame_loop_stop_idle(); |
1049 | 3200d108 | Gerd Hoffmann | break;
|
1050 | 3200d108 | Gerd Hoffmann | } else {
|
1051 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_frame_loop_continue(); |
1052 | 3200d108 | Gerd Hoffmann | td_count = 0;
|
1053 | 3200d108 | Gerd Hoffmann | qhdb_reset(&qhdb); |
1054 | 3200d108 | Gerd Hoffmann | qhdb_insert(&qhdb, link); |
1055 | 3200d108 | Gerd Hoffmann | } |
1056 | 54f254f9 | aliguori | } |
1057 | 54f254f9 | aliguori | |
1058 | 9fe2fd67 | David Gibson | pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh)); |
1059 | 54f254f9 | aliguori | le32_to_cpus(&qh.link); |
1060 | 54f254f9 | aliguori | le32_to_cpus(&qh.el_link); |
1061 | 54f254f9 | aliguori | |
1062 | 54f254f9 | aliguori | if (!is_valid(qh.el_link)) {
|
1063 | 54f254f9 | aliguori | /* QH w/o elements */
|
1064 | 54f254f9 | aliguori | curr_qh = 0;
|
1065 | 54f254f9 | aliguori | link = qh.link; |
1066 | 54f254f9 | aliguori | } else {
|
1067 | 54f254f9 | aliguori | /* QH with elements */
|
1068 | 54f254f9 | aliguori | curr_qh = link; |
1069 | 54f254f9 | aliguori | link = qh.el_link; |
1070 | 54f254f9 | aliguori | } |
1071 | 54f254f9 | aliguori | continue;
|
1072 | 54f254f9 | aliguori | } |
1073 | 54f254f9 | aliguori | |
1074 | 54f254f9 | aliguori | /* TD */
|
1075 | 963a68b5 | Hans de Goede | uhci_read_td(s, &td, link); |
1076 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token); |
1077 | 54f254f9 | aliguori | |
1078 | 54f254f9 | aliguori | old_td_ctrl = td.ctrl; |
1079 | 66a08cbe | Hans de Goede | ret = uhci_handle_td(s, NULL, curr_qh, &td, link, &int_mask);
|
1080 | b9dc033c | balrog | if (old_td_ctrl != td.ctrl) {
|
1081 | 54f254f9 | aliguori | /* update the status bits of the TD */
|
1082 | b9dc033c | balrog | val = cpu_to_le32(td.ctrl); |
1083 | 9fe2fd67 | David Gibson | pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); |
1084 | b9dc033c | balrog | } |
1085 | 54f254f9 | aliguori | |
1086 | 971a5a40 | Gerd Hoffmann | switch (ret) {
|
1087 | 60e1b2a6 | Gerd Hoffmann | case TD_RESULT_STOP_FRAME: /* interrupted frame */ |
1088 | 971a5a40 | Gerd Hoffmann | goto out;
|
1089 | b9dc033c | balrog | |
1090 | 60e1b2a6 | Gerd Hoffmann | case TD_RESULT_NEXT_QH:
|
1091 | 4efe4ef3 | Gerd Hoffmann | case TD_RESULT_ASYNC_CONT:
|
1092 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_td_nextqh(curr_qh & ~0xf, link & ~0xf); |
1093 | 54f254f9 | aliguori | link = curr_qh ? qh.link : td.link; |
1094 | 54f254f9 | aliguori | continue;
|
1095 | 54f254f9 | aliguori | |
1096 | 4efe4ef3 | Gerd Hoffmann | case TD_RESULT_ASYNC_START:
|
1097 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf); |
1098 | 971a5a40 | Gerd Hoffmann | link = curr_qh ? qh.link : td.link; |
1099 | 971a5a40 | Gerd Hoffmann | continue;
|
1100 | 54f254f9 | aliguori | |
1101 | 60e1b2a6 | Gerd Hoffmann | case TD_RESULT_COMPLETE:
|
1102 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf); |
1103 | 971a5a40 | Gerd Hoffmann | link = td.link; |
1104 | 971a5a40 | Gerd Hoffmann | td_count++; |
1105 | 4aed20e2 | Gerd Hoffmann | s->frame_bytes += (td.ctrl & 0x7ff) + 1; |
1106 | 54f254f9 | aliguori | |
1107 | 971a5a40 | Gerd Hoffmann | if (curr_qh) {
|
1108 | 971a5a40 | Gerd Hoffmann | /* update QH element link */
|
1109 | 971a5a40 | Gerd Hoffmann | qh.el_link = link; |
1110 | 971a5a40 | Gerd Hoffmann | val = cpu_to_le32(qh.el_link); |
1111 | 971a5a40 | Gerd Hoffmann | pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); |
1112 | 54f254f9 | aliguori | |
1113 | 971a5a40 | Gerd Hoffmann | if (!depth_first(link)) {
|
1114 | 971a5a40 | Gerd Hoffmann | /* done with this QH */
|
1115 | 971a5a40 | Gerd Hoffmann | curr_qh = 0;
|
1116 | 971a5a40 | Gerd Hoffmann | link = qh.link; |
1117 | 971a5a40 | Gerd Hoffmann | } |
1118 | 54f254f9 | aliguori | } |
1119 | 971a5a40 | Gerd Hoffmann | break;
|
1120 | 971a5a40 | Gerd Hoffmann | |
1121 | 971a5a40 | Gerd Hoffmann | default:
|
1122 | 971a5a40 | Gerd Hoffmann | assert(!"unknown return code");
|
1123 | 4d611c9a | pbrook | } |
1124 | 54f254f9 | aliguori | |
1125 | 54f254f9 | aliguori | /* go to the next entry */
|
1126 | 4d611c9a | pbrook | } |
1127 | 54f254f9 | aliguori | |
1128 | 971a5a40 | Gerd Hoffmann | out:
|
1129 | 8e65b7c0 | David S. Ahern | s->pending_int_mask |= int_mask; |
1130 | 4d611c9a | pbrook | } |
1131 | 4d611c9a | pbrook | |
1132 | 9a16c595 | Gerd Hoffmann | static void uhci_bh(void *opaque) |
1133 | 9a16c595 | Gerd Hoffmann | { |
1134 | 9a16c595 | Gerd Hoffmann | UHCIState *s = opaque; |
1135 | 9a16c595 | Gerd Hoffmann | uhci_process_frame(s); |
1136 | 9a16c595 | Gerd Hoffmann | } |
1137 | 9a16c595 | Gerd Hoffmann | |
1138 | bb36d470 | bellard | static void uhci_frame_timer(void *opaque) |
1139 | bb36d470 | bellard | { |
1140 | bb36d470 | bellard | UHCIState *s = opaque; |
1141 | f8f48b69 | Hans de Goede | uint64_t t_now, t_last_run; |
1142 | f8f48b69 | Hans de Goede | int i, frames;
|
1143 | f8f48b69 | Hans de Goede | const uint64_t frame_t = get_ticks_per_sec() / FRAME_TIMER_FREQ;
|
1144 | 8e65b7c0 | David S. Ahern | |
1145 | 88793816 | Hans de Goede | s->completions_only = false;
|
1146 | 9a16c595 | Gerd Hoffmann | qemu_bh_cancel(s->bh); |
1147 | bb36d470 | bellard | |
1148 | bb36d470 | bellard | if (!(s->cmd & UHCI_CMD_RS)) {
|
1149 | 54f254f9 | aliguori | /* Full stop */
|
1150 | 50dcc0f8 | Gerd Hoffmann | trace_usb_uhci_schedule_stop(); |
1151 | bb36d470 | bellard | qemu_del_timer(s->frame_timer); |
1152 | d9a528db | Gerd Hoffmann | uhci_async_cancel_all(s); |
1153 | 52328140 | bellard | /* set hchalted bit in status - UHCI11D 2.1.2 */
|
1154 | 52328140 | bellard | s->status |= UHCI_STS_HCHALTED; |
1155 | bb36d470 | bellard | return;
|
1156 | bb36d470 | bellard | } |
1157 | 54f254f9 | aliguori | |
1158 | f8f48b69 | Hans de Goede | /* We still store expire_time in our state, for migration */
|
1159 | f8f48b69 | Hans de Goede | t_last_run = s->expire_time - frame_t; |
1160 | f8f48b69 | Hans de Goede | t_now = qemu_get_clock_ns(vm_clock); |
1161 | 54f254f9 | aliguori | |
1162 | f8f48b69 | Hans de Goede | /* Process up to MAX_FRAMES_PER_TICK frames */
|
1163 | f8f48b69 | Hans de Goede | frames = (t_now - t_last_run) / frame_t; |
1164 | 9fdf7027 | Hans de Goede | if (frames > s->maxframes) {
|
1165 | 9fdf7027 | Hans de Goede | int skipped = frames - s->maxframes;
|
1166 | 9fdf7027 | Hans de Goede | s->expire_time += skipped * frame_t; |
1167 | 9fdf7027 | Hans de Goede | s->frnum = (s->frnum + skipped) & 0x7ff;
|
1168 | 9fdf7027 | Hans de Goede | frames -= skipped; |
1169 | 9fdf7027 | Hans de Goede | } |
1170 | f8f48b69 | Hans de Goede | if (frames > MAX_FRAMES_PER_TICK) {
|
1171 | f8f48b69 | Hans de Goede | frames = MAX_FRAMES_PER_TICK; |
1172 | f8f48b69 | Hans de Goede | } |
1173 | b9dc033c | balrog | |
1174 | f8f48b69 | Hans de Goede | for (i = 0; i < frames; i++) { |
1175 | f8f48b69 | Hans de Goede | s->frame_bytes = 0;
|
1176 | f8f48b69 | Hans de Goede | trace_usb_uhci_frame_start(s->frnum); |
1177 | f8f48b69 | Hans de Goede | uhci_async_validate_begin(s); |
1178 | f8f48b69 | Hans de Goede | uhci_process_frame(s); |
1179 | f8f48b69 | Hans de Goede | uhci_async_validate_end(s); |
1180 | f8f48b69 | Hans de Goede | /* The spec says frnum is the frame currently being processed, and
|
1181 | f8f48b69 | Hans de Goede | * the guest must look at frnum - 1 on interrupt, so inc frnum now */
|
1182 | f8f48b69 | Hans de Goede | s->frnum = (s->frnum + 1) & 0x7ff; |
1183 | f8f48b69 | Hans de Goede | s->expire_time += frame_t; |
1184 | f8f48b69 | Hans de Goede | } |
1185 | 719c130d | Hans de Goede | |
1186 | f8f48b69 | Hans de Goede | /* Complete the previous frame(s) */
|
1187 | 719c130d | Hans de Goede | if (s->pending_int_mask) {
|
1188 | 719c130d | Hans de Goede | s->status2 |= s->pending_int_mask; |
1189 | 719c130d | Hans de Goede | s->status |= UHCI_STS_USBINT; |
1190 | 719c130d | Hans de Goede | uhci_update_irq(s); |
1191 | 719c130d | Hans de Goede | } |
1192 | 719c130d | Hans de Goede | s->pending_int_mask = 0;
|
1193 | 719c130d | Hans de Goede | |
1194 | f8f48b69 | Hans de Goede | qemu_mod_timer(s->frame_timer, t_now + frame_t); |
1195 | bb36d470 | bellard | } |
1196 | bb36d470 | bellard | |
1197 | a03f66e4 | Avi Kivity | static const MemoryRegionOps uhci_ioport_ops = { |
1198 | 89eb147c | Gerd Hoffmann | .read = uhci_port_read, |
1199 | 89eb147c | Gerd Hoffmann | .write = uhci_port_write, |
1200 | 89eb147c | Gerd Hoffmann | .valid.min_access_size = 1,
|
1201 | 89eb147c | Gerd Hoffmann | .valid.max_access_size = 4,
|
1202 | 89eb147c | Gerd Hoffmann | .impl.min_access_size = 2,
|
1203 | 89eb147c | Gerd Hoffmann | .impl.max_access_size = 2,
|
1204 | 89eb147c | Gerd Hoffmann | .endianness = DEVICE_LITTLE_ENDIAN, |
1205 | a03f66e4 | Avi Kivity | }; |
1206 | bb36d470 | bellard | |
1207 | 0d86d2be | Gerd Hoffmann | static USBPortOps uhci_port_ops = {
|
1208 | 0d86d2be | Gerd Hoffmann | .attach = uhci_attach, |
1209 | 618c169b | Gerd Hoffmann | .detach = uhci_detach, |
1210 | 4706ab6c | Hans de Goede | .child_detach = uhci_child_detach, |
1211 | 9159f679 | Gerd Hoffmann | .wakeup = uhci_wakeup, |
1212 | 13a9a0d3 | Gerd Hoffmann | .complete = uhci_async_complete, |
1213 | 0d86d2be | Gerd Hoffmann | }; |
1214 | 0d86d2be | Gerd Hoffmann | |
1215 | 07771f6f | Gerd Hoffmann | static USBBusOps uhci_bus_ops = {
|
1216 | 07771f6f | Gerd Hoffmann | }; |
1217 | 07771f6f | Gerd Hoffmann | |
1218 | dc638fad | Isaku Yamahata | static int usb_uhci_common_initfn(PCIDevice *dev) |
1219 | bb36d470 | bellard | { |
1220 | 973002c1 | Gerd Hoffmann | PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); |
1221 | 8f3f90b0 | Gerd Hoffmann | UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class); |
1222 | dc638fad | Isaku Yamahata | UHCIState *s = DO_UPCAST(UHCIState, dev, dev); |
1223 | 6cf9b6f1 | Gerd Hoffmann | uint8_t *pci_conf = s->dev.config; |
1224 | bb36d470 | bellard | int i;
|
1225 | bb36d470 | bellard | |
1226 | db579e9e | Michael S. Tsirkin | pci_conf[PCI_CLASS_PROG] = 0x00;
|
1227 | db579e9e | Michael S. Tsirkin | /* TODO: reset value should be 0. */
|
1228 | e59d33a7 | Brad Hards | pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
|
1229 | 3b46e624 | ths | |
1230 | 8f3f90b0 | Gerd Hoffmann | s->irq_pin = u->info.irq_pin; |
1231 | 973002c1 | Gerd Hoffmann | pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1);
|
1232 | 973002c1 | Gerd Hoffmann | |
1233 | 35e4977f | Hans de Goede | if (s->masterbus) {
|
1234 | 35e4977f | Hans de Goede | USBPort *ports[NB_PORTS]; |
1235 | 35e4977f | Hans de Goede | for(i = 0; i < NB_PORTS; i++) { |
1236 | 35e4977f | Hans de Goede | ports[i] = &s->ports[i].port; |
1237 | 35e4977f | Hans de Goede | } |
1238 | 35e4977f | Hans de Goede | if (usb_register_companion(s->masterbus, ports, NB_PORTS,
|
1239 | 35e4977f | Hans de Goede | s->firstport, s, &uhci_port_ops, |
1240 | 35e4977f | Hans de Goede | USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
|
1241 | 35e4977f | Hans de Goede | return -1; |
1242 | 35e4977f | Hans de Goede | } |
1243 | 35e4977f | Hans de Goede | } else {
|
1244 | 35e4977f | Hans de Goede | usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); |
1245 | 35e4977f | Hans de Goede | for (i = 0; i < NB_PORTS; i++) { |
1246 | 35e4977f | Hans de Goede | usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, |
1247 | 35e4977f | Hans de Goede | USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); |
1248 | 35e4977f | Hans de Goede | } |
1249 | bb36d470 | bellard | } |
1250 | 9a16c595 | Gerd Hoffmann | s->bh = qemu_bh_new(uhci_bh, s); |
1251 | 74475455 | Paolo Bonzini | s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); |
1252 | 64e58fe5 | Juan Quintela | s->num_ports_vmstate = NB_PORTS; |
1253 | f8af1e88 | Gerd Hoffmann | QTAILQ_INIT(&s->queues); |
1254 | bb36d470 | bellard | |
1255 | a08d4367 | Jan Kiszka | qemu_register_reset(uhci_reset, s); |
1256 | bb36d470 | bellard | |
1257 | a03f66e4 | Avi Kivity | memory_region_init_io(&s->io_bar, &uhci_ioport_ops, s, "uhci", 0x20); |
1258 | 38ca0f6d | pbrook | /* Use region 4 for consistency with real hardware. BSD guests seem
|
1259 | 38ca0f6d | pbrook | to rely on this. */
|
1260 | e824b2cc | Avi Kivity | pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
|
1261 | 6f382b5e | aliguori | |
1262 | 6cf9b6f1 | Gerd Hoffmann | return 0; |
1263 | bb36d470 | bellard | } |
1264 | afcc3cdf | ths | |
1265 | 30235a54 | Huacai Chen | static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) |
1266 | 30235a54 | Huacai Chen | { |
1267 | 30235a54 | Huacai Chen | UHCIState *s = DO_UPCAST(UHCIState, dev, dev); |
1268 | 30235a54 | Huacai Chen | uint8_t *pci_conf = s->dev.config; |
1269 | 30235a54 | Huacai Chen | |
1270 | 30235a54 | Huacai Chen | /* USB misc control 1/2 */
|
1271 | 30235a54 | Huacai Chen | pci_set_long(pci_conf + 0x40,0x00001000); |
1272 | 30235a54 | Huacai Chen | /* PM capability */
|
1273 | 30235a54 | Huacai Chen | pci_set_long(pci_conf + 0x80,0x00020001); |
1274 | 30235a54 | Huacai Chen | /* USB legacy support */
|
1275 | 30235a54 | Huacai Chen | pci_set_long(pci_conf + 0xc0,0x00002000); |
1276 | 30235a54 | Huacai Chen | |
1277 | dc638fad | Isaku Yamahata | return usb_uhci_common_initfn(dev);
|
1278 | 30235a54 | Huacai Chen | } |
1279 | 30235a54 | Huacai Chen | |
1280 | f90c2bcd | Alex Williamson | static void usb_uhci_exit(PCIDevice *dev) |
1281 | a03f66e4 | Avi Kivity | { |
1282 | a03f66e4 | Avi Kivity | UHCIState *s = DO_UPCAST(UHCIState, dev, dev); |
1283 | a03f66e4 | Avi Kivity | |
1284 | a03f66e4 | Avi Kivity | memory_region_destroy(&s->io_bar); |
1285 | a03f66e4 | Avi Kivity | } |
1286 | a03f66e4 | Avi Kivity | |
1287 | 1b5a7570 | Gerd Hoffmann | static Property uhci_properties[] = {
|
1288 | 1b5a7570 | Gerd Hoffmann | DEFINE_PROP_STRING("masterbus", UHCIState, masterbus),
|
1289 | 1b5a7570 | Gerd Hoffmann | DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), |
1290 | 40141d12 | Gerd Hoffmann | DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), |
1291 | 9fdf7027 | Hans de Goede | DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), |
1292 | 1b5a7570 | Gerd Hoffmann | DEFINE_PROP_END_OF_LIST(), |
1293 | 1b5a7570 | Gerd Hoffmann | }; |
1294 | 1b5a7570 | Gerd Hoffmann | |
1295 | 2c2e8525 | Gerd Hoffmann | static void uhci_class_init(ObjectClass *klass, void *data) |
1296 | 40021f08 | Anthony Liguori | { |
1297 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
1298 | 40021f08 | Anthony Liguori | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
1299 | 8f3f90b0 | Gerd Hoffmann | UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); |
1300 | 2c2e8525 | Gerd Hoffmann | UHCIInfo *info = data; |
1301 | 2c2e8525 | Gerd Hoffmann | |
1302 | 2c2e8525 | Gerd Hoffmann | k->init = info->initfn ? info->initfn : usb_uhci_common_initfn; |
1303 | 2c2e8525 | Gerd Hoffmann | k->exit = info->unplug ? usb_uhci_exit : NULL;
|
1304 | 2c2e8525 | Gerd Hoffmann | k->vendor_id = info->vendor_id; |
1305 | 2c2e8525 | Gerd Hoffmann | k->device_id = info->device_id; |
1306 | 2c2e8525 | Gerd Hoffmann | k->revision = info->revision; |
1307 | 2c2e8525 | Gerd Hoffmann | k->class_id = PCI_CLASS_SERIAL_USB; |
1308 | 6c2d1c32 | Gerd Hoffmann | k->no_hotplug = 1;
|
1309 | 39bffca2 | Anthony Liguori | dc->vmsd = &vmstate_uhci; |
1310 | 39bffca2 | Anthony Liguori | dc->props = uhci_properties; |
1311 | 8f3f90b0 | Gerd Hoffmann | u->info = *info; |
1312 | 40021f08 | Anthony Liguori | } |
1313 | 40021f08 | Anthony Liguori | |
1314 | 2c2e8525 | Gerd Hoffmann | static UHCIInfo uhci_info[] = {
|
1315 | 2c2e8525 | Gerd Hoffmann | { |
1316 | 2c2e8525 | Gerd Hoffmann | .name = "piix3-usb-uhci",
|
1317 | 2c2e8525 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1318 | 2c2e8525 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, |
1319 | 2c2e8525 | Gerd Hoffmann | .revision = 0x01,
|
1320 | 8f3f90b0 | Gerd Hoffmann | .irq_pin = 3,
|
1321 | 2c2e8525 | Gerd Hoffmann | .unplug = true,
|
1322 | 2c2e8525 | Gerd Hoffmann | },{ |
1323 | 2c2e8525 | Gerd Hoffmann | .name = "piix4-usb-uhci",
|
1324 | 2c2e8525 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1325 | 2c2e8525 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, |
1326 | 2c2e8525 | Gerd Hoffmann | .revision = 0x01,
|
1327 | 8f3f90b0 | Gerd Hoffmann | .irq_pin = 3,
|
1328 | 2c2e8525 | Gerd Hoffmann | .unplug = true,
|
1329 | 2c2e8525 | Gerd Hoffmann | },{ |
1330 | 2c2e8525 | Gerd Hoffmann | .name = "vt82c686b-usb-uhci",
|
1331 | 2c2e8525 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_VIA, |
1332 | 2c2e8525 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_VIA_UHCI, |
1333 | 2c2e8525 | Gerd Hoffmann | .revision = 0x01,
|
1334 | 8f3f90b0 | Gerd Hoffmann | .irq_pin = 3,
|
1335 | 2c2e8525 | Gerd Hoffmann | .initfn = usb_uhci_vt82c686b_initfn, |
1336 | 2c2e8525 | Gerd Hoffmann | .unplug = true,
|
1337 | 2c2e8525 | Gerd Hoffmann | },{ |
1338 | 74625ea2 | Gerd Hoffmann | .name = "ich9-usb-uhci1", /* 00:1d.0 */ |
1339 | 2c2e8525 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1340 | 2c2e8525 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, |
1341 | 2c2e8525 | Gerd Hoffmann | .revision = 0x03,
|
1342 | 8f3f90b0 | Gerd Hoffmann | .irq_pin = 0,
|
1343 | 2c2e8525 | Gerd Hoffmann | .unplug = false,
|
1344 | 2c2e8525 | Gerd Hoffmann | },{ |
1345 | 74625ea2 | Gerd Hoffmann | .name = "ich9-usb-uhci2", /* 00:1d.1 */ |
1346 | 2c2e8525 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1347 | 2c2e8525 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, |
1348 | 2c2e8525 | Gerd Hoffmann | .revision = 0x03,
|
1349 | 8f3f90b0 | Gerd Hoffmann | .irq_pin = 1,
|
1350 | 2c2e8525 | Gerd Hoffmann | .unplug = false,
|
1351 | 2c2e8525 | Gerd Hoffmann | },{ |
1352 | 74625ea2 | Gerd Hoffmann | .name = "ich9-usb-uhci3", /* 00:1d.2 */ |
1353 | 2c2e8525 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1354 | 2c2e8525 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, |
1355 | 2c2e8525 | Gerd Hoffmann | .revision = 0x03,
|
1356 | 8f3f90b0 | Gerd Hoffmann | .irq_pin = 2,
|
1357 | 2c2e8525 | Gerd Hoffmann | .unplug = false,
|
1358 | 74625ea2 | Gerd Hoffmann | },{ |
1359 | 74625ea2 | Gerd Hoffmann | .name = "ich9-usb-uhci4", /* 00:1a.0 */ |
1360 | 74625ea2 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1361 | 74625ea2 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI4, |
1362 | 74625ea2 | Gerd Hoffmann | .revision = 0x03,
|
1363 | 74625ea2 | Gerd Hoffmann | .irq_pin = 0,
|
1364 | 74625ea2 | Gerd Hoffmann | .unplug = false,
|
1365 | 74625ea2 | Gerd Hoffmann | },{ |
1366 | 74625ea2 | Gerd Hoffmann | .name = "ich9-usb-uhci5", /* 00:1a.1 */ |
1367 | 74625ea2 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1368 | 74625ea2 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI5, |
1369 | 74625ea2 | Gerd Hoffmann | .revision = 0x03,
|
1370 | 74625ea2 | Gerd Hoffmann | .irq_pin = 1,
|
1371 | 74625ea2 | Gerd Hoffmann | .unplug = false,
|
1372 | 74625ea2 | Gerd Hoffmann | },{ |
1373 | 74625ea2 | Gerd Hoffmann | .name = "ich9-usb-uhci6", /* 00:1a.2 */ |
1374 | 74625ea2 | Gerd Hoffmann | .vendor_id = PCI_VENDOR_ID_INTEL, |
1375 | 74625ea2 | Gerd Hoffmann | .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI6, |
1376 | 74625ea2 | Gerd Hoffmann | .revision = 0x03,
|
1377 | 74625ea2 | Gerd Hoffmann | .irq_pin = 2,
|
1378 | 74625ea2 | Gerd Hoffmann | .unplug = false,
|
1379 | 2c2e8525 | Gerd Hoffmann | } |
1380 | 6cf9b6f1 | Gerd Hoffmann | }; |
1381 | afcc3cdf | ths | |
1382 | 83f7d43a | Andreas Färber | static void uhci_register_types(void) |
1383 | 6cf9b6f1 | Gerd Hoffmann | { |
1384 | 2c2e8525 | Gerd Hoffmann | TypeInfo uhci_type_info = { |
1385 | 2c2e8525 | Gerd Hoffmann | .parent = TYPE_PCI_DEVICE, |
1386 | 2c2e8525 | Gerd Hoffmann | .instance_size = sizeof(UHCIState),
|
1387 | 8f3f90b0 | Gerd Hoffmann | .class_size = sizeof(UHCIPCIDeviceClass),
|
1388 | 2c2e8525 | Gerd Hoffmann | .class_init = uhci_class_init, |
1389 | 2c2e8525 | Gerd Hoffmann | }; |
1390 | 2c2e8525 | Gerd Hoffmann | int i;
|
1391 | 2c2e8525 | Gerd Hoffmann | |
1392 | 2c2e8525 | Gerd Hoffmann | for (i = 0; i < ARRAY_SIZE(uhci_info); i++) { |
1393 | 2c2e8525 | Gerd Hoffmann | uhci_type_info.name = uhci_info[i].name; |
1394 | 2c2e8525 | Gerd Hoffmann | uhci_type_info.class_data = uhci_info + i; |
1395 | 2c2e8525 | Gerd Hoffmann | type_register(&uhci_type_info); |
1396 | 2c2e8525 | Gerd Hoffmann | } |
1397 | 6cf9b6f1 | Gerd Hoffmann | } |
1398 | 83f7d43a | Andreas Färber | |
1399 | 83f7d43a | Andreas Färber | type_init(uhci_register_types) |