Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | // SPDX-License-Identifier: GPL-2.0-or-later /* * Interface for hwdep device * * Copyright (C) 2004 Takashi Iwai <tiwai@suse.de> */ #include <sound/core.h> #include <sound/hwdep.h> #include <linux/uaccess.h> #include <linux/nospec.h> #include "emux_voice.h" #define TMP_CLIENT_ID 0x1001 /* * load patch */ static int snd_emux_hwdep_load_patch(struct snd_emux *emu, void __user *arg) { int err; struct soundfont_patch_info patch; if (copy_from_user(&patch, arg, sizeof(patch))) return -EFAULT; if (patch.key == GUS_PATCH) return snd_soundfont_load_guspatch(emu->card, emu->sflist, arg, patch.len + sizeof(patch)); if (patch.type >= SNDRV_SFNT_LOAD_INFO && patch.type <= SNDRV_SFNT_PROBE_DATA) { err = snd_soundfont_load(emu->card, emu->sflist, arg, patch.len + sizeof(patch), TMP_CLIENT_ID); if (err < 0) return err; } else { if (emu->ops.load_fx) return emu->ops.load_fx(emu, patch.type, patch.optarg, arg, patch.len + sizeof(patch)); else return -EINVAL; } return 0; } /* * set misc mode */ static int snd_emux_hwdep_misc_mode(struct snd_emux *emu, void __user *arg) { struct snd_emux_misc_mode info; int i; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; if (info.mode < 0 || info.mode >= EMUX_MD_END) return -EINVAL; info.mode = array_index_nospec(info.mode, EMUX_MD_END); if (info.port < 0) { for (i = 0; i < emu->num_ports; i++) emu->portptrs[i]->ctrls[info.mode] = info.value; } else { if (info.port < emu->num_ports) { info.port = array_index_nospec(info.port, emu->num_ports); emu->portptrs[info.port]->ctrls[info.mode] = info.value; } } return 0; } /* * ioctl */ static int snd_emux_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg) { struct snd_emux *emu = hw->private_data; switch (cmd) { case SNDRV_EMUX_IOCTL_VERSION: return put_user(SNDRV_EMUX_VERSION, (unsigned int __user *)arg); case SNDRV_EMUX_IOCTL_LOAD_PATCH: return snd_emux_hwdep_load_patch(emu, (void __user *)arg); case SNDRV_EMUX_IOCTL_RESET_SAMPLES: snd_soundfont_remove_samples(emu->sflist); break; case SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES: snd_soundfont_remove_unlocked(emu->sflist); break; case SNDRV_EMUX_IOCTL_MEM_AVAIL: if (emu->memhdr) { int size = snd_util_mem_avail(emu->memhdr); return put_user(size, (unsigned int __user *)arg); } break; case SNDRV_EMUX_IOCTL_MISC_MODE: return snd_emux_hwdep_misc_mode(emu, (void __user *)arg); } return 0; } /* * register hwdep device */ int snd_emux_init_hwdep(struct snd_emux *emu) { struct snd_hwdep *hw; int err; err = snd_hwdep_new(emu->card, SNDRV_EMUX_HWDEP_NAME, emu->hwdep_idx, &hw); if (err < 0) return err; emu->hwdep = hw; strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME); hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE; hw->ops.ioctl = snd_emux_hwdep_ioctl; /* The ioctl parameter types are compatible between 32- and * 64-bit architectures, so use the same function. */ hw->ops.ioctl_compat = snd_emux_hwdep_ioctl; hw->exclusive = 1; hw->private_data = emu; err = snd_card_register(emu->card); if (err < 0) return err; return 0; } /* * unregister */ void snd_emux_delete_hwdep(struct snd_emux *emu) { if (emu->hwdep) { snd_device_free(emu->card, emu->hwdep); emu->hwdep = NULL; } } |