diff --git a/src/add-ons/kernel/drivers/audio/Jamfile b/src/add-ons/kernel/drivers/audio/Jamfile index 016d2c337b..8d2425835f 100644 --- a/src/add-ons/kernel/drivers/audio/Jamfile +++ b/src/add-ons/kernel/drivers/audio/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/Jamfile b/src/add-ons/kernel/drivers/audio/usb_audio/Jamfile new file mode 100644 index 0000000000..f8879ee299 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/Jamfile @@ -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 = usb_audio ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : usb_audio ; +} diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/R3MediaDefs.h b/src/add-ons/kernel/drivers/audio/usb_audio/R3MediaDefs.h new file mode 100644 index 0000000000..814a5c8c03 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/R3MediaDefs.h @@ -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 +#include +#if defined(__cplusplus) + #include +#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*/ diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_spec.h b/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_spec.h new file mode 100644 index 0000000000..1d31175409 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_spec.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 diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_utils.c b/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_utils.c new file mode 100644 index 0000000000..dbc50d7427 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/USB_audio_utils.c @@ -0,0 +1,360 @@ +/* + * USB audio spec utils + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; iinterface_count; i++) { + dprintf("INTERFACE %d, %d alternatitives:\n", i, conf->interface[i].alt_count); + for (j=0; jinterface[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(" \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; ibcd_release_no); + dprintf(" total_length: %d bytes\n", h->total_length); + dprintf(" in_collection: %d\n", h->in_collection); + for (i=0; iin_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; iattributes); + 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 + +#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 diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/audio.h b/src/add-ons/kernel/drivers/audio/usb_audio/audio.h new file mode 100644 index 0000000000..36415fb7af --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/audio.h @@ -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 diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/audio_driver.h b/src/add-ons/kernel/drivers/audio/usb_audio/audio_driver.h new file mode 100644 index 0000000000..5b571e15d0 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/audio_driver.h @@ -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 +#endif /* _SUPPORT_DEFS_H */ +#if !defined(_DRIVERS_H) +#include +#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 */ + diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/sound.h b/src/add-ons/kernel/drivers/audio/usb_audio/sound.h new file mode 100644 index 0000000000..77a6cb10bc --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/sound.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 +#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 diff --git a/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.c b/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.c new file mode 100644 index 0000000000..67bc04ec61 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/usb_audio/usb_audio.c @@ -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 +#include +#include +#include +#include + +#include +#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; pniso_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; pniso_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; pncancel_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; isource_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; +}