Publishing USB Audio driver from my dev.branch on the old SVN repo.

This commit is contained in:
Siarzhuk Zharski
2012-05-03 20:37:59 +02:00
parent b2070d2855
commit 408c7ab11d
22 changed files with 6513 additions and 1464 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,319 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _AUDIO_CONTROL_INTERFACE_H_
#define _AUDIO_CONTROL_INTERFACE_H_
#include <util/VectorMap.h>
#include "Driver.h"
#include "USB_audio_spec.h"
class AudioControlInterface;
class AudioChannelCluster {
public:
AudioChannelCluster();
virtual ~AudioChannelCluster();
uint8 ChannelsCount() { return fOutChannelsNumber; }
uint32 ChannelsConfig() { return fChannelsConfig; }
protected:
uint8 fOutChannelsNumber;
uint32 fChannelsConfig;
uint8 fChannelNames;
};
template< class __base_class_name >
class _AudioChannelCluster
: public __base_class_name, public AudioChannelCluster {
public:
_AudioChannelCluster(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header)
:
__base_class_name(interface, Header) {}
virtual ~_AudioChannelCluster() {}
virtual AudioChannelCluster*
OutCluster() { return this; }
};
//
// base class for Audio Controls (Units and Terminals)
//
//
class _AudioControl {
public:
_AudioControl(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~_AudioControl();
uint8 ID() { return fID; }
uint8 SourceID() { return fSourceID; }
uint8 SubType() { return fSubType; }
status_t InitCheck() { return fStatus; };
virtual const char* Name() { return ""; }
virtual AudioChannelCluster* OutCluster();
protected:
// state tracking
status_t fStatus;
AudioControlInterface* fInterface;
uint8 fSubType;
uint8 fID;
uint8 fSourceID;
uint8 fStringIndex;
};
class _Terminal : public _AudioControl {
public:
_Terminal(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~_Terminal();
uint16 TerminalType() { return fTerminalType; }
bool IsUSBIO();
virtual const char* Name();
protected:
uint16 fTerminalType;
uint8 fAssociatedTerminal;
uint8 fClockSourceId;
uint16 fControlsBitmap;
};
class InputTerminal : public _AudioChannelCluster<_Terminal> {
public:
InputTerminal(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~InputTerminal();
protected:
};
class OutputTerminal : public _Terminal {
public:
OutputTerminal(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~OutputTerminal();
protected:
};
class MixerUnit : public _AudioChannelCluster<_AudioControl> {
public:
MixerUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~MixerUnit();
protected:
Vector<uint8> fInputPins;
Vector<uint8> fProgrammableControls;
uint8 fControlsBitmap;
};
class SelectorUnit : public _AudioControl {
public:
SelectorUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~SelectorUnit();
virtual AudioChannelCluster* OutCluster();
// protected:
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
};
class FeatureUnit : public _AudioControl {
public:
FeatureUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~FeatureUnit();
virtual const char* Name();
bool HasControl(int32 Channel, uint32 Control);
// protected:
void NormalizeAndTraceChannel(int32 Channel);
Vector<uint32> fControlBitmaps;
};
class EffectUnit : public _AudioControl/*, public _AudioChannelsCluster*/ {
public:
EffectUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~EffectUnit();
protected:
/* uint16 fProcessType;
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
Vector<uint16> fModes;
*/};
class ProcessingUnit : public _AudioChannelCluster<_AudioControl> {
public:
ProcessingUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ProcessingUnit();
protected:
uint16 fProcessType;
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
Vector<uint16> fModes;
};
class ExtensionUnit : public _AudioChannelCluster<_AudioControl> {
public:
ExtensionUnit(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ExtensionUnit();
protected:
uint16 fExtensionCode;
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
};
class ClockSource : public _AudioControl {
public:
ClockSource(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ClockSource();
protected:
};
class ClockSelector : public _AudioControl {
public:
ClockSelector(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ClockSelector();
protected:
};
class ClockMultiplier : public _AudioControl {
public:
ClockMultiplier(AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ClockMultiplier();
protected:
};
class SampleRateConverter : public _AudioControl {
public:
SampleRateConverter(
AudioControlInterface* interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~SampleRateConverter();
protected:
};
typedef VectorMap<uint32, _AudioControl*> AudioControlsMap;
typedef VectorMap<uint32, _AudioControl*>::Iterator AudioControlsIterator;
typedef Vector<_AudioControl*> AudioControlsVector;
// typedef Vector<_AudioControl*>::Iterator AudioControlsIterator;
class Device;
class AudioControlInterface {
public:
AudioControlInterface(Device* device);
~AudioControlInterface();
status_t InitCheck() { return fStatus; }
status_t Init(size_t interface, usb_interface_info *Interface);
_AudioControl* Find(uint8 id);
_AudioControl* FindOutputTerminal(uint8 id);
uint16 SpecReleaseNumber() { return fADCSpecification; }
AudioControlsMap& Controls() { return fAudioControls; }
uint32 GetChannelsDescription(
Vector<multi_channel_info>& Channels,
multi_description *Description,
AudioControlsVector &USBTerminals);
uint32 GetBusChannelsDescription(
Vector<multi_channel_info>& Channels,
multi_description *Description);
status_t GetMix(multi_mix_value_info *Info);
status_t SetMix(multi_mix_value_info *Info);
status_t ListMixControls(multi_mix_control_info* Info);
protected:
status_t InitACHeader(size_t interface,
usb_audiocontrol_header_descriptor* Header);
uint32 GetTerminalChannels(
Vector<multi_channel_info>& Channels,
AudioChannelCluster* cluster,
channel_kind kind, uint32 connectors = 0);
void _HarvestRecordFeatureUnits(_AudioControl* rootControl,
AudioControlsMap& Map);
void _ListMixControlsPage(int32& index,
multi_mix_control_info* Info,
AudioControlsMap& Map, const char* Name);
int32 _ListFeatureUnitControl(int32& index, int32 parentId,
multi_mix_control_info* Info,
_AudioControl* control);
uint32 _ListFeatureUnitOption(uint32 controlType,
int32& index, int32 parentIndex,
multi_mix_control_info* Info, FeatureUnit* unit,
uint32 channel, uint32 channels);
void _ListSelectorUnitControl(int32& index, int32 parentGroup,
multi_mix_control_info* Info,
_AudioControl* control);
void _InitGainLimits(multi_mix_control& Control);
size_t fInterface;
status_t fStatus;
// part of AudioControl Header description
uint16 fADCSpecification;
Vector<uint8> fStreams;
uint8 fFunctionCategory;
uint8 fControlsBitmap;
Device* fDevice;
// map to store all controls and lookup by control ID
AudioControlsMap fAudioControls;
// map to store output terminal and lookup them by source ID
AudioControlsMap fOutputTerminals;
// map to store output terminal and lookup them by control ID
AudioControlsMap fInputTerminals;
};
#endif // _AUDIO_CONTROL_INTERFACE_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,349 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _AUDIO_FUNCTION_H_
#define _AUDIO_FUNCTION_H_
#include <VectorMap.h>
#include "Driver.h"
#include "USB_audio_spec.h"
class Device;
//
// base class for all entities in Audio Function
//
//
class _AudioFunctionEntity {
public:
_AudioFunctionEntity(Device* device, size_t interface);
~_AudioFunctionEntity();
status_t InitCheck() { return fStatus; };
protected:
// state tracking
status_t fStatus;
Device* fDevice;
size_t fInterface;
};
class _AudioChannelsCluster {
public:
_AudioChannelsCluster();
virtual ~_AudioChannelsCluster();
virtual _AudioChannelsCluster* OutputCluster();
protected:
uint8 fOutChannelsNumber;
uint32 fChannelsConfig;
uint8 fChannelNames;
};
//
// base class for Audio Controls (Units and Terminals)
//
//
class _AudioControl : public _AudioFunctionEntity {
public:
_AudioControl(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~_AudioControl();
uint8 ID() { return fID; }
uint8 SourceID() { return fSourceID; }
uint8 SubType() { return fSubType; }
virtual const char* Name() { return ""; }
virtual _AudioChannelsCluster* OutputCluster();
protected:
// state tracking
uint8 fSubType;
uint8 fID;
uint8 fSourceID;
uint8 fStringIndex;
};
class AudioControlHeader : public _AudioControl {
public:
AudioControlHeader(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~AudioControlHeader();
// protected:
uint16 fADCSpecification;
Vector<uint8> fStreams;
uint8 fFunctionCategory;
uint8 fControlsBitmap;
};
class _Terminal : public _AudioControl {
public:
_Terminal(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~_Terminal();
uint16 TerminalType() { return fTerminalType; }
bool IsUSBIO();
virtual const char* Name();
protected:
uint16 fTerminalType;
uint8 fAssociatedTerminal;
uint8 fClockSourceId;
uint16 fControlsBitmap;
};
class InputTerminal : public _Terminal, public _AudioChannelsCluster {
public:
InputTerminal(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~InputTerminal();
protected:
};
class OutputTerminal : public _Terminal {
public:
OutputTerminal(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~OutputTerminal();
protected:
};
class MixerUnit : public _AudioControl, public _AudioChannelsCluster {
public:
MixerUnit(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~MixerUnit();
protected:
Vector<uint8> fInputPins;
Vector<uint8> fProgrammableControls;
uint8 fControlsBitmap;
};
class SelectorUnit : public _AudioControl {
public:
SelectorUnit(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~SelectorUnit();
protected:
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
};
class FeatureUnit : public _AudioControl {
public:
FeatureUnit(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~FeatureUnit();
virtual const char* Name();
bool HasControl(int32 Channel, uint32 Control);
protected:
void TraceChannel(Device* device, int32 Channel);
Vector<uint32> fControlBitmaps;
};
class EffectUnit : public _AudioControl/*, public _AudioChannelsCluster*/ {
public:
EffectUnit(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~EffectUnit();
protected:
/* uint16 fProcessType;
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
Vector<uint16> fModes;
*/};
class ProcessingUnit : public _AudioControl, public _AudioChannelsCluster {
public:
ProcessingUnit(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ProcessingUnit();
protected:
uint16 fProcessType;
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
Vector<uint16> fModes;
};
class ExtensionUnit : public _AudioControl, public _AudioChannelsCluster {
public:
ExtensionUnit(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ExtensionUnit();
protected:
uint16 fExtensionCode;
Vector<uint8> fInputPins;
uint8 fControlsBitmap;
};
class ClockSource : public _AudioControl {
public:
ClockSource(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ClockSource();
protected:
};
class ClockSelector : public _AudioControl {
public:
ClockSelector(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ClockSelector();
protected:
};
class ClockMultiplier : public _AudioControl {
public:
ClockMultiplier(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~ClockMultiplier();
protected:
};
class SampleRateConverter : public _AudioControl {
public:
SampleRateConverter(Device* device, size_t interface,
usb_audiocontrol_header_descriptor* Header);
virtual ~SampleRateConverter();
protected:
};
//
// Audio Streaming Interface information entities
//
//
class ASInterfaceDescriptor : public _AudioFunctionEntity {
public:
ASInterfaceDescriptor(Device* device, size_t interface,
usb_as_interface_descriptor_r1* Descriptor);
~ASInterfaceDescriptor();
// protected:
uint8 fTerminalLink;
uint8 fDelay;
uint16 fFormatTag;
};
class ASEndpointDescriptor : public _AudioFunctionEntity {
public:
ASEndpointDescriptor(Device* device, size_t interface,
usb_as_cs_endpoint_descriptor* Descriptor);
~ASEndpointDescriptor();
// protected:
uint8 fAttributes;
uint8 fLockDelayUnits;
uint16 fLockDelay;
};
class _ASFormatDescriptor : public _AudioFunctionEntity {
public:
_ASFormatDescriptor(Device* device, size_t interface);
virtual ~_ASFormatDescriptor();
// protected:
uint32 GetSamFreq(uint8* freq);
};
class TypeIFormatDescriptor : public _ASFormatDescriptor {
public:
TypeIFormatDescriptor(Device* device, size_t interface,
usb_type_I_format_descriptor* Descriptor);
virtual ~TypeIFormatDescriptor();
status_t Init(usb_type_I_format_descriptor* Descriptor);
// protected:
uint8 fNumChannels;
uint8 fSubframeSize;
uint8 fBitResolution;
uint8 fSampleFrequencyType;
Vector<uint32> fSampleFrequencies;
};
class TypeIIFormatDescriptor : public _ASFormatDescriptor {
public:
TypeIIFormatDescriptor(Device* device, size_t interface,
usb_type_II_format_descriptor* Descriptor);
virtual ~TypeIIFormatDescriptor();
// protected:
uint16 fMaxBitRate;
uint16 fSamplesPerFrame;
uint8 fSampleFrequencyType;
Vector<uint32> fSampleFrequencies;
};
class TypeIIIFormatDescriptor : public TypeIFormatDescriptor {
public:
TypeIIIFormatDescriptor(Device* device, size_t interface,
usb_type_III_format_descriptor* Descriptor);
virtual ~TypeIIIFormatDescriptor();
// protected:
};
class AudioStreamAlternate {
public:
AudioStreamAlternate(size_t alternate,
ASInterfaceDescriptor* interface,
ASEndpointDescriptor* endpoint,
_ASFormatDescriptor* format);
~AudioStreamAlternate();
ASInterfaceDescriptor* Interface() { return fInterface; }
ASEndpointDescriptor* Endpoint() { return fEndpoint; }
_ASFormatDescriptor* Format() { return fFormat; }
protected:
size_t fAlternate;
ASInterfaceDescriptor* fInterface;
ASEndpointDescriptor* fEndpoint;
_ASFormatDescriptor* fFormat;
};
#endif // _AUDIO_FUNCTION_H_
@@ -0,0 +1,406 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the tems of the MIT license.
*
*/
#include "AudioStreamingInterface.h"
#include "Settings.h"
#include "Device.h"
#include "audio.h"
//
// Audio Stream information entities
//
//
ASInterfaceDescriptor::ASInterfaceDescriptor(/*Device* device, size_t interface,*/
usb_as_interface_descriptor_r1* Descriptor)
:
// _AudioFunctionEntity(device, interface),
fTerminalLink(0),
fDelay(0),
fFormatTag(0)
{
fTerminalLink = Descriptor->terminal_link;
fDelay = Descriptor->delay;
fFormatTag = Descriptor->format_tag;
TRACE("fTerminalLink:%d\n", fTerminalLink);
TRACE("fDelay:%d\n", fDelay);
TRACE("fFormatTag:%#06x\n", fFormatTag);
// fStatus = B_OK;
}
ASInterfaceDescriptor::~ASInterfaceDescriptor()
{
}
ASEndpointDescriptor::ASEndpointDescriptor(usb_endpoint_descriptor* Endpoint,
usb_as_cs_endpoint_descriptor* Descriptor)
:
fAttributes(0),
fLockDelayUnits(0),
fLockDelay(0),
fMaxPacketSize(0),
fEndpointAddress(0)
{
// usb_audiocontrol_header_descriptor *Header
// = (usb_audiocontrol_header_descriptor *)Interface->generic[i];
fAttributes = Descriptor->attributes;
fLockDelayUnits = Descriptor->lock_delay_units;
fLockDelay = Descriptor->lock_delay;
// usb_endpoint_descriptor* endpoint = Interface->endpoint[0]->descr;
fEndpointAddress = Endpoint->endpoint_address;
fMaxPacketSize = Endpoint->max_packet_size;
TRACE("fAttributes:%d\n", fAttributes);
TRACE("fLockDelayUnits:%d\n", fLockDelayUnits);
TRACE("fLockDelay:%d\n", fLockDelay);
TRACE("fMaxPacketSize:%d\n", fMaxPacketSize);
TRACE("fEndpointAddress:%#02x\n", fEndpointAddress);
}
ASEndpointDescriptor::~ASEndpointDescriptor()
{
}
_ASFormatDescriptor::_ASFormatDescriptor(usb_type_I_format_descriptor* Descriptor)
:
/*_AudioFunctionEntity(device, interface)*/
fFormatType(UAF_FORMAT_TYPE_UNDEFINED)
{
fFormatType = Descriptor->format_type;
}
_ASFormatDescriptor::~_ASFormatDescriptor()
{
}
uint32
_ASFormatDescriptor::GetSamFreq(uint8* freq)
{
return freq[0] | freq[1] << 8 | freq[2] << 16;
}
TypeIFormatDescriptor::TypeIFormatDescriptor(/*Device* device, size_t interface,*/
usb_type_I_format_descriptor* Descriptor)
:
_ASFormatDescriptor(Descriptor),
fNumChannels(0),
fSubframeSize(0),
fBitResolution(0),
fSampleFrequencyType(0)
{
/*fStatus =*/ Init(Descriptor);
}
TypeIFormatDescriptor::~TypeIFormatDescriptor()
{
}
status_t
TypeIFormatDescriptor::Init(usb_type_I_format_descriptor* Descriptor)
{
fNumChannels = Descriptor->nr_channels;
fSubframeSize = Descriptor->subframe_size;
fBitResolution = Descriptor->bit_resolution;
fSampleFrequencyType = Descriptor->sam_freq_type;
if (fSampleFrequencyType == 0) {
fSampleFrequencies.PushBack(
GetSamFreq(Descriptor->sf.cont.lower_sam_freq));
fSampleFrequencies.PushBack(
GetSamFreq(Descriptor->sf.cont.upper_sam_freq));
} else {
for (size_t i = 0; i < fSampleFrequencyType; i++) {
fSampleFrequencies.PushBack(
GetSamFreq(Descriptor->sf.discr.sam_freq[i]));
}
}
TRACE("fNumChannels:%d\n", fNumChannels);
TRACE("fSubframeSize:%d\n", fSubframeSize);
TRACE("fBitResolution:%d\n", fBitResolution);
TRACE("fSampleFrequencyType:%d\n", fSampleFrequencyType);
for (int32 i = 0; i < fSampleFrequencies.Count(); i++) {
TRACE("Frequency #%d: %d\n", i, fSampleFrequencies[i]);
}
return B_OK;
}
TypeIIFormatDescriptor::TypeIIFormatDescriptor(/*Device* device, size_t interface,*/
usb_type_II_format_descriptor* Descriptor)
:
_ASFormatDescriptor((usb_type_I_format_descriptor*)Descriptor),
fMaxBitRate(0),
fSamplesPerFrame(0),
fSampleFrequencyType(0),
fSampleFrequencies(0)
{
}
TypeIIFormatDescriptor::~TypeIIFormatDescriptor()
{
}
TypeIIIFormatDescriptor::TypeIIIFormatDescriptor(/*Device* device, size_t interface,*/
usb_type_III_format_descriptor* Descriptor)
:
TypeIFormatDescriptor(/*device, interface, */
(usb_type_I_format_descriptor*) Descriptor)
{
}
TypeIIIFormatDescriptor::~TypeIIIFormatDescriptor()
{
}
AudioStreamAlternate::AudioStreamAlternate(size_t alternate,
ASInterfaceDescriptor* interface,
ASEndpointDescriptor* endpoint,
_ASFormatDescriptor* format)
:
fAlternate(alternate),
fInterface(interface),
fEndpoint(endpoint),
fFormat(format)
{
}
AudioStreamAlternate::~AudioStreamAlternate()
{
delete fInterface;
delete fEndpoint;
delete fFormat;
}
AudioStreamingInterface::AudioStreamingInterface(
AudioControlInterface* controlInterface,
size_t interface, usb_interface_list *List)
:
fInterface(interface),
fControlInterface(controlInterface),
fIsInput(false),
fActiveAlternate(0)
{
TRACE_ALWAYS("if[%d]:alt_count:%d\n", interface, List->alt_count);
for (size_t alt = 0; alt < List->alt_count; alt++) {
ASInterfaceDescriptor* ASInterface = NULL;
ASEndpointDescriptor* ASEndpoint = NULL;
_ASFormatDescriptor* ASFormat = NULL;
usb_interface_info *Interface = &List->alt[alt];
TRACE_ALWAYS("if[%d]:alt[%d]:descrs_count:%d\n",
interface, alt, Interface->generic_count);
for (size_t i = 0; i < Interface->generic_count; i++) {
usb_audiocontrol_header_descriptor *Header
= (usb_audiocontrol_header_descriptor *)Interface->generic[i];
if (Header->descriptor_type == AC_CS_INTERFACE) {
switch(Header->descriptor_subtype) {
case UAS_AS_GENERAL:
if (ASInterface == 0) {
ASInterface = new ASInterfaceDescriptor(
/*this, interface, */
(usb_as_interface_descriptor_r1*) Header);
} else
TRACE_ALWAYS("Duplicate AStream interface ignored.\n");
break;
case UAS_FORMAT_TYPE:
if (ASFormat == 0) {
ASFormat = new TypeIFormatDescriptor(
(usb_type_I_format_descriptor*) Header);
} else
TRACE_ALWAYS("Duplicate AStream format ignored.\n");
break;
default:
TRACE_ALWAYS("Ignore AStream descr subtype %#04x\n",
Header->descriptor_subtype);
break;
}
continue;
}
if (Header->descriptor_type == AC_CS_ENDPOINT) {
if (ASEndpoint == 0) {
usb_endpoint_descriptor* Endpoint
= Interface->endpoint[0].descr;
ASEndpoint = new ASEndpointDescriptor(Endpoint,
(usb_as_cs_endpoint_descriptor*)Header);
} else
TRACE_ALWAYS("Duplicate AStream endpoint ignored.\n");
continue;
}
TRACE_ALWAYS("Ignore Audio Stream of "
"unknown descriptor type %#04x.\n", Header->descriptor_type);
}
fAlternates.Add(new AudioStreamAlternate(alt, ASInterface,
ASEndpoint, ASFormat));
}
}
AudioStreamingInterface::~AudioStreamingInterface()
{
// alternates of the streams
// StreamAlternatesVector fAlternates;
// size_t fActiveAlternate;
// we own stream header objects too, so free them
for (StreamAlternatesIterator I = fAlternates.Begin();
I != fAlternates.End(); I++) {
delete *I;
}
fAlternates.MakeEmpty();
}
uint8
AudioStreamingInterface::TerminalLink()
{
if (fAlternates[fActiveAlternate]->Interface() != 0) {
return fAlternates[fActiveAlternate]->Interface()->fTerminalLink;
}
return 0;
}
AudioChannelCluster*
AudioStreamingInterface::ChannelCluster()
{
_AudioControl* control = fControlInterface->Find(TerminalLink());
if (control == 0) {
TRACE_ALWAYS("Control was not found for terminal id:%d.\n",
TerminalLink());
return NULL;
}
return control->OutCluster();
}
/*
ASInterfaceDescriptor*
Stream::ASInterface()
{
return fAlternates[fActiveAlternate]->Interface();
}
_ASFormatDescriptor*
Stream::ASFormat()
{
return fAlternates[fActiveAlternate]->Format();
} */
void
AudioStreamingInterface::GetFormatsAndRates(multi_description *Description)
{
// TODO: fIsInput ??????
Description->interface_flags
|= fIsInput ? B_MULTI_INTERFACE_RECORD : B_MULTI_INTERFACE_PLAYBACK;
uint32 rates = 0;
uint32 formats = 0;
TypeIFormatDescriptor* format
= static_cast<TypeIFormatDescriptor*>(
fAlternates[fActiveAlternate]->Format());
if (format == NULL || fAlternates[fActiveAlternate]->Interface() == NULL) {
TRACE_ALWAYS("Ignore alternate %d due format "
"%#08x or interface %#08x null.\n", fActiveAlternate, format,
fAlternates[fActiveAlternate]->Interface());
}
if (format->fSampleFrequencyType == 0) { // continuous frequencies
rates = B_SR_CVSR;
Description->min_cvsr_rate = float(format->fSampleFrequencies[0]);
Description->max_cvsr_rate = float(format->fSampleFrequencies[1]);
} else {
for (int i = 0; i < format->fSampleFrequencies.Count(); i++) {
switch(format->fSampleFrequencies[i]) {
case 8000: rates |= B_SR_8000; break;
case 11025: rates |= B_SR_11025; break;
case 12000: rates |= B_SR_12000; break;
case 16000: rates |= B_SR_16000; break;
case 22050: rates |= B_SR_22050; break;
case 24000: rates |= B_SR_24000; break;
case 32000: rates |= B_SR_32000; break;
case 44100: rates |= B_SR_44100; break;
case 48000: rates |= B_SR_48000; break;
case 64000: rates |= B_SR_64000; break;
case 88200: rates |= B_SR_88200; break;
case 96000: rates |= B_SR_96000; break;
case 176400: rates |= B_SR_176400; break;
case 192000: rates |= B_SR_192000; break;
case 384000: rates |= B_SR_384000; break;
case 1536000: rates |= B_SR_1536000; break;
default:
TRACE_ALWAYS("Ignore unsupported "
"sample rate %d for alternate %d.\n",
format->fSampleFrequencies[i], fActiveAlternate);
break;
}
}
}
switch (fAlternates[fActiveAlternate]->Interface()->fFormatTag) {
case UAF_PCM8: formats = B_FMT_8BIT_U; break;
case UAF_IEEE_FLOAT: formats = B_FMT_FLOAT; break;
case UAF_PCM:
switch(format->fBitResolution) {
case 8: formats = B_FMT_8BIT_S; break;
case 16: formats = B_FMT_16BIT; break;
case 18: formats = B_FMT_18BIT; break;
case 20: formats = B_FMT_20BIT; break;
case 24: formats = B_FMT_24BIT; break;
case 32: formats = B_FMT_32BIT; break;
break;
default:
TRACE_ALWAYS("Ignore unsupported "
"bit resolution %d for alternate %d.\n",
format->fBitResolution, fActiveAlternate);
break;
}
break;
}
if (fIsInput) {
Description->input_rates = rates;
Description->input_formats = formats;
} else {
Description->output_rates = rates;
Description->output_formats = formats;
}
}
@@ -0,0 +1,156 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _AUDIO_STREAMING_INTERFACE_H_
#define _AUDIO_STREAMING_INTERFACE_H_
#include "Driver.h"
#include "AudioControlInterface.h"
#include <util/VectorMap.h>
#include "USB_audio_spec.h"
//
// Audio Streaming Interface information entities
//
//
class ASInterfaceDescriptor /*: public _AudioFunctionEntity*/ {
public:
ASInterfaceDescriptor(/*Device* device, size_t interface,*/
usb_as_interface_descriptor_r1* Descriptor);
~ASInterfaceDescriptor();
// protected:
uint8 fTerminalLink;
uint8 fDelay;
uint16 fFormatTag;
};
class ASEndpointDescriptor /*: public _AudioFunctionEntity*/ {
public:
ASEndpointDescriptor(
usb_endpoint_descriptor* Endpoint,
usb_as_cs_endpoint_descriptor* Descriptor);
~ASEndpointDescriptor();
// protected:
uint8 fAttributes;
uint8 fLockDelayUnits;
uint16 fLockDelay;
uint16 fMaxPacketSize;
uint8 fEndpointAddress;
};
class _ASFormatDescriptor /*: public _AudioFunctionEntity*/ {
public:
_ASFormatDescriptor(
usb_type_I_format_descriptor* Descriptor);
virtual ~_ASFormatDescriptor();
// protected:
uint8 fFormatType;
uint32 GetSamFreq(uint8* freq);
};
class TypeIFormatDescriptor : public _ASFormatDescriptor {
public:
TypeIFormatDescriptor(/*Device* device, size_t interface,*/
usb_type_I_format_descriptor* Descriptor);
virtual ~TypeIFormatDescriptor();
status_t Init(usb_type_I_format_descriptor* Descriptor);
// protected:
uint8 fNumChannels;
uint8 fSubframeSize;
uint8 fBitResolution;
uint8 fSampleFrequencyType;
Vector<uint32> fSampleFrequencies;
};
class TypeIIFormatDescriptor : public _ASFormatDescriptor {
public:
TypeIIFormatDescriptor(/*Device* device, size_t interface,*/
usb_type_II_format_descriptor* Descriptor);
virtual ~TypeIIFormatDescriptor();
// protected:
uint16 fMaxBitRate;
uint16 fSamplesPerFrame;
uint8 fSampleFrequencyType;
Vector<uint32> fSampleFrequencies;
};
class TypeIIIFormatDescriptor : public TypeIFormatDescriptor {
public:
TypeIIIFormatDescriptor(/*Device* device, size_t interface, */
usb_type_III_format_descriptor* Descriptor);
virtual ~TypeIIIFormatDescriptor();
// protected:
};
class AudioStreamAlternate {
public:
AudioStreamAlternate(size_t alternate,
ASInterfaceDescriptor* interface,
ASEndpointDescriptor* endpoint,
_ASFormatDescriptor* format);
~AudioStreamAlternate();
ASInterfaceDescriptor* Interface() { return fInterface; }
ASEndpointDescriptor* Endpoint() { return fEndpoint; }
_ASFormatDescriptor* Format() { return fFormat; }
protected:
size_t fAlternate;
ASInterfaceDescriptor* fInterface;
ASEndpointDescriptor* fEndpoint;
_ASFormatDescriptor* fFormat;
};
typedef Vector<AudioStreamAlternate*> StreamAlternatesVector;
typedef Vector<AudioStreamAlternate*>::Iterator StreamAlternatesIterator;
class AudioStreamingInterface {
public:
AudioStreamingInterface(
AudioControlInterface* controlInterface,
size_t interface, usb_interface_list *List);
~AudioStreamingInterface();
// status_t InitCheck() { return fStatus; }
uint8 TerminalLink();
bool IsInput() { return fIsInput; }
AudioChannelCluster* ChannelCluster();
void GetFormatsAndRates(multi_description *Description);
protected:
size_t fInterface;
AudioControlInterface* fControlInterface;
// status_t fStatus;
bool fIsInput;
// alternates of the streams
StreamAlternatesVector fAlternates;
size_t fActiveAlternate;
};
#endif // _AUDIO_STREAMING_INTERFACE_H_
@@ -0,0 +1,778 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include "Driver.h"
#include "Device.h"
#include "Settings.h"
//#include "audio.h"
#include "AudioStreamingInterface.h"
// #include "StreamFormats.h"
#include <malloc.h>
Device::Device(usb_device device)
:
fStatus(B_ERROR),
fOpen(false),
fRemoved(false),
fInsideNotify(0),
fDevice(device),
fNonBlocking(false),
fAudioControl(this),
fControlEndpoint(0),
fInStreamEndpoint(0),
fOutStreamEndpoint(0),
fNotifyReadSem(-1),
fNotifyWriteSem(-1),
fNotifyBuffer(NULL),
fNotifyBufferLength(0),
fBuffersReadySem(-1)
{
const usb_device_descriptor* deviceDescriptor
= gUSBModule->get_device_descriptor(device);
if (deviceDescriptor == NULL) {
TRACE_ALWAYS("Error of getting USB device descriptor.\n");
return;
}
fVendorID = deviceDescriptor->vendor_id;
fProductID = deviceDescriptor->product_id;
fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
if (fNotifyReadSem < B_OK) {
TRACE_ALWAYS("Error of creating read notify semaphore:%#010x\n",
fNotifyReadSem);
return;
}
fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
if (fNotifyWriteSem < B_OK) {
TRACE_ALWAYS("Error of creating write notify semaphore:%#010x\n",
fNotifyWriteSem);
return;
}
fBuffersReadySem = create_sem(0, DRIVER_NAME "_buffers_ready");
if (fBuffersReadySem < B_OK) {
TRACE_ALWAYS("Error of creating ready buffers semaphore:%#010x\n",
fBuffersReadySem);
return;
}
if (_SetupEndpoints() != B_OK) {
return;
}
// must be set in derived class constructor
fStatus = B_OK;
}
Device::~Device()
{
// we have to clear because we own those objects here
/*
for (AudioControlsIterator I = fAudioControls.Begin();
I != fAudioControls.End(); I++) {
delete I->Value();
}
fAudioControls.MakeEmpty();
// object already freed. just purge the map
fOutputTerminals.MakeEmpty();
// object already freed. just purge the map
fInputTerminals.MakeEmpty();
*/
// free stream objects too.
for (AudioStreamsIterator I = fStreams.Begin();
I != fStreams.End(); I++) {
delete *I;
}
fStreams.MakeEmpty();
if (fNotifyReadSem >= B_OK)
delete_sem(fNotifyReadSem);
if (fNotifyWriteSem >= B_OK)
delete_sem(fNotifyWriteSem);
if (fBuffersReadySem > B_OK)
delete_sem(fBuffersReadySem);
// if (!fRemoved) // ???
// gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
if (fNotifyBuffer)
free(fNotifyBuffer);
}
status_t
Device::Open(uint32 flags)
{
if (fOpen)
return B_BUSY;
if (fRemoved)
return B_ERROR;
status_t result = StartDevice();
if (result != B_OK) {
return result;
}
// setup state notifications
/* result = gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer,
fNotifyBufferLength, _NotifyCallback, this);
if (result != B_OK) {
TRACE_ALWAYS("Error of requesting notify interrupt:%#010x\n", result);
return result;
}
*/
fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK;
fOpen = true;
return result;
}
status_t
Device::Close()
{
if (fRemoved) {
fOpen = false;
return B_OK;
}
for (int i = 0; i < fStreams.Count(); i++) {
fStreams[i]->Stop();
}
// wait until possible notification handling finished...
while (atomic_add(&fInsideNotify, 0) != 0)
snooze(100);
gUSBModule->cancel_queued_transfers(fControlEndpoint);
gUSBModule->cancel_queued_transfers(fInStreamEndpoint);
gUSBModule->cancel_queued_transfers(fOutStreamEndpoint);
fOpen = false;
return StopDevice();
}
status_t
Device::Free()
{
return B_OK;
}
status_t
Device::Read(uint8 *buffer, size_t *numBytes)
{
*numBytes = 0;
return B_IO_ERROR;
}
status_t
Device::Write(const uint8 *buffer, size_t *numBytes)
{
*numBytes = 0;
return B_IO_ERROR;
}
status_t
Device::Control(uint32 op, void *buffer, size_t length)
{
switch (op) {
case B_MULTI_GET_DESCRIPTION:
return _MultiGetDescription((multi_description*)buffer);
case B_MULTI_GET_EVENT_INFO:
TRACE(("B_MULTI_GET_EVENT_INFO\n"));
return B_ERROR;
case B_MULTI_SET_EVENT_INFO:
TRACE(("B_MULTI_SET_EVENT_INFO\n"));
return B_ERROR;
case B_MULTI_GET_EVENT:
TRACE(("B_MULTI_GET_EVENT\n"));
return B_ERROR;
case B_MULTI_GET_ENABLED_CHANNELS:
return _MultiGetEnabledChannels((multi_channel_enable*)buffer);
case B_MULTI_SET_ENABLED_CHANNELS:
return _MultiSetEnabledChannels((multi_channel_enable*)buffer);
case B_MULTI_GET_GLOBAL_FORMAT:
return _MultiGetGlobalFormat((multi_format_info*)buffer);
case B_MULTI_SET_GLOBAL_FORMAT:
return _MultiSetGlobalFormat((multi_format_info*)buffer);
case B_MULTI_GET_CHANNEL_FORMATS:
TRACE(("B_MULTI_GET_CHANNEL_FORMATS\n"));
return B_ERROR;
case B_MULTI_SET_CHANNEL_FORMATS: /* only implemented if possible */
TRACE(("B_MULTI_SET_CHANNEL_FORMATS\n"));
return B_ERROR;
case B_MULTI_GET_MIX:
return _MultiGetMix((multi_mix_value_info *)buffer);
case B_MULTI_SET_MIX:
return _MultiSetMix((multi_mix_value_info *)buffer);
case B_MULTI_LIST_MIX_CHANNELS:
TRACE(("B_MULTI_LIST_MIX_CHANNELS\n"));
return B_ERROR;
case B_MULTI_LIST_MIX_CONTROLS:
return _MultiListMixControls((multi_mix_control_info*)buffer);
case B_MULTI_LIST_MIX_CONNECTIONS:
TRACE(("B_MULTI_LIST_MIX_CONNECTIONS\n"));
return B_ERROR;
case B_MULTI_GET_BUFFERS:
// Fill out the struct for the first time; doesn't start anything.
return _MultiGetBuffers((multi_buffer_list*)buffer);
case B_MULTI_SET_BUFFERS:
// Set what buffers to use, if the driver supports soft buffers.
TRACE(("B_MULTI_SET_BUFFERS\n"));
return B_ERROR; /* we do not support soft buffers */
case B_MULTI_SET_START_TIME:
// When to actually start
TRACE(("B_MULTI_SET_START_TIME\n"));
return B_ERROR;
case B_MULTI_BUFFER_EXCHANGE:
// stop and go are derived from this being called
return _MultiBufferExchange((multi_buffer_info*)buffer);
case B_MULTI_BUFFER_FORCE_STOP:
// force stop of playback, nothing in data
return _MultiBufferForceStop();
default:
TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op);
}
return B_DEV_INVALID_IOCTL;
}
void
Device::Removed()
{
fRemoved = true;
// fHasConnection = false;
// the notify hook is different from the read and write hooks as it does
// itself schedule traffic (while the other hooks only release a semaphore
// to notify another thread which in turn safly checks for the removed
// case) - so we must ensure that we are not inside the notify hook anymore
// before returning, as we would otherwise violate the promise not to use
// any of the pipes after returning from the removed hook
while (atomic_add(&fInsideNotify, 0) != 0)
snooze(100);
gUSBModule->cancel_queued_transfers(fControlEndpoint);
gUSBModule->cancel_queued_transfers(fInStreamEndpoint);
gUSBModule->cancel_queued_transfers(fOutStreamEndpoint);
/*
if (fLinkStateChangeSem >= B_OK)
release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
*/
}
status_t
Device::SetupDevice(bool deviceReplugged)
{
return B_OK;
}
status_t
Device::CompareAndReattach(usb_device device)
{
const usb_device_descriptor *deviceDescriptor
= gUSBModule->get_device_descriptor(device);
if (deviceDescriptor == NULL) {
TRACE_ALWAYS("Error of getting USB device descriptor.\n");
return B_ERROR;
}
if (deviceDescriptor->vendor_id != fVendorID
&& deviceDescriptor->product_id != fProductID) {
// this certainly isn't the same device
return B_BAD_VALUE;
}
// this is the same device that was replugged - clear the removed state,
// re- setup the endpoints and transfers and open the device if it was
// previously opened
fDevice = device;
fRemoved = false;
status_t result = _SetupEndpoints();
if (result != B_OK) {
fRemoved = true;
return result;
}
// we need to setup hardware on device replug
result = SetupDevice(true);
if (result != B_OK) {
return result;
}
if (fOpen) {
fOpen = false;
result = Open(fNonBlocking ? O_NONBLOCK : 0);
}
return result;
}
status_t
Device::_MultiGetDescription(multi_description *multiDescription)
{
multi_description Description;
if (user_memcpy(&Description, multiDescription,
sizeof(multi_description)) != B_OK) {
return B_BAD_ADDRESS;
}
Description.interface_version = B_CURRENT_INTERFACE_VERSION;
Description.interface_minimum = B_CURRENT_INTERFACE_VERSION;
strncpy(Description.friendly_name, "USB Audio", // TODO: ????
sizeof(Description.friendly_name));
strncpy(Description.vendor_info, "S.Zharski",
sizeof(Description.vendor_info));
Description.output_channel_count = 0;
Description.input_channel_count = 0;
Description.output_bus_channel_count = 0;
Description.input_bus_channel_count = 0;
Description.aux_bus_channel_count = 0;
Description.output_rates = 0;
Description.input_rates = 0;
Description.min_cvsr_rate = 0;
Description.max_cvsr_rate = 0;
Description.output_formats = 0;
Description.input_formats = 0;
Description.lock_sources = B_MULTI_LOCK_INTERNAL;
Description.timecode_sources = 0;
Description.interface_flags = 0;
Description.start_latency = 3000;
Description.control_panel[0] = '\0';
AudioControlsVector USBTerminals;
// channels (USB I/O terminals) are already in fStreams
// in outputs->inputs order, use them.
for (int i = 0; i < fStreams.Count(); i++) {
uint8 id = fStreams[i]->TerminalLink();
_AudioControl *control = fAudioControl.Find(id);
// if (control->SubType() == IDSOutputTerminal) {
// USBTerminals.PushFront(control);
// fStreams[i]->GetFormatsAndRates(Description);
// } else
// if (control->SubType() == IDSInputTerminal) {
USBTerminals.PushBack(control);
fStreams[i]->GetFormatsAndRates(&Description);
// }
}
// int32 index = 0;
Vector<multi_channel_info> Channels;
/*uint32 channels =*/ fAudioControl.GetChannelsDescription(Channels, &Description, USBTerminals);
/*uint32 bus_channels =*/ fAudioControl.GetBusChannelsDescription(Channels, &Description );
// Description.request_channel_count = channels + bus_channels;
TraceMultiDescription(&Description, Channels);
if (user_memcpy(multiDescription, &Description,
sizeof(multi_description)) != B_OK) {
return B_BAD_ADDRESS;
}
// if (Description.request_channel_count >=
// (int)(sizeof(channel_descriptions) / sizeof(channel_descriptions[0])))
// {
if (user_memcpy(multiDescription->channels,
&Channels[0], min_c(Channels.Count(),
Description.request_channel_count)) != B_OK)
return B_BAD_ADDRESS;
// }
return B_OK;
}
void
Device::TraceMultiDescription(multi_description *Description,
Vector<multi_channel_info>& Channels)
{
TRACE("interface_version:%d\n", Description->interface_version);
TRACE("interface_minimum:%d\n", Description->interface_minimum);
TRACE("friendly_name:%s\n", Description->friendly_name);
TRACE("vendor_info:%s\n", Description->vendor_info);
TRACE("output_channel_count:%d\n", Description->output_channel_count);
TRACE("input_channel_count:%d\n", Description->input_channel_count);
TRACE("output_bus_channel_count:%d\n", Description->output_bus_channel_count);
TRACE("input_bus_channel_count:%d\n", Description->input_bus_channel_count);
TRACE("aux_bus_channel_count:%d\n", Description->aux_bus_channel_count);
TRACE("output_rates:%#08x\n", Description->output_rates);
TRACE("input_rates:%#08x\n", Description->input_rates);
TRACE("min_cvsr_rate:%f\n", Description->min_cvsr_rate);
TRACE("max_cvsr_rate:%f\n", Description->max_cvsr_rate);
TRACE("output_formats:%#08x\n", Description->output_formats);
TRACE("input_formats:%#08x\n", Description->input_formats);
TRACE("lock_sources:%d\n", Description->lock_sources);
TRACE("timecode_sources:%d\n", Description->timecode_sources);
TRACE("interface_flags:%#08x\n", Description->interface_flags);
TRACE("start_latency:%d\n", Description->start_latency);
TRACE("control_panel:%s\n", Description->control_panel);
// multi_channel_info* Channels = Description->channels;
// for (int i = 0; i < Description->request_channel_count; i++) {
for (int i = 0; i < Channels.Count(); i++) {
TRACE(" channel_id:%d\n", Channels[i].channel_id);
TRACE(" kind:%#02x\n", Channels[i].kind);
TRACE(" designations:%#08x\n", Channels[i].designations);
TRACE(" connectors:%#08x\n", Channels[i].connectors);
}
TRACE("request_channel_count:%d\n\n", Description->request_channel_count);
}
status_t
Device::_MultiGetEnabledChannels(multi_channel_enable *Enable)
{
status_t status = B_OK;
Enable->lock_source = B_MULTI_LOCK_INTERNAL;
uint32 offset = 0;
for (int i = 0; i < fStreams.Count() && status == B_OK; i++) {
status = fStreams[i]->GetEnabledChannels(offset, Enable);
}
return status;
}
status_t
Device::_MultiSetEnabledChannels(multi_channel_enable *Enable)
{
status_t status = B_OK;
uint32 offset = 0;
for (int i = 0; i < fStreams.Count() && status == B_OK; i++) {
status = fStreams[i]->SetEnabledChannels(offset, Enable);
}
return status;
}
status_t
Device::_MultiGetGlobalFormat(multi_format_info *Format)
{
status_t status = B_OK;
Format->output_latency = 0;
Format->input_latency = 0;
Format->timecode_kind = 0;
// uint32 offset = 0;
for (int i = 0; i < fStreams.Count() && status == B_OK; i++) {
status = fStreams[i]->GetGlobalFormat(Format);
}
return status;
}
status_t
Device::_MultiSetGlobalFormat(multi_format_info *Format)
{
status_t status = B_OK;
TRACE("output_latency:%lld\n", Format->output_latency);
TRACE("input_latency:%lld\n", Format->input_latency);
TRACE("timecode_kind:%#08x\n", Format->timecode_kind);
// uint32 offset = 0;
for (int i = 0; i < fStreams.Count() && status == B_OK; i++) {
status = fStreams[i]->SetGlobalFormat(Format);
}
return status;
}
status_t
Device::_MultiGetBuffers(multi_buffer_list* List)
{
status_t status = B_OK;
TRACE("info_size:%d\n"
"request_playback_buffers:%d\n"
"request_playback_channels:%d\n"
"request_playback_buffer_size:%d\n"
"request_record_buffers:%d\n"
"request_record_channels:%d\n"
"request_record_buffer_size:%d\n",
List->info_size,
List->request_playback_buffers,
List->request_playback_channels,
List->request_playback_buffer_size,
List->request_record_buffers,
List->request_record_channels,
List->request_record_buffer_size);
for (int i = 0; i < fStreams.Count() && status == B_OK; i++) {
status = fStreams[i]->GetBuffers(List);
}
return B_OK;
}
status_t
Device::_MultiBufferExchange(multi_buffer_info* Info)
{
for (int i = 0; i < fStreams.Count(); i++) {
if (!fStreams[i]->IsRunning()) {
fStreams[i]->Start();
}
}
TRACE_ALWAYS("Exchange!\n");
snooze(1000000);
return B_OK;
status_t status = B_ERROR;
bool anyBufferProcessed = false;
for (int i = 0; i < fStreams.Count() && !anyBufferProcessed; i++) {
status = acquire_sem_etc(fBuffersReadySem, 1,
B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 50000);
if (status == B_TIMED_OUT) {
TRACE_ALWAYS("Timeout during buffers exchange.\n");
break;
}
anyBufferProcessed = fStreams[i]->ExchangeBuffer(Info);
status = anyBufferProcessed ? B_OK : B_ERROR;
}
return status;
}
status_t
Device::_MultiBufferForceStop()
{
for (int i = 0; i < fStreams.Count(); i++) {
fStreams[i]->Stop();
}
return B_OK;
}
status_t
Device::_MultiGetMix(multi_mix_value_info *Info)
{
return fAudioControl.GetMix(Info);
}
status_t
Device::_MultiSetMix(multi_mix_value_info *Info)
{
return fAudioControl.SetMix(Info);
}
status_t
Device::_MultiListMixControls(multi_mix_control_info* Info)
{
status_t status = fAudioControl.ListMixControls(Info);
TraceListMixControls(Info);
return status;
}
void
Device::TraceListMixControls(multi_mix_control_info *Info)
{
TRACE("control_count:%d\n.", Info->control_count);
int32 i = 0;
while (Info->controls[i].id > 0) {
multi_mix_control &c = Info->controls[i];
TRACE("id:%#08x\n", c.id);
TRACE("flags:%#08x\n", c.flags);
TRACE("master:%#08x\n", c.master);
TRACE("parent:%#08x\n", c.parent);
TRACE("string:%d\n", c.string);
TRACE("name:%s\n", c.name);
i++;
}
}
status_t
Device::_SetupEndpoints()
{
const usb_configuration_info *config
= gUSBModule->get_nth_configuration(fDevice, 0);
if (config == NULL) {
TRACE_ALWAYS("Error of getting USB device configuration.\n");
return B_ERROR;
}
if (config->interface_count <= 0) {
TRACE_ALWAYS("Error:no interfaces found in USB device configuration\n");
return B_ERROR;
}
for (size_t i = 0; i < config->interface_count; i++) {
usb_interface_info *Interface = config->interface[i].active;
if (Interface->descr->interface_class != UAS_AUDIO) {
continue;
}
switch (Interface->descr->interface_subclass) {
case UAS_AUDIOCONTROL:
fAudioControl.Init(i, Interface);
break;
case UAS_AUDIOSTREAMING:
{
Stream *stream = new Stream(this, i, &config->interface[i]);
if (B_OK == stream->Init()) {
// put the stream in the correct order:
// first output that input ones.
if (stream->IsInput()) {
fStreams.PushBack(stream);
} else {
fStreams.PushFront(stream);
}
} else {
delete stream;
}
}
break;
default:
TRACE_ALWAYS("Ignore interface of unsupported subclass %#x.\n",
Interface->descr->interface_subclass);
break;
}
}
if (fAudioControl.InitCheck() == B_OK && fStreams.Count() > 0) {
TRACE("Found device %#06x:%#06x\n", fVendorID, fProductID);
gUSBModule->set_configuration(fDevice, config);
for (int i = 0; i < fStreams.Count(); i++) {
fStreams[i]->OnSetConfiguration(fDevice, config);
}
return B_OK;
}
return B_NO_INIT;
}
status_t
Device::StopDevice()
{
status_t result = B_OK; // WriteRXControlRegister(0);
if (result != B_OK) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result);
}
TRACE_RET(result);
return result;
}
void
Device::_ReadCallback(void *cookie, int32 status, void *data,
uint32 actualLength)
{
TRACE_FLOW("ReadCB: %d bytes; status:%#010x\n", actualLength, status);
Device *device = (Device *)cookie;
device->fActualLengthRead = actualLength;
device->fStatusRead = status;
release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
}
void
Device::_WriteCallback(void *cookie, int32 status, void *data,
uint32 actualLength)
{
TRACE_FLOW("WriteCB: %d bytes; status:%#010x\n", actualLength, status);
Device *device = (Device *)cookie;
device->fActualLengthWrite = actualLength;
device->fStatusWrite = status;
release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
}
void
Device::_NotifyCallback(void *cookie, int32 status, void *data,
uint32 actualLength)
{
Device *device = (Device *)cookie;
atomic_add(&device->fInsideNotify, 1);
if (status == B_CANCELED || device->fRemoved) {
atomic_add(&device->fInsideNotify, -1);
return;
}
/* if (status != B_OK) {
TRACE_ALWAYS("Device status error:%#010x\n", status);
status_t result = gUSBModule->clear_feature(device->fControLeNDPOint,
USB_FEATURE_ENDPOINT_HALT);
if (result != B_OK)
TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", result);
}
*/
// parse data in overriden class
// device->OnNotify(actualLength);
// schedule next notification buffer
// gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer,
// device->fNotifyBufferLength, _NotifyCallback, device);
atomic_add(&device->fInsideNotify, -1);
}
@@ -0,0 +1,139 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_AUDIO_DEVICE_H_
#define _USB_AUDIO_DEVICE_H_
#include "Driver.h"
#include "USB_audio_spec.h"
#include "AudioControlInterface.h"
#include "AudioStreamingInterface.h"
#include "Stream.h"
// typedef VectorMap<uint32, _AudioControl*> AudioControlsMap;
// typedef VectorMap<uint32, _AudioControl*>::Iterator AudioControlsIterator;
typedef Vector<Stream*> AudioStreamsVector;
typedef Vector<Stream*>::Iterator AudioStreamsIterator;
class FeatureUnit;
class Device {
friend class FeatureUnit;
friend class Stream;
public:
Device(usb_device device);
virtual ~Device();
status_t InitCheck() { return fStatus; };
status_t Open(uint32 flags);
bool IsOpen() { return fOpen; };
status_t Close();
status_t Free();
status_t Read(uint8 *buffer, size_t *numBytes);
status_t Write(const uint8 *buffer, size_t *numBytes);
status_t Control(uint32 op, void *buffer, size_t length);
void Removed();
bool IsRemoved() { return fRemoved; };
status_t CompareAndReattach(usb_device device);
virtual status_t SetupDevice(bool deviceReplugged);
// uint16 SpecReleaseNumber();
// _AudioControl* FindAudioControl(uint8 ID);
usb_device USBDevice() { return fDevice; }
AudioControlInterface& AudioControl() { return fAudioControl; }
private:
static void _ReadCallback(void *cookie, int32 status,
void *data, uint32 actualLength);
static void _WriteCallback(void *cookie, int32 status,
void *data, uint32 actualLength);
static void _NotifyCallback(void *cookie, int32 status,
void *data, uint32 actualLength);
status_t _SetupEndpoints();
// void ParseAudioControlInterface(usb_device device,
// size_t interface, usb_interface_info *Interface);
// void ParseAudioStreamingInterface(usb_device device,
// size_t interface, usb_interface_list *List);
protected:
virtual status_t StartDevice() { return B_OK; }
virtual status_t StopDevice();
void TraceMultiDescription(multi_description *Description,
Vector<multi_channel_info>& Channels);
void TraceListMixControls(multi_mix_control_info *Info);
// state tracking
status_t fStatus;
bool fOpen;
bool fRemoved;
vint32 fInsideNotify;
usb_device fDevice;
uint16 fVendorID;
uint16 fProductID;
const char * fDescription;
bool fNonBlocking;
AudioControlInterface fAudioControl;
/*
AudioControlHeader* fAudioControlHeader;
// map to store all controls and lookup by control ID
AudioControlsMap fAudioControls;
// map to store output terminal and lookup them by source ID
AudioControlsMap fOutputTerminals;
// map to store output terminal and lookup them by control ID
AudioControlsMap fInputTerminals;
*/
// vector of audio streams
AudioStreamsVector fStreams;
protected:
status_t _MultiGetDescription(multi_description *Description);
status_t _MultiGetEnabledChannels(multi_channel_enable *Enable);
status_t _MultiSetEnabledChannels(multi_channel_enable *Enable);
status_t _MultiGetBuffers(multi_buffer_list* List);
status_t _MultiGetGlobalFormat(multi_format_info *Format);
status_t _MultiSetGlobalFormat(multi_format_info *Format);
status_t _MultiGetMix(multi_mix_value_info *Info);
status_t _MultiSetMix(multi_mix_value_info *Info);
status_t _MultiListMixControls(multi_mix_control_info* Info);
status_t _MultiBufferExchange(multi_buffer_info* Info);
status_t _MultiBufferForceStop();
// interface and device infos
// uint16 fFrameSize;
// pipes for notifications and data io
usb_pipe fControlEndpoint;
usb_pipe fInStreamEndpoint;
usb_pipe fOutStreamEndpoint;
// data stores for async usb transfers
uint32 fActualLengthRead;
uint32 fActualLengthWrite;
int32 fStatusRead;
int32 fStatusWrite;
sem_id fNotifyReadSem;
sem_id fNotifyWriteSem;
uint8 * fNotifyBuffer;
uint32 fNotifyBufferLength;
sem_id fBuffersReadySem;
};
#endif // _USB_AUDIO_DEVICE_H_
@@ -0,0 +1,303 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lock.h> // for mutex
#include "Driver.h"
#include "Settings.h"
#include "Device.h"
#include "USB_audio_spec.h"
int32 api_version = B_CUR_DRIVER_API_VERSION;
static const char *sDeviceBaseName = "audio/hmulti/USB Audio/";
Device *gDevices[MAX_DEVICES];
char *gDeviceNames[MAX_DEVICES + 1];
usb_module_info *gUSBModule = NULL;
mutex gDriverLock;
// auto - release helper class
class DriverSmartLock {
public:
DriverSmartLock() { mutex_lock(&gDriverLock); }
~DriverSmartLock() { mutex_unlock(&gDriverLock); }
};
status_t
usb_audio_device_added(usb_device device, void **cookie)
{
*cookie = NULL;
DriverSmartLock driverLock; // released on exit
// check if this is a replug of an existing device first
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] == NULL)
continue;
if (gDevices[i]->CompareAndReattach(device) != B_OK)
continue;
TRACE("The device is plugged back. Use entry at %ld.\n", i);
*cookie = gDevices[i];
return B_OK;
}
// no such device yet, create a new one
Device *audioDevice = new Device(device);
if (audioDevice == 0) {
return ENODEV;
}
status_t status = audioDevice->InitCheck();
if (status < B_OK) {
delete audioDevice;
return status;
}
status = audioDevice->SetupDevice(false);
if (status < B_OK) {
delete audioDevice;
return status;
}
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] != NULL)
continue;
gDevices[i] = audioDevice;
*cookie = audioDevice;
TRACE("New device is added at %ld.\n", i);
return B_OK;
}
// no space for the device
TRACE_ALWAYS("Error: no more device entries availble.\n");
delete audioDevice;
return B_ERROR;
}
status_t
usb_audio_device_removed(void *cookie)
{
DriverSmartLock driverLock; // released on exit
Device *device = (Device *)cookie;
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] == device) {
if (device->IsOpen()) {
// the device will be deleted upon being freed
device->Removed();
} else {
gDevices[i] = NULL;
delete device;
TRACE("Device at %ld deleted.\n", i);
}
break;
}
}
return B_OK;
}
status_t
init_hardware()
{
return B_OK;
}
status_t
init_driver()
{
status_t status = get_module(B_USB_MODULE_NAME,
(module_info **)&gUSBModule);
if (status < B_OK)
return status;
load_settings();
TRACE_ALWAYS("%s\n", kVersion);
for (int32 i = 0; i < MAX_DEVICES; i++)
gDevices[i] = NULL;
gDeviceNames[0] = NULL;
mutex_init(&gDriverLock, DRIVER_NAME"_devices");
static usb_notify_hooks notifyHooks = {
&usb_audio_device_added,
&usb_audio_device_removed
};
static usb_support_descriptor supportedDevices[] = {
{UAS_AUDIO, 0, 0, 0, 0 }
};
gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 0, NULL);
gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
return B_OK;
}
void
uninit_driver()
{
gUSBModule->uninstall_notify(DRIVER_NAME);
mutex_lock(&gDriverLock);
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i]) {
delete gDevices[i];
gDevices[i] = NULL;
}
}
for (int32 i = 0; gDeviceNames[i]; i++) {
free(gDeviceNames[i]);
gDeviceNames[i] = NULL;
}
mutex_destroy(&gDriverLock);
put_module(B_USB_MODULE_NAME);
release_settings();
}
static status_t
usb_audio_open(const char *name, uint32 flags, void **cookie)
{
DriverSmartLock driverLock; // released on exit
*cookie = NULL;
status_t status = ENODEV;
int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10);
if (index >= 0 && index < MAX_DEVICES && gDevices[index]) {
status = gDevices[index]->Open(flags);
*cookie = gDevices[index];
}
return status;
}
static status_t
usb_audio_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
Device *device = (Device *)cookie;
return device->Read((uint8 *)buffer, numBytes);
}
static status_t
usb_audio_write(void *cookie, off_t position, const void *buffer,
size_t *numBytes)
{
Device *device = (Device *)cookie;
return device->Write((const uint8 *)buffer, numBytes);
}
static status_t
usb_audio_control(void *cookie, uint32 op, void *buffer, size_t length)
{
Device *device = (Device *)cookie;
return device->Control(op, buffer, length);
}
static status_t
usb_audio_close(void *cookie)
{
Device *device = (Device *)cookie;
return device->Close();
}
static status_t
usb_audio_free(void *cookie)
{
Device *device = (Device *)cookie;
DriverSmartLock driverLock; // released on exit
status_t status = device->Free();
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] == device) {
// the device is removed already but as it was open the
// removed hook has not deleted the object
gDevices[i] = NULL;
delete device;
TRACE("Device at %ld deleted.\n", i);
break;
}
}
return status;
}
const char **
publish_devices()
{
for (int32 i = 0; gDeviceNames[i]; i++) {
free(gDeviceNames[i]);
gDeviceNames[i] = NULL;
}
DriverSmartLock driverLock; // released on exit
int32 deviceCount = 0;
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] == NULL)
continue;
gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4);
if (gDeviceNames[deviceCount]) {
sprintf(gDeviceNames[deviceCount], "%s%ld", sDeviceBaseName, i);
TRACE("publishing %s\n", gDeviceNames[deviceCount]);
deviceCount++;
} else
TRACE_ALWAYS("Error: out of memory during allocating device name.\n");
}
gDeviceNames[deviceCount] = NULL;
return (const char **)&gDeviceNames[0];
}
device_hooks *
find_device(const char *name)
{
static device_hooks deviceHooks = {
usb_audio_open,
usb_audio_close,
usb_audio_free,
usb_audio_control,
usb_audio_read,
usb_audio_write,
NULL, /* select */
NULL /* deselect */
};
return &deviceHooks;
}
@@ -0,0 +1,53 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_AUDIO_DRIVER_H_
#define _USB_AUDIO_DRIVER_H_
#include <OS.h>
#include <KernelExport.h>
#include <Drivers.h>
#include <USB3.h>
#include <hmulti_audio.h>
#define DRIVER_NAME "usb_audio"
#define MAX_DEVICES 8
const char* const kVersion = "ver.0.0.4";
const uint32 kSamplesBufferSize = 1024;
const uint32 kSamplesBufferCount = 2;
// calculate count of array members
#ifdef _countof
#warning "countof(...) WAS ALREADY DEFINED!!! Remove local definition!"
#undef countof
#endif
#define _countof(array)(sizeof(array) / sizeof(array[0]))
extern usb_module_info *gUSBModule;
extern "C" {
status_t usb_audio_device_added(usb_device device, void **cookie);
status_t usb_audio_device_removed(void *cookie);
status_t init_hardware();
void uninit_driver();
const char **publish_devices();
device_hooks *find_device(const char *name);
}
#endif // _USB_AUDIO_DRIVER_H_
@@ -2,10 +2,17 @@ SubDir HAIKU_TOP src add-ons kernel drivers audio usb_audio ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders audio ;
UsePrivateHeaders kernel media ;
UsePrivateHeaders kernel net ;
UsePrivateHeaders [ FDirName kernel util ] ;
KernelAddon usb_audio :
usb_audio.c
USB_audio_utils.c
;
Driver.cpp
Device.cpp
AudioControlInterface.cpp
AudioStreamingInterface.cpp
Stream.cpp
Settings.cpp
;
@@ -0,0 +1,113 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include <lock.h> // for mutex
#include <stdlib.h> // for file operation
#include <string.h> // for file operation
#include <stdio.h> // for file operation
#include "Settings.h"
bool gTraceOn = false;
bool gTruncateLogFile = false;
bool gAddTimeStamp = true;
bool gTraceFlow = false;
static char *gLogFilePath = NULL;
mutex gLogLock;
static
void create_log()
{
if (gLogFilePath == NULL)
return;
int flags = O_WRONLY | O_CREAT | ((gTruncateLogFile) ? O_TRUNC : 0);
close(open(gLogFilePath, flags, 0666));
mutex_init(&gLogLock, DRIVER_NAME"-logging");
}
void load_settings()
{
void *handle = load_driver_settings(DRIVER_NAME);
if (handle == 0)
return;
gTraceOn = get_driver_boolean_parameter(handle, "trace", gTraceOn, true);
gTraceFlow = get_driver_boolean_parameter(handle, "trace_flow",
gTraceFlow, true);
gTruncateLogFile = get_driver_boolean_parameter(handle, "truncate_logfile",
gTruncateLogFile, true);
gAddTimeStamp = get_driver_boolean_parameter(handle, "add_timestamp",
gAddTimeStamp, true);
const char * logFilePath = get_driver_parameter(handle, "logfile",
NULL, "/var/log/"DRIVER_NAME".log");
if (logFilePath != NULL) {
gLogFilePath = strdup(logFilePath);
}
unload_driver_settings(handle);
create_log();
}
void release_settings()
{
if (gLogFilePath != NULL) {
mutex_destroy(&gLogLock);
free(gLogFilePath);
}
}
void usb_audio_trace(bool force, const char* func, const char *fmt, ...)
{
if (!(force || gTraceOn)) {
return;
}
va_list arg_list;
static const char *prefix = DRIVER_NAME":";
static char buffer[1024];
char *buf_ptr = buffer;
if (gLogFilePath == NULL) {
strcpy(buffer, prefix);
buf_ptr += strlen(prefix);
}
if (gAddTimeStamp) {
bigtime_t time = system_time();
uint32 msec = time / 1000;
uint32 sec = msec / 1000;
sprintf(buf_ptr, "%02ld.%02ld.%03ld:",
sec / 60, sec % 60, msec % 1000);
buf_ptr += strlen(buf_ptr);
}
if (func != NULL) {
sprintf(buf_ptr, "%s::", func);
buf_ptr += strlen(buf_ptr);
}
va_start(arg_list, fmt);
vsprintf(buf_ptr, fmt, arg_list);
va_end(arg_list);
if (gLogFilePath == NULL) {
dprintf(buffer);
return;
}
mutex_lock(&gLogLock);
int fd = open(gLogFilePath, O_WRONLY | O_APPEND);
write(fd, buffer, strlen(buffer));
close(fd);
mutex_unlock(&gLogLock);
}
@@ -0,0 +1,34 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_AUDIO_SETTINGS_H_
#define _USB_AUDIO_SETTINGS_H_
#include <driver_settings.h>
#include "Driver.h"
void load_settings();
void release_settings();
void usb_audio_trace(bool force, const char *func, const char *fmt, ...);
#ifdef TRACE
#undef TRACE
#endif
#define TRACE(x...) usb_audio_trace(false, __func__, x)
#define TRACE_ALWAYS(x...) usb_audio_trace(true, __func__, x)
extern bool gTraceFlow;
#define TRACE_FLOW(x...) usb_audio_trace(gTraceFlow, NULL, x)
#define TRACE_RET(result) usb_audio_trace(false, __func__, \
"Returns:%#010x\n", result);
#endif /*_USB_AUDIO_SETTINGS_H_*/
@@ -0,0 +1,528 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include "Stream.h"
#include "Device.h"
#include "Driver.h"
#include "Settings.h"
Stream::Stream(Device *device, size_t interface, usb_interface_list *List
/*, bool isInput, uint32 HWChannel*/)
:
AudioStreamingInterface(&device->AudioControl(), interface, List),
fDevice(device),
fStatus(B_NO_INIT),
fStreamEndpoint(0),
fIsRunning(false),
fArea(-1),
fDescriptors(0),
fDescriptorsCount(0),
fCurrentBuffer(0),
fStartingFrame(0),
fSamplesCount(0),
fProcessedBuffers(0)/*,
fBuffersPhysAddress(0)/ *,
fRealTime(0),
fFramesCount(0),
fBufferCycle(0)*/
{
}
Stream::~Stream()
{
delete_area(fArea);
}
status_t
Stream::Init()
{
// lookup alternate with maximal (ch * 100 + resolution)
uint16 maxChxRes = 0;
for (int i = 0; i < fAlternates.Count(); i++) {
if (fAlternates[i]->Interface() == 0) {
TRACE("Ignore alternate %d - zero interface description.\n", i);
continue;
}
if (fAlternates[i]->Format() == 0) {
TRACE("Ignore alternate %d - zero format description.\n", i);
continue;
}
if (fAlternates[i]->Format()->fFormatType != UAF_FORMAT_TYPE_I) {
TRACE("Ignore alternate %d - format type %#02x is not supported.\n",
i, fAlternates[i]->Format()->fFormatType);
continue;
}
switch (fAlternates[i]->Interface()->fFormatTag) {
case UAF_PCM:
case UAF_PCM8:
case UAF_IEEE_FLOAT:
// case UAF_ALAW:
// case UAF_MULAW:
break;
default:
TRACE("Ignore alternate %d - format %#04x is not supported.\n",
i, fAlternates[i]->Interface()->fFormatTag);
continue;
}
TypeIFormatDescriptor* format
= static_cast<TypeIFormatDescriptor*>(fAlternates[i]->Format());
if (fAlternates[i]->Interface()->fFormatTag == UAF_PCM) {
switch(format->fBitResolution) {
default:
TRACE("Ignore alternate %d - bit resolution %d "
"is not supported.\n", i, format->fBitResolution);
continue;
case 8: case 16: case 18: case 20: case 24: case 32:
break;
}
}
uint16 chxRes = format->fNumChannels * 100 + format->fBitResolution;
if (chxRes > maxChxRes) {
maxChxRes = chxRes;
fActiveAlternate = i;
}
}
if (maxChxRes <= 0) {
TRACE("No compatible alternate found. Stream initialization failed.\n");
return fStatus;
}
const ASEndpointDescriptor* endpoint = fAlternates[
fActiveAlternate]->Endpoint();
fIsInput = (endpoint->fEndpointAddress & USB_ENDPOINT_ADDR_DIR_IN)
== USB_ENDPOINT_ADDR_DIR_IN;
TRACE("Alternate %d selected!\n", fActiveAlternate);
TypeIFormatDescriptor* format
= static_cast<TypeIFormatDescriptor*>(fAlternates[
fActiveAlternate]->Format());
size_t bufferSize = format->fNumChannels * format->fSubframeSize;
TRACE("bufferSize:%d\n", bufferSize);
bufferSize *= kSamplesBufferSize;
TRACE("bufferSize:%d\n", bufferSize);
bufferSize *= (sizeof(usb_iso_packet_descriptor) + endpoint->fMaxPacketSize);
TRACE("bufferSize:%d\n", bufferSize);
bufferSize /= endpoint->fMaxPacketSize;
TRACE("bufferSize:%d\n", bufferSize);
bufferSize = (bufferSize + (B_PAGE_SIZE - 1)) &~ (B_PAGE_SIZE - 1);
TRACE("bufferSize:%d\n", bufferSize);
fArea = create_area( (fIsInput) ? DRIVER_NAME "_record_area" :
DRIVER_NAME "_playback_area",
(void**)&fDescriptors, B_ANY_KERNEL_ADDRESS,
bufferSize, B_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA);
if (fArea < 0) {
TRACE_ALWAYS("Error of creating %#x - bytes size buffer area:%#010x\n",
bufferSize, fArea);
fStatus = fArea;
return fStatus;
}
physical_entry PhysEntry;
get_memory_map(fDescriptors, bufferSize, &PhysEntry, 1);
TRACE_ALWAYS("Created area id: "
"%d\naddress:%#010x[phys:%#010x]\nsize:%#010x\n",
fArea, fDescriptors, PhysEntry.address, bufferSize);
fDescriptorsCount = bufferSize;
fDescriptorsCount /= (sizeof(usb_iso_packet_descriptor)
+ endpoint->fMaxPacketSize);
fDescriptorsCount /= kSamplesBufferCount;
// we need same size buffers. round it!
fDescriptorsCount *= kSamplesBufferCount;
fSamplesCount = fDescriptorsCount * endpoint->fMaxPacketSize;
TRACE("samplesCount:%d\n", fSamplesCount);
fSamplesCount /= format->fNumChannels * format->fSubframeSize;
TRACE("samplesCount:%d\n", fSamplesCount);
for (size_t i = 0; i < fDescriptorsCount; i++) {
fDescriptors[i].request_length = endpoint->fMaxPacketSize;
fDescriptors[i].actual_length = 0;
fDescriptors[i].status = B_OK;
}
/* uint32* b = (uint32*)(fDescriptors + fDescriptorsCount);
for (size_t i = 0; i < fSamplesCount; i++) {
b[i] = i * 10;
}*/
TRACE_ALWAYS("Descriptors count:%d\nsample size:%d\nchannels:%d:%d\n",
fDescriptorsCount, format->fSubframeSize, format->fNumChannels,
sizeof(usb_iso_packet_descriptor));
return fStatus = B_OK;
}
status_t
Stream::OnSetConfiguration(usb_device device,
const usb_configuration_info *config)
{
if (config == NULL) {
TRACE_ALWAYS("NULL configuration. Not set.\n");
return B_ERROR;
}
usb_interface_info* interface
= &config->interface[fInterface].alt[fActiveAlternate];
if (interface == NULL) {
TRACE_ALWAYS("NULL interface. Not set.\n");
return B_ERROR;
}
/*status_t status =*/ gUSBModule->set_alt_interface(device, interface);
uint8 address = fAlternates[fActiveAlternate]->Endpoint()->fEndpointAddress;
for (size_t i = 0; i < interface->endpoint_count; i++) {
if (address == interface->endpoint[i].descr->endpoint_address) {
fStreamEndpoint = interface->endpoint[i].handle;
TRACE("%s Stream Endpoint [address %#04x] handle is: %#010x.\n",
fIsInput ? "Input" : "Output", address, fStreamEndpoint);
return B_OK;
}
}
TRACE("%s Stream Endpoint [address %#04x] was not found.\n",
fIsInput ? "Input" : "Output", address);
return B_ERROR;
}
status_t
Stream::Start()
{
status_t result = B_BUSY;
if (!fIsRunning) {
if (!fIsInput) {
// for (size_t i = 0; i < kSamplesBufferCount; i++)
// result = _QueueNextTransfer(i);
// TODO
// result = _QueueNextTransfer(0);
result = B_OK;
} else
result = B_OK;
fIsRunning = result == B_OK;
}
return result;
}
status_t
Stream::Stop()
{
if (fIsRunning) {
gUSBModule->cancel_queued_transfers(fStreamEndpoint);
fIsRunning = false;
}
return B_OK;
}
status_t
Stream::_QueueNextTransfer(size_t queuedBuffer)
{
TypeIFormatDescriptor* format
= static_cast<TypeIFormatDescriptor*>(fAlternates[
fActiveAlternate]->Format());
size_t bufferSize = format->fNumChannels * format->fSubframeSize;
bufferSize *= fSamplesCount / kSamplesBufferCount;
uint8* buffers = (uint8*)(fDescriptors + fDescriptorsCount);
size_t packetsCount = fDescriptorsCount / kSamplesBufferCount;
TRACE("buffers:%#010x[%#x]\ndescrs:%#010x[%#x]\n",
buffers + bufferSize * queuedBuffer, bufferSize,
fDescriptors + queuedBuffer * packetsCount, packetsCount);
return gUSBModule->queue_isochronous(fStreamEndpoint,
buffers + bufferSize * queuedBuffer, bufferSize,
fDescriptors + queuedBuffer * packetsCount, packetsCount,
NULL/*&fStartingFrame*/, USB_ISO_ASAP,
Stream::_TransferCallback, this);
return B_OK;
}
void
Stream::_TransferCallback(void *cookie, int32 status, void *data,
uint32 actualLength)
{
Stream *stream = (Stream *)cookie;
stream->fCurrentBuffer++;
if (stream->fCurrentBuffer >= kSamplesBufferCount) {
stream->fCurrentBuffer = 0;
}
stream->_DumpDescriptors();
stream->_DumpDescriptors();
/*
status_t result = stream->_QueueNextTransfer(stream->fCurrentBuffer);
if (atomic_add(&stream->fProcessedBuffers, 1) > (int32)kSamplesBufferCount) {
TRACE_ALWAYS("Processed buffers overflow:%d\n", stream->fProcessedBuffers);
}
*/
release_sem_etc(stream->fDevice->fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE);
// TRACE_ALWAYS("st:%#010x, len:%d -> %#010x\n", status, actualLength, result);
TRACE_ALWAYS("st:%#010x, data:%#010x, len:%d\n", status, data, actualLength);
/* if (status != B_OK) {
TRACE_ALWAYS("Device status error:%#010x\n", status);
status_t result = gUSBModule->clear_feature(device->fControLeNDPOint,
USB_FEATURE_ENDPOINT_HALT);
if (result != B_OK)
TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", result);
}
*/
}
void
Stream::_DumpDescriptors()
{
size_t packetsCount = fDescriptorsCount / kSamplesBufferCount;
size_t from = /*fCurrentBuffer > 0 ? packetsCount :*/ 0 ;
size_t to = /*fCurrentBuffer > 0 ?*/ fDescriptorsCount /*: packetsCount*/ ;
for (size_t i = from; i < to; i++) {
TRACE_ALWAYS("%d:req_len:%d; act_len:%d; stat:%#010x\n", i,
fDescriptors[i].request_length, fDescriptors[i].actual_length,
fDescriptors[i].status);
}
}
status_t
Stream::GetEnabledChannels(uint32& offset, multi_channel_enable *Enable)
{
AudioChannelCluster* cluster = ChannelCluster();
if (cluster == 0) {
return B_ERROR;
}
for (size_t i = 0; i < cluster->ChannelsCount(); i++) {
B_SET_CHANNEL(Enable->enable_bits, offset++, true);
TRACE("Report channel %d as enabled.\n", offset);
}
return B_OK;
}
status_t
Stream::SetEnabledChannels(uint32& offset, multi_channel_enable *Enable)
{
AudioChannelCluster* cluster = ChannelCluster();
if (cluster == 0) {
return B_ERROR;
}
for (size_t i = 0; i < cluster->ChannelsCount(); i++) {
TRACE("%s channel %d.\n", (B_TEST_CHANNEL(Enable->enable_bits, offset++)
? "Enable" : "Disable"), offset + 1);
}
return B_OK;
}
status_t
Stream::GetGlobalFormat(multi_format_info *Format)
{
if (IsInput()) {
// TODO
Format->input.rate = B_SR_48000;
Format->input.cvsr = 48000;
Format->input.format = B_FMT_16BIT;
} else {
// TODO
Format->output.rate = B_SR_48000;
Format->output.cvsr = 48000;
Format->output.format = B_FMT_16BIT;
}
return B_OK;
}
status_t
Stream::SetGlobalFormat(multi_format_info *Format)
{
if (IsInput()) {
// TODO
TRACE("input.rate:%d\n", Format->input.rate);
TRACE("input.cvsr:%f\n", Format->input.cvsr);
TRACE("input.format:%#08x\n", Format->input.format);
} else {
// TODO
TRACE("output.rate:%d\n", Format->output.rate);
TRACE("output.cvsr:%f\n", Format->output.cvsr);
TRACE("output.format:%#08x\n", Format->output.format);
}
return B_OK;
}
status_t
Stream::GetBuffers(multi_buffer_list* List)
{
// TODO: check the available buffers count!
int32 startChannel = List->return_playback_channels;
buffer_desc** Buffers = List->playback_buffers;
if (fIsInput) {
List->flags |= B_MULTI_BUFFER_RECORD;
List->return_record_buffer_size = fSamplesCount / kSamplesBufferCount;
List->return_record_buffers = kSamplesBufferCount;
startChannel = List->return_record_channels;
Buffers = List->record_buffers;
TRACE("flags:%#10x\nreturn_record_buffer_size:%#010x\n"
"return_record_buffers:%#010x\n", List->flags,
List->return_record_buffer_size, List->return_record_buffers);
} else {
List->flags |= B_MULTI_BUFFER_PLAYBACK;
List->return_playback_buffer_size = fSamplesCount / kSamplesBufferCount;
List->return_playback_buffers = kSamplesBufferCount;
TRACE("flags:%#10x\nreturn_playback_buffer_size:%#010x\n"
"return_playback_buffers:%#010x\n", List->flags,
List->return_playback_buffer_size, List->return_playback_buffers);
}
TypeIFormatDescriptor* format
= static_cast<TypeIFormatDescriptor*>(
fAlternates[fActiveAlternate]->Format());
const ASEndpointDescriptor* endpoint
= fAlternates[fActiveAlternate]->Endpoint();
// [buffer][channel] init buffers
for (size_t buffer = 0; buffer < kSamplesBufferCount; buffer++) {
TRACE("%s buffer #%d:\n", fIsInput ? "input" : "output", buffer + 1);
for (size_t channel = startChannel;
channel < format->fNumChannels; channel++)
{
// init stride to the same for all buffers
uint32 stride = format->fSubframeSize * format->fNumChannels;
Buffers[buffer][channel].stride = stride;
// init to buffers area begin
Buffers[buffer][channel].base
= (char*)(fDescriptors + fDescriptorsCount);
// shift for whole buffer if required
size_t bufferSize = endpoint->fMaxPacketSize
* (fDescriptorsCount / kSamplesBufferCount);
Buffers[buffer][channel].base += buffer * bufferSize;
// shift for channel if required
Buffers[buffer][channel].base += channel * format->fSubframeSize;
TRACE("%d:%d: base:%#010x; stride:%#010x\n", buffer, channel,
Buffers[buffer][channel].base, Buffers[buffer][channel].stride);
}
}
if (fIsInput) {
List->return_record_channels += format->fNumChannels;
TRACE("return_record_channels:%#010x\n", List->return_record_channels);
} else {
List->return_playback_channels += format->fNumChannels;
TRACE("return_playback_channels:%#010x\n",
List->return_playback_channels);
}
return B_OK;
}
/*
int32
Stream::InterruptHandler(uint32 SignaledChannelsMask)
{
uint32 ChannelMask = 1 << fHWChannel;
if ((SignaledChannelsMask & ChannelMask) == 0) {
return B_UNHANDLED_INTERRUPT;
}
uint32 CurrentSamplePositionFlag = fDevice->ReadPCI32(TrCurSPFBReg);
fRealTime = system_time();
fFramesCount += fBufferSize;
fBufferCycle = ((CurrentSamplePositionFlag & ChannelMask) == ChannelMask) ? 1 : 0;
fCSP = CurrentSamplePositionFlag;
release_sem_etc(fDevice->fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE);
return B_HANDLED_INTERRUPT;
}*/
bool
Stream::ExchangeBuffer(multi_buffer_info* Info)
{
if (fProcessedBuffers <= 0) {
// looks like somebody else has processed buffers but this stream
release_sem_etc(fDevice->fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE);
return false;
}
Info->played_real_time = system_time();// TODO fRealTime;
Info->played_frames_count += fSamplesCount / kSamplesBufferCount;
Info->playback_buffer_cycle = fCurrentBuffer;
fCurrentBuffer++;
fCurrentBuffer %= kSamplesBufferCount;
atomic_add(&fProcessedBuffers, -1);
return true;
}
/*
ASInterfaceDescriptor*
Stream::ASInterface()
{
return fAlternates[fActiveAlternate]->Interface();
}
_ASFormatDescriptor*
Stream::ASFormat()
{
return fAlternates[fActiveAlternate]->Format();
}*/
@@ -0,0 +1,96 @@
/*
* Driver for USB Audio Device Class devices.
* Copyright (c) 2009,10,12 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _STREAM_H_
#define _STREAM_H_
#include <OS.h>
#include "AudioStreamingInterface.h"
class Device;
// typedef Vector<AudioStreamAlternate*> StreamAlternatesVector;
// typedef Vector<AudioStreamAlternate*>::Iterator StreamAlternatesIterator;
class Stream : public AudioStreamingInterface {
friend class Device;
public:
Stream(Device* device, size_t interface,
usb_interface_list *List
/*, bool isInput, uint32 HWChannel*/);
~Stream();
status_t Init();
status_t InitCheck() { return fStatus; }
status_t Start();
status_t Stop();
bool IsRunning() { return fIsRunning; }
status_t GetBuffers(multi_buffer_list* List);
status_t OnSetConfiguration(usb_device device,
const usb_configuration_info *config);
bool ExchangeBuffer(multi_buffer_info* Info);
/*
int32 InterruptHandler(uint32 SignaledChannelsMask);
*/
// ASInterfaceDescriptor* ASInterface();
// _ASFormatDescriptor* ASFormat();
status_t GetEnabledChannels(uint32& offset,
multi_channel_enable *Enable);
status_t SetEnabledChannels(uint32& offset,
multi_channel_enable *Enable);
status_t GetGlobalFormat(multi_format_info *Format);
status_t SetGlobalFormat(multi_format_info *Format);
protected:
Device* fDevice;
status_t fStatus;
// alternates of the streams
// StreamAlternatesVector fAlternates;
// size_t fActiveAlternate;
uint8 fTerminalID;
usb_pipe fStreamEndpoint;
bool fIsRunning;
/* uint32 fHWChannel;*/
area_id fArea;
usb_iso_packet_descriptor* fDescriptors;
size_t fDescriptorsCount;
size_t fCurrentBuffer;
uint32 fStartingFrame;
size_t fSamplesCount;
int32 fProcessedBuffers;
// void* fBuffersPhysAddress;
/* bigtime_t fRealTime;
bigtime_t fFramesCount;
int32 fBufferCycle;
public:
uint32 fCSP; */
private:
status_t _QueueNextTransfer(size_t buffer);
static void _TransferCallback(void *cookie, int32 status,
void *data, uint32 actualLength);
void _DumpDescriptors();
};
/*
class RecordStream : public Stream {
public:
RecordStream(Device* device, uint32 HWChannel);
~RecordStream();
status_t Start();
};
*/
#endif // _STREAM_H_
@@ -1,9 +1,9 @@
/*
* USB audio spec stuctures
*
*
* Based on the USB Device Class Definition for Audio Devices Release 1.0
* (March 18, 1998)
*
*
* And the USB Device Class Definition for Audio Formats Release 1.0
* (March 18, 1998)
*/
@@ -39,6 +39,28 @@ extern "C" {
#define UAS_CS_INTERFACE 0x24
#define UAS_CS_ENDPOINT 0x25
// Audio Function Categories
// Table A-7 page 132
enum AudioFunctionCategory {
AFCUndefined = 0x00,
AFCDesktopSpeaker = 0x01,
AFCHomeTheater = 0x02,
AFCMicrophone = 0x03,
AFCHeadset = 0x04,
AFCTelephone = 0x05,
AFCConverter = 0x06,
AFCVoiceRecorder = 0x07,
AFCIOBox = 0x08,
AFCInstrument = 0x09,
AFCProAudio = 0x0a,
AFCAudioVideo = 0x0b,
AFCControlPanel = 0x0c,
AFCOther = 0xff
};
/*
// Table A-5, page 100
#define UAS_AC_DESCRIPTOR_UNDEFINED 0x00
#define UAS_HEADER 0x01
@@ -49,6 +71,25 @@ extern "C" {
#define UAS_FEATURE_UNIT 0x06
#define UAS_PROCESSING_UNIT 0x07
#define UAS_EXTENSION_UNIT 0x08
*/
// Specification Release 2.0
// Table A-9 page 131
enum ACInterfaceDescriptorSubtype {
IDSUndefined = 0x00,
IDSHeader = 0x01,
IDSInputTerminal = 0x02,
IDSOutputTerminal = 0x03,
IDSMixerUnit = 0x04,
IDSSelectorUnit = 0x05,
IDSFeatureUnit = 0x06,
IDSEffectUnit = 0x07,
IDSProcessingUnit = 0x08,
IDSExtensionUnit = 0x09,
IDSClockSource = 0x0a,
IDSClockSelector = 0x0b,
IDSClockMultiplier = 0x0c,
IDSSampleRateConverter = 0x0d
};
// Table A-6, page 100
#define UAS_AS_DESCRIPTOR_UNDEFINED 0x00
@@ -99,45 +140,123 @@ extern "C" {
#define UAS_SAMPLING_FREQ_CONTROL 0x01
#define UAS_PITCH_CONTROL 0x02
// header descriptor
typedef struct {
uint8 length;
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // HEADER
uint16 bcd_release_no; // Audio Device Class Specification relno
uint16 total_length; //
uint16 total_length; //
uint8 in_collection; // # of audiostreaming units
uint8 interface_numbers[1]; // or more
} _PACKED usb_audiocontrol_header_descriptor_r1;
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // HEADER
uint16 bcd_release_no; // Audio Device Class Specification relno
uint8 function_category; // this Audio function Category
uint16 total_length; //
uint8 bm_controls; // bitmap of controls
} _PACKED usb_audiocontrol_header_descriptor;
// input terminal descriptor
// Table 4-3, page 39
// Table 4-3, page 39
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // INPUT_TERMINAL
uint8 terminal_id; //
uint8 terminal_id; //
uint16 terminal_type; // 0x0101 ?
uint8 assoc_terminal; // OT terminal ID of corresponding outp
uint8 num_channels; // stereo = 2
uint8 channel_config; // spatial location of two channels (bitmap 0x03 is plain stereo)
uint8 channel_names; // index of string descr, name of first logical channel
uint8 terminal; // index of string descr, name of Input Terminal
uint8 channel_config; // spatial location of two channels
// (bitmap 0x03 is plain stereo)
uint8 channel_names; // index of string descr,
// name of first logical channel
uint8 terminal; // index of string descr,
// name of Input Terminal
} _PACKED usb_input_terminal_descriptor_r1;
// Table 4-9, page 53
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // INPUT_TERMINAL
uint8 terminal_id; //
uint16 terminal_type; // 0x0101 ?
uint8 assoc_terminal; // OT terminal ID of corresponding outp
uint8 clock_source_id; //
uint8 num_channels; // stereo = 2
uint32 channel_config; // spatial location of two channels
// (bitmap 0x03 is plain stereo)
uint8 channel_names; // index of string descr,
// name of first logical channel
uint16 bm_controls; //
uint8 terminal; // index of string descr,
// name of Input Terminal
} _PACKED usb_input_terminal_descriptor;
// output terminal descriptor
// Table 4-4, page 40
// Table 4-4, page 40
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // OUTPUT_TERMINAL
uint8 terminal_id; //
uint8 terminal_id; //
uint16 terminal_type; // 0x0101 ?
uint8 assoc_terminal; // OT terminal ID of corresponding outp
uint8 source_id; // ID of the unit or terminal to which this terminal is connected
uint8 terminal; // index of string descr, name of Input Terminal
uint8 source_id; // ID of the unit or terminal to which
// this terminal is connected
uint8 terminal; // index of string descr,
// name of Input Terminal
} _PACKED usb_output_terminal_descriptor_r1;
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // OUTPUT_TERMINAL
uint8 terminal_id; //
uint16 terminal_type; // 0x0101 ?
uint8 assoc_terminal; // OT terminal ID of corresponding outp
uint8 source_id; // ID of the unit or terminal to which
// this terminal is connected
uint8 clock_source_id; //
uint16 bm_controls; //
uint8 terminal; // index of string descr,
// name of Input Terminal
} _PACKED usb_output_terminal_descriptor;
// mixer unit descriptor
// Table 4-5, page 41
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // MIXER_UNIT
uint8 unit_id; // unique within audio function
uint8 num_input_pins; //
uint8 input_pins[1]; // array of source ids for the mixer
// use usb_output_channels_descriptor
// to parse the rest
} _PACKED usb_mixer_unit_descriptor;
// pseudo-descriptor for a section corresponding to logical output channels
// used in mixer, processing and extension descriptions.
typedef struct {
uint8 num_output_pins; // number of mixer output pins
uint16 channel_config; // location of logical channels
uint8 channel_names; // id of name string of first logical channel
} _PACKED usb_output_channels_descriptor_r1;
typedef struct {
uint8 num_output_pins; // number of mixer output pins
uint32 channel_config; // location of logical channels
uint8 channel_names; // id of name string of first logical channel
} _PACKED usb_output_channels_descriptor;
// selector unit descriptor
// Table 4-6, page 43
typedef struct {
@@ -145,11 +264,13 @@ typedef struct {
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // SELECTOR_UNIT
uint8 unit_id; // unique within audio function
uint8 num_input_pins; //
uint8 input_pins[2]; //
uint8 selector_string[1]; // YUK - doesn't work if num_input_pins!=2 - in that case, add (num_input_pins-2) to the index of this array...
uint8 num_input_pins; //
uint8 input_pins[1]; // id of the unit or terminal
// this pin is connected to
/* uint8 selector_string; */ // be afraid of the variable
// size of input_pins array!
} _PACKED usb_selector_unit_descriptor;
// feature unit descriptor
// Table 4-7, page 43
typedef struct {
@@ -157,13 +278,88 @@ typedef struct {
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // FEATURE_UNIT
uint8 unit_id; // unique within audio function
uint8 source_id; // id of the unit or terminal to which this unit is connected
uint8 control_size; // bma_controls is 2 bytes per element
uint16 master_bma_control; // only if control_size is 2
uint16 bma_controls[2]; // if control_size = 2 and two channels
uint8 feature_string; // if control_size = 2 and ch = 2
uint8 source_id; // id of the unit or terminal to
// which this unit is connected
uint8 control_size; // size of element in bma_controls array
uint8 bma_controls[1]; // the size of element must be equal
// to control_size!!
// the channel 0 is master one!
/* uint8 feature_string; */ // be afraid of the variable size
// of bma_controls array!
} _PACKED usb_feature_unit_descriptor_r1;
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // FEATURE_UNIT
uint8 unit_id; // unique within audio function
uint8 source_id; // id of the unit or terminal to
// which this unit is connected
uint32 bma_controls[1]; // the channel 0 is master one!
/* uint8 feature_string; */ // be afraid of the variable size of
// bma_controls array!
} _PACKED usb_feature_unit_descriptor;
// bitset for feature control bitmap
// Table 4-7, page 44, bmaControls field
enum FeatureControls {
MuteControl1 = 0x0001,
VolumeControl1 = 0x0002,
BassControl1 = 0x0004,
MidControl1 = 0x0008,
TrebleControl1 = 0x0010,
GraphEqControl1 = 0x0020,
AutoGainControl1 = 0x0040,
DelayControl1 = 0x0080,
BassBoostControl1 = 0x0100,
LoudnessControl1 = 0x0200,
// Release 2.0
MuteControl = 0x00000003,
VolumeControl = 0x0000000c,
BassControl = 0x00000030,
MidControl = 0x000000c0,
TrebleControl = 0x00000300,
GraphEqControl = 0x00000c00,
AutoGainControl = 0x00003000,
DelayControl = 0x0000c000,
BassBoostControl = 0x00030000,
LoudnessControl = 0x000c0000,
InputGainControl = 0x00300000,
InputGainPadControl = 0x00c00000,
PhaseInverterControl= 0x03000000,
UnderflowControl = 0x0c000000,
OverflowControl = 0x30000000
};
// processing unit descriptor
// Table 4-8, page 45
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // PROCESSING_UNIT
uint8 unit_id; // unique within audio function
uint16 process_type; // type of processing this unit is performing
uint8 num_input_pins; // number of input pins of this unit
uint8 input_pins[1]; // array of source ids for the processing unit
// use usb_output_channels_descriptor
// to parse the rest
// TODO - the bmControl!!!!
} _PACKED usb_processing_unit_descriptor;
// extension unit descriptor
// Table 4-15, page 56
typedef struct {
uint8 length;
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // EXTENSION_UNIT
uint8 unit_id; // unique within audio function
uint16 extension_code; // vendor-specific code identifying this unit
uint8 num_input_pins; // number of input pins
uint8 input_pins[1]; // array of source ids for the processing unit
// use usb_output_channels_descriptor
// to parse the rest
} _PACKED usb_extension_unit_descriptor;
// Audio Streaming (as) descriptors
@@ -177,6 +373,20 @@ typedef struct {
uint8 terminal_link; // terminal ID to which this endp is connected
uint8 delay; // delay in # of frames
uint16 format_tag; // wFormatTag, 0x0001 = PCM
} _PACKED usb_as_interface_descriptor_r1;
typedef struct {
uint8 length; // 7
uint8 descriptor_type; // CS_INTERFACE descriptor type (0x24)
uint8 descriptor_subtype; // UAS_AS_GENERAL
uint8 terminal_link; // terminal ID to which this endp is connected
uint8 bm_controls; // controls bitmap
uint8 format_type; // type of audio streaming use
uint32 bm_formats; // audio data formats to be used with
// this interface
uint8 num_output_pins; // number of physical channels in the claster
uint32 channel_config; // spatial location of channels
uint8 channel_names; // id of name string of first physical channel
} _PACKED usb_as_interface_descriptor;
// Class-specific As Isochronous Audio Data Endpoint descriptor
@@ -185,9 +395,10 @@ typedef struct {
uint8 length; // 7
uint8 descriptor_type; // UAS_CS_ENDPOINT descriptor type (0x25)
uint8 descriptor_subtype; // UAS_EP_GENERAL
uint8 attributes; // d0=samfq d1=pitch d7=maxpacketsonly
uint8 lock_delay_units; // 1=ms 2=decpcmsampl
uint16 lock_delay; // time for endp to lock internal clock recovery circuitry
uint8 attributes; // d0 = samfq d1 = pitch d7 = maxpacketsonly
uint8 lock_delay_units; // 1 = ms 2 = decpcmsampl
uint16 lock_delay; // time for endp to lock internal
// clock recovery circuitry
} _PACKED usb_as_cs_endpoint_descriptor;
@@ -197,7 +408,7 @@ typedef struct {
uint8 data[3];
} _PACKED usb_triplet;
// and
// and
/*
* Audio data formats spec
@@ -237,18 +448,48 @@ typedef union {
// Table 2-1, page 10
typedef struct {
uint8 length; // 0e for
uint8 length; // 0e for
uint8 descriptor_type; // UAS_CS_INTERFACE (0x24)
uint8 descriptor_subtype; // UAS_FORMAT_TYPE (0x02)
uint8 format_type; // UAF_FORMAT_TYPE_I (0x01)
uint8 nr_channels; // hopefully 2
uint8 subframe_size; // 1, 2, or 4 bytes
uint8 bit_resolution; // 8, 16 or 20 bits
uint8 sam_freq_type; // 0 == continuous, 1 == a fixed number of discrete sam freqs
uint8 sam_freq_type; // 0 == continuous, 1 == a fixed
// number of discrete sam freqs
usb_audio_sam_freq_descr sf; // union
// uint8 sam_freq[12 * 3];
} _PACKED usb_type_I_format_descriptor;
// Table 2-4, page 13
typedef struct {
uint8 length; // 0e for
uint8 descriptor_type; // UAS_CS_INTERFACE (0x24)
uint8 descriptor_subtype; // UAS_FORMAT_TYPE (0x02)
uint8 format_type; // UAF_FORMAT_TYPE_II (0x02)
uint16 max_bit_rate; // max bit rate in kbits/sec
uint16 samples_per_frame; // samples per frame
uint8 sam_freq_type; // 0 == continuous, 1 == a fixed
// number of discrete sam freqs
usb_audio_sam_freq_descr sf; // union
// uint8 sam_freq[12 * 3];
} _PACKED usb_type_II_format_descriptor;
// Table 2-23, page 26 (the same as Type I)
typedef struct {
uint8 length; // 0e for
uint8 descriptor_type; // UAS_CS_INTERFACE (0x24)
uint8 descriptor_subtype; // UAS_FORMAT_TYPE (0x02)
uint8 format_type; // UAF_FORMAT_TYPE_III (0x03)
uint8 nr_channels; // hopefully 2
uint8 subframe_size; // 1, 2, or 4 bytes
uint8 bit_resolution; // 8, 16 or 20 bits
uint8 sam_freq_type; // 0 == continuous, 1 == a fixed
// number of discrete sam freqs
usb_audio_sam_freq_descr sf; // union
// uint8 sam_freq[12 * 3];
} _PACKED usb_type_III_format_descriptor;
#ifdef __cplusplus
}
#endif
@@ -1,395 +0,0 @@
/*
* USB audio spec utils
*/
#include <OS.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <ByteOrder.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <USB.h>
#include <USB_spec.h>
#include "USB_audio_spec.h"
#include "USB_audio_utils.h"
extern void set_triplet(int8* ptr, uint32 param);
uint32
get_triplet(int8 *ptr)
{
uint32 lsb = ptr[0] & 0xff;
uint32 hsb = ptr[1] & 0xff;
uint32 msb = ptr[2] & 0xff;
return (msb << 16) | (hsb << 8) | (lsb);
}
/*
* dump a whole configuration
*/
void
dump_usb_configuration_info(const usb_configuration_info *conf)
{
usb_interface_info *inf;
int i, j, k;
bool isactive;
dprintf("Dumping usb_configuration_info... %d interfaces\n", conf->interface_count);
for (i = 0; i < conf->interface_count; i++) {
dprintf("INTERFACE %d, %d alternatitives:\n", i, conf->interface[i].alt_count);
for (j=0; j<conf->interface[i].alt_count; j++) {
isactive = &conf->interface[i].alt[j] == conf->interface[i].active;
dprintf(" altconf #%d (%s):\n", j, isactive?"*":"-");
if (isactive) {
inf = conf->interface[i].active;
dump_interface(inf);
} else {
inf = &conf->interface[i].alt[j];
dump_interface(inf);
// dprintf(" #endp: %d\n",conf->interface[i].alt[j].endpoint_count);
}
}
}
}
void
dump_interface_descriptor(usb_interface_descriptor *intf_des)
{
dprintf(" interface descriptor: %d bytes\n", intf_des->length);
dprintf(" interface_number: %d\n", intf_des->interface_number);
dprintf(" alternate_setting: %d\n", intf_des->alternate_setting);
dprintf(" num_endpoints: %d\n", intf_des->num_endpoints);
dprintf(" class/sub/prot: %d/%d/%d\n", intf_des->interface_class,
intf_des->interface_subclass, intf_des->interface_protocol);
dprintf(" interface str: %d\n", intf_des->interface);
}
void
dump_interface(usb_interface_info *inf)
{
int i;
usb_interface_descriptor *intf_des = inf->descr;
dump_interface_descriptor(intf_des);
if (inf->descr->interface_class == 1) {
if (inf->descr->interface_subclass == 1) {
dump_usb_class_110(inf->endpoint, inf->endpoint_count,
(int8**)inf->generic, inf->generic_count);
}
if (inf->descr->interface_subclass == 2) {
dump_usb_class_120(inf->endpoint, inf->endpoint_count,
(int8**)inf->generic, inf->generic_count);
}
}
//if (inf->descr->interface_class == 3) {
// if (inf->descr->interface_subclass == 0) {
// dump_usb_class_300(inf->endpoint, inf->endpoint_count,
// (int8**)inf->generic, inf->generic_count);
// // dprintf(" <HID stuff here>\n");
// }
//}
}
char *
attr(int8 attrib)
{
switch (attrib) {
case 0x00: return "Control";
case 0x01: return "Isochronous";
case 0x02: return "Bulk";
case 0x03: return "Interrupt";
}
return "?";
}
void
dump_endpoint_info(int i, usb_endpoint_info *ep_inf)
{
dprintf(" endpoint %d:\n",i);
dprintf(" address: %d, %s\n",ep_inf->descr->endpoint_address & 0x07, (ep_inf->descr->endpoint_address & 0x80)?"IN":"OUT");
dprintf(" transfer type: %s\n",attr(ep_inf->descr->attributes));
dprintf(" max_packet_size: %d\n",ep_inf->descr->max_packet_size);
dprintf(" interval: %d\n",ep_inf->descr->interval);
dprintf(" usb_pipe handle: %p\n",ep_inf->handle);
}
/*
* dump class 1/1/0
*/
void
dump_usb_class_110(usb_endpoint_info *ep_inf, size_t endpoint_count,
int8 **ptr, size_t count)
{
int i, length;
int8* data;
int descr;
dprintf(" Class 1/1/0 - Audio Control\n");
dprintf(" With %d endpoints:\n", endpoint_count);
for (i=0; i<endpoint_count; i++) {
dump_endpoint_info(i, &ep_inf[i]);
}
dprintf(" And %d other descriptors:\n",count);
for (i=0; i<count; i++) {
data = ptr[i];
length = data[0];
if (data[1] != UAS_CS_INTERFACE) { // 0x24
dump_data(&data[0], length);
} else {
descr = data[2];
switch (descr) {
default:
case UAS_AC_DESCRIPTOR_UNDEFINED:
dump_data(&data[0], length);
break;
case UAS_HEADER:
dump_usb_audiocontrol_header_descriptor(data);
break;
case UAS_INPUT_TERMINAL:
dump_usb_input_terminal_descriptor(data);
break;
case UAS_OUTPUT_TERMINAL:
dump_usb_output_terminal_descriptor(data);
break;
case UAS_MIXER_UNIT:
dump_data(&data[0], length);
break;
case UAS_SELECTOR_UNIT:
dump_usb_selector_unit_descriptor(data);
break;
case UAS_FEATURE_UNIT:
dump_usb_feature_unit_descriptor(data);
break;
case UAS_PROCESSING_UNIT:
case UAS_EXTENSION_UNIT:
dump_data(&data[0], length);
break;
}
}
}
}
void
dump_usb_audiocontrol_header_descriptor(int8 *data)
{
int i;
usb_audiocontrol_header_descriptor* h = (usb_audiocontrol_header_descriptor*)data;
dump_descr(data);
dprintf(" HEADER\n");
dprintf(" bcd_release_no: 0x%x\n", h->bcd_release_no);
dprintf(" total_length: %d bytes\n", h->total_length);
dprintf(" in_collection: %d\n", h->in_collection);
for (i = 0; i < h->in_collection; i++) {
dprintf(" %d\n", h->interface_numbers[i]);
}
}
void
dump_usb_input_terminal_descriptor(int8 *data)
{
usb_input_terminal_descriptor* it = (usb_input_terminal_descriptor*)data;
dump_descr(data);
dprintf(" INPUT_TERMINAL\n");
dprintf(" terminal id: %d\n", it->terminal_id);
dprintf(" terminal_type: 0x%x\n", it->terminal_type);
dprintf(" assoc_terminal: %d\n", it->assoc_terminal);
dprintf(" num_channels: %d\n", it->num_channels);
dprintf(" channel_config: %d\n", it->channel_config);
dprintf(" channel_names: %d\n", it->channel_names);
dprintf(" terminal str: %d\n", it->terminal);
}
void
dump_usb_output_terminal_descriptor(int8 *data)
{
usb_output_terminal_descriptor* ot = (usb_output_terminal_descriptor*)data;
dump_descr(data);
dprintf(" OUTPUT_TERMINAL\n");
dprintf(" terminal id: %d\n", ot->terminal_id);
dprintf(" terminal_type: 0x%x\n", ot->terminal_type);
dprintf(" assoc_terminal: %d\n", ot->assoc_terminal);
dprintf(" source_id: %d\n", ot->source_id);
dprintf(" terminal str: %d\n", ot->terminal);
}
void
dump_usb_selector_unit_descriptor(int8 *data)
{
int i;
usb_selector_unit_descriptor* su = (usb_selector_unit_descriptor*)data;
dump_descr(data);
dprintf(" SELECTOR_UNIT\n");
dprintf(" unit_id: %d\n", su->unit_id);
dprintf(" num_input_pins: %d\n", su->num_input_pins);
for (i=0; i < su->num_input_pins; i++) {
dprintf(" %d\n", su->input_pins[i]);
}
dprintf(" selector str: %d\n", su->input_pins[i]); // or selector_string
}
void
bma_dec(int16 bitmap)
{
if (bitmap & 0x0001) dprintf("Mute ");
if (bitmap & 0x0002) dprintf("Volume ");
if (bitmap & 0x0004) dprintf("Bass ");
if (bitmap & 0x0008) dprintf("Mid ");
if (bitmap & 0x0010) dprintf("Treble ");
if (bitmap & 0x0020) dprintf("Graphic EQ ");
if (bitmap & 0x0040) dprintf("Automatic Gain ");
if (bitmap & 0x0080) dprintf("Delay ");
if (bitmap & 0x0100) dprintf("Bass Boost ");
if (bitmap & 0x0200) dprintf("Loudness ");
dprintf("\n");
}
void
dump_usb_feature_unit_descriptor(int8 *data)
{
// warning: either doc is out of date, or this & 1325 implementation is wrong on control_size vs bma_controls[] length
int i, length;
usb_feature_unit_descriptor* fu = (usb_feature_unit_descriptor*)data;
dump_descr(data);
length = data[0];
dprintf(" FEATURE_UNIT\n");
dprintf(" unit_id: %d\n", fu->unit_id);
dprintf(" source_id: %d\n", fu->source_id);
dprintf(" control_size: %d\n", fu->control_size);
if (fu->control_size == 2) {
dprintf(" master bma: ");
bma_dec(fu->master_bma_control);
for (i=0; i < fu->control_size; i++) {
dprintf(" ch %d bma: ", i);
bma_dec(fu->bma_controls[i]);
}
dprintf(" featurestr: %d\n", fu->feature_string);
} else {
dprintf(" err: unsupported control_size\n");
}
}
/*
* dump class 1/2/0
*/
void
dump_usb_class_120(usb_endpoint_info *ep_inf,
size_t endpoint_count, int8 **ptr, size_t count)
{
int i, length;
int8* data;
dprintf(" Class 1/2/0 - Audio Streaming\n");
dprintf(" With %d endpoints:\n", endpoint_count);
for (i=0; i<endpoint_count; i++) {
dump_endpoint_info(i, &ep_inf[i]);
}
dprintf(" And %d other descriptors:\n",count);
for (i=0; i<count; i++) {
data = ptr[i];
length = data[0];
switch (data[1]) {
case UAS_CS_INTERFACE: // 0x24
switch (data[2]) {
case UAS_AS_GENERAL:
dump_usb_as_interface_descriptor(data);
break;
case UAS_FORMAT_TYPE:
dump_usb_type_I_format_descriptor(data);
break;
default:
dump_data(&data[0], length);
break;
}
break;
case UAS_CS_ENDPOINT:
switch (data[2]) {
case UAS_EP_GENERAL:
dump_usb_as_cs_endpoint_descriptor(data);
break;
default:
dump_data(&data[0], length);
break;
}
break;
default:
dump_data(&data[0], length);
break;
}
}
}
void
dump_usb_as_interface_descriptor(int8 *data)
{
usb_as_interface_descriptor* as = (usb_as_interface_descriptor*)data;
}
void
dump_usb_type_I_format_descriptor(int8 *data)
{
int i;
usb_type_I_format_descriptor* fmt = (usb_type_I_format_descriptor*)data;
}
void
dump_usb_as_cs_endpoint_descriptor(int8 *data)
{
usb_as_cs_endpoint_descriptor* ep = (usb_as_cs_endpoint_descriptor*)data;
dump_descr(data);
dprintf(" Audio Streaming Endpoint Descriptor\n");
dprintf(" attributes: %d\n", ep->attributes);
dprintf(" lock_delay_units: %d\n", ep->lock_delay_units);
dprintf(" lock_delay: %d\n", ep->lock_delay);
}
/*
* Last resort...
*/
void
dump_descr(int8 *data)
{
dump_data(data + 1, *data);
}
void
dump_data(int8 *data, int length)
{
int i;
for (i=0; i<length; i++) {
dprintf("%02x ",data[i]);
}
dprintf("\n");
}
@@ -1,43 +0,0 @@
/*
* USB audio spec utils
*/
#ifndef __USB_AUDIO_SPEC_UTILS_H__
#define __USB_AUDIO_SPEC_UTILS_H__
#include <OS.h>
#ifdef __cplusplus
extern "C" {
#endif
// A whole config
void dump_usb_configuration_info(const usb_configuration_info *conf);
void dump_interface_descriptor(usb_interface_descriptor* intf_des);
void dump_interface(usb_interface_info* inf);
void dump_endpoint_info(int i, usb_endpoint_info* ep_inf);
// Class 1/1/0 (Audio Control)
void dump_usb_class_110(usb_endpoint_info* ep_inf, size_t endpoint_count, int8** ptr, size_t count);
void dump_usb_audiocontrol_header_descriptor(int8* data);
void dump_usb_input_terminal_descriptor(int8* data);
void dump_usb_output_terminal_descriptor(int8* data);
void dump_usb_selector_unit_descriptor(int8* data);
void dump_usb_feature_unit_descriptor(int8* data);
// Class 1/2/0 (Audio Streaming)
void dump_usb_class_120(usb_endpoint_info* ep_inf, size_t endpoint_count, int8** ptr, size_t count);
void dump_usb_as_interface_descriptor(int8* data);
void dump_usb_type_I_format_descriptor(int8* data);
void dump_usb_as_cs_endpoint_descriptor(int8* data);
// utils
void dump_descr(int8* data);
void dump_data(int8* data, int length);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,127 @@
## BeOS Generic Makefile v2.2 ##
## Fill in this file to specify the project being created, and the referenced
## makefile-engine will do all of the hard work for you. This handles both
## Intel and PowerPC builds of the BeOS.
## Application Specific Settings ---------------------------------------------
# specify the name of the binary
NAME=usb_audio
# specify the type of binary
# APP: Application
# SHARED: Shared library or add-on
# STATIC: Static library archive
# DRIVER: Kernel Driver
TYPE=DRIVER
# add support for new Pe and Eddie features
# to fill in generic makefile
#%{
# @src->@
# specify the source files to use
# full paths or paths relative to the makefile can be included
# all files, regardless of directory, will have their object
# files created in the common object directory.
# Note that this means this makefile will not work correctly
# if two source files with the same name (source.c or source.cpp)
# are included from different directories. Also note that spaces
# in folder names do not work well with this makefile.
SRCS=Driver.cpp\
Device.cpp\
AudioControlInterface.cpp\
AudioStreamingInterface.cpp\
Stream.cpp\
Settings.cpp
# specify the resource files to use
# full path or a relative path to the resource file can be used.
RSRCS=
# @<-src@
#%}
# end support for Pe and Eddie
# specify additional libraries to link against
# there are two acceptable forms of library specifications
# - if your library follows the naming pattern of:
# libXXX.so or libXXX.a you can simply specify XXX
# library: libbe.so entry: be
#
# - if your library does not follow the standard library
# naming scheme you need to specify the path to the library
# and it's name
# library: my_lib.a entry: my_lib.a or path/my_lib.a
LIBS=
# specify additional paths to directories following the standard
# libXXX.so or libXXX.a naming scheme. You can specify full paths
# or paths relative to the makefile. The paths included may not
# be recursive, so include all of the paths where libraries can
# be found. Directories where source files are found are
# automatically included.
LIBPATHS=
# additional paths to look for system headers
# thes use the form: #include <header>
# source file directories are NOT auto-included here
SYSTEM_INCLUDE_PATHS=../../../../../../headers/private/kernel \
../../../../../../headers/private/kernel/util \
../../../../../../headers/private/media
# additional paths to look for local headers
# thes use the form: #include "header"
# source file directories are automatically included
LOCAL_INCLUDE_PATHS=../../../../../../build/config_headers
# specify the level of optimization that you desire
# NONE, SOME, FULL
OPTIMIZE=
# specify any preprocessor symbols to be defined. The symbols will not
# have their values set automatically; you must supply the value (if any)
# to use. For example, setting DEFINES to "DEBUG=1" will cause the
# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG"
# would pass "-DDEBUG" on the compiler's command line.
DEFINES=
# specify special warning levels
# if unspecified default warnings will be used
# NONE = supress all warnings
# ALL = enable all warnings
WARNINGS=ALL
# specify whether image symbols will be created
# so that stack crawls in the debugger are meaningful
# if TRUE symbols will be created
SYMBOLS=
# specify debug settings
# if TRUE will allow application to be run from a source-level
# debugger. Note that this will disable all optimzation.
DEBUGGER=
# specify additional compiler flags for all files
COMPILER_FLAGS=
# specify additional linker flags
LINKER_FLAGS=
# specify the version of this particular item
# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL")
# This may also be specified in a resource.
APP_VERSION=
# (for TYPE == DRIVER only) Specify desired location of driver in the /dev
# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will
# instruct the driverinstall rule to place a symlink to your driver's binary in
# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at
# /dev/video/usb when loaded. Default is "misc".
DRIVER_PATH=audio/hmulti
## include the makefile-engine
include $(BUILDHOME)/etc/makefile-engine
@@ -1,87 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
Data structures and control calls for using the sound driver
*/
#ifndef _SOUND_H
#define _SOUND_H
#include <Drivers.h>
enum adc_source {
line=0, aux1, mic, loopback
};
enum sample_rate {
kHz_8_0 = 0, kHz_5_51, kHz_16_0, kHz_11_025, kHz_27_42, kHz_18_9,
kHz_32_0, kHz_22_05, kHz_37_8 = 9, kHz_44_1 = 11, kHz_48_0, kHz_33_075,
kHz_9_6, kHz_6_62
};
enum sample_format {
linear_8bit_unsigned_mono = 0, linear_8bit_unsigned_stereo,
ulaw_8bit_companded_mono, ulaw_8bit_companded_stereo,
linear_16bit_little_endian_mono, linear_16bit_little_endian_stereo,
alaw_8bit_companded_mono, alaw_8bit_companded_stereo,
sample_format_reserved_1, sample_format_reserved_2,
adpcm_4bit_mono, adpcm_4bit_stereo,
linear_16bit_big_endian_mono, linear_16bit_big_endian_stereo,
sample_format_reserved_3, sample_format_reserved_4
};
struct channel {
enum adc_source adc_source; /* adc input source */
char adc_gain; /* 0..15 adc gain, in 1.5 dB steps */
char mic_gain_enable; /* non-zero enables 20 dB MIC input gain */
char aux1_mix_gain; /* 0..31 aux1 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char aux1_mix_mute; /* non-zero mutes aux1 mix */
char aux2_mix_gain; /* 0..31 aux2 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char aux2_mix_mute; /* non-zero mutes aux2 mix */
char line_mix_gain; /* 0..31 line mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char line_mix_mute; /* non-zero mutes line mix */
char dac_attn; /* 0..61 dac attenuation, in -1.5 dB steps */
char dac_mute; /* non-zero mutes dac output */
};
typedef struct sound_setup {
struct channel left; /* left channel setup */
struct channel right; /* right channel setup */
enum sample_rate sample_rate; /* sample rate */
enum sample_format playback_format;/* sample format for playback */
enum sample_format capture_format; /* sample format for capture */
char dither_enable; /* non-zero enables dither on 16 => 8 bit */
char loop_attn; /* 0..64 adc to dac loopback attenuation, in -1.5 dB steps */
char loop_enable; /* non-zero enables loopback */
char output_boost; /* zero (2.0 Vpp) non-zero (2.8 Vpp) output level boost */
char highpass_enable;/* non-zero enables highpass filter in adc */
char mono_gain; /* 0..64 mono speaker gain */
char mono_mute; /* non-zero mutes speaker */
} sound_setup;
/* -----
control opcodes for sound driver
----- */
enum {
SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END,
SOUND_SET_PARAMS, /* 10000 */
SOUND_SET_PLAYBACK_COMPLETION_SEM,
SOUND_SET_CAPTURE_COMPLETION_SEM,
SOUND_GET_PLAYBACK_TIMESTAMP, /* 10003 */
SOUND_GET_CAPTURE_TIMESTAMP,
SOUND_DEBUG_ON,
SOUND_DEBUG_OFF, /* 10006 */
SOUND_UNSAFE_WRITE,
SOUND_UNSAFE_READ,
SOUND_LOCK_FOR_DMA, /* 10009 */
SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE, /* 10010 */
SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE, /* 10011 */
SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE, /* 10012 */
SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE /* 10013 */
};
#endif /* _SOUND_H */
@@ -1,906 +0,0 @@
/* Copyright 2000 Be Incorporated. All Rights Reserved.
** This file may be used under the terms of the Be Sample Code
** License.
*/
#include <OS.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <stdlib.h>
#include <stdio.h>
#include <USB.h>
#include "audio.h" // USB Audio Class
#include "sound.h" // OLD-style ioctl()'s
#include "R3MediaDefs.h" // media kit
#include "USB_audio_utils.h" // Mark-Jan
#define DEBUG_DRIVER 1
#if DEBUG_DRIVER
#define DPRINTF(x) dprintf x
#else
#define DPRINTF(x) ((void)0)
#endif
static status_t device_open(const char *name, uint32 flags, void **cookie);
static status_t device_close(void *cookie);
static status_t device_free(void *cookie);
static status_t device_control(void *cookie, uint32 op, void *data, size_t len);
//static status_t pcm_read(void *cookie, off_t pos, void *data, size_t *len);
static status_t device_write(void *cookie, off_t pos, const void *data, size_t *len);
//static status_t pcm_writev(void *cookie, off_t pos, const iovec *vec, size_t count, size_t *len); /* */
/* ~44 kHz 16bit stereo */
#define SAMPLE_SIZE 4
#define QS_OUT 1764 //(44*SAMPLE_SIZE*TS) /* 1764, TS=10 was: (44*SAMPLE_SIZE*TS) */
#define TS 10 // was 4 /* 4 ms buffers */
#define NB 2
int32 api_version = B_CUR_DRIVER_API_VERSION;
int qs_out = QS_OUT;
typedef struct audiodev audiodev;
typedef struct iso_channel iso_channel;
typedef struct iso_packet iso_packet;
struct iso_packet
{
struct iso_packet* next;
struct iso_channel* channel;
void* buffer;
uint32 status;
size_t buffer_size;
rlea* rle_array;
int64 rw_count;
bigtime_t time;
};
struct iso_channel
{
usb_pipe* ep;
iso_packet* next_to_queue;
iso_packet* current_rw;
size_t remain;
char* buf_ptr;
sem_id num_available_packets;
area_id buffer_area;
iso_packet iso_packets[NB];
int64 rw_count;
int64 current_count;
bigtime_t current_time;
int active;
};
struct audiodev {
audiodev *next;
int open;
int number;
const usb_device *dev;
iso_channel out_channel;
// audio specific stuff
sound_setup setup;
sem_id playback_sem;
bigtime_t write_time;
uint64 write_total;
};
static sound_setup usb_sound_setup = {
// left channel
{
aux1, // adc_source
20, // adc_gain
0, // mic_gain_enable
30, // aux1_mix_gain
0, // aux1_mix_mute
20, // aux2_mix_gain
0, // aux2_mix_mute
20, // line_mix_gain
0, // line_mix_mute
10, // dac_attn
0 // dac_mute
},
// right channel
{
aux1, // adc_source
20, // adc_gain
0, // mic_gain_enable
30, // aux1_mix_gain
0, // aux1_mix_mute
20, // aux2_mix_gain
0, // aux2_mix_mute
20, // line_mix_gain
0, // line_mix_mute
10, // dac_attn
0 // dac_mute
},
kHz_44_1, // sample_rate
16, // playback_format (ignored, always 16bit-linear)
16, // capture_format (ignored, always 16bit-linear)
0, // dither_enable
0, // mic_attn
0, // mic_enable
0, // output_boost (ignored, always on)
0, // highpass_enable (ignored, always on)
0, // mono_gain
1 // mono_mute
};
/* handy strings for referring to ourself */
#define ID "usb_audio: "
static const char *drivername = "usb_audio";
static const char *basename = "audio/old/usb_audio/";
/* list of device instances and names for publishing */
static audiodev *device_list = NULL;
static sem_id dev_list_lock = -1;
static int device_count = 0;
static char **device_names = NULL;
/* handles for the USB bus manager */
static char *usb_name = B_USB_MODULE_NAME;
static usb_module_info *usb;
/* USB Isoch Transaction Stuff ------------------------------------------
**
** Create and Destroy an Isochronous "channel" and handle queueing
** packets, callbacks, etc.
**
*/
void
init_iso_channel(iso_channel* ch, usb_pipe* ep, size_t buf_size, bool is_in)
{
int pn;
void* big_buffer;
ch->ep = ep;
ch->buffer_area = create_area("usb_device_buffer",
(void **)&big_buffer,
B_ANY_KERNEL_ADDRESS,
((buf_size*NB) + B_PAGE_SIZE-1) & ~(B_PAGE_SIZE-1),
B_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA);
DPRINTF((ID "buffer_area %d @ 0x%08x\n", ch->buffer_area, big_buffer));
for(pn=0; pn<NB; pn++) {
ch->iso_packets[pn].channel = ch;
ch->iso_packets[pn].buffer = (char*)big_buffer + buf_size*pn;
ch->iso_packets[pn].rw_count = 0;
ch->iso_packets[pn].time = 0;
if(is_in) {
ch->iso_packets[pn].rle_array = malloc(sizeof(rlea) + (4-1)*sizeof(rle));
ch->iso_packets[pn].rle_array->length = 4;
} else {
ch->iso_packets[pn].rle_array = NULL;
}
ch->iso_packets[pn].next = &ch->iso_packets[(pn+1)%NB];
}
ch->current_rw = ch->next_to_queue = &ch->iso_packets[0];
if(!is_in) ch->current_rw = &ch->iso_packets[NB-1];
ch->num_available_packets = create_sem(0, "iso_channel");
ch->remain = 0;
ch->buf_ptr = 0;
ch->active = 1;
ch->rw_count = 0;
ch->current_count = 0;
ch->current_time = 0;
}
void
uninit_iso_channel(iso_channel* ch)
{
int pn;
ch->active = 0;
for(pn=0; pn<NB; pn++) {
free(ch->iso_packets[pn].rle_array);
}
delete_sem(ch->num_available_packets);
delete_area(ch->buffer_area);
}
static void cb_notify_out(void *cookie, uint32 status,
void *data, uint32 actual_len);
static void queue_packet_out(iso_channel* ch);
static void
cb_notify_out(void *cookie, uint32 status, void *data, uint32 actual_len)
{
iso_packet* const packet = (iso_packet*) cookie;
iso_channel* const channel = packet->channel;
int num_queued_packets;
static bigtime_t t, t0;
static c;
int i;
int32 sc;
#if 0
uint8 *data8 = (uint8*)data;
if (data8[0] != 0 &&
data8[1] != 0 &&
data8[2] != 0) {
uint32 val = (data8[2] << 16) | (data8[1] << 8) | data8[0];
//dprintf(ID "actual_len: %ld\n", actual_len);
dprintf(ID "cb_notify_out: data - 0x%.2X%.2X%.2X / %d (%d)\n",data8[2],data8[1],data8[0],val, val>>10);
}
#endif
packet->status = status;
packet->buffer_size = qs_out;
if((status == B_OK) && channel->active) {
queue_packet_out(channel);
channel->current_rw = packet;
channel->rw_count += packet->buffer_size;
packet->rw_count = channel->rw_count;
// hack for startup time
get_sem_count(channel->num_available_packets, &sc);
if(sc < 2) release_sem(channel->num_available_packets);
}
}
static void
queue_packet_out(iso_channel* ch)
{
iso_packet* packet = ch->next_to_queue;
status_t s;
//dprintf("queue_isochronous_out(%d, %p, %p)\n", qs_out, packet->buffer, packet->rle_array);
if(s = usb->queue_isochronous(ch->ep, packet->buffer, qs_out,
NULL, TS, cb_notify_out, packet)) {
dprintf(ID "packet out %p status %d\n", packet, s);
} else {
ch->next_to_queue = packet->next;
}
}
void
start_iso_channel_out(iso_channel* channel)
{
int pn;
for(pn=0; pn<NB; pn++) {
queue_packet_out(channel);
}
}
void
stop_iso_channel(iso_channel* channel)
{
usb->cancel_queued_transfers(channel->ep);
}
/* These rather inelegant routines are used to assign numbers to
** device instances so that they have unique names in devfs.
*/
static uint32 device_numbers = 0;
static int
get_number()
{
int num;
for(num = 0; num < 32; num++) {
if(!(device_numbers & (1 << num))){
device_numbers |= (1 << num);
return num;
}
}
return -1;
}
static void
put_number(int num)
{
device_numbers &= ~(1 << num);
}
/* Device addition and removal ---------------------------------------
**
** add_device() and remove_device() are used to create and tear down
** device instances. They are driver by the callbacks device_added()
** and device_removed() which are invoked by the USB bus manager.
*/
static audiodev *
add_device(const usb_device *dev, const usb_configuration_info *conf)
{
audiodev *ad = NULL;
int num,i,ifc,alt;
const usb_interface_info *ii;
status_t st;
usb_pipe *out;
DPRINTF((ID "add_device(%p, %p)\n", dev, conf));
if((num = get_number()) < 0) {
return NULL;
}
for(ifc = 0; ifc < conf->interface_count; ifc++){
for(alt = 0; alt < conf->interface[ifc].alt_count; alt++) {
int j;
int16 sample_rate;
ii = &conf->interface[ifc].alt[alt];
/* does it have an AudioStreaming interface? */
if(ii->descr->interface_class != AC_AUDIO) continue;
/* mute that damn mic! */
if (ii->descr->interface_subclass == AC_AUDIOCONTROL) {
int8 **ptr = (int8 **)ii->generic;
int32 count = ii->generic_count;
int8 *data = 0;
for (i=0; i<count; i++) {
data = ptr[i];
//length = data[0];
if (data[1] == AC_CS_INTERFACE) { // 0x24
int8 descr = data[2];
switch (descr) {
default: break;
case AC_AC_DESCRIPTOR_UNDEFINED:
//dump_data(&data[0], length);
break;
case AC_HEADER:
//dump_usb_audiocontrol_header_descriptor(data);
break;
case AC_INPUT_TERMINAL:
//dump_usb_input_terminal_descriptor(data);
break;
case AC_OUTPUT_TERMINAL:
//dump_usb_output_terminal_descriptor(data);
break;
case AC_MIXER_UNIT:
//dump_data(&data[0], length);
break;
case AC_SELECTOR_UNIT:
//dump_usb_selector_unit_descriptor(data);
break;
case AC_FEATURE_UNIT: {
//dump_usb_feature_unit_descriptor(data);
usb_feature_unit_descr *fu = (usb_feature_unit_descr*)data;
uint32 mask=0;
DPRINTF((ID "Feature Unit->source_id: 0x%X, controls = 0x%X\n", fu->source_id, fu->controls));
} break;
case AC_PROCESSING_UNIT:
case AC_EXTENSION_UNIT:
//dump_data(&data[0], length);
break;
}
}
}
}
if(ii->descr->interface_subclass != AC_AUDIOSTREAMING) continue;
dump_usb_class_120(ii->endpoint, ii->endpoint_count,
(int8**)ii->generic, ii->generic_count);
if(ii->endpoint_count != 1) continue;
/* ignore input endpoints */
if(ii->endpoint[0].descr->endpoint_address & 0x80) continue;
//dprintf(ID "found an AudioStreaming output interface @ %d/%d..\n", ifc, alt);
/* does it support 2 channel, 16 bit audio? */
for(i = 0; i < ii->generic_count; i++){
uint8 param_block[3];
size_t written=10;
status_t status = 10;
usb_format_type_descr *ft = (usb_format_type_descr*) ii->generic[i];
//dprintf(ID "type: 0x%X(0x%X), subtype: 0x%X(0x%X), length: %d(%d), num_channels: %d(%d), subframe_size: %d(%d), sample_freq_type: %d(%d)\n",
// ft->type, AC_CS_INTERFACE, ft->subtype, AC_FORMAT_TYPE,
// ft->length, sizeof(usb_format_type_descr),
// ft->num_channels, 2,ft->subframe_size, 2,ft->sample_freq_type, 0);
if(ft->type != AC_CS_INTERFACE) continue;
if(ft->subtype != AC_FORMAT_TYPE) continue;
if(ft->length < sizeof(usb_format_type_descr)) continue;
if(ft->num_channels != 2) continue;
if(ft->subframe_size != 2) continue;
#if 0
status_t (*send_request)(const usb_device *d,
uint8 request_type, uint8 request,
uint16 value, uint16 index, uint16 length,
void *data, size_t data_len, size_t *actual_len);
#endif
// TRY SET:
// page 96, usb audio spec 1.0
sample_rate = 44100;
param_block[0] = sample_rate;
param_block[1] = sample_rate >> 8;
param_block[2] = sample_rate >> 16;
status = usb->send_request(dev,
USB_REQTYPE_CLASS|USB_REQTYPE_ENDPOINT_OUT,
AC_SET_CUR,
1 << 8, // sampling freq control
ii->endpoint[0].descr->endpoint_address, /* endpoint */
3,
param_block, 3, &written);
dprintf(ID "%d bytes written, 0x%.2X%.2X%.2X back (status: %d).\n",
written, param_block[0],param_block[1],param_block[2], status);
#if 0 // LINUX
usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN)
SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) {
#endif
#if 0
dump_endpoint_info(ii->endpoint[0].descr->endpoint_address, &ii->endpoint[0]);
// TRY SET:
// page 96, usb audio spec 1.0
param_block[0] = 0x44;
param_block[1] = 0xAC;
param_block[2] = 0x00;
usb->send_request(dev,
34 /* 00100010b */, AC_SET_CUR,
0x0100, /* SAMPLING_FREQ_CONTROL, high-byte */
ii->endpoint[0].descr->endpoint_address, /* endpoint */
3,
param_block, 3, &written);
//tic
//if(ft->sample_freq_type != 0) continue;
dprintf(ID "sample freq: 0x%X%X%X/0x%X%X%X\n",
ft->lower_sample_freq[0],
ft->lower_sample_freq[1],
ft->lower_sample_freq[2],
ft->upper_sample_freq[0],
ft->upper_sample_freq[1],
ft->upper_sample_freq[2]);
#endif
dprintf(ID "found a 2ch, 16bit isoch output (ifc=%d, alt=%d, mp=%d)\n",
ifc, alt, ii->endpoint[0].descr->max_packet_size);
goto got_one;
}
}
}
fail:
put_number(num);
if(ad) free(ad);
return NULL;
got_one:
if((ad = (audiodev *) malloc(sizeof(audiodev))) == NULL) goto fail;
ad->dev = dev;
ad->number = num;
ad->open = 0;
if((st = usb->set_alt_interface(dev, ii)) != B_OK) {
dprintf(ID "set_alt_interface(0) returns %d\n", st);
goto fail;
}
if((st = usb->set_configuration(dev,conf)) != B_OK) {
dprintf(ID "set_configuration() returns %d\n", st);
goto fail;
}
out = ii->endpoint[0].handle;
DPRINTF((ID "added %p (out=%p) (/dev/%s%d)\n", ad, out, basename, num));
if((st = usb->set_pipe_policy(out, NB, TS, SAMPLE_SIZE)) != B_OK){
dprintf(ID "set_pipe_policy(out) returns %d\n", st);
goto fail;
}
init_iso_channel(&ad->out_channel, out, qs_out, FALSE);
start_iso_channel_out(&ad->out_channel);
/* add it to the list of devices so it will be published, etc */
acquire_sem(dev_list_lock);
ad->next = device_list;
device_list = ad;
device_count++;
release_sem(dev_list_lock);
return ad;
}
static void
remove_device(audiodev *ad)
{
uninit_iso_channel(&ad->out_channel);
put_number(ad->number);
free(ad);
}
static status_t
device_added(const usb_device *dev, void **cookie)
{
const usb_configuration_info *conf;
audiodev *ad;
int i;
DPRINTF((ID "device_added(%p,...)\n", dev));
if(conf = usb->get_nth_configuration(dev, 0)) {
if((ad = add_device(dev, conf)) != NULL){
*cookie = (void*) ad;
return B_OK;
}
}
return B_ERROR;
}
static status_t
device_removed(void *cookie)
{
audiodev *ad = (audiodev *) cookie;
int i;
DPRINTF((ID "device_removed(%p)\n",ad));
acquire_sem(dev_list_lock);
/* mark it as inactive and encourage IO to finish */
ad->out_channel.active = 0;
delete_sem(ad->out_channel.num_available_packets);
/* remove it from the list of devices */
if(ad == device_list){
device_list = ad->next;
} else {
audiodev *n;
for(n = device_list; n; n = n->next){
if(n->next == ad){
n->next = ad->next;
break;
}
}
}
device_count--;
/* tear it down if it's not open --
otherwise the last device_free() will handle it */
if(ad->open == 0){
remove_device(ad);
} else {
DPRINTF((ID "device /dev/%s%d still open -- marked for removal\n",
basename,ad->number));
}
release_sem(dev_list_lock);
return B_OK;
}
/* Device Hooks -----------------------------------------------------------
**
** Here we implement the posixy driver hooks (open/close/read/write/ioctl)
*/
static status_t
device_open(const char *dname, uint32 flags, void **cookie)
{
audiodev *ad;
int n;
n = atoi(dname + strlen(basename));
DPRINTF((ID "device_open(\"%s\",%d,...)\n",dname,flags));
acquire_sem(dev_list_lock);
for(ad = device_list; ad; ad = ad->next){
if(ad->number == n){
if(ad->out_channel.active) {
ad->open++;
// set default values
memcpy(&ad->setup, &usb_sound_setup, sizeof(struct sound_setup));
ad->write_time = 0;
ad->write_total = 0;
*cookie = ad;
release_sem(dev_list_lock);
return B_OK;
} else {
dprintf(ID "device is going offline. cannot open\n");
}
}
}
release_sem(dev_list_lock);
return B_ERROR;
}
static status_t
device_close (void *cookie)
{
audiodev *ad = (audiodev *)cookie;
if(ad->out_channel.active) stop_iso_channel(&ad->out_channel);
DPRINTF((ID "device_close() name = \"%s%d\"\n",basename,ad->number));
return B_OK;
}
static status_t
device_free(void *cookie)
{
audiodev *ad = (audiodev *) cookie;
DPRINTF((ID "device_free() name = \"%s%d\"\n",basename,ad->number));
acquire_sem(dev_list_lock);
ad->open--;
if((ad->open == 0) && (ad->out_channel.active == 0)) remove_device(ad);
release_sem(dev_list_lock);
return B_OK;
}
static status_t
device_read(void *cookie, off_t pos, void *buf, size_t *count)
{
return B_ERROR;
}
static status_t
device_write(void *cookie, off_t pos, const void *buf, size_t *count)
{
audiodev* ad = (audiodev*) cookie;
iso_channel* channel = &ad->out_channel;
status_t st;
#if 0
DPRINTF((ID "device_write(%p,%Ld,0x%x,%d) name = \"%s%d\"\n",
cookie, pos, buf, *count, basename, ad->number));
#endif
if(channel->remain == 0) {
st = acquire_sem_etc(channel->num_available_packets, 1, B_RELATIVE_TIMEOUT, 4*1000*1000);
if(st) {
if(st == B_TIMED_OUT) {
dprintf("st = B_TIMED_OUT\n");
*count = 0;
}
return st;
}
if(channel->current_rw == channel->next_to_queue) dprintf("e");
channel->remain = channel->current_rw->buffer_size;
channel->buf_ptr = channel->current_rw->buffer;
}
if(channel->remain >= *count) {
memcpy(channel->buf_ptr, buf, *count);
channel->remain -= *count;
channel->buf_ptr += *count;
} else {
memcpy(channel->buf_ptr, buf, channel->remain);
*count = channel->remain;
channel->remain = 0;
channel->buf_ptr = NULL;
}
ad->write_total += *count;
return B_OK;
}
static status_t
device_control(void *cookie, uint32 msg, void *data, size_t len)
{
status_t err = B_BAD_VALUE;
audiodev *ad = (audiodev *)cookie;
// only support old-style drivers.
switch (msg) {
case SOUND_GET_PARAMS: {
sound_setup *setup = (sound_setup *)data;
memcpy(setup, &ad->setup, sizeof(struct sound_setup));
err = B_OK;
} break;
case SOUND_SET_PARAMS: {
sound_setup *setup = (sound_setup *)data;
memcpy(&ad->setup, setup, sizeof(struct sound_setup));
err = B_OK;
} break;
case SOUND_SET_PLAYBACK_COMPLETION_SEM: {
ad->playback_sem = *(sem_id *)data;
} break;
case SOUND_UNSAFE_WRITE: {
audio_buffer_header *header = (audio_buffer_header *)data;
int32 data_length = header->reserved_1 - sizeof(*header);
int16 *data_address = (int16 *)(header + 1);
iso_channel* ch = &ad->out_channel;
//header->time = (ch->current_time - ch->remain * 2500LL / 441
// + NB * TS * 1000);
//header->sample_clock = (ch->current_rw->rw_count - ch->remain) * 2500LL / 441;
header->sample_clock = ad->write_total*1000/48;// * 10000/441;
header->time = header->sample_clock;
device_write(cookie, 0, data_address, &data_length);
release_sem(ad->playback_sem);
err = B_OK;
} break;
case 10012:
case 10013:
*(int32*)data = QS_OUT;
err = B_OK;
break;
default: {
dprintf(ID "ioctl() - unknown msg %d\n", msg);
} break;
}
return err;
}
/* Driver Hooks ---------------------------------------------------------
**
** These functions provide the glue used by DevFS to load/unload
** the driver and also handle registering with the USB bus manager
** to receive device added and removed events
*/
static usb_notify_hooks notify_hooks =
{
&device_added,
&device_removed
};
usb_support_descriptor supported_devices[] =
{
{ AC_AUDIO, 0, 0, 0, 0}
};
_EXPORT status_t
init_hardware(void)
{
return B_OK;
}
_EXPORT status_t
init_driver(void)
{
int i;
DPRINTF((ID "init_driver(), built %s %s\n", __DATE__, __TIME__));
#if DEBUG_DRIVER && !defined(__HAIKU__)
if(load_driver_symbols(drivername) == B_OK) {
DPRINTF((ID "loaded symbols\n"));
} else {
DPRINTF((ID "no symbols for you!\n"));
}
#endif
if(get_module(usb_name,(module_info**) &usb) != B_OK){
dprintf(ID "cannot get module \"%s\"\n",usb_name);
return B_ERROR;
}
if((dev_list_lock = create_sem(1,"dev_list_lock")) < 0){
put_module(usb_name);
return dev_list_lock;
}
usb->register_driver(drivername, supported_devices, 1, NULL);
usb->install_notify(drivername, &notify_hooks);
return B_OK;
}
_EXPORT void
uninit_driver(void)
{
int i;
DPRINTF((ID "uninit_driver()\n"));
usb->uninstall_notify(drivername);
delete_sem(dev_list_lock);
put_module(usb_name);
if(device_names){
for(i=0;device_names[i];i++) free(device_names[i]);
free(device_names);
}
}
_EXPORT const char**
publish_devices()
{
audiodev *ad;
int i;
DPRINTF((ID "publish_devices()\n"));
if(device_names){
for(i=0;device_names[i];i++) free((char *) device_names[i]);
free(device_names);
}
acquire_sem(dev_list_lock);
device_names = (char **) malloc(sizeof(char*) * (device_count + 1));
if(device_names){
for(i = 0, ad = device_list; ad; ad = ad->next){
if(ad->out_channel.active){
if(device_names[i] = (char *) malloc(strlen(basename) + 4)){
sprintf(device_names[i],"%s%d",basename,ad->number);
DPRINTF((ID "publishing: \"/dev/%s\"\n",device_names[i]));
i++;
}
}
}
device_names[i] = NULL;
}
release_sem(dev_list_lock);
return (const char **) device_names;
}
static device_hooks DeviceHooks = {
device_open,
device_close,
device_free,
device_control,
device_read,
device_write,
};
_EXPORT device_hooks*
find_device(const char* name)
{
return &DeviceHooks;
}
@@ -0,0 +1,28 @@
##
## Driver for USB Audio Device Class devices.
## Copyright (c) 2009,10,12 S.Zharski <[email protected]>
## Distributed under the terms of the MIT license.
##
## trace [on|off] - activate additional tracing.
## default value: off
trace on
## logfile [full path to private log file]
## default path value: /var/log/usb_asix.log
## if disabled - all output goes to syslog
logfile /boot/home/usb_audio.log
## reset_logfile [on|off] - truncate private log file on driver/system restart
## default value: off
##
# reset_logfile off
## add_timestamp [on|off] - add time of writing the string in private log file.
## default value: on
##
# add_timestamp off