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:
Nathan Whitehorn
2004-10-16 19:29:53 +00:00
parent c60f83369f
commit 8dbc8c1979
10 changed files with 2076 additions and 0 deletions
+1
View File
@@ -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, &notify_hooks);
return B_OK;
}
_EXPORT void
uninit_driver(void)
{
int i;
DPRINTF((ID "uninit_driver()\n"));
usb->uninstall_notify(drivername);
delete_sem(dev_list_lock);
put_module(usb_name);
if(device_names){
for(i=0;device_names[i];i++) free(device_names[i]);
free(device_names);
}
}
_EXPORT const char**
publish_devices()
{
audiodev *ad;
int i;
DPRINTF((ID "publish_devices()\n"));
if(device_names){
for(i=0;device_names[i];i++) free((char *) device_names[i]);
free(device_names);
}
acquire_sem(dev_list_lock);
device_names = (char **) malloc(sizeof(char*) * (device_count + 1));
if(device_names){
for(i = 0, ad = device_list; ad; ad = ad->next){
if(ad->out_channel.active){
if(device_names[i] = (char *) malloc(strlen(basename) + 4)){
sprintf(device_names[i],"%s%d",basename,ad->number);
DPRINTF((ID "publishing: \"/dev/%s\"\n",device_names[i]));
i++;
}
}
}
device_names[i] = NULL;
}
release_sem(dev_list_lock);
return (const char **) device_names;
}
static device_hooks DeviceHooks = {
device_open,
device_close,
device_free,
device_control,
device_read,
device_write,
};
_EXPORT device_hooks*
find_device(const char* name)
{
return &DeviceHooks;
}