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