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