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