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 <sound/core.h>
  28#include <sound/control.h>
  29#include <sound/minors.h>
  30#include <sound/hwdep.h>
  31#include <sound/info.h>
  32
  33MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  34MODULE_DESCRIPTION("Hardware dependent layer");
  35MODULE_LICENSE("GPL");
  36
  37static LIST_HEAD(snd_hwdep_devices);
  38static DEFINE_MUTEX(register_mutex);
  39
  40static int snd_hwdep_free(struct snd_hwdep *hwdep);
  41static int snd_hwdep_dev_free(struct snd_device *device);
  42static int snd_hwdep_dev_register(struct snd_device *device);
  43static int snd_hwdep_dev_disconnect(struct snd_device *device);
  44
  45
  46static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
  47{
  48        struct snd_hwdep *hwdep;
  49
  50        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
  51                if (hwdep->card == card && hwdep->device == device)
  52                        return hwdep;
  53        return NULL;
  54}
  55
  56static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig)
  57{
  58        struct snd_hwdep *hw = file->private_data;
  59        if (hw->ops.llseek)
  60                return hw->ops.llseek(hw, file, offset, orig);
  61        return -ENXIO;
  62}
  63
  64static ssize_t snd_hwdep_read(struct file * file, char __user *buf,
  65                              size_t count, loff_t *offset)
  66{
  67        struct snd_hwdep *hw = file->private_data;
  68        if (hw->ops.read)
  69                return hw->ops.read(hw, buf, count, offset);
  70        return -ENXIO;  
  71}
  72
  73static ssize_t snd_hwdep_write(struct file * file, const char __user *buf,
  74                               size_t count, loff_t *offset)
  75{
  76        struct snd_hwdep *hw = file->private_data;
  77        if (hw->ops.write)
  78                return hw->ops.write(hw, buf, count, offset);
  79        return -ENXIO;  
  80}
  81
  82static int snd_hwdep_open(struct inode *inode, struct file * file)
  83{
  84        int major = imajor(inode);
  85        struct snd_hwdep *hw;
  86        int err;
  87        wait_queue_t wait;
  88
  89        if (major == snd_major) {
  90                hw = snd_lookup_minor_data(iminor(inode),
  91                                           SNDRV_DEVICE_TYPE_HWDEP);
  92#ifdef CONFIG_SND_OSSEMUL
  93        } else if (major == SOUND_MAJOR) {
  94                hw = snd_lookup_oss_minor_data(iminor(inode),
  95                                               SNDRV_OSS_DEVICE_TYPE_DMFM);
  96#endif
  97        } else
  98                return -ENXIO;
  99        if (hw == NULL)
 100                return -ENODEV;
 101
 102        if (!try_module_get(hw->card->module))
 103                return -EFAULT;
 104
 105        init_waitqueue_entry(&wait, current);
 106        add_wait_queue(&hw->open_wait, &wait);
 107        mutex_lock(&hw->open_mutex);
 108        while (1) {
 109                if (hw->exclusive && hw->used > 0) {
 110                        err = -EBUSY;
 111                        break;
 112                }
 113                if (!hw->ops.open) {
 114                        err = 0;
 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 = 0;
 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        if (snd_BUG_ON(!card))
 358                return -ENXIO;
 359        if (rhwdep)
 360                *rhwdep = NULL;
 361        hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
 362        if (hwdep == NULL) {
 363                snd_printk(KERN_ERR "hwdep: cannot allocate\n");
 364                return -ENOMEM;
 365        }
 366        hwdep->card = card;
 367        hwdep->device = device;
 368        if (id)
 369                strlcpy(hwdep->id, id, sizeof(hwdep->id));
 370#ifdef CONFIG_SND_OSSEMUL
 371        hwdep->oss_type = -1;
 372#endif
 373        if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
 374                snd_hwdep_free(hwdep);
 375                return err;
 376        }
 377        init_waitqueue_head(&hwdep->open_wait);
 378        mutex_init(&hwdep->open_mutex);
 379        if (rhwdep)
 380                *rhwdep = hwdep;
 381        return 0;
 382}
 383
 384static int snd_hwdep_free(struct snd_hwdep *hwdep)
 385{
 386        if (!hwdep)
 387                return 0;
 388        if (hwdep->private_free)
 389                hwdep->private_free(hwdep);
 390        kfree(hwdep);
 391        return 0;
 392}
 393
 394static int snd_hwdep_dev_free(struct snd_device *device)
 395{
 396        struct snd_hwdep *hwdep = device->device_data;
 397        return snd_hwdep_free(hwdep);
 398}
 399
 400static int snd_hwdep_dev_register(struct snd_device *device)
 401{
 402        struct snd_hwdep *hwdep = device->device_data;
 403        int err;
 404        char name[32];
 405
 406        mutex_lock(&register_mutex);
 407        if (snd_hwdep_search(hwdep->card, hwdep->device)) {
 408                mutex_unlock(&register_mutex);
 409                return -EBUSY;
 410        }
 411        list_add_tail(&hwdep->list, &snd_hwdep_devices);
 412        sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
 413        if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
 414                                       hwdep->card, hwdep->device,
 415                                       &snd_hwdep_f_ops, hwdep, name)) < 0) {
 416                snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
 417                           hwdep->card->number, hwdep->device);
 418                list_del(&hwdep->list);
 419                mutex_unlock(&register_mutex);
 420                return err;
 421        }
 422#ifdef CONFIG_SND_OSSEMUL
 423        hwdep->ossreg = 0;
 424        if (hwdep->oss_type >= 0) {
 425                if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
 426                        snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
 427                } else {
 428                        if (snd_register_oss_device(hwdep->oss_type,
 429                                                    hwdep->card, hwdep->device,
 430                                                    &snd_hwdep_f_ops, hwdep,
 431                                                    hwdep->oss_dev) < 0) {
 432                                snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
 433                                           hwdep->card->number, hwdep->device);
 434                        } else
 435                                hwdep->ossreg = 1;
 436                }
 437        }
 438#endif
 439        mutex_unlock(&register_mutex);
 440        return 0;
 441}
 442
 443static int snd_hwdep_dev_disconnect(struct snd_device *device)
 444{
 445        struct snd_hwdep *hwdep = device->device_data;
 446
 447        if (snd_BUG_ON(!hwdep))
 448                return -ENXIO;
 449        mutex_lock(&register_mutex);
 450        if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
 451                mutex_unlock(&register_mutex);
 452                return -EINVAL;
 453        }
 454#ifdef CONFIG_SND_OSSEMUL
 455        if (hwdep->ossreg)
 456                snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
 457#endif
 458        snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
 459        list_del_init(&hwdep->list);
 460        mutex_unlock(&register_mutex);
 461        return 0;
 462}
 463
 464#ifdef CONFIG_PROC_FS
 465/*
 466 *  Info interface
 467 */
 468
 469static void snd_hwdep_proc_read(struct snd_info_entry *entry,
 470                                struct snd_info_buffer *buffer)
 471{
 472        struct snd_hwdep *hwdep;
 473
 474        mutex_lock(&register_mutex);
 475        list_for_each_entry(hwdep, &snd_hwdep_devices, list)
 476                snd_iprintf(buffer, "%02i-%02i: %s\n",
 477                            hwdep->card->number, hwdep->device, hwdep->name);
 478        mutex_unlock(&register_mutex);
 479}
 480
 481static struct snd_info_entry *snd_hwdep_proc_entry;
 482
 483static void __init snd_hwdep_proc_init(void)
 484{
 485        struct snd_info_entry *entry;
 486
 487        if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
 488                entry->c.text.read = snd_hwdep_proc_read;
 489                if (snd_info_register(entry) < 0) {
 490                        snd_info_free_entry(entry);
 491                        entry = NULL;
 492                }
 493        }
 494        snd_hwdep_proc_entry = entry;
 495}
 496
 497static void __exit snd_hwdep_proc_done(void)
 498{
 499        snd_info_free_entry(snd_hwdep_proc_entry);
 500}
 501#else /* !CONFIG_PROC_FS */
 502#define snd_hwdep_proc_init()
 503#define snd_hwdep_proc_done()
 504#endif /* CONFIG_PROC_FS */
 505
 506
 507/*
 508 *  ENTRY functions
 509 */
 510
 511static int __init alsa_hwdep_init(void)
 512{
 513        snd_hwdep_proc_init();
 514        snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
 515        snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
 516        return 0;
 517}
 518
 519static void __exit alsa_hwdep_exit(void)
 520{
 521        snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
 522        snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
 523        snd_hwdep_proc_done();
 524}
 525
 526module_init(alsa_hwdep_init)
 527module_exit(alsa_hwdep_exit)
 528
 529EXPORT_SYMBOL(snd_hwdep_new);
 530