USB Host WAN Dongle library

Fork of USBHostWANDongle_bleedingedge by Donatien Garnier

Revision:
0:ae46a0638b2c
Child:
1:49df46e3295c
--- /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();
+  }
+}