/* Copyright (C) 2015-2016 Andrew J. Kroll and Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact information ------------------- Circuits At Home, LTD Web : https://www.circuitsathome.com e-mail : support@circuitsathome.com */ #if !defined(_UHS_host_h_) || defined(__ADDRESS_H__) #error "Never include UHS_address.h directly; include UHS_Usb.h instead" #else #define __ADDRESS_H__ /* NAK powers. To save space in endpoint data structure, amount of retries before giving up and returning 0x4 is stored in */ /* bmNakPower as a power of 2. The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */ #define UHS_USB_NAK_MAX_POWER 14 // NAK binary order maximum value #define UHS_USB_NAK_DEFAULT 13 // default 16K-1 NAKs before giving up #define UHS_USB_NAK_NOWAIT 1 // Single NAK stops transfer #define UHS_USB_NAK_NONAK 0 // Do not count NAKs, stop retrying after USB Timeout. Try not to use this. #define bmUSB_DEV_ADDR_PORT 0x07 #define bmUSB_DEV_ADDR_PARENT 0x78 #define bmUSB_DEV_ADDR_HUB 0x40 // TODO: embed parent? struct UHS_EpInfo { uint8_t epAddr; // Endpoint address uint8_t bIface; uint16_t maxPktSize; // Maximum packet size union { uint8_t epAttribs; struct { uint8_t bmSndToggle : 1; // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise uint8_t bmRcvToggle : 1; // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise uint8_t bmNeedPing : 1; // 1 == ping protocol needed for next out packet uint8_t bmNakPower : 5; // Binary order for NAK_LIMIT value } __attribute__((packed)); }; } __attribute__((packed)); // TODO: embed parent address and port into epinfo struct, // and nuke this address stupidity. // This is a compact scheme. Should also support full spec. // This produces a 7 hub limit, 49 devices + 7 hubs, 56 total. // // 7 6 5 4 3 2 1 0 // --------------------------------- // | | H | P | P | P | A | A | A | // --------------------------------- // // H - if 1 the address is a hub address // P - parent hub number // A - port number of parent // struct UHS_DeviceAddress { union { struct { uint8_t bmAddress : 3; // port number uint8_t bmParent : 3; // parent hub address uint8_t bmHub : 1; // hub flag uint8_t bmReserved : 1; // reserved, must be zero } __attribute__((packed)); uint8_t devAddress; }; } __attribute__((packed)); struct UHS_Device { volatile UHS_EpInfo *epinfo[UHS_HOST_MAX_INTERFACE_DRIVERS]; // endpoint info pointer UHS_DeviceAddress address; uint8_t epcount; // number of endpoints uint8_t speed; // indicates device speed } __attribute__((packed)); typedef void (*UsbDeviceHandleFunc)(UHS_Device *pdev); class AddressPool { UHS_EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device // In order to avoid hub address duplication, this should use bits uint8_t hubCounter; // hub counter UHS_Device thePool[UHS_HOST_MAX_INTERFACE_DRIVERS]; // Initializes address pool entry void UHS_NI InitEntry(uint8_t index) { thePool[index].address.devAddress = 0; thePool[index].epcount = 1; thePool[index].speed = 0; for(uint8_t i = 0; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) { thePool[index].epinfo[i] = &dev0ep; } }; // Returns thePool index for a given address uint8_t UHS_NI FindAddressIndex(uint8_t address = 0) { for(uint8_t i = 1; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) { if(thePool[i].address.devAddress == address) return i; } return 0; }; // Returns thePool child index for a given parent uint8_t UHS_NI FindChildIndex(UHS_DeviceAddress addr, uint8_t start = 1) { for(uint8_t i = (start < 1 || start >= UHS_HOST_MAX_INTERFACE_DRIVERS) ? 1 : start; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) { if(thePool[i].address.bmParent == addr.bmAddress) return i; } return 0; }; // Frees address entry specified by index parameter void UHS_NI FreeAddressByIndex(uint8_t index) { // Zero field is reserved and should not be affected if(index == 0) return; UHS_DeviceAddress uda = thePool[index].address; // If a hub was switched off all port addresses should be freed if(uda.bmHub == 1) { for(uint8_t i = 1; (i = FindChildIndex(uda, i));) FreeAddressByIndex(i); // FIXME: use BIT MASKS // If the hub had the last allocated address, hubCounter should be decremented if(hubCounter == uda.bmAddress) hubCounter--; } InitEntry(index); } void InitAllAddresses() { for(uint8_t i = 1; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) InitEntry(i); hubCounter = 0; }; public: AddressPool() { hubCounter = 0; // Zero address is reserved InitEntry(0); thePool[0].epinfo[0] = &dev0ep; dev0ep.epAddr = 0; #if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE dev0ep.maxPktSize = 0x40; //starting at 0x40 and work down #else dev0ep.maxPktSize = 0x08; #endif dev0ep.epAttribs = 0; //set DATA0/1 toggles to 0 dev0ep.bmNakPower = UHS_USB_NAK_MAX_POWER; InitAllAddresses(); }; // Returns a pointer to a specified address entry UHS_Device* UHS_NI GetUsbDevicePtr(uint8_t addr) { if(!addr) return thePool; uint8_t index = FindAddressIndex(addr); return (!index) ? NULL : &thePool[index]; }; // Allocates new address uint8_t UHS_NI AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 1) { /* if (parent != 0 && port == 0) USB_HOST_SERIAL.println("PRT:0"); */ UHS_DeviceAddress _parent; _parent.devAddress = parent; if(_parent.bmReserved || port > 7) //if(parent > 127 || port > 7) return 0; // FIXME: use BIT MASKS if(is_hub && hubCounter == 7) return 0; // finds first empty address entry starting from one uint8_t index = FindAddressIndex(0); if(!index) // if empty entry is not found return 0; UHS_DeviceAddress addr; addr.devAddress = port; addr.bmParent = _parent.bmAddress; // FIXME: use BIT MASKS if(is_hub) { hubCounter++; addr.bmHub = 1; addr.bmAddress = hubCounter; } thePool[index].address = addr; #if DEBUG_PRINTF_EXTRA_HUGE #ifdef UHS_DEBUG_USB_ADDRESS printf("Address: %x (%x.%x.%x)\r\n", addr.devAddress, addr.bmHub, addr.bmParent, addr.bmAddress); #endif #endif return thePool[index].address.devAddress; }; void UHS_NI FreeAddress(uint8_t addr) { // if the root hub is disconnected all the addresses should be initialized if(addr == 0x41) { InitAllAddresses(); return; } uint8_t index = FindAddressIndex(addr); FreeAddressByIndex(index); }; }; #endif // __ADDRESS_H__