xref: /openbmc/linux/sound/synth/emux/emux_hwdep.c (revision dd1fc3c5)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Interface for hwdep device
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 2004 Takashi Iwai <tiwai@suse.de>
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include <sound/core.h>
91da177e4SLinus Torvalds #include <sound/hwdep.h>
10976412fbSTakashi Iwai #include <linux/uaccess.h>
114aea96f4SGustavo A. R. Silva #include <linux/nospec.h>
121da177e4SLinus Torvalds #include "emux_voice.h"
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #define TMP_CLIENT_ID	0x1001
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds /*
171da177e4SLinus Torvalds  * load patch
181da177e4SLinus Torvalds  */
191da177e4SLinus Torvalds static int
snd_emux_hwdep_load_patch(struct snd_emux * emu,void __user * arg)2003da312aSTakashi Iwai snd_emux_hwdep_load_patch(struct snd_emux *emu, void __user *arg)
211da177e4SLinus Torvalds {
221da177e4SLinus Torvalds 	int err;
2303da312aSTakashi Iwai 	struct soundfont_patch_info patch;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	if (copy_from_user(&patch, arg, sizeof(patch)))
261da177e4SLinus Torvalds 		return -EFAULT;
271da177e4SLinus Torvalds 
28e42dd3eeSTakashi Iwai 	if (patch.key == GUS_PATCH)
29e42dd3eeSTakashi Iwai 		return snd_soundfont_load_guspatch(emu->sflist, arg,
30e42dd3eeSTakashi Iwai 						   patch.len + sizeof(patch),
31e42dd3eeSTakashi Iwai 						   TMP_CLIENT_ID);
32e42dd3eeSTakashi Iwai 
331da177e4SLinus Torvalds 	if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
341da177e4SLinus Torvalds 	    patch.type <= SNDRV_SFNT_PROBE_DATA) {
351da177e4SLinus Torvalds 		err = snd_soundfont_load(emu->sflist, arg, patch.len + sizeof(patch), TMP_CLIENT_ID);
361da177e4SLinus Torvalds 		if (err < 0)
371da177e4SLinus Torvalds 			return err;
381da177e4SLinus Torvalds 	} else {
391da177e4SLinus Torvalds 		if (emu->ops.load_fx)
401da177e4SLinus Torvalds 			return emu->ops.load_fx(emu, patch.type, patch.optarg, arg, patch.len + sizeof(patch));
411da177e4SLinus Torvalds 		else
421da177e4SLinus Torvalds 			return -EINVAL;
431da177e4SLinus Torvalds 	}
441da177e4SLinus Torvalds 	return 0;
451da177e4SLinus Torvalds }
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds /*
481da177e4SLinus Torvalds  * set misc mode
491da177e4SLinus Torvalds  */
501da177e4SLinus Torvalds static int
snd_emux_hwdep_misc_mode(struct snd_emux * emu,void __user * arg)5103da312aSTakashi Iwai snd_emux_hwdep_misc_mode(struct snd_emux *emu, void __user *arg)
521da177e4SLinus Torvalds {
5303da312aSTakashi Iwai 	struct snd_emux_misc_mode info;
541da177e4SLinus Torvalds 	int i;
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds 	if (copy_from_user(&info, arg, sizeof(info)))
571da177e4SLinus Torvalds 		return -EFAULT;
581da177e4SLinus Torvalds 	if (info.mode < 0 || info.mode >= EMUX_MD_END)
591da177e4SLinus Torvalds 		return -EINVAL;
604aea96f4SGustavo A. R. Silva 	info.mode = array_index_nospec(info.mode, EMUX_MD_END);
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	if (info.port < 0) {
631da177e4SLinus Torvalds 		for (i = 0; i < emu->num_ports; i++)
641da177e4SLinus Torvalds 			emu->portptrs[i]->ctrls[info.mode] = info.value;
651da177e4SLinus Torvalds 	} else {
664aea96f4SGustavo A. R. Silva 		if (info.port < emu->num_ports) {
674aea96f4SGustavo A. R. Silva 			info.port = array_index_nospec(info.port, emu->num_ports);
681da177e4SLinus Torvalds 			emu->portptrs[info.port]->ctrls[info.mode] = info.value;
691da177e4SLinus Torvalds 		}
704aea96f4SGustavo A. R. Silva 	}
711da177e4SLinus Torvalds 	return 0;
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds /*
761da177e4SLinus Torvalds  * ioctl
771da177e4SLinus Torvalds  */
781da177e4SLinus Torvalds static int
snd_emux_hwdep_ioctl(struct snd_hwdep * hw,struct file * file,unsigned int cmd,unsigned long arg)7903da312aSTakashi Iwai snd_emux_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
8003da312aSTakashi Iwai 		     unsigned int cmd, unsigned long arg)
811da177e4SLinus Torvalds {
8203da312aSTakashi Iwai 	struct snd_emux *emu = hw->private_data;
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds 	switch (cmd) {
851da177e4SLinus Torvalds 	case SNDRV_EMUX_IOCTL_VERSION:
861da177e4SLinus Torvalds 		return put_user(SNDRV_EMUX_VERSION, (unsigned int __user *)arg);
871da177e4SLinus Torvalds 	case SNDRV_EMUX_IOCTL_LOAD_PATCH:
881da177e4SLinus Torvalds 		return snd_emux_hwdep_load_patch(emu, (void __user *)arg);
891da177e4SLinus Torvalds 	case SNDRV_EMUX_IOCTL_RESET_SAMPLES:
901da177e4SLinus Torvalds 		snd_soundfont_remove_samples(emu->sflist);
911da177e4SLinus Torvalds 		break;
921da177e4SLinus Torvalds 	case SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES:
931da177e4SLinus Torvalds 		snd_soundfont_remove_unlocked(emu->sflist);
941da177e4SLinus Torvalds 		break;
951da177e4SLinus Torvalds 	case SNDRV_EMUX_IOCTL_MEM_AVAIL:
961da177e4SLinus Torvalds 		if (emu->memhdr) {
971da177e4SLinus Torvalds 			int size = snd_util_mem_avail(emu->memhdr);
981da177e4SLinus Torvalds 			return put_user(size, (unsigned int __user *)arg);
991da177e4SLinus Torvalds 		}
1001da177e4SLinus Torvalds 		break;
1011da177e4SLinus Torvalds 	case SNDRV_EMUX_IOCTL_MISC_MODE:
1021da177e4SLinus Torvalds 		return snd_emux_hwdep_misc_mode(emu, (void __user *)arg);
1031da177e4SLinus Torvalds 	}
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	return 0;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds /*
1101da177e4SLinus Torvalds  * register hwdep device
1111da177e4SLinus Torvalds  */
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds int
snd_emux_init_hwdep(struct snd_emux * emu)11403da312aSTakashi Iwai snd_emux_init_hwdep(struct snd_emux *emu)
1151da177e4SLinus Torvalds {
11603da312aSTakashi Iwai 	struct snd_hwdep *hw;
1171da177e4SLinus Torvalds 	int err;
1181da177e4SLinus Torvalds 
119*dd1fc3c5STakashi Iwai 	err = snd_hwdep_new(emu->card, SNDRV_EMUX_HWDEP_NAME, emu->hwdep_idx, &hw);
120*dd1fc3c5STakashi Iwai 	if (err < 0)
1211da177e4SLinus Torvalds 		return err;
1221da177e4SLinus Torvalds 	emu->hwdep = hw;
1231da177e4SLinus Torvalds 	strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME);
1241da177e4SLinus Torvalds 	hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE;
1251da177e4SLinus Torvalds 	hw->ops.ioctl = snd_emux_hwdep_ioctl;
126a254dba3SBen Hutchings 	/* The ioctl parameter types are compatible between 32- and
127a254dba3SBen Hutchings 	 * 64-bit architectures, so use the same function. */
128a254dba3SBen Hutchings 	hw->ops.ioctl_compat = snd_emux_hwdep_ioctl;
1291da177e4SLinus Torvalds 	hw->exclusive = 1;
1301da177e4SLinus Torvalds 	hw->private_data = emu;
131*dd1fc3c5STakashi Iwai 	err = snd_card_register(emu->card);
132*dd1fc3c5STakashi Iwai 	if (err < 0)
1331da177e4SLinus Torvalds 		return err;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	return 0;
1361da177e4SLinus Torvalds }
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds /*
1401da177e4SLinus Torvalds  * unregister
1411da177e4SLinus Torvalds  */
1421da177e4SLinus Torvalds void
snd_emux_delete_hwdep(struct snd_emux * emu)14303da312aSTakashi Iwai snd_emux_delete_hwdep(struct snd_emux *emu)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	if (emu->hwdep) {
1461da177e4SLinus Torvalds 		snd_device_free(emu->card, emu->hwdep);
1471da177e4SLinus Torvalds 		emu->hwdep = NULL;
1481da177e4SLinus Torvalds 	}
1491da177e4SLinus Torvalds }
150