cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hwdep.c (12943B)


      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
     20MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
     21MODULE_DESCRIPTION("Hardware dependent layer");
     22MODULE_LICENSE("GPL");
     23
     24static LIST_HEAD(snd_hwdep_devices);
     25static DEFINE_MUTEX(register_mutex);
     26
     27static int snd_hwdep_dev_free(struct snd_device *device);
     28static int snd_hwdep_dev_register(struct snd_device *device);
     29static int snd_hwdep_dev_disconnect(struct snd_device *device);
     30
     31
     32static 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
     42static 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
     50static 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
     59static 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
     68static 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
    146static 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
    165static __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
    173static 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	strscpy(info.id, hw->id, sizeof(info.id));
    181	strscpy(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
    188static 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	err = hw->ops.dsp_status(hw, &info);
    199	if (err < 0)
    200		return err;
    201	if (copy_to_user(_info, &info, sizeof(info)))
    202		return -EFAULT;
    203	return 0;
    204}
    205
    206static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
    207			      struct snd_hwdep_dsp_image *info)
    208{
    209	int err;
    210	
    211	if (! hw->ops.dsp_load)
    212		return -ENXIO;
    213	if (info->index >= 32)
    214		return -EINVAL;
    215	/* check whether the dsp was already loaded */
    216	if (hw->dsp_loaded & (1u << info->index))
    217		return -EBUSY;
    218	err = hw->ops.dsp_load(hw, info);
    219	if (err < 0)
    220		return err;
    221	hw->dsp_loaded |= (1u << info->index);
    222	return 0;
    223}
    224
    225static int snd_hwdep_dsp_load_user(struct snd_hwdep *hw,
    226				   struct snd_hwdep_dsp_image __user *_info)
    227{
    228	struct snd_hwdep_dsp_image info = {};
    229
    230	if (copy_from_user(&info, _info, sizeof(info)))
    231		return -EFAULT;
    232	return snd_hwdep_dsp_load(hw, &info);
    233}
    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_user(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
    341static void release_hwdep_device(struct device *dev)
    342{
    343	kfree(container_of(dev, struct snd_hwdep, dev));
    344}
    345
    346/**
    347 * snd_hwdep_new - create a new hwdep instance
    348 * @card: the card instance
    349 * @id: the id string
    350 * @device: the device index (zero-based)
    351 * @rhwdep: the pointer to store the new hwdep instance
    352 *
    353 * Creates a new hwdep instance with the given index on the card.
    354 * The callbacks (hwdep->ops) must be set on the returned instance
    355 * after this call manually by the caller.
    356 *
    357 * Return: Zero if successful, or a negative error code on failure.
    358 */
    359int snd_hwdep_new(struct snd_card *card, char *id, int device,
    360		  struct snd_hwdep **rhwdep)
    361{
    362	struct snd_hwdep *hwdep;
    363	int err;
    364	static const struct snd_device_ops ops = {
    365		.dev_free = snd_hwdep_dev_free,
    366		.dev_register = snd_hwdep_dev_register,
    367		.dev_disconnect = snd_hwdep_dev_disconnect,
    368	};
    369
    370	if (snd_BUG_ON(!card))
    371		return -ENXIO;
    372	if (rhwdep)
    373		*rhwdep = NULL;
    374	hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
    375	if (!hwdep)
    376		return -ENOMEM;
    377
    378	init_waitqueue_head(&hwdep->open_wait);
    379	mutex_init(&hwdep->open_mutex);
    380	hwdep->card = card;
    381	hwdep->device = device;
    382	if (id)
    383		strscpy(hwdep->id, id, sizeof(hwdep->id));
    384
    385	snd_device_initialize(&hwdep->dev, card);
    386	hwdep->dev.release = release_hwdep_device;
    387	dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device);
    388#ifdef CONFIG_SND_OSSEMUL
    389	hwdep->oss_type = -1;
    390#endif
    391
    392	err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
    393	if (err < 0) {
    394		put_device(&hwdep->dev);
    395		return err;
    396	}
    397
    398	if (rhwdep)
    399		*rhwdep = hwdep;
    400	return 0;
    401}
    402EXPORT_SYMBOL(snd_hwdep_new);
    403
    404static int snd_hwdep_dev_free(struct snd_device *device)
    405{
    406	struct snd_hwdep *hwdep = device->device_data;
    407	if (!hwdep)
    408		return 0;
    409	if (hwdep->private_free)
    410		hwdep->private_free(hwdep);
    411	put_device(&hwdep->dev);
    412	return 0;
    413}
    414
    415static int snd_hwdep_dev_register(struct snd_device *device)
    416{
    417	struct snd_hwdep *hwdep = device->device_data;
    418	struct snd_card *card = hwdep->card;
    419	int err;
    420
    421	mutex_lock(&register_mutex);
    422	if (snd_hwdep_search(card, hwdep->device)) {
    423		mutex_unlock(&register_mutex);
    424		return -EBUSY;
    425	}
    426	list_add_tail(&hwdep->list, &snd_hwdep_devices);
    427	err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
    428				  hwdep->card, hwdep->device,
    429				  &snd_hwdep_f_ops, hwdep, &hwdep->dev);
    430	if (err < 0) {
    431		dev_err(&hwdep->dev, "unable to register\n");
    432		list_del(&hwdep->list);
    433		mutex_unlock(&register_mutex);
    434		return err;
    435	}
    436
    437#ifdef CONFIG_SND_OSSEMUL
    438	hwdep->ossreg = 0;
    439	if (hwdep->oss_type >= 0) {
    440		if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM &&
    441		    hwdep->device)
    442			dev_warn(&hwdep->dev,
    443				 "only hwdep device 0 can be registered as OSS direct FM device!\n");
    444		else if (snd_register_oss_device(hwdep->oss_type,
    445						 card, hwdep->device,
    446						 &snd_hwdep_f_ops, hwdep) < 0)
    447			dev_warn(&hwdep->dev,
    448				 "unable to register OSS compatibility device\n");
    449		else
    450			hwdep->ossreg = 1;
    451	}
    452#endif
    453	mutex_unlock(&register_mutex);
    454	return 0;
    455}
    456
    457static int snd_hwdep_dev_disconnect(struct snd_device *device)
    458{
    459	struct snd_hwdep *hwdep = device->device_data;
    460
    461	if (snd_BUG_ON(!hwdep))
    462		return -ENXIO;
    463	mutex_lock(&register_mutex);
    464	if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
    465		mutex_unlock(&register_mutex);
    466		return -EINVAL;
    467	}
    468	mutex_lock(&hwdep->open_mutex);
    469	wake_up(&hwdep->open_wait);
    470#ifdef CONFIG_SND_OSSEMUL
    471	if (hwdep->ossreg)
    472		snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
    473#endif
    474	snd_unregister_device(&hwdep->dev);
    475	list_del_init(&hwdep->list);
    476	mutex_unlock(&hwdep->open_mutex);
    477	mutex_unlock(&register_mutex);
    478	return 0;
    479}
    480
    481#ifdef CONFIG_SND_PROC_FS
    482/*
    483 *  Info interface
    484 */
    485
    486static void snd_hwdep_proc_read(struct snd_info_entry *entry,
    487				struct snd_info_buffer *buffer)
    488{
    489	struct snd_hwdep *hwdep;
    490
    491	mutex_lock(&register_mutex);
    492	list_for_each_entry(hwdep, &snd_hwdep_devices, list)
    493		snd_iprintf(buffer, "%02i-%02i: %s\n",
    494			    hwdep->card->number, hwdep->device, hwdep->name);
    495	mutex_unlock(&register_mutex);
    496}
    497
    498static struct snd_info_entry *snd_hwdep_proc_entry;
    499
    500static void __init snd_hwdep_proc_init(void)
    501{
    502	struct snd_info_entry *entry;
    503
    504	entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL);
    505	if (entry) {
    506		entry->c.text.read = snd_hwdep_proc_read;
    507		if (snd_info_register(entry) < 0) {
    508			snd_info_free_entry(entry);
    509			entry = NULL;
    510		}
    511	}
    512	snd_hwdep_proc_entry = entry;
    513}
    514
    515static void __exit snd_hwdep_proc_done(void)
    516{
    517	snd_info_free_entry(snd_hwdep_proc_entry);
    518}
    519#else /* !CONFIG_SND_PROC_FS */
    520#define snd_hwdep_proc_init()
    521#define snd_hwdep_proc_done()
    522#endif /* CONFIG_SND_PROC_FS */
    523
    524
    525/*
    526 *  ENTRY functions
    527 */
    528
    529static int __init alsa_hwdep_init(void)
    530{
    531	snd_hwdep_proc_init();
    532	snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
    533	snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
    534	return 0;
    535}
    536
    537static void __exit alsa_hwdep_exit(void)
    538{
    539	snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
    540	snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
    541	snd_hwdep_proc_done();
    542}
    543
    544module_init(alsa_hwdep_init)
    545module_exit(alsa_hwdep_exit)