Used in Live Traffic Update Nokia LCD Display Project
Fork of NetServices by
Diff: drv/serial/usb/UsbSerial.cpp
- Revision:
- 0:ac1725ba162c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drv/serial/usb/UsbSerial.cpp Tue Nov 09 20:54:15 2010 +0000 @@ -0,0 +1,300 @@ + +/* +Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com) + +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 "rpc.h" + +#include "UsbSerial.h" + +//#define __DEBUG +#include "dbg/dbg.h" + +#include "netCfg.h" +#if NET_USB_SERIAL + +namespace mbed { + +#define BUF_LEN 64 +#define FLUSH_TMOUT 100000 //US + +UsbSerial::UsbSerial(UsbDevice* pDevice, int epIn, int epOut, const char* name /*= NULL*/) : Stream(name), m_epIn(pDevice, epIn, true, USB_BULK, BUF_LEN), m_epOut(pDevice, epOut, false, USB_BULK, BUF_LEN), +m_pInCbItem(NULL), m_pInCbMeth(NULL), m_pOutCbItem(NULL), m_pOutCbMeth(NULL) +{ + m_inBufEven = new char[BUF_LEN]; + m_inBufOdd = new char[BUF_LEN]; + m_pInBufPos = m_inBufUsr = m_inBufEven; + m_inBufTrmt = m_inBufOdd; + + m_outBufEven = new char[BUF_LEN]; + m_outBufOdd = new char[BUF_LEN]; + m_pOutBufPos = m_outBufUsr = m_outBufEven; + m_outBufTrmt = m_outBufOdd; + + m_inBufLen = m_outBufLen = 0; + + DBG("Starting RX'ing on in ep\n"); + + m_timeout = false; + + m_epIn.setOnCompletion(this, &UsbSerial::onEpInTransfer); + m_epOut.setOnCompletion(this, &UsbSerial::onEpOutTransfer); + + startRx(); +} + +UsbSerial::~UsbSerial() +{ + delete[] m_inBufEven; + delete[] m_inBufOdd; + delete[] m_outBufEven; + delete[] m_outBufOdd; +} + +void UsbSerial::baud(int baudrate) { + // +} + +void UsbSerial::format(int bits, int parity, int stop) { + // +} + +#if 0 //For doc only +template <class T> +void attach(T* pCbItem, void (T::*pCbMeth)()) +{ + m_pCbItem = (CDummy*) pCbItem; + m_pCbMeth = (void (CDummy::*)()) pCbMeth; +} +#endif + +int UsbSerial::_getc() { + NVIC_DisableIRQ(US_TICKER_TIMER_IRQn); + NVIC_DisableIRQ(USB_IRQn); + char c; + c = *m_pInBufPos; + m_pInBufPos++; + NVIC_EnableIRQ(USB_IRQn); + NVIC_EnableIRQ(US_TICKER_TIMER_IRQn); + return c; +} + +int UsbSerial::_putc(int c) { + NVIC_DisableIRQ(US_TICKER_TIMER_IRQn); + NVIC_DisableIRQ(USB_IRQn); + if( (m_pOutBufPos - m_outBufUsr) < BUF_LEN ) + { + *m_pOutBufPos = (char) c; + m_pOutBufPos++; + } + else + { + DBG("NO WAY!!!\n"); + } + #if 1 + if( (m_pOutBufPos - m_outBufUsr) >= BUF_LEN ) //Must flush + { + if(m_timeout) + m_txTimeout.detach(); + startTx(); + } + else + { + /*if(m_timeout) + m_txTimeout.detach(); + m_timeout = true; + m_txTimeout.attach_us(this, &UsbSerial::startTx, FLUSH_TMOUT);*/ + if(!m_timeout) + { + m_timeout = true; + m_txTimeout.attach_us(this, &UsbSerial::startTx, FLUSH_TMOUT); + } + } + #endif + //startTx(); + NVIC_EnableIRQ(USB_IRQn); + NVIC_EnableIRQ(US_TICKER_TIMER_IRQn); + return c; +} + +int UsbSerial::readable() { + NVIC_DisableIRQ(US_TICKER_TIMER_IRQn); + NVIC_DisableIRQ(USB_IRQn); + int res; + if( (m_pInBufPos - m_inBufUsr) < m_inBufLen ) + { + //DBG("\r\nREADABLE\r\n"); + res = true; + } + else + { + //DBG("\r\nNOT READABLE\r\n"); + startRx(); //Try to swap packets & start another transmission + res = ((m_pInBufPos - m_inBufUsr) < m_inBufLen )?true:false; + } + NVIC_EnableIRQ(USB_IRQn); + NVIC_EnableIRQ(US_TICKER_TIMER_IRQn); + return (bool)res; +} + +int UsbSerial::writeable() { + NVIC_DisableIRQ(US_TICKER_TIMER_IRQn); + NVIC_DisableIRQ(USB_IRQn); + // DBG("\r\nWRITEABLE???\r\n"); + int res = (bool)( (m_pOutBufPos - m_outBufUsr) < BUF_LEN); + NVIC_EnableIRQ(USB_IRQn); + NVIC_EnableIRQ(US_TICKER_TIMER_IRQn); + return res; +} + +void UsbSerial::onReadable() +{ + if(m_pInCbItem && m_pInCbMeth) + (m_pInCbItem->*m_pInCbMeth)(); +} + +void UsbSerial::onWriteable() +{ + if(m_pOutCbItem && m_pOutCbMeth) + (m_pOutCbItem->*m_pOutCbMeth)(); +} + +void UsbSerial::onEpInTransfer() +{ + int len = m_epIn.status(); + DBG("RX transfer completed w len=%d\n",len); + startRx(); + if(len > 0) + onReadable(); +} + +void UsbSerial::onEpOutTransfer() +{ + int len = m_epOut.status(); + DBG("TX transfer completed w len=%d\n",len); + if(m_timeout) + m_txTimeout.detach(); + startTx(); + if(len > 0) + onWriteable(); +} + +void UsbSerial::startTx() +{ + + DBG("Transfer>\n"); + + m_timeout = false; + +// m_txTimeout.detach(); + + if(!(m_pOutBufPos - m_outBufUsr)) + { + DBG("?!?!?\n"); + return; + } + + if( m_epOut.status() == USBERR_PROCESSING ) + { + //Wait & retry + //m_timeout = true; + //m_txTimeout.attach_us(this, &UsbSerial::startTx, FLUSH_TMOUT); + DBG("Ep is busy...\n"); + return; + } + + if( m_epOut.status() < 0 ) + { + DBG("Tx trying again...\n"); + m_epOut.transfer((volatile uint8_t*)m_outBufTrmt, m_outBufLen); + return; + } + + m_outBufLen = m_pOutBufPos - m_outBufUsr; + + //Swap buffers + volatile char* swapBuf = m_outBufUsr; + m_outBufUsr = m_outBufTrmt; + m_outBufTrmt = swapBuf; + + m_epOut.transfer((volatile uint8_t*)m_outBufTrmt, m_outBufLen); + + m_pOutBufPos = m_outBufUsr; + +} + +void UsbSerial::startRx() +{ + if( (m_pInBufPos - m_inBufUsr) < m_inBufLen ) + { + //User buf is not empty, cannot swap now... + return; + } + int len = m_epIn.status(); + if( len == USBERR_PROCESSING ) + { + //Previous transmission not completed + return; + } + if( len < 0 ) + { + DBG("Rx trying again...\n"); + m_epIn.transfer((volatile uint8_t*)m_inBufTrmt, BUF_LEN); //Start another transmission + return; + } + + m_inBufLen = len; + + //Swap buffers + volatile char* swapBuf = m_inBufUsr; + m_inBufUsr = m_inBufTrmt; + m_inBufTrmt = swapBuf; + m_pInBufPos = m_inBufUsr; + + DBG("Starting new transfer\n"); + m_epIn.transfer((volatile uint8_t*)m_inBufTrmt, BUF_LEN); //Start another transmission + +} + +#ifdef MBED_RPC +const struct rpc_method *UsbSerial::get_rpc_methods() { + static const rpc_method methods[] = { + { "readable", rpc_method_caller<int, UsbSerial, &UsbSerial::readable> }, + { "writeable", rpc_method_caller<int, UsbSerial, &UsbSerial::writeable> }, + RPC_METHOD_SUPER(Stream) + }; + return methods; +} + +struct rpc_class *UsbSerial::get_rpc_class() { + static const rpc_function funcs[] = { + /*{ "new", rpc_function_caller<const char*, UsbDevice*, int, int, const char*, Base::construct<UsbSerial,UsbDevice*,int,int,const char*> > },*/ //RPC is buggy + RPC_METHOD_END + }; + static rpc_class c = { "UsbSerial", funcs, NULL }; + return &c; +} +#endif + +} // namespace mbed + +#endif +