VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/DevOHCI.cpp@ 29070

Last change on this file since 29070 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 191.8 KB
Line 
1/* $Id: DevOHCI.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * DevOHCI - Open Host Controller Interface for USB.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_dev_ohci OHCI - Open Host Controller Interface Emulation.
19 *
20 * This component implements an OHCI USB controller. It is split roughly in
21 * to two main parts, the first part implements the register level
22 * specification of USB OHCI and the second part maintains the root hub (which
23 * is an integrated component of the device).
24 *
25 * The OHCI registers are used for the usual stuff like enabling and disabling
26 * interrupts. Since the USB time is divided in to 1ms frames and various
27 * interrupts may need to be triggered at frame boundary time, a timer-based
28 * approach was taken. Whenever the bus is enabled ohci->eof_timer will be set.
29 *
30 * The actual USB transfers are stored in main memory (along with endpoint and
31 * transfer descriptors). The ED's for all the control and bulk endpoints are
32 * found by consulting the HcControlHeadED and HcBulkHeadED registers
33 * respectively. Interrupt ED's are different, they are found by looking
34 * in the HCCA (another communication area in main memory).
35 *
36 * At the start of every frame (in function ohci_sof) we traverse all enabled
37 * ED lists and queue up as many transfers as possible. No attention is paid
38 * to control/bulk service ratios or bandwidth requirements since our USB
39 * could conceivably contain a dozen high speed busses so this would
40 * artificially limit the performance.
41 *
42 * Once we have a transfer ready to go (in function ohciServiceTd) we
43 * allocate an URB on the stack, fill in all the relevant fields and submit
44 * it using the VUSBIRhSubmitUrb function. The roothub device and the virtual
45 * USB core code (vusb.c) coordinates everything else from this point onwards.
46 *
47 * When the URB has been successfully handed to the lower level driver, our
48 * prepare callback gets called and we can remove the TD from the ED transfer
49 * list. This stops us queueing it twice while it completes.
50 * bird: no, we don't remove it because that confuses the guest! (=> crashes)
51 *
52 * Completed URBs are reaped at the end of every frame (in function
53 * ohci_frame_boundary). Our completion routine makes use of the ED and TD
54 * fields in the URB to store the physical addresses of the descriptors so
55 * that they may be modified in the roothub callbacks. Our completion
56 * routine (ohciRhXferComplete) carries out a number of tasks:
57 * -# Retires the TD associated with the transfer, setting the
58 * relevent error code etc.
59 * -# Updates done-queue interrupt timer and potentially causes
60 * a writeback of the done-queue.
61 * -# If the transfer was device-to-host, we copy the data in to
62 * the host memory.
63 *
64 * As for error handling OHCI allows for 3 retries before failing a transfer,
65 * an error count is stored in each transfer descriptor. A halt flag is also
66 * stored in the transfer descriptor. That allows for ED's to be disabled
67 * without stopping the bus and de-queuing them.
68 *
69 * When the bus is started and stopped we call VUSBIDevPowerOn/Off() on our
70 * roothub to indicate it's powering up and powering down. Whenever we power
71 * down, the USB core makes sure to synchronously complete all outstanding
72 * requests so that the OHCI is never seen in an inconsistent state by the
73 * guest OS (Transfers are not meant to be unlinked until they've actually
74 * completed, but we can't do that unless we work synchronously, so we just
75 * have to fake it).
76 * bird: we do work synchronously now, anything causes guest crashes.
77 */
78
79
80/*******************************************************************************
81* Header Files *
82*******************************************************************************/
83#define LOG_GROUP LOG_GROUP_DEV_USB
84#include <VBox/pci.h>
85#include <VBox/pdm.h>
86#include <VBox/mm.h>
87#include <VBox/err.h>
88#include <VBox/log.h>
89#include <iprt/assert.h>
90#include <iprt/string.h>
91#include <iprt/asm.h>
92#ifdef IN_RING3
93# include <iprt/alloca.h>
94# include <iprt/mem.h>
95# include <iprt/thread.h>
96# include <iprt/uuid.h>
97#endif
98#include <VBox/vusb.h>
99#include "../Builtins.h"
100
101
102/*******************************************************************************
103* Structures and Typedefs *
104*******************************************************************************/
105/** The saved state version. */
106#define OHCI_SAVED_STATE_VERSION 4
107/** The saved state version used in 3.0 and earlier.
108 *
109 * @remarks Because of the SSMR3MemPut/Get laziness we ended up with an
110 * accidental format change between 2.0 and 2.1 that didn't get its own
111 * version number. It is therefore not possible to restore states from
112 * 2.0 and earlier with 2.1 and later. */
113#define OHCI_SAVED_STATE_VERSION_MEM_HELL 3
114
115
116/* Number of Downstream Ports on the root hub, if you change this
117 * you need to add more status register words to the 'opreg' array
118 */
119#define OHCI_NDP 8
120
121/** Pointer to OHCI device data. */
122typedef struct OHCI *POHCI;
123
124
125/**
126 * An OHCI root hub port.
127 */
128typedef struct OHCIHUBPORT
129{
130 /** The port register. */
131 uint32_t fReg;
132#if HC_ARCH_BITS == 64
133 uint32_t Alignment0; /**< Align the pointer correctly. */
134#endif
135 /** The device attached to the port. */
136 R3PTRTYPE(PVUSBIDEVICE) pDev;
137} OHCIHUBPORT;
138#if HC_ARCH_BITS == 64
139AssertCompile(sizeof(OHCIHUBPORT) == 16); /* saved state */
140#endif
141/** Pointer to an OHCI hub port. */
142typedef OHCIHUBPORT *POHCIHUBPORT;
143
144/**
145 * The OHCI root hub.
146 *
147 * @implements PDMIBASE
148 * @implements VUSBIROOTHUBPORT
149 * @implements PDMILEDPORTS
150 */
151typedef struct ohci_roothub
152{
153 /** Pointer to the base interface of the VUSB RootHub. */
154 R3PTRTYPE(PPDMIBASE) pIBase;
155 /** Pointer to the connector interface of the VUSB RootHub. */
156 R3PTRTYPE(PVUSBIROOTHUBCONNECTOR) pIRhConn;
157 /** Pointer to the device interface of the VUSB RootHub. */
158 R3PTRTYPE(PVUSBIDEVICE) pIDev;
159 /** The base interface exposed to the roothub driver. */
160 PDMIBASE IBase;
161 /** The roothub port interface exposed to the roothub driver. */
162 VUSBIROOTHUBPORT IRhPort;
163
164 /** The LED. */
165 PDMLED Led;
166 /** The LED ports. */
167 PDMILEDPORTS ILeds;
168 /** Partner of ILeds. */
169 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
170
171 uint32_t status;
172 uint32_t desc_a;
173 uint32_t desc_b;
174#if HC_ARCH_BITS == 64
175 uint32_t Alignment0; /**< Align aPorts on a 8 byte boundary. */
176#endif
177 OHCIHUBPORT aPorts[OHCI_NDP];
178 R3PTRTYPE(POHCI) pOhci;
179} OHCIROOTHUB;
180#if HC_ARCH_BITS == 64
181AssertCompile(sizeof(OHCIROOTHUB) == 280); /* saved state */
182#endif
183/** Pointer to the OHCI root hub. */
184typedef OHCIROOTHUB *POHCIROOTHUB;
185
186
187/**
188 * Data used for reattaching devices on a state load.
189 */
190typedef struct ohci_load {
191 /** Timer used once after state load to inform the guest about new devices.
192 * We do this to be sure the guest get any disconnect / reconnect on the
193 * same port. */
194 PTMTIMERR3 pTimer;
195 /** Number of detached devices. */
196 unsigned cDevs;
197 /** Array of devices which were detached. */
198 PVUSBIDEVICE apDevs[OHCI_NDP];
199} OHCILOAD;
200/** Pointer to an OHCILOAD structure. */
201typedef OHCILOAD *POHCILOAD;
202
203
204/**
205 * OHCI device data.
206 */
207typedef struct OHCI
208{
209 /** The PCI device. */
210 PCIDEVICE PciDev;
211
212 /** Pointer to the device instance - R3 ptr. */
213 PPDMDEVINSR3 pDevInsR3;
214 /** The End-Of-Frame timer - R3 Ptr. */
215 PTMTIMERR3 pEndOfFrameTimerR3;
216
217 /** Pointer to the device instance - R0 ptr */
218 PPDMDEVINSR0 pDevInsR0;
219 /** The End-Of-Frame timer - R0 Ptr. */
220 PTMTIMERR0 pEndOfFrameTimerR0;
221
222 /** Pointer to the device instance - RC ptr. */
223 PPDMDEVINSRC pDevInsRC;
224 /** The End-Of-Frame timer - RC Ptr. */
225 PTMTIMERRC pEndOfFrameTimerRC;
226
227 /** Start of current frame. */
228 uint64_t SofTime;
229 /* done queue interrupt counter */
230 uint32_t dqic : 3;
231 /** frame number overflow. */
232 uint32_t fno : 1;
233 /** Address of the MMIO region assigned by PCI. */
234 RTGCPHYS32 MMIOBase;
235
236 /* Root hub device */
237 OHCIROOTHUB RootHub;
238
239 /* OHCI registers */
240
241 /** @name Control partition
242 * @{ */
243 /** HcControl. */
244 uint32_t ctl;
245 /** HcCommandStatus. */
246 uint32_t status;
247 /** HcInterruptStatus. */
248 uint32_t intr_status;
249 /** HcInterruptEnabled. */
250 uint32_t intr;
251 /** @} */
252
253 /** @name Memory pointer partition
254 * @{ */
255 /** HcHCCA. */
256 uint32_t hcca;
257 /** HcPeriodCurrentEd. */
258 uint32_t per_cur;
259 /** HcControlCurrentED. */
260 uint32_t ctrl_cur;
261 /** HcControlHeadED. */
262 uint32_t ctrl_head;
263 /** HcBlockCurrendED. */
264 uint32_t bulk_cur;
265 /** HcBlockHeadED. */
266 uint32_t bulk_head;
267 /** HcDoneHead. */
268 uint32_t done;
269 /** @} */
270
271 /** @name Frame counter partition
272 * @{ */
273 /** HcFmInterval.FSMPS - FSLargestDataPacket */
274 uint32_t fsmps : 15;
275 /** HcFmInterval.FIT - FrameItervalToggle */
276 uint32_t fit : 1;
277 /** HcFmInterval.FI - FrameInterval */
278 uint32_t fi : 14;
279 /** HcFmRemaining.FRT - toggle bit. */
280 uint32_t frt : 1;
281 /** HcFmNumber.
282 * @remark The register size is 16-bit, but for debugging and performance
283 * reasons we maintain a 32-bit counter. */
284 uint32_t HcFmNumber;
285 /** HcPeriodicStart */
286 uint32_t pstart;
287 /** @} */
288
289 /** The number of virtual time ticks per frame. */
290 uint64_t cTicksPerFrame;
291 /** The number of virtual time ticks per USB bus tick. */
292 uint64_t cTicksPerUsbTick;
293
294 /** Number of in-flight TDs. */
295 unsigned cInFlight;
296 unsigned Alignment1; /**< Align aInFlight on a 8 byte boundary. */
297 /** Array of in-flight TDs. */
298 struct ohci_td_in_flight
299 {
300 /** Address of the transport descriptor. */
301 uint32_t GCPhysTD;
302#if HC_ARCH_BITS == 64
303 uint32_t Alignment0; /**< Alignment pUrb correctly. */
304#endif
305 /** Pointer to the URB. */
306 R3PTRTYPE(PVUSBURB) pUrb;
307 } aInFlight[257];
308
309 /** Number of in-done-queue TDs. */
310 unsigned cInDoneQueue;
311 /** Array of in-done-queue TDs. */
312 struct ohci_td_in_done_queue
313 {
314 /** Address of the transport descriptor. */
315 uint32_t GCPhysTD;
316 } aInDoneQueue[64];
317 /** When the tail of the done queue was added.
318 * Used to calculate the age of the done queue. */
319 uint32_t u32FmDoneQueueTail;
320#if R3_ARCH_BITS == 32
321 /** Align pLoad, the stats and the struct size correctly. */
322 uint32_t Alignment2;
323#endif
324 /** Pointer to state load data. */
325 R3PTRTYPE(POHCILOAD) pLoad;
326
327 /** Detected canceled isochronous URBs. */
328 STAMCOUNTER StatCanceledIsocUrbs;
329 /** Detected canceled general URBs. */
330 STAMCOUNTER StatCanceledGenUrbs;
331 /** Dropped URBs (endpoint halted, or URB canceled). */
332 STAMCOUNTER StatDroppedUrbs;
333 /** Profiling ohciFrameBoundaryTimer. */
334 STAMPROFILE StatTimer;
335
336 /** This member and all the following are not part of saved state. */
337 uint64_t SavedStateEnd;
338
339 /** VM timer frequency used for frame timer calculations. */
340 uint64_t u64TimerHz;
341 /** Number of USB work cycles with no transfers. */
342 uint32_t cIdleCycles;
343 /** Current frame timer rate (default 1000). */
344 uint32_t uFrameRate;
345 /** Idle detection flag; must be cleared at start of frame */
346 bool fIdle;
347
348 uint32_t Alignment3; /**< Align size on a 8 byte boundary. */
349} OHCI;
350
351/* Standard OHCI bus speed */
352#define OHCI_DEFAULT_TIMER_FREQ 1000
353
354/* Host Controller Communications Area */
355#define OHCI_HCCA_NUM_INTR 32
356#define OHCI_HCCA_OFS (OHCI_HCCA_NUM_INTR * sizeof(uint32_t))
357struct ohci_hcca
358{
359 uint16_t frame;
360 uint16_t pad;
361 uint32_t done;
362};
363AssertCompileSize(ohci_hcca, 8);
364
365/** @name OHCI Endpoint Descriptor
366 * @{ */
367
368#define ED_PTR_MASK (~(uint32_t)0xf)
369#define ED_HWINFO_MPS 0x07ff0000
370#define ED_HWINFO_ISO RT_BIT(15)
371#define ED_HWINFO_SKIP RT_BIT(14)
372#define ED_HWINFO_LOWSPEED RT_BIT(13)
373#define ED_HWINFO_IN RT_BIT(12)
374#define ED_HWINFO_OUT RT_BIT(11)
375#define ED_HWINFO_DIR (RT_BIT(11) | RT_BIT(12))
376#define ED_HWINFO_ENDPOINT 0x780 /* 4 bits */
377#define ED_HWINFO_ENDPOINT_SHIFT 7
378#define ED_HWINFO_FUNCTION 0x7f /* 7 bits */
379#define ED_HEAD_CARRY RT_BIT(1)
380#define ED_HEAD_HALTED RT_BIT(0)
381
382/**
383 * OHCI Endpoint Descriptor.
384 */
385typedef struct OHCIED
386{
387 /** Flags and stuff. */
388 uint32_t hwinfo;
389 /** TailP - TD Queue Tail pointer. Bits 0-3 ignored / preserved. */
390 uint32_t TailP;
391 /** HeadP - TD Queue head pointer. Bit 0 - Halted, Bit 1 - toggleCarry. Bit 2&3 - 0. */
392 uint32_t HeadP;
393 /** NextED - Next Endpoint Desciptor. Bits 0-3 ignored / preserved. */
394 uint32_t NextED;
395} OHCIED, *POHCIED;
396typedef const OHCIED *PCOHCIED;
397AssertCompileSize(OHCIED, 16);
398
399/** @} */
400
401
402/** @name Completion Codes
403 * @{ */
404#define OHCI_CC_NO_ERROR (UINT32_C(0x00) << 28)
405#define OHCI_CC_CRC (UINT32_C(0x01) << 28)
406#define OHCI_CC_STALL (UINT32_C(0x04) << 28)
407#define OHCI_CC_DEVICE_NOT_RESPONDING (UINT32_C(0x05) << 28)
408#define OHCI_CC_DNR OHCI_CC_DEVICE_NOT_RESPONDING
409#define OHCI_CC_PID_CHECK_FAILURE (UINT32_C(0x06) << 28)
410#define OHCI_CC_UNEXPECTED_PID (UINT32_C(0x07) << 28)
411#define OHCI_CC_DATA_OVERRUN (UINT32_C(0x08) << 28)
412#define OHCI_CC_DATA_UNDERRUN (UINT32_C(0x09) << 28)
413/* 0x0a..0x0b - reserved */
414#define OHCI_CC_BUFFER_OVERRUN (UINT32_C(0x0c) << 28)
415#define OHCI_CC_BUFFER_UNDERRUN (UINT32_C(0x0d) << 28)
416#define OHCI_CC_NOT_ACCESSED_0 (UINT32_C(0x0e) << 28)
417#define OHCI_CC_NOT_ACCESSED_1 (UINT32_C(0x0f) << 28)
418/** @} */
419
420
421/** @name OHCI General transfer descriptor
422 * @{ */
423
424/** Error count (EC) shift. */
425#define TD_ERRORS_SHIFT 26
426/** Error count max. (One greater than what the EC field can hold.) */
427#define TD_ERRORS_MAX 4
428
429/** CC - Condition code mask. */
430#define TD_HWINFO_CC (UINT32_C(0xf0000000))
431#define TD_HWINFO_CC_SHIFT 28
432/** EC - Error count. */
433#define TD_HWINFO_ERRORS (RT_BIT(26) | RT_BIT(27))
434/** T - Data toggle. */
435#define TD_HWINFO_TOGGLE (RT_BIT(24) | RT_BIT(25))
436#define TD_HWINFO_TOGGLE_HI (RT_BIT(25))
437#define TD_HWINFO_TOGGLE_LO (RT_BIT(24))
438/** DI - Delay interrupt. */
439#define TD_HWINFO_DI (RT_BIT(21) | RT_BIT(22) | RT_BIT(23))
440#define TD_HWINFO_IN (RT_BIT(20))
441#define TD_HWINFO_OUT (RT_BIT(19))
442/** DP - Direction / PID. */
443#define TD_HWINFO_DIR (RT_BIT(19) | RT_BIT(20))
444/** R - Buffer rounding. */
445#define TD_HWINFO_ROUNDING (RT_BIT(18))
446/** Bits that are reserved / unknown. */
447#define TD_HWINFO_UNKNOWN_MASK (UINT32_C(0x0003ffff))
448
449/** SETUP - to endpoint. */
450#define OHCI_TD_DIR_SETUP 0x0
451/** OUT - to endpoint. */
452#define OHCI_TD_DIR_OUT 0x1
453/** IN - from endpoint. */
454#define OHCI_TD_DIR_IN 0x2
455/** Reserved. */
456#define OHCI_TD_DIR_RESERVED 0x3
457
458/**
459 * OHCI general transfer descriptor
460 */
461typedef struct OHCITD
462{
463 uint32_t hwinfo;
464 /** CBP - Current Buffer Pointer. (32-bit physical address) */
465 uint32_t cbp;
466 /** NextTD - Link to the next transfer descriptor. (32-bit physical address, dword aligned) */
467 uint32_t NextTD;
468 /** BE - Buffer End (inclusive). (32-bit physical address) */
469 uint32_t be;
470} OHCITD, *POHCITD;
471typedef const OHCITD *PCOHCITD;
472AssertCompileSize(OHCIED, 16);
473/** @} */
474
475
476/** @name OHCI isochronous transfer descriptor.
477 * @{ */
478/** SF - Start frame number. */
479#define ITD_HWINFO_SF 0xffff
480/** DI - Delay interrupt. (TD_HWINFO_DI) */
481#define ITD_HWINFO_DI (RT_BIT(21) | RT_BIT(22) | RT_BIT(23))
482#define ITD_HWINFO_DI_SHIFT 21
483/** FC - Frame count. */
484#define ITD_HWINFO_FC (RT_BIT(24) | RT_BIT(25) | RT_BIT(26))
485#define ITD_HWINFO_FC_SHIFT 24
486/** CC - Condition code mask. (=TD_HWINFO_CC) */
487#define ITD_HWINFO_CC UINT32_C(0xf0000000)
488#define ITD_HWINFO_CC_SHIFT 28
489/** The buffer page 0 mask (lower 12 bits are ignored). */
490#define ITD_BP0_MASK UINT32_C(0xfffff000)
491
492#define ITD_NUM_PSW 8
493/** OFFSET - offset of the package into the buffer page.
494 * (Only valid when CC set to Not Accessed.)
495 *
496 * Note that the top bit of the OFFSET field is overlapping with the
497 * first bit in the CC field. This is ok because both 0xf and 0xe are
498 * defined as "Not Accessed".
499 */
500#define ITD_PSW_OFFSET 0x1fff
501/** SIZE field mask for IN bound transfers.
502 * (Only valid when CC isn't Not Accessed.)*/
503#define ITD_PSW_SIZE 0x07ff
504/** CC field mask.
505 * USed to indicate the format of SIZE (Not Accessed -> OFFSET). */
506#define ITD_PSW_CC 0xf000
507#define ITD_PSW_CC_SHIFT 12
508
509/**
510 * OHCI isochronous transfer descriptor.
511 */
512typedef struct OHCIITD
513{
514 uint32_t HwInfo;
515 /** BP0 - Buffer Page 0. The lower 12 bits are ignored. */
516 uint32_t BP0;
517 /** NextTD - Link to the next transfer descriptor. (32-bit physical address, dword aligned) */
518 uint32_t NextTD;
519 /** BE - Buffer End (inclusive). (32-bit physical address) */
520 uint32_t BE;
521 /** (OffsetN/)PSWN - package status word array (0..7).
522 * The format varies depending on whether the package has been completed or not. */
523 uint16_t aPSW[ITD_NUM_PSW];
524} OHCIITD, *POHCIITD;
525typedef const OHCIITD *PCOHCIITD;
526AssertCompileSize(OHCIITD, 32);
527/** @} */
528
529/**
530 * OHCI register operator.
531 */
532typedef struct ohci_opreg
533{
534 const char *pszName;
535 int (*pfnRead )(POHCI ohci, uint32_t iReg, uint32_t *pu32Value);
536 int (*pfnWrite)(POHCI ohci, uint32_t iReg, uint32_t u32Value);
537} OHCIOPREG;
538
539
540/* OHCI Local stuff */
541#define OHCI_CTL_CBSR ((1<<0)|(1<<1))
542#define OHCI_CTL_PLE (1<<2)
543#define OHCI_CTL_IE (1<<3)
544#define OHCI_CTL_CLE (1<<4)
545#define OHCI_CTL_BLE (1<<5)
546#define OHCI_CTL_HCFS ((1<<6)|(1<<7))
547#define OHCI_USB_RESET 0x00
548#define OHCI_USB_RESUME 0x40
549#define OHCI_USB_OPERATIONAL 0x80
550#define OHCI_USB_SUSPEND 0xc0
551#define OHCI_CTL_IR (1<<8)
552#define OHCI_CTL_RWC (1<<9)
553#define OHCI_CTL_RWE (1<<10)
554
555#define OHCI_STATUS_HCR (1<<0)
556#define OHCI_STATUS_CLF (1<<1)
557#define OHCI_STATUS_BLF (1<<2)
558#define OHCI_STATUS_OCR (1<<3)
559#define OHCI_STATUS_SOC ((1<<6)|(1<<7))
560
561/** @name Interrupt Status and Enabled/Disabled Flags
562 * @{ */
563/** SO - Scheduling overrun. */
564#define OHCI_INTR_SCHEDULEING_OVERRUN RT_BIT(0)
565/** WDH - HcDoneHead writeback. */
566#define OHCI_INTR_WRITE_DONE_HEAD RT_BIT(1)
567/** SF - Start of frame. */
568#define OHCI_INTR_START_OF_FRAME RT_BIT(2)
569/** RD - Resume detect. */
570#define OHCI_INTR_RESUME_DETECT RT_BIT(3)
571/** UE - Unrecoverable error. */
572#define OHCI_INTR_UNRECOVERABLE_ERROR RT_BIT(4)
573/** FNO - Frame number overflow. */
574#define OHCI_INTR_FRAMENUMBER_OVERFLOW RT_BIT(5)
575/** RHSC- Root hub status change. */
576#define OHCI_INTR_ROOT_HUB_STATUS_CHANGE RT_BIT(6)
577/** OC - Ownership change. */
578#define OHCI_INTR_OWNERSHIP_CHANGE RT_BIT(30)
579/** MIE - Master interrupt enable. */
580#define OHCI_INTR_MASTER_INTERRUPT_ENABLED RT_BIT(31)
581/** @} */
582
583#define OHCI_HCCA_SIZE 0x100
584#define OHCI_HCCA_MASK UINT32_C(0xffffff00)
585
586#define OHCI_FMI_FI UINT32_C(0x00003fff)
587#define OHCI_FMI_FSMPS UINT32_C(0x7fff0000)
588#define OHCI_FMI_FSMPS_SHIFT 16
589#define OHCI_FMI_FIT UINT32_C(0x80000000)
590#define OHCI_FMI_FIT_SHIFT 31
591
592#define OHCI_FR_RT RT_BIT_32(31)
593
594#define OHCI_LS_THRESH 0x628
595
596#define OHCI_RHA_NDP (0xff)
597#define OHCI_RHA_PSM RT_BIT_32(8)
598#define OHCI_RHA_NPS RT_BIT_32(9)
599#define OHCI_RHA_DT RT_BIT_32(10)
600#define OHCI_RHA_OCPM RT_BIT_32(11)
601#define OHCI_RHA_NOCP RT_BIT_32(12)
602#define OHCI_RHA_POTPGP UINT32_C(0xff000000)
603
604#define OHCI_RHS_LPS RT_BIT_32(0)
605#define OHCI_RHS_OCI RT_BIT_32(1)
606#define OHCI_RHS_DRWE RT_BIT_32(15)
607#define OHCI_RHS_LPSC RT_BIT_32(16)
608#define OHCI_RHS_OCIC RT_BIT_32(17)
609#define OHCI_RHS_CRWE RT_BIT_32(31)
610
611/** @name HcRhPortStatus[n] - RH Port Status register (read).
612 * @{ */
613/** CCS - CurrentConnectionStatus - 0 = no device, 1 = device. */
614#define OHCI_PORT_CCS RT_BIT(0)
615/** PES - PortEnableStatus. */
616#define OHCI_PORT_PES RT_BIT(1)
617/** PSS - PortSuspendStatus */
618#define OHCI_PORT_PSS RT_BIT(2)
619/** POCI- PortOverCurrentIndicator. */
620#define OHCI_PORT_POCI RT_BIT(3)
621/** PRS - PortResetStatus */
622#define OHCI_PORT_PRS RT_BIT(4)
623/** PPS - PortPowerStatus */
624#define OHCI_PORT_PPS RT_BIT(8)
625/** LSDA - LowSpeedDeviceAttached */
626#define OHCI_PORT_LSDA RT_BIT(9)
627/** CSC - ConnectStatusChange */
628#define OHCI_PORT_CSC RT_BIT(16)
629/** PESC - PortEnableStatusChange */
630#define OHCI_PORT_PESC RT_BIT(17)
631/** PSSC - PortSuspendStatusChange */
632#define OHCI_PORT_PSSC RT_BIT(18)
633/** OCIC - OverCurrentIndicatorChange */
634#define OHCI_PORT_OCIC RT_BIT(19)
635/** PRSC - PortResetStatusChange */
636#define OHCI_PORT_PRSC RT_BIT(20)
637/** @} */
638
639
640/** @name HcRhPortStatus[n] - Root Hub Port Status Registers - read.
641 * @{ */
642/** CCS - CurrentConnectStatus - 0 = no device, 1 = device. */
643#define OHCI_PORT_R_CURRENT_CONNECT_STATUS RT_BIT(0)
644/** PES - PortEnableStatus. */
645#define OHCI_PORT_R_ENABLE_STATUS RT_BIT(1)
646/** PSS - PortSuspendStatus */
647#define OHCI_PORT_R_SUSPEND_STATUS RT_BIT(2)
648/** POCI- PortOverCurrentIndicator. */
649#define OHCI_PORT_R_OVER_CURRENT_INDICATOR RT_BIT(3)
650/** PRS - PortResetStatus */
651#define OHCI_PORT_R_RESET_STATUS RT_BIT(4)
652/** PPS - PortPowerStatus */
653#define OHCI_PORT_R_POWER_STATUS RT_BIT(8)
654/** LSDA - LowSpeedDeviceAttached */
655#define OHCI_PORT_R_LOW_SPEED_DEVICE_ATTACHED RT_BIT(9)
656/** CSC - ConnectStatusChange */
657#define OHCI_PORT_R_CONNECT_STATUS_CHANGE RT_BIT(16)
658/** PESC - PortEnableStatusChange */
659#define OHCI_PORT_R_ENABLE_STATUS_CHANGE RT_BIT(17)
660/** PSSC - PortSuspendStatusChange */
661#define OHCI_PORT_R_SUSPEND_STATUS_CHANGE RT_BIT(18)
662/** OCIC - OverCurrentIndicatorChange */
663#define OHCI_PORT_R_OVER_CURRENT_INDICATOR_CHANGE RT_BIT(19)
664/** PRSC - PortResetStatusChange */
665#define OHCI_PORT_R_RESET_STATUS_CHANGE RT_BIT(20)
666/** @} */
667
668/** @name HcRhPortStatus[n] - Root Hub Port Status Registers - write.
669 * @{ */
670/** CCS - ClearPortEnable. */
671#define OHCI_PORT_W_CLEAR_ENABLE RT_BIT(0)
672/** PES - SetPortEnable. */
673#define OHCI_PORT_W_SET_ENABLE RT_BIT(1)
674/** PSS - SetPortSuspend */
675#define OHCI_PORT_W_SET_SUSPEND RT_BIT(2)
676/** POCI- ClearSuspendStatus. */
677#define OHCI_PORT_W_CLEAR_SUSPEND_STATUS RT_BIT(3)
678/** PRS - SetPortReset */
679#define OHCI_PORT_W_SET_RESET RT_BIT(4)
680/** PPS - SetPortPower */
681#define OHCI_PORT_W_SET_POWER RT_BIT(8)
682/** LSDA - ClearPortPower */
683#define OHCI_PORT_W_CLEAR_POWER RT_BIT(9)
684/** CSC - ClearConnectStatusChange */
685#define OHCI_PORT_W_CLEAR_CSC RT_BIT(16)
686/** PESC - PortEnableStatusChange */
687#define OHCI_PORT_W_CLEAR_PESC RT_BIT(17)
688/** PSSC - PortSuspendStatusChange */
689#define OHCI_PORT_W_CLEAR_PSSC RT_BIT(18)
690/** OCIC - OverCurrentIndicatorChange */
691#define OHCI_PORT_W_CLEAR_OCIC RT_BIT(19)
692/** PRSC - PortResetStatusChange */
693#define OHCI_PORT_W_CLEAR_PRSC RT_BIT(20)
694/** The mask of bit which are used to clear themselves. */
695#define OHCI_PORT_W_CLEAR_CHANGE_MASK ( OHCI_PORT_W_CLEAR_CSC | OHCI_PORT_W_CLEAR_PESC | OHCI_PORT_W_CLEAR_PSSC \
696 | OHCI_PORT_W_CLEAR_OCIC | OHCI_PORT_W_CLEAR_PRSC)
697/** @} */
698
699
700#ifndef VBOX_DEVICE_STRUCT_TESTCASE
701/*******************************************************************************
702* Global Variables *
703*******************************************************************************/
704#if defined(LOG_ENABLED) && defined(IN_RING3)
705static bool g_fLogBulkEPs = false;
706static bool g_fLogControlEPs = false;
707static bool g_fLogInterruptEPs = false;
708#endif
709#ifdef IN_RING3
710/**
711 * SSM descriptor table for the OHCI structure.
712 */
713static SSMFIELD const g_aOhciFields[] =
714{
715 SSMFIELD_ENTRY( OHCI, SofTime),
716 SSMFIELD_ENTRY_CUSTOM( dpic+fno, RT_OFFSETOF(OHCI, SofTime) + RT_SIZEOFMEMB(OHCI, SofTime), 4),
717 SSMFIELD_ENTRY( OHCI, RootHub.status),
718 SSMFIELD_ENTRY( OHCI, RootHub.desc_a),
719 SSMFIELD_ENTRY( OHCI, RootHub.desc_b),
720 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[0].fReg),
721 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[1].fReg),
722 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[2].fReg),
723 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[3].fReg),
724 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[4].fReg),
725 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[5].fReg),
726 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[6].fReg),
727 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[7].fReg),
728 SSMFIELD_ENTRY( OHCI, ctl),
729 SSMFIELD_ENTRY( OHCI, status),
730 SSMFIELD_ENTRY( OHCI, intr_status),
731 SSMFIELD_ENTRY( OHCI, intr),
732 SSMFIELD_ENTRY( OHCI, hcca),
733 SSMFIELD_ENTRY( OHCI, per_cur),
734 SSMFIELD_ENTRY( OHCI, ctrl_cur),
735 SSMFIELD_ENTRY( OHCI, ctrl_head),
736 SSMFIELD_ENTRY( OHCI, bulk_cur),
737 SSMFIELD_ENTRY( OHCI, bulk_head),
738 SSMFIELD_ENTRY( OHCI, done),
739 SSMFIELD_ENTRY_CUSTOM( fsmps+fit+fi+frt, RT_OFFSETOF(OHCI, done) + RT_SIZEOFMEMB(OHCI, done), 4),
740 SSMFIELD_ENTRY( OHCI, HcFmNumber),
741 SSMFIELD_ENTRY( OHCI, pstart),
742 SSMFIELD_ENTRY_TERM()
743};
744#endif
745
746
747/*******************************************************************************
748* Internal Functions *
749*******************************************************************************/
750RT_C_DECLS_BEGIN
751#ifdef IN_RING3
752/* Update host controller state to reflect a device attach */
753static void rhport_power(POHCIROOTHUB pRh, unsigned iPort, bool fPowerUp);
754static void ohciBusResume(POHCI ohci, bool fHardware);
755
756static DECLCALLBACK(void) ohciRhXferCompletion(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb);
757static DECLCALLBACK(bool) ohciRhXferError(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb);
758
759static int ohci_in_flight_find(POHCI pOhci, uint32_t GCPhysTD);
760# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
761static int ohci_in_done_queue_find(POHCI pOhci, uint32_t GCPhysTD);
762# endif
763static DECLCALLBACK(void) ohciR3LoadReattachDevices(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
764#endif /* IN_RING3 */
765PDMBOTHCBDECL(int) ohciWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
766PDMBOTHCBDECL(int) ohciRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
767RT_C_DECLS_END
768
769
770/**
771 * Update PCI IRQ levels
772 */
773static void ohciUpdateInterrupt(POHCI ohci, const char *msg)
774{
775 int level = 0;
776
777 if ( (ohci->intr & OHCI_INTR_MASTER_INTERRUPT_ENABLED)
778 && (ohci->intr_status & ohci->intr)
779 && !(ohci->ctl & OHCI_CTL_IR))
780 level = 1;
781
782 PDMDevHlpPCISetIrq(ohci->CTX_SUFF(pDevIns), 0, level);
783 if (level)
784 {
785 uint32_t val = ohci->intr_status & ohci->intr;
786 Log2(("ohci: Fired off interrupt %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d - %s\n",
787 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
788 (val >> 6) & 1, (val >> 30) & 1, msg)); NOREF(val); NOREF(msg);
789 }
790}
791
792/**
793 * Set an interrupt, use the wrapper ohciSetInterrupt.
794 */
795DECLINLINE(void) ohciSetInterruptInt(POHCI ohci, uint32_t intr, const char *msg)
796{
797 if ( (ohci->intr_status & intr) == intr )
798 return;
799 ohci->intr_status |= intr;
800 ohciUpdateInterrupt(ohci, msg);
801}
802
803/**
804 * Set an interrupt wrapper macro for logging purposes.
805 */
806#define ohciSetInterrupt(ohci, intr) ohciSetInterruptInt(ohci, intr, #intr)
807
808
809#ifdef IN_RING3
810
811/* Carry out a hardware remote wakeup */
812static void ohci_remote_wakeup(POHCI pOhci)
813{
814 if ((pOhci->ctl & OHCI_CTL_HCFS) != OHCI_USB_SUSPEND)
815 return;
816 if (!(pOhci->RootHub.status & OHCI_RHS_DRWE))
817 return;
818 ohciBusResume(pOhci, true /* hardware */);
819}
820
821
822/**
823 * Query interface method for the roothub LUN.
824 */
825static DECLCALLBACK(void *) ohciRhQueryInterface(PPDMIBASE pInterface, const char *pszIID)
826{
827 POHCI pThis = RT_FROM_MEMBER(pInterface, OHCI, RootHub.IBase);
828 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->RootHub.IBase);
829 PDMIBASE_RETURN_INTERFACE(pszIID, VUSBIROOTHUBPORT, &pThis->RootHub.IRhPort);
830 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->RootHub.ILeds);
831 return NULL;
832}
833
834/**
835 * Gets the pointer to the status LED of a unit.
836 *
837 * @returns VBox status code.
838 * @param pInterface Pointer to the interface structure containing the called function pointer.
839 * @param iLUN The unit which status LED we desire.
840 * @param ppLed Where to store the LED pointer.
841 */
842static DECLCALLBACK(int) ohciRhQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
843{
844 POHCI pOhci = (POHCI)((uintptr_t)pInterface - RT_OFFSETOF(OHCI, RootHub.ILeds));
845 if (iLUN == 0)
846 {
847 *ppLed = &pOhci->RootHub.Led;
848 return VINF_SUCCESS;
849 }
850 return VERR_PDM_LUN_NOT_FOUND;
851}
852
853
854/** Converts a OHCI.roothub.IRhPort pointer to a POHCI. */
855#define VUSBIROOTHUBPORT_2_OHCI(pInterface) ((POHCI)( (uintptr_t)(pInterface) - RT_OFFSETOF(OHCI, RootHub.IRhPort) ))
856
857
858/**
859 * Get the number of avilable ports in the hub.
860 *
861 * @returns The number of ports available.
862 * @param pInterface Pointer to this structure.
863 * @param pAvailable Bitmap indicating the available ports. Set bit == available port.
864 */
865static DECLCALLBACK(unsigned) ohciRhGetAvailablePorts(PVUSBIROOTHUBPORT pInterface, PVUSBPORTBITMAP pAvailable)
866{
867 POHCI pOhci = VUSBIROOTHUBPORT_2_OHCI(pInterface);
868 unsigned iPort;
869 unsigned cPorts = 0;
870
871 memset(pAvailable, 0, sizeof(*pAvailable));
872 for (iPort = 0; iPort < RT_ELEMENTS(pOhci->RootHub.aPorts); iPort++)
873 {
874 if (!pOhci->RootHub.aPorts[iPort].pDev)
875 {
876 cPorts++;
877 ASMBitSet(pAvailable, iPort + 1);
878 }
879 }
880
881 return cPorts;
882}
883
884
885/**
886 * Gets the supported USB versions.
887 *
888 * @returns The mask of supported USB versions.
889 * @param pInterface Pointer to this structure.
890 */
891static DECLCALLBACK(uint32_t) ohciRhGetUSBVersions(PVUSBIROOTHUBPORT pInterface)
892{
893 return VUSB_STDVER_11;
894}
895
896
897/**
898 * A device is being attached to a port in the roothub.
899 *
900 * @param pInterface Pointer to this structure.
901 * @param pDev Pointer to the device being attached.
902 * @param uPort The port number assigned to the device.
903 */
904static DECLCALLBACK(int) ohciRhAttach(PVUSBIROOTHUBPORT pInterface, PVUSBIDEVICE pDev, unsigned uPort)
905{
906 POHCI pOhci = VUSBIROOTHUBPORT_2_OHCI(pInterface);
907 LogFlow(("ohciRhAttach: pDev=%p uPort=%u\n", pDev, uPort));
908
909 /*
910 * Validate and adjust input.
911 */
912 Assert(uPort >= 1 && uPort <= RT_ELEMENTS(pOhci->RootHub.aPorts));
913 uPort--;
914 Assert(!pOhci->RootHub.aPorts[uPort].pDev);
915
916 /*
917 * Attach it.
918 */
919 pOhci->RootHub.aPorts[uPort].fReg = OHCI_PORT_R_CURRENT_CONNECT_STATUS | OHCI_PORT_R_CONNECT_STATUS_CHANGE;
920 pOhci->RootHub.aPorts[uPort].pDev = pDev;
921 rhport_power(&pOhci->RootHub, uPort, 1 /* power on */);
922
923 ohci_remote_wakeup(pOhci);
924 ohciSetInterrupt(pOhci, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
925
926 return VINF_SUCCESS;
927}
928
929
930/**
931 * A device is being detached from a port in the roothub.
932 *
933 * @param pInterface Pointer to this structure.
934 * @param pDev Pointer to the device being detached.
935 * @param uPort The port number assigned to the device.
936 */
937static DECLCALLBACK(void) ohciRhDetach(PVUSBIROOTHUBPORT pInterface, PVUSBIDEVICE pDev, unsigned uPort)
938{
939 POHCI pOhci = VUSBIROOTHUBPORT_2_OHCI(pInterface);
940 LogFlow(("ohciRhDetach: pDev=%p uPort=%u\n", pDev, uPort));
941
942 /*
943 * Validate and adjust input.
944 */
945 Assert(uPort >= 1 && uPort <= RT_ELEMENTS(pOhci->RootHub.aPorts));
946 uPort--;
947 Assert(pOhci->RootHub.aPorts[uPort].pDev == pDev);
948
949 /*
950 * Detach it.
951 */
952 pOhci->RootHub.aPorts[uPort].pDev = NULL;
953 if (pOhci->RootHub.aPorts[uPort].fReg & OHCI_PORT_PES)
954 pOhci->RootHub.aPorts[uPort].fReg = OHCI_PORT_R_CONNECT_STATUS_CHANGE | OHCI_PORT_PESC;
955 else
956 pOhci->RootHub.aPorts[uPort].fReg = OHCI_PORT_R_CONNECT_STATUS_CHANGE;
957
958 ohci_remote_wakeup(pOhci);
959 ohciSetInterrupt(pOhci, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
960}
961
962
963#ifdef IN_RING3
964/**
965 * One of the roothub devices has completed its reset operation.
966 *
967 * Currently, we don't think anything is required to be done here
968 * so it's just a stub for forcing async resetting of the devices
969 * during a root hub reset.
970 *
971 * @param pDev The root hub device.
972 * @param rc The result of the operation.
973 * @param pvUser Pointer to the controller.
974 */
975static DECLCALLBACK(void) ohciRhResetDoneOneDev(PVUSBIDEVICE pDev, int rc, void *pvUser)
976{
977 LogRel(("OHCI: root hub reset completed with %Rrc\n", rc));
978 NOREF(pDev); NOREF(rc); NOREF(pvUser);
979}
980#endif
981
982
983/**
984 * Reset the root hub.
985 *
986 * @returns VBox status code.
987 * @param pInterface Pointer to this structure.
988 * @param fResetOnLinux This is used to indicate whether we're at VM reset time and
989 * can do real resets or if we're at any other time where that
990 * isn't such a good idea.
991 * @remark Do NOT call VUSBIDevReset on the root hub in an async fashion!
992 * @thread EMT
993 */
994static DECLCALLBACK(int) ohciRhReset(PVUSBIROOTHUBPORT pInterface, bool fResetOnLinux)
995{
996 POHCI pOhci = VUSBIROOTHUBPORT_2_OHCI(pInterface);
997
998 pOhci->RootHub.status = 0;
999 pOhci->RootHub.desc_a = OHCI_RHA_NPS | OHCI_NDP;
1000 pOhci->RootHub.desc_b = 0x0; /* Impl. specific */
1001
1002 /*
1003 * We're prending to _reattach_ the device without resetting them.
1004 * Except, during VM reset where we use the opportunity to do a proper
1005 * reset before the guest comes along and expect things.
1006 *
1007 * However, it's very very likely that we're not doing the right thing
1008 * here if comming from the guest (USB Reset state). The docs talks about
1009 * root hub resetting, however what exact behaviour in terms of root hub
1010 * status and changed bits, and HC interrupts aren't stated clearly. IF we
1011 * get trouble and see the guest doing "USB Resets" we will have to look
1012 * into this. For the time being we stick with simple.
1013 */
1014 for (unsigned iPort = 0; iPort < RT_ELEMENTS(pOhci->RootHub.aPorts); iPort++)
1015 {
1016 if (pOhci->RootHub.aPorts[iPort].pDev)
1017 {
1018 pOhci->RootHub.aPorts[iPort].fReg = OHCI_PORT_R_CURRENT_CONNECT_STATUS | OHCI_PORT_R_CONNECT_STATUS_CHANGE;
1019 if (fResetOnLinux)
1020 {
1021 PVM pVM = PDMDevHlpGetVM(pOhci->CTX_SUFF(pDevIns));
1022 VUSBIDevReset(pOhci->RootHub.aPorts[iPort].pDev, fResetOnLinux, ohciRhResetDoneOneDev, pOhci, pVM);
1023 }
1024 }
1025 else
1026 pOhci->RootHub.aPorts[iPort].fReg = 0;
1027 }
1028
1029 return VINF_SUCCESS;
1030}
1031
1032
1033/**
1034 * Does a software or hardware reset of the controller.
1035 *
1036 * This is called in response to setting HcCommandStatus.HCR, hardware reset,
1037 * and device construction.
1038 *
1039 * @param pOhci The ohci instance data.
1040 * @param fNewMode The new mode of operation. This is UsbSuspend if it's a
1041 * software reset, and UsbReset if it's a hardware reset / cold boot.
1042 * @param fResetOnLinux Set if we can do a real reset of the devices attached to the root hub.
1043 * This is really a just a hack for the non-working linux device reset.
1044 * Linux has this feature called 'logical disconnect' if device reset fails
1045 * which prevents us from doing resets when the guest asks for it - the guest
1046 * will get confused when the device seems to be reconnected everytime it tries
1047 * to reset it. But if we're at hardware reset time, we can allow a device to
1048 * be 'reconnected' without upsetting the guest.
1049 *
1050 * @remark This hasn't got anything to do with software setting the mode to UsbReset.
1051 */
1052static void ohciDoReset(POHCI pOhci, uint32_t fNewMode, bool fResetOnLinux)
1053{
1054 Log(("ohci: %s reset%s\n", fNewMode == OHCI_USB_RESET ? "hardware" : "software",
1055 fResetOnLinux ? " (reset on linux)" : ""));
1056
1057 /*
1058 * Cancel all outstanding URBs.
1059 *
1060 * We can't, and won't, deal with URBs until we're moved out of the
1061 * suspend/reset state. Also, a real HC isn't going to send anything
1062 * any more when a reset has been signaled.
1063 */
1064 pOhci->RootHub.pIRhConn->pfnCancelAllUrbs(pOhci->RootHub.pIRhConn);
1065
1066 /*
1067 * Reset the hardware registers.
1068 */
1069 if (fNewMode == OHCI_USB_RESET)
1070 pOhci->ctl |= OHCI_CTL_RWC; /* We're the firmware, set RemoteWakeupConnected. */
1071 else
1072 pOhci->ctl &= OHCI_CTL_IR | OHCI_CTL_RWC; /* IR and RWC are preserved on software reset. */
1073 pOhci->ctl |= fNewMode;
1074 pOhci->status = 0;
1075 pOhci->intr_status = 0;
1076 pOhci->intr = OHCI_INTR_MASTER_INTERRUPT_ENABLED; /* (We follow the text and the not reset value column,) */
1077
1078 pOhci->hcca = 0;
1079 pOhci->per_cur = 0;
1080 pOhci->ctrl_head = pOhci->ctrl_cur = 0;
1081 pOhci->bulk_head = pOhci->bulk_cur = 0;
1082 pOhci->done = 0;
1083
1084 pOhci->fsmps = 0x2778; /* To-Be-Defined, use the value linux sets...*/
1085 pOhci->fit = 0;
1086 pOhci->fi = 11999; /* (12MHz ticks, one frame is 1ms) */
1087 pOhci->frt = 0;
1088 pOhci->HcFmNumber = 0;
1089 pOhci->pstart = 0;
1090
1091 pOhci->dqic = 0x7;
1092 pOhci->fno = 0;
1093
1094 /*
1095 * If this is a hardware reset, we will initialize the root hub too.
1096 * Software resets doesn't do this according to the specs.
1097 * (It's not possible to have device connected at the time of the
1098 * device construction, so nothing to worry about there.)
1099 */
1100 if (fNewMode == OHCI_USB_RESET)
1101 VUSBIDevReset(pOhci->RootHub.pIDev, fResetOnLinux, NULL, NULL, NULL);
1102}
1103#endif /* IN_RING3 */
1104
1105/**
1106 * Reads physical memory.
1107 */
1108DECLINLINE(void) ohciPhysRead(POHCI pOhci, uint32_t Addr, void *pvBuf, size_t cbBuf)
1109{
1110 PDMDevHlpPhysRead(pOhci->CTX_SUFF(pDevIns), Addr, pvBuf, cbBuf);
1111}
1112
1113/**
1114 * Writes physical memory.
1115 */
1116DECLINLINE(void) ohciPhysWrite(POHCI pOhci, uint32_t Addr, const void *pvBuf, size_t cbBuf)
1117{
1118 PDMDevHlpPhysWrite(pOhci->CTX_SUFF(pDevIns), Addr, pvBuf, cbBuf);
1119}
1120
1121/**
1122 * Read an array of dwords from physical memory and correct endianness.
1123 */
1124DECLINLINE(void) ohciGetDWords(POHCI pOhci, uint32_t Addr, uint32_t *pau32s, int c32s)
1125{
1126 ohciPhysRead(pOhci, Addr, pau32s, c32s * sizeof(uint32_t));
1127#if BYTE_ORDER != LITTLE_ENDIAN
1128 for(int i = 0; i < c32s; i++)
1129 pau32s[i] = RT_H2LE_U32(pau32s[i]);
1130#endif
1131}
1132
1133/**
1134 * Write an array of dwords from physical memory and correct endianness.
1135 */
1136DECLINLINE(void) ohciPutDWords(POHCI pOhci, uint32_t Addr, const uint32_t *pau32s, int cu32s)
1137{
1138#if BYTE_ORDER == LITTLE_ENDIAN
1139 ohciPhysWrite(pOhci, Addr, pau32s, cu32s << 2);
1140#else
1141 for (int i = 0; i < c32s; i++, pau32s++, Addr += sizeof(*pau32s))
1142 {
1143 uint32_t u32Tmp = RT_H2LE_U32((*pau32s);
1144 ohciPhysWrite(pOhci, Addr, (uint8_t *)&u32Tmp, sizeof(u32Tmp));
1145 }
1146#endif
1147}
1148
1149
1150#ifdef IN_RING3
1151
1152/**
1153 * Reads an OHCIED.
1154 */
1155DECLINLINE(void) ohciReadEd(POHCI pOhci, uint32_t EdAddr, POHCIED pEd)
1156{
1157 ohciGetDWords(pOhci, EdAddr, (uint32_t *)pEd, sizeof(*pEd) >> 2);
1158}
1159
1160/**
1161 * Reads an OHCITD.
1162 */
1163DECLINLINE(void) ohciReadTd(POHCI pOhci, uint32_t TdAddr, POHCITD pTd)
1164{
1165 ohciGetDWords(pOhci, TdAddr, (uint32_t *)pTd, sizeof(*pTd) >> 2);
1166#ifdef LOG_ENABLED
1167 if (LogIs3Enabled())
1168 {
1169 uint32_t hichg;
1170 hichg = pTd->hwinfo;
1171 Log3(("ohciReadTd(,%#010x,): R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x UNK=%#x\n",
1172 TdAddr,
1173 (pTd->hwinfo >> 18) & 1,
1174 (pTd->hwinfo >> 19) & 3,
1175 (pTd->hwinfo >> 21) & 7,
1176 (pTd->hwinfo >> 24) & 3,
1177 (pTd->hwinfo >> 26) & 3,
1178 (pTd->hwinfo >> 28) &15,
1179 pTd->cbp,
1180 pTd->NextTD,
1181 pTd->be,
1182 pTd->hwinfo & TD_HWINFO_UNKNOWN_MASK));
1183#if 0
1184 if (LogIs3Enabled())
1185 {
1186 /*
1187 * usbohci.sys (32-bit XP) allocates 0x80 bytes per TD:
1188 * 0x00-0x0f is the OHCI TD.
1189 * 0x10-0x1f for isochronous TDs
1190 * 0x20 is the physical address of this TD.
1191 * 0x24 is initialized with 0x64745948, probably a magic.
1192 * 0x28 is some kind of flags. the first bit begin the allocated / not allocated indicator.
1193 * 0x30 is a pointer to something. endpoint? interface? device?
1194 * 0x38 is initialized to 0xdeadface. but is changed into a pointer or something.
1195 * 0x40 looks like a pointer.
1196 * The rest is unknown and initialized with zeros.
1197 */
1198 uint8_t abXpTd[0x80];
1199 ohciPhysRead(pOhci, TdAddr, abXpTd, sizeof(abXpTd));
1200 Log3(("WinXpTd: alloc=%d PhysSelf=%RX32 s2=%RX32 magic=%RX32 s4=%RX32 s5=%RX32\n"
1201 "%.*Rhxd\n",
1202 abXpTd[28] & RT_BIT(0),
1203 *((uint32_t *)&abXpTd[0x20]), *((uint32_t *)&abXpTd[0x30]),
1204 *((uint32_t *)&abXpTd[0x24]), *((uint32_t *)&abXpTd[0x38]),
1205 *((uint32_t *)&abXpTd[0x40]),
1206 sizeof(abXpTd), &abXpTd[0]));
1207 }
1208#endif
1209 }
1210#endif
1211}
1212
1213/**
1214 * Reads an OHCIITD.
1215 */
1216DECLINLINE(void) ohciReadITd(POHCI pOhci, uint32_t ITdAddr, POHCIITD pITd)
1217{
1218 ohciGetDWords(pOhci, ITdAddr, (uint32_t *)pITd, sizeof(*pITd) / sizeof(uint32_t));
1219#ifdef LOG_ENABLED
1220 if (LogIs3Enabled())
1221 {
1222 Log3(("ohciReadITd(,%#010x,): SF=%#06x (%#RX32) DI=%#x FC=%d CC=%#x BP0=%#010x NextTD=%#010x BE=%#010x\n",
1223 ITdAddr,
1224 pITd->HwInfo & 0xffff, pOhci->HcFmNumber,
1225 (pITd->HwInfo >> 21) & 7,
1226 (pITd->HwInfo >> 24) & 7,
1227 (pITd->HwInfo >> 28) &15,
1228 pITd->BP0,
1229 pITd->NextTD,
1230 pITd->BE));
1231 Log3(("psw0=%x:%03x psw1=%x:%03x psw2=%x:%03x psw3=%x:%03x psw4=%x:%03x psw5=%x:%03x psw6=%x:%03x psw7=%x:%03x\n",
1232 pITd->aPSW[0] >> 12, pITd->aPSW[0] & 0xfff,
1233 pITd->aPSW[1] >> 12, pITd->aPSW[1] & 0xfff,
1234 pITd->aPSW[2] >> 12, pITd->aPSW[2] & 0xfff,
1235 pITd->aPSW[3] >> 12, pITd->aPSW[3] & 0xfff,
1236 pITd->aPSW[4] >> 12, pITd->aPSW[4] & 0xfff,
1237 pITd->aPSW[5] >> 12, pITd->aPSW[5] & 0xfff,
1238 pITd->aPSW[6] >> 12, pITd->aPSW[6] & 0xfff,
1239 pITd->aPSW[7] >> 12, pITd->aPSW[7] & 0xfff));
1240 }
1241#endif
1242}
1243
1244
1245/**
1246 * Writes an OHCIED.
1247 */
1248DECLINLINE(void) ohciWriteEd(POHCI pOhci, uint32_t EdAddr, PCOHCIED pEd)
1249{
1250#ifdef LOG_ENABLED
1251 if (LogIs3Enabled())
1252 {
1253 OHCIED EdOld;
1254 uint32_t hichg;
1255
1256 ohciGetDWords(pOhci, EdAddr, (uint32_t *)&EdOld, sizeof(EdOld) >> 2);
1257 hichg = EdOld.hwinfo ^ pEd->hwinfo;
1258 Log3(("ohciWriteEd(,%#010x,): %sFA=%#x %sEN=%#x %sD=%#x %sS=%d %sK=%d %sF=%d %sMPS=%#x %sTailP=%#010x %sHeadP=%#010x %sH=%d %sC=%d %sNextED=%#010x\n",
1259 EdAddr,
1260 (hichg >> 0) & 0x7f ? "*" : "", (pEd->hwinfo >> 0) & 0x7f,
1261 (hichg >> 7) & 0xf ? "*" : "", (pEd->hwinfo >> 7) & 0xf,
1262 (hichg >> 11) & 3 ? "*" : "", (pEd->hwinfo >> 11) & 3,
1263 (hichg >> 13) & 1 ? "*" : "", (pEd->hwinfo >> 13) & 1,
1264 (hichg >> 14) & 1 ? "*" : "", (pEd->hwinfo >> 14) & 1,
1265 (hichg >> 15) & 1 ? "*" : "", (pEd->hwinfo >> 15) & 1,
1266 (hichg >> 24) &0x3ff ? "*" : "", (pEd->hwinfo >> 16) &0x3ff,
1267 EdOld.TailP != pEd->TailP ? "*" : "", pEd->TailP,
1268 (EdOld.HeadP & ~3) != (pEd->HeadP & ~3) ? "*" : "", pEd->HeadP & ~3,
1269 (EdOld.HeadP ^ pEd->HeadP) & 1 ? "*" : "", pEd->HeadP & 1,
1270 (EdOld.HeadP ^ pEd->HeadP) & 2 ? "*" : "", (pEd->HeadP >> 1) & 1,
1271 EdOld.NextED != pEd->NextED ? "*" : "", pEd->NextED));
1272 }
1273#endif
1274
1275 ohciPutDWords(pOhci, EdAddr, (uint32_t *)pEd, sizeof(*pEd) >> 2);
1276}
1277
1278
1279/**
1280 * Writes an OHCITD.
1281 */
1282DECLINLINE(void) ohciWriteTd(POHCI pOhci, uint32_t TdAddr, PCOHCITD pTd, const char *pszLogMsg)
1283{
1284#ifdef LOG_ENABLED
1285 if (LogIs3Enabled())
1286 {
1287 OHCITD TdOld;
1288 ohciGetDWords(pOhci, TdAddr, (uint32_t *)&TdOld, sizeof(TdOld) >> 2);
1289 uint32_t hichg = TdOld.hwinfo ^ pTd->hwinfo;
1290 Log3(("ohciWriteTd(,%#010x,): %sR=%d %sDP=%d %sDI=%#x %sT=%d %sEC=%d %sCC=%#x %sCBP=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
1291 TdAddr,
1292 (hichg >> 18) & 1 ? "*" : "", (pTd->hwinfo >> 18) & 1,
1293 (hichg >> 19) & 3 ? "*" : "", (pTd->hwinfo >> 19) & 3,
1294 (hichg >> 21) & 7 ? "*" : "", (pTd->hwinfo >> 21) & 7,
1295 (hichg >> 24) & 3 ? "*" : "", (pTd->hwinfo >> 24) & 3,
1296 (hichg >> 26) & 3 ? "*" : "", (pTd->hwinfo >> 26) & 3,
1297 (hichg >> 28) &15 ? "*" : "", (pTd->hwinfo >> 28) &15,
1298 TdOld.cbp != pTd->cbp ? "*" : "", pTd->cbp,
1299 TdOld.NextTD != pTd->NextTD ? "*" : "", pTd->NextTD,
1300 TdOld.be != pTd->be ? "*" : "", pTd->be,
1301 pszLogMsg));
1302 }
1303#endif
1304 ohciPutDWords(pOhci, TdAddr, (uint32_t *)pTd, sizeof(*pTd) >> 2);
1305}
1306
1307/**
1308 * Writes an OHCIITD.
1309 */
1310DECLINLINE(void) ohciWriteITd(POHCI pOhci, uint32_t ITdAddr, PCOHCIITD pITd, const char *pszLogMsg)
1311{
1312#ifdef LOG_ENABLED
1313 if (LogIs3Enabled())
1314 {
1315 OHCIITD ITdOld;
1316 ohciGetDWords(pOhci, ITdAddr, (uint32_t *)&ITdOld, sizeof(ITdOld) / sizeof(uint32_t));
1317 uint32_t HIChg = ITdOld.HwInfo ^ pITd->HwInfo;
1318 Log3(("ohciWriteITd(,%#010x,): %sSF=%#x (now=%#RX32) %sDI=%#x %sFC=%d %sCC=%#x %sBP0=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
1319 ITdAddr,
1320 (HIChg & 0xffff) & 1 ? "*" : "", pITd->HwInfo & 0xffff, pOhci->HcFmNumber,
1321 (HIChg >> 21) & 7 ? "*" : "", (pITd->HwInfo >> 21) & 7,
1322 (HIChg >> 24) & 7 ? "*" : "", (pITd->HwInfo >> 24) & 7,
1323 (HIChg >> 28) &15 ? "*" : "", (pITd->HwInfo >> 28) &15,
1324 ITdOld.BP0 != pITd->BP0 ? "*" : "", pITd->BP0,
1325 ITdOld.NextTD != pITd->NextTD ? "*" : "", pITd->NextTD,
1326 ITdOld.BE != pITd->BE ? "*" : "", pITd->BE,
1327 pszLogMsg));
1328 Log3(("psw0=%s%x:%s%03x psw1=%s%x:%s%03x psw2=%s%x:%s%03x psw3=%s%x:%s%03x psw4=%s%x:%s%03x psw5=%s%x:%s%03x psw6=%s%x:%s%03x psw7=%s%x:%s%03x\n",
1329 (ITdOld.aPSW[0] >> 12) != (pITd->aPSW[0] >> 12) ? "*" : "", pITd->aPSW[0] >> 12, (ITdOld.aPSW[0] & 0xfff) != (pITd->aPSW[0] & 0xfff) ? "*" : "", pITd->aPSW[0] & 0xfff,
1330 (ITdOld.aPSW[1] >> 12) != (pITd->aPSW[1] >> 12) ? "*" : "", pITd->aPSW[1] >> 12, (ITdOld.aPSW[1] & 0xfff) != (pITd->aPSW[1] & 0xfff) ? "*" : "", pITd->aPSW[1] & 0xfff,
1331 (ITdOld.aPSW[2] >> 12) != (pITd->aPSW[2] >> 12) ? "*" : "", pITd->aPSW[2] >> 12, (ITdOld.aPSW[2] & 0xfff) != (pITd->aPSW[2] & 0xfff) ? "*" : "", pITd->aPSW[2] & 0xfff,
1332 (ITdOld.aPSW[3] >> 12) != (pITd->aPSW[3] >> 12) ? "*" : "", pITd->aPSW[3] >> 12, (ITdOld.aPSW[3] & 0xfff) != (pITd->aPSW[3] & 0xfff) ? "*" : "", pITd->aPSW[3] & 0xfff,
1333 (ITdOld.aPSW[4] >> 12) != (pITd->aPSW[4] >> 12) ? "*" : "", pITd->aPSW[4] >> 12, (ITdOld.aPSW[4] & 0xfff) != (pITd->aPSW[4] & 0xfff) ? "*" : "", pITd->aPSW[4] & 0xfff,
1334 (ITdOld.aPSW[5] >> 12) != (pITd->aPSW[5] >> 12) ? "*" : "", pITd->aPSW[5] >> 12, (ITdOld.aPSW[5] & 0xfff) != (pITd->aPSW[5] & 0xfff) ? "*" : "", pITd->aPSW[5] & 0xfff,
1335 (ITdOld.aPSW[6] >> 12) != (pITd->aPSW[6] >> 12) ? "*" : "", pITd->aPSW[6] >> 12, (ITdOld.aPSW[6] & 0xfff) != (pITd->aPSW[6] & 0xfff) ? "*" : "", pITd->aPSW[6] & 0xfff,
1336 (ITdOld.aPSW[7] >> 12) != (pITd->aPSW[7] >> 12) ? "*" : "", pITd->aPSW[7] >> 12, (ITdOld.aPSW[7] & 0xfff) != (pITd->aPSW[7] & 0xfff) ? "*" : "", pITd->aPSW[7] & 0xfff));
1337 }
1338#endif
1339 ohciPutDWords(pOhci, ITdAddr, (uint32_t *)pITd, sizeof(*pITd) / sizeof(uint32_t));
1340}
1341
1342
1343#ifdef LOG_ENABLED
1344
1345/**
1346 * Core TD queue dumper. LOG_ENABLED builds only.
1347 */
1348DECLINLINE(void) ohciDumpTdQueueCore(POHCI pOhci, uint32_t GCPhysHead, uint32_t GCPhysTail, bool fFull)
1349{
1350 uint32_t GCPhys = GCPhysHead;
1351 int cMax = 100;
1352 for (;;)
1353 {
1354 OHCITD Td;
1355 Log4(("%#010x%s%s", GCPhys,
1356 GCPhys && ohci_in_flight_find(pOhci, GCPhys) >= 0 ? "~" : "",
1357 GCPhys && ohci_in_done_queue_find(pOhci, GCPhys) >= 0 ? "^" : ""));
1358 if (GCPhys == 0 || GCPhys == GCPhysTail)
1359 break;
1360
1361 /* can't use ohciReadTd() because of Log4. */
1362 ohciGetDWords(pOhci, GCPhys, (uint32_t *)&Td, sizeof(Td) >> 2);
1363 if (fFull)
1364 Log4((" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ",
1365 (Td.hwinfo >> 18) & 1,
1366 (Td.hwinfo >> 19) & 3,
1367 (Td.hwinfo >> 21) & 7,
1368 (Td.hwinfo >> 24) & 3,
1369 (Td.hwinfo >> 26) & 3,
1370 (Td.hwinfo >> 28) &15,
1371 Td.cbp,
1372 Td.NextTD,
1373 Td.be));
1374 else
1375 Log4((" -> "));
1376 GCPhys = Td.NextTD & ED_PTR_MASK;
1377 Assert(GCPhys != GCPhysHead);
1378 Assert(cMax-- > 0); NOREF(cMax);
1379 }
1380}
1381
1382/**
1383 * Dumps a TD queue. LOG_ENABLED builds only.
1384 */
1385DECLINLINE(void) ohciDumpTdQueue(POHCI pOhci, uint32_t GCPhysHead, const char *pszMsg)
1386{
1387 if (pszMsg)
1388 Log4(("%s: ", pszMsg));
1389 ohciDumpTdQueueCore(pOhci, GCPhysHead, 0, true);
1390 Log4(("\n"));
1391}
1392
1393/**
1394 * Core ITD queue dumper. LOG_ENABLED builds only.
1395 */
1396DECLINLINE(void) ohciDumpITdQueueCore(POHCI pOhci, uint32_t GCPhysHead, uint32_t GCPhysTail, bool fFull)
1397{
1398 uint32_t GCPhys = GCPhysHead;
1399 int cMax = 100;
1400 for (;;)
1401 {
1402 OHCIITD ITd;
1403 Log4(("%#010x%s%s", GCPhys,
1404 GCPhys && ohci_in_flight_find(pOhci, GCPhys) >= 0 ? "~" : "",
1405 GCPhys && ohci_in_done_queue_find(pOhci, GCPhys) >= 0 ? "^" : ""));
1406 if (GCPhys == 0 || GCPhys == GCPhysTail)
1407 break;
1408
1409 /* can't use ohciReadTd() because of Log4. */
1410 ohciGetDWords(pOhci, GCPhys, (uint32_t *)&ITd, sizeof(ITd) / sizeof(uint32_t));
1411 /*if (fFull)
1412 Log4((" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ",
1413 (Td.hwinfo >> 18) & 1,
1414 (Td.hwinfo >> 19) & 3,
1415 (Td.hwinfo >> 21) & 7,
1416 (Td.hwinfo >> 24) & 3,
1417 (Td.hwinfo >> 26) & 3,
1418 (Td.hwinfo >> 28) &15,
1419 Td.cbp,
1420 Td.NextTD,
1421 Td.be));
1422 else*/
1423 Log4((" -> "));
1424 GCPhys = ITd.NextTD & ED_PTR_MASK;
1425 Assert(GCPhys != GCPhysHead);
1426 Assert(cMax-- > 0); NOREF(cMax);
1427 }
1428}
1429
1430/**
1431 * Dumps a ED list. LOG_ENABLED builds only.
1432 */
1433DECLINLINE(void) ohciDumpEdList(POHCI pOhci, uint32_t GCPhysHead, const char *pszMsg, bool fTDs)
1434{
1435 uint32_t GCPhys = GCPhysHead;
1436 if (pszMsg)
1437 Log4(("%s:", pszMsg));
1438 for (;;)
1439 {
1440 OHCIED Ed;
1441
1442 /* ED */
1443 Log4((" %#010x={", GCPhys));
1444 if (!GCPhys)
1445 {
1446 Log4(("END}\n"));
1447 return;
1448 }
1449
1450 /* TDs */
1451 ohciReadEd(pOhci, GCPhys, &Ed);
1452 if (Ed.hwinfo & ED_HWINFO_ISO)
1453 Log4(("[I]"));
1454 if ((Ed.HeadP & ED_HEAD_HALTED) || (Ed.hwinfo & ED_HWINFO_SKIP))
1455 {
1456 if ((Ed.HeadP & ED_HEAD_HALTED) && (Ed.hwinfo & ED_HWINFO_SKIP))
1457 Log4(("SH}"));
1458 else if (Ed.hwinfo & ED_HWINFO_SKIP)
1459 Log4(("S-}"));
1460 else
1461 Log4(("-H}"));
1462 }
1463 else
1464 {
1465 if (Ed.hwinfo & ED_HWINFO_ISO)
1466 ohciDumpITdQueueCore(pOhci, Ed.HeadP & ED_PTR_MASK, Ed.TailP & ED_PTR_MASK, false);
1467 else
1468 ohciDumpTdQueueCore(pOhci, Ed.HeadP & ED_PTR_MASK, Ed.TailP & ED_PTR_MASK, false);
1469 Log4(("}"));
1470 }
1471
1472 /* next */
1473 GCPhys = Ed.NextED & ED_PTR_MASK;
1474 Assert(GCPhys != GCPhysHead);
1475 }
1476 Log4(("\n"));
1477}
1478
1479#endif /* LOG_ENABLED */
1480
1481
1482DECLINLINE(int) ohci_in_flight_find_free(POHCI pOhci, const int iStart)
1483{
1484 unsigned i = iStart;
1485 while (i < RT_ELEMENTS(pOhci->aInFlight))
1486 {
1487 if (pOhci->aInFlight[i].GCPhysTD == 0)
1488 return i;
1489 i++;
1490 }
1491 i = iStart;
1492 while (i-- > 0)
1493 {
1494 if (pOhci->aInFlight[i].GCPhysTD == 0)
1495 return i;
1496 }
1497 return -1;
1498}
1499
1500
1501/**
1502 * Record an in-flight TD.
1503 *
1504 * @param pOhci OHCI instance data.
1505 * @param GCPhysTD Physical address of the TD.
1506 * @param pUrb The URB.
1507 */
1508static void ohci_in_flight_add(POHCI pOhci, uint32_t GCPhysTD, PVUSBURB pUrb)
1509{
1510 int i = ohci_in_flight_find_free(pOhci, (GCPhysTD >> 4) % RT_ELEMENTS(pOhci->aInFlight));
1511 if (i >= 0)
1512 {
1513#ifdef LOG_ENABLED
1514 pUrb->Hci.u32FrameNo = pOhci->HcFmNumber;
1515#endif
1516 pOhci->aInFlight[i].GCPhysTD = GCPhysTD;
1517 pOhci->aInFlight[i].pUrb = pUrb;
1518 pOhci->cInFlight++;
1519 return;
1520 }
1521 AssertMsgFailed(("Out of space cInFlight=%d!\n", pOhci->cInFlight));
1522}
1523
1524
1525/**
1526 * Record in-flight TDs for an URB.
1527 *
1528 * @param pOhci OHCI instance data.
1529 * @param pUrb The URB.
1530 */
1531static void ohci_in_flight_add_urb(POHCI pOhci, PVUSBURB pUrb)
1532{
1533 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
1534 ohci_in_flight_add(pOhci, pUrb->Hci.paTds[iTd].TdAddr, pUrb);
1535}
1536
1537
1538/**
1539 * Finds a in-flight TD.
1540 *
1541 * @returns Index of the record.
1542 * @returns -1 if not found.
1543 * @param pOhci OHCI instance data.
1544 * @param GCPhysTD Physical address of the TD.
1545 * @remark This has to be fast.
1546 */
1547static int ohci_in_flight_find(POHCI pOhci, uint32_t GCPhysTD)
1548{
1549 unsigned cLeft = pOhci->cInFlight;
1550 unsigned i = (GCPhysTD >> 4) % RT_ELEMENTS(pOhci->aInFlight);
1551 const int iLast = i;
1552 while (i < RT_ELEMENTS(pOhci->aInFlight))
1553 {
1554 if (pOhci->aInFlight[i].GCPhysTD == GCPhysTD)
1555 return i;
1556 if (pOhci->aInFlight[i].GCPhysTD)
1557 if (cLeft-- <= 1)
1558 return -1;
1559 i++;
1560 }
1561 i = iLast;
1562 while (i-- > 0)
1563 {
1564 if (pOhci->aInFlight[i].GCPhysTD == GCPhysTD)
1565 return i;
1566 if (pOhci->aInFlight[i].GCPhysTD)
1567 if (cLeft-- <= 1)
1568 return -1;
1569 }
1570 return -1;
1571}
1572
1573
1574/**
1575 * Checks if a TD is in-flight.
1576 *
1577 * @returns true if in flight, false if not.
1578 * @param pOhci OHCI instance data.
1579 * @param GCPhysTD Physical address of the TD.
1580 */
1581static bool ohciIsTdInFlight(POHCI pOhci, uint32_t GCPhysTD)
1582{
1583 return ohci_in_flight_find(pOhci, GCPhysTD) >= 0;
1584}
1585
1586
1587/**
1588 * Removes a in-flight TD.
1589 *
1590 * @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
1591 * @returns -1 if not found.
1592 * @param pOhci OHCI instance data.
1593 * @param GCPhysTD Physical address of the TD.
1594 */
1595static int ohci_in_flight_remove(POHCI pOhci, uint32_t GCPhysTD)
1596{
1597 int i = ohci_in_flight_find(pOhci, GCPhysTD);
1598 if (i >= 0)
1599 {
1600#ifdef LOG_ENABLED
1601 const int cFramesInFlight = pOhci->HcFmNumber - pOhci->aInFlight[i].pUrb->Hci.u32FrameNo;
1602#else
1603 const int cFramesInFlight = 0;
1604#endif
1605 Log2(("ohci_in_flight_remove: reaping TD=%#010x %d frames (%#010x-%#010x)\n",
1606 GCPhysTD, cFramesInFlight, pOhci->aInFlight[i].pUrb->Hci.u32FrameNo, pOhci->HcFmNumber));
1607 pOhci->aInFlight[i].GCPhysTD = 0;
1608 pOhci->aInFlight[i].pUrb = NULL;
1609 pOhci->cInFlight--;
1610 return cFramesInFlight;
1611 }
1612 AssertMsgFailed(("TD %#010x is not in flight\n", GCPhysTD));
1613 return -1;
1614}
1615
1616
1617/**
1618 * Removes all TDs associated with a URB from the in-flight tracking.
1619 *
1620 * @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
1621 * @returns -1 if not found.
1622 * @param pOhci OHCI instance data.
1623 * @param pUrb The URB.
1624 */
1625static int ohci_in_flight_remove_urb(POHCI pOhci, PVUSBURB pUrb)
1626{
1627 int cFramesInFlight = ohci_in_flight_remove(pOhci, pUrb->Hci.paTds[0].TdAddr);
1628 if (pUrb->Hci.cTds > 1)
1629 {
1630 for (unsigned iTd = 1; iTd < pUrb->Hci.cTds; iTd++)
1631 if (ohci_in_flight_remove(pOhci, pUrb->Hci.paTds[iTd].TdAddr) < 0)
1632 cFramesInFlight = -1;
1633 }
1634 return cFramesInFlight;
1635}
1636
1637
1638#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
1639
1640/**
1641 * Empties the in-done-queue.
1642 * @param pOhci OHCI instance data.
1643 */
1644static void ohci_in_done_queue_zap(POHCI pOhci)
1645{
1646 pOhci->cInDoneQueue = 0;
1647}
1648
1649/**
1650 * Finds a TD in the in-done-queue.
1651 * @returns >= 0 on success.
1652 * @returns -1 if not found.
1653 * @param pOhci OHCI instance data.
1654 * @param GCPhysTD Physical address of the TD.
1655 */
1656static int ohci_in_done_queue_find(POHCI pOhci, uint32_t GCPhysTD)
1657{
1658 unsigned i = pOhci->cInDoneQueue;
1659 while (i-- > 0)
1660 if (pOhci->aInDoneQueue[i].GCPhysTD == GCPhysTD)
1661 return i;
1662 return -1;
1663}
1664
1665/**
1666 * Checks that the specified TD is not in the done queue.
1667 * @param pOhci OHCI instance data.
1668 * @param GCPhysTD Physical address of the TD.
1669 */
1670static bool ohci_in_done_queue_check(POHCI pOhci, uint32_t GCPhysTD)
1671{
1672 int i = ohci_in_done_queue_find(pOhci, GCPhysTD);
1673 AssertMsg(i < 0, ("TD %#010x (i=%d)\n", GCPhysTD, i));
1674 return i < 0;
1675}
1676
1677
1678# ifdef VBOX_STRICT
1679/**
1680 * Adds a TD to the in-done-queue tracking, checking that it's not there already.
1681 * @param pOhci OHCI instance data.
1682 * @param GCPhysTD Physical address of the TD.
1683 */
1684static void ohci_in_done_queue_add(POHCI pOhci, uint32_t GCPhysTD)
1685{
1686 Assert(pOhci->cInDoneQueue + 1 <= RT_ELEMENTS(pOhci->aInDoneQueue));
1687 if (ohci_in_done_queue_check(pOhci, GCPhysTD))
1688 pOhci->aInDoneQueue[pOhci->cInDoneQueue++].GCPhysTD = GCPhysTD;
1689}
1690# endif /* VBOX_STRICT */
1691#endif /* defined(VBOX_STRICT) || defined(LOG_ENABLED) */
1692
1693
1694/**
1695 * OHCI Transport Buffer - represents a OHCI Transport Descriptor (TD).
1696 * A TD may be split over max 2 pages.
1697 */
1698typedef struct OHCIBUF
1699{
1700 /** Pages involved. */
1701 struct OHCIBUFVEC
1702 {
1703 /** The 32-bit physical address of this part. */
1704 uint32_t Addr;
1705 /** The length. */
1706 uint32_t cb;
1707 } aVecs[2];
1708 /** Number of valid entries in aVecs. */
1709 uint32_t cVecs;
1710 /** The total length. */
1711 uint32_t cbTotal;
1712} OHCIBUF, *POHCIBUF;
1713
1714
1715/**
1716 * Sets up a OHCI transport buffer.
1717 *
1718 * @param pBuf Ohci buffer.
1719 * @param cbp Current buffer pointer. 32-bit physical address.
1720 * @param be Last byte in buffer (BufferEnd). 32-bit physical address.
1721 */
1722static void ohciBufInit(POHCIBUF pBuf, uint32_t cbp, uint32_t be)
1723{
1724 if (!cbp || !be)
1725 {
1726 pBuf->cVecs = 0;
1727 pBuf->cbTotal = 0;
1728 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=0 EMPTY\n", cbp, be));
1729 }
1730 else if ((cbp & ~0xfff) == (be & ~0xfff))
1731 {
1732 pBuf->aVecs[0].Addr = cbp;
1733 pBuf->aVecs[0].cb = (be - cbp) + 1;
1734 pBuf->cVecs = 1;
1735 pBuf->cbTotal = pBuf->aVecs[0].cb;
1736 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=%u\n", cbp, be, pBuf->cbTotal));
1737 }
1738 else
1739 {
1740 pBuf->aVecs[0].Addr = cbp;
1741 pBuf->aVecs[0].cb = 0x1000 - (cbp & 0xfff);
1742 pBuf->aVecs[1].Addr = be & ~0xfff;
1743 pBuf->aVecs[1].cb = (be & 0xfff) + 1;
1744 pBuf->cVecs = 2;
1745 pBuf->cbTotal = pBuf->aVecs[0].cb + pBuf->aVecs[1].cb;
1746 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=%u PAGE FLIP\n", cbp, be, pBuf->cbTotal));
1747 }
1748}
1749
1750/**
1751 * Updates a OHCI transport buffer.
1752 *
1753 * This is called upon completion to adjust the sector lengths if
1754 * the total length has changed. (received less then we had space for
1755 * or a parital transfer.)
1756 *
1757 * @param pBuf The buffer to update. cbTotal contains the new total on input.
1758 * While the aVecs[*].cb members is updated upon return.
1759 */
1760static void ohciBufUpdate(POHCIBUF pBuf)
1761{
1762 for (uint32_t i = 0, cbCur = 0; i < pBuf->cVecs; i++)
1763 {
1764 if (cbCur + pBuf->aVecs[i].cb > pBuf->cbTotal)
1765 {
1766 pBuf->aVecs[i].cb = pBuf->cbTotal - cbCur;
1767 pBuf->cVecs = i + 1;
1768 return;
1769 }
1770 cbCur += pBuf->aVecs[i].cb;
1771 }
1772}
1773
1774
1775/** A worker for ohciUnlinkTds(). */
1776static bool ohciUnlinkIsochronousTdInList(POHCI pOhci, uint32_t TdAddr, POHCIITD pITd, POHCIED pEd)
1777{
1778 const uint32_t LastTdAddr = pEd->TailP & ED_PTR_MASK;
1779 Log(("ohciUnlinkIsocTdInList: Unlinking non-head ITD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
1780 TdAddr, pEd->HeadP & ED_PTR_MASK, LastTdAddr));
1781 AssertMsgReturn(LastTdAddr != TdAddr, ("TdAddr=%#010RX32\n", TdAddr), false);
1782
1783 uint32_t cMax = 256;
1784 uint32_t CurTdAddr = pEd->HeadP & ED_PTR_MASK;
1785 while ( CurTdAddr != LastTdAddr
1786 && cMax-- > 0)
1787 {
1788 OHCIITD ITd;
1789 ohciReadITd(pOhci, CurTdAddr, &ITd);
1790 if ((ITd.NextTD & ED_PTR_MASK) == TdAddr)
1791 {
1792 ITd.NextTD = (pITd->NextTD & ED_PTR_MASK) | (ITd.NextTD & ~ED_PTR_MASK);
1793 ohciWriteITd(pOhci, CurTdAddr, &ITd, "ohciUnlinkIsocTdInList");
1794 pITd->NextTD &= ~ED_PTR_MASK;
1795 return true;
1796 }
1797
1798 /* next */
1799 CurTdAddr = ITd.NextTD & ED_PTR_MASK;
1800 }
1801
1802 Log(("ohciUnlinkIsocTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n", TdAddr, cMax));
1803 return false;
1804}
1805
1806
1807/** A worker for ohciUnlinkTds(). */
1808static bool ohciUnlinkGeneralTdInList(POHCI pOhci, uint32_t TdAddr, POHCITD pTd, POHCIED pEd)
1809{
1810 const uint32_t LastTdAddr = pEd->TailP & ED_PTR_MASK;
1811 Log(("ohciUnlinkGeneralTdInList: Unlinking non-head TD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
1812 TdAddr, pEd->HeadP & ED_PTR_MASK, LastTdAddr));
1813 AssertMsgReturn(LastTdAddr != TdAddr, ("TdAddr=%#010RX32\n", TdAddr), false);
1814
1815 uint32_t cMax = 256;
1816 uint32_t CurTdAddr = pEd->HeadP & ED_PTR_MASK;
1817 while ( CurTdAddr != LastTdAddr
1818 && cMax-- > 0)
1819 {
1820 OHCITD Td;
1821 ohciReadTd(pOhci, CurTdAddr, &Td);
1822 if ((Td.NextTD & ED_PTR_MASK) == TdAddr)
1823 {
1824 Td.NextTD = (pTd->NextTD & ED_PTR_MASK) | (Td.NextTD & ~ED_PTR_MASK);
1825 ohciWriteTd(pOhci, CurTdAddr, &Td, "ohciUnlinkGeneralTdInList");
1826 pTd->NextTD &= ~ED_PTR_MASK;
1827 return true;
1828 }
1829
1830 /* next */
1831 CurTdAddr = Td.NextTD & ED_PTR_MASK;
1832 }
1833
1834 Log(("ohciUnlinkGeneralTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n", TdAddr, cMax));
1835 return false;
1836}
1837
1838
1839/**
1840 * Unlinks the TDs that makes up the URB from the ED.
1841 *
1842 * @returns success indicator. true if successfully unlinked.
1843 * @returns false if the TD was not found in the list.
1844 */
1845static bool ohciUnlinkTds(POHCI pOhci, PVUSBURB pUrb, POHCIED pEd)
1846{
1847 /*
1848 * Don't unlink more than once.
1849 */
1850 if (pUrb->Hci.fUnlinked)
1851 return true;
1852 pUrb->Hci.fUnlinked = true;
1853
1854 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
1855 {
1856 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
1857 {
1858 POHCIITD pITd = (POHCIITD)&pUrb->Hci.paTds[iTd].TdCopy[0];
1859 const uint32_t ITdAddr = pUrb->Hci.paTds[iTd].TdAddr;
1860
1861 /*
1862 * Unlink the TD from the ED list.
1863 * The normal case is that it's at the head of the list.
1864 */
1865 Assert((ITdAddr & ED_PTR_MASK) == ITdAddr);
1866 if ((pEd->HeadP & ED_PTR_MASK) == ITdAddr)
1867 {
1868 pEd->HeadP = (pITd->NextTD & ED_PTR_MASK) | (pEd->HeadP & ~ED_PTR_MASK);
1869 pITd->NextTD &= ~ED_PTR_MASK;
1870 }
1871 else
1872 {
1873 /*
1874 * It's proably somewhere in the list, not a unlikely situation with
1875 * the current isochronous code.
1876 */
1877 if (!ohciUnlinkIsochronousTdInList(pOhci, ITdAddr, pITd, pEd))
1878 return false;
1879 }
1880 }
1881 }
1882 else
1883 {
1884 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
1885 {
1886 POHCITD pTd = (POHCITD)&pUrb->Hci.paTds[iTd].TdCopy[0];
1887 const uint32_t TdAddr = pUrb->Hci.paTds[iTd].TdAddr;
1888
1889 /** @todo r=bird: Messing with the toggle flag in prepare is probably not correct
1890 * when we encounter a STALL error, 4.3.1.3.7.2: "If an endpoint returns a STALL
1891 * PID, the Host Controller retires the General TD with the ConditionCode set
1892 * to STALL and halts the endpoint. The CurrentBufferPointer, ErrorCount, and
1893 * dataToggle fields retain the values that they had at the start of the
1894 * transaction." */
1895
1896 /* update toggle and set data toggle carry */
1897 pTd->hwinfo &= ~TD_HWINFO_TOGGLE;
1898 if ( pTd->hwinfo & TD_HWINFO_TOGGLE_HI )
1899 {
1900 if ( !!(pTd->hwinfo & TD_HWINFO_TOGGLE_LO) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
1901 pTd->hwinfo |= TD_HWINFO_TOGGLE_LO;
1902 else
1903 pTd->hwinfo &= ~TD_HWINFO_TOGGLE_LO;
1904 }
1905 else
1906 {
1907 if ( !!(pEd->HeadP & ED_HEAD_CARRY) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
1908 pEd->HeadP |= ED_HEAD_CARRY;
1909 else
1910 pEd->HeadP &= ~ED_HEAD_CARRY;
1911 }
1912
1913 /*
1914 * Unlink the TD from the ED list.
1915 * The normal case is that it's at the head of the list.
1916 */
1917 Assert((TdAddr & ED_PTR_MASK) == TdAddr);
1918 if ((pEd->HeadP & ED_PTR_MASK) == TdAddr)
1919 {
1920 pEd->HeadP = (pTd->NextTD & ED_PTR_MASK) | (pEd->HeadP & ~ED_PTR_MASK);
1921 pTd->NextTD &= ~ED_PTR_MASK;
1922 }
1923 else
1924 {
1925 /*
1926 * The TD is probably somewhere in the list.
1927 *
1928 * This shouldn't ever happen unless there was a failure! Even on failure,
1929 * we can screw up the HCD state by picking out a TD from within the list
1930 * like this! If this turns out to be a problem, we have to find a better
1931 * solution. For now we'll hope the HCD handles it...
1932 */
1933 if (!ohciUnlinkGeneralTdInList(pOhci, TdAddr, pTd, pEd))
1934 return false;
1935 }
1936
1937 /*
1938 * Only unlink the first TD on error.
1939 * See comment in ohciRhXferCompleteGeneralURB().
1940 */
1941 if (pUrb->enmStatus != VUSBSTATUS_OK)
1942 break;
1943 }
1944 }
1945
1946 return true;
1947}
1948
1949
1950/**
1951 * Checks that the transport descriptors associated with the URB
1952 * hasn't been changed in any way indicating that they may have been canceled.
1953 *
1954 * This rountine also updates the TD copies contained within the URB.
1955 *
1956 * @returns true if the URB has been canceled, otherwise false.
1957 * @param pOhci The OHCI instance.
1958 * @param pUrb The URB in question.
1959 * @param pEd The ED pointer (optional).
1960 */
1961static bool ohciHasUrbBeenCanceled(POHCI pOhci, PVUSBURB pUrb, PCOHCIED pEd)
1962{
1963 if (!pUrb)
1964 return true;
1965
1966 /*
1967 * Make sure we've got an endpoint descriptor so we can
1968 * check for tail TDs.
1969 */
1970 OHCIED Ed;
1971 if (!pEd)
1972 {
1973 ohciReadEd(pOhci, pUrb->Hci.EdAddr, &Ed);
1974 pEd = &Ed;
1975 }
1976
1977 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
1978 {
1979 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
1980 {
1981 union
1982 {
1983 OHCIITD ITd;
1984 uint32_t au32[8];
1985 } u;
1986 if ( (pUrb->Hci.paTds[iTd].TdAddr & ED_PTR_MASK)
1987 == (pEd->TailP & ED_PTR_MASK))
1988 {
1989 Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)! [iso]\n",
1990 pUrb->pszDesc, iTd, pUrb->Hci.cTds, pUrb->Hci.paTds[iTd].TdAddr));
1991 STAM_COUNTER_INC(&pOhci->StatCanceledIsocUrbs);
1992 return true;
1993 }
1994 ohciReadITd(pOhci, pUrb->Hci.paTds[iTd].TdAddr, &u.ITd);
1995 if ( u.au32[0] != pUrb->Hci.paTds[iTd].TdCopy[0] /* hwinfo */
1996 || u.au32[1] != pUrb->Hci.paTds[iTd].TdCopy[1] /* bp0 */
1997 || u.au32[3] != pUrb->Hci.paTds[iTd].TdCopy[3] /* be */
1998 || ( u.au32[2] != pUrb->Hci.paTds[iTd].TdCopy[2] /* NextTD */
1999 && iTd + 1 < pUrb->Hci.cTds /* ignore the last one */)
2000 || u.au32[4] != pUrb->Hci.paTds[iTd].TdCopy[4] /* psw0&1 */
2001 || u.au32[5] != pUrb->Hci.paTds[iTd].TdCopy[5] /* psw2&3 */
2002 || u.au32[6] != pUrb->Hci.paTds[iTd].TdCopy[6] /* psw4&5 */
2003 || u.au32[7] != pUrb->Hci.paTds[iTd].TdCopy[7] /* psw6&7 */
2004 )
2005 {
2006 Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled! [iso]\n",
2007 pUrb->pszDesc, iTd, pUrb->Hci.cTds, pUrb->Hci.paTds[iTd].TdAddr));
2008 Log2((" %.*Rhxs (cur)\n"
2009 "!= %.*Rhxs (copy)\n",
2010 sizeof(u.ITd), &u.ITd, sizeof(u.ITd), &pUrb->Hci.paTds[iTd].TdCopy[0]));
2011 STAM_COUNTER_INC(&pOhci->StatCanceledIsocUrbs);
2012 return true;
2013 }
2014 pUrb->Hci.paTds[iTd].TdCopy[2] = u.au32[2];
2015 }
2016 }
2017 else
2018 {
2019 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
2020 {
2021 union
2022 {
2023 OHCITD Td;
2024 uint32_t au32[4];
2025 } u;
2026 if ( (pUrb->Hci.paTds[iTd].TdAddr & ED_PTR_MASK)
2027 == (pEd->TailP & ED_PTR_MASK))
2028 {
2029 Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)!\n",
2030 pUrb->pszDesc, iTd, pUrb->Hci.cTds, pUrb->Hci.paTds[iTd].TdAddr));
2031 STAM_COUNTER_INC(&pOhci->StatCanceledGenUrbs);
2032 return true;
2033 }
2034 ohciReadTd(pOhci, pUrb->Hci.paTds[iTd].TdAddr, &u.Td);
2035 if ( u.au32[0] != pUrb->Hci.paTds[iTd].TdCopy[0] /* hwinfo */
2036 || u.au32[1] != pUrb->Hci.paTds[iTd].TdCopy[1] /* cbp */
2037 || u.au32[3] != pUrb->Hci.paTds[iTd].TdCopy[3] /* be */
2038 || ( u.au32[2] != pUrb->Hci.paTds[iTd].TdCopy[2] /* NextTD */
2039 && iTd + 1 < pUrb->Hci.cTds /* ignore the last one */)
2040 )
2041 {
2042 Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled!\n",
2043 pUrb->pszDesc, iTd, pUrb->Hci.cTds, pUrb->Hci.paTds[iTd].TdAddr));
2044 Log2((" %.*Rhxs (cur)\n"
2045 "!= %.*Rhxs (copy)\n",
2046 sizeof(u.Td), &u.Td, sizeof(u.Td), &pUrb->Hci.paTds[iTd].TdCopy[0]));
2047 STAM_COUNTER_INC(&pOhci->StatCanceledGenUrbs);
2048 return true;
2049 }
2050 pUrb->Hci.paTds[iTd].TdCopy[2] = u.au32[2];
2051 }
2052 }
2053 return false;
2054}
2055
2056
2057/**
2058 * Returns the OHCI_CC_* corresponding to the VUSB status code.
2059 *
2060 * @returns OHCI_CC_* value.
2061 * @param enmStatus The VUSB status code.
2062 */
2063static uint32_t ohciVUsbStatus2OhciStatus(VUSBSTATUS enmStatus)
2064{
2065 switch (enmStatus)
2066 {
2067 case VUSBSTATUS_OK: return OHCI_CC_NO_ERROR;
2068 case VUSBSTATUS_STALL: return OHCI_CC_STALL;
2069 case VUSBSTATUS_CRC: return OHCI_CC_CRC;
2070 case VUSBSTATUS_DATA_UNDERRUN: return OHCI_CC_DATA_UNDERRUN;
2071 case VUSBSTATUS_DATA_OVERRUN: return OHCI_CC_DATA_OVERRUN;
2072 case VUSBSTATUS_DNR: return OHCI_CC_DNR;
2073 case VUSBSTATUS_NOT_ACCESSED: return OHCI_CC_NOT_ACCESSED_1;
2074 default:
2075 Log(("pUrb->enmStatus=%#x!!!\n", enmStatus));
2076 return OHCI_CC_DNR;
2077 }
2078}
2079
2080/**
2081 * Worker for ohciRhXferCompletion that handles the completion of
2082 * a URB made up of isochronous TDs.
2083 *
2084 * In general, all URBs should have status OK.
2085 */
2086static void ohciRhXferCompleteIsochronousURB(POHCI pOhci, PVUSBURB pUrb, POHCIED pEd, int cFmAge)
2087{
2088 /*
2089 * Copy the data back (if IN operation) and update the TDs.
2090 */
2091 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
2092 {
2093 POHCIITD pITd = (POHCIITD)&pUrb->Hci.paTds[iTd].TdCopy[0];
2094 const uint32_t ITdAddr = pUrb->Hci.paTds[iTd].TdAddr;
2095 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
2096 unsigned R = (pUrb->Hci.u32FrameNo & ITD_HWINFO_SF) - (pITd->HwInfo & ITD_HWINFO_SF);
2097 if (R >= 8)
2098 R = 0; /* submitted ahead of time. */
2099
2100 /*
2101 * Only one case of TD level condition code is document, so
2102 * just set NO_ERROR here to reduce number duplicate code.
2103 */
2104 pITd->HwInfo &= ~TD_HWINFO_CC;
2105 AssertCompile(OHCI_CC_NO_ERROR == 0);
2106
2107 if (pUrb->enmStatus == VUSBSTATUS_OK)
2108 {
2109 /*
2110 * Update the frames and copy back the data.
2111 * We assume that we don't get incorrect lengths here.
2112 */
2113 for (unsigned i = 0; i < cFrames; i++)
2114 {
2115 if ( i < R
2116 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2117 {
2118 /* It should already be NotAccessed. */
2119 pITd->aPSW[i] |= 0xe000; /* (Don't touch the 12th bit.) */
2120 continue;
2121 }
2122
2123 /* Update the PSW (save the offset first in case of a IN). */
2124 uint32_t off = pITd->aPSW[i] & ITD_PSW_OFFSET;
2125 pITd->aPSW[i] = ohciVUsbStatus2OhciStatus(pUrb->aIsocPkts[i - R].enmStatus)
2126 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2127
2128 if ( pUrb->enmDir == VUSBDIRECTION_IN
2129 && ( pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_OK
2130 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_UNDERRUN
2131 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_OVERRUN))
2132 {
2133 /* Set the size. */
2134 const unsigned cb = pUrb->aIsocPkts[i - R].cb;
2135 pITd->aPSW[i] |= cb & ITD_PSW_SIZE;
2136 /* Copy data. */
2137 if (cb)
2138 {
2139 uint8_t *pb = &pUrb->abData[pUrb->aIsocPkts[i - R].off];
2140 if (off + cb > 0x1000)
2141 {
2142 if (off < 0x1000)
2143 {
2144 /* both */
2145 const unsigned cb0 = 0x1000 - off;
2146 ohciPhysWrite(pOhci, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb0);
2147 ohciPhysWrite(pOhci, pITd->BE & ITD_BP0_MASK, pb + cb0, cb - cb0);
2148 }
2149 else /* only in the 2nd page */
2150 ohciPhysWrite(pOhci, (pITd->BE & ITD_BP0_MASK) + (off & ITD_BP0_MASK), pb, cb);
2151 }
2152 else /* only in the 1st page */
2153 ohciPhysWrite(pOhci, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb);
2154 Log5(("packet %d: off=%#x cb=%#x pb=%p (%#x)\n"
2155 "%.*Rhxd\n",
2156 i + R, off, cb, pb, pb - &pUrb->abData[0], cb, pb));
2157 //off += cb;
2158 }
2159 }
2160 }
2161
2162 /*
2163 * If the last package ended with a NotAccessed status, set ITD CC
2164 * to DataOverrun to indicate scheduling overrun.
2165 */
2166 if (pUrb->aIsocPkts[pUrb->cIsocPkts - 1].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2167 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
2168 }
2169 else
2170 {
2171 Log(("DevOHCI: Taking untested code path at line %d...\n", __LINE__));
2172 /*
2173 * Most status codes only applies to the individual packets.
2174 *
2175 * If we get a URB level error code of this kind, we'll distribute
2176 * it to all the packages unless some other status is available for
2177 * a package. This is a bit fuzzy, and we will get rid of this code
2178 * before long!
2179 */
2180 //if (pUrb->enmStatus != VUSBSTATUS_DATA_OVERRUN)
2181 {
2182 const unsigned uCC = ohciVUsbStatus2OhciStatus(pUrb->enmStatus)
2183 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2184 for (unsigned i = 0; i < cFrames; i++)
2185 pITd->aPSW[i] = uCC;
2186 }
2187 //else
2188 // pITd->HwInfo |= ohciVUsbStatus2OhciStatus(pUrb->enmStatus);
2189 }
2190
2191 /*
2192 * Update the done queue interrupt timer.
2193 */
2194 uint32_t DoneInt = (pITd->HwInfo & ITD_HWINFO_DI) >> ITD_HWINFO_DI_SHIFT;
2195 if ((pITd->HwInfo & TD_HWINFO_CC) != OHCI_CC_NO_ERROR)
2196 DoneInt = 0; /* It's cleared on error. */
2197 if ( DoneInt != 0x7
2198 && DoneInt < pOhci->dqic)
2199 pOhci->dqic = DoneInt;
2200
2201 /*
2202 * Move on to the done list and write back the modified TD.
2203 */
2204#ifdef LOG_ENABLED
2205 if (!pOhci->done)
2206 pOhci->u32FmDoneQueueTail = pOhci->HcFmNumber;
2207# ifdef VBOX_STRICT
2208 ohci_in_done_queue_add(pOhci, ITdAddr);
2209# endif
2210#endif
2211 pITd->NextTD = pOhci->done;
2212 pOhci->done = ITdAddr;
2213
2214 Log(("%s: ohciRhXferCompleteIsochronousURB: ITdAddr=%#010x EdAddr=%#010x SF=%#x (%#x) CC=%#x FC=%d "
2215 "psw0=%x:%x psw1=%x:%x psw2=%x:%x psw3=%x:%x psw4=%x:%x psw5=%x:%x psw6=%x:%x psw7=%x:%x R=%d\n",
2216 pUrb->pszDesc, ITdAddr,
2217 pUrb->Hci.EdAddr,
2218 pITd->HwInfo & ITD_HWINFO_SF, pOhci->HcFmNumber,
2219 (pITd->HwInfo & ITD_HWINFO_CC) >> ITD_HWINFO_CC_SHIFT,
2220 (pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT,
2221 pITd->aPSW[0] >> ITD_PSW_CC_SHIFT, pITd->aPSW[0] & ITD_PSW_SIZE,
2222 pITd->aPSW[1] >> ITD_PSW_CC_SHIFT, pITd->aPSW[1] & ITD_PSW_SIZE,
2223 pITd->aPSW[2] >> ITD_PSW_CC_SHIFT, pITd->aPSW[2] & ITD_PSW_SIZE,
2224 pITd->aPSW[3] >> ITD_PSW_CC_SHIFT, pITd->aPSW[3] & ITD_PSW_SIZE,
2225 pITd->aPSW[4] >> ITD_PSW_CC_SHIFT, pITd->aPSW[4] & ITD_PSW_SIZE,
2226 pITd->aPSW[5] >> ITD_PSW_CC_SHIFT, pITd->aPSW[5] & ITD_PSW_SIZE,
2227 pITd->aPSW[6] >> ITD_PSW_CC_SHIFT, pITd->aPSW[6] & ITD_PSW_SIZE,
2228 pITd->aPSW[7] >> ITD_PSW_CC_SHIFT, pITd->aPSW[7] & ITD_PSW_SIZE,
2229 R));
2230 ohciWriteITd(pOhci, ITdAddr, pITd, "retired");
2231 }
2232}
2233
2234
2235/**
2236 * Worker for ohciRhXferCompletion that handles the completion of
2237 * a URB made up of general TDs.
2238 */
2239static void ohciRhXferCompleteGeneralURB(POHCI pOhci, PVUSBURB pUrb, POHCIED pEd, int cFmAge)
2240{
2241 /*
2242 * Copy the data back (if IN operation) and update the TDs.
2243 */
2244 unsigned cbLeft = pUrb->cbData;
2245 uint8_t *pb = &pUrb->abData[0];
2246 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
2247 {
2248 POHCITD pTd = (POHCITD)&pUrb->Hci.paTds[iTd].TdCopy[0];
2249 const uint32_t TdAddr = pUrb->Hci.paTds[iTd].TdAddr;
2250
2251 /*
2252 * Setup a ohci transfer buffer and calc the new cbp value.
2253 */
2254 OHCIBUF Buf;
2255 ohciBufInit(&Buf, pTd->cbp, pTd->be);
2256 uint32_t NewCbp;
2257 if (cbLeft >= Buf.cbTotal)
2258 NewCbp = 0;
2259 else
2260 {
2261 /* (len may have changed for short transfers) */
2262 Buf.cbTotal = cbLeft;
2263 ohciBufUpdate(&Buf);
2264 Assert(Buf.cVecs >= 1);
2265 NewCbp = Buf.aVecs[Buf.cVecs-1].Addr + Buf.aVecs[Buf.cVecs-1].cb;
2266 }
2267
2268 /*
2269 * Write back IN buffers.
2270 */
2271 if ( pUrb->enmDir == VUSBDIRECTION_IN
2272 && ( pUrb->enmStatus == VUSBSTATUS_OK
2273 || pUrb->enmStatus == VUSBSTATUS_DATA_OVERRUN
2274 || pUrb->enmStatus == VUSBSTATUS_DATA_UNDERRUN)
2275 && Buf.cbTotal > 0)
2276 {
2277 Assert(Buf.cVecs > 0);
2278 ohciPhysWrite(pOhci, Buf.aVecs[0].Addr, pb, Buf.aVecs[0].cb);
2279 if (Buf.cVecs > 1)
2280 ohciPhysWrite(pOhci, Buf.aVecs[1].Addr, pb + Buf.aVecs[0].cb, Buf.aVecs[1].cb);
2281 }
2282
2283 /* advance the data buffer. */
2284 cbLeft -= Buf.cbTotal;
2285 pb += Buf.cbTotal;
2286
2287 /*
2288 * Set writeback field.
2289 */
2290 /* zero out writeback fields for retirement */
2291 pTd->hwinfo &= ~TD_HWINFO_CC;
2292 /* always update the CurrentBufferPointer; essential for underrun/overrun errors */
2293 pTd->cbp = NewCbp;
2294
2295 if (pUrb->enmStatus == VUSBSTATUS_OK)
2296 {
2297 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
2298
2299 /* update done queue interrupt timer */
2300 uint32_t DoneInt = (pTd->hwinfo & TD_HWINFO_DI) >> 21;
2301 if ( DoneInt != 0x7
2302 && DoneInt < pOhci->dqic)
2303 pOhci->dqic = DoneInt;
2304 Log(("%s: ohciRhXferCompleteGeneralURB: ED=%#010x TD=%#010x Age=%d cbTotal=%#x NewCbp=%#010RX32 dqic=%d\n",
2305 pUrb->pszDesc, pUrb->Hci.EdAddr, TdAddr, cFmAge, pUrb->enmStatus, Buf.cbTotal, NewCbp, pOhci->dqic));
2306 }
2307 else
2308 {
2309 Log(("%s: ohciRhXferCompleteGeneralURB: HALTED ED=%#010x TD=%#010x (age %d) pUrb->enmStatus=%d\n",
2310 pUrb->pszDesc, pUrb->Hci.EdAddr, TdAddr, cFmAge, pUrb->enmStatus));
2311 pEd->HeadP |= ED_HEAD_HALTED;
2312 pOhci->dqic = 0; /* "If the Transfer Descriptor is being retired with an error,
2313 * then the Done Queue Interrupt Counter is cleared as if the
2314 * InterruptDelay field were zero."
2315 */
2316 switch (pUrb->enmStatus)
2317 {
2318 case VUSBSTATUS_STALL:
2319 pTd->hwinfo |= OHCI_CC_STALL;
2320 break;
2321 case VUSBSTATUS_CRC:
2322 pTd->hwinfo |= OHCI_CC_CRC;
2323 break;
2324 case VUSBSTATUS_DATA_UNDERRUN:
2325 pTd->hwinfo |= OHCI_CC_DATA_UNDERRUN;
2326 break;
2327 case VUSBSTATUS_DATA_OVERRUN:
2328 pTd->hwinfo |= OHCI_CC_DATA_OVERRUN;
2329 break;
2330 default: /* what the hell */
2331 Log(("pUrb->enmStatus=%#x!!!\n", pUrb->enmStatus));
2332 case VUSBSTATUS_DNR:
2333 pTd->hwinfo |= OHCI_CC_DNR;
2334 break;
2335 }
2336 }
2337
2338 /*
2339 * Move on to the done list and write back the modified TD.
2340 */
2341#ifdef LOG_ENABLED
2342 if (!pOhci->done)
2343 pOhci->u32FmDoneQueueTail = pOhci->HcFmNumber;
2344# ifdef VBOX_STRICT
2345 ohci_in_done_queue_add(pOhci, TdAddr);
2346# endif
2347#endif
2348 pTd->NextTD = pOhci->done;
2349 pOhci->done = TdAddr;
2350
2351 ohciWriteTd(pOhci, TdAddr, pTd, "retired");
2352
2353 /*
2354 * If we've halted the endpoint, we stop here.
2355 * ohciUnlinkTds() will make sure we've only unliked the first TD.
2356 *
2357 * The reason for this is that while we can have more than one TD in a URB, real
2358 * OHCI hardware will only deal with one TD at the time and it's therefore incorrect
2359 * to retire TDs after the endpoint has been halted. Win2k will crash or enter infinite
2360 * kernel loop if we don't behave correctly. (See #1646.)
2361 */
2362 if (pEd->HeadP & ED_HEAD_HALTED)
2363 break;
2364 }
2365}
2366
2367
2368/**
2369 * Transfer completion callback routine.
2370 *
2371 * VUSB will call this when a transfer have been completed
2372 * in a one or another way.
2373 *
2374 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2375 * @param pUrb Pointer to the URB in question.
2376 */
2377static DECLCALLBACK(void) ohciRhXferCompletion(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2378{
2379 POHCI pOhci = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2380 LogFlow(("%s: ohciRhXferCompletion: EdAddr=%#010RX32 cTds=%d TdAddr0=%#010RX32\n",
2381 pUrb->pszDesc, pUrb->Hci.EdAddr, pUrb->Hci.cTds, pUrb->Hci.paTds[0].TdAddr));
2382
2383 pOhci->fIdle = false; /* Mark as active */
2384
2385 /* get the current end point descriptor. */
2386 OHCIED Ed;
2387 ohciReadEd(pOhci, pUrb->Hci.EdAddr, &Ed);
2388
2389 /*
2390 * Check that the URB hasn't been canceled and then try unlink the TDs.
2391 *
2392 * We drop the URB if the ED is marked halted/skip ASSUMING that this
2393 * means the HCD has canceled the URB.
2394 *
2395 * If we succeed here (i.e. not dropping the URB), the TdCopy members will
2396 * be updated but not yet written. We will delay the writing till we're done
2397 * with the data copying, buffer pointer advancing and error handling.
2398 */
2399 int cFmAge = ohci_in_flight_remove_urb(pOhci, pUrb);
2400 bool fHasBeenCanceled = false;
2401 if ( (Ed.HeadP & ED_HEAD_HALTED)
2402 || (Ed.hwinfo & ED_HWINFO_SKIP)
2403 || cFmAge < 0
2404 || (fHasBeenCanceled = ohciHasUrbBeenCanceled(pOhci, pUrb, &Ed))
2405 || !ohciUnlinkTds(pOhci, pUrb, &Ed)
2406 )
2407 {
2408 Log(("%s: ohciRhXferCompletion: DROPPED {ED=%#010x cTds=%d TD0=%#010x age %d} because:%s%s%s%s%s!!!\n",
2409 pUrb->pszDesc, pUrb->Hci.EdAddr, pUrb->Hci.cTds, pUrb->Hci.paTds[0].TdAddr, cFmAge,
2410 (Ed.HeadP & ED_HEAD_HALTED) ? " ep halted" : "",
2411 (Ed.hwinfo & ED_HWINFO_SKIP) ? " ep skip" : "",
2412 (Ed.HeadP & ED_PTR_MASK) != pUrb->Hci.paTds[0].TdAddr ? " ep head-changed" : "",
2413 cFmAge < 0 ? " td not-in-flight" : "",
2414 fHasBeenCanceled ? " td canceled" : ""));
2415 NOREF(fHasBeenCanceled);
2416 STAM_COUNTER_INC(&pOhci->StatDroppedUrbs);
2417 return;
2418 }
2419
2420 /*
2421 * Complete the TD updating and write the back.
2422 * When appropirate also copy data back to the guest memory.
2423 */
2424 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2425 ohciRhXferCompleteIsochronousURB(pOhci, pUrb, &Ed, cFmAge);
2426 else
2427 ohciRhXferCompleteGeneralURB(pOhci, pUrb, &Ed, cFmAge);
2428
2429 /* finaly write back the endpoint descriptor. */
2430 ohciWriteEd(pOhci, pUrb->Hci.EdAddr, &Ed);
2431}
2432
2433
2434/**
2435 * Handle transfer errors.
2436 *
2437 * VUSB calls this when a transfer attempt failed. This function will respond
2438 * indicating wheter to retry or complete the URB with failure.
2439 *
2440 * @returns true if the URB should be retired.
2441 * @returns false if the URB should be retried.
2442 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2443 * @param pUrb Pointer to the URB in question.
2444 */
2445static DECLCALLBACK(bool) ohciRhXferError(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2446{
2447 POHCI pOhci = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2448
2449 /*
2450 * Isochronous URBs can't be retried.
2451 */
2452 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2453 return true;
2454
2455 /*
2456 * Don't retry on stall.
2457 */
2458 if (pUrb->enmStatus == VUSBSTATUS_STALL)
2459 {
2460 Log2(("%s: ohciRhXferError: STALL, giving up.\n", pUrb->pszDesc, pUrb->enmStatus));
2461 return true;
2462 }
2463
2464 /*
2465 * Check if the TDs still are valid.
2466 * This will make sure the TdCopy is up to date.
2467 */
2468 const uint32_t TdAddr = pUrb->Hci.paTds[0].TdAddr;
2469/** @todo IMPORTANT! we must check if the ED is still valid at this point!!! */
2470 if (ohciHasUrbBeenCanceled(pOhci, pUrb, NULL))
2471 {
2472 Log(("%s: ohciRhXferError: TdAddr0=%#x canceled!\n", pUrb->pszDesc, TdAddr));
2473 return true;
2474 }
2475
2476 /*
2477 * Get and update the error counter.
2478 */
2479 POHCITD pTd = (POHCITD)&pUrb->Hci.paTds[0].TdCopy[0];
2480 unsigned cErrs = (pTd->hwinfo & TD_HWINFO_ERRORS) >> TD_ERRORS_SHIFT;
2481 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
2482 cErrs++;
2483 pTd->hwinfo |= (cErrs % TD_ERRORS_MAX) << TD_ERRORS_SHIFT;
2484 ohciWriteTd(pOhci, TdAddr, pTd, "ohciRhXferError");
2485
2486 if (cErrs >= TD_ERRORS_MAX - 1)
2487 {
2488 Log2(("%s: ohciRhXferError: too many errors, giving up!\n", pUrb->pszDesc));
2489 return true;
2490 }
2491 Log2(("%s: ohciRhXferError: cErrs=%d: retrying...\n", pUrb->pszDesc, cErrs));
2492 return false;
2493}
2494
2495
2496/**
2497 * Service a general transport descriptor.
2498 */
2499static bool ohciServiceTd(POHCI pOhci, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
2500{
2501 /*
2502 * Read the TD and setup the buffer data.
2503 */
2504 OHCITD Td;
2505 ohciReadTd(pOhci, TdAddr, &Td);
2506 OHCIBUF Buf;
2507 ohciBufInit(&Buf, Td.cbp, Td.be);
2508
2509 *pNextTdAddr = Td.NextTD & ED_PTR_MASK;
2510
2511 /*
2512 * Determin the direction.
2513 */
2514 VUSBDIRECTION enmDir;
2515 switch (pEd->hwinfo & ED_HWINFO_DIR)
2516 {
2517 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2518 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2519 default:
2520 switch (Td.hwinfo & TD_HWINFO_DIR)
2521 {
2522 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2523 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2524 case 0: enmDir = VUSBDIRECTION_SETUP; break;
2525 default:
2526 Log(("ohciServiceTd: Invalid direction!!!! Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Td.hwinfo, pEd->hwinfo));
2527 /* TODO: Do the correct thing here */
2528 return false;
2529 }
2530 break;
2531 }
2532
2533 pOhci->fIdle = false; /* Mark as active */
2534
2535 /*
2536 * Allocate and initialize a new URB.
2537 */
2538 PVUSBURB pUrb = VUSBIRhNewUrb(pOhci->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, Buf.cbTotal, 1);
2539 if (!pUrb)
2540 return false; /* retry later... */
2541 Assert(pUrb->Hci.cTds == 1);
2542
2543 pUrb->enmType = enmType;
2544 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
2545 pUrb->enmDir = enmDir;
2546 pUrb->fShortNotOk = !(Td.hwinfo & TD_HWINFO_ROUNDING);
2547 pUrb->enmStatus = VUSBSTATUS_OK;
2548 pUrb->Hci.EdAddr = EdAddr;
2549 pUrb->Hci.fUnlinked = false;
2550 pUrb->Hci.paTds[0].TdAddr = TdAddr;
2551 pUrb->Hci.u32FrameNo = pOhci->HcFmNumber;
2552 AssertCompile(sizeof(pUrb->Hci.paTds[0].TdCopy) >= sizeof(Td));
2553 memcpy(pUrb->Hci.paTds[0].TdCopy, &Td, sizeof(Td));
2554#ifdef LOG_ENABLED
2555 static unsigned s_iSerial = 0;
2556 s_iSerial = (s_iSerial + 1) % 10000;
2557 RTStrAPrintf(&pUrb->pszDesc, "URB %p %10s/s%c%04d", pUrb, pszListName,
2558 enmDir == VUSBDIRECTION_IN ? '<' : enmDir == VUSBDIRECTION_OUT ? '>' : '-', s_iSerial);
2559#endif
2560
2561 /* copy data if out bound transfer. */
2562 pUrb->cbData = Buf.cbTotal;
2563 if ( Buf.cbTotal
2564 && Buf.cVecs > 0
2565 && enmDir != VUSBDIRECTION_IN)
2566 {
2567 ohciPhysRead(pOhci, Buf.aVecs[0].Addr, pUrb->abData, Buf.aVecs[0].cb);
2568 if (Buf.cVecs > 1)
2569 ohciPhysRead(pOhci, Buf.aVecs[1].Addr, &pUrb->abData[Buf.aVecs[0].cb], Buf.aVecs[1].cb);
2570 }
2571
2572 /*
2573 * Submit the URB.
2574 */
2575 ohci_in_flight_add(pOhci, TdAddr, pUrb);
2576 Log(("%s: ohciServiceTd: submitting TdAddr=%#010x EdAddr=%#010x cbData=%#x\n",
2577 pUrb->pszDesc, TdAddr, EdAddr, pUrb->cbData));
2578
2579 int rc = VUSBIRhSubmitUrb(pOhci->RootHub.pIRhConn, pUrb, &pOhci->RootHub.Led);
2580 if (RT_SUCCESS(rc))
2581 return true;
2582
2583 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
2584 Log(("ohciServiceTd: failed submitting TdAddr=%#010x EdAddr=%#010x pUrb=%p!!\n",
2585 TdAddr, EdAddr, pUrb));
2586 ohci_in_flight_remove(pOhci, TdAddr);
2587 return false;
2588}
2589
2590
2591/**
2592 * Service a the head TD of an endpoint.
2593 */
2594static bool ohciServiceHeadTd(POHCI pOhci, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
2595{
2596 /*
2597 * Read the TD, after first checking if it's already in-flight.
2598 */
2599 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
2600 if (ohciIsTdInFlight(pOhci, TdAddr))
2601 return false;
2602#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
2603 ohci_in_done_queue_check(pOhci, TdAddr);
2604#endif
2605 return ohciServiceTd(pOhci, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
2606}
2607
2608
2609/**
2610 * Service one or more general transport descriptors (bulk or interrupt).
2611 */
2612static bool ohciServiceTdMultiple(POHCI pOhci, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr,
2613 uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
2614{
2615 /*
2616 * Read the TDs involved in this URB.
2617 */
2618 struct OHCITDENTRY
2619 {
2620 /** The TD. */
2621 OHCITD Td;
2622 /** The associated OHCI buffer tracker. */
2623 OHCIBUF Buf;
2624 /** The TD address. */
2625 uint32_t TdAddr;
2626 /** Pointer to the next element in the chain (stack). */
2627 struct OHCITDENTRY *pNext;
2628 } Head;
2629
2630 /* read the head */
2631 ohciReadTd(pOhci, TdAddr, &Head.Td);
2632 ohciBufInit(&Head.Buf, Head.Td.cbp, Head.Td.be);
2633 Head.TdAddr = TdAddr;
2634 Head.pNext = NULL;
2635
2636 /* combine with more TDs. */
2637 struct OHCITDENTRY *pTail = &Head;
2638 unsigned cbTotal = pTail->Buf.cbTotal;
2639 unsigned cTds = 1;
2640 while ( (pTail->Buf.cbTotal == 0x1000 || pTail->Buf.cbTotal == 0x2000)
2641 && !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING) /* This isn't right for *BSD, but let's not . */
2642 && (pTail->Td.NextTD & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
2643 && cTds < 128)
2644 {
2645 struct OHCITDENTRY *pCur = (struct OHCITDENTRY *)alloca(sizeof(*pCur));
2646
2647 pCur->pNext = NULL;
2648 pCur->TdAddr = pTail->Td.NextTD & ED_PTR_MASK;
2649 ohciReadTd(pOhci, pCur->TdAddr, &pCur->Td);
2650 ohciBufInit(&pCur->Buf, pCur->Td.cbp, pCur->Td.be);
2651
2652 /* don't combine if the direction doesn't match up. */
2653 if ( (pCur->Td.hwinfo & (TD_HWINFO_DIR))
2654 != (pCur->Td.hwinfo & (TD_HWINFO_DIR)))
2655 break;
2656
2657 pTail->pNext = pCur;
2658 pTail = pCur;
2659 cbTotal += pCur->Buf.cbTotal;
2660 cTds++;
2661 }
2662
2663 /* calc next TD address */
2664 *pNextTdAddr = pTail->Td.NextTD & ED_PTR_MASK;
2665
2666 /*
2667 * Determin the direction.
2668 */
2669 VUSBDIRECTION enmDir;
2670 switch (pEd->hwinfo & ED_HWINFO_DIR)
2671 {
2672 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2673 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2674 default:
2675 Log(("ohciServiceTdMultiple: WARNING! Ed.hwdinfo=%#x bulk or interrupt EP shouldn't rely on the TD for direction...\n", pEd->hwinfo));
2676 switch (Head.Td.hwinfo & TD_HWINFO_DIR)
2677 {
2678 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2679 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2680 default:
2681 Log(("ohciServiceTdMultiple: Invalid direction!!!! Head.Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Head.Td.hwinfo, pEd->hwinfo));
2682 /* TODO: Do the correct thing here */
2683 return false;
2684 }
2685 break;
2686 }
2687
2688 pOhci->fIdle = false; /* Mark as active */
2689
2690 /*
2691 * Allocate and initialize a new URB.
2692 */
2693 PVUSBURB pUrb = VUSBIRhNewUrb(pOhci->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, cbTotal, cTds);
2694 if (!pUrb)
2695 /* retry later... */
2696 return false;
2697 Assert(pUrb->Hci.cTds == cTds);
2698 Assert(pUrb->cbData == cbTotal);
2699
2700 pUrb->enmType = enmType;
2701 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
2702 pUrb->enmDir = enmDir;
2703 pUrb->fShortNotOk = !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING);
2704 pUrb->enmStatus = VUSBSTATUS_OK;
2705 pUrb->Hci.EdAddr = EdAddr;
2706 pUrb->Hci.fUnlinked = false;
2707 pUrb->Hci.u32FrameNo = pOhci->HcFmNumber;
2708#ifdef LOG_ENABLED
2709 static unsigned s_iSerial = 0;
2710 s_iSerial = (s_iSerial + 1) % 10000;
2711 RTStrAPrintf(&pUrb->pszDesc, "URB %p %10s/m%c%04d", pUrb, pszListName,
2712 enmDir == VUSBDIRECTION_IN ? '<' : enmDir == VUSBDIRECTION_OUT ? '>' : '-', s_iSerial);
2713#endif
2714
2715 /* Copy data and TD information. */
2716 unsigned iTd = 0;
2717 uint8_t *pb = &pUrb->abData[0];
2718 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
2719 {
2720 /* data */
2721 if ( cbTotal
2722 && enmDir != VUSBDIRECTION_IN
2723 && pCur->Buf.cVecs > 0)
2724 {
2725 ohciPhysRead(pOhci, pCur->Buf.aVecs[0].Addr, pb, pCur->Buf.aVecs[0].cb);
2726 if (pCur->Buf.cVecs > 1)
2727 ohciPhysRead(pOhci, pCur->Buf.aVecs[1].Addr, pb + pCur->Buf.aVecs[0].cb, pCur->Buf.aVecs[1].cb);
2728 }
2729 pb += pCur->Buf.cbTotal;
2730
2731 /* TD info */
2732 pUrb->Hci.paTds[iTd].TdAddr = pCur->TdAddr;
2733 AssertCompile(sizeof(pUrb->Hci.paTds[iTd].TdCopy) >= sizeof(pCur->Td));
2734 memcpy(pUrb->Hci.paTds[iTd].TdCopy, &pCur->Td, sizeof(pCur->Td));
2735 }
2736
2737 /*
2738 * Submit the URB.
2739 */
2740 ohci_in_flight_add_urb(pOhci, pUrb);
2741 Log(("%s: ohciServiceTdMultiple: submitting cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x\n",
2742 pUrb->pszDesc, pUrb->cbData, EdAddr, cTds, TdAddr));
2743 int rc = VUSBIRhSubmitUrb(pOhci->RootHub.pIRhConn, pUrb, &pOhci->RootHub.Led);
2744 if (RT_SUCCESS(rc))
2745 return true;
2746
2747 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
2748 Log(("ohciServiceTdMultiple: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x - rc=%Rrc\n",
2749 pUrb, cbTotal, EdAddr, cTds, TdAddr, rc));
2750 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
2751 ohci_in_flight_remove(pOhci, pCur->TdAddr);
2752 return false;
2753}
2754
2755
2756/**
2757 * Service the head TD of an endpoint.
2758 */
2759static bool ohciServiceHeadTdMultiple(POHCI pOhci, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
2760{
2761 /*
2762 * First, check that it's not already in-flight.
2763 */
2764 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
2765 if (ohciIsTdInFlight(pOhci, TdAddr))
2766 return false;
2767#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
2768 ohci_in_done_queue_check(pOhci, TdAddr);
2769#endif
2770 return ohciServiceTdMultiple(pOhci, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
2771}
2772
2773
2774/**
2775 * A worker for ohciServiceIsochronousEndpoint which unlinks a ITD
2776 * that belongs to the past.
2777 */
2778static bool ohciServiceIsochronousTdUnlink(POHCI pOhci, POHCIITD pITd, uint32_t ITdAddr, uint32_t ITdAddrPrev,
2779 PVUSBURB pUrb, POHCIED pEd, uint32_t EdAddr)
2780{
2781 LogFlow(("%s%sohciServiceIsochronousTdUnlink: Unlinking ITD: ITdAddr=%#010x EdAddr=%#010x ITdAddrPrev=%#010x\n",
2782 pUrb ? pUrb->pszDesc : "", pUrb ? ": " : "", ITdAddr, EdAddr, ITdAddrPrev));
2783
2784 /*
2785 * Do the unlinking.
2786 */
2787 const uint32_t ITdAddrNext = pITd->NextTD & ED_PTR_MASK;
2788 if (ITdAddrPrev)
2789 {
2790 /* Get validate the previous TD */
2791 int iInFlightPrev = ohci_in_flight_find(pOhci, ITdAddr);
2792 AssertMsgReturn(iInFlightPrev >= 0, ("ITdAddr=%#RX32\n", ITdAddr), false);
2793 PVUSBURB pUrbPrev = pOhci->aInFlight[iInFlightPrev].pUrb;
2794 if (ohciHasUrbBeenCanceled(pOhci, pUrbPrev, pEd)) /* ensures the copy is correct. */
2795 return false;
2796
2797 /* Update the copy and write it back. */
2798 POHCIITD pITdPrev = ((POHCIITD)pUrbPrev->Hci.paTds[0].TdCopy);
2799 pITdPrev->NextTD = (pITdPrev->NextTD & ~ED_PTR_MASK) | ITdAddrNext;
2800 ohciWriteITd(pOhci, ITdAddrPrev, pITdPrev, "ohciServiceIsochronousEndpoint");
2801 }
2802 else
2803 {
2804 /* It's the head node. update the copy from the caller and write it back. */
2805 pEd->HeadP = (pEd->HeadP & ~ED_PTR_MASK) | ITdAddrNext;
2806 ohciWriteEd(pOhci, EdAddr, pEd);
2807 }
2808
2809 /*
2810 * If it's in flight, just mark the URB as unlinked (there is only one ITD per URB atm).
2811 * Otherwise, retire it to the done queue with an error and cause a done line interrupt (?).
2812 */
2813 if (pUrb)
2814 {
2815 pUrb->Hci.fUnlinked = true;
2816 if (ohciHasUrbBeenCanceled(pOhci, pUrb, pEd)) /* ensures the copy is correct (paranoia). */
2817 return false;
2818
2819 POHCIITD pITdCopy = ((POHCIITD)pUrb->Hci.paTds[0].TdCopy);
2820 pITd->NextTD = pITdCopy->NextTD &= ~ED_PTR_MASK;
2821 }
2822 else
2823 {
2824 pITd->HwInfo &= ~ITD_HWINFO_CC;
2825 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
2826
2827 pITd->NextTD = pOhci->done;
2828 pOhci->done = ITdAddr;
2829
2830 pOhci->dqic = 0;
2831 }
2832
2833 ohciWriteITd(pOhci, ITdAddr, pITd, "ohciServiceIsochronousTdUnlink");
2834 return true;
2835}
2836
2837
2838/**
2839 * A worker for ohciServiceIsochronousEndpoint which submits the specified TD.
2840 *
2841 * @returns true on success.
2842 * @returns false on failure to submit.
2843 * @param R The start packet (frame) relative to the start of frame in HwInfo.
2844 */
2845static bool ohciServiceIsochronousTd(POHCI pOhci, POHCIITD pITd, uint32_t ITdAddr, const unsigned R, PCOHCIED pEd, uint32_t EdAddr)
2846{
2847 /*
2848 * Determine the endpoint direction.
2849 */
2850 VUSBDIRECTION enmDir;
2851 switch (pEd->hwinfo & ED_HWINFO_DIR)
2852 {
2853 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2854 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2855 default:
2856 Log(("ohciServiceIsochronousTd: Invalid direction!!!! Ed.hwdinfo=%#x\n", pEd->hwinfo));
2857 /* Should probably raise an unrecoverable HC error here */
2858 return false;
2859 }
2860
2861 /*
2862 * Extract the packet sizes and calc the total URB size.
2863 */
2864 struct
2865 {
2866 uint16_t cb;
2867 uint16_t off;
2868 } aPkts[ITD_NUM_PSW];
2869
2870 /* first entry (R) */
2871 uint32_t cbTotal = 0;
2872 if (((uint32_t)pITd->aPSW[R] >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
2873 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, R, pITd->aPSW[R] >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
2874 uint16_t offPrev = aPkts[0].off = (pITd->aPSW[R] & ITD_PSW_OFFSET);
2875
2876 /* R+1..cFrames */
2877 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
2878 for (unsigned iR = R + 1; iR < cFrames; iR++)
2879 {
2880 const uint16_t PSW = pITd->aPSW[iR];
2881 const uint16_t off = aPkts[iR - R].off = (PSW & ITD_PSW_OFFSET);
2882 cbTotal += aPkts[iR - R - 1].cb = off - offPrev;
2883 if (off < offPrev)
2884 Log(("ITdAddr=%RX32 PSW%d.offset=%#x < offPrev=%#x!\n", ITdAddr, iR, off, offPrev)); /* => Unrecoverable Error*/
2885 if (((uint32_t)PSW >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
2886 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, iR, PSW >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
2887 offPrev = off;
2888 }
2889
2890 /* calc offEnd and figure out the size of the last packet. */
2891 const uint32_t offEnd = (pITd->BE & 0xfff)
2892 + (((pITd->BE & ITD_BP0_MASK) != (pITd->BP0 & ITD_BP0_MASK)) << 12)
2893 + 1 /* BE is inclusive */;
2894 if (offEnd < offPrev)
2895 Log(("ITdAddr=%RX32 offEnd=%#x < offPrev=%#x!\n", ITdAddr, offEnd, offPrev)); /* => Unrecoverable Error*/
2896 cbTotal += aPkts[cFrames - 1 - R].cb = offEnd - offPrev;
2897 Assert(cbTotal <= 0x2000);
2898
2899 pOhci->fIdle = false; /* Mark as active */
2900
2901 /*
2902 * Allocate and initialize a new URB.
2903 */
2904 PVUSBURB pUrb = VUSBIRhNewUrb(pOhci->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, cbTotal, 1);
2905 if (!pUrb)
2906 /* retry later... */
2907 return false;
2908
2909 pUrb->enmType = VUSBXFERTYPE_ISOC;
2910 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
2911 pUrb->enmDir = enmDir;
2912 pUrb->fShortNotOk = false;
2913 pUrb->enmStatus = VUSBSTATUS_OK;
2914 pUrb->Hci.EdAddr = EdAddr;
2915 pUrb->Hci.fUnlinked = false;
2916 pUrb->Hci.u32FrameNo = pOhci->HcFmNumber;
2917 pUrb->Hci.paTds[0].TdAddr = ITdAddr;
2918 AssertCompile(sizeof(pUrb->Hci.paTds[0].TdCopy) >= sizeof(*pITd));
2919 memcpy(pUrb->Hci.paTds[0].TdCopy, pITd, sizeof(*pITd));
2920#if 0 /* color the data */
2921 memset(pUrb->abData, 0xfe, cbTotal);
2922#endif
2923#ifdef LOG_ENABLED
2924 static unsigned s_iSerial = 0;
2925 s_iSerial = (s_iSerial + 1) % 10000;
2926 RTStrAPrintf(&pUrb->pszDesc, "URB %p isoc%c%04d", pUrb, enmDir == VUSBDIRECTION_IN ? '<' : '>', s_iSerial);
2927#endif
2928
2929 /* copy the data */
2930 if ( cbTotal
2931 && enmDir != VUSBDIRECTION_IN)
2932 {
2933 const uint32_t off0 = pITd->aPSW[R] & ITD_PSW_OFFSET;
2934 if (off0 < 0x1000)
2935 {
2936 if (offEnd > 0x1000)
2937 {
2938 /* both pages. */
2939 const unsigned cb0 = 0x1000 - off0;
2940 ohciPhysRead(pOhci, (pITd->BP0 & ITD_BP0_MASK) + off0, &pUrb->abData[0], cb0);
2941 ohciPhysRead(pOhci, pITd->BE & ITD_BP0_MASK, &pUrb->abData[cb0], offEnd & 0xfff);
2942 }
2943 else /* a portion of the 1st page. */
2944 ohciPhysRead(pOhci, (pITd->BP0 & ITD_BP0_MASK) + off0, pUrb->abData, offEnd - off0);
2945 }
2946 else /* a portion of the 2nd page. */
2947 ohciPhysRead(pOhci, (pITd->BE & UINT32_C(0xfffff000)) + (off0 & 0xfff), pUrb->abData, cbTotal);
2948 }
2949
2950 /* setup the packets */
2951 pUrb->cIsocPkts = cFrames - R;
2952 unsigned off = 0;
2953 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
2954 {
2955 pUrb->aIsocPkts[i].enmStatus = VUSBSTATUS_NOT_ACCESSED;
2956 pUrb->aIsocPkts[i].off = off;
2957 off += pUrb->aIsocPkts[i].cb = aPkts[i].cb;
2958 }
2959 Assert(off == cbTotal);
2960
2961 /*
2962 * Submit the URB.
2963 */
2964 ohci_in_flight_add_urb(pOhci, pUrb);
2965 Log(("%s: ohciServiceIsochronousTd: submitting cbData=%#x cIsocPkts=%d EdAddr=%#010x TdAddr=%#010x SF=%#x (%#x)\n",
2966 pUrb->pszDesc, pUrb->cbData, pUrb->cIsocPkts, EdAddr, ITdAddr, pITd->HwInfo & ITD_HWINFO_SF, pOhci->HcFmNumber));
2967 int rc = VUSBIRhSubmitUrb(pOhci->RootHub.pIRhConn, pUrb, &pOhci->RootHub.Led);
2968 if (RT_SUCCESS(rc))
2969 return true;
2970
2971 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
2972 Log(("ohciServiceIsochronousTd: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d ITdAddr0=%#010x - rc=%Rrc\n",
2973 pUrb, cbTotal, EdAddr, 1, ITdAddr, rc));
2974 ohci_in_flight_remove(pOhci, ITdAddr);
2975 return false;
2976}
2977
2978
2979/**
2980 * Service an isochronous endpoint.
2981 */
2982static void ohciServiceIsochronousEndpoint(POHCI pOhci, POHCIED pEd, uint32_t EdAddr)
2983{
2984 /*
2985 * We currently process this as if the guest follows the interrupt end point chaining
2986 * hiearchy described in the documenation. This means that for an isochronous endpoint
2987 * with a 1 ms interval we expect to find in-flight TDs at the head of the list. We will
2988 * skip over all in-flight TDs which timeframe has been exceed. Those which aren't in
2989 * flight but which are too late will be retired (possibly out of order, but, we don't
2990 * care right now).
2991 *
2992 * When we reach a TD which still has a buffer which is due for take off, we will
2993 * stop iterating TDs. If it's in-flight, there isn't anything to be done. Otherwise
2994 * we will push it onto the runway for immediate take off. In this process we
2995 * might have to complete buffers which didn't make it on time, something which
2996 * complicates the kind of status info we need to keep around for the TD.
2997 *
2998 * Note: We're currently not making any attempt at reassembling ITDs into URBs.
2999 * However, this will become necessary because of EMT scheduling and guest
3000 * like linux using one TD for each frame (simple but inefficient for us).
3001 */
3002 OHCIITD ITd;
3003 uint32_t ITdAddr = pEd->HeadP & ED_PTR_MASK;
3004 uint32_t ITdAddrPrev = 0;
3005 uint32_t u32NextFrame = UINT32_MAX;
3006 const uint16_t u16CurFrame = pOhci->HcFmNumber;
3007 for (;;)
3008 {
3009 /* check for end-of-chain. */
3010 if ( ITdAddr == (pEd->TailP & ED_PTR_MASK)
3011 || !ITdAddr)
3012 break;
3013
3014 /*
3015 * If isochronous endpoints are around, don't slow down the timer. Getting the timing right
3016 * is difficult enough as it is.
3017 */
3018 pOhci->fIdle = false;
3019
3020 /*
3021 * Read the current ITD and check what we're supposed to do about it.
3022 */
3023 ohciReadITd(pOhci, ITdAddr, &ITd);
3024 const uint32_t ITdAddrNext = ITd.NextTD & ED_PTR_MASK;
3025 const int16_t R = u16CurFrame - (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF); /* 4.3.2.3 */
3026 const int16_t cFrames = ((ITd.HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3027
3028 if (R < cFrames)
3029 {
3030 /*
3031 * It's inside the current or a future launch window.
3032 *
3033 * We will try maximize the TD in flight here to deal with EMT scheduling
3034 * issues and similar stuff which will screw up the time. So, we will only
3035 * stop submitting TD when we reach a gap (in time) or end of the list.
3036 */
3037 if ( R < 0 /* (a future frame) */
3038 && (uint16_t)u32NextFrame != (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF))
3039 break;
3040 if (ohci_in_flight_find(pOhci, ITdAddr) < 0)
3041 if (!ohciServiceIsochronousTd(pOhci, &ITd, ITdAddr, R < 0 ? 0 : R, pEd, EdAddr))
3042 break;
3043
3044 ITdAddrPrev = ITdAddr;
3045 }
3046 else
3047 {
3048#if 1
3049 /*
3050 * Ok, the launch window for this TD has passed.
3051 * If it's not in flight it should be retired with a DataOverrun status (TD).
3052 *
3053 * Don't remove in-flight TDs before they complete.
3054 * Windows will, upon the completion of another ITD it seems, check for if
3055 * any other TDs has been unlinked. If we unlink them before they really
3056 * complete all the packet status codes will be NotAccesed and Windows
3057 * will fail the URB with status USBD_STATUS_ISOCH_REQUEST_FAILED.
3058 *
3059 * I don't know if unlinking TDs out of order could cause similar problems,
3060 * time will show.
3061 */
3062 int iInFlight = ohci_in_flight_find(pOhci, ITdAddr);
3063 if (iInFlight >= 0)
3064 ITdAddrPrev = ITdAddr;
3065 else if (!ohciServiceIsochronousTdUnlink(pOhci, &ITd, ITdAddr, ITdAddrPrev,
3066 NULL, pEd, EdAddr))
3067 {
3068 Log(("ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3069 break;
3070 }
3071#else /* BAD IDEA: */
3072 /*
3073 * Ok, the launch window for this TD has passed.
3074 * If it's not in flight it should be retired with a DataOverrun status (TD).
3075 *
3076 * If it's in flight we will try unlink it from the list prematurely to
3077 * help the guest to move on and shorten the list we have to walk. We currently
3078 * are successfull with the first URB but then it goes too slowly...
3079 */
3080 int iInFlight = ohci_in_flight_find(pOhci, ITdAddr);
3081 if (!ohciServiceIsochronousTdUnlink(pOhci, &ITd, ITdAddr, ITdAddrPrev,
3082 iInFlight < 0 ? NULL : pOhci->aInFlight[iInFlight].pUrb,
3083 pEd, EdAddr))
3084 {
3085 Log(("ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3086 break;
3087 }
3088#endif
3089 }
3090
3091 /* advance to the next ITD */
3092 ITdAddr = ITdAddrNext;
3093 u32NextFrame = (ITd.HwInfo & ITD_HWINFO_SF) + cFrames;
3094 }
3095}
3096
3097
3098/**
3099 * Checks if a endpoints has TDs queued and is ready to have them processed.
3100 *
3101 * @returns true if it's ok to process TDs.
3102 * @param pEd The endpoint data.
3103 */
3104DECLINLINE(bool) ohciIsEdReady(PCOHCIED pEd)
3105{
3106 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3107 && !(pEd->HeadP & ED_HEAD_HALTED)
3108 && !(pEd->hwinfo & ED_HWINFO_SKIP);
3109}
3110
3111
3112/**
3113 * Services the bulk list.
3114 *
3115 * On the bulk list we must reassemble URBs from multiple TDs using heuristics
3116 * derived from USB tracing done in the guests and guest source code (when available).
3117 */
3118static void ohciServiceBulkList(POHCI pOhci)
3119{
3120#ifdef LOG_ENABLED
3121 if (g_fLogBulkEPs)
3122 ohciDumpEdList(pOhci, pOhci->bulk_head, "Bulk before", true);
3123 if (pOhci->bulk_cur)
3124 Log(("ohciServiceBulkList: bulk_cur=%#010x before listprocessing!!! HCD have positioned us!!!\n", pOhci->bulk_cur));
3125#endif
3126
3127 /*
3128 * ", HC will start processing the Bulk list and will set BF [BulkListFilled] to 0"
3129 * - We've simplified and are always starting at the head of the list and working
3130 * our way thru to the end each time.
3131 */
3132 pOhci->status &= ~OHCI_STATUS_BLF;
3133 pOhci->bulk_cur = 0;
3134
3135 uint32_t EdAddr = pOhci->bulk_head;
3136 while (EdAddr)
3137 {
3138 OHCIED Ed;
3139 ohciReadEd(pOhci, EdAddr, &Ed);
3140 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3141 if (ohciIsEdReady(&Ed))
3142 {
3143 pOhci->status |= OHCI_STATUS_BLF;
3144
3145#if 1
3146 /*
3147
3148 * After we figured out that all the TDs submitted for dealing with MSD
3149 * read/write data really makes up on single URB, and that we must
3150 * reassemble these TDs into an URB before submitting it, there is no
3151 * longer any need for servicing anything other than the head *URB*
3152 * on a bulk endpoint.
3153 */
3154 ohciServiceHeadTdMultiple(pOhci, VUSBXFERTYPE_BULK, &Ed, EdAddr, "Bulk");
3155#else
3156 /*
3157 * This alternative code was used before we started reassembling URBs from
3158 * multiple TDs. We keep it handy for debugging.
3159 */
3160 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3161 if (!ohciIsTdInFlight(pOhci, TdAddr))
3162 {
3163 do
3164 {
3165 if (!ohciServiceTdMultiple(pOhci, VUSBXFERTYPE_BULK, &Ed, EdAddr, TdAddr, &TdAddr, "Bulk"))
3166 {
3167 LogFlow(("ohciServiceBulkList: ohciServiceTdMultiple -> false\n"));
3168 break;
3169 }
3170 if ( (TdAddr & ED_PTR_MASK) == (Ed.TailP & ED_PTR_MASK)
3171 || !TdAddr /* paranoia */)
3172 {
3173 LogFlow(("ohciServiceBulkList: TdAddr=%#010RX32 Ed.TailP=%#010RX32\n", TdAddr, Ed.TailP));
3174 break;
3175 }
3176
3177 ohciReadEd(pOhci, EdAddr, &Ed); /* It might have been updated on URB completion. */
3178 } while (ohciIsEdReady(&Ed));
3179 }
3180#endif
3181 }
3182
3183 /* next end point */
3184 EdAddr = Ed.NextED & ED_PTR_MASK;
3185
3186 }
3187
3188#ifdef LOG_ENABLED
3189 if (g_fLogBulkEPs)
3190 ohciDumpEdList(pOhci, pOhci->bulk_head, "Bulk after ", true);
3191#endif
3192}
3193
3194
3195/**
3196 * Services the control list.
3197 *
3198 * The control list has complex URB assembling, but that's taken
3199 * care of at VUSB level (unlike the other transfer types).
3200 */
3201static void ohciServiceCtrlList(POHCI pOhci)
3202{
3203#ifdef LOG_ENABLED
3204 if (g_fLogControlEPs)
3205 ohciDumpEdList(pOhci, pOhci->ctrl_head, "Ctrl before", true);
3206 if (pOhci->ctrl_cur)
3207 Log(("ohciServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n", pOhci->ctrl_cur));
3208#endif
3209
3210 /*
3211 * ", HC will start processing the list and will set ControlListFilled to 0"
3212 * - We've simplified and are always starting at the head of the list and working
3213 * our way thru to the end each time.
3214 */
3215 pOhci->status &= ~OHCI_STATUS_CLF;
3216 pOhci->ctrl_cur = 0;
3217
3218 uint32_t EdAddr = pOhci->ctrl_head;
3219 while (EdAddr)
3220 {
3221 OHCIED Ed;
3222 ohciReadEd(pOhci, EdAddr, &Ed);
3223 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3224 if (ohciIsEdReady(&Ed))
3225 {
3226#if 1
3227 /*
3228 * Control TDs depends on order and stage. Only one can be in-flight
3229 * at any given time. OTOH, some stages are completed immediately,
3230 * so we process the list until we've got a head which is in-fligth
3231 * or reach the end of the list.
3232 */
3233 do
3234 {
3235 if ( !ohciServiceHeadTd(pOhci, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control")
3236 || ohciIsTdInFlight(pOhci, Ed.HeadP & ED_PTR_MASK))
3237 {
3238 pOhci->status |= OHCI_STATUS_CLF;
3239 break;
3240 }
3241 ohciReadEd(pOhci, EdAddr, &Ed); /* It might have been updated on URB completion. */
3242 } while (ohciIsEdReady(&Ed));
3243#else
3244 /* Simplistic, for debugging. */
3245 ohciServiceHeadTd(pOhci, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control");
3246 pOhci->status |= OHCI_STATUS_CLF;
3247#endif
3248 }
3249
3250 /* next end point */
3251 EdAddr = Ed.NextED & ED_PTR_MASK;
3252 }
3253
3254#ifdef LOG_ENABLED
3255 if (g_fLogControlEPs)
3256 ohciDumpEdList(pOhci, pOhci->ctrl_head, "Ctrl after ", true);
3257#endif
3258}
3259
3260
3261/**
3262 * Services the periodic list.
3263 *
3264 * On the interrupt portion of the periodic list we must reassemble URBs from multiple
3265 * TDs using heuristics derived from USB tracing done in the guests and guest source
3266 * code (when available).
3267 */
3268static void ohciServicePeriodicList(POHCI pOhci)
3269{
3270 /*
3271 * Read the list head from the HCCA.
3272 */
3273 const unsigned iList = pOhci->HcFmNumber % OHCI_HCCA_NUM_INTR;
3274 uint32_t EdAddr;
3275 ohciGetDWords(pOhci, pOhci->hcca + iList * sizeof(EdAddr), &EdAddr, 1);
3276
3277#ifdef LOG_ENABLED
3278 const uint32_t EdAddrHead = EdAddr;
3279 if (g_fLogInterruptEPs)
3280 {
3281 char sz[48];
3282 RTStrPrintf(sz, sizeof(sz), "Int%02x before", iList);
3283 ohciDumpEdList(pOhci, EdAddrHead, sz, true);
3284 }
3285#endif
3286
3287 /*
3288 * Iterate the endpoint list.
3289 */
3290 while (EdAddr)
3291 {
3292 OHCIED Ed;
3293 ohciReadEd(pOhci, EdAddr, &Ed);
3294
3295 if (ohciIsEdReady(&Ed))
3296 {
3297 /*
3298 * "There is no separate head pointer of isochronous transfers. The first
3299 * isochronous Endpoint Descriptor simply links to the last interrupt
3300 * Endpoint Descriptor."
3301 */
3302 if (!(Ed.hwinfo & ED_HWINFO_ISO))
3303 {
3304 /*
3305 * Presently we will only process the head URB on an interrupt endpoint.
3306 */
3307 ohciServiceHeadTdMultiple(pOhci, VUSBXFERTYPE_INTR, &Ed, EdAddr, "Periodic");
3308 }
3309 else if (pOhci->ctl & OHCI_CTL_IE)
3310 {
3311 /*
3312 * Presently only the head ITD.
3313 */
3314 ohciServiceIsochronousEndpoint(pOhci, &Ed, EdAddr);
3315 }
3316 else
3317 break;
3318 }
3319
3320 /* next end point */
3321 EdAddr = Ed.NextED & ED_PTR_MASK;
3322 }
3323
3324#ifdef LOG_ENABLED
3325 if (g_fLogInterruptEPs)
3326 {
3327 char sz[48];
3328 RTStrPrintf(sz, sizeof(sz), "Int%02x after ", iList);
3329 ohciDumpEdList(pOhci, EdAddrHead, sz, true);
3330 }
3331#endif
3332}
3333
3334
3335/**
3336 * Update the HCCA.
3337 *
3338 * @param pOhci The OHCI instance data.
3339 */
3340static void ohciUpdateHCCA(POHCI pOhci)
3341{
3342 struct ohci_hcca hcca;
3343 ohciPhysRead(pOhci, pOhci->hcca + OHCI_HCCA_OFS, &hcca, sizeof(hcca));
3344
3345 hcca.frame = RT_H2LE_U16((uint16_t)pOhci->HcFmNumber);
3346 hcca.pad = 0;
3347
3348 bool fWriteDoneHeadInterrupt = false;
3349 if ( pOhci->dqic == 0
3350 && (pOhci->intr_status & OHCI_INTR_WRITE_DONE_HEAD) == 0)
3351 {
3352 uint32_t done = pOhci->done;
3353
3354 if (pOhci->intr_status & ~( OHCI_INTR_MASTER_INTERRUPT_ENABLED | OHCI_INTR_OWNERSHIP_CHANGE
3355 | OHCI_INTR_WRITE_DONE_HEAD) )
3356 done |= 0x1;
3357
3358 hcca.done = RT_H2LE_U32(done);
3359 pOhci->done = 0;
3360 pOhci->dqic = 0x7;
3361
3362 Log(("ohci: Writeback Done (%#010x) on frame %#x (age %#x)\n", hcca.done,
3363 pOhci->HcFmNumber, pOhci->HcFmNumber - pOhci->u32FmDoneQueueTail));
3364#ifdef LOG_ENABLED
3365 ohciDumpTdQueue(pOhci, hcca.done & ED_PTR_MASK, "DoneQueue");
3366#endif
3367 Assert(RT_OFFSETOF(struct ohci_hcca, done) == 4);
3368#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3369 ohci_in_done_queue_zap(pOhci);
3370#endif
3371 fWriteDoneHeadInterrupt = true;
3372 }
3373
3374 ohciPhysWrite(pOhci, pOhci->hcca + OHCI_HCCA_OFS, (uint8_t *)&hcca, sizeof(hcca));
3375 if (fWriteDoneHeadInterrupt)
3376 ohciSetInterrupt(pOhci, OHCI_INTR_WRITE_DONE_HEAD);
3377}
3378
3379
3380/**
3381 * Calculate frame timer variables given a frame rate (1,000 Hz is the full speed).
3382 */
3383static void ohciCalcTimerIntervals(POHCI pOhci, uint32_t u32FrameRate)
3384{
3385 Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
3386
3387
3388 pOhci->cTicksPerFrame = pOhci->u64TimerHz / u32FrameRate;
3389 if (!pOhci->cTicksPerFrame)
3390 pOhci->cTicksPerFrame = 1;
3391 pOhci->cTicksPerUsbTick = pOhci->u64TimerHz >= VUSB_BUS_HZ ? pOhci->u64TimerHz / VUSB_BUS_HZ : 1;
3392 pOhci->uFrameRate = u32FrameRate;
3393}
3394
3395
3396/**
3397 * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
3398 */
3399static void ohciStartOfFrame(POHCI pOhci)
3400{
3401 uint32_t uNewFrameRate = pOhci->uFrameRate;
3402#ifdef LOG_ENABLED
3403 const uint32_t status_old = pOhci->status;
3404#endif
3405
3406 /*
3407 * Update HcFmRemaining.FRT and re-arm the timer.
3408 */
3409 pOhci->frt = pOhci->fit;
3410#if 1 /* This is required for making the quickcam work on the mac. Should really look
3411 into that adaptive polling stuff... */
3412 pOhci->SofTime += pOhci->cTicksPerFrame;
3413 const uint64_t u64Now = TMTimerGet(pOhci->CTX_SUFF(pEndOfFrameTimer));
3414 if (pOhci->SofTime + pOhci->cTicksPerFrame < u64Now)
3415 pOhci->SofTime = u64Now - pOhci->cTicksPerFrame / 2;
3416#else
3417 pOhci->SofTime = TMTimerGet(pOhci->CTX_SUFF(pEndOfFrameTimer));
3418#endif
3419 TMTimerSet(pOhci->CTX_SUFF(pEndOfFrameTimer), pOhci->SofTime + pOhci->cTicksPerFrame);
3420
3421 /*
3422 * Check that the HCCA address isn't bogus. Linux 2.4.x is known to start
3423 * the bus with a hcca of 0 to work around problem with a specific controller.
3424 */
3425 bool fValidHCCA = !( pOhci->hcca >= OHCI_HCCA_MASK
3426 || pOhci->hcca < ~OHCI_HCCA_MASK);
3427
3428#if 0 /* moved down for higher speed. */
3429 /*
3430 * Update the HCCA.
3431 * Should be done after SOF but before HC read first ED in this frame.
3432 */
3433 if (fValidHCCA)
3434 ohciUpdateHCCA(pOhci);
3435#endif
3436
3437 /* "After writing to HCCA, HC will set SF in HcInterruptStatus" - guest isn't executing, so ignore the order! */
3438 ohciSetInterrupt(pOhci, OHCI_INTR_START_OF_FRAME);
3439
3440 if (pOhci->fno)
3441 {
3442 ohciSetInterrupt(pOhci, OHCI_INTR_FRAMENUMBER_OVERFLOW);
3443 pOhci->fno = 0;
3444 }
3445
3446 /* If the HCCA address is invalid, we're quitting here to avoid doing something which cannot be reported to the HCD. */
3447 if (!fValidHCCA)
3448 {
3449 Log(("ohciStartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
3450 pOhci->hcca, ~OHCI_HCCA_MASK, OHCI_HCCA_MASK));
3451 return;
3452 }
3453
3454 /*
3455 * Periodic EPs.
3456 */
3457 if (pOhci->ctl & OHCI_CTL_PLE)
3458 ohciServicePeriodicList(pOhci);
3459
3460 /*
3461 * Control EPs.
3462 */
3463 if ( (pOhci->ctl & OHCI_CTL_CLE)
3464 && (pOhci->status & OHCI_STATUS_CLF) )
3465 ohciServiceCtrlList(pOhci);
3466
3467 /*
3468 * Bulk EPs.
3469 */
3470 if ( (pOhci->ctl & OHCI_CTL_BLE)
3471 && (pOhci->status & OHCI_STATUS_BLF))
3472 ohciServiceBulkList(pOhci);
3473
3474#if 1
3475 /*
3476 * Update the HCCA after processing the lists and everything. A bit experimental.
3477 *
3478 * ASSUME the guest won't be very upset if a TD is completed, retired and handed
3479 * back immediately. The idea is to be able to retire the data and/or status stages
3480 * of a control transfer together with the setup stage, thus saving a frame. This
3481 * behaviour is should be perfectly ok, since the setup (and maybe data) stages
3482 * have already taken at least one frame to complete.
3483 *
3484 * But, when implementing the first synchronous virtual USB devices, we'll have to
3485 * verify that the guest doesn't choke when having a TD returned in the same frame
3486 * as it was submitted.
3487 */
3488 ohciUpdateHCCA(pOhci);
3489#endif
3490
3491#ifdef LOG_ENABLED
3492 if (pOhci->status ^ status_old)
3493 {
3494 uint32_t val = pOhci->status;
3495 uint32_t chg = val ^ status_old; NOREF(chg);
3496 Log2(("ohciStartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
3497 val,
3498 chg & RT_BIT(0) ? "*" : "", val & 1,
3499 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
3500 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
3501 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
3502 chg & (3<<16)? "*" : "", (val >> 16) & 3));
3503 }
3504#endif
3505
3506 /*
3507 * Adjust the frame timer interval based on idle detection.
3508 */
3509 if (pOhci->fIdle)
3510 {
3511 pOhci->cIdleCycles++;
3512 /* Set the new frame rate based on how long we've been idle. Tunable. */
3513 switch (pOhci->cIdleCycles)
3514 {
3515 case 4: uNewFrameRate = 500; break; /* 2ms interval */
3516 case 16:uNewFrameRate = 125; break; /* 8ms interval */
3517 case 24:uNewFrameRate = 50; break; /* 20ms interval */
3518 default: break;
3519 }
3520 /* Avoid overflow. */
3521 if (pOhci->cIdleCycles > 60000)
3522 pOhci->cIdleCycles = 20000;
3523 }
3524 else
3525 {
3526 if (pOhci->cIdleCycles)
3527 {
3528 pOhci->cIdleCycles = 0;
3529 uNewFrameRate = OHCI_DEFAULT_TIMER_FREQ;
3530 }
3531 }
3532 if (uNewFrameRate != pOhci->uFrameRate)
3533 {
3534 ohciCalcTimerIntervals(pOhci, uNewFrameRate);
3535 if (uNewFrameRate == OHCI_DEFAULT_TIMER_FREQ)
3536 {
3537 /* If we're switching back to full speed, re-program the timer immediately to minimize latency. */
3538 TMTimerSet(pOhci->CTX_SUFF(pEndOfFrameTimer), pOhci->SofTime + pOhci->cTicksPerFrame);
3539 }
3540 }
3541}
3542
3543/**
3544 * Updates the HcFmNumber and FNO registers.
3545 */
3546static void bump_frame_number(POHCI pOhci)
3547{
3548 const uint16_t u16OldFmNumber = pOhci->HcFmNumber++;
3549 if ((u16OldFmNumber ^ pOhci->HcFmNumber) & RT_BIT(15))
3550 pOhci->fno = 1;
3551}
3552
3553/**
3554 * Do frame processing on frame boundary
3555 */
3556static void ohciFrameBoundaryTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3557{
3558 POHCI pOhci = (POHCI)pvUser;
3559 STAM_PROFILE_START(&pOhci->StatTimer, a);
3560
3561 /* Reset idle detection flag */
3562 pOhci->fIdle = true;
3563
3564 VUSBIRhReapAsyncUrbs(pOhci->RootHub.pIRhConn, 0);
3565
3566 /* Frame boundary, so do EOF stuf here */
3567 bump_frame_number(pOhci);
3568 if ( (pOhci->dqic != 0x7) && (pOhci->dqic != 0) )
3569 pOhci->dqic--;
3570
3571 /* Start the next frame */
3572 ohciStartOfFrame(pOhci);
3573
3574 STAM_PROFILE_STOP(&pOhci->StatTimer, a);
3575}
3576
3577/**
3578 * Start sending SOF tokens across the USB bus, lists are processed in
3579 * next frame
3580 */
3581static void ohciBusStart(POHCI pOhci)
3582{
3583 VUSBIDevPowerOn(pOhci->RootHub.pIDev);
3584 bump_frame_number(pOhci);
3585 pOhci->dqic = 0x7;
3586
3587 Log(("ohci: %s: Bus started\n", pOhci->PciDev.name));
3588
3589 pOhci->SofTime = TMTimerGet(pOhci->CTX_SUFF(pEndOfFrameTimer)) - pOhci->cTicksPerFrame;
3590 pOhci->fIdle = false; /* Assume we won't be idle */
3591 ohciStartOfFrame(pOhci);
3592}
3593
3594/**
3595 * Stop sending SOF tokens on the bus
3596 */
3597static void ohciBusStop(POHCI pOhci)
3598{
3599 if (pOhci->CTX_SUFF(pEndOfFrameTimer))
3600 TMTimerStop(pOhci->CTX_SUFF(pEndOfFrameTimer));
3601 VUSBIDevPowerOff(pOhci->RootHub.pIDev);
3602}
3603
3604/**
3605 * Move in to resume state
3606 */
3607static void ohciBusResume(POHCI pOhci, bool fHardware)
3608{
3609 pOhci->ctl &= ~OHCI_CTL_HCFS;
3610 pOhci->ctl |= OHCI_USB_RESUME;
3611
3612 Log(("pOhci: ohciBusResume fHardware=%RTbool RWE=%s\n",
3613 fHardware, (pOhci->ctl & OHCI_CTL_RWE) ? "on" : "off"));
3614
3615 if (fHardware && (pOhci->ctl & OHCI_CTL_RWE))
3616 ohciSetInterrupt(pOhci, OHCI_INTR_RESUME_DETECT);
3617
3618 ohciBusStart(pOhci);
3619}
3620
3621
3622/* Power a port up or down */
3623static void rhport_power(POHCIROOTHUB pRh, unsigned iPort, bool fPowerUp)
3624{
3625 POHCIHUBPORT pPort = &pRh->aPorts[iPort];
3626 bool fOldPPS = !!(pPort->fReg & OHCI_PORT_PPS);
3627 if (fPowerUp)
3628 {
3629 /* power up */
3630 if (pPort->pDev)
3631 pPort->fReg |= OHCI_PORT_R_CURRENT_CONNECT_STATUS;
3632 if (pPort->fReg & OHCI_PORT_R_CURRENT_CONNECT_STATUS)
3633 pPort->fReg |= OHCI_PORT_R_POWER_STATUS;
3634 if (pPort->pDev && !fOldPPS)
3635 VUSBIDevPowerOn(pPort->pDev);
3636 }
3637 else
3638 {
3639 /* power down */
3640 pPort->fReg &= ~( OHCI_PORT_R_POWER_STATUS
3641 | OHCI_PORT_R_CURRENT_CONNECT_STATUS
3642 | OHCI_PORT_R_SUSPEND_STATUS
3643 | OHCI_PORT_R_RESET_STATUS);
3644 if (pPort->pDev && fOldPPS)
3645 VUSBIDevPowerOff(pPort->pDev);
3646 }
3647}
3648
3649#endif /* IN_RING3 */
3650
3651/**
3652 * Read the HcRevision register.
3653 */
3654static int HcRevision_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3655{
3656 Log2(("HcRevision_r() -> 0x10\n"));
3657 *pu32Value = 0x10; /* OHCI revision 1.0, no emulation. */
3658 return VINF_SUCCESS;
3659}
3660
3661/**
3662 * Write to the HcRevision register.
3663 */
3664static int HcRevision_w(POHCI pOhci, uint32_t iReg, uint32_t u32Value)
3665{
3666 Log2(("HcRevision_w(%#010x) - denied\n", u32Value));
3667 AssertMsgFailed(("Invalid operation!!! u32Value=%#010x\n", u32Value));
3668 return VINF_SUCCESS;
3669}
3670
3671/**
3672 * Read the HcControl register.
3673 */
3674static int HcControl_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3675{
3676 uint32_t ctl = pOhci->ctl;
3677 Log2(("HcControl_r -> %#010x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
3678 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
3679 (ctl >> 9) & 1, (ctl >> 10) & 1));
3680 *pu32Value = ctl;
3681 return VINF_SUCCESS;
3682}
3683
3684/**
3685 * Write the HcControl register.
3686 */
3687static int HcControl_w(POHCI pOhci, uint32_t iReg, uint32_t val)
3688{
3689 /* log it. */
3690 uint32_t chg = pOhci->ctl ^ val; NOREF(chg);
3691 Log2(("HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
3692 val,
3693 chg & 3 ? "*" : "", val & 3,
3694 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
3695 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
3696 chg & RT_BIT(4) ? "*" : "", (val >> 4) & 1,
3697 chg & RT_BIT(5) ? "*" : "", (val >> 5) & 1,
3698 chg & (3 << 6)? "*" : "", (val >> 6) & 3,
3699 chg & RT_BIT(8) ? "*" : "", (val >> 8) & 1,
3700 chg & RT_BIT(9) ? "*" : "", (val >> 9) & 1,
3701 chg & RT_BIT(10) ? "*" : "", (val >> 10) & 1));
3702 if (val & ~0x07ff)
3703 Log2(("Unknown bits %#x are set!!!\n", val & ~0x07ff));
3704
3705 /* see what changed and take action on that. */
3706 uint32_t old_state = pOhci->ctl & OHCI_CTL_HCFS;
3707 uint32_t new_state = val & OHCI_CTL_HCFS;
3708
3709#ifdef IN_RING3
3710 pOhci->ctl = val;
3711 if (new_state != old_state)
3712 {
3713 switch (new_state)
3714 {
3715 case OHCI_USB_OPERATIONAL:
3716 LogRel(("OHCI: USB Operational\n"));
3717 ohciBusStart(pOhci);
3718 break;
3719 case OHCI_USB_SUSPEND:
3720 ohciBusStop(pOhci);
3721 LogRel(("OHCI: USB Suspended\n"));
3722 break;
3723 case OHCI_USB_RESUME:
3724 LogRel(("OHCI: USB Resume\n"));
3725 ohciBusResume(pOhci, false /* not hardware */);
3726 break;
3727 case OHCI_USB_RESET:
3728 {
3729 LogRel(("OHCI: USB Reset\n"));
3730 ohciBusStop(pOhci);
3731 /** @todo This should probably do a real reset, but we don't implement
3732 * that correctly in the roothub reset callback yet. check it's
3733 * comments and argument for more details. */
3734 VUSBIDevReset(pOhci->RootHub.pIDev, false /* don't do a real reset */, NULL, NULL, NULL);
3735 break;
3736 }
3737 }
3738 }
3739#else /* !IN_RING3 */
3740 if ( new_state != old_state )
3741 {
3742 Log2(("HcControl_w: state changed -> VINF_IOM_HC_MMIO_WRITE\n"));
3743 return VINF_IOM_HC_MMIO_WRITE;
3744 }
3745 pOhci->ctl = val;
3746#endif /* !IN_RING3 */
3747
3748 return VINF_SUCCESS;
3749}
3750
3751/**
3752 * Read the HcCommandStatus register.
3753 */
3754static int HcCommandStatus_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3755{
3756 uint32_t status = pOhci->status;
3757 Log2(("HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
3758 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3));
3759 *pu32Value = status;
3760 return VINF_SUCCESS;
3761}
3762
3763/**
3764 * Write to the HcCommandStatus register.
3765 */
3766static int HcCommandStatus_w(POHCI pOhci, uint32_t iReg, uint32_t val)
3767{
3768 /* log */
3769 uint32_t chg = pOhci->status ^ val; NOREF(chg);
3770 Log2(("HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
3771 val,
3772 chg & RT_BIT(0) ? "*" : "", val & 1,
3773 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
3774 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
3775 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
3776 chg & (3<<16)? "!!!":"", (pOhci->status >> 16) & 3));
3777 if (val & ~0x0003000f)
3778 Log2(("Unknown bits %#x are set!!!\n", val & ~0x0003000f));
3779
3780 /* SOC is read-only */
3781 val = (val & ~OHCI_STATUS_SOC);
3782
3783#ifdef IN_RING3
3784 /* "bits written as '0' remain unchanged in the register" */
3785 pOhci->status |= val;
3786 if (pOhci->status & OHCI_STATUS_HCR)
3787 {
3788 LogRel(("OHCI: Software reset\n"));
3789 ohciDoReset(pOhci, OHCI_USB_SUSPEND, false /* N/A */);
3790 }
3791#else
3792 if ((pOhci->status | val) & OHCI_STATUS_HCR)
3793 {
3794 LogFlow(("HcCommandStatus_w: reset -> VINF_IOM_HC_MMIO_WRITE\n"));
3795 return VINF_IOM_HC_MMIO_WRITE;
3796 }
3797 pOhci->status |= val;
3798#endif
3799 return VINF_SUCCESS;
3800}
3801
3802/**
3803 * Read the HcInterruptStatus register.
3804 */
3805static int HcInterruptStatus_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3806{
3807 uint32_t val = pOhci->intr_status;
3808 Log2(("HcInterruptStatus_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
3809 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
3810 (val >> 6) & 1, (val >> 30) & 1));
3811 *pu32Value = val;
3812 return VINF_SUCCESS;
3813}
3814
3815/**
3816 * Write to the HcInterruptStatus register.
3817 */
3818static int HcInterruptStatus_w(POHCI pOhci, uint32_t iReg, uint32_t val)
3819{
3820 uint32_t res = pOhci->intr_status & ~val;
3821 uint32_t chg = pOhci->intr_status ^ res; NOREF(chg);
3822 Log2(("HcInterruptStatus_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d\n",
3823 val,
3824 chg & RT_BIT(0) ? "*" : "", res & 1,
3825 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
3826 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
3827 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
3828 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
3829 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
3830 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
3831 chg & RT_BIT(30)? "*" : "", (res >> 30) & 1));
3832 if ( (val & ~0xc000007f)
3833 && val != 0xffffffff /* ignore clear-all-like requests from xp. */)
3834 Log2(("Unknown bits %#x are set!!!\n", val & ~0xc000007f));
3835
3836 /* "The Host Controller Driver may clear specific bits in this
3837 * register by writing '1' to bit positions to be cleared"
3838 */
3839 pOhci->intr_status &= ~val;
3840 ohciUpdateInterrupt(pOhci, "HcInterruptStatus_w");
3841 return VINF_SUCCESS;
3842}
3843
3844/**
3845 * Read the HcInterruptEnable register
3846 */
3847static int HcInterruptEnable_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3848{
3849 uint32_t val = pOhci->intr;
3850 Log2(("HcInterruptEnable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
3851 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
3852 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
3853 *pu32Value = val;
3854 return VINF_SUCCESS;
3855}
3856
3857/**
3858 * Writes to the HcInterruptEnable register.
3859 */
3860static int HcInterruptEnable_w(POHCI pOhci, uint32_t iReg, uint32_t val)
3861{
3862 uint32_t res = pOhci->intr | val;
3863 uint32_t chg = pOhci->intr ^ res; NOREF(chg);
3864 Log2(("HcInterruptEnable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
3865 val,
3866 chg & RT_BIT(0) ? "*" : "", res & 1,
3867 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
3868 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
3869 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
3870 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
3871 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
3872 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
3873 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
3874 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
3875 if (val & ~0xc000007f)
3876 Log2(("Uknown bits %#x are set!!!\n", val & ~0xc000007f));
3877
3878 pOhci->intr |= val;
3879 ohciUpdateInterrupt(pOhci, "HcInterruptEnable_w");
3880 return VINF_SUCCESS;
3881}
3882
3883/**
3884 * Reads the HcInterruptDisable register.
3885 */
3886static int HcInterruptDisable_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3887{
3888#if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */
3889 uint32_t val = pOhci->intr;
3890#else /* old code. */
3891 uint32_t val = ~pOhci->intr;
3892#endif
3893 Log2(("HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
3894 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
3895 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
3896
3897 *pu32Value = val;
3898 return VINF_SUCCESS;
3899}
3900
3901/**
3902 * Writes to the HcInterruptDisable register.
3903 */
3904static int HcInterruptDisable_w(POHCI pOhci, uint32_t iReg, uint32_t val)
3905{
3906 uint32_t res = pOhci->intr & ~val;
3907 uint32_t chg = pOhci->intr ^ res; NOREF(chg);
3908 Log2(("HcInterruptDisable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
3909 val,
3910 chg & RT_BIT(0) ? "*" : "", res & 1,
3911 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
3912 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
3913 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
3914 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
3915 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
3916 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
3917 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
3918 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
3919 /* Don't bitch about invalid bits here since it makes sense to disble
3920 * interrupts you don't know about. */
3921
3922 pOhci->intr &= ~val;
3923 ohciUpdateInterrupt(pOhci, "HcInterruptDisable_w");
3924 return VINF_SUCCESS;
3925}
3926
3927/**
3928 * Read the HcHCCA register (Host Controller Communications Area physical address).
3929 */
3930static int HcHCCA_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3931{
3932 Log2(("HcHCCA_r() -> %#010x\n", pOhci->hcca));
3933 *pu32Value = pOhci->hcca;
3934 return VINF_SUCCESS;
3935}
3936
3937/**
3938 * Write to the HcHCCA register (Host Controller Communications Area physical address).
3939 */
3940static int HcHCCA_w(POHCI pOhci, uint32_t iReg, uint32_t Value)
3941{
3942 Log2(("HcHCCA_w(%#010x) - old=%#010x new=%#010x\n", Value, pOhci->hcca, Value & OHCI_HCCA_MASK));
3943 pOhci->hcca = Value & OHCI_HCCA_MASK;
3944 return VINF_SUCCESS;
3945}
3946
3947/**
3948 * Read the HcPeriodCurrentED register.
3949 */
3950static int HcPeriodCurrentED_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3951{
3952 Log2(("HcPeriodCurrentED_r() -> %#010x\n", pOhci->per_cur));
3953 *pu32Value = pOhci->per_cur;
3954 return VINF_SUCCESS;
3955}
3956
3957/**
3958 * Write to the HcPeriodCurrentED register.
3959 */
3960static int HcPeriodCurrentED_w(POHCI pOhci, uint32_t iReg, uint32_t val)
3961{
3962 Log(("HcPeriodCurrentED_w(%#010x) - old=%#010x new=%#010x (This is a read only register, only the linux guys don't respect that!)\n",
3963 val, pOhci->per_cur, val & ~7));
3964 //AssertMsgFailed(("HCD (Host Controller Driver) should not write to HcPeriodCurrentED! val=%#010x (old=%#010x)\n", val, pOhci->per_cur));
3965 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
3966 pOhci->per_cur = val & ~7;
3967 return VINF_SUCCESS;
3968}
3969
3970/**
3971 * Read the HcControlHeadED register.
3972 */
3973static int HcControlHeadED_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3974{
3975 Log2(("HcControlHeadED_r() -> %#010x\n", pOhci->ctrl_head));
3976 *pu32Value = pOhci->ctrl_head;
3977 return VINF_SUCCESS;
3978}
3979
3980/**
3981 * Write to the HcControlHeadED register.
3982 */
3983static int HcControlHeadED_w(POHCI pOhci, uint32_t iReg, uint32_t val)
3984{
3985 Log2(("HcControlHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pOhci->ctrl_head, val & ~7));
3986 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
3987 pOhci->ctrl_head = val & ~7;
3988 return VINF_SUCCESS;
3989}
3990
3991/**
3992 * Read the HcControlCurrentED register.
3993 */
3994static int HcControlCurrentED_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
3995{
3996 Log2(("HcControlCurrentED_r() -> %#010x\n", pOhci->ctrl_cur));
3997 *pu32Value = pOhci->ctrl_cur;
3998 return VINF_SUCCESS;
3999}
4000
4001/**
4002 * Write to the HcControlCurrentED register.
4003 */
4004static int HcControlCurrentED_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4005{
4006 Log2(("HcControlCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pOhci->ctrl_cur, val & ~7));
4007 AssertMsg(!(pOhci->ctl & OHCI_CTL_CLE), ("Illegal write! HcControl.ControlListEnabled is set! val=%#010x\n", val));
4008 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4009 pOhci->ctrl_cur = val & ~7;
4010 return VINF_SUCCESS;
4011}
4012
4013/**
4014 * Read the HcBulkHeadED register.
4015 */
4016static int HcBulkHeadED_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4017{
4018 Log2(("HcBulkHeadED_r() -> %#010x\n", pOhci->bulk_head));
4019 *pu32Value = pOhci->bulk_head;
4020 return VINF_SUCCESS;
4021}
4022
4023/**
4024 * Write to the HcBulkHeadED register.
4025 */
4026static int HcBulkHeadED_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4027{
4028 Log2(("HcBulkHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pOhci->bulk_head, val & ~7));
4029 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4030 pOhci->bulk_head = val & ~7;
4031 return VINF_SUCCESS;
4032}
4033
4034/**
4035 * Read the HcBulkCurrentED register.
4036 */
4037static int HcBulkCurrentED_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4038{
4039 Log2(("HcBulkCurrentED_r() -> %#010x\n", pOhci->bulk_cur));
4040 *pu32Value = pOhci->bulk_cur;
4041 return VINF_SUCCESS;
4042}
4043
4044/**
4045 * Write to the HcBulkCurrentED register.
4046 */
4047static int HcBulkCurrentED_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4048{
4049 Log2(("HcBulkCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pOhci->bulk_cur, val & ~7));
4050 AssertMsg(!(pOhci->ctl & OHCI_CTL_BLE), ("Illegal write! HcControl.BulkListEnabled is set! val=%#010x\n", val));
4051 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4052 pOhci->bulk_cur = val & ~7;
4053 return VINF_SUCCESS;
4054}
4055
4056
4057/**
4058 * Read the HcDoneHead register.
4059 */
4060static int HcDoneHead_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4061{
4062 Log2(("HcDoneHead_r() -> 0x%#08x\n", pOhci->done));
4063 *pu32Value = pOhci->done;
4064 return VINF_SUCCESS;
4065}
4066
4067/**
4068 * Write to the HcDoneHead register.
4069 */
4070static int HcDoneHead_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4071{
4072 Log2(("HcDoneHead_w(0x%#08x) - denied!!!\n", val));
4073 AssertMsgFailed(("Illegal operation!!! val=%#010x\n", val));
4074 return VINF_SUCCESS;
4075}
4076
4077
4078/**
4079 * Read the HcFmInterval (Fm=Frame) register.
4080 */
4081static int HcFmInterval_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4082{
4083 uint32_t val = (pOhci->fit << 31) | (pOhci->fsmps << 16) | (pOhci->fi);
4084 Log2(("HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
4085 val, val & 0x3fff, (val >> 16) & 0x7fff, val >> 31));
4086 *pu32Value = val;
4087 return VINF_SUCCESS;
4088}
4089
4090/**
4091 * Write to the HcFmInterval (Fm = Frame) register.
4092 */
4093static int HcFmInterval_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4094{
4095 /* log */
4096 uint32_t chg = val ^ ((pOhci->fit << 31) | (pOhci->fsmps << 16) | pOhci->fi); NOREF(chg);
4097 Log2(("HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
4098 val,
4099 chg & 0x00003fff ? "*" : "", val & 0x3fff,
4100 chg & 0x7fff0000 ? "*" : "", (val >> 16) & 0x7fff,
4101 chg >> 31 ? "*" : "", (val >> 31) & 1));
4102 if ( pOhci->fi != (val & OHCI_FMI_FI) )
4103 {
4104 Log(("ohci: FrameInterval: %#010x -> %#010x\n", pOhci->fi, val & OHCI_FMI_FI));
4105 AssertMsg(pOhci->fit != ((val >> OHCI_FMI_FIT_SHIFT) & 1), ("HCD did't toggle the FIT bit!!!\n"));
4106 }
4107
4108 /* update */
4109 pOhci->fi = val & OHCI_FMI_FI;
4110 pOhci->fit = (val & OHCI_FMI_FIT) >> OHCI_FMI_FIT_SHIFT;
4111 pOhci->fsmps = (val & OHCI_FMI_FSMPS) >> OHCI_FMI_FSMPS_SHIFT;
4112 return VINF_SUCCESS;
4113}
4114
4115/**
4116 * Read the HcFmRemaining (Fm = Frame) register.
4117 */
4118static int HcFmRemaining_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4119{
4120 uint32_t Value = pOhci->frt << 31;
4121 if ((pOhci->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL)
4122 {
4123 /*
4124 * Being in USB operational state guarantees SofTime was set already.
4125 */
4126 uint64_t tks = TMTimerGet(pOhci->CTX_SUFF(pEndOfFrameTimer)) - pOhci->SofTime;
4127 if (tks < pOhci->cTicksPerFrame) /* avoid muldiv if possible */
4128 {
4129 uint16_t fr;
4130 tks = ASMMultU64ByU32DivByU32(1, tks, pOhci->cTicksPerUsbTick);
4131 fr = (uint16_t)(pOhci->fi - tks);
4132 Value |= fr;
4133 }
4134 }
4135
4136 Log2(("HcFmRemaining_r() -> %#010x - FR=%d FRT=%d\n", Value, Value & 0x3fff, Value >> 31));
4137 *pu32Value = Value;
4138 return VINF_SUCCESS;
4139}
4140
4141/**
4142 * Write to the HcFmRemaining (Fm = Frame) register.
4143 */
4144static int HcFmRemaining_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4145{
4146 Log2(("HcFmRemaining_w(%#010x) - denied\n", val));
4147 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
4148 return VINF_SUCCESS;
4149}
4150
4151/**
4152 * Read the HcFmNumber (Fm = Frame) register.
4153 */
4154static int HcFmNumber_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4155{
4156 uint32_t val = (uint16_t)pOhci->HcFmNumber;
4157 Log2(("HcFmNumber_r() -> %#010x - FN=%#x(%d) (32-bit=%#x(%d))\n", val, val, val, pOhci->HcFmNumber, pOhci->HcFmNumber));
4158 *pu32Value = val;
4159 return VINF_SUCCESS;
4160}
4161
4162/**
4163 * Write to the HcFmNumber (Fm = Frame) register.
4164 */
4165static int HcFmNumber_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4166{
4167 Log2(("HcFmNumber_w(%#010x) - denied\n", val));
4168 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
4169 return VINF_SUCCESS;
4170}
4171
4172/**
4173 * Read the HcPeriodicStart register.
4174 * The register determins when in a frame to switch from control&bulk to periodic lists.
4175 */
4176static int HcPeriodicStart_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4177{
4178 Log2(("HcPeriodicStart_r() -> %#010x - PS=%d\n", pOhci->pstart, pOhci->pstart & 0x3fff));
4179 *pu32Value = pOhci->pstart;
4180 return VINF_SUCCESS;
4181}
4182
4183/**
4184 * Write to the HcPeriodicStart register.
4185 * The register determins when in a frame to switch from control&bulk to periodic lists.
4186 */
4187static int HcPeriodicStart_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4188{
4189 Log2(("HcPeriodicStart_w(%#010x) => PS=%d\n", val, val & 0x3fff));
4190 if (val & ~0x3fff)
4191 Log2(("Unknown bits %#x are set!!!\n", val & ~0x3fff));
4192 pOhci->pstart = val; /** @todo r=bird: should we support setting the other bits? */
4193 return VINF_SUCCESS;
4194}
4195
4196/**
4197 * Read the HcLSThreshold register.
4198 */
4199static int HcLSThreshold_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4200{
4201 Log2(("HcLSThreshold_r() -> %#010x\n", OHCI_LS_THRESH));
4202 *pu32Value = OHCI_LS_THRESH;
4203 return VINF_SUCCESS;
4204}
4205
4206/**
4207 * Write to the HcLSThreshold register.
4208 *
4209 * Docs are inconsistent here:
4210 *
4211 * "Neither the Host Controller nor the Host Controller Driver are allowed to change this value."
4212 *
4213 * "This value is calculated by HCD with the consideration of transmission and setup overhead."
4214 *
4215 * The register is marked "R/W" the HCD column.
4216 *
4217 */
4218static int HcLSThreshold_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4219{
4220 Log2(("HcLSThreshold_w(%#010x) => LST=0x%03x(%d)\n", val, val & 0x0fff, val & 0x0fff));
4221 AssertMsg(val == OHCI_LS_THRESH,
4222 ("HCD tried to write bad LS threshold: 0x%x (see function header)\n", val));
4223 return VINF_SUCCESS;
4224}
4225
4226/**
4227 * Read the HcRhDescriptorA register.
4228 */
4229static int HcRhDescriptorA_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4230{
4231 uint32_t val = pOhci->RootHub.desc_a;
4232#if 0 /* annoying */
4233 Log2(("HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
4234 val, val & 0xff, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1,
4235 (val >> 12) & 1, (val >> 24) & 0xff));
4236#endif
4237 *pu32Value = val;
4238 return VINF_SUCCESS;
4239}
4240
4241/**
4242 * Write to the HcRhDescriptorA register.
4243 */
4244static int HcRhDescriptorA_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4245{
4246 uint32_t chg = val ^ pOhci->RootHub.desc_a; NOREF(chg);
4247 Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
4248 val,
4249 chg & 0xff ?"!!!": "", OHCI_NDP,
4250 (chg >> 8) & 1 ? "*" : "", (val >> 8) & 1,
4251 (chg >> 9) & 1 ? "*" : "", (val >> 9) & 1,
4252 (chg >> 10) & 1 ?"!!!": "", 0,
4253 (chg >> 11) & 1 ? "*" : "", (val >> 11) & 1,
4254 (chg >> 12) & 1 ? "*" : "", (val >> 12) & 1,
4255 (chg >> 24)&0xff? "*" : "", (val >> 24) & 0xff,
4256 val & OHCI_RHA_NPS ? "No" : "",
4257 val & OHCI_RHA_PSM ? "Port" : "Global"));
4258 if (val & ~0xff001fff)
4259 Log2(("Unknown bits %#x are set!!!\n", val & ~0xff001fff));
4260
4261
4262 if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP)
4263 {
4264 Log(("ohci: %s: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n",
4265 pOhci->PciDev.name, val));
4266 val &= ~(OHCI_RHA_NDP | OHCI_RHA_DT);
4267 val |= OHCI_NDP;
4268 }
4269
4270 pOhci->RootHub.desc_a = val;
4271 return VINF_SUCCESS;
4272}
4273
4274/**
4275 * Read the HcRhDescriptorB register.
4276 */
4277static int HcRhDescriptorB_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4278{
4279 uint32_t val = pOhci->RootHub.desc_b;
4280 Log2(("HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
4281 val, val & 0xffff, val >> 16));
4282 *pu32Value = val;
4283 return VINF_SUCCESS;
4284}
4285
4286/**
4287 * Write to the HcRhDescriptorB register.
4288 */
4289static int HcRhDescriptorB_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4290{
4291 uint32_t chg = pOhci->RootHub.desc_b ^ val; NOREF(chg);
4292 Log2(("HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
4293 val,
4294 chg & 0xffff ? "!!!" : "", val & 0xffff,
4295 chg >> 16 ? "!!!" : "", val >> 16));
4296
4297 if ( pOhci->RootHub.desc_b != val )
4298 Log(("ohci: %s: unsupported write to root decriptor B!!! 0x%.8x -> 0x%.8x\n",
4299 pOhci->PciDev.name,
4300 pOhci->RootHub.desc_b, val));
4301 pOhci->RootHub.desc_b = val;
4302 return VINF_SUCCESS;
4303}
4304
4305/**
4306 * Read the HcRhStatus (Rh = Root Hub) register.
4307 */
4308static int HcRhStatus_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4309{
4310 uint32_t val = pOhci->RootHub.status;
4311 if (val & (OHCI_RHS_LPSC | OHCI_RHS_OCIC))
4312 Log2(("HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
4313 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1));
4314 *pu32Value = val;
4315 return VINF_SUCCESS;
4316}
4317
4318/**
4319 * Write to the HcRhStatus (Rh = Root Hub) register.
4320 */
4321static int HcRhStatus_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4322{
4323#ifdef IN_RING3
4324 /* log */
4325 uint32_t old = pOhci->RootHub.status;
4326 uint32_t chg;
4327 if (val & ~0x80038003)
4328 Log2(("HcRhStatus_w: Unknown bits %#x are set!!!\n", val & ~0x80038003));
4329 if ( (val & OHCI_RHS_LPSC) && (val & OHCI_RHS_LPS) )
4330 Log2(("HcRhStatus_w: Warning both CGP and SGP are set! (Clear/Set Global Power)\n"));
4331 if ( (val & OHCI_RHS_DRWE) && (val & OHCI_RHS_CRWE) )
4332 Log2(("HcRhStatus_w: Warning both CRWE and SRWE are set! (Clear/Set Remote Wakeup Enable)\n"));
4333
4334
4335 /* write 1 to clear OCIC */
4336 if ( val & OHCI_RHS_OCIC )
4337 pOhci->RootHub.status &= ~OHCI_RHS_OCIC;
4338
4339 /* SetGlobalPower */
4340 if ( val & OHCI_RHS_LPSC )
4341 {
4342 int i;
4343 Log2(("ohci: %s: global power up\n", pOhci->PciDev.name));
4344 for (i = 0; i < OHCI_NDP; i++)
4345 rhport_power(&pOhci->RootHub, i, true /* power up */);
4346 }
4347
4348 /* ClearGlobalPower */
4349 if ( val & OHCI_RHS_LPS )
4350 {
4351 int i;
4352 Log2(("ohci: %s: global power down\n", pOhci->PciDev.name));
4353 for (i = 0; i < OHCI_NDP; i++)
4354 rhport_power(&pOhci->RootHub, i, false /* power down */);
4355 }
4356
4357 if ( val & OHCI_RHS_DRWE )
4358 pOhci->RootHub.status |= OHCI_RHS_DRWE;
4359
4360 if ( val & OHCI_RHS_CRWE )
4361 pOhci->RootHub.status &= ~OHCI_RHS_DRWE;
4362
4363 chg = pOhci->RootHub.status ^ old;
4364 Log2(("HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
4365 val,
4366 chg & 1 ? "*" : "", val & 1,
4367 (chg >> 1) & 1 ?"!!!": "", (val >> 1) & 1,
4368 (chg >> 15) & 1 ? "*" : "", (val >> 15) & 1,
4369 (chg >> 16) & 1 ? "*" : "", (val >> 16) & 1,
4370 (chg >> 17) & 1 ? "*" : "", (val >> 17) & 1,
4371 (chg >> 31) & 1 ? "*" : "", (val >> 31) & 1));
4372 return VINF_SUCCESS;
4373#else /* !IN_RING3 */
4374 return VINF_IOM_HC_MMIO_WRITE;
4375#endif /* !IN_RING3 */
4376}
4377
4378/**
4379 * Read the HcRhPortStatus register of a port.
4380 */
4381static int HcRhPortStatus_r(POHCI pOhci, uint32_t iReg, uint32_t *pu32Value)
4382{
4383 const unsigned i = iReg - 21;
4384 uint32_t val = pOhci->RootHub.aPorts[i].fReg | OHCI_PORT_R_POWER_STATUS; /* PortPowerStatus: see todo on power in _w function. */
4385 if (val & OHCI_PORT_R_RESET_STATUS)
4386 {
4387#ifdef IN_RING3
4388 RTThreadYield();
4389#else
4390 Log2(("HcRhPortStatus_r: yield -> VINF_IOM_HC_MMIO_READ\n"));
4391 return VINF_IOM_HC_MMIO_READ;
4392#endif
4393 }
4394 if (val & (OHCI_PORT_R_RESET_STATUS | OHCI_PORT_CSC | OHCI_PORT_PESC | OHCI_PORT_PSSC | OHCI_PORT_OCIC | OHCI_PORT_PRSC))
4395 Log2(("HcRhPortStatus_r(): port %u: -> %#010x - CCS=%d PES=%d PSS=%d POCI=%d RRS=%d PPS=%d LSDA=%d CSC=%d PESC=%d PSSC=%d OCIC=%d PRSC=%d\n",
4396 i, val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
4397 (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1));
4398 *pu32Value = val;
4399 return VINF_SUCCESS;
4400}
4401
4402#ifdef IN_RING3
4403/**
4404 * Completion callback for the vusb_dev_reset() operation.
4405 * @thread EMT.
4406 */
4407static DECLCALLBACK(void) uchi_port_reset_done(PVUSBIDEVICE pDev, int rc, void *pvUser)
4408{
4409 POHCI pOhci = (POHCI)pvUser;
4410
4411 /*
4412 * Find the port in question
4413 */
4414 POHCIHUBPORT pPort = NULL;
4415 unsigned iPort;
4416 for (iPort = 0; iPort < RT_ELEMENTS(pOhci->RootHub.aPorts); iPort++) /* lazy bird */
4417 if (pOhci->RootHub.aPorts[iPort].pDev == pDev)
4418 {
4419 pPort = &pOhci->RootHub.aPorts[iPort];
4420 break;
4421 }
4422 if (!pPort)
4423 {
4424 Assert(pPort); /* sometimes happends because of #1510 */
4425 return;
4426 }
4427
4428 if (RT_SUCCESS(rc))
4429 {
4430 /*
4431 * Successful reset.
4432 */
4433 Log2(("uchi_port_reset_done: Reset completed.\n"));
4434 pPort->fReg &= ~(OHCI_PORT_R_RESET_STATUS | OHCI_PORT_R_SUSPEND_STATUS | OHCI_PORT_R_SUSPEND_STATUS_CHANGE);
4435 pPort->fReg |= OHCI_PORT_R_ENABLE_STATUS | OHCI_PORT_R_RESET_STATUS_CHANGE;
4436 }
4437 else
4438 {
4439 /* desperate measures. */
4440 if ( pPort->pDev
4441 && VUSBIDevGetState(pPort->pDev) == VUSB_DEVICE_STATE_ATTACHED)
4442 {
4443 /*
4444 * Damn, something weird happend during reset. We'll pretend the user did an
4445 * incredible fast reconnect or something. (prolly not gonna work)
4446 */
4447 Log2(("uchi_port_reset_done: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
4448 pPort->fReg = OHCI_PORT_R_CURRENT_CONNECT_STATUS | OHCI_PORT_R_CONNECT_STATUS_CHANGE;
4449 }
4450 else
4451 {
4452 /*
4453 * The device have / will be disconnected.
4454 */
4455 Log2(("uchi_port_reset_done: Disconnected (rc=%Rrc)!!!\n", rc));
4456 pPort->fReg &= ~(OHCI_PORT_R_RESET_STATUS | OHCI_PORT_R_SUSPEND_STATUS | OHCI_PORT_R_SUSPEND_STATUS_CHANGE | OHCI_PORT_R_RESET_STATUS_CHANGE);
4457 pPort->fReg |= OHCI_PORT_R_CONNECT_STATUS_CHANGE;
4458 }
4459 }
4460
4461 /* Raise roothub status change interrupt. */
4462 ohciSetInterrupt(pOhci, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
4463}
4464
4465/**
4466 * Sets a flag in a port status register but only set it if a device is
4467 * connected, if not set ConnectStatusChange flag to force HCD to reevaluate
4468 * connect status.
4469 *
4470 * @returns true if device was connected and the flag was cleared.
4471 */
4472static bool rhport_set_if_connected(POHCIROOTHUB pRh, int iPort, uint32_t fValue)
4473{
4474 /*
4475 * Writing a 0 has no effect
4476 */
4477 if (fValue == 0)
4478 return false;
4479
4480 /*
4481 * If CurrentConnectStatus is cleared we set ConnectStatusChange.
4482 */
4483 if (!(pRh->aPorts[iPort].fReg & OHCI_PORT_R_CURRENT_CONNECT_STATUS))
4484 {
4485 pRh->aPorts[iPort].fReg |= OHCI_PORT_R_CONNECT_STATUS_CHANGE;
4486 ohciSetInterrupt(pRh->pOhci, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
4487 return false;
4488 }
4489
4490 bool fRc = !(pRh->aPorts[iPort].fReg & fValue);
4491
4492 /* set the bit */
4493 pRh->aPorts[iPort].fReg |= fValue;
4494
4495 return fRc;
4496}
4497#endif /* IN_RING3 */
4498
4499/**
4500 * Write to the HcRhPortStatus register of a port.
4501 */
4502static int HcRhPortStatus_w(POHCI pOhci, uint32_t iReg, uint32_t val)
4503{
4504#ifdef IN_RING3
4505 const unsigned i = iReg - 21;
4506 POHCIHUBPORT p = &pOhci->RootHub.aPorts[i];
4507 uint32_t old_state = p->fReg;
4508
4509#ifdef LOG_ENABLED
4510 /*
4511 * Log it.
4512 */
4513 static const char *apszCmdNames[32] =
4514 {
4515 "ClearPortEnable", "SetPortEnable", "SetPortSuspend", "!!!ClearSuspendStatus",
4516 "SetPortReset", "!!!5", "!!!6", "!!!7",
4517 "SetPortPower", "ClearPortPower", "!!!10", "!!!11",
4518 "!!!12", "!!!13", "!!!14", "!!!15",
4519 "ClearCSC", "ClearPESC", "ClearPSSC", "ClearOCIC",
4520 "ClearPRSC", "!!!21", "!!!22", "!!!23",
4521 "!!!24", "!!!25", "!!!26", "!!!27",
4522 "!!!28", "!!!29", "!!!30", "!!!31"
4523 };
4524 Log2(("HcRhPortStatus_w(%#010x): port %u:", val, i));
4525 for (unsigned j = 0; j < RT_ELEMENTS(apszCmdNames); j++)
4526 if (val & (1 << j))
4527 Log2((" %s", apszCmdNames[j]));
4528 Log2(("\n"));
4529#endif
4530
4531 /* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */
4532 if (val & OHCI_PORT_W_CLEAR_CHANGE_MASK)
4533 p->fReg &= ~(val & OHCI_PORT_W_CLEAR_CHANGE_MASK);
4534
4535 if (val & OHCI_PORT_W_CLEAR_ENABLE)
4536 {
4537 p->fReg &= ~OHCI_PORT_R_ENABLE_STATUS;
4538 Log2(("HcRhPortStatus_w(): port %u: DISABLE\n", i));
4539 }
4540
4541 if (rhport_set_if_connected(&pOhci->RootHub, i, val & OHCI_PORT_W_SET_ENABLE))
4542 Log2(("HcRhPortStatus_w(): port %u: ENABLE\n", i));
4543
4544 if (rhport_set_if_connected(&pOhci->RootHub, i, val & OHCI_PORT_W_SET_SUSPEND))
4545 Log2(("HcRhPortStatus_w(): port %u: SUSPEND - not implemented correctly!!!\n", i));
4546
4547 if (val & OHCI_PORT_W_SET_RESET)
4548 {
4549 if (rhport_set_if_connected(&pOhci->RootHub, i, val & OHCI_PORT_W_SET_RESET))
4550 {
4551 PVM pVM = PDMDevHlpGetVM(pOhci->CTX_SUFF(pDevIns));
4552 p->fReg &= ~OHCI_PORT_R_RESET_STATUS_CHANGE;
4553 VUSBIDevReset(p->pDev, false /* don't reset on linux */, uchi_port_reset_done, pOhci, pVM);
4554 }
4555 else if (p->fReg & OHCI_PORT_R_RESET_STATUS)
4556 {
4557 /* the guest is getting impatient. */
4558 Log2(("HcRhPortStatus_w(): port %u: Impatient guest!\n"));
4559 RTThreadYield();
4560 }
4561 }
4562
4563 if (!(pOhci->RootHub.desc_a & OHCI_RHA_NPS))
4564 {
4565 /** @todo To implement per-device power-switching
4566 * we need to check PortPowerControlMask to make
4567 * sure it isn't gang powered
4568 */
4569 if (val & OHCI_PORT_W_CLEAR_POWER)
4570 rhport_power(&pOhci->RootHub, i, false /* power down */);
4571 if (val & OHCI_PORT_W_SET_POWER)
4572 rhport_power(&pOhci->RootHub, i, true /* power up */);
4573 }
4574
4575 /** @todo r=frank: ClearSuspendStatus. Timing? */
4576 if (val & OHCI_PORT_W_CLEAR_SUSPEND_STATUS)
4577 {
4578 rhport_power(&pOhci->RootHub, i, true /* power up */);
4579 pOhci->RootHub.aPorts[i].fReg &= ~OHCI_PORT_R_SUSPEND_STATUS;
4580 pOhci->RootHub.aPorts[i].fReg |= OHCI_PORT_R_SUSPEND_STATUS_CHANGE;
4581 }
4582
4583 if (p->fReg != old_state)
4584 {
4585 uint32_t res = p->fReg;
4586 uint32_t chg = res ^ old_state; NOREF(chg);
4587 Log2(("HcRhPortStatus_w(%#010x): port %u: => %sCCS=%d %sPES=%d %sPSS=%d %sPOCI=%d %sRRS=%d %sPPS=%d %sLSDA=%d %sCSC=%d %sPESC=%d %sPSSC=%d %sOCIC=%d %sPRSC=%d\n",
4588 val, i,
4589 chg & 1 ? "*" : "", res & 1,
4590 (chg >> 1) & 1 ? "*" : "", (res >> 1) & 1,
4591 (chg >> 2) & 1 ? "*" : "", (res >> 2) & 1,
4592 (chg >> 3) & 1 ? "*" : "", (res >> 3) & 1,
4593 (chg >> 4) & 1 ? "*" : "", (res >> 4) & 1,
4594 (chg >> 8) & 1 ? "*" : "", (res >> 8) & 1,
4595 (chg >> 9) & 1 ? "*" : "", (res >> 9) & 1,
4596 (chg >> 16) & 1 ? "*" : "", (res >> 16) & 1,
4597 (chg >> 17) & 1 ? "*" : "", (res >> 17) & 1,
4598 (chg >> 18) & 1 ? "*" : "", (res >> 18) & 1,
4599 (chg >> 19) & 1 ? "*" : "", (res >> 19) & 1,
4600 (chg >> 20) & 1 ? "*" : "", (res >> 20) & 1));
4601 }
4602 return VINF_SUCCESS;
4603#else /* !IN_RING3 */
4604 return VINF_IOM_HC_MMIO_WRITE;
4605#endif /* !IN_RING3 */
4606}
4607
4608/**
4609 * Register descriptor table
4610 */
4611static const OHCIOPREG g_aOpRegs[] =
4612{
4613 {"HcRevision", HcRevision_r, HcRevision_w},
4614 {"HcControl", HcControl_r, HcControl_w},
4615 {"HcCommandStatus", HcCommandStatus_r, HcCommandStatus_w},
4616 {"HcInterruptStatus", HcInterruptStatus_r, HcInterruptStatus_w},
4617 {"HcInterruptEnable", HcInterruptEnable_r, HcInterruptEnable_w},
4618 {"HcInterruptDisable", HcInterruptDisable_r, HcInterruptDisable_w},
4619 {"HcHCCA", HcHCCA_r, HcHCCA_w},
4620 {"HcPeriodCurrentED", HcPeriodCurrentED_r, HcPeriodCurrentED_w},
4621 {"HcControlHeadED", HcControlHeadED_r, HcControlHeadED_w},
4622 {"HcControlCurrentED", HcControlCurrentED_r, HcControlCurrentED_w},
4623 {"HcBulkHeadED", HcBulkHeadED_r, HcBulkHeadED_w},
4624 {"HcBulkCurrentED", HcBulkCurrentED_r, HcBulkCurrentED_w},
4625 {"HcDoneHead", HcDoneHead_r, HcDoneHead_w},
4626 {"HcFmInterval", HcFmInterval_r, HcFmInterval_w},
4627 {"HcFmRemaining", HcFmRemaining_r, HcFmRemaining_w},
4628 {"HcFmNumber", HcFmNumber_r, HcFmNumber_w},
4629 {"HcPeriodicStart", HcPeriodicStart_r, HcPeriodicStart_w},
4630 {"HcLSThreshold", HcLSThreshold_r, HcLSThreshold_w},
4631 {"HcRhDescriptorA", HcRhDescriptorA_r, HcRhDescriptorA_w},
4632 {"HcRhDescriptorB", HcRhDescriptorB_r, HcRhDescriptorB_w},
4633 {"HcRhStatus", HcRhStatus_r, HcRhStatus_w},
4634
4635 /* The number of port status register depends on the definition
4636 * of OHCI_NDP macro
4637 */
4638 {"HcRhPortStatus[0]", HcRhPortStatus_r, HcRhPortStatus_w},
4639 {"HcRhPortStatus[1]", HcRhPortStatus_r, HcRhPortStatus_w},
4640 {"HcRhPortStatus[2]", HcRhPortStatus_r, HcRhPortStatus_w},
4641 {"HcRhPortStatus[3]", HcRhPortStatus_r, HcRhPortStatus_w},
4642 {"HcRhPortStatus[4]", HcRhPortStatus_r, HcRhPortStatus_w},
4643 {"HcRhPortStatus[5]", HcRhPortStatus_r, HcRhPortStatus_w},
4644 {"HcRhPortStatus[6]", HcRhPortStatus_r, HcRhPortStatus_w},
4645 {"HcRhPortStatus[7]", HcRhPortStatus_r, HcRhPortStatus_w},
4646};
4647
4648
4649/**
4650 * Read a MMIO register.
4651 *
4652 * We only accept 32-bit writes that are 32-bit aligned.
4653 *
4654 * @returns VBox status code suitable for scheduling.
4655 * @param pDevIns The device instance.
4656 * @param pvUser A user argument (ignored).
4657 * @param GCPhysAddr The physical address being written to. (This is within our MMIO memory range.)
4658 * @param pv Where to put the data we read.
4659 * @param cb The size of the read.
4660 */
4661PDMBOTHCBDECL(int) ohciRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
4662{
4663 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
4664
4665 /*
4666 * Validate the access.
4667 */
4668 if (cb != sizeof(uint32_t))
4669 {
4670 Log2(("ohciRead: Bad read size!!! GCPhysAddr=%RGp cb=%d\n", GCPhysAddr, cb));
4671 return VINF_IOM_MMIO_UNUSED_FF; /* No idea what really would happen... */
4672 }
4673 if (GCPhysAddr & 0x3)
4674 {
4675 Log2(("ohciRead: Unaligned read!!! GCPhysAddr=%RGp cb=%d\n", GCPhysAddr, cb));
4676 return VINF_IOM_MMIO_UNUSED_FF;
4677 }
4678
4679 /*
4680 * Validate the register and call the read operator.
4681 */
4682 int rc;
4683 const uint32_t iReg = (GCPhysAddr - pOhci->MMIOBase) >> 2;
4684 if (iReg < RT_ELEMENTS(g_aOpRegs))
4685 {
4686 const OHCIOPREG *pReg = &g_aOpRegs[iReg];
4687 rc = pReg->pfnRead(pOhci, iReg, (uint32_t *)pv);
4688 }
4689 else
4690 {
4691 Log(("ohci: Trying to read register %u/%u!!!\n", iReg, RT_ELEMENTS(g_aOpRegs)));
4692 rc = VINF_IOM_MMIO_UNUSED_FF;
4693 }
4694 return rc;
4695}
4696
4697
4698/**
4699 * Write to a MMIO register.
4700 *
4701 * We only accept 32-bit writes that are 32-bit aligned.
4702 *
4703 * @returns VBox status code suitable for scheduling.
4704 * @param pDevIns The device instance.
4705 * @param pvUser A user argument (ignored).
4706 * @param GCPhysAddr The physical address being written to. (This is within our MMIO memory range.)
4707 * @param pv Pointer to the data being written.
4708 * @param cb The size of the data being written.
4709 */
4710PDMBOTHCBDECL(int) ohciWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
4711{
4712 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
4713
4714 /*
4715 * Validate the access.
4716 */
4717 if (cb != sizeof(uint32_t))
4718 {
4719 Log2(("ohciWrite: Bad write size!!! GCPhysAddr=%RGp cb=%d\n", GCPhysAddr, cb));
4720 return VINF_SUCCESS;
4721 }
4722 if (GCPhysAddr & 0x3)
4723 {
4724 Log2(("ohciWrite: Unaligned write!!! GCPhysAddr=%RGp cb=%d\n", GCPhysAddr, cb));
4725 return VINF_SUCCESS;
4726 }
4727
4728 /*
4729 * Validate the register and call the read operator.
4730 */
4731 int rc;
4732 const uint32_t iReg = (GCPhysAddr - pOhci->MMIOBase) >> 2;
4733 if (iReg < RT_ELEMENTS(g_aOpRegs))
4734 {
4735 const OHCIOPREG *pReg = &g_aOpRegs[iReg];
4736 rc = pReg->pfnWrite(pOhci, iReg, *(uint32_t *)pv);
4737 }
4738 else
4739 {
4740 Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, RT_ELEMENTS(g_aOpRegs)));
4741 rc = VINF_SUCCESS;
4742 }
4743 return rc;
4744}
4745
4746#ifdef IN_RING3
4747
4748static DECLCALLBACK(int)
4749ohciR3Map(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
4750{
4751 POHCI pOhci = (POHCI)pPciDev;
4752 int rc = PDMDevHlpMMIORegister(pOhci->CTX_SUFF(pDevIns),
4753 GCPhysAddress,
4754 cb,
4755 NULL,
4756 ohciWrite,
4757 ohciRead,
4758 NULL,
4759 "USB OHCI");
4760 if (RT_FAILURE(rc))
4761 return rc;
4762
4763# if 1 /* this enabled / disabled GC/R0 stuff */
4764 rc = PDMDevHlpMMIORegisterRC(pOhci->CTX_SUFF(pDevIns),
4765 GCPhysAddress,
4766 cb,
4767 0,
4768 "ohciWrite",
4769 "ohciRead",
4770 NULL);
4771 if (RT_FAILURE(rc))
4772 return rc;
4773
4774 rc = PDMDevHlpMMIORegisterR0(pOhci->CTX_SUFF(pDevIns),
4775 GCPhysAddress,
4776 cb,
4777 0,
4778 "ohciWrite",
4779 "ohciRead",
4780 NULL);
4781 if (RT_FAILURE(rc))
4782 return rc;
4783
4784# endif
4785
4786 pOhci->MMIOBase = GCPhysAddress;
4787 return VINF_SUCCESS;
4788}
4789
4790/**
4791 * Prepares for state saving.
4792 * All URBs needs to be canceled.
4793 *
4794 * @returns VBox status code.
4795 * @param pDevIns The device instance.
4796 * @param pSSM The handle to save the state to.
4797 */
4798static DECLCALLBACK(int) ohciR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4799{
4800 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
4801 POHCIROOTHUB pRh = &pOhci->RootHub;
4802 unsigned i;
4803 LogFlow(("ohciR3SavePrep: \n"));
4804
4805 /*
4806 * Detach all proxied devices.
4807 */
4808 /** @todo we a) can't tell which are proxied, and b) this won't work well when continuing after saving! */
4809 for (i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
4810 {
4811 PVUSBIDEVICE pDev = pRh->aPorts[i].pDev;
4812 if (pDev)
4813 {
4814 VUSBIRhDetachDevice(pRh->pIRhConn, pDev);
4815 /*
4816 * Save the device pointers here so we can reattach them afterwards.
4817 * This will work fine even if the save fails since the Done handler is
4818 * called unconditionally if the Prep handler was called.
4819 */
4820 pRh->aPorts[i].pDev = pDev;
4821 }
4822 }
4823
4824 /*
4825 * Kill old load data which might be hanging around.
4826 */
4827 if (pOhci->pLoad)
4828 {
4829 TMR3TimerDestroy(pOhci->pLoad->pTimer);
4830 MMR3HeapFree(pOhci->pLoad);
4831 pOhci->pLoad = NULL;
4832 }
4833 return VINF_SUCCESS;
4834}
4835
4836/**
4837 * Saves the state of the OHCI device.
4838 *
4839 * @returns VBox status code.
4840 * @param pDevIns The device instance.
4841 * @param pSSM The handle to save the state to.
4842 */
4843static DECLCALLBACK(int) ohciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4844{
4845 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
4846 LogFlow(("ohciR3SaveExec: \n"));
4847
4848 int rc = SSMR3PutStructEx(pSSM, pOhci, sizeof(*pOhci), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
4849 if (RT_SUCCESS(rc))
4850 rc = TMR3TimerSave(pOhci->CTX_SUFF(pEndOfFrameTimer), pSSM);
4851 return rc;
4852}
4853
4854
4855/**
4856 * Done state save operation.
4857 *
4858 * @returns VBox load code.
4859 * @param pDevIns Device instance of the device which registered the data unit.
4860 * @param pSSM SSM operation handle.
4861 */
4862static DECLCALLBACK(int) ohciR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4863{
4864 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
4865 POHCIROOTHUB pRh = &pOhci->RootHub;
4866 OHCIROOTHUB Rh;
4867 unsigned i;
4868 LogFlow(("ohciR3SavePrep: \n"));
4869
4870 /*
4871 * NULL the dev pointers.
4872 */
4873 Rh = *pRh;
4874 for (i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
4875 pRh->aPorts[i].pDev = NULL;
4876
4877 /*
4878 * Attach the devices.
4879 */
4880 for (i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
4881 {
4882 PVUSBIDEVICE pDev = Rh.aPorts[i].pDev;
4883 if (pDev)
4884 VUSBIRhAttachDevice(pRh->pIRhConn, pDev);
4885 }
4886
4887 return VINF_SUCCESS;
4888}
4889
4890
4891/**
4892 * Prepare loading the state of the OHCI device.
4893 * This must detach the devices currently attached and save
4894 * the up for reconnect after the state load have been completed
4895 *
4896 * @returns VBox status code.
4897 * @param pDevIns The device instance.
4898 * @param pSSM The handle to the saved state.
4899 * @param u32Version The data unit version number.
4900 */
4901static DECLCALLBACK(int) ohciR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4902{
4903 int rc = VINF_SUCCESS;
4904 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
4905 LogFlow(("ohciR3LoadPrep:\n"));
4906 if (!pOhci->pLoad)
4907 {
4908 POHCIROOTHUB pRh = &pOhci->RootHub;
4909 OHCILOAD Load;
4910 unsigned i;
4911
4912 /*
4913 * Detach all devices which are present in this session. Save them in the load
4914 * structure so we can reattach them after restoring the guest.
4915 */
4916 Load.pTimer = NULL;
4917 Load.cDevs = 0;
4918 for (i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
4919 {
4920 PVUSBIDEVICE pDev = pRh->aPorts[i].pDev;
4921 if (pDev)
4922 {
4923 Load.apDevs[Load.cDevs++] = pDev;
4924 VUSBIRhDetachDevice(pRh->pIRhConn, pDev);
4925 Assert(!pRh->aPorts[i].pDev);
4926 }
4927 }
4928
4929 /*
4930 * Any devices to reattach, if so duplicate the Load struct.
4931 */
4932 if (Load.cDevs)
4933 {
4934 pOhci->pLoad = (POHCILOAD)PDMDevHlpMMHeapAlloc(pDevIns, sizeof(Load));
4935 if (!pOhci->pLoad)
4936 return VERR_NO_MEMORY;
4937 *pOhci->pLoad = Load;
4938 }
4939 }
4940 /* else: we ASSUME no device can be attached or detach in the periode
4941 * between a state load and the pLoad stuff is processed. */
4942 return rc;
4943}
4944
4945
4946/**
4947 * Loads the state of the OHCI device.
4948 *
4949 * @returns VBox status code.
4950 * @param pDevIns The device instance.
4951 * @param pSSM The handle to the saved state.
4952 * @param uVersion The data unit version number.
4953 * @param uPass The data pass.
4954 */
4955static DECLCALLBACK(int) ohciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4956{
4957 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
4958 int rc;
4959 LogFlow(("ohciR3LoadExec:\n"));
4960 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
4961
4962 if (uVersion == OHCI_SAVED_STATE_VERSION)
4963 {
4964 rc = SSMR3GetStructEx(pSSM, pOhci, sizeof(*pOhci), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
4965 if (RT_FAILURE(rc))
4966 return rc;
4967 }
4968 else if (uVersion == OHCI_SAVED_STATE_VERSION_MEM_HELL)
4969 {
4970 static SSMFIELD const s_aOhciFields22[] =
4971 {
4972 SSMFIELD_ENTRY_OLD( PciDev.config, 256), /* DevPCI restores this. */
4973 SSMFIELD_ENTRY_OLD( PciDev.Int, 224),
4974 SSMFIELD_ENTRY_OLD( PciDev.devfn, 4),
4975 SSMFIELD_ENTRY_OLD( PciDev.Alignment0, 4),
4976 SSMFIELD_ENTRY_OLD_HCPTR( PciDev.name),
4977 SSMFIELD_ENTRY_OLD_HCPTR( PciDev.pDevIns),
4978 SSMFIELD_ENTRY_OLD_HCPTR( pDevInsR3),
4979 SSMFIELD_ENTRY_OLD_HCPTR( pEndOfFrameTimerR3),
4980 SSMFIELD_ENTRY_OLD_HCPTR( pDevInsR0),
4981 SSMFIELD_ENTRY_OLD_HCPTR( pEndOfFrameTimerR0),
4982 SSMFIELD_ENTRY_OLD_RCPTR( pDevInsRC),
4983 SSMFIELD_ENTRY_OLD_RCPTR( pEndOfFrameTimerRC),
4984 SSMFIELD_ENTRY( OHCI, SofTime),
4985 SSMFIELD_ENTRY_CUSTOM( dpic+fno, RT_OFFSETOF(OHCI, SofTime) + RT_SIZEOFMEMB(OHCI, SofTime), 4),
4986 SSMFIELD_ENTRY_OLD( MMIOBase, 4), /* DevPCI implicitly restores this. */
4987 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pIBase),
4988 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pIRhConn),
4989 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pIDev),
4990 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IBase.pfnQueryInterface),
4991 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnGetAvailablePorts),
4992 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnGetUSBVersions),
4993 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnAttach),
4994 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnDetach),
4995 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnReset),
4996 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnXferCompletion),
4997 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnXferError),
4998 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.Alignment),
4999 SSMFIELD_ENTRY_OLD( RootHub.Led, 16), /* No device restored. */
5000 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.ILeds.pfnQueryStatusLed),
5001 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pLedsConnector),
5002 SSMFIELD_ENTRY( OHCI, RootHub.status),
5003 SSMFIELD_ENTRY( OHCI, RootHub.desc_a),
5004 SSMFIELD_ENTRY( OHCI, RootHub.desc_b),
5005 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.Alignment0, 4),
5006 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[0].fReg),
5007 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[0].Alignment0, 4),
5008 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[0].pDev),
5009 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[1].fReg),
5010 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[1].Alignment0, 4),
5011 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[1].pDev),
5012 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[2].fReg),
5013 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[2].Alignment0, 4),
5014 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[2].pDev),
5015 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[3].fReg),
5016 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[3].Alignment0, 4),
5017 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[3].pDev),
5018 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[4].fReg),
5019 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[4].Alignment0, 4),
5020 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[4].pDev),
5021 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[5].fReg),
5022 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[5].Alignment0, 4),
5023 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[5].pDev),
5024 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[6].fReg),
5025 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[6].Alignment0, 4),
5026 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[6].pDev),
5027 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[7].fReg),
5028 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[7].Alignment0, 4),
5029 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[7].pDev),
5030 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pOhci),
5031 SSMFIELD_ENTRY( OHCI, ctl),
5032 SSMFIELD_ENTRY( OHCI, status),
5033 SSMFIELD_ENTRY( OHCI, intr_status),
5034 SSMFIELD_ENTRY( OHCI, intr),
5035 SSMFIELD_ENTRY( OHCI, hcca),
5036 SSMFIELD_ENTRY( OHCI, per_cur),
5037 SSMFIELD_ENTRY( OHCI, ctrl_cur),
5038 SSMFIELD_ENTRY( OHCI, ctrl_head),
5039 SSMFIELD_ENTRY( OHCI, bulk_cur),
5040 SSMFIELD_ENTRY( OHCI, bulk_head),
5041 SSMFIELD_ENTRY( OHCI, done),
5042 SSMFIELD_ENTRY_CUSTOM( fsmps+fit+fi+frt, RT_OFFSETOF(OHCI, done) + RT_SIZEOFMEMB(OHCI, done), 4),
5043 SSMFIELD_ENTRY( OHCI, HcFmNumber),
5044 SSMFIELD_ENTRY( OHCI, pstart),
5045 SSMFIELD_ENTRY_OLD( cTicksPerFrame, 8), /* done by the constructor */
5046 SSMFIELD_ENTRY_OLD( cTicksPerUsbTick, 8), /* ditto */
5047 SSMFIELD_ENTRY_OLD( cInFlight, 4), /* no in-flight stuff when saving. */
5048 SSMFIELD_ENTRY_OLD( Alignment1, 4),
5049 SSMFIELD_ENTRY_OLD( aInFlight, 257 * 8),
5050 SSMFIELD_ENTRY_OLD_PAD_HC64( aInFlight, 257 * 8),
5051 SSMFIELD_ENTRY_OLD( cInDoneQueue, 4), /* strict builds only, so don't bother. */
5052 SSMFIELD_ENTRY_OLD( aInDoneQueue, 4*64),
5053 SSMFIELD_ENTRY_OLD( u32FmDoneQueueTail, 4), /* logging only */
5054 SSMFIELD_ENTRY_OLD_PAD_HC32( Alignment2, 4),
5055 SSMFIELD_ENTRY_OLD_HCPTR( pLoad),
5056 SSMFIELD_ENTRY_OLD( StatCanceledIsocUrbs, 8),
5057 SSMFIELD_ENTRY_OLD( StatCanceledGenUrbs, 8),
5058 SSMFIELD_ENTRY_OLD( StatDroppedUrbs, 8),
5059 SSMFIELD_ENTRY_OLD( StatTimer, 32),
5060 SSMFIELD_ENTRY_TERM()
5061 };
5062
5063 /* deserialize the struct */
5064 rc = SSMR3GetStructEx(pSSM, pOhci, sizeof(*pOhci), SSMSTRUCT_FLAGS_NO_MARKERS /*fFlags*/, &s_aOhciFields22[0], NULL);
5065 if (RT_FAILURE(rc))
5066 return rc;
5067
5068 /* check delimiter */
5069 uint32_t u32;
5070 rc = SSMR3GetU32(pSSM, &u32);
5071 if (RT_FAILURE(rc))
5072 return rc;
5073 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
5074 }
5075 else
5076 AssertMsgFailedReturn(("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
5077
5078 /*
5079 * Finally restore the timer.
5080 */
5081 return TMR3TimerLoad(pOhci->pEndOfFrameTimerR3, pSSM);
5082}
5083
5084
5085/**
5086 * Done state load operation.
5087 *
5088 * @returns VBox load code.
5089 * @param pDevIns Device instance of the device which registered the data unit.
5090 * @param pSSM SSM operation handle.
5091 */
5092static DECLCALLBACK(int) ohciR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5093{
5094 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
5095 LogFlow(("ohciR3LoadDone:\n"));
5096
5097 /*
5098 * Start a timer if we've got devices to reattach
5099 */
5100 if (pOhci->pLoad)
5101 {
5102 int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ohciR3LoadReattachDevices, pOhci,
5103 TMTIMER_FLAGS_NO_CRIT_SECT, "OHCI reattach devices on load",
5104 &pOhci->pLoad->pTimer);
5105 if (RT_SUCCESS(rc))
5106 rc = TMTimerSetMillies(pOhci->pLoad->pTimer, 250);
5107 return rc;
5108 }
5109
5110 return VINF_SUCCESS;
5111}
5112
5113
5114/**
5115 * Reattaches devices after a saved state load.
5116 */
5117static DECLCALLBACK(void) ohciR3LoadReattachDevices(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5118{
5119 POHCI pOhci = (POHCI)pvUser;
5120 POHCILOAD pLoad = pOhci->pLoad;
5121 POHCIROOTHUB pRh = &pOhci->RootHub;
5122 LogFlow(("ohciR3LoadReattachDevices:\n"));
5123
5124 /*
5125 * Reattach devices.
5126 */
5127 for (unsigned i = 0; i < pLoad->cDevs; i++)
5128 VUSBIRhAttachDevice(pRh->pIRhConn, pLoad->apDevs[i]);
5129
5130 /*
5131 * Cleanup.
5132 */
5133 TMR3TimerDestroy(pTimer);
5134 MMR3HeapFree(pLoad);
5135 pOhci->pLoad = NULL;
5136}
5137
5138
5139/**
5140 * Reset notification.
5141 *
5142 * @returns VBox status.
5143 * @param pDevIns The device instance data.
5144 */
5145static DECLCALLBACK(void) ohciR3Reset(PPDMDEVINS pDevIns)
5146{
5147 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
5148 LogFlow(("ohciR3Reset:\n"));
5149
5150 /*
5151 * There is no distinction between cold boot, warm reboot and software reboots,
5152 * all of these are treated as cold boots. We are also doing the initialization
5153 * job of a BIOS or SMM driver.
5154 *
5155 * Important: Don't confuse UsbReset with hardware reset. Hardware reset is
5156 * just one way of getting into the UsbReset state.
5157 */
5158 ohciBusStop(pOhci);
5159 ohciDoReset(pOhci, OHCI_USB_RESET, true /* reset devices */);
5160}
5161
5162
5163/**
5164 * Info handler, device version. Dumps OHCI control registers.
5165 *
5166 * @param pDevIns Device instance which registered the info.
5167 * @param pHlp Callback functions for doing output.
5168 * @param pszArgs Argument string. Optional and specific to the handler.
5169 */
5170static DECLCALLBACK(void) ohciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
5171{
5172 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
5173 uint32_t val, ctl, status;
5174
5175 /* Control register */
5176 ctl = pOhci->ctl;
5177 pHlp->pfnPrintf(pHlp, "HcControl: %08x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
5178 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
5179 (ctl >> 9) & 1, (ctl >> 10) & 1);
5180
5181 /* Command status register */
5182 status = pOhci->status;
5183 pHlp->pfnPrintf(pHlp, "HcCommandStatus: %08x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
5184 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3);
5185
5186 /* Interrupt status register */
5187 val = pOhci->intr_status;
5188 pHlp->pfnPrintf(pHlp, "HcInterruptStatus: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
5189 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5190 (val >> 6) & 1, (val >> 30) & 1);
5191
5192 /* Interrupt enable register */
5193 val = pOhci->intr;
5194 pHlp->pfnPrintf(pHlp, "HcInterruptEnable: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
5195 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5196 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1);
5197
5198 /* HCCA address register */
5199 pHlp->pfnPrintf(pHlp, "HcHCCA: %08x\n", pOhci->hcca);
5200
5201 /* Current periodic ED register */
5202 pHlp->pfnPrintf(pHlp, "HcPeriodCurrentED: %08x\n", pOhci->per_cur);
5203
5204 /* Control ED registers */
5205 pHlp->pfnPrintf(pHlp, "HcControlHeadED: %08x\n", pOhci->ctrl_head);
5206 pHlp->pfnPrintf(pHlp, "HcControlCurrentED: %08x\n", pOhci->ctrl_cur);
5207
5208 /* Bulk ED registers */
5209 pHlp->pfnPrintf(pHlp, "HcBulkHeadED: %08x\n", pOhci->bulk_head);
5210 pHlp->pfnPrintf(pHlp, "HcBulkCurrentED: %08x\n", pOhci->bulk_cur);
5211
5212 /* Done head register */
5213 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pOhci->done);
5214
5215 pHlp->pfnPrintf(pHlp, "\n");
5216}
5217
5218
5219/**
5220 * Relocate device instance data.
5221 *
5222 * @returns VBox status.
5223 * @param pDevIns The device instance data.
5224 * @param offDelta The relocation delta.
5225 */
5226static DECLCALLBACK(void) ohciR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5227{
5228 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
5229 pOhci->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5230 pOhci->pEndOfFrameTimerRC = TMTimerRCPtr(pOhci->pEndOfFrameTimerR3);
5231}
5232
5233
5234/**
5235 * Destruct a device instance.
5236 *
5237 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5238 * resources can be freed correctly.
5239 *
5240 * @returns VBox status.
5241 * @param pDevIns The device instance data.
5242 */
5243static DECLCALLBACK(int) ohciR3Destruct(PPDMDEVINS pDevIns)
5244{
5245 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5246
5247 /*
5248 * Tear down the per endpoint in-flight tracking...
5249 */
5250
5251 return VINF_SUCCESS;
5252}
5253
5254
5255/**
5256 * @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor}
5257 */
5258static DECLCALLBACK(int) ohciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5259{
5260 POHCI pOhci = PDMINS_2_DATA(pDevIns, POHCI);
5261 int rc;
5262 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5263
5264 /*
5265 * Read configuration. No configuration keys are currently supported.
5266 */
5267 if (!CFGMR3AreValuesValid(pCfg, "\0"))
5268 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5269 N_("Configuration error: Unknown config key"));
5270
5271 /*
5272 * Init instance data.
5273 */
5274 pOhci->pDevInsR3 = pDevIns;
5275 pOhci->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5276 pOhci->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5277 const uint16_t vid = 0x106b;
5278 pOhci->PciDev.config[0x00] = vid & 0xff;
5279 pOhci->PciDev.config[0x01] = (vid >> 8) & 0xff;
5280 const uint16_t did = 0x003f;
5281 pOhci->PciDev.config[0x02] = did & 0xff;
5282 pOhci->PciDev.config[0x03] = (did >> 8) & 0xff;
5283 pOhci->PciDev.config[0x09] = 0x10; /* OHCI */
5284 pOhci->PciDev.config[0x0a] = 0x3;
5285 pOhci->PciDev.config[0x0b] = 0xc;
5286 pOhci->PciDev.config[0x3d] = 0x01;
5287 pOhci->RootHub.pOhci = pOhci;
5288 pOhci->RootHub.IBase.pfnQueryInterface = ohciRhQueryInterface;
5289 pOhci->RootHub.IRhPort.pfnGetAvailablePorts = ohciRhGetAvailablePorts;
5290 pOhci->RootHub.IRhPort.pfnGetUSBVersions = ohciRhGetUSBVersions;
5291 pOhci->RootHub.IRhPort.pfnAttach = ohciRhAttach;
5292 pOhci->RootHub.IRhPort.pfnDetach = ohciRhDetach;
5293 pOhci->RootHub.IRhPort.pfnReset = ohciRhReset;
5294 pOhci->RootHub.IRhPort.pfnXferCompletion = ohciRhXferCompletion;
5295 pOhci->RootHub.IRhPort.pfnXferError = ohciRhXferError;
5296
5297 /* USB LED */
5298 pOhci->RootHub.Led.u32Magic = PDMLED_MAGIC;
5299 pOhci->RootHub.ILeds.pfnQueryStatusLed = ohciRhQueryStatusLed;
5300
5301 /*
5302 * Register PCI device and I/O region.
5303 */
5304 rc = PDMDevHlpPCIRegister(pDevIns, &pOhci->PciDev);
5305 if (RT_FAILURE(rc))
5306 return rc;
5307
5308 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 4096, PCI_ADDRESS_SPACE_MEM, ohciR3Map);
5309 if (RT_FAILURE(rc))
5310 return rc;
5311
5312 /*
5313 * Create the end-of-frame timer.
5314 */
5315 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ohciFrameBoundaryTimer, pOhci,
5316 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "USB Frame Timer",
5317 &pOhci->pEndOfFrameTimerR3);
5318 if (RT_FAILURE(rc))
5319 return rc;
5320 pOhci->pEndOfFrameTimerR0 = TMTimerR0Ptr(pOhci->pEndOfFrameTimerR3);
5321 pOhci->pEndOfFrameTimerRC = TMTimerRCPtr(pOhci->pEndOfFrameTimerR3);
5322
5323 /*
5324 * Register the saved state data unit.
5325 */
5326 rc = PDMDevHlpSSMRegisterEx(pDevIns, OHCI_SAVED_STATE_VERSION, sizeof(*pOhci), NULL,
5327 NULL, NULL, NULL,
5328 ohciR3SavePrep, ohciR3SaveExec, ohciR3SaveDone,
5329 ohciR3LoadPrep, ohciR3LoadExec, ohciR3LoadDone);
5330 if (RT_FAILURE(rc))
5331 return rc;
5332
5333 /*
5334 * Attach to the VBox USB RootHub Driver on LUN #0.
5335 */
5336 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pOhci->RootHub.IBase, &pOhci->RootHub.pIBase, "RootHub");
5337 if (RT_FAILURE(rc))
5338 {
5339 AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
5340 return rc;
5341 }
5342 pOhci->RootHub.pIRhConn = PDMIBASE_QUERY_INTERFACE(pOhci->RootHub.pIBase, VUSBIROOTHUBCONNECTOR);
5343 AssertMsgReturn(pOhci->RootHub.pIRhConn,
5344 ("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
5345 VERR_PDM_MISSING_INTERFACE);
5346 pOhci->RootHub.pIDev = PDMIBASE_QUERY_INTERFACE(pOhci->RootHub.pIBase, VUSBIDEVICE);
5347 AssertMsgReturn(pOhci->RootHub.pIDev,
5348 ("Configuration error: The driver doesn't provide the VUSBIDEVICE interface!\n"),
5349 VERR_PDM_MISSING_INTERFACE);
5350
5351 /*
5352 * Attach status driver (optional).
5353 */
5354 PPDMIBASE pBase;
5355 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pOhci->RootHub.IBase, &pBase, "Status Port");
5356 if (RT_SUCCESS(rc))
5357 pOhci->RootHub.pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5358 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
5359 {
5360 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5361 return rc;
5362 }
5363
5364 /*
5365 * Calculate the timer intervals.
5366 * This assumes that the VM timer doesn't change frequency during the run.
5367 */
5368 pOhci->u64TimerHz = TMTimerGetFreq(pOhci->CTX_SUFF(pEndOfFrameTimer));
5369 ohciCalcTimerIntervals(pOhci, OHCI_DEFAULT_TIMER_FREQ);
5370 Log(("ohci: cTicksPerFrame=%RU64 cTicksPerUsbTick=%RU64\n",
5371 pOhci->cTicksPerFrame, pOhci->cTicksPerUsbTick));
5372
5373 /*
5374 * Do a hardware reset.
5375 */
5376 ohciDoReset(pOhci, OHCI_USB_RESET, false /* don't reset devices */);
5377
5378#ifdef VBOX_WITH_STATISTICS
5379 /*
5380 * Register statistics.
5381 */
5382 PDMDevHlpSTAMRegister(pDevIns, &pOhci->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
5383 PDMDevHlpSTAMRegister(pDevIns, &pOhci->StatCanceledGenUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
5384 PDMDevHlpSTAMRegister(pDevIns, &pOhci->StatDroppedUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
5385 PDMDevHlpSTAMRegister(pDevIns, &pOhci->StatTimer, STAMTYPE_PROFILE, "/Devices/OHCI/Timer", STAMUNIT_TICKS_PER_CALL, "Profiling ohciFrameBoundaryTimer.");
5386#endif
5387
5388 /*
5389 * Register debugger info callbacks.
5390 */
5391 PDMDevHlpDBGFInfoRegister(pDevIns, "ohci", "OHCI control registers.", ohciR3InfoRegs);
5392
5393#if 0/*def DEBUG_bird*/
5394// g_fLogInterruptEPs = true;
5395 g_fLogControlEPs = true;
5396 g_fLogBulkEPs = true;
5397#endif
5398
5399 return VINF_SUCCESS;
5400}
5401
5402
5403const PDMDEVREG g_DeviceOHCI =
5404{
5405 /* u32version */
5406 PDM_DEVREG_VERSION,
5407 /* szName */
5408 "usb-ohci",
5409 /* szRCMod */
5410 "VBoxDDGC.gc",
5411 /* szR0Mod */
5412 "VBoxDDR0.r0",
5413 /* pszDescription */
5414 "OHCI USB controller.\n",
5415 /* fFlags */
5416 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
5417 /* fClass */
5418 PDM_DEVREG_CLASS_BUS_USB,
5419 /* cMaxInstances */
5420 ~0,
5421 /* cbInstance */
5422 sizeof(OHCI),
5423 /* pfnConstruct */
5424 ohciR3Construct,
5425 /* pfnDestruct */
5426 ohciR3Destruct,
5427 /* pfnRelocate */
5428 ohciR3Relocate,
5429 /* pfnIOCtl */
5430 NULL,
5431 /* pfnPowerOn */
5432 NULL,
5433 /* pfnReset */
5434 ohciR3Reset,
5435 /* pfnSuspend */
5436 NULL,
5437 /* pfnResume */
5438 NULL,
5439 /* pfnAttach */
5440 NULL,
5441 /* pfnDetach */
5442 NULL,
5443 /* pfnQueryInterface */
5444 NULL,
5445 /* pfnInitComplete */
5446 NULL,
5447 /* pfnPowerOff */
5448 NULL,
5449 /* pfnSoftReset */
5450 NULL,
5451 /* u32VersionEnd */
5452 PDM_DEVREG_VERSION
5453};
5454
5455#endif /* IN_RING3 */
5456#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5457
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette