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 <sound/core.h>
  29#include <sound/control.h>
  30#include <sound/minors.h>
  31#include <sound/hwdep.h>
  32#include <sound/info.h>
  33
  34MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  35MODULE_DESCRIPTION("Hardware dependent layer");
  36MODULE_LICENSE("GPL");
  37
  38static LIST_HEAD(snd_hwdep_devices);
  39static DEFINE_MUTEX(register_mutex);
  40
  41static int snd_hwdep_free(struct snd_hwdep *hwdep);
  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_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 unsigned int 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        /* check whether the dsp was already loaded */
 232        if (hw->dsp_loaded & (1 << info.index))
 233                return -EBUSY;
 234        if (!access_ok(VERIFY_READ, info.image, info.length))
 235                return -EFAULT;
 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
 348/**
 349 * snd_hwdep_new - create a new hwdep instance
 350 * @card: the card instance
 351 * @id: the id string
 352 * @device: the device index (zero-based)
 353 * @rhwdep: the pointer to store the new hwdep instance
 354 *
 355 * Creates a new hwdep instance with the given index on the card.
 356 * The callbacks (hwdep->ops) must be set on the returned instance
 357 * after this call manually by the caller.
 358 *
 359 * Returns zero if successful, or a negative error code on failure.
 360 */
 361int snd_hwdep_new(struct snd_card *card, char *id, int device,
 362                  struct snd_hwdep **rhwdep)
 363{
 364        struct snd_hwdep *hwdep;
 365        int err;
 366        static struct snd_device_ops ops = {
 367                .dev_free = snd_hwdep_dev_free,
 368                .dev_register = snd_hwdep_dev_register,
 369                .dev_disconnect = snd_hwdep_dev_disconnect,
 370        };
 371
 372        if (snd_BUG_ON(!card))
 373                return -ENXIO;
 374        if (rhwdep)
 375                *rhwdep = NULL;
 376        hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
 377        if (hwdep == NULL) {
 378                snd_printk(KERN_ERR "hwdep: cannot allocate\n");
 379                return -ENOMEM;
 380        }
 381        hwdep->card = card;
 382        hwdep->device = device;
 383        if (id)
 384                strlcpy(hwdep->id, id, sizeof(hwdep->id));
 385#ifdef CONFIG_SND_OSSEMUL
 386        hwdep->oss_type = -1;
 387#endif
 388        if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
 389                snd_hwdep_free(hwdep);
 390                return err;
 391        }
 392        init_waitqueue_head(&hwdep->open_wait);
 393        mutex_init(&hwdep->open_mutex);
 394        if (rhwdep)
 395                *rhwdep = hwdep;
 396        return 0;
 397}
 398
 399static int snd_hwdep_free(struct snd_hwdep *hwdep)
 400{
 401        if (!hwdep)
 402                return 0;
 403        if (hwdep->private_free)
 404                hwdep->private_free(hwdep);
 405        kfree(hwdep);
 406        return 0;
 407}
 408
 409static int snd_hwdep_dev_free(struct snd_device *device)
 410{
 411        struct snd_hwdep *hwdep = device->device_data;
 412        return snd_hwdep_free(hwdep);
 413}
 414
 415static int snd_hwdep_dev_register(struct snd_device *device)
 416{
 417        struct snd_hwdep *hwdep = device->device_data;
 418        int err;
 419        char name[32];
 420
 421        mutex_lock(&register_mutex);
 422        if (snd_hwdep_search(hwdep->card, hwdep->device)) {
 423                mutex_unlock(&register_mutex);
 424                return -EBUSY;
 425        }
 426        list_add_tail(&hwdep->list, &snd_hwdep_devices);
 427        sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
 428        if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
 429                                       hwdep->card, hwdep->device,
 430                                       &snd_hwdep_f_ops, hwdep, name)) < 0) {
 431                snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
 432                           hwdep->card->number, hwdep->device);
 433                list_del(&hwdep->list);
 434                mutex_unlock(&register_mutex);
 435                return err;
 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) && (hwdep->device != 0)) {
 441                        snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
 442                } else {
 443                        if (snd_register_oss_device(hwdep->oss_type,
 444                                                    hwdep->card, hwdep->device,
 445                                                    &snd_hwdep_f_ops, hwdep,
 446                                                    hwdep->oss_dev) < 0) {
 447                                snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
 448                                           hwdep->card->number, hwdep->device);
 449                        } else
 450                                hwdep->ossreg = 1;
 451                }
 452        }
 453#endif
 454        mutex_unlock(&register_mutex);
 455        return 0;
 456}
 457
 458static int snd_hwdep_dev_disconnect(struct snd_device *device)
 459{
 460        struct snd_hwdep *hwdep = device->device_data;
 461
 462        if (snd_BUG_ON(!hwdep))
 463                return -ENXIO;
 464        mutex_lock(&register_mutex);
 465        if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
 466                mutex_unlock(&register_mutex);
 467                return -EINVAL;
 468        }
 469        mutex_lock(&hwdep->open_mutex);
 470        wake_up(&hwdep->open_wait);
 471#ifdef CONFIG_SND_OSSEMUL
 472        if (hwdep->ossreg)
 473                snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
 474#endif
 475        snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
 476        list_del_init(&hwdep->list);
 477        mutex_unlock(&hwdep->open_mutex);
 478        mutex_unlock(&register_mutex);
 479        return 0;
 480}
 481
 482#ifdef CONFIG_PROC_FS
 483/*
 484 *  Info interface
 485 */
 486
 487static void snd_hwdep_proc_read(struct snd_info_entry *entry,
 488                                struct snd_info_buffer *buffer)
 489{
 490        struct snd_hwdep *hwdep;
 491
 492        mutex_lock(&register_mutex);
 493        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
 494                snd_iprintf(buffer, "%02i-%02i: %s\n",
 495                            hwdep->card->number, hwdep->device, hwdep->name);
 496        mutex_unlock(&register_mutex);
 497}
 498
 499static struct snd_info_entry *snd_hwdep_proc_entry;
 500
 501static void __init snd_hwdep_proc_init(void)
 502{
 503        struct snd_info_entry *entry;
 504
 505        if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
 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_PROC_FS */
 520#define snd_hwdep_proc_init()
 521#define snd_hwdep_proc_done()
 522#endif /* CONFIG_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
 547EXPORT_SYMBOL(snd_hwdep_new);
 548
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.