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 <sound/driver.h>
  23#include <linux/major.h>
  24#include <linux/init.h>
  25#include <linux/slab.h>
  26#include <linux/time.h>
  27#include <linux/mutex.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 (!hw->ops.open)
 104                return -ENXIO;
 105
 106        if (!try_module_get(hw->card->module))
 107                return -EFAULT;
 108
 109        init_waitqueue_entry(&wait, current);
 110        add_wait_queue(&hw->open_wait, &wait);
 111        mutex_lock(&hw->open_mutex);
 112        while (1) {
 113                if (hw->exclusive && hw->used > 0) {
 114                        err = -EBUSY;
 115                        break;
 116                }
 117                err = hw->ops.open(hw, file);
 118                if (err >= 0)
 119                        break;
 120                if (err == -EAGAIN) {
 121                        if (file->f_flags & O_NONBLOCK) {
 122                                err = -EBUSY;
 123                                break;
 124                        }
 125                } else
 126                        break;
 127                set_current_state(TASK_INTERRUPTIBLE);
 128                mutex_unlock(&hw->open_mutex);
 129                schedule();
 130                mutex_lock(&hw->open_mutex);
 131                if (signal_pending(current)) {
 132                        err = -ERESTARTSYS;
 133                        break;
 134                }
 135        }
 136        remove_wait_queue(&hw->open_wait, &wait);
 137        if (err >= 0) {
 138                err = snd_card_file_add(hw->card, file);
 139                if (err >= 0) {
 140                        file->private_data = hw;
 141                        hw->used++;
 142                } else {
 143                        if (hw->ops.release)
 144                                hw->ops.release(hw, file);
 145                }
 146        }
 147        mutex_unlock(&hw->open_mutex);
 148        if (err < 0)
 149                module_put(hw->card->module);
 150        return err;
 151}
 152
 153static int snd_hwdep_release(struct inode *inode, struct file * file)
 154{
 155        int err = -ENXIO;
 156        struct snd_hwdep *hw = file->private_data;
 157        struct module *mod = hw->card->module;
 158
 159        mutex_lock(&hw->open_mutex);
 160        if (hw->ops.release)
 161                err = hw->ops.release(hw, file);
 162        if (hw->used > 0)
 163                hw->used--;
 164        mutex_unlock(&hw->open_mutex);
 165        wake_up(&hw->open_wait);
 166
 167        snd_card_file_remove(hw->card, file);
 168        module_put(mod);
 169        return err;
 170}
 171
 172static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
 173{
 174        struct snd_hwdep *hw = file->private_data;
 175        if (hw->ops.poll)
 176                return hw->ops.poll(hw, file, wait);
 177        return 0;
 178}
 179
 180static int snd_hwdep_info(struct snd_hwdep *hw,
 181                          struct snd_hwdep_info __user *_info)
 182{
 183        struct snd_hwdep_info info;
 184        
 185        memset(&info, 0, sizeof(info));
 186        info.card = hw->card->number;
 187        strlcpy(info.id, hw->id, sizeof(info.id));      
 188        strlcpy(info.name, hw->name, sizeof(info.name));
 189        info.iface = hw->iface;
 190        if (copy_to_user(_info, &info, sizeof(info)))
 191                return -EFAULT;
 192        return 0;
 193}
 194
 195static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
 196                                struct snd_hwdep_dsp_status __user *_info)
 197{
 198        struct snd_hwdep_dsp_status info;
 199        int err;
 200        
 201        if (! hw->ops.dsp_status)
 202                return -ENXIO;
 203        memset(&info, 0, sizeof(info));
 204        info.dsp_loaded = hw->dsp_loaded;
 205        if ((err = hw->ops.dsp_status(hw, &info)) < 0)
 206                return err;
 207        if (copy_to_user(_info, &info, sizeof(info)))
 208                return -EFAULT;
 209        return 0;
 210}
 211
 212static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
 213                              struct snd_hwdep_dsp_image __user *_info)
 214{
 215        struct snd_hwdep_dsp_image info;
 216        int err;
 217        
 218        if (! hw->ops.dsp_load)
 219                return -ENXIO;
 220        memset(&info, 0, sizeof(info));
 221        if (copy_from_user(&info, _info, sizeof(info)))
 222                return -EFAULT;
 223        /* check whether the dsp was already loaded */
 224        if (hw->dsp_loaded & (1 << info.index))
 225                return -EBUSY;
 226        if (!access_ok(VERIFY_READ, info.image, info.length))
 227                return -EFAULT;
 228        err = hw->ops.dsp_load(hw, &info);
 229        if (err < 0)
 230                return err;
 231        hw->dsp_loaded |= (1 << info.index);
 232        return 0;
 233}
 234
 235static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
 236                            unsigned long arg)
 237{
 238        struct snd_hwdep *hw = file->private_data;
 239        void __user *argp = (void __user *)arg;
 240        switch (cmd) {
 241        case SNDRV_HWDEP_IOCTL_PVERSION:
 242                return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);
 243        case SNDRV_HWDEP_IOCTL_INFO:
 244                return snd_hwdep_info(hw, argp);
 245        case SNDRV_HWDEP_IOCTL_DSP_STATUS:
 246                return snd_hwdep_dsp_status(hw, argp);
 247        case SNDRV_HWDEP_IOCTL_DSP_LOAD:
 248                return snd_hwdep_dsp_load(hw, argp);
 249        }
 250        if (hw->ops.ioctl)
 251                return hw->ops.ioctl(hw, file, cmd, arg);
 252        return -ENOTTY;
 253}
 254
 255static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma)
 256{
 257        struct snd_hwdep *hw = file->private_data;
 258        if (hw->ops.mmap)
 259                return hw->ops.mmap(hw, file, vma);
 260        return -ENXIO;
 261}
 262
 263static int snd_hwdep_control_ioctl(struct snd_card *card,
 264                                   struct snd_ctl_file * control,
 265                                   unsigned int cmd, unsigned long arg)
 266{
 267        switch (cmd) {
 268        case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
 269                {
 270                        int device;
 271
 272                        if (get_user(device, (int __user *)arg))
 273                                return -EFAULT;
 274                        mutex_lock(&register_mutex);
 275                        device = device < 0 ? 0 : device + 1;
 276                        while (device < SNDRV_MINOR_HWDEPS) {
 277                                if (snd_hwdep_search(card, device))
 278                                        break;
 279                                device++;
 280                        }
 281                        if (device >= SNDRV_MINOR_HWDEPS)
 282                                device = -1;
 283                        mutex_unlock(&register_mutex);
 284                        if (put_user(device, (int __user *)arg))
 285                                return -EFAULT;
 286                        return 0;
 287                }
 288        case SNDRV_CTL_IOCTL_HWDEP_INFO:
 289                {
 290                        struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg;
 291                        int device, err;
 292                        struct snd_hwdep *hwdep;
 293
 294                        if (get_user(device, &info->device))
 295                                return -EFAULT;
 296                        mutex_lock(&register_mutex);
 297                        hwdep = snd_hwdep_search(card, device);
 298                        if (hwdep)
 299                                err = snd_hwdep_info(hwdep, info);
 300                        else
 301                                err = -ENXIO;
 302                        mutex_unlock(&register_mutex);
 303                        return err;
 304                }
 305        }
 306        return -ENOIOCTLCMD;
 307}
 308
 309#ifdef CONFIG_COMPAT
 310#include "hwdep_compat.c"
 311#else
 312#define snd_hwdep_ioctl_compat  NULL
 313#endif
 314
 315/*
 316
 317 */
 318
 319static const struct file_operations snd_hwdep_f_ops =
 320{
 321        .owner =        THIS_MODULE,
 322        .llseek =       snd_hwdep_llseek,
 323        .read =         snd_hwdep_read,
 324        .write =        snd_hwdep_write,
 325        .open =         snd_hwdep_open,
 326        .release =      snd_hwdep_release,
 327        .poll =         snd_hwdep_poll,
 328        .unlocked_ioctl =       snd_hwdep_ioctl,
 329        .compat_ioctl = snd_hwdep_ioctl_compat,
 330        .mmap =         snd_hwdep_mmap,
 331};
 332
 333/**
 334 * snd_hwdep_new - create a new hwdep instance
 335 * @card: the card instance
 336 * @id: the id string
 337 * @device: the device index (zero-based)
 338 * @rhwdep: the pointer to store the new hwdep instance
 339 *
 340 * Creates a new hwdep instance with the given index on the card.
 341 * The callbacks (hwdep->ops) must be set on the returned instance
 342 * after this call manually by the caller.
 343 *
 344 * Returns zero if successful, or a negative error code on failure.
 345 */
 346int snd_hwdep_new(struct snd_card *card, char *id, int device,
 347                  struct snd_hwdep **rhwdep)
 348{
 349        struct snd_hwdep *hwdep;
 350        int err;
 351        static struct snd_device_ops ops = {
 352                .dev_free = snd_hwdep_dev_free,
 353                .dev_register = snd_hwdep_dev_register,
 354                .dev_disconnect = snd_hwdep_dev_disconnect,
 355        };
 356
 357        snd_assert(rhwdep != NULL, return -EINVAL);
 358        *rhwdep = NULL;
 359        snd_assert(card != NULL, return -ENXIO);
 360        hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
 361        if (hwdep == NULL) {
 362                snd_printk(KERN_ERR "hwdep: cannot allocate\n");
 363                return -ENOMEM;
 364        }
 365        hwdep->card = card;
 366        hwdep->device = device;
 367        if (id)
 368                strlcpy(hwdep->id, id, sizeof(hwdep->id));
 369#ifdef CONFIG_SND_OSSEMUL
 370        hwdep->oss_type = -1;
 371#endif
 372        if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
 373                snd_hwdep_free(hwdep);
 374                return err;
 375        }
 376        init_waitqueue_head(&hwdep->open_wait);
 377        mutex_init(&hwdep->open_mutex);
 378        *rhwdep = hwdep;
 379        return 0;
 380}
 381
 382static int snd_hwdep_free(struct snd_hwdep *hwdep)
 383{
 384        snd_assert(hwdep != NULL, return -ENXIO);
 385        if (hwdep->private_free)
 386                hwdep->private_free(hwdep);
 387        kfree(hwdep);
 388        return 0;
 389}
 390
 391static int snd_hwdep_dev_free(struct snd_device *device)
 392{
 393        struct snd_hwdep *hwdep = device->device_data;
 394        return snd_hwdep_free(hwdep);
 395}
 396
 397static int snd_hwdep_dev_register(struct snd_device *device)
 398{
 399        struct snd_hwdep *hwdep = device->device_data;
 400        int err;
 401        char name[32];
 402
 403        mutex_lock(&register_mutex);
 404        if (snd_hwdep_search(hwdep->card, hwdep->device)) {
 405                mutex_unlock(&register_mutex);
 406                return -EBUSY;
 407        }
 408        list_add_tail(&hwdep->list, &snd_hwdep_devices);
 409        sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
 410        if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
 411                                       hwdep->card, hwdep->device,
 412                                       &snd_hwdep_f_ops, hwdep, name)) < 0) {
 413                snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
 414                           hwdep->card->number, hwdep->device);
 415                list_del(&hwdep->list);
 416                mutex_unlock(&register_mutex);
 417                return err;
 418        }
 419#ifdef CONFIG_SND_OSSEMUL
 420        hwdep->ossreg = 0;
 421        if (hwdep->oss_type >= 0) {
 422                if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
 423                        snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
 424                } else {
 425                        if (snd_register_oss_device(hwdep->oss_type,
 426                                                    hwdep->card, hwdep->device,
 427                                                    &snd_hwdep_f_ops, hwdep,
 428                                                    hwdep->oss_dev) < 0) {
 429                                snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
 430                                           hwdep->card->number, hwdep->device);
 431                        } else
 432                                hwdep->ossreg = 1;
 433                }
 434        }
 435#endif
 436        mutex_unlock(&register_mutex);
 437        return 0;
 438}
 439
 440static int snd_hwdep_dev_disconnect(struct snd_device *device)
 441{
 442        struct snd_hwdep *hwdep = device->device_data;
 443
 444        snd_assert(hwdep != NULL, return -ENXIO);
 445        mutex_lock(&register_mutex);
 446        if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
 447                mutex_unlock(&register_mutex);
 448                return -EINVAL;
 449        }
 450#ifdef CONFIG_SND_OSSEMUL
 451        if (hwdep->ossreg)
 452                snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
 453#endif
 454        snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
 455        list_del_init(&hwdep->list);
 456        mutex_unlock(&register_mutex);
 457        return 0;
 458}
 459
 460#ifdef CONFIG_PROC_FS
 461/*
 462 *  Info interface
 463 */
 464
 465static void snd_hwdep_proc_read(struct snd_info_entry *entry,
 466                                struct snd_info_buffer *buffer)
 467{
 468        struct snd_hwdep *hwdep;
 469
 470        mutex_lock(&register_mutex);
 471        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
 472                snd_iprintf(buffer, "%02i-%02i: %s\n",
 473                            hwdep->card->number, hwdep->device, hwdep->name);
 474        mutex_unlock(&register_mutex);
 475}
 476
 477static struct snd_info_entry *snd_hwdep_proc_entry;
 478
 479static void __init snd_hwdep_proc_init(void)
 480{
 481        struct snd_info_entry *entry;
 482
 483        if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
 484                entry->c.text.read = snd_hwdep_proc_read;
 485                if (snd_info_register(entry) < 0) {
 486                        snd_info_free_entry(entry);
 487                        entry = NULL;
 488                }
 489        }
 490        snd_hwdep_proc_entry = entry;
 491}
 492
 493static void __exit snd_hwdep_proc_done(void)
 494{
 495        snd_info_free_entry(snd_hwdep_proc_entry);
 496}
 497#else /* !CONFIG_PROC_FS */
 498#define snd_hwdep_proc_init()
 499#define snd_hwdep_proc_done()
 500#endif /* CONFIG_PROC_FS */
 501
 502
 503/*
 504 *  ENTRY functions
 505 */
 506
 507static int __init alsa_hwdep_init(void)
 508{
 509        snd_hwdep_proc_init();
 510        snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
 511        snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
 512        return 0;
 513}
 514
 515static void __exit alsa_hwdep_exit(void)
 516{
 517        snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
 518        snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
 519        snd_hwdep_proc_done();
 520}
 521
 522module_init(alsa_hwdep_init)
 523module_exit(alsa_hwdep_exit)
 524
 525EXPORT_SYMBOL(snd_hwdep_new);
 526