1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *   32bit -> 64bit ioctl wrapper for raw MIDI API
4 *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
5 */
6
7/* This file included from rawmidi.c */
8
9#include <linux/compat.h>
10
11struct snd_rawmidi_params32 {
12	s32 stream;
13	u32 buffer_size;
14	u32 avail_min;
15	unsigned int no_active_sensing; /* avoid bit-field */
16	unsigned int mode;
17	unsigned char reserved[12];
18} __attribute__((packed));
19
20static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
21					   struct snd_rawmidi_params32 __user *src)
22{
23	struct snd_rawmidi_params params;
24	unsigned int val;
25
26	if (get_user(params.stream, &src->stream) ||
27	    get_user(params.buffer_size, &src->buffer_size) ||
28	    get_user(params.avail_min, &src->avail_min) ||
29	    get_user(params.mode, &src->mode) ||
30	    get_user(val, &src->no_active_sensing))
31		return -EFAULT;
32	params.no_active_sensing = val;
33	switch (params.stream) {
34	case SNDRV_RAWMIDI_STREAM_OUTPUT:
35		if (!rfile->output)
36			return -EINVAL;
37		return snd_rawmidi_output_params(rfile->output, &params);
38	case SNDRV_RAWMIDI_STREAM_INPUT:
39		if (!rfile->input)
40			return -EINVAL;
41		return snd_rawmidi_input_params(rfile->input, &params);
42	}
43	return -EINVAL;
44}
45
46struct compat_snd_rawmidi_status64 {
47	s32 stream;
48	u8 rsvd[4]; /* alignment */
49	s64 tstamp_sec;
50	s64 tstamp_nsec;
51	u32 avail;
52	u32 xruns;
53	unsigned char reserved[16];
54} __attribute__((packed));
55
56static int snd_rawmidi_ioctl_status_compat64(struct snd_rawmidi_file *rfile,
57					     struct compat_snd_rawmidi_status64 __user *src)
58{
59	int err;
60	struct snd_rawmidi_status64 status;
61	struct compat_snd_rawmidi_status64 compat_status;
62
63	if (get_user(status.stream, &src->stream))
64		return -EFAULT;
65
66	switch (status.stream) {
67	case SNDRV_RAWMIDI_STREAM_OUTPUT:
68		if (!rfile->output)
69			return -EINVAL;
70		err = snd_rawmidi_output_status(rfile->output, &status);
71		break;
72	case SNDRV_RAWMIDI_STREAM_INPUT:
73		if (!rfile->input)
74			return -EINVAL;
75		err = snd_rawmidi_input_status(rfile->input, &status);
76		break;
77	default:
78		return -EINVAL;
79	}
80	if (err < 0)
81		return err;
82
83	compat_status = (struct compat_snd_rawmidi_status64) {
84		.stream = status.stream,
85		.tstamp_sec = status.tstamp_sec,
86		.tstamp_nsec = status.tstamp_nsec,
87		.avail = status.avail,
88		.xruns = status.xruns,
89	};
90
91	if (copy_to_user(src, &compat_status, sizeof(*src)))
92		return -EFAULT;
93
94	return 0;
95}
96
97enum {
98	SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
99	SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
100	SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64 = _IOWR('W', 0x20, struct compat_snd_rawmidi_status64),
101};
102
103static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
104{
105	struct snd_rawmidi_file *rfile;
106	void __user *argp = compat_ptr(arg);
107
108	rfile = file->private_data;
109	switch (cmd) {
110	case SNDRV_RAWMIDI_IOCTL_PVERSION:
111	case SNDRV_RAWMIDI_IOCTL_INFO:
112	case SNDRV_RAWMIDI_IOCTL_DROP:
113	case SNDRV_RAWMIDI_IOCTL_DRAIN:
114#if IS_ENABLED(CONFIG_SND_UMP)
115	case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
116	case SNDRV_UMP_IOCTL_BLOCK_INFO:
117#endif
118		return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp);
119	case SNDRV_RAWMIDI_IOCTL_PARAMS32:
120		return snd_rawmidi_ioctl_params_compat(rfile, argp);
121	case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32:
122		return snd_rawmidi_ioctl_status32(rfile, argp);
123	case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64:
124		return snd_rawmidi_ioctl_status_compat64(rfile, argp);
125	}
126	return -ENOIOCTLCMD;
127}
128