USB Host WAN Dongle library
Fork of USBHostWANDongle_bleedingedge by
USB3GModule/WANDongle.cpp
- Committer:
- donatien
- Date:
- 2012-05-25
- Revision:
- 1:49df46e3295c
- Parent:
- 0:ae46a0638b2c
- Child:
- 2:a8b2d0cd9bbd
File content as of revision 1:49df46e3295c:
/* 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 in MSD mode"); 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 descriptor found on device %p, intf %d, will now try to switch into serial 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 //Not wait for the dongle to reconnect 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); DBG("Found one device with vid: %04x pid: %04x", dev->getVid(), dev->getPid()); //Vodafone K3770 if ((dev->getVid() == 0x12D1) && (dev->getPid() == 0x14C9)) { DBG("Switched successfully"); 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(); } }