Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Hardware dependent layer
0004  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
0005  */
0006 
0007 #include <linux/major.h>
0008 #include <linux/init.h>
0009 #include <linux/slab.h>
0010 #include <linux/time.h>
0011 #include <linux/mutex.h>
0012 #include <linux/module.h>
0013 #include <linux/sched/signal.h>
0014 #include <sound/core.h>
0015 #include <sound/control.h>
0016 #include <sound/minors.h>
0017 #include <sound/hwdep.h>
0018 #include <sound/info.h>
0019 
0020 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
0021 MODULE_DESCRIPTION("Hardware dependent layer");
0022 MODULE_LICENSE("GPL");
0023 
0024 static LIST_HEAD(snd_hwdep_devices);
0025 static DEFINE_MUTEX(register_mutex);
0026 
0027 static int snd_hwdep_dev_free(struct snd_device *device);
0028 static int snd_hwdep_dev_register(struct snd_device *device);
0029 static int snd_hwdep_dev_disconnect(struct snd_device *device);
0030 
0031 
0032 static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
0033 {
0034     struct snd_hwdep *hwdep;
0035 
0036     list_for_each_entry(hwdep, &snd_hwdep_devices, list)
0037         if (hwdep->card == card && hwdep->device == device)
0038             return hwdep;
0039     return NULL;
0040 }
0041 
0042 static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig)
0043 {
0044     struct snd_hwdep *hw = file->private_data;
0045     if (hw->ops.llseek)
0046         return hw->ops.llseek(hw, file, offset, orig);
0047     return -ENXIO;
0048 }
0049 
0050 static ssize_t snd_hwdep_read(struct file * file, char __user *buf,
0051                   size_t count, loff_t *offset)
0052 {
0053     struct snd_hwdep *hw = file->private_data;
0054     if (hw->ops.read)
0055         return hw->ops.read(hw, buf, count, offset);
0056     return -ENXIO;  
0057 }
0058 
0059 static ssize_t snd_hwdep_write(struct file * file, const char __user *buf,
0060                    size_t count, loff_t *offset)
0061 {
0062     struct snd_hwdep *hw = file->private_data;
0063     if (hw->ops.write)
0064         return hw->ops.write(hw, buf, count, offset);
0065     return -ENXIO;  
0066 }
0067 
0068 static int snd_hwdep_open(struct inode *inode, struct file * file)
0069 {
0070     int major = imajor(inode);
0071     struct snd_hwdep *hw;
0072     int err;
0073     wait_queue_entry_t wait;
0074 
0075     if (major == snd_major) {
0076         hw = snd_lookup_minor_data(iminor(inode),
0077                        SNDRV_DEVICE_TYPE_HWDEP);
0078 #ifdef CONFIG_SND_OSSEMUL
0079     } else if (major == SOUND_MAJOR) {
0080         hw = snd_lookup_oss_minor_data(iminor(inode),
0081                            SNDRV_OSS_DEVICE_TYPE_DMFM);
0082 #endif
0083     } else
0084         return -ENXIO;
0085     if (hw == NULL)
0086         return -ENODEV;
0087 
0088     if (!try_module_get(hw->card->module)) {
0089         snd_card_unref(hw->card);
0090         return -EFAULT;
0091     }
0092 
0093     init_waitqueue_entry(&wait, current);
0094     add_wait_queue(&hw->open_wait, &wait);
0095     mutex_lock(&hw->open_mutex);
0096     while (1) {
0097         if (hw->exclusive && hw->used > 0) {
0098             err = -EBUSY;
0099             break;
0100         }
0101         if (!hw->ops.open) {
0102             err = 0;
0103             break;
0104         }
0105         err = hw->ops.open(hw, file);
0106         if (err >= 0)
0107             break;
0108         if (err == -EAGAIN) {
0109             if (file->f_flags & O_NONBLOCK) {
0110                 err = -EBUSY;
0111                 break;
0112             }
0113         } else
0114             break;
0115         set_current_state(TASK_INTERRUPTIBLE);
0116         mutex_unlock(&hw->open_mutex);
0117         schedule();
0118         mutex_lock(&hw->open_mutex);
0119         if (hw->card->shutdown) {
0120             err = -ENODEV;
0121             break;
0122         }
0123         if (signal_pending(current)) {
0124             err = -ERESTARTSYS;
0125             break;
0126         }
0127     }
0128     remove_wait_queue(&hw->open_wait, &wait);
0129     if (err >= 0) {
0130         err = snd_card_file_add(hw->card, file);
0131         if (err >= 0) {
0132             file->private_data = hw;
0133             hw->used++;
0134         } else {
0135             if (hw->ops.release)
0136                 hw->ops.release(hw, file);
0137         }
0138     }
0139     mutex_unlock(&hw->open_mutex);
0140     if (err < 0)
0141         module_put(hw->card->module);
0142     snd_card_unref(hw->card);
0143     return err;
0144 }
0145 
0146 static int snd_hwdep_release(struct inode *inode, struct file * file)
0147 {
0148     int err = 0;
0149     struct snd_hwdep *hw = file->private_data;
0150     struct module *mod = hw->card->module;
0151 
0152     mutex_lock(&hw->open_mutex);
0153     if (hw->ops.release)
0154         err = hw->ops.release(hw, file);
0155     if (hw->used > 0)
0156         hw->used--;
0157     mutex_unlock(&hw->open_mutex);
0158     wake_up(&hw->open_wait);
0159 
0160     snd_card_file_remove(hw->card, file);
0161     module_put(mod);
0162     return err;
0163 }
0164 
0165 static __poll_t snd_hwdep_poll(struct file * file, poll_table * wait)
0166 {
0167     struct snd_hwdep *hw = file->private_data;
0168     if (hw->ops.poll)
0169         return hw->ops.poll(hw, file, wait);
0170     return 0;
0171 }
0172 
0173 static int snd_hwdep_info(struct snd_hwdep *hw,
0174               struct snd_hwdep_info __user *_info)
0175 {
0176     struct snd_hwdep_info info;
0177     
0178     memset(&info, 0, sizeof(info));
0179     info.card = hw->card->number;
0180     strscpy(info.id, hw->id, sizeof(info.id));
0181     strscpy(info.name, hw->name, sizeof(info.name));
0182     info.iface = hw->iface;
0183     if (copy_to_user(_info, &info, sizeof(info)))
0184         return -EFAULT;
0185     return 0;
0186 }
0187 
0188 static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
0189                 struct snd_hwdep_dsp_status __user *_info)
0190 {
0191     struct snd_hwdep_dsp_status info;
0192     int err;
0193     
0194     if (! hw->ops.dsp_status)
0195         return -ENXIO;
0196     memset(&info, 0, sizeof(info));
0197     info.dsp_loaded = hw->dsp_loaded;
0198     err = hw->ops.dsp_status(hw, &info);
0199     if (err < 0)
0200         return err;
0201     if (copy_to_user(_info, &info, sizeof(info)))
0202         return -EFAULT;
0203     return 0;
0204 }
0205 
0206 static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
0207                   struct snd_hwdep_dsp_image *info)
0208 {
0209     int err;
0210     
0211     if (! hw->ops.dsp_load)
0212         return -ENXIO;
0213     if (info->index >= 32)
0214         return -EINVAL;
0215     /* check whether the dsp was already loaded */
0216     if (hw->dsp_loaded & (1u << info->index))
0217         return -EBUSY;
0218     err = hw->ops.dsp_load(hw, info);
0219     if (err < 0)
0220         return err;
0221     hw->dsp_loaded |= (1u << info->index);
0222     return 0;
0223 }
0224 
0225 static int snd_hwdep_dsp_load_user(struct snd_hwdep *hw,
0226                    struct snd_hwdep_dsp_image __user *_info)
0227 {
0228     struct snd_hwdep_dsp_image info = {};
0229 
0230     if (copy_from_user(&info, _info, sizeof(info)))
0231         return -EFAULT;
0232     return snd_hwdep_dsp_load(hw, &info);
0233 }
0234 
0235 
0236 static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
0237                 unsigned long arg)
0238 {
0239     struct snd_hwdep *hw = file->private_data;
0240     void __user *argp = (void __user *)arg;
0241     switch (cmd) {
0242     case SNDRV_HWDEP_IOCTL_PVERSION:
0243         return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);
0244     case SNDRV_HWDEP_IOCTL_INFO:
0245         return snd_hwdep_info(hw, argp);
0246     case SNDRV_HWDEP_IOCTL_DSP_STATUS:
0247         return snd_hwdep_dsp_status(hw, argp);
0248     case SNDRV_HWDEP_IOCTL_DSP_LOAD:
0249         return snd_hwdep_dsp_load_user(hw, argp);
0250     }
0251     if (hw->ops.ioctl)
0252         return hw->ops.ioctl(hw, file, cmd, arg);
0253     return -ENOTTY;
0254 }
0255 
0256 static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma)
0257 {
0258     struct snd_hwdep *hw = file->private_data;
0259     if (hw->ops.mmap)
0260         return hw->ops.mmap(hw, file, vma);
0261     return -ENXIO;
0262 }
0263 
0264 static int snd_hwdep_control_ioctl(struct snd_card *card,
0265                    struct snd_ctl_file * control,
0266                    unsigned int cmd, unsigned long arg)
0267 {
0268     switch (cmd) {
0269     case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
0270         {
0271             int device;
0272 
0273             if (get_user(device, (int __user *)arg))
0274                 return -EFAULT;
0275             mutex_lock(&register_mutex);
0276 
0277             if (device < 0)
0278                 device = 0;
0279             else if (device < SNDRV_MINOR_HWDEPS)
0280                 device++;
0281             else
0282                 device = SNDRV_MINOR_HWDEPS;
0283 
0284             while (device < SNDRV_MINOR_HWDEPS) {
0285                 if (snd_hwdep_search(card, device))
0286                     break;
0287                 device++;
0288             }
0289             if (device >= SNDRV_MINOR_HWDEPS)
0290                 device = -1;
0291             mutex_unlock(&register_mutex);
0292             if (put_user(device, (int __user *)arg))
0293                 return -EFAULT;
0294             return 0;
0295         }
0296     case SNDRV_CTL_IOCTL_HWDEP_INFO:
0297         {
0298             struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg;
0299             int device, err;
0300             struct snd_hwdep *hwdep;
0301 
0302             if (get_user(device, &info->device))
0303                 return -EFAULT;
0304             mutex_lock(&register_mutex);
0305             hwdep = snd_hwdep_search(card, device);
0306             if (hwdep)
0307                 err = snd_hwdep_info(hwdep, info);
0308             else
0309                 err = -ENXIO;
0310             mutex_unlock(&register_mutex);
0311             return err;
0312         }
0313     }
0314     return -ENOIOCTLCMD;
0315 }
0316 
0317 #ifdef CONFIG_COMPAT
0318 #include "hwdep_compat.c"
0319 #else
0320 #define snd_hwdep_ioctl_compat  NULL
0321 #endif
0322 
0323 /*
0324 
0325  */
0326 
0327 static const struct file_operations snd_hwdep_f_ops =
0328 {
0329     .owner =    THIS_MODULE,
0330     .llseek =   snd_hwdep_llseek,
0331     .read =     snd_hwdep_read,
0332     .write =    snd_hwdep_write,
0333     .open =     snd_hwdep_open,
0334     .release =  snd_hwdep_release,
0335     .poll =     snd_hwdep_poll,
0336     .unlocked_ioctl =   snd_hwdep_ioctl,
0337     .compat_ioctl = snd_hwdep_ioctl_compat,
0338     .mmap =     snd_hwdep_mmap,
0339 };
0340 
0341 static void release_hwdep_device(struct device *dev)
0342 {
0343     kfree(container_of(dev, struct snd_hwdep, dev));
0344 }
0345 
0346 /**
0347  * snd_hwdep_new - create a new hwdep instance
0348  * @card: the card instance
0349  * @id: the id string
0350  * @device: the device index (zero-based)
0351  * @rhwdep: the pointer to store the new hwdep instance
0352  *
0353  * Creates a new hwdep instance with the given index on the card.
0354  * The callbacks (hwdep->ops) must be set on the returned instance
0355  * after this call manually by the caller.
0356  *
0357  * Return: Zero if successful, or a negative error code on failure.
0358  */
0359 int snd_hwdep_new(struct snd_card *card, char *id, int device,
0360           struct snd_hwdep **rhwdep)
0361 {
0362     struct snd_hwdep *hwdep;
0363     int err;
0364     static const struct snd_device_ops ops = {
0365         .dev_free = snd_hwdep_dev_free,
0366         .dev_register = snd_hwdep_dev_register,
0367         .dev_disconnect = snd_hwdep_dev_disconnect,
0368     };
0369 
0370     if (snd_BUG_ON(!card))
0371         return -ENXIO;
0372     if (rhwdep)
0373         *rhwdep = NULL;
0374     hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
0375     if (!hwdep)
0376         return -ENOMEM;
0377 
0378     init_waitqueue_head(&hwdep->open_wait);
0379     mutex_init(&hwdep->open_mutex);
0380     hwdep->card = card;
0381     hwdep->device = device;
0382     if (id)
0383         strscpy(hwdep->id, id, sizeof(hwdep->id));
0384 
0385     snd_device_initialize(&hwdep->dev, card);
0386     hwdep->dev.release = release_hwdep_device;
0387     dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device);
0388 #ifdef CONFIG_SND_OSSEMUL
0389     hwdep->oss_type = -1;
0390 #endif
0391 
0392     err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
0393     if (err < 0) {
0394         put_device(&hwdep->dev);
0395         return err;
0396     }
0397 
0398     if (rhwdep)
0399         *rhwdep = hwdep;
0400     return 0;
0401 }
0402 EXPORT_SYMBOL(snd_hwdep_new);
0403 
0404 static int snd_hwdep_dev_free(struct snd_device *device)
0405 {
0406     struct snd_hwdep *hwdep = device->device_data;
0407     if (!hwdep)
0408         return 0;
0409     if (hwdep->private_free)
0410         hwdep->private_free(hwdep);
0411     put_device(&hwdep->dev);
0412     return 0;
0413 }
0414 
0415 static int snd_hwdep_dev_register(struct snd_device *device)
0416 {
0417     struct snd_hwdep *hwdep = device->device_data;
0418     struct snd_card *card = hwdep->card;
0419     int err;
0420 
0421     mutex_lock(&register_mutex);
0422     if (snd_hwdep_search(card, hwdep->device)) {
0423         mutex_unlock(&register_mutex);
0424         return -EBUSY;
0425     }
0426     list_add_tail(&hwdep->list, &snd_hwdep_devices);
0427     err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
0428                   hwdep->card, hwdep->device,
0429                   &snd_hwdep_f_ops, hwdep, &hwdep->dev);
0430     if (err < 0) {
0431         dev_err(&hwdep->dev, "unable to register\n");
0432         list_del(&hwdep->list);
0433         mutex_unlock(&register_mutex);
0434         return err;
0435     }
0436 
0437 #ifdef CONFIG_SND_OSSEMUL
0438     hwdep->ossreg = 0;
0439     if (hwdep->oss_type >= 0) {
0440         if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM &&
0441             hwdep->device)
0442             dev_warn(&hwdep->dev,
0443                  "only hwdep device 0 can be registered as OSS direct FM device!\n");
0444         else if (snd_register_oss_device(hwdep->oss_type,
0445                          card, hwdep->device,
0446                          &snd_hwdep_f_ops, hwdep) < 0)
0447             dev_warn(&hwdep->dev,
0448                  "unable to register OSS compatibility device\n");
0449         else
0450             hwdep->ossreg = 1;
0451     }
0452 #endif
0453     mutex_unlock(&register_mutex);
0454     return 0;
0455 }
0456 
0457 static int snd_hwdep_dev_disconnect(struct snd_device *device)
0458 {
0459     struct snd_hwdep *hwdep = device->device_data;
0460 
0461     if (snd_BUG_ON(!hwdep))
0462         return -ENXIO;
0463     mutex_lock(&register_mutex);
0464     if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
0465         mutex_unlock(&register_mutex);
0466         return -EINVAL;
0467     }
0468     mutex_lock(&hwdep->open_mutex);
0469     wake_up(&hwdep->open_wait);
0470 #ifdef CONFIG_SND_OSSEMUL
0471     if (hwdep->ossreg)
0472         snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
0473 #endif
0474     snd_unregister_device(&hwdep->dev);
0475     list_del_init(&hwdep->list);
0476     mutex_unlock(&hwdep->open_mutex);
0477     mutex_unlock(&register_mutex);
0478     return 0;
0479 }
0480 
0481 #ifdef CONFIG_SND_PROC_FS
0482 /*
0483  *  Info interface
0484  */
0485 
0486 static void snd_hwdep_proc_read(struct snd_info_entry *entry,
0487                 struct snd_info_buffer *buffer)
0488 {
0489     struct snd_hwdep *hwdep;
0490 
0491     mutex_lock(&register_mutex);
0492     list_for_each_entry(hwdep, &snd_hwdep_devices, list)
0493         snd_iprintf(buffer, "%02i-%02i: %s\n",
0494                 hwdep->card->number, hwdep->device, hwdep->name);
0495     mutex_unlock(&register_mutex);
0496 }
0497 
0498 static struct snd_info_entry *snd_hwdep_proc_entry;
0499 
0500 static void __init snd_hwdep_proc_init(void)
0501 {
0502     struct snd_info_entry *entry;
0503 
0504     entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL);
0505     if (entry) {
0506         entry->c.text.read = snd_hwdep_proc_read;
0507         if (snd_info_register(entry) < 0) {
0508             snd_info_free_entry(entry);
0509             entry = NULL;
0510         }
0511     }
0512     snd_hwdep_proc_entry = entry;
0513 }
0514 
0515 static void __exit snd_hwdep_proc_done(void)
0516 {
0517     snd_info_free_entry(snd_hwdep_proc_entry);
0518 }
0519 #else /* !CONFIG_SND_PROC_FS */
0520 #define snd_hwdep_proc_init()
0521 #define snd_hwdep_proc_done()
0522 #endif /* CONFIG_SND_PROC_FS */
0523 
0524 
0525 /*
0526  *  ENTRY functions
0527  */
0528 
0529 static int __init alsa_hwdep_init(void)
0530 {
0531     snd_hwdep_proc_init();
0532     snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
0533     snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
0534     return 0;
0535 }
0536 
0537 static void __exit alsa_hwdep_exit(void)
0538 {
0539     snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
0540     snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
0541     snd_hwdep_proc_done();
0542 }
0543 
0544 module_init(alsa_hwdep_init)
0545 module_exit(alsa_hwdep_exit)