USB Host WAN Dongle library

Fork of USBHostWANDongle_bleedingedge by Donatien Garnier

USB3GModule/WANDongle.cpp

Committer:
donatien
Date:
2012-05-24
Revision:
0:ae46a0638b2c
Child:
1:49df46e3295c

File content as of revision 0:ae46a0638b2c:

/* 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();
  }
}