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 GR-PEACH_producer_meeting

USBHostDac.cpp

Committer:
dkato
Date:
2015-04-01
Revision:
0:3a3146f89bcc
Child:
1:9ff4cba6524d

File content as of revision 0:3a3146f89bcc:

/*******************************************************************************
* 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();
    m_isoEp = new IsochronousEp;
    p_rest_data = new uint8_t[USBDAC_DATA_SIZE];

    init();
}

void USBHostDac::init() {
    dev = NULL;
    dev_connected = false;
    audio_device_found = false;
    audio_intf = -1;
    audio_intf_cnt = 0;
    rest_data_index = 0;

    wMaxPacketSize    = 0;
    bInterfaceNumber  = 0;
    bAlternateSetting = 0;
    bEndpointAddress  = 0;
}

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();
                m_isoEp->init(addr, bEndpointAddress, PACKET_SIZE, FRAME_COUNT, QUEUE_NUM);
                setInterface(bAlternateSetting, bInterfaceNumber);
                setSamplingRate(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;
    uint32_t def_send_size = PACKET_SIZE * FRAME_COUNT;

    if (rest_data_index != 0) {
        if (rest_size > (def_send_size - rest_data_index)) {
            copy_size = (def_send_size - rest_data_index);
        } else {
            copy_size = rest_size;
        }
        memcpy(&p_rest_data[rest_data_index], &buf[send_index], copy_size);
        send_index      += copy_size;
        rest_size       -= copy_size;
        rest_data_index += copy_size;
        if ((flush != false) || (rest_data_index >= def_send_size)) {
            m_isoEp->isochronousSend(&p_rest_data[0], rest_data_index, 100);
            rest_data_index = 0;
        }
    }

    while ((dev_connected) && (rest_size > 0)) {
        if ((flush == false) && (rest_size < def_send_size)) {
            memcpy(&p_rest_data[0], &buf[send_index], rest_size);
            rest_data_index = rest_size;
            break;
        } else {
            if (rest_size >= def_send_size) {
                send_size = def_send_size;
            } else {
                send_size = rest_size;
            }
            m_isoEp->isochronousSend(&buf[send_index], send_size, 100);
            send_index += send_size;
            rest_size  -= send_size;
        }
    }

    return send_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 != -1) {
        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;
        }
    } 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;

    /* bNumEndpoints */
    if (conf_descr[index + 4] >= 1) {
        bInterfaceNumber  = conf_descr[index + 2];
        bAlternateSetting = conf_descr[index + 3];

        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) {
                        bEndpointAddress = conf_descr[index + 2];
                        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 (((bEndpointAddress & 0x80) == 0) && (wMaxPacketSize >= PACKET_SIZE)
         && (channels == 2) && (SubframeSize == 2) && (smpling_ok != false)) {
            return true;
        }
    }

    return false;
}

void USBHostDac::onDisconnect() {
    if (dev_connected) {
        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(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, bEndpointAddress, data, 3);
}