* added C-Media CMI8338/8738-based sound card device driver, it originates

from C-Media itself, and was later improved upon by Marko Koscak
* includes some fixes by Dr. Hartmut Reh
* uses old audio driver API
* builds, but I don't know if it works yet. I do have such a card, but
  on another computer... will check if it works and add it to the image if
  it does


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21536 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2007-07-01 13:15:01 +00:00
parent c1071005c9
commit 028e368c21
13 changed files with 4047 additions and 0 deletions

View File

@ -1,6 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio cmedia ;
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 ;

View File

@ -0,0 +1,22 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio cmedia ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders audio ;
KernelAddon cmedia :
cm.c
joy.c
midi.c
mixer.c
mux.c
pcm.c
;
Package haiku-cmedia-cvs
:
cmedia
:
boot home config add-ons kernel drivers bin ;
PackageDriverSymLink haiku-cmedia-cvs : audio cmedia ;

View File

@ -0,0 +1,784 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include "cmedia_pci.h"
#include "cm_private.h"
#include <string.h>
#include <stdio.h>
#if !defined(_KERNEL_EXPORT_H)
#include <KernelExport.h>
#endif
#if DEBUG
#define KPRINTF(x) kprintf x
#else
#define KPRINTF(x)
#endif
EXPORT status_t init_hardware(void);
EXPORT status_t init_driver(void);
EXPORT void uninit_driver(void);
EXPORT const char ** publish_devices(void);
EXPORT device_hooks * find_device(const char *);
static char pci_name[] = B_PCI_MODULE_NAME;
static pci_module_info *pci;
static char gameport_name[] = "generic/gameport/v1";
generic_gameport_module * gameport;
static char mpu401_name[] = B_MPU_401_MODULE_NAME;
generic_mpu401_module * mpu401;
#define DO_JOY 1
#define DO_MIDI 1
#define DO_PCM 1
#define DO_MUX 0
#define DO_MIXER 0
#if DO_MIDI
extern device_hooks midi_hooks;
#endif /* DO_MIDI */
#if DO_JOY
extern device_hooks joy_hooks;
#endif /* DO_JOY */
#if DO_PCM
extern device_hooks pcm_hooks;
#endif /* DO_PCM */
#if DO_MUX
extern device_hooks mux_hooks;
#endif /* DO_MUX */
#if DO_MIXER
extern device_hooks mixer_hooks;
#endif /* DO_MIXER */
int32 num_cards;
cmedia_pci_dev cards[NUM_CARDS];
int num_names;
char * names[NUM_CARDS*7+1];
/* vuchar *io_base; */
/* ----------
PCI_IO_RD - read a byte from pci i/o space
----- */
uint8
PCI_IO_RD (int offset)
{
return (*pci->read_io_8) (offset);
}
/* ----------
PCI_IO_RD_32 - read a 32 bit value from pci i/o space
----- */
uint32
PCI_IO_RD_32 (int offset)
{
return (*pci->read_io_32) (offset);
}
/* ----------
PCI_IO_WR - write a byte to pci i/o space
----- */
void
PCI_IO_WR (int offset, uint8 val)
{
(*pci->write_io_8) (offset, val);
}
/* detect presence of our hardware */
status_t
init_hardware(void)
{
int ix=0;
pci_info info;
status_t err = ENODEV;
ddprintf(("cmedia_pci: init_hardware()\n"));
if (get_module(pci_name, (module_info **)&pci))
return ENOSYS;
while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
if (info.vendor_id == CMEDIA_PCI_VENDOR_ID &&
(info.device_id == CMEDIA_8338A_DEVICE_ID ||
info.device_id == CMEDIA_8338B_DEVICE_ID ||
info.device_id == CMEDIA_8738A_DEVICE_ID ||
info.device_id == CMEDIA_8738B_DEVICE_ID )) {
err = B_OK;
}
ix++;
}
#if defined(__POWERPC__) && 0
{
char area_name [32];
area_info area;
area_id id;
sprintf (area_name, "pci_bus%d_isa_io", info.bus);
id = find_area (area_name);
if (id < 0)
err = id;
else if ((err = get_area_info (id, &area)) == B_OK)
io_base = area.address;
}
#endif
put_module(pci_name);
return err;
}
void set_direct( cmedia_pci_dev * card, int regno, uchar value, uchar mask)
{
if (mask == 0)
{
return;
}
if (mask != 0xff)
{
uchar old = PCI_IO_RD(card->enhanced+regno);
value = (value&mask)|(old&~mask);
}
PCI_IO_WR(card->enhanced+regno, value);
ddprintf(("cmedia_pci: CM%02x = %02x\n", regno, value));
}
uchar get_direct(cmedia_pci_dev * card, int regno)
{
uchar ret = PCI_IO_RD(card->enhanced+regno);
return ret;
}
void set_indirect(cmedia_pci_dev * card, int regno, uchar value, uchar mask)
{
PCI_IO_WR(card->enhanced+0x23, regno);
EIEIO();
if (mask == 0)
{
return;
}
if (mask != 0xff)
{
uchar old = PCI_IO_RD(card->enhanced+0x22);
value = (value&mask)|(old&~mask);
}
PCI_IO_WR(card->enhanced+0x22, value);
EIEIO();
ddprintf(("cmedia_pci: CMX%02x = %02x\n", regno, value));
}
uchar get_indirect(cmedia_pci_dev * card,int regno)
{
uchar ret;
PCI_IO_WR(card->enhanced+0x23, regno);
EIEIO();
ret = PCI_IO_RD(card->enhanced+0x22);
return ret;
}
#if 0
void dump_card(cmedia_pci_dev * card)
{
int ix;
dprintf("\n");
dprintf("CM: ");
for (ix=0; ix<6; ix++) {
if (ix == 2 || ix == 3) dprintf(" ");
else dprintf(" %02x", get_direct(card, ix));
}
for (ix=0; ix<0x32; ix++) {
if (!(ix & 7)) {
dprintf("\nCMX%02x:", ix);
}
dprintf(" %02x", get_indirect(card, ix));
}
dprintf("\n");
dprintf("\n");
}
#else
void dump_card(cmedia_pci_dev * card)
{
}
#endif
static void
disable_card_interrupts(cmedia_pci_dev * card)
{
set_direct(card, 0x0e, 0x00, 0x03);
}
static status_t
setup_dma(cmedia_pci_dev * card)
{
/* we're appropriating some ISA space here... */
/* need kernel support to do it right */
const uint16 base = card->enhanced+0x80;
ddprintf(("cmedia_pci: dma base is 0x%04x\n", base));
if(0 == base)
return B_DEV_RESOURCE_CONFLICT;
card->dma_base = base;
return B_OK;
}
static void
set_default_registers(
cmedia_pci_dev * card)
{
static uchar values[] = {
#ifdef DO_JOY
0x04, 0x02, 0x02, /* enable joystick */
#endif
0x0a, 0x01, 0x01, /* enable SPDIF inverse before SPDIF_LOOP */
0x04, 0x80, 0x80, /* enable SPDIF_LOOP */
0x1a, 0x00, 0x20, /* SPD32SEL disable */
0x1a, 0x00, 0x10, /* SPDFLOOPI disable */
0x1b, 0x04, 0x04, /* dual channel mode enable */
0x1a, 0x00, 0x80, /* Double DAC structure disable */
0x24, 0x00, 0x02, /* 3D surround disable */
0x24, 0x00, 0x01, /* disable SPDIF_IN PCM to DAC */
#ifdef DO_MIDI
0x04, 0x04, 0x04, /* enable MPU-401 */
0x17, 0x00, 0x60, /* default at 0x330 */
#endif
};
uchar * ptr = values;
while (ptr < values+sizeof(values)) {
set_direct(card, ptr[0], ptr[1], ptr[2]);
ptr += 3;
}
}
static void
make_device_names(
cmedia_pci_dev * card)
{
char * name = card->name;
sprintf(name, "cmedia_pci/%ld", card-cards+1);
#if DO_MIDI
sprintf(card->midi.name, "midi/%s", name);
names[num_names++] = card->midi.name;
#endif /* DO_MIDI */
#if DO_JOY
sprintf(card->joy.name1, "joystick/%s", name);
names[num_names++] = card->joy.name1;
#endif /* DO_JOY */
#if DO_PCM
/* cmedia_pci DMA doesn't work when physical NULL isn't NULL from PCI */
/* this is a hack to not export bad devices on BeBox hardware */
if ((*pci->ram_address)(NULL) == NULL) {
sprintf(card->pcm.name, "audio/raw/%s", name);
names[num_names++] = card->pcm.name;
sprintf(card->pcm.oldname, "audio/old/%s", name);
names[num_names++] = card->pcm.oldname;
}
#endif /* DO_PCM */
#if DO_MUX
sprintf(card->mux.name, "audio/mux/%s", name);
names[num_names++] = card->mux.name;
#endif /* DO_MUX */
#if DO_MIXER
sprintf(card->mixer.name, "audio/mix/%s", name);
names[num_names++] = card->mixer.name;
#endif /* DO_MIXER */
names[num_names] = NULL;
}
/* We use the SV chip in ISA DMA addressing mode, which is 24 bits */
/* so we need to find suitable, locked, contiguous memory in that */
/* physical address range. */
static status_t
find_low_memory(
cmedia_pci_dev * card)
{
size_t low_size = (MIN_MEMORY_SIZE+(B_PAGE_SIZE-1))&~(B_PAGE_SIZE-1);
physical_entry where;
size_t trysize;
area_id curarea;
void * addr;
char name[DEVNAME];
sprintf(name, "%s_low", card->name);
if (low_size < MIN_MEMORY_SIZE) {
low_size = MIN_MEMORY_SIZE;
}
trysize = low_size;
curarea = find_area(name);
if (curarea >= 0) { /* area there from previous run */
area_info ainfo;
ddprintf(("cmedia_pci: testing likely candidate...\n"));
if (get_area_info(curarea, &ainfo)) {
ddprintf(("cmedia_pci: no info\n"));
goto allocate;
}
/* test area we found */
trysize = ainfo.size;
addr = ainfo.address;
if (trysize < low_size) {
ddprintf(("cmedia_pci: too small (%lx)\n", trysize));
goto allocate;
}
if (get_memory_map(addr, trysize, &where, 1) < B_OK) {
ddprintf(("cmedia_pci: no memory map\n"));
goto allocate;
}
if ((uint32)where.address & 0xff000000) {
ddprintf(("cmedia_pci: bad physical address\n"));
goto allocate;
}
if (ainfo.lock < B_FULL_LOCK || where.size < low_size) {
ddprintf(("cmedia_pci: lock not contiguous\n"));
goto allocate;
}
dprintf("cmedia_pci: physical %p logical %p\n", where.address, ainfo.address);
goto a_o_k;
}
allocate:
if (curarea >= 0) {
delete_area(curarea); /* area didn't work */
curarea = -1;
}
ddprintf(("cmedia_pci: allocating new low area\n"));
curarea = create_area(name, &addr, B_ANY_KERNEL_ADDRESS,
trysize, B_LOMEM, B_READ_AREA | B_WRITE_AREA);
ddprintf(("cmedia_pci: create_area(%lx) returned %lx logical %p\n",
trysize, curarea, addr));
if (curarea < 0) {
goto oops;
}
if (get_memory_map(addr, low_size, &where, 1) < 0) {
delete_area(curarea);
curarea = B_ERROR;
goto oops;
}
ddprintf(("cmedia_pci: physical %p\n", where.address));
if ((uint32)where.address & 0xff000000) {
delete_area(curarea);
curarea = B_ERROR;
goto oops;
}
if ((((uint32)where.address)+low_size) & 0xff000000) {
delete_area(curarea);
curarea = B_ERROR;
goto oops;
}
/* hey, it worked! */
if (trysize > low_size) { /* don't waste */
resize_area(curarea, low_size);
}
oops:
if (curarea < 0) {
dprintf("cmedia_pci: failed to create low_mem area\n");
return curarea;
}
a_o_k:
ddprintf(("cmedia_pci: successfully found or created low area!\n"));
card->low_size = low_size;
card->low_mem = addr;
card->low_phys = (vuchar *)where.address;
card->map_low = curarea;
return B_OK;
}
static status_t
setup_cmedia_pci(
cmedia_pci_dev * card)
{
status_t err = B_OK;
/* cpu_status cp; */
ddprintf(("cmedia_pci: setup_cmedia_pci(%p)\n", card));
if ((card->pcm.init_sem = create_sem(1, "cm pcm init")) < B_OK)
goto bail;
#if 1
if ((*mpu401->create_device)(0x330, &card->midi.driver,
#else
if ((*mpu401->create_device)(card->info.u.h0.base_registers[3], &card->midi.driver,
#endif
0, midi_interrupt_op, &card->midi) < B_OK)
goto bail3;
#if 1
if ((*gameport->create_device)(0x201, &card->joy.driver) < B_OK)
#else
if ((*gameport->create_device)(card->info.u.h0.base_registers[4], &card->joy.driver) < B_OK)
#endif
goto bail4;
ddprintf(("midi %p gameport %p\n", card->midi.driver, card->joy.driver));
card->midi.card = card;
err = find_low_memory(card);
if (err < B_OK) {
goto bail5;
}
//cp = disable_interrupts();
//acquire_spinlock(&card->hardware);
make_device_names(card);
card->enhanced = card->info.u.h0.base_registers[0];
ddprintf(("cmedia_pci: %s enhanced at %x\n", card->name, card->enhanced));
ddprintf(("cmedia_pci: revision %x\n", get_indirect(card, 0x15)));
disable_card_interrupts(card);
if (setup_dma(card) != B_OK)
{
dprintf("cmedia pci: can't setup DMA\n");
goto bail6;
}
set_default_registers(card);
//release_spinlock(&card->hardware);
//restore_interrupts(cp);
return B_OK;
bail6:
// deallocate low memory
bail5:
(*gameport->delete_device)(card->joy.driver);
bail4:
(*mpu401->delete_device)(card->midi.driver);
bail3:
delete_sem(card->pcm.init_sem);
bail:
return err < B_OK ? err : B_ERROR;
}
static int
debug_cmedia(
int argc,
char * argv[])
{
int ix = 0;
if (argc == 2) {
ix = parse_expression(argv[1])-1;
}
if (argc > 2 || ix < 0 || ix >= num_cards) {
dprintf("cmedia_pci: dude, you gotta watch your syntax!\n");
return -1;
}
dprintf("%s: enhanced registers at 0x%x\n", cards[ix].name,
cards[ix].enhanced);
dprintf("%s: open %ld dma_a at 0x%x dma_c 0x%x\n", cards[ix].pcm.name,
cards[ix].pcm.open_count, cards[ix].pcm.dma_a, cards[ix].pcm.dma_c);
if (cards[ix].pcm.open_count) {
dprintf(" dma_a: 0x%lx+0x%lx dma_c: 0x%lx+0x%lx\n",
PCI_IO_RD_32((int)cards[ix].pcm.dma_a), PCI_IO_RD_32((int)cards[ix].pcm.dma_a+4),
PCI_IO_RD_32((int)cards[ix].pcm.dma_c), PCI_IO_RD_32((int)cards[ix].pcm.dma_c+4));
}
return 0;
}
status_t
init_driver(void)
{
int ix=0;
pci_info info;
num_cards = 0;
ddprintf(("cmedia_pci: init_driver()\n"));
load_driver_symbols("cmedia_pci");
if (get_module(pci_name, (module_info **) &pci))
return ENOSYS;
if (get_module(gameport_name, (module_info **) &gameport)) {
put_module(pci_name);
return ENOSYS;
}
ddprintf(("MPU\n"));
if (get_module(mpu401_name, (module_info **) &mpu401)) {
put_module(gameport_name);
put_module(pci_name);
return ENOSYS;
}
ddprintf(("MPU: %p\n", mpu401));
while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
if (info.vendor_id == CMEDIA_PCI_VENDOR_ID &&
(info.device_id == CMEDIA_8338A_DEVICE_ID ||
info.device_id == CMEDIA_8338B_DEVICE_ID ||
info.device_id == CMEDIA_8738A_DEVICE_ID ||
info.device_id == CMEDIA_8738B_DEVICE_ID )) {
if (num_cards == NUM_CARDS) {
dprintf("Too many C-Media cards installed!\n");
break;
}
memset(&cards[num_cards], 0, sizeof(cmedia_pci_dev));
cards[num_cards].info = info;
if (setup_cmedia_pci(&cards[num_cards])) {
dprintf("Setup of C-Media %ld failed\n", num_cards+1);
}
else {
num_cards++;
}
}
ix++;
}
if (!num_cards) {
KPRINTF(("no cards\n"));
put_module(mpu401_name);
put_module(gameport_name);
put_module(pci_name);
ddprintf(("cmedia_pci: no suitable cards found\n"));
return ENODEV;
}
#if DEBUG
add_debugger_command("cmedia", debug_cmedia, "cmedia [card# (1-n)]");
#endif
return B_OK;
}
static void
teardown_cmedia_pci(
cmedia_pci_dev * card)
{
static uchar regs[] = {
#ifdef DO_JOY
0x04, 0x00, 0x02, /* enable joystick */
#endif
#ifdef DO_MIDI
0x04, 0x00, 0x04, /* enable MPU-401 */
#endif
};
uchar * ptr = regs;
cpu_status cp;
/* remove created devices */
(*gameport->delete_device)(card->joy.driver);
(*mpu401->delete_device)(card->midi.driver);
cp = disable_interrupts();
acquire_spinlock(&card->hardware);
while (ptr < regs+sizeof(regs)) {
set_direct(card, ptr[0], ptr[1], ptr[2]);
ptr += 3;
}
disable_card_interrupts(card);
release_spinlock(&card->hardware);
restore_interrupts(cp);
delete_sem(card->pcm.init_sem);
}
void
uninit_driver(void)
{
int ix, cnt = num_cards;
num_cards = 0;
ddprintf(("cmedia_pci: uninit_driver()\n"));
remove_debugger_command("cmedia", debug_cmedia);
for (ix=0; ix<cnt; ix++) {
teardown_cmedia_pci(&cards[ix]);
}
memset(&cards, 0, sizeof(cards));
put_module(mpu401_name);
put_module(gameport_name);
put_module(pci_name);
}
const char **
publish_devices(void)
{
int ix = 0;
ddprintf(("cmedia_pci: publish_devices()\n"));
for (ix=0; names[ix]; ix++) {
ddprintf(("cmedia_pci: publish %s\n", names[ix]));
}
return (const char **)names;
}
device_hooks *
find_device(
const char * name)
{
int ix;
ddprintf(("cmedia_pci: find_device(%s)\n", name));
for (ix=0; ix<num_cards; ix++) {
#if DO_MIDI
if (!strcmp(cards[ix].midi.name, name)) {
return &midi_hooks;
}
#endif /* DO_MIDI */
#if DO_JOY
if (!strcmp(cards[ix].joy.name1, name)) {
return &joy_hooks;
}
#endif /* DO_JOY */
#if DO_PCM
if (!strcmp(cards[ix].pcm.name, name)) {
return &pcm_hooks;
}
if (!strcmp(cards[ix].pcm.oldname, name)) {
return &pcm_hooks;
}
#endif /* DO_PCM */
#if DO_MUX
if (!strcmp(cards[ix].mux.name, name)) {
return &mux_hooks;
}
#endif /* DO_MUX */
#if DO_MIXER
if (!strcmp(cards[ix].mixer.name, name)) {
return &mixer_hooks;
}
#endif /* DO_MIXER */
}
ddprintf(("cmedia_pci: find_device(%s) failed\n", name));
return NULL;
}
int32 api_version = B_CUR_DRIVER_API_VERSION;
static int32
cmedia_pci_interrupt(
void * data)
{
cpu_status cp = disable_interrupts();
cmedia_pci_dev * card = (cmedia_pci_dev *)data;
uchar status;
int32 handled = B_UNHANDLED_INTERRUPT;
/* KTRACE(); / * */
acquire_spinlock(&card->hardware);
status = get_direct(card, 0x10);
#if DEBUG
/* kprintf("%x\n", status); / * */
#endif
#if DO_PCM
if (status & 0x02) {
if (dma_c_interrupt(card)) {
handled = B_INVOKE_SCHEDULER;
}
else {
handled = B_HANDLED_INTERRUPT;
}
/* acknowledge interrupt */
set_direct(card, 0x0e, 0x00, 0x02);
set_direct(card, 0x0e, 0x02, 0x02);
}
if (status & 0x01) {
if (dma_a_interrupt(card)) {
handled = B_INVOKE_SCHEDULER;
}
else {
handled = B_HANDLED_INTERRUPT;
}
/* acknowledge interrupt */
set_direct(card, 0x0e, 0x00, 0x01);
set_direct(card, 0x0e, 0x01, 0x01);
}
#endif
#if DO_MIDI
status = get_direct(card, 0x12);
if (status & 0x01) {
if (midi_interrupt(card)) {
handled = B_INVOKE_SCHEDULER;
} else {
handled = B_HANDLED_INTERRUPT;
}
}
#endif
/* Sometimes, the Sonic Vibes will receive a byte of Midi data...
** And duly note it in the MPU401 status register...
** And generate an interrupt...
** But not bother setting the midi interrupt bit in the ISR.
** Thanks a lot, S3.
*/
if(handled == B_UNHANDLED_INTERRUPT){
if (midi_interrupt(card)) {
handled = B_INVOKE_SCHEDULER;
}
}
/* KTRACE(); / * */
release_spinlock(&card->hardware);
restore_interrupts(cp);
return handled;
// return (handled == B_INVOKE_SCHEDULER) ? B_HANDLED_INTERRUPT : handled;
}
void
increment_interrupt_handler(
cmedia_pci_dev * card)
{
KPRINTF(("cmedia_pci: increment_interrupt_handler()\n"));
if (atomic_add(&card->inth_count, 1) == 0) {
// !!!
KPRINTF(("cmedia_pci: intline %d int %p\n", card->info.u.h0.interrupt_line, cmedia_pci_interrupt));
install_io_interrupt_handler(card->info.u.h0.interrupt_line,
cmedia_pci_interrupt, card, 0);
}
}
void
decrement_interrupt_handler(
cmedia_pci_dev * card)
{
KPRINTF(("cmedia_pci: decrement_interrupt_handler()\n"));
if (atomic_add(&card->inth_count, -1) == 1) {
KPRINTF(("cmedia_pci: remove_io_interrupt_handler()\n"));
remove_io_interrupt_handler(card->info.u.h0.interrupt_line, cmedia_pci_interrupt, card);
}
}

View File

@ -0,0 +1,194 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#if !defined(_CM_PRIVATE_H)
#define _CM_PRIVATE_H
#if !defined(_CMEDIA_PCI_H)
#include "cmedia_pci.h"
#endif
#if !defined(_PCI_H)
#include <PCI.h>
#endif
#if !defined(DEBUG)
#define DEBUG 0
#endif
#define NUM_CARDS 3
#define DEVNAME 32
#define EXPORT __declspec(dllexport)
#if defined(__INTEL__)
#define EIEIO()
#elif defined(__POWERPC__)
#define EIEIO() __eieio()
#endif
#if DEBUG
#define ddprintf(x) dprintf x
#define KTRACE() kprintf("%s %d\n", __FILE__, __LINE__)
#else
#define ddprintf(x)
#define KTRACE()
#endif
/* neither value may be larger than 65536 */
#define PLAYBACK_BUF_SIZE 2048
#define RECORD_BUF_SIZE PLAYBACK_BUF_SIZE
/* always create this much, so larger buffers can be requested */
#define MIN_MEMORY_SIZE 32768
/* clock crystal frequency */
#define F_REF 24576000
/* data book says 80 MHz ... */
#define MIN_FREQ 80000000
/* tolerance for sample rate clock derivation - book has this at 0.005 */
#define TOLERANCE 0.001
/* there are five logical devices: midi, joystick, pcm, mux and mixer */
typedef struct _midi_dev
{
struct _cmedia_pci_dev *card;
void * driver;
void * cookie;
int32 count;
char name[64];
} midi_dev;
typedef struct _joy_dev
{
void * driver;
char name1[64];
} joy_dev;
typedef cmedia_pci_audio_format pcm_cfg;
typedef cmedia_pci_audio_buf_header pcm_buf_hdr;
enum
{ /* these map to the mode enable bits in the CMX13 register */
kPlayback = 1,
kRecord = 2
};
typedef struct
{
struct _cmedia_pci_dev * card;
char name[DEVNAME];
char oldname[DEVNAME];
sem_id init_sem;
int32 open_count;
int32 open_mode;
/* playback from a cyclic, small-ish buffer */
int32 wr_lock;
int dma_a;
vuchar * wr_1;
vuchar * wr_2;
vuchar * wr_cur;
size_t wr_size;
int wr_silence;
int32 write_waiting;
sem_id write_sem;
size_t was_written;
uint32 wr_skipped;
sem_id wr_entry;
bigtime_t wr_time;
uint64 wr_total;
sem_id wr_time_sem;
int32 wr_time_wait;
/* recording into a cyclic, somewhat larger buffer */
int32 rd_lock;
int dma_c;
vuchar * rd_1;
vuchar * rd_2;
vuchar * rd_cur;
size_t rd_size;
int32 read_waiting;
sem_id read_sem;
size_t was_read;
bigtime_t next_rd_time;
bigtime_t rd_time;
uint32 rd_skipped; /* count of misses */
sem_id rd_entry;
uint64 rd_total;
sem_id rd_time_sem;
int32 rd_time_wait;
/* buffers are owned by the device record (because of allocation) */
pcm_cfg config;
sem_id old_cap_sem;
sem_id old_play_sem;
} pcm_dev;
typedef struct
{
struct _cmedia_pci_dev * card;
char name[DEVNAME];
int32 open_count;
} mux_dev;
typedef struct
{
struct _cmedia_pci_dev * card;
char name[DEVNAME];
int32 open_count;
} mixer_dev;
typedef struct _cmedia_pci_dev
{
char name[DEVNAME]; /* used for resources */
int32 hardware; /* spinlock */
int enhanced; /* offset to port */
int32 inth_count;
int dma_base;
size_t low_size; /* size of low memory */
vuchar * low_mem;
vuchar * low_phys; /* physical address */
area_id map_low; /* area pointing to low memory */
pci_info info;
midi_dev midi;
joy_dev joy;
pcm_dev pcm;
mux_dev mux;
mixer_dev mixer;
} cmedia_pci_dev;
extern int32 num_cards;
extern cmedia_pci_dev cards[NUM_CARDS];
extern void set_direct(cmedia_pci_dev *, int, uchar, uchar);
extern uchar get_direct(cmedia_pci_dev *, int);
extern void set_indirect(cmedia_pci_dev *, int, uchar, uchar);
extern uchar get_indirect(cmedia_pci_dev *, int);
extern void increment_interrupt_handler(cmedia_pci_dev *);
extern void decrement_interrupt_handler(cmedia_pci_dev *);
extern bool midi_interrupt(cmedia_pci_dev *);
extern void midi_interrupt_op(int32 op, void * data);
extern bool dma_a_interrupt(cmedia_pci_dev *);
extern bool dma_c_interrupt(cmedia_pci_dev *);
extern void PCI_IO_WR(int offset, uint8 val);
extern uint8 PCI_IO_RD(int offset);
extern uint32 PCI_IO_RD_32(int offset);
extern generic_gameport_module * gameport;
#endif /* _CM_PRIVATE_H */

View File

@ -0,0 +1,112 @@
/* cmedia_pci.h -- specifics for S3-based PCI audio cards */
/* $Id: cmedia_pci.h,v 1.3 1999/10/13 02:29:19 cltien Exp $ */
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#if !defined(_CMEDIA_PCI_H)
#define _CMEDIA_PCI_H
#include <Drivers.h>
#include <SupportDefs.h>
#include <OS.h>
#include "audio_driver.h"
#include "midi_driver.h"
#include "joystick_driver.h"
#define CMEDIA_PCI_VENDOR_ID 0x13F6 /* C-Media Inc */
#define CMEDIA_8338A_DEVICE_ID 0x0100 /* CM8338A */
#define CMEDIA_8338B_DEVICE_ID 0x0101 /* CM8338B */
#define CMEDIA_8738A_DEVICE_ID 0x0111 /* CM8738A */
#define CMEDIA_8738B_DEVICE_ID 0x0112 /* CM8738B */
#define CMEDIA_PCI_JOYSTICK_MIN_LATENCY 5000 /* 200 times a second! */
#define CMEDIA_PCI_JOYSTICK_MAX_LATENCY 100000 /* 10 times a second */
typedef struct joystick cmedia_pci_joystick;
typedef struct audio_format cmedia_pci_audio_format;
typedef struct audio_buf_header cmedia_pci_audio_buf_header;
/* the mux devices use these records */
typedef audio_routing cmedia_pci_routing;
/* this is the argument for ioctl() */
typedef audio_routing_cmd cmedia_pci_routing_cmd;
/* selectors for routing */
#define CMEDIA_PCI_INPUT_MUX B_AUDIO_INPUT_SELECT
#define CMEDIA_PCI_MIC_BOOST B_AUDIO_MIC_BOOST
#define CMEDIA_PCI_MIDI_OUTPUT_TO_SYNTH B_AUDIO_MIDI_OUTPUT_TO_SYNTH
#define CMEDIA_PCI_MIDI_INPUT_TO_SYNTH B_AUDIO_MIDI_INPUT_TO_SYNTH
#define CMEDIA_PCI_MIDI_OUTPUT_TO_PORT B_AUDIO_MIDI_OUTPUT_TO_PORT
/* input MUX source values */
#define CMEDIA_PCI_INPUT_CD B_AUDIO_INPUT_CD
#define CMEDIA_PCI_INPUT_DAC B_AUDIO_INPUT_DAC
#define CMEDIA_PCI_INPUT_AUX2 B_AUDIO_INPUT_AUX2
#define CMEDIA_PCI_INPUT_LINE B_AUDIO_INPUT_LINE_IN
#define CMEDIA_PCI_INPUT_AUX1 B_AUDIO_INPUT_AUX1
#define CMEDIA_PCI_INPUT_MIC B_AUDIO_INPUT_MIC
#define CMEDIA_PCI_INPUT_MIX_OUT B_AUDIO_INPUT_MIX_OUT
/* the mixer devices use these records */
typedef audio_level cmedia_pci_level;
/* this is the arg to ioctl() */
typedef audio_level_cmd cmedia_pci_level_cmd;
/* bitmask for the flags */
#define CMEDIA_PCI_LEVEL_MUTED B_AUDIO_LEVEL_MUTED
/* selectors for levels */
#define CMEDIA_PCI_LEFT_ADC_INPUT_G B_AUDIO_MIX_ADC_LEFT
#define CMEDIA_PCI_RIGHT_ADC_INPUT_G B_AUDIO_MIX_ADC_RIGHT
#define CMEDIA_PCI_LEFT_AUX1_LOOPBACK_GAM B_AUDIO_MIX_VIDEO_LEFT
#define CMEDIA_PCI_RIGHT_AUX1_LOOPBACK_GAM B_AUDIO_MIX_VIDEO_RIGHT
#define CMEDIA_PCI_LEFT_CD_LOOPBACK_GAM B_AUDIO_MIX_CD_LEFT
#define CMEDIA_PCI_RIGHT_CD_LOOPBACK_GAM B_AUDIO_MIX_CD_RIGHT
#define CMEDIA_PCI_LEFT_LINE_LOOPBACK_GAM B_AUDIO_MIX_LINE_IN_LEFT
#define CMEDIA_PCI_RIGHT_LINE_LOOPBACK_GAM B_AUDIO_MIX_LINE_IN_RIGHT
#define CMEDIA_PCI_MIC_LOOPBACK_GAM B_AUDIO_MIX_MIC
#define CMEDIA_PCI_LEFT_SYNTH_OUTPUT_GAM B_AUDIO_MIX_SYNTH_LEFT
#define CMEDIA_PCI_RIGHT_SYNTH_OUTPUT_GAM B_AUDIO_MIX_SYNTH_RIGHT
#define CMEDIA_PCI_LEFT_AUX2_LOOPBACK_GAM B_AUDIO_MIX_AUX_LEFT
#define CMEDIA_PCI_RIGHT_AUX2_LOOPBACK_GAM B_AUDIO_MIX_AUX_RIGHT
#define CMEDIA_PCI_LEFT_MASTER_VOLUME_AM B_AUDIO_MIX_LINE_OUT_LEFT
#define CMEDIA_PCI_RIGHT_MASTER_VOLUME_AM B_AUDIO_MIX_LINE_OUT_RIGHT
#define CMEDIA_PCI_LEFT_PCM_OUTPUT_GAM B_AUDIO_MIX_DAC_LEFT
#define CMEDIA_PCI_RIGHT_PCM_OUTPUT_GAM B_AUDIO_MIX_DAC_RIGHT
#define CMEDIA_PCI_DIGITAL_LOOPBACK_AM B_AUDIO_MIX_LOOPBACK_LEVEL
/* secret handshake ioctl()s */
#define SV_SECRET_HANDSHAKE 10100
typedef struct {
bigtime_t wr_time;
bigtime_t rd_time;
uint32 wr_skipped;
uint32 rd_skipped;
uint64 wr_total;
uint64 rd_total;
uint32 _reserved_[6];
} sv_handshake;
#define SV_RD_TIME_WAIT 10101
#define SV_WR_TIME_WAIT 10102
typedef struct {
bigtime_t time;
bigtime_t bytes;
uint32 skipped;
uint32 _reserved_[3];
} sv_timing;
#endif /* _CMEDIA_PCI_H */

View File

@ -0,0 +1,112 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include "cm_private.h"
#include <string.h>
#if !defined(_KERNEL_EXPORT_H)
#include <KernelExport.h>
#endif /* _KERNEL_EXPORT_H */
static status_t joy_open(const char *name, uint32 flags, void **cookie);
static status_t joy_close(void *cookie);
static status_t joy_free(void *cookie);
static status_t joy_control(void *cookie, uint32 op, void *data, size_t len);
static status_t joy_read(void *cookie, off_t pos, void *data, size_t *len);
static status_t joy_write(void *cookie, off_t pos, const void *data, size_t *len);
#define MIN_COMP -7
#define MAX_COMP 8
device_hooks joy_hooks = {
&joy_open,
&joy_close,
&joy_free,
&joy_control,
&joy_read,
&joy_write,
NULL, /* select */
NULL, /* deselect */
NULL, /* readv */
NULL /* writev */
};
static status_t
joy_open(
const char * name,
uint32 flags,
void ** cookie)
{
int ix;
int offset = -1;
ddprintf(("cmedia_pci: joy_open()\n"));
*cookie = NULL;
for (ix=0; ix<num_cards; ix++) {
if (!strcmp(name, cards[ix].joy.name1)) {
offset = 0;
break;
}
}
if (offset < 0) {
return ENODEV;
}
return (*gameport->open_hook)(cards[ix].joy.driver, flags, cookie);
}
static status_t
joy_close(
void * cookie)
{
return (*gameport->close_hook)(cookie);
}
static status_t
joy_free(
void * cookie)
{
return (*gameport->free_hook)(cookie);
}
static status_t
joy_control(
void * cookie,
uint32 iop,
void * data,
size_t len)
{
return (*gameport->control_hook)(cookie, iop, data, len);
}
static status_t
joy_read(
void * cookie,
off_t pos,
void * data,
size_t * nread)
{
return (*gameport->read_hook)(cookie, pos, data, nread);
}
static status_t
joy_write(
void * cookie,
off_t pos,
const void * data,
size_t * nwritten)
{
return (*gameport->write_hook)(cookie, pos, data, nwritten);
}

View File

@ -0,0 +1,113 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _JOYSTICK_DRIVER_H
#define _JOYSTICK_DRIVER_H
#include <SupportDefs.h>
#include <Drivers.h>
#include <module.h>
typedef struct _joystick {
bigtime_t timestamp;
uint32 horizontal;
uint32 vertical;
bool button1;
bool button2;
} joystick;
/* maximum number of axes on one controller (pads count as 2 axes each) */
#define MAX_AXES 12
/* maximum number of hats on one controller -- PADS SHOULD BE RETURNED AS AXES! */
#define MAX_HATS 8
/* maximum number of buttons on one controller */
#define MAX_BUTTONS 32
/* maximum number of controllers on one port */
#define MAX_STICKS 4
typedef struct _extended_joystick {
bigtime_t timestamp; /* system_time when it was read */
uint32 buttons; /* lsb to msb, 1 == on */
int16 axes[MAX_AXES]; /* -32768 to 32767, X, Y, Z, U, V, W */
uint8 hats[MAX_HATS]; /* 0 through 8 (1 == N, 3 == E, 5 == S, 7 == W) */
} extended_joystick;
#define MAX_CONFIG_SIZE 100
enum { /* flags for joystick module info */
js_flag_force_feedback = 0x1,
js_flag_force_feedback_directional = 0x2
};
typedef struct _joystick_module_info {
char module_name[64];
char device_name[64];
int16 num_axes;
int16 num_buttons;
int16 num_hats;
uint16 _reserved[7];
uint32 flags;
uint16 num_sticks;
int16 config_size;
char device_config[MAX_CONFIG_SIZE]; /* device specific */
} joystick_module_info;
/* Note that joystick_module is something used by the game port driver */
/* to talk to digital joysticks; if you're writing a sound card driver */
/* and want to add support for a /dev/joystick device, use the generic_gameport */
/* module. */
typedef struct _joystick_module {
module_info minfo;
/** "configure" might change the "info" if it auto-detects a device */
int (*configure)(int port, joystick_module_info * info, size_t size, void ** out_cookie);
/** "read" actual data from device into "data" */
int (*read)(void * cookie, int port, extended_joystick * data, size_t size);
/** "crumble" the cookie (deallocate) when done */
int (*crumble)(void * cookie, int port);
/** "force" tells the joystick to exert force on the same axes as input for the specified duration */
int (*force)(void * cookie, int port, bigtime_t duration, extended_joystick * force, size_t size);
int _reserved_;
} joystick_module;
/** Doing force feedback means writing an extended_joystick to the device with force values.
The "timestamp" should be the duration of the feedback. Successive writes will be queued
by the device module. */
enum { /* Joystick driver ioctl() opcodes */
B_JOYSTICK_GET_SPEED_COMPENSATION = B_JOYSTICK_DRIVER_BASE,
/* arg -> ptr to int32 */
B_JOYSTICK_SET_SPEED_COMPENSATION, /* arg -> ptr to int32 */
B_JOYSTICK_GET_MAX_LATENCY, /* arg -> ptr to long long */
B_JOYSTICK_SET_MAX_LATENCY, /* arg -> ptr to long long */
B_JOYSTICK_SET_DEVICE_MODULE, /* arg -> ptr to joystick_module; also enters enhanced mode */
B_JOYSTICK_GET_DEVICE_MODULE, /* arg -> ptr to joystick_module */
B_JOYSTICK_SET_RAW_MODE /* arg -> ptr to bool (true or false) */
};
/* Speed compensation is not generally necessary, because the joystick */
/* driver is measuring using real time, not just # cycles. "0" means the */
/* default, center value. + typically returns higher values; - returns lower */
/* A typical range might be from -10 to +10, but it varies by driver */
/* Lower latency will make for more overhead in reading the joystick */
/* ideally, you set this value to just short of how long it takes you */
/* to calculate and render a frame. 30 fps -> latency 33000 */
typedef struct _generic_gameport_module {
module_info minfo;
status_t (*create_device)(int port, void ** out_storage);
status_t (*delete_device)(void * storage);
status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie);
status_t (*close_hook)(void * cookie);
status_t (*free_hook)(void * cookie);
status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len);
status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len);
status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len);
int _reserved_;
} generic_gameport_module;
#endif

View File

@ -0,0 +1,186 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include "cm_private.h"
extern void dump_card(cmedia_pci_dev * card);
#if !defined(_KERNEL_EXPORT_H)
#include <KernelExport.h>
#endif /* _KERNEL_EXPORT_H */
#define MIDI_ACTIVE_SENSE 0xfe
#define SNOOZE_GRANULARITY 500
extern generic_mpu401_module * mpu401;
void
midi_interrupt_op(
int32 op,
void * data)
{
midi_dev * port = (midi_dev *)data;
ddprintf(("port = %p\n", port));
if (op == B_MPU_401_ENABLE_CARD_INT) {
cpu_status cp;
ddprintf(("cmedia_pci: B_MPU_401_ENABLE_CARD_INT\n"));
cp = disable_interrupts();
acquire_spinlock(&port->card->hardware);
increment_interrupt_handler(port->card);
set_direct(port->card, 0x01, 0x00, 0x80);
set_indirect(port->card, 0x2A, 0x04, 0xff);
release_spinlock(&port->card->hardware);
restore_interrupts(cp);
}
else if (op == B_MPU_401_DISABLE_CARD_INT) {
/* turn off MPU interrupts */
cpu_status cp;
ddprintf(("cmedia_pci: B_MPU_401_DISABLE_CARD_INT\n"));
cp = disable_interrupts();
acquire_spinlock(&port->card->hardware);
set_direct(port->card, 0x01, 0x80, 0x80);
/* remove interrupt handler if necessary */
decrement_interrupt_handler(port->card);
release_spinlock(&port->card->hardware);
restore_interrupts(cp);
}
ddprintf(("cmedia_pci: midi_interrupt_op() done\n"));
}
static status_t midi_open(const char *name, uint32 flags, void **cookie);
static status_t midi_close(void *cookie);
static status_t midi_free(void *cookie);
static status_t midi_control(void *cookie, uint32 op, void *data, size_t len);
static status_t midi_read(void *cookie, off_t pos, void *data, size_t *len);
static status_t midi_write(void *cookie, off_t pos, const void *data, size_t *len);
device_hooks midi_hooks = {
&midi_open,
&midi_close,
&midi_free,
&midi_control,
&midi_read,
&midi_write,
NULL, /* select */
NULL, /* deselect */
NULL, /* readv */
NULL /* writev */
};
static status_t
midi_open(
const char * name,
uint32 flags,
void ** cookie)
{
int ix;
int ret;
ddprintf(("cmedia_pci: midi_open()\n"));
*cookie = NULL;
for (ix=0; ix<num_cards; ix++) {
if (!strcmp(name, cards[ix].midi.name)) {
break;
}
}
if (ix >= num_cards) {
ddprintf(("bad device\n"));
return ENODEV;
}
ddprintf(("cmedia_pci: mpu401: %p open(): %p driver: %p\n", mpu401, mpu401->open_hook, cards[ix].midi.driver));
ret = (*mpu401->open_hook)(cards[ix].midi.driver, flags, cookie);
if (ret >= B_OK) {
cards[ix].midi.cookie = *cookie;
atomic_add(&cards[ix].midi.count, 1);
}
ddprintf(("cmedia_pci: mpu401: open returns %x / %p\n", ret, *cookie));
return ret;
}
static status_t
midi_close(
void * cookie)
{
ddprintf(("cmedia_pci: midi_close()\n"));
return (*mpu401->close_hook)(cookie);
}
static status_t
midi_free(
void * cookie)
{
int ix;
status_t f;
ddprintf(("cmedia_pci: midi_free()\n"));
f = (*mpu401->free_hook)(cookie);
for (ix=0; ix<num_cards; ix++) {
if (cards[ix].midi.cookie == cookie) {
if (atomic_add(&cards[ix].midi.count, -1) == 1) {
cards[ix].midi.cookie = NULL;
ddprintf(("cleared %p card %d\n", cookie, ix));
}
break;
}
}
ddprintf(("cmedia_pci: midi_free() done\n"));
return f;
}
static status_t
midi_control(
void * cookie,
uint32 iop,
void * data,
size_t len)
{
return (*mpu401->control_hook)(cookie, iop, data, len);
}
static status_t
midi_read(
void * cookie,
off_t pos,
void * ptr,
size_t * nread)
{
return (*mpu401->read_hook)(cookie, pos, ptr, nread);
}
static status_t
midi_write(
void * cookie,
off_t pos,
const void * ptr,
size_t * nwritten)
{
return (*mpu401->write_hook)(cookie, pos, ptr, nwritten);
}
bool
midi_interrupt(
cmedia_pci_dev * dev)
{
if (!dev->midi.driver) {
// kprintf("aiigh\n");
return false;
}
return (*mpu401->interrupt_hook)(dev->midi.driver);
}

