PHS module SMA-01 library. see: https://developer.mbed.org/users/phsfan/notebook/abitusbmodem/

Dependencies:   Socket lwip-sys lwip

Dependents:   AbitUSBModem_HTTPTest AbitUSBModem_MQTTTest AbitUSBModem_WebsocketTest AbitUSBModem_SMSTest

Fork of VodafoneUSBModem by mbed official

/media/uploads/phsfan/sma01_003.png

USBHostPhs/USBHostPhs.cpp

Committer:
phsfan
Date:
2015-02-25
Revision:
99:514e67a69ad6
Parent:
97:7d9cc95e2ea7

File content as of revision 99:514e67a69ad6:

/* USBHostPhs.cpp */
/* Modified by 2015 phsfan
 *  for ABIT SMA-01
 */
/* mbed USBHost Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* WANDongle.cpp, WANDongleSerialPort.cpp */
/* Copyright (c) 2010-2012 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
#ifndef __MODULE__
#define __MODULE__ "USBHostPhs.cpp"
#endif

#include "core/dbg.h"
#include <cstdint>
#include "rtos.h"

#include "USBHostPhs.h"

#define CHECK_INTERFACE(cls,subcls,proto) \
        (((cls == 0xFF)         && (subcls == 0xFF) && (proto == 0xFF)) /* QUALCOM CDC */  || \
         ((cls == SERIAL_CLASS) && (subcls == 0x00) && (proto == 0x00)) /* STANDARD CDC */ )

USBHostPhs::USBHostPhs()
{
    host = USBHost::getHostInst();
    ports_found = 0;
    dev_connected = false;
}

bool USBHostPhs::connected()
{
    return dev_connected;
}

void USBHostPhs::disconnect(void)
{
    USBHostPhsPort::reset();
    ports_found = 0;
    dev = NULL;
}

bool USBHostPhs::connect() {

    if (dev)
    {
        for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++)
        {
            USBDeviceConnected* d = host->getDevice(i);
            if (dev == d)
                return true;
        }
        disconnect();
    }
    for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++)
    {
        USBDeviceConnected* d = host->getDevice(i);
        if (d != NULL) {

            USB_DBG("Trying to connect serial device \r\n");
            if(host->enumerate(d, this))
                break;
            DBG("Device has VID:%04x PID:%04x", d->getVid(), d->getPid());
            if ((d->getVid() != PHS_VID) || (d->getPid() != PHS_PID)) break;

            USBEndpoint* bulk_in  = d->getEndpoint(port_intf, BULK_ENDPOINT, IN);
            USBEndpoint* bulk_out = d->getEndpoint(port_intf, BULK_ENDPOINT, OUT);
            if (bulk_in && bulk_out)
            {
                USBHostPhsPort::connect(host,d,port_intf,bulk_in, bulk_out);
                dev = d;
                dev_connected = true;
                DBG("Device connected");
            }
        }
    }
    return dev != NULL;
}

/*virtual*/ void USBHostPhs::setVidPid(uint16_t vid, uint16_t pid)
{
    // we don't check VID/PID for MSD driver
}

/*virtual*/ bool USBHostPhs::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
{
    if (!ports_found &&
        CHECK_INTERFACE(intf_class, intf_subclass, intf_protocol)) {
        port_intf = intf_nb;
        ports_found = true;
        return true;
    }
    return false;
}

/*virtual*/ bool USBHostPhs::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
{
    if (ports_found && (intf_nb == port_intf)) {
        if (type == BULK_ENDPOINT)
            return true;
    }
    return false;
}

//------------------------------------------------------------------------------

#define SET_LINE_CODING 0x20

USBHostPhsPort::USBHostPhsPort() : cb_tx_en(false), cb_rx_en(false), listener(NULL)
{
    init();
    reset();
}

void USBHostPhsPort::init(void)
{
    host = NULL;
    dev = NULL;
    serial_intf = NULL;
    size_bulk_in = 0;
    size_bulk_out = 0;
    bulk_in = NULL;
    bulk_out = NULL;
    line_coding.baudrate = 921600;
    line_coding.data_bits = 8;
    line_coding.parity = None;
    line_coding.stop_bits = 1;
}

void USBHostPhsPort::connect(USBHost* _host, USBDeviceConnected * _dev,
        uint8_t _serial_intf, USBEndpoint* _bulk_in, USBEndpoint* _bulk_out)
{
    host = _host;
    dev = _dev;
    serial_intf = _serial_intf;
    bulk_in = _bulk_in;
    bulk_out = _bulk_out;

  max_out_size = bulk_out->getSize();
  if( max_out_size > WANDONGLE_MAX_OUTEP_SIZE )
  {
    max_out_size = WANDONGLE_MAX_OUTEP_SIZE;
  }

    USB_INFO("New Serial device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, serial_intf);
    dev->setName("Serial", serial_intf);
    host->registerDriver(dev, serial_intf, this, &USBHostPhsPort::init);
    baud(921600);
    size_bulk_in = bulk_in->getSize();
    size_bulk_out = bulk_out->getSize();
    bulk_in->attach(this, &USBHostPhsPort::rxHandler);
    bulk_out->attach(this, &USBHostPhsPort::txHandler);

  readPacket(); //Start receiving data
}

void USBHostPhsPort::rxHandler() {
  if (((USBEndpoint *) bulk_in)->getState() == USB_TYPE_IDLE) //Success
  {
    buf_in_read_pos = 0;
    buf_in_len = ((USBEndpoint *) 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();
    DBG("Trying again");
    readPacket();
  }
}

void USBHostPhsPort::txHandler() {
  if (((USBEndpoint *) 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();
  }
}

void USBHostPhsPort::baud(int baudrate) {
    line_coding.baudrate = baudrate;
    format(line_coding.data_bits, (Parity)line_coding.parity, line_coding.stop_bits);
}

void USBHostPhsPort::format(int bits, Parity parity, int stop_bits) {
    line_coding.data_bits = bits;
    line_coding.parity = parity;
    line_coding.stop_bits = (stop_bits == 1) ? 0 : 2;

    // set line coding
    host->controlWrite( dev,
                        USB_RECIPIENT_INTERFACE | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS,
                        SET_LINE_CODING,
                        0, serial_intf, (uint8_t *)&line_coding, 7);
}


void USBHostPhsPort::reset()
{
  tx_mtx.lock();
  rx_mtx.lock();

  buf_out_len = 0;
  max_out_size = 0;
  lock_tx = false;
  cb_tx_pending = false;

  buf_in_len = 0;
  buf_in_read_pos = 0;
  lock_rx = false;
  cb_rx_pending = false;
  
  tx_mtx.unlock();
  rx_mtx.unlock();
}

int USBHostPhsPort::readPacket()
{
  DBG("Read packet on %p", this);
  rx_mtx.lock();
  if(lock_rx)
  {
    ERR("Fail");
    rx_mtx.unlock();
    return -1;
  }
  
  if( bulk_in == NULL )
  {
    WARN("Port is disconnected");
    rx_mtx.unlock();
    return -1;
  }

  lock_rx = true; //Receiving
  rx_mtx.unlock();
//  DBG("readPacket");
  //lock_rx.lock();
  USB_TYPE res = host->bulkRead(dev, (USBEndpoint *)bulk_in, buf_in, ((USBEndpoint *)bulk_in)->getSize(), false); //Queue transfer
#if __DEBUG__ >= 5
  printf(" - bulkRead:");
  for (int i = 0; i < buf_in_len; i ++) {
    if (buf_in[i] >= 0x20 && buf_in[i] < 0x7f) {
      printf(" %c", buf_in[i]);
    } else {
      printf(" %02x", buf_in[i]);
    }
  }
  printf("\r\n");
#endif
  if(res != USB_TYPE_PROCESSING)
  {
    //lock_rx.unlock();
    ERR("host->bulkRead() returned %d", res);
    Thread::wait(100);
    return -1;
  }
  return 0;
}

int USBHostPhsPort::writePacket()
{
  tx_mtx.lock();
  if(lock_tx)
  {
    ERR("Fail");
    tx_mtx.unlock();
    return -1;
  }
  
  if( bulk_out == NULL )
  {
    WARN("Port is disconnected");
    tx_mtx.unlock();
    return -1;
  }

  lock_tx = true; //Transmitting
  tx_mtx.unlock();
//  DBG("writePacket");

  //lock_tx.lock();
  USB_TYPE res = host->bulkWrite(dev, (USBEndpoint *)bulk_out, buf_out, buf_out_len, false); //Queue transfer
#if __DEBUG__ >= 5
  printf(" - bulkWrite:");
  for (int i = 0; i < buf_out_len; i ++) {
    if (buf_out[i] >= 0x20 && buf_out[i] < 0x7f) {
      printf(" %c", buf_out[i]);
    } else {
      printf(" %02x", buf_out[i]);
    }
  }
  printf("\r\n");
#endif
  if(res != USB_TYPE_PROCESSING)
  {
    //lock_tx.unlock();
    ERR("host->bulkWrite() returned %d", res);
    Thread::wait(100);
    return -1;
  }
  return 0;
}

int USBHostPhsPort::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 USBHostPhsPort::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 USBHostPhsPort::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 USBHostPhsPort::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 USBHostPhsPort::attach(IUSBHostSerialListener* pListener)
{
  if(pListener == NULL)
  {
    setupIrq(false, IUSBHostSerial::RxIrq);
    setupIrq(false, IUSBHostSerial::TxIrq);
  }
  listener = pListener;
  if(pListener != NULL)
  {
    setupIrq(true, IUSBHostSerial::RxIrq);
    setupIrq(true, IUSBHostSerial::TxIrq);
  }
}

void USBHostPhsPort::setupIrq(bool en, IUSBHostSerial::IrqType irq /*= RxIrq*/)
{
  switch(irq)
  {
  case IUSBHostSerial::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 IUSBHostSerial::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;
  }
}