/** * 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 */ #pragma once #ifndef _usb_h_ #error "Never include address.h directly; include Usb.h instead" #endif /* 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 USB_NAK_MAX_POWER 15 //NAK binary order maximum value #define USB_NAK_DEFAULT 14 //default 32K-1 NAKs before giving up #define USB_NAK_NOWAIT 1 //Single NAK stops transfer #define USB_NAK_NONAK 0 //Do not count NAKs, stop retrying after USB Timeout struct EpInfo { uint8_t epAddr; // Endpoint address uint8_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 bmNakPower : 6; // Binary order for NAK_LIMIT value } __attribute__((packed)); }; } __attribute__((packed)); // 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 address // A - device address / port number in case of hub // struct UsbDeviceAddress { union { struct { uint8_t bmAddress : 3; // device address/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)); #define bmUSB_DEV_ADDR_ADDRESS 0x07 #define bmUSB_DEV_ADDR_PARENT 0x38 #define bmUSB_DEV_ADDR_HUB 0x40 struct UsbDevice { EpInfo *epinfo; // endpoint info pointer UsbDeviceAddress address; uint8_t epcount; // number of endpoints bool lowspeed; // indicates if a device is the low speed one // uint8_t devclass; // device class } __attribute__((packed)); class AddressPool { public: virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) = 0; virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) = 0; virtual void FreeAddress(uint8_t addr) = 0; }; typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev); #define ADDR_ERROR_INVALID_INDEX 0xFF #define ADDR_ERROR_INVALID_ADDRESS 0xFF template class AddressPoolImpl : public AddressPool { EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device uint8_t hubCounter; // hub counter is kept // in order to avoid hub address duplication UsbDevice thePool[MAX_DEVICES_ALLOWED]; // Initialize address pool entry void InitEntry(uint8_t index) { thePool[index].address.devAddress = 0; thePool[index].epcount = 1; thePool[index].lowspeed = 0; thePool[index].epinfo = &dev0ep; } // Return thePool index for a given address uint8_t FindAddressIndex(uint8_t address = 0) { for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) if (thePool[i].address.devAddress == address) return i; return 0; } // Return thePool child index for a given parent uint8_t FindChildIndex(UsbDeviceAddress addr, uint8_t start = 1) { for (uint8_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++) { if (thePool[i].address.bmParent == addr.bmAddress) return i; } return 0; } // Frees address entry specified by index parameter void FreeAddressByIndex(uint8_t index) { // Zero field is reserved and should not be affected if (index == 0) return; UsbDeviceAddress 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); // If the hub had the last allocated address, hubCounter should be decremented if (hubCounter == uda.bmAddress) hubCounter--; } InitEntry(index); } // Initialize the whole address pool at once void InitAllAddresses() { for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) InitEntry(i); hubCounter = 0; } public: AddressPoolImpl() : hubCounter(0) { // Zero address is reserved InitEntry(0); thePool[0].address.devAddress = 0; thePool[0].epinfo = &dev0ep; dev0ep.epAddr = 0; dev0ep.maxPktSize = 8; dev0ep.bmSndToggle = 0; // Set DATA0/1 toggles to 0 dev0ep.bmRcvToggle = 0; dev0ep.bmNakPower = USB_NAK_MAX_POWER; InitAllAddresses(); } // Return a pointer to a specified address entry virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) { if (!addr) return thePool; uint8_t index = FindAddressIndex(addr); return index ? thePool + index : nullptr; } // Perform an operation specified by pfunc for each addressed device void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) { if (pfunc) { for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) if (thePool[i].address.devAddress) pfunc(thePool + i); } } // Allocate new address virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) { /* if (parent != 0 && port == 0) USB_HOST_SERIAL.println("PRT:0"); */ UsbDeviceAddress _parent; _parent.devAddress = parent; if (_parent.bmReserved || port > 7) //if(parent > 127 || port > 7) return 0; if (is_hub && hubCounter == 7) return 0; // finds first empty address entry starting from one uint8_t index = FindAddressIndex(0); if (!index) return 0; // if empty entry is not found if (_parent.devAddress == 0) { if (is_hub) { thePool[index].address.devAddress = 0x41; hubCounter++; } else thePool[index].address.devAddress = 1; return thePool[index].address.devAddress; } UsbDeviceAddress addr; addr.devAddress = 0; // Ensure all bits are zero addr.bmParent = _parent.bmAddress; if (is_hub) { addr.bmHub = 1; addr.bmAddress = ++hubCounter; } else { addr.bmHub = 0; addr.bmAddress = port; } thePool[index].address = addr; /* USB_HOST_SERIAL.print("Addr:"); USB_HOST_SERIAL.print(addr.bmHub, HEX); USB_HOST_SERIAL.print("."); USB_HOST_SERIAL.print(addr.bmParent, HEX); USB_HOST_SERIAL.print("."); USB_HOST_SERIAL.println(addr.bmAddress, HEX); */ return thePool[index].address.devAddress; } // Empty the pool entry virtual void FreeAddress(uint8_t addr) { // if the root hub is disconnected all the addresses should be initialized if (addr == 0x41) { InitAllAddresses(); return; } FreeAddressByIndex(FindAddressIndex(addr)); } // Return number of hubs attached // It can be helpful to find out if hubs are attached when getting the exact number of hubs. //uint8_t GetNumHubs() { return hubCounter; } //uint8_t GetNumDevices() { // uint8_t counter = 0; // for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) // if (thePool[i].address != 0); counter++; // return counter; //} };