USB Host WAN Dongle library
Fork of USBHostWANDongle_bleedingedge by
Revision 0:ae46a0638b2c, committed 2012-05-24
- Comitter:
- donatien
- Date:
- Thu May 24 16:39:35 2012 +0000
- Child:
- 1:49df46e3295c
- Commit message:
- Initial Commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USB3GModule/IUSBHostSerial.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,90 @@ +/* IUSBHostSerial.h */ +/* +Copyright (C) 2012 ARM Limited. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + + +#ifndef IUSBHOSTSERIAL_H_ +#define IUSBHOSTSERIAL_H_ + +/** + * Generic interface to abstract 3G dongles' impl + */ + +#include "IUSBHostSerialListener.h" + +class IUSBHostSerial { +public: + + enum IrqType { + RxIrq, + TxIrq + }; + + /* + * Get a char from the dongle's serial interface + */ + virtual int getc() = 0; + + /* + * Put a char to the dongle's serial interface + */ + virtual int putc(int c) = 0; + + /* + * Read a packet from the dongle's serial interface, to be called after multiple getc() calls + */ + virtual int readPacket() = 0; + + /* + * Write a packet to the dongle's serial interface, to be called after multiple putc() calls + */ + virtual int writePacket() = 0; + + /** + * Check the number of bytes available. + * + * @returns the number of bytes available + */ + virtual int readable() = 0; + + /** + * Check the free space in output. + * + * @returns the number of bytes available + */ + virtual int writeable() = 0; + + /** + * Attach a handler to call when a packet is received / when a packet has been transmitted. + * + * @param pListener instance of the listener deriving from the IUSBHostSerialListener + */ + virtual void attach(IUSBHostSerialListener* pListener) = 0; + + /** + * Enable or disable readable/writeable callbacks + */ + virtual void setupIrq(bool en, IrqType irq = RxIrq) = 0; + +}; + +#endif /* IUSBHOSTSERIAL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USB3GModule/IUSBHostSerialListener.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,36 @@ +/* IUSBHostSerialListener.h */ +/* +Copyright (C) 2012 ARM Limited. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + + +#ifndef IUSBHOSTSERIALLISTENER_H_ +#define IUSBHOSTSERIALLISTENER_H_ + +class IUSBHostSerialListener +{ +public: + virtual void readable() = 0; //Called when new data is available + virtual void writeable() = 0; //Called when new space is available +}; + + +#endif /* IUSBHOSTSERIALLISTENER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USB3GModule/WANDongle.cpp Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,441 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#define __DEBUG__ 4 //Maximum verbosity +#ifndef __MODULE__ +#define __MODULE__ "WANDongle.cpp" +#endif + +#include "dbg.h" +#include <cstdint> +#include "rtos.h" + +#include "WANDongle.h" + +//TODO refactor + +//Huawei K3770 (Vodafone) +uint8_t vodafone_k3770_switch_packet[] = { + 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0, 0, 0, 0, 0, 0, 0, 0x11, 0x06, 0x20, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +WANDongle::WANDongle() : cb_tx_en(false), cb_rx_en(false), listener(NULL) +{ + host = USBHost::getHostInst(); + init(); +} + + +bool WANDongle::connected() { + return dev_connected; +} + +bool WANDongle::tryConnect() +{ + bool found = false; + + //FIXME should run on USB thread + + DBG("Trying to connect device"); + + if (dev_connected) { + return true; + } + + host->lock(); + + for (int i = 0; i < MAX_DEVICE_NB; i++) { + if ((dev = host->getDevice(i)) != NULL) { + + DBG("Found one device, reset it"); + host->resetDevice(dev); + + DBG("Enumerate"); + host->enumerate(dev); + + //Vodafone K3770 + if ((dev->getVid() == 0x12D1) && (dev->getPid() == 0x14D1)) { + DBG("Vodafone K3370 dongle detected!!!"); + + for (int j = 0; j < dev->getNbInterface(); j++) { + + if (dev->getInterface(j)->intf_class == MSD_CLASS) { + + if ( (bulk_out = dev->getEndpoint(j, BULK_ENDPOINT, OUT)) != NULL ) { + + DBG("MSD FOUND on device %p, intf %d, WILL NOW SWITCH MODE!!!", (void *)dev, j); + + host->bulkWrite(dev, (Endpoint *)bulk_out, vodafone_k3770_switch_packet, 31); + + DBG("SWITCH PACKET SENT"); + host->unlock(); + + + Thread::wait(500); //Not in a thread + + // now wait for the new device connected......... :( + while (1) { + + if (found) { + break; + } + + Thread::wait(100); + + host->lock(); + + + for (int i = 0; i < MAX_DEVICE_NB; i++) { + if ((dev = host->getDevice(i)) != NULL) { + host->resetDevice(dev); + host->enumerate(dev); + + //Vodafone K3770 + if ((dev->getVid() == 0x12D1) && (dev->getPid() == 0x14C9)) { + DBG("SWITCH MODE OK!!!!!!!!!!\r\n"); + found = true; + break; + } + } + } + + host->unlock(); + + } + host->lock(); + + if (fetchEndpoints()) { + DBG("ep: %p, %p\r\n", bulk_in, bulk_out); + max_out_size = bulk_out->getSize(); + if( max_out_size > WANDONGLE_MAX_OUTEP_SIZE ) + { + max_out_size = WANDONGLE_MAX_OUTEP_SIZE; + } + host->unlock(); + return true; + } + host->unlock(); + } + } + } + } else if ((dev->getVid() == 0x12D1) && (dev->getPid() == 0x14C9)) { + if (fetchEndpoints()) { + DBG("ep: %p, %p\r\n", bulk_in, bulk_out); + max_out_size = bulk_out->getSize(); + if( max_out_size > WANDONGLE_MAX_OUTEP_SIZE ) + { + max_out_size = WANDONGLE_MAX_OUTEP_SIZE; + } + host->unlock(); + return true; + } + } + } + } + host->unlock(); + bulk_in = NULL; + bulk_out = NULL; + dev_connected = false; + return false; +} + +int WANDongle::readPacket() +{ + rx_mtx.lock(); + if(lock_rx) + { + ERR("Fail"); + rx_mtx.unlock(); + return -1; + } + + lock_rx = true; //Receiving + rx_mtx.unlock(); +// DBG("readPacket"); + //lock_rx.lock(); + host->lock(); + USB_TYPE res = host->bulkRead(dev, (Endpoint *)bulk_in, buf_in, ((Endpoint *)bulk_in)->getSize(), false); //Queue transfer + if(res != USB_TYPE_PROCESSING) + { + host->unlock(); + //lock_rx.unlock(); + ERR("host->bulkRead() returned %d", res); + Thread::wait(100); + return -1; + } + host->unlock(); + return 0; +} + +int WANDongle::writePacket() +{ + tx_mtx.lock(); + if(lock_tx) + { + ERR("Fail"); + tx_mtx.unlock(); + return -1; + } + + lock_tx = true; //Transmitting + tx_mtx.unlock(); +// DBG("writePacket"); + + //lock_tx.lock(); + host->lock(); + USB_TYPE res = host->bulkWrite(dev, (Endpoint *)bulk_out, buf_out, buf_out_len, false); //Queue transfer + if(res != USB_TYPE_PROCESSING) + { + host->unlock(); + //lock_tx.unlock(); + ERR("host->bulkWrite() returned %d", res); + Thread::wait(100); + return -1; + } + host->unlock(); + return 0; +} + +int WANDongle::putc(int c) +{ + tx_mtx.lock(); + if(!lock_tx) + { + if(buf_out_len < max_out_size) + { + buf_out[buf_out_len] = (uint8_t)c; + buf_out_len++; + } + } + else + { + ERR("CAN'T WRITE!"); + } + tx_mtx.unlock(); + return c; +} + +int WANDongle::getc() +{ + rx_mtx.lock(); + int c = 0; + if(!lock_rx) + { + if(buf_in_read_pos < buf_in_len) + { + c = (int)buf_in[buf_in_read_pos]; + buf_in_read_pos++; + } + } + else + { + ERR("CAN'T READ!"); + } + rx_mtx.unlock(); + return c; +} + +int WANDongle::readable() +{ + rx_mtx.lock(); + if (lock_rx) + { + rx_mtx.unlock(); + return 0; + } + + /* if( !lock_rx.trylock() ) + { + return 0; + }*/ + int res = buf_in_len - buf_in_read_pos; + //lock_rx.unlock(); + rx_mtx.unlock(); + return res; +} + +int WANDongle::writeable() +{ + tx_mtx.lock(); + if (lock_tx) + { + tx_mtx.unlock(); + return 0; + } + + /*if( !lock_tx.trylock() ) + { + return 0; + }*/ + int res = max_out_size - buf_out_len; + tx_mtx.unlock(); + //lock_tx.unlock(); + return res; +} + +void WANDongle::attach(IUSBHostSerialListener* pListener) +{ + if(pListener == NULL) + { + setupIrq(false, RxIrq); + setupIrq(false, TxIrq); + } + listener = pListener; + if(pListener != NULL) + { + setupIrq(true, RxIrq); + setupIrq(true, TxIrq); + } +} + +void WANDongle::setupIrq(bool en, IrqType irq /*= RxIrq*/) +{ + switch(irq) + { + case RxIrq: + rx_mtx.lock(); + cb_rx_en = en; + if(en && cb_rx_pending) + { + cb_rx_pending = false; + rx_mtx.unlock(); + listener->readable(); //Process the interrupt that was raised + } + else + { + rx_mtx.unlock(); + } + break; + case TxIrq: + tx_mtx.lock(); + cb_tx_en = en; + if(en && cb_tx_pending) + { + cb_tx_pending = false; + tx_mtx.unlock(); + listener->writeable(); //Process the interrupt that was raised + } + else + { + tx_mtx.unlock(); + } + break; + } +} + +//Private methods + +void WANDongle::init() +{ + dev_connected = false; + + bulk_in = NULL; + bulk_out = NULL; + + buf_out_len = 0; + lock_tx = false; + cb_tx_en = false; + cb_tx_pending = false; + + buf_in_len = 0; + buf_in_read_pos = 0; + lock_rx = false; + cb_rx_en = false; + cb_rx_pending = false; + +} + +bool WANDongle::fetchEndpoints() +{ + bulk_in = dev->getEndpoint(0, BULK_ENDPOINT, IN); + bulk_out = dev->getEndpoint(0, BULK_ENDPOINT, OUT); + if ((bulk_in != NULL) && (bulk_out != NULL)) + { + + DBG("SERIAL FOUND on device %p, intf %d, bulk_in: %p, bulk_out: %p\r\n", + (void *)dev, 0, (void *)bulk_in, (void *)bulk_out); + + bulk_in->attach(this, &WANDongle::rxHandler); + bulk_out->attach(this, &WANDongle::txHandler); + host->lock(); + host->registerDriver(dev, 0, this, &WANDongle::init); + host->unlock(); + dev_connected = true; + + readPacket(); //Start receiving data + + return true; + } + return false; +} + +void WANDongle::rxHandler() +{ + if (((Endpoint *) bulk_in)->getState() == USB_TYPE_IDLE) //Success + { + buf_in_read_pos = 0; + buf_in_len = ((Endpoint *) bulk_in)->getLengthTransferred(); //Update length + //lock_rx.unlock(); + rx_mtx.lock(); + lock_rx = false; //Transmission complete + if(cb_rx_en) + { + rx_mtx.unlock(); + listener->readable(); //Call handler from the IRQ context + //readPacket() should be called by the handler subsequently once the buffer has been emptied + } + else + { + cb_rx_pending = true; //Queue the callback + rx_mtx.unlock(); + } + + } + else //Error, try reading again + { + //lock_rx.unlock(); + readPacket(); + } +} + +void WANDongle::txHandler() +{ + if (((Endpoint *) bulk_out)->getState() == USB_TYPE_IDLE) //Success + { + tx_mtx.lock(); + buf_out_len = 0; //Reset length + lock_tx = false; //Transmission complete + //lock_tx.unlock(); + if(cb_tx_en) + { + tx_mtx.unlock(); + listener->writeable(); //Call handler from the IRQ context + //writePacket() should be called by the handler subsequently once the buffer has been filled + } + else + { + cb_tx_pending = true; //Queue the callback + tx_mtx.unlock(); + } + } + else //Error, try reading again + { + //lock_tx.unlock(); + writePacket(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USB3GModule/WANDongle.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,140 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef WANDONGLE_H +#define WANDONGLE_H + +#include "USBHost.h" +#include "IUSBHostSerial.h" + +#include "rtos.h" + + +#define WANDONGLE_MAX_OUTEP_SIZE 64 +#define WANDONGLE_MAX_INEP_SIZE 64 + +/** A class to use a WAN (3G/LTE) access dongle + * + */ +class WANDongle : public IUSBHostSerial { +public: + /* + * Constructor + * + * @param rootdir mount name + */ + WANDongle(); + + /* + * Check if a serial port device is connected + * + * @return true if a serial device is connected + */ + bool connected(); + + /* + * Try to connect device + * + * * @return true if connection was successful + */ + bool tryConnect(); + + /* + * Get a char from the dongle's serial interface + */ + virtual int getc(); + + /* + * Put a char to the dongle's serial interface + */ + virtual int putc(int c); + + //void test(); + + /* + * Read a packet from the dongle's serial interface, to be called after multiple getc() calls + */ + virtual int readPacket(); + + /* + * Write a packet to the dongle's serial interface, to be called after multiple putc() calls + */ + virtual int writePacket(); + + /** + * Check the number of bytes available. + * + * @returns the number of bytes available + */ + virtual int readable(); + + /** + * Check the free space in output. + * + * @returns the number of bytes available + */ + virtual int writeable(); + + /** + * Attach a handler to call when a packet is received / when a packet has been transmitted. + * + * @param pListener instance of the listener deriving from the IUSBHostSerialListener + */ + virtual void attach(IUSBHostSerialListener* pListener); + + /** + * Enable or disable readable/writeable callbacks + */ + virtual void setupIrq(bool en, IrqType irq = RxIrq); + + +protected: + Endpoint * bulk_in; + Endpoint * bulk_out; + USBHost * host; + USBDeviceConnected * dev; + bool dev_connected; + + uint8_t buf_out[WANDONGLE_MAX_OUTEP_SIZE]; + volatile uint32_t buf_out_len; + uint32_t max_out_size; + volatile bool lock_tx; + //Mutex lock_tx; + volatile bool cb_tx_en; + volatile bool cb_tx_pending; + Mutex tx_mtx; + + uint8_t buf_in[WANDONGLE_MAX_INEP_SIZE]; + volatile uint32_t buf_in_len; + volatile uint32_t buf_in_read_pos; + volatile bool lock_rx; + //Mutex lock_rx; + volatile bool cb_rx_en; + volatile bool cb_rx_pending; + Mutex rx_mtx; + + IUSBHostSerialListener* listener; + + void init(); + void rxHandler(); + void txHandler(); + bool fetchEndpoints(); + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/Endpoint.cpp Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,228 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +#define __DEBUG__ 0 //Maximum verbosity +#ifndef __MODULE__ +#define __MODULE__ "Endpoint.cpp" +#endif + +#include "dbg.h" +#include <cstdint> + +#include "Endpoint.h" + + +void Endpoint::init(HCED * hced, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint32_t size, uint8_t ep_number, HCTD* td_list[2]) { + this->hced = hced; + this->type = type; + this->dir = /*(type == CONTROL_ENDPOINT) ? OUT :*/ dir; + setup = (type == CONTROL_ENDPOINT) ? true : false; + + //TDs have been allocated by the host + memcpy((HCTD**)this->td_list, td_list, sizeof(HCTD*)*2); //TODO: Maybe should add a param for td_list size... at least a define + memcpy(td_list[0], 0, sizeof(HCTD)); + memcpy(td_list[1], 0, sizeof(HCTD)); + + this->hced->control = 0; + //Empty queue + this->hced->tailTD = (uint32_t)td_list[0]; + this->hced->headTD = (uint32_t)td_list[0]; + this->hced->nextED = 0; + + this->hced->control = ((ep_number & 0x7F) << 7) // Endpoint address + | (type != CONTROL_ENDPOINT ? ( dir << 11) : 0 ) // direction : Out = 1, 2 = In + | ((size & 0x3ff) << 16); // MaxPkt Size + + //carry = false; + transfer_len = 0; + transferred = 0; + buf_start = 0; + nextEp = NULL; + + td_current = td_list[0]; + td_next = td_list[1]; + + state = USB_TYPE_IDLE; +} + +void Endpoint::setSize(uint32_t size) { + hced->control &= ~(0x3ff << 16); + hced->control |= (size << 16); +} + + +uint32_t Endpoint::getSize() { + return (hced->control >> 16) & 0x3ff; +} + +void Endpoint::setDeviceAddress(uint8_t addr) { + hced->control &= ~(0x7f); + hced->control |= (addr & 0x7F); +} + +uint8_t Endpoint::getDeviceAddress() { + return hced->control & 0x7f; +} + +void Endpoint::setSpeed(uint8_t speed) { + if(speed) { + DBG("SET LOW SPEED"); + } + hced->control &= ~(1 << 13); + hced->control |= (speed << 13); +} + + +void Endpoint::setNextToken(uint32_t token) { //Only for control Eps + switch (token) { + case TD_SETUP: + dir = OUT; + setup = true; + break; + case TD_IN: + dir = IN; + setup = false; + break; + case TD_OUT: + dir = OUT; + setup = false; + break; + } +} + +volatile HCTD* Endpoint::getNextTD() +{ + return td_current/*(HCTD*) hced->tailTD*/; //It's the tailing one +} + +void Endpoint::queueTransfer() { + //Try with OHCI impl + //Caller of getNextTD() has now populated the td + //So insert it into queue + + //Find an OTHER free td + //TODO: if we had more than 2 tds, this would have to be changed + /*HCTD* toSendTD = (HCTD*) hced->tailTD;*/ + //HCTD* freeTD; +/* + if( hced->tailTD == td_list[0] ) + { + freeTD = td_list[1]; + } + else *//*if( hced->tailTD == (uint32_t) td_list[1] )*/ + /*{ + freeTD = td_list[0]; + } + */ + + /* + freeTD->control = 0; + freeTD->currBufPtr = 0; + freeTD->bufEnd = 0; + freeTD->nextTD = 0; + + td_current = toSendTD; +*/ + transfer_len = td_current->bufEnd - td_current->currBufPtr + 1; + transferred = transfer_len; + buf_start = td_current->currBufPtr; + + //No add this free TD at this end of the queue + state = USB_TYPE_PROCESSING; + td_current->nextTD = (volatile uint32_t)td_next; + hced->tailTD = (volatile uint32_t)td_next; + + #if 0 + // if TD list empty -> we put the head of the list + if (!hced->headTD) { + state = USB_TYPE_IDLE; + hced->headTD = (uint32_t)(td); + hced->tailTD = (uint32_t)(td); + tailTD = (HCTD *) (hced->headTD); + //DBG("queue null transfer: endpoint: %p, %08X\r\n", this, (uint32_t)(td)); + } else { + state = USB_TYPE_PROCESSING; + td->nextTD = (uint32_t)headTD & ~(0x0f); + hced->headTD = (uint32_t)(td) | ((carry) ? 0x2 : 0); + } + headTD = (HCTD *) ((hced->headTD) & ~(0x3)); + transfer_len = td->bufEnd - td->currBufPtr + 1; + transferred = transfer_len; + buf_start = td->currBufPtr; +#endif + //printf("queue real transfer: endpoint: %p \t headTD: %p \t head: %08X \t tail: %08X \t td: %08X \t nexttd: %08X\r\n", this, hced->headTD, hced->headTD, ((HCTD *)((hced->headTD) & ~(0x0f)))->nextTD, toSendTD, toSendTD->nextTD); +} + +volatile HCTD * Endpoint::getProcessedTD() +{ + return td_current; +} + +void Endpoint::setLengthTransferred(int len) { + transferred = len; +} + +uint32_t Endpoint::getBufStart() { + return buf_start; +} + +void Endpoint::unqueueTransfer(volatile HCTD * td) { + //printf("unqueue transfer: %p on endpoint: %p\r\n", (void *)td, this); + //headTD = tailTD; //FIXME FIXME +// hced->headTD = hced->headTD | (td-> & 0x02); + if(td != td_current) + { + ERR("MISMATCH"); + ERR("this=%p, td_current = %p, td_next=%p, td=%p", this, td_current, td_next, td); + error(""); + } + td->control=0; + td->currBufPtr=0; + td->bufEnd=0; + td->nextTD=0; + hced->headTD = hced->tailTD | (hced->headTD & 0x2); //Carry bit + td_current = td_next; + td_next = td; + DBG("current:%p, next:%p", td_current, td_next); +} + +ENDPOINT_TYPE Endpoint::getType() { + return type; +} + + +Endpoint * Endpoint::nextEndpoint() { + return (Endpoint*)nextEp; +} + + +void Endpoint::queueEndpoint(Endpoint * ed) { + nextEp = ed; + hced->nextED = (ed == NULL) ? 0 : (uint32_t)ed->getHCED(); +} + +volatile HCED * Endpoint::getHCED() { + return hced; +} + + +volatile HCTD * Endpoint::getHeadTD() { + //return headTD; + return (volatile HCTD*) (hced->headTD & ~0xF); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/Endpoint.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,188 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ENDPOINT_H +#define ENDPOINT_H + +#include "stdint.h" +#include "FunctionPointer.h" +#include "USBHostTypes.h" + + +enum ENDPOINT_TYPE { + CONTROL_ENDPOINT = 0, + ISOCHRONOUS_ENDPOINT, + BULK_ENDPOINT, + INTERRUPT_ENDPOINT +}; + +enum ENDPOINT_DIRECTION { + OUT = 1, + IN +}; + +class Endpoint { +public: + /* + * Constructor + */ + Endpoint() {state = USB_TYPE_FREE; nextEp = NULL;}; + + /* + * Initialize an endpoint + * + * @param hced hced associated to the endpoint + * @param type endpoint type + * @param dir endpoint direction + * @param size endpoint size + * @param ep_number endpoint number + */ + void init(HCED * hced, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint32_t size, uint8_t ep_number, HCTD* td_list[2]); + + /* + * Set next token. Warining: only useful for the control endpoint + * + * @param token IN, OUT or SETUP token + */ + void setNextToken(uint32_t token); + + /* + * Queue an endpoint + * + * endpoint endpoint which will be queued in the linked list + */ + void queueEndpoint(Endpoint * endpoint); + + /* + * Get a td to be queued + * + * @returns td hctd which will be queued + */ + volatile HCTD* getNextTD(); + + /* + * Queue a transfer on the endpoint + * + */ + void queueTransfer(); + + /* + * Get the currently processed td + * + * @returns td hctd that was queued + */ + volatile HCTD * getProcessedTD(); + + /* + * Unqueue a transfer from the endpoint + * + * @param td hctd which will be unqueued + */ + void unqueueTransfer(volatile HCTD * td); + + /* + * Return the next endpoint in the linked list + * + * @returns next endpoint + */ + Endpoint * nextEndpoint(); + + /** + * Attach a member function to call when a transfer is finished + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template<typename T> + void attach(T* tptr, void (T::*mptr)(void)) { + if((mptr != NULL) && (tptr != NULL)) { + rx.attach(tptr, mptr); + } + } + + /** + * Attach a callback called when a transfer is finished + * + * @param fptr function pointer + */ + void attach(void (*fn)(void)) { + if(fn != NULL) { + rx.attach(fn); + } + } + + /* + * Call the handler associted to the end of a transfer + */ + void call() { + rx.call(); + }; + + + /* + * Setters + */ + void setState(USB_TYPE st) {state = st;} + void setDeviceAddress(uint8_t addr); + void setLengthTransferred(int len); + void setSpeed(uint8_t speed); + void setSize(uint32_t size); + void setDir(ENDPOINT_DIRECTION d) {dir = d;} + + /* + * Getters + */ + USB_TYPE getState() {return state;} + ENDPOINT_TYPE getType(); + uint8_t getDeviceAddress(); + int getLengthTransferred() {return transferred;} + uint32_t getBufStart(); + uint32_t getSize(); + volatile HCTD * getHeadTD(); + volatile HCED * getHCED(); + ENDPOINT_DIRECTION getDir() {return dir;} + bool isSetup() {return setup;} + + +private: + ENDPOINT_TYPE type; + volatile USB_TYPE state; + ENDPOINT_DIRECTION dir; + bool setup; + + int transfer_len; + int transferred; + uint32_t buf_start; + + FunctionPointer rx; + + Endpoint* nextEp; + + // Endpoint descriptor + volatile HCED * hced; + + volatile HCTD * td_list[2]; + volatile HCTD * td_current; + volatile HCTD * td_next; + /*bool carry;*/ + + int count; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBDeviceConnected.cpp Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,115 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "USBDeviceConnected.h" + +USBDeviceConnected::USBDeviceConnected() { + init(); +} + +void USBDeviceConnected::init() { + hub = 0; + port = 0; + vid = 0; + pid = 0; + nb_interf = 0; + enumerated = false; + activeAddr = false; + sizeControlEndpoint = 8; + device_class = 0; + device_subclass = 0; + proto = 0; + speed = false; + for (int i = 0; i < MAX_INTF; i++) { + memset((void *)&intf[i], 0, sizeof(INTERFACE)); + intf[i].in_use = false; + for (int j = 0; j < MAX_ENDPOINT_PER_INTERFACE; j++) { + intf[i].ep[j] = NULL; + } + } +} + +INTERFACE * USBDeviceConnected::getInterface(uint8_t index) { + if (index >= MAX_INTF) { + return NULL; + } + return &intf[index]; +} + +bool USBDeviceConnected::addInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) { + if ((intf_nb >= MAX_INTF) || (intf[intf_nb].in_use)) { + return false; + } + intf[intf_nb].in_use = true; + intf[intf_nb].intf_class = intf_class; + intf[intf_nb].intf_subclass = intf_subclass; + intf[intf_nb].intf_protocol = intf_protocol; + intf[intf_nb].nb_endpoint = 0; + nb_interf++; + return true; +} + +bool USBDeviceConnected::addEndpoint(uint8_t intf_nb, Endpoint * ept) { + if ((intf_nb >= MAX_INTF) || (intf[intf_nb].in_use == false) || (intf[intf_nb].nb_endpoint >= MAX_ENDPOINT_PER_INTERFACE)) { + return false; + } + intf[intf_nb].nb_endpoint++; + + for (int i = 0; i < MAX_ENDPOINT_PER_INTERFACE; i++) { + if (intf[intf_nb].ep[i] == NULL) { + intf[intf_nb].ep[i] = ept; + return true; + } + } + return false; +} + +void USBDeviceConnected::init(uint8_t hub, uint8_t port, bool lowSpeed) { + init(); + this->hub = hub; + this->port = port; + this->speed = lowSpeed; +} + +void USBDeviceConnected::disconnect() { + for(int i = 0; i < nb_interf; i++) { + intf[i].detach.call(); + } + init(); +} + + + +Endpoint * USBDeviceConnected::getEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) { + if (intf_nb >= MAX_INTF) { + return NULL; + } + for (int i = 0; i < MAX_ENDPOINT_PER_INTERFACE; i++) { + if ((intf[intf_nb].ep[i]->getType() == type) && (intf[intf_nb].ep[i]->getDir() == dir)) { + return intf[intf_nb].ep[i]; + } + } + return NULL; +} + +Endpoint * USBDeviceConnected::getEndpoint(uint8_t intf_nb, uint8_t index) { + if ((intf_nb >= MAX_INTF) || (index >= MAX_ENDPOINT_PER_INTERFACE)) { + return NULL; + } + return intf[intf_nb].ep[index]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBDeviceConnected.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,203 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBDEVICECONNECTED_H +#define USBDEVICECONNECTED_H + +#include "stdint.h" +#include "Endpoint.h" + +#define MAX_ENDPOINT_PER_INTERFACE 4 +#define MAX_INTF 4 + +typedef struct { + bool in_use; + uint8_t nb_endpoint; + uint8_t intf_class; + uint8_t intf_subclass; + uint8_t intf_protocol; + Endpoint * ep[MAX_ENDPOINT_PER_INTERFACE]; + FunctionPointer detach; +}INTERFACE; + + +class USBDeviceConnected { +public: + + /* + * Constructor + */ + USBDeviceConnected(); + + /* + * Attach an endpoint to this device + * + * @param ep pointeur on the endpoint which will be attached + * @returns true if successful, false otherwise + */ + bool addEndpoint(uint8_t intf_nb, Endpoint * ep); + + /* + * Retrieve an endpoint by its TYPE and DIRECTION + * + * @param type type of the endpoint looked for + * @param direction of the endpoint looked for + * @returns pointer on the endpoint if found, NULL otherwise + */ + Endpoint * getEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); + + /* + * Retrieve an endpoint by its index + * + * @param index index of the endpoint + * @returns pointer on the endpoint if found, NULL otherwise + */ + Endpoint * getEndpoint(uint8_t intf_nb, uint8_t index); + + bool addInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); + + uint8_t getNbInterface() {return nb_interf;}; + + INTERFACE * getInterface(uint8_t index); + + /** + * Attach a member function to call when a the device has been disconnected + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template<typename T> + void onDisconnect(uint8_t intf_nb, T* tptr, void (T::*mptr)(void)) { + if ((mptr != NULL) && (tptr != NULL)) { + intf[intf_nb].detach.attach(tptr, mptr); + } + } + + /** + * Attach a callback called when the device has been disconnected + * + * @param fptr function pointer + */ + void onDisconnect(uint8_t intf_nb, void (*fn)(void)) { + if (fn != NULL) { + intf[intf_nb].detach.attach(fn); + } + } + + /* + * Disconnect the device by calling a callback function registered by a driver + */ + void disconnect(); + + /* + * Setters + */ + void init(uint8_t hub, uint8_t port, bool lowSpeed); + void setAddress(uint8_t addr) { + this->addr = addr; + }; + void setVid(uint16_t vid) { + this->vid = vid; + }; + void setPid(uint16_t pid) { + this->pid = pid; + }; + void setClass(uint8_t device_class) { + this->device_class = device_class; + }; + void setSubClass(uint8_t device_subclass) { + this->device_subclass = device_subclass; + }; + void setProtocol(uint8_t pr) { + proto = pr; + }; + void setSizeControlEndpoint(uint32_t size) { + sizeControlEndpoint = size; + }; + void activeAddress() { + activeAddr = true; + }; + void setEnumerated() { + enumerated = true; + }; + + /* + * Getters + */ + uint8_t getPort() { + return port; + }; + uint8_t getHub() { + return hub; + }; + uint8_t getAddress() { + return addr; + }; + uint16_t getVid() { + return vid; + }; + uint16_t getPid() { + return pid; + }; + uint8_t getClass() { + return device_class; + }; + uint8_t getSubClass() { + return device_subclass; + }; + uint8_t getProtocol() { + return proto; + }; + bool getSpeed() { + return speed; + }; + uint32_t getSizeControlEndpoint() { + return sizeControlEndpoint; + }; + bool isActiveAddress() { + return activeAddr; + }; + bool isEnumerated() { + return enumerated; + }; + + +private: + INTERFACE intf[MAX_INTF]; + //Endpoint * ep[MAX_ENDPOINT_PER_DEVICE]; + uint32_t sizeControlEndpoint; + uint8_t hub; + uint8_t port; + uint16_t vid; + uint16_t pid; + uint8_t addr; + uint8_t device_class; + uint8_t device_subclass; + uint8_t proto; + bool speed; + bool activeAddr; + bool enumerated; + + uint8_t nb_interf; + + + void init(); + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHALHost.cpp Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,404 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ + + +#define __DEBUG__ 0 //WARN: ENABLING DEBUGGING HERE WILL PRINTF IN IRQS!! UNEXPECTED BEHAVIOUR MAY RESULT... +#ifndef __MODULE__ +#define __MODULE__ "USBHALHost.cpp" +#endif + +#include "dbg.h" +#include <cstdint> + +#include "mbed.h" +#include "USBHALHost.h" + +// bits of the USB/OTG clock control register +#define HOST_CLK_EN (1<<0) +#define DEV_CLK_EN (1<<1) +#define PORTSEL_CLK_EN (1<<3) +#define AHB_CLK_EN (1<<4) + +// bits of the USB/OTG clock status register +#define HOST_CLK_ON (1<<0) +#define DEV_CLK_ON (1<<1) +#define PORTSEL_CLK_ON (1<<3) +#define AHB_CLK_ON (1<<4) + +// we need host clock, OTG/portsel clock and AHB clock +#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN) + + + +#define HCCA_SIZE 0x100 +#define ED_SIZE 0x10 +#define TD_SIZE 0x10 + +#define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE)) + +static volatile __align(256) uint8_t usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM0"),aligned)); //256 bytes aligned! + +USBHALHost * USBHALHost::instHost; + +USBHALHost::USBHALHost() : thread(USBHALHost::staticCb, (void*)this, osPriorityNormal, 4*128) { + instHost = this; + memInit(); + memset((void*)usb_hcca, 0, HCCA_SIZE); + for (int i = 0; i < MAX_ENDPOINT; i++) { + edBufAlloc[i] = false; + } + for (int i = 0; i < MAX_TD; i++) { + tdBufAlloc[i] = false; + } +} + + +void USBHALHost::init() { + thread.signal_set(USBHALHOST_SIG_INIT); +} + + +uint32_t USBHALHost::controlHeadED() { + return LPC_USB->HcControlHeadED; +} + +uint32_t USBHALHost::bulkHeadED() { + return LPC_USB->HcBulkHeadED; +} + +uint32_t USBHALHost::interruptHeadED() { + return usb_hcca->IntTable[0]; +} + +void USBHALHost::updateBulkHeadED(uint32_t addr) { + LPC_USB->HcBulkHeadED = addr; +} + + +void USBHALHost::updateControlHeadED(uint32_t addr) { + LPC_USB->HcControlHeadED = addr; +} + +void USBHALHost::updateInterruptHeadED(uint32_t addr) { + usb_hcca->IntTable[0] = addr; +} + + +void USBHALHost::enableControlList() { + LPC_USB->HcCommandStatus = OR_CMD_STATUS_CLF; + LPC_USB->HcControl |= OR_CONTROL_CLE; //Enable control list +} + +void USBHALHost::enableBulkList() { + LPC_USB->HcCommandStatus = OR_CMD_STATUS_BLF; + LPC_USB->HcControl |= OR_CONTROL_BLE; //Enable bulk list +} + +void USBHALHost::enableInterruptList() { + LPC_USB->HcControl |= OR_CONTROL_PLE; +} + +bool USBHALHost::disableControlList() { + if(LPC_USB->HcControl & OR_CONTROL_CLE) + { + LPC_USB->HcControl &= ~OR_CONTROL_CLE; //Disable control list + return true; + } + else + { + return false; + } +} + +bool USBHALHost::disableBulkList() { + if(LPC_USB->HcControl & OR_CONTROL_BLE) + { + LPC_USB->HcControl &= ~OR_CONTROL_BLE; //Disable bulk list + return true; + } + else + { + return false; + } +} + +bool USBHALHost::disableInterruptList() { + if(LPC_USB->HcControl & OR_CONTROL_PLE) + { + LPC_USB->HcControl &= ~OR_CONTROL_PLE; //Disable interrupt list + return true; + } + else + { + return false; + } +} + +//Lock processing +void USBHALHost::lock() +{ + mtx.lock(); +} + +void USBHALHost::unlock() +{ + mtx.unlock(); +} + +void USBHALHost::memInit() { + usb_hcca = (volatile HCCA *)usb_buf; + usb_edBuf = usb_buf + HCCA_SIZE; + usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE); +} + +volatile uint8_t * USBHALHost::getED() { + for (int i = 0; i < MAX_ENDPOINT; i++) { + if ( !edBufAlloc[i] ) { + edBufAlloc[i] = true; + return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE); + } + } + perror("Could not allocate ED\r\n"); + return NULL; //Could not alloc ED +} + +volatile uint8_t * USBHALHost::getTD() { + int i; + for (i = 0; i < MAX_TD; i++) { + if ( !tdBufAlloc[i] ) { + tdBufAlloc[i] = true; + return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE); + } + } + perror("Could not allocate TD\r\n"); + return NULL; //Could not alloc TD +} + + +void USBHALHost::freeED(volatile uint8_t * ed) { + int i; + i = (ed - usb_edBuf) / ED_SIZE; + edBufAlloc[i] = false; +} + +void USBHALHost::freeTD(volatile uint8_t * td) { + int i; + i = (td - usb_tdBuf) / TD_SIZE; + tdBufAlloc[i] = false; +} + + +void USBHALHost::resetPort(int hub, int port) { + DBG("Resetting hub %d, port %d\n", hub, port); + if (hub == 0) { //Root hub + // USB 2.0 spec says at least 50ms delay before port reset + Thread::wait(200); + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset + while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS); + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal + // Wait for 100 MS after port reset + Thread::wait(200); + } else { + //TODO: Hubs + } +} + + +void USBHALHost::_usbisr(void) { + if (instHost) { + instHost->UsbIrqhandler(); + } +} + +void USBHALHost::UsbIrqhandler() { + if( LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable ) //Is there something to actually process? + { + NVIC_DisableIRQ(USB_IRQn); + NVIC_ClearPendingIRQ(USB_IRQn); + thread.signal_set(USBHALHOST_SIG_IRQ); //Signal processing thread + } + +} + +void USBHALHost::process() +{ + DBG("USB Process started"); + + lock(); + Thread::signal_wait(USBHALHOST_SIG_INIT); + + NVIC_DisableIRQ(USB_IRQn); // Disable the USB interrupt source + + LPC_SC->PCONP &= ~(1UL<<31); //Cut power + Thread::wait(200); + + // turn on power for USB + LPC_SC->PCONP |= (1UL<<31); + + // Enable USB host clock, port selection and AHB clock + LPC_USB->USBClkCtrl |= CLOCK_MASK; + + // Wait for clocks to become available + while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK); + + // it seems the bits[0:1] mean the following + // 0: U1=device, U2=host + // 1: U1=host, U2=host + // 2: reserved + // 3: U1=host, U2=device + // NB: this register is only available if OTG clock (aka "port select") is enabled!! + // since we don't care about port 2, set just bit 0 to 1 (U1=host) + LPC_USB->OTGStCtrl |= 1; + + // now that we've configured the ports, we can turn off the portsel clock + LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN; + + // configure USB D+/D- pins + // P0[29] = USB_D+, 01 + // P0[30] = USB_D-, 01 + LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28)); + LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // 0x14000000 + + LPC_USB->HcControl = 0; // HARDWARE RESET + LPC_USB->HcControlHeadED = 0; // Initialize Control list head to Zero + LPC_USB->HcBulkHeadED = 0; // Initialize Bulk list head to Zero + + //wait_ms(100); // Wait 50 ms before apply reset + Thread::wait(100); + + // SOFTWARE RESET + LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR; + LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL; // Write Fm Interval and Largest Data Packet Counter + LPC_USB->HcPeriodicStart = FI * 90 / 100; + + // Put HC in operational state + LPC_USB->HcControl = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER; + LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC; // Set Global Power + + LPC_USB->HcHCCA = (uint32_t)(usb_hcca); + LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; // Clear Interrrupt Status + + LPC_USB->HcInterruptEnable = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC; + + //DG: Do not set prio + //NVIC_SetPriority(USB_IRQn, 0); // highest priority + // Enable the USB Interrupt + NVIC_SetVector(USB_IRQn, (uint32_t)(_usbisr)); + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC; + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; + + NVIC_EnableIRQ(USB_IRQn); + + + // Check for any connected devices + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) { //Root device connected + //Device connected + Thread::wait(500); + DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus1); + deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed + } + + unlock(); + + + for(;;) + { + Thread::signal_wait(USBHALHOST_SIG_IRQ); //Wait for IRQ to process + + lock(); + DBG("Locked"); + + WARN("isr %08x [EN %08x]", LPC_USB->HcInterruptStatus, LPC_USB->HcInterruptEnable); + + //Now process IRQ + uint32_t int_status = LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable; + + if (int_status & OR_INTR_STATUS_RHSC) + { // Root hub status change interrupt + WARN("Port status %08x", LPC_USB->HcRhPortStatus1); + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC) + { + if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE) + { + + // When DRWE is on, Connect Status Change + // means a remote wakeup event. + + } + else + { + + // When DRWE is off, Connect Status Change + // is NOT a remote wakeup event + + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) + { //Root device connected + //Device connected + WARN("Device connected!!"); + // Thread::wait(500); + deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed + } + else + { //Root device disconnected + //Device disconnected + WARN("Device disconnected!!"); + Thread::wait(500); + if (!(int_status & OR_INTR_STATUS_WDH)) + { + usb_hcca->DoneHead = 0; + } + deviceDisconnected(0, 1, usb_hcca->DoneHead & 0xFFFFFFFE); + if (int_status & OR_INTR_STATUS_WDH) + { + usb_hcca->DoneHead = 0; + LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH; + } + + } + //TODO: HUBS + } + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC; + } + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC) + { + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; + //int_status &= ~OR_RH_PORT_PRSC; + } + LPC_USB->HcInterruptStatus = OR_INTR_STATUS_RHSC; + } + + if (int_status & OR_INTR_STATUS_WDH) + { // Writeback Done Head interrupt + transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE); + LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH; + } + + //IRQ Processed + + DBG("Unlocked"); + + NVIC_EnableIRQ(USB_IRQn); + + unlock(); + + } +} + +/*static*/ void USBHALHost::staticCb(void const* p) +{ + ((USBHALHost*)p)->process(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHALHost.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,206 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBHALHOST_H +#define USBHALHOST_H + +#include "rtos.h" + +#include "USBHostTypes.h" + +#define MAX_ENDPOINT 6 +#define MAX_TD (MAX_ENDPOINT*2) + +#define USBHALHOST_SIG_INIT 0x01 +#define USBHALHOST_SIG_IRQ 0x02 + +class USBHALHost { +public: + + /* + * Constructor + * init variables and memory where will be stored HCCA, ED and TD + */ + USBHALHost(); + + /* + * Initialize host controller. Enable USB interrupts. This part is not in the constructor because, + * this function calls a virtual method if a device is already connected + */ + void init(); + + /* + * reset a port of a specific hub + * TODO: support hub + */ + void resetPort(int hub, int port); + + /* + * return the value contained in the control HEAD ED register + * + * @returns address of the control Head ED + */ + uint32_t controlHeadED(); + + /* + * return the value contained in the bulk HEAD ED register + * + * @returns address of the bulk head ED + */ + uint32_t bulkHeadED(); + + /* + * return the value of the head interrupt ED contained in the HCCA + * + * @returns address of the head interrupt ED contained in the HCCA + */ + uint32_t interruptHeadED(); + + + /* + * Update the head ED for control transfers + */ + void updateControlHeadED(uint32_t addr); + + /* + * Update the head ED for bulk transfers + */ + void updateBulkHeadED(uint32_t addr); + + /* + * Update the head ED for interrupt transfers + */ + void updateInterruptHeadED(uint32_t addr); + + /* + * Enable control list ED + */ + void enableControlList(); + + /* + * Enable bulk list ED + */ + void enableBulkList(); + + /* + * Enable Interrupt list ED + */ + void enableInterruptList(); + + /* + * Disable control list ED + */ + bool disableControlList(); + + /* + * Disable bulk list ED + */ + bool disableBulkList(); + + /* + * Disable Interrupt list ED + */ + bool disableInterruptList(); + + //Lock processing + void lock(); + + void unlock(); + + +protected: + /* + * Virtual method called when a device has been connected + * + * @param hub hub number of the device + * @param port port number of the device + * @param lowSpeed 1 if low speed, 0 otherwise + */ + virtual void deviceConnected(int hub, int port, bool lowSpeed) {}; + + /* + * Virtuel method called when a device has been disconnected + * + * @param hub hub number of the device + * @param port port number of the device + * @param addr list of the TDs which have been completed to dequeue freed TDs + */ + virtual void deviceDisconnected(int hub, int port, volatile uint32_t addr) {}; + + /* + * Virtual method called when a transfer has been completed + * + * @param addr list of the TDs which have been completed + */ + virtual void transferCompleted(volatile uint32_t addr){}; + + /* + * Find a memory section for a new ED + * + * @returns the address of this section + */ + volatile uint8_t * getED(); + + /* + * Find a memory section for a new TD + * + * @returns the address of this section + */ + volatile uint8_t * getTD(); + + /* + * Release a previous memory section reserved for an ED + * + * @param ed address of the ED + */ + void freeED(volatile uint8_t * ed); + + /* + * Release a previous memory section reserved for an TD + * + * @param ed address of the TD + */ + void freeTD(volatile uint8_t * td); + + +private: + static void _usbisr(void); + void UsbIrqhandler(); + + void memInit(); + + void process(); + + static void staticCb(void const* p); + + HCCA volatile * usb_hcca; //256 bytes aligned! + uint8_t volatile * usb_edBuf; //4 bytes aligned! + uint8_t volatile * usb_tdBuf; //4 bytes aligned! + + static USBHALHost * instHost; + + bool volatile edBufAlloc[MAX_ENDPOINT]; + bool volatile tdBufAlloc[MAX_TD]; + + //RTOS impl + Thread thread; + Mutex mtx; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost.cpp Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,945 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#define __DEBUG__ 0 //WARN: ENABLING DEBUGGING HERE WILL PRINTF IN IRQS!! UNEXPECTED BEHAVIOUR MAY RESULT... +#ifndef __MODULE__ +#define __MODULE__ "USBHost.cpp" +#endif + +#include "dbg.h" +#include <cstdint> + +#include "USBHost.h" +#include "rtos.h" + + +#define NB_MAX_INTF 3 + +USBHost * USBHost::instHost = NULL; + +USBHost::USBHost() +#if 0 //try not to use this + : m_usbQueue(), m_usbThread(3, this, &USBHost::usbProcess) +#endif +{ + headControlEndpoint = NULL; + headBulkEndpoint = NULL; + headInterruptEndpoint = NULL; + tailControlEndpoint = NULL; + tailBulkEndpoint = NULL; + tailInterruptEndpoint = NULL; + + nb_devices = 0; + lenReportDescr = 0; + + controlEndpointAllocated = false; + + for (int i = 0; i < MAX_DEVICE_NB; i++) { + deviceInUse[i] = false; + devices[i].setAddress(i + 1); + deviceReset[i] = false; + } +} + + +void USBHost::transferCompleted(volatile uint32_t addr) { +#if 0 //try not to use this + Interrupt::enter(); + m_usbQueue.post(addr); + Interrupt::leave(); +#else + + if(addr == NULL) //Nothing to process? + { + return; + } + + volatile HCTD* tdList = NULL; + + //First we must reverse the list order and dequeue each TD + do + { + volatile HCTD* td = (volatile HCTD*)addr; + + if(td->control & 0xF0000000 != 0) + { + WARN("Condition code %02x", td->control >> 28); + } + + addr = td->nextTD; //Dequeue from physical list + td->nextTD = (uint32_t)tdList; //Enqueue into reversed list + tdList = td; + } while(addr); + + //Now we can process the list + Endpoint * volatile iter = NULL; + + while(tdList != NULL) + { + bool found = false; + volatile HCTD* td = tdList; + tdList = (volatile HCTD*)td->nextTD; //Dequeue element now as it could be modified below + for (int i = 0; i < 3; i++) { + if (found) { + break; + } + iter = (i == 0) ? headControlEndpoint : ( (i == 1) ? headBulkEndpoint : headInterruptEndpoint); + while (iter != NULL) { + if (iter->getProcessedTD() == td) { + DBG("td=%p FOUND ed: %08X", td, (void *)iter->getHCED()); + if (((HCTD *)td)->control >> 28) { + DBG("TD Error: %d", td->control >> 28); + iter->setState(USB_TYPE_TDFAIL); + } else if ((uint32_t)iter->getHCED() & 0x1) { + DBG("HALTED"); + iter->setState(USB_TYPE_HALTED); + } else if (!td->currBufPtr) { + DBG("!%p", iter); + iter->setState(USB_TYPE_IDLE); + found=true; + } else { + DBG("!%p", iter); + iter->setState(USB_TYPE_IDLE); + iter->setLengthTransferred(td->currBufPtr - iter->getBufStart()); + found=true; + } + break; + } + iter = iter->nextEndpoint(); + } + } + + + if (found) { + iter->unqueueTransfer(td); + + if (iter->getType() != CONTROL_ENDPOINT) { + iter->call(); + } + } + else + { + WARN("TD not found!!!"); + freeTD((uint8_t *)td); //Device must have been disconnected meanwhile + } + + } +#endif +} + +USBHost * USBHost::getHostInst() { + if (instHost == NULL) { + instHost = new USBHost(); + instHost->init(); + } + return instHost; +} + + +/* + * Call in ISR when a device has been connected + */ +void USBHost::deviceConnected(int hub, int port, bool lowSpeed) { + + for (int i = 0; i < MAX_DEVICE_NB; i++) { + if (!deviceInUse[i]) { + deviceInUse[i] = true; + WARN("will call init on device %p: speed: %d", (void *)&devices[i], lowSpeed); + devices[i].init(hub, port, lowSpeed); + deviceReset[i] = false; + break; + } + } + + if (!controlEndpointAllocated) { + control = newEndpoint(CONTROL_ENDPOINT, OUT, 0x08, 0x00); + addEndpoint(NULL, 0, (Endpoint*)control); + controlEndpointAllocated = true; + } +} + +/* + * Call in ISR when a device has been disconnected + */ +void USBHost::deviceDisconnected(int hub, int port, volatile uint32_t addr) { + + bool controlListState = disableControlList(); + bool bulkListState = disableBulkList(); + bool interruptListState = disableInterruptList(); + + transferCompleted(addr); //Finish processing any pending completed TD + + for (int i = 0; i < MAX_DEVICE_NB; i++) { + if ((devices[i].getHub() == hub) && (devices[i].getPort() == port)) { + WARN("device disconnected: %p", (void *)&devices[i]); + deviceInUse[i] = false; + deviceReset[i] = false; + freeDevice((USBDeviceConnected*)&devices[i]); + break; + } + } + nb_devices--; + + if (controlListState) enableControlList(); + if (bulkListState) enableBulkList(); + if (interruptListState) enableInterruptList(); +} + +void USBHost::freeDevice(USBDeviceConnected * dev) { + Endpoint * ep = NULL; +// HCTD * td = NULL; + HCED * ed = NULL; + + for (int j = 0; j < dev->getNbInterface(); j++) { + DBG("FREE INTF %d, %p, nb_endpot: %d", j, (void *)dev->getInterface(j), dev->getInterface(j)->nb_endpoint); + for (int i = 0; i < dev->getInterface(j)->nb_endpoint; i++) { + if ((ep = dev->getEndpoint(j, i)) != NULL) { + DBG("Freeing endpoint"); + ed = (HCED *)ep->getHCED(); + ed->control |= (1 << 13); //sKip bit + DBG("Dequeueing endpoint"); + unqueueEndpoint(ep); + + DBG("Freeing head transfer descriptor"); + freeTD((volatile uint8_t*)ep->getHeadTD()); + DBG("Freeing tail transfer descriptor"); + freeTD((volatile uint8_t*)ep->getNextTD()); + + DBG("Freeing endpoint descriptor"); + freeED((uint8_t *)ep->getHCED()); + } + //printBulk(); + //printInt(); + } + } + DBG("Disconnecting device"); + dev->disconnect(); + DBG("Device disconnected"); +} + + +void USBHost::unqueueEndpoint(Endpoint * ep) { + Endpoint * prec = NULL; + Endpoint * current = NULL; + bool found = false; + + DBG("want to unqueue ep: %p", (void *)ep->getHCED()); + + for (int i = 0; i < 2; i++) { + if (found) { + DBG("endpoint unqueued: %p", (void *)ep->getHCED()); + break; + } + current = (i == 0) ? (Endpoint*)headBulkEndpoint : (Endpoint*)headInterruptEndpoint; + prec = current; + while (current != NULL) { + if (current == ep) { + if (current->nextEndpoint() != NULL) { + prec->queueEndpoint(current->nextEndpoint()); + if (current == headBulkEndpoint) { + updateBulkHeadED((uint32_t)current->nextEndpoint()->getHCED()); + headBulkEndpoint = current->nextEndpoint(); + } + if (current == headInterruptEndpoint) { + updateInterruptHeadED((uint32_t)current->nextEndpoint()->getHCED()); + headInterruptEndpoint = current->nextEndpoint(); + } + } else { + prec->queueEndpoint(NULL); + if (current == headBulkEndpoint) { + updateBulkHeadED(0); + headBulkEndpoint = current->nextEndpoint(); + } + if (current == headInterruptEndpoint) { + updateInterruptHeadED(0); + headInterruptEndpoint = current->nextEndpoint(); + } + } + found = true; + current->setState(USB_TYPE_FREE); + break; + } + prec = current; + current = current->nextEndpoint(); + } + } + //printBulk(); + //printInt(); +} + + +USBDeviceConnected * USBHost::getDevice(uint8_t index) { + if ((index >= MAX_DEVICE_NB) || (!deviceInUse[index])) { + return NULL; + } + return (USBDeviceConnected*)&devices[index]; +} + + + +// create an endpoint descriptor. the endpoint is not linked +Endpoint * USBHost::newEndpoint(ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint32_t size, uint8_t addr) { + int i = 0; + HCED * ed = (HCED *)getED(); + HCTD* td_list[2] = { (HCTD*)getTD(), (HCTD*)getTD() }; + + memset((void *)td_list[0], 0x00, sizeof(HCTD)); + memset((void *)td_list[1], 0x00, sizeof(HCTD)); + + // search a free endpoint + for (i = 0; i < MAX_ENDPOINT; i++) { + if (endpoints[i].getState() == USB_TYPE_FREE) { + DBG("Trying to create ep"); + endpoints[i].init(ed, type, dir, size, addr, td_list); + //endpoints[i].queueTransfer(nullTd); + DBG("Endpoint created (%p): type: %d, dir: %d, size: %d, addr: %d", &endpoints[i], type, dir, size, addr); + return &endpoints[i]; + } + } + DBG("could not allocate more endpoints!!!!"); + return NULL; +} + + +void USBHost::resetDevice(USBDeviceConnected * dev) { + int index = findDevice(dev); + if ((index != -1) && (!deviceReset[index])) { + resetPort(dev->getHub(), dev->getPort()); + deviceReset[index] = true; + } +} + +// link the endpoint to the linked list and attach an endpoint to a device +bool USBHost::addEndpoint(USBDeviceConnected * dev, uint8_t intf_nb, Endpoint * ep) { + + if (ep == NULL) { + return false; + } + + DBG("New ep %p", ep); + + HCED * prevEd; + + // set device address in the endpoint descriptor + if (dev == NULL) { + ep->setDeviceAddress(0); + } else { + ep->setDeviceAddress(dev->getAddress()); + } + + if (dev != NULL && dev->getSpeed()) { + DBG("add endpoint: set speed"); + ep->setSpeed(dev->getSpeed()); + } + + // queue the new endpoint on the ED list + switch (ep->getType()) { + + case CONTROL_ENDPOINT: + prevEd = ( HCED*) controlHeadED(); + if (!prevEd) { + updateControlHeadED((uint32_t) ep->getHCED()); + DBG("First control endpoint: %08X", (uint32_t) ep->getHCED()); + headControlEndpoint = ep; + tailControlEndpoint = ep; + return true; + } + tailControlEndpoint->queueEndpoint(ep); + tailControlEndpoint = ep; + return true; + + case BULK_ENDPOINT: + prevEd = ( HCED*) bulkHeadED(); + if (!prevEd) { + updateBulkHeadED((uint32_t) ep->getHCED()); + //DBG("First bulk endpoint: %08X\r\n", (uint32_t) ep->getHCED()); + headBulkEndpoint = ep; + tailBulkEndpoint = ep; + break; + } + tailBulkEndpoint->queueEndpoint(ep); + tailBulkEndpoint = ep; + break; + + case INTERRUPT_ENDPOINT: + prevEd = ( HCED*) interruptHeadED(); + if (!prevEd) { + updateInterruptHeadED((uint32_t) ep->getHCED()); + //DBG("First interrupt endpoint: %08X\r\n", (uint32_t) ep->getHCED()); + headInterruptEndpoint = ep; + tailInterruptEndpoint = ep; + break; + } + tailInterruptEndpoint->queueEndpoint(ep); + tailInterruptEndpoint = ep; + break; + default: + return false; + } + + dev->addEndpoint(intf_nb, ep); + //printBulk(); + //printInt(); + + return true; +} + + +int USBHost::findDevice(USBDeviceConnected * dev) { + for (int i = 0; i < MAX_DEVICE_NB; i++) { + if (dev == &devices[i]) { + return i; + } + } + return -1; +} + +void USBHost::printBulk() { + HCED * hced = (HCED *)bulkHeadED(); + HCTD * hctd = NULL; + printf("---------State of Bulk:--------\r\n"); + while (hced != NULL) { + printf("hced: %p\r\n", hced); + hctd = (HCTD *)((uint32_t)(hced->headTD) & ~(0x0f)); + while (((uint32_t)hctd & ~(0x0f)) != ((hced->tailTD) & ~(0x0f))) { + printf("\thctd: %p\r\n", hctd); + hctd = (HCTD *)((uint32_t)(hctd->nextTD) & ~(0x0f)); + } + printf("\thctd: %p\r\n", hctd); + hced = (HCED *)((uint32_t)(hced->nextED) & ~(0x0f)); + } + printf("--------------------\r\n"); +} + +void USBHost::printInt() { + HCED * hced = (HCED *)interruptHeadED(); + HCTD * hctd = NULL; + printf("---------State of Int:--------\r\n"); + while (hced != NULL) { + printf("hced: %p\r\n", hced); + hctd = (HCTD *)((uint32_t)(hced->headTD) & ~(0x0f)); + while (((uint32_t)hctd & ~(0x0f)) != ((hced->tailTD) & ~(0x0f))) { + printf("\thctd: %p\r\n", hctd); + hctd = (HCTD *)((uint32_t)(hctd->nextTD) & ~(0x0f)); + } + printf("\thctd: %p\r\n", hctd); + hced = (HCED *)((uint32_t)(hced->nextED) & ~(0x0f)); + } + printf("--------------------\r\n"); +} + + +// add a transfer on the TD linked list +USB_TYPE USBHost::addTransfer(Endpoint * ed, uint8_t * buf, uint32_t len) { + + // allocate a TD which will be freed in TDcompletion + volatile HCTD * td = ed->getNextTD(); + if (td == NULL) { + return USB_TYPE_ERROR; + } + + DBG("Next td = %p",td); + + uint32_t token = (ed->isSetup() ? TD_SETUP : ( (ed->getDir() == IN) ? TD_IN : TD_OUT )); + + uint32_t td_toggle; + + if (ed->getType() == CONTROL_ENDPOINT) { + if (ed->isSetup()) { + td_toggle = TD_TOGGLE_0; + } else { + td_toggle = TD_TOGGLE_1; + } + } else { + td_toggle = 0; + } + + DBG("Buf=%d, len=%d", buf, len); + td->control = (TD_ROUNDING | token | TD_DELAY_INT(0) | td_toggle | TD_CC); + td->currBufPtr = (uint32_t) buf; + td->bufEnd = (uint32_t)(buf + (len - 1)); + + DBG("Now do queue transfer on ep %p", ed); + + ed->queueTransfer(); + + DBG("Enable list if needed"); + + switch (ed->getType()) { + case CONTROL_ENDPOINT: + enableControlList(); + break; + case BULK_ENDPOINT: + enableBulkList(); + break; + case INTERRUPT_ENDPOINT: + //printInt(); + enableInterruptList(); + break; + } + + DBG("Wait for HC to process TD"); + + + return USB_TYPE_PROCESSING; +} + + + +USB_TYPE USBHost::getDeviceDescriptor(USBDeviceConnected * dev, uint8_t * buf) { + return controlRead( dev, + USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, + GET_DESCRIPTOR, + (DEVICE_DESCRIPTOR << 8) | (0), + 0, + buf, + DEVICE_DESCRIPTOR_LENGTH); +} + +USB_TYPE USBHost::getConfigurationDescriptor(USBDeviceConnected * dev, uint8_t * buf, uint8_t * len_conf_descr) { + USB_TYPE res; + uint8_t total_conf_descr_length = 0; + + // fourth step: get the beginning of the configuration descriptor to have the total length of the conf descr + res = controlRead( dev, + USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, + GET_DESCRIPTOR, + (CONFIGURATION_DESCRIPTOR << 8) | (0), + 0, + buf, + CONFIGURATION_DESCRIPTOR_LENGTH); + + if (res != USB_TYPE_OK) { + ERR("GET CONF 1 DESCR FAILED"); + return res; + } + total_conf_descr_length = buf[2] | (buf[3] << 8); + if (len_conf_descr != NULL) { + *len_conf_descr = total_conf_descr_length; + } + DBG("TOTAL_LENGTH: %d \t NUM_INTERF: %d", total_conf_descr_length, buf[4]); + + return controlRead( dev, + USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, + GET_DESCRIPTOR, + (CONFIGURATION_DESCRIPTOR << 8) | (0), + 0, + buf, + total_conf_descr_length); + +} + +USB_TYPE USBHost::setConfiguration(USBDeviceConnected * dev, uint8_t conf) { + return controlWrite( dev, + USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, + SET_CONFIGURATION, + conf, + 0, + NULL, + 0); + +} + + +// enumerate a device with the control endpoint +USB_TYPE USBHost::enumerate(USBDeviceConnected * dev) { + uint8_t data[256]; + uint8_t total_conf_descr_length = 0; + USB_TYPE res; + + DBG("data = %p", data); + + if (dev->isEnumerated()) { + return USB_TYPE_OK; + } + + // first step: get the size of endpoint 0 + DBG("Get size of EP 0"); + res = controlRead( dev, + USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, + GET_DESCRIPTOR, + (DEVICE_DESCRIPTOR << 8) | (0), + 0, + data, + 8); + + if (res != USB_TYPE_OK) { + ERR("Control read failed!!"); + return res; + } + dev->setSizeControlEndpoint(data[7]); + DBG("size control Endpoint: %d", dev->getSizeControlEndpoint()); + +DBG("Now set addr"); + // second step: set an address to the device + res = controlWrite( dev, + USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, + SET_ADDRESS, + dev->getAddress(), + 0, + NULL, + 0); + + if (res != USB_TYPE_OK) { + DBG("SET ADDR FAILED"); + freeDevice(dev); + return res; + } + dev->activeAddress(); + + + // third step: get the whole device descriptor to see vid, pid + res = getDeviceDescriptor(dev, data); + + if (res != USB_TYPE_OK) { + DBG("GET DEV DESCR FAILED"); + return res; + } + dev->setClass(data[4]); + dev->setSubClass(data[5]); + dev->setProtocol(data[6]); + dev->setVid(data[8] | (data[9] << 8)); + dev->setPid(data[10] | (data[11] << 8)); + DBG("CLASS: %02X \t VID: %04X \t PID: %04X", data[4], data[8] | (data[9] << 8), data[10] | (data[11] << 8)); + + res = getConfigurationDescriptor(dev, data, &total_conf_descr_length); + if (res != USB_TYPE_OK) { + return res; + } + + // Parse the configuration descriptor + parseConfDescr(dev, data, total_conf_descr_length); + + + // sixth step: set configuration (only 1 supported) + res = setConfiguration(dev, 1); + + if (res != USB_TYPE_OK) { + DBG("SET CONF FAILED"); + freeDevice(dev); + return res; + } + + // Now the device is enumerated! + dev->setEnumerated(); + DBG("device enumerated!!!!"); + + // Some devices may require this delay + Thread::wait(100); + + return USB_TYPE_OK; +} + +// this method fills the USBDeviceConnected object: class,.... . It also add endpoints found in the descriptor. +void USBHost::parseConfDescr(USBDeviceConnected * dev, uint8_t * conf_descr, uint32_t len) { + uint32_t index = 0; + uint32_t len_desc = 0; + uint8_t id = 0; + int nb_endpoints_used = 0; + Endpoint * ep = NULL; + uint8_t intf_nb = 0; + + while (index < len) { + len_desc = conf_descr[index]; + id = conf_descr[index+1]; + switch (id) { + case CONFIGURATION_DESCRIPTOR: + break; + case INTERFACE_DESCRIPTOR: + if (intf_nb++ < NB_MAX_INTF) { + dev->addInterface(intf_nb - 1, conf_descr[index + 5], conf_descr[index + 6], conf_descr[index + 7]); + nb_endpoints_used = 0; + DBG("ADD INTF %d on device %p: class: %d, subclass: %d, proto: %d", intf_nb - 1, (void *)dev, conf_descr[index + 5],conf_descr[index + 6],conf_descr[index + 7]); + } else { + DBG("Drop intf..."); + } + break; + case ENDPOINT_DESCRIPTOR: + if (intf_nb < NB_MAX_INTF) { + if (nb_endpoints_used < MAX_ENDPOINT_PER_INTERFACE) { + // if the endpoint is isochronous -> skip it (TODO: fix this) + if ((conf_descr[index + 3] & 0x03) != ISOCHRONOUS_ENDPOINT) { + ep = newEndpoint((ENDPOINT_TYPE)(conf_descr[index+3] & 0x03), + (ENDPOINT_DIRECTION)((conf_descr[index + 2] >> 7) + 1), + conf_descr[index + 4] | (conf_descr[index + 5] << 8), + conf_descr[index + 2] & 0x0f); + DBG("ADD ENDPOINT %p, on interf %d on device %p", (void *)ep, intf_nb - 1, (void *)dev); + if (ep != NULL && dev != NULL) { + addEndpoint(dev, intf_nb - 1, ep); + } else { + DBG("EP NULL\r\n"); + } + nb_endpoints_used++; + } else { + DBG("ISO ENDPOINT NOT SUPPORTED"); + } + } + } + //DBG("ENDPOINT DESCR\r\n"); + break; + case HID_DESCRIPTOR: + lenReportDescr = conf_descr[index + 7] | (conf_descr[index + 8] << 8); + break; + default: + break; + } + index += len_desc; + } +} + + +USB_TYPE USBHost::bulkRead(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking) { + USB_TYPE res; + + if (dev == NULL || ep == NULL) { + return USB_TYPE_ERROR; + } + + if ((ep->getDir() != IN) || (ep->getType() != BULK_ENDPOINT)) { + DBG("wrong dir or bad endpoint type"); + return USB_TYPE_ERROR; + } + if (dev->getAddress() != ep->getDeviceAddress()) { + DBG("endpoint addr and device addr don't match"); + return USB_TYPE_ERROR; + } + addTransfer(ep, buf, len); + if (blocking) { + unlock(); + while ((res = control->getState()) == USB_TYPE_PROCESSING); + lock(); + if (res != USB_TYPE_IDLE) { + return res; + } + return USB_TYPE_OK; + } + return USB_TYPE_PROCESSING; +} + +USB_TYPE USBHost::bulkWrite(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking) { + USB_TYPE res; + + if (dev == NULL || ep == NULL) { + return USB_TYPE_ERROR; + } + + if ((ep->getDir() != OUT) || (ep->getType() != BULK_ENDPOINT)) { + DBG("wrong dir or bad endpoint type"); + return USB_TYPE_ERROR; + } + if (dev->getAddress() != ep->getDeviceAddress()) { + DBG("endpoint addr and device addr don't match"); + return USB_TYPE_ERROR; + } + addTransfer(ep, buf, len); + if (blocking) { + unlock(); + while ((res = control->getState()) == USB_TYPE_PROCESSING) + { + DBG("!!!!!!!!!!!!!wait bulkwrite"); + Thread::wait(100); + } + lock(); + DBG("!!!!!!!!!!!!! bulkwrite finished"); + if (res != USB_TYPE_IDLE) { + return res; + } + return USB_TYPE_OK; + } + return USB_TYPE_PROCESSING; +} + +USB_TYPE USBHost::interruptWrite(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking) { + USB_TYPE res; + + if (dev == NULL || ep == NULL) { + return USB_TYPE_ERROR; + } + + if (ep->getState() != USB_TYPE_IDLE) { + return ep->getState(); + } + + if ((ep->getDir() != OUT) || (ep->getType() != INTERRUPT_ENDPOINT)) { + ERR("wrong dir or bad endpoint type: %d, %d", ep->getDir(), ep->getType()); + return USB_TYPE_ERROR; + } + if (dev->getAddress() != ep->getDeviceAddress()) { + ERR("endpoint addr and device addr don't match"); + return USB_TYPE_ERROR; + } + addTransfer(ep, buf, len); + if (blocking) { + while ((res = ep->getState()) == USB_TYPE_PROCESSING); + if (res != USB_TYPE_IDLE) { + return res; + } + return USB_TYPE_OK; + } + return USB_TYPE_PROCESSING; +} + +USB_TYPE USBHost::interruptRead(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking) { + USB_TYPE res; + + if (dev == NULL || ep == NULL) { + return USB_TYPE_ERROR; + } + + if (ep->getState() != USB_TYPE_IDLE) { + return ep->getState(); + } + + if ((ep->getDir() != IN) || (ep->getType() != INTERRUPT_ENDPOINT)) { + ERR("wrong dir or bad endpoint type"); + return USB_TYPE_ERROR; + } + + if (dev->getAddress() != ep->getDeviceAddress()) { + ERR("endpoint addr and device addr don't match"); + return USB_TYPE_ERROR; + } + addTransfer(ep, buf, len); + if (blocking) { + while ((res = ep->getState()) == USB_TYPE_PROCESSING); + if (res != USB_TYPE_IDLE) { + return res; + } + return USB_TYPE_OK; + } + return USB_TYPE_PROCESSING; +} + + +USB_TYPE USBHost::controlRead(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) { + int length_transfer = len; + //DBG("want to transfer: %d bytes\r\n", length_transfer); + USB_TYPE res; + control->setSpeed(dev->getSpeed()); + control->setSize(dev->getSizeControlEndpoint()); + if (dev->isActiveAddress()) { + control->setDeviceAddress(dev->getAddress()); + } else { + control->setDeviceAddress(0); + } + fillControlBuf(requestType, request, value, index, len); + /* DBG("will call transfer: "); + for (int i = 0; i < 8; i++) { + DBG("%02X ", setupPacket[i]); + }*/ + control->setNextToken(TD_SETUP); + addTransfer(control, (uint8_t*)setupPacket, 8); + DBG("Now wait for TD to be processed"); + unlock(); + DBG("Unlocked"); + while ((res = control->getState()) == USB_TYPE_PROCESSING); + lock(); + DBG("TD processed with result %d", res); + if (res != USB_TYPE_IDLE) { + return res; + } + + if (length_transfer) { + DBG("In data to be transfered..."); + control->setNextToken(TD_IN); + addTransfer(control, (uint8_t *)buf, length_transfer); + unlock(); + while ((res = control->getState()) == USB_TYPE_PROCESSING); + lock(); + if (res != USB_TYPE_IDLE) { + return res; + } + } + + DBG("Transfer NULL packet (OUT)"); + control->setNextToken(TD_OUT); + addTransfer(control, NULL, 0); + unlock(); + while ((res = control->getState()) == USB_TYPE_PROCESSING); + lock(); + if (res != USB_TYPE_IDLE) { + return res; + } + return USB_TYPE_OK; +} + + +USB_TYPE USBHost::controlWrite(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) { + control->setSpeed(dev->getSpeed()); + + int length_transfer = len; + USB_TYPE res; + + control->setSize(dev->getSizeControlEndpoint()); + if (dev->isActiveAddress()) { + control->setDeviceAddress(dev->getAddress()); + } else { + control->setDeviceAddress(0); + } + fillControlBuf(requestType, request, value, index, len); + /*DBG("will call transfer: "); + for (int i = 0; i < 8; i++) { + printf("%01X ", setupPacket[i]); + } + printf("\r\n");*/ + control->setNextToken(TD_SETUP); + addTransfer(control, (uint8_t*)setupPacket, 8); + unlock(); + while ((res = control->getState()) == USB_TYPE_PROCESSING); + lock(); + if (res != USB_TYPE_IDLE) { + return res; + } + + if (length_transfer) { + control->setNextToken(TD_OUT); + addTransfer(control, (uint8_t *)buf, length_transfer); + unlock(); + while ((res = control->getState()) == USB_TYPE_PROCESSING); + lock(); + if (res != USB_TYPE_IDLE) { + return res; + } + } + + control->setNextToken(TD_IN); + addTransfer(control, NULL, 0); + unlock(); + while ((res = control->getState()) == USB_TYPE_PROCESSING); + lock(); + if (res != USB_TYPE_IDLE) { + return res; + } + return USB_TYPE_OK; +} + + +void USBHost::fillControlBuf(uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, int len) { +#ifdef __BIG_ENDIAN +#error "Must implement BE to LE conv here" +#endif + setupPacket[0] = requestType; + setupPacket[1] = request; + //We are in LE so it's fine + *((uint32_t*)&setupPacket[2]) = value; + *((uint32_t*)&setupPacket[4]) = index; + *((uint32_t*)&setupPacket[6]) = (uint32_t) len; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,303 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBHOST_H +#define USBHOST_H + +#include "USBHALHost.h" +#include "USBDeviceConnected.h" +#include "Endpoint.h" + +#define MAX_DEVICE_NB 1 + +// singleton class +class USBHost : public USBHALHost { +public: + /* + * Static method to create or retrieve the single USBHost instance + */ + static USBHost * getHostInst(); + + USB_TYPE getDeviceDescriptor(USBDeviceConnected * dev, uint8_t * buf) ; + USB_TYPE getConfigurationDescriptor(USBDeviceConnected * dev, uint8_t * buf, uint8_t * len_conf_descr = NULL) ; + USB_TYPE setConfiguration(USBDeviceConnected * dev, uint8_t conf) ; + USB_TYPE getStringDescriptor(USBDeviceConnected * dev, uint8_t index, uint8_t * buf) ; + USB_TYPE getReportDescriptor(USBDeviceConnected * dev, uint8_t * buf, uint8_t len) ; + + /* + * Control read: setup stage, data stage and status stage + * + * @param dev the control read will be done for this device + * @param requestType request type + * @param request request + * @param value value + * @param index index + * @param buf pointer on a buffer where will be store the data received + * @param len length of the transfer + * + * @returns status of the control read + */ + USB_TYPE controlRead(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) ; + + /* + * Control write: setup stage, data stage and status stage + * + * @param dev the control write will be done for this device + * @param requestType request type + * @param request request + * @param value value + * @param index index + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * + * @returns status of the control write + */ + USB_TYPE controlWrite(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) ; + + + /* + * Bulk read + * + * @param dev the bulk transfer will be done for this device + * @param ep endpoint which will be used to read a packet + * @param buf pointer on a buffer where will be store the data received + * @param len length of the transfer + * @param blocking if true, the read is blocking (wait for completion) + * + * @returns status of the bulk read + */ + USB_TYPE bulkRead(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true) ; + + /* + * Bulk write + * + * @param dev the bulk transfer will be done for this device + * @param ep endpoint which will be used to write a packet + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * @param blocking if true, the write is blocking (wait for completion) + * + * @returns status of the bulk write + */ + USB_TYPE bulkWrite(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true) ; + + /* + * Interrupt read + * + * @param dev the bulk transfer will be done for this device + * @param ep endpoint which will be used to write a packet + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * @param blocking if true, the read is blocking (wait for completion) + * + * @returns status of the interrupt read + */ + USB_TYPE interruptRead(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true) ; + + /* + * Interrupt write + * + * @param dev the bulk transfer will be done for this device + * @param ep endpoint which will be used to write a packet + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * @param blocking if true, the write is blocking (wait for completion) + * + * @returns status of the interrupt write + */ + USB_TYPE interruptWrite(USBDeviceConnected * dev, Endpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true) ; + + /* + * Enumerate a device. This method is responsible for: + * - set the address of the device + * - fill a USBDeviceConnected object: + * - add interfaces, endpoints, ... + * - set a configuration + * + * @param dev device which will be enumerated + * + * @returns status of the enumeration + */ + USB_TYPE enumerate(USBDeviceConnected * dev) ; + + /* + * Get a device + * + * @param index index of the device which will be returned + * + * @returns pointer on the "index" device + */ + USBDeviceConnected * getDevice(uint8_t index) ; + + /* + * reset port and hub of a specific device + * + * @param pointer on the device hich will be reseted + */ + void resetDevice(USBDeviceConnected * dev) ; + + /* + * If there is a HID device connected, the host stores the length of the report descriptor. + * This avoid to the driver to re-ask the configuration descriptor to request the report descriptor + * + * @returns length of the report descriptor + */ + uint16_t getLengthReportDescr() { + return lenReportDescr; + }; + + /** + * register a driver into the host associated with a callback function called when the device is disconnected + * + * @param dev device + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template<typename T> + void registerDriver(USBDeviceConnected * dev, uint8_t intf, T* tptr, void (T::*mptr)(void)) { + int index = findDevice(dev); + if ((index != -1) && (mptr != NULL) && (tptr != NULL)) { + dev->onDisconnect(intf, tptr, mptr); + } + } + + /** + * register a driver into the host associated with a callback function called when the device is disconnected + * + * @param dev device + * @param fn callback called when the specified device has been disconnected + */ + void registerDriver(USBDeviceConnected * dev, uint8_t intf, void (*fn)(void)) { + int index = findDevice(dev); + if ((index != -1) && (fn != NULL)) { + dev->onDisconnect(intf, fn); + } + } + + +protected: + + /* + * Virtual method called when a device has been connected + * + * @param hub hub number of the device + * @param port port number of the device + * @param lowSpeed 1 if low speed, 0 otherwise + */ + virtual void deviceConnected(int hub, int port, bool lowSpeed) ; + + /* + * Virtuel method called when a device has been disconnected + * + * @param hub hub number of the device + * @param port port number of the device + * @param addr list of the TDs which have been completed to dequeue freed TDs + */ + virtual void deviceDisconnected(int hub, int port, volatile uint32_t addr) ; + + /* + * Virtual method called when a transfer has been completed + * + * @param addr list of the TDs which have been completed + */ + virtual void transferCompleted(volatile uint32_t addr) ; + + +private: + // singleton class -> constructor is private + USBHost(); + + static USBHost * instHost; + + uint8_t nb_devices; + uint16_t lenReportDescr; + + void fillControlBuf(uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, int len) ; + void parseConfDescr(USBDeviceConnected * dev, uint8_t * conf_descr, uint32_t len) ; + void freeDevice(USBDeviceConnected * dev) ; + int findDevice(USBDeviceConnected * dev) ; + + + // endpoints + void unqueueEndpoint(Endpoint * ep) ; + Endpoint endpoints[MAX_ENDPOINT]; + Endpoint* volatile control; + + Endpoint* volatile headControlEndpoint; + Endpoint* volatile headBulkEndpoint; + Endpoint* volatile headInterruptEndpoint; + + Endpoint* volatile tailControlEndpoint; + Endpoint* volatile tailBulkEndpoint; + Endpoint* volatile tailInterruptEndpoint; + + bool controlEndpointAllocated; + + + // devices connected + USBDeviceConnected devices[MAX_DEVICE_NB]; + volatile bool deviceInUse[MAX_DEVICE_NB]; + volatile bool deviceReset[MAX_DEVICE_NB]; + + /* + * Add a transfer on the TD linked list associated to an ED + * + * @param ed the transfer is associated to this ed + * @param buf pointer on a buffer where will be read/write data to send or receive + * @param len transfer length + * + * @return status of the transfer + */ + USB_TYPE addTransfer(Endpoint * ed, uint8_t * buf, uint32_t len) ; + + /* + * Link the endpoint to the linked list and attach an endpoint this endpoint to a device + * + * @param dev pointer on a USBDeviceConnected object + * @param ep pointer on the Endpoint which will be added + * + * return true if successful + */ + bool addEndpoint(USBDeviceConnected * dev, uint8_t intf_nb, Endpoint * ep) ; + + /* + * Create an endpoint descriptor. Warning: the endpoint is not linked. + * + * @param type endpoint type (CONTROL_ENDPOINT, BULK_ENDPOINT, INTERRUPT_ENDPOINT) + * @param dir endpoint direction (no meaning for CONTROL_ENDPOINT) + * @param size endpoint max packet size + * @param addr endpoint address + * + * @returns pointer on the Endpoint created + */ + Endpoint * newEndpoint(ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint32_t size, uint8_t addr) ; + + + // to store a setup packet + uint8_t setupPacket[8]; + + + ///////////////////////// + /// FOR DEBUG + ///////////////////////// + void printBulk(); + void printInt(); + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostTypes.h Thu May 24 16:39:35 2012 +0000 @@ -0,0 +1,191 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USB_INC_H +#define USB_INC_H + +#include "mbed.h" + +enum USB_TYPE { + USB_TYPE_DISCONNECTED = -10, + USB_TYPE_NOTFOUND = -9, + USB_TYPE_BADCONFIG = -8, + USB_TYPE_FREE = -7, + USB_TYPE_IDLE = -6, + USB_TYPE_PROCESSING = -5, + USB_TYPE_HALTED = -4, //Transfer on an ep is stalled + USB_TYPE_BUSY = -3, + USB_TYPE_TDFAIL = -2, + USB_TYPE_ERROR = -1, + USB_TYPE_OK = 0 +}; + +#define AUDIO_CLASS 0x01 +#define CDC_CLASS 0x02 +#define MSD_CLASS 0x08 +#define HID_CLASS 0x03 + + +// From NXP's USBHostLite stack's usbhost_lpc17xx.h +// Only the types names have been changed to avoid unecessary typedefs + + +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_lpc17xx.h +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + + +// ------------------ HcControl Register --------------------- +#define OR_CONTROL_PLE 0x00000004 +#define OR_CONTROL_CLE 0x00000010 +#define OR_CONTROL_BLE 0x00000020 +#define OR_CONTROL_HCFS 0x000000C0 +#define OR_CONTROL_HC_OPER 0x00000080 +// ----------------- HcCommandStatus Register ----------------- +#define OR_CMD_STATUS_HCR 0x00000001 +#define OR_CMD_STATUS_CLF 0x00000002 +#define OR_CMD_STATUS_BLF 0x00000004 +// --------------- HcInterruptStatus Register ----------------- +#define OR_INTR_STATUS_WDH 0x00000002 +#define OR_INTR_STATUS_RHSC 0x00000040 +#define OR_INTR_STATUS_UE 0x00000010 +// --------------- HcInterruptEnable Register ----------------- +#define OR_INTR_ENABLE_WDH 0x00000002 +#define OR_INTR_ENABLE_RHSC 0x00000040 +#define OR_INTR_ENABLE_MIE 0x80000000 +// ---------------- HcRhDescriptorA Register ------------------ +#define OR_RH_STATUS_LPSC 0x00010000 +#define OR_RH_STATUS_DRWE 0x00008000 +// -------------- HcRhPortStatus[1:NDP] Register -------------- +#define OR_RH_PORT_CCS 0x00000001 +#define OR_RH_PORT_PRS 0x00000010 +#define OR_RH_PORT_CSC 0x00010000 +#define OR_RH_PORT_PRSC 0x00100000 +#define OR_RH_PORT_LSDA 0x00000200 + + +// +//************************************************************************************************************** +//* FRAME INTERVAL +//************************************************************************************************************** +// + +#define FI 0x2EDF // 12000 bits per frame (-1) +#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI) + +// +//************************************************************************************************************** +//* ENDPOINT DESCRIPTOR CONTROL FIELDS +//************************************************************************************************************** +// + +#define ED_SKIP (uint32_t) (0x00001000) // Skip this ep in queue + +// +//************************************************************************************************************** +//* TRANSFER DESCRIPTOR CONTROL FIELDS +//************************************************************************************************************** +// + +#define TD_ROUNDING (uint32_t) (0x00040000) // Buffer Rounding +#define TD_SETUP (uint32_t)(0) // Direction of Setup Packet +#define TD_IN (uint32_t)(0x00100000) // Direction In +#define TD_OUT (uint32_t)(0x00080000) // Direction Out +#define TD_DELAY_INT(x) (uint32_t)((x) << 21) // Delay Interrupt +#define TD_TOGGLE_0 (uint32_t)(0x02000000) // Toggle 0 +#define TD_TOGGLE_1 (uint32_t)(0x03000000) // Toggle 1 +#define TD_CC (uint32_t)(0xF0000000) // Completion Code + +// +//************************************************************************************************************** +//* USB STANDARD REQUEST DEFINITIONS +//************************************************************************************************************** +// + +#define DEVICE_DESCRIPTOR (1) +#define CONFIGURATION_DESCRIPTOR (2) +#define INTERFACE_DESCRIPTOR (4) +#define ENDPOINT_DESCRIPTOR (5) +#define HID_DESCRIPTOR (33) + +// ----------- Control RequestType Fields ----------- +#define USB_DEVICE_TO_HOST 0x80 +#define USB_HOST_TO_DEVICE 0x00 +#define USB_REQUEST_TYPE_CLASS 0x20 +#define USB_RECIPIENT_DEVICE 0x00 +#define USB_RECIPIENT_INTERFACE 0x01 + +// -------------- USB Standard Requests -------------- +#define SET_ADDRESS 5 +#define GET_DESCRIPTOR 6 +#define SET_CONFIGURATION 9 +#define SET_INTERFACE 11 + +// -------------- USB Descriptor Length -------------- +#define DEVICE_DESCRIPTOR_LENGTH 0x12 +#define CONFIGURATION_DESCRIPTOR_LENGTH 0x09 + + +// +//************************************************************************************************************** +//* TYPE DEFINITIONS +//************************************************************************************************************** +// + +// ----------- HostController EndPoint Descriptor ------------- +typedef struct hcEd { + volatile uint32_t control; // Endpoint descriptor control + volatile uint32_t tailTD; // Physical address of tail in Transfer descriptor list + volatile uint32_t headTD; // Physcial address of head in Transfer descriptor list + volatile uint32_t nextED; // Physical address of next Endpoint descriptor +} HCED; + +// ------------ HostController Transfer Descriptor ------------ +typedef struct hcTd { + volatile uint32_t control; // Transfer descriptor control + volatile uint32_t currBufPtr; // Physical address of current buffer pointer + volatile uint32_t nextTD; // Physical pointer to next Transfer Descriptor + volatile uint32_t bufEnd; // Physical address of end of buffer +} HCTD; + +// ----------- Host Controller Communication Area ------------ +typedef struct hcca { + volatile uint32_t IntTable[32]; // Interrupt Table + volatile uint32_t FrameNumber; // Frame Number + volatile uint32_t DoneHead; // Done Head + volatile uint8_t Reserved[116]; // Reserved for future use + volatile uint8_t Unknown[4]; // Unused +} HCCA; + + + +#endif