From cf7fc816a171a2cbd2709e3a010c7cb1b87f0237 Mon Sep 17 00:00:00 2001 From: Johannes Anderwald Date: Mon, 29 Jun 2015 17:03:33 +0000 Subject: [PATCH] [HDAUDBUS] - start implementing HDAUDIO Bus driver - based heavily on Haiku exellent driver svn path=/trunk/; revision=68311 --- reactos/drivers/wdm/audio/CMakeLists.txt | 1 + .../drivers/wdm/audio/hdaudbus/CMakeLists.txt | 14 + reactos/drivers/wdm/audio/hdaudbus/driver.h | 402 +++++++ .../wdm/audio/hdaudbus/hda_codec_defs.h | 386 ++++++ .../wdm/audio/hdaudbus/hda_controller_defs.h | 178 +++ .../drivers/wdm/audio/hdaudbus/hdaudbus.cpp | 1051 +++++++++++++++++ reactos/drivers/wdm/audio/hdaudbus/hdaudbus.h | 103 ++ 7 files changed, 2135 insertions(+) create mode 100644 reactos/drivers/wdm/audio/hdaudbus/CMakeLists.txt create mode 100644 reactos/drivers/wdm/audio/hdaudbus/driver.h create mode 100644 reactos/drivers/wdm/audio/hdaudbus/hda_codec_defs.h create mode 100644 reactos/drivers/wdm/audio/hdaudbus/hda_controller_defs.h create mode 100644 reactos/drivers/wdm/audio/hdaudbus/hdaudbus.cpp create mode 100644 reactos/drivers/wdm/audio/hdaudbus/hdaudbus.h diff --git a/reactos/drivers/wdm/audio/CMakeLists.txt b/reactos/drivers/wdm/audio/CMakeLists.txt index 2166bbcbd10..0547233be8e 100644 --- a/reactos/drivers/wdm/audio/CMakeLists.txt +++ b/reactos/drivers/wdm/audio/CMakeLists.txt @@ -3,5 +3,6 @@ add_subdirectory(backpln) add_subdirectory(drivers) add_subdirectory(drm) add_subdirectory(filters) +add_subdirectory(hdaudbus) add_subdirectory(legacy) add_subdirectory(sysaudio) diff --git a/reactos/drivers/wdm/audio/hdaudbus/CMakeLists.txt b/reactos/drivers/wdm/audio/hdaudbus/CMakeLists.txt new file mode 100644 index 00000000000..27e4bd0d9a3 --- /dev/null +++ b/reactos/drivers/wdm/audio/hdaudbus/CMakeLists.txt @@ -0,0 +1,14 @@ + +set_cpp() + +remove_definitions(-D_WIN32_WINNT=0x502) +add_definitions(-D_WIN32_WINNT=0x600) + +list(APPEND SOURCE + hdaudbus.cpp) + +add_library(hdaudbus SHARED ${SOURCE}) +set_module_type(hdaudbus kernelmodedriver) +target_link_libraries(hdaudbus libcntpr ${PSEH_LIB}) +add_importlibs(hdaudbus ntoskrnl hal) +add_cd_file(TARGET hdaudbus DESTINATION reactos/system32/drivers FOR all) diff --git a/reactos/drivers/wdm/audio/hdaudbus/driver.h b/reactos/drivers/wdm/audio/hdaudbus/driver.h new file mode 100644 index 00000000000..5a6b5177294 --- /dev/null +++ b/reactos/drivers/wdm/audio/hdaudbus/driver.h @@ -0,0 +1,402 @@ +/* + * Copyright 2007-2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ithamar Adema, ithamar AT unet DOT nl + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef _HDA_H_ +#define _HDA_H_ + +#ifndef __REACTOS__ +#include +#include +#include +#include + +#include +#include + +#ifndef HAIKU_TARGET_PLATFORM_HAIKU +# define DEVFS_PATH_FORMAT "audio/multi/hda/%lu" +# include +#else +# define DEVFS_PATH_FORMAT "audio/hmulti/hda/%lu" +# include +#endif +#endif + +#include "hda_controller_defs.h" +#include "hda_codec_defs.h" + +#define MAX_CARDS 4 + +/* values for the class_sub field for class_base = 0x04 (multimedia device) */ +#ifndef __HAIKU__ +# define PCI_hd_audio 3 +#endif + +#define HDA_MAX_AUDIO_GROUPS 15 +#define HDA_MAX_CODECS 15 +#define HDA_MAX_STREAMS 16 +#define MAX_CODEC_RESPONSES 16 +#define MAX_CODEC_UNSOL_RESPONSES 16 +#define MAX_INPUTS 32 +#define MAX_IO_WIDGETS 8 +#define MAX_ASSOCIATIONS 16 +#define MAX_ASSOCIATION_PINS 16 + +#define STREAM_MAX_BUFFERS 10 +#define STREAM_MIN_BUFFERS 2 + + +enum { + STREAM_PLAYBACK, + STREAM_RECORD +}; + +struct hda_codec; +struct hda_stream; +struct hda_multi; + +/*! This structure describes a single HDA compliant + controller. It contains a list of available streams + for use by the codecs contained, and the messaging queue + (verb/response) buffers for communication. +*/ +#ifndef __REACTOS__ +struct hda_controller { + struct pci_info pci_info; + int32 opened; + const char* devfs_path; + + area_id regs_area; + vuint8* regs; + uint32 irq; + bool msi; + bool dma_snooping; + + uint16 codec_status; + uint32 num_input_streams; + uint32 num_output_streams; + uint32 num_bidir_streams; + + uint32 corb_length; + uint32 rirb_length; + uint32 rirb_read_pos; + uint32 corb_write_pos; + area_id corb_rirb_pos_area; + corb_t* corb; + rirb_t* rirb; + uint32* stream_positions; + + hda_codec* codecs[HDA_MAX_CODECS + 1]; + hda_codec* active_codec; + uint32 num_codecs; + + hda_stream* streams[HDA_MAX_STREAMS]; + sem_id buffer_ready_sem; + + uint8 Read8(uint32 reg) + { + return *(regs + reg); + } + + uint16 Read16(uint32 reg) + { + return *(vuint16*)(regs + reg); + } + + uint32 Read32(uint32 reg) + { + return *(vuint32*)(regs + reg); + } + + void Write8(uint32 reg, uint8 value) + { + *(regs + reg) = value; + } + + void Write16(uint32 reg, uint16 value) + { + *(vuint16*)(regs + reg) = value; + } + + void Write32(uint32 reg, uint32 value) + { + *(vuint32*)(regs + reg) = value; + } + + void ReadModifyWrite8(uint32 reg, uint8 mask, uint8 value) + { + uint8 temp = Read8(reg); + temp &= ~mask; + temp |= value; + Write8(reg, temp); + } + + void ReadModifyWrite16(uint32 reg, uint16 mask, uint16 value) + { + uint16 temp = Read16(reg); + temp &= ~mask; + temp |= value; + Write16(reg, temp); + } + + void ReadModifyWrite32(uint32 reg, uint32 mask, uint32 value) + { + uint32 temp = Read32(reg); + temp &= ~mask; + temp |= value; + Write32(reg, temp); + } +}; + +/*! This structure describes a single stream of audio data, + which is can have multiple channels (for stereo or better). +*/ +struct hda_stream { + uint32 id; /* HDA controller stream # */ + uint32 offset; /* HDA I/O/B descriptor offset */ + bool running; + spinlock lock; /* Write lock */ + uint32 type; + + hda_controller* controller; + + uint32 pin_widget; /* PIN Widget ID */ + uint32 io_widgets[MAX_IO_WIDGETS]; /* Input/Output Converter Widget ID */ + uint32 num_io_widgets; + + uint32 sample_rate; + uint32 sample_format; + + uint32 num_buffers; + uint32 num_channels; + uint32 buffer_length; /* size of buffer in samples */ + uint32 buffer_size; /* actual (aligned) size of buffer in bytes */ + uint32 sample_size; + uint8* buffers[STREAM_MAX_BUFFERS]; + /* Virtual addresses for buffer */ + phys_addr_t physical_buffers[STREAM_MAX_BUFFERS]; + /* Physical addresses for buffer */ + + volatile bigtime_t real_time; + volatile uint64 frames_count; + uint32 last_link_frame_position; + volatile int32 buffer_cycle; + + uint32 rate, bps; /* Samplerate & bits per sample */ + + area_id buffer_area; + area_id buffer_descriptors_area; + phys_addr_t physical_buffer_descriptors; /* BDL physical address */ + + int32 incorrect_position_count; + bool use_dma_position; + + uint8 Read8(uint32 reg) + { + return controller->Read8(HDAC_STREAM_BASE + offset + reg); + } + + uint16 Read16(uint32 reg) + { + return controller->Read16(HDAC_STREAM_BASE + offset + reg); + } + + uint32 Read32(uint32 reg) + { + return controller->Read32(HDAC_STREAM_BASE + offset + reg); + } + + void Write8(uint32 reg, uint8 value) + { + *(controller->regs + HDAC_STREAM_BASE + offset + reg) = value; + } + + void Write16(uint32 reg, uint16 value) + { + *(vuint16*)(controller->regs + HDAC_STREAM_BASE + offset + reg) = value; + } + + void Write32(uint32 reg, uint32 value) + { + *(vuint32*)(controller->regs + HDAC_STREAM_BASE + offset + reg) = value; + } +}; + +struct hda_widget { + uint32 node_id; + + uint32 num_inputs; + int32 active_input; + uint32 inputs[MAX_INPUTS]; + uint32 flags; + + hda_widget_type type; + uint32 pm; + + struct { + uint32 audio; + uint32 output_amplifier; + uint32 input_amplifier; + } capabilities; + + union { + struct { + uint32 formats; + uint32 rates; + } io; + struct { + } mixer; + struct { + uint32 capabilities; + uint32 config; + } pin; + } d; +}; + +struct hda_association { + uint32 index; + bool enabled; + uint32 pin_count; + uint32 pins[MAX_ASSOCIATION_PINS]; +}; +#endif + +#define WIDGET_FLAG_OUTPUT_PATH 0x01 +#define WIDGET_FLAG_INPUT_PATH 0x02 +#define WIDGET_FLAG_WIDGET_PATH 0x04 + +/*! This structure describes a single Audio Function Group. An AFG + is a group of audio widgets which can be used to configure multiple + streams of audio either from the HDA Link to an output device (= playback) + or from an input device to the HDA link (= recording). +*/ +#ifndef __REACTOS__ +struct hda_audio_group { + hda_codec* codec; + hda_widget widget; + + /* Multi Audio API data */ + hda_stream* playback_stream; + hda_stream* record_stream; + + uint32 widget_start; + uint32 widget_count; + + uint32 association_count; + uint32 gpio; + + hda_widget* widgets; + hda_association associations[MAX_ASSOCIATIONS]; + + hda_multi* multi; +}; + +/*! This structure describes a single codec module in the + HDA compliant device. This is a discrete component, which + can contain both Audio Function Groups, Modem Function Groups, + and other customized (vendor specific) Function Groups. + + NOTE: ATM, only Audio Function Groups are supported. +*/ +struct hda_codec { + uint16 vendor_id; + uint16 product_id; + uint8 major; + uint8 minor; + uint8 revision; + uint8 stepping; + uint8 addr; + + uint32 quirks; + + sem_id response_sem; + uint32 responses[MAX_CODEC_RESPONSES]; + uint32 response_count; + + sem_id unsol_response_sem; + thread_id unsol_response_thread; + uint32 unsol_responses[MAX_CODEC_UNSOL_RESPONSES]; + uint32 unsol_response_read, unsol_response_write; + + hda_audio_group* audio_groups[HDA_MAX_AUDIO_GROUPS]; + uint32 num_audio_groups; + + struct hda_controller* controller; +}; + + +#define MULTI_CONTROL_FIRSTID 1024 +#define MULTI_CONTROL_MASTERID 0 +#define MULTI_MAX_CONTROLS 128 +#define MULTI_MAX_CHANNELS 128 + +struct hda_multi_mixer_control { + hda_multi *multi; + int32 nid; + int32 type; + bool input; + uint32 mute; + uint32 gain; + uint32 capabilities; + int32 index; + multi_mix_control mix_control; +}; + + +struct hda_multi { + hda_audio_group *group; + hda_multi_mixer_control controls[MULTI_MAX_CONTROLS]; + uint32 control_count; + + multi_channel_info chans[MULTI_MAX_CHANNELS]; + uint32 output_channel_count; + uint32 input_channel_count; + uint32 output_bus_channel_count; + uint32 input_bus_channel_count; + uint32 aux_bus_channel_count; +}; + + +/* driver.c */ +extern device_hooks gDriverHooks; +extern pci_module_info* gPci; +extern pci_x86_module_info* gPCIx86Module; +extern hda_controller gCards[MAX_CARDS]; +extern uint32 gNumCards; + +/* hda_codec.c */ +const char* get_widget_location(uint32 location); +hda_widget* hda_audio_group_get_widget(hda_audio_group* audioGroup, uint32 nodeID); + +status_t hda_audio_group_get_widgets(hda_audio_group* audioGroup, + hda_stream* stream); +hda_codec* hda_codec_new(hda_controller* controller, uint32 cad); +void hda_codec_delete(hda_codec* codec); + +/* hda_multi_audio.c */ +status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t length); + +/* hda_controller.c: Basic controller support */ +status_t hda_hw_init(hda_controller* controller); +void hda_hw_stop(hda_controller* controller); +void hda_hw_uninit(hda_controller* controller); +status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, + uint32 count); +status_t hda_verb_write(hda_codec* codec, uint32 nid, uint32 vid, uint16 payload); +status_t hda_verb_read(hda_codec* codec, uint32 nid, uint32 vid, uint32 *response); + +/* hda_controller.c: Stream support */ +hda_stream* hda_stream_new(hda_audio_group* audioGroup, int type); +void hda_stream_delete(hda_stream* stream); +status_t hda_stream_setup_buffers(hda_audio_group* audioGroup, + hda_stream* stream, const char* desc); +status_t hda_stream_start(hda_controller* controller, hda_stream* stream); +status_t hda_stream_stop(hda_controller* controller, hda_stream* stream); +#endif + +#endif /* _HDA_H_ */ diff --git a/reactos/drivers/wdm/audio/hdaudbus/hda_codec_defs.h b/reactos/drivers/wdm/audio/hdaudbus/hda_codec_defs.h new file mode 100644 index 00000000000..94797c2f717 --- /dev/null +++ b/reactos/drivers/wdm/audio/hdaudbus/hda_codec_defs.h @@ -0,0 +1,386 @@ +/* + * Copyright 2007-2008, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ithamar Adema, ithamar AT unet DOT nl + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef HDA_CODEC_H +#define HDA_CODEC_H + +enum hda_widget_type { + WT_AUDIO_OUTPUT = 0, + WT_AUDIO_INPUT = 1, + WT_AUDIO_MIXER = 2, + WT_AUDIO_SELECTOR = 3, + WT_PIN_COMPLEX = 4, + WT_POWER = 5, + WT_VOLUME_KNOB = 6, + WT_BEEP_GENERATOR = 7, + WT_VENDOR_DEFINED = 15 +}; + +enum pin_connectivity_type { + PIN_CONN_JACK, + PIN_CONN_NONE, + PIN_CONN_FIXED, + PIN_CONN_BOTH +}; + +enum pin_dev_type { + PIN_DEV_LINE_OUT = 0, + PIN_DEV_SPEAKER, + PIN_DEV_HEAD_PHONE_OUT, + PIN_DEV_CD, + PIN_DEV_SPDIF_OUT, + PIN_DEV_DIGITAL_OTHER_OUT, + PIN_DEV_MODEM_LINE_SIDE, + PIN_DEV_MODEM_HAND_SIDE, + PIN_DEV_LINE_IN, + PIN_DEV_AUX, + PIN_DEV_MIC_IN, + PIN_DEV_TELEPHONY, + PIN_DEV_SPDIF_IN, + PIN_DEV_DIGITAL_OTHER_IN, + PIN_DEV_RESERVED, + PIN_DEV_OTHER +}; + + +/* Verb Helper Macro */ +#define MAKE_VERB(cad, nid, vid, payload) \ + (((cad) << 28) | ((nid) << 20) | (vid) | (payload)) + +/* Verb IDs */ +#define VID_GET_PARAMETER 0xf0000 +#define VID_GET_CONNECTION_SELECT 0xf0100 +#define VID_SET_CONNECTION_SELECT 0x70100 +#define VID_GET_CONNECTION_LIST_ENTRY 0xf0200 +#define VID_GET_PROCESSING_STATE 0xf0300 +#define VID_SET_PROCESSING_STATE 0x70300 +#define VID_GET_COEFFICIENT_INDEX 0xd0000 +#define VID_SET_COEFFICIENT_INDEX 0x50000 +#define VID_GET_PROCESSING_COEFFICIENT 0xc0000 +#define VID_SET_PROCESSING_COEFFICIENT 0x40000 +#define VID_GET_AMPLIFIER_GAIN_MUTE 0xb0000 +#define VID_SET_AMPLIFIER_GAIN_MUTE 0x30000 +#define VID_GET_CONVERTER_FORMAT 0xa0000 +#define VID_SET_CONVERTER_FORMAT 0x20000 +#define VID_GET_CONVERTER_STREAM_CHANNEL 0xf0600 +#define VID_SET_CONVERTER_STREAM_CHANNEL 0x70600 +#define VID_GET_DIGITAL_CONVERTER_CONTROL 0xf0d00 +#define VID_SET_DIGITAL_CONVERTER_CONTROL1 0x70d00 +#define VID_SET_DIGITAL_CONVERTER_CONTROL2 0x70e00 +#define VID_GET_POWER_STATE 0xf0500 +#define VID_SET_POWER_STATE 0x70500 +#define VID_GET_SDI_SELECT 0xf0400 +#define VID_SET_SDI_SELECT 0x70400 +#define VID_GET_PIN_WIDGET_CONTROL 0xf0700 +#define VID_SET_PIN_WIDGET_CONTROL 0x70700 +#define VID_GET_UNSOLRESP 0xF0800 +#define VID_SET_UNSOLRESP 0x70800 +#define VID_GET_PINSENSE 0xF0900 +#define VID_SET_PINSENSE 0x70900 +#define VID_GET_EAPDBTL_EN 0xF0C00 +#define VID_SET_EAPDBTL_EN 0x70C00 +#define VID_GET_VOLUME_KNOB_CONTROL 0xF0F00 +#define VID_SET_VOLUME_KNOB_CONTROL 0x70F00 +#define VID_GET_GPIDATA 0xF1000 +#define VID_SET_GPIDATA 0x71000 +#define VID_GET_GPIWAKE_EN 0xF1100 +#define VID_SET_GPIWAKE_EN 0x71100 +#define VID_GET_GPIUNSOL 0xF1200 +#define VID_SET_GPIUNSOL 0x71200 +#define VID_GET_GPISTICKY 0xF1300 +#define VID_SET_GPISTICKY 0x71300 +#define VID_GET_GPO_DATA 0xF1400 +#define VID_SET_GPO_DATA 0x71400 +#define VID_GET_GPIO_DATA 0xF1500 +#define VID_SET_GPIO_DATA 0x71500 +#define VID_GET_GPIO_EN 0xF1600 +#define VID_SET_GPIO_EN 0x71600 +#define VID_GET_GPIO_DIR 0xF1700 +#define VID_SET_GPIO_DIR 0x71700 +#define VID_GET_GPIOWAKE_EN 0xF1800 +#define VID_SET_GPIOWAKE_EN 0x71800 +#define VID_GET_GPIOUNSOL_EN 0xF1900 +#define VID_SET_GPIOUNSOL_EN 0x71900 +#define VID_GET_GPIOSTICKY 0xF1A00 +#define VID_SET_GPIOSTICKY 0x71A00 +#define VID_GET_BEEPGEN 0xF0A00 +#define VID_SET_BEEPGEN 0x70A00 +#define VID_GET_VOLUME_KNOB 0xf0f00 +#define VID_SET_VOLUME_KNOB 0x70f00 +#define VID_GET_SUBSYSTEMID 0xF2000 +#define VID_SET_SUBSYSTEMID1 0x72000 +#define VID_SET_SUBSYSTEMID2 0x72100 +#define VID_SET_SUBSYSTEMID3 0x72200 +#define VID_SET_SUBSYSTEMID4 0x72300 +#define VID_GET_CONFIGURATION_DEFAULT 0xf1c00 +#define VID_SET_CONFIGURATION_DEFAULT1 0x71c00 +#define VID_SET_CONFIGURATION_DEFAULT2 0x71d00 +#define VID_SET_CONFIGURATION_DEFAULT3 0x71e00 +#define VID_SET_CONFIGURATION_DEFAULT4 0x71f00 +#define VID_GET_STRIPE_CONTROL 0xf2400 +#define VID_SET_STRIPE_CONTROL 0x72000 +#define VID_FUNCTION_RESET 0x7ff00 +/* later specification updates */ +#define VID_GET_EDID_LIKE_DATA 0xf2f00 +#define VID_GET_CONVERTER_CHANNEL_COUNT 0xf2d00 +#define VID_SET_CONVERTER_CHANNEL_COUNT 0x72d00 +#define VID_GET_DATA_ISLAND_PACKET_SIZE 0xf2e00 +#define VID_GET_DATA_ISLAND_PACKET_INDEX 0xf3000 +#define VID_SET_DATA_ISLAND_PACKET_INDEX 0x73000 +#define VID_GET_DATA_ISLAND_PACKET_DATA 0xf3100 +#define VID_SET_DATA_ISLAND_PACKET_DATA 0x73100 +#define VID_GET_DATA_ISLAND_PACKET_XMITCTRL 0xf3200 +#define VID_SET_DATA_ISLAND_PACKET_XMITCTRL 0x73200 +#define VID_GET_CONTENT_PROTECTION_CONTROL 0xf3300 +#define VID_SET_CONTENT_PROTECTION_CONTROL 0x73300 +#define VID_GET_ASP_CHANNEL_MAPPING 0xf3400 +#define VID_SET_ASP_CHANNEL_MAPPING 0x73400 + +/* Parameter IDs */ +#define PID_VENDOR_ID 0x00 +#define PID_REVISION_ID 0x02 +#define PID_SUB_NODE_COUNT 0x04 +#define PID_FUNCTION_GROUP_TYPE 0x05 +#define PID_AUDIO_GROUP_CAP 0x08 +#define PID_AUDIO_WIDGET_CAP 0x09 +#define PID_PCM_SUPPORT 0x0a +#define PID_STREAM_SUPPORT 0x0b +#define PID_PIN_CAP 0x0c +#define PID_INPUT_AMPLIFIER_CAP 0x0d +#define PID_CONNECTION_LIST_LENGTH 0x0e +#define PID_POWERSTATE_SUPPORT 0x0f +#define PID_PROCESSING_CAP 0x10 +#define PID_GPIO_COUNT 0x11 +#define PID_OUTPUT_AMPLIFIER_CAP 0x12 +#define PID_VOLUME_KNOB_CAP 0x13 + +/* Subordinate node count */ +#define SUB_NODE_COUNT_TOTAL_MASK 0x000000ff +#define SUB_NODE_COUNT_TOTAL_SHIFT 0 +#define SUB_NODE_COUNT_START_MASK 0x00ff0000 +#define SUB_NODE_COUNT_START_SHIFT 16 + +#define SUB_NODE_COUNT_TOTAL(c) ((c & SUB_NODE_COUNT_TOTAL_MASK) \ + >> SUB_NODE_COUNT_TOTAL_SHIFT) +#define SUB_NODE_COUNT_START(c) ((c & SUB_NODE_COUNT_START_MASK) \ + >> SUB_NODE_COUNT_START_SHIFT) + +/* Function group type */ +#define FUNCTION_GROUP_NODETYPE_MASK 0x000000ff +#define FUNCTION_GROUP_UNSOLCAPABLE_MASK 0x00000100 + +#define FUNCTION_GROUP_NODETYPE_AUDIO 0x00000001 +#define FUNCTION_GROUP_NODETYPE_MODEM 0x00000002 + +/* Audio Function group capabilities */ +#define AUDIO_GROUP_CAP_OUTPUT_DELAY_MASK 0x0000000f +#define AUDIO_GROUP_CAP_OUTPUT_DELAY_SHIFT 0 +#define AUDIO_GROUP_CAP_INPUT_DELAY_MASK 0x00000f00 +#define AUDIO_GROUP_CAP_INPUT_DELAY_SHIFT 8 +#define AUDIO_GROUP_CAP_BEEPGEN_MASK 0x00010000 +#define AUDIO_GROUP_CAP_BEEPGEN_SHIFT 16 + +#define AUDIO_GROUP_CAP_OUTPUT_DELAY(c) ((c & AUDIO_GROUP_CAP_OUTPUT_DELAY_MASK) \ + >> AUDIO_GROUP_CAP_OUTPUT_DELAY_SHIFT) +#define AUDIO_GROUP_CAP_INPUT_DELAY(c) ((c & AUDIO_GROUP_CAP_INPUT_DELAY_MASK) \ + >> AUDIO_GROUP_CAP_INPUT_DELAY_SHIFT) +#define AUDIO_GROUP_CAP_BEEPGEN(c) ((c & AUDIO_GROUP_CAP_BEEPGEN_MASK) \ + >> AUDIO_GROUP_CAP_BEEPGEN_SHIFT) + + +/* Audio widget capabilities */ +#define AUDIO_CAP_CHANNEL_COUNT_MASK 0x0000e000 +#define AUDIO_CAP_CHANNEL_COUNT_SHIFT 13 +#define AUDIO_CAP_DELAY_MASK 0x000f0000 +#define AUDIO_CAP_DELAY_SHIFT 16 +#define AUDIO_CAP_TYPE_MASK 0x00f00000 +#define AUDIO_CAP_TYPE_SHIFT 20 + +#define AUDIO_CAP_STEREO (1L << 0) +#define AUDIO_CAP_INPUT_AMPLIFIER (1L << 1) +#define AUDIO_CAP_OUTPUT_AMPLIFIER (1L << 2) +#define AUDIO_CAP_AMPLIFIER_OVERRIDE (1L << 3) +#define AUDIO_CAP_FORMAT_OVERRIDE (1L << 4) +#define AUDIO_CAP_STRIPE (1L << 5) +#define AUDIO_CAP_PROCESSING_CONTROLS (1L << 6) +#define AUDIO_CAP_UNSOLICITED_RESPONSES (1L << 7) +#define AUDIO_CAP_CONNECTION_LIST (1L << 8) +#define AUDIO_CAP_DIGITAL (1L << 9) +#define AUDIO_CAP_POWER_CONTROL (1L << 10) +#define AUDIO_CAP_LEFT_RIGHT_SWAP (1L << 11) +#define AUDIO_CAP_CP_CAPS (1L << 12) + +#define AUDIO_CAP_CHANNEL_COUNT(c) \ + (((c & AUDIO_CAP_CHANNEL_COUNT_MASK) >> (AUDIO_CAP_CHANNEL_COUNT_SHIFT - 1)) \ + | AUDIO_CAP_STEREO) + +/* Amplifier capabilities */ +#define AMP_CAP_MUTE 0xf0000000 +#define AMP_CAP_STEP_SIZE_MASK 0x007f0000 +#define AMP_CAP_STEP_SIZE_SHIFT 16 +#define AMP_CAP_NUM_STEPS_MASK 0x00007f00 +#define AMP_CAP_NUM_STEPS_SHIFT 8 +#define AMP_CAP_OFFSET_MASK 0x0000007f + +#define AMP_CAP_STEP_SIZE(c) ((((c & AMP_CAP_STEP_SIZE_MASK) \ + >> AMP_CAP_STEP_SIZE_SHIFT) + 1) / 4.0) +#define AMP_CAP_NUM_STEPS(c) ((c & AMP_CAP_NUM_STEPS_MASK) \ + >> AMP_CAP_NUM_STEPS_SHIFT) +#define AMP_CAP_OFFSET(c) (c & AMP_CAP_OFFSET_MASK) + +/* Pin capabilities */ +#define PIN_CAP_IMP_SENSE (1L << 0) +#define PIN_CAP_TRIGGER_REQ (1L << 1) +#define PIN_CAP_PRES_DETECT (1L << 2) +#define PIN_CAP_HP_DRIVE (1L << 3) +#define PIN_CAP_OUTPUT (1L << 4) +#define PIN_CAP_INPUT (1L << 5) +#define PIN_CAP_BALANCE (1L << 6) +#define PIN_CAP_HDMI (1L << 7) +#define PIN_CAP_VREF_CTRL_HIZ (1L << 8) +#define PIN_CAP_VREF_CTRL_50 (1L << 9) +#define PIN_CAP_VREF_CTRL_GROUND (1L << 10) +#define PIN_CAP_VREF_CTRL_80 (1L << 12) +#define PIN_CAP_VREF_CTRL_100 (1L << 13) +#define PIN_CAP_EAPD_CAP (1L << 16) +#define PIN_CAP_DP (1L << 24) +#define PIN_CAP_HBR (1L << 27) + +#define PIN_CAP_IS_PRES_DETECT_CAP(c) ((c & PIN_CAP_PRES_DETECT) != 0) +#define PIN_CAP_IS_OUTPUT(c) ((c & PIN_CAP_OUTPUT) != 0) +#define PIN_CAP_IS_INPUT(c) ((c & PIN_CAP_INPUT) != 0) +#define PIN_CAP_IS_BALANCE(c) ((c & PIN_CAP_BALANCE) != 0) +#define PIN_CAP_IS_HDMI(c) ((c & PIN_CAP_HDMI) != 0) +#define PIN_CAP_IS_VREF_CTRL_50_CAP(c) ((c & PIN_CAP_VREF_CTRL_50) != 0) +#define PIN_CAP_IS_VREF_CTRL_80_CAP(c) ((c & PIN_CAP_VREF_CTRL_80) != 0) +#define PIN_CAP_IS_VREF_CTRL_100_CAP(c) ((c & PIN_CAP_VREF_CTRL_100) != 0) +#define PIN_CAP_IS_EAPD_CAP(c) ((c & PIN_CAP_EAPD_CAP) != 0) +#define PIN_CAP_IS_DP(c) ((c & PIN_CAP_DP) != 0) +#define PIN_CAP_IS_HBR(c) ((c & PIN_CAP_HBR) != 0) + +/* PCM support */ +#define PCM_8_BIT (1L << 16) +#define PCM_16_BIT (1L << 17) +#define PCM_20_BIT (1L << 18) +#define PCM_24_BIT (1L << 19) +#define PCM_32_BIT (1L << 20) + +/* stream support */ +#define STREAM_AC3 0x00000004 +#define STREAM_FLOAT 0x00000002 +#define STREAM_PCM 0x00000001 + +/* Amplifier Gain/Mute */ +#define AMP_GET_OUTPUT (1L << 15) +#define AMP_GET_INPUT (0L << 15) +#define AMP_GET_LEFT_CHANNEL (1L << 13) +#define AMP_GET_RIGHT_CHANNEL (0L << 13) +#define AMP_GET_INPUT_INDEX_MASK 0x0000000f +#define AMP_GET_INPUT_INDEX_SHIFT 0 + +#define AMP_GET_INPUT_INDEX(x) ((x << AMP_GET_INPUT_INDEX_SHIFT) & AMP_GET_INPUT_INDEX_MASK) + +#define AMP_SET_OUTPUT (1L << 15) +#define AMP_SET_INPUT (1L << 14) +#define AMP_SET_LEFT_CHANNEL (1L << 13) +#define AMP_SET_RIGHT_CHANNEL (1L << 12) +#define AMP_SET_INPUT_INDEX_MASK 0x00000f00 +#define AMP_SET_INPUT_INDEX_SHIFT 8 + +#define AMP_SET_INPUT_INDEX(x) ((x << AMP_SET_INPUT_INDEX_SHIFT) & AMP_SET_INPUT_INDEX_MASK) + +#define AMP_GAIN_MASK 0x0000007f +#define AMP_MUTE (1L << 7) + +/* Pin Widget Control */ +#define PIN_ENABLE_HEAD_PHONE (1L << 7) +#define PIN_ENABLE_OUTPUT (1L << 6) +#define PIN_ENABLE_INPUT (1L << 5) +#define PIN_ENABLE_VREF_HIZ 0 +#define PIN_ENABLE_VREF_50 1 +#define PIN_ENABLE_VREF_GROUND 2 +#define PIN_ENABLE_VREF_80 4 +#define PIN_ENABLE_VREF_100 5 + +/* Unsolicited Response */ +#define UNSOLRESP_ENABLE (1L << 7) +#define UNSOLRESP_TAG_MASK 0x0000003f +#define UNSOLRESP_TAG_SHIFT 0 + +/* Pin sense */ +#define PIN_SENSE_PRESENCE_DETECT (1L << 31) +#define PIN_SENSE_ELD_VALID (1L << 30) +#define PIN_SENSE_IMPEDANCE_MASK 0x7fffffff +#define PIN_SENSE_IMPEDANCE_SHIFT 0 + +#define PIN_SENSE_IMPEDANCE_INVALID 0x7fffffff +#define PIN_SENSE_SET_CHANNEL_LEFT 0 +#define PIN_SENSE_SET_CHANNEL_RIGHT 1 + +/* Supported power states */ +#define POWER_STATE_D0 (1L << 0) +#define POWER_STATE_D1 (1L << 1) +#define POWER_STATE_D2 (1L << 2) +#define POWER_STATE_D3 (1L << 3) +#define POWER_STATE_D3COLD (1L << 4) +#define POWER_STATE_S3D3COLD (1L << 29) +#define POWER_STATE_CLKSTOP (1L << 30) +#define POWER_STATE_EPSS (1L << 31) + +/* Configuration default */ +#define CONF_DEFAULT_SEQUENCE_MASK 0x0000000f +#define CONF_DEFAULT_SEQUENCE_SHIFT 0 +#define CONF_DEFAULT_ASSOCIATION_MASK 0x000000f0 +#define CONF_DEFAULT_ASSOCIATION_SHIFT 4 +#define CONF_DEFAULT_MISC_MASK 0x00000f00 +#define CONF_DEFAULT_MISC_SHIFT 8 +#define CONF_DEFAULT_COLOR_MASK 0x0000f000 +#define CONF_DEFAULT_COLOR_SHIFT 12 +#define CONF_DEFAULT_CONNTYPE_MASK 0x000f0000 +#define CONF_DEFAULT_CONNTYPE_SHIFT 16 +#define CONF_DEFAULT_DEVICE_MASK 0x00f00000 +#define CONF_DEFAULT_DEVICE_SHIFT 20 +#define CONF_DEFAULT_LOCATION_MASK 0x3f000000 +#define CONF_DEFAULT_LOCATION_SHIFT 24 +#define CONF_DEFAULT_CONNECTIVITY_MASK 0xc0000000 +#define CONF_DEFAULT_CONNECTIVITY_SHIFT 30 + +#define CONF_DEFAULT_SEQUENCE(c) ((c & CONF_DEFAULT_SEQUENCE_MASK) >> CONF_DEFAULT_SEQUENCE_SHIFT) +#define CONF_DEFAULT_ASSOCIATION(c) ((c & CONF_DEFAULT_ASSOCIATION_MASK) >> CONF_DEFAULT_ASSOCIATION_SHIFT) +#define CONF_DEFAULT_MISC(c) ((c & CONF_DEFAULT_MISC_MASK) >> CONF_DEFAULT_MISC_SHIFT) +#define CONF_DEFAULT_COLOR(c) ((c & CONF_DEFAULT_COLOR_MASK) >> CONF_DEFAULT_COLOR_SHIFT) +#define CONF_DEFAULT_CONNTYPE(c) ((c & CONF_DEFAULT_CONNTYPE_MASK) >> CONF_DEFAULT_CONNTYPE_SHIFT) +#define CONF_DEFAULT_DEVICE(c) ((c & CONF_DEFAULT_DEVICE_MASK) >> CONF_DEFAULT_DEVICE_SHIFT) +#define CONF_DEFAULT_LOCATION(c) ((c & CONF_DEFAULT_LOCATION_MASK) >> CONF_DEFAULT_LOCATION_SHIFT) +#define CONF_DEFAULT_CONNECTIVITY(c) ((c & CONF_DEFAULT_CONNECTIVITY_MASK) >> CONF_DEFAULT_CONNECTIVITY_SHIFT) + +/* EAPD/BTL enable */ +#define EAPDBTL_ENABLE_BTL 0x1 +#define EAPDBTL_ENABLE_EAPD 0x2 +#define EAPDBTL_ENABLE_LRSWAP 0x4 + +/* GP I/O count */ +#define GPIO_COUNT_NUM_GPIO_MASK 0x000000ff +#define GPIO_COUNT_NUM_GPIO_SHIFT 0 +#define GPIO_COUNT_NUM_GPO_MASK 0x0000ff00 +#define GPIO_COUNT_NUM_GPO_SHIFT 8 +#define GPIO_COUNT_NUM_GPI_MASK 0x00ff0000 +#define GPIO_COUNT_NUM_GPI_SHIFT 16 +#define GPIO_COUNT_GPIUNSOL_MASK 0x40000000 +#define GPIO_COUNT_GPIUNSOL_SHIFT 30 +#define GPIO_COUNT_GPIWAKE_MASK 0x80000000 +#define GPIO_COUNT_GPIWAKE_SHIFT 31 + +#define GPIO_COUNT_NUM_GPIO(c) ((c & GPIO_COUNT_NUM_GPIO_MASK) >> GPIO_COUNT_NUM_GPIO_SHIFT) +#define GPIO_COUNT_NUM_GPO(c) ((c & GPIO_COUNT_NUM_GPO_MASK) >> GPIO_COUNT_NUM_GPO_SHIFT) +#define GPIO_COUNT_NUM_GPI(c) ((c & GPIO_COUNT_NUM_GPI_MASK) >> GPIO_COUNT_NUM_GPI_SHIFT) +#define GPIO_COUNT_GPIUNSOL(c) ((c & GPIO_COUNT_GPIUNSOL_MASK) >> GPIO_COUNT_GPIUNSOL_SHIFT) +#define GPIO_COUNT_GPIWAKE(c) ((c & GPIO_COUNT_GPIWAKE_MASK) >> GPIO_COUNT_GPIWAKE_SHIFT) + + +#endif /* HDA_CODEC_H */ diff --git a/reactos/drivers/wdm/audio/hdaudbus/hda_controller_defs.h b/reactos/drivers/wdm/audio/hdaudbus/hda_controller_defs.h new file mode 100644 index 00000000000..8dd341e7e69 --- /dev/null +++ b/reactos/drivers/wdm/audio/hdaudbus/hda_controller_defs.h @@ -0,0 +1,178 @@ +/* + * Copyright 2007-2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ithamar Adema, ithamar AT unet DOT nl + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef HDAC_REGS_H +#define HDAC_REGS_H + +#ifndef __REACTOS__ +#include +#endif + +/* Controller register definitions */ +#define HDAC_GLOBAL_CAP 0x00 // 16bits, GCAP +#define GLOBAL_CAP_OUTPUT_STREAMS(cap) (((cap) >> 12) & 15) +#define GLOBAL_CAP_INPUT_STREAMS(cap) (((cap) >> 8) & 15) +#define GLOBAL_CAP_BIDIR_STREAMS(cap) (((cap) >> 3) & 15) +#define GLOBAL_CAP_NUM_SDO(cap) ((((cap) >> 1) & 3) ? (cap & 6) : 1) +#define GLOBAL_CAP_64BIT(cap) (((cap) & 1) != 0) + +#define HDAC_VERSION_MINOR 0x02 // 8bits, VMIN +#define HDAC_VERSION_MAJOR 0x03 // 8bits, VMAJ + +#define HDAC_GLOBAL_CONTROL 0x08 // 32bits, GCTL +#define GLOBAL_CONTROL_UNSOLICITED (1 << 8) + // accept unsolicited responses +#define GLOBAL_CONTROL_FLUSH (1 << 1) +#define GLOBAL_CONTROL_RESET (1 << 0) + +#define HDAC_WAKE_ENABLE 0x0c // 16bits, WAKEEN +#define HDAC_WAKE_ENABLE_MASK 0x7fff +#define HDAC_STATE_STATUS 0x0e // 16bits, STATESTS + +#define HDAC_INTR_CONTROL 0x20 // 32bits, INTCTL +#define INTR_CONTROL_GLOBAL_ENABLE (1U << 31) +#define INTR_CONTROL_CONTROLLER_ENABLE (1 << 30) + +#define HDAC_INTR_STATUS 0x24 // 32bits, INTSTS +#define INTR_STATUS_GLOBAL (1U << 31) +#define INTR_STATUS_CONTROLLER (1 << 30) +#define INTR_STATUS_STREAM_MASK 0x3fffffff + +#define HDAC_CORB_BASE_LOWER 0x40 // 32bits, CORBLBASE +#define HDAC_CORB_BASE_UPPER 0x44 // 32bits, CORBUBASE +#define HDAC_CORB_WRITE_POS 0x48 // 16bits, CORBWP +#define HDAC_CORB_WRITE_POS_MASK 0xff + +#define HDAC_CORB_READ_POS 0x4a // 16bits, CORBRP +#define CORB_READ_POS_RESET (1 << 15) + +#define HDAC_CORB_CONTROL 0x4c // 8bits, CORBCTL +#define HDAC_CORB_CONTROL_MASK 0x3 +#define CORB_CONTROL_RUN (1 << 1) +#define CORB_CONTROL_MEMORY_ERROR_INTR (1 << 0) + +#define HDAC_CORB_STATUS 0x4d // 8bits, CORBSTS +#define CORB_STATUS_MEMORY_ERROR (1 << 0) + +#define HDAC_CORB_SIZE 0x4e // 8bits, CORBSIZE +#define HDAC_CORB_SIZE_MASK 0x3 +#define CORB_SIZE_CAP_2_ENTRIES (1 << 4) +#define CORB_SIZE_CAP_16_ENTRIES (1 << 5) +#define CORB_SIZE_CAP_256_ENTRIES (1 << 6) +#define CORB_SIZE_2_ENTRIES 0x00 // 8 byte +#define CORB_SIZE_16_ENTRIES 0x01 // 64 byte +#define CORB_SIZE_256_ENTRIES 0x02 // 1024 byte + +#define HDAC_RIRB_BASE_LOWER 0x50 // 32bits, RIRBLBASE +#define HDAC_RIRB_BASE_UPPER 0x54 // 32bits, RIRBUBASE + +#define HDAC_RIRB_WRITE_POS 0x58 // 16bits, RIRBWP +#define RIRB_WRITE_POS_RESET (1 << 15) + +#define HDAC_RESPONSE_INTR_COUNT 0x5a // 16bits, RINTCNT +#define HDAC_RESPONSE_INTR_COUNT_MASK 0xff + +#define HDAC_RIRB_CONTROL 0x5c // 8bits, RIRBCTL +#define HDAC_RIRB_CONTROL_MASK 0x7 +#define RIRB_CONTROL_OVERRUN_INTR (1 << 2) +#define RIRB_CONTROL_DMA_ENABLE (1 << 1) +#define RIRB_CONTROL_RESPONSE_INTR (1 << 0) + +#define HDAC_RIRB_STATUS 0x5d // 8bits, RIRBSTS +#define RIRB_STATUS_OVERRUN (1 << 2) +#define RIRB_STATUS_RESPONSE (1 << 0) + +#define HDAC_RIRB_SIZE 0x5e // 8bits, RIRBSIZE +#define HDAC_RIRB_SIZE_MASK 0x3 +#define RIRB_SIZE_CAP_2_ENTRIES (1 << 4) +#define RIRB_SIZE_CAP_16_ENTRIES (1 << 5) +#define RIRB_SIZE_CAP_256_ENTRIES (1 << 6) +#define RIRB_SIZE_2_ENTRIES 0x00 +#define RIRB_SIZE_16_ENTRIES 0x01 +#define RIRB_SIZE_256_ENTRIES 0x02 + +#define HDAC_DMA_POSITION_BASE_LOWER 0x70 // 32bits, DPLBASE +#define HDAC_DMA_POSITION_BASE_UPPER 0x74 // 32bits, DPUBASE +#define DMA_POSITION_ENABLED 1 + +/* Stream Descriptor Registers */ +#define HDAC_STREAM_BASE 0x80 +#define HDAC_STREAM_SIZE 0x20 + +#define HDAC_STREAM_CONTROL0 0x00 // 8bits, CTL0 +#define CONTROL0_RESET (1 << 0) +#define CONTROL0_RUN (1 << 1) +#define CONTROL0_BUFFER_COMPLETED_INTR (1 << 2) +#define CONTROL0_FIFO_ERROR_INTR (1 << 3) +#define CONTROL0_DESCRIPTOR_ERROR_INTR (1 << 4) +#define HDAC_STREAM_CONTROL1 0x01 // 8bits, CTL1 +#define HDAC_STREAM_CONTROL2 0x02 // 8bits, CTL2 +#define CONTROL2_STREAM_MASK 0xf0 +#define CONTROL2_STREAM_SHIFT 4 +#define CONTROL2_BIDIR (1 << 3) +#define CONTROL2_TRAFFIC_PRIORITY (1 << 2) +#define CONTROL2_STRIPE_SDO_MASK 0x03 +#define HDAC_STREAM_STATUS 0x03 // 8bits, STS +#define STATUS_BUFFER_COMPLETED (1 << 2) +#define STATUS_FIFO_ERROR (1 << 3) +#define STATUS_DESCRIPTOR_ERROR (1 << 4) +#define STATUS_FIFO_READY (1 << 5) +#define HDAC_STREAM_POSITION 0x04 // 32bits, LPIB +#define HDAC_STREAM_BUFFER_SIZE 0x08 // 32bits, CBL +#define HDAC_STREAM_LAST_VALID 0x0c // 16bits, LVI +#define HDAC_STREAM_FIFO_SIZE 0x10 // 16bits, FIFOS +#define HDAC_STREAM_FORMAT 0x12 // 16bits, FMT +#define FORMAT_8BIT (0 << 4) +#define FORMAT_16BIT (1 << 4) +#define FORMAT_20BIT (2 << 4) +#define FORMAT_24BIT (3 << 4) +#define FORMAT_32BIT (4 << 4) +#define FORMAT_44_1_BASE_RATE (1 << 14) +#define FORMAT_MULTIPLY_RATE_SHIFT 11 +#define FORMAT_DIVIDE_RATE_SHIFT 8 +#define HDAC_STREAM_BUFFERS_BASE_LOWER 0x18 // 32bits, BDPL +#define HDAC_STREAM_BUFFERS_BASE_UPPER 0x1c // 32bits, BDPU + +/* PCI space register definitions */ +#define PCI_HDA_TCSEL 0x44 +#define PCI_HDA_TCSEL_MASK 0xf8 + +#define ATI_HDA_MISC_CNTR2 0x42 +#define ATI_HDA_MISC_CNTR2_MASK 0xf8 +#define ATI_HDA_ENABLE_SNOOP 0x02 +#define NVIDIA_HDA_OSTRM_COH 0x4c +#define NVIDIA_HDA_ISTRM_COH 0x4d +#define NVIDIA_HDA_ENABLE_COHBIT 0x01 +#define NVIDIA_HDA_TRANSREG 0x4e +#define NVIDIA_HDA_TRANSREG_MASK 0xf0 +#define NVIDIA_HDA_ENABLE_COHBITS 0x0f +#define INTEL_SCH_HDA_DEVC 0x78 +#define INTEL_SCH_HDA_DEVC_SNOOP 0x800 + + +#ifndef __REACTOS__ +typedef uint32 corb_t; +typedef struct { + uint32 response; + uint32 flags; +} rirb_t; +#endif + +#define RESPONSE_FLAGS_CODEC_MASK 0x0000000f +#define RESPONSE_FLAGS_UNSOLICITED (1 << 4) + +#ifndef __REACTOS__ +typedef struct { + uint32 lower; + uint32 upper; + uint32 length; + uint32 ioc; +} bdl_entry_t; +#endif + +#endif /* HDAC_REGS_H */ diff --git a/reactos/drivers/wdm/audio/hdaudbus/hdaudbus.cpp b/reactos/drivers/wdm/audio/hdaudbus/hdaudbus.cpp new file mode 100644 index 00000000000..b07c431636f --- /dev/null +++ b/reactos/drivers/wdm/audio/hdaudbus/hdaudbus.cpp @@ -0,0 +1,1051 @@ +/* +* COPYRIGHT: See COPYING in the top level directory +* PROJECT: ReactOS Kernel Streaming +* FILE: drivers/wdm/audio/hdaudbus/hdaudbus.cpp +* PURPOSE: HDA Driver Entry +* PROGRAMMER: Johannes Anderwald +*/ + + +#include "hdaudbus.h" + + +PVOID +AllocateItem( +IN POOL_TYPE PoolType, +IN SIZE_T NumberOfBytes) +{ + PVOID Item = ExAllocatePoolWithTag(PoolType, NumberOfBytes, TAG_HDA); + if (!Item) + return Item; + + RtlZeroMemory(Item, NumberOfBytes); + return Item; +} + +VOID +FreeItem( + IN PVOID Item) +{ + ExFreePool(Item); +} + +BOOLEAN +NTAPI +HDA_InterruptService( +IN PKINTERRUPT Interrupt, +IN PVOID ServiceContext) +{ + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + ULONG InterruptStatus, Response, ResponseFlags, Cad; + UCHAR RirbStatus, CorbStatus; + USHORT WritePos; + PHDA_CODEC_ENTRY Codec; + + /* get device extension */ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)ServiceContext; + + // Check if this interrupt is ours + InterruptStatus = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_INTR_STATUS)); + + DPRINT1("HDA_InterruptService %lx\n", InterruptStatus); + if ((InterruptStatus & INTR_STATUS_GLOBAL) == 0) + return FALSE; + + // Controller or stream related? + if (InterruptStatus & INTR_STATUS_CONTROLLER) { + RirbStatus = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_STATUS); + CorbStatus = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_STATUS); + + // Check for incoming responses + if (RirbStatus) { + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_STATUS, RirbStatus); + + if ((RirbStatus & RIRB_STATUS_RESPONSE) != 0) { + WritePos = (READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS)) + 1) % DeviceExtension->RirbLength; + + for (; DeviceExtension->RirbReadPos != WritePos; DeviceExtension->RirbReadPos = (DeviceExtension->RirbReadPos + 1) % DeviceExtension->RirbLength) + { + + Response = DeviceExtension->RirbBase[DeviceExtension->RirbReadPos].response; + ResponseFlags = DeviceExtension->RirbBase[DeviceExtension->RirbReadPos].flags; + Cad = ResponseFlags & RESPONSE_FLAGS_CODEC_MASK; + DPRINT1("Response %lx ResponseFlags %lx Cad %lx\n", Response, ResponseFlags, Cad); + + /* get codec */ + Codec = DeviceExtension->Codecs[Cad]; + if (Codec == NULL) + { + DPRINT1("hda: response for unknown codec %x Response %x ResponseFlags %x\n", Cad, Response, ResponseFlags); + continue; + } + + /* check response count */ + if (Codec->ResponseCount >= MAX_CODEC_RESPONSES) + { + DPRINT1("too many responses for codec %x Response %x ResponseFlags %x\n", Cad, Response, ResponseFlags); + continue; + } + + // FIXME handle unsolicited responses + ASSERT((ResponseFlags & RESPONSE_FLAGS_UNSOLICITED) == 0); + + /* store response */ + Codec->Responses[Codec->ResponseCount] = Response; + Codec->ResponseCount++; + } + } + + if ((RirbStatus & RIRB_STATUS_OVERRUN) != 0) + DPRINT1("hda: RIRB Overflow\n"); + } + + // Check for sending errors + if (CorbStatus) { + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_STATUS, CorbStatus); + + if ((CorbStatus & CORB_STATUS_MEMORY_ERROR) != 0) + DPRINT1("hda: CORB Memory Error!\n"); + } + } +#if 0 + if ((intrStatus & INTR_STATUS_STREAM_MASK) != 0) { + for (uint32 index = 0; index < HDA_MAX_STREAMS; index++) { + if ((intrStatus & (1 << index)) != 0) { + if (controller->streams[index]) { + if (stream_handle_interrupt(controller, + controller->streams[index], index)) { + handled = B_INVOKE_SCHEDULER; + } + } + else { + dprintf("hda: Stream interrupt for unconfigured stream " + "%ld!\n", index); + } + } + } + } +#endif + return TRUE; +} + +VOID +HDA_SendVerbs( + IN PDEVICE_OBJECT DeviceObject, + IN PHDA_CODEC_ENTRY Codec, + IN PULONG Verbs, + OUT PULONG Responses, + IN ULONG Count) +{ + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + ULONG Sent = 0, ReadPosition, WritePosition, Queued; + + /* get device extension */ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* reset response count */ + Codec->ResponseCount = 0; + + while (Sent < Count) { + ReadPosition = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS)); + + Queued = 0; + + while (Sent < Count) { + WritePosition = (DeviceExtension->CorbWritePos + 1) % DeviceExtension->CorbLength; + + if (WritePosition == ReadPosition) { + // There is no space left in the ring buffer; execute the + // queued commands and wait until + break; + } + + DeviceExtension->CorbBase[WritePosition] = Verbs[Sent++]; + DeviceExtension->CorbWritePos = WritePosition; + + // FIXME HACK + // do proper synchronization + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS), DeviceExtension->CorbWritePos); + KeStallExecutionProcessor(30); + Queued++; + } + + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS), DeviceExtension->CorbWritePos); + } + + if (Responses != NULL) { + memcpy(Responses, Codec->Responses, Codec->ResponseCount * sizeof(ULONG)); + } +} + +NTSTATUS +HDA_InitCodec( + IN PDEVICE_OBJECT DeviceObject, + IN ULONG codecAddress) +{ + PHDA_CODEC_ENTRY Entry; + ULONG verbs[3]; + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + CODEC_RESPONSE Response; + ULONG NodeId, GroupType; + NTSTATUS Status; + PHDA_CODEC_AUDIO_GROUP AudioGroup; + PHDA_PDO_DEVICE_EXTENSION ChildDeviceExtension; + + /* lets allocate the entry */ + Entry = (PHDA_CODEC_ENTRY)AllocateItem(NonPagedPool, sizeof(HDA_CODEC_ENTRY)); + if (!Entry) + { + DPRINT1("hda: failed to allocate memory"); + return STATUS_UNSUCCESSFUL; + } + + /* init codec */ + Entry->Addr = codecAddress; + + /* get device extension */ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* store codec */ + DeviceExtension->Codecs[codecAddress] = Entry; + + verbs[0] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_VENDOR_ID); + verbs[1] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_REVISION_ID); + verbs[2] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_SUB_NODE_COUNT); + + /* get basic info */ + HDA_SendVerbs(DeviceObject, Entry, verbs, (PULONG)&Response, 3); + + /* store codec details */ + Entry->Major = Response.major; + Entry->Minor = Response.minor; + Entry->ProductId = Response.device; + Entry->Revision = Response.revision; + Entry->Stepping = Response.stepping; + Entry->VendorId = Response.vendor; + + DPRINT1("hda Codec %ld Vendor: %04lx Product: %04lx, Revision: %lu.%lu.%lu.%lu NodeStart %u NodeCount %u \n", codecAddress, Response.vendor, + Response.device, Response.major, Response.minor, Response.revision, Response.stepping, Response.start, Response.count); + + for (NodeId = Response.start; NodeId < Response.start + Response.count; NodeId++) { + + /* get function type */ + verbs[0] = MAKE_VERB(codecAddress, NodeId, VID_GET_PARAMETER, PID_FUNCTION_GROUP_TYPE); + + HDA_SendVerbs(DeviceObject, Entry, verbs, &GroupType, 1); + DPRINT1("NodeId %u GroupType %x\n", NodeId, GroupType); + + if ((GroupType & FUNCTION_GROUP_NODETYPE_MASK) == FUNCTION_GROUP_NODETYPE_AUDIO) { + + AudioGroup = (PHDA_CODEC_AUDIO_GROUP)AllocateItem(NonPagedPool, sizeof(HDA_CODEC_AUDIO_GROUP)); + if (!AudioGroup) + { + DPRINT1("hda: insufficient memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* init audio group */ + AudioGroup->NodeId = NodeId; + AudioGroup->FunctionGroup = FUNCTION_GROUP_NODETYPE_AUDIO; + + // Found an Audio Function Group! + DPRINT1("NodeId %x found an audio function group!\n"); + + Status = IoCreateDevice(DeviceObject->DriverObject, sizeof(HDA_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_SOUND, 0, FALSE, &AudioGroup->ChildPDO); + if (!NT_SUCCESS(Status)) + { + FreeItem(AudioGroup); + DPRINT1("hda failed to create device object %x\n", Status); + return Status; + } + + /* init child pdo*/ + ChildDeviceExtension = (PHDA_PDO_DEVICE_EXTENSION)AudioGroup->ChildPDO->DeviceExtension; + ChildDeviceExtension->IsFDO = FALSE; + ChildDeviceExtension->Codec = Entry; + ChildDeviceExtension->AudioGroup = AudioGroup; + + /* setup flags */ + AudioGroup->ChildPDO->Flags |= DO_POWER_PAGABLE; + AudioGroup->ChildPDO->Flags &= ~DO_DEVICE_INITIALIZING; + + /* add audio group*/ + Entry->AudioGroups[Entry->AudioGroupCount] = AudioGroup; + Entry->AudioGroupCount++; + } + } + return STATUS_SUCCESS; + +} + +NTSTATUS +NTAPI +HDA_InitCorbRirbPos( + IN PDEVICE_OBJECT DeviceObject) +{ + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + UCHAR corbSize, value, rirbSize; + PHYSICAL_ADDRESS HighestPhysicalAddress, CorbPhysicalAddress; + ULONG Index; + USHORT corbReadPointer, rirbWritePointer, interruptValue, corbControl, rirbControl; + + /* get device extension */ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // Determine and set size of CORB + corbSize = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE); + if ((corbSize & CORB_SIZE_CAP_256_ENTRIES) != 0) { + DeviceExtension->CorbLength = 256; + + value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & HDAC_CORB_SIZE_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_256_ENTRIES); + } + else if (corbSize & CORB_SIZE_CAP_16_ENTRIES) { + DeviceExtension->CorbLength = 16; + + value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & HDAC_CORB_SIZE_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_16_ENTRIES); + } + else if (corbSize & CORB_SIZE_CAP_2_ENTRIES) { + DeviceExtension->CorbLength = 2; + + value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE) & HDAC_CORB_SIZE_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_SIZE, value | CORB_SIZE_2_ENTRIES); + } + + // Determine and set size of RIRB + rirbSize = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE); + if (rirbSize & RIRB_SIZE_CAP_256_ENTRIES) { + DeviceExtension->RirbLength = 256; + + value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & HDAC_RIRB_SIZE_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_256_ENTRIES); + } + else if (rirbSize & RIRB_SIZE_CAP_16_ENTRIES) { + DeviceExtension->RirbLength = 16; + + value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & HDAC_RIRB_SIZE_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_16_ENTRIES); + } + else if (rirbSize & RIRB_SIZE_CAP_2_ENTRIES) { + DeviceExtension->RirbLength = 2; + + value = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE) & HDAC_RIRB_SIZE_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_SIZE, value | RIRB_SIZE_2_ENTRIES); + } + + /* init corb */ + HighestPhysicalAddress.QuadPart = 0x00000000FFFFFFFF; + DeviceExtension->CorbBase = (PULONG)MmAllocateContiguousMemory(PAGE_SIZE * 3, HighestPhysicalAddress); + + // FIXME align rirb 128bytes + ASSERT(DeviceExtension->CorbLength == 256); + ASSERT(DeviceExtension->RirbLength == 256); + + CorbPhysicalAddress = MmGetPhysicalAddress(DeviceExtension->CorbBase); + + // Program CORB/RIRB for these locations + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_CORB_BASE_LOWER), CorbPhysicalAddress.LowPart); + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_CORB_BASE_UPPER), CorbPhysicalAddress.HighPart); + + DeviceExtension->RirbBase = (PRIRB_RESPONSE)((ULONG_PTR)DeviceExtension->CorbBase + PAGE_SIZE); + CorbPhysicalAddress.QuadPart += PAGE_SIZE; + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_RIRB_BASE_LOWER), CorbPhysicalAddress.LowPart); + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_RIRB_BASE_UPPER), CorbPhysicalAddress.HighPart); + + // Program DMA position update + DeviceExtension->StreamPositions = (PVOID)((ULONG_PTR)DeviceExtension->RirbBase + PAGE_SIZE); + CorbPhysicalAddress.QuadPart += PAGE_SIZE; + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_LOWER), CorbPhysicalAddress.LowPart); + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_UPPER), CorbPhysicalAddress.HighPart); + + value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS)) & HDAC_CORB_WRITE_POS_MASK; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_WRITE_POS), value); + + // Reset CORB read pointer. Preseve bits marked as RsvdP. + // After setting the reset bit, we must wait for the hardware + // to acknowledge it, then manually unset it and wait for that + // to be acknowledged as well. + corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS)); + + corbReadPointer |= CORB_READ_POS_RESET; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS), corbReadPointer); + + for (Index = 0; Index < 100; Index++) { + KeStallExecutionProcessor(10); + corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS)); + if ((corbReadPointer & CORB_READ_POS_RESET) != 0) + break; + } + if ((corbReadPointer & CORB_READ_POS_RESET) == 0) { + DPRINT1("hda: CORB read pointer reset not acknowledged\n"); + + // According to HDA spec v1.0a ch3.3.21, software must read the + // bit as 1 to verify that the reset completed. However, at least + // some nVidia HDA controllers do not update the bit after reset. + // Thus don't fail here on nVidia controllers. + //if (controller->pci_info.vendor_id != PCI_VENDOR_NVIDIA) + // return B_BUSY; + } + + corbReadPointer &= ~CORB_READ_POS_RESET; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS), corbReadPointer); + for (Index = 0; Index < 10; Index++) { + KeStallExecutionProcessor(10); + corbReadPointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_READ_POS)); + if ((corbReadPointer & CORB_READ_POS_RESET) == 0) + break; + } + if ((corbReadPointer & CORB_READ_POS_RESET) != 0) { + DPRINT1("hda: CORB read pointer reset failed\n"); + return STATUS_UNSUCCESSFUL; + } + + // Reset RIRB write pointer + rirbWritePointer = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS)) & RIRB_WRITE_POS_RESET; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_WRITE_POS), rirbWritePointer | RIRB_WRITE_POS_RESET); + + // Generate interrupt for every response + interruptValue = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RESPONSE_INTR_COUNT)) & HDAC_RESPONSE_INTR_COUNT_MASK; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RESPONSE_INTR_COUNT), interruptValue | 1); + + // Setup cached read/write indices + DeviceExtension->RirbReadPos = 1; + DeviceExtension->CorbWritePos = 0; + + // Gentlemen, start your engines... + corbControl = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_CONTROL)) &HDAC_CORB_CONTROL_MASK; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_CORB_CONTROL), corbControl | CORB_CONTROL_RUN | CORB_CONTROL_MEMORY_ERROR_INTR); + + rirbControl = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_CONTROL)) & HDAC_RIRB_CONTROL_MASK; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_RIRB_CONTROL), rirbControl | RIRB_CONTROL_DMA_ENABLE | RIRB_CONTROL_OVERRUN_INTR | RIRB_CONTROL_RESPONSE_INTR); + + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +HDA_ResetController( + IN PDEVICE_OBJECT DeviceObject) +{ + USHORT ValCapabilities; + ULONG Index; + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + ULONG InputStreams, OutputStreams, BiDirStreams, Control; + UCHAR corbControl, rirbControl; + + /* get device extension */ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* read caps */ + ValCapabilities = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_GLOBAL_CAP)); + + InputStreams = GLOBAL_CAP_INPUT_STREAMS(ValCapabilities); + OutputStreams = GLOBAL_CAP_OUTPUT_STREAMS(ValCapabilities); + BiDirStreams = GLOBAL_CAP_BIDIR_STREAMS(ValCapabilities); + + DPRINT1("NumInputStreams %u\n", InputStreams); + DPRINT1("NumOutputStreams %u\n", OutputStreams); + DPRINT1("NumBiDirStreams %u\n", BiDirStreams); + + /* stop all streams */ + for (Index = 0; Index < InputStreams; Index++) + { + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_INPUT_STREAM_OFFSET(Index), 0); + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_INPUT_STREAM_OFFSET(Index), 0); + } + + for (Index = 0; Index < OutputStreams; Index++) { + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_OUTPUT_STREAM_OFFSET(InputStreams, Index), 0); + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_OUTPUT_STREAM_OFFSET(InputStreams, Index), 0); + } + + for (Index = 0; Index < BiDirStreams; Index++) { + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_CONTROL0 + HDAC_STREAM_BASE + HDAC_BIDIR_STREAM_OFFSET(InputStreams, OutputStreams, Index), 0); + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_STREAM_STATUS + HDAC_STREAM_BASE + HDAC_BIDIR_STREAM_OFFSET(InputStreams, OutputStreams, Index), 0); + } + + // stop DMA + Control = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL) & HDAC_CORB_CONTROL_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL, Control); + + Control = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL) & HDAC_RIRB_CONTROL_MASK; + WRITE_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL, Control); + + for (int timeout = 0; timeout < 10; timeout++) { + KeStallExecutionProcessor(10); + + corbControl = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_CORB_CONTROL); + rirbControl = READ_REGISTER_UCHAR(DeviceExtension->RegBase + HDAC_RIRB_CONTROL); + if (corbControl == 0 && rirbControl == 0) + break; + } + if (corbControl != 0 || rirbControl != 0) { + DPRINT1("hda: unable to stop dma\n"); + return STATUS_UNSUCCESSFUL; + } + + // reset DMA position buffer + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_LOWER), 0); + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_DMA_POSITION_BASE_UPPER), 0); + + // Set reset bit - it must be asserted for at least 100us + Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL)); + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control & ~GLOBAL_CONTROL_RESET); + + for (int timeout = 0; timeout < 10; timeout++) { + KeStallExecutionProcessor(10); + + Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL)); + if ((Control & GLOBAL_CONTROL_RESET) == 0) + break; + } + if ((Control & GLOBAL_CONTROL_RESET) != 0) + { + DPRINT1("hda: unable to reset controller\n"); + return STATUS_UNSUCCESSFUL; + } + + // Unset reset bit + Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL)); + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control | GLOBAL_CONTROL_RESET); + + for (int timeout = 0; timeout < 10; timeout++) { + KeStallExecutionProcessor(10); + + Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL)); + if ((Control & GLOBAL_CONTROL_RESET) != 0) + break; + } + if ((Control & GLOBAL_CONTROL_RESET) == 0) { + DPRINT1("hda: unable to exit reset\n"); + return STATUS_UNSUCCESSFUL; + } + + // Wait for codecs to finish their own reset (apparently needs more + // time than documented in the specs) + KeStallExecutionProcessor(100); + + // Enable unsolicited responses + Control = READ_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL)); + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_GLOBAL_CONTROL), Control | GLOBAL_CONTROL_UNSOLICITED); + + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +HDA_QueryId( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + WCHAR DeviceName[200]; + PHDA_PDO_DEVICE_EXTENSION DeviceExtension; + ULONG Length; + LPWSTR Device; + + /* get device extension */ + DeviceExtension = (PHDA_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(DeviceExtension->IsFDO == FALSE); + + /* get current irp stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID) + { + UNIMPLEMENTED; + + // FIXME + swprintf(DeviceName, L"%08x", 1); + Length = wcslen(DeviceName) + 20; + + /* allocate result buffer*/ + Device = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR)); + if (!Device) + return STATUS_INSUFFICIENT_RESOURCES; + + swprintf(Device, L"%08x", 1); + + DPRINT1("ID: %S\n", Device); + /* store result */ + Irp->IoStatus.Information = (ULONG_PTR)Device; + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID || + IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs) + { + + /* calculate size */ + swprintf(DeviceName, L"HDAUDIO\\FUNC_%02X&VEN_%04X&DEV_%04X&SUBSYS_%08X", DeviceExtension->AudioGroup->FunctionGroup, DeviceExtension->Codec->VendorId, DeviceExtension->Codec->ProductId, DeviceExtension->Codec->VendorId << 16 | DeviceExtension->Codec->ProductId); + Length = wcslen(DeviceName) + 20; + + /* allocate result buffer*/ + Device = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR)); + if (!Device) + return STATUS_INSUFFICIENT_RESOURCES; + + swprintf(Device, L"HDAUDIO\\FUNC_%02X&VEN_%04X&DEV_%04X&SUBSYS_%08X", DeviceExtension->AudioGroup->FunctionGroup, DeviceExtension->Codec->VendorId, DeviceExtension->Codec->ProductId, DeviceExtension->Codec->VendorId << 16 | DeviceExtension->Codec->ProductId); + + DPRINT1("ID: %S\n", Device); + /* store result */ + Irp->IoStatus.Information = (ULONG_PTR)Device; + return STATUS_SUCCESS; + } + else + { + DPRINT1("QueryID Type %x not implemented\n", IoStack->Parameters.QueryId.IdType); + return Irp->IoStatus.Status; + } + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HDA_StartDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status = STATUS_SUCCESS; + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + PCM_RESOURCE_LIST Resources; + ULONG Index; + USHORT Value; + + /* get current irp stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + /* get device extension */ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(DeviceExtension->IsFDO == TRUE); + + Resources = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated; + for (Index = 0; Index < Resources->List[0].PartialResourceList.Count; Index++) + { + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor = &Resources->List[0].PartialResourceList.PartialDescriptors[Index]; + + if (Descriptor->Type == CmResourceTypeMemory) + { + DeviceExtension->RegBase = (PUCHAR)MmMapIoSpace(Descriptor->u.Memory.Start, Descriptor->u.Memory.Length, MmNonCached); + if (DeviceExtension->RegBase == NULL) + { + DPRINT1("[HDAB] Failed to map registers\n"); + Status = STATUS_UNSUCCESSFUL; + break; + } + } + else if (Descriptor->Type == CmResourceTypeInterrupt) + { + Status = IoConnectInterrupt(&DeviceExtension->Interrupt, + HDA_InterruptService, + (PVOID)DeviceExtension, + NULL, + Descriptor->u.Interrupt.Vector, + Descriptor->u.Interrupt.Level, + Descriptor->u.Interrupt.Level, + (KINTERRUPT_MODE)(Descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED), + (Descriptor->ShareDisposition != CmResourceShareDeviceExclusive), + Descriptor->u.Interrupt.Affinity, + FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("[HDAB] Failed to connect interrupt\n"); + break; + } + + } + } + + if (NT_SUCCESS(Status)) + { + // Get controller into valid state + Status = HDA_ResetController(DeviceObject); + if (!NT_SUCCESS(Status)) return Status; + + // Setup CORB/RIRB/DMA POS + Status = HDA_InitCorbRirbPos(DeviceObject); + if (!NT_SUCCESS(Status)) return Status; + + + // Don't enable codec state change interrupts. We don't handle + // them, as we want to use the STATE_STATUS register to identify + // available codecs. We'd have to clear that register in the interrupt + // handler to 'ack' the codec change. + Value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_WAKE_ENABLE)) & HDAC_WAKE_ENABLE_MASK; + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_WAKE_ENABLE), Value); + + // Enable controller interrupts + WRITE_REGISTER_ULONG((PULONG)(DeviceExtension->RegBase + HDAC_INTR_CONTROL), INTR_CONTROL_GLOBAL_ENABLE | INTR_CONTROL_CONTROLLER_ENABLE); + + KeStallExecutionProcessor(100); + + Value = READ_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_STATE_STATUS)); + if (!Value) { + DPRINT1("hda: bad codec status\n"); + return STATUS_UNSUCCESSFUL; + } + WRITE_REGISTER_USHORT((PUSHORT)(DeviceExtension->RegBase + HDAC_STATE_STATUS), Value); + + // Create codecs + DPRINT1("Codecs %lx\n", Value); + for (Index = 0; Index < HDA_MAX_CODECS; Index++) { + if ((Value & (1 << Index)) != 0) { + HDA_InitCodec(DeviceObject, Index); + } + } + } + + return Status; +} + +NTSTATUS +NTAPI +HDA_QueryBusRelations( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + ULONG DeviceCount, CodecIndex, AFGIndex; + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + PHDA_CODEC_ENTRY Codec; + PDEVICE_RELATIONS DeviceRelations; + + /* get device extension */ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(DeviceExtension->IsFDO == TRUE); + + DeviceCount = 0; + for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++) + { + if (DeviceExtension->Codecs[CodecIndex] == NULL) + continue; + + Codec = DeviceExtension->Codecs[CodecIndex]; + DeviceCount += Codec->AudioGroupCount; + } + + if (DeviceCount == 0) + return STATUS_UNSUCCESSFUL; + + DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(NonPagedPool, sizeof(DEVICE_RELATIONS) + (DeviceCount > 1 ? sizeof(PDEVICE_OBJECT) * (DeviceCount - 1) : 0)); + if (!DeviceRelations) + return STATUS_INSUFFICIENT_RESOURCES; + + DeviceCount = 0; + for (CodecIndex = 0; CodecIndex < HDA_MAX_CODECS; CodecIndex++) + { + if (DeviceExtension->Codecs[CodecIndex] == NULL) + continue; + + Codec = DeviceExtension->Codecs[CodecIndex]; + for (AFGIndex = 0; AFGIndex < Codec->AudioGroupCount; AFGIndex++) + { + DeviceRelations->Objects[DeviceRelations->Count] = Codec->AudioGroups[AFGIndex]->ChildPDO; + ObReferenceObject(Codec->AudioGroups[AFGIndex]->ChildPDO); + DeviceRelations->Count++; + } + } + + /* FIXME handle existing device relations */ + ASSERT(Irp->IoStatus.Information == 0); + + /* store device relations */ + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + + /* done */ + return STATUS_SUCCESS; +} + +NTSTATUS +HDA_QueryBusInformation( + IN PIRP Irp) +{ + PPNP_BUS_INFORMATION BusInformation; + + /* allocate bus information */ + BusInformation = (PPNP_BUS_INFORMATION)AllocateItem(PagedPool, sizeof(PNP_BUS_INFORMATION)); + + if (!BusInformation) + { + /* no memory */ + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* return info */ + BusInformation->BusNumber = 0; + BusInformation->LegacyBusType = PCIBus; + RtlMoveMemory(&BusInformation->BusTypeGuid, &GUID_HDAUDIO_BUS_INTERFACE, sizeof(GUID)); + + /* store result */ + Irp->IoStatus.Information = (ULONG_PTR)BusInformation; + + /* done */ + return STATUS_SUCCESS; +} + +NTSTATUS +HDA_QueryBusDeviceCapabilities( + IN PIRP Irp) +{ + PDEVICE_CAPABILITIES Capabilities; + PIO_STACK_LOCATION IoStack; + + /* get stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + /* get capabilities */ + Capabilities = IoStack->Parameters.DeviceCapabilities.Capabilities; + + RtlZeroMemory(Capabilities, sizeof(DEVICE_CAPABILITIES)); + + /* setup capabilities */ + Capabilities->UniqueID = TRUE; + Capabilities->SilentInstall = TRUE; + Capabilities->SurpriseRemovalOK = TRUE; + Capabilities->Address = 0; + Capabilities->UINumber = 0; + Capabilities->SystemWake = PowerSystemWorking; /* FIXME common device extension */ + Capabilities->DeviceWake = PowerDeviceD0; + + /* done */ + return STATUS_SUCCESS; +} + +NTSTATUS +HDA_QueryBusDevicePnpState( + IN PIRP Irp) +{ + /* set device flags */ + Irp->IoStatus.Information = PNP_DEVICE_DONT_DISPLAY_IN_UI | PNP_DEVICE_NOT_DISABLEABLE; + + /* done */ + return STATUS_SUCCESS; +} + +NTSTATUS +HDA_PdoHandleQueryDeviceText( + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + LPWSTR Buffer; + static WCHAR DeviceText[] = L"Audio Device on High Definition Audio Bus"; + + IoStack = IoGetCurrentIrpStackLocation(Irp); + if (IoStack->Parameters.QueryDeviceText.DeviceTextType == DeviceTextDescription) + { + DPRINT("HDA_PdoHandleQueryDeviceText DeviceTextDescription\n"); + + Buffer = (LPWSTR)AllocateItem(PagedPool, sizeof(DeviceText)); + if (!Buffer) + { + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + wcscpy(Buffer, DeviceText); + + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + return STATUS_SUCCESS; + } + else + { + DPRINT("HDA_PdoHandleQueryDeviceText DeviceTextLocationInformation\n"); + + Buffer = (LPWSTR)AllocateItem(PagedPool, sizeof(DeviceText)); + if (!Buffer) + { + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + wcscpy(Buffer, DeviceText); + + /* save result */ + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + return STATUS_SUCCESS; + } + +} + +NTSTATUS +NTAPI +HDA_Pnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + NTSTATUS Status = STATUS_NOT_SUPPORTED; + PIO_STACK_LOCATION IoStack; + PDEVICE_RELATIONS DeviceRelation; + PHDA_FDO_DEVICE_EXTENSION FDODeviceExtension; + PHDA_PDO_DEVICE_EXTENSION ChildDeviceExtension; + + FDODeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ChildDeviceExtension = (PHDA_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT1("HDA_Pnp Minor: %u IsFDO%d\n", IoStack->MinorFunction, FDODeviceExtension->IsFDO); + + if (FDODeviceExtension->IsFDO) + { + if (IoStack->MinorFunction == IRP_MN_START_DEVICE) + { + DPRINT1("IRP_MN_START_DEVICE\n"); + Status = HDA_StartDevice(DeviceObject, Irp); + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_DEVICE_RELATIONS) + { + DPRINT1("IRP_MN_QUERY_DEVICE_RELATIONS\n"); + /* handle bus device relations */ + if (IoStack->Parameters.QueryDeviceRelations.Type == BusRelations) + { + Status = HDA_QueryBusRelations(DeviceObject, Irp); + } + else + { + Status = Irp->IoStatus.Status; + } + } + else + { + /* get default status */ + Status = Irp->IoStatus.Status; + } + } + else + { + if (IoStack->MinorFunction == IRP_MN_START_DEVICE) + { + DPRINT1("IRP_MN_START_DEVICE\n"); + /* no op for pdo */ + Status = STATUS_SUCCESS; + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_BUS_INFORMATION) + { + DPRINT1("IRP_MN_QUERY_BUS_INFORMATION\n"); + /* query bus information */ + Status = HDA_QueryBusInformation(Irp); + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_PNP_DEVICE_STATE) + { + DPRINT1("IRP_MN_QUERY_PNP_DEVICE_STATE\n"); + /* query pnp state */ + Status = HDA_QueryBusDevicePnpState(Irp); + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_DEVICE_RELATIONS) + { + DPRINT1("IRP_MN_QUERY_DEVICE_RELATIONS\n"); + if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) + { + /* handle target device relations */ + ASSERT(IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation); + ASSERT(Irp->IoStatus.Information == 0); + + /* allocate device relation */ + DeviceRelation = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS)); + if (DeviceRelation) + { + DeviceRelation->Count = 1; + DeviceRelation->Objects[0] = DeviceObject; + + /* reference self */ + ObReferenceObject(DeviceObject); + + /* store result */ + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelation; + + /* done */ + Status = STATUS_SUCCESS; + } + else + { + /* no memory */ + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_CAPABILITIES) + { + DPRINT1("IRP_MN_QUERY_CAPABILITIES\n"); + /* query capabilities */ + Status = HDA_QueryBusDeviceCapabilities(Irp); + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_RESOURCE_REQUIREMENTS) + { + DPRINT1("IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n"); + /* no op */ + Status = STATUS_SUCCESS; + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_ID) + { + DPRINT1("IRP_MN_QUERY_ID\n"); + Status = HDA_QueryId(DeviceObject, Irp); + } + else if (IoStack->MinorFunction == IRP_MN_QUERY_DEVICE_TEXT) + { + DPRINT1("IRP_MN_QUERY_DEVICE_TEXT\n"); + Status = HDA_PdoHandleQueryDeviceText(Irp); + } + else + { + /* get default status */ + Status = Irp->IoStatus.Status; + } + } + + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + + return Status; +} + + +//PDRIVER_ADD_DEVICE HDA_AddDevice; + +NTSTATUS +NTAPI +HDA_AddDevice( +IN PDRIVER_OBJECT DriverObject, +IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + PDEVICE_OBJECT DeviceObject; + PHDA_FDO_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + + /* create device object */ + Status = IoCreateDevice(DriverObject, sizeof(HDA_FDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &DeviceObject); + if (!NT_SUCCESS(Status)) + { + /* failed */ + return Status; + } + + /* get device extension*/ + DeviceExtension = (PHDA_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* init device extension*/ + DeviceExtension->IsFDO = TRUE; + DeviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject); + RtlZeroMemory(DeviceExtension->Codecs, sizeof(PHDA_CODEC_ENTRY) * (HDA_MAX_CODECS + 1)); + + + /* set device flags */ + DeviceObject->Flags |= DO_POWER_PAGABLE; + + return Status; +} +extern "C" +{ +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPathName) +{ + DriverObject->DriverExtension->AddDevice = HDA_AddDevice; + DriverObject->MajorFunction[IRP_MJ_PNP] = HDA_Pnp; + + return STATUS_SUCCESS; +} + +} \ No newline at end of file diff --git a/reactos/drivers/wdm/audio/hdaudbus/hdaudbus.h b/reactos/drivers/wdm/audio/hdaudbus/hdaudbus.h new file mode 100644 index 00000000000..3ecf224c52f --- /dev/null +++ b/reactos/drivers/wdm/audio/hdaudbus/hdaudbus.h @@ -0,0 +1,103 @@ +#pragma once + +#define YDEBUG +#include +#include +#include +#include +#include + +#define TAG_HDA 'bADH' + + +// include Haiku headers +#include "driver.h" + +#define MAKE_RATE(base, multiply, divide) \ + ((base == 44100 ? FORMAT_44_1_BASE_RATE : 0) \ + | ((multiply - 1) << FORMAT_MULTIPLY_RATE_SHIFT) \ + | ((divide - 1) << FORMAT_DIVIDE_RATE_SHIFT)) + +#define HDAC_INPUT_STREAM_OFFSET(index) \ + ((index) * HDAC_STREAM_SIZE) +#define HDAC_OUTPUT_STREAM_OFFSET(num_input_streams, index) \ + ((num_input_streams + (index)) * HDAC_STREAM_SIZE) +#define HDAC_BIDIR_STREAM_OFFSET(num_input_streams, num_output_streams, index) \ + ((num_input_streams + num_output_streams \ + + (index)) * HDAC_STREAM_SIZE) + +#define ALIGN(size, align) (((size) + align - 1) & ~(align - 1)) + + +typedef struct { + ULONG response; + ULONG flags; +}RIRB_RESPONSE, *PRIRB_RESPONSE; + +typedef struct +{ + PDEVICE_OBJECT ChildPDO; + ULONG FunctionGroup; + ULONG NodeId; +}HDA_CODEC_AUDIO_GROUP, *PHDA_CODEC_AUDIO_GROUP; + +typedef struct +{ + USHORT VendorId; + USHORT ProductId; + UCHAR Major; + UCHAR Minor; + UCHAR Revision; + UCHAR Stepping; + UCHAR Addr; + + ULONG Responses[MAX_CODEC_RESPONSES]; + ULONG ResponseCount; + + PHDA_CODEC_AUDIO_GROUP AudioGroups[HDA_MAX_AUDIO_GROUPS]; + ULONG AudioGroupCount; + +}HDA_CODEC_ENTRY, *PHDA_CODEC_ENTRY; + + +typedef struct +{ + BOOLEAN IsFDO; + PDEVICE_OBJECT LowerDevice; + + PUCHAR RegBase; + PKINTERRUPT Interrupt; + + ULONG CorbLength; + PULONG CorbBase; + ULONG RirbLength; + PRIRB_RESPONSE RirbBase; + ULONG RirbReadPos; + ULONG CorbWritePos; + PVOID StreamPositions; + + PHDA_CODEC_ENTRY Codecs[HDA_MAX_CODECS + 1]; + +}HDA_FDO_DEVICE_EXTENSION, *PHDA_FDO_DEVICE_EXTENSION; + +typedef struct +{ + BOOLEAN IsFDO; + PHDA_CODEC_ENTRY Codec; + PHDA_CODEC_AUDIO_GROUP AudioGroup; +}HDA_PDO_DEVICE_EXTENSION, *PHDA_PDO_DEVICE_EXTENSION; + + +typedef struct { + ULONG device : 16; + ULONG vendor : 16; + ULONG stepping : 8; + ULONG revision : 8; + ULONG minor : 4; + ULONG major : 4; + ULONG _reserved0 : 8; + ULONG count : 8; + ULONG _reserved1 : 8; + ULONG start : 8; + ULONG _reserved2 : 8; +}CODEC_RESPONSE, *PCODEC_RESPONSE;