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                return -EFAULT;
 105
 106        init_waitqueue_entry(&wait, current);
 107        add_wait_queue(&hw->open_wait, &wait);
 108        mutex_lock(&hw->open_mutex);
 109        while (1) {
 110                if (hw->exclusive && hw->used > 0) {
 111                        err = -EBUSY;
 112                        break;
 113                }
 114                if (!hw->ops.open) {
 115                        err = 0;
 116                        break;
 117                }
 118                err = hw->ops.open(hw, file);
 119                if (err >= 0)
 120                        break;
 121                if (err == -EAGAIN) {
 122                        if (file->f_flags & O_NONBLOCK) {
 123                                err = -EBUSY;
 124                                break;
 125                        }
 126                } else
 127                        break;
 128                set_current_state(TASK_INTERRUPTIBLE);
 129                mutex_unlock(&hw->open_mutex);
 130                schedule();
 131                mutex_lock(&hw->open_mutex);
 132                if (signal_pending(current)) {
 133                        err = -ERESTARTSYS;
 134                        break;
 135                }
 136        }
 137        remove_wait_queue(&hw->open_wait, &wait);
 138        if (err >= 0) {
 139                err = snd_card_file_add(hw->card, file);
 140                if (err >= 0) {
 141                        file->private_data = hw;
 142                        hw->used++;
 143                } else {
 144                        if (hw->ops.release)
 145                                hw->ops.release(hw, file);
 146                }
 147        }
 148        mutex_unlock(&hw->open_mutex);
 149        if (err < 0)
 150                module_put(hw->card->module);
 151        return err;
 152}
 153
 154static int snd_hwdep_release(struct inode *inode, struct file * file)
 155{
 156        int err = 0;
 157        struct snd_hwdep *hw = file->private_data;
 158        struct module *mod = hw->card->module;
 159
 160        mutex_lock(&hw->open_mutex);
 161        if (hw->ops.release)
 162                err = hw->ops.release(hw, file);
 163        if (hw->used > 0)
 164                hw->used--;
 165        mutex_unlock(&hw->open_mutex);
 166        wake_up(&hw->open_wait);
 167
 168        snd_card_file_remove(hw->card, file);
 169        module_put(mod);
 170        return err;
 171}
 172
 173static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
 174{
 175        struct snd_hwdep *hw = file->private_data;
 176        if (hw->ops.poll)
 177                return hw->ops.poll(hw, file, wait);
 178        return 0;
 179}
 180
 181static int snd_hwdep_info(struct snd_hwdep *hw,
 182                          struct snd_hwdep_info __user *_info)
 183{
 184        struct snd_hwdep_info info;
 185        
 186        memset(&info, 0, sizeof(info));
 187        info.card = hw->card->number;
 188        strlcpy(info.id, hw->id, sizeof(info.id));      
 189        strlcpy(info.name, hw->name, sizeof(info.name));
 190        info.iface = hw->iface;
 191        if (copy_to_user(_info, &info, sizeof(info)))
 192                return -EFAULT;
 193        return 0;
 194}
 195
 196static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
 197                                struct snd_hwdep_dsp_status __user *_info)
 198{
 199        struct snd_hwdep_dsp_status info;
 200        int err;
 201        
 202        if (! hw->ops.dsp_status)
 203                return -ENXIO;
 204        memset(&info, 0, sizeof(info));
 205        info.dsp_loaded = hw->dsp_loaded;
 206        if ((err = hw->ops.dsp_status(hw, &info)) < 0)
 207                return err;
 208        if (copy_to_user(_info, &info, sizeof(info)))
 209                return -EFAULT;
 210        return 0;
 211}
 212
 213static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
 214                              struct snd_hwdep_dsp_image __user *_info)
 215{
 216        struct snd_hwdep_dsp_image info;
 217        int err;
 218        
 219        if (! hw->ops.dsp_load)
 220                return -ENXIO;
 221        memset(&info, 0, sizeof(info));
 222        if (copy_from_user(&info, _info, sizeof(info)))
 223                return -EFAULT;
 224        /* check whether the dsp was already loaded */
 225        if (hw->dsp_loaded & (1 << info.index))
 226                return -EBUSY;
 227        if (!access_ok(VERIFY_READ, info.image, info.length))
 228                return -EFAULT;
 229        err = hw->ops.dsp_load(hw, &info);
 230        if (err < 0)
 231                return err;
 232        hw->dsp_loaded |= (1 << info.index);
 233        return 0;
 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(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
 341/**
 342 * snd_hwdep_new - create a new hwdep instance
 343 * @card: the card instance
 344 * @id: the id string
 345 * @device: the device index (zero-based)
 346 * @rhwdep: the pointer to store the new hwdep instance
 347 *
 348 * Creates a new hwdep instance with the given index on the card.
 349 * The callbacks (hwdep->ops) must be set on the returned instance
 350 * after this call manually by the caller.
 351 *
 352 * Returns zero if successful, or a negative error code on failure.
 353 */
 354int snd_hwdep_new(struct snd_card *card, char *id, int device,
 355                  struct snd_hwdep **rhwdep)
 356{
 357        struct snd_hwdep *hwdep;
 358        int err;
 359        static struct snd_device_ops ops = {
 360                .dev_free = snd_hwdep_dev_free,
 361                .dev_register = snd_hwdep_dev_register,
 362                .dev_disconnect = snd_hwdep_dev_disconnect,
 363        };
 364
 365        if (snd_BUG_ON(!card))
 366                return -ENXIO;
 367        if (rhwdep)
 368                *rhwdep = NULL;
 369        hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
 370        if (hwdep == NULL) {
 371                snd_printk(KERN_ERR "hwdep: cannot allocate\n");
 372                return -ENOMEM;
 373        }
 374        hwdep->card = card;
 375        hwdep->device = device;
 376        if (id)
 377                strlcpy(hwdep->id, id, sizeof(hwdep->id));
 378#ifdef CONFIG_SND_OSSEMUL
 379        hwdep->oss_type = -1;
 380#endif
 381        if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
 382                snd_hwdep_free(hwdep);
 383                return err;
 384        }
 385        init_waitqueue_head(&hwdep->open_wait);
 386        mutex_init(&hwdep->open_mutex);
 387        if (rhwdep)
 388                *rhwdep = hwdep;
 389        return 0;
 390}
 391
 392static int snd_hwdep_free(struct snd_hwdep *hwdep)
 393{
 394        if (!hwdep)
 395                return 0;
 396        if (hwdep->private_free)
 397                hwdep->private_free(hwdep);
 398        kfree(hwdep);
 399        return 0;
 400}
 401
 402static int snd_hwdep_dev_free(struct snd_device *device)
 403{
 404        struct snd_hwdep *hwdep = device->device_data;
 405        return snd_hwdep_free(hwdep);
 406}
 407
 408static int snd_hwdep_dev_register(struct snd_device *device)
 409{
 410        struct snd_hwdep *hwdep = device->device_data;
 411        int err;
 412        char name[32];
 413
 414        mutex_lock(&register_mutex);
 415        if (snd_hwdep_search(hwdep->card, hwdep->device)) {
 416                mutex_unlock(&register_mutex);
 417                return -EBUSY;
 418        }
 419        list_add_tail(&hwdep->list, &snd_hwdep_devices);
 420        sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
 421        if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
 422                                       hwdep->card, hwdep->device,
 423                                       &snd_hwdep_f_ops, hwdep, name)) < 0) {
 424                snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
 425                           hwdep->card->number, hwdep->device);
 426                list_del(&hwdep->list);
 427                mutex_unlock(&register_mutex);
 428                return err;
 429        }
 430#ifdef CONFIG_SND_OSSEMUL
 431        hwdep->ossreg = 0;
 432        if (hwdep->oss_type >= 0) {
 433                if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
 434                        snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
 435                } else {
 436                        if (snd_register_oss_device(hwdep->oss_type,
 437                                                    hwdep->card, hwdep->device,
 438                                                    &snd_hwdep_f_ops, hwdep,
 439                                                    hwdep->oss_dev) < 0) {
 440                                snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
 441                                           hwdep->card->number, hwdep->device);
 442                        } else
 443                                hwdep->ossreg = 1;
 444                }
 445        }
 446#endif
 447        mutex_unlock(&register_mutex);
 448        return 0;
 449}
 450
 451static int snd_hwdep_dev_disconnect(struct snd_device *device)
 452{
 453        struct snd_hwdep *hwdep = device->device_data;
 454
 455        if (snd_BUG_ON(!hwdep))
 456                return -ENXIO;
 457        mutex_lock(&register_mutex);
 458        if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
 459                mutex_unlock(&register_mutex);
 460                return -EINVAL;
 461        }
 462#ifdef CONFIG_SND_OSSEMUL
 463        if (hwdep->ossreg)
 464                snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
 465#endif
 466        snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
 467        list_del_init(&hwdep->list);
 468        mutex_unlock(&register_mutex);
 469        return 0;
 470}
 471
 472#ifdef CONFIG_PROC_FS
 473/*
 474 *  Info interface
 475 */
 476
 477static void snd_hwdep_proc_read(struct snd_info_entry *entry,
 478                                struct snd_info_buffer *buffer)
 479{
 480        struct snd_hwdep *hwdep;
 481
 482        mutex_lock(&register_mutex);
 483        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
 484                snd_iprintf(buffer, "%02i-%02i: %s\n",
 485                            hwdep->card->number, hwdep->device, hwdep->name);
 486        mutex_unlock(&register_mutex);
 487}
 488
 489static struct snd_info_entry *snd_hwdep_proc_entry;
 490
 491static void __init snd_hwdep_proc_init(void)
 492{
 493        struct snd_info_entry *entry;
 494
 495        if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
 496                entry->c.text.read = snd_hwdep_proc_read;
 497                if (snd_info_register(entry) < 0) {
 498                        snd_info_free_entry(entry);
 499                        entry = NULL;
 500                }
 501        }
 502        snd_hwdep_proc_entry = entry;
 503}
 504
 505static void __exit snd_hwdep_proc_done(void)
 506{
 507        snd_info_free_entry(snd_hwdep_proc_entry);
 508}
 509#else /* !CONFIG_PROC_FS */
 510#define snd_hwdep_proc_init()
 511#define snd_hwdep_proc_done()
 512#endif /* CONFIG_PROC_FS */
 513
 514
 515/*
 516 *  ENTRY functions
 517 */
 518
 519static int __init alsa_hwdep_init(void)
 520{
 521        snd_hwdep_proc_init();
 522        snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
 523        snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
 524        return 0;
 525}
 526
 527static void __exit alsa_hwdep_exit(void)
 528{
 529        snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
 530        snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
 531        snd_hwdep_proc_done();
 532}
 533
 534module_init(alsa_hwdep_init)
 535module_exit(alsa_hwdep_exit)
 536
 537EXPORT_SYMBOL(snd_hwdep_new);
 538