Pulse Oximeter (NONIN) communicates with mbed via Bluetooth dongle and sends Heart Rate and Oxygen Saturation via GPRS module
Dependencies: C12832 GPS GSM mbed
Fork of myBlueUSB_localfix by
myUSBHost/USBHost.cpp@3:55a622e3dbb5, 2015-04-14 (annotated)
- Committer:
- samialshorman
- Date:
- Tue Apr 14 21:48:07 2015 +0000
- Revision:
- 3:55a622e3dbb5
- Parent:
- 0:003889bc474f
Nonin (Pulse Oximeter) connected to mbed lpc 1768 by Bluetooth dongle and sends SMS including Heart Rate and Oxygen saturation by GPRS module
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
nobukuma | 0:003889bc474f | 1 | |
nobukuma | 0:003889bc474f | 2 | /* |
nobukuma | 0:003889bc474f | 3 | Copyright (c) 2010 Peter Barrett |
nobukuma | 0:003889bc474f | 4 | |
nobukuma | 0:003889bc474f | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy |
nobukuma | 0:003889bc474f | 6 | of this software and associated documentation files (the "Software"), to deal |
nobukuma | 0:003889bc474f | 7 | in the Software without restriction, including without limitation the rights |
nobukuma | 0:003889bc474f | 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
nobukuma | 0:003889bc474f | 9 | copies of the Software, and to permit persons to whom the Software is |
nobukuma | 0:003889bc474f | 10 | furnished to do so, subject to the following conditions: |
nobukuma | 0:003889bc474f | 11 | |
nobukuma | 0:003889bc474f | 12 | The above copyright notice and this permission notice shall be included in |
nobukuma | 0:003889bc474f | 13 | all copies or substantial portions of the Software. |
nobukuma | 0:003889bc474f | 14 | |
nobukuma | 0:003889bc474f | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
nobukuma | 0:003889bc474f | 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
nobukuma | 0:003889bc474f | 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
nobukuma | 0:003889bc474f | 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
nobukuma | 0:003889bc474f | 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
nobukuma | 0:003889bc474f | 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
nobukuma | 0:003889bc474f | 21 | THE SOFTWARE. |
nobukuma | 0:003889bc474f | 22 | */ |
nobukuma | 0:003889bc474f | 23 | |
nobukuma | 0:003889bc474f | 24 | #include "mbed.h" |
nobukuma | 0:003889bc474f | 25 | #include "USBHost.h" |
nobukuma | 0:003889bc474f | 26 | #include "Utils.h" |
nobukuma | 0:003889bc474f | 27 | |
nobukuma | 0:003889bc474f | 28 | // Config (default uses x bytes) |
nobukuma | 0:003889bc474f | 29 | #define MAX_DEVICES 8 // Max number of devices |
nobukuma | 0:003889bc474f | 30 | #define MAX_ENDPOINTS_TOTAL 16 // Max number of endpoints total |
nobukuma | 0:003889bc474f | 31 | #define MAX_ENDPOINTS_PER_DEVICE 8 // Max number of endpoints for any one device |
nobukuma | 0:003889bc474f | 32 | |
nobukuma | 0:003889bc474f | 33 | #define USBLOG 1 |
nobukuma | 0:003889bc474f | 34 | #if USBLOG |
nobukuma | 0:003889bc474f | 35 | #define LOG(...) printf(__VA_ARGS__) |
nobukuma | 0:003889bc474f | 36 | #else |
nobukuma | 0:003889bc474f | 37 | #define LOG(...) do {} while(0) |
nobukuma | 0:003889bc474f | 38 | #endif |
nobukuma | 0:003889bc474f | 39 | |
nobukuma | 0:003889bc474f | 40 | // USB host structures |
nobukuma | 0:003889bc474f | 41 | |
nobukuma | 0:003889bc474f | 42 | #define USB_RAM_SIZE 16*1024 // AHB SRAM block 1 TODO MACHINE DEPENDENT |
nobukuma | 0:003889bc474f | 43 | #define USB_RAM_BASE 0x2007C000 |
nobukuma | 0:003889bc474f | 44 | |
nobukuma | 0:003889bc474f | 45 | #define TOKEN_SETUP 0 |
nobukuma | 0:003889bc474f | 46 | #define TOKEN_IN 1 |
nobukuma | 0:003889bc474f | 47 | #define TOKEN_OUT 2 |
nobukuma | 0:003889bc474f | 48 | |
nobukuma | 0:003889bc474f | 49 | // Status flags from hub |
nobukuma | 0:003889bc474f | 50 | #define PORT_CONNECTION 0 |
nobukuma | 0:003889bc474f | 51 | #define PORT_ENABLE 1 |
nobukuma | 0:003889bc474f | 52 | #define PORT_SUSPEND 2 |
nobukuma | 0:003889bc474f | 53 | #define PORT_OVER_CURRENT 3 |
nobukuma | 0:003889bc474f | 54 | #define PORT_RESET 4 |
nobukuma | 0:003889bc474f | 55 | #define PORT_POWER 8 |
nobukuma | 0:003889bc474f | 56 | #define PORT_LOW_SPEED 9 |
nobukuma | 0:003889bc474f | 57 | |
nobukuma | 0:003889bc474f | 58 | #define C_PORT_CONNECTION 16 |
nobukuma | 0:003889bc474f | 59 | #define C_PORT_ENABLE 17 |
nobukuma | 0:003889bc474f | 60 | #define C_PORT_SUSPEND 18 |
nobukuma | 0:003889bc474f | 61 | #define C_PORT_OVER_CURRENT 19 |
nobukuma | 0:003889bc474f | 62 | #define C_PORT_RESET 20 |
nobukuma | 0:003889bc474f | 63 | |
nobukuma | 0:003889bc474f | 64 | typedef struct { |
nobukuma | 0:003889bc474f | 65 | u8 bm_request_type; |
nobukuma | 0:003889bc474f | 66 | u8 b_request; |
nobukuma | 0:003889bc474f | 67 | u16 w_value; |
nobukuma | 0:003889bc474f | 68 | u16 w_index; |
nobukuma | 0:003889bc474f | 69 | u16 w_length; |
nobukuma | 0:003889bc474f | 70 | } Setup; |
nobukuma | 0:003889bc474f | 71 | |
nobukuma | 0:003889bc474f | 72 | |
nobukuma | 0:003889bc474f | 73 | // Hub stuff is kept private just to keep api simple |
nobukuma | 0:003889bc474f | 74 | int SetPortFeature(int device, int feature, int index); |
nobukuma | 0:003889bc474f | 75 | int ClearPortFeature(int device, int feature, int index); |
nobukuma | 0:003889bc474f | 76 | int SetPortPower(int device, int port); |
nobukuma | 0:003889bc474f | 77 | int SetPortReset(int device, int port); |
nobukuma | 0:003889bc474f | 78 | int GetPortStatus(int device, int port, u32* status); |
nobukuma | 0:003889bc474f | 79 | |
nobukuma | 0:003889bc474f | 80 | //=================================================================== |
nobukuma | 0:003889bc474f | 81 | //=================================================================== |
nobukuma | 0:003889bc474f | 82 | // Hardware defines |
nobukuma | 0:003889bc474f | 83 | |
nobukuma | 0:003889bc474f | 84 | // HcControl |
nobukuma | 0:003889bc474f | 85 | #define PeriodicListEnable 0x00000004 |
nobukuma | 0:003889bc474f | 86 | #define IsochronousEnable 0x00000008 |
nobukuma | 0:003889bc474f | 87 | #define ControlListEnable 0x00000010 |
nobukuma | 0:003889bc474f | 88 | #define BulkListEnable 0x00000020 |
nobukuma | 0:003889bc474f | 89 | #define OperationalMask 0x00000080 |
nobukuma | 0:003889bc474f | 90 | #define HostControllerFunctionalState 0x000000C0 |
nobukuma | 0:003889bc474f | 91 | |
nobukuma | 0:003889bc474f | 92 | // HcCommandStatus |
nobukuma | 0:003889bc474f | 93 | #define HostControllerReset 0x00000001 |
nobukuma | 0:003889bc474f | 94 | #define ControlListFilled 0x00000002 |
nobukuma | 0:003889bc474f | 95 | #define BulkListFilled 0x00000004 |
nobukuma | 0:003889bc474f | 96 | |
nobukuma | 0:003889bc474f | 97 | // HcInterruptStatus Register |
nobukuma | 0:003889bc474f | 98 | #define WritebackDoneHead 0x00000002 |
nobukuma | 0:003889bc474f | 99 | #define StartofFrame 0x00000004 |
nobukuma | 0:003889bc474f | 100 | #define ResumeDetected 0x00000008 |
nobukuma | 0:003889bc474f | 101 | #define UnrecoverableError 0x00000010 |
nobukuma | 0:003889bc474f | 102 | #define FrameNumberOverflow 0x00000020 |
nobukuma | 0:003889bc474f | 103 | #define RootHubStatusChange 0x00000040 |
nobukuma | 0:003889bc474f | 104 | #define OwnershipChange 0x00000080 |
nobukuma | 0:003889bc474f | 105 | #define MasterInterruptEnable 0x80000000 |
nobukuma | 0:003889bc474f | 106 | |
nobukuma | 0:003889bc474f | 107 | // HcRhStatus |
nobukuma | 0:003889bc474f | 108 | #define SetGlobalPower 0x00010000 |
nobukuma | 0:003889bc474f | 109 | #define DeviceRemoteWakeupEnable 0x00008000 |
nobukuma | 0:003889bc474f | 110 | |
nobukuma | 0:003889bc474f | 111 | // HcRhPortStatus (hub 0, port 1) |
nobukuma | 0:003889bc474f | 112 | #define CurrentConnectStatus 0x00000001 |
nobukuma | 0:003889bc474f | 113 | #define PortEnableStatus 0x00000002 |
nobukuma | 0:003889bc474f | 114 | #define PortSuspendStatus 0x00000004 |
nobukuma | 0:003889bc474f | 115 | #define PortOverCurrentIndicator 0x00000008 |
nobukuma | 0:003889bc474f | 116 | #define PortResetStatus 0x00000010 |
nobukuma | 0:003889bc474f | 117 | |
nobukuma | 0:003889bc474f | 118 | #define PortPowerStatus 0x00000100 |
nobukuma | 0:003889bc474f | 119 | #define LowspeedDevice 0x00000200 |
nobukuma | 0:003889bc474f | 120 | #define HighspeedDevice 0x00000400 |
nobukuma | 0:003889bc474f | 121 | |
nobukuma | 0:003889bc474f | 122 | #define ConnectStatusChange (CurrentConnectStatus << 16) |
nobukuma | 0:003889bc474f | 123 | #define PortResetStatusChange (PortResetStatus << 16) |
nobukuma | 0:003889bc474f | 124 | |
nobukuma | 0:003889bc474f | 125 | |
nobukuma | 0:003889bc474f | 126 | #define TD_ROUNDING (u32)0x00040000 |
nobukuma | 0:003889bc474f | 127 | #define TD_SETUP (u32)0x00000000 |
nobukuma | 0:003889bc474f | 128 | #define TD_IN (u32)0x00100000 |
nobukuma | 0:003889bc474f | 129 | #define TD_OUT (u32)0x00080000 |
nobukuma | 0:003889bc474f | 130 | #define TD_DELAY_INT(x) (u32)((x) << 21) |
nobukuma | 0:003889bc474f | 131 | #define TD_TOGGLE_0 (u32)0x02000000 |
nobukuma | 0:003889bc474f | 132 | #define TD_TOGGLE_1 (u32)0x03000000 |
nobukuma | 0:003889bc474f | 133 | #define TD_CC (u32)0xF0000000 |
nobukuma | 0:003889bc474f | 134 | |
nobukuma | 0:003889bc474f | 135 | // HostController EndPoint Descriptor |
nobukuma | 0:003889bc474f | 136 | typedef struct { |
nobukuma | 0:003889bc474f | 137 | volatile u32 Control; |
nobukuma | 0:003889bc474f | 138 | volatile u32 TailTd; |
nobukuma | 0:003889bc474f | 139 | volatile u32 HeadTd; |
nobukuma | 0:003889bc474f | 140 | volatile u32 Next; |
nobukuma | 0:003889bc474f | 141 | } HCED; |
nobukuma | 0:003889bc474f | 142 | |
nobukuma | 0:003889bc474f | 143 | // HostController Transfer Descriptor |
nobukuma | 0:003889bc474f | 144 | typedef struct { |
nobukuma | 0:003889bc474f | 145 | volatile u32 Control; |
nobukuma | 0:003889bc474f | 146 | volatile u32 CurrBufPtr; |
nobukuma | 0:003889bc474f | 147 | volatile u32 Next; |
nobukuma | 0:003889bc474f | 148 | volatile u32 BufEnd; |
nobukuma | 0:003889bc474f | 149 | } HCTD; |
nobukuma | 0:003889bc474f | 150 | |
nobukuma | 0:003889bc474f | 151 | // Host Controller Communication Area |
nobukuma | 0:003889bc474f | 152 | typedef struct { |
nobukuma | 0:003889bc474f | 153 | volatile u32 InterruptTable[32]; |
nobukuma | 0:003889bc474f | 154 | volatile u16 FrameNumber; |
nobukuma | 0:003889bc474f | 155 | volatile u16 FrameNumberPad; |
nobukuma | 0:003889bc474f | 156 | volatile u32 DoneHead; |
nobukuma | 0:003889bc474f | 157 | volatile u8 Reserved[120]; |
nobukuma | 0:003889bc474f | 158 | } HCCA; |
nobukuma | 0:003889bc474f | 159 | |
nobukuma | 0:003889bc474f | 160 | //==================================================================================== |
nobukuma | 0:003889bc474f | 161 | //==================================================================================== |
nobukuma | 0:003889bc474f | 162 | |
nobukuma | 0:003889bc474f | 163 | class HostController; |
nobukuma | 0:003889bc474f | 164 | class Endpoint; |
nobukuma | 0:003889bc474f | 165 | class Device; |
nobukuma | 0:003889bc474f | 166 | |
nobukuma | 0:003889bc474f | 167 | // must be 3*16 bytes long |
nobukuma | 0:003889bc474f | 168 | class Endpoint |
nobukuma | 0:003889bc474f | 169 | { |
nobukuma | 0:003889bc474f | 170 | public: |
nobukuma | 0:003889bc474f | 171 | HCED EndpointDescriptor; // Pointer to EndpointDescriptor == Pointer to Endpoint |
nobukuma | 0:003889bc474f | 172 | HCTD TDHead; |
nobukuma | 0:003889bc474f | 173 | |
nobukuma | 0:003889bc474f | 174 | enum State |
nobukuma | 0:003889bc474f | 175 | { |
nobukuma | 0:003889bc474f | 176 | Free, |
nobukuma | 0:003889bc474f | 177 | NotQueued, |
nobukuma | 0:003889bc474f | 178 | Idle, |
nobukuma | 0:003889bc474f | 179 | SetupQueued, |
nobukuma | 0:003889bc474f | 180 | DataQueued, |
nobukuma | 0:003889bc474f | 181 | StatusQueued, |
nobukuma | 0:003889bc474f | 182 | CallbackPending |
nobukuma | 0:003889bc474f | 183 | }; |
nobukuma | 0:003889bc474f | 184 | |
nobukuma | 0:003889bc474f | 185 | volatile u8 CurrentState; |
nobukuma | 0:003889bc474f | 186 | u8 Flags; // 0x80 In, 0x03 mask endpoint type |
nobukuma | 0:003889bc474f | 187 | |
nobukuma | 0:003889bc474f | 188 | u16 Length; |
nobukuma | 0:003889bc474f | 189 | u8* Data; |
nobukuma | 0:003889bc474f | 190 | USBCallback Callback; // Must be a multiple of 16 bytes long |
nobukuma | 0:003889bc474f | 191 | void* UserData; |
nobukuma | 0:003889bc474f | 192 | |
nobukuma | 0:003889bc474f | 193 | int Address() |
nobukuma | 0:003889bc474f | 194 | { |
nobukuma | 0:003889bc474f | 195 | int ep = (EndpointDescriptor.Control >> 7) & 0xF; |
nobukuma | 0:003889bc474f | 196 | if (ep) |
nobukuma | 0:003889bc474f | 197 | ep |= Flags & 0x80; |
nobukuma | 0:003889bc474f | 198 | return ep; |
nobukuma | 0:003889bc474f | 199 | } |
nobukuma | 0:003889bc474f | 200 | |
nobukuma | 0:003889bc474f | 201 | int Device() |
nobukuma | 0:003889bc474f | 202 | { |
nobukuma | 0:003889bc474f | 203 | return EndpointDescriptor.Control & 0x7F; |
nobukuma | 0:003889bc474f | 204 | } |
nobukuma | 0:003889bc474f | 205 | |
nobukuma | 0:003889bc474f | 206 | int Status() |
nobukuma | 0:003889bc474f | 207 | { |
nobukuma | 0:003889bc474f | 208 | return (TDHead.Control >> 28) & 0xF; |
nobukuma | 0:003889bc474f | 209 | } |
nobukuma | 0:003889bc474f | 210 | |
nobukuma | 0:003889bc474f | 211 | u32 Enqueue(u32 head) |
nobukuma | 0:003889bc474f | 212 | { |
nobukuma | 0:003889bc474f | 213 | if (CurrentState == NotQueued) |
nobukuma | 0:003889bc474f | 214 | { |
nobukuma | 0:003889bc474f | 215 | EndpointDescriptor.Next = head; |
nobukuma | 0:003889bc474f | 216 | head = (u32)&EndpointDescriptor; |
nobukuma | 0:003889bc474f | 217 | CurrentState = Idle; |
nobukuma | 0:003889bc474f | 218 | } |
nobukuma | 0:003889bc474f | 219 | return head; |
nobukuma | 0:003889bc474f | 220 | } |
nobukuma | 0:003889bc474f | 221 | }; |
nobukuma | 0:003889bc474f | 222 | |
nobukuma | 0:003889bc474f | 223 | class Device |
nobukuma | 0:003889bc474f | 224 | { |
nobukuma | 0:003889bc474f | 225 | public: |
nobukuma | 0:003889bc474f | 226 | u8 _endpointMap[MAX_ENDPOINTS_PER_DEVICE*2]; |
nobukuma | 0:003889bc474f | 227 | u8 Hub; |
nobukuma | 0:003889bc474f | 228 | u8 Port; |
nobukuma | 0:003889bc474f | 229 | u8 Addr; |
nobukuma | 0:003889bc474f | 230 | u8 Pad; |
nobukuma | 0:003889bc474f | 231 | |
nobukuma | 0:003889bc474f | 232 | // Only if this device is a hub |
nobukuma | 0:003889bc474f | 233 | u8 HubPortCount; // nonzero if this is a hub |
nobukuma | 0:003889bc474f | 234 | u8 HubInterruptData; |
nobukuma | 0:003889bc474f | 235 | u8 HubMap; |
nobukuma | 0:003889bc474f | 236 | u8 HubMask; |
nobukuma | 0:003889bc474f | 237 | |
nobukuma | 0:003889bc474f | 238 | int Flags; // 1 = Disconnected |
nobukuma | 0:003889bc474f | 239 | |
nobukuma | 0:003889bc474f | 240 | Setup SetupBuffer; |
nobukuma | 0:003889bc474f | 241 | |
nobukuma | 0:003889bc474f | 242 | // Allocate endpoint zero |
nobukuma | 0:003889bc474f | 243 | int Init(DeviceDescriptor* d, int hub, int port, int addr, int lowSpeed) |
nobukuma | 0:003889bc474f | 244 | { |
nobukuma | 0:003889bc474f | 245 | Hub = hub; |
nobukuma | 0:003889bc474f | 246 | Port = port; |
nobukuma | 0:003889bc474f | 247 | Addr = addr; |
nobukuma | 0:003889bc474f | 248 | Flags = lowSpeed; |
nobukuma | 0:003889bc474f | 249 | memset(_endpointMap,0xFF,sizeof(_endpointMap)); |
nobukuma | 0:003889bc474f | 250 | return 0; |
nobukuma | 0:003889bc474f | 251 | } |
nobukuma | 0:003889bc474f | 252 | |
nobukuma | 0:003889bc474f | 253 | int SetEndpointIndex(int ep, int endpointIndex) |
nobukuma | 0:003889bc474f | 254 | { |
nobukuma | 0:003889bc474f | 255 | for (int i = 0; i < MAX_ENDPOINTS_PER_DEVICE*2; i += 2) |
nobukuma | 0:003889bc474f | 256 | { |
nobukuma | 0:003889bc474f | 257 | if (_endpointMap[i] == 0xFF) // Add endpoint to map |
nobukuma | 0:003889bc474f | 258 | { |
nobukuma | 0:003889bc474f | 259 | _endpointMap[i] = ep; |
nobukuma | 0:003889bc474f | 260 | _endpointMap[i+1] = endpointIndex; |
nobukuma | 0:003889bc474f | 261 | return 0; |
nobukuma | 0:003889bc474f | 262 | } |
nobukuma | 0:003889bc474f | 263 | } |
nobukuma | 0:003889bc474f | 264 | return ERR_ENDPOINT_NONE_LEFT; |
nobukuma | 0:003889bc474f | 265 | } |
nobukuma | 0:003889bc474f | 266 | |
nobukuma | 0:003889bc474f | 267 | int GetEndpointIndex(int ep) |
nobukuma | 0:003889bc474f | 268 | { |
nobukuma | 0:003889bc474f | 269 | for (int i = 0; i < MAX_ENDPOINTS_PER_DEVICE*2; i += 2) |
nobukuma | 0:003889bc474f | 270 | { |
nobukuma | 0:003889bc474f | 271 | if (_endpointMap[i] == ep) |
nobukuma | 0:003889bc474f | 272 | return _endpointMap[i+1]; |
nobukuma | 0:003889bc474f | 273 | if (_endpointMap[i] == 0xFF) |
nobukuma | 0:003889bc474f | 274 | break; |
nobukuma | 0:003889bc474f | 275 | } |
nobukuma | 0:003889bc474f | 276 | return -1; |
nobukuma | 0:003889bc474f | 277 | } |
nobukuma | 0:003889bc474f | 278 | }; |
nobukuma | 0:003889bc474f | 279 | |
nobukuma | 0:003889bc474f | 280 | class HostController |
nobukuma | 0:003889bc474f | 281 | { |
nobukuma | 0:003889bc474f | 282 | public: |
nobukuma | 0:003889bc474f | 283 | HCCA CommunicationArea; |
nobukuma | 0:003889bc474f | 284 | Endpoint Endpoints[MAX_ENDPOINTS_TOTAL]; // Multiple of 16 |
nobukuma | 0:003889bc474f | 285 | |
nobukuma | 0:003889bc474f | 286 | Endpoint EndpointZero; // For device enumeration |
nobukuma | 0:003889bc474f | 287 | HCTD _commonTail; |
nobukuma | 0:003889bc474f | 288 | Setup _setupZero; |
nobukuma | 0:003889bc474f | 289 | |
nobukuma | 0:003889bc474f | 290 | Device Devices[MAX_DEVICES]; |
nobukuma | 0:003889bc474f | 291 | u32 _frameNumber; // 32 bit ms counter |
nobukuma | 0:003889bc474f | 292 | |
nobukuma | 0:003889bc474f | 293 | u8 _callbacksPending; // Endpoints with callbacks are pending, set from ISR via ProcessDoneQueue |
nobukuma | 0:003889bc474f | 294 | u8 _rootHubStatusChange; // Root hub status has changed, set from ISR |
nobukuma | 0:003889bc474f | 295 | u8 _unused0; |
nobukuma | 0:003889bc474f | 296 | u8 _unused1; |
nobukuma | 0:003889bc474f | 297 | |
nobukuma | 0:003889bc474f | 298 | u8 _connectPending; // Reset has initiated a connect |
nobukuma | 0:003889bc474f | 299 | u8 _connectCountdown; // Number of ms left after reset before we can connect |
nobukuma | 0:003889bc474f | 300 | u8 _connectHub; // Will connect on this hub |
nobukuma | 0:003889bc474f | 301 | u8 _connectPort; // ... and this port |
nobukuma | 0:003889bc474f | 302 | |
nobukuma | 0:003889bc474f | 303 | u8 SRAM[0]; // Start of free SRAM |
nobukuma | 0:003889bc474f | 304 | |
nobukuma | 0:003889bc474f | 305 | void Loop() |
nobukuma | 0:003889bc474f | 306 | { |
nobukuma | 0:003889bc474f | 307 | u16 elapsed = CommunicationArea.FrameNumber - (u16)_frameNumber; // extend to 32 bits |
nobukuma | 0:003889bc474f | 308 | _frameNumber += elapsed; |
nobukuma | 0:003889bc474f | 309 | |
nobukuma | 0:003889bc474f | 310 | // Do callbacks, if any |
nobukuma | 0:003889bc474f | 311 | while (_callbacksPending) |
nobukuma | 0:003889bc474f | 312 | { |
nobukuma | 0:003889bc474f | 313 | for (int i = 0; i < MAX_ENDPOINTS_TOTAL; i++) |
nobukuma | 0:003889bc474f | 314 | { |
nobukuma | 0:003889bc474f | 315 | Endpoint* endpoint = Endpoints + i; |
nobukuma | 0:003889bc474f | 316 | if (endpoint->CurrentState == Endpoint::CallbackPending) |
nobukuma | 0:003889bc474f | 317 | { |
nobukuma | 0:003889bc474f | 318 | _callbacksPending--; |
nobukuma | 0:003889bc474f | 319 | endpoint->CurrentState = Endpoint::Idle; |
nobukuma | 0:003889bc474f | 320 | endpoint->Callback(endpoint->Device(),endpoint->Address(),endpoint->Status(),endpoint->Data,endpoint->Length,endpoint->UserData); |
nobukuma | 0:003889bc474f | 321 | } |
nobukuma | 0:003889bc474f | 322 | } |
nobukuma | 0:003889bc474f | 323 | } |
nobukuma | 0:003889bc474f | 324 | |
nobukuma | 0:003889bc474f | 325 | // Deal with changes on the root hub |
nobukuma | 0:003889bc474f | 326 | if (_rootHubStatusChange) |
nobukuma | 0:003889bc474f | 327 | { |
nobukuma | 0:003889bc474f | 328 | u32 status = LPC_USB->HcRhPortStatus1; |
nobukuma | 0:003889bc474f | 329 | _rootHubStatusChange = 0; |
nobukuma | 0:003889bc474f | 330 | if (status >> 16) |
nobukuma | 0:003889bc474f | 331 | { |
nobukuma | 0:003889bc474f | 332 | HubStatusChange(0,1,status); |
nobukuma | 0:003889bc474f | 333 | LPC_USB->HcRhPortStatus1 = status & 0xFFFF0000; // clear status changes |
nobukuma | 0:003889bc474f | 334 | } |
nobukuma | 0:003889bc474f | 335 | } |
nobukuma | 0:003889bc474f | 336 | |
nobukuma | 0:003889bc474f | 337 | // Connect after reset timeout |
nobukuma | 0:003889bc474f | 338 | if (_connectCountdown) |
nobukuma | 0:003889bc474f | 339 | { |
nobukuma | 0:003889bc474f | 340 | if (elapsed >= _connectCountdown) |
nobukuma | 0:003889bc474f | 341 | { |
nobukuma | 0:003889bc474f | 342 | _connectCountdown = 0; |
nobukuma | 0:003889bc474f | 343 | Connect(_connectHub,_connectPort & 0x7F,_connectPort & 0x80); |
nobukuma | 0:003889bc474f | 344 | } else |
nobukuma | 0:003889bc474f | 345 | _connectCountdown -= elapsed; |
nobukuma | 0:003889bc474f | 346 | } |
nobukuma | 0:003889bc474f | 347 | } |
nobukuma | 0:003889bc474f | 348 | |
nobukuma | 0:003889bc474f | 349 | // HubInterrupt - bitmap in dev->HubInterruptData |
nobukuma | 0:003889bc474f | 350 | void HubInterrupt(int device) |
nobukuma | 0:003889bc474f | 351 | { |
nobukuma | 0:003889bc474f | 352 | Device* dev = &Devices[device-1]; |
nobukuma | 0:003889bc474f | 353 | for (int i = 0; i < dev->HubPortCount; i++) |
nobukuma | 0:003889bc474f | 354 | { |
nobukuma | 0:003889bc474f | 355 | int port = i+1; |
nobukuma | 0:003889bc474f | 356 | if (dev->HubInterruptData & (1 << port)) |
nobukuma | 0:003889bc474f | 357 | { |
nobukuma | 0:003889bc474f | 358 | u32 status = 0; |
nobukuma | 0:003889bc474f | 359 | GetPortStatus(device,port,&status); |
nobukuma | 0:003889bc474f | 360 | if (status >> 16) |
nobukuma | 0:003889bc474f | 361 | { |
nobukuma | 0:003889bc474f | 362 | if (_connectPending && (status & ConnectStatusChange)) |
nobukuma | 0:003889bc474f | 363 | continue; // Don't connect again until previous device has been added and addressed |
nobukuma | 0:003889bc474f | 364 | |
nobukuma | 0:003889bc474f | 365 | HubStatusChange(device,port,status); |
nobukuma | 0:003889bc474f | 366 | if (status & ConnectStatusChange) |
nobukuma | 0:003889bc474f | 367 | ClearPortFeature(device,C_PORT_CONNECTION,port); |
nobukuma | 0:003889bc474f | 368 | if (status & PortResetStatusChange) |
nobukuma | 0:003889bc474f | 369 | ClearPortFeature(device,C_PORT_RESET,port); |
nobukuma | 0:003889bc474f | 370 | } |
nobukuma | 0:003889bc474f | 371 | } |
nobukuma | 0:003889bc474f | 372 | } |
nobukuma | 0:003889bc474f | 373 | } |
nobukuma | 0:003889bc474f | 374 | |
nobukuma | 0:003889bc474f | 375 | static void HubInterruptCallback(int device, int endpoint, int status, u8* data, int len, void* userData) |
nobukuma | 0:003889bc474f | 376 | { |
nobukuma | 0:003889bc474f | 377 | HostController* controller = (HostController*)userData; |
nobukuma | 0:003889bc474f | 378 | if (status == 0) |
nobukuma | 0:003889bc474f | 379 | controller->HubInterrupt(device); |
nobukuma | 0:003889bc474f | 380 | USBInterruptTransfer(device,endpoint,data,1,HubInterruptCallback,userData); |
nobukuma | 0:003889bc474f | 381 | } |
nobukuma | 0:003889bc474f | 382 | |
nobukuma | 0:003889bc474f | 383 | int InitHub(int device) |
nobukuma | 0:003889bc474f | 384 | { |
nobukuma | 0:003889bc474f | 385 | u8 buf[16]; |
nobukuma | 0:003889bc474f | 386 | int r= USBControlTransfer(device,DEVICE_TO_HOST | REQUEST_TYPE_CLASS | RECIPIENT_DEVICE,GET_DESCRIPTOR,(DESCRIPTOR_TYPE_HUB << 8),0,buf,sizeof(buf)); |
nobukuma | 0:003889bc474f | 387 | if (r < 0) |
nobukuma | 0:003889bc474f | 388 | return ERR_HUB_INIT_FAILED; |
nobukuma | 0:003889bc474f | 389 | |
nobukuma | 0:003889bc474f | 390 | // turn on power on the hubs ports |
nobukuma | 0:003889bc474f | 391 | Device* dev = &Devices[device-1]; |
nobukuma | 0:003889bc474f | 392 | int ports = buf[2]; |
nobukuma | 0:003889bc474f | 393 | dev->HubPortCount = ports; |
nobukuma | 0:003889bc474f | 394 | for (int i = 0; i < ports; i++) |
nobukuma | 0:003889bc474f | 395 | SetPortPower(device,i+1); |
nobukuma | 0:003889bc474f | 396 | |
nobukuma | 0:003889bc474f | 397 | // Enable hub change interrupts |
nobukuma | 0:003889bc474f | 398 | return USBInterruptTransfer(device,0x81,&dev->HubInterruptData,1,HubInterruptCallback,this); |
nobukuma | 0:003889bc474f | 399 | } |
nobukuma | 0:003889bc474f | 400 | |
nobukuma | 0:003889bc474f | 401 | int AddEndpoint(int device, int ep, int attributes, int maxPacketSize, int interval) |
nobukuma | 0:003889bc474f | 402 | { |
nobukuma | 0:003889bc474f | 403 | LOG("AddEndpoint D:%02X A:%02X T:%02X P:%04X I:%02X\n",device,ep,attributes,maxPacketSize,interval); |
nobukuma | 0:003889bc474f | 404 | Device* dev = &Devices[device-1]; |
nobukuma | 0:003889bc474f | 405 | Endpoint* endpoint = AllocateEndpoint(device,ep,attributes,maxPacketSize); |
nobukuma | 0:003889bc474f | 406 | if (!endpoint) |
nobukuma | 0:003889bc474f | 407 | return ERR_ENDPOINT_NONE_LEFT; |
nobukuma | 0:003889bc474f | 408 | dev->SetEndpointIndex(ep,endpoint - Endpoints); |
nobukuma | 0:003889bc474f | 409 | endpoint->EndpointDescriptor.Control |= dev->Flags; // Map in slow speed |
nobukuma | 0:003889bc474f | 410 | return 0; // TODO ed->bInterval |
nobukuma | 0:003889bc474f | 411 | } |
nobukuma | 0:003889bc474f | 412 | |
nobukuma | 0:003889bc474f | 413 | int AddEndpoint(int device, EndpointDescriptor* ed) |
nobukuma | 0:003889bc474f | 414 | { |
nobukuma | 0:003889bc474f | 415 | return AddEndpoint(device,ed->bEndpointAddress,ed->bmAttributes,ed->wMaxPacketSize,ed->bInterval); |
nobukuma | 0:003889bc474f | 416 | } |
nobukuma | 0:003889bc474f | 417 | |
nobukuma | 0:003889bc474f | 418 | // allocate a endpoint |
nobukuma | 0:003889bc474f | 419 | Endpoint* AllocateEndpoint(int device, int endpointAddress, int type, int maxPacketSize) |
nobukuma | 0:003889bc474f | 420 | { |
nobukuma | 0:003889bc474f | 421 | for (int i = 0; i < MAX_ENDPOINTS_TOTAL; i++) |
nobukuma | 0:003889bc474f | 422 | { |
nobukuma | 0:003889bc474f | 423 | Endpoint* ep = &Endpoints[i]; |
nobukuma | 0:003889bc474f | 424 | if (ep->CurrentState == 0) |
nobukuma | 0:003889bc474f | 425 | { |
nobukuma | 0:003889bc474f | 426 | //LOG("Allocated endpoint %d to %02X:%02X\n",i,device,endpointAddress); |
nobukuma | 0:003889bc474f | 427 | ep->Flags = (endpointAddress & 0x80) | (type & 3); |
nobukuma | 0:003889bc474f | 428 | ep->CurrentState = Endpoint::NotQueued; |
nobukuma | 0:003889bc474f | 429 | ep->EndpointDescriptor.Control = (maxPacketSize << 16) | ((endpointAddress & 0x7F) << 7) | device; |
nobukuma | 0:003889bc474f | 430 | return ep; |
nobukuma | 0:003889bc474f | 431 | } |
nobukuma | 0:003889bc474f | 432 | } |
nobukuma | 0:003889bc474f | 433 | return 0; |
nobukuma | 0:003889bc474f | 434 | } |
nobukuma | 0:003889bc474f | 435 | |
nobukuma | 0:003889bc474f | 436 | Endpoint* GetEndpoint(int device, int ep) |
nobukuma | 0:003889bc474f | 437 | { |
nobukuma | 0:003889bc474f | 438 | if (device == 0) |
nobukuma | 0:003889bc474f | 439 | { |
nobukuma | 0:003889bc474f | 440 | //printf("WARNING: USING DEVICE 0\n"); |
nobukuma | 0:003889bc474f | 441 | return &EndpointZero; |
nobukuma | 0:003889bc474f | 442 | } |
nobukuma | 0:003889bc474f | 443 | if (device > MAX_DEVICES) |
nobukuma | 0:003889bc474f | 444 | return 0; |
nobukuma | 0:003889bc474f | 445 | int i = Devices[device-1].GetEndpointIndex(ep); |
nobukuma | 0:003889bc474f | 446 | if (i == -1) |
nobukuma | 0:003889bc474f | 447 | return 0; |
nobukuma | 0:003889bc474f | 448 | return Endpoints + i; |
nobukuma | 0:003889bc474f | 449 | } |
nobukuma | 0:003889bc474f | 450 | |
nobukuma | 0:003889bc474f | 451 | int Transfer(Endpoint* endpoint, int token, u8* data, int len, int state) |
nobukuma | 0:003889bc474f | 452 | { |
nobukuma | 0:003889bc474f | 453 | //LOG("Transfer %02X T:%d Len:%d S:%d\n",endpoint->Address(),token,len,state); |
nobukuma | 0:003889bc474f | 454 | |
nobukuma | 0:003889bc474f | 455 | int toggle = 0; |
nobukuma | 0:003889bc474f | 456 | if (endpoint->Address() == 0) |
nobukuma | 0:003889bc474f | 457 | toggle = (token == TOKEN_SETUP) ? TD_TOGGLE_0 : TD_TOGGLE_1; |
nobukuma | 0:003889bc474f | 458 | |
nobukuma | 0:003889bc474f | 459 | if (token != TOKEN_SETUP) |
nobukuma | 0:003889bc474f | 460 | token = (token == TOKEN_IN ? TD_IN : TD_OUT); |
nobukuma | 0:003889bc474f | 461 | |
nobukuma | 0:003889bc474f | 462 | HCTD* head = &endpoint->TDHead; |
nobukuma | 0:003889bc474f | 463 | HCTD* tail = &_commonTail; |
nobukuma | 0:003889bc474f | 464 | |
nobukuma | 0:003889bc474f | 465 | head->Control = TD_ROUNDING | token | TD_DELAY_INT(0) | toggle | TD_CC; |
nobukuma | 0:003889bc474f | 466 | head->CurrBufPtr = (u32)data; |
nobukuma | 0:003889bc474f | 467 | head->BufEnd = (u32)(data + len - 1); |
nobukuma | 0:003889bc474f | 468 | head->Next = (u32)tail; |
nobukuma | 0:003889bc474f | 469 | |
nobukuma | 0:003889bc474f | 470 | HCED* ed = &endpoint->EndpointDescriptor; |
nobukuma | 0:003889bc474f | 471 | ed->HeadTd = (u32)head | (ed->HeadTd & 0x00000002); // carry toggle |
nobukuma | 0:003889bc474f | 472 | ed->TailTd = (u32)tail; |
nobukuma | 0:003889bc474f | 473 | |
nobukuma | 0:003889bc474f | 474 | //HCTD* td = head; |
nobukuma | 0:003889bc474f | 475 | //LOG("%04X TD %08X %08X %08X Next:%08X\n",CommunicationArea.FrameNumber,td->Control,td->CurrBufPtr,td->BufEnd,td->Next); |
nobukuma | 0:003889bc474f | 476 | //LOG("%04X ED %08X %08X %08X\n",CommunicationArea.FrameNumber,ed->Control,ed->HeadTd,ed->TailTd); |
nobukuma | 0:003889bc474f | 477 | |
nobukuma | 0:003889bc474f | 478 | switch (endpoint->Flags & 3) |
nobukuma | 0:003889bc474f | 479 | { |
nobukuma | 0:003889bc474f | 480 | case ENDPOINT_CONTROL: |
nobukuma | 0:003889bc474f | 481 | LPC_USB->HcControlHeadED = endpoint->Enqueue(LPC_USB->HcControlHeadED); // May change state NotQueued->Idle |
nobukuma | 0:003889bc474f | 482 | endpoint->CurrentState = state; // Get in before an int |
nobukuma | 0:003889bc474f | 483 | LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | ControlListFilled; |
nobukuma | 0:003889bc474f | 484 | LPC_USB->HcControl = LPC_USB->HcControl | ControlListEnable; |
nobukuma | 0:003889bc474f | 485 | break; |
nobukuma | 0:003889bc474f | 486 | |
nobukuma | 0:003889bc474f | 487 | case ENDPOINT_BULK: |
nobukuma | 0:003889bc474f | 488 | LPC_USB->HcBulkHeadED = endpoint->Enqueue(LPC_USB->HcBulkHeadED); |
nobukuma | 0:003889bc474f | 489 | endpoint->CurrentState = state; |
nobukuma | 0:003889bc474f | 490 | LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | BulkListFilled; |
nobukuma | 0:003889bc474f | 491 | LPC_USB->HcControl = LPC_USB->HcControl | BulkListEnable; |
nobukuma | 0:003889bc474f | 492 | break; |
nobukuma | 0:003889bc474f | 493 | |
nobukuma | 0:003889bc474f | 494 | case ENDPOINT_INTERRUPT: |
nobukuma | 0:003889bc474f | 495 | CommunicationArea.InterruptTable[0] = endpoint->Enqueue(CommunicationArea.InterruptTable[0]); |
nobukuma | 0:003889bc474f | 496 | endpoint->CurrentState = state; |
nobukuma | 0:003889bc474f | 497 | LPC_USB->HcControl |= PeriodicListEnable; |
nobukuma | 0:003889bc474f | 498 | break; |
nobukuma | 0:003889bc474f | 499 | } |
nobukuma | 0:003889bc474f | 500 | return 0; |
nobukuma | 0:003889bc474f | 501 | } |
nobukuma | 0:003889bc474f | 502 | |
nobukuma | 0:003889bc474f | 503 | // Remove an endpoint from an active queue |
nobukuma | 0:003889bc474f | 504 | bool Remove(HCED* ed, volatile HCED** queue) |
nobukuma | 0:003889bc474f | 505 | { |
nobukuma | 0:003889bc474f | 506 | if (*queue == 0) |
nobukuma | 0:003889bc474f | 507 | return false; |
nobukuma | 0:003889bc474f | 508 | if (*queue == (volatile HCED*)ed) |
nobukuma | 0:003889bc474f | 509 | { |
nobukuma | 0:003889bc474f | 510 | *queue = (volatile HCED*)ed->Next; // At head of queue |
nobukuma | 0:003889bc474f | 511 | return true; |
nobukuma | 0:003889bc474f | 512 | } |
nobukuma | 0:003889bc474f | 513 | |
nobukuma | 0:003889bc474f | 514 | volatile HCED* head = *queue; |
nobukuma | 0:003889bc474f | 515 | while (head) |
nobukuma | 0:003889bc474f | 516 | { |
nobukuma | 0:003889bc474f | 517 | if (head->Next == (u32)ed) |
nobukuma | 0:003889bc474f | 518 | { |
nobukuma | 0:003889bc474f | 519 | head->Next = ed->Next; |
nobukuma | 0:003889bc474f | 520 | return true; |
nobukuma | 0:003889bc474f | 521 | } |
nobukuma | 0:003889bc474f | 522 | head = (volatile HCED*)head->Next; |
nobukuma | 0:003889bc474f | 523 | } |
nobukuma | 0:003889bc474f | 524 | return false; |
nobukuma | 0:003889bc474f | 525 | } |
nobukuma | 0:003889bc474f | 526 | |
nobukuma | 0:003889bc474f | 527 | void Release(Endpoint* endpoint) |
nobukuma | 0:003889bc474f | 528 | { |
nobukuma | 0:003889bc474f | 529 | if (endpoint->CurrentState == Endpoint::NotQueued) |
nobukuma | 0:003889bc474f | 530 | { |
nobukuma | 0:003889bc474f | 531 | // Never event used it, nothing to do |
nobukuma | 0:003889bc474f | 532 | } |
nobukuma | 0:003889bc474f | 533 | else |
nobukuma | 0:003889bc474f | 534 | { |
nobukuma | 0:003889bc474f | 535 | HCED* ed = (HCED*)endpoint; |
nobukuma | 0:003889bc474f | 536 | ed->Control |= 0x4000; // SKIP |
nobukuma | 0:003889bc474f | 537 | switch (endpoint->Flags & 0x03) |
nobukuma | 0:003889bc474f | 538 | { |
nobukuma | 0:003889bc474f | 539 | case ENDPOINT_CONTROL: |
nobukuma | 0:003889bc474f | 540 | Remove(ed,(volatile HCED**)&LPC_USB->HcControlHeadED); |
nobukuma | 0:003889bc474f | 541 | break; |
nobukuma | 0:003889bc474f | 542 | case ENDPOINT_BULK: |
nobukuma | 0:003889bc474f | 543 | Remove(ed,(volatile HCED**)&LPC_USB->HcBulkHeadED); |
nobukuma | 0:003889bc474f | 544 | break; |
nobukuma | 0:003889bc474f | 545 | case ENDPOINT_INTERRUPT: |
nobukuma | 0:003889bc474f | 546 | for (int i = 0; i < 32; i++) |
nobukuma | 0:003889bc474f | 547 | Remove(ed,(volatile HCED**)&CommunicationArea.InterruptTable[i]); |
nobukuma | 0:003889bc474f | 548 | break; |
nobukuma | 0:003889bc474f | 549 | } |
nobukuma | 0:003889bc474f | 550 | |
nobukuma | 0:003889bc474f | 551 | u16 fn = CommunicationArea.FrameNumber; |
nobukuma | 0:003889bc474f | 552 | while (fn == CommunicationArea.FrameNumber) |
nobukuma | 0:003889bc474f | 553 | ; // Wait for next frame |
nobukuma | 0:003889bc474f | 554 | |
nobukuma | 0:003889bc474f | 555 | } |
nobukuma | 0:003889bc474f | 556 | |
nobukuma | 0:003889bc474f | 557 | // In theory, the endpoint is now dead. |
nobukuma | 0:003889bc474f | 558 | // TODO: Will Callbacks ever be pending? BUGBUG |
nobukuma | 0:003889bc474f | 559 | memset(endpoint,0,sizeof(Endpoint)); |
nobukuma | 0:003889bc474f | 560 | } |
nobukuma | 0:003889bc474f | 561 | |
nobukuma | 0:003889bc474f | 562 | // Pop the last TD from the list |
nobukuma | 0:003889bc474f | 563 | HCTD* Reverse(HCTD* current) |
nobukuma | 0:003889bc474f | 564 | { //int n = 0; |
nobukuma | 0:003889bc474f | 565 | HCTD *result = NULL,*temp; |
nobukuma | 0:003889bc474f | 566 | while (current) |
nobukuma | 0:003889bc474f | 567 | { //n++; |
nobukuma | 0:003889bc474f | 568 | temp = (HCTD*)current->Next; |
nobukuma | 0:003889bc474f | 569 | current->Next = (u32)result; |
nobukuma | 0:003889bc474f | 570 | result = current; |
nobukuma | 0:003889bc474f | 571 | current = temp; |
nobukuma | 0:003889bc474f | 572 | } |
nobukuma | 0:003889bc474f | 573 | //if (n>1) printf("%d TD's on Done Queue\n", n); |
nobukuma | 0:003889bc474f | 574 | return result; |
nobukuma | 0:003889bc474f | 575 | } |
nobukuma | 0:003889bc474f | 576 | |
nobukuma | 0:003889bc474f | 577 | // Called from interrupt... |
nobukuma | 0:003889bc474f | 578 | // Control endpoints use a state machine to progress through the transfers |
nobukuma | 0:003889bc474f | 579 | void ProcessDoneQueue(u32 tdList) |
nobukuma | 0:003889bc474f | 580 | { |
nobukuma | 0:003889bc474f | 581 | HCTD* list = Reverse((HCTD*)tdList);//because completed transfers were added to the head of the list |
nobukuma | 0:003889bc474f | 582 | while (list) |
nobukuma | 0:003889bc474f | 583 | {//horrible hack: an Endpoint starts with an HCED and an HCTD and apparently all HCTDs are contained in Endpoints this way |
nobukuma | 0:003889bc474f | 584 | //furthermore the sizeof an HCTD equeals the size of an HCED, hence the next expression gives indeed the Endpoint* for the HCTD |
nobukuma | 0:003889bc474f | 585 | Endpoint* endpoint = (Endpoint*)(list-1); |
nobukuma | 0:003889bc474f | 586 | list = (HCTD*)list->Next; |
nobukuma | 0:003889bc474f | 587 | int ep = endpoint->Address(); |
nobukuma | 0:003889bc474f | 588 | bool in = endpoint->Flags & 0x80; |
nobukuma | 0:003889bc474f | 589 | int status = (endpoint->TDHead.Control >> 28) & 0xF; |
nobukuma | 0:003889bc474f | 590 | //the printf below is essential for correct working !?!? remove it and some messages will be missed |
nobukuma | 0:003889bc474f | 591 | //LOG("ProcessDoneQueue %02X %08X\n",ep,endpoint->TDHead.Control); |
nobukuma | 0:003889bc474f | 592 | wait_us(0);//200, 100, 50, 20, 10, 5, 2, 1,0 works, deleting the statement does not work |
nobukuma | 0:003889bc474f | 593 | //I suspect it has to do with interrupt priorities rather than with delays |
nobukuma | 0:003889bc474f | 594 | if (status != 0) |
nobukuma | 0:003889bc474f | 595 | { |
nobukuma | 0:003889bc474f | 596 | LOG("ProcessDoneQueue status %02X %d\n",ep,status); |
nobukuma | 0:003889bc474f | 597 | endpoint->CurrentState = Endpoint::Idle; |
nobukuma | 0:003889bc474f | 598 | } else { |
nobukuma | 0:003889bc474f | 599 | switch (endpoint->CurrentState) |
nobukuma | 0:003889bc474f | 600 | { |
nobukuma | 0:003889bc474f | 601 | case Endpoint::SetupQueued: |
nobukuma | 0:003889bc474f | 602 | if (endpoint->Length == 0){ |
nobukuma | 0:003889bc474f | 603 | Transfer(endpoint,in ? TOKEN_OUT : TOKEN_IN,0,0,Endpoint::StatusQueued); // Skip Data Phase |
nobukuma | 0:003889bc474f | 604 | } else {//here is a potential deadlock |
nobukuma | 0:003889bc474f | 605 | //printf("Deadlock Avoidance, ep=%02x, in=%d\n", ep, in);//with printf before transfer -> no deadlock |
nobukuma | 0:003889bc474f | 606 | Transfer(endpoint,in ? TOKEN_IN : TOKEN_OUT,endpoint->Data,endpoint->Length, Endpoint::DataQueued); // Setup is done, now Data |
nobukuma | 0:003889bc474f | 607 | //printf("SetupQueued->DataQueued\n");//with printf after Transfer ->deadlock |
nobukuma | 0:003889bc474f | 608 | } |
nobukuma | 0:003889bc474f | 609 | break; |
nobukuma | 0:003889bc474f | 610 | |
nobukuma | 0:003889bc474f | 611 | case Endpoint::DataQueued: |
nobukuma | 0:003889bc474f | 612 | if (endpoint->TDHead.CurrBufPtr) |
nobukuma | 0:003889bc474f | 613 | endpoint->Length = endpoint->TDHead.CurrBufPtr - (u32)endpoint->Data; |
nobukuma | 0:003889bc474f | 614 | |
nobukuma | 0:003889bc474f | 615 | if (ep == 0){ |
nobukuma | 0:003889bc474f | 616 | Transfer(endpoint,in ? TOKEN_OUT : TOKEN_IN,0,0,Endpoint::StatusQueued); // Data is done, now Status, Control only |
nobukuma | 0:003889bc474f | 617 | } else |
nobukuma | 0:003889bc474f | 618 | endpoint->CurrentState = Endpoint::Idle; |
nobukuma | 0:003889bc474f | 619 | break; |
nobukuma | 0:003889bc474f | 620 | |
nobukuma | 0:003889bc474f | 621 | case Endpoint::StatusQueued: // Transaction is done |
nobukuma | 0:003889bc474f | 622 | endpoint->CurrentState = Endpoint::Idle; |
nobukuma | 0:003889bc474f | 623 | break; |
nobukuma | 0:003889bc474f | 624 | } |
nobukuma | 0:003889bc474f | 625 | } |
nobukuma | 0:003889bc474f | 626 | |
nobukuma | 0:003889bc474f | 627 | // Complete, flag if we need a callback |
nobukuma | 0:003889bc474f | 628 | if (endpoint->Callback && endpoint->CurrentState == Endpoint::Idle) |
nobukuma | 0:003889bc474f | 629 | { |
nobukuma | 0:003889bc474f | 630 | endpoint->CurrentState = Endpoint::CallbackPending; |
nobukuma | 0:003889bc474f | 631 | _callbacksPending++; |
nobukuma | 0:003889bc474f | 632 | } |
nobukuma | 0:003889bc474f | 633 | } |
nobukuma | 0:003889bc474f | 634 | } |
nobukuma | 0:003889bc474f | 635 | |
nobukuma | 0:003889bc474f | 636 | // Hack to reset devices that don't want to connect |
nobukuma | 0:003889bc474f | 637 | int AddDevice(int hub, int port, bool isLowSpeed) |
nobukuma | 0:003889bc474f | 638 | { |
nobukuma | 0:003889bc474f | 639 | int device = AddDeviceCore(hub,port,isLowSpeed); |
nobukuma | 0:003889bc474f | 640 | if (device < 0) |
nobukuma | 0:003889bc474f | 641 | { |
nobukuma | 0:003889bc474f | 642 | LOG("========RETRY ADD DEVICE========\n"); // This will go for ever.. TODO power cycle root? |
nobukuma | 0:003889bc474f | 643 | Disconnect(hub,port); // Could not read descriptor at assigned address, reset this port and try again |
nobukuma | 0:003889bc474f | 644 | ResetPort(hub,port); // Cheap bluetooth dongles often need this on a hotplug |
nobukuma | 0:003889bc474f | 645 | return -1; |
nobukuma | 0:003889bc474f | 646 | } |
nobukuma | 0:003889bc474f | 647 | return device; |
nobukuma | 0:003889bc474f | 648 | } |
nobukuma | 0:003889bc474f | 649 | |
nobukuma | 0:003889bc474f | 650 | int AddDeviceCore(int hub, int port, bool isLowSpeed) |
nobukuma | 0:003889bc474f | 651 | { |
nobukuma | 0:003889bc474f | 652 | int lowSpeed = isLowSpeed ? 0x2000 : 0; |
nobukuma | 0:003889bc474f | 653 | DeviceDescriptor desc; |
nobukuma | 0:003889bc474f | 654 | EndpointZero.EndpointDescriptor.Control = (8 << 16) | lowSpeed; // MaxPacketSize == 8 |
nobukuma | 0:003889bc474f | 655 | int r = GetDescriptor(0,DESCRIPTOR_TYPE_DEVICE,0,(u8*)&desc,8); |
nobukuma | 0:003889bc474f | 656 | if (r < 0) |
nobukuma | 0:003889bc474f | 657 | { |
nobukuma | 0:003889bc474f | 658 | LOG("FAILED TO LOAD DESCRIPTOR FOR DEVICE 0\n"); |
nobukuma | 0:003889bc474f | 659 | return r; |
nobukuma | 0:003889bc474f | 660 | } |
nobukuma | 0:003889bc474f | 661 | |
nobukuma | 0:003889bc474f | 662 | EndpointZero.EndpointDescriptor.Control = (desc.bMaxPacketSize << 16) | lowSpeed; // Actual MaxPacketSize |
nobukuma | 0:003889bc474f | 663 | r = GetDescriptor(0,DESCRIPTOR_TYPE_DEVICE,0,(u8*)&desc,sizeof(desc)); |
nobukuma | 0:003889bc474f | 664 | if (r < 0) |
nobukuma | 0:003889bc474f | 665 | return r; |
nobukuma | 0:003889bc474f | 666 | |
nobukuma | 0:003889bc474f | 667 | LOG("\nClass %02X found %04X:%04X\n\n",desc.bDeviceClass,desc.idVendor,desc.idProduct); |
nobukuma | 0:003889bc474f | 668 | |
nobukuma | 0:003889bc474f | 669 | // Now assign the device an address, move off EndpointZero |
nobukuma | 0:003889bc474f | 670 | int device = 0; |
nobukuma | 0:003889bc474f | 671 | for (int i = 0; i < MAX_DEVICES; i++) |
nobukuma | 0:003889bc474f | 672 | { |
nobukuma | 0:003889bc474f | 673 | if (Devices[i].Port == 0) |
nobukuma | 0:003889bc474f | 674 | { |
nobukuma | 0:003889bc474f | 675 | device = i+1; |
nobukuma | 0:003889bc474f | 676 | break; |
nobukuma | 0:003889bc474f | 677 | } |
nobukuma | 0:003889bc474f | 678 | } |
nobukuma | 0:003889bc474f | 679 | if (!device) |
nobukuma | 0:003889bc474f | 680 | return ERR_DEVICE_NONE_LEFT; |
nobukuma | 0:003889bc474f | 681 | |
nobukuma | 0:003889bc474f | 682 | r = SetAddress(0,device); |
nobukuma | 0:003889bc474f | 683 | if (r) |
nobukuma | 0:003889bc474f | 684 | return r; |
nobukuma | 0:003889bc474f | 685 | DelayMS(2); |
nobukuma | 0:003889bc474f | 686 | |
nobukuma | 0:003889bc474f | 687 | // Now at a nonzero address, create control endpoint |
nobukuma | 0:003889bc474f | 688 | Device* dev = &Devices[device-1]; |
nobukuma | 0:003889bc474f | 689 | dev->Init(&desc,hub,port,device,lowSpeed); |
nobukuma | 0:003889bc474f | 690 | AddEndpoint(device,0,ENDPOINT_CONTROL,desc.bMaxPacketSize,0); |
nobukuma | 0:003889bc474f | 691 | _connectPending = 0; |
nobukuma | 0:003889bc474f | 692 | |
nobukuma | 0:003889bc474f | 693 | // Verify this all works |
nobukuma | 0:003889bc474f | 694 | r = GetDescriptor(device,DESCRIPTOR_TYPE_DEVICE,0,(u8*)&desc,sizeof(desc)); |
nobukuma | 0:003889bc474f | 695 | if (r < 0) |
nobukuma | 0:003889bc474f | 696 | return r; |
nobukuma | 0:003889bc474f | 697 | |
nobukuma | 0:003889bc474f | 698 | // Set to interface 0 by default |
nobukuma | 0:003889bc474f | 699 | // Calls LoadDevice if interface is found |
nobukuma | 0:003889bc474f | 700 | r = SetConfigurationAndInterface(device,1,0,&desc); |
nobukuma | 0:003889bc474f | 701 | |
nobukuma | 0:003889bc474f | 702 | if (desc.bDeviceClass == CLASS_HUB) |
nobukuma | 0:003889bc474f | 703 | InitHub(device); // Handle hubs in this code |
nobukuma | 0:003889bc474f | 704 | |
nobukuma | 0:003889bc474f | 705 | return device; |
nobukuma | 0:003889bc474f | 706 | } |
nobukuma | 0:003889bc474f | 707 | |
nobukuma | 0:003889bc474f | 708 | // Walk descriptors and create endpoints for a given device |
nobukuma | 0:003889bc474f | 709 | // TODO configuration !=1, alternate settings etc. |
nobukuma | 0:003889bc474f | 710 | int SetConfigurationAndInterface(int device, int configuration, int interfaceNumber, DeviceDescriptor* desc) |
nobukuma | 0:003889bc474f | 711 | { |
nobukuma | 0:003889bc474f | 712 | u8 buffer[255]; |
nobukuma | 0:003889bc474f | 713 | int err = GetDescriptor(device,DESCRIPTOR_TYPE_CONFIGURATION,0,buffer,sizeof(buffer)); |
nobukuma | 0:003889bc474f | 714 | if (err < 0) |
nobukuma | 0:003889bc474f | 715 | return err; |
nobukuma | 0:003889bc474f | 716 | |
nobukuma | 0:003889bc474f | 717 | err = SetConfiguration(device,configuration); |
nobukuma | 0:003889bc474f | 718 | if (err < 0) |
nobukuma | 0:003889bc474f | 719 | return err; |
nobukuma | 0:003889bc474f | 720 | |
nobukuma | 0:003889bc474f | 721 | // Add the endpoints for this interface |
nobukuma | 0:003889bc474f | 722 | int len = buffer[2] | (buffer[3] << 8); |
nobukuma | 0:003889bc474f | 723 | u8* d = buffer; |
nobukuma | 0:003889bc474f | 724 | u8* end = d + len; |
nobukuma | 0:003889bc474f | 725 | InterfaceDescriptor* found = 0; |
nobukuma | 0:003889bc474f | 726 | while (d < end) |
nobukuma | 0:003889bc474f | 727 | { |
nobukuma | 0:003889bc474f | 728 | if (d[1] == DESCRIPTOR_TYPE_INTERFACE) |
nobukuma | 0:003889bc474f | 729 | { |
nobukuma | 0:003889bc474f | 730 | InterfaceDescriptor* id = (InterfaceDescriptor*)d; |
nobukuma | 0:003889bc474f | 731 | if (id->bInterfaceNumber == interfaceNumber) |
nobukuma | 0:003889bc474f | 732 | { |
nobukuma | 0:003889bc474f | 733 | found = id; |
nobukuma | 0:003889bc474f | 734 | d += d[0]; |
nobukuma | 0:003889bc474f | 735 | while (d < end && d[1] != DESCRIPTOR_TYPE_INTERFACE) |
nobukuma | 0:003889bc474f | 736 | { |
nobukuma | 0:003889bc474f | 737 | switch (d[1]) |
nobukuma | 0:003889bc474f | 738 | { |
nobukuma | 0:003889bc474f | 739 | case DESCRIPTOR_TYPE_ENDPOINT: |
nobukuma | 0:003889bc474f | 740 | AddEndpoint(device,(EndpointDescriptor*)d); |
nobukuma | 0:003889bc474f | 741 | break; |
nobukuma | 0:003889bc474f | 742 | default: |
nobukuma | 0:003889bc474f | 743 | LOG("Skipping descriptor %02X (%d bytes)\n",d[1],d[0]); |
nobukuma | 0:003889bc474f | 744 | } |
nobukuma | 0:003889bc474f | 745 | d += d[0]; |
nobukuma | 0:003889bc474f | 746 | } |
nobukuma | 0:003889bc474f | 747 | } |
nobukuma | 0:003889bc474f | 748 | } |
nobukuma | 0:003889bc474f | 749 | d += d[0]; |
nobukuma | 0:003889bc474f | 750 | } |
nobukuma | 0:003889bc474f | 751 | |
nobukuma | 0:003889bc474f | 752 | if (!found) |
nobukuma | 0:003889bc474f | 753 | return ERR_INTERFACE_NOT_FOUND; |
nobukuma | 0:003889bc474f | 754 | OnLoadDevice(device,desc,found); |
nobukuma | 0:003889bc474f | 755 | return 0; |
nobukuma | 0:003889bc474f | 756 | } |
nobukuma | 0:003889bc474f | 757 | |
nobukuma | 0:003889bc474f | 758 | void Init() |
nobukuma | 0:003889bc474f | 759 | { |
nobukuma | 0:003889bc474f | 760 | LOG("USB INIT (Controller is %d bytes)\n",sizeof(*this)); |
nobukuma | 0:003889bc474f | 761 | memset(this,0,sizeof(HostController)); |
nobukuma | 0:003889bc474f | 762 | EndpointZero.CurrentState = Endpoint::NotQueued; |
nobukuma | 0:003889bc474f | 763 | HWInit(&CommunicationArea); |
nobukuma | 0:003889bc474f | 764 | DelayMS(10); |
nobukuma | 0:003889bc474f | 765 | } |
nobukuma | 0:003889bc474f | 766 | |
nobukuma | 0:003889bc474f | 767 | void ResetPort(int hub, int port) |
nobukuma | 0:003889bc474f | 768 | { |
nobukuma | 0:003889bc474f | 769 | LOG("ResetPort Hub:%d Port:%d\n",hub,port); |
nobukuma | 0:003889bc474f | 770 | _connectPending++; // Only reset/add 1 device at a time |
nobukuma | 0:003889bc474f | 771 | if (hub == 0) |
nobukuma | 0:003889bc474f | 772 | LPC_USB->HcRhPortStatus1 = PortResetStatus; // Reset Root Hub, port 1 |
nobukuma | 0:003889bc474f | 773 | else |
nobukuma | 0:003889bc474f | 774 | SetPortReset(hub,port); // or reset other hub |
nobukuma | 0:003889bc474f | 775 | } |
nobukuma | 0:003889bc474f | 776 | |
nobukuma | 0:003889bc474f | 777 | void Disconnect(int hub, int port) |
nobukuma | 0:003889bc474f | 778 | { |
nobukuma | 0:003889bc474f | 779 | LOG("Disconnect Hub:%d Port:%d\n",hub,port); // Mark a device for destruction |
nobukuma | 0:003889bc474f | 780 | for (int i = 0; i < MAX_DEVICES; i++) |
nobukuma | 0:003889bc474f | 781 | { |
nobukuma | 0:003889bc474f | 782 | Device* dev = Devices + i; |
nobukuma | 0:003889bc474f | 783 | if (dev->Port == port && dev->Hub == hub) |
nobukuma | 0:003889bc474f | 784 | { |
nobukuma | 0:003889bc474f | 785 | // Disconnect everything that is attached to this device if it is a hub |
nobukuma | 0:003889bc474f | 786 | for (int p = 0; p < dev->HubPortCount; p++) |
nobukuma | 0:003889bc474f | 787 | Disconnect(i+1,p+1); |
nobukuma | 0:003889bc474f | 788 | |
nobukuma | 0:003889bc474f | 789 | // Now release endpoints |
nobukuma | 0:003889bc474f | 790 | for (int j = 1; j < MAX_ENDPOINTS_PER_DEVICE*2; j += 2) |
nobukuma | 0:003889bc474f | 791 | { |
nobukuma | 0:003889bc474f | 792 | u8 endpointIndex = dev->_endpointMap[j]; |
nobukuma | 0:003889bc474f | 793 | if (endpointIndex != 0xFF) |
nobukuma | 0:003889bc474f | 794 | Release(Endpoints + endpointIndex); |
nobukuma | 0:003889bc474f | 795 | } |
nobukuma | 0:003889bc474f | 796 | dev->Port = 0; // Device is now free |
nobukuma | 0:003889bc474f | 797 | dev->Flags = 0; |
nobukuma | 0:003889bc474f | 798 | return; |
nobukuma | 0:003889bc474f | 799 | } |
nobukuma | 0:003889bc474f | 800 | } |
nobukuma | 0:003889bc474f | 801 | } |
nobukuma | 0:003889bc474f | 802 | |
nobukuma | 0:003889bc474f | 803 | // called after reset |
nobukuma | 0:003889bc474f | 804 | void Connect(int hub, int port, bool lowspeed) |
nobukuma | 0:003889bc474f | 805 | { |
nobukuma | 0:003889bc474f | 806 | LOG("Connect Hub:%d Port:%d %s\n",hub,port,lowspeed ? "slow" : "full"); |
nobukuma | 0:003889bc474f | 807 | AddDevice(hub,port,lowspeed); |
nobukuma | 0:003889bc474f | 808 | } |
nobukuma | 0:003889bc474f | 809 | |
nobukuma | 0:003889bc474f | 810 | // Called from interrupt |
nobukuma | 0:003889bc474f | 811 | void HubStatusChange(int hub, int port, u32 status) |
nobukuma | 0:003889bc474f | 812 | { |
nobukuma | 0:003889bc474f | 813 | LOG("HubStatusChange Hub:%d Port:%d %08X\n",hub,port,status); |
nobukuma | 0:003889bc474f | 814 | if (status & ConnectStatusChange) |
nobukuma | 0:003889bc474f | 815 | { |
nobukuma | 0:003889bc474f | 816 | if (status & CurrentConnectStatus) // Connecting |
nobukuma | 0:003889bc474f | 817 | ResetPort(hub,port); // Reset to initiate connect (state machine?) |
nobukuma | 0:003889bc474f | 818 | else |
nobukuma | 0:003889bc474f | 819 | Disconnect(hub,port); |
nobukuma | 0:003889bc474f | 820 | } |
nobukuma | 0:003889bc474f | 821 | |
nobukuma | 0:003889bc474f | 822 | if (status & PortResetStatusChange) |
nobukuma | 0:003889bc474f | 823 | { |
nobukuma | 0:003889bc474f | 824 | if (!(status & PortResetStatus)) |
nobukuma | 0:003889bc474f | 825 | { |
nobukuma | 0:003889bc474f | 826 | _connectCountdown = 200; // Schedule a connection in 200ms |
nobukuma | 0:003889bc474f | 827 | if (status & LowspeedDevice) |
nobukuma | 0:003889bc474f | 828 | port |= 0x80; |
nobukuma | 0:003889bc474f | 829 | _connectHub = hub; |
nobukuma | 0:003889bc474f | 830 | _connectPort = port; |
nobukuma | 0:003889bc474f | 831 | } |
nobukuma | 0:003889bc474f | 832 | } |
nobukuma | 0:003889bc474f | 833 | } |
nobukuma | 0:003889bc474f | 834 | |
nobukuma | 0:003889bc474f | 835 | #define HOST_CLK_EN (1<<0) |
nobukuma | 0:003889bc474f | 836 | #define PORTSEL_CLK_EN (1<<3) |
nobukuma | 0:003889bc474f | 837 | #define AHB_CLK_EN (1<<4) |
nobukuma | 0:003889bc474f | 838 | #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN) |
nobukuma | 0:003889bc474f | 839 | |
nobukuma | 0:003889bc474f | 840 | #define FRAMEINTERVAL (12000-1) // 1ms |
nobukuma | 0:003889bc474f | 841 | #define DEFAULT_FMINTERVAL ((((6 * (FRAMEINTERVAL - 210)) / 7) << 16) | FRAMEINTERVAL) |
nobukuma | 0:003889bc474f | 842 | |
nobukuma | 0:003889bc474f | 843 | void DelayMS(int ms) |
nobukuma | 0:003889bc474f | 844 | { |
nobukuma | 0:003889bc474f | 845 | u16 f = ms + CommunicationArea.FrameNumber; |
nobukuma | 0:003889bc474f | 846 | while (f != CommunicationArea.FrameNumber) |
nobukuma | 0:003889bc474f | 847 | ; |
nobukuma | 0:003889bc474f | 848 | } |
nobukuma | 0:003889bc474f | 849 | |
nobukuma | 0:003889bc474f | 850 | static void HWInit(HCCA* cca) |
nobukuma | 0:003889bc474f | 851 | { |
nobukuma | 0:003889bc474f | 852 | NVIC_DisableIRQ(USB_IRQn); |
nobukuma | 0:003889bc474f | 853 | |
nobukuma | 0:003889bc474f | 854 | // turn on power for USB |
nobukuma | 0:003889bc474f | 855 | LPC_SC->PCONP |= (1UL<<31); |
nobukuma | 0:003889bc474f | 856 | // Enable USB host clock, port selection and AHB clock |
nobukuma | 0:003889bc474f | 857 | LPC_USB->USBClkCtrl |= CLOCK_MASK; |
nobukuma | 0:003889bc474f | 858 | // Wait for clocks to become available |
nobukuma | 0:003889bc474f | 859 | while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK) |
nobukuma | 0:003889bc474f | 860 | ; |
nobukuma | 0:003889bc474f | 861 | |
nobukuma | 0:003889bc474f | 862 | // We are a Host |
nobukuma | 0:003889bc474f | 863 | LPC_USB->OTGStCtrl |= 1; |
nobukuma | 0:003889bc474f | 864 | LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN; // we don't need port selection clock until we do OTG |
nobukuma | 0:003889bc474f | 865 | |
nobukuma | 0:003889bc474f | 866 | // configure USB pins |
nobukuma | 0:003889bc474f | 867 | LPC_PINCON->PINSEL1 &= ~((3<<26)|(3<<28)); |
nobukuma | 0:003889bc474f | 868 | LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // USB D+/D- |
nobukuma | 0:003889bc474f | 869 | |
nobukuma | 0:003889bc474f | 870 | LPC_PINCON->PINSEL3 &= ~((3 << 6) | (3 << 22)); // USB_PPWR, USB_OVRCR |
nobukuma | 0:003889bc474f | 871 | LPC_PINCON->PINSEL3 |= ((2 << 6) | (2 << 22)); |
nobukuma | 0:003889bc474f | 872 | |
nobukuma | 0:003889bc474f | 873 | LPC_PINCON->PINSEL4 &= ~(3 << 18); // USB_CONNECT |
nobukuma | 0:003889bc474f | 874 | LPC_PINCON->PINSEL4 |= (1 << 18); |
nobukuma | 0:003889bc474f | 875 | |
nobukuma | 0:003889bc474f | 876 | // Reset OHCI block |
nobukuma | 0:003889bc474f | 877 | LPC_USB->HcControl = 0; |
nobukuma | 0:003889bc474f | 878 | LPC_USB->HcControlHeadED = 0; |
nobukuma | 0:003889bc474f | 879 | LPC_USB->HcBulkHeadED = 0; |
nobukuma | 0:003889bc474f | 880 | |
nobukuma | 0:003889bc474f | 881 | LPC_USB->HcCommandStatus = HostControllerReset; |
nobukuma | 0:003889bc474f | 882 | LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL; |
nobukuma | 0:003889bc474f | 883 | LPC_USB->HcPeriodicStart = FRAMEINTERVAL*90/100; |
nobukuma | 0:003889bc474f | 884 | |
nobukuma | 0:003889bc474f | 885 | LPC_USB->HcControl = (LPC_USB->HcControl & (~HostControllerFunctionalState)) | OperationalMask; |
nobukuma | 0:003889bc474f | 886 | LPC_USB->HcRhStatus = SetGlobalPower; |
nobukuma | 0:003889bc474f | 887 | |
nobukuma | 0:003889bc474f | 888 | LPC_USB->HcHCCA = (u32)cca; |
nobukuma | 0:003889bc474f | 889 | LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; |
nobukuma | 0:003889bc474f | 890 | LPC_USB->HcInterruptEnable = MasterInterruptEnable | WritebackDoneHead | RootHubStatusChange | FrameNumberOverflow; |
nobukuma | 0:003889bc474f | 891 | |
nobukuma | 0:003889bc474f | 892 | NVIC_SetPriority(USB_IRQn, 0); |
nobukuma | 0:003889bc474f | 893 | NVIC_EnableIRQ(USB_IRQn); |
nobukuma | 0:003889bc474f | 894 | while (cca->FrameNumber < 10) |
nobukuma | 0:003889bc474f | 895 | ; // 10ms delay before diving in |
nobukuma | 0:003889bc474f | 896 | } |
nobukuma | 0:003889bc474f | 897 | }; |
nobukuma | 0:003889bc474f | 898 | |
nobukuma | 0:003889bc474f | 899 | //==================================================================================== |
nobukuma | 0:003889bc474f | 900 | //==================================================================================== |
nobukuma | 0:003889bc474f | 901 | // Host controller instance and Interrupt handler |
nobukuma | 0:003889bc474f | 902 | |
nobukuma | 0:003889bc474f | 903 | static HostController _controller __attribute__((at(USB_RAM_BASE))); |
nobukuma | 0:003889bc474f | 904 | |
nobukuma | 0:003889bc474f | 905 | extern "C" void USB_IRQHandler(void) __irq; |
nobukuma | 0:003889bc474f | 906 | void USB_IRQHandler (void) __irq |
nobukuma | 0:003889bc474f | 907 | { |
nobukuma | 0:003889bc474f | 908 | u32 int_status = LPC_USB->HcInterruptStatus; |
nobukuma | 0:003889bc474f | 909 | |
nobukuma | 0:003889bc474f | 910 | if (int_status & RootHubStatusChange) // Root hub status change |
nobukuma | 0:003889bc474f | 911 | _controller._rootHubStatusChange++; // Just flag the controller, will be processed in USBLoop |
nobukuma | 0:003889bc474f | 912 | |
nobukuma | 0:003889bc474f | 913 | u32 head = 0; |
nobukuma | 0:003889bc474f | 914 | if (int_status & WritebackDoneHead) |
nobukuma | 0:003889bc474f | 915 | { |
nobukuma | 0:003889bc474f | 916 | head = _controller.CommunicationArea.DoneHead; // Writeback Done |
nobukuma | 0:003889bc474f | 917 | _controller.CommunicationArea.DoneHead = 0; |
nobukuma | 0:003889bc474f | 918 | } |
nobukuma | 0:003889bc474f | 919 | // LPC_USB->HcInterruptStatus = int_status; |
nobukuma | 0:003889bc474f | 920 | |
nobukuma | 0:003889bc474f | 921 | if (head){ |
nobukuma | 0:003889bc474f | 922 | _controller.ProcessDoneQueue(head & ~1U); // TODO - low bit can be set BUGBUG |
nobukuma | 0:003889bc474f | 923 | if (head & 1) { |
nobukuma | 0:003889bc474f | 924 | int othercause = int_status ^ WritebackDoneHead; |
nobukuma | 0:003889bc474f | 925 | printf("HcInterruptStatus = %08X\n", int_status); |
nobukuma | 0:003889bc474f | 926 | } |
nobukuma | 0:003889bc474f | 927 | } |
nobukuma | 0:003889bc474f | 928 | else |
nobukuma | 0:003889bc474f | 929 | int othercause = int_status; |
nobukuma | 0:003889bc474f | 930 | LPC_USB->HcInterruptStatus = int_status; |
nobukuma | 0:003889bc474f | 931 | //See page 435 of OHCI Specification |
nobukuma | 0:003889bc474f | 932 | //if the LSb=1 int_status should be checked for other causes |
nobukuma | 0:003889bc474f | 933 | //if the head=0 there are no TDs on the DoneQueue and int_status should be checked to see what caused this interrupt |
nobukuma | 0:003889bc474f | 934 | } |
nobukuma | 0:003889bc474f | 935 | |
nobukuma | 0:003889bc474f | 936 | //==================================================================================== |
nobukuma | 0:003889bc474f | 937 | //==================================================================================== |
nobukuma | 0:003889bc474f | 938 | // API Methods |
nobukuma | 0:003889bc474f | 939 | |
nobukuma | 0:003889bc474f | 940 | void USBInit() |
nobukuma | 0:003889bc474f | 941 | { |
nobukuma | 0:003889bc474f | 942 | return _controller.Init(); |
nobukuma | 0:003889bc474f | 943 | } |
nobukuma | 0:003889bc474f | 944 | |
nobukuma | 0:003889bc474f | 945 | void USBLoop() |
nobukuma | 0:003889bc474f | 946 | { |
nobukuma | 0:003889bc474f | 947 | return _controller.Loop(); |
nobukuma | 0:003889bc474f | 948 | } |
nobukuma | 0:003889bc474f | 949 | |
nobukuma | 0:003889bc474f | 950 | u8* USBGetBuffer(u32* len) |
nobukuma | 0:003889bc474f | 951 | { |
nobukuma | 0:003889bc474f | 952 | *len = USB_RAM_SIZE - sizeof(HostController); |
nobukuma | 0:003889bc474f | 953 | return _controller.SRAM; |
nobukuma | 0:003889bc474f | 954 | } |
nobukuma | 0:003889bc474f | 955 | |
nobukuma | 0:003889bc474f | 956 | static Setup* GetSetup(int device) |
nobukuma | 0:003889bc474f | 957 | { |
nobukuma | 0:003889bc474f | 958 | if (device == 0) |
nobukuma | 0:003889bc474f | 959 | return &_controller._setupZero; |
nobukuma | 0:003889bc474f | 960 | |
nobukuma | 0:003889bc474f | 961 | if (device < 1 || device > MAX_DEVICES) |
nobukuma | 0:003889bc474f | 962 | return 0; |
nobukuma | 0:003889bc474f | 963 | return &_controller.Devices[device-1].SetupBuffer; |
nobukuma | 0:003889bc474f | 964 | } |
nobukuma | 0:003889bc474f | 965 | |
nobukuma | 0:003889bc474f | 966 | // Loop until IO on endpoint is complete |
nobukuma | 0:003889bc474f | 967 | static int WaitIODone(Endpoint* endpoint) |
nobukuma | 0:003889bc474f | 968 | { |
nobukuma | 0:003889bc474f | 969 | if (endpoint->CurrentState == Endpoint::NotQueued) |
nobukuma | 0:003889bc474f | 970 | return 0; |
nobukuma | 0:003889bc474f | 971 | while (endpoint->CurrentState != Endpoint::Idle) |
nobukuma | 0:003889bc474f | 972 | USBLoop(); // May generate callbacks, mount or unmount devices etc |
nobukuma | 0:003889bc474f | 973 | int status = endpoint->Status(); |
nobukuma | 0:003889bc474f | 974 | if (status == 0) |
nobukuma | 0:003889bc474f | 975 | return endpoint->Length; |
nobukuma | 0:003889bc474f | 976 | return -status; |
nobukuma | 0:003889bc474f | 977 | } |
nobukuma | 0:003889bc474f | 978 | |
nobukuma | 0:003889bc474f | 979 | int USBTransfer(int device, int ep, u8 flags, u8* data, int length, USBCallback callback, void* userData) |
nobukuma | 0:003889bc474f | 980 | { |
nobukuma | 0:003889bc474f | 981 | Endpoint* endpoint = _controller.GetEndpoint(device,ep); |
nobukuma | 0:003889bc474f | 982 | if (!endpoint) |
nobukuma | 0:003889bc474f | 983 | return ERR_ENDPOINT_NOT_FOUND; |
nobukuma | 0:003889bc474f | 984 | |
nobukuma | 0:003889bc474f | 985 | WaitIODone(endpoint); |
nobukuma | 0:003889bc474f | 986 | endpoint->Flags = flags; |
nobukuma | 0:003889bc474f | 987 | endpoint->Data = data; |
nobukuma | 0:003889bc474f | 988 | endpoint->Length = length; |
nobukuma | 0:003889bc474f | 989 | endpoint->Callback = callback; |
nobukuma | 0:003889bc474f | 990 | endpoint->UserData = userData; |
nobukuma | 0:003889bc474f | 991 | if (ep == 0) |
nobukuma | 0:003889bc474f | 992 | _controller.Transfer(endpoint,TOKEN_SETUP,(u8*)GetSetup(device),8,Endpoint::SetupQueued); |
nobukuma | 0:003889bc474f | 993 | else |
nobukuma | 0:003889bc474f | 994 | _controller.Transfer(endpoint,flags & 0x80 ? TOKEN_IN : TOKEN_OUT,data,length,Endpoint::DataQueued); |
nobukuma | 0:003889bc474f | 995 | if (callback) |
nobukuma | 0:003889bc474f | 996 | return IO_PENDING; |
nobukuma | 0:003889bc474f | 997 | return WaitIODone(endpoint); |
nobukuma | 0:003889bc474f | 998 | } |
nobukuma | 0:003889bc474f | 999 | |
nobukuma | 0:003889bc474f | 1000 | int USBControlTransfer(int device, int request_type, int request, int value, int index, u8* data, int length, USBCallback callback, void * userData) |
nobukuma | 0:003889bc474f | 1001 | { |
nobukuma | 0:003889bc474f | 1002 | Setup* setup = GetSetup(device); |
nobukuma | 0:003889bc474f | 1003 | if (!setup) |
nobukuma | 0:003889bc474f | 1004 | return ERR_DEVICE_NOT_FOUND; |
nobukuma | 0:003889bc474f | 1005 | |
nobukuma | 0:003889bc474f | 1006 | // Async control calls may overwrite setup buffer of previous call, so we need to wait before setting up next call |
nobukuma | 0:003889bc474f | 1007 | WaitIODone(_controller.GetEndpoint(device,0)); |
nobukuma | 0:003889bc474f | 1008 | |
nobukuma | 0:003889bc474f | 1009 | setup->bm_request_type = request_type; |
nobukuma | 0:003889bc474f | 1010 | setup->b_request = request; |
nobukuma | 0:003889bc474f | 1011 | setup->w_value = value; |
nobukuma | 0:003889bc474f | 1012 | setup->w_index = index; |
nobukuma | 0:003889bc474f | 1013 | setup->w_length = length; |
nobukuma | 0:003889bc474f | 1014 | return USBTransfer(device,0,request_type & DEVICE_TO_HOST,data,length,callback,userData); |
nobukuma | 0:003889bc474f | 1015 | } |
nobukuma | 0:003889bc474f | 1016 | |
nobukuma | 0:003889bc474f | 1017 | int USBInterruptTransfer(int device, int ep, u8* data, int length, USBCallback callback, void* userData) |
nobukuma | 0:003889bc474f | 1018 | { |
nobukuma | 0:003889bc474f | 1019 | return USBTransfer(device,ep,(ep & 0x80) | ENDPOINT_INTERRUPT,data,length,callback,userData); |
nobukuma | 0:003889bc474f | 1020 | } |
nobukuma | 0:003889bc474f | 1021 | |
nobukuma | 0:003889bc474f | 1022 | int USBBulkTransfer(int device, int ep, u8* data, int length, USBCallback callback, void* userData) |
nobukuma | 0:003889bc474f | 1023 | { |
nobukuma | 0:003889bc474f | 1024 | return USBTransfer(device,ep,(ep & 0x80) | ENDPOINT_BULK,data,length,callback,userData); |
nobukuma | 0:003889bc474f | 1025 | } |
nobukuma | 0:003889bc474f | 1026 | |
nobukuma | 0:003889bc474f | 1027 | int GetDescriptor(int device, int descType,int descIndex, u8* data, int length) |
nobukuma | 0:003889bc474f | 1028 | { |
nobukuma | 0:003889bc474f | 1029 | return USBControlTransfer(device,DEVICE_TO_HOST | RECIPIENT_DEVICE, GET_DESCRIPTOR,(descType << 8)|(descIndex), 0, data, length, 0); |
nobukuma | 0:003889bc474f | 1030 | } |
nobukuma | 0:003889bc474f | 1031 | |
nobukuma | 0:003889bc474f | 1032 | int GetString(int device, int index, char* dst, int length) |
nobukuma | 0:003889bc474f | 1033 | { |
nobukuma | 0:003889bc474f | 1034 | u8 buffer[255]; |
nobukuma | 0:003889bc474f | 1035 | int le = GetDescriptor(device,DESCRIPTOR_TYPE_STRING,index,buffer,sizeof(buffer)); |
nobukuma | 0:003889bc474f | 1036 | if (le < 0) |
nobukuma | 0:003889bc474f | 1037 | return le; |
nobukuma | 0:003889bc474f | 1038 | if (length < 1) |
nobukuma | 0:003889bc474f | 1039 | return -1; |
nobukuma | 0:003889bc474f | 1040 | length <<= 1; |
nobukuma | 0:003889bc474f | 1041 | if (le > length) |
nobukuma | 0:003889bc474f | 1042 | le = length; |
nobukuma | 0:003889bc474f | 1043 | for (int j = 2; j < le; j += 2) |
nobukuma | 0:003889bc474f | 1044 | *dst++ = buffer[j]; |
nobukuma | 0:003889bc474f | 1045 | *dst = 0; |
nobukuma | 0:003889bc474f | 1046 | return (le>>1)-1; |
nobukuma | 0:003889bc474f | 1047 | } |
nobukuma | 0:003889bc474f | 1048 | |
nobukuma | 0:003889bc474f | 1049 | int SetAddress(int device, int new_addr) |
nobukuma | 0:003889bc474f | 1050 | { |
nobukuma | 0:003889bc474f | 1051 | return USBControlTransfer(device,HOST_TO_DEVICE | RECIPIENT_DEVICE, SET_ADDRESS, new_addr, 0, 0, 0, 0); |
nobukuma | 0:003889bc474f | 1052 | } |
nobukuma | 0:003889bc474f | 1053 | |
nobukuma | 0:003889bc474f | 1054 | int SetConfiguration(int device, int configNum) |
nobukuma | 0:003889bc474f | 1055 | { |
nobukuma | 0:003889bc474f | 1056 | return USBControlTransfer(device,HOST_TO_DEVICE | RECIPIENT_DEVICE, SET_CONFIGURATION, configNum, 0, 0, 0, 0); |
nobukuma | 0:003889bc474f | 1057 | } |
nobukuma | 0:003889bc474f | 1058 | |
nobukuma | 0:003889bc474f | 1059 | int SetInterface(int device, int ifNum, int altNum) |
nobukuma | 0:003889bc474f | 1060 | { |
nobukuma | 0:003889bc474f | 1061 | return USBControlTransfer(device,HOST_TO_DEVICE | RECIPIENT_INTERFACE, SET_INTERFACE, altNum, ifNum, 0, 0, 0); |
nobukuma | 0:003889bc474f | 1062 | } |
nobukuma | 0:003889bc474f | 1063 | |
nobukuma | 0:003889bc474f | 1064 | // HUB stuff |
nobukuma | 0:003889bc474f | 1065 | int SetPortFeature(int device, int feature, int index) |
nobukuma | 0:003889bc474f | 1066 | { |
nobukuma | 0:003889bc474f | 1067 | return USBControlTransfer(device,HOST_TO_DEVICE | REQUEST_TYPE_CLASS | RECIPIENT_OTHER,SET_FEATURE,feature,index,0,0); |
nobukuma | 0:003889bc474f | 1068 | } |
nobukuma | 0:003889bc474f | 1069 | |
nobukuma | 0:003889bc474f | 1070 | int ClearPortFeature(int device, int feature, int index) |
nobukuma | 0:003889bc474f | 1071 | { |
nobukuma | 0:003889bc474f | 1072 | return USBControlTransfer(device,HOST_TO_DEVICE | REQUEST_TYPE_CLASS | RECIPIENT_OTHER,CLEAR_FEATURE,feature,index,0,0); |
nobukuma | 0:003889bc474f | 1073 | } |
nobukuma | 0:003889bc474f | 1074 | |
nobukuma | 0:003889bc474f | 1075 | int SetPortPower(int device, int port) |
nobukuma | 0:003889bc474f | 1076 | { |
nobukuma | 0:003889bc474f | 1077 | int r = SetPortFeature(device,PORT_POWER,port); |
nobukuma | 0:003889bc474f | 1078 | _controller.DelayMS(20); // 80ms to turn on a hubs power... DESCRIPTOR? todo |
nobukuma | 0:003889bc474f | 1079 | return r; |
nobukuma | 0:003889bc474f | 1080 | } |
nobukuma | 0:003889bc474f | 1081 | |
nobukuma | 0:003889bc474f | 1082 | int SetPortReset(int device, int port) |
nobukuma | 0:003889bc474f | 1083 | { |
nobukuma | 0:003889bc474f | 1084 | return SetPortFeature(device,PORT_RESET,port); |
nobukuma | 0:003889bc474f | 1085 | } |
nobukuma | 0:003889bc474f | 1086 | |
nobukuma | 0:003889bc474f | 1087 | int GetPortStatus(int device, int port, u32* status) |
nobukuma | 0:003889bc474f | 1088 | { |
nobukuma | 0:003889bc474f | 1089 | return USBControlTransfer(device,DEVICE_TO_HOST | REQUEST_TYPE_CLASS | RECIPIENT_OTHER,GET_STATUS,0,port,(u8*)status,4); |
nobukuma | 0:003889bc474f | 1090 | } |