Publishing USB Audio driver from my dev.branch on the old SVN repo.
This commit is contained in:
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, ¬ifyHooks);
|
||||
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, ¬ify_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
|
||||
Reference in New Issue
Block a user