// // cusb3xxxinterface.cpp // ambed // // Created by Jean-Luc Deltombe (LX3JL) on 26/04/2017. // Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. // // ---------------------------------------------------------------------------- // This file is part of ambed. // // xlxd is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // xlxd is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Foobar. If not, see . // ---------------------------------------------------------------------------- #include "main.h" #include #include "ctimepoint.h" #include "cusb3xxxinterface.h" #include "cvocodecchannel.h" #include "cambeserver.h" // queues ID #define QUEUE_CHANNEL 0 #define QUEUE_SPEECH 1 //////////////////////////////////////////////////////////////////////////////////////// // constructor CUsb3xxxInterface::CUsb3xxxInterface(uint32 uiVid, uint32 uiPid, const char *szDeviceName, const char *szDeviceSerial) { m_FtdiHandle = NULL; m_uiVid = uiVid; m_uiPid = uiPid; ::strcpy(m_szDeviceName, szDeviceName); ::strcpy(m_szDeviceSerial, szDeviceSerial); m_iSpeechFifolLevel = 0; m_iChannelFifolLevel = 0; } //////////////////////////////////////////////////////////////////////////////////////// // destructor CUsb3xxxInterface::~CUsb3xxxInterface() { // delete m_SpeechQueues for ( int i = 0; i < m_SpeechQueues.size(); i++ ) { delete m_SpeechQueues[i]; } m_SpeechQueues.clear(); // delete m_ChannelQueues for ( int i = 0; i < m_ChannelQueues.size(); i++ ) { delete m_ChannelQueues[i]; } m_ChannelQueues.clear(); } //////////////////////////////////////////////////////////////////////////////////////// // initialization bool CUsb3xxxInterface::Init(void) { bool ok = true; // open USB device std::cout << "Opening " << m_szDeviceName << ":" << m_szDeviceSerial << " device" << std::endl; if ( ok &= OpenDevice() ) { // reset //std::cout << "Reseting " << m_szDeviceName << "device" << std::endl; if ( ok &= ResetDevice() ) { // read version //std::cout << "Reading " << m_szDeviceName << " device version" << std::endl; if ( ok &= ReadDeviceVersion() ) { // send configuration packet(s) //std::cout << "Configuring " << m_szDeviceName << " device" << std::endl; ok &= DisableParity(); ok &= ConfigureDevice(); } } } std::cout << std::endl; // create our queues for ( int i = 0; i < GetNbChannels(); i++ ) { m_SpeechQueues.push_back(new CPacketQueue); m_ChannelQueues.push_back(new CPacketQueue); } // base class if ( ok ) { ok &= CVocodecInterface::Init(); } // done return ok; } //////////////////////////////////////////////////////////////////////////////////////// // task void CUsb3xxxInterface::Task(void) { CBuffer Buffer; int iCh; CPacketQueue *Queue; CVocodecChannel *Channel; CAmbePacket AmbePacket; CVoicePacket VoicePacket; bool done; // TODO : // preserve packets PIDs, so the transcoded CAmbePacket returned // to CStream client is garantied to have the same PID // than the corresponding incoming packet // process the device incoming packet // get all packets from device and push them // to the relevant clients queues if ( ReadBuffer(&Buffer) ) { if ( IsValidSpeechPacket(Buffer, &iCh, &VoicePacket) ) { // update fifo level // as we get a speech packet, it means that the device // channel fifo input decreased by 1 m_iChannelFifolLevel = MAX(0, m_iChannelFifolLevel-1); // push back to relevant channel voice queue // our incoming channel packet has now been through the decoder // find the coupled channel encoder and push to it's queue // this is were the DVSI enc-dec channel crossover take place Channel = GetChannelWithChannelIn(iCh); if ( Channel != NULL ) { Queue = Channel->GetVoiceQueue(); CVoicePacket *clone = new CVoicePacket(VoicePacket); Channel->ProcessSignal(*clone); Queue->push(clone); Channel->ReleaseVoiceQueue(); } } else if ( IsValidChannelPacket(Buffer, &iCh, &AmbePacket) ) { // update fifo level // as we get a channel packet, it means that the device // speech fifo input decreased by 1 m_iSpeechFifolLevel = MAX(0, m_iSpeechFifolLevel-1); // push back to relevant channel outcoming queue // we are done with this packet transcoding // it's final step Channel = GetChannelWithChannelOut(iCh); if ( Channel != NULL ) { Queue = Channel->GetPacketQueueOut(); CAmbePacket *clone = new CAmbePacket(AmbePacket); Queue->push(clone); Channel->ReleasePacketQueueOut(); } } } // process the streams (channels) incoming queue // make sure that packets from different channels // are interlaced so to keep the device fifo busy do { done = true; for ( int i = 0; i < m_Channels.size(); i++) { // get channel Channel = m_Channels[i]; // any packet in voice queue ? if ( Channel->IsInterfaceOut(this) ) { Queue = Channel->GetVoiceQueue(); if ( !Queue->empty() ) { // get packet CVoicePacket *Packet = (CVoicePacket *)Queue->front(); Queue->pop(); // this is second step of transcoding // we just received from hardware a decoded speech packet // post it to relevant channel encoder int i = Channel->GetChannelOut(); Packet->SetChannel(i); m_SpeechQueues[i]->push(Packet); // done done = false; } Channel->ReleaseVoiceQueue(); } // any packet in ambe queue for us ? if ( Channel->IsInterfaceIn(this) ) { Queue = Channel->GetPacketQueueIn(); if ( !Queue->empty() ) { // get packet CAmbePacket *Packet = (CAmbePacket *)Queue->front(); Queue->pop(); // this is first step of transcoding // a fresh new packet to be transcoded is showing up // post it to relevant channel decoder int i = Channel->GetChannelIn(); Packet->SetChannel(i); m_ChannelQueues[i]->push(Packet); // done done = false; } Channel->ReleasePacketQueueIn(); } } } while (!done); // process device incoming queues (aka to device) // interlace speech and channels packets // and post to final device queue do { done = true; // loop on all channels for ( int i = 0; i < GetNbChannels(); i++ ) { // speech if ( !m_SpeechQueues[i]->empty() ) { // get packet CPacket *Packet = m_SpeechQueues[i]->front(); m_SpeechQueues[i]->pop(); // and push to device queue m_DeviceQueue.push(Packet); // next done = false; } // ambe if ( !m_ChannelQueues[i]->empty() ) { // get packet CPacket *Packet = m_ChannelQueues[i]->front(); m_ChannelQueues[i]->pop(); // and push to device queue m_DeviceQueue.push(Packet); // done = false; } } } while (!done); // process device queue to feed hardware // make sure that device fifo is fed all the time int fifoSize = GetDeviceFifoSize(); do { done = true; // if device fifo level is zero (device idle) // wait that at least 3 packets are in incoming // queue before restarting if ( ((m_iSpeechFifolLevel+m_iChannelFifolLevel) > 0) || (m_DeviceQueue.size() >= (fifoSize+1)) ) { // any packet to send ? if ( m_DeviceQueue.size() > 0 ) { // yes, get it CPacket *Packet = m_DeviceQueue.front(); if ( Packet->IsVoice() && (m_iSpeechFifolLevel < fifoSize) ) { // encode & post EncodeSpeechPacket(&Buffer, Packet->GetChannel(), (CVoicePacket *)Packet); WriteBuffer(Buffer); // remove from queue m_DeviceQueue.pop(); // and delete it delete Packet; // update fifo level m_iSpeechFifolLevel++; // next done = false; #ifdef DEBUG_DUMPFILE g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "Sp" << Packet->GetChannel() << "->" << std::endl; std::cout.flush(); #endif } else if ( Packet->IsAmbe() && (m_iChannelFifolLevel < fifoSize) ) { // encode & post EncodeChannelPacket(&Buffer, Packet->GetChannel(), (CAmbePacket *)Packet); WriteBuffer(Buffer); // remove from queue m_DeviceQueue.pop(); // and delete it delete Packet; // update fifo level m_iChannelFifolLevel++; // next done = false; #ifdef DEBUG_DUMPFILE g_AmbeServer.m_DebugFile << m_szDeviceName << "\t" << "Ch" << Packet->GetChannel() << "->" << std::endl; std::cout.flush(); #endif } } } } while (!done); // and wait a bit CTimePoint::TaskSleepFor(2); } //////////////////////////////////////////////////////////////////////////////////////// // low level bool CUsb3xxxInterface::ReadDeviceVersion(void) { bool ok = false; int i, len; char rxpacket[128]; char txpacket[8] = { PKT_HEADER, 0, 4, PKT_CONTROL, PKT_PRODID, PKT_VERSTRING, PKT_PARITYBYTE, 4 ^ PKT_CONTROL ^ PKT_PRODID ^ PKT_VERSTRING ^ PKT_PARITYBYTE }; // write packet if ( FTDI_write_packet(m_FtdiHandle, txpacket, sizeof(txpacket)) ) { // read reply len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ) - 4; ok = (len != 0); //we succeed in reading a packet, print it out std::cout << "ReadDeviceVersion : "; for ( i = 4; i < len+4 ; i++ ) { std::cout << (char)(rxpacket[i] & 0x00ff); } std::cout << std::endl; } return ok; } bool CUsb3xxxInterface::DisableParity(void) { bool ok = false; int len; char rxpacket[16]; char txpacket[8] = { PKT_HEADER, 0, 4, PKT_CONTROL, PKT_PARITYMODE,0x00, PKT_PARITYBYTE, 4 ^ PKT_CONTROL ^ PKT_PARITYMODE ^ 0x00 ^ PKT_PARITYBYTE }; // write packet if ( FTDI_write_packet(m_FtdiHandle, txpacket, sizeof(txpacket)) ) { // read reply len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ) - 4; ok = ((len == 2) && (rxpacket[4] == PKT_PARITYMODE) &&(rxpacket[5] == 0x00) ); } return ok; } bool CUsb3xxxInterface::ConfigureChannel(uint8 pkt_ch, const uint8 *pkt_ratep, int in_gain, int out_gain) { bool ok = false; int len; char rxpacket[64]; char txpacket[] = { PKT_HEADER, 0, 33, PKT_CONTROL, 0x00, PKT_ECMODE, 0x00,0x00, PKT_DCMODE, 0x00,0x00, PKT_COMPAND,0x00, PKT_RATEP, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, PKT_CHANFMT,0x00,0x00, PKT_SPCHFMT,0x00,0x00, PKT_GAIN, 0x00,0x00, PKT_INIT, 0x03 }; // update packet content txpacket[4] = pkt_ch; :: memcpy(&(txpacket[14]), pkt_ratep, 12); txpacket[33] = (uint8)(signed char)in_gain; txpacket[34] = (uint8)(signed char)out_gain; // write packet if ( FTDI_write_packet(m_FtdiHandle, txpacket, sizeof(txpacket)) ) { // read reply len = FTDI_read_packet( m_FtdiHandle, rxpacket, sizeof(rxpacket) ) - 4; ok = ((len == 18) && (rxpacket[20] == PKT_INIT) &&(rxpacket[21] == 0x00) ); } return ok; } //////////////////////////////////////////////////////////////////////////////////////// // io level bool CUsb3xxxInterface::ReadBuffer(CBuffer *buffer) { bool ok = false; int n; // any byte in tx queue ? if ( FT_GetQueueStatus(m_FtdiHandle, (LPDWORD)&n) == FT_OK ) { //if ( (FT_GetQueueStatus(m_FtdiHandle, (LPDWORD)&n) == FT_OK) && (n != 0) ) if ( n != 0 ) { buffer->clear(); buffer->resize(USB3XXX_MAXPACKETSIZE); n = FTDI_read_packet(m_FtdiHandle, (char *)buffer->data(), USB3XXX_MAXPACKETSIZE); buffer->resize(n); ok = (n != 0); } } return ok; } bool CUsb3xxxInterface::WriteBuffer(const CBuffer &buffer) { return FTDI_write_packet(m_FtdiHandle, (const char *)buffer.data(), (int)buffer.size()); } int CUsb3xxxInterface::FTDI_read_packet(FT_HANDLE ftHandle, char *pkt, int maxlen) { int plen; // first read 4 bytes header if ( FTDI_read_bytes(ftHandle, pkt, 4) ) { // get payload length plen = (pkt[1] & 0x00ff); plen <<= 8; plen += (pkt[2] & 0x00ff); // check buffer length if (plen+4 > maxlen) { std::cout << "FTDI_read_packet supplied buffer is not large enough for packet" << std::endl; FT_Purge(ftHandle, FT_PURGE_RX); return 0; } // and get payload if ( FTDI_read_bytes(ftHandle, &pkt[4], plen) ) { return plen+4; } } return 0; } bool CUsb3xxxInterface::FTDI_read_bytes(FT_HANDLE ftHandle, char *buffer, int len) { // this relies on FT_SetTimouts() mechanism int n; bool ok = false; ok = (FT_Read(ftHandle, (LPVOID)buffer, len, (LPDWORD)&n) == FT_OK) && (n == len); if ( !ok ) { //FT_Purge(ftHandle, FT_PURGE_RX); std::cout << "FTDI_read_bytes(" << len << ") failed : " << n << std::endl; } return ok; } bool CUsb3xxxInterface::FTDI_write_packet(FT_HANDLE ft_handle, const char *pkt, int len) { FT_STATUS ftStatus; bool ok = true; int nwritten; if ( len > 0 ) { ftStatus = FT_Write(m_FtdiHandle, (LPVOID *)pkt, (DWORD)len, (LPDWORD)&nwritten); ok = (ftStatus == FT_OK) && (len == nwritten); if ( !ok ) { FTDI_Error((char *)"FT_Write", ftStatus); } } return ok; } //////////////////////////////////////////////////////////////////////////////////////// // error reporting void CUsb3xxxInterface::FTDI_Error(char *func_string, FT_STATUS ftStatus) { std::cout << "FTDI function " << func_string << " error " << (int)ftStatus << std::endl; }