linux/sound/core/oss/mixer_oss.c
<<
>>
Prefs
   1/*
   2 *  OSS emulation layer for the mixer interface
   3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   4 *
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 *
  20 */
  21
  22#include <sound/driver.h>
  23#include <linux/init.h>
  24#include <linux/slab.h>
  25#include <linux/time.h>
  26#include <linux/string.h>
  27#include <sound/core.h>
  28#include <sound/minors.h>
  29#include <sound/control.h>
  30#include <sound/info.h>
  31#include <sound/mixer_oss.h>
  32#include <linux/soundcard.h>
  33
  34#define OSS_ALSAEMULVER         _SIOR ('M', 249, int)
  35
  36MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  37MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");
  38MODULE_LICENSE("GPL");
  39MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER);
  40
  41static int snd_mixer_oss_open(struct inode *inode, struct file *file)
  42{
  43        struct snd_card *card;
  44        struct snd_mixer_oss_file *fmixer;
  45        int err;
  46
  47        card = snd_lookup_oss_minor_data(iminor(inode),
  48                                         SNDRV_OSS_DEVICE_TYPE_MIXER);
  49        if (card == NULL)
  50                return -ENODEV;
  51        if (card->mixer_oss == NULL)
  52                return -ENODEV;
  53        err = snd_card_file_add(card, file);
  54        if (err < 0)
  55                return err;
  56        fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
  57        if (fmixer == NULL) {
  58                snd_card_file_remove(card, file);
  59                return -ENOMEM;
  60        }
  61        fmixer->card = card;
  62        fmixer->mixer = card->mixer_oss;
  63        file->private_data = fmixer;
  64        if (!try_module_get(card->module)) {
  65                kfree(fmixer);
  66                snd_card_file_remove(card, file);
  67                return -EFAULT;
  68        }
  69        return 0;
  70}
  71
  72static int snd_mixer_oss_release(struct inode *inode, struct file *file)
  73{
  74        struct snd_mixer_oss_file *fmixer;
  75
  76        if (file->private_data) {
  77                fmixer = (struct snd_mixer_oss_file *) file->private_data;
  78                module_put(fmixer->card->module);
  79                snd_card_file_remove(fmixer->card, file);
  80                kfree(fmixer);
  81        }
  82        return 0;
  83}
  84
  85static int snd_mixer_oss_info(struct snd_mixer_oss_file *fmixer,
  86                              mixer_info __user *_info)
  87{
  88        struct snd_card *card = fmixer->card;
  89        struct snd_mixer_oss *mixer = fmixer->mixer;
  90        struct mixer_info info;
  91        
  92        memset(&info, 0, sizeof(info));
  93        strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
  94        strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
  95        info.modify_counter = card->mixer_oss_change_count;
  96        if (copy_to_user(_info, &info, sizeof(info)))
  97                return -EFAULT;
  98        return 0;
  99}
 100
 101static int snd_mixer_oss_info_obsolete(struct snd_mixer_oss_file *fmixer,
 102                                       _old_mixer_info __user *_info)
 103{
 104        struct snd_card *card = fmixer->card;
 105        struct snd_mixer_oss *mixer = fmixer->mixer;
 106        _old_mixer_info info;
 107        
 108        memset(&info, 0, sizeof(info));
 109        strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
 110        strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
 111        if (copy_to_user(_info, &info, sizeof(info)))
 112                return -EFAULT;
 113        return 0;
 114}
 115
 116static int snd_mixer_oss_caps(struct snd_mixer_oss_file *fmixer)
 117{
 118        struct snd_mixer_oss *mixer = fmixer->mixer;
 119        int result = 0;
 120
 121        if (mixer == NULL)
 122                return -EIO;
 123        if (mixer->get_recsrc && mixer->put_recsrc)
 124                result |= SOUND_CAP_EXCL_INPUT;
 125        return result;
 126}
 127
 128static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)
 129{
 130        struct snd_mixer_oss *mixer = fmixer->mixer;
 131        struct snd_mixer_oss_slot *pslot;
 132        int result = 0, chn;
 133
 134        if (mixer == NULL)
 135                return -EIO;
 136        for (chn = 0; chn < 31; chn++) {
 137                pslot = &mixer->slots[chn];
 138                if (pslot->put_volume || pslot->put_recsrc)
 139                        result |= 1 << chn;
 140        }
 141        return result;
 142}
 143
 144static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)
 145{
 146        struct snd_mixer_oss *mixer = fmixer->mixer;
 147        struct snd_mixer_oss_slot *pslot;
 148        int result = 0, chn;
 149
 150        if (mixer == NULL)
 151                return -EIO;
 152        for (chn = 0; chn < 31; chn++) {
 153                pslot = &mixer->slots[chn];
 154                if (pslot->put_volume && pslot->stereo)
 155                        result |= 1 << chn;
 156        }
 157        return result;
 158}
 159
 160static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
 161{
 162        struct snd_mixer_oss *mixer = fmixer->mixer;
 163        int result = 0;
 164
 165        if (mixer == NULL)
 166                return -EIO;
 167        if (mixer->put_recsrc && mixer->get_recsrc) {   /* exclusive */
 168                result = mixer->mask_recsrc;
 169        } else {
 170                struct snd_mixer_oss_slot *pslot;
 171                int chn;
 172                for (chn = 0; chn < 31; chn++) {
 173                        pslot = &mixer->slots[chn];
 174                        if (pslot->put_recsrc)
 175                                result |= 1 << chn;
 176                }
 177        }
 178        return result;
 179}
 180
 181static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
 182{
 183        struct snd_mixer_oss *mixer = fmixer->mixer;
 184        int result = 0;
 185
 186        if (mixer == NULL)
 187                return -EIO;
 188        if (mixer->put_recsrc && mixer->get_recsrc) {   /* exclusive */
 189                int err;
 190                if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
 191                        return err;
 192                result = 1 << result;
 193        } else {
 194                struct snd_mixer_oss_slot *pslot;
 195                int chn;
 196                for (chn = 0; chn < 31; chn++) {
 197                        pslot = &mixer->slots[chn];
 198                        if (pslot->get_recsrc) {
 199                                int active = 0;
 200                                pslot->get_recsrc(fmixer, pslot, &active);
 201                                if (active)
 202                                        result |= 1 << chn;
 203                        }
 204                }
 205        }
 206        return mixer->oss_recsrc = result;
 207}
 208
 209static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc)
 210{
 211        struct snd_mixer_oss *mixer = fmixer->mixer;
 212        struct snd_mixer_oss_slot *pslot;
 213        int chn, active;
 214        int result = 0;
 215
 216        if (mixer == NULL)
 217                return -EIO;
 218        if (mixer->get_recsrc && mixer->put_recsrc) {   /* exclusive input */
 219                if (recsrc & ~mixer->oss_recsrc)
 220                        recsrc &= ~mixer->oss_recsrc;
 221                mixer->put_recsrc(fmixer, ffz(~recsrc));
 222                mixer->get_recsrc(fmixer, &result);
 223                result = 1 << result;
 224        }
 225        for (chn = 0; chn < 31; chn++) {
 226                pslot = &mixer->slots[chn];
 227                if (pslot->put_recsrc) {
 228                        active = (recsrc & (1 << chn)) ? 1 : 0;
 229                        pslot->put_recsrc(fmixer, pslot, active);
 230                }
 231        }
 232        if (! result) {
 233                for (chn = 0; chn < 31; chn++) {
 234                        pslot = &mixer->slots[chn];
 235                        if (pslot->get_recsrc) {
 236                                active = 0;
 237                                pslot->get_recsrc(fmixer, pslot, &active);
 238                                if (active)
 239                                        result |= 1 << chn;
 240                        }
 241                }
 242        }
 243        return result;
 244}
 245
 246static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
 247{
 248        struct snd_mixer_oss *mixer = fmixer->mixer;
 249        struct snd_mixer_oss_slot *pslot;
 250        int result = 0, left, right;
 251
 252        if (mixer == NULL || slot > 30)
 253                return -EIO;
 254        pslot = &mixer->slots[slot];
 255        left = pslot->volume[0];
 256        right = pslot->volume[1];
 257        if (pslot->get_volume)
 258                result = pslot->get_volume(fmixer, pslot, &left, &right);
 259        if (!pslot->stereo)
 260                right = left;
 261        snd_assert(left >= 0 && left <= 100, return -EIO);
 262        snd_assert(right >= 0 && right <= 100, return -EIO);
 263        if (result >= 0) {
 264                pslot->volume[0] = left;
 265                pslot->volume[1] = right;
 266                result = (left & 0xff) | ((right & 0xff) << 8);
 267        }
 268        return result;
 269}
 270
 271static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
 272                                    int slot, int volume)
 273{
 274        struct snd_mixer_oss *mixer = fmixer->mixer;
 275        struct snd_mixer_oss_slot *pslot;
 276        int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff;
 277
 278        if (mixer == NULL || slot > 30)
 279                return -EIO;
 280        pslot = &mixer->slots[slot];
 281        if (left > 100)
 282                left = 100;
 283        if (right > 100)
 284                right = 100;
 285        if (!pslot->stereo)
 286                right = left;
 287        if (pslot->put_volume)
 288                result = pslot->put_volume(fmixer, pslot, left, right);
 289        if (result < 0)
 290                return result;
 291        pslot->volume[0] = left;
 292        pslot->volume[1] = right;
 293        return (left & 0xff) | ((right & 0xff) << 8);
 294}
 295
 296static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg)
 297{
 298        void __user *argp = (void __user *)arg;
 299        int __user *p = argp;
 300        int tmp;
 301
 302        snd_assert(fmixer != NULL, return -ENXIO);
 303        if (((cmd >> 8) & 0xff) == 'M') {
 304                switch (cmd) {
 305                case SOUND_MIXER_INFO:
 306                        return snd_mixer_oss_info(fmixer, argp);
 307                case SOUND_OLD_MIXER_INFO:
 308                        return snd_mixer_oss_info_obsolete(fmixer, argp);
 309                case SOUND_MIXER_WRITE_RECSRC:
 310                        if (get_user(tmp, p))
 311                                return -EFAULT;
 312                        tmp = snd_mixer_oss_set_recsrc(fmixer, tmp);
 313                        if (tmp < 0)
 314                                return tmp;
 315                        return put_user(tmp, p);
 316                case OSS_GETVERSION:
 317                        return put_user(SNDRV_OSS_VERSION, p);
 318                case OSS_ALSAEMULVER:
 319                        return put_user(1, p);
 320                case SOUND_MIXER_READ_DEVMASK:
 321                        tmp = snd_mixer_oss_devmask(fmixer);
 322                        if (tmp < 0)
 323                                return tmp;
 324                        return put_user(tmp, p);
 325                case SOUND_MIXER_READ_STEREODEVS:
 326                        tmp = snd_mixer_oss_stereodevs(fmixer);
 327                        if (tmp < 0)
 328                                return tmp;
 329                        return put_user(tmp, p);
 330                case SOUND_MIXER_READ_RECMASK:
 331                        tmp = snd_mixer_oss_recmask(fmixer);
 332                        if (tmp < 0)
 333                                return tmp;
 334                        return put_user(tmp, p);
 335                case SOUND_MIXER_READ_CAPS:
 336                        tmp = snd_mixer_oss_caps(fmixer);
 337                        if (tmp < 0)
 338                                return tmp;
 339                        return put_user(tmp, p);
 340                case SOUND_MIXER_READ_RECSRC:
 341                        tmp = snd_mixer_oss_get_recsrc(fmixer);
 342                        if (tmp < 0)
 343                                return tmp;
 344                        return put_user(tmp, p);
 345                }
 346        }
 347        if (cmd & SIOC_IN) {
 348                if (get_user(tmp, p))
 349                        return -EFAULT;
 350                tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp);
 351                if (tmp < 0)
 352                        return tmp;
 353                return put_user(tmp, p);
 354        } else if (cmd & SIOC_OUT) {
 355                tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff);
 356                if (tmp < 0)
 357                        return tmp;
 358                return put_user(tmp, p);
 359        }
 360        return -ENXIO;
 361}
 362
 363static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 364{
 365        return snd_mixer_oss_ioctl1((struct snd_mixer_oss_file *) file->private_data, cmd, arg);
 366}
 367
 368int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg)
 369{
 370        struct snd_mixer_oss_file fmixer;
 371        
 372        snd_assert(card != NULL, return -ENXIO);
 373        if (card->mixer_oss == NULL)
 374                return -ENXIO;
 375        memset(&fmixer, 0, sizeof(fmixer));
 376        fmixer.card = card;
 377        fmixer.mixer = card->mixer_oss;
 378        return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);
 379}
 380
 381#ifdef CONFIG_COMPAT
 382/* all compatible */
 383#define snd_mixer_oss_ioctl_compat      snd_mixer_oss_ioctl
 384#else
 385#define snd_mixer_oss_ioctl_compat      NULL
 386#endif
 387
 388/*
 389 *  REGISTRATION PART
 390 */
 391
 392static const struct file_operations snd_mixer_oss_f_ops =
 393{
 394        .owner =        THIS_MODULE,
 395        .open =         snd_mixer_oss_open,
 396        .release =      snd_mixer_oss_release,
 397        .unlocked_ioctl =       snd_mixer_oss_ioctl,
 398        .compat_ioctl = snd_mixer_oss_ioctl_compat,
 399};
 400
 401/*
 402 *  utilities
 403 */
 404
 405static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax)
 406{
 407        long orange = omax - omin, nrange = nmax - nmin;
 408        
 409        if (orange == 0)
 410                return 0;
 411        return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
 412}
 413
 414/* convert from alsa native to oss values (0-100) */
 415static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
 416{
 417        if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))
 418                return *old;
 419        return snd_mixer_oss_conv(val, min, max, 0, 100);
 420}
 421
 422/* convert from oss to alsa native values */
 423static long snd_mixer_oss_conv2(long val, long min, long max)
 424{
 425        return snd_mixer_oss_conv(val, 0, 100, min, max);
 426}
 427
 428#if 0
 429static void snd_mixer_oss_recsrce_set(struct snd_card *card, int slot)
 430{
 431        struct snd_mixer_oss *mixer = card->mixer_oss;
 432        if (mixer)
 433                mixer->mask_recsrc |= 1 << slot;
 434}
 435
 436static int snd_mixer_oss_recsrce_get(struct snd_card *card, int slot)
 437{
 438        struct snd_mixer_oss *mixer = card->mixer_oss;
 439        if (mixer && (mixer->mask_recsrc & (1 << slot)))
 440                return 1;
 441        return 0;
 442}
 443#endif
 444
 445#define SNDRV_MIXER_OSS_SIGNATURE               0x65999250
 446
 447#define SNDRV_MIXER_OSS_ITEM_GLOBAL     0
 448#define SNDRV_MIXER_OSS_ITEM_GSWITCH    1
 449#define SNDRV_MIXER_OSS_ITEM_GROUTE     2
 450#define SNDRV_MIXER_OSS_ITEM_GVOLUME    3
 451#define SNDRV_MIXER_OSS_ITEM_PSWITCH    4
 452#define SNDRV_MIXER_OSS_ITEM_PROUTE     5
 453#define SNDRV_MIXER_OSS_ITEM_PVOLUME    6
 454#define SNDRV_MIXER_OSS_ITEM_CSWITCH    7
 455#define SNDRV_MIXER_OSS_ITEM_CROUTE     8
 456#define SNDRV_MIXER_OSS_ITEM_CVOLUME    9
 457#define SNDRV_MIXER_OSS_ITEM_CAPTURE    10
 458
 459#define SNDRV_MIXER_OSS_ITEM_COUNT      11
 460
 461#define SNDRV_MIXER_OSS_PRESENT_GLOBAL  (1<<0)
 462#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1)
 463#define SNDRV_MIXER_OSS_PRESENT_GROUTE  (1<<2)
 464#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3)
 465#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4)
 466#define SNDRV_MIXER_OSS_PRESENT_PROUTE  (1<<5)
 467#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6)
 468#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7)
 469#define SNDRV_MIXER_OSS_PRESENT_CROUTE  (1<<8)
 470#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9)
 471#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10)
 472
 473struct slot {
 474        unsigned int signature;
 475        unsigned int present;
 476        unsigned int channels;
 477        unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT];
 478        unsigned int capture_item;
 479        struct snd_mixer_oss_assign_table *assigned;
 480        unsigned int allocated: 1;
 481};
 482
 483#define ID_UNKNOWN      ((unsigned int)-1)
 484
 485static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, const char *name, int index)
 486{
 487        struct snd_card *card = mixer->card;
 488        struct snd_ctl_elem_id id;
 489        
 490        memset(&id, 0, sizeof(id));
 491        id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 492        strcpy(id.name, name);
 493        id.index = index;
 494        return snd_ctl_find_id(card, &id);
 495}
 496
 497static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
 498                                          struct snd_mixer_oss_slot *pslot,
 499                                          unsigned int numid,
 500                                          int *left, int *right)
 501{
 502        struct snd_ctl_elem_info *uinfo;
 503        struct snd_ctl_elem_value *uctl;
 504        struct snd_kcontrol *kctl;
 505        struct snd_card *card = fmixer->card;
 506
 507        if (numid == ID_UNKNOWN)
 508                return;
 509        down_read(&card->controls_rwsem);
 510        if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
 511                up_read(&card->controls_rwsem);
 512                return;
 513        }
 514        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
 515        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
 516        if (uinfo == NULL || uctl == NULL)
 517                goto __unalloc;
 518        if (kctl->info(kctl, uinfo))
 519                goto __unalloc;
 520        if (kctl->get(kctl, uctl))
 521                goto __unalloc;
 522        if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
 523            uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
 524                goto __unalloc;
 525        *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
 526        if (uinfo->count > 1)
 527                *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
 528      __unalloc:
 529        up_read(&card->controls_rwsem);
 530        kfree(uctl);
 531        kfree(uinfo);
 532}
 533
 534static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
 535                                         struct snd_mixer_oss_slot *pslot,
 536                                         unsigned int numid,
 537                                         int *left, int *right,
 538                                         int route)
 539{
 540        struct snd_ctl_elem_info *uinfo;
 541        struct snd_ctl_elem_value *uctl;
 542        struct snd_kcontrol *kctl;
 543        struct snd_card *card = fmixer->card;
 544
 545        if (numid == ID_UNKNOWN)
 546                return;
 547        down_read(&card->controls_rwsem);
 548        if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
 549                up_read(&card->controls_rwsem);
 550                return;
 551        }
 552        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
 553        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
 554        if (uinfo == NULL || uctl == NULL)
 555                goto __unalloc;
 556        if (kctl->info(kctl, uinfo))
 557                goto __unalloc;
 558        if (kctl->get(kctl, uctl))
 559                goto __unalloc;
 560        if (!uctl->value.integer.value[0]) {
 561                *left = 0;
 562                if (uinfo->count == 1)
 563                        *right = 0;
 564        }
 565        if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
 566                *right = 0;
 567      __unalloc:
 568        up_read(&card->controls_rwsem);
 569        kfree(uctl);
 570        kfree(uinfo);
 571}
 572
 573static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
 574                                     struct snd_mixer_oss_slot *pslot,
 575                                     int *left, int *right)
 576{
 577        struct slot *slot = (struct slot *)pslot->private_data;
 578        
 579        *left = *right = 100;
 580        if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
 581                snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
 582        } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
 583                snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
 584        } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
 585                snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
 586        }
 587        if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
 588                snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
 589        } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
 590                snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
 591        } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
 592                snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
 593        } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
 594                snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
 595        }
 596        return 0;
 597}
 598
 599static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
 600                                          struct snd_mixer_oss_slot *pslot,
 601                                          unsigned int numid,
 602                                          int left, int right)
 603{
 604        struct snd_ctl_elem_info *uinfo;
 605        struct snd_ctl_elem_value *uctl;
 606        struct snd_kcontrol *kctl;
 607        struct snd_card *card = fmixer->card;
 608        int res;
 609
 610        if (numid == ID_UNKNOWN)
 611                return;
 612        down_read(&card->controls_rwsem);
 613        if ((kctl = snd_ctl_find_numid(card, numid)) == NULL)
 614                return;
 615        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
 616        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
 617        if (uinfo == NULL || uctl == NULL)
 618                goto __unalloc;
 619        if (kctl->info(kctl, uinfo))
 620                goto __unalloc;
 621        if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
 622            uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
 623                goto __unalloc;
 624        uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
 625        if (uinfo->count > 1)
 626                uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
 627        if ((res = kctl->put(kctl, uctl)) < 0)
 628                goto __unalloc;
 629        if (res > 0)
 630                snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
 631      __unalloc:
 632        up_read(&card->controls_rwsem);
 633        kfree(uctl);
 634        kfree(uinfo);
 635}
 636
 637static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
 638                                         struct snd_mixer_oss_slot *pslot,
 639                                         unsigned int numid,
 640                                         int left, int right,
 641                                         int route)
 642{
 643        struct snd_ctl_elem_info *uinfo;
 644        struct snd_ctl_elem_value *uctl;
 645        struct snd_kcontrol *kctl;
 646        struct snd_card *card = fmixer->card;
 647        int res;
 648
 649        if (numid == ID_UNKNOWN)
 650                return;
 651        down_read(&card->controls_rwsem);
 652        if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
 653                up_read(&fmixer->card->controls_rwsem);
 654                return;
 655        }
 656        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
 657        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
 658        if (uinfo == NULL || uctl == NULL)
 659                goto __unalloc;
 660        if (kctl->info(kctl, uinfo))
 661                goto __unalloc;
 662        if (uinfo->count > 1) {
 663                uctl->value.integer.value[0] = left > 0 ? 1 : 0;
 664                uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
 665                if (route) {
 666                        uctl->value.integer.value[1] =
 667                        uctl->value.integer.value[2] = 0;
 668                }
 669        } else {
 670                uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
 671        }
 672        if ((res = kctl->put(kctl, uctl)) < 0)
 673                goto __unalloc;
 674        if (res > 0)
 675                snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
 676      __unalloc:
 677        up_read(&card->controls_rwsem);
 678        kfree(uctl);
 679        kfree(uinfo);
 680}
 681
 682static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
 683                                     struct snd_mixer_oss_slot *pslot,
 684                                     int left, int right)
 685{
 686        struct slot *slot = (struct slot *)pslot->private_data;
 687        
 688        if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
 689                snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
 690                if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)
 691                        snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
 692        } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
 693                snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
 694        } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
 695                snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
 696        }
 697        if (left || right) {
 698                if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
 699                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
 700                if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
 701                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
 702                if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
 703                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
 704                if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
 705                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
 706        } else {
 707                if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
 708                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
 709                } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
 710                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
 711                } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
 712                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
 713                } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
 714                        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
 715                }
 716        }
 717        return 0;
 718}
 719
 720static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
 721                                        struct snd_mixer_oss_slot *pslot,
 722                                        int *active)
 723{
 724        struct slot *slot = (struct slot *)pslot->private_data;
 725        int left, right;
 726        
 727        left = right = 1;
 728        snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0);
 729        *active = (left || right) ? 1 : 0;
 730        return 0;
 731}
 732
 733static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer,
 734                                           struct snd_mixer_oss_slot *pslot,
 735                                           int *active)
 736{
 737        struct slot *slot = (struct slot *)pslot->private_data;
 738        int left, right;
 739        
 740        left = right = 1;
 741        snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1);
 742        *active = (left || right) ? 1 : 0;
 743        return 0;
 744}
 745
 746static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
 747                                        struct snd_mixer_oss_slot *pslot,
 748                                        int active)
 749{
 750        struct slot *slot = (struct slot *)pslot->private_data;
 751        
 752        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
 753        return 0;
 754}
 755
 756static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer,
 757                                           struct snd_mixer_oss_slot *pslot,
 758                                           int active)
 759{
 760        struct slot *slot = (struct slot *)pslot->private_data;
 761        
 762        snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
 763        return 0;
 764}
 765
 766static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int *active_index)
 767{
 768        struct snd_card *card = fmixer->card;
 769        struct snd_mixer_oss *mixer = fmixer->mixer;
 770        struct snd_kcontrol *kctl;
 771        struct snd_mixer_oss_slot *pslot;
 772        struct slot *slot;
 773        struct snd_ctl_elem_info *uinfo;
 774        struct snd_ctl_elem_value *uctl;
 775        int err, idx;
 776        
 777        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
 778        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
 779        if (uinfo == NULL || uctl == NULL) {
 780                err = -ENOMEM;
 781                goto __unlock;
 782        }
 783        down_read(&card->controls_rwsem);
 784        kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
 785        if (! kctl) {
 786                err = -ENOENT;
 787                goto __unlock;
 788        }
 789        if ((err = kctl->info(kctl, uinfo)) < 0)
 790                goto __unlock;
 791        if ((err = kctl->get(kctl, uctl)) < 0)
 792                goto __unlock;
 793        for (idx = 0; idx < 32; idx++) {
 794                if (!(mixer->mask_recsrc & (1 << idx)))
 795                        continue;
 796                pslot = &mixer->slots[idx];
 797                slot = (struct slot *)pslot->private_data;
 798                if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
 799                        continue;
 800                if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
 801                        continue;
 802                if (slot->capture_item == uctl->value.enumerated.item[0]) {
 803                        *active_index = idx;
 804                        break;
 805                }
 806        }
 807        err = 0;
 808      __unlock:
 809        up_read(&card->controls_rwsem);
 810        kfree(uctl);
 811        kfree(uinfo);
 812        return err;
 813}
 814
 815static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index)
 816{
 817        struct snd_card *card = fmixer->card;
 818        struct snd_mixer_oss *mixer = fmixer->mixer;
 819        struct snd_kcontrol *kctl;
 820        struct snd_mixer_oss_slot *pslot;
 821        struct slot *slot = NULL;
 822        struct snd_ctl_elem_info *uinfo;
 823        struct snd_ctl_elem_value *uctl;
 824        int err;
 825        unsigned int idx;
 826
 827        uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
 828        uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
 829        if (uinfo == NULL || uctl == NULL) {
 830                err = -ENOMEM;
 831                goto __unlock;
 832        }
 833        down_read(&card->controls_rwsem);
 834        kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
 835        if (! kctl) {
 836                err = -ENOENT;
 837                goto __unlock;
 838        }
 839        if ((err = kctl->info(kctl, uinfo)) < 0)
 840                goto __unlock;
 841        for (idx = 0; idx < 32; idx++) {
 842                if (!(mixer->mask_recsrc & (1 << idx)))
 843                        continue;
 844                pslot = &mixer->slots[idx];
 845                slot = (struct slot *)pslot->private_data;
 846                if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
 847                        continue;
 848                if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
 849                        continue;
 850                if (idx == active_index)
 851                        break;
 852                slot = NULL;
 853        }
 854        if (! slot)
 855                goto __unlock;
 856        for (idx = 0; idx < uinfo->count; idx++)
 857                uctl->value.enumerated.item[idx] = slot->capture_item;
 858        err = kctl->put(kctl, uctl);
 859        if (err > 0)
 860                snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
 861        err = 0;
 862      __unlock:
 863        up_read(&card->controls_rwsem);
 864        kfree(uctl);
 865        kfree(uinfo);
 866        return err;
 867}
 868
 869struct snd_mixer_oss_assign_table {
 870        int oss_id;
 871        const char *name;
 872        int index;
 873};
 874
 875static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item)
 876{
 877        struct snd_ctl_elem_info *info;
 878        struct snd_kcontrol *kcontrol;
 879        struct snd_card *card = mixer->card;
 880        int err;
 881
 882        down_read(&card->controls_rwsem);
 883        kcontrol = snd_mixer_oss_test_id(mixer, name, index);
 884        if (kcontrol == NULL) {
 885                up_read(&card->controls_rwsem);
 886                return 0;
 887        }
 888        info = kmalloc(sizeof(*info), GFP_KERNEL);
 889        if (! info) {
 890                up_read(&card->controls_rwsem);
 891                return -ENOMEM;
 892        }
 893        if ((err = kcontrol->info(kcontrol, info)) < 0) {
 894                up_read(&card->controls_rwsem);
 895                kfree(info);
 896                return err;
 897        }
 898        slot->numid[item] = kcontrol->id.numid;
 899        up_read(&card->controls_rwsem);
 900        if (info->count > slot->channels)
 901                slot->channels = info->count;
 902        slot->present |= 1 << item;
 903        kfree(info);
 904        return 0;
 905}
 906
 907static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)
 908{
 909        struct slot *p = (struct slot *)chn->private_data;
 910        if (p) {
 911                if (p->allocated && p->assigned) {
 912                        kfree(p->assigned->name);
 913                        kfree(p->assigned);
 914                }
 915                kfree(p);
 916        }
 917}
 918
 919static void mixer_slot_clear(struct snd_mixer_oss_slot *rslot)
 920{
 921        int idx = rslot->number; /* remember this */
 922        if (rslot->private_free)
 923                rslot->private_free(rslot);
 924        memset(rslot, 0, sizeof(*rslot));
 925        rslot->number = idx;
 926}
 927
 928/* In a separate function to keep gcc 3.2 happy - do NOT merge this in
 929   snd_mixer_oss_build_input! */
 930static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,
 931                                        struct snd_mixer_oss_assign_table *ptr,
 932                                        struct slot *slot)
 933{
 934        char str[64];
 935        int err;
 936
 937        err = snd_mixer_oss_build_test(mixer, slot, ptr->name, ptr->index,
 938                                       SNDRV_MIXER_OSS_ITEM_GLOBAL);
 939        if (err)
 940                return err;
 941        sprintf(str, "%s Switch", ptr->name);
 942        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 943                                       SNDRV_MIXER_OSS_ITEM_GSWITCH);
 944        if (err)
 945                return err;
 946        sprintf(str, "%s Route", ptr->name);
 947        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 948                                       SNDRV_MIXER_OSS_ITEM_GROUTE);
 949        if (err)
 950                return err;
 951        sprintf(str, "%s Volume", ptr->name);
 952        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 953                                       SNDRV_MIXER_OSS_ITEM_GVOLUME);
 954        if (err)
 955                return err;
 956        sprintf(str, "%s Playback Switch", ptr->name);
 957        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 958                                       SNDRV_MIXER_OSS_ITEM_PSWITCH);
 959        if (err)
 960                return err;
 961        sprintf(str, "%s Playback Route", ptr->name);
 962        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 963                                       SNDRV_MIXER_OSS_ITEM_PROUTE);
 964        if (err)
 965                return err;
 966        sprintf(str, "%s Playback Volume", ptr->name);
 967        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 968                                       SNDRV_MIXER_OSS_ITEM_PVOLUME);
 969        if (err)
 970                return err;
 971        sprintf(str, "%s Capture Switch", ptr->name);
 972        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 973                                       SNDRV_MIXER_OSS_ITEM_CSWITCH);
 974        if (err)
 975                return err;
 976        sprintf(str, "%s Capture Route", ptr->name);
 977        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 978                                       SNDRV_MIXER_OSS_ITEM_CROUTE);
 979        if (err)
 980                return err;
 981        sprintf(str, "%s Capture Volume", ptr->name);
 982        err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 983                                       SNDRV_MIXER_OSS_ITEM_CVOLUME);
 984        if (err)
 985                return err;
 986
 987        return 0;
 988}
 989
 990/*
 991 * build an OSS mixer element.
 992 * ptr_allocated means the entry is dynamically allocated (change via proc file).
 993 * when replace_old = 1, the old entry is replaced with the new one.
 994 */
 995static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated, int replace_old)
 996{
 997        struct slot slot;
 998        struct slot *pslot;
 999        struct snd_kcontrol *kctl;
1000        struct snd_mixer_oss_slot *rslot;
1001        char str[64];   
1002        
1003        /* check if already assigned */
1004        if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)
1005                return 0;
1006
1007        memset(&slot, 0, sizeof(slot));
1008        memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
1009        if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
1010                return 0;
1011        down_read(&mixer->card->controls_rwsem);
1012        if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
1013                struct snd_ctl_elem_info *uinfo;
1014
1015                uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
1016                if (! uinfo) {
1017                        up_read(&mixer->card->controls_rwsem);
1018                        return -ENOMEM;
1019                }
1020                        
1021                if (kctl->info(kctl, uinfo)) {
1022                        up_read(&mixer->card->controls_rwsem);
1023                        return 0;
1024                }
1025                strcpy(str, ptr->name);
1026                if (!strcmp(str, "Master"))
1027                        strcpy(str, "Mix");
1028                if (!strcmp(str, "Master Mono"))
1029                        strcpy(str, "Mix Mono");
1030                slot.capture_item = 0;
1031                if (!strcmp(uinfo->value.enumerated.name, str)) {
1032                        slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
1033                } else {
1034                        for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
1035                                uinfo->value.enumerated.item = slot.capture_item;
1036                                if (kctl->info(kctl, uinfo)) {
1037                                        up_read(&mixer->card->controls_rwsem);
1038                                        return 0;
1039                                }
1040                                if (!strcmp(uinfo->value.enumerated.name, str)) {
1041                                        slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
1042                                        break;
1043                                }
1044                        }
1045                }
1046                kfree(uinfo);
1047        }
1048        up_read(&mixer->card->controls_rwsem);
1049        if (slot.present != 0) {
1050                pslot = kmalloc(sizeof(slot), GFP_KERNEL);
1051                if (! pslot)
1052                        return -ENOMEM;
1053                *pslot = slot;
1054                pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
1055                pslot->assigned = ptr;
1056                pslot->allocated = ptr_allocated;
1057                rslot = &mixer->slots[ptr->oss_id];
1058                mixer_slot_clear(rslot);
1059                rslot->stereo = slot.channels > 1 ? 1 : 0;
1060                rslot->get_volume = snd_mixer_oss_get_volume1;
1061                rslot->put_volume = snd_mixer_oss_put_volume1;
1062                /* note: ES18xx have both Capture Source and XX Capture Volume !!! */
1063                if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
1064                        rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw;
1065                        rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw;
1066                } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
1067                        rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route;
1068                        rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route;
1069                } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) {
1070                        mixer->mask_recsrc |= 1 << ptr->oss_id;
1071                }
1072                rslot->private_data = pslot;
1073                rslot->private_free = snd_mixer_oss_slot_free;
1074                return 1;
1075        }
1076        return 0;
1077}
1078
1079#ifdef CONFIG_PROC_FS
1080/*
1081 */
1082#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
1083static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
1084        MIXER_VOL(VOLUME),
1085        MIXER_VOL(BASS),
1086        MIXER_VOL(TREBLE),
1087        MIXER_VOL(SYNTH),
1088        MIXER_VOL(PCM),
1089        MIXER_VOL(SPEAKER),
1090        MIXER_VOL(LINE),
1091        MIXER_VOL(MIC),
1092        MIXER_VOL(CD),
1093        MIXER_VOL(IMIX),
1094        MIXER_VOL(ALTPCM),
1095        MIXER_VOL(RECLEV),
1096        MIXER_VOL(IGAIN),
1097        MIXER_VOL(OGAIN),
1098        MIXER_VOL(LINE1),
1099        MIXER_VOL(LINE2),
1100        MIXER_VOL(LINE3),
1101        MIXER_VOL(DIGITAL1),
1102        MIXER_VOL(DIGITAL2),
1103        MIXER_VOL(DIGITAL3),
1104        MIXER_VOL(PHONEIN),
1105        MIXER_VOL(PHONEOUT),
1106        MIXER_VOL(VIDEO),
1107        MIXER_VOL(RADIO),
1108        MIXER_VOL(MONITOR),
1109};
1110        
1111/*
1112 *  /proc interface
1113 */
1114
1115static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
1116                                    struct snd_info_buffer *buffer)
1117{
1118        struct snd_mixer_oss *mixer = entry->private_data;
1119        int i;
1120
1121        mutex_lock(&mixer->reg_mutex);
1122        for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
1123                struct slot *p;
1124
1125                if (! oss_mixer_names[i])
1126                        continue;
1127                p = (struct slot *)mixer->slots[i].private_data;
1128                snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
1129                if (p && p->assigned)
1130                        snd_iprintf(buffer, "\"%s\" %d\n",
1131                                    p->assigned->name,
1132                                    p->assigned->index);
1133                else
1134                        snd_iprintf(buffer, "\"\" 0\n");
1135        }
1136        mutex_unlock(&mixer->reg_mutex);
1137}
1138
1139static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
1140                                     struct snd_info_buffer *buffer)
1141{
1142        struct snd_mixer_oss *mixer = entry->private_data;
1143        char line[128], str[32], idxstr[16], *cptr;
1144        int ch, idx;
1145        struct snd_mixer_oss_assign_table *tbl;
1146        struct slot *slot;
1147
1148        while (!snd_info_get_line(buffer, line, sizeof(line))) {
1149                cptr = snd_info_get_str(str, line, sizeof(str));
1150                for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
1151                        if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
1152                                break;
1153                if (ch >= SNDRV_OSS_MAX_MIXERS) {
1154                        snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
1155                        continue;
1156                }
1157                cptr = snd_info_get_str(str, cptr, sizeof(str));
1158                if (! *str) {
1159                        /* remove the entry */
1160                        mutex_lock(&mixer->reg_mutex);
1161                        mixer_slot_clear(&mixer->slots[ch]);
1162                        mutex_unlock(&mixer->reg_mutex);
1163                        continue;
1164                }
1165                snd_info_get_str(idxstr, cptr, sizeof(idxstr));
1166                idx = simple_strtoul(idxstr, NULL, 10);
1167                if (idx >= 0x4000) { /* too big */
1168                        snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
1169                        continue;
1170                }
1171                mutex_lock(&mixer->reg_mutex);
1172                slot = (struct slot *)mixer->slots[ch].private_data;
1173                if (slot && slot->assigned &&
1174                    slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
1175                        /* not changed */
1176                        goto __unlock;
1177                tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
1178                if (! tbl) {
1179                        snd_printk(KERN_ERR "mixer_oss: no memory\n");
1180                        goto __unlock;
1181                }
1182                tbl->oss_id = ch;
1183                tbl->name = kstrdup(str, GFP_KERNEL);
1184                if (! tbl->name) {
1185                        kfree(tbl);
1186                        goto __unlock;
1187                }
1188                tbl->index = idx;
1189                if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
1190                        kfree(tbl->name);
1191                        kfree(tbl);
1192                }
1193        __unlock:
1194                mutex_unlock(&mixer->reg_mutex);
1195        }
1196}
1197
1198static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
1199{
1200        struct snd_info_entry *entry;
1201
1202        entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
1203                                           mixer->card->proc_root);
1204        if (! entry)
1205                return;
1206        entry->content = SNDRV_INFO_CONTENT_TEXT;
1207        entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
1208        entry->c.text.read = snd_mixer_oss_proc_read;
1209        entry->c.text.write = snd_mixer_oss_proc_write;
1210        entry->private_data = mixer;
1211        if (snd_info_register(entry) < 0) {
1212                snd_info_free_entry(entry);
1213                entry = NULL;
1214        }
1215        mixer->proc_entry = entry;
1216}
1217
1218static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
1219{
1220        snd_info_free_entry(mixer->proc_entry);
1221        mixer->proc_entry = NULL;
1222}
1223#else /* !CONFIG_PROC_FS */
1224#define snd_mixer_oss_proc_init(mix)
1225#define snd_mixer_oss_proc_done(mix)
1226#endif /* CONFIG_PROC_FS */
1227
1228static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
1229{
1230        static struct snd_mixer_oss_assign_table table[] = {
1231                { SOUND_MIXER_VOLUME,   "Master",               0 },
1232                { SOUND_MIXER_VOLUME,   "Front",                0 }, /* fallback */
1233                { SOUND_MIXER_BASS,     "Tone Control - Bass",  0 },
1234                { SOUND_MIXER_TREBLE,   "Tone Control - Treble", 0 },
1235                { SOUND_MIXER_SYNTH,    "Synth",                0 },
1236                { SOUND_MIXER_SYNTH,    "FM",                   0 }, /* fallback */
1237                { SOUND_MIXER_SYNTH,    "Music",                0 }, /* fallback */
1238                { SOUND_MIXER_PCM,      "PCM",                  0 },
1239                { SOUND_MIXER_SPEAKER,  "PC Speaker",           0 },
1240                { SOUND_MIXER_LINE,     "Line",                 0 },
1241                { SOUND_MIXER_MIC,      "Mic",                  0 },
1242                { SOUND_MIXER_CD,       "CD",                   0 },
1243                { SOUND_MIXER_IMIX,     "Monitor Mix",          0 },
1244                { SOUND_MIXER_ALTPCM,   "PCM",                  1 },
1245                { SOUND_MIXER_ALTPCM,   "Headphone",            0 }, /* fallback */
1246                { SOUND_MIXER_ALTPCM,   "Wave",                 0 }, /* fallback */
1247                { SOUND_MIXER_RECLEV,   "-- nothing --",        0 },
1248                { SOUND_MIXER_IGAIN,    "Capture",              0 },
1249                { SOUND_MIXER_OGAIN,    "Playback",             0 },
1250                { SOUND_MIXER_LINE1,    "Aux",                  0 },
1251                { SOUND_MIXER_LINE2,    "Aux",                  1 },
1252                { SOUND_MIXER_LINE3,    "Aux",                  2 },
1253                { SOUND_MIXER_DIGITAL1, "Digital",              0 },
1254                { SOUND_MIXER_DIGITAL1, "IEC958",               0 }, /* fallback */
1255                { SOUND_MIXER_DIGITAL1, "IEC958 Optical",       0 }, /* fallback */
1256                { SOUND_MIXER_DIGITAL1, "IEC958 Coaxial",       0 }, /* fallback */
1257                { SOUND_MIXER_DIGITAL2, "Digital",              1 },
1258                { SOUND_MIXER_DIGITAL3, "Digital",              2 },
1259                { SOUND_MIXER_PHONEIN,  "Phone",                0 },
1260                { SOUND_MIXER_PHONEOUT, "Master Mono",          0 },
1261                { SOUND_MIXER_PHONEOUT, "Phone",                0 }, /* fallback */
1262                { SOUND_MIXER_VIDEO,    "Video",                0 },
1263                { SOUND_MIXER_RADIO,    "Radio",                0 },
1264                { SOUND_MIXER_MONITOR,  "Monitor",              0 }
1265        };
1266        unsigned int idx;
1267        
1268        for (idx = 0; idx < ARRAY_SIZE(table); idx++)
1269                snd_mixer_oss_build_input(mixer, &table[idx], 0, 0);
1270        if (mixer->mask_recsrc) {
1271                mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
1272                mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
1273        }
1274}
1275
1276/*
1277 *
1278 */
1279
1280static int snd_mixer_oss_free1(void *private)
1281{
1282        struct snd_mixer_oss *mixer = private;
1283        struct snd_card *card;
1284        int idx;
1285 
1286        snd_assert(mixer != NULL, return -ENXIO);
1287        card = mixer->card;
1288        snd_assert(mixer == card->mixer_oss, return -ENXIO);
1289        card->mixer_oss = NULL;
1290        for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
1291                struct snd_mixer_oss_slot *chn = &mixer->slots[idx];
1292                if (chn->private_free)
1293                        chn->private_free(chn);
1294        }
1295        kfree(mixer);
1296        return 0;
1297}
1298
1299static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
1300{
1301        struct snd_mixer_oss *mixer;
1302
1303        if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
1304                char name[128];
1305                int idx, err;
1306
1307                mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);
1308                if (mixer == NULL)
1309                        return -ENOMEM;
1310                mutex_init(&mixer->reg_mutex);
1311                sprintf(name, "mixer%i%i", card->number, 0);
1312                if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
1313                                                   card, 0,
1314                                                   &snd_mixer_oss_f_ops, card,
1315                                                   name)) < 0) {
1316                        snd_printk(KERN_ERR "unable to register OSS mixer device %i:%i\n",
1317                                   card->number, 0);
1318                        kfree(mixer);
1319                        return err;
1320                }
1321                mixer->oss_dev_alloc = 1;
1322                mixer->card = card;
1323                if (*card->mixername)
1324                        strlcpy(mixer->name, card->mixername, sizeof(mixer->name));
1325                else
1326                        strlcpy(mixer->name, name, sizeof(mixer->name));
1327#ifdef SNDRV_OSS_INFO_DEV_MIXERS
1328                snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
1329                                      card->number,
1330                                      mixer->name);
1331#endif
1332                for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
1333                        mixer->slots[idx].number = idx;
1334                card->mixer_oss = mixer;
1335                snd_mixer_oss_build(mixer);
1336                snd_mixer_oss_proc_init(mixer);
1337        } else {
1338                mixer = card->mixer_oss;
1339                if (mixer == NULL)
1340                        return 0;
1341                if (mixer->oss_dev_alloc) {
1342#ifdef SNDRV_OSS_INFO_DEV_MIXERS
1343                        snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
1344#endif
1345                        snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
1346                        mixer->oss_dev_alloc = 0;
1347                }
1348                if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT)
1349                        return 0;
1350                snd_mixer_oss_proc_done(mixer);
1351                return snd_mixer_oss_free1(mixer);
1352        }
1353        return 0;
1354}
1355
1356static int __init alsa_mixer_oss_init(void)
1357{
1358        int idx;
1359        
1360        snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
1361        for (idx = 0; idx < SNDRV_CARDS; idx++) {
1362                if (snd_cards[idx])
1363                        snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
1364        }
1365        return 0;
1366}
1367
1368static void __exit alsa_mixer_oss_exit(void)
1369{
1370        int idx;
1371
1372        snd_mixer_oss_notify_callback = NULL;
1373        for (idx = 0; idx < SNDRV_CARDS; idx++) {
1374                if (snd_cards[idx])
1375                        snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
1376        }
1377}
1378
1379module_init(alsa_mixer_oss_init)
1380module_exit(alsa_mixer_oss_exit)
1381
1382EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
1383