diff --git a/src/add-ons/kernel/drivers/audio/Jamfile b/src/add-ons/kernel/drivers/audio/Jamfile index 545bcb9cb7..ace84cfcd2 100644 --- a/src/add-ons/kernel/drivers/audio/Jamfile +++ b/src/add-ons/kernel/drivers/audio/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/audio/sb16/Jamfile b/src/add-ons/kernel/drivers/audio/sb16/Jamfile new file mode 100644 index 0000000000..91e4f28794 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/sb16/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/audio/sb16/driver.c b/src/add-ons/kernel/drivers/audio/sb16/driver.c new file mode 100644 index 0000000000..e25f1de329 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/sb16/driver.c @@ -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; +} diff --git a/src/add-ons/kernel/drivers/audio/sb16/driver.h b/src/add-ons/kernel/drivers/audio/sb16/driver.h new file mode 100644 index 0000000000..a0a25b3492 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/sb16/driver.h @@ -0,0 +1,66 @@ +#ifndef DRIVER_H +#define DRIVER_H + +#include +#include +#include + +#include +#include + +#ifdef COMPILE_FOR_R5 + #define DEVFS_PATH "audio/multi" + #include +#else + #define DEVFS_PATH "audio/hmulti" + #include +#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 */ + diff --git a/src/add-ons/kernel/drivers/audio/sb16/hardware.h b/src/add-ons/kernel/drivers/audio/sb16/hardware.h new file mode 100644 index 0000000000..c9163e9991 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/sb16/hardware.h @@ -0,0 +1,259 @@ +/* + * @(#)hardware.h 1.0 1998/12/22 Carlos Hasan (chasan@dcc.uchile.cl) + * + * 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 diff --git a/src/add-ons/kernel/drivers/audio/sb16/hooks.c b/src/add-ons/kernel/drivers/audio/sb16/hooks.c new file mode 100644 index 0000000000..f51e8d7bfb --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/sb16/hooks.c @@ -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 */ +}; diff --git a/src/add-ons/kernel/drivers/audio/sb16/sb16_hw.c b/src/add-ons/kernel/drivers/audio/sb16/sb16_hw.c new file mode 100644 index 0000000000..7642840ca5 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/sb16/sb16_hw.c @@ -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 + +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); + } + +} + diff --git a/src/add-ons/kernel/drivers/audio/sb16/sb16_multi_audio.c b/src/add-ons/kernel/drivers/audio/sb16/sb16_multi_audio.c new file mode 100644 index 0000000000..c7de295519 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/sb16/sb16_multi_audio.c @@ -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; +}