View File

@ -0,0 +1,110 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _MIDI_DRIVER_H
#define _MIDI_DRIVER_H
#include <Drivers.h>
#include <module.h>
/* -----
ioctl codes
----- */
/* the old opcodes are deprecated, and may or may not work
in newer drivers */
enum {
B_MIDI_GET_READ_TIMEOUT = B_MIDI_DRIVER_BASE,
B_MIDI_SET_READ_TIMEOUT,
B_MIDI_TIMED_READ,
B_MIDI_TIMED_WRITE,
B_MIDI_WRITE_SYNC,
B_MIDI_WRITE_CLEAR,
B_MIDI_GET_READ_TIMEOUT_OLD = B_DEVICE_OP_CODES_END + 1,
B_MIDI_SET_READ_TIMEOUT_OLD
};
/* the default timeout when you open a midi driver */
#define B_MIDI_DEFAULT_TIMEOUT 1000000000000000LL
/* Usage:
To read, set "data" to a pointer to your buffer, and "size" to the
maximum size of this buffer. On return, "when" will contain the time
at which the data was received (first byte) and "size" will contain
the actual amount of data read.
Call ioctl(fd, B_MIDI_TIMED_READ, &midi_timed_data, sizeof(midi_timed_data));
To write, set "when" to when you want the first byte to go out the
wire, set "data" to point to your data, and set "size" to the size
of your data.
Call ioctl(fd, B_MIDI_TIMED_WRITE, &midi_timed_data, sizeof(midi_timed_data));
*/
typedef struct {
bigtime_t when;
size_t size;
unsigned char * data;
} midi_timed_data;
/* The MIDI parser returns the number of bytes that a message contains, given the */
/* initial byte. For some messages, this is not known until the second byte is seen. */
/* For such messages, a state > 0 is returned as well as some count > 0. When state */
/* is > 0, you should call (*parse) for the next byte as well, which might modify */
/* the returned message size. Message size will always be returned with the current */
/* byte being counted as byte 1. A return of 0 means that the byte initiates a new */
/* message. SysX is handled by returning max_size until the end or next initial message */
/* is seen. So your loop looks something like: */
/*
uint32 state = 0; // Only set this to 0 the first time you call the parser.
// preserve the 'state' between invocations of your read() hook.
int todo = 0;
unsigned char * end = in_buf+buf_size
unsigned char * out_buf = in_buf;
while (true) {
uchar byte = read_midi();
if (!todo || state) {
todo = (*parser->parse)(&state, byte, end-out_buf);
}
if (todo < 1) {
unput_midi(byte);
} else {
*(out_buf++) = byte;
todo--;
}
if (todo < 1 || out_buf >= end) {
received_midi_message(in_buf, out_buf-in_buf);
todo = 0;
}
}
*/
#define B_MIDI_PARSER_MODULE_NAME "media/midiparser/v1"
typedef struct _midi_parser_module_info {
module_info minfo;
int (*parse)(uint32 * state, uchar byte, size_t max_size);
int _reserved_;
} midi_parser_module_info;
#define B_MPU_401_MODULE_NAME "generic/mpu401/v1"
enum {
B_MPU_401_ENABLE_CARD_INT = 1,
B_MPU_401_DISABLE_CARD_INT
};
typedef struct _generic_mpu401_module {
module_info minfo;
status_t (*create_device)(int port, void ** out_storage, uint32 workarounds, void (*interrupt_op)(int32 op, void * card), void * card);
status_t (*delete_device)(void * storage);
status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie);
status_t (*close_hook)(void * cookie);
status_t (*free_hook)(void * cookie);
status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len);
status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len);
status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len);
bool (*interrupt_hook)(void * cookie);
int _reserved_;
} generic_mpu401_module;
#endif

