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:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user