Added a highly experimental USB audio driver on behalf of Mikael Jansson (a.k.a. tic). This should probably be converted to the multi_audio API, among other changes, in future.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9380 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -3,4 +3,5 @@ SubDir OBOS_TOP src add-ons kernel drivers audio ;
|
||||
SubInclude OBOS_TOP src add-ons kernel drivers audio ac97 ;
|
||||
SubInclude OBOS_TOP src add-ons kernel drivers audio echo ;
|
||||
SubInclude OBOS_TOP src add-ons kernel drivers audio emuxki ;
|
||||
SubInclude OBOS_TOP src add-ons kernel drivers audio usb_audio ;
|
||||
SubInclude OBOS_TOP src add-ons kernel drivers audio module_driver ;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
SubDir OBOS_TOP src add-ons kernel drivers audio usb_audio ;
|
||||
|
||||
R5KernelAddon usb_audio : kernel drivers bin :
|
||||
usb_audio.c
|
||||
USB_audio_utils.c
|
||||
;
|
||||
|
||||
# Link to kernel/drivers/dev/audio/multi
|
||||
{
|
||||
local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev audio old ] ;
|
||||
local instDriver = <kernel!drivers!dev!audio!old>usb_audio ;
|
||||
MakeLocate $(instDriver) : $(dir) ;
|
||||
RelSymLink $(instDriver) : usb_audio ;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
|
||||
File: MediaDefs.h
|
||||
|
||||
Copyright 1995-1999, Be Incorporated
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
|
||||
******************************************************************************/
|
||||
#ifndef _R3_MEDIA_DEFS_H
|
||||
#define _R3_MEDIA_DEFS_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
#include <Errors.h>
|
||||
#if defined(__cplusplus)
|
||||
#include <MediaDefs.h>
|
||||
#else
|
||||
/* values for byte_ordering */
|
||||
enum { B_BIG_ENDIAN, B_LITTLE_ENDIAN };
|
||||
|
||||
/* values for sample_format */
|
||||
enum {
|
||||
B_UNDEFINED_SAMPLES,
|
||||
B_LINEAR_SAMPLES,
|
||||
B_FLOAT_SAMPLES,
|
||||
B_MULAW_SAMPLES
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Buffer header for audio server */
|
||||
|
||||
typedef struct audio_buffer_header {
|
||||
int32 buffer_number;
|
||||
int32 subscriber_count;
|
||||
bigtime_t time;
|
||||
int32 reserved_1;
|
||||
int32 reserved_2;
|
||||
bigtime_t sample_clock;
|
||||
} audio_buffer_header;
|
||||
|
||||
|
||||
#define B_MEDIA_LEVEL B_REAL_TIME_PRIORITY
|
||||
|
||||
#define B_NO_CHANGE (-1)
|
||||
|
||||
/* ================
|
||||
Subscriber IDs and special values
|
||||
================ */
|
||||
|
||||
#define B_NO_SUBSCRIBER_ID ((subscriber_id)-1)
|
||||
#define B_NO_SUBSCRIBER_NAME "not subscribed"
|
||||
|
||||
#define B_SHARED_SUBSCRIBER_ID ((subscriber_id)-2)
|
||||
#define B_SHARED_SUBSCRIBER_NAME "shared subscriber"
|
||||
|
||||
/* ================
|
||||
Values for sound files and audio streams
|
||||
================ */
|
||||
|
||||
|
||||
/* Audio device codes for BAudioSubscriber's
|
||||
* Get/SetVolume() and EnableDevice() calls
|
||||
*/
|
||||
enum {
|
||||
B_CD_THROUGH=0,
|
||||
B_LINE_IN_THROUGH,
|
||||
B_ADC_IN,
|
||||
B_LOOPBACK,
|
||||
B_DAC_OUT,
|
||||
B_MASTER_OUT,
|
||||
B_SPEAKER_OUT,
|
||||
B_SOUND_DEVICE_END
|
||||
};
|
||||
|
||||
/* ADC input codes */
|
||||
enum {
|
||||
B_CD_IN,
|
||||
B_LINE_IN,
|
||||
B_MIC_IN
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
B_DAC_STREAM = 2354,
|
||||
B_ADC_STREAM
|
||||
};
|
||||
|
||||
#endif
|
||||
/* #ifndef _R3_MEDIA_DEFS_H*/
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef __USB_AUDIO_SPEC_H__
|
||||
#define __USB_AUDIO_SPEC_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Audio Device Class Codes
|
||||
*/
|
||||
|
||||
// Audio Control descriptors
|
||||
|
||||
// Audio Control Interface descriptors
|
||||
#define UAS_AUDIO 0x01
|
||||
|
||||
#define UAS_SUBCLASS_UNDEFINED 0x00
|
||||
#define UAS_AUDIOCONTROL 0x01
|
||||
#define UAS_AUDIOSTREAMING 0x02
|
||||
#define UAS_MIDISTREAMING 0x03
|
||||
|
||||
#define UAS_PR_PROTOCOL_UNDEFINED 0x00
|
||||
|
||||
// Table A-4, page 99
|
||||
#define UAS_CS_UNDEFINED 0x20
|
||||
#define UAS_CS_DEVICE 0x21
|
||||
#define UAS_CS_CONFIGURATION 0x22
|
||||
#define UAS_CS_STRING 0x23
|
||||
#define UAS_CS_INTERFACE 0x24
|
||||
#define UAS_CS_ENDPOINT 0x25
|
||||
|
||||
// Table A-5, page 100
|
||||
#define UAS_AC_DESCRIPTOR_UNDEFINED 0x00
|
||||
#define UAS_HEADER 0x01
|
||||
#define UAS_INPUT_TERMINAL 0x02
|
||||
#define UAS_OUTPUT_TERMINAL 0x03
|
||||
#define UAS_MIXER_UNIT 0x04
|
||||
#define UAS_SELECTOR_UNIT 0x05
|
||||
#define UAS_FEATURE_UNIT 0x06
|
||||
#define UAS_PROCESSING_UNIT 0x07
|
||||
#define UAS_EXTENSION_UNIT 0x08
|
||||
|
||||
// Table A-6, page 100
|
||||
#define UAS_AS_DESCRIPTOR_UNDEFINED 0x00
|
||||
#define UAS_AS_GENERAL 0x01
|
||||
#define UAS_FORMAT_TYPE 0x02
|
||||
#define UAS_FORMAT_SPECIFIC 0x03
|
||||
|
||||
// Audio Class-Specific Endpoint Descriptor Subtypes
|
||||
// Table A-8, page 101
|
||||
#define UAS_DESCRIPTOR_UNDEFINED 0x00
|
||||
#define UAS_EP_GENERAL 0x01
|
||||
|
||||
// Audio Class-Specific Request Codes
|
||||
#define UAS_REQUEST_CODE_UNDEFINED 0x00
|
||||
#define UAS_SET_CUR 0x01
|
||||
#define UAS_GET_CUR 0x81
|
||||
#define UAS_SET_MIN 0x02
|
||||
#define UAS_GET_MIN 0x82
|
||||
#define UAS_SET_MAX 0x0x
|
||||
#define UAS_GET_MAX 0x83
|
||||
#define UAS_SET_RES 0x04
|
||||
#define UAS_GET_RES 0x84
|
||||
#define UAS_SET_MEM 0x05
|
||||
#define UAS_GET_MEM 0x85
|
||||
#define UAS_GET_STAT 0xff
|
||||
|
||||
#define UAS_bmReqType_00100001B 0x21
|
||||
#define UAS_bmReqType_00100010B 0x22
|
||||
#define UAS_bmReqType_10100001B 0xa1
|
||||
#define UAS_bmReqType_10100010B 0xa2
|
||||
|
||||
// Table A-10.2, page 102
|
||||
#define UAS_FU_CONTROL_UNDEFINED 0x00
|
||||
#define UAS_MUTE_CONTROL 0x01
|
||||
#define UAS_VOLUME_CONTROL 0x02
|
||||
#define UAS_BASS_CONTROL 0x03
|
||||
#define UAS_MID_CONTROL 0x04
|
||||
#define UAS_TREBLE_CONTROL 0x05
|
||||
#define UAS_GRAPHIC_EQUALIZER_CONTROL 0x06
|
||||
#define UAS_AUTOMATIC_GAIN_CONTROL 0x07
|
||||
#define UAS_DELAY_CONTROL 0x08
|
||||
#define UAS_BASS_BOOST_CONTROL 0x09
|
||||
#define UAS_LOUDNESS_CONTROL 0x0a
|
||||
|
||||
// Endpoint control selectors
|
||||
// Table A-10.5, page 104
|
||||
#define UAS_EP_CONTROL_UNDEFINED 0x00
|
||||
#define UAS_SAMPLING_FREQ_CONTROL 0x01
|
||||
#define UAS_PITCH_CONTROL 0x02
|
||||
|
||||
// header descriptor
|
||||
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
|
||||
uint16 total_length; //
|
||||
uint8 in_collection; // # of audiostreaming units
|
||||
uint8 interface_numbers[1]; // or more
|
||||
} _PACKED usb_audiocontrol_header_descriptor;
|
||||
|
||||
// input terminal descriptor
|
||||
// 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; //
|
||||
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
|
||||
} _PACKED usb_input_terminal_descriptor;
|
||||
|
||||
// output terminal descriptor
|
||||
// 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; //
|
||||
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
|
||||
} _PACKED usb_output_terminal_descriptor;
|
||||
|
||||
// selector unit descriptor
|
||||
// Table 4-6, page 43
|
||||
typedef struct {
|
||||
uint8 length;
|
||||
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...
|
||||
} _PACKED usb_selector_unit_descriptor;
|
||||
|
||||
// feature unit descriptor
|
||||
// Table 4-7, page 43
|
||||
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
|
||||
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
|
||||
} _PACKED usb_feature_unit_descriptor;
|
||||
|
||||
|
||||
// Audio Streaming (as) descriptors
|
||||
|
||||
|
||||
// Class-specific AS interface descriptor
|
||||
// Table 4-19, page 60
|
||||
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 delay; // delay in # of frames
|
||||
uint16 format_tag; // wFormatTag, 0x0001 = PCM
|
||||
} _PACKED usb_as_interface_descriptor;
|
||||
|
||||
// Class-specific As Isochronous Audio Data Endpoint descriptor
|
||||
// Table 4-21, page 62
|
||||
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
|
||||
} _PACKED usb_as_cs_endpoint_descriptor;
|
||||
|
||||
|
||||
|
||||
/* 3-byte integer */
|
||||
typedef struct {
|
||||
uint8 data[3];
|
||||
} _PACKED usb_triplet;
|
||||
|
||||
// and
|
||||
|
||||
/*
|
||||
* Audio data formats spec
|
||||
*/
|
||||
|
||||
// constants
|
||||
// Table A-1, page 29, wFormatTag
|
||||
// Audio Data Format Type I codes
|
||||
#define UAF_TYPE_I_UNDEFINED 0x0000
|
||||
#define UAF_PCM 0x0001
|
||||
#define UAF_PCM8 0x0002
|
||||
#define UAF_IEEE_FLOAT 0x0003
|
||||
#define UAF_ALAW 0x0004
|
||||
#define UAF_MULAW 0x0005
|
||||
|
||||
// Table A-4, page 30
|
||||
// Format Type Codes
|
||||
#define UAF_FORMAT_TYPE_UNDEFINED 0x00
|
||||
#define UAF_FORMAT_TYPE_I 0x01
|
||||
#define UAF_FORMAT_TYPE_II 0x02
|
||||
#define UAF_FORMAT_TYPE_III 0x03 // Not _II again I guess..
|
||||
|
||||
// Table 2-2 and 2-3, page 10
|
||||
typedef struct {
|
||||
uint8 lower_sam_freq[3];
|
||||
uint8 upper_sam_freq[3];
|
||||
} _PACKED usb_audio_continuous_freq_descr;
|
||||
|
||||
typedef struct {
|
||||
uint8 sam_freq[1][3];
|
||||
} _PACKED usb_audio_discrete_freq_descr;
|
||||
|
||||
typedef union {
|
||||
usb_audio_continuous_freq_descr cont;
|
||||
usb_audio_discrete_freq_descr discr;
|
||||
} _PACKED usb_audio_sam_freq_descr;
|
||||
|
||||
// Table 2-1, page 10
|
||||
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_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
|
||||
usb_audio_sam_freq_descr sf; // union
|
||||
// uint8 sam_freq[12 * 3];
|
||||
} _PACKED usb_type_I_format_descriptor;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* USB audio spec utils
|
||||
*/
|
||||
|
||||
#include <OS.h>
|
||||
#include <Drivers.h>
|
||||
#include <KernelExport.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <byteorder.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;
|
||||
uint32 res;
|
||||
res = (msb << 16) | (hsb << 8) | (lsb);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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,165 @@
|
||||
/* Copyright 2000 Be Incorporated. All Rights Reserved.
|
||||
** This file may be used under the terms of the Be Sample Code
|
||||
** License.
|
||||
*/
|
||||
|
||||
#ifndef _USB_AUDIO_H_
|
||||
#define _USB_AUDIO_H_
|
||||
|
||||
/* See: Universal Serial Bus Device Class Definition for Audio Devices, Release 1.0 */
|
||||
|
||||
|
||||
/* A.1 Audio Interface Class Code */
|
||||
#define AC_AUDIO 0x01
|
||||
|
||||
/* A.2 Audio Interface Subclass Codes */
|
||||
#define AC_SUBCLASS_UNDEFINED 0x00
|
||||
#define AC_AUDIOCONTROL 0x01
|
||||
#define AC_AUDIOSTREAMING 0x02
|
||||
#define AC_MIDISTREAMING 0x03
|
||||
|
||||
/* A.3 Audio Interface Protocol Codes */
|
||||
#define AC_PR_PROTOCOL_UNDEFINED 0x00
|
||||
|
||||
/* A.4 Audio Class-Specific Descriptor Types */
|
||||
#define AC_CS_UNDEFINED 0x20
|
||||
#define AC_CS_DEVICE 0x21
|
||||
#define AC_CS_CONFIGURATION 0x22
|
||||
#define AC_CS_STRING 0x23
|
||||
#define AC_CS_INTERFACE 0x24
|
||||
#define AC_CS_ENDPOINT 0x25
|
||||
|
||||
/* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */
|
||||
#define AC_AC_DESCRIPTOR_UNDEFINED 0x00
|
||||
#define AC_HEADER 0x01
|
||||
#define AC_INPUT_TERMINAL 0x02
|
||||
#define AC_OUTPUT_TERMINAL 0x03
|
||||
#define AC_MIXER_UNIT 0x04
|
||||
#define AC_SELECTOR_UNIT 0x05
|
||||
#define AC_FEATURE_UNIT 0x06
|
||||
#define AC_PROCESSING_UNIT 0x07
|
||||
#define AC_EXTENSION_UNIT 0x08
|
||||
|
||||
/* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
|
||||
#define AC_AS_DESCRIPTOR_UNDEFINED 0x00
|
||||
#define AC_AS_GENERAL 0x01
|
||||
#define AC_FORMAT_TYPE 0x02
|
||||
#define AC_FORMAT_SPECIFIC 0x03
|
||||
|
||||
/* A.7 Processing Unit Process Types */
|
||||
#define AC_PROCESS_UNDEFINED 0x00
|
||||
#define AC_UPDOWNMIX_PROCESS 0x01
|
||||
#define AC_DOLBY_PROLOGIC_PROCESS 0x02
|
||||
#define AC_3D_STEREO_EXTENDER_PROCESS 0x03
|
||||
#define AC_REVERBERATION_PROCESS 0x04
|
||||
#define AC_CHORUS_PROCESS 0x05
|
||||
#define AC_DYN_RANGE_COMP_PROCESS 0x07
|
||||
|
||||
/* A.8 Audio Class-Specific Endpoint Descriptor Subtypes */
|
||||
#define AC_DESCRIPTOR_UNDEFINED 0x00
|
||||
#define AC_EP_GENERAL 0x01
|
||||
|
||||
/* A.9 Audio Class-Specific Request Codes */
|
||||
#define AC_REQUEST_CODE_UNDEFINED 0x00
|
||||
#define AC_SET_CUR 0x01
|
||||
#define AC_GET_CUR 0x81
|
||||
#define AC_SET_MIN 0x02
|
||||
#define AC_GET_MIN 0x82
|
||||
#define AC_SET_MAX 0x03
|
||||
#define AC_GET_MAX 0x83
|
||||
#define AC_SET_RES 0x04
|
||||
#define AC_GET_RES 0x84
|
||||
#define AC_SET_MEM 0x05
|
||||
#define AC_GET_MEM 0x85
|
||||
#define AC_GET_STAT 0xFF
|
||||
|
||||
/* A.10.1 Terminal Control Selectors */
|
||||
#define AC_TE_CONTROL_UNDEFINED 0x00
|
||||
#define AC_COPY_PROTECT_CONTROL 0x01
|
||||
|
||||
/* A.10.2 Feature Unit Control Selectors */
|
||||
#define AC_FU_CONTROL_UNDEFINED 0x00
|
||||
#define AC_MUTE_CONTROL 0x01
|
||||
#define AC_VOLUME_CONTROL 0x02
|
||||
#define AC_BASS_CONTROL 0x03
|
||||
#define AC_MID_CONTROL 0x04
|
||||
#define AC_TREBLE_CONTROL 0x05
|
||||
#define AC_GRAPHIC_EQUALIZER_CONTROL 0x06
|
||||
#define AC_AUTOMATIC_GAIN_CONTROL 0x07
|
||||
#define AC_DELAY_CONTROL 0x08
|
||||
#define AC_BASS_BOOST_CONTROL 0x09
|
||||
#define AC_LOUDNESS_CONTROL 0x0A
|
||||
|
||||
/* A.10.3.1 Up/Down-mix Processing Unit Control Selectors */
|
||||
#define AC_UD_CONTROL_UNDEFINED 0x00
|
||||
#define AC_UD_ENABLED_CONTROL 0x01
|
||||
#define AC_UD_MODE_SELECT_CONTROL 0x02
|
||||
|
||||
/* A.10.3.2 Dolby Prologic(tm) Processing Unit Control Selectors */
|
||||
#define AC_DP_CONTROL_UNDEFINED 0x00
|
||||
#define AC_DP_ENABLE_CONTROL 0x01
|
||||
#define AC_DP_MODE_SELECT_CONTROL 0x02
|
||||
|
||||
/* A.10.3.3 3D Stereo Extender Processing Unit Control Selectors */
|
||||
#define AC_3D_CONTROL_UNDEFINED 0x00
|
||||
#define AC_3D_ENABLED_CONTROL 0x01
|
||||
#define AC_3D_SPACIOUSNESS_CONTROL 0x03
|
||||
|
||||
/* A.10.3.4 Reverberation Processing Unit Control Selectors */
|
||||
#define AC_RV_CONTROL_UNDEFINED 0x00
|
||||
#define AC_RV_ENABLE_CONTROL 0x01
|
||||
#define AC_RV_REVERB_LEVEL_CONTROL 0x02
|
||||
#define AC_RV_REVERB_TIME_CONTROL 0x03
|
||||
#define AC_RV_REVERB_FEEDBACK_CONTROL 0x04
|
||||
|
||||
/* A.10.3.5 Chorus Processing Unit Control Selectors */
|
||||
#define AC_CH_CONTROL_UNDEFINED 0x00
|
||||
#define AC_CH_ENABLE_CONTROL 0x01
|
||||
#define AC_CHORUS_LEVEL_CONTROL 0x02
|
||||
#define AC_CHORUS_RATE_CONTROL 0x03
|
||||
#define AC_CHORUS_DETH_CONTROL 0x04
|
||||
|
||||
/* A.10.3.6 Dynamic Range Compressor Processing Unit Control Selectors */
|
||||
#define AC_DR_CONTROL_UNDEFINED 0x00
|
||||
#define AC_DR_ENABLE_CONTROL 0x01
|
||||
#define AC_COMPRESSION_RATE_CONTROL 0x02
|
||||
#define AC_MAXAMPL_CONTROL 0x03
|
||||
#define AC_THRESHOLD_CONTROL 0x04
|
||||
#define AC_ATTACK_TIME 0x05
|
||||
#define AC_RELEASE_TIME 0x06
|
||||
|
||||
/* A.10.4 Extension Unit Control Selectors */
|
||||
#define AC_XU_CONTROL_UNDEFINED 0x00
|
||||
#define AC_XU_ENABLE_CONTROL 0x01
|
||||
|
||||
/* A.10.5 Endpoint Control Selectors */
|
||||
#define AC_EP_CONTROL_UNDEFINED 0x00
|
||||
#define AC_SAMPLING_FREQ_CONTROL 0x01
|
||||
#define AC_PITCH_CONTROL 0x02
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 length;
|
||||
uint8 type;
|
||||
uint8 subtype;
|
||||
uint8 unit_id;
|
||||
uint8 source_id;
|
||||
uint8 control_size;
|
||||
uint16 controls[0];
|
||||
} _PACKED usb_feature_unit_descr;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 length;
|
||||
uint8 type;
|
||||
uint8 subtype;
|
||||
uint8 format_type;
|
||||
uint8 num_channels;
|
||||
uint8 subframe_size;
|
||||
uint8 bit_resolution;
|
||||
uint8 sample_freq_type;
|
||||
uint8 lower_sample_freq[3];
|
||||
uint8 upper_sample_freq[3];
|
||||
} _PACKED usb_format_type_descr;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,151 @@
|
||||
/* audio_driver.h */
|
||||
/* Jon Watte 19971223 */
|
||||
/* Interface to drivers found in /dev/audio */
|
||||
/* Devices found in /dev/audio/old follow a different API! */
|
||||
|
||||
/*
|
||||
Copyright 1999, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
#if !defined(_AUDIO_DRIVER_H)
|
||||
#define _AUDIO_DRIVER_H
|
||||
|
||||
#if !defined(_SUPPORT_DEFS_H)
|
||||
#include <SupportDefs.h>
|
||||
#endif /* _SUPPORT_DEFS_H */
|
||||
#if !defined(_DRIVERS_H)
|
||||
#include <Drivers.h>
|
||||
#endif /* _DRIVERS_H */
|
||||
|
||||
enum {
|
||||
/* arg = ptr to struct audio_format */
|
||||
B_AUDIO_GET_AUDIO_FORMAT = B_AUDIO_DRIVER_BASE,
|
||||
B_AUDIO_SET_AUDIO_FORMAT,
|
||||
/* arg = ptr to float[4] */
|
||||
B_AUDIO_GET_PREFERRED_SAMPLE_RATES,
|
||||
/* arg = ptr to struct audio_routing_cmd */
|
||||
B_ROUTING_GET_VALUES,
|
||||
B_ROUTING_SET_VALUES,
|
||||
/* arg = ptr to struct audio_routing_cmd */
|
||||
B_MIXER_GET_VALUES,
|
||||
B_MIXER_SET_VALUES,
|
||||
/* arg = ptr to struct audio_timing */
|
||||
B_AUDIO_GET_TIMING /* used to be SV_SECRET_HANDSHAKE (10100) */
|
||||
};
|
||||
|
||||
/* this is the definition of what the audio driver can do for you */
|
||||
typedef struct audio_format {
|
||||
float sample_rate; /* ~4000 - ~48000, maybe more */
|
||||
int32 channels; /* 1 or 2 */
|
||||
int32 format; /* 0x11 (uchar), 0x2 (short) or 0x24 (float) */
|
||||
int32 big_endian; /* 0 for little endian, 1 for big endian */
|
||||
size_t buf_header; /* typically 0 or 16 */
|
||||
size_t play_buf_size; /* size of playback buffer (latency) */
|
||||
size_t rec_buf_size; /* size of record buffer (latency) */
|
||||
} audio_format;
|
||||
|
||||
/* when buffer header is in effect, this is what gets read before data */
|
||||
typedef struct audio_buf_header {
|
||||
bigtime_t capture_time;
|
||||
uint32 capture_size;
|
||||
float sample_rate;
|
||||
} audio_buf_header;
|
||||
|
||||
/* the mux devices use these records */
|
||||
typedef struct audio_routing {
|
||||
int32 selector; /* for GET, these need to be filled in! */
|
||||
int32 value;
|
||||
} audio_routing;
|
||||
|
||||
/* this is the argument for ioctl() */
|
||||
typedef struct audio_routing_cmd {
|
||||
int32 count;
|
||||
audio_routing* data;
|
||||
} audio_routing_cmd;
|
||||
|
||||
typedef struct {
|
||||
bigtime_t wr_time;
|
||||
bigtime_t rd_time;
|
||||
uint32 wr_skipped;
|
||||
uint32 rd_skipped;
|
||||
uint64 wr_total;
|
||||
uint64 rd_total;
|
||||
uint32 _reserved_[6];
|
||||
} audio_timing;
|
||||
|
||||
|
||||
enum { /* selectors for routing */
|
||||
B_AUDIO_INPUT_SELECT,
|
||||
B_AUDIO_MIC_BOOST,
|
||||
B_AUDIO_MIDI_OUTPUT_TO_SYNTH,
|
||||
B_AUDIO_MIDI_INPUT_TO_SYNTH,
|
||||
B_AUDIO_MIDI_OUTPUT_TO_PORT,
|
||||
B_AUDIO_PCM_OUT_POST_3D,
|
||||
B_AUDIO_STEREO_ENHANCEMENT,
|
||||
B_AUDIO_3D_STEREO_ENHANCEMENT,
|
||||
B_AUDIO_BASS_BOOST,
|
||||
B_AUDIO_LOCAL_LOOPBACK,
|
||||
B_AUDIO_REMOTE_LOOPBACK,
|
||||
B_AUDIO_MONO_OUTPUT_FROM_MIC,
|
||||
B_AUDIO_ALTERNATE_MIC,
|
||||
B_AUDIO_ADC_DAC_LOOPBACK
|
||||
};
|
||||
|
||||
enum { /* input MUX source values */
|
||||
B_AUDIO_INPUT_DAC = 1, /* (not in AC97) */
|
||||
B_AUDIO_INPUT_LINE_IN,
|
||||
B_AUDIO_INPUT_CD,
|
||||
B_AUDIO_INPUT_VIDEO,
|
||||
B_AUDIO_INPUT_AUX1, /* synth */
|
||||
B_AUDIO_INPUT_AUX2, /* (not in AC97) */
|
||||
B_AUDIO_INPUT_PHONE,
|
||||
B_AUDIO_INPUT_MIC,
|
||||
B_AUDIO_INPUT_MIX_OUT, /* sum of all outputs */
|
||||
B_AUDIO_INPUT_MONO_OUT
|
||||
};
|
||||
|
||||
/* the mixer devices use these records */
|
||||
typedef struct audio_level {
|
||||
int32 selector; /* for GET, this still needs to be filled in! */
|
||||
uint32 flags;
|
||||
float value; /* in dB */
|
||||
} audio_level;
|
||||
|
||||
/* this is the arg to ioctl() */
|
||||
typedef struct audio_level_cmd {
|
||||
int32 count;
|
||||
audio_level* data;
|
||||
} audio_level_cmd;
|
||||
|
||||
/* bitmask for the flags */
|
||||
#define B_AUDIO_LEVEL_MUTED 1
|
||||
|
||||
enum { /* selectors for levels */
|
||||
B_AUDIO_MIX_ADC_LEFT,
|
||||
B_AUDIO_MIX_ADC_RIGHT,
|
||||
B_AUDIO_MIX_DAC_LEFT,
|
||||
B_AUDIO_MIX_DAC_RIGHT,
|
||||
B_AUDIO_MIX_LINE_IN_LEFT,
|
||||
B_AUDIO_MIX_LINE_IN_RIGHT,
|
||||
B_AUDIO_MIX_CD_LEFT,
|
||||
B_AUDIO_MIX_CD_RIGHT,
|
||||
B_AUDIO_MIX_VIDEO_LEFT,
|
||||
B_AUDIO_MIX_VIDEO_RIGHT,
|
||||
B_AUDIO_MIX_SYNTH_LEFT,
|
||||
B_AUDIO_MIX_SYNTH_RIGHT,
|
||||
B_AUDIO_MIX_AUX_LEFT, /* (not in AC97) */
|
||||
B_AUDIO_MIX_AUX_RIGHT, /* (not in AC97) */
|
||||
B_AUDIO_MIX_PC_BEEP,
|
||||
B_AUDIO_MIX_PHONE,
|
||||
B_AUDIO_MIX_MIC,
|
||||
B_AUDIO_MIX_LINE_OUT_LEFT,
|
||||
B_AUDIO_MIX_LINE_OUT_RIGHT,
|
||||
B_AUDIO_MIX_HEADPHONE_OUT_LEFT,
|
||||
B_AUDIO_MIX_HEADPHONE_OUT_RIGHT,
|
||||
B_AUDIO_MIX_MONO_OUT,
|
||||
B_AUDIO_MIX_LOOPBACK_LEVEL /* (not in AC97) */
|
||||
};
|
||||
|
||||
#endif /* _AUDIO_DRIVER_H */
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/* ++++++++++
|
||||
$Source: /tmp/bonefish/open-beos/current/src/add-ons/kernel/drivers/audio/usb_audio/sound.h,v $
|
||||
$Revision: 1.1 $
|
||||
$Author: nwhitehorn $
|
||||
$Date: 2004/10/16 19:29:53 $
|
||||
|
||||
Data structures and control calls for using the sound driver
|
||||
+++++ */
|
||||
/*
|
||||
Copyright 1999, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
#ifndef _SOUND_H
|
||||
#define _SOUND_H
|
||||
|
||||
#ifndef _DRIVERS_H
|
||||
#include <Drivers.h>
|
||||
#endif
|
||||
|
||||
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
|
||||
@@ -0,0 +1,904 @@
|
||||
/* 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
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user