MAX3421E-based USB Host Shield Library
Dependents: UsbHostMAX3421E_Hello
Usb.cpp@1:2263e77400e9, 2020-07-13 (annotated)
- Committer:
- hudakz
- Date:
- Mon Jul 13 07:03:06 2020 +0000
- Revision:
- 1:2263e77400e9
- Parent:
- 0:84353c479782
MAX3421E-based USB Host Shield Library
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
hudakz | 0:84353c479782 | 1 | /* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. |
hudakz | 0:84353c479782 | 2 | |
hudakz | 0:84353c479782 | 3 | This software may be distributed and modified under the terms of the GNU |
hudakz | 0:84353c479782 | 4 | General Public License version 2 (GPL2) as published by the Free Software |
hudakz | 0:84353c479782 | 5 | Foundation and appearing in the file GPL2.TXT included in the packaging of |
hudakz | 0:84353c479782 | 6 | this file. Please note that GPL2 Section 2[b] requires that all works based |
hudakz | 0:84353c479782 | 7 | on this software must also be made publicly available under the terms of |
hudakz | 0:84353c479782 | 8 | the GPL2 ("Copyleft"). |
hudakz | 0:84353c479782 | 9 | |
hudakz | 0:84353c479782 | 10 | Contact information |
hudakz | 0:84353c479782 | 11 | ------------------- |
hudakz | 0:84353c479782 | 12 | |
hudakz | 0:84353c479782 | 13 | Circuits At Home, LTD |
hudakz | 0:84353c479782 | 14 | Web : http://www.circuitsathome.com |
hudakz | 0:84353c479782 | 15 | e-mail : support@circuitsathome.com |
hudakz | 0:84353c479782 | 16 | */ |
hudakz | 0:84353c479782 | 17 | /* USB functions */ |
hudakz | 0:84353c479782 | 18 | #include "Usb.h" |
hudakz | 0:84353c479782 | 19 | |
hudakz | 0:84353c479782 | 20 | static uint8_t usb_error = 0; |
hudakz | 0:84353c479782 | 21 | static uint8_t usb_task_state; |
hudakz | 0:84353c479782 | 22 | |
hudakz | 0:84353c479782 | 23 | /* constructor */ |
hudakz | 1:2263e77400e9 | 24 | Usb::Usb(PinName mosi, PinName miso, PinName sck, PinName ss, PinName intr) : |
hudakz | 0:84353c479782 | 25 | MAX3421E(mosi, miso, sck, ss, intr), |
hudakz | 0:84353c479782 | 26 | bmHubPre(0) |
hudakz | 0:84353c479782 | 27 | { |
hudakz | 0:84353c479782 | 28 | usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; //set up state machine |
hudakz | 0:84353c479782 | 29 | //init(); |
hudakz | 0:84353c479782 | 30 | } |
hudakz | 0:84353c479782 | 31 | |
hudakz | 0:84353c479782 | 32 | /* Initialize data structures */ |
hudakz | 1:2263e77400e9 | 33 | int8_t Usb::init() |
hudakz | 0:84353c479782 | 34 | { |
hudakz | 0:84353c479782 | 35 | //devConfigIndex = 0; |
hudakz | 0:84353c479782 | 36 | bmHubPre = 0; |
hudakz | 0:84353c479782 | 37 | return MAX3421E::init(); |
hudakz | 0:84353c479782 | 38 | } |
hudakz | 0:84353c479782 | 39 | |
hudakz | 0:84353c479782 | 40 | /** |
hudakz | 0:84353c479782 | 41 | * @brief |
hudakz | 0:84353c479782 | 42 | * @note |
hudakz | 0:84353c479782 | 43 | * @param |
hudakz | 0:84353c479782 | 44 | * @retval |
hudakz | 0:84353c479782 | 45 | */ |
hudakz | 1:2263e77400e9 | 46 | uint8_t Usb::getUsbTaskState(void) |
hudakz | 0:84353c479782 | 47 | { |
hudakz | 0:84353c479782 | 48 | return(usb_task_state); |
hudakz | 0:84353c479782 | 49 | } |
hudakz | 0:84353c479782 | 50 | |
hudakz | 0:84353c479782 | 51 | /** |
hudakz | 0:84353c479782 | 52 | * @brief |
hudakz | 0:84353c479782 | 53 | * @note |
hudakz | 0:84353c479782 | 54 | * @param |
hudakz | 0:84353c479782 | 55 | * @retval |
hudakz | 0:84353c479782 | 56 | */ |
hudakz | 1:2263e77400e9 | 57 | void Usb::setUsbTaskState(uint8_t state) |
hudakz | 0:84353c479782 | 58 | { |
hudakz | 0:84353c479782 | 59 | usb_task_state = state; |
hudakz | 0:84353c479782 | 60 | } |
hudakz | 0:84353c479782 | 61 | |
hudakz | 0:84353c479782 | 62 | /** |
hudakz | 0:84353c479782 | 63 | * @brief |
hudakz | 0:84353c479782 | 64 | * @note |
hudakz | 0:84353c479782 | 65 | * @param |
hudakz | 0:84353c479782 | 66 | * @retval |
hudakz | 0:84353c479782 | 67 | */ |
hudakz | 1:2263e77400e9 | 68 | EpInfo* Usb::getEpInfoEntry(uint8_t addr, uint8_t ep) |
hudakz | 0:84353c479782 | 69 | { |
hudakz | 0:84353c479782 | 70 | UsbDevice* p = addrPool.GetUsbDevicePtr(addr); |
hudakz | 0:84353c479782 | 71 | |
hudakz | 0:84353c479782 | 72 | if (!p || !p->epinfo) |
hudakz | 0:84353c479782 | 73 | return NULL; |
hudakz | 0:84353c479782 | 74 | |
hudakz | 0:84353c479782 | 75 | EpInfo* pep = p->epinfo; |
hudakz | 0:84353c479782 | 76 | |
hudakz | 0:84353c479782 | 77 | for (uint8_t i = 0; i < p->epcount; i++) { |
hudakz | 0:84353c479782 | 78 | if ((pep)->epAddr == ep) |
hudakz | 0:84353c479782 | 79 | return pep; |
hudakz | 0:84353c479782 | 80 | |
hudakz | 0:84353c479782 | 81 | pep++; |
hudakz | 0:84353c479782 | 82 | } |
hudakz | 0:84353c479782 | 83 | |
hudakz | 0:84353c479782 | 84 | return NULL; |
hudakz | 0:84353c479782 | 85 | } |
hudakz | 0:84353c479782 | 86 | |
hudakz | 0:84353c479782 | 87 | /* set device table entry */ |
hudakz | 0:84353c479782 | 88 | |
hudakz | 0:84353c479782 | 89 | /* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */ |
hudakz | 1:2263e77400e9 | 90 | uint8_t Usb::setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr) |
hudakz | 0:84353c479782 | 91 | { |
hudakz | 0:84353c479782 | 92 | if (!eprecord_ptr) |
hudakz | 0:84353c479782 | 93 | return USB_ERROR_INVALID_ARGUMENT; |
hudakz | 0:84353c479782 | 94 | |
hudakz | 0:84353c479782 | 95 | UsbDevice* p = addrPool.GetUsbDevicePtr(addr); |
hudakz | 0:84353c479782 | 96 | |
hudakz | 0:84353c479782 | 97 | if (!p) |
hudakz | 0:84353c479782 | 98 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; |
hudakz | 0:84353c479782 | 99 | |
hudakz | 0:84353c479782 | 100 | p->address.devAddress = addr; |
hudakz | 0:84353c479782 | 101 | p->epinfo = eprecord_ptr; |
hudakz | 0:84353c479782 | 102 | p->epcount = epcount; |
hudakz | 0:84353c479782 | 103 | |
hudakz | 0:84353c479782 | 104 | return 0; |
hudakz | 0:84353c479782 | 105 | } |
hudakz | 0:84353c479782 | 106 | |
hudakz | 0:84353c479782 | 107 | /** |
hudakz | 0:84353c479782 | 108 | * @brief |
hudakz | 0:84353c479782 | 109 | * @note |
hudakz | 0:84353c479782 | 110 | * @param |
hudakz | 0:84353c479782 | 111 | * @retval |
hudakz | 0:84353c479782 | 112 | */ |
hudakz | 1:2263e77400e9 | 113 | uint8_t Usb::setAddress(uint8_t addr, uint8_t ep, EpInfo ** ppep, uint16_t* nak_limit) |
hudakz | 0:84353c479782 | 114 | { |
hudakz | 0:84353c479782 | 115 | UsbDevice* p = addrPool.GetUsbDevicePtr(addr); |
hudakz | 0:84353c479782 | 116 | |
hudakz | 0:84353c479782 | 117 | if (!p) |
hudakz | 0:84353c479782 | 118 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; |
hudakz | 0:84353c479782 | 119 | |
hudakz | 0:84353c479782 | 120 | if (!p->epinfo) |
hudakz | 0:84353c479782 | 121 | return USB_ERROR_EPINFO_IS_NULL; |
hudakz | 0:84353c479782 | 122 | |
hudakz | 0:84353c479782 | 123 | *ppep = getEpInfoEntry(addr, ep); |
hudakz | 0:84353c479782 | 124 | |
hudakz | 0:84353c479782 | 125 | if (!*ppep) |
hudakz | 0:84353c479782 | 126 | return USB_ERROR_EP_NOT_FOUND_IN_TBL; |
hudakz | 0:84353c479782 | 127 | |
hudakz | 0:84353c479782 | 128 | *nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower)); |
hudakz | 0:84353c479782 | 129 | (*nak_limit)--; |
hudakz | 0:84353c479782 | 130 | |
hudakz | 0:84353c479782 | 131 | /* |
hudakz | 0:84353c479782 | 132 | USBTRACE2("\r\nAddress: ", addr); |
hudakz | 0:84353c479782 | 133 | USBTRACE2(" EP: ", ep); |
hudakz | 0:84353c479782 | 134 | USBTRACE2(" NAK Power: ",(*ppep)->bmNakPower); |
hudakz | 0:84353c479782 | 135 | USBTRACE2(" NAK Limit: ", nak_limit); |
hudakz | 0:84353c479782 | 136 | USBTRACE("\r\n"); |
hudakz | 0:84353c479782 | 137 | */ |
hudakz | 0:84353c479782 | 138 | regWr(rPERADDR, addr); //set peripheral address |
hudakz | 0:84353c479782 | 139 | uint8_t mode = regRd(rMODE); |
hudakz | 0:84353c479782 | 140 | |
hudakz | 0:84353c479782 | 141 | //Serial.print("\r\nMode: "); |
hudakz | 0:84353c479782 | 142 | |
hudakz | 0:84353c479782 | 143 | //Serial.println( mode, HEX); |
hudakz | 0:84353c479782 | 144 | //Serial.print("\r\nLS: "); |
hudakz | 0:84353c479782 | 145 | //Serial.println(p->lowspeed, HEX); |
hudakz | 0:84353c479782 | 146 | // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise |
hudakz | 0:84353c479782 | 147 | regWr(rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode &~(bmHUBPRE | bmLOWSPEED)); |
hudakz | 0:84353c479782 | 148 | |
hudakz | 0:84353c479782 | 149 | return 0; |
hudakz | 0:84353c479782 | 150 | } |
hudakz | 0:84353c479782 | 151 | |
hudakz | 0:84353c479782 | 152 | /* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */ |
hudakz | 0:84353c479782 | 153 | /* depending on request. Actual requests are defined as inlines */ |
hudakz | 0:84353c479782 | 154 | /* return codes: */ |
hudakz | 0:84353c479782 | 155 | /* 00 = success */ |
hudakz | 0:84353c479782 | 156 | |
hudakz | 0:84353c479782 | 157 | /* 01-0f = non-zero HRSLT */ |
hudakz | 1:2263e77400e9 | 158 | uint8_t Usb::ctrlReq |
hudakz | 0:84353c479782 | 159 | ( |
hudakz | 0:84353c479782 | 160 | uint8_t addr, |
hudakz | 0:84353c479782 | 161 | uint8_t ep, |
hudakz | 0:84353c479782 | 162 | uint8_t bmReqType, |
hudakz | 0:84353c479782 | 163 | uint8_t bRequest, |
hudakz | 0:84353c479782 | 164 | uint8_t wValLo, |
hudakz | 0:84353c479782 | 165 | uint8_t wValHi, |
hudakz | 0:84353c479782 | 166 | uint16_t wInd, |
hudakz | 0:84353c479782 | 167 | uint16_t total, |
hudakz | 0:84353c479782 | 168 | uint16_t nbytes, |
hudakz | 0:84353c479782 | 169 | uint8_t* dataptr, |
hudakz | 0:84353c479782 | 170 | USBReadParser* p |
hudakz | 0:84353c479782 | 171 | ) |
hudakz | 0:84353c479782 | 172 | { |
hudakz | 0:84353c479782 | 173 | bool direction = false; //request direction, IN or OUT |
hudakz | 0:84353c479782 | 174 | uint8_t rcode; |
hudakz | 0:84353c479782 | 175 | SETUP_PKT setup_pkt; |
hudakz | 0:84353c479782 | 176 | |
hudakz | 0:84353c479782 | 177 | EpInfo* pep = NULL; |
hudakz | 0:84353c479782 | 178 | uint16_t nak_limit = 0; |
hudakz | 0:84353c479782 | 179 | |
hudakz | 0:84353c479782 | 180 | rcode = setAddress(addr, ep, &pep, &nak_limit); |
hudakz | 0:84353c479782 | 181 | |
hudakz | 0:84353c479782 | 182 | if (rcode) |
hudakz | 0:84353c479782 | 183 | return rcode; |
hudakz | 0:84353c479782 | 184 | |
hudakz | 0:84353c479782 | 185 | direction = ((bmReqType & 0x80) > 0); |
hudakz | 0:84353c479782 | 186 | |
hudakz | 0:84353c479782 | 187 | /* fill in setup packet */ |
hudakz | 0:84353c479782 | 188 | setup_pkt.ReqType_u.bmRequestType = bmReqType; |
hudakz | 0:84353c479782 | 189 | setup_pkt.bRequest = bRequest; |
hudakz | 0:84353c479782 | 190 | setup_pkt.wVal_u.wValueLo = wValLo; |
hudakz | 0:84353c479782 | 191 | setup_pkt.wVal_u.wValueHi = wValHi; |
hudakz | 0:84353c479782 | 192 | setup_pkt.wIndex = wInd; |
hudakz | 0:84353c479782 | 193 | setup_pkt.wLength = total; |
hudakz | 0:84353c479782 | 194 | |
hudakz | 0:84353c479782 | 195 | bytesWr(rSUDFIFO, 8, (uint8_t*) &setup_pkt); //transfer to setup packet FIFO |
hudakz | 0:84353c479782 | 196 | rcode = dispatchPkt(tokSETUP, ep, nak_limit); //dispatch packet |
hudakz | 0:84353c479782 | 197 | if (rcode) //return HRSLT if not zero |
hudakz | 0:84353c479782 | 198 | return(rcode); |
hudakz | 0:84353c479782 | 199 | |
hudakz | 0:84353c479782 | 200 | if (dataptr != NULL) { |
hudakz | 0:84353c479782 | 201 | |
hudakz | 0:84353c479782 | 202 | //data stage, if present |
hudakz | 0:84353c479782 | 203 | if (direction) { |
hudakz | 0:84353c479782 | 204 | |
hudakz | 0:84353c479782 | 205 | //IN transfer |
hudakz | 0:84353c479782 | 206 | uint16_t left = total; |
hudakz | 0:84353c479782 | 207 | |
hudakz | 0:84353c479782 | 208 | pep->bmRcvToggle = 1; //bmRCVTOG1; |
hudakz | 0:84353c479782 | 209 | while (left) |
hudakz | 0:84353c479782 | 210 | { |
hudakz | 0:84353c479782 | 211 | // Bytes read into buffer |
hudakz | 0:84353c479782 | 212 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 213 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 214 | #endif |
hudakz | 0:84353c479782 | 215 | |
hudakz | 0:84353c479782 | 216 | uint16_t read = nbytes; |
hudakz | 0:84353c479782 | 217 | //uint16_t read = (left<nbytes) ? left : nbytes; |
hudakz | 0:84353c479782 | 218 | |
hudakz | 0:84353c479782 | 219 | rcode = inTransfer(pep, nak_limit, &read, dataptr); |
hudakz | 0:84353c479782 | 220 | if (rcode == hrTOGERR) { |
hudakz | 0:84353c479782 | 221 | |
hudakz | 0:84353c479782 | 222 | // yes, we flip it wrong here so that next time it is actually correct! |
hudakz | 0:84353c479782 | 223 | pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; |
hudakz | 0:84353c479782 | 224 | continue; |
hudakz | 0:84353c479782 | 225 | } |
hudakz | 0:84353c479782 | 226 | |
hudakz | 0:84353c479782 | 227 | if (rcode) |
hudakz | 0:84353c479782 | 228 | return rcode; |
hudakz | 0:84353c479782 | 229 | |
hudakz | 0:84353c479782 | 230 | // Invoke callback function if inTransfer completed successfully and callback function pointer is specified |
hudakz | 0:84353c479782 | 231 | if (!rcode && p) |
hudakz | 0:84353c479782 | 232 | ((USBReadParser*)p)->Parse(read, dataptr, total - left); |
hudakz | 0:84353c479782 | 233 | |
hudakz | 0:84353c479782 | 234 | left -= read; |
hudakz | 0:84353c479782 | 235 | |
hudakz | 0:84353c479782 | 236 | if (read < nbytes) |
hudakz | 0:84353c479782 | 237 | break; |
hudakz | 0:84353c479782 | 238 | } |
hudakz | 0:84353c479782 | 239 | } |
hudakz | 0:84353c479782 | 240 | else { |
hudakz | 0:84353c479782 | 241 | |
hudakz | 0:84353c479782 | 242 | //OUT transfer |
hudakz | 0:84353c479782 | 243 | pep->bmSndToggle = 1; //bmSNDTOG1; |
hudakz | 0:84353c479782 | 244 | rcode = outTransfer(pep, nak_limit, nbytes, dataptr); |
hudakz | 0:84353c479782 | 245 | } |
hudakz | 0:84353c479782 | 246 | |
hudakz | 0:84353c479782 | 247 | if (rcode) //return error |
hudakz | 0:84353c479782 | 248 | return(rcode); |
hudakz | 0:84353c479782 | 249 | } |
hudakz | 0:84353c479782 | 250 | |
hudakz | 0:84353c479782 | 251 | // Status stage |
hudakz | 0:84353c479782 | 252 | return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); //GET if direction |
hudakz | 0:84353c479782 | 253 | } |
hudakz | 0:84353c479782 | 254 | |
hudakz | 0:84353c479782 | 255 | /* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ |
hudakz | 0:84353c479782 | 256 | /* Keep sending INs and writes data to memory area pointed by 'data' */ |
hudakz | 0:84353c479782 | 257 | |
hudakz | 0:84353c479782 | 258 | /* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error, |
hudakz | 0:84353c479782 | 259 | fe USB xfer timeout */ |
hudakz | 1:2263e77400e9 | 260 | uint8_t Usb::inTransfer(uint8_t addr, uint8_t ep, uint16_t* nbytesptr, uint8_t* data, uint8_t bInterval /*= 0*/ ) |
hudakz | 0:84353c479782 | 261 | { |
hudakz | 0:84353c479782 | 262 | EpInfo* pep = NULL; |
hudakz | 0:84353c479782 | 263 | uint16_t nak_limit = 0; |
hudakz | 0:84353c479782 | 264 | |
hudakz | 0:84353c479782 | 265 | uint8_t rcode = setAddress(addr, ep, &pep, &nak_limit); |
hudakz | 0:84353c479782 | 266 | |
hudakz | 0:84353c479782 | 267 | if (rcode) { |
hudakz | 0:84353c479782 | 268 | USBTRACE3("(USB::InTransfer) SetAddress Failed ", rcode, 0x81); |
hudakz | 0:84353c479782 | 269 | USBTRACE3("(USB::InTransfer) addr requested ", addr, 0x81); |
hudakz | 0:84353c479782 | 270 | USBTRACE3("(USB::InTransfer) ep requested ", ep, 0x81); |
hudakz | 0:84353c479782 | 271 | return rcode; |
hudakz | 0:84353c479782 | 272 | } |
hudakz | 0:84353c479782 | 273 | |
hudakz | 0:84353c479782 | 274 | return inTransfer(pep, nak_limit, nbytesptr, data, bInterval); |
hudakz | 0:84353c479782 | 275 | } |
hudakz | 0:84353c479782 | 276 | |
hudakz | 0:84353c479782 | 277 | /** |
hudakz | 0:84353c479782 | 278 | * @brief |
hudakz | 0:84353c479782 | 279 | * @note |
hudakz | 0:84353c479782 | 280 | * @param |
hudakz | 0:84353c479782 | 281 | * @retval |
hudakz | 0:84353c479782 | 282 | */ |
hudakz | 1:2263e77400e9 | 283 | uint8_t Usb::inTransfer(EpInfo* pep, uint16_t nak_limit, uint16_t* nbytesptr, uint8_t* data, uint8_t bInterval /*= 0*/ ) |
hudakz | 0:84353c479782 | 284 | { |
hudakz | 0:84353c479782 | 285 | uint8_t rcode = 0; |
hudakz | 0:84353c479782 | 286 | uint8_t pktsize; |
hudakz | 0:84353c479782 | 287 | |
hudakz | 0:84353c479782 | 288 | uint16_t nbytes = *nbytesptr; |
hudakz | 0:84353c479782 | 289 | |
hudakz | 0:84353c479782 | 290 | //printf("Requesting %i bytes ", nbytes); |
hudakz | 0:84353c479782 | 291 | uint8_t maxpktsize = pep->maxPktSize; |
hudakz | 0:84353c479782 | 292 | |
hudakz | 0:84353c479782 | 293 | *nbytesptr = 0; |
hudakz | 0:84353c479782 | 294 | regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value |
hudakz | 0:84353c479782 | 295 | |
hudakz | 0:84353c479782 | 296 | // use a 'break' to exit this loop |
hudakz | 0:84353c479782 | 297 | while (1) |
hudakz | 0:84353c479782 | 298 | { |
hudakz | 0:84353c479782 | 299 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 300 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 301 | #endif |
hudakz | 0:84353c479782 | 302 | rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); //IN packet to EP-'endpoint'. Function takes care of NAKS. |
hudakz | 0:84353c479782 | 303 | if (rcode == hrTOGERR) { |
hudakz | 0:84353c479782 | 304 | |
hudakz | 0:84353c479782 | 305 | // yes, we flip it wrong here so that next time it is actually correct! |
hudakz | 0:84353c479782 | 306 | pep->bmRcvToggle = (regRd(rHRSL) & bmRCVTOGRD) ? 0 : 1; |
hudakz | 0:84353c479782 | 307 | regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value |
hudakz | 0:84353c479782 | 308 | continue; |
hudakz | 0:84353c479782 | 309 | } |
hudakz | 0:84353c479782 | 310 | |
hudakz | 0:84353c479782 | 311 | if (rcode) { |
hudakz | 0:84353c479782 | 312 | |
hudakz | 0:84353c479782 | 313 | //printf(">>>>>>>> Problem! dispatchPkt %2.2x\r\n", rcode); |
hudakz | 0:84353c479782 | 314 | break; //should be 0, indicating ACK. Else return error code. |
hudakz | 0:84353c479782 | 315 | } |
hudakz | 0:84353c479782 | 316 | |
hudakz | 0:84353c479782 | 317 | /* check for RCVDAVIRQ and generate error if not present |
hudakz | 0:84353c479782 | 318 | * the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. |
hudakz | 0:84353c479782 | 319 | * Need to add handling for that |
hudakz | 0:84353c479782 | 320 | * |
hudakz | 0:84353c479782 | 321 | * NOTE: I've seen this happen with SPI corruption -- xxxajk |
hudakz | 0:84353c479782 | 322 | */ |
hudakz | 0:84353c479782 | 323 | if ((regRd(rHIRQ) & bmRCVDAVIRQ) == 0) { |
hudakz | 0:84353c479782 | 324 | |
hudakz | 0:84353c479782 | 325 | //printf(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n"); |
hudakz | 0:84353c479782 | 326 | rcode = 0xf0; //receive error |
hudakz | 0:84353c479782 | 327 | break; |
hudakz | 0:84353c479782 | 328 | } |
hudakz | 0:84353c479782 | 329 | |
hudakz | 0:84353c479782 | 330 | pktsize = regRd(rRCVBC); //number of received bytes |
hudakz | 0:84353c479782 | 331 | |
hudakz | 0:84353c479782 | 332 | //printf("Got %i bytes \r\n", pktsize); |
hudakz | 0:84353c479782 | 333 | // This would be OK, but... |
hudakz | 0:84353c479782 | 334 | //assert(pktsize <= nbytes); |
hudakz | 0:84353c479782 | 335 | if (pktsize > nbytes) { |
hudakz | 0:84353c479782 | 336 | |
hudakz | 0:84353c479782 | 337 | // This can happen. Use of assert on Arduino locks up the Arduino. |
hudakz | 0:84353c479782 | 338 | // So I will trim the value, and hope for the best. |
hudakz | 0:84353c479782 | 339 | //printf(">>>>>>>> Problem! Wanted %i bytes but got %i.\r\n", nbytes, pktsize); |
hudakz | 0:84353c479782 | 340 | pktsize = nbytes; |
hudakz | 0:84353c479782 | 341 | } |
hudakz | 0:84353c479782 | 342 | |
hudakz | 0:84353c479782 | 343 | int16_t mem_left = (int16_t) nbytes -*((int16_t*)nbytesptr); |
hudakz | 0:84353c479782 | 344 | |
hudakz | 0:84353c479782 | 345 | if (mem_left < 0) |
hudakz | 0:84353c479782 | 346 | mem_left = 0; |
hudakz | 0:84353c479782 | 347 | |
hudakz | 0:84353c479782 | 348 | data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data); |
hudakz | 0:84353c479782 | 349 | |
hudakz | 0:84353c479782 | 350 | regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer |
hudakz | 0:84353c479782 | 351 | *nbytesptr += pktsize; // add this packet's byte count to total transfer length |
hudakz | 0:84353c479782 | 352 | |
hudakz | 0:84353c479782 | 353 | /* The transfer is complete under two conditions: */ |
hudakz | 0:84353c479782 | 354 | /* 1. The device sent a short packet (L.T. maxPacketSize) */ |
hudakz | 0:84353c479782 | 355 | /* 2. 'nbytes' have been transferred. */ |
hudakz | 0:84353c479782 | 356 | if ((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) { |
hudakz | 0:84353c479782 | 357 | |
hudakz | 0:84353c479782 | 358 | // have we transferred 'nbytes' bytes? |
hudakz | 0:84353c479782 | 359 | // Save toggle value |
hudakz | 0:84353c479782 | 360 | pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0; |
hudakz | 0:84353c479782 | 361 | |
hudakz | 0:84353c479782 | 362 | //printf("\r\n"); |
hudakz | 0:84353c479782 | 363 | rcode = 0; |
hudakz | 0:84353c479782 | 364 | break; |
hudakz | 0:84353c479782 | 365 | } |
hudakz | 0:84353c479782 | 366 | else |
hudakz | 0:84353c479782 | 367 | if (bInterval > 0) |
hudakz | 0:84353c479782 | 368 | wait_ms(bInterval); // Delay according to polling interval |
hudakz | 0:84353c479782 | 369 | } //while( 1 ) |
hudakz | 0:84353c479782 | 370 | |
hudakz | 0:84353c479782 | 371 | return(rcode); |
hudakz | 0:84353c479782 | 372 | } |
hudakz | 0:84353c479782 | 373 | |
hudakz | 0:84353c479782 | 374 | /* OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ |
hudakz | 0:84353c479782 | 375 | /* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer */ |
hudakz | 0:84353c479782 | 376 | |
hudakz | 0:84353c479782 | 377 | /* rcode 0 if no errors. rcode 01-0f is relayed from HRSL */ |
hudakz | 1:2263e77400e9 | 378 | uint8_t Usb::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) |
hudakz | 0:84353c479782 | 379 | { |
hudakz | 0:84353c479782 | 380 | EpInfo* pep = NULL; |
hudakz | 0:84353c479782 | 381 | uint16_t nak_limit = 0; |
hudakz | 0:84353c479782 | 382 | |
hudakz | 0:84353c479782 | 383 | uint8_t rcode = setAddress(addr, ep, &pep, &nak_limit); |
hudakz | 0:84353c479782 | 384 | |
hudakz | 0:84353c479782 | 385 | if (rcode) |
hudakz | 0:84353c479782 | 386 | return rcode; |
hudakz | 0:84353c479782 | 387 | |
hudakz | 0:84353c479782 | 388 | return outTransfer(pep, nak_limit, nbytes, data); |
hudakz | 0:84353c479782 | 389 | } |
hudakz | 0:84353c479782 | 390 | |
hudakz | 0:84353c479782 | 391 | /** |
hudakz | 0:84353c479782 | 392 | * @brief |
hudakz | 0:84353c479782 | 393 | * @note |
hudakz | 0:84353c479782 | 394 | * @param |
hudakz | 0:84353c479782 | 395 | * @retval |
hudakz | 0:84353c479782 | 396 | */ |
hudakz | 1:2263e77400e9 | 397 | uint8_t Usb::outTransfer(EpInfo* pep, uint16_t nak_limit, uint16_t nbytes, uint8_t* data) |
hudakz | 0:84353c479782 | 398 | { |
hudakz | 0:84353c479782 | 399 | uint8_t rcode = hrSUCCESS, retry_count; |
hudakz | 0:84353c479782 | 400 | uint8_t* data_p = data; //local copy of the data pointer |
hudakz | 0:84353c479782 | 401 | uint16_t bytes_tosend, nak_count; |
hudakz | 0:84353c479782 | 402 | uint16_t bytes_left = nbytes; |
hudakz | 0:84353c479782 | 403 | |
hudakz | 0:84353c479782 | 404 | uint8_t maxpktsize = pep->maxPktSize; |
hudakz | 0:84353c479782 | 405 | |
hudakz | 0:84353c479782 | 406 | if (maxpktsize < 1 || maxpktsize > 64) |
hudakz | 0:84353c479782 | 407 | return USB_ERROR_INVALID_MAX_PKT_SIZE; |
hudakz | 0:84353c479782 | 408 | |
hudakz | 0:84353c479782 | 409 | uint32_t timeout = (uint32_t)millis() + USB_XFER_TIMEOUT; |
hudakz | 0:84353c479782 | 410 | |
hudakz | 0:84353c479782 | 411 | regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value |
hudakz | 0:84353c479782 | 412 | while (bytes_left) |
hudakz | 0:84353c479782 | 413 | { |
hudakz | 0:84353c479782 | 414 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 415 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 416 | #endif |
hudakz | 0:84353c479782 | 417 | retry_count = 0; |
hudakz | 0:84353c479782 | 418 | nak_count = 0; |
hudakz | 0:84353c479782 | 419 | bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left; |
hudakz | 0:84353c479782 | 420 | bytesWr(rSNDFIFO, bytes_tosend, data_p); //filling output FIFO |
hudakz | 0:84353c479782 | 421 | regWr(rSNDBC, bytes_tosend); //set number of bytes |
hudakz | 0:84353c479782 | 422 | regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet |
hudakz | 0:84353c479782 | 423 | while (!(regRd(rHIRQ) & bmHXFRDNIRQ)) |
hudakz | 0:84353c479782 | 424 | { |
hudakz | 0:84353c479782 | 425 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 426 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 427 | #endif |
hudakz | 0:84353c479782 | 428 | } //wait for the completion IRQ |
hudakz | 0:84353c479782 | 429 | |
hudakz | 0:84353c479782 | 430 | regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ |
hudakz | 0:84353c479782 | 431 | rcode = (regRd(rHRSL) & 0x0f); |
hudakz | 0:84353c479782 | 432 | |
hudakz | 0:84353c479782 | 433 | while (rcode && ((int32_t) ((uint32_t)millis() - timeout) < 0L)) |
hudakz | 0:84353c479782 | 434 | { |
hudakz | 0:84353c479782 | 435 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 436 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 437 | #endif |
hudakz | 0:84353c479782 | 438 | switch (rcode) { |
hudakz | 0:84353c479782 | 439 | case hrNAK: |
hudakz | 0:84353c479782 | 440 | nak_count++; |
hudakz | 0:84353c479782 | 441 | if (nak_limit && (nak_count == nak_limit)) |
hudakz | 0:84353c479782 | 442 | goto breakout; |
hudakz | 0:84353c479782 | 443 | |
hudakz | 0:84353c479782 | 444 | //return ( rcode); |
hudakz | 0:84353c479782 | 445 | break; |
hudakz | 0:84353c479782 | 446 | |
hudakz | 0:84353c479782 | 447 | case hrTIMEOUT: |
hudakz | 0:84353c479782 | 448 | retry_count++; |
hudakz | 0:84353c479782 | 449 | if (retry_count == USB_RETRY_LIMIT) |
hudakz | 0:84353c479782 | 450 | goto breakout; |
hudakz | 0:84353c479782 | 451 | |
hudakz | 0:84353c479782 | 452 | //return ( rcode); |
hudakz | 0:84353c479782 | 453 | break; |
hudakz | 0:84353c479782 | 454 | |
hudakz | 0:84353c479782 | 455 | case hrTOGERR: |
hudakz | 0:84353c479782 | 456 | // yes, we flip it wrong here so that next time it is actually correct! |
hudakz | 0:84353c479782 | 457 | pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; |
hudakz | 0:84353c479782 | 458 | regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value |
hudakz | 0:84353c479782 | 459 | break; |
hudakz | 0:84353c479782 | 460 | |
hudakz | 0:84353c479782 | 461 | default: |
hudakz | 0:84353c479782 | 462 | goto breakout; |
hudakz | 0:84353c479782 | 463 | } //switch( rcode |
hudakz | 0:84353c479782 | 464 | |
hudakz | 0:84353c479782 | 465 | /* process NAK according to Host out NAK bug */ |
hudakz | 0:84353c479782 | 466 | regWr(rSNDBC, 0); |
hudakz | 0:84353c479782 | 467 | regWr(rSNDFIFO, *data_p); |
hudakz | 0:84353c479782 | 468 | regWr(rSNDBC, bytes_tosend); |
hudakz | 0:84353c479782 | 469 | regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet |
hudakz | 0:84353c479782 | 470 | while (!(regRd(rHIRQ) & bmHXFRDNIRQ)) |
hudakz | 0:84353c479782 | 471 | { |
hudakz | 0:84353c479782 | 472 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 473 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 474 | #endif |
hudakz | 0:84353c479782 | 475 | } //wait for the completion IRQ |
hudakz | 0:84353c479782 | 476 | |
hudakz | 0:84353c479782 | 477 | regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ |
hudakz | 0:84353c479782 | 478 | rcode = (regRd(rHRSL) & 0x0f); |
hudakz | 0:84353c479782 | 479 | } //while( rcode && .... |
hudakz | 0:84353c479782 | 480 | |
hudakz | 0:84353c479782 | 481 | bytes_left -= bytes_tosend; |
hudakz | 0:84353c479782 | 482 | data_p += bytes_tosend; |
hudakz | 0:84353c479782 | 483 | } //while( bytes_left... |
hudakz | 0:84353c479782 | 484 | |
hudakz | 0:84353c479782 | 485 | breakout: |
hudakz | 0:84353c479782 | 486 | pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle |
hudakz | 0:84353c479782 | 487 | return(rcode); //should be 0 in all cases |
hudakz | 0:84353c479782 | 488 | } |
hudakz | 0:84353c479782 | 489 | |
hudakz | 0:84353c479782 | 490 | /* dispatch USB packet. Assumes peripheral address is set and relevant buffer is loaded/empty */ |
hudakz | 0:84353c479782 | 491 | /* If NAK, tries to re-send up to nak_limit times */ |
hudakz | 0:84353c479782 | 492 | /* If nak_limit == 0, do not count NAKs, exit after timeout */ |
hudakz | 0:84353c479782 | 493 | /* If bus timeout, re-sends up to USB_RETRY_LIMIT times */ |
hudakz | 0:84353c479782 | 494 | |
hudakz | 0:84353c479782 | 495 | /* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */ |
hudakz | 1:2263e77400e9 | 496 | uint8_t Usb::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) |
hudakz | 0:84353c479782 | 497 | { |
hudakz | 0:84353c479782 | 498 | uint32_t timeout = (uint32_t)millis() + USB_XFER_TIMEOUT; |
hudakz | 0:84353c479782 | 499 | uint8_t tmpdata; |
hudakz | 0:84353c479782 | 500 | uint8_t rcode = hrSUCCESS; |
hudakz | 0:84353c479782 | 501 | uint8_t retry_count = 0; |
hudakz | 0:84353c479782 | 502 | uint16_t nak_count = 0; |
hudakz | 0:84353c479782 | 503 | |
hudakz | 0:84353c479782 | 504 | while ((int32_t) ((uint32_t)millis() - timeout) < 0L) |
hudakz | 0:84353c479782 | 505 | { |
hudakz | 0:84353c479782 | 506 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 507 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 508 | #endif |
hudakz | 0:84353c479782 | 509 | regWr(rHXFR, (token | ep)); //launch the transfer |
hudakz | 0:84353c479782 | 510 | rcode = USB_ERROR_TRANSFER_TIMEOUT; |
hudakz | 0:84353c479782 | 511 | |
hudakz | 0:84353c479782 | 512 | while ((int32_t) ((uint32_t)millis() - timeout) < 0L) //wait for transfer completion |
hudakz | 0:84353c479782 | 513 | { |
hudakz | 0:84353c479782 | 514 | #if defined(ESP8266) || defined(ESP32) |
hudakz | 0:84353c479782 | 515 | yield(); // needed in order to reset the watchdog timer on the ESP8266 |
hudakz | 0:84353c479782 | 516 | #endif |
hudakz | 0:84353c479782 | 517 | tmpdata = regRd(rHIRQ); |
hudakz | 0:84353c479782 | 518 | |
hudakz | 0:84353c479782 | 519 | if (tmpdata & bmHXFRDNIRQ) { |
hudakz | 0:84353c479782 | 520 | regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt |
hudakz | 0:84353c479782 | 521 | rcode = 0x00; |
hudakz | 0:84353c479782 | 522 | break; |
hudakz | 0:84353c479782 | 523 | } //if( tmpdata & bmHXFRDNIRQ |
hudakz | 0:84353c479782 | 524 | } //while ( millis() < timeout |
hudakz | 0:84353c479782 | 525 | |
hudakz | 0:84353c479782 | 526 | //if (rcode != 0x00) //exit if timeout |
hudakz | 0:84353c479782 | 527 | // return ( rcode); |
hudakz | 0:84353c479782 | 528 | rcode = (regRd(rHRSL) & 0x0f); //analyze transfer result |
hudakz | 0:84353c479782 | 529 | switch (rcode) { |
hudakz | 0:84353c479782 | 530 | case hrNAK: |
hudakz | 0:84353c479782 | 531 | nak_count++; |
hudakz | 0:84353c479782 | 532 | if (nak_limit && (nak_count == nak_limit)) |
hudakz | 0:84353c479782 | 533 | return(rcode); |
hudakz | 0:84353c479782 | 534 | break; |
hudakz | 0:84353c479782 | 535 | |
hudakz | 0:84353c479782 | 536 | case hrTIMEOUT: |
hudakz | 0:84353c479782 | 537 | retry_count++; |
hudakz | 0:84353c479782 | 538 | if (retry_count == USB_RETRY_LIMIT) |
hudakz | 0:84353c479782 | 539 | return(rcode); |
hudakz | 0:84353c479782 | 540 | break; |
hudakz | 0:84353c479782 | 541 | |
hudakz | 0:84353c479782 | 542 | default: |
hudakz | 0:84353c479782 | 543 | return(rcode); |
hudakz | 0:84353c479782 | 544 | } //switch( rcode |
hudakz | 0:84353c479782 | 545 | } //while( timeout > millis() |
hudakz | 0:84353c479782 | 546 | |
hudakz | 0:84353c479782 | 547 | return(rcode); |
hudakz | 0:84353c479782 | 548 | } |
hudakz | 0:84353c479782 | 549 | |
hudakz | 0:84353c479782 | 550 | /* USB main task. Performs enumeration/cleanup */ |
hudakz | 1:2263e77400e9 | 551 | void Usb::task(void) //USB state machine |
hudakz | 0:84353c479782 | 552 | { |
hudakz | 0:84353c479782 | 553 | uint8_t rcode; |
hudakz | 0:84353c479782 | 554 | uint8_t tmpdata; |
hudakz | 0:84353c479782 | 555 | static uint32_t delay = 0; |
hudakz | 0:84353c479782 | 556 | |
hudakz | 0:84353c479782 | 557 | //USB_DEVICE_DESCRIPTOR buf; |
hudakz | 0:84353c479782 | 558 | bool lowspeed = false; |
hudakz | 0:84353c479782 | 559 | |
hudakz | 0:84353c479782 | 560 | MAX3421E::task(); |
hudakz | 0:84353c479782 | 561 | |
hudakz | 0:84353c479782 | 562 | tmpdata = getVbusState(); |
hudakz | 0:84353c479782 | 563 | |
hudakz | 0:84353c479782 | 564 | /* modify USB task state if Vbus changed */ |
hudakz | 0:84353c479782 | 565 | switch (tmpdata) { |
hudakz | 0:84353c479782 | 566 | case SE1: //illegal state |
hudakz | 0:84353c479782 | 567 | usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; |
hudakz | 0:84353c479782 | 568 | lowspeed = false; |
hudakz | 0:84353c479782 | 569 | break; |
hudakz | 0:84353c479782 | 570 | |
hudakz | 0:84353c479782 | 571 | case SE0: //disconnected |
hudakz | 0:84353c479782 | 572 | if ((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED) |
hudakz | 0:84353c479782 | 573 | usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; |
hudakz | 0:84353c479782 | 574 | lowspeed = false; |
hudakz | 0:84353c479782 | 575 | break; |
hudakz | 0:84353c479782 | 576 | |
hudakz | 0:84353c479782 | 577 | case LSHOST: |
hudakz | 0:84353c479782 | 578 | lowspeed = true; |
hudakz | 0:84353c479782 | 579 | |
hudakz | 0:84353c479782 | 580 | //intentional fallthrough |
hudakz | 0:84353c479782 | 581 | case FSHOST: //attached |
hudakz | 0:84353c479782 | 582 | if ((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) { |
hudakz | 0:84353c479782 | 583 | delay = (uint32_t)millis() + USB_SETTLE_DELAY; |
hudakz | 0:84353c479782 | 584 | usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; |
hudakz | 0:84353c479782 | 585 | } |
hudakz | 0:84353c479782 | 586 | break; |
hudakz | 0:84353c479782 | 587 | } // switch( tmpdata |
hudakz | 0:84353c479782 | 588 | |
hudakz | 0:84353c479782 | 589 | for (uint8_t i = 0; i < USB_NUMDEVICES; i++) |
hudakz | 0:84353c479782 | 590 | if (devConfig[i]) |
hudakz | 0:84353c479782 | 591 | rcode = devConfig[i]->Poll(); |
hudakz | 0:84353c479782 | 592 | |
hudakz | 0:84353c479782 | 593 | switch (usb_task_state) { |
hudakz | 0:84353c479782 | 594 | case USB_DETACHED_SUBSTATE_INITIALIZE: |
hudakz | 0:84353c479782 | 595 | init(); |
hudakz | 0:84353c479782 | 596 | |
hudakz | 0:84353c479782 | 597 | for (uint8_t i = 0; i < USB_NUMDEVICES; i++) |
hudakz | 0:84353c479782 | 598 | if (devConfig[i]) |
hudakz | 0:84353c479782 | 599 | rcode = devConfig[i]->Release(); |
hudakz | 0:84353c479782 | 600 | |
hudakz | 0:84353c479782 | 601 | usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; |
hudakz | 0:84353c479782 | 602 | break; |
hudakz | 0:84353c479782 | 603 | |
hudakz | 0:84353c479782 | 604 | case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here |
hudakz | 0:84353c479782 | 605 | break; |
hudakz | 0:84353c479782 | 606 | |
hudakz | 0:84353c479782 | 607 | case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here |
hudakz | 0:84353c479782 | 608 | break; |
hudakz | 0:84353c479782 | 609 | |
hudakz | 0:84353c479782 | 610 | case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device |
hudakz | 0:84353c479782 | 611 | if ((int32_t) ((uint32_t)millis() - delay) >= 0L) |
hudakz | 0:84353c479782 | 612 | usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; |
hudakz | 0:84353c479782 | 613 | else |
hudakz | 0:84353c479782 | 614 | break; // don't fall through |
hudakz | 0:84353c479782 | 615 | |
hudakz | 0:84353c479782 | 616 | case USB_ATTACHED_SUBSTATE_RESET_DEVICE: |
hudakz | 0:84353c479782 | 617 | regWr(rHCTL, bmBUSRST); //issue bus reset |
hudakz | 0:84353c479782 | 618 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; |
hudakz | 0:84353c479782 | 619 | break; |
hudakz | 0:84353c479782 | 620 | |
hudakz | 0:84353c479782 | 621 | case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: |
hudakz | 0:84353c479782 | 622 | if ((regRd(rHCTL) & bmBUSRST) == 0) { |
hudakz | 0:84353c479782 | 623 | tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation |
hudakz | 0:84353c479782 | 624 | regWr(rMODE, tmpdata); |
hudakz | 0:84353c479782 | 625 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; |
hudakz | 0:84353c479782 | 626 | |
hudakz | 0:84353c479782 | 627 | //delay = (uint32_t)millis() + 20; //20ms wait after reset per USB spec |
hudakz | 0:84353c479782 | 628 | } |
hudakz | 0:84353c479782 | 629 | break; |
hudakz | 0:84353c479782 | 630 | |
hudakz | 0:84353c479782 | 631 | case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order |
hudakz | 0:84353c479782 | 632 | if (regRd(rHIRQ) & bmFRAMEIRQ) { |
hudakz | 0:84353c479782 | 633 | |
hudakz | 0:84353c479782 | 634 | //when first SOF received _and_ 20ms has passed we can continue |
hudakz | 0:84353c479782 | 635 | /* |
hudakz | 0:84353c479782 | 636 | if (delay < (uint32_t)millis()) //20ms passed |
hudakz | 0:84353c479782 | 637 | usb_task_state = USB_STATE_CONFIGURING; |
hudakz | 0:84353c479782 | 638 | */ |
hudakz | 0:84353c479782 | 639 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET; |
hudakz | 0:84353c479782 | 640 | delay = (uint32_t)millis() + 20; |
hudakz | 0:84353c479782 | 641 | } |
hudakz | 0:84353c479782 | 642 | break; |
hudakz | 0:84353c479782 | 643 | |
hudakz | 0:84353c479782 | 644 | case USB_ATTACHED_SUBSTATE_WAIT_RESET: |
hudakz | 0:84353c479782 | 645 | if ((int32_t) ((uint32_t)millis() - delay) >= 0L) |
hudakz | 0:84353c479782 | 646 | usb_task_state = USB_STATE_CONFIGURING; |
hudakz | 0:84353c479782 | 647 | else |
hudakz | 0:84353c479782 | 648 | break; // don't fall through |
hudakz | 0:84353c479782 | 649 | |
hudakz | 0:84353c479782 | 650 | case USB_STATE_CONFIGURING: |
hudakz | 0:84353c479782 | 651 | //Serial.print("\r\nConf.LS: "); |
hudakz | 0:84353c479782 | 652 | //Serial.println(lowspeed, HEX); |
hudakz | 0:84353c479782 | 653 | rcode = configuring(0, 0, lowspeed); |
hudakz | 0:84353c479782 | 654 | |
hudakz | 0:84353c479782 | 655 | if (rcode) { |
hudakz | 0:84353c479782 | 656 | if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) { |
hudakz | 0:84353c479782 | 657 | usb_error = rcode; |
hudakz | 0:84353c479782 | 658 | usb_task_state = USB_STATE_ERROR; |
hudakz | 0:84353c479782 | 659 | } |
hudakz | 0:84353c479782 | 660 | } |
hudakz | 0:84353c479782 | 661 | else |
hudakz | 0:84353c479782 | 662 | usb_task_state = USB_STATE_RUNNING; |
hudakz | 0:84353c479782 | 663 | break; |
hudakz | 0:84353c479782 | 664 | |
hudakz | 0:84353c479782 | 665 | case USB_STATE_RUNNING: |
hudakz | 0:84353c479782 | 666 | break; |
hudakz | 0:84353c479782 | 667 | |
hudakz | 0:84353c479782 | 668 | case USB_STATE_ERROR: |
hudakz | 0:84353c479782 | 669 | //MAX3421E::Init(); |
hudakz | 0:84353c479782 | 670 | break; |
hudakz | 0:84353c479782 | 671 | } // switch( usb_task_state ) |
hudakz | 0:84353c479782 | 672 | } |
hudakz | 0:84353c479782 | 673 | |
hudakz | 0:84353c479782 | 674 | /** |
hudakz | 0:84353c479782 | 675 | * @brief |
hudakz | 0:84353c479782 | 676 | * @note |
hudakz | 0:84353c479782 | 677 | * @param |
hudakz | 0:84353c479782 | 678 | * @retval |
hudakz | 0:84353c479782 | 679 | */ |
hudakz | 1:2263e77400e9 | 680 | uint8_t Usb::defaultAddressing(uint8_t parent, uint8_t port, bool lowspeed) |
hudakz | 0:84353c479782 | 681 | { |
hudakz | 0:84353c479782 | 682 | //uint8_t buf[12]; |
hudakz | 0:84353c479782 | 683 | uint8_t rcode; |
hudakz | 0:84353c479782 | 684 | UsbDevice* p0 = NULL, *p = NULL; |
hudakz | 0:84353c479782 | 685 | |
hudakz | 0:84353c479782 | 686 | // Get pointer to pseudo device with address 0 assigned |
hudakz | 0:84353c479782 | 687 | |
hudakz | 0:84353c479782 | 688 | p0 = addrPool.GetUsbDevicePtr(0); |
hudakz | 0:84353c479782 | 689 | |
hudakz | 0:84353c479782 | 690 | if (!p0) |
hudakz | 0:84353c479782 | 691 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; |
hudakz | 0:84353c479782 | 692 | |
hudakz | 0:84353c479782 | 693 | if (!p0->epinfo) |
hudakz | 0:84353c479782 | 694 | return USB_ERROR_EPINFO_IS_NULL; |
hudakz | 0:84353c479782 | 695 | |
hudakz | 0:84353c479782 | 696 | p0->lowspeed = (lowspeed) ? true : false; |
hudakz | 0:84353c479782 | 697 | |
hudakz | 0:84353c479782 | 698 | // Allocate new address according to device class |
hudakz | 0:84353c479782 | 699 | uint8_t bAddress = addrPool.AllocAddress(parent, false, port); |
hudakz | 0:84353c479782 | 700 | |
hudakz | 0:84353c479782 | 701 | if (!bAddress) |
hudakz | 0:84353c479782 | 702 | return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; |
hudakz | 0:84353c479782 | 703 | |
hudakz | 0:84353c479782 | 704 | p = addrPool.GetUsbDevicePtr(bAddress); |
hudakz | 0:84353c479782 | 705 | |
hudakz | 0:84353c479782 | 706 | if (!p) |
hudakz | 0:84353c479782 | 707 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; |
hudakz | 0:84353c479782 | 708 | |
hudakz | 0:84353c479782 | 709 | p->lowspeed = lowspeed; |
hudakz | 0:84353c479782 | 710 | |
hudakz | 0:84353c479782 | 711 | // Assign new address to the device |
hudakz | 0:84353c479782 | 712 | rcode = setAddr(0, 0, bAddress); |
hudakz | 0:84353c479782 | 713 | |
hudakz | 0:84353c479782 | 714 | if (rcode) { |
hudakz | 0:84353c479782 | 715 | addrPool.FreeAddress(bAddress); |
hudakz | 0:84353c479782 | 716 | bAddress = 0; |
hudakz | 0:84353c479782 | 717 | return rcode; |
hudakz | 0:84353c479782 | 718 | } |
hudakz | 0:84353c479782 | 719 | |
hudakz | 0:84353c479782 | 720 | return 0; |
hudakz | 0:84353c479782 | 721 | } |
hudakz | 0:84353c479782 | 722 | |
hudakz | 0:84353c479782 | 723 | /** |
hudakz | 0:84353c479782 | 724 | * @brief |
hudakz | 0:84353c479782 | 725 | * @note |
hudakz | 0:84353c479782 | 726 | * @param |
hudakz | 0:84353c479782 | 727 | * @retval |
hudakz | 0:84353c479782 | 728 | */ |
hudakz | 1:2263e77400e9 | 729 | uint8_t Usb::attemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) |
hudakz | 0:84353c479782 | 730 | { |
hudakz | 0:84353c479782 | 731 | //printf("AttemptConfig: parent = %i, port = %i\r\n", parent, port); |
hudakz | 0:84353c479782 | 732 | uint8_t retries = 0; |
hudakz | 0:84353c479782 | 733 | |
hudakz | 0:84353c479782 | 734 | again: |
hudakz | 0:84353c479782 | 735 | uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed); |
hudakz | 0:84353c479782 | 736 | if (rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) { |
hudakz | 0:84353c479782 | 737 | if (parent == 0) { |
hudakz | 0:84353c479782 | 738 | |
hudakz | 0:84353c479782 | 739 | // Send a bus reset on the root interface. |
hudakz | 0:84353c479782 | 740 | regWr(rHCTL, bmBUSRST); //issue bus reset |
hudakz | 0:84353c479782 | 741 | wait_ms(102); // delay 102ms, compensate for clock inaccuracy. |
hudakz | 0:84353c479782 | 742 | } |
hudakz | 0:84353c479782 | 743 | else { |
hudakz | 0:84353c479782 | 744 | |
hudakz | 0:84353c479782 | 745 | // reset parent port |
hudakz | 0:84353c479782 | 746 | devConfig[parent]->ResetHubPort(port); |
hudakz | 0:84353c479782 | 747 | } |
hudakz | 0:84353c479782 | 748 | } |
hudakz | 0:84353c479782 | 749 | else |
hudakz | 0:84353c479782 | 750 | if (rcode == hrJERR && retries < 3) { |
hudakz | 0:84353c479782 | 751 | |
hudakz | 0:84353c479782 | 752 | // Some devices returns this when plugged in - trying to initialize the device again usually works |
hudakz | 0:84353c479782 | 753 | wait_ms(100); |
hudakz | 0:84353c479782 | 754 | retries++; |
hudakz | 0:84353c479782 | 755 | goto again; |
hudakz | 0:84353c479782 | 756 | } |
hudakz | 0:84353c479782 | 757 | else |
hudakz | 0:84353c479782 | 758 | if (rcode) |
hudakz | 0:84353c479782 | 759 | return rcode; |
hudakz | 0:84353c479782 | 760 | |
hudakz | 0:84353c479782 | 761 | rcode = devConfig[driver]->Init(parent, port, lowspeed); |
hudakz | 0:84353c479782 | 762 | if (rcode == hrJERR && retries < 3) { |
hudakz | 0:84353c479782 | 763 | |
hudakz | 0:84353c479782 | 764 | // Some devices returns this when plugged in - trying to initialize the device again usually works |
hudakz | 0:84353c479782 | 765 | wait_ms(100); |
hudakz | 0:84353c479782 | 766 | retries++; |
hudakz | 0:84353c479782 | 767 | goto again; |
hudakz | 0:84353c479782 | 768 | } |
hudakz | 0:84353c479782 | 769 | |
hudakz | 0:84353c479782 | 770 | if (rcode) { |
hudakz | 0:84353c479782 | 771 | |
hudakz | 0:84353c479782 | 772 | // Issue a bus reset, because the device may be in a limbo state |
hudakz | 0:84353c479782 | 773 | if (parent == 0) { |
hudakz | 0:84353c479782 | 774 | |
hudakz | 0:84353c479782 | 775 | // Send a bus reset on the root interface. |
hudakz | 0:84353c479782 | 776 | regWr(rHCTL, bmBUSRST); //issue bus reset |
hudakz | 0:84353c479782 | 777 | wait_ms(102); // delay 102ms, compensate for clock inaccuracy. |
hudakz | 0:84353c479782 | 778 | } |
hudakz | 0:84353c479782 | 779 | else { |
hudakz | 0:84353c479782 | 780 | |
hudakz | 0:84353c479782 | 781 | // reset parent port |
hudakz | 0:84353c479782 | 782 | devConfig[parent]->ResetHubPort(port); |
hudakz | 0:84353c479782 | 783 | } |
hudakz | 0:84353c479782 | 784 | } |
hudakz | 0:84353c479782 | 785 | |
hudakz | 0:84353c479782 | 786 | return rcode; |
hudakz | 0:84353c479782 | 787 | } |
hudakz | 0:84353c479782 | 788 | |
hudakz | 0:84353c479782 | 789 | /* |
hudakz | 0:84353c479782 | 790 | * This is broken. We need to enumerate differently. |
hudakz | 0:84353c479782 | 791 | * It causes major problems with several devices if detected in an unexpected order. |
hudakz | 0:84353c479782 | 792 | * |
hudakz | 0:84353c479782 | 793 | * |
hudakz | 0:84353c479782 | 794 | * Oleg - I wouldn't do anything before the newly connected device is considered sane. |
hudakz | 0:84353c479782 | 795 | * i.e.(delays are not indicated for brevity): |
hudakz | 0:84353c479782 | 796 | * 1. reset |
hudakz | 0:84353c479782 | 797 | * 2. GetDevDescr(); |
hudakz | 0:84353c479782 | 798 | * 3a. If ACK, continue with allocating address, addressing, etc. |
hudakz | 0:84353c479782 | 799 | * 3b. Else reset again, count resets, stop at some number (5?). |
hudakz | 0:84353c479782 | 800 | * 4. When max.number of resets is reached, toggle power/fail |
hudakz | 0:84353c479782 | 801 | * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD() |
hudakz | 0:84353c479782 | 802 | * it doesn't need to be reset again |
hudakz | 0:84353c479782 | 803 | * New steps proposal: |
hudakz | 0:84353c479782 | 804 | * 1: get address pool instance. exit on fail |
hudakz | 0:84353c479782 | 805 | * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail. |
hudakz | 0:84353c479782 | 806 | * 3: bus reset, 100ms delay |
hudakz | 0:84353c479782 | 807 | * 4: set address |
hudakz | 0:84353c479782 | 808 | * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail |
hudakz | 0:84353c479782 | 809 | * 6: while (configurations) { |
hudakz | 0:84353c479782 | 810 | * for(each configuration) { |
hudakz | 0:84353c479782 | 811 | * for (each driver) { |
hudakz | 0:84353c479782 | 812 | * 6a: Ask device if it likes configuration. Returns 0 on OK. |
hudakz | 0:84353c479782 | 813 | * If successful, the driver configured device. |
hudakz | 0:84353c479782 | 814 | * The driver now owns the endpoints, and takes over managing them. |
hudakz | 0:84353c479782 | 815 | * The following will need codes: |
hudakz | 0:84353c479782 | 816 | * Everything went well, instance consumed, exit with success. |
hudakz | 0:84353c479782 | 817 | * Instance already in use, ignore it, try next driver. |
hudakz | 0:84353c479782 | 818 | * Not a supported device, ignore it, try next driver. |
hudakz | 0:84353c479782 | 819 | * Not a supported configuration for this device, ignore it, try next driver. |
hudakz | 0:84353c479782 | 820 | * Could not configure device, fatal, exit with fail. |
hudakz | 0:84353c479782 | 821 | * } |
hudakz | 0:84353c479782 | 822 | * } |
hudakz | 0:84353c479782 | 823 | * } |
hudakz | 0:84353c479782 | 824 | * 7: for(each driver) { |
hudakz | 0:84353c479782 | 825 | * 7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID |
hudakz | 0:84353c479782 | 826 | * 8: if we get here, no driver likes the device plugged in, so exit failure. |
hudakz | 0:84353c479782 | 827 | * |
hudakz | 0:84353c479782 | 828 | */ |
hudakz | 1:2263e77400e9 | 829 | uint8_t Usb::configuring(uint8_t parent, uint8_t port, bool lowspeed) |
hudakz | 0:84353c479782 | 830 | { |
hudakz | 0:84353c479782 | 831 | //uint8_t bAddress = 0; |
hudakz | 0:84353c479782 | 832 | |
hudakz | 0:84353c479782 | 833 | //printf("Configuring: parent = %i, port = %i\r\n", parent, port); |
hudakz | 0:84353c479782 | 834 | uint8_t devConfigIndex; |
hudakz | 0:84353c479782 | 835 | uint8_t rcode = 0; |
hudakz | 0:84353c479782 | 836 | uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)]; |
hudakz | 0:84353c479782 | 837 | USB_DEVICE_DESCRIPTOR* udd = reinterpret_cast < USB_DEVICE_DESCRIPTOR * > (buf); |
hudakz | 0:84353c479782 | 838 | UsbDevice* p = NULL; |
hudakz | 0:84353c479782 | 839 | EpInfo* oldep_ptr = NULL; |
hudakz | 0:84353c479782 | 840 | EpInfo epInfo; |
hudakz | 0:84353c479782 | 841 | |
hudakz | 0:84353c479782 | 842 | epInfo.epAddr = 0; |
hudakz | 0:84353c479782 | 843 | epInfo.maxPktSize = 8; |
hudakz | 0:84353c479782 | 844 | epInfo.bmSndToggle = 0; |
hudakz | 0:84353c479782 | 845 | epInfo.bmRcvToggle = 0; |
hudakz | 0:84353c479782 | 846 | epInfo.bmNakPower = USB_NAK_MAX_POWER; |
hudakz | 0:84353c479782 | 847 | |
hudakz | 0:84353c479782 | 848 | //wait_ms(2000); |
hudakz | 0:84353c479782 | 849 | AddressPool& addrPool = GetAddressPool(); |
hudakz | 0:84353c479782 | 850 | // Get pointer to pseudo device with address 0 assigned |
hudakz | 0:84353c479782 | 851 | |
hudakz | 0:84353c479782 | 852 | p = addrPool.GetUsbDevicePtr(0); |
hudakz | 0:84353c479782 | 853 | if (!p) { |
hudakz | 0:84353c479782 | 854 | |
hudakz | 0:84353c479782 | 855 | //printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n"); |
hudakz | 0:84353c479782 | 856 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; |
hudakz | 0:84353c479782 | 857 | } |
hudakz | 0:84353c479782 | 858 | |
hudakz | 0:84353c479782 | 859 | // Save old pointer to EP_RECORD of address 0 |
hudakz | 0:84353c479782 | 860 | oldep_ptr = p->epinfo; |
hudakz | 0:84353c479782 | 861 | |
hudakz | 0:84353c479782 | 862 | // Temporary assign new pointer to epInfo to p->epinfo in order to |
hudakz | 0:84353c479782 | 863 | // avoid toggle inconsistence |
hudakz | 0:84353c479782 | 864 | p->epinfo = &epInfo; |
hudakz | 0:84353c479782 | 865 | |
hudakz | 0:84353c479782 | 866 | p->lowspeed = lowspeed; |
hudakz | 0:84353c479782 | 867 | |
hudakz | 0:84353c479782 | 868 | // Get device descriptor |
hudakz | 0:84353c479782 | 869 | rcode = getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); |
hudakz | 0:84353c479782 | 870 | |
hudakz | 0:84353c479782 | 871 | // Restore p->epinfo |
hudakz | 0:84353c479782 | 872 | p->epinfo = oldep_ptr; |
hudakz | 0:84353c479782 | 873 | |
hudakz | 0:84353c479782 | 874 | if (rcode) { |
hudakz | 0:84353c479782 | 875 | |
hudakz | 0:84353c479782 | 876 | //printf("Configuring error: Can't get USB_DEVICE_DESCRIPTOR\r\n"); |
hudakz | 0:84353c479782 | 877 | return rcode; |
hudakz | 0:84353c479782 | 878 | } |
hudakz | 0:84353c479782 | 879 | |
hudakz | 0:84353c479782 | 880 | // to-do? |
hudakz | 0:84353c479782 | 881 | // Allocate new address according to device class |
hudakz | 0:84353c479782 | 882 | //bAddress = addrPool.AllocAddress(parent, false, port); |
hudakz | 0:84353c479782 | 883 | uint16_t vid = udd->idVendor; |
hudakz | 0:84353c479782 | 884 | uint16_t pid = udd->idProduct; |
hudakz | 0:84353c479782 | 885 | uint8_t klass = udd->bDeviceClass; |
hudakz | 0:84353c479782 | 886 | uint8_t subklass = udd->bDeviceSubClass; |
hudakz | 0:84353c479782 | 887 | // Attempt to configure if VID/PID or device class matches with a driver |
hudakz | 0:84353c479782 | 888 | |
hudakz | 0:84353c479782 | 889 | // Qualify with subclass too. |
hudakz | 0:84353c479782 | 890 | // |
hudakz | 0:84353c479782 | 891 | // VID/PID & class tests default to false for drivers not yet ported |
hudakz | 0:84353c479782 | 892 | // subclass defaults to true, so you don't have to define it if you don't have to. |
hudakz | 0:84353c479782 | 893 | // |
hudakz | 0:84353c479782 | 894 | for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { |
hudakz | 0:84353c479782 | 895 | if (!devConfig[devConfigIndex]) |
hudakz | 0:84353c479782 | 896 | continue; // no driver |
hudakz | 0:84353c479782 | 897 | if (devConfig[devConfigIndex]->GetAddress()) |
hudakz | 0:84353c479782 | 898 | continue; // consumed |
hudakz | 0:84353c479782 | 899 | if |
hudakz | 0:84353c479782 | 900 | ( |
hudakz | 0:84353c479782 | 901 | devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && |
hudakz | 0:84353c479782 | 902 | (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass)) |
hudakz | 0:84353c479782 | 903 | ) { |
hudakz | 0:84353c479782 | 904 | rcode = attemptConfig(devConfigIndex, parent, port, lowspeed); |
hudakz | 0:84353c479782 | 905 | if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED) |
hudakz | 0:84353c479782 | 906 | break; |
hudakz | 0:84353c479782 | 907 | } |
hudakz | 0:84353c479782 | 908 | } |
hudakz | 0:84353c479782 | 909 | |
hudakz | 0:84353c479782 | 910 | if (devConfigIndex < USB_NUMDEVICES) { |
hudakz | 0:84353c479782 | 911 | return rcode; |
hudakz | 0:84353c479782 | 912 | } |
hudakz | 0:84353c479782 | 913 | |
hudakz | 0:84353c479782 | 914 | // blindly attempt to configure |
hudakz | 0:84353c479782 | 915 | for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { |
hudakz | 0:84353c479782 | 916 | if (!devConfig[devConfigIndex]) |
hudakz | 0:84353c479782 | 917 | continue; |
hudakz | 0:84353c479782 | 918 | if (devConfig[devConfigIndex]->GetAddress()) |
hudakz | 0:84353c479782 | 919 | continue; // consumed |
hudakz | 0:84353c479782 | 920 | if |
hudakz | 0:84353c479782 | 921 | ( |
hudakz | 0:84353c479782 | 922 | devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && |
hudakz | 0:84353c479782 | 923 | (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass)) |
hudakz | 0:84353c479782 | 924 | ) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above |
hudakz | 0:84353c479782 | 925 | rcode = attemptConfig(devConfigIndex, parent, port, lowspeed); |
hudakz | 0:84353c479782 | 926 | |
hudakz | 0:84353c479782 | 927 | //printf("ERROR ENUMERATING %2.2x\r\n", rcode); |
hudakz | 0:84353c479782 | 928 | if (!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) { |
hudakz | 0:84353c479782 | 929 | |
hudakz | 0:84353c479782 | 930 | // in case of an error dev_index should be reset to 0 |
hudakz | 0:84353c479782 | 931 | // in order to start from the very beginning the |
hudakz | 0:84353c479782 | 932 | // next time the program gets here |
hudakz | 0:84353c479782 | 933 | //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) |
hudakz | 0:84353c479782 | 934 | // devConfigIndex = 0; |
hudakz | 0:84353c479782 | 935 | return rcode; |
hudakz | 0:84353c479782 | 936 | } |
hudakz | 0:84353c479782 | 937 | } |
hudakz | 0:84353c479782 | 938 | |
hudakz | 0:84353c479782 | 939 | // if we get here that means that the device class is not supported by any of registered classes |
hudakz | 0:84353c479782 | 940 | rcode = defaultAddressing(parent, port, lowspeed); |
hudakz | 0:84353c479782 | 941 | |
hudakz | 0:84353c479782 | 942 | return rcode; |
hudakz | 0:84353c479782 | 943 | } |
hudakz | 0:84353c479782 | 944 | |
hudakz | 0:84353c479782 | 945 | /** |
hudakz | 0:84353c479782 | 946 | * @brief |
hudakz | 0:84353c479782 | 947 | * @note |
hudakz | 0:84353c479782 | 948 | * @param |
hudakz | 0:84353c479782 | 949 | * @retval |
hudakz | 0:84353c479782 | 950 | */ |
hudakz | 1:2263e77400e9 | 951 | uint8_t Usb::releaseDevice(uint8_t addr) |
hudakz | 0:84353c479782 | 952 | { |
hudakz | 0:84353c479782 | 953 | if (!addr) |
hudakz | 0:84353c479782 | 954 | return 0; |
hudakz | 0:84353c479782 | 955 | |
hudakz | 0:84353c479782 | 956 | for (uint8_t i = 0; i < USB_NUMDEVICES; i++) { |
hudakz | 0:84353c479782 | 957 | if (!devConfig[i]) |
hudakz | 0:84353c479782 | 958 | continue; |
hudakz | 0:84353c479782 | 959 | if (devConfig[i]->GetAddress() == addr) |
hudakz | 0:84353c479782 | 960 | return devConfig[i]->Release(); |
hudakz | 0:84353c479782 | 961 | } |
hudakz | 0:84353c479782 | 962 | |
hudakz | 0:84353c479782 | 963 | return 0; |
hudakz | 0:84353c479782 | 964 | } |
hudakz | 0:84353c479782 | 965 | |
hudakz | 0:84353c479782 | 966 | #if 1 //!defined(USB_METHODS_INLINE) |
hudakz | 0:84353c479782 | 967 | |
hudakz | 0:84353c479782 | 968 | //get device descriptor |
hudakz | 1:2263e77400e9 | 969 | uint8_t Usb::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) |
hudakz | 0:84353c479782 | 970 | { |
hudakz | 0:84353c479782 | 971 | return |
hudakz | 0:84353c479782 | 972 | ( |
hudakz | 0:84353c479782 | 973 | ctrlReq |
hudakz | 0:84353c479782 | 974 | ( |
hudakz | 0:84353c479782 | 975 | addr, |
hudakz | 0:84353c479782 | 976 | ep, |
hudakz | 0:84353c479782 | 977 | bmREQ_GET_DESCR, |
hudakz | 0:84353c479782 | 978 | USB_REQUEST_GET_DESCRIPTOR, |
hudakz | 0:84353c479782 | 979 | 0x00, |
hudakz | 0:84353c479782 | 980 | USB_DESCRIPTOR_DEVICE, |
hudakz | 0:84353c479782 | 981 | 0x0000, |
hudakz | 0:84353c479782 | 982 | nbytes, |
hudakz | 0:84353c479782 | 983 | nbytes, |
hudakz | 0:84353c479782 | 984 | dataptr, |
hudakz | 0:84353c479782 | 985 | NULL |
hudakz | 0:84353c479782 | 986 | ) |
hudakz | 0:84353c479782 | 987 | ); |
hudakz | 0:84353c479782 | 988 | } |
hudakz | 0:84353c479782 | 989 | |
hudakz | 0:84353c479782 | 990 | //get configuration descriptor |
hudakz | 1:2263e77400e9 | 991 | uint8_t Usb::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) |
hudakz | 0:84353c479782 | 992 | { |
hudakz | 0:84353c479782 | 993 | return |
hudakz | 0:84353c479782 | 994 | ( |
hudakz | 0:84353c479782 | 995 | ctrlReq |
hudakz | 0:84353c479782 | 996 | ( |
hudakz | 0:84353c479782 | 997 | addr, |
hudakz | 0:84353c479782 | 998 | ep, |
hudakz | 0:84353c479782 | 999 | bmREQ_GET_DESCR, |
hudakz | 0:84353c479782 | 1000 | USB_REQUEST_GET_DESCRIPTOR, |
hudakz | 0:84353c479782 | 1001 | conf, |
hudakz | 0:84353c479782 | 1002 | USB_DESCRIPTOR_CONFIGURATION, |
hudakz | 0:84353c479782 | 1003 | 0x0000, |
hudakz | 0:84353c479782 | 1004 | nbytes, |
hudakz | 0:84353c479782 | 1005 | nbytes, |
hudakz | 0:84353c479782 | 1006 | dataptr, |
hudakz | 0:84353c479782 | 1007 | NULL |
hudakz | 0:84353c479782 | 1008 | ) |
hudakz | 0:84353c479782 | 1009 | ); |
hudakz | 0:84353c479782 | 1010 | } |
hudakz | 0:84353c479782 | 1011 | |
hudakz | 0:84353c479782 | 1012 | /* Requests Configuration Descriptor. Sends two Get Conf Descr requests. The first one gets the total length of all descriptors, then the second one requests this |
hudakz | 0:84353c479782 | 1013 | total length. The length of the first request can be shorter ( 4 bytes ), however, there are devices which won't work unless this length is set to 9 */ |
hudakz | 1:2263e77400e9 | 1014 | uint8_t Usb::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser* p) |
hudakz | 0:84353c479782 | 1015 | { |
hudakz | 0:84353c479782 | 1016 | const uint8_t bufSize = 64; |
hudakz | 0:84353c479782 | 1017 | uint8_t buf[bufSize]; |
hudakz | 0:84353c479782 | 1018 | USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast < USB_CONFIGURATION_DESCRIPTOR * > (buf); |
hudakz | 0:84353c479782 | 1019 | |
hudakz | 0:84353c479782 | 1020 | uint8_t ret = getConfDescr(addr, ep, 9, conf, buf); |
hudakz | 0:84353c479782 | 1021 | |
hudakz | 0:84353c479782 | 1022 | if (ret) |
hudakz | 0:84353c479782 | 1023 | return ret; |
hudakz | 0:84353c479782 | 1024 | |
hudakz | 0:84353c479782 | 1025 | uint16_t total = ucd->wTotalLength; |
hudakz | 0:84353c479782 | 1026 | |
hudakz | 0:84353c479782 | 1027 | //USBTRACE2("\r\ntotal conf.size:", total); |
hudakz | 0:84353c479782 | 1028 | |
hudakz | 0:84353c479782 | 1029 | return |
hudakz | 0:84353c479782 | 1030 | ( |
hudakz | 0:84353c479782 | 1031 | ctrlReq |
hudakz | 0:84353c479782 | 1032 | ( |
hudakz | 0:84353c479782 | 1033 | addr, |
hudakz | 0:84353c479782 | 1034 | ep, |
hudakz | 0:84353c479782 | 1035 | bmREQ_GET_DESCR, |
hudakz | 0:84353c479782 | 1036 | USB_REQUEST_GET_DESCRIPTOR, |
hudakz | 0:84353c479782 | 1037 | conf, |
hudakz | 0:84353c479782 | 1038 | USB_DESCRIPTOR_CONFIGURATION, |
hudakz | 0:84353c479782 | 1039 | 0x0000, |
hudakz | 0:84353c479782 | 1040 | total, |
hudakz | 0:84353c479782 | 1041 | bufSize, |
hudakz | 0:84353c479782 | 1042 | buf, |
hudakz | 0:84353c479782 | 1043 | p |
hudakz | 0:84353c479782 | 1044 | ) |
hudakz | 0:84353c479782 | 1045 | ); |
hudakz | 0:84353c479782 | 1046 | } |
hudakz | 0:84353c479782 | 1047 | |
hudakz | 0:84353c479782 | 1048 | //get string descriptor |
hudakz | 1:2263e77400e9 | 1049 | uint8_t Usb::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) |
hudakz | 0:84353c479782 | 1050 | { |
hudakz | 0:84353c479782 | 1051 | return |
hudakz | 0:84353c479782 | 1052 | ( |
hudakz | 0:84353c479782 | 1053 | ctrlReq |
hudakz | 0:84353c479782 | 1054 | ( |
hudakz | 0:84353c479782 | 1055 | addr, |
hudakz | 0:84353c479782 | 1056 | ep, |
hudakz | 0:84353c479782 | 1057 | bmREQ_GET_DESCR, |
hudakz | 0:84353c479782 | 1058 | USB_REQUEST_GET_DESCRIPTOR, |
hudakz | 0:84353c479782 | 1059 | index, |
hudakz | 0:84353c479782 | 1060 | USB_DESCRIPTOR_STRING, |
hudakz | 0:84353c479782 | 1061 | langid, |
hudakz | 0:84353c479782 | 1062 | ns, |
hudakz | 0:84353c479782 | 1063 | ns, |
hudakz | 0:84353c479782 | 1064 | dataptr, |
hudakz | 0:84353c479782 | 1065 | NULL |
hudakz | 0:84353c479782 | 1066 | ) |
hudakz | 0:84353c479782 | 1067 | ); |
hudakz | 0:84353c479782 | 1068 | } |
hudakz | 0:84353c479782 | 1069 | |
hudakz | 0:84353c479782 | 1070 | //set address |
hudakz | 1:2263e77400e9 | 1071 | uint8_t Usb::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) |
hudakz | 0:84353c479782 | 1072 | { |
hudakz | 0:84353c479782 | 1073 | uint8_t rcode = ctrlReq |
hudakz | 0:84353c479782 | 1074 | ( |
hudakz | 0:84353c479782 | 1075 | oldaddr, |
hudakz | 0:84353c479782 | 1076 | ep, |
hudakz | 0:84353c479782 | 1077 | bmREQ_SET, |
hudakz | 0:84353c479782 | 1078 | USB_REQUEST_SET_ADDRESS, |
hudakz | 0:84353c479782 | 1079 | newaddr, |
hudakz | 0:84353c479782 | 1080 | 0x00, |
hudakz | 0:84353c479782 | 1081 | 0x0000, |
hudakz | 0:84353c479782 | 1082 | 0x0000, |
hudakz | 0:84353c479782 | 1083 | 0x0000, |
hudakz | 0:84353c479782 | 1084 | NULL, |
hudakz | 0:84353c479782 | 1085 | NULL |
hudakz | 0:84353c479782 | 1086 | ); |
hudakz | 0:84353c479782 | 1087 | //wait_ms(2); //per USB 2.0 sect.9.2.6.3 |
hudakz | 0:84353c479782 | 1088 | |
hudakz | 0:84353c479782 | 1089 | wait_ms(300); // Older spec says you should wait at least 200ms |
hudakz | 0:84353c479782 | 1090 | return rcode; |
hudakz | 0:84353c479782 | 1091 | |
hudakz | 0:84353c479782 | 1092 | //return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL)); |
hudakz | 0:84353c479782 | 1093 | } |
hudakz | 0:84353c479782 | 1094 | |
hudakz | 0:84353c479782 | 1095 | //set configuration |
hudakz | 1:2263e77400e9 | 1096 | uint8_t Usb::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) |
hudakz | 0:84353c479782 | 1097 | { |
hudakz | 0:84353c479782 | 1098 | return |
hudakz | 0:84353c479782 | 1099 | ( |
hudakz | 0:84353c479782 | 1100 | ctrlReq |
hudakz | 0:84353c479782 | 1101 | ( |
hudakz | 0:84353c479782 | 1102 | addr, |
hudakz | 0:84353c479782 | 1103 | ep, |
hudakz | 0:84353c479782 | 1104 | bmREQ_SET, |
hudakz | 0:84353c479782 | 1105 | USB_REQUEST_SET_CONFIGURATION, |
hudakz | 0:84353c479782 | 1106 | conf_value, |
hudakz | 0:84353c479782 | 1107 | 0x00, |
hudakz | 0:84353c479782 | 1108 | 0x0000, |
hudakz | 0:84353c479782 | 1109 | 0x0000, |
hudakz | 0:84353c479782 | 1110 | 0x0000, |
hudakz | 0:84353c479782 | 1111 | NULL, |
hudakz | 0:84353c479782 | 1112 | NULL |
hudakz | 0:84353c479782 | 1113 | ) |
hudakz | 0:84353c479782 | 1114 | ); |
hudakz | 0:84353c479782 | 1115 | } |
hudakz | 0:84353c479782 | 1116 | #endif // defined(USB_METHODS_INLINE) |