linux/sound/core/hwdep.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Hardware dependent layer
   4 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   5 */
   6
   7#include <linux/major.h>
   8#include <linux/init.h>
   9#include <linux/slab.h>
  10#include <linux/time.h>
  11#include <linux/mutex.h>
  12#include <linux/module.h>
  13#include <linux/sched/signal.h>
  14#include <sound/core.h>
  15#include <sound/control.h>
  16#include <sound/minors.h>
  17#include <sound/hwdep.h>
  18#include <sound/info.h>
  19
  20MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  21MODULE_DESCRIPTION("Hardware dependent layer");
  22MODULE_LICENSE("GPL");
  23
  24static LIST_HEAD(snd_hwdep_devices);
  25static DEFINE_MUTEX(register_mutex);
  26
  27static int snd_hwdep_dev_free(struct snd_device *device);
  28static int snd_hwdep_dev_register(struct snd_device *device);
  29static int snd_hwdep_dev_disconnect(struct snd_device *device);
  30
  31
  32static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
  33{
  34        struct snd_hwdep *hwdep;
  35
  36        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
  37                if (hwdep->card == card && hwdep->device == device)
  38                        return hwdep;
  39        return NULL;
  40}
  41
  42static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig)
  43{
  44        struct snd_hwdep *hw = file->private_data;
  45        if (hw->ops.llseek)
  46                return hw->ops.llseek(hw, file, offset, orig);
  47        return -ENXIO;
  48}
  49
  50static ssize_t snd_hwdep_read(struct file * file, char __user *buf,
  51                              size_t count, loff_t *offset)
  52{
  53        struct snd_hwdep *hw = file->private_data;
  54        if (hw->ops.read)
  55                return hw->ops.read(hw, buf, count, offset);
  56        return -ENXIO;  
  57}
  58
  59static ssize_t snd_hwdep_write(struct file * file, const char __user *buf,
  60                               size_t count, loff_t *offset)
  61{
  62        struct snd_hwdep *hw = file->private_data;
  63        if (hw->ops.write)
  64                return hw->ops.write(hw, buf, count, offset);
  65        return -ENXIO;  
  66}
  67
  68static int snd_hwdep_open(struct inode *inode, struct file * file)
  69{
  70        int major = imajor(inode);
  71        struct snd_hwdep *hw;
  72        int err;
  73        wait_queue_entry_t wait;
  74
  75        if (major == snd_major) {
  76                hw = snd_lookup_minor_data(iminor(inode),
  77                                           SNDRV_DEVICE_TYPE_HWDEP);
  78#ifdef CONFIG_SND_OSSEMUL
  79        } else if (major == SOUND_MAJOR) {
  80                hw = snd_lookup_oss_minor_data(iminor(inode),
  81                                               SNDRV_OSS_DEVICE_TYPE_DMFM);
  82#endif
  83        } else
  84                return -ENXIO;
  85        if (hw == NULL)
  86                return -ENODEV;
  87
  88        if (!try_module_get(hw->card->module)) {
  89                snd_card_unref(hw->card);
  90                return -EFAULT;
  91        }
  92
  93        init_waitqueue_entry(&wait, current);
  94        add_wait_queue(&hw->open_wait, &wait);
  95        mutex_lock(&hw->open_mutex);
  96        while (1) {
  97                if (hw->exclusive && hw->used > 0) {
  98                        err = -EBUSY;
  99                        break;
 100                }
 101                if (!hw->ops.open) {
 102                        err = 0;
 103                        break;
 104                }
 105                err = hw->ops.open(hw, file);
 106                if (err >= 0)
 107                        break;
 108                if (err == -EAGAIN) {
 109                        if (file->f_flags & O_NONBLOCK) {
 110                                err = -EBUSY;
 111                                break;
 112                        }
 113                } else
 114                        break;
 115                set_current_state(TASK_INTERRUPTIBLE);
 116                mutex_unlock(&hw->open_mutex);
 117                schedule();
 118                mutex_lock(&hw->open_mutex);
 119                if (hw->card->shutdown) {
 120                        err = -ENODEV;
 121                        break;
 122                }
 123                if (signal_pending(current)) {
 124                        err = -ERESTARTSYS;
 125                        break;
 126                }
 127        }
 128        remove_wait_queue(&hw->open_wait, &wait);
 129        if (err >= 0) {
 130                err = snd_card_file_add(hw->card, file);
 131                if (err >= 0) {
 132                        file->private_data = hw;
 133                        hw->used++;
 134                } else {
 135                        if (hw->ops.release)
 136                                hw->ops.release(hw, file);
 137                }
 138        }
 139        mutex_unlock(&hw->open_mutex);
 140        if (err < 0)
 141                module_put(hw->card->module);
 142        snd_card_unref(hw->card);
 143        return err;
 144}
 145
 146static int snd_hwdep_release(struct inode *inode, struct file * file)
 147{
 148        int err = 0;
 149        struct snd_hwdep *hw = file->private_data;
 150        struct module *mod = hw->card->module;
 151
 152        mutex_lock(&hw->open_mutex);
 153        if (hw->ops.release)
 154                err = hw->ops.release(hw, file);
 155        if (hw->used > 0)
 156                hw->used--;
 157        mutex_unlock(&hw->open_mutex);
 158        wake_up(&hw->open_wait);
 159
 160        snd_card_file_remove(hw->card, file);
 161        module_put(mod);
 162        return err;
 163}
 164
 165static __poll_t snd_hwdep_poll(struct file * file, poll_table * wait)
 166{
 167        struct snd_hwdep *hw = file->private_data;
 168        if (hw->ops.poll)
 169                return hw->ops.poll(hw, file, wait);
 170        return 0;
 171}
 172
 173static int snd_hwdep_info(struct snd_hwdep *hw,
 174                          struct snd_hwdep_info __user *_info)
 175{
 176        struct snd_hwdep_info info;
 177        
 178        memset(&info, 0, sizeof(info));
 179        info.card = hw->card->number;
 180        strscpy(info.id, hw->id, sizeof(info.id));
 181        strscpy(info.name, hw->name, sizeof(info.name));
 182        info.iface = hw->iface;
 183        if (copy_to_user(_info, &info, sizeof(info)))
 184                return -EFAULT;
 185        return 0;
 186}
 187
 188static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
 189                                struct snd_hwdep_dsp_status __user *_info)
 190{
 191        struct snd_hwdep_dsp_status info;
 192        int err;
 193        
 194        if (! hw->ops.dsp_status)
 195                return -ENXIO;
 196        memset(&info, 0, sizeof(info));
 197        info.dsp_loaded = hw->dsp_loaded;
 198        err = hw->ops.dsp_status(hw, &info);
 199        if (err < 0)
 200                return err;
 201        if (copy_to_user(_info, &info, sizeof(info)))
 202                return -EFAULT;
 203        return 0;
 204}
 205
 206static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
 207                              struct snd_hwdep_dsp_image *info)
 208{
 209        int err;
 210        
 211        if (! hw->ops.dsp_load)
 212                return -ENXIO;
 213        if (info->index >= 32)
 214                return -EINVAL;
 215        /* check whether the dsp was already loaded */
 216        if (hw->dsp_loaded & (1u << info->index))
 217                return -EBUSY;
 218        err = hw->ops.dsp_load(hw, info);
 219        if (err < 0)
 220                return err;
 221        hw->dsp_loaded |= (1u << info->index);
 222        return 0;
 223}
 224
 225static int snd_hwdep_dsp_load_user(struct snd_hwdep *hw,
 226                                   struct snd_hwdep_dsp_image __user *_info)
 227{
 228        struct snd_hwdep_dsp_image info = {};
 229
 230        if (copy_from_user(&info, _info, sizeof(info)))
 231                return -EFAULT;
 232        return snd_hwdep_dsp_load(hw, &info);
 233}
 234
 235
 236static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
 237                            unsigned long arg)
 238{
 239        struct snd_hwdep *hw = file->private_data;
 240        void __user *argp = (void __user *)arg;
 241        switch (cmd) {
 242        case SNDRV_HWDEP_IOCTL_PVERSION:
 243                return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);
 244        case SNDRV_HWDEP_IOCTL_INFO:
 245                return snd_hwdep_info(hw, argp);
 246        case SNDRV_HWDEP_IOCTL_DSP_STATUS:
 247                return snd_hwdep_dsp_status(hw, argp);
 248        case SNDRV_HWDEP_IOCTL_DSP_LOAD:
 249                return snd_hwdep_dsp_load_user(hw, argp);
 250        }
 251        if (hw->ops.ioctl)
 252                return hw->ops.ioctl(hw, file, cmd, arg);
 253        return -ENOTTY;
 254}
 255
 256static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma)
 257{
 258        struct snd_hwdep *hw = file->private_data;
 259        if (hw->ops.mmap)
 260                return hw->ops.mmap(hw, file, vma);
 261        return -ENXIO;
 262}
 263
 264static int snd_hwdep_control_ioctl(struct snd_card *card,
 265                                   struct snd_ctl_file * control,
 266                                   unsigned int cmd, unsigned long arg)
 267{
 268        switch (cmd) {
 269        case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
 270                {
 271                        int device;
 272
 273                        if (get_user(device, (int __user *)arg))
 274                                return -EFAULT;
 275                        mutex_lock(&register_mutex);
 276
 277                        if (device < 0)
 278                                device = 0;
 279                        else if (device < SNDRV_MINOR_HWDEPS)
 280                                device++;
 281                        else
 282                                device = SNDRV_MINOR_HWDEPS;
 283
 284                        while (device < SNDRV_MINOR_HWDEPS) {
 285                                if (snd_hwdep_search(card, device))
 286                                        break;
 287                                device++;
 288                        }
 289                        if (device >= SNDRV_MINOR_HWDEPS)
 290                                device = -1;
 291                        mutex_unlock(&register_mutex);
 292                        if (put_user(device, (int __user *)arg))
 293                                return -EFAULT;
 294                        return 0;
 295                }
 296        case SNDRV_CTL_IOCTL_HWDEP_INFO:
 297                {
 298                        struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg;
 299                        int device, err;
 300                        struct snd_hwdep *hwdep;
 301
 302                        if (get_user(device, &info->device))
 303                                return -EFAULT;
 304                        mutex_lock(&register_mutex);
 305                        hwdep = snd_hwdep_search(card, device);
 306                        if (hwdep)
 307                                err = snd_hwdep_info(hwdep, info);
 308                        else
 309                                err = -ENXIO;
 310                        mutex_unlock(&register_mutex);
 311                        return err;
 312                }
 313        }
 314        return -ENOIOCTLCMD;
 315}
 316
 317#ifdef CONFIG_COMPAT
 318#include "hwdep_compat.c"
 319#else
 320#define snd_hwdep_ioctl_compat  NULL
 321#endif
 322
 323/*
 324
 325 */
 326
 327static const struct file_operations snd_hwdep_f_ops =
 328{
 329        .owner =        THIS_MODULE,
 330        .llseek =       snd_hwdep_llseek,
 331        .read =         snd_hwdep_read,
 332        .write =        snd_hwdep_write,
 333        .open =         snd_hwdep_open,
 334        .release =      snd_hwdep_release,
 335        .poll =         snd_hwdep_poll,
 336        .unlocked_ioctl =       snd_hwdep_ioctl,
 337        .compat_ioctl = snd_hwdep_ioctl_compat,
 338        .mmap =         snd_hwdep_mmap,
 339};
 340
 341static void release_hwdep_device(struct device *dev)
 342{
 343        kfree(container_of(dev, struct snd_hwdep, dev));
 344}
 345
 346/**
 347 * snd_hwdep_new - create a new hwdep instance
 348 * @card: the card instance
 349 * @id: the id string
 350 * @device: the device index (zero-based)
 351 * @rhwdep: the pointer to store the new hwdep instance
 352 *
 353 * Creates a new hwdep instance with the given index on the card.
 354 * The callbacks (hwdep->ops) must be set on the returned instance
 355 * after this call manually by the caller.
 356 *
 357 * Return: Zero if successful, or a negative error code on failure.
 358 */
 359int snd_hwdep_new(struct snd_card *card, char *id, int device,
 360                  struct snd_hwdep **rhwdep)
 361{
 362        struct snd_hwdep *hwdep;
 363        int err;
 364        static const struct snd_device_ops ops = {
 365                .dev_free = snd_hwdep_dev_free,
 366                .dev_register = snd_hwdep_dev_register,
 367                .dev_disconnect = snd_hwdep_dev_disconnect,
 368        };
 369
 370        if (snd_BUG_ON(!card))
 371                return -ENXIO;
 372        if (rhwdep)
 373                *rhwdep = NULL;
 374        hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
 375        if (!hwdep)
 376                return -ENOMEM;
 377
 378        init_waitqueue_head(&hwdep->open_wait);
 379        mutex_init(&hwdep->open_mutex);
 380        hwdep->card = card;
 381        hwdep->device = device;
 382        if (id)
 383                strscpy(hwdep->id, id, sizeof(hwdep->id));
 384
 385        snd_device_initialize(&hwdep->dev, card);
 386        hwdep->dev.release = release_hwdep_device;
 387        dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device);
 388#ifdef CONFIG_SND_OSSEMUL
 389        hwdep->oss_type = -1;
 390#endif
 391
 392        err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
 393        if (err < 0) {
 394                put_device(&hwdep->dev);
 395                return err;
 396        }
 397
 398        if (rhwdep)
 399                *rhwdep = hwdep;
 400        return 0;
 401}
 402EXPORT_SYMBOL(snd_hwdep_new);
 403
 404static int snd_hwdep_dev_free(struct snd_device *device)
 405{
 406        struct snd_hwdep *hwdep = device->device_data;
 407        if (!hwdep)
 408                return 0;
 409        if (hwdep->private_free)
 410                hwdep->private_free(hwdep);
 411        put_device(&hwdep->dev);
 412        return 0;
 413}
 414
 415static int snd_hwdep_dev_register(struct snd_device *device)
 416{
 417        struct snd_hwdep *hwdep = device->device_data;
 418        struct snd_card *card = hwdep->card;
 419        int err;
 420
 421        mutex_lock(&register_mutex);
 422        if (snd_hwdep_search(card, hwdep->device)) {
 423                mutex_unlock(&register_mutex);
 424                return -EBUSY;
 425        }
 426        list_add_tail(&hwdep->list, &snd_hwdep_devices);
 427        err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
 428                                  hwdep->card, hwdep->device,
 429                                  &snd_hwdep_f_ops, hwdep, &hwdep->dev);
 430        if (err < 0) {
 431                dev_err(&hwdep->dev, "unable to register\n");
 432                list_del(&hwdep->list);
 433                mutex_unlock(&register_mutex);
 434                return err;
 435        }
 436
 437#ifdef CONFIG_SND_OSSEMUL
 438        hwdep->ossreg = 0;
 439        if (hwdep->oss_type >= 0) {
 440                if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM &&
 441                    hwdep->device)
 442                        dev_warn(&hwdep->dev,
 443                                 "only hwdep device 0 can be registered as OSS direct FM device!\n");
 444                else if (snd_register_oss_device(hwdep->oss_type,
 445                                                 card, hwdep->device,
 446                                                 &snd_hwdep_f_ops, hwdep) < 0)
 447                        dev_warn(&hwdep->dev,
 448                                 "unable to register OSS compatibility device\n");
 449                else
 450                        hwdep->ossreg = 1;
 451        }
 452#endif
 453        mutex_unlock(&register_mutex);
 454        return 0;
 455}
 456
 457static int snd_hwdep_dev_disconnect(struct snd_device *device)
 458{
 459        struct snd_hwdep *hwdep = device->device_data;
 460
 461        if (snd_BUG_ON(!hwdep))
 462                return -ENXIO;
 463        mutex_lock(&register_mutex);
 464        if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
 465                mutex_unlock(&register_mutex);
 466                return -EINVAL;
 467        }
 468        mutex_lock(&hwdep->open_mutex);
 469        wake_up(&hwdep->open_wait);
 470#ifdef CONFIG_SND_OSSEMUL
 471        if (hwdep->ossreg)
 472                snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
 473#endif
 474        snd_unregister_device(&hwdep->dev);
 475        list_del_init(&hwdep->list);
 476        mutex_unlock(&hwdep->open_mutex);
 477        mutex_unlock(&register_mutex);
 478        return 0;
 479}
 480
 481#ifdef CONFIG_SND_PROC_FS
 482/*
 483 *  Info interface
 484 */
 485
 486static void snd_hwdep_proc_read(struct snd_info_entry *entry,
 487                                struct snd_info_buffer *buffer)
 488{
 489        struct snd_hwdep *hwdep;
 490
 491        mutex_lock(&register_mutex);
 492        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
 493                snd_iprintf(buffer, "%02i-%02i: %s\n",
 494                            hwdep->card->number, hwdep->device, hwdep->name);
 495        mutex_unlock(&register_mutex);
 496}
 497
 498static struct snd_info_entry *snd_hwdep_proc_entry;
 499
 500static void __init snd_hwdep_proc_init(void)
 501{
 502        struct snd_info_entry *entry;
 503
 504        entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL);
 505        if (entry) {
 506                entry->c.text.read = snd_hwdep_proc_read;
 507                if (snd_info_register(entry) < 0) {
 508                        snd_info_free_entry(entry);
 509                        entry = NULL;
 510                }
 511        }
 512        snd_hwdep_proc_entry = entry;
 513}
 514
 515static void __exit snd_hwdep_proc_done(void)
 516{
 517        snd_info_free_entry(snd_hwdep_proc_entry);
 518}
 519#else /* !CONFIG_SND_PROC_FS */
 520#define snd_hwdep_proc_init()
 521#define snd_hwdep_proc_done()
 522#endif /* CONFIG_SND_PROC_FS */
 523
 524
 525/*
 526 *  ENTRY functions
 527 */
 528
 529static int __init alsa_hwdep_init(void)
 530{
 531        snd_hwdep_proc_init();
 532        snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
 533        snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
 534        return 0;
 535}
 536
 537static void __exit alsa_hwdep_exit(void)
 538{
 539        snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
 540        snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
 541        snd_hwdep_proc_done();
 542}
 543
 544module_init(alsa_hwdep_init)
 545module_exit(alsa_hwdep_exit)
 546