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  
snd_hwdep_search(struct snd_card * card,int device)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  
snd_hwdep_llseek(struct file * file,loff_t offset,int orig)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  
snd_hwdep_read(struct file * file,char __user * buf,size_t count,loff_t * offset)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  
snd_hwdep_write(struct file * file,const char __user * buf,size_t count,loff_t * offset)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  
snd_hwdep_open(struct inode * inode,struct file * file)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  
snd_hwdep_release(struct inode * inode,struct file * file)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  	scoped_guard(mutex, &hw->open_mutex) {
153  		if (hw->ops.release)
154  			err = hw->ops.release(hw, file);
155  		if (hw->used > 0)
156  			hw->used--;
157  	}
158  	wake_up(&hw->open_wait);
159  
160  	snd_card_file_remove(hw->card, file);
161  	module_put(mod);
162  	return err;
163  }
164  
snd_hwdep_poll(struct file * file,poll_table * wait)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  
snd_hwdep_info(struct snd_hwdep * hw,struct snd_hwdep_info __user * _info)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  	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  
snd_hwdep_dsp_status(struct snd_hwdep * hw,struct snd_hwdep_dsp_status __user * _info)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  	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  
snd_hwdep_dsp_load(struct snd_hwdep * hw,struct snd_hwdep_dsp_image * info)206  static 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  
snd_hwdep_dsp_load_user(struct snd_hwdep * hw,struct snd_hwdep_dsp_image __user * _info)225  static 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  
snd_hwdep_ioctl(struct file * file,unsigned int cmd,unsigned long arg)236  static 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  
snd_hwdep_mmap(struct file * file,struct vm_area_struct * vma)256  static 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  
snd_hwdep_control_ioctl(struct snd_card * card,struct snd_ctl_file * control,unsigned int cmd,unsigned long arg)264  static 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  
276  			scoped_guard(mutex, &register_mutex) {
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  			}
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;
300  			struct snd_hwdep *hwdep;
301  
302  			if (get_user(device, &info->device))
303  				return -EFAULT;
304  			scoped_guard(mutex, &register_mutex) {
305  				hwdep = snd_hwdep_search(card, device);
306  				if (!hwdep)
307  					return -ENXIO;
308  				return snd_hwdep_info(hwdep, info);
309  			}
310  			break;
311  		}
312  	}
313  	return -ENOIOCTLCMD;
314  }
315  
316  #ifdef CONFIG_COMPAT
317  #include "hwdep_compat.c"
318  #else
319  #define snd_hwdep_ioctl_compat	NULL
320  #endif
321  
322  /*
323  
324   */
325  
326  static const struct file_operations snd_hwdep_f_ops =
327  {
328  	.owner = 	THIS_MODULE,
329  	.llseek =	snd_hwdep_llseek,
330  	.read = 	snd_hwdep_read,
331  	.write =	snd_hwdep_write,
332  	.open =		snd_hwdep_open,
333  	.release =	snd_hwdep_release,
334  	.poll =		snd_hwdep_poll,
335  	.unlocked_ioctl =	snd_hwdep_ioctl,
336  	.compat_ioctl =	snd_hwdep_ioctl_compat,
337  	.mmap =		snd_hwdep_mmap,
338  };
339  
snd_hwdep_free(struct snd_hwdep * hwdep)340  static void snd_hwdep_free(struct snd_hwdep *hwdep)
341  {
342  	if (!hwdep)
343  		return;
344  	if (hwdep->private_free)
345  		hwdep->private_free(hwdep);
346  	put_device(hwdep->dev);
347  	kfree(hwdep);
348  }
349  
350  /**
351   * snd_hwdep_new - create a new hwdep instance
352   * @card: the card instance
353   * @id: the id string
354   * @device: the device index (zero-based)
355   * @rhwdep: the pointer to store the new hwdep instance
356   *
357   * Creates a new hwdep instance with the given index on the card.
358   * The callbacks (hwdep->ops) must be set on the returned instance
359   * after this call manually by the caller.
360   *
361   * Return: Zero if successful, or a negative error code on failure.
362   */
snd_hwdep_new(struct snd_card * card,char * id,int device,struct snd_hwdep ** rhwdep)363  int snd_hwdep_new(struct snd_card *card, char *id, int device,
364  		  struct snd_hwdep **rhwdep)
365  {
366  	struct snd_hwdep *hwdep;
367  	int err;
368  	static const struct snd_device_ops ops = {
369  		.dev_free = snd_hwdep_dev_free,
370  		.dev_register = snd_hwdep_dev_register,
371  		.dev_disconnect = snd_hwdep_dev_disconnect,
372  	};
373  
374  	if (snd_BUG_ON(!card))
375  		return -ENXIO;
376  	if (rhwdep)
377  		*rhwdep = NULL;
378  	hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
379  	if (!hwdep)
380  		return -ENOMEM;
381  
382  	init_waitqueue_head(&hwdep->open_wait);
383  	mutex_init(&hwdep->open_mutex);
384  	hwdep->card = card;
385  	hwdep->device = device;
386  	if (id)
387  		strscpy(hwdep->id, id, sizeof(hwdep->id));
388  
389  	err = snd_device_alloc(&hwdep->dev, card);
390  	if (err < 0) {
391  		snd_hwdep_free(hwdep);
392  		return err;
393  	}
394  
395  	dev_set_name(hwdep->dev, "hwC%iD%i", card->number, device);
396  #ifdef CONFIG_SND_OSSEMUL
397  	hwdep->oss_type = -1;
398  #endif
399  
400  	err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
401  	if (err < 0) {
402  		snd_hwdep_free(hwdep);
403  		return err;
404  	}
405  
406  	if (rhwdep)
407  		*rhwdep = hwdep;
408  	return 0;
409  }
410  EXPORT_SYMBOL(snd_hwdep_new);
411  
snd_hwdep_dev_free(struct snd_device * device)412  static int snd_hwdep_dev_free(struct snd_device *device)
413  {
414  	snd_hwdep_free(device->device_data);
415  	return 0;
416  }
417  
snd_hwdep_dev_register(struct snd_device * device)418  static int snd_hwdep_dev_register(struct snd_device *device)
419  {
420  	struct snd_hwdep *hwdep = device->device_data;
421  	struct snd_card *card = hwdep->card;
422  	int err;
423  
424  	guard(mutex)(&register_mutex);
425  	if (snd_hwdep_search(card, hwdep->device))
426  		return -EBUSY;
427  	list_add_tail(&hwdep->list, &snd_hwdep_devices);
428  	err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
429  				  hwdep->card, hwdep->device,
430  				  &snd_hwdep_f_ops, hwdep, hwdep->dev);
431  	if (err < 0) {
432  		dev_err(hwdep->dev, "unable to register\n");
433  		list_del(&hwdep->list);
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  	return 0;
454  }
455  
snd_hwdep_dev_disconnect(struct snd_device * device)456  static int snd_hwdep_dev_disconnect(struct snd_device *device)
457  {
458  	struct snd_hwdep *hwdep = device->device_data;
459  
460  	if (snd_BUG_ON(!hwdep))
461  		return -ENXIO;
462  	guard(mutex)(&register_mutex);
463  	if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep)
464  		return -EINVAL;
465  	guard(mutex)(&hwdep->open_mutex);
466  	wake_up(&hwdep->open_wait);
467  #ifdef CONFIG_SND_OSSEMUL
468  	if (hwdep->ossreg)
469  		snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
470  #endif
471  	snd_unregister_device(hwdep->dev);
472  	list_del_init(&hwdep->list);
473  	return 0;
474  }
475  
476  #ifdef CONFIG_SND_PROC_FS
477  /*
478   *  Info interface
479   */
480  
snd_hwdep_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)481  static void snd_hwdep_proc_read(struct snd_info_entry *entry,
482  				struct snd_info_buffer *buffer)
483  {
484  	struct snd_hwdep *hwdep;
485  
486  	guard(mutex)(&register_mutex);
487  	list_for_each_entry(hwdep, &snd_hwdep_devices, list)
488  		snd_iprintf(buffer, "%02i-%02i: %s\n",
489  			    hwdep->card->number, hwdep->device, hwdep->name);
490  }
491  
492  static struct snd_info_entry *snd_hwdep_proc_entry;
493  
snd_hwdep_proc_init(void)494  static void __init snd_hwdep_proc_init(void)
495  {
496  	struct snd_info_entry *entry;
497  
498  	entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL);
499  	if (entry) {
500  		entry->c.text.read = snd_hwdep_proc_read;
501  		if (snd_info_register(entry) < 0) {
502  			snd_info_free_entry(entry);
503  			entry = NULL;
504  		}
505  	}
506  	snd_hwdep_proc_entry = entry;
507  }
508  
snd_hwdep_proc_done(void)509  static void __exit snd_hwdep_proc_done(void)
510  {
511  	snd_info_free_entry(snd_hwdep_proc_entry);
512  }
513  #else /* !CONFIG_SND_PROC_FS */
514  #define snd_hwdep_proc_init()
515  #define snd_hwdep_proc_done()
516  #endif /* CONFIG_SND_PROC_FS */
517  
518  
519  /*
520   *  ENTRY functions
521   */
522  
alsa_hwdep_init(void)523  static int __init alsa_hwdep_init(void)
524  {
525  	snd_hwdep_proc_init();
526  	snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
527  	snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
528  	return 0;
529  }
530  
alsa_hwdep_exit(void)531  static void __exit alsa_hwdep_exit(void)
532  {
533  	snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
534  	snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
535  	snd_hwdep_proc_done();
536  }
537  
538  module_init(alsa_hwdep_init)
539  module_exit(alsa_hwdep_exit)
540