Basic SoundBlaster 16 (ISA) driver, designed to be used with QEMU, but should become usable on real hardware too. Very much a work in progress, based on the sb16 driver available for BeOS.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21175 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ithamar R. Adema
2007-05-19 17:22:27 +00:00
parent 641541a4f3
commit 633b979176
8 changed files with 1096 additions and 0 deletions
+1
View File
@@ -5,6 +5,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers audio echo ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio emuxki ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio hda ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio module_driver ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio sb16 ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio sis7018 ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio usb_audio ;
@@ -0,0 +1,17 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio sb16 ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders media ;
KernelAddon sb16 :
driver.c
hooks.c
sb16_hw.c
sb16_multi_audio.c
;
Package haiku-sb16-cvs :
sb16 :
boot home config add-ons kernel drivers bin ;
PackageDriverSymLink haiku-sb16-cvs : audio multi sb16 ;
@@ -0,0 +1,83 @@
#include "driver.h"
sb16_dev_t device;
const char* devs[2] = {
NULL, NULL
};
const char** publish_devices(void); /* Just to silence compiler */
static status_t
extract_driver_settings(void* settings, sb16_dev_t* sb16)
{
const char* port;
const char* irq;
const char* dma8;
const char* dma16;
const char* midiport;
status_t rc;
if ((port=get_driver_parameter(settings, "port", "220", NULL)) != NULL &&
(irq=get_driver_parameter(settings, "irq", "5", NULL)) != NULL &&
(dma8=get_driver_parameter(settings, "dma8", "1", NULL)) != NULL &&
(dma16=get_driver_parameter(settings, "dma16", "5", NULL)) != NULL &&
(midiport=get_driver_parameter(settings, "midiport", "330", NULL)) != NULL) {
sb16->port = strtol(port, NULL, 16);
sb16->irq = strtol(irq, NULL, 16);
sb16->dma8 = strtol(dma8, NULL, 16);
sb16->dma16 = strtol(dma16, NULL, 16);
sb16->midiport = strtol(midiport, NULL, 16);
rc = B_OK;
} else {
rc = B_BAD_VALUE;
}
return rc;
}
//#pragma mark --
int32 api_version = B_CUR_DRIVER_API_VERSION;
status_t
init_hardware(void)
{
return B_OK;
}
status_t
init_driver (void)
{
void* settings = load_driver_settings("sb16");
status_t rc;
if (settings != NULL) {
rc = extract_driver_settings(settings, &device);
if (rc == B_OK) {
devs[0] = DEVFS_PATH "/sb16/0";
dprintf("%s: publishing %s\n", __func__, devs[0]);
}
} else
rc = ENODEV;
return rc;
}
void
uninit_driver (void)
{
}
const char**
publish_devices(void)
{
return devs;
}
device_hooks*
find_device(const char* name)
{
return &driver_hooks;
}
@@ -0,0 +1,66 @@
#ifndef DRIVER_H
#define DRIVER_H
#include <drivers/driver_settings.h>
#include <drivers/KernelExport.h>
#include <drivers/Drivers.h>
#include <string.h>
#include <stdlib.h>
#ifdef COMPILE_FOR_R5
#define DEVFS_PATH "audio/multi"
#include <multi_audio.h>
#else
#define DEVFS_PATH "audio/hmulti"
#include <hmulti_audio.h>
#endif
#define STRMINBUF 2
#define STRMAXBUF 2
#define DEFAULT_FRAMESPERBUF 512
#define SB16_MULTI_CONTROL_FIRSTID 1024
#define SB16_MULTI_CONTROL_MASTERID 0
typedef struct {
int running;
spinlock lock;
int bits;
void* buffers[STRMAXBUF];
uint32 num_buffers;
uint32 num_channels;
uint32 sample_size;
uint32 sampleformat;
uint32 samplerate;
uint32 buffer_length;
sem_id buffer_ready_sem;
uint32 frames_count;
uint32 buffer_cycle;
bigtime_t real_time;
} sb16_stream_t;
typedef struct {
int port, irq, dma8, dma16, midiport;
int opened;
sb16_stream_t playback_stream;
sb16_stream_t record_stream;
} sb16_dev_t;
extern device_hooks driver_hooks;
status_t sb16_hw_init(sb16_dev_t* dev);
void sb16_hw_stop(sb16_dev_t* dev);
void sb16_hw_uninit(sb16_dev_t* dev);
status_t sb16_stream_setup_buffers(sb16_dev_t* dev, sb16_stream_t* s, const char* desc);
status_t sb16_stream_start(sb16_dev_t* dev, sb16_stream_t* s);
status_t sb16_stream_stop(sb16_dev_t* dev, sb16_stream_t* s);
void sb16_stream_buffer_done(sb16_stream_t* stream);
status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t len);
#endif /* DRIVER_H */
@@ -0,0 +1,259 @@
/*
* @(#)hardware.h 1.0 1998/12/22 Carlos Hasan ([email protected])
*
* Hardware definitions for the Sound Blaster 16 device driver.
*
* Copyright (C) 1998 Carlos Hasan. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __HARDWARE_H
#define __HARDWARE_H
/*****************************************************************************
* SOUND BLASTER 16 PLUG-AND-PLAY DEVICE IDENTIFIERS
*****************************************************************************/
/*****************************************************************************
* ==========================================================================
* Product ID Description
* ==========================================================================
* CTL0001 Creative Sound Blaster 16 Plug and Play
*
* CTL0021 Creative AWE32 Wavetable MIDI
* CTL0022 Creative AWE64 Wavetable MIDI
* CTL0023 Creative AWE64 Gold Wavetable MIDI
* CTL0024 Creative AWE64 Compatible Wavetable MIDI
*
* CTL0031 Creative AWE32 16-bit Audio (SB16 compatible)
*
* CTL0041 Creative SB16 Value Plug and Play (V16CL/V32D/V32G)
* CTL0042 Creative SB AWE32 Plug and Play
* CTL0043 Creative ViBRA 16X Plug and Play
* CTL0044 Creative SB AWE64 Gold Plug and Play
* CTL0045 Creative SB AWE64
* CTL0046 Creative SB AWE64 Compatible Plug and Play
* CTL0047 Creative SB16 Plug and Play
* CTL0048 Creative SB AWE64 Gold Plug and Play
*
* CTL0051 Creative 3D Stereo Enhancement
*
* CTL00F0 Creative ViBRA 16X Plug and Play
*
* CTL7001 Creative Programmable Game Port
* CTL7002 Creative Programmable Game Port
* CTL7005 Creative Programmable Game Port (1 I/O)
*
* CTL8001 SB Legacy Device
* PNPB003 SB16 Legacy Device
*
* ==========================================================================
* Serial Card Name Device Name Vendor ID Device ID
* ==========================================================================
* CT3980 Creative SB AWE32 PnP Audio CTL0042 CTL0031
* WaveTable CTL0042 CTL0021
* Game CTL0042 CTL7001
* IDE CTL0042 CTL2011
*
* CT2230 Creative ViBRA 16X PnP Audio CTL00f0 CTL0043
* Game CTL00f0 CTL7005
*/
#define PNP_ISA_PRODUCT_ID(ch0, ch1, ch2, prod, rev) \
(((ch0 & 0x1f) << 2) | ((ch1 & 0x18) >> 3) | \
((ch1 & 0x07) << 13) | ((ch2 & 0x1f) << 8) | \
((prod & 0xff0) << 12) | ((prod & 0x00f) << 28) | ((rev & 0xf) << 24))
#define PNP_IS_SB16_DEVICE(id) \
(((id & 0xf0ffffff) == PNP_ISA_PRODUCT_ID('C', 'T', 'L', 0x000, 0x0)) || \
((id & 0xf0ffffff) == PNP_ISA_PRODUCT_ID('C', 'T', 'L', 0x003, 0x0)) || \
((id & 0xf0ffffff) == PNP_ISA_PRODUCT_ID('C', 'T', 'L', 0x004, 0x0)))
/*****************************************************************************
* SOUND BLASTER 16 HARDWARE SPECIFICATIONS
*****************************************************************************/
/*****************************************************************************
* Sound Blaster 16 DSP I/O port register offsets
*/
enum {
SB16_MIXER_ADDRESS = 0x004,
SB16_MIXER_DATA = 0x005,
SB16_CODEC_RESET = 0x006,
SB16_CODEC_READ_DATA = 0x00a,
SB16_CODEC_WRITE_DATA = 0x00c,
SB16_CODEC_WRITE_STATUS = 0x00c,
SB16_CODEC_READ_STATUS = 0x00e,
SB16_CODEC_ACK_8_BIT = 0x00e,
SB16_CODEC_ACK_16_BIT = 0x00f
};
/*****************************************************************************
* Sound Blaster 16 DSP programming commands
*/
enum {
SB16_SPEAKER_ENABLE = 0xd1,
SB16_SPEAKER_DISABLE = 0xd3,
SB16_SPEAKER_STATUS = 0xd8,
SB16_PLAYBACK_RATE = 0x41,
SB16_CAPTURE_RATE = 0x42,
SB16_PLAYBACK_8_BIT = 0xc6,
SB16_CAPTURE_8_BIT = 0xce,
SB16_DMA_DISABLE_8_BIT = 0xd0,
SB16_DMA_ENABLE_8_BIT = 0xd4,
SB16_DMA_EXIT_8_BIT = 0xda,
SB16_PLAYBACK_16_BIT = 0xb6,
SB16_CAPTURE_16_BIT = 0xbe,
SB16_DMA_DISABLE_16_BIT = 0xd5,
SB16_DMA_ENABLE_16_BIT = 0xd6,
SB16_DMA_EXIT_16_BIT = 0xd9,
SB16_CODEC_VERSION = 0xe1
};
/*****************************************************************************
* Sound Blaster 16 mixer indirect registers
*/
enum {
SB16_MIXER_RESET = 0x00,
SB16_LINE_OUT_LEFT = 0x30,
SB16_LINE_OUT_RIGHT = 0x31,
SB16_DAC_LEFT = 0x32,
SB16_DAC_RIGHT = 0x33,
SB16_SYNTH_LEFT = 0x34,
SB16_SYNTH_RIGHT = 0x35,
SB16_CD_LEFT = 0x36,
SB16_CD_RIGHT = 0x37,
SB16_LINE_IN_LEFT = 0x38,
SB16_LINE_IN_RIGHT = 0x39,
SB16_MIC = 0x3a,
SB16_PC_BEEP = 0x3b,
SB16_OUTPUT_MUX = 0x3c,
SB16_INPUT_MUX_LEFT = 0x3d,
SB16_INPUT_MUX_RIGHT = 0x3e,
SB16_ADC_GAIN_LEFT = 0x3f,
SB16_ADC_GAIN_RIGHT = 0x40,
SB16_DAC_GAIN_LEFT = 0x41,
SB16_DAC_GAIN_RIGHT = 0x42,
SB16_MIC_BOOST = 0x43,
SB16_TREBLE_LEFT = 0x44,
SB16_TREBLE_RIGHT = 0x45,
SB16_BASS_LEFT = 0x46,
SB16_BASS_RIGHT = 0x47,
SB16_IRQ_SETUP = 0x80,
SB16_DMA_SETUP = 0x81,
SB16_IRQ_STATUS = 0x82,
SB16_STEREO_ENHANCEMENT = 0x90
};
/*****************************************************************************
* Sound Blaster 16 playback and capture sample formats
*/
enum {
B_SB16_FORMAT_UNSIGNED = 0x00,
B_SB16_FORMAT_SIGNED = 0x10,
B_SB16_FORMAT_MONO = 0x00,
B_SB16_FORMAT_STEREO = 0x20
};
/*****************************************************************************
* Sound Blaster 16 playback and capture control bits
*/
enum {
B_SB16_MUX_SYNTH_LEFT = 0x20,
B_SB16_MUX_SYNTH_RIGHT = 0x40,
B_SB16_MUX_LINE_LEFT = 0x10,
B_SB16_MUX_LINE_RIGHT = 0x08,
B_SB16_MUX_CD_LEFT = 0x04,
B_SB16_MUX_CD_RIGHT = 0x02,
B_SB16_MUX_MIC = 0x01,
B_SB16_MUX_ALL_LEFT = 0x35,
B_SB16_MUX_ALL_RIGHT = 0x4b
};
/*****************************************************************************
* Sound Blaster 16 Stereo Enhancenment
*/
enum {
B_SB16_SE_ENABLE = 0x01,
B_SB16_SE_DETECT_ENABLE = 0x40,
B_SB16_SE_DETECT_STATUS = 0x80
};
/*****************************************************************************
* Sound Blaster 16 DSP I/O port delays
*/
enum {
SB16_CODEC_IO_DELAY = 200000,
SB16_CODEC_RESET_DELAY = 200
};
/*****************************************************************************
* Sound Blaster 16 DMA low memory buffer
*/
typedef struct {
area_id area;
char *address;
int size;
} hw_dma_buffer;
/*****************************************************************************
* SOUND BLASTER 16 MPU-401 MIDI PORT HARDWARE SPECIFICATIONS
*****************************************************************************/
/*****************************************************************************
* Sound Blaster 16 MPU-401 I/O port register offsets
*/
enum {
MPU401_DATA = 0x000,
MPU401_STATUS = 0x001,
MPU401_COMMAND = 0x001
};
/*****************************************************************************
* Sound Blaster 16 MPU-401 status register bit fields
*/
enum {
B_MPU401_WRITE_BUSY = 0x40,
B_MPU401_READ_BUSY = 0x80
};
/*****************************************************************************
* Sound Blaster 16 MPU-401 MIDI port commands
*/
enum {
MPU401_CMD_RESET = 0xff,
MPU401_CMD_UART_MODE = 0x3f
};
/*****************************************************************************
* Sound Blaster 16 MPU-401 port timeout and latency
*/
enum {
MPU401_IO_LATENCY = 1000,
MPU401_IO_TIMEOUT = 10000
};
#endif
@@ -0,0 +1,73 @@
#include "driver.h"
extern sb16_dev_t device;
static status_t
sb16_open (const char *name, uint32 flags, void** cookie)
{
sb16_dev_t* dev = &device;
status_t rc = B_OK;
if (dev->opened)
return B_BUSY;
rc = sb16_hw_init(dev);
if (rc != B_OK)
return rc;
dev->opened++;
*cookie = dev;
return B_OK;
}
static status_t
sb16_read (void* cookie, off_t position, void *buf, size_t* num_bytes)
{
*num_bytes = 0; /* tell caller nothing was read */
return B_IO_ERROR;
}
static status_t
sb16_write (void* cookie, off_t position, const void* buffer, size_t* num_bytes)
{
*num_bytes = 0; /* tell caller nothing was written */
return B_IO_ERROR;
}
static status_t
sb16_control (void* cookie, uint32 op, void* arg, size_t len)
{
if (cookie)
return multi_audio_control(cookie, op, arg, len);
return B_BAD_VALUE;
}
static status_t
sb16_close (void* cookie)
{
sb16_dev_t* dev = (sb16_dev_t*)cookie;
sb16_hw_stop(dev);
return B_OK;
}
static status_t
sb16_free (void* cookie)
{
sb16_dev_t* dev = (sb16_dev_t*)cookie;
sb16_hw_uninit(dev);
--dev->opened;
return B_OK;
}
device_hooks driver_hooks = {
sb16_open, /* -> open entry point */
sb16_close, /* -> close entry point */
sb16_free, /* -> free cookie */
sb16_control, /* -> control entry point */
sb16_read, /* -> read entry point */
sb16_write /* -> write entry point */
};
@@ -0,0 +1,298 @@
/*
* This code is very much based on the BeOS R4_ sb16 driver, which is:
* Copyright (C) 1998 Carlos Hasan. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "driver.h"
#include "hardware.h"
#include <drivers/ISA.h>
static isa_module_info* gISA;
static void
hw_codec_write_byte(sb16_dev_t* dev, uint8 value)
{
int i;
/* wait until the DSP is ready to receive data */
for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
if (!(gISA->read_io_8(dev->port + SB16_CODEC_WRITE_STATUS) & 0x80))
break;
}
/* write a byte to the DSP data port */
gISA->write_io_8(dev->port + SB16_CODEC_WRITE_DATA, value);
}
static int
hw_codec_read_byte(sb16_dev_t* dev)
{
int i;
/* wait until the DSP has data available */
for (i = 0; i < SB16_CODEC_IO_DELAY; i++) {
if (gISA->read_io_8(dev->port + SB16_CODEC_READ_STATUS) & 0x80)
break;
}
/* read a byte from the DSP data port */
return gISA->read_io_8(dev->port + SB16_CODEC_READ_DATA);
}
static void
hw_codec_reg_write(sb16_dev_t* dev, uint8 index, uint8 value)
{
/* write a Mixer indirect register */
gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
gISA->write_io_8(dev->port + SB16_MIXER_DATA, value);
}
static int
hw_codec_reg_read(sb16_dev_t* dev, uint8 index)
{
/* read a Mixer indirect register */
gISA->write_io_8(dev->port + SB16_MIXER_ADDRESS, index);
return gISA->read_io_8(dev->port + SB16_MIXER_DATA);
}
static int
hw_codec_read_version(sb16_dev_t* dev)
{
int minor, major;
/* query the DSP hardware version number */
hw_codec_write_byte(dev, SB16_CODEC_VERSION);
major = hw_codec_read_byte(dev);
minor = hw_codec_read_byte(dev);
dprintf("%s: SB16 version %d.%d\n", __func__, major, minor);
return (major << 8) + minor;
}
static void
hw_codec_read_irq_setup(sb16_dev_t* dev)
{
/* query the current IRQ line resource */
int mask = hw_codec_reg_read(dev, SB16_IRQ_SETUP);
dev->irq = 5;
if (mask & 0x01)
dev->irq = 2;
if (mask & 0x02)
dev->irq = 5;
if (mask & 0x04)
dev->irq = 7;
if (mask & 0x08)
dev->irq = 10;
}
static void
hw_codec_read_dma_setup(sb16_dev_t* dev)
{
/* query the current DMA channel resources */
int mask = hw_codec_reg_read(dev, SB16_DMA_SETUP);
dev->dma8 = 1;
if (mask & 0x01)
dev->dma8 = 0;
if (mask & 0x02)
dev->dma8 = 1;
if (mask & 0x08)
dev->dma8 = 3;
dev->dma16 = dev->dma8;
if (mask & 0x20)
dev->dma16 = 5;
if (mask & 0x40)
dev->dma16 = 6;
if (mask & 0x80)
dev->dma16 = 7;
}
static void
hw_codec_write_irq_setup(sb16_dev_t* dev)
{
/* change programmable IRQ line resource */
int mask = 0x02;
if (dev->irq == 2)
mask = 0x01;
if (dev->irq == 5)
mask = 0x02;
if (dev->irq == 7)
mask = 0x04;
if (dev->irq == 10)
mask = 0x08;
hw_codec_reg_write(dev, SB16_IRQ_SETUP, mask);
}
static void
hw_codec_write_dma_setup(sb16_dev_t* dev)
{
/* change programmable DMA channel resources */
hw_codec_reg_write(dev, SB16_DMA_SETUP, (1 << dev->dma8) | (1 << dev->dma16));
}
static int32
hw_codec_inth(void* cookie)
{
sb16_dev_t* dev = (sb16_dev_t*)cookie;
int32 rc = B_UNHANDLED_INTERRUPT;
/* read the IRQ interrupt status register */
int status = hw_codec_reg_read(dev, SB16_IRQ_STATUS);
/* check if this hardware raised this interrupt */
if (status & 0x03) {
rc = B_HANDLED_INTERRUPT;
/* acknowledge DMA memory transfers */
if (status & 0x01)
gISA->read_io_8(dev->port + SB16_CODEC_ACK_8_BIT);
if (status & 0x02)
gISA->read_io_8(dev->port + SB16_CODEC_ACK_16_BIT);
/* acknowledge PIC interrupt signal */
if (dev->irq >= 8)
gISA->write_io_8(0xa0, 0x20);
gISA->write_io_8(0x20, 0x20);
/* handle buffer finished interrupt */
if (((dev->playback_stream.bits >> 3) & status) != 0) {
sb16_stream_buffer_done(&dev->playback_stream);
rc = B_INVOKE_SCHEDULER;
}
if (((dev->record_stream.bits >> 3) & status) != 0) {
sb16_stream_buffer_done(&dev->record_stream);
rc = B_INVOKE_SCHEDULER;
}
if ((status & 0x04) != 0) {
/* MIDI stream done */
rc = B_INVOKE_SCHEDULER;
}
}
return rc;
}
static status_t
hw_codec_reset(sb16_dev_t* dev)
{
int times, delay;
/* try to reset the DSP hardware */
for (times = 0; times < 10; times++) {
gISA->write_io_8(dev->port + SB16_CODEC_RESET, 1);
for (delay = 0; delay < SB16_CODEC_RESET_DELAY; delay++)
gISA->read_io_8(dev->port + SB16_CODEC_RESET);
gISA->write_io_8(dev->port + SB16_CODEC_RESET, 0);
if (hw_codec_read_byte(dev) == 0xaa)
return B_OK;
}
return B_IO_ERROR;
}
static status_t
hw_codec_detect(sb16_dev_t* dev)
{
status_t rc;
if ((rc=hw_codec_reset(dev)) == B_OK) {
if (hw_codec_read_version(dev) >= 0x400) {
hw_codec_write_irq_setup(dev);
hw_codec_write_dma_setup(dev);
rc = B_OK;
} else {
rc = B_BAD_VALUE;
}
}
return rc;
}
//#pragma mark -
status_t
sb16_stream_setup_buffers(sb16_dev_t* dev, sb16_stream_t* s, const char* desc)
{
return B_OK;
}
status_t
sb16_stream_start(sb16_dev_t* dev, sb16_stream_t* s)
{
return B_OK;
}
status_t
sb16_stream_stop(sb16_dev_t* dev, sb16_stream_t* s)
{
return B_OK;
}
void
sb16_stream_buffer_done(sb16_stream_t* stream)
{
}
//#pragma mark -
status_t
sb16_hw_init(sb16_dev_t* dev)
{
status_t rc;
/* First of all, grab the ISA module */
if ((rc=get_module(B_ISA_MODULE_NAME, (module_info**)&gISA)) != B_OK)
return rc;
/* Check if the hardware is sensible... */
if ((rc=hw_codec_detect(dev)) == B_OK) {
if ((rc=gISA->lock_isa_dma_channel(dev->dma8)) == B_OK &&
(rc=gISA->lock_isa_dma_channel(dev->dma16)) == B_OK) {
rc = install_io_interrupt_handler(dev->irq, hw_codec_inth, dev, 0);
}
}
return rc;
}
void
sb16_hw_stop(sb16_dev_t* dev)
{
}
void
sb16_hw_uninit(sb16_dev_t* dev)
{
remove_io_interrupt_handler(dev->irq, hw_codec_inth, dev);
if (gISA != NULL) {
gISA->unlock_isa_dma_channel(dev->dma8);
if (dev->dma8 != dev->dma16)
gISA->unlock_isa_dma_channel(dev->dma16);
put_module(B_ISA_MODULE_NAME);
}
}
@@ -0,0 +1,299 @@
#include "driver.h"
multi_channel_info chans[] = {
{ 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 int32
format2size(uint32 format)
{
switch(format) {
case B_FMT_8BIT_S:
case B_FMT_16BIT:
return 2;
default:
return -1;
}
}
static status_t
get_description(sb16_dev_t* dev, multi_description* data)
{
data->interface_version = B_CURRENT_INTERFACE_VERSION;
data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
strcpy(data->friendly_name,"SoundBlaster 16");
strcpy(data->vendor_info,"Haiku");
data->output_channel_count = 2;
data->input_channel_count = 2;
data->output_bus_channel_count = 2;
data->input_bus_channel_count = 2;
data->aux_bus_channel_count = 0;
if (data->request_channel_count >= (int)(sizeof(chans) / sizeof(chans[0]))) {
memcpy(data->channels,&chans,sizeof(chans));
}
/* determine output/input rates */
data->output_rates =
data->input_rates = B_SR_44100 | B_SR_22050 | B_SR_11025;
data->max_cvsr_rate = 0;
data->min_cvsr_rate = 0;
data->output_formats =
data->input_formats = B_FMT_8BIT_S | B_FMT_16BIT;
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(sb16_dev_t* dev, 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(sb16_dev_t* dev, multi_format_info* data)
{
data->output_latency = 0;
data->input_latency = 0;
data->timecode_kind = 0;
data->output.format = dev->playback_stream.sampleformat;
data->output.rate = dev->playback_stream.samplerate;
data->input.format = dev->record_stream.sampleformat;
data->input.rate = dev->record_stream.samplerate;
return B_OK;
}
static status_t
set_global_format(sb16_dev_t* dev, multi_format_info* data)
{
dev->playback_stream.sampleformat = data->output.format;
dev->playback_stream.samplerate = data->output.rate;
dev->playback_stream.sample_size = format2size(dev->playback_stream.sampleformat);
dev->record_stream.sampleformat = data->input.format;
dev->record_stream.samplerate = data->input.rate;
dev->record_stream.sample_size = format2size(dev->record_stream.sampleformat);
return B_OK;
}
static int32
create_group_control(multi_mix_control* multi, int32 idx, int32 parent, int32 string, const char* name)
{
multi->id = SB16_MULTI_CONTROL_FIRSTID + idx;
multi->parent = parent;
multi->flags = B_MULTI_MIX_GROUP;
multi->master = SB16_MULTI_CONTROL_MASTERID;
multi->string = string;
if(name)
strcpy(multi->name, name);
return multi->id;
}
static status_t
list_mix_controls(sb16_dev_t* dev, multi_mix_control_info * data)
{
int32 parent;
parent = create_group_control(data->controls +0, 0, 0, 0, "Record");
parent = create_group_control(data->controls +1, 1, 0, 0, "AC97 Mixer");
parent = create_group_control(data->controls +2, 2, 0, S_SETUP, NULL);
data->control_count = 3;
return B_OK;
}
static status_t
list_mix_connections(sb16_dev_t* dev, multi_mix_connection_info * data)
{
return B_ERROR;
}
static status_t
list_mix_channels(sb16_dev_t* dev, multi_mix_channel_info *data)
{
return B_ERROR;
}
static status_t
get_buffers(sb16_dev_t* dev, multi_buffer_list* data)
{
uint32 playback_sample_size = dev->playback_stream.sample_size;
uint32 record_sample_size = dev->record_stream.sample_size;
uint32 cidx, bidx;
status_t rc;
dprintf("%s: playback: %ld buffers, %ld channels, %ld samples\n", __func__,
data->request_playback_buffers, data->request_playback_channels, data->request_playback_buffer_size);
dprintf("%s: record: %ld buffers, %ld channels, %ld samples\n", __func__,
data->request_record_buffers, data->request_record_channels, data->request_record_buffer_size);
/* Workaround for Haiku multi_audio API, since it prefers to let the driver pick
values, while the BeOS multi_audio actually gives the user's defaults. */
if (data->request_playback_buffers > STRMAXBUF ||
data->request_playback_buffers < STRMINBUF) {
data->request_playback_buffers = STRMINBUF;
}
if (data->request_record_buffers > STRMAXBUF ||
data->request_record_buffers < STRMINBUF) {
data->request_record_buffers = STRMINBUF;
}
if (data->request_playback_buffer_size == 0)
data->request_playback_buffer_size = DEFAULT_FRAMESPERBUF;
if (data->request_record_buffer_size == 0)
data->request_record_buffer_size = DEFAULT_FRAMESPERBUF;
/* ... from here on, we can assume again that a reasonable request is being made */
data->flags = 0;
/* Copy the requested settings into the streams */
dev->playback_stream.num_buffers = data->request_playback_buffers;
dev->playback_stream.num_channels = data->request_playback_channels;
dev->playback_stream.buffer_length = data->request_playback_buffer_size;
if ((rc=sb16_stream_setup_buffers(dev, &dev->playback_stream, "Playback")) != B_OK) {
dprintf("%s: Error setting up playback buffers (%s)\n", __func__, strerror(rc));
return rc;
}
dev->record_stream.num_buffers = data->request_record_buffers;
dev->record_stream.num_channels = data->request_record_channels;
dev->record_stream.buffer_length = data->request_record_buffer_size;
if ((rc=sb16_stream_setup_buffers(dev, &dev->record_stream, "Recording")) != B_OK) {
dprintf("%s: Error setting up recording buffers (%s)\n", __func__, strerror(rc));
return rc;
}
/* Setup data structure for multi_audio API... */
data->return_playback_buffers = data->request_playback_buffers;
data->return_playback_channels = data->request_playback_channels;
data->return_playback_buffer_size = data->request_playback_buffer_size; /* frames */
for (bidx=0; bidx < data->return_playback_buffers; bidx++) {
for (cidx=0; cidx < data->return_playback_channels; cidx++) {
data->playback_buffers[bidx][cidx].base = dev->playback_stream.buffers[bidx] + (playback_sample_size * cidx);
data->playback_buffers[bidx][cidx].stride = playback_sample_size * data->return_playback_channels;
}
}
data->return_record_buffers = data->request_record_buffers;
data->return_record_channels = data->request_record_channels;
data->return_record_buffer_size = data->request_record_buffer_size; /* frames */
for (bidx=0; bidx < data->return_record_buffers; bidx++) {
for (cidx=0; cidx < data->return_record_channels; cidx++) {
data->record_buffers[bidx][cidx].base = dev->record_stream.buffers[bidx] + (record_sample_size * cidx);
data->record_buffers[bidx][cidx].stride = record_sample_size * data->return_record_channels;
}
}
return B_OK;
}
static status_t
buffer_exchange(sb16_dev_t* dev, multi_buffer_info* data)
{
static int debug_buffers_exchanged = 0;
cpu_status status;
status_t rc;
if (!dev->playback_stream.running)
sb16_stream_start(dev, &dev->playback_stream);
/* do playback */
rc=acquire_sem(dev->playback_stream.buffer_ready_sem);
if (rc != B_OK) {
dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__,
strerror(rc));
return rc;
}
status = disable_interrupts();
acquire_spinlock(&dev->playback_stream.lock);
data->playback_buffer_cycle = dev->playback_stream.buffer_cycle;
data->played_real_time = dev->playback_stream.real_time;
data->played_frames_count = dev->playback_stream.frames_count;
release_spinlock(&dev->playback_stream.lock);
restore_interrupts(status);
debug_buffers_exchanged++;
if (((debug_buffers_exchanged % 100) == 1) && (debug_buffers_exchanged < 1111)) {
dprintf("%s: %d buffers processed\n", __func__, debug_buffers_exchanged);
}
return B_OK;
}
static status_t
buffer_force_stop(sb16_dev_t* dev)
{
sb16_stream_stop(dev, &dev->playback_stream);
sb16_stream_stop(dev, &dev->record_stream);
delete_sem(dev->playback_stream.buffer_ready_sem);
delete_sem(dev->record_stream.buffer_ready_sem);
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 B_ERROR;
case B_MULTI_SET_MIX: return B_ERROR;
case B_MULTI_LIST_MIX_CHANNELS: return list_mix_channels(cookie, arg);
case B_MULTI_LIST_MIX_CONTROLS: return list_mix_controls(cookie, arg);
case B_MULTI_LIST_MIX_CONNECTIONS: return list_mix_connections(cookie, arg);
case B_MULTI_GET_BUFFERS: return get_buffers(cookie, arg);
case B_MULTI_SET_BUFFERS: return B_ERROR;
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 buffer_force_stop(cookie);
}
return B_BAD_VALUE;
}