📄 mixer_oss.c
字号:
/* * OSS emulation layer for the mixer interface * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/string.h>#include <sound/core.h>#include <sound/minors.h>#include <sound/control.h>#include <sound/info.h>#include <sound/mixer_oss.h>#include <linux/soundcard.h>#define OSS_ALSAEMULVER _SIOR ('M', 249, int)MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");MODULE_LICENSE("GPL");MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER);static int snd_mixer_oss_open(struct inode *inode, struct file *file){ int cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode)); snd_card_t *card; snd_mixer_oss_file_t *fmixer; int err; if ((card = snd_cards[cardnum]) == NULL) return -ENODEV; if (card->mixer_oss == NULL) return -ENODEV; err = snd_card_file_add(card, file); if (err < 0) return err; fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL); if (fmixer == NULL) { snd_card_file_remove(card, file); return -ENOMEM; } fmixer->card = card; fmixer->mixer = card->mixer_oss; file->private_data = fmixer; if (!try_module_get(card->module)) { kfree(fmixer); snd_card_file_remove(card, file); return -EFAULT; } return 0;}static int snd_mixer_oss_release(struct inode *inode, struct file *file){ snd_mixer_oss_file_t *fmixer; if (file->private_data) { fmixer = (snd_mixer_oss_file_t *) file->private_data; module_put(fmixer->card->module); snd_card_file_remove(fmixer->card, file); kfree(fmixer); } return 0;}static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer, mixer_info __user *_info){ snd_card_t *card = fmixer->card; snd_mixer_oss_t *mixer = fmixer->mixer; struct mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id)); strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name)); info.modify_counter = card->mixer_oss_change_count; if (copy_to_user(_info, &info, sizeof(info))) return -EFAULT; return 0;}static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer, _old_mixer_info __user *_info){ snd_card_t *card = fmixer->card; snd_mixer_oss_t *mixer = fmixer->mixer; _old_mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id)); strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name)); if (copy_to_user(_info, &info, sizeof(info))) return -EFAULT; return 0;}static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer){ snd_mixer_oss_t *mixer = fmixer->mixer; int result = 0; if (mixer == NULL) return -EIO; if (mixer->get_recsrc && mixer->put_recsrc) result |= SOUND_CAP_EXCL_INPUT; return result;}static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer){ snd_mixer_oss_t *mixer = fmixer->mixer; snd_mixer_oss_slot_t *pslot; int result = 0, chn; if (mixer == NULL) return -EIO; for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->put_volume || pslot->put_recsrc) result |= 1 << chn; } return result;}static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer){ snd_mixer_oss_t *mixer = fmixer->mixer; snd_mixer_oss_slot_t *pslot; int result = 0, chn; if (mixer == NULL) return -EIO; for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->put_volume && pslot->stereo) result |= 1 << chn; } return result;}static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer){ snd_mixer_oss_t *mixer = fmixer->mixer; int result = 0; if (mixer == NULL) return -EIO; if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ result = mixer->mask_recsrc; } else { snd_mixer_oss_slot_t *pslot; int chn; for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->put_recsrc) result |= 1 << chn; } } return result;}static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer){ snd_mixer_oss_t *mixer = fmixer->mixer; int result = 0; if (mixer == NULL) return -EIO; if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ int err; if ((err = mixer->get_recsrc(fmixer, &result)) < 0) return err; result = 1 << result; } else { snd_mixer_oss_slot_t *pslot; int chn; for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->get_recsrc) { int active = 0; pslot->get_recsrc(fmixer, pslot, &active); if (active) result |= 1 << chn; } } } return mixer->oss_recsrc = result;}static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc){ snd_mixer_oss_t *mixer = fmixer->mixer; snd_mixer_oss_slot_t *pslot; int chn, active; int result = 0; if (mixer == NULL) return -EIO; if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */ if (recsrc & ~mixer->oss_recsrc) recsrc &= ~mixer->oss_recsrc; mixer->put_recsrc(fmixer, ffz(~recsrc)); mixer->get_recsrc(fmixer, &result); result = 1 << result; } for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->put_recsrc) { active = (recsrc & (1 << chn)) ? 1 : 0; pslot->put_recsrc(fmixer, pslot, active); } } if (! result) { for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->get_recsrc) { active = 0; pslot->get_recsrc(fmixer, pslot, &active); if (active) result |= 1 << chn; } } } return result;}static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot){ snd_mixer_oss_t *mixer = fmixer->mixer; snd_mixer_oss_slot_t *pslot; int result = 0, left, right; if (mixer == NULL || slot > 30) return -EIO; pslot = &mixer->slots[slot]; left = pslot->volume[0]; right = pslot->volume[1]; if (pslot->get_volume) result = pslot->get_volume(fmixer, pslot, &left, &right); if (!pslot->stereo) right = left; snd_assert(left >= 0 && left <= 100, return -EIO); snd_assert(right >= 0 && right <= 100, return -EIO); if (result >= 0) { pslot->volume[0] = left; pslot->volume[1] = right; result = (left & 0xff) | ((right & 0xff) << 8); } return result;}static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer, int slot, int volume){ snd_mixer_oss_t *mixer = fmixer->mixer; snd_mixer_oss_slot_t *pslot; int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff; if (mixer == NULL || slot > 30) return -EIO; pslot = &mixer->slots[slot]; if (left > 100) left = 100; if (right > 100) right = 100; if (!pslot->stereo) right = left; if (pslot->put_volume) result = pslot->put_volume(fmixer, pslot, left, right); if (result < 0) return result; pslot->volume[0] = left; pslot->volume[1] = right; return (left & 0xff) | ((right & 0xff) << 8);}static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg){ void __user *argp = (void __user *)arg; int __user *p = argp; int tmp; snd_assert(fmixer != NULL, return -ENXIO); if (((cmd >> 8) & 0xff) == 'M') { switch (cmd) { case SOUND_MIXER_INFO: return snd_mixer_oss_info(fmixer, argp); case SOUND_OLD_MIXER_INFO: return snd_mixer_oss_info_obsolete(fmixer, argp); case SOUND_MIXER_WRITE_RECSRC: if (get_user(tmp, p)) return -EFAULT; tmp = snd_mixer_oss_set_recsrc(fmixer, tmp); if (tmp < 0) return tmp; return put_user(tmp, p); case OSS_GETVERSION: return put_user(SNDRV_OSS_VERSION, p); case OSS_ALSAEMULVER: return put_user(1, p); case SOUND_MIXER_READ_DEVMASK: tmp = snd_mixer_oss_devmask(fmixer); if (tmp < 0) return tmp; return put_user(tmp, p); case SOUND_MIXER_READ_STEREODEVS: tmp = snd_mixer_oss_stereodevs(fmixer); if (tmp < 0) return tmp; return put_user(tmp, p); case SOUND_MIXER_READ_RECMASK: tmp = snd_mixer_oss_recmask(fmixer); if (tmp < 0) return tmp; return put_user(tmp, p); case SOUND_MIXER_READ_CAPS: tmp = snd_mixer_oss_caps(fmixer); if (tmp < 0) return tmp; return put_user(tmp, p); case SOUND_MIXER_READ_RECSRC: tmp = snd_mixer_oss_get_recsrc(fmixer); if (tmp < 0) return tmp; return put_user(tmp, p); } } if (cmd & SIOC_IN) { if (get_user(tmp, p)) return -EFAULT; tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp); if (tmp < 0) return tmp; return put_user(tmp, p); } else if (cmd & SIOC_OUT) { tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff); if (tmp < 0) return tmp; return put_user(tmp, p); } return -ENXIO;}static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg);}int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg){ snd_mixer_oss_file_t fmixer; snd_assert(card != NULL, return -ENXIO); if (card->mixer_oss == NULL) return -ENXIO; memset(&fmixer, 0, sizeof(fmixer)); fmixer.card = card; fmixer.mixer = card->mixer_oss; return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);}#ifdef CONFIG_COMPAT/* all compatible */#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl#else#define snd_mixer_oss_ioctl_compat NULL#endif/* * REGISTRATION PART */static struct file_operations snd_mixer_oss_f_ops ={ .owner = THIS_MODULE, .open = snd_mixer_oss_open, .release = snd_mixer_oss_release, .unlocked_ioctl = snd_mixer_oss_ioctl, .compat_ioctl = snd_mixer_oss_ioctl_compat,};static snd_minor_t snd_mixer_oss_reg ={ .comment = "mixer", .f_ops = &snd_mixer_oss_f_ops,};/* * utilities */static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax){ long orange = omax - omin, nrange = nmax - nmin; if (orange == 0) return 0; return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;}/* convert from alsa native to oss values (0-100) */static long snd_mixer_oss_conv1(long val, long min, long max, int *old){ if (val == snd_mixer_oss_conv(*old, 0, 100, min, max)) return *old; return snd_mixer_oss_conv(val, min, max, 0, 100);}/* convert from oss to alsa native values */static long snd_mixer_oss_conv2(long val, long min, long max){ return snd_mixer_oss_conv(val, 0, 100, min, max);}#if 0static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot){ snd_mixer_oss_t *mixer = card->mixer_oss; if (mixer) mixer->mask_recsrc |= 1 << slot;}static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot){ snd_mixer_oss_t *mixer = card->mixer_oss; if (mixer && (mixer->mask_recsrc & (1 << slot))) return 1; return 0;}#endif#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1#define SNDRV_MIXER_OSS_ITEM_GROUTE 2#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -