ALI 5451 audio driver for Haiku multi audio by Krzysiek Cwiertnia
* playback, tested with 16bit format, with sample rate range from 8 to 48kHz * recording, fixed at 48kHz 16 bit (read below) * controlling some mixers, input selector, etc. I placed the driver in the ac97 directory as it fits better. Also a few coding style fixes by me. This driver collides at least with one pci id of the sis7018 driver. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34452 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
def9898c9b
commit
88e5c1ac3b
@ -1,5 +1,6 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ali5451 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 auich ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 auvia ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 es1370 ;
|
||||
|
12
src/add-ons/kernel/drivers/audio/ac97/ali5451/Jamfile
Normal file
12
src/add-ons/kernel/drivers/audio/ac97/ali5451/Jamfile
Normal file
@ -0,0 +1,12 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 ali5451 ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
|
||||
UsePrivateHeaders media ;
|
||||
|
||||
KernelAddon ali5451 :
|
||||
driver.c
|
||||
ali_multi.c
|
||||
ali_hardware.c
|
||||
util.c
|
||||
;
|
404
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.c
Normal file
404
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.c
Normal file
@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <drivers/Drivers.h>
|
||||
#include <drivers/KernelExport.h>
|
||||
#include <drivers/PCI.h>
|
||||
#include <hmulti_audio.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "driver.h"
|
||||
#include "util.h"
|
||||
#include "ali_hardware.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#define ALI_5451_V02 0x02
|
||||
#define ALI_AC97_CODEC_ID 0x41445374
|
||||
#define ALI_AC97_CAPS_HEAD 0x10
|
||||
|
||||
// ALI registers
|
||||
|
||||
#define ALI_MPUR0 0x20
|
||||
#define ALI_MPUR1 0x21
|
||||
#define ALI_MPUR2 0x22
|
||||
#define ALI_MPUR3 0x23
|
||||
|
||||
#define ALI_ACR0 0x40
|
||||
#define ALI_ACR1 0x44
|
||||
#define ALI_ACR2 0x48
|
||||
|
||||
#define ALI_START_A 0x80
|
||||
#define ALI_STOP_A 0x84
|
||||
|
||||
#define ALI_AIN_A 0x98
|
||||
#define ALI_LFO_A 0xa0
|
||||
#define ALI_LFO_A_ENDLP_IE 0x1000
|
||||
#define ALI_LFO_A_MIDLP_IE 0x2000
|
||||
#define ALI_AINTEN_A 0xa4
|
||||
#define ALI_VOL_A 0xa8
|
||||
#define ALI_STIMER 0xc8
|
||||
#define ALI_T_DIGIMIXER 0xd4
|
||||
#define ALI_AIN_B 0xd8
|
||||
|
||||
#define ALI_CSO_ALPHA_FMS 0xe0
|
||||
#define ALI_PPTR_LBA 0xe4
|
||||
#define ALI_ESO_DELTA 0xe8
|
||||
#define ALI_FMC_RVOL_CVOL 0xec
|
||||
#define ALI_GVSEL_MISC 0xf0
|
||||
#define ALI_EBUF1 0xf4
|
||||
#define ALI_EBUF2 0xf8
|
||||
|
||||
extern pci_module_info *gPCI;
|
||||
|
||||
|
||||
/* card i/o */
|
||||
|
||||
|
||||
static uint32
|
||||
io_read32(ali_dev *card, ulong offset)
|
||||
{
|
||||
return (*gPCI->read_io_32)(card->io_base+offset);
|
||||
}
|
||||
|
||||
|
||||
static uint8
|
||||
io_read8(ali_dev *card, ulong offset)
|
||||
{
|
||||
return (*gPCI->read_io_8)(card->io_base+offset);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
io_write32(ali_dev *card, ulong offset, uint32 value)
|
||||
{
|
||||
(*gPCI->write_io_32)(card->io_base+offset, value);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
io_write16(ali_dev *card, ulong offset, uint16 value)
|
||||
{
|
||||
(*gPCI->write_io_16)(card->io_base+offset, value);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
io_write8(ali_dev *card, ulong offset, uint8 value)
|
||||
{
|
||||
(*gPCI->write_io_8)(card->io_base+offset, value);
|
||||
}
|
||||
|
||||
|
||||
/* codec i/o */
|
||||
|
||||
|
||||
static bool
|
||||
ac97_is_ready(ali_dev *card, uint32 offset)
|
||||
{
|
||||
uint8 cnt = 200;
|
||||
uint32 val;
|
||||
|
||||
while (cnt--) {
|
||||
val = io_read32(card, offset);
|
||||
if (!(val & 0x8000))
|
||||
return true;
|
||||
spin(1);
|
||||
}
|
||||
|
||||
io_write32(card, offset, val & ~0x8000);
|
||||
TRACE("ac97 not ready\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ac97_is_stimer_ready(ali_dev *card)
|
||||
{
|
||||
uint8 cnt = 200;
|
||||
uint32 t1, t2;
|
||||
|
||||
t1 = io_read32(card, ALI_STIMER);
|
||||
|
||||
while (cnt--) {
|
||||
t2 = io_read32(card, ALI_STIMER);
|
||||
if (t2 != t1)
|
||||
return true;
|
||||
spin(1);
|
||||
}
|
||||
|
||||
TRACE("ac97 stimer not ready\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
uint16
|
||||
ac97_read(ali_dev *card, uint8 reg)
|
||||
{
|
||||
uint32 ac_reg;
|
||||
|
||||
ac_reg = (card->info.revision == ALI_5451_V02)?ALI_ACR0:ALI_ACR1;
|
||||
|
||||
if (!ac97_is_ready(card, ac_reg) || !ac97_is_stimer_ready(card))
|
||||
return 0;
|
||||
|
||||
io_write16(card, ac_reg, (uint16) reg | 0x8000);
|
||||
|
||||
if (!ac97_is_stimer_ready(card) || !ac97_is_ready(card, ac_reg))
|
||||
return 0;
|
||||
|
||||
return (uint16) (io_read32(card, ac_reg) >> 16);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ac97_write(ali_dev *card, uint8 reg, uint16 val)
|
||||
{
|
||||
uint32 dw_val;
|
||||
|
||||
if (!ac97_is_ready(card, ALI_ACR0) || !ac97_is_stimer_ready(card))
|
||||
return;
|
||||
|
||||
dw_val = reg | 0x8000 | (uint32) val << 16;
|
||||
if (card->info.revision == ALI_5451_V02)
|
||||
dw_val |= 0x0100;
|
||||
|
||||
io_write32(card, ALI_ACR0, dw_val);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ac97_init(ali_dev *card)
|
||||
{
|
||||
uint16 capabilities;
|
||||
uint32 id;
|
||||
|
||||
// ac97 reset
|
||||
ac97_write(card, AC97_REG_POWER, 0);
|
||||
ac97_write(card, AC97_REG_RESET, 0);
|
||||
snooze(100000);
|
||||
ac97_write(card, AC97_REG_POWER, 0);
|
||||
ac97_write(card, AC97_REG_GEN, 0);
|
||||
|
||||
capabilities = ac97_read(card, AC97_REG_RESET);
|
||||
TRACE("ac97 capabilities: %04x\n", capabilities & 0x3ff);
|
||||
|
||||
id = (uint32) ac97_read(card, AC97_REG_ID1) << 16
|
||||
| ac97_read(card, AC97_REG_ID2);
|
||||
TRACE("ac97 id: %08x\n", (uint) id);
|
||||
if (id != ALI_AC97_CODEC_ID)
|
||||
return false;
|
||||
|
||||
if ((capabilities & ALI_AC97_CAPS_HEAD) != 0)
|
||||
ac97_write(card, AC97_JACK_SENSE, (uint16) 1 << 10);
|
||||
// enable headphone jack sense
|
||||
|
||||
// some sane default values
|
||||
ac97_write(card, AC97_MIX_MASTER, 0x707);
|
||||
ac97_write(card, AC97_MIX_PCM, 0x707);
|
||||
ac97_write(card, AC97_MIX_HEADPHONE, 0x707);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
hardware_reset(ali_dev *card)
|
||||
{
|
||||
uint32 cmd_reg;
|
||||
uint16 val;
|
||||
uint8 bval;
|
||||
|
||||
// pci reset
|
||||
cmd_reg = (*gPCI->read_pci_config)(card->info.bus, card->info.device,
|
||||
card->info.function, 0x44, 4);
|
||||
(*gPCI->write_pci_config)(card->info.bus, card->info.device,
|
||||
card->info.function, 0x44, 4, cmd_reg | 0x08000000);
|
||||
snooze(500);
|
||||
cmd_reg = (*gPCI->read_pci_config)(card->info.bus, card->info.device,
|
||||
card->info.function, 0x44, 4);
|
||||
(*gPCI->write_pci_config)(card->info.bus, card->info.device,
|
||||
card->info.function, 0x44, 4, cmd_reg & 0xfffbffff);
|
||||
snooze(5000);
|
||||
|
||||
bval = io_read8(card, ALI_ACR2);
|
||||
bval |= 2;
|
||||
io_write8(card, ALI_ACR2, bval);
|
||||
snooze(5000);
|
||||
bval = io_read8(card, ALI_ACR2);
|
||||
bval &= 0xfd;
|
||||
io_write8(card, ALI_ACR2, bval);
|
||||
snooze(15000);
|
||||
|
||||
cmd_reg = 200;
|
||||
while (cmd_reg--) {
|
||||
val = ac97_read(card, AC97_REG_POWER);
|
||||
if ((val & 0xf) == 0xf)
|
||||
break;
|
||||
snooze(5000);
|
||||
}
|
||||
if (cmd_reg == 0xffffffff) {
|
||||
TRACE("hardware reset: not ready\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
hardware_init(ali_dev *card)
|
||||
{
|
||||
if (!hardware_reset(card))
|
||||
return B_ERROR;
|
||||
|
||||
LOCK(card->lock_hw);
|
||||
|
||||
io_write32(card, ALI_AINTEN_A, 0);
|
||||
// disable voice interrupts
|
||||
io_write32(card, ALI_AIN_A, 0xffffffff);
|
||||
// clr voice interrupt flags
|
||||
io_write32(card, ALI_VOL_A, 0);
|
||||
// master & wave volumes
|
||||
io_write8(card, ALI_MPUR2, 0x10);
|
||||
|
||||
io_write32(card, ALI_LFO_A, ALI_LFO_A_ENDLP_IE | ALI_LFO_A_MIDLP_IE);
|
||||
|
||||
UNLOCK(card->lock_hw);
|
||||
|
||||
if (!ac97_init(card))
|
||||
return B_ERROR;
|
||||
|
||||
TRACE("hardware init ok\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
hardware_terminate(ali_dev *card)
|
||||
{
|
||||
LOCK(card->lock_hw);
|
||||
|
||||
io_write32(card, ALI_AINTEN_A, 0);
|
||||
// disable voice interrupts
|
||||
io_write32(card, ALI_AIN_A, 0xffffffff);
|
||||
// clr voice interrupt flags
|
||||
|
||||
UNLOCK(card->lock_hw);
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
ali_read_int(ali_dev *card)
|
||||
{
|
||||
return io_read32(card, ALI_AIN_A);
|
||||
}
|
||||
|
||||
|
||||
/* voices handling */
|
||||
|
||||
|
||||
static uint16
|
||||
ali_delta_from_rate(uint32 rate, bool rec)
|
||||
{
|
||||
uint32 delta;
|
||||
|
||||
if (rate < 8000)
|
||||
rate = 8000;
|
||||
if (48000 < rate)
|
||||
rate = 48000;
|
||||
|
||||
switch (rate) {
|
||||
case 8000: delta = (rec?0x6000:0x2ab); break;
|
||||
case 44100: delta = (rec?0x116a:0xeb3); break;
|
||||
case 48000: delta = 0x1000; break;
|
||||
default:
|
||||
delta = (rec?((48000 << 12) / rate):(((rate << 12) + rate) / 48000));
|
||||
}
|
||||
|
||||
return (uint16) delta;
|
||||
}
|
||||
|
||||
|
||||
static uint32
|
||||
ali_control_mode(ali_stream *stream)
|
||||
{
|
||||
uint32 ctrl = 1;
|
||||
|
||||
if (util_format_is_signed(stream->format.format))
|
||||
ctrl |= 2;
|
||||
if (1 < stream->channels)
|
||||
ctrl |= 4;
|
||||
if (util_format_to_sample_size(stream->format.format) == 2)
|
||||
ctrl |= 8;
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ali_voice_start(ali_stream *stream)
|
||||
{
|
||||
LOCK(stream->card->lock_hw);
|
||||
|
||||
// setup per-voice registers
|
||||
|
||||
io_write8(stream->card, ALI_LFO_A, stream->used_voice);
|
||||
// select voice
|
||||
|
||||
io_write32(stream->card, ALI_CSO_ALPHA_FMS, 0);
|
||||
// cso = 0, alpha = 0
|
||||
io_write32(stream->card, ALI_PPTR_LBA, (uint32) stream->buffer->phy_base);
|
||||
// pptr & lba
|
||||
io_write32(stream->card, ALI_ESO_DELTA,
|
||||
((uint32) stream->buf_frames * stream->buf_count - 1) << 16
|
||||
| ali_delta_from_rate(stream->format.rate, stream->rec));
|
||||
// eso & delta
|
||||
|
||||
io_write32(stream->card, ALI_GVSEL_MISC,
|
||||
ali_control_mode(stream) << 12);
|
||||
// gvsel=0, pan=0, vol=0, ctrl, ec=0
|
||||
|
||||
// reset envelopes
|
||||
io_write32(stream->card, ALI_EBUF1, 0);
|
||||
io_write32(stream->card, ALI_EBUF2, 0);
|
||||
|
||||
UNLOCK(stream->card->lock_hw);
|
||||
|
||||
io_write32(stream->card, ALI_AIN_A, (uint32) 1 << stream->used_voice);
|
||||
// clr voice interrupt
|
||||
io_write32(stream->card, ALI_AINTEN_A, stream->card->used_voices);
|
||||
// enable interupt for voice
|
||||
|
||||
io_write32(stream->card, ALI_START_A, (uint32) 1 << stream->used_voice);
|
||||
// start voice
|
||||
|
||||
if (stream->rec)
|
||||
io_write32(stream->card, ALI_T_DIGIMIXER,
|
||||
(uint32) 1 << stream->used_voice);
|
||||
// enable special channel for recording
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ali_voice_stop(ali_stream *stream)
|
||||
{
|
||||
io_write32(stream->card, ALI_STOP_A, (uint32) 1 << stream->used_voice);
|
||||
|
||||
if (stream->rec)
|
||||
io_write32(stream->card, ALI_T_DIGIMIXER, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ali_voice_clr_int(ali_stream *stream)
|
||||
{
|
||||
io_write32(stream->card, ALI_AIN_A, (uint32) 1 << stream->used_voice);
|
||||
}
|
47
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.h
Normal file
47
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ALI_HARDWARE_H
|
||||
#define _ALI_HARDWARE_H
|
||||
|
||||
// AC97 codec registers
|
||||
|
||||
#define AC97_REG_RESET 0x00
|
||||
#define AC97_MIX_MASTER 0x02
|
||||
#define AC97_MIX_HEADPHONE 0x04
|
||||
#define AC97_MIX_MONO 0x06
|
||||
#define AC97_MIX_PHONE 0x0c
|
||||
#define AC97_MIX_MIC 0x0e
|
||||
#define AC97_MIX_LINEIN 0x10
|
||||
#define AC97_MIX_CD 0x12
|
||||
#define AC97_MIX_AUX 0x16
|
||||
#define AC97_MIX_PCM 0x18
|
||||
#define AC97_MIX_RECORDSELECT 0x1a
|
||||
#define AC97_MIX_RECORDGAIN 0x1c
|
||||
#define AC97_REG_GEN 0x20
|
||||
#define AC97_REG_POWER 0x26
|
||||
#define AC97_FRONT_DAC_SR 0x2c
|
||||
#define AC97_ADC_RATE 0x32
|
||||
#define AC97_JACK_SENSE 0x72
|
||||
#define AC97_REG_ID1 0x7c
|
||||
#define AC97_REG_ID2 0x7e
|
||||
|
||||
|
||||
uint16 ac97_read(ali_dev *card, uint8 reg);
|
||||
void ac97_write(ali_dev *card, uint8 reg, uint16 val);
|
||||
|
||||
status_t hardware_init(ali_dev *card);
|
||||
void hardware_terminate(ali_dev *card);
|
||||
|
||||
uint32 ali_read_int(ali_dev *card);
|
||||
|
||||
bool ali_voice_start(ali_stream *stream);
|
||||
void ali_voice_stop(ali_stream *stream);
|
||||
void ali_voice_clr_int(ali_stream *stream);
|
||||
|
||||
#endif // _ALI_HARDWARE_H
|
576
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.c
Normal file
576
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.c
Normal file
@ -0,0 +1,576 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <drivers/Drivers.h>
|
||||
#include <drivers/KernelExport.h>
|
||||
#include <drivers/PCI.h>
|
||||
#include <hmulti_audio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "driver.h"
|
||||
#include "util.h"
|
||||
#include "ali_hardware.h"
|
||||
#include "ali_multi.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#define ALI_SUPPORTED_OUTPUT_CHANNELS 2
|
||||
#define ALI_SUPPORTED_INPUT_CHANNELS 2
|
||||
|
||||
#define ALI_VALID_OUTPUT_SAMPLE_RATES (B_SR_8000 | B_SR_11025 | B_SR_12000 | \
|
||||
B_SR_16000 | B_SR_22050 | B_SR_24000 | B_SR_32000 | B_SR_44100 | B_SR_48000)
|
||||
#define ALI_VALID_OUTPUT_FORMATS (B_FMT_16BIT | B_FMT_8BIT_S | B_FMT_8BIT_U)
|
||||
#define ALI_VALID_INPUT_SAMPLE_RATES (B_SR_48000)
|
||||
#define ALI_VALID_INPUT_FORMATS ALI_VALID_OUTPUT_FORMATS
|
||||
|
||||
#define ALI_MIXER_GRANULARITY 1.5f
|
||||
|
||||
#define ALI_MIX_OUTPUT_ID 0x000
|
||||
#define ALI_MIX_INPUT_ID 0x100
|
||||
|
||||
#define ALI_MIX_GROUP_ID 1
|
||||
#define ALI_MIX_MUTE_ID 2
|
||||
#define ALI_MIX_LEFT_ID 3
|
||||
#define ALI_MIX_RIGHT_ID 4
|
||||
#define ALI_MIX_MONO_ID ALI_MIX_RIGHT_ID
|
||||
#define ALI_MIX_MIC_BOOST_ID 5
|
||||
#define ALI_MIX_INPUT_SELECTOR_ID 6
|
||||
#define ALI_MIX_RECORD_INPUT 15
|
||||
|
||||
#define ALI_MIX_FT_STEREO 1
|
||||
#define ALI_MIX_FT_MUTE 2
|
||||
#define ALI_MIX_FT_MICBST 4
|
||||
#define ALI_MIX_FT_GAIN 8
|
||||
#define ALI_MIX_FT_INSEL 16
|
||||
|
||||
typedef struct {
|
||||
int32 string_id;
|
||||
const char *name;
|
||||
uint8 reg;
|
||||
float min_gain;
|
||||
float max_gain;
|
||||
uint8 features;
|
||||
} ali_ac97_mixer_info;
|
||||
|
||||
#define ALI_OUTPUT_MIXERS 7
|
||||
#define ALI_INPUT_MIXERS 1
|
||||
#define ALI_ALL_MIXERS (ALI_OUTPUT_MIXERS + ALI_INPUT_MIXERS)
|
||||
|
||||
static const ali_ac97_mixer_info ali_ac97_mixers[ALI_ALL_MIXERS] = {
|
||||
{S_MASTER, NULL, AC97_MIX_MASTER, -46.5f, 0.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
|
||||
{S_WAVE, NULL, AC97_MIX_PCM, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
|
||||
{S_HEADPHONE, NULL, AC97_MIX_HEADPHONE, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
|
||||
{S_CD, NULL, AC97_MIX_CD, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
|
||||
{S_LINE, NULL, AC97_MIX_LINEIN, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
|
||||
{S_MIC, NULL, AC97_MIX_MIC, -34.5f, 12.0f, ALI_MIX_FT_MUTE | ALI_MIX_FT_MICBST},
|
||||
{S_AUX, NULL, AC97_MIX_AUX, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
|
||||
{S_null, "Record", AC97_MIX_RECORDGAIN, 0.0f, 22.5f,
|
||||
ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE | ALI_MIX_FT_GAIN | ALI_MIX_FT_INSEL},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int32 string_id;
|
||||
const char *name;
|
||||
} ali_ac97_mux_input_info;
|
||||
|
||||
#define ALI_MUX_INPUTS 7
|
||||
|
||||
static const ali_ac97_mux_input_info ali_ac97_mux_inputs[ALI_MUX_INPUTS] = {
|
||||
{S_MIC, NULL},
|
||||
{S_CD, NULL},
|
||||
{S_MUTE, NULL},
|
||||
{S_AUX, NULL},
|
||||
{S_LINE, NULL},
|
||||
{S_STEREO_MIX, NULL},
|
||||
{S_MONO_MIX, NULL},
|
||||
};
|
||||
|
||||
static const multi_channel_info channel_descriptions[] = {
|
||||
{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
|
||||
{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
|
||||
{ 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
|
||||
{ 3, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
|
||||
{ 4, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
|
||||
{ 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
|
||||
{ 6, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
|
||||
{ 7, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
|
||||
};
|
||||
|
||||
|
||||
static status_t
|
||||
get_description(void *cookie, multi_description *data)
|
||||
{
|
||||
data->interface_version = B_CURRENT_INTERFACE_VERSION;
|
||||
data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
|
||||
|
||||
strcpy(data->friendly_name, "ALI M5451");
|
||||
strcpy(data->vendor_info, "Krzysztof Ćwiertnia");
|
||||
|
||||
data->output_channel_count = ALI_SUPPORTED_OUTPUT_CHANNELS;
|
||||
data->input_channel_count = ALI_SUPPORTED_INPUT_CHANNELS;
|
||||
data->output_bus_channel_count = ALI_SUPPORTED_OUTPUT_CHANNELS;
|
||||
data->input_bus_channel_count = ALI_SUPPORTED_INPUT_CHANNELS;
|
||||
data->aux_bus_channel_count = 0;
|
||||
|
||||
if ((int32) (sizeof(channel_descriptions)/sizeof(channel_descriptions[0]))
|
||||
<= data->request_channel_count) {
|
||||
memcpy(data->channels, &channel_descriptions,
|
||||
sizeof(channel_descriptions));
|
||||
}
|
||||
|
||||
data->output_formats = ALI_VALID_OUTPUT_FORMATS;
|
||||
data->output_rates = ALI_VALID_OUTPUT_SAMPLE_RATES;
|
||||
|
||||
data->input_formats = ALI_VALID_INPUT_FORMATS;
|
||||
data->input_rates = ALI_VALID_INPUT_SAMPLE_RATES;
|
||||
|
||||
data->lock_sources = B_MULTI_LOCK_INTERNAL;
|
||||
data->timecode_sources = 0;
|
||||
data->interface_flags = B_MULTI_INTERFACE_PLAYBACK|B_MULTI_INTERFACE_RECORD;
|
||||
data->start_latency = 30000;
|
||||
|
||||
strcpy(data->control_panel,"");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
get_enabled_channels(ali_dev *card, multi_channel_enable *data)
|
||||
{
|
||||
B_SET_CHANNEL(data->enable_bits, 0, true);
|
||||
B_SET_CHANNEL(data->enable_bits, 1, true);
|
||||
B_SET_CHANNEL(data->enable_bits, 2, true);
|
||||
B_SET_CHANNEL(data->enable_bits, 3, true);
|
||||
|
||||
data->lock_source = B_MULTI_LOCK_INTERNAL;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
get_global_format(ali_dev *card, multi_format_info *data)
|
||||
{
|
||||
data->output_latency = 0;
|
||||
data->input_latency = 0;
|
||||
data->timecode_kind = 0;
|
||||
|
||||
data->output.format = card->global_output_format.format;
|
||||
data->output.rate = card->global_output_format.rate;
|
||||
|
||||
data->input.format = card->global_input_format.format;
|
||||
data->input.rate = card->global_input_format.rate;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
set_global_format(ali_dev *card, multi_format_info *data)
|
||||
{
|
||||
if ((ALI_VALID_OUTPUT_FORMATS & data->output.format) != 0)
|
||||
card->global_output_format.format = data->output.format;
|
||||
if ((ALI_VALID_OUTPUT_SAMPLE_RATES & data->output.rate) != 0)
|
||||
card->global_output_format.rate = data->output.rate;
|
||||
|
||||
if ((ALI_VALID_INPUT_FORMATS & data->input.format) != 0)
|
||||
card->global_input_format.format = data->input.format;
|
||||
if ((ALI_VALID_INPUT_SAMPLE_RATES & data->input.rate) != 0)
|
||||
card->global_input_format.rate = data->input.rate;
|
||||
|
||||
if (!ali_stream_prepare(card->playback_stream, 2,
|
||||
card->global_output_format.format,
|
||||
util_sample_rate_in_bits(card->global_output_format.rate)))
|
||||
return B_ERROR;
|
||||
if (!ali_stream_prepare(card->record_stream, 2,
|
||||
card->global_input_format.format,
|
||||
util_sample_rate_in_bits(card->global_input_format.rate)))
|
||||
return B_ERROR;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
get_mix_mixer(ali_dev *card, multi_mix_value *mmv, int32 id)
|
||||
{
|
||||
uint8 mixer_id = (uint8) id >> 4, gadget_id = (uint8) id & 0xf;
|
||||
const ali_ac97_mixer_info *mixer_info;
|
||||
|
||||
if (ALI_ALL_MIXERS <= mixer_id)
|
||||
return;
|
||||
mixer_info = &ali_ac97_mixers[mixer_id];
|
||||
|
||||
switch (gadget_id) {
|
||||
case ALI_MIX_MUTE_ID:
|
||||
mmv->u.enable = ((ac97_read(card, mixer_info->reg) & 0x8000)
|
||||
== 0x8000);
|
||||
break;
|
||||
|
||||
case ALI_MIX_LEFT_ID:
|
||||
case ALI_MIX_RIGHT_ID:
|
||||
{
|
||||
float val = (ALI_MIXER_GRANULARITY
|
||||
* ((ac97_read(card, mixer_info->reg) >>
|
||||
((gadget_id==ALI_MIX_LEFT_ID)?8:0)) & 0x1f));
|
||||
mmv->u.gain = ((mixer_info->features&ALI_MIX_FT_GAIN)
|
||||
? mixer_info->min_gain+val:mixer_info->max_gain-val);
|
||||
}
|
||||
break;
|
||||
|
||||
case ALI_MIX_MIC_BOOST_ID:
|
||||
mmv->u.enable = ((ac97_read(card, mixer_info->reg) & 0x40) == 0x40);
|
||||
break;
|
||||
|
||||
case ALI_MIX_INPUT_SELECTOR_ID:
|
||||
mmv->u.mux = (ac97_read(card, AC97_MIX_RECORDSELECT) & 0x7);
|
||||
if (ALI_MUX_INPUTS <= mmv->u.mux) {
|
||||
mmv->u.mux = 0x2;
|
||||
ac97_write(card, AC97_MIX_RECORDSELECT, 0x202);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
get_mix(ali_dev *card, multi_mix_value_info *mmvi)
|
||||
{
|
||||
int32 i;
|
||||
|
||||
for (i = 0; i < mmvi->item_count; i++)
|
||||
get_mix_mixer(card, &(mmvi->values[i]),
|
||||
mmvi->values[i].id-MULTI_AUDIO_BASE_ID);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_mix_mixer(ali_dev *card, multi_mix_value *mmv, int32 id)
|
||||
{
|
||||
uint8 mixer_id = (uint8) id >> 4, gadget_id = (uint8) id & 0xf;
|
||||
const ali_ac97_mixer_info *mixer_info;
|
||||
uint16 val;
|
||||
|
||||
if (ALI_ALL_MIXERS <= mixer_id)
|
||||
return;
|
||||
mixer_info = &ali_ac97_mixers[mixer_id];
|
||||
|
||||
switch (gadget_id) {
|
||||
case ALI_MIX_MUTE_ID:
|
||||
val = ac97_read(card, mixer_info->reg);
|
||||
val = mmv->u.enable ? val | 0x8000 : val & 0x7fff;
|
||||
ac97_write(card, mixer_info->reg, val);
|
||||
break;
|
||||
|
||||
case ALI_MIX_LEFT_ID:
|
||||
case ALI_MIX_RIGHT_ID:
|
||||
val = ac97_read(card, mixer_info->reg);
|
||||
val &= (gadget_id == ALI_MIX_LEFT_ID) ? 0x805f : 0x9f00;
|
||||
val |= (uint16) (((mixer_info->features&ALI_MIX_FT_GAIN)
|
||||
? mixer_info->min_gain+mmv->u.gain
|
||||
: mixer_info->max_gain-mmv->u.gain)
|
||||
/ ALI_MIXER_GRANULARITY) <<
|
||||
((gadget_id == ALI_MIX_LEFT_ID) ? 8 : 0);
|
||||
ac97_write(card, mixer_info->reg, val);
|
||||
break;
|
||||
|
||||
case ALI_MIX_MIC_BOOST_ID:
|
||||
val = ac97_read(card, mixer_info->reg);
|
||||
val = mmv->u.enable ? val | 0x40 : val & 0xffbf;
|
||||
ac97_write(card, mixer_info->reg, val);
|
||||
break;
|
||||
|
||||
case ALI_MIX_INPUT_SELECTOR_ID:
|
||||
val = (mmv->u.mux << 8) | mmv->u.mux;
|
||||
ac97_write(card, AC97_MIX_RECORDSELECT, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
set_mix(ali_dev *card, multi_mix_value_info *mmvi)
|
||||
{
|
||||
int32 i;
|
||||
|
||||
for (i = 0; i < mmvi->item_count; i++)
|
||||
set_mix_mixer(card, &(mmvi->values[i]),
|
||||
mmvi->values[i].id-MULTI_AUDIO_BASE_ID);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
create_group_control(multi_mix_control *multi, int32 *idx, int32 id,
|
||||
int32 parent, int32 string_id, const char *name)
|
||||
{
|
||||
multi_mix_control *control = multi + *idx;
|
||||
|
||||
control->id = MULTI_AUDIO_BASE_ID + id;
|
||||
control->parent = parent;
|
||||
control->flags = B_MULTI_MIX_GROUP;
|
||||
control->master = MULTI_AUDIO_MASTER_ID;
|
||||
control->string = string_id;
|
||||
if (string_id == S_null && name)
|
||||
strcpy(control->name, name);
|
||||
|
||||
(*idx)++;
|
||||
|
||||
return control->id;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
create_vol_control(multi_mix_control *multi, int32 *idx, int32 id, int32 parent)
|
||||
{
|
||||
const ali_ac97_mixer_info *mixer_info;
|
||||
multi_mix_control *control;
|
||||
|
||||
id &= 0xf;
|
||||
|
||||
if (ALI_ALL_MIXERS <= id)
|
||||
return;
|
||||
mixer_info = &ali_ac97_mixers[id];
|
||||
|
||||
id <<= 4;
|
||||
|
||||
parent = create_group_control(multi, idx, (parent & 0x100) | id
|
||||
| ALI_MIX_GROUP_ID, parent, mixer_info->string_id, mixer_info->name);
|
||||
|
||||
control = multi + *idx;
|
||||
|
||||
if (mixer_info->features & ALI_MIX_FT_MUTE) {
|
||||
control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id
|
||||
| ALI_MIX_MUTE_ID;
|
||||
control->parent = parent;
|
||||
control->flags = B_MULTI_MIX_ENABLE;
|
||||
control->master = MULTI_AUDIO_MASTER_ID;
|
||||
control->string = S_MUTE;
|
||||
|
||||
control++;
|
||||
(*idx)++;
|
||||
}
|
||||
|
||||
control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id
|
||||
| ((mixer_info->features&ALI_MIX_FT_STEREO)
|
||||
?ALI_MIX_LEFT_ID:ALI_MIX_MONO_ID);
|
||||
control->parent = parent;
|
||||
control->flags = B_MULTI_MIX_GAIN;
|
||||
control->master = MULTI_AUDIO_MASTER_ID;
|
||||
control->string = S_GAIN;
|
||||
// S_null; ? FIXME
|
||||
|
||||
control->u.gain.min_gain = mixer_info->min_gain;
|
||||
control->u.gain.max_gain = mixer_info->max_gain;
|
||||
control->u.gain.granularity = ALI_MIXER_GRANULARITY;
|
||||
|
||||
if (mixer_info->features & ALI_MIX_FT_STEREO) {
|
||||
control[1] = control[0];
|
||||
control++;
|
||||
(*idx)++;
|
||||
|
||||
control->master = control->id;
|
||||
control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id
|
||||
| ALI_MIX_RIGHT_ID;
|
||||
}
|
||||
|
||||
control++;
|
||||
(*idx)++;
|
||||
|
||||
if (mixer_info->features & ALI_MIX_FT_MICBST) {
|
||||
control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id
|
||||
| ALI_MIX_MIC_BOOST_ID;
|
||||
control->parent = parent;
|
||||
control->flags = B_MULTI_MIX_ENABLE;
|
||||
control->master = MULTI_AUDIO_MASTER_ID;
|
||||
control->string = S_null;
|
||||
strcpy(control->name, "+20dB");
|
||||
|
||||
control++;
|
||||
(*idx)++;
|
||||
}
|
||||
|
||||
if (mixer_info->features & ALI_MIX_FT_INSEL) {
|
||||
int32 is_parent;
|
||||
const ali_ac97_mux_input_info *mux_input;
|
||||
|
||||
is_parent = control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100)
|
||||
| id | ALI_MIX_INPUT_SELECTOR_ID;
|
||||
control->parent = parent;
|
||||
control->flags = B_MULTI_MIX_MUX;
|
||||
control->master = MULTI_AUDIO_MASTER_ID;
|
||||
control->string = S_INPUT;
|
||||
|
||||
control++;
|
||||
(*idx)++;
|
||||
|
||||
for (id = 0; id < ALI_MUX_INPUTS; id++) {
|
||||
mux_input = &ali_ac97_mux_inputs[id];
|
||||
|
||||
control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | (id<<4)
|
||||
| ALI_MIX_RECORD_INPUT;
|
||||
control->parent = is_parent;
|
||||
control->flags = B_MULTI_MIX_MUX_VALUE;
|
||||
control->master = MULTI_AUDIO_MASTER_ID;
|
||||
control->string = mux_input->string_id;
|
||||
if (control->string == S_null && mux_input->name)
|
||||
strcpy(control->name, mux_input->name);
|
||||
|
||||
control++;
|
||||
(*idx)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
list_mix_controls(ali_dev *card, multi_mix_control_info *mmci)
|
||||
{
|
||||
int32 index = 0, parent, i;
|
||||
|
||||
parent = create_group_control(mmci->controls, &index, ALI_MIX_OUTPUT_ID, 0,
|
||||
S_OUTPUT, NULL);
|
||||
for (i = 0; i < ALI_OUTPUT_MIXERS; i++)
|
||||
create_vol_control(mmci->controls, &index, i, parent);
|
||||
|
||||
parent = create_group_control(mmci->controls, &index, ALI_MIX_INPUT_ID, 0,
|
||||
S_INPUT, NULL);
|
||||
for (i = ALI_OUTPUT_MIXERS; i < ALI_OUTPUT_MIXERS + ALI_INPUT_MIXERS; i++)
|
||||
create_vol_control(mmci->controls, &index, i, parent);
|
||||
|
||||
mmci->control_count = index;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
get_buffers(ali_dev *card, multi_buffer_list *data)
|
||||
{
|
||||
uint8 buf_ndx, ch_ndx;
|
||||
|
||||
data->return_playback_buffers = card->playback_stream->buf_count;
|
||||
data->return_playback_channels = card->playback_stream->channels;
|
||||
data->return_playback_buffer_size = card->playback_stream->buf_frames;
|
||||
|
||||
for (buf_ndx=0; buf_ndx<card->playback_stream->buf_count; buf_ndx++)
|
||||
for (ch_ndx = 0; ch_ndx < card->playback_stream->channels; ch_ndx++)
|
||||
ali_stream_get_buffer_part(card->playback_stream, ch_ndx, buf_ndx,
|
||||
&data->playback_buffers[buf_ndx][ch_ndx].base,
|
||||
&data->playback_buffers[buf_ndx][ch_ndx].stride);
|
||||
|
||||
data->return_record_buffers = card->record_stream->buf_count;
|
||||
data->return_record_channels = card->record_stream->channels;
|
||||
data->return_record_buffer_size = card->record_stream->buf_frames;
|
||||
|
||||
for (buf_ndx = 0; buf_ndx < card->record_stream->buf_count; buf_ndx++)
|
||||
for (ch_ndx = 0; ch_ndx < card->record_stream->channels; ch_ndx++)
|
||||
ali_stream_get_buffer_part(card->record_stream, ch_ndx, buf_ndx,
|
||||
&data->record_buffers[buf_ndx][ch_ndx].base,
|
||||
&data->record_buffers[buf_ndx][ch_ndx].stride);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
buffer_exchange(ali_dev *card, multi_buffer_info *buffer_info)
|
||||
{
|
||||
status_t res;
|
||||
ali_stream *play_s, *rec_s;
|
||||
|
||||
play_s = card->playback_stream;
|
||||
rec_s = card->record_stream;
|
||||
|
||||
if (!play_s || !rec_s)
|
||||
return B_ERROR;
|
||||
|
||||
if (!play_s->is_playing)
|
||||
if (!ali_stream_start(play_s))
|
||||
return B_ERROR;
|
||||
if (!rec_s->is_playing)
|
||||
if (!ali_stream_start(rec_s))
|
||||
return B_ERROR;
|
||||
|
||||
res = acquire_sem_etc(card->sem_buf_ready, 1, B_CAN_INTERRUPT, 0);
|
||||
if (res != B_OK) {
|
||||
TRACE("buffer_exchange: error acquiring semaphore\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
LOCK(card->lock_sts);
|
||||
|
||||
buffer_info->played_frames_count = play_s->frames_count;
|
||||
buffer_info->played_real_time = play_s->real_time;
|
||||
buffer_info->playback_buffer_cycle = play_s->buffer_cycle;
|
||||
|
||||
buffer_info->recorded_frames_count = rec_s->frames_count;
|
||||
buffer_info->recorded_real_time = rec_s->real_time;
|
||||
buffer_info->record_buffer_cycle = rec_s->buffer_cycle;
|
||||
|
||||
UNLOCK(card->lock_sts);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
force_stop(ali_dev *card)
|
||||
{
|
||||
ali_stream *stream;
|
||||
|
||||
TRACE("force_stop\n");
|
||||
|
||||
LIST_FOREACH(stream, &card->streams, next) {
|
||||
ali_stream_stop(stream);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
multi_audio_control(void *cookie, uint32 op, void *arg, size_t len)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MULTI_GET_DESCRIPTION: return get_description(cookie, arg);
|
||||
|
||||
case B_MULTI_GET_EVENT_INFO: return B_ERROR;
|
||||
case B_MULTI_SET_EVENT_INFO: return B_ERROR;
|
||||
case B_MULTI_GET_EVENT: return B_ERROR;
|
||||
|
||||
case B_MULTI_GET_ENABLED_CHANNELS: return get_enabled_channels(cookie, arg);
|
||||
case B_MULTI_SET_ENABLED_CHANNELS: return B_OK;
|
||||
case B_MULTI_GET_GLOBAL_FORMAT: return get_global_format(cookie, arg);
|
||||
case B_MULTI_SET_GLOBAL_FORMAT: return set_global_format(cookie, arg);
|
||||
|
||||
case B_MULTI_GET_CHANNEL_FORMATS: return B_ERROR;
|
||||
case B_MULTI_SET_CHANNEL_FORMATS: return B_ERROR;
|
||||
case B_MULTI_GET_MIX: return get_mix(cookie, arg);
|
||||
case B_MULTI_SET_MIX: return set_mix(cookie, arg);
|
||||
|
||||
case B_MULTI_LIST_MIX_CHANNELS: return B_ERROR;
|
||||
case B_MULTI_LIST_MIX_CONTROLS: return list_mix_controls(cookie, arg);
|
||||
case B_MULTI_LIST_MIX_CONNECTIONS: return B_ERROR;
|
||||
|
||||
case B_MULTI_GET_BUFFERS: return get_buffers(cookie, arg);
|
||||
case B_MULTI_SET_BUFFERS: return B_ERROR;
|
||||
// do not support in-software buffers
|
||||
|
||||
case B_MULTI_SET_START_TIME: return B_ERROR;
|
||||
case B_MULTI_BUFFER_EXCHANGE: return buffer_exchange(cookie, arg);
|
||||
case B_MULTI_BUFFER_FORCE_STOP: return force_stop(cookie);
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
14
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.h
Normal file
14
src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.h
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ALI_MULTI_H
|
||||
#define _ALI_MULTI_H
|
||||
|
||||
status_t multi_audio_control(void *cookie, uint32 op, void *arg, size_t len);
|
||||
|
||||
#endif // _ALI_MULTI_H
|
24
src/add-ons/kernel/drivers/audio/ac97/ali5451/debug.h
Normal file
24
src/add-ons/kernel/drivers/audio/ac97/ali5451/debug.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ALI_DEBUG_H
|
||||
#define _ALI_DEBUG_H
|
||||
|
||||
#ifdef TRACE
|
||||
#undef TRACE
|
||||
#endif
|
||||
|
||||
#define TRACE_ALI
|
||||
|
||||
#ifdef TRACE_ALI
|
||||
#define TRACE(a...) dprintf("\33[34mali5451:\33[0m " a)
|
||||
#else
|
||||
#define TRACE(a...)
|
||||
#endif
|
||||
|
||||
#endif // _ALI_DEBUG_H
|
543
src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.c
Normal file
543
src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.c
Normal file
@ -0,0 +1,543 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <drivers/Drivers.h>
|
||||
#include <drivers/KernelExport.h>
|
||||
#include <drivers/PCI.h>
|
||||
#include <hmulti_audio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "ali_multi.h"
|
||||
#include "driver.h"
|
||||
#include "util.h"
|
||||
#include "ali_hardware.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||
|
||||
int32 gCardsCount;
|
||||
ali_dev gCards[MAX_CARDS];
|
||||
pci_module_info *gPCI;
|
||||
|
||||
|
||||
/* streams handling */
|
||||
|
||||
|
||||
static ali_stream *
|
||||
ali_stream_new(ali_dev *card, bool rec, uint8 buf_count)
|
||||
{
|
||||
ali_stream *stream;
|
||||
|
||||
if (!buf_count)
|
||||
return NULL;
|
||||
|
||||
stream = malloc(sizeof(ali_stream));
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
stream->card = card;
|
||||
|
||||
stream->buffer = NULL;
|
||||
|
||||
stream->buf_count = buf_count;
|
||||
stream->buf_frames = 0;
|
||||
|
||||
stream->rec = rec;
|
||||
stream->channels = 0;
|
||||
stream->format.format = 0;
|
||||
stream->format.rate = 0;
|
||||
|
||||
stream->is_playing = false;
|
||||
|
||||
stream->used_voice = -1;
|
||||
|
||||
stream->frames_count = 0;
|
||||
stream->real_time = 0;
|
||||
stream->buffer_cycle = (rec?buf_count-1:0);
|
||||
|
||||
LOCK(card->lock_sts);
|
||||
LIST_INSERT_HEAD(&(card->streams), stream, next);
|
||||
UNLOCK(card->lock_sts);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ali_stream_unprepare(ali_stream *stream)
|
||||
{
|
||||
ali_stream_stop(stream);
|
||||
|
||||
if (stream->buffer) {
|
||||
ali_mem_free(stream->card, stream->buffer->log_base);
|
||||
stream->buffer = NULL;
|
||||
}
|
||||
|
||||
stream->channels = 0;
|
||||
stream->format.format = 0;
|
||||
stream->format.rate = 0;
|
||||
|
||||
if (-1 < stream->used_voice) {
|
||||
LOCK(stream->card->lock_voices);
|
||||
stream->card->used_voices &= ~((uint32) 1 << stream->used_voice);
|
||||
UNLOCK(stream->card->lock_voices);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ali_stream_delete(ali_stream *stream)
|
||||
{
|
||||
ali_stream_unprepare(stream);
|
||||
|
||||
LOCK(stream->card->lock_sts);
|
||||
LIST_REMOVE(stream, next);
|
||||
UNLOCK(stream->card->lock_sts);
|
||||
|
||||
free(stream);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ali_stream_prepare(ali_stream *stream, uint8 channels, uint32 format,
|
||||
uint32 sample_rate)
|
||||
{
|
||||
uint8 frame_size = util_format_to_sample_size(format) * channels, ch;
|
||||
|
||||
TRACE("stream_prepare: frame_size: %d\n", (int) frame_size);
|
||||
|
||||
if (!frame_size)
|
||||
return false;
|
||||
|
||||
if (stream->channels == channels && stream->format.format == format &&
|
||||
stream->format.rate == sample_rate)
|
||||
return true;
|
||||
|
||||
ali_stream_unprepare(stream);
|
||||
|
||||
stream->channels = channels;
|
||||
stream->format.format = format;
|
||||
stream->format.rate = sample_rate;
|
||||
|
||||
// find and set voice
|
||||
LOCK(stream->card->lock_voices);
|
||||
if (stream->rec) {
|
||||
if ((stream->card->used_voices & (uint32) 1 << ALI_RECORD_VOICE) == 0) {
|
||||
stream->used_voice = ALI_RECORD_VOICE;
|
||||
stream->card->used_voices |= (uint32) 1 << ALI_RECORD_VOICE;
|
||||
}
|
||||
} else {
|
||||
for (ch = 0; ch < ALI_VOICES; ch++) {
|
||||
if ((stream->card->used_voices & (uint32) 1 << ch) == 0) {
|
||||
stream->used_voice = ch;
|
||||
stream->card->used_voices |= (uint32) 1 << ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
UNLOCK(stream->card->lock_voices);
|
||||
|
||||
if (stream->used_voice == -1) {
|
||||
ali_stream_unprepare(stream);
|
||||
TRACE("stream_prepare: no free voices\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream->buf_frames = util_get_buffer_length_for_rate(stream->format.rate);
|
||||
|
||||
stream->buffer = ali_mem_alloc(stream->card, stream->buf_frames
|
||||
* frame_size * stream->buf_count);
|
||||
if (!stream->buffer) {
|
||||
ali_stream_unprepare(stream);
|
||||
TRACE("stream_prepare: failed to create buffer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ali_stream_start(ali_stream *stream)
|
||||
{
|
||||
if (stream->is_playing)
|
||||
return true;
|
||||
|
||||
if (!ali_voice_start(stream)) {
|
||||
TRACE("stream_start: trouble starting voice\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream->is_playing = true;
|
||||
|
||||
TRACE("stream_start: %s using voice: %d\n",
|
||||
stream->rec?"recording":"playing", stream->used_voice);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ali_stream_stop(ali_stream *stream)
|
||||
{
|
||||
if (!stream->is_playing)
|
||||
return;
|
||||
|
||||
ali_voice_stop(stream);
|
||||
|
||||
stream->is_playing = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ali_stream_get_buffer_part(ali_stream *stream, uint8 ch_ndx, uint8 buf_ndx,
|
||||
char **buffer, size_t *stride)
|
||||
{
|
||||
uint8 sample_size = util_format_to_sample_size(stream->format.format),
|
||||
frame_size = sample_size * stream->channels;
|
||||
|
||||
*buffer = stream->buffer->log_base + frame_size * stream->buf_frames
|
||||
* buf_ndx + sample_size * ch_ndx;
|
||||
*stride = frame_size;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
ali_interrupt_handler(void *cookie)
|
||||
{
|
||||
ali_dev *card = (ali_dev *) cookie;
|
||||
bool handled = false;
|
||||
uint32 aina;
|
||||
ali_stream *stream;
|
||||
|
||||
aina = ali_read_int(card);
|
||||
if (!aina)
|
||||
goto exit_;
|
||||
|
||||
LIST_FOREACH(stream, &card->streams, next) {
|
||||
if (!stream->is_playing || stream->used_voice == -1)
|
||||
continue;
|
||||
|
||||
if ((aina & (uint32) 1 << stream->used_voice) == 0)
|
||||
continue;
|
||||
|
||||
ali_voice_clr_int(stream);
|
||||
// clear interrupt flag
|
||||
|
||||
// interrupts are disabled already when entering handler, lock only
|
||||
acquire_spinlock(&card->lock_sts);
|
||||
stream->frames_count += stream->buf_frames;
|
||||
stream->real_time = system_time();
|
||||
stream->buffer_cycle = (stream->buffer_cycle + 1) % stream->buf_count;
|
||||
release_spinlock(&card->lock_sts);
|
||||
|
||||
release_sem_etc(card->sem_buf_ready, 1, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
handled = true;
|
||||
}
|
||||
|
||||
exit_:
|
||||
if (!handled)
|
||||
TRACE("unhandled interrupt\n");
|
||||
|
||||
return handled ? B_INVOKE_SCHEDULER : B_UNHANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
make_device_names(ali_dev *card)
|
||||
{
|
||||
sprintf(card->name, DEVFS_PATH_FORMAT, card-gCards+1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ali_terminate(ali_dev *card)
|
||||
{
|
||||
TRACE("terminate\n");
|
||||
|
||||
remove_io_interrupt_handler(card->irq, ali_interrupt_handler, card);
|
||||
|
||||
if (card->sem_buf_ready != -1)
|
||||
delete_sem(card->sem_buf_ready);
|
||||
|
||||
hardware_terminate(card);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ali_setup(ali_dev *card)
|
||||
{
|
||||
uint32 cmd_reg;
|
||||
status_t res;
|
||||
|
||||
make_device_names(card);
|
||||
|
||||
cmd_reg = (*gPCI->read_pci_config)(card->info.bus, card->info.device,
|
||||
card->info.function, PCI_command, 2);
|
||||
cmd_reg |= PCI_command_io | PCI_command_memory | PCI_command_master;
|
||||
(*gPCI->write_pci_config)(card->info.bus, card->info.device,
|
||||
card->info.function, PCI_command, 2, cmd_reg);
|
||||
|
||||
card->io_base = card->info.u.h0.base_registers[0];
|
||||
TRACE("setup: base io is: %08x\n", (unsigned int) card->io_base);
|
||||
|
||||
card->irq = card->info.u.h0.interrupt_line;
|
||||
TRACE("setup: irq is: %02x\n", card->irq);
|
||||
|
||||
card->lock_hw = 0;
|
||||
card->lock_sts = 0;
|
||||
card->lock_voices = 0;
|
||||
|
||||
card->sem_buf_ready = create_sem(0, "ali5451_buf_ready");
|
||||
if (card->sem_buf_ready < B_OK) {
|
||||
TRACE("setup: failed to create semaphore\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
TRACE("setup: revision: %02x\n", card->info.revision);
|
||||
|
||||
card->playback_stream = NULL;
|
||||
card->record_stream = NULL;
|
||||
|
||||
card->used_voices = 0;
|
||||
|
||||
card->global_output_format.format = B_FMT_16BIT;
|
||||
card->global_output_format.rate = B_SR_48000;
|
||||
card->global_input_format = card->global_output_format;
|
||||
|
||||
res = install_io_interrupt_handler(card->irq, ali_interrupt_handler, card,
|
||||
0);
|
||||
if (res != B_OK) {
|
||||
TRACE("setup: cannot install interrupt handler\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = hardware_init(card);
|
||||
if (res != B_OK)
|
||||
ali_terminate(card);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* driver's public functions */
|
||||
|
||||
|
||||
status_t
|
||||
init_hardware(void)
|
||||
{
|
||||
status_t res = ENODEV;
|
||||
pci_info info;
|
||||
int32 i;
|
||||
|
||||
if (get_module(B_PCI_MODULE_NAME, (module_info**) &gPCI) != B_OK)
|
||||
return res;
|
||||
|
||||
for (i=0; (*gPCI->get_nth_pci_info)(i, &info) == B_OK; i++) {
|
||||
if (info.class_base == PCI_multimedia &&
|
||||
info.vendor_id == ALI_VENDOR_ID &&
|
||||
info.device_id == ALI_DEVICE_ID) {
|
||||
TRACE("hardware found\n");
|
||||
res = B_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
init_driver(void)
|
||||
{
|
||||
pci_info info;
|
||||
int32 i;
|
||||
|
||||
if (get_module(B_PCI_MODULE_NAME, (module_info**) &gPCI) != B_OK)
|
||||
return ENODEV;
|
||||
|
||||
gCardsCount = 0;
|
||||
|
||||
for (i = 0; (*gPCI->get_nth_pci_info)(i, &info) == B_OK; i++) {
|
||||
if (info.class_base == PCI_multimedia &&
|
||||
info.vendor_id == ALI_VENDOR_ID &&
|
||||
info.device_id == ALI_DEVICE_ID) {
|
||||
if (gCardsCount == MAX_CARDS) {
|
||||
TRACE("init_driver: too many cards\n");
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&gCards[gCardsCount], 0, sizeof(ali_dev));
|
||||
gCards[gCardsCount].info = info;
|
||||
if (ali_setup(&gCards[gCardsCount]))
|
||||
TRACE("init_driver: setup of ali %ld failed\n", gCardsCount+1);
|
||||
else
|
||||
gCardsCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gCardsCount) {
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
TRACE("init_driver: no cards found\n");
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
uninit_driver(void)
|
||||
{
|
||||
int32 i = gCardsCount;
|
||||
|
||||
while (i--)
|
||||
ali_terminate(&gCards[i]);
|
||||
memset(&gCards, 0, sizeof(gCards));
|
||||
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
|
||||
gCardsCount = 0;
|
||||
}
|
||||
|
||||
|
||||
const char **
|
||||
publish_devices(void)
|
||||
{
|
||||
static const char *names[MAX_CARDS+1];
|
||||
int32 i;
|
||||
|
||||
for (i = 0; i < gCardsCount; i++)
|
||||
names[i] = gCards[i].name;
|
||||
names[gCardsCount] = NULL;
|
||||
|
||||
return (const char **) names;
|
||||
}
|
||||
|
||||
|
||||
/* driver's hooks */
|
||||
|
||||
|
||||
static status_t
|
||||
ali_audio_open(const char *name, uint32 flags, void **cookie)
|
||||
{
|
||||
ali_dev *card = NULL;
|
||||
int32 i;
|
||||
|
||||
for (i = 0; i < gCardsCount; i++) {
|
||||
if (!strcmp(gCards[i].name, name)) {
|
||||
card = &gCards[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!card) {
|
||||
TRACE("audio_open: card not found\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (card->playback_stream || card->record_stream)
|
||||
return B_ERROR;
|
||||
|
||||
*cookie = card;
|
||||
|
||||
card->playback_stream = ali_stream_new(card, false, ALI_BUF_CNT);
|
||||
card->record_stream = ali_stream_new(card, true, ALI_BUF_CNT);
|
||||
|
||||
if (!ali_stream_prepare(card->playback_stream, 2,
|
||||
card->global_output_format.format,
|
||||
util_sample_rate_in_bits(card->global_output_format.rate)))
|
||||
return B_ERROR;
|
||||
if (!ali_stream_prepare(card->record_stream, 2,
|
||||
card->global_input_format.format,
|
||||
util_sample_rate_in_bits(card->global_input_format.rate)))
|
||||
return B_ERROR;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ali_audio_read(void *cookie, off_t a, void *b, size_t *num_bytes)
|
||||
{
|
||||
TRACE("audio_read attempt\n");
|
||||
|
||||
*num_bytes = 0;
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ali_audio_write(void *cookie, off_t a, const void *b, size_t *num_bytes)
|
||||
{
|
||||
TRACE("audio_write attempt\n");
|
||||
|
||||
*num_bytes = 0;
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ali_audio_control(void *cookie, uint32 op, void *arg, size_t len)
|
||||
{
|
||||
if (!cookie)
|
||||
TRACE("audio_control: no cookie\n");
|
||||
|
||||
return cookie?multi_audio_control(cookie, op, arg, len):B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ali_audio_close(void *cookie)
|
||||
{
|
||||
ali_dev *card = (ali_dev *) cookie;
|
||||
ali_stream *stream;
|
||||
|
||||
LIST_FOREACH(stream, &card->streams, next) {
|
||||
ali_stream_stop(stream);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ali_audio_free(void *cookie)
|
||||
{
|
||||
ali_dev *card = (ali_dev *) cookie;
|
||||
|
||||
while (!LIST_EMPTY(&card->streams))
|
||||
ali_stream_delete(LIST_FIRST(&card->streams));
|
||||
|
||||
card->playback_stream = NULL;
|
||||
card->record_stream = NULL;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
device_hooks driver_hooks = {
|
||||
ali_audio_open,
|
||||
ali_audio_close,
|
||||
ali_audio_free,
|
||||
ali_audio_control,
|
||||
ali_audio_read,
|
||||
ali_audio_write
|
||||
};
|
||||
|
||||
|
||||
device_hooks *
|
||||
find_device(const char *name)
|
||||
{
|
||||
return &driver_hooks;
|
||||
}
|
110
src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.h
Normal file
110
src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ALI_AUDIO_DRIVER_H
|
||||
#define _ALI_AUDIO_DRIVER_H
|
||||
|
||||
#define MULTI_AUDIO_BASE_ID 1024
|
||||
#define MULTI_AUDIO_DEV_PATH "audio/hmulti"
|
||||
#define MULTI_AUDIO_MASTER_ID 0
|
||||
|
||||
#define DEVFS_PATH_FORMAT MULTI_AUDIO_DEV_PATH "/ali5451/%lu"
|
||||
|
||||
#define MAX_CARDS 3
|
||||
#define DEVNAME 32
|
||||
|
||||
#define ALI_VENDOR_ID 0x10b9
|
||||
#define ALI_DEVICE_ID 0x5451
|
||||
|
||||
#define ALI_BUF_CNT 2
|
||||
#define ALI_MAX_CHN 2
|
||||
#define ALI_VOICES 32
|
||||
#define ALI_RECORD_VOICE 31
|
||||
|
||||
typedef struct _ali_mem
|
||||
{
|
||||
LIST_ENTRY(_ali_mem) next;
|
||||
|
||||
void *log_base;
|
||||
void *phy_base;
|
||||
|
||||
area_id area;
|
||||
size_t size;
|
||||
} ali_mem;
|
||||
|
||||
typedef struct {
|
||||
uint32 format;
|
||||
uint32 rate;
|
||||
} ali_format;
|
||||
|
||||
typedef struct _ali_stream
|
||||
{
|
||||
LIST_ENTRY(_ali_stream) next;
|
||||
|
||||
struct _ali_dev *card;
|
||||
|
||||
ali_mem *buffer;
|
||||
|
||||
uint8 buf_count;
|
||||
uint32 buf_frames;
|
||||
|
||||
bool rec;
|
||||
uint8 channels;
|
||||
ali_format format;
|
||||
|
||||
bool is_playing;
|
||||
|
||||
int8 used_voice;
|
||||
|
||||
// multi_audio
|
||||
volatile int64 frames_count;
|
||||
volatile bigtime_t real_time;
|
||||
volatile int32 buffer_cycle;
|
||||
} ali_stream;
|
||||
|
||||
typedef struct _ali_dev
|
||||
{
|
||||
char name[DEVNAME];
|
||||
pci_info info;
|
||||
ulong io_base;
|
||||
uint8 irq;
|
||||
|
||||
spinlock lock_hw;
|
||||
spinlock lock_sts;
|
||||
spinlock lock_voices;
|
||||
sem_id sem_buf_ready;
|
||||
|
||||
ali_stream *playback_stream;
|
||||
ali_stream *record_stream;
|
||||
|
||||
uint32 used_voices;
|
||||
|
||||
ali_format global_output_format;
|
||||
ali_format global_input_format;
|
||||
|
||||
LIST_HEAD(, _ali_stream) streams;
|
||||
LIST_HEAD(, _ali_mem) mems;
|
||||
} ali_dev;
|
||||
|
||||
#define LOCK(_lock) \
|
||||
{ \
|
||||
cpu_status cp = disable_interrupts(); \
|
||||
acquire_spinlock(&_lock)
|
||||
#define UNLOCK(_lock) \
|
||||
release_spinlock(&_lock); \
|
||||
restore_interrupts(cp); \
|
||||
}
|
||||
|
||||
bool ali_stream_prepare(ali_stream *stream, uint8 channels, uint32 format,
|
||||
uint32 sample_rate);
|
||||
bool ali_stream_start(ali_stream *stream);
|
||||
void ali_stream_stop(ali_stream *stream);
|
||||
void ali_stream_get_buffer_part(ali_stream *stream, uint8 ch_ndx, uint8 buf_ndx,
|
||||
char ** buffer, size_t *stride);
|
||||
|
||||
#endif // _ALI_AUDIO_DRIVER_H
|
529
src/add-ons/kernel/drivers/audio/ac97/ali5451/queue.h
Normal file
529
src/add-ons/kernel/drivers/audio/ac97/ali5451/queue.h
Normal file
@ -0,0 +1,529 @@
|
||||
/* $NetBSD: queue.h,v 1.31 2002/06/01 23:51:05 lukem Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues, and circular queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The
|
||||
* elements are singly linked for minimum space and pointer manipulation
|
||||
* overhead at the expense of O(n) removal for arbitrary elements. New
|
||||
* elements can be added to the list after an existing element or at the
|
||||
* head of the list. Elements being removed from the head of the list
|
||||
* should use the explicit macro for this purpose for optimum
|
||||
* efficiency. A singly-linked list may only be traversed in the forward
|
||||
* direction. Singly-linked lists are ideal for applications with large
|
||||
* datasets and few or no removals or for implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so only elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#if defined(_KERNEL) && defined(QUEUEDEBUG)
|
||||
#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
|
||||
if ((head)->lh_first && \
|
||||
(head)->lh_first->field.le_prev != &(head)->lh_first) \
|
||||
panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_LIST_OP(elm, field) \
|
||||
if ((elm)->field.le_next && \
|
||||
(elm)->field.le_next->field.le_prev != \
|
||||
&(elm)->field.le_next) \
|
||||
panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
|
||||
if (*(elm)->field.le_prev != (elm)) \
|
||||
panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
|
||||
(elm)->field.le_next = (void *)1L; \
|
||||
(elm)->field.le_prev = (void *)1L;
|
||||
#else
|
||||
#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
|
||||
#define QUEUEDEBUG_LIST_OP(elm, field)
|
||||
#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
|
||||
#endif
|
||||
|
||||
#define LIST_INIT(head) do { \
|
||||
(head)->lh_first = NULL; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
QUEUEDEBUG_LIST_OP((listelm), field) \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QUEUEDEBUG_LIST_OP((listelm), field) \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
QUEUEDEBUG_LIST_OP((elm), field) \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->lh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.le_next))
|
||||
|
||||
/*
|
||||
* List access methods.
|
||||
*/
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
|
||||
|
||||
#define SLIST_INIT(head) do { \
|
||||
(head)->slh_first = NULL; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
while(curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->sqh_first == (elm)) { \
|
||||
SIMPLEQ_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->sqh_first; \
|
||||
while (curelm->field.sqe_next != (elm)) \
|
||||
curelm = curelm->field.sqe_next; \
|
||||
if ((curelm->field.sqe_next = \
|
||||
curelm->field.sqe_next->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(curelm)->field.sqe_next; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->sqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.sqe_next))
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#if defined(_KERNEL) && defined(QUEUEDEBUG)
|
||||
#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
|
||||
if ((head)->tqh_first && \
|
||||
(head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
|
||||
panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
|
||||
if (*(head)->tqh_last != NULL) \
|
||||
panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_TAILQ_OP(elm, field) \
|
||||
if ((elm)->field.tqe_next && \
|
||||
(elm)->field.tqe_next->field.tqe_prev != \
|
||||
&(elm)->field.tqe_next) \
|
||||
panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
|
||||
if (*(elm)->field.tqe_prev != (elm)) \
|
||||
panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__);
|
||||
#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
|
||||
(elm)->field.tqe_next = (void *)1L; \
|
||||
(elm)->field.tqe_prev = (void *)1L;
|
||||
#else
|
||||
#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
|
||||
#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
|
||||
#define QUEUEDEBUG_TAILQ_OP(elm, field)
|
||||
#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
|
||||
#endif
|
||||
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
QUEUEDEBUG_TAILQ_OP((listelm), field) \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QUEUEDEBUG_TAILQ_OP((listelm), field) \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
QUEUEDEBUG_TAILQ_OP((elm), field) \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
/*
|
||||
* Tail queue access methods.
|
||||
*/
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->tqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.tqe_next))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
|
||||
(var); \
|
||||
(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||
{ (void *)&head, (void *)&head }
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = (void *)(head); \
|
||||
(head)->cqh_last = (void *)(head); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = (void *)(head); \
|
||||
if ((head)->cqh_last == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (void *)(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->cqh_first); \
|
||||
(var) != (void *)(head); \
|
||||
(var) = ((var)->field.cqe_next))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||
for ((var) = ((head)->cqh_last); \
|
||||
(var) != (void *)(head); \
|
||||
(var) = ((var)->field.cqe_prev))
|
||||
|
||||
/*
|
||||
* Circular queue access methods.
|
||||
*/
|
||||
#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
193
src/add-ons/kernel/drivers/audio/ac97/ali5451/util.c
Normal file
193
src/add-ons/kernel/drivers/audio/ac97/ali5451/util.c
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* Original code for memory management is
|
||||
* Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <drivers/Drivers.h>
|
||||
#include <drivers/KernelExport.h>
|
||||
#include <drivers/PCI.h>
|
||||
#include <hmulti_audio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "driver.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/* memory management */
|
||||
|
||||
|
||||
static uint32
|
||||
round_to_pagesize(uint32 size)
|
||||
{
|
||||
return (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
|
||||
static area_id
|
||||
alloc_mem(void **phy, void **log, size_t size, const char *name)
|
||||
{
|
||||
physical_entry pe;
|
||||
void *logadr;
|
||||
area_id areaid;
|
||||
status_t rv;
|
||||
|
||||
size = round_to_pagesize(size);
|
||||
areaid = create_area(name, &logadr, B_ANY_KERNEL_ADDRESS, size,
|
||||
B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
|
||||
if (areaid < B_OK) {
|
||||
TRACE("alloc_mem: couldn't allocate area %s\n", name);
|
||||
return B_ERROR;
|
||||
}
|
||||
rv = get_memory_map(logadr, size, &pe, 1);
|
||||
if (rv < B_OK) {
|
||||
delete_area(areaid);
|
||||
TRACE("alloc_mem: couldn't map %s\n",name);
|
||||
return B_ERROR;
|
||||
}
|
||||
memset(logadr, 0, size);
|
||||
if (log)
|
||||
*log = logadr;
|
||||
if (phy)
|
||||
*phy = pe.address;
|
||||
|
||||
return areaid;
|
||||
}
|
||||
|
||||
|
||||
static ali_mem
|
||||
*ali_mem_new(ali_dev *card, size_t size)
|
||||
{
|
||||
ali_mem *mem;
|
||||
|
||||
if (!(mem = malloc(sizeof(*mem))))
|
||||
return NULL;
|
||||
|
||||
mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "ali buffer");
|
||||
mem->size = size;
|
||||
|
||||
if (mem->area < B_OK) {
|
||||
free(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ali_mem_delete(ali_mem *mem)
|
||||
{
|
||||
if (B_OK < mem->area)
|
||||
delete_area(mem->area);
|
||||
|
||||
free(mem);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
ali_mem_alloc(ali_dev *card, size_t size)
|
||||
{
|
||||
ali_mem *mem;
|
||||
|
||||
mem = ali_mem_new(card, size);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
|
||||
LIST_INSERT_HEAD(&(card->mems), mem, next);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ali_mem_free(ali_dev *card, void *ptr)
|
||||
{
|
||||
ali_mem *mem;
|
||||
|
||||
LIST_FOREACH(mem, &card->mems, next) {
|
||||
if (mem->log_base != ptr)
|
||||
continue;
|
||||
|
||||
LIST_REMOVE(mem, next);
|
||||
|
||||
ali_mem_delete(mem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* misc utils */
|
||||
|
||||
|
||||
uint8
|
||||
util_format_to_sample_size(uint32 format)
|
||||
{
|
||||
switch (format) {
|
||||
case B_FMT_8BIT_U:
|
||||
case B_FMT_8BIT_S: return 1;
|
||||
|
||||
case B_FMT_16BIT: return 2;
|
||||
|
||||
case B_FMT_18BIT:
|
||||
case B_FMT_24BIT:
|
||||
case B_FMT_32BIT:
|
||||
case B_FMT_FLOAT: return 4;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
util_format_is_signed(uint32 format)
|
||||
{
|
||||
return (format == B_FMT_8BIT_S || format == B_FMT_16BIT);
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
util_get_buffer_length_for_rate(uint32 rate)
|
||||
{
|
||||
// values are based on Haiku's hda driver
|
||||
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
case 11025: return 512;
|
||||
|
||||
case 16000:
|
||||
case 22050: return 1024;
|
||||
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000: return 2048;
|
||||
}
|
||||
|
||||
return 2048;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
util_sample_rate_in_bits(uint32 rate)
|
||||
{
|
||||
switch (rate) {
|
||||
case B_SR_8000: return 8000;
|
||||
case B_SR_11025: return 11025;
|
||||
case B_SR_12000: return 12000;
|
||||
case B_SR_16000: return 16000;
|
||||
case B_SR_22050: return 22050;
|
||||
case B_SR_24000: return 24000;
|
||||
case B_SR_32000: return 32000;
|
||||
case B_SR_44100: return 44100;
|
||||
case B_SR_48000: return 48000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
23
src/add-ons/kernel/drivers/audio/ac97/ali5451/util.h
Normal file
23
src/add-ons/kernel/drivers/audio/ac97/ali5451/util.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
||||
*
|
||||
* Original code for memory management is
|
||||
* Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr).
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
void *ali_mem_alloc(ali_dev *card, size_t size);
|
||||
void ali_mem_free(ali_dev *card, void *ptr);
|
||||
|
||||
uint8 util_format_to_sample_size(uint32 format);
|
||||
bool util_format_is_signed(uint32 format);
|
||||
uint32 util_get_buffer_length_for_rate(uint32 rate);
|
||||
uint32 util_sample_rate_in_bits(uint32 rate);
|
||||
|
||||
#endif // _UTIL_H
|
Loading…
x
Reference in New Issue
Block a user