View File

@ -0,0 +1,304 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include "cm_private.h"
#include <string.h>
#if !defined(_KERNEL_EXPORT_H)
#include <KernelExport.h>
#endif /* _KERNEL_EXPORT_H */
static status_t mixer_open(const char *name, uint32 flags, void **cookie);
static status_t mixer_close(void *cookie);
static status_t mixer_free(void *cookie);
static status_t mixer_control(void *cookie, uint32 op, void *data, size_t len);
static status_t mixer_read(void *cookie, off_t pos, void *data, size_t *len);
static status_t mixer_write(void *cookie, off_t pos, const void *data, size_t *len);
device_hooks mixer_hooks = {
&mixer_open,
&mixer_close,
&mixer_free,
&mixer_control,
&mixer_read,
&mixer_write,
NULL, /* select */
NULL, /* deselect */
NULL, /* readv */
NULL /* writev */
};
typedef struct {
int selector;
int port;
float div;
float sub;
int minval;
int maxval;
int leftshift;
int mask;
int mutemask;
} mixer_info;
/* mute is special -- when it's 0x01, it means enable... */
mixer_info the_mixers[] = {
{CMEDIA_PCI_LEFT_ADC_INPUT_G, 0, 1.5, 0.0, 0, 15, 0, 0x0f, 0x00},
{CMEDIA_PCI_RIGHT_ADC_INPUT_G, 1, 1.5, 0.0, 0, 15, 0, 0x0f, 0x00},
{CMEDIA_PCI_LEFT_AUX1_LOOPBACK_GAM, 2, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_RIGHT_AUX1_LOOPBACK_GAM, 3, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_LEFT_CD_LOOPBACK_GAM, 4, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_RIGHT_CD_LOOPBACK_GAM, 5, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_LEFT_LINE_LOOPBACK_GAM, 6, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_RIGHT_LINE_LOOPBACK_GAM, 7, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_MIC_LOOPBACK_GAM, 8, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_LEFT_SYNTH_OUTPUT_GAM, 0xa, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_RIGHT_SYNTH_OUTPUT_GAM, 0xb, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_LEFT_AUX2_LOOPBACK_GAM, 0xc, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_RIGHT_AUX2_LOOPBACK_GAM, 0xd, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_LEFT_MASTER_VOLUME_AM, 0xe, -1.5, 0.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_RIGHT_MASTER_VOLUME_AM, 0xf, -1.5, 0.0, 0, 31, 0, 0x1f, 0x80},
{CMEDIA_PCI_LEFT_PCM_OUTPUT_GAM, 0x10, -1.5, 0.0, 0, 63, 0, 0x3f, 0x80},
{CMEDIA_PCI_RIGHT_PCM_OUTPUT_GAM, 0x11, -1.5, 0.0, 0, 63, 0, 0x3f, 0x80},
{CMEDIA_PCI_DIGITAL_LOOPBACK_AM, 0x16, -1.5, 0.0, 0, 63, 2, 0xfc, 0x01},
};
#define N_MIXERS (sizeof(the_mixers) / sizeof(the_mixers[0]))
static int
map_mixer(int selector) {
int i;
for (i = 0; i < N_MIXERS; i++)
if (the_mixers[i].selector == selector)
return i;
return -1;
}
static status_t
mixer_open(
const char * name,
uint32 flags,
void ** cookie)
{
int ix;
/* mixer_dev * it = NULL; */
ddprintf(("cmedia_pci: mixer_open()\n"));
*cookie = NULL;
for (ix=0; ix<num_cards; ix++) {
if (!strcmp(name, cards[ix].mixer.name)) {
break;
}
}
if (ix == num_cards) {
return ENODEV;
}
atomic_add(&cards[ix].mixer.open_count, 1);
cards[ix].mixer.card = &cards[ix];
*cookie = &cards[ix].mixer;
return B_OK;
}
static status_t
mixer_close(
void * cookie)
{
mixer_dev * it = (mixer_dev *)cookie;
atomic_add(&it->open_count, -1);
return B_OK;
}
static status_t
mixer_free(
void * cookie)
{
ddprintf(("cmedia_pci: mixer_free()\n"));
if (((mixer_dev *)cookie)->open_count != 0) {
dprintf("cmedia_pci: mixer open_count is bad in mixer_free()!\n");
}
return B_OK; /* already done in close */
}
static int
get_mixer_value(
cmedia_pci_dev * card,
cmedia_pci_level * lev)
{
int ix = map_mixer(lev->selector);
uchar val;
if (ix < 0) {
return B_BAD_VALUE;
}
val = get_indirect(card, the_mixers[ix].port);
lev->flags = 0;
if (!the_mixers[ix].mutemask) {
/* no change */
}
else if (the_mixers[ix].mutemask == 0x01) {
if (!(val & 0x01)) {
lev->flags |= CMEDIA_PCI_LEVEL_MUTED;
}
}
else if (val & the_mixers[ix].mutemask) {
lev->flags |= CMEDIA_PCI_LEVEL_MUTED;
}
val &= the_mixers[ix].mask;
val >>= the_mixers[ix].leftshift;
lev->value = ((float)val)*the_mixers[ix].div+the_mixers[ix].sub;
return B_OK;
}
static int
gather_info(
mixer_dev * mixer,
cmedia_pci_level * data,
int count)
{
int ix;
cpu_status cp;
cp = disable_interrupts();
acquire_spinlock(&mixer->card->hardware);
for (ix=0; ix<count; ix++) {
if (get_mixer_value(mixer->card, &data[ix]) < B_OK)
break;
}
release_spinlock(&mixer->card->hardware);
restore_interrupts(cp);
return ix;
}
static status_t
set_mixer_value(
cmedia_pci_dev * card,
cmedia_pci_level * lev)
{
int selector = map_mixer(lev->selector);
int value;
int mask;
if (selector < 0) {
return EINVAL;
}
value = (lev->value-the_mixers[selector].sub)/the_mixers[selector].div;
if (value < the_mixers[selector].minval) {
value = the_mixers[selector].minval;
}
if (value > the_mixers[selector].maxval) {
value = the_mixers[selector].maxval;
}
value <<= the_mixers[selector].leftshift;
if (the_mixers[selector].mutemask) {
if (the_mixers[selector].mutemask == 0x01) {
if (!(lev->flags & CMEDIA_PCI_LEVEL_MUTED)) {
value |= the_mixers[selector].mutemask;
}
} else {
if (lev->flags & CMEDIA_PCI_LEVEL_MUTED) {
value |= the_mixers[selector].mutemask;
}
}
}
mask = the_mixers[selector].mutemask | the_mixers[selector].mask;
set_indirect(card, the_mixers[selector].port, value, mask);
return B_OK;
}
static int
disperse_info(
mixer_dev * mixer,
cmedia_pci_level * data,
int count)
{
int ix;
cpu_status cp;
cp = disable_interrupts();
acquire_spinlock(&mixer->card->hardware);
for (ix=0; ix<count; ix++) {
if (set_mixer_value(mixer->card, &data[ix]) < B_OK)
break;
}
release_spinlock(&mixer->card->hardware);
restore_interrupts(cp);
return ix;
}
static status_t
mixer_control(
void * cookie,
uint32 iop,
void * data,
size_t len)
{
mixer_dev * it = (mixer_dev *)cookie;
status_t err = B_OK;
if (!data) {
return B_BAD_VALUE;
}
ddprintf(("cmedia_pci: mixer_control()\n")); /* slow printing */
switch (iop) {
case B_MIXER_GET_VALUES:
((cmedia_pci_level_cmd *)data)->count =
gather_info(it, ((cmedia_pci_level_cmd *)data)->data,
((cmedia_pci_level_cmd *)data)->count);
break;
case B_MIXER_SET_VALUES:
((cmedia_pci_level_cmd *)data)->count =
disperse_info(it, ((cmedia_pci_level_cmd *)data)->data,
((cmedia_pci_level_cmd *)data)->count);
break;
default:
err = B_BAD_VALUE;
break;
}
return err;
}
static status_t
mixer_read(
void * cookie,
off_t pos,
void * data,
size_t * nread)
{
return EPERM;
}
static status_t
mixer_write(
void * cookie,
off_t pos,
const void * data,
size_t * nwritten)
{
return EPERM;
}

View File

@ -0,0 +1,281 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include "cm_private.h"
#include <string.h>
#if !defined(_KERNEL_EXPORT_H)
#include <KernelExport.h>
#endif /* _KERNEL_EXPORT_H */
static status_t mux_open(const char *name, uint32 flags, void **cookie);
static status_t mux_close(void *cookie);
static status_t mux_free(void *cookie);
static status_t mux_control(void *cookie, uint32 op, void *data, size_t len);
static status_t mux_read(void *cookie, off_t pos, void *data, size_t *len);
static status_t mux_write(void *cookie, off_t pos, const void *data, size_t *len);
device_hooks mux_hooks = {
&mux_open,
&mux_close,
&mux_free,
&mux_control,
&mux_read,
&mux_write,
NULL, /* select */
NULL, /* deselect */
NULL, /* readv */
NULL /* writev */
};
typedef struct {
int port_l;
int port_r;
int minval;
int maxval;
int leftshift;
int mask;
char name[B_OS_NAME_LENGTH];
} mux_info;
const mux_info the_muxes[] = {
{ 0, 1, 1, 7, 5, 0xe0, "Sampling Input" },
{ 0, -1, 0, 1, 4, 0x10, "Mic +20dB Selection" },
{ 0x2a, -1, 0, 1, 0, 0x01, "MIDI Output To Synth" },
{ 0x2a, -1, 0, 1, 1, 0x02, "MIDI Input To Synth" },
{ 0x2a, -1, 0, 1, 2, 0x04, "MIDI Output To Port" },
};
static const uchar unmap_input[] = {
0,
CMEDIA_PCI_INPUT_CD,
CMEDIA_PCI_INPUT_DAC,
CMEDIA_PCI_INPUT_AUX2,
CMEDIA_PCI_INPUT_LINE,
CMEDIA_PCI_INPUT_AUX1,
CMEDIA_PCI_INPUT_MIC,
CMEDIA_PCI_INPUT_MIX_OUT
};
static uchar
map_input(uchar val) {
int i;
for (i = 0; i < 8; i++)
if (unmap_input[i] == val)
return i;
return 0;
}
static status_t
mux_open(
const char * name,
uint32 flags,
void ** cookie)
{
int ix;
/* mux_dev * plex = NULL; */
ddprintf(("cmedia_pci: mux_open()\n"));
*cookie = NULL;
for (ix=0; ix<num_cards; ix++) {
if (!strcmp(name, cards[ix].mux.name)) {
break;
}
}
if (ix == num_cards) {
return ENODEV;
}
atomic_add(&cards[ix].mux.open_count, 1);
cards[ix].mux.card = &cards[ix];
*cookie = &cards[ix].mux;
return B_OK;
}
static status_t
mux_close(
void * cookie)
{
mux_dev * plex = (mux_dev *)cookie;
atomic_add(&plex->open_count, -1);
return B_OK;
}
static status_t
mux_free(
void * cookie)
{
ddprintf(("cmedia_pci: mux_free()\n"));
if (((mux_dev *)cookie)->open_count != 0) {
dprintf("cmedia_pci: mux open_count is bad in mux_free()!\n");
}
return B_OK; /* already done in close */
}
static int
get_mux_value(
cmedia_pci_dev * card,
int ix)
{
uchar val;
if (ix < 0) {
return -1;
}
if (ix > 4) {
return -1;
}
val = get_indirect(card, the_muxes[ix].port_l);
val &= the_muxes[ix].mask;
val >>= the_muxes[ix].leftshift;
if (ix == CMEDIA_PCI_INPUT_MUX)
return unmap_input[val];
return val;
}
static int
gather_info(
mux_dev * mux,
cmedia_pci_routing * data,
int count)
{
int ix;
cpu_status cp;
cp = disable_interrupts();
acquire_spinlock(&mux->card->hardware);
for (ix=0; ix<count; ix++) {
data[ix].value = get_mux_value(mux->card, data[ix].selector);
if (data[ix].value < 0) {
break;
}
}
release_spinlock(&mux->card->hardware);
restore_interrupts(cp);
return ix;
}
static status_t
set_mux_value(
cmedia_pci_dev * card,
int selector,
int value)
{
ddprintf(("set_mux_value(%d,%d)\n", selector, value));
if (selector < 0 || selector > 4) {
ddprintf(("selector EINVAL\n"));
return EINVAL;
}
if (selector == CMEDIA_PCI_INPUT_MUX)
value = map_input(value);
if (value < the_muxes[selector].minval ||
value > the_muxes[selector].maxval) {
ddprintf(("value EINVAL\n"));
return EINVAL;
}
set_indirect(card, the_muxes[selector].port_l,
(value << the_muxes[selector].leftshift),
the_muxes[selector].mask);
if (the_muxes[selector].port_r > -1) {
set_indirect(card, the_muxes[selector].port_r,
(value << the_muxes[selector].leftshift),
the_muxes[selector].mask);
}
return B_OK;
}
static int
disperse_info(
mux_dev * mux,
cmedia_pci_routing * data,
int count)
{
int ix;
cpu_status cp;
cp = disable_interrupts();
acquire_spinlock(&mux->card->hardware);
for (ix=0; ix<count; ix++) {
if (set_mux_value(mux->card, data[ix].selector, data[ix].value) < B_OK) {
break;
}
}
release_spinlock(&mux->card->hardware);
restore_interrupts(cp);
return ix;
}
static status_t
mux_control(
void * cookie,
uint32 iop,
void * data,
size_t len)
{
mux_dev * plex = (mux_dev *)cookie;
status_t err = B_OK;
ddprintf(("cmedia_pci: mux_control()\n")); /* slow printing */
switch (iop) {
case B_ROUTING_GET_VALUES:
((cmedia_pci_routing_cmd *)data)->count =
gather_info(plex, ((cmedia_pci_routing_cmd *)data)->data,
((cmedia_pci_routing_cmd *)data)->count);
break;
case B_ROUTING_SET_VALUES:
((cmedia_pci_routing_cmd *)data)->count =
disperse_info(plex, ((cmedia_pci_routing_cmd *)data)->data,
((cmedia_pci_routing_cmd *)data)->count);
break;
default:
err = B_BAD_VALUE;
break;
}
return err;
}
static status_t
mux_read(
void * cookie,
off_t pos,
void * data,
size_t * nread)
{
return EPERM;
}
static status_t
mux_write(
void * cookie,
off_t pos,
const void * data,
size_t * nwritten)
{
return EPERM;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
/* ++++++++++
$Source: /home/cltien/cvs/BeOS/inc/sound.h,v $
$Revision: 1.1.1.1 $
$Author: cltien $
$Date: 1999/10/12 18:38:08 $
Data structures and control calls for using the sound driver
+++++ */
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _SOUND_H
#define _SOUND_H
#ifndef _DRIVERS_H
#include <Drivers.h>
#endif
enum adc_source {
line=0, aux1, mic, loopback
};
enum sample_rate {
kHz_8_0 = 0, kHz_5_51, kHz_16_0, kHz_11_025, kHz_27_42, kHz_18_9,
kHz_32_0, kHz_22_05, kHz_37_8 = 9, kHz_44_1 = 11, kHz_48_0, kHz_33_075,
kHz_9_6, kHz_6_62
};
enum sample_format {
linear_8bit_unsigned_mono = 0, linear_8bit_unsigned_stereo,
ulaw_8bit_companded_mono, ulaw_8bit_companded_stereo,
linear_16bit_little_endian_mono, linear_16bit_little_endian_stereo,
alaw_8bit_companded_mono, alaw_8bit_companded_stereo,
sample_format_reserved_1, sample_format_reserved_2,
adpcm_4bit_mono, adpcm_4bit_stereo,
linear_16bit_big_endian_mono, linear_16bit_big_endian_stereo,
sample_format_reserved_3, sample_format_reserved_4
};
struct channel {
enum adc_source adc_source; /* adc input source */
char adc_gain; /* 0..15 adc gain, in 1.5 dB steps */
char mic_gain_enable; /* non-zero enables 20 dB MIC input gain */
char aux1_mix_gain; /* 0..31 aux1 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char aux1_mix_mute; /* non-zero mutes aux1 mix */
char aux2_mix_gain; /* 0..31 aux2 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char aux2_mix_mute; /* non-zero mutes aux2 mix */
char line_mix_gain; /* 0..31 line mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char line_mix_mute; /* non-zero mutes line mix */
char dac_attn; /* 0..61 dac attenuation, in -1.5 dB steps */
char dac_mute; /* non-zero mutes dac output */
};
typedef struct sound_setup {
struct channel left; /* left channel setup */
struct channel right; /* right channel setup */
enum sample_rate sample_rate; /* sample rate */
enum sample_format playback_format;/* sample format for playback */
enum sample_format capture_format; /* sample format for capture */
char dither_enable; /* non-zero enables dither on 16 => 8 bit */
char loop_attn; /* 0..64 adc to dac loopback attenuation, in -1.5 dB steps */
char loop_enable; /* non-zero enables loopback */
char output_boost; /* zero (2.0 Vpp) non-zero (2.8 Vpp) output level boost */
char highpass_enable;/* non-zero enables highpass filter in adc */
char mono_gain; /* 0..64 mono speaker gain */
char mono_mute; /* non-zero mutes speaker */
// char spdif_mute; /* non-zero mutes spdif out */
} sound_setup;
/* -----
control opcodes for sound driver
----- */
enum {
SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END,
SOUND_SET_PARAMS, /* 10000 */
SOUND_SET_PLAYBACK_COMPLETION_SEM,
SOUND_SET_CAPTURE_COMPLETION_SEM,
SOUND_GET_PLAYBACK_TIMESTAMP, /* 10003 */
SOUND_GET_CAPTURE_TIMESTAMP,
SOUND_DEBUG_ON,
SOUND_DEBUG_OFF, /* 10006 */
SOUND_UNSAFE_WRITE,
SOUND_UNSAFE_READ,
SOUND_LOCK_FOR_DMA, /* 10009 */
SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE,
SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE,
SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE, /* 10012 */
SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE,
// control ports for SPDIF settings
SOUND_GET_SPDIF_IN_OUT_LOOPBACK,
SOUND_SET_SPDIF_IN_OUT_LOOPBACK,
SOUND_GET_SPDIF_OUT,
SOUND_SET_SPDIF_OUT,
SOUND_GET_SPDIF_MONITOR,
SOUND_SET_SPDIF_MONITOR,
SOUND_GET_SPDIF_OUT_LEVEL,
SOUND_SET_SPDIF_OUT_LEVEL,
SOUND_GET_SPDIF_IN_FORMAT,
SOUND_SET_SPDIF_IN_FORMAT,
SOUND_GET_SPDIF_IN_OUT_COPYRIGHT,
SOUND_SET_SPDIF_IN_OUT_COPYRIGHT,
SOUND_GET_SPDIF_IN_VALIDITY,
SOUND_SET_SPDIF_IN_VALIDITY,
// control ports for analog settings
SOUND_GET_4_CHANNEL_DUPLICATE,
SOUND_SET_4_CHANNEL_DUPLICATE,
// control ports for additional info
SOUND_GET_DEVICE_ID,
SOUND_GET_INTERNAL_CHIP_ID,
SOUND_GET_DRIVER_VERSION
};
#endif