root/sound/core/hwdep.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_hwdep_search
  2. snd_hwdep_llseek
  3. snd_hwdep_read
  4. snd_hwdep_write
  5. snd_hwdep_open
  6. snd_hwdep_release
  7. snd_hwdep_poll
  8. snd_hwdep_info
  9. snd_hwdep_dsp_status
  10. snd_hwdep_dsp_load
  11. snd_hwdep_ioctl
  12. snd_hwdep_mmap
  13. snd_hwdep_control_ioctl
  14. release_hwdep_device
  15. snd_hwdep_new
  16. snd_hwdep_dev_free
  17. snd_hwdep_dev_register
  18. snd_hwdep_dev_disconnect
  19. snd_hwdep_proc_read
  20. snd_hwdep_proc_init
  21. snd_hwdep_proc_done
  22. alsa_hwdep_init
  23. alsa_hwdep_exit

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

/* [<][>][^][v][top][bottom][index][help] */