/*
- * sound/pss.c
+ * sound/oss/pss.c
*
* The low level driver for the Personal Sound System (ECHO ESC614).
*
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spinlock.h>
/* If compiled into kernel, it enable or disable pss mixer */
#ifdef CONFIG_PSS_MIXER
-static unsigned char pss_mixer = 1;
+static int pss_mixer = 1;
#else
-static unsigned char pss_mixer;
+static int pss_mixer;
#endif
static pss_confdata pss_data;
static pss_confdata *devc = &pss_data;
-static spinlock_t lock=SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(lock);
static int pss_initialized;
static int nonstandard_microcode;
static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */
static int pss_enable_joystick; /* Parameter for enabling the joystick */
+static coproc_operations pss_coproc_operations;
static void pss_write(pss_confdata *devc, int data)
{
printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data);
}
-int __init probe_pss(struct address_info *hw_config)
+static int __init probe_pss(struct address_info *hw_config)
{
unsigned short id;
int irq, dma;
if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */
return 0;
- if (check_region(devc->base, 0x19 /*16*/)) {
+ if (!request_region(devc->base, 0x10, "PSS mixer, SB emulation")) {
printk(KERN_ERR "PSS: I/O port conflict\n");
return 0;
}
id = inw(REG(PSS_ID));
if ((id >> 8) != 'E') {
printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id);
+ release_region(devc->base, 0x10);
+ return 0;
+ }
+ if (!request_region(devc->base + 0x10, 0x9, "PSS config")) {
+ printk(KERN_ERR "PSS: I/O port conflict\n");
+ release_region(devc->base, 0x10);
return 0;
}
return 1;
}
}
-static void arg_to_volume_mono(unsigned int volume, int *aleft)
+static int set_volume_mono(unsigned __user *p, int *aleft)
{
int left;
+ unsigned volume;
+ if (get_user(volume, p))
+ return -EFAULT;
- left = volume & 0x00ff;
+ left = volume & 0xff;
if (left > 100)
left = 100;
*aleft = left;
+ return 0;
}
-static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright)
+static int set_volume_stereo(unsigned __user *p, int *aleft, int *aright)
{
- arg_to_volume_mono(volume, aleft);
- arg_to_volume_mono(volume >> 8, aright);
+ int left, right;
+ unsigned volume;
+ if (get_user(volume, p))
+ return -EFAULT;
+
+ left = volume & 0xff;
+ if (left > 100)
+ left = 100;
+ right = (volume >> 8) & 0xff;
+ if (right > 100)
+ right = 100;
+ *aleft = left;
+ *aright = right;
+ return 0;
}
static int ret_vol_mono(int left)
return ((right << 8) | left);
}
-static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, caddr_t arg)
+static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, void __user *arg)
{
if (devc->ad_mixer_dev != NO_WSS_MIXER)
return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg);
return -EINVAL;
}
-static int pss_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+static int pss_mixer_ioctl (int dev, unsigned int cmd, void __user *arg)
{
pss_confdata *devc = mixer_devs[dev]->devc;
int cmdf = cmd & 0xff;
return call_ad_mixer(devc, cmd, arg);
else
{
- if (*(int *)arg != 0)
+ int v;
+ if (get_user(v, (int __user *)arg))
+ return -EFAULT;
+ if (v != 0)
return -EINVAL;
return 0;
}
case SOUND_MIXER_VOLUME:
- arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l,
- &devc->mixer.volume_r);
+ if (set_volume_stereo(arg,
+ &devc->mixer.volume_l,
+ &devc->mixer.volume_r))
+ return -EFAULT;
set_master_volume(devc, devc->mixer.volume_l,
devc->mixer.volume_r);
return ret_vol_stereo(devc->mixer.volume_l,
devc->mixer.volume_r);
case SOUND_MIXER_BASS:
- arg_to_volume_mono(*(unsigned int *)arg,
- &devc->mixer.bass);
+ if (set_volume_mono(arg, &devc->mixer.bass))
+ return -EFAULT;
set_bass(devc, devc->mixer.bass);
return ret_vol_mono(devc->mixer.bass);
case SOUND_MIXER_TREBLE:
- arg_to_volume_mono(*(unsigned int *)arg,
- &devc->mixer.treble);
+ if (set_volume_mono(arg, &devc->mixer.treble))
+ return -EFAULT;
set_treble(devc, devc->mixer.treble);
return ret_vol_mono(devc->mixer.treble);
case SOUND_MIXER_SYNTH:
- arg_to_volume_mono(*(unsigned int *)arg,
- &devc->mixer.synth);
+ if (set_volume_mono(arg, &devc->mixer.synth))
+ return -EFAULT;
set_synth_volume(devc, devc->mixer.synth);
return ret_vol_mono(devc->mixer.synth);
}
else
{
+ int val, and_mask = 0, or_mask = 0;
/*
* Return parameters
*/
switch (cmdf)
{
-
case SOUND_MIXER_DEVMASK:
if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
- *(int *)arg = 0; /* no mixer devices */
- return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH);
+ break;
+ and_mask = ~0;
+ or_mask = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH;
+ break;
case SOUND_MIXER_STEREODEVS:
if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
- *(int *)arg = 0; /* no stereo devices */
- return (*(int *)arg |= SOUND_MASK_VOLUME);
+ break;
+ and_mask = ~0;
+ or_mask = SOUND_MASK_VOLUME;
+ break;
case SOUND_MIXER_RECMASK:
if (devc->ad_mixer_dev != NO_WSS_MIXER)
return call_ad_mixer(devc, cmd, arg);
- else
- return (*(int *)arg = 0); /* no record devices */
+ break;
case SOUND_MIXER_CAPS:
if (devc->ad_mixer_dev != NO_WSS_MIXER)
return call_ad_mixer(devc, cmd, arg);
- else
- return (*(int *)arg = SOUND_CAP_EXCL_INPUT);
+ or_mask = SOUND_CAP_EXCL_INPUT;
+ break;
case SOUND_MIXER_RECSRC:
if (devc->ad_mixer_dev != NO_WSS_MIXER)
return call_ad_mixer(devc, cmd, arg);
- else
- return (*(int *)arg = 0); /* no record source */
+ break;
case SOUND_MIXER_VOLUME:
- return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r));
+ or_mask = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r);
+ break;
case SOUND_MIXER_BASS:
- return (*(int *)arg = ret_vol_mono(devc->mixer.bass));
+ or_mask = ret_vol_mono(devc->mixer.bass);
+ break;
case SOUND_MIXER_TREBLE:
- return (*(int *)arg = ret_vol_mono(devc->mixer.treble));
+ or_mask = ret_vol_mono(devc->mixer.treble);
+ break;
case SOUND_MIXER_SYNTH:
- return (*(int *)arg = ret_vol_mono(devc->mixer.synth));
+ or_mask = ret_vol_mono(devc->mixer.synth);
+ break;
default:
return -EINVAL;
}
+ if (get_user(val, (int __user *)arg))
+ return -EFAULT;
+ val &= and_mask;
+ val |= or_mask;
+ if (put_user(val, (int __user *)arg))
+ return -EFAULT;
+ return val;
}
}
.ioctl = pss_mixer_ioctl
};
-void disable_all_emulations(void)
+static void disable_all_emulations(void)
{
outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */
outw(0x0000, REG(CONF_WSS));
outw(0x0000, REG(CONF_CDROM));
}
-void configure_nonsound_components(void)
+static void configure_nonsound_components(void)
{
/* Configure Joystick port */
}
}
-void __init attach_pss(struct address_info *hw_config)
+static int __init attach_pss(struct address_info *hw_config)
{
unsigned short id;
char tmp[100];
devc->ad_mixer_dev = NO_WSS_MIXER;
if (!probe_pss(hw_config))
- return;
-
- request_region(hw_config->io_base, 0x10, "PSS mixer, SB emulation");
- request_region(hw_config->io_base + 0x10, 0x9, "PSS config");
+ return 0;
id = inw(REG(PSS_ID)) & 0x00ff;
disable_all_emulations();
-#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
+#ifdef YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
if (sound_alloc_dma(hw_config->dma, "PSS"))
{
printk("pss.c: Can't allocate DMA channel.\n");
- return;
+ release_region(hw_config->io_base, 0x10);
+ release_region(hw_config->io_base+0x10, 0x9);
+ return 0;
}
if (!set_irq(devc, CONF_PSS, devc->irq))
{
printk("PSS: IRQ allocation error.\n");
- return;
+ release_region(hw_config->io_base, 0x10);
+ release_region(hw_config->io_base+0x10, 0x9);
+ return 0;
}
if (!set_dma(devc, CONF_PSS, devc->dma))
{
printk(KERN_ERR "PSS: DMA allocation error\n");
- return;
+ release_region(hw_config->io_base, 0x10);
+ release_region(hw_config->io_base+0x10, 0x9);
+ return 0;
}
#endif
pss_initialized = 1;
sprintf(tmp, "ECHO-PSS Rev. %d", id);
conf_printf(tmp, hw_config);
+ return 1;
}
-int __init probe_pss_mpu(struct address_info *hw_config)
+static int __init probe_pss_mpu(struct address_info *hw_config)
{
+ struct resource *ports;
int timeout;
if (!pss_initialized)
return 0;
- if (check_region(hw_config->io_base, 2))
- {
+ ports = request_region(hw_config->io_base, 2, "mpu401");
+
+ if (!ports) {
printk(KERN_ERR "PSS: MPU I/O port conflict\n");
return 0;
}
- if (!set_io_base(devc, CONF_MIDI, hw_config->io_base))
- {
- printk(KERN_ERR "PSS: MIDI base could not be set.\n");
- return 0;
+ if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) {
+ printk(KERN_ERR "PSS: MIDI base could not be set.\n");
+ goto fail;
}
- if (!set_irq(devc, CONF_MIDI, hw_config->irq))
- {
- printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n");
- return 0;
+ if (!set_irq(devc, CONF_MIDI, hw_config->irq)) {
+ printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n");
+ goto fail;
}
- if (!pss_synthLen)
- {
+ if (!pss_synthLen) {
printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n");
- return 0;
+ goto fail;
}
- if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
- {
+ if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) {
printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
- return 0;
+ goto fail;
}
/*
break; /* No more input */
}
- return probe_mpu401(hw_config);
+ if (!probe_mpu401(hw_config, ports))
+ goto fail;
+
+ attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */
+ if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
+ midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
+ return 1;
+fail:
+ release_region(hw_config->io_base, 2);
+ return 0;
}
static int pss_coproc_open(void *dev_info, int sub_device)
return 0;
}
-static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
+static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
{
copr_buffer *buf;
copr_msg *mbuf;
&pss_data
};
-static void __init attach_pss_mpu(struct address_info *hw_config)
-{
- attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */
- if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
- midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
-}
-
static int __init probe_pss_mss(struct address_info *hw_config)
{
volatile int timeout;
+ struct resource *ports;
+ int my_mix = -999; /* gcc shut up */
if (!pss_initialized)
return 0;
- if (check_region(hw_config->io_base, 8))
- {
- printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
- return 0;
+ if (!request_region(hw_config->io_base, 4, "WSS config")) {
+ printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
+ return 0;
}
- if (!set_io_base(devc, CONF_WSS, hw_config->io_base))
- {
- printk("PSS: WSS base not settable.\n");
+ ports = request_region(hw_config->io_base + 4, 4, "ad1848");
+ if (!ports) {
+ printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
+ release_region(hw_config->io_base, 4);
return 0;
}
- if (!set_irq(devc, CONF_WSS, hw_config->irq))
- {
+ if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) {
+ printk("PSS: WSS base not settable.\n");
+ goto fail;
+ }
+ if (!set_irq(devc, CONF_WSS, hw_config->irq)) {
printk("PSS: WSS IRQ allocation error.\n");
- return 0;
+ goto fail;
}
- if (!set_dma(devc, CONF_WSS, hw_config->dma))
- {
+ if (!set_dma(devc, CONF_WSS, hw_config->dma)) {
printk(KERN_ERR "PSS: WSS DMA allocation error\n");
- return 0;
+ goto fail;
}
/*
* For some reason the card returns 0xff in the WSS status register
(timeout < 100000); timeout++)
;
- return probe_ms_sound(hw_config);
-}
+ if (!probe_ms_sound(hw_config, ports))
+ goto fail;
-static void __init attach_pss_mss(struct address_info *hw_config)
-{
- int my_mix = -999; /* gcc shut up */
-
devc->ad_mixer_dev = NO_WSS_MIXER;
if (pss_mixer)
{
devc)) < 0)
{
printk(KERN_ERR "Could not install PSS mixer\n");
- return;
+ goto fail;
}
}
pss_mixer_reset(devc);
- attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */
+ attach_ms_sound(hw_config, ports, THIS_MODULE); /* Slot 0 */
if (hw_config->slots[0] != -1)
{
devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev;
}
}
+ return 1;
+fail:
+ release_region(hw_config->io_base + 4, 4);
+ release_region(hw_config->io_base, 4);
+ return 0;
}
static inline void __exit unload_pss(struct address_info *hw_config)
static int mss_dma __initdata = -1;
static int mpu_io __initdata = -1;
static int mpu_irq __initdata = -1;
-static int pss_no_sound __initdata = 0; /* Just configure non-sound components */
+static int pss_no_sound = 0; /* Just configure non-sound components */
static int pss_keep_settings = 1; /* Keep hardware settings at module exit */
static char *pss_firmware = "/etc/sound/pss_synth";
-MODULE_PARM(pss_io, "i");
+module_param(pss_io, int, 0);
MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)");
-MODULE_PARM(mss_io, "i");
+module_param(mss_io, int, 0);
MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)");
-MODULE_PARM(mss_irq, "i");
+module_param(mss_irq, int, 0);
MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)");
-MODULE_PARM(mss_dma, "i");
+module_param(mss_dma, int, 0);
MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)");
-MODULE_PARM(mpu_io, "i");
+module_param(mpu_io, int, 0);
MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)");
-MODULE_PARM(mpu_irq, "i");
+module_param(mpu_irq, int, 0);
MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)");
-MODULE_PARM(pss_cdrom_port, "i");
+module_param(pss_cdrom_port, int, 0);
MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)");
-MODULE_PARM(pss_enable_joystick, "i");
+module_param(pss_enable_joystick, bool, 0);
MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)");
-MODULE_PARM(pss_no_sound, "i");
+module_param(pss_no_sound, bool, 0);
MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)");
-MODULE_PARM(pss_keep_settings, "i");
+module_param(pss_keep_settings, bool, 0);
MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)");
-MODULE_PARM(pss_firmware, "s");
+module_param(pss_firmware, charp, 0);
MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)");
-MODULE_PARM(pss_mixer, "b");
+module_param(pss_mixer, bool, 0);
MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards.");
MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl");
MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).");
printk(KERN_INFO "PSS: loading in no sound mode.\n");
disable_all_emulations();
configure_nonsound_components();
+ release_region(pss_io, 0x10);
+ release_region(pss_io + 0x10, 0x9);
return 0;
}
fw_load = 1;
pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth);
}
- if (!probe_pss(&cfg))
+ if (!attach_pss(&cfg))
return -ENODEV;
- attach_pss(&cfg);
/*
* Attach stuff
*/
- if (probe_pss_mpu(&cfg_mpu)) {
+ if (probe_pss_mpu(&cfg_mpu))
pssmpu = 1;
- attach_pss_mpu(&cfg_mpu);
- }
- if (probe_pss_mss(&cfg2)) {
+
+ if (probe_pss_mss(&cfg2))
pssmss = 1;
- attach_pss_mss(&cfg2);
- }
return 0;
}