The Pedigree Project  0.1
UsbHub.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "modules/system/usb/UsbHub.h"
21 #include "modules/system/usb/Usb.h"
22 #include "modules/system/usb/UsbDevice.h"
23 #include "modules/system/usb/UsbPnP.h"
24 #include "pedigree/kernel/Log.h"
25 #include "pedigree/kernel/utilities/ExtensibleBitmap.h"
26 #include "pedigree/kernel/utilities/Vector.h"
27 #include "pedigree/kernel/utilities/new"
28 
29 UsbHub::UsbHub()
30 {
32 }
33 
34 UsbHub::UsbHub(Device *p) : Device(p)
35 {
36 }
37 
38 UsbHub::~UsbHub()
39 {
40 }
41 
43 {
44  return UsbController;
45 }
46 
47 bool UsbHub::deviceConnected(uint8_t nPort, UsbSpeed speed)
48 {
49  NOTICE("USB: Adding device on port " << Dec << nPort << Hex);
50 
51  // Find the root hub
52  UsbHub *pRootHub = this;
53  while (pRootHub->getParent()->getType() == Device::UsbController)
54  pRootHub = static_cast<UsbHub *>(pRootHub->getParent());
55 
56  size_t nRetry = 0;
57  uint8_t lastAddress = 0, nAddress = 0;
58 
59  pRootHub->ignoreConnectionChanges(nPort);
60 
61  // Try twice with two different addresses
62  UsbDevice *pDevice = 0;
63  while (nRetry < 2)
64  {
65  // Get first unused address and check it
66  nAddress = pRootHub->m_UsedAddresses.getFirstClear();
67  if (nAddress > 127)
68  {
69  ERROR("USB: HUB: Out of addresses!");
70  return false;
71  }
72 
73  // This address is now used
74  pRootHub->m_UsedAddresses.set(nAddress);
75  if (lastAddress)
76  pRootHub->m_UsedAddresses.clear(lastAddress);
77  NOTICE(
78  "USB: Allocated device on port " << Dec << nPort << Hex
79  << " address " << nAddress);
80 
81  // Create the UsbDevice instance and set us as parent
82  pDevice = new UsbDevice(this, nPort, speed);
83 
84  // Initialise the device - it basically sets the address and gets the
85  // descriptors
86  pDevice->initialise(nAddress);
87 
88  // Check for initialisation failures
89  if (pDevice->getUsbState() != UsbDevice::Configured)
90  {
91  NOTICE(
92  "USB: Device initialisation ended up not giving a configured "
93  "device [retry "
94  << nRetry << " of 2].");
95 
96  // Cleanup descriptors
97  if (pDevice->getUsbState() >= UsbDevice::HasDescriptors)
98  delete pDevice->getDescriptor();
99 
100  delete pDevice;
101 
102  // Reset the port that this device is attached to.
103  NOTICE("USB: Performing a port reset on port " << nPort);
104  if ((!pRootHub->portReset(nPort, true)) && (nRetry < 1))
105  {
106  // Give up completely
107  NOTICE("USB: Port reset failed (port " << nPort << ")");
108  pRootHub->ignoreConnectionChanges(nPort, false);
109  pRootHub->m_UsedAddresses.clear(nAddress);
110  return false;
111  }
112  }
113  else
114  {
115  NOTICE(
116  "USB: Device on port " << Dec << nPort << Hex
117  << " accepted address " << nAddress);
118  break;
119  }
120 
121  lastAddress = nAddress;
122  nRetry++;
123  }
124 
125  pRootHub->ignoreConnectionChanges(nPort, false);
126 
127  if (nRetry == 2)
128  {
129  NOTICE("Device initialisation couldn't configure the device.");
130  return false;
131  }
132 
133  // Get the device descriptor
134  UsbDevice::DeviceDescriptor *pDescriptor = pDevice->getDescriptor();
135 
136  // Iterate all interfaces
137  Vector<UsbDevice::Interface *> interfaceList =
138  pDevice->getConfiguration()->interfaceList;
139  for (size_t i = 0; i < interfaceList.count(); i++)
140  {
141  UsbDevice::Interface *pInterface = interfaceList[i];
142 
143  // Skip alternate settings
144  if (pInterface->nAlternateSetting)
145  continue;
146 
147  // If we're not at the first interface, we have to clone the UsbDevice
148  if (i)
149  pDevice = new UsbDevice(pDevice);
150 
151  // Set the right interface
152  pDevice->useInterface(i);
153 
154  // Add the device as a child
155  UsbDeviceContainer *pContainer = new UsbDeviceContainer(pDevice);
156  addChild(pContainer);
157  pContainer->setParent(this);
158 
159  NOTICE(
160  "USB: Device (address "
161  << nAddress << "): " << pDescriptor->sVendor << " "
162  << pDescriptor->sProduct << ", class " << Dec << pInterface->nClass
163  << ":" << pInterface->nSubclass << ":" << pInterface->nProtocol
164  << Hex);
165 
166  // Send it to the USB PnP manager
167  NOTICE("pnp instance is: " << &UsbPnP::instance() << ".");
168  UsbPnP::instance().probeDevice(pContainer);
169  }
170  return true;
171 }
172 
173 void UsbHub::deviceDisconnected(uint8_t nPort)
174 {
175  uint8_t nAddress = 0;
176  UsbDevice::DeviceDescriptor *pDescriptor = 0;
177 
178  for (size_t i = 0; i < m_Children.count(); i++)
179  {
180  UsbDevice *pDevice =
181  static_cast<UsbDeviceContainer *>(m_Children[i])->getUsbDevice();
182 
183  if (!pDevice)
184  continue;
185 
186  if (pDevice->getPort() != nPort)
187  continue;
188 
189  if (!nAddress)
190  nAddress = pDevice->getAddress();
191  else if (nAddress != pDevice->getAddress())
192  ERROR("USB: HUB: Found devices on the same port with different "
193  "addresses");
194 
195  if (pDevice->getUsbState() >= UsbDevice::HasDescriptors)
196  {
197  if (!pDescriptor)
198  pDescriptor = pDevice->getDescriptor();
199  else if (pDescriptor != pDevice->getDescriptor())
200  ERROR("USB: HUB: Found devices on the same port with different "
201  "device descriptors");
202  }
203 
204  delete pDevice;
205  }
206 
207  if (pDescriptor)
208  delete pDescriptor;
209 
210  if (!nAddress)
211  return;
212 
213  // Find the root hub
214  UsbHub *pRootHub = this;
215  while (pRootHub->getParent()->getType() == Device::UsbController)
216  pRootHub = static_cast<UsbHub *>(pRootHub->getParent());
217 
218  // This address is now free
219  pRootHub->m_UsedAddresses.clear(nAddress);
220 }
221 
222 void UsbHub::syncCallback(uintptr_t pParam, ssize_t nResult)
223 {
224  if (!pParam)
225  return;
226  SyncParam *pSyncParam = reinterpret_cast<SyncParam *>(pParam);
227  pSyncParam->nResult = nResult;
228  pSyncParam->semaphore.release();
229 
230  if (pSyncParam->timedOut)
231  delete pSyncParam;
232 }
233 
234 ssize_t UsbHub::doSync(uintptr_t nTransaction, uint32_t timeout)
235 {
236  // Create a structure to hold the semaphore and the result
237  SyncParam *pSyncParam = new SyncParam();
238 
239  // Send the async request
240  doAsync(
241  nTransaction, syncCallback, reinterpret_cast<uintptr_t>(pSyncParam));
242  // Wait for the semaphore to release
247  bool bTimeout = !pSyncParam->semaphore.acquire(
248  1, timeout / 1000, (timeout % 1000) * 1000);
249  // Return the result
250  if (bTimeout)
251  {
252  WARNING("USB: a transaction timed out.");
253  pSyncParam->timedOut = true;
254  return -TransactionError;
255  }
256  else
257  {
258  ssize_t ret = pSyncParam->nResult;
259  delete pSyncParam;
260  return ret;
261  }
262 }
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)=0
Gets a UsbDevice from a given vendor:product pair.
size_t count() const
Definition: Vector.h:264
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition: Semaphore.h:62
ConfigDescriptor * getConfiguration()
Returns the configuration in use.
Definition: UsbDevice.h:224
void deviceDisconnected(uint8_t nPort)
Called when a device is disconnected from a port on the hub.
Definition: UsbHub.cc:173
ExtensibleBitmap m_UsedAddresses
Definition: UsbHub.h:112
Definition: Device.h:43
void initialise(uint8_t nAddress)
Initialises the device at the given address.
Definition: UsbDevice.cc:155
#define WARNING(text)
Definition: Log.h:78
bool probeDevice(Device *pDeviceBase)
Tries to find a suitable driver for the given USB device.
Definition: UsbPnP.cc:36
virtual Type getType()
Definition: UsbHub.cc:42
Structure used synchronous transactions.
Definition: UsbHub.h:95
void release(size_t n=1)
Definition: Semaphore.cc:239
#define NOTICE(text)
Definition: Log.h:74
Same as Controller, but exposes USB devices.
Definition: Device.h:66
void addChild(Device *pDevice)
Definition: Device.cc:127
Definition: Log.h:136
Definition: UsbHub.h:30
uint8_t getAddress()
Returns the current address of the device.
Definition: UsbDevice.h:194
bool deviceConnected(uint8_t nPort, UsbSpeed speed)
Called when a device is connected to a port on the hub.
Definition: UsbHub.cc:47
void setParent(Device *p)
Definition: Device.h:154
void set(size_t n)
DeviceDescriptor * getDescriptor()
Returns the device descriptor of the device.
Definition: UsbDevice.h:218
static void syncCallback(uintptr_t pParam, ssize_t ret)
Callback used by synchronous transactions.
Definition: UsbHub.cc:222
ssize_t doSync(uintptr_t nTransaction, uint32_t timeout=5000)
Definition: UsbHub.cc:234
static UsbPnP & instance()
Singleton design.
Definition: UsbPnP.h:51
virtual Type getType()
Definition: Device.h:163
#define ERROR(text)
Definition: Log.h:82
Definition: Log.h:138
Type
Definition: Device.h:50
uint8_t getPort()
Returns the number of the port on which the device is connected.
Definition: UsbDevice.h:200
UsbState getUsbState()
Returns the current state of the device.
Definition: UsbDevice.h:212
Device * getParent() const
Definition: Device.h:149
void clear(size_t n)
void ignoreConnectionChanges(uint8_t nPort, bool bIgnore=true)
Definition: UsbHub.h:85
void useInterface(uint8_t nInterface)
Switches to the given interface.
Definition: UsbDevice.cc:536
Vector< Device * > m_Children
Definition: Device.h:364