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