/* 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 #if !defined(_KERNEL_EXPORT_H) #include #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; ixopen_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; ixcard, &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; ixcard, &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; }