A class to communicate a USB dac (send:only 48kHz,16bit,2ch , receive:only 48kHz,16bit,1ch). Need "USBHost_AddIso" library.
Dependents: USBHostDac_Audio_in_out
Fork of USBHostDac by
USBHostDac.cpp
- Committer:
- dkato
- Date:
- 2015-09-30
- Revision:
- 1:9ff4cba6524d
- Parent:
- 0:3a3146f89bcc
File content as of revision 1:9ff4cba6524d:
/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright (C) 2015 Renesas Electronics Corporation. All rights reserved.
*******************************************************************************/
#include "USBHostDac.h"
#define SAMPLING (0x0100)
#define SET_CUR (0x01)
#define FRAME_COUNT (8)
#define PACKET_SIZE (192)
#define QUEUE_NUM (3)
USBHostDac::USBHostDac() {
host = USBHost::getHostInst();
iso_send.m_isoEp = new IsochronousEp;
iso_send.p_rest_data = NULL;
iso_recv.m_isoEp = new IsochronousEp;
iso_recv.p_rest_data = NULL;
init();
}
USBHostDac::~USBHostDac() {
delete iso_send.m_isoEp;
delete iso_recv.m_isoEp;
}
void USBHostDac::init() {
dev = NULL;
dev_connected = false;
audio_device_found = false;
audio_intf = -1;
audio_intf_cnt = 0;
iso_send.bEndpointAddress = 0;
iso_send.rest_data_index = 0;
iso_send.rest_data_size = 0;
if (iso_send.p_rest_data != NULL) {
delete iso_send.p_rest_data;
iso_send.p_rest_data = NULL;
}
iso_recv.bEndpointAddress = 0;
iso_recv.rest_data_index = 0;
iso_recv.rest_data_size = 0;
if (iso_recv.p_rest_data != NULL) {
delete iso_recv.p_rest_data;
iso_recv.p_rest_data = NULL;
}
}
bool USBHostDac::connected() {
return dev_connected;
}
bool USBHostDac::connect() {
if (dev_connected) {
return true;
}
for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
if ((dev = host->getDevice(i)) != NULL) {
if (host->enumerate(dev, this)) {
break;
}
if (audio_device_found) {
USB_INFO("New UsbDac device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, audio_intf);
dev->setName("UsbDac", audio_intf);
host->registerDriver(dev, audio_intf, this, &USBHostDac::onDisconnect);
int addr = dev->getAddress();
if (iso_send.bEndpointAddress != 0) {
iso_send.p_rest_data = new uint8_t[PACKET_SIZE * FRAME_COUNT];
iso_send.m_isoEp->init(addr, iso_send.bEndpointAddress, PACKET_SIZE, FRAME_COUNT, QUEUE_NUM);
setInterface(iso_send.bAlternateSetting, iso_send.bInterfaceNumber);
setSamplingRate(iso_send.bEndpointAddress, 48000);
}
if (iso_recv.bEndpointAddress != 0) {
iso_recv.p_rest_data = new uint8_t[iso_recv.wMaxPacketSize * FRAME_COUNT];
iso_recv.m_isoEp->init(addr, iso_recv.bEndpointAddress, iso_recv.wMaxPacketSize, FRAME_COUNT, QUEUE_NUM);
setInterface(iso_recv.bAlternateSetting, iso_recv.bInterfaceNumber);
setSamplingRate(iso_recv.bEndpointAddress, 48000);
}
dev_connected = true;
return true;
}
}
}
init();
return false;
}
uint32_t USBHostDac::send(uint8_t* buf, uint32_t len, bool flush) {
uint32_t send_index = 0;
uint32_t rest_size = len;
uint32_t send_size;
uint32_t copy_size;
int result;
if (iso_send.bEndpointAddress == 0) {
return 0;
}
if (iso_send.rest_data_index != 0) {
if (rest_size > iso_send.rest_data_size) {
copy_size = iso_send.rest_data_size;
} else {
copy_size = rest_size;
}
memcpy(&iso_send.p_rest_data[iso_send.rest_data_index], &buf[send_index], copy_size);
send_index += copy_size;
rest_size -= copy_size;
iso_send.rest_data_index += copy_size;
if ((flush != false) || (iso_send.rest_data_index >= (PACKET_SIZE * FRAME_COUNT))) {
if (iso_send.m_isoEp->getQueueNum() == 0) {
iso_send.m_isoEp->reset(4);
}
result = iso_send.m_isoEp->isochronousSend(&iso_send.p_rest_data[0], iso_send.rest_data_index, 100);
iso_send.rest_data_index = 0;
}
}
while ((dev_connected) && (rest_size > 0)) {
if ((flush == false) && (rest_size < (PACKET_SIZE * FRAME_COUNT))) {
memcpy(&iso_send.p_rest_data[0], &buf[send_index], rest_size);
iso_send.rest_data_index = rest_size;
iso_send.rest_data_size = (PACKET_SIZE * FRAME_COUNT) - rest_size;
break;
} else {
if (rest_size >= (PACKET_SIZE * FRAME_COUNT)) {
send_size = (PACKET_SIZE * FRAME_COUNT);
} else {
send_size = rest_size;
}
if (iso_send.m_isoEp->getQueueNum() == 0) {
iso_send.m_isoEp->reset(4);
}
result = iso_send.m_isoEp->isochronousSend(&buf[send_index], send_size, 100);
send_index += result;
rest_size -= result;
}
}
return send_index;
}
uint32_t USBHostDac::receive(uint8_t* buf, uint32_t len) {
uint32_t recv_index = 0;
uint32_t rest_size = len;
uint32_t copy_size;
if (iso_recv.bEndpointAddress == 0) {
return 0;
}
if (iso_recv.rest_data_size != 0) {
if (rest_size > iso_recv.rest_data_size) {
copy_size = iso_recv.rest_data_size;
} else {
copy_size = rest_size;
}
memcpy(&buf[recv_index], &iso_recv.p_rest_data[iso_recv.rest_data_index], copy_size);
recv_index += copy_size;
rest_size -= copy_size;
iso_recv.rest_data_index += copy_size;
iso_recv.rest_data_size -= copy_size;
}
while ((dev_connected) && (rest_size > 0)) {
iso_recv.rest_data_index = 0;
iso_recv.rest_data_size = 0;
if (iso_recv.m_isoEp->getQueueNum() == 0) {
iso_recv.m_isoEp->reset(4);
}
HCITD* itd = iso_recv.m_isoEp->isochronousReceive(100);
if (itd) {
uint8_t cc = itd->ConditionCode();
if (cc == 0) {
int fc = itd->FrameCount();
uint8_t* wk_buf = const_cast<uint8_t*>(itd->buf);
int mps = iso_recv.m_isoEp->m_PacketSize;
for (int i = 0; i < fc; i++) {
uint16_t psw = itd->OffsetPSW[i];
cc = psw>>12;
if (cc == 0 || cc == 9) {
int wk_len = psw & 0x7ff;
if (rest_size > 0) {
if (rest_size > wk_len) {
copy_size = wk_len;
} else {
copy_size = rest_size;
}
memcpy(&buf[recv_index], wk_buf, copy_size);
recv_index += copy_size;
rest_size -= copy_size;
if (copy_size < wk_len) {
memcpy(&iso_recv.p_rest_data[iso_recv.rest_data_size], &wk_buf[copy_size], (wk_len - copy_size));
iso_recv.rest_data_size += (wk_len - copy_size);
}
} else {
memcpy(&iso_recv.p_rest_data[iso_recv.rest_data_size], &wk_buf[0], wk_len);
iso_recv.rest_data_size += wk_len;
}
}
wk_buf += mps;
}
}
delete itd;
}
}
return recv_index;
}
/*virtual*/ void USBHostDac::setVidPid(uint16_t vid, uint16_t pid)
{
// we don't check VID/PID for audio driver
}
/*virtual*/ bool USBHostDac::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
{
bool ret;
if (audio_intf_cnt >= 2) {
ret = false;
} else if ((intf_class == AUDIO_CLASS) && (intf_subclass == 2) && (intf_protocol == 0)) {
// AUDIOSTREAMING Subclass
ret = chkAudioStreaming();
if (ret != false) {
audio_intf = intf_nb;
audio_device_found = true;
audio_intf_cnt++;
}
} else {
ret = false;
}
return false;
}
/*virtual*/ bool USBHostDac::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
{
return false;
}
bool USBHostDac::chkAudioStreaming() {
uint8_t * conf_descr = host->getConfDescrCurPtr();
uint16_t len = host->getConfDescrRestLen();
uint32_t index = 0;
uint32_t len_desc = conf_descr[index];
uint32_t cnt;
uint32_t wk_sampling;
bool smpling_ok = false;
bool loop_end = false;
uint8_t channels = 0;
uint8_t SubframeSize = 0;
uint8_t id;
uint16_t wk_wMaxPacketSize;
uint8_t wk_bEndpointAddress;
uint8_t wk_bInterfaceNumber;
uint8_t wk_bAlternateSetting;
/* bNumEndpoints */
if (conf_descr[index + 4] >= 1) {
wk_bInterfaceNumber = conf_descr[index + 2];
wk_bAlternateSetting = conf_descr[index + 3];
wk_wMaxPacketSize = 0;
wk_bEndpointAddress = 0;
index += len_desc;
while ((index < len) && (loop_end == false)) {
len_desc = conf_descr[index];
id = conf_descr[index+1];
switch (id) {
case INTERFACE_DESCRIPTOR:
/* next interface descriptor */
loop_end = true;
break;
case ENDPOINT_DESCRIPTOR:
if ((conf_descr[index + 3] & 0x03) == ISOCHRONOUS_ENDPOINT) {
wk_bEndpointAddress = conf_descr[index + 2];
wk_wMaxPacketSize = (conf_descr[index + 5] << 8) + conf_descr[index + 4];
loop_end = true;
}
break;
case 0x24: /* Audio Class Specific INTERFACE Descriptor */
if ((conf_descr[index + 2] == 2) && (conf_descr[index + 3] == 1)) {
channels = conf_descr[index + 4];
SubframeSize = conf_descr[index + 5];
for (cnt = 8; (cnt + 3) <= len_desc; cnt += 3) {
wk_sampling = ((uint32_t)conf_descr[index + cnt + 0] << 0)
| ((uint32_t)conf_descr[index + cnt + 1] << 8)
| ((uint32_t)conf_descr[index + cnt + 2] << 16);
if (wk_sampling == 48000) {
smpling_ok = true;
}
}
}
break;
default:
break;
}
index += len_desc;
}
if (((wk_bEndpointAddress & 0x80) == 0) && (wk_wMaxPacketSize >= PACKET_SIZE)
&& (channels == 2) && (SubframeSize == 2) && (smpling_ok != false)) {
iso_send.wMaxPacketSize = wk_wMaxPacketSize;
iso_send.bEndpointAddress = wk_bEndpointAddress;
iso_send.bInterfaceNumber = wk_bInterfaceNumber;
iso_send.bAlternateSetting = wk_bAlternateSetting;
return true;
}
if (((wk_bEndpointAddress & 0x80) != 0) && (wk_wMaxPacketSize >= (PACKET_SIZE/2))
&& (channels == 1) && (SubframeSize == 2) && (smpling_ok != false)) {
iso_recv.wMaxPacketSize = wk_wMaxPacketSize;
iso_recv.bEndpointAddress = wk_bEndpointAddress;
iso_recv.bInterfaceNumber = wk_bInterfaceNumber;
iso_recv.bAlternateSetting = wk_bAlternateSetting;
return true;
}
}
return false;
}
void USBHostDac::onDisconnect() {
if (dev_connected) {
if (iso_send.bEndpointAddress != 0) {
iso_send.m_isoEp->disconnect();
}
if (iso_recv.bEndpointAddress != 0) {
iso_recv.m_isoEp->disconnect();
}
init();
}
}
USB_TYPE USBHostDac::setInterface(uint16_t alt, uint16_t index) {
return host->controlWrite( dev,
USB_RECIPIENT_INTERFACE | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD,
SET_INTERFACE,
alt, index, NULL, 0);
}
void USBHostDac::setSamplingRate(uint8_t endpoint_adder, uint32_t sampling_rate) {
uint8_t data[3];
data[0] = (uint8_t)((sampling_rate >> 0) & 0xff);
data[1] = (uint8_t)((sampling_rate >> 8) & 0xff);
data[2] = (uint8_t)((sampling_rate >> 16) & 0xff);
host->controlWrite( dev,
USB_RECIPIENT_ENDPOINT | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS,
SET_CUR,
SAMPLING, endpoint_adder, data, 3);
}
