xref: /openbmc/linux/sound/core/rawmidi.c (revision 2af5acbaa74c91266457fa1494685729d6f9e540)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Abstract layer for MIDI v1.0 stream
4c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds #include <sound/core.h>
81da177e4SLinus Torvalds #include <linux/major.h>
91da177e4SLinus Torvalds #include <linux/init.h>
10174cd4b1SIngo Molnar #include <linux/sched/signal.h>
111da177e4SLinus Torvalds #include <linux/slab.h>
121da177e4SLinus Torvalds #include <linux/time.h>
131da177e4SLinus Torvalds #include <linux/wait.h>
141a60d4c5SIngo Molnar #include <linux/mutex.h>
1565a77217SPaul Gortmaker #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/delay.h>
17ef4db239STakashi Iwai #include <linux/mm.h>
182b1d9c8fSGustavo A. R. Silva #include <linux/nospec.h>
191da177e4SLinus Torvalds #include <sound/rawmidi.h>
201da177e4SLinus Torvalds #include <sound/info.h>
211da177e4SLinus Torvalds #include <sound/control.h>
221da177e4SLinus Torvalds #include <sound/minors.h>
231da177e4SLinus Torvalds #include <sound/initval.h>
24e3a8a5b7STakashi Iwai #include <sound/ump.h>
251da177e4SLinus Torvalds 
26c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
271da177e4SLinus Torvalds MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
281da177e4SLinus Torvalds MODULE_LICENSE("GPL");
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
316581f4e7STakashi Iwai static int midi_map[SNDRV_CARDS];
321da177e4SLinus Torvalds static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
331da177e4SLinus Torvalds module_param_array(midi_map, int, NULL, 0444);
341da177e4SLinus Torvalds MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device.");
351da177e4SLinus Torvalds module_param_array(amidi_map, int, NULL, 0444);
361da177e4SLinus Torvalds MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");
371da177e4SLinus Torvalds #endif /* CONFIG_SND_OSSEMUL */
381da177e4SLinus Torvalds 
3948c9d417STakashi Iwai static int snd_rawmidi_dev_free(struct snd_device *device);
4048c9d417STakashi Iwai static int snd_rawmidi_dev_register(struct snd_device *device);
4148c9d417STakashi Iwai static int snd_rawmidi_dev_disconnect(struct snd_device *device);
421da177e4SLinus Torvalds 
43f87135f5SClemens Ladisch static LIST_HEAD(snd_rawmidi_devices);
441a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex);
451da177e4SLinus Torvalds 
46ca20d292STakashi Iwai #define rmidi_err(rmidi, fmt, args...) \
47ea29a02fSTakashi Iwai 	dev_err((rmidi)->dev, fmt, ##args)
48ca20d292STakashi Iwai #define rmidi_warn(rmidi, fmt, args...) \
49ea29a02fSTakashi Iwai 	dev_warn((rmidi)->dev, fmt, ##args)
50ca20d292STakashi Iwai #define rmidi_dbg(rmidi, fmt, args...) \
51ea29a02fSTakashi Iwai 	dev_dbg((rmidi)->dev, fmt, ##args)
52ca20d292STakashi Iwai 
53d9e5582cSBaolin Wang struct snd_rawmidi_status32 {
54d9e5582cSBaolin Wang 	s32 stream;
55d9e5582cSBaolin Wang 	s32 tstamp_sec;			/* Timestamp */
56d9e5582cSBaolin Wang 	s32 tstamp_nsec;
57d9e5582cSBaolin Wang 	u32 avail;			/* available bytes */
58d9e5582cSBaolin Wang 	u32 xruns;			/* count of overruns since last status (in bytes) */
59d9e5582cSBaolin Wang 	unsigned char reserved[16];	/* reserved for future use */
60d9e5582cSBaolin Wang };
61d9e5582cSBaolin Wang 
62d9e5582cSBaolin Wang #define SNDRV_RAWMIDI_IOCTL_STATUS32	_IOWR('W', 0x20, struct snd_rawmidi_status32)
63d9e5582cSBaolin Wang 
64d9e5582cSBaolin Wang struct snd_rawmidi_status64 {
65d9e5582cSBaolin Wang 	int stream;
66d9e5582cSBaolin Wang 	u8 rsvd[4];			/* alignment */
67d9e5582cSBaolin Wang 	s64 tstamp_sec;			/* Timestamp */
68d9e5582cSBaolin Wang 	s64 tstamp_nsec;
69d9e5582cSBaolin Wang 	size_t avail;			/* available bytes */
70d9e5582cSBaolin Wang 	size_t xruns;			/* count of overruns since last status (in bytes) */
71d9e5582cSBaolin Wang 	unsigned char reserved[16];	/* reserved for future use */
72d9e5582cSBaolin Wang };
73d9e5582cSBaolin Wang 
74d9e5582cSBaolin Wang #define SNDRV_RAWMIDI_IOCTL_STATUS64	_IOWR('W', 0x20, struct snd_rawmidi_status64)
75d9e5582cSBaolin Wang 
76e3a8a5b7STakashi Iwai #define rawmidi_is_ump(rmidi) \
77e3a8a5b7STakashi Iwai 	(IS_ENABLED(CONFIG_SND_UMP) && ((rmidi)->info_flags & SNDRV_RAWMIDI_INFO_UMP))
78e3a8a5b7STakashi Iwai 
snd_rawmidi_search(struct snd_card * card,int device)79f87135f5SClemens Ladisch static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
80f87135f5SClemens Ladisch {
81f87135f5SClemens Ladisch 	struct snd_rawmidi *rawmidi;
82f87135f5SClemens Ladisch 
839244b2c3SJohannes Berg 	list_for_each_entry(rawmidi, &snd_rawmidi_devices, list)
84f87135f5SClemens Ladisch 		if (rawmidi->card == card && rawmidi->device == device)
85f87135f5SClemens Ladisch 			return rawmidi;
86f87135f5SClemens Ladisch 	return NULL;
87f87135f5SClemens Ladisch }
88f87135f5SClemens Ladisch 
snd_rawmidi_file_flags(struct file * file)891da177e4SLinus Torvalds static inline unsigned short snd_rawmidi_file_flags(struct file *file)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds 	switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
921da177e4SLinus Torvalds 	case FMODE_WRITE:
931da177e4SLinus Torvalds 		return SNDRV_RAWMIDI_LFLG_OUTPUT;
941da177e4SLinus Torvalds 	case FMODE_READ:
951da177e4SLinus Torvalds 		return SNDRV_RAWMIDI_LFLG_INPUT;
961da177e4SLinus Torvalds 	default:
971da177e4SLinus Torvalds 		return SNDRV_RAWMIDI_LFLG_OPEN;
981da177e4SLinus Torvalds 	}
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds 
__snd_rawmidi_ready(struct snd_rawmidi_runtime * runtime)10188a06d6fSTakashi Iwai static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime)
10288a06d6fSTakashi Iwai {
10388a06d6fSTakashi Iwai 	return runtime->avail >= runtime->avail_min;
10488a06d6fSTakashi Iwai }
10588a06d6fSTakashi Iwai 
snd_rawmidi_ready(struct snd_rawmidi_substream * substream)10688a06d6fSTakashi Iwai static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
1071da177e4SLinus Torvalds {
10888a06d6fSTakashi Iwai 	unsigned long flags;
10988a06d6fSTakashi Iwai 	bool ready;
1105bed9139STakashi Iwai 
111f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
112f1d40433STakashi Iwai 	ready = __snd_rawmidi_ready(substream->runtime);
113f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
11488a06d6fSTakashi Iwai 	return ready;
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds 
snd_rawmidi_ready_append(struct snd_rawmidi_substream * substream,size_t count)11748c9d417STakashi Iwai static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream,
11848c9d417STakashi Iwai 					   size_t count)
1191da177e4SLinus Torvalds {
12048c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
1215bed9139STakashi Iwai 
1221da177e4SLinus Torvalds 	return runtime->avail >= runtime->avail_min &&
1231da177e4SLinus Torvalds 	       (!substream->append || runtime->avail >= count);
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds 
snd_rawmidi_input_event_work(struct work_struct * work)126b3c705aaSTakashi Iwai static void snd_rawmidi_input_event_work(struct work_struct *work)
1271da177e4SLinus Torvalds {
128b3c705aaSTakashi Iwai 	struct snd_rawmidi_runtime *runtime =
129b3c705aaSTakashi Iwai 		container_of(work, struct snd_rawmidi_runtime, event_work);
1305bed9139STakashi Iwai 
131b3c705aaSTakashi Iwai 	if (runtime->event)
132b3c705aaSTakashi Iwai 		runtime->event(runtime->substream);
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
135f1d40433STakashi Iwai /* buffer refcount management: call with substream->lock held */
snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime * runtime)136c1f6e3c8STakashi Iwai static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime)
137c1f6e3c8STakashi Iwai {
138c1f6e3c8STakashi Iwai 	runtime->buffer_ref++;
139c1f6e3c8STakashi Iwai }
140c1f6e3c8STakashi Iwai 
snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime * runtime)141c1f6e3c8STakashi Iwai static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime)
142c1f6e3c8STakashi Iwai {
143c1f6e3c8STakashi Iwai 	runtime->buffer_ref--;
144c1f6e3c8STakashi Iwai }
145c1f6e3c8STakashi Iwai 
snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream * substream)1463809db64STakashi Iwai static void snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream *substream)
1473809db64STakashi Iwai {
1483809db64STakashi Iwai 	int loop = HZ;
1493809db64STakashi Iwai 
1503809db64STakashi Iwai 	spin_lock_irq(&substream->lock);
1513809db64STakashi Iwai 	while (substream->runtime->buffer_ref) {
1523809db64STakashi Iwai 		spin_unlock_irq(&substream->lock);
1533809db64STakashi Iwai 		if (!--loop) {
1543809db64STakashi Iwai 			rmidi_err(substream->rmidi, "Buffer ref sync timeout\n");
1553809db64STakashi Iwai 			return;
1563809db64STakashi Iwai 		}
1573809db64STakashi Iwai 		schedule_timeout_uninterruptible(1);
1583809db64STakashi Iwai 		spin_lock_irq(&substream->lock);
1593809db64STakashi Iwai 	}
1603809db64STakashi Iwai 	spin_unlock_irq(&substream->lock);
1613809db64STakashi Iwai }
1623809db64STakashi Iwai 
snd_rawmidi_runtime_create(struct snd_rawmidi_substream * substream)16348c9d417STakashi Iwai static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
1641da177e4SLinus Torvalds {
16548c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime;
1661da177e4SLinus Torvalds 
1675bed9139STakashi Iwai 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
1685bed9139STakashi Iwai 	if (!runtime)
1691da177e4SLinus Torvalds 		return -ENOMEM;
170b3c705aaSTakashi Iwai 	runtime->substream = substream;
1711da177e4SLinus Torvalds 	init_waitqueue_head(&runtime->sleep);
172b3c705aaSTakashi Iwai 	INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
1731da177e4SLinus Torvalds 	runtime->event = NULL;
1741da177e4SLinus Torvalds 	runtime->buffer_size = PAGE_SIZE;
1751da177e4SLinus Torvalds 	runtime->avail_min = 1;
1761da177e4SLinus Torvalds 	if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
1771da177e4SLinus Torvalds 		runtime->avail = 0;
1781da177e4SLinus Torvalds 	else
1791da177e4SLinus Torvalds 		runtime->avail = runtime->buffer_size;
1805a7b44a8STakashi Iwai 	runtime->buffer = kvzalloc(runtime->buffer_size, GFP_KERNEL);
1815bed9139STakashi Iwai 	if (!runtime->buffer) {
1821da177e4SLinus Torvalds 		kfree(runtime);
1831da177e4SLinus Torvalds 		return -ENOMEM;
1841da177e4SLinus Torvalds 	}
1851da177e4SLinus Torvalds 	runtime->appl_ptr = runtime->hw_ptr = 0;
1861da177e4SLinus Torvalds 	substream->runtime = runtime;
187e3a8a5b7STakashi Iwai 	if (rawmidi_is_ump(substream->rmidi))
188e3a8a5b7STakashi Iwai 		runtime->align = 3;
1891da177e4SLinus Torvalds 	return 0;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
192e3a8a5b7STakashi Iwai /* get the current alignment (either 0 or 3) */
get_align(struct snd_rawmidi_runtime * runtime)193e3a8a5b7STakashi Iwai static inline int get_align(struct snd_rawmidi_runtime *runtime)
194e3a8a5b7STakashi Iwai {
195e3a8a5b7STakashi Iwai 	if (IS_ENABLED(CONFIG_SND_UMP))
196e3a8a5b7STakashi Iwai 		return runtime->align;
197e3a8a5b7STakashi Iwai 	else
198e3a8a5b7STakashi Iwai 		return 0;
199e3a8a5b7STakashi Iwai }
200e3a8a5b7STakashi Iwai 
201e3a8a5b7STakashi Iwai /* get the trimmed size with the current alignment */
202e3a8a5b7STakashi Iwai #define get_aligned_size(runtime, size) ((size) & ~get_align(runtime))
203e3a8a5b7STakashi Iwai 
snd_rawmidi_runtime_free(struct snd_rawmidi_substream * substream)20448c9d417STakashi Iwai static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)
2051da177e4SLinus Torvalds {
20648c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
2071da177e4SLinus Torvalds 
208ef4db239STakashi Iwai 	kvfree(runtime->buffer);
2091da177e4SLinus Torvalds 	kfree(runtime);
2101da177e4SLinus Torvalds 	substream->runtime = NULL;
2111da177e4SLinus Torvalds 	return 0;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
snd_rawmidi_output_trigger(struct snd_rawmidi_substream * substream,int up)21448c9d417STakashi Iwai static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
2151da177e4SLinus Torvalds {
216219df32fSTakashi Iwai 	if (!substream->opened)
217219df32fSTakashi Iwai 		return;
218b3c705aaSTakashi Iwai 	substream->ops->trigger(substream, up);
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
snd_rawmidi_input_trigger(struct snd_rawmidi_substream * substream,int up)22148c9d417STakashi Iwai static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
2221da177e4SLinus Torvalds {
223219df32fSTakashi Iwai 	if (!substream->opened)
224219df32fSTakashi Iwai 		return;
2251da177e4SLinus Torvalds 	substream->ops->trigger(substream, up);
226b3c705aaSTakashi Iwai 	if (!up)
227b3c705aaSTakashi Iwai 		cancel_work_sync(&substream->runtime->event_work);
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
__reset_runtime_ptrs(struct snd_rawmidi_runtime * runtime,bool is_input)230f5beb598STakashi Iwai static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime,
231f5beb598STakashi Iwai 				 bool is_input)
232f5beb598STakashi Iwai {
233f5beb598STakashi Iwai 	runtime->drain = 0;
234f5beb598STakashi Iwai 	runtime->appl_ptr = runtime->hw_ptr = 0;
235f5beb598STakashi Iwai 	runtime->avail = is_input ? 0 : runtime->buffer_size;
236f5beb598STakashi Iwai }
237f5beb598STakashi Iwai 
reset_runtime_ptrs(struct snd_rawmidi_substream * substream,bool is_input)238f1d40433STakashi Iwai static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream,
239f5beb598STakashi Iwai 			       bool is_input)
2401da177e4SLinus Torvalds {
2411da177e4SLinus Torvalds 	unsigned long flags;
2421da177e4SLinus Torvalds 
243f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
244463a20fdSTakashi Iwai 	if (substream->opened && substream->runtime)
245f1d40433STakashi Iwai 		__reset_runtime_ptrs(substream->runtime, is_input);
246f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
247f5beb598STakashi Iwai }
248f5beb598STakashi Iwai 
snd_rawmidi_drop_output(struct snd_rawmidi_substream * substream)249f5beb598STakashi Iwai int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
250f5beb598STakashi Iwai {
251f5beb598STakashi Iwai 	snd_rawmidi_output_trigger(substream, 0);
252f1d40433STakashi Iwai 	reset_runtime_ptrs(substream, false);
2531da177e4SLinus Torvalds 	return 0;
2541da177e4SLinus Torvalds }
2556776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_drop_output);
2561da177e4SLinus Torvalds 
snd_rawmidi_drain_output(struct snd_rawmidi_substream * substream)25748c9d417STakashi Iwai int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
2581da177e4SLinus Torvalds {
2593809db64STakashi Iwai 	int err = 0;
2601da177e4SLinus Torvalds 	long timeout;
2613809db64STakashi Iwai 	struct snd_rawmidi_runtime *runtime;
2621da177e4SLinus Torvalds 
2633809db64STakashi Iwai 	spin_lock_irq(&substream->lock);
2643809db64STakashi Iwai 	runtime = substream->runtime;
2653809db64STakashi Iwai 	if (!substream->opened || !runtime || !runtime->buffer) {
2663809db64STakashi Iwai 		err = -EINVAL;
2673809db64STakashi Iwai 	} else {
2683809db64STakashi Iwai 		snd_rawmidi_buffer_ref(runtime);
2691da177e4SLinus Torvalds 		runtime->drain = 1;
2703809db64STakashi Iwai 	}
2713809db64STakashi Iwai 	spin_unlock_irq(&substream->lock);
2723809db64STakashi Iwai 	if (err < 0)
2733809db64STakashi Iwai 		return err;
2743809db64STakashi Iwai 
2751da177e4SLinus Torvalds 	timeout = wait_event_interruptible_timeout(runtime->sleep,
2761da177e4SLinus Torvalds 				(runtime->avail >= runtime->buffer_size),
2771da177e4SLinus Torvalds 				10*HZ);
2783809db64STakashi Iwai 
2793809db64STakashi Iwai 	spin_lock_irq(&substream->lock);
2801da177e4SLinus Torvalds 	if (signal_pending(current))
2811da177e4SLinus Torvalds 		err = -ERESTARTSYS;
2821da177e4SLinus Torvalds 	if (runtime->avail < runtime->buffer_size && !timeout) {
283ca20d292STakashi Iwai 		rmidi_warn(substream->rmidi,
284ca20d292STakashi Iwai 			   "rawmidi drain error (avail = %li, buffer_size = %li)\n",
285ca20d292STakashi Iwai 			   (long)runtime->avail, (long)runtime->buffer_size);
2861da177e4SLinus Torvalds 		err = -EIO;
2871da177e4SLinus Torvalds 	}
2881da177e4SLinus Torvalds 	runtime->drain = 0;
2893809db64STakashi Iwai 	spin_unlock_irq(&substream->lock);
2903809db64STakashi Iwai 
2911da177e4SLinus Torvalds 	if (err != -ERESTARTSYS) {
2921da177e4SLinus Torvalds 		/* we need wait a while to make sure that Tx FIFOs are empty */
2931da177e4SLinus Torvalds 		if (substream->ops->drain)
2941da177e4SLinus Torvalds 			substream->ops->drain(substream);
2951da177e4SLinus Torvalds 		else
2961da177e4SLinus Torvalds 			msleep(50);
2971da177e4SLinus Torvalds 		snd_rawmidi_drop_output(substream);
2981da177e4SLinus Torvalds 	}
2993809db64STakashi Iwai 
3003809db64STakashi Iwai 	spin_lock_irq(&substream->lock);
3013809db64STakashi Iwai 	snd_rawmidi_buffer_unref(runtime);
3023809db64STakashi Iwai 	spin_unlock_irq(&substream->lock);
3033809db64STakashi Iwai 
3041da177e4SLinus Torvalds 	return err;
3051da177e4SLinus Torvalds }
3066776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_drain_output);
3071da177e4SLinus Torvalds 
snd_rawmidi_drain_input(struct snd_rawmidi_substream * substream)30848c9d417STakashi Iwai int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds 	snd_rawmidi_input_trigger(substream, 0);
311f1d40433STakashi Iwai 	reset_runtime_ptrs(substream, true);
3121da177e4SLinus Torvalds 	return 0;
3131da177e4SLinus Torvalds }
3146776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_drain_input);
3151da177e4SLinus Torvalds 
3169a1b64caSTakashi Iwai /* look for an available substream for the given stream direction;
3179a1b64caSTakashi Iwai  * if a specific subdevice is given, try to assign it
3189a1b64caSTakashi Iwai  */
assign_substream(struct snd_rawmidi * rmidi,int subdevice,int stream,int mode,struct snd_rawmidi_substream ** sub_ret)3199a1b64caSTakashi Iwai static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
3209a1b64caSTakashi Iwai 			    int stream, int mode,
3219a1b64caSTakashi Iwai 			    struct snd_rawmidi_substream **sub_ret)
3229a1b64caSTakashi Iwai {
3239a1b64caSTakashi Iwai 	struct snd_rawmidi_substream *substream;
3249a1b64caSTakashi Iwai 	struct snd_rawmidi_str *s = &rmidi->streams[stream];
325edb87ed5STakashi Iwai 	static const unsigned int info_flags[2] = {
3269a1b64caSTakashi Iwai 		[SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
3279a1b64caSTakashi Iwai 		[SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
3289a1b64caSTakashi Iwai 	};
3299a1b64caSTakashi Iwai 
3309a1b64caSTakashi Iwai 	if (!(rmidi->info_flags & info_flags[stream]))
3319a1b64caSTakashi Iwai 		return -ENXIO;
3329a1b64caSTakashi Iwai 	if (subdevice >= 0 && subdevice >= s->substream_count)
3339a1b64caSTakashi Iwai 		return -ENODEV;
3349a1b64caSTakashi Iwai 
3359a1b64caSTakashi Iwai 	list_for_each_entry(substream, &s->substreams, list) {
3369a1b64caSTakashi Iwai 		if (substream->opened) {
3379a1b64caSTakashi Iwai 			if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
33816fb1096SClemens Ladisch 			    !(mode & SNDRV_RAWMIDI_LFLG_APPEND) ||
33916fb1096SClemens Ladisch 			    !substream->append)
3409a1b64caSTakashi Iwai 				continue;
3419a1b64caSTakashi Iwai 		}
3429a1b64caSTakashi Iwai 		if (subdevice < 0 || subdevice == substream->number) {
3439a1b64caSTakashi Iwai 			*sub_ret = substream;
3449a1b64caSTakashi Iwai 			return 0;
3459a1b64caSTakashi Iwai 		}
3469a1b64caSTakashi Iwai 	}
3479a1b64caSTakashi Iwai 	return -EAGAIN;
3489a1b64caSTakashi Iwai }
3499a1b64caSTakashi Iwai 
3509a1b64caSTakashi Iwai /* open and do ref-counting for the given substream */
open_substream(struct snd_rawmidi * rmidi,struct snd_rawmidi_substream * substream,int mode)3519a1b64caSTakashi Iwai static int open_substream(struct snd_rawmidi *rmidi,
3529a1b64caSTakashi Iwai 			  struct snd_rawmidi_substream *substream,
3539a1b64caSTakashi Iwai 			  int mode)
3549a1b64caSTakashi Iwai {
3559a1b64caSTakashi Iwai 	int err;
3569a1b64caSTakashi Iwai 
3578579d2d7SClemens Ladisch 	if (substream->use_count == 0) {
3589a1b64caSTakashi Iwai 		err = snd_rawmidi_runtime_create(substream);
3599a1b64caSTakashi Iwai 		if (err < 0)
3609a1b64caSTakashi Iwai 			return err;
3619a1b64caSTakashi Iwai 		err = substream->ops->open(substream);
362b7fe750fSClemens Ladisch 		if (err < 0) {
363b7fe750fSClemens Ladisch 			snd_rawmidi_runtime_free(substream);
3649a1b64caSTakashi Iwai 			return err;
365b7fe750fSClemens Ladisch 		}
366463a20fdSTakashi Iwai 		spin_lock_irq(&substream->lock);
3679a1b64caSTakashi Iwai 		substream->opened = 1;
3682d4b8420SClemens Ladisch 		substream->active_sensing = 0;
3699a1b64caSTakashi Iwai 		if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
3709a1b64caSTakashi Iwai 			substream->append = 1;
3717584af10SClemens Ladisch 		substream->pid = get_pid(task_pid(current));
37291d12c48SClemens Ladisch 		rmidi->streams[substream->stream].substream_opened++;
373463a20fdSTakashi Iwai 		spin_unlock_irq(&substream->lock);
3748579d2d7SClemens Ladisch 	}
3758579d2d7SClemens Ladisch 	substream->use_count++;
3769a1b64caSTakashi Iwai 	return 0;
3779a1b64caSTakashi Iwai }
3789a1b64caSTakashi Iwai 
3799a1b64caSTakashi Iwai static void close_substream(struct snd_rawmidi *rmidi,
3809a1b64caSTakashi Iwai 			    struct snd_rawmidi_substream *substream,
3819a1b64caSTakashi Iwai 			    int cleanup);
3829a1b64caSTakashi Iwai 
rawmidi_open_priv(struct snd_rawmidi * rmidi,int subdevice,int mode,struct snd_rawmidi_file * rfile)3839a1b64caSTakashi Iwai static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
3849a1b64caSTakashi Iwai 			     struct snd_rawmidi_file *rfile)
3859a1b64caSTakashi Iwai {
3869a1b64caSTakashi Iwai 	struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
3879a1b64caSTakashi Iwai 	int err;
3889a1b64caSTakashi Iwai 
3899a1b64caSTakashi Iwai 	rfile->input = rfile->output = NULL;
3909a1b64caSTakashi Iwai 	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
3919a1b64caSTakashi Iwai 		err = assign_substream(rmidi, subdevice,
3929a1b64caSTakashi Iwai 				       SNDRV_RAWMIDI_STREAM_INPUT,
3939a1b64caSTakashi Iwai 				       mode, &sinput);
3949a1b64caSTakashi Iwai 		if (err < 0)
395b7fe750fSClemens Ladisch 			return err;
3969a1b64caSTakashi Iwai 	}
3979a1b64caSTakashi Iwai 	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
3989a1b64caSTakashi Iwai 		err = assign_substream(rmidi, subdevice,
3999a1b64caSTakashi Iwai 				       SNDRV_RAWMIDI_STREAM_OUTPUT,
4009a1b64caSTakashi Iwai 				       mode, &soutput);
4019a1b64caSTakashi Iwai 		if (err < 0)
402b7fe750fSClemens Ladisch 			return err;
4039a1b64caSTakashi Iwai 	}
4049a1b64caSTakashi Iwai 
4059a1b64caSTakashi Iwai 	if (sinput) {
4069a1b64caSTakashi Iwai 		err = open_substream(rmidi, sinput, mode);
4079a1b64caSTakashi Iwai 		if (err < 0)
408b7fe750fSClemens Ladisch 			return err;
4099a1b64caSTakashi Iwai 	}
4109a1b64caSTakashi Iwai 	if (soutput) {
4119a1b64caSTakashi Iwai 		err = open_substream(rmidi, soutput, mode);
4129a1b64caSTakashi Iwai 		if (err < 0) {
4139a1b64caSTakashi Iwai 			if (sinput)
4149a1b64caSTakashi Iwai 				close_substream(rmidi, sinput, 0);
415b7fe750fSClemens Ladisch 			return err;
4169a1b64caSTakashi Iwai 		}
4179a1b64caSTakashi Iwai 	}
4189a1b64caSTakashi Iwai 
4199a1b64caSTakashi Iwai 	rfile->rmidi = rmidi;
4209a1b64caSTakashi Iwai 	rfile->input = sinput;
4219a1b64caSTakashi Iwai 	rfile->output = soutput;
4229a1b64caSTakashi Iwai 	return 0;
4239a1b64caSTakashi Iwai }
4249a1b64caSTakashi Iwai 
4259a1b64caSTakashi Iwai /* called from sound/core/seq/seq_midi.c */
snd_rawmidi_kernel_open(struct snd_rawmidi * rmidi,int subdevice,int mode,struct snd_rawmidi_file * rfile)42609b62892STakashi Iwai int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice,
42748c9d417STakashi Iwai 			    int mode, struct snd_rawmidi_file *rfile)
4281da177e4SLinus Torvalds {
42909b62892STakashi Iwai 	int err;
4301da177e4SLinus Torvalds 
4319a1b64caSTakashi Iwai 	if (snd_BUG_ON(!rfile))
4329a1b64caSTakashi Iwai 		return -EINVAL;
43309b62892STakashi Iwai 	if (!try_module_get(rmidi->card->module))
43409b62892STakashi Iwai 		return -ENXIO;
435f9d20283STakashi Iwai 
4361a60d4c5SIngo Molnar 	mutex_lock(&rmidi->open_mutex);
4379a1b64caSTakashi Iwai 	err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
4381a60d4c5SIngo Molnar 	mutex_unlock(&rmidi->open_mutex);
4399a1b64caSTakashi Iwai 	if (err < 0)
440f9d20283STakashi Iwai 		module_put(rmidi->card->module);
4411da177e4SLinus Torvalds 	return err;
4421da177e4SLinus Torvalds }
4436776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_kernel_open);
4441da177e4SLinus Torvalds 
snd_rawmidi_open(struct inode * inode,struct file * file)4451da177e4SLinus Torvalds static int snd_rawmidi_open(struct inode *inode, struct file *file)
4461da177e4SLinus Torvalds {
4471da177e4SLinus Torvalds 	int maj = imajor(inode);
44848c9d417STakashi Iwai 	struct snd_card *card;
449f87135f5SClemens Ladisch 	int subdevice;
4501da177e4SLinus Torvalds 	unsigned short fflags;
4511da177e4SLinus Torvalds 	int err;
45248c9d417STakashi Iwai 	struct snd_rawmidi *rmidi;
4539a1b64caSTakashi Iwai 	struct snd_rawmidi_file *rawmidi_file = NULL;
454ac6424b9SIngo Molnar 	wait_queue_entry_t wait;
4551da177e4SLinus Torvalds 
4569a1b64caSTakashi Iwai 	if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
4579a1b64caSTakashi Iwai 		return -EINVAL;		/* invalid combination */
4589a1b64caSTakashi Iwai 
459c5bf68feSKirill Smelkov 	err = stream_open(inode, file);
46002f4865fSTakashi Iwai 	if (err < 0)
46102f4865fSTakashi Iwai 		return err;
46202f4865fSTakashi Iwai 
463f1902860SClemens Ladisch 	if (maj == snd_major) {
464f87135f5SClemens Ladisch 		rmidi = snd_lookup_minor_data(iminor(inode),
465f87135f5SClemens Ladisch 					      SNDRV_DEVICE_TYPE_RAWMIDI);
4661da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
467f1902860SClemens Ladisch 	} else if (maj == SOUND_MAJOR) {
468f87135f5SClemens Ladisch 		rmidi = snd_lookup_oss_minor_data(iminor(inode),
469f87135f5SClemens Ladisch 						  SNDRV_OSS_DEVICE_TYPE_MIDI);
4701da177e4SLinus Torvalds #endif
471f1902860SClemens Ladisch 	} else
4721da177e4SLinus Torvalds 		return -ENXIO;
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	if (rmidi == NULL)
4751da177e4SLinus Torvalds 		return -ENODEV;
4769a1b64caSTakashi Iwai 
477a0830dbdSTakashi Iwai 	if (!try_module_get(rmidi->card->module)) {
478a0830dbdSTakashi Iwai 		snd_card_unref(rmidi->card);
4799a1b64caSTakashi Iwai 		return -ENXIO;
480a0830dbdSTakashi Iwai 	}
4819a1b64caSTakashi Iwai 
4829a1b64caSTakashi Iwai 	mutex_lock(&rmidi->open_mutex);
4831da177e4SLinus Torvalds 	card = rmidi->card;
4841da177e4SLinus Torvalds 	err = snd_card_file_add(card, file);
4851da177e4SLinus Torvalds 	if (err < 0)
4869a1b64caSTakashi Iwai 		goto __error_card;
4871da177e4SLinus Torvalds 	fflags = snd_rawmidi_file_flags(file);
488f1902860SClemens Ladisch 	if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */
4891da177e4SLinus Torvalds 		fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
4901da177e4SLinus Torvalds 	rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
4911da177e4SLinus Torvalds 	if (rawmidi_file == NULL) {
4929a1b64caSTakashi Iwai 		err = -ENOMEM;
4939a1b64caSTakashi Iwai 		goto __error;
4941da177e4SLinus Torvalds 	}
49539a8fc49SJaroslav Kysela 	rawmidi_file->user_pversion = 0;
4961da177e4SLinus Torvalds 	init_waitqueue_entry(&wait, current);
4971da177e4SLinus Torvalds 	add_wait_queue(&rmidi->open_wait, &wait);
4981da177e4SLinus Torvalds 	while (1) {
49923c18d4bSTakashi Iwai 		subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_RAWMIDI);
5009a1b64caSTakashi Iwai 		err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
5011da177e4SLinus Torvalds 		if (err >= 0)
5021da177e4SLinus Torvalds 			break;
5031da177e4SLinus Torvalds 		if (err == -EAGAIN) {
5041da177e4SLinus Torvalds 			if (file->f_flags & O_NONBLOCK) {
5051da177e4SLinus Torvalds 				err = -EBUSY;
5061da177e4SLinus Torvalds 				break;
5071da177e4SLinus Torvalds 			}
5081da177e4SLinus Torvalds 		} else
5091da177e4SLinus Torvalds 			break;
5101da177e4SLinus Torvalds 		set_current_state(TASK_INTERRUPTIBLE);
5111a60d4c5SIngo Molnar 		mutex_unlock(&rmidi->open_mutex);
5121da177e4SLinus Torvalds 		schedule();
5131a60d4c5SIngo Molnar 		mutex_lock(&rmidi->open_mutex);
5140914f796STakashi Iwai 		if (rmidi->card->shutdown) {
5150914f796STakashi Iwai 			err = -ENODEV;
5160914f796STakashi Iwai 			break;
5170914f796STakashi Iwai 		}
5181da177e4SLinus Torvalds 		if (signal_pending(current)) {
5191da177e4SLinus Torvalds 			err = -ERESTARTSYS;
5201da177e4SLinus Torvalds 			break;
5211da177e4SLinus Torvalds 		}
5221da177e4SLinus Torvalds 	}
5239a1b64caSTakashi Iwai 	remove_wait_queue(&rmidi->open_wait, &wait);
5249a1b64caSTakashi Iwai 	if (err < 0) {
5259a1b64caSTakashi Iwai 		kfree(rawmidi_file);
5269a1b64caSTakashi Iwai 		goto __error;
5279a1b64caSTakashi Iwai 	}
5281da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
5291da177e4SLinus Torvalds 	if (rawmidi_file->input && rawmidi_file->input->runtime)
5301da177e4SLinus Torvalds 		rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
5311da177e4SLinus Torvalds 	if (rawmidi_file->output && rawmidi_file->output->runtime)
5321da177e4SLinus Torvalds 		rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
5331da177e4SLinus Torvalds #endif
5341da177e4SLinus Torvalds 	file->private_data = rawmidi_file;
5351a60d4c5SIngo Molnar 	mutex_unlock(&rmidi->open_mutex);
536a0830dbdSTakashi Iwai 	snd_card_unref(rmidi->card);
5379a1b64caSTakashi Iwai 	return 0;
5389a1b64caSTakashi Iwai 
5399a1b64caSTakashi Iwai  __error:
5409a1b64caSTakashi Iwai 	snd_card_file_remove(card, file);
5419a1b64caSTakashi Iwai  __error_card:
5429a1b64caSTakashi Iwai 	mutex_unlock(&rmidi->open_mutex);
5439a1b64caSTakashi Iwai 	module_put(rmidi->card->module);
544a0830dbdSTakashi Iwai 	snd_card_unref(rmidi->card);
5451da177e4SLinus Torvalds 	return err;
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds 
close_substream(struct snd_rawmidi * rmidi,struct snd_rawmidi_substream * substream,int cleanup)5489a1b64caSTakashi Iwai static void close_substream(struct snd_rawmidi *rmidi,
5499a1b64caSTakashi Iwai 			    struct snd_rawmidi_substream *substream,
5509a1b64caSTakashi Iwai 			    int cleanup)
5511da177e4SLinus Torvalds {
5529a1b64caSTakashi Iwai 	if (--substream->use_count)
5539a1b64caSTakashi Iwai 		return;
5541da177e4SLinus Torvalds 
5559a1b64caSTakashi Iwai 	if (cleanup) {
5569a1b64caSTakashi Iwai 		if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
5571da177e4SLinus Torvalds 			snd_rawmidi_input_trigger(substream, 0);
5589a1b64caSTakashi Iwai 		else {
5591da177e4SLinus Torvalds 			if (substream->active_sensing) {
5601da177e4SLinus Torvalds 				unsigned char buf = 0xfe;
5619a1b64caSTakashi Iwai 				/* sending single active sensing message
5629a1b64caSTakashi Iwai 				 * to shut the device up
5639a1b64caSTakashi Iwai 				 */
5641da177e4SLinus Torvalds 				snd_rawmidi_kernel_write(substream, &buf, 1);
5651da177e4SLinus Torvalds 			}
5661da177e4SLinus Torvalds 			if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
5671da177e4SLinus Torvalds 				snd_rawmidi_output_trigger(substream, 0);
5689a1b64caSTakashi Iwai 		}
5693809db64STakashi Iwai 		snd_rawmidi_buffer_ref_sync(substream);
5709a1b64caSTakashi Iwai 	}
571463a20fdSTakashi Iwai 	spin_lock_irq(&substream->lock);
572463a20fdSTakashi Iwai 	substream->opened = 0;
573463a20fdSTakashi Iwai 	substream->append = 0;
574463a20fdSTakashi Iwai 	spin_unlock_irq(&substream->lock);
5751da177e4SLinus Torvalds 	substream->ops->close(substream);
5769a1b64caSTakashi Iwai 	if (substream->runtime->private_free)
5779a1b64caSTakashi Iwai 		substream->runtime->private_free(substream);
5781da177e4SLinus Torvalds 	snd_rawmidi_runtime_free(substream);
5797584af10SClemens Ladisch 	put_pid(substream->pid);
5807584af10SClemens Ladisch 	substream->pid = NULL;
58191d12c48SClemens Ladisch 	rmidi->streams[substream->stream].substream_opened--;
5821da177e4SLinus Torvalds }
5839a1b64caSTakashi Iwai 
rawmidi_release_priv(struct snd_rawmidi_file * rfile)5849a1b64caSTakashi Iwai static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
5859a1b64caSTakashi Iwai {
5869a1b64caSTakashi Iwai 	struct snd_rawmidi *rmidi;
5879a1b64caSTakashi Iwai 
5889a1b64caSTakashi Iwai 	rmidi = rfile->rmidi;
5899a1b64caSTakashi Iwai 	mutex_lock(&rmidi->open_mutex);
5909a1b64caSTakashi Iwai 	if (rfile->input) {
5919a1b64caSTakashi Iwai 		close_substream(rmidi, rfile->input, 1);
5929a1b64caSTakashi Iwai 		rfile->input = NULL;
5931da177e4SLinus Torvalds 	}
5949a1b64caSTakashi Iwai 	if (rfile->output) {
5959a1b64caSTakashi Iwai 		close_substream(rmidi, rfile->output, 1);
5969a1b64caSTakashi Iwai 		rfile->output = NULL;
5979a1b64caSTakashi Iwai 	}
5989a1b64caSTakashi Iwai 	rfile->rmidi = NULL;
5991a60d4c5SIngo Molnar 	mutex_unlock(&rmidi->open_mutex);
6009a1b64caSTakashi Iwai 	wake_up(&rmidi->open_wait);
6019a1b64caSTakashi Iwai }
6029a1b64caSTakashi Iwai 
6039a1b64caSTakashi Iwai /* called from sound/core/seq/seq_midi.c */
snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile)6049a1b64caSTakashi Iwai int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
6059a1b64caSTakashi Iwai {
6069a1b64caSTakashi Iwai 	struct snd_rawmidi *rmidi;
6079a1b64caSTakashi Iwai 
6089a1b64caSTakashi Iwai 	if (snd_BUG_ON(!rfile))
6099a1b64caSTakashi Iwai 		return -ENXIO;
6109a1b64caSTakashi Iwai 
6119a1b64caSTakashi Iwai 	rmidi = rfile->rmidi;
6129a1b64caSTakashi Iwai 	rawmidi_release_priv(rfile);
6131da177e4SLinus Torvalds 	module_put(rmidi->card->module);
6141da177e4SLinus Torvalds 	return 0;
6151da177e4SLinus Torvalds }
6166776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_kernel_release);
6171da177e4SLinus Torvalds 
snd_rawmidi_release(struct inode * inode,struct file * file)6181da177e4SLinus Torvalds static int snd_rawmidi_release(struct inode *inode, struct file *file)
6191da177e4SLinus Torvalds {
62048c9d417STakashi Iwai 	struct snd_rawmidi_file *rfile;
62148c9d417STakashi Iwai 	struct snd_rawmidi *rmidi;
622aa73aec6SClemens Ladisch 	struct module *module;
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	rfile = file->private_data;
6251da177e4SLinus Torvalds 	rmidi = rfile->rmidi;
6269a1b64caSTakashi Iwai 	rawmidi_release_priv(rfile);
6271da177e4SLinus Torvalds 	kfree(rfile);
628aa73aec6SClemens Ladisch 	module = rmidi->card->module;
6291da177e4SLinus Torvalds 	snd_card_file_remove(rmidi->card, file);
630aa73aec6SClemens Ladisch 	module_put(module);
6319a1b64caSTakashi Iwai 	return 0;
6321da177e4SLinus Torvalds }
6331da177e4SLinus Torvalds 
snd_rawmidi_info(struct snd_rawmidi_substream * substream,struct snd_rawmidi_info * info)63418612048SAdrian Bunk static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
63548c9d417STakashi Iwai 			    struct snd_rawmidi_info *info)
6361da177e4SLinus Torvalds {
63748c9d417STakashi Iwai 	struct snd_rawmidi *rmidi;
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds 	if (substream == NULL)
6401da177e4SLinus Torvalds 		return -ENODEV;
6411da177e4SLinus Torvalds 	rmidi = substream->rmidi;
6421da177e4SLinus Torvalds 	memset(info, 0, sizeof(*info));
6431da177e4SLinus Torvalds 	info->card = rmidi->card->number;
6441da177e4SLinus Torvalds 	info->device = rmidi->device;
6451da177e4SLinus Torvalds 	info->subdevice = substream->number;
6461da177e4SLinus Torvalds 	info->stream = substream->stream;
6471da177e4SLinus Torvalds 	info->flags = rmidi->info_flags;
6481da177e4SLinus Torvalds 	strcpy(info->id, rmidi->id);
6491da177e4SLinus Torvalds 	strcpy(info->name, rmidi->name);
6501da177e4SLinus Torvalds 	strcpy(info->subname, substream->name);
6511da177e4SLinus Torvalds 	info->subdevices_count = substream->pstr->substream_count;
6521da177e4SLinus Torvalds 	info->subdevices_avail = (substream->pstr->substream_count -
6531da177e4SLinus Torvalds 				  substream->pstr->substream_opened);
6541da177e4SLinus Torvalds 	return 0;
6551da177e4SLinus Torvalds }
6561da177e4SLinus Torvalds 
snd_rawmidi_info_user(struct snd_rawmidi_substream * substream,struct snd_rawmidi_info __user * _info)65748c9d417STakashi Iwai static int snd_rawmidi_info_user(struct snd_rawmidi_substream *substream,
65848c9d417STakashi Iwai 				 struct snd_rawmidi_info __user *_info)
6591da177e4SLinus Torvalds {
66048c9d417STakashi Iwai 	struct snd_rawmidi_info info;
6611da177e4SLinus Torvalds 	int err;
6625bed9139STakashi Iwai 
6635bed9139STakashi Iwai 	err = snd_rawmidi_info(substream, &info);
6645bed9139STakashi Iwai 	if (err < 0)
6651da177e4SLinus Torvalds 		return err;
66648c9d417STakashi Iwai 	if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
6671da177e4SLinus Torvalds 		return -EFAULT;
6681da177e4SLinus Torvalds 	return 0;
6691da177e4SLinus Torvalds }
6701da177e4SLinus Torvalds 
__snd_rawmidi_info_select(struct snd_card * card,struct snd_rawmidi_info * info)671c1cfd902STakashi Iwai static int __snd_rawmidi_info_select(struct snd_card *card,
672c1cfd902STakashi Iwai 				     struct snd_rawmidi_info *info)
6731da177e4SLinus Torvalds {
67448c9d417STakashi Iwai 	struct snd_rawmidi *rmidi;
67548c9d417STakashi Iwai 	struct snd_rawmidi_str *pstr;
67648c9d417STakashi Iwai 	struct snd_rawmidi_substream *substream;
677f87135f5SClemens Ladisch 
678f87135f5SClemens Ladisch 	rmidi = snd_rawmidi_search(card, info->device);
679a106cd3dSClemens Ladisch 	if (!rmidi)
680a106cd3dSClemens Ladisch 		return -ENXIO;
6811da177e4SLinus Torvalds 	if (info->stream < 0 || info->stream > 1)
6821da177e4SLinus Torvalds 		return -EINVAL;
6832b1d9c8fSGustavo A. R. Silva 	info->stream = array_index_nospec(info->stream, 2);
6841da177e4SLinus Torvalds 	pstr = &rmidi->streams[info->stream];
6851da177e4SLinus Torvalds 	if (pstr->substream_count == 0)
6861da177e4SLinus Torvalds 		return -ENOENT;
6871da177e4SLinus Torvalds 	if (info->subdevice >= pstr->substream_count)
6881da177e4SLinus Torvalds 		return -ENXIO;
6899244b2c3SJohannes Berg 	list_for_each_entry(substream, &pstr->substreams, list) {
6901da177e4SLinus Torvalds 		if ((unsigned int)substream->number == info->subdevice)
6911da177e4SLinus Torvalds 			return snd_rawmidi_info(substream, info);
6921da177e4SLinus Torvalds 	}
6931da177e4SLinus Torvalds 	return -ENXIO;
6941da177e4SLinus Torvalds }
695c1cfd902STakashi Iwai 
snd_rawmidi_info_select(struct snd_card * card,struct snd_rawmidi_info * info)696c1cfd902STakashi Iwai int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
697c1cfd902STakashi Iwai {
698c1cfd902STakashi Iwai 	int ret;
699c1cfd902STakashi Iwai 
700c1cfd902STakashi Iwai 	mutex_lock(&register_mutex);
701c1cfd902STakashi Iwai 	ret = __snd_rawmidi_info_select(card, info);
702c1cfd902STakashi Iwai 	mutex_unlock(&register_mutex);
703c1cfd902STakashi Iwai 	return ret;
704c1cfd902STakashi Iwai }
7056776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_info_select);
7061da177e4SLinus Torvalds 
snd_rawmidi_info_select_user(struct snd_card * card,struct snd_rawmidi_info __user * _info)70748c9d417STakashi Iwai static int snd_rawmidi_info_select_user(struct snd_card *card,
70848c9d417STakashi Iwai 					struct snd_rawmidi_info __user *_info)
7091da177e4SLinus Torvalds {
7101da177e4SLinus Torvalds 	int err;
71148c9d417STakashi Iwai 	struct snd_rawmidi_info info;
7125bed9139STakashi Iwai 
7131da177e4SLinus Torvalds 	if (get_user(info.device, &_info->device))
7141da177e4SLinus Torvalds 		return -EFAULT;
7151da177e4SLinus Torvalds 	if (get_user(info.stream, &_info->stream))
7161da177e4SLinus Torvalds 		return -EFAULT;
7171da177e4SLinus Torvalds 	if (get_user(info.subdevice, &_info->subdevice))
7181da177e4SLinus Torvalds 		return -EFAULT;
7195bed9139STakashi Iwai 	err = snd_rawmidi_info_select(card, &info);
7205bed9139STakashi Iwai 	if (err < 0)
7211da177e4SLinus Torvalds 		return err;
72248c9d417STakashi Iwai 	if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
7231da177e4SLinus Torvalds 		return -EFAULT;
7241da177e4SLinus Torvalds 	return 0;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds 
resize_runtime_buffer(struct snd_rawmidi_substream * substream,struct snd_rawmidi_params * params,bool is_input)727f1d40433STakashi Iwai static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
728f5beb598STakashi Iwai 				 struct snd_rawmidi_params *params,
729f5beb598STakashi Iwai 				 bool is_input)
7301da177e4SLinus Torvalds {
731f1d40433STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
73239675f7aSTakashi Iwai 	char *newbuf, *oldbuf;
73308fdced6SDavid Henningsson 	unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
7341da177e4SLinus Torvalds 
7355bed9139STakashi Iwai 	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L)
7361da177e4SLinus Torvalds 		return -EINVAL;
73708fdced6SDavid Henningsson 	if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0)
73808fdced6SDavid Henningsson 		return -EINVAL;
7395bed9139STakashi Iwai 	if (params->avail_min < 1 || params->avail_min > params->buffer_size)
7401da177e4SLinus Torvalds 		return -EINVAL;
741e3a8a5b7STakashi Iwai 	if (params->buffer_size & get_align(runtime))
742e3a8a5b7STakashi Iwai 		return -EINVAL;
7431da177e4SLinus Torvalds 	if (params->buffer_size != runtime->buffer_size) {
7445a7b44a8STakashi Iwai 		newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
7454fa95ef6SJesper Juhl 		if (!newbuf)
7461da177e4SLinus Torvalds 			return -ENOMEM;
747f1d40433STakashi Iwai 		spin_lock_irq(&substream->lock);
748c1f6e3c8STakashi Iwai 		if (runtime->buffer_ref) {
749f1d40433STakashi Iwai 			spin_unlock_irq(&substream->lock);
750c1f6e3c8STakashi Iwai 			kvfree(newbuf);
751c1f6e3c8STakashi Iwai 			return -EBUSY;
752c1f6e3c8STakashi Iwai 		}
75339675f7aSTakashi Iwai 		oldbuf = runtime->buffer;
7541da177e4SLinus Torvalds 		runtime->buffer = newbuf;
7551da177e4SLinus Torvalds 		runtime->buffer_size = params->buffer_size;
756f5beb598STakashi Iwai 		__reset_runtime_ptrs(runtime, is_input);
757f1d40433STakashi Iwai 		spin_unlock_irq(&substream->lock);
758ef4db239STakashi Iwai 		kvfree(oldbuf);
7591da177e4SLinus Torvalds 	}
7601da177e4SLinus Torvalds 	runtime->avail_min = params->avail_min;
7611da177e4SLinus Torvalds 	return 0;
7621da177e4SLinus Torvalds }
763f5beb598STakashi Iwai 
snd_rawmidi_output_params(struct snd_rawmidi_substream * substream,struct snd_rawmidi_params * params)764f5beb598STakashi Iwai int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
765f5beb598STakashi Iwai 			      struct snd_rawmidi_params *params)
766f5beb598STakashi Iwai {
76794b98194STakashi Iwai 	int err;
76894b98194STakashi Iwai 
769f5beb598STakashi Iwai 	snd_rawmidi_drain_output(substream);
77094b98194STakashi Iwai 	mutex_lock(&substream->rmidi->open_mutex);
77194b98194STakashi Iwai 	if (substream->append && substream->use_count > 1)
77294b98194STakashi Iwai 		err = -EBUSY;
77394b98194STakashi Iwai 	else
77494b98194STakashi Iwai 		err = resize_runtime_buffer(substream, params, false);
77594b98194STakashi Iwai 
77694b98194STakashi Iwai 	if (!err)
777f5beb598STakashi Iwai 		substream->active_sensing = !params->no_active_sensing;
77894b98194STakashi Iwai 	mutex_unlock(&substream->rmidi->open_mutex);
77994b98194STakashi Iwai 	return err;
780f5beb598STakashi Iwai }
7816776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_output_params);
7821da177e4SLinus Torvalds 
snd_rawmidi_input_params(struct snd_rawmidi_substream * substream,struct snd_rawmidi_params * params)78348c9d417STakashi Iwai int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
78448c9d417STakashi Iwai 			     struct snd_rawmidi_params *params)
7851da177e4SLinus Torvalds {
78608fdced6SDavid Henningsson 	unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
78708fdced6SDavid Henningsson 	unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK;
78808fdced6SDavid Henningsson 	int err;
78908fdced6SDavid Henningsson 
7901da177e4SLinus Torvalds 	snd_rawmidi_drain_input(substream);
79194b98194STakashi Iwai 	mutex_lock(&substream->rmidi->open_mutex);
79294b98194STakashi Iwai 	if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE)
79394b98194STakashi Iwai 		err = -EINVAL;
79494b98194STakashi Iwai 	else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW)
79594b98194STakashi Iwai 		err = -EINVAL;
79694b98194STakashi Iwai 	else if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
79794b98194STakashi Iwai 		err = -EINVAL;
79894b98194STakashi Iwai 	else
799f1d40433STakashi Iwai 		err = resize_runtime_buffer(substream, params, true);
80008fdced6SDavid Henningsson 
80194b98194STakashi Iwai 	if (!err) {
80208fdced6SDavid Henningsson 		substream->framing = framing;
80308fdced6SDavid Henningsson 		substream->clock_type = clock_type;
80494b98194STakashi Iwai 	}
80594b98194STakashi Iwai 	mutex_unlock(&substream->rmidi->open_mutex);
80608fdced6SDavid Henningsson 	return 0;
8071da177e4SLinus Torvalds }
8086776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_input_params);
8091da177e4SLinus Torvalds 
snd_rawmidi_output_status(struct snd_rawmidi_substream * substream,struct snd_rawmidi_status64 * status)81048c9d417STakashi Iwai static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
811d9e5582cSBaolin Wang 				     struct snd_rawmidi_status64 *status)
8121da177e4SLinus Torvalds {
81348c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
8141da177e4SLinus Torvalds 
8151da177e4SLinus Torvalds 	memset(status, 0, sizeof(*status));
8161da177e4SLinus Torvalds 	status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
817f1d40433STakashi Iwai 	spin_lock_irq(&substream->lock);
8181da177e4SLinus Torvalds 	status->avail = runtime->avail;
819f1d40433STakashi Iwai 	spin_unlock_irq(&substream->lock);
8201da177e4SLinus Torvalds 	return 0;
8211da177e4SLinus Torvalds }
8221da177e4SLinus Torvalds 
snd_rawmidi_input_status(struct snd_rawmidi_substream * substream,struct snd_rawmidi_status64 * status)82348c9d417STakashi Iwai static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
824d9e5582cSBaolin Wang 				    struct snd_rawmidi_status64 *status)
8251da177e4SLinus Torvalds {
82648c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds 	memset(status, 0, sizeof(*status));
8291da177e4SLinus Torvalds 	status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
830f1d40433STakashi Iwai 	spin_lock_irq(&substream->lock);
8311da177e4SLinus Torvalds 	status->avail = runtime->avail;
8321da177e4SLinus Torvalds 	status->xruns = runtime->xruns;
8331da177e4SLinus Torvalds 	runtime->xruns = 0;
834f1d40433STakashi Iwai 	spin_unlock_irq(&substream->lock);
8351da177e4SLinus Torvalds 	return 0;
8361da177e4SLinus Torvalds }
8371da177e4SLinus Torvalds 
snd_rawmidi_ioctl_status32(struct snd_rawmidi_file * rfile,struct snd_rawmidi_status32 __user * argp)838d9e5582cSBaolin Wang static int snd_rawmidi_ioctl_status32(struct snd_rawmidi_file *rfile,
839d9e5582cSBaolin Wang 				      struct snd_rawmidi_status32 __user *argp)
840d9e5582cSBaolin Wang {
841d9e5582cSBaolin Wang 	int err = 0;
842d9e5582cSBaolin Wang 	struct snd_rawmidi_status32 __user *status = argp;
843d9e5582cSBaolin Wang 	struct snd_rawmidi_status32 status32;
844d9e5582cSBaolin Wang 	struct snd_rawmidi_status64 status64;
845d9e5582cSBaolin Wang 
846d9e5582cSBaolin Wang 	if (copy_from_user(&status32, argp,
847d9e5582cSBaolin Wang 			   sizeof(struct snd_rawmidi_status32)))
848d9e5582cSBaolin Wang 		return -EFAULT;
849d9e5582cSBaolin Wang 
850d9e5582cSBaolin Wang 	switch (status32.stream) {
851d9e5582cSBaolin Wang 	case SNDRV_RAWMIDI_STREAM_OUTPUT:
852d9e5582cSBaolin Wang 		if (rfile->output == NULL)
853d9e5582cSBaolin Wang 			return -EINVAL;
854d9e5582cSBaolin Wang 		err = snd_rawmidi_output_status(rfile->output, &status64);
855d9e5582cSBaolin Wang 		break;
856d9e5582cSBaolin Wang 	case SNDRV_RAWMIDI_STREAM_INPUT:
857d9e5582cSBaolin Wang 		if (rfile->input == NULL)
858d9e5582cSBaolin Wang 			return -EINVAL;
859d9e5582cSBaolin Wang 		err = snd_rawmidi_input_status(rfile->input, &status64);
860d9e5582cSBaolin Wang 		break;
861d9e5582cSBaolin Wang 	default:
862d9e5582cSBaolin Wang 		return -EINVAL;
863d9e5582cSBaolin Wang 	}
864d9e5582cSBaolin Wang 	if (err < 0)
865d9e5582cSBaolin Wang 		return err;
866d9e5582cSBaolin Wang 
867d9e5582cSBaolin Wang 	status32 = (struct snd_rawmidi_status32) {
868d9e5582cSBaolin Wang 		.stream = status64.stream,
869d9e5582cSBaolin Wang 		.tstamp_sec = status64.tstamp_sec,
870d9e5582cSBaolin Wang 		.tstamp_nsec = status64.tstamp_nsec,
871d9e5582cSBaolin Wang 		.avail = status64.avail,
872d9e5582cSBaolin Wang 		.xruns = status64.xruns,
873d9e5582cSBaolin Wang 	};
874d9e5582cSBaolin Wang 
875d9e5582cSBaolin Wang 	if (copy_to_user(status, &status32, sizeof(*status)))
876d9e5582cSBaolin Wang 		return -EFAULT;
877d9e5582cSBaolin Wang 
878d9e5582cSBaolin Wang 	return 0;
879d9e5582cSBaolin Wang }
880d9e5582cSBaolin Wang 
snd_rawmidi_ioctl_status64(struct snd_rawmidi_file * rfile,struct snd_rawmidi_status64 __user * argp)881d9e5582cSBaolin Wang static int snd_rawmidi_ioctl_status64(struct snd_rawmidi_file *rfile,
882d9e5582cSBaolin Wang 				      struct snd_rawmidi_status64 __user *argp)
883d9e5582cSBaolin Wang {
884d9e5582cSBaolin Wang 	int err = 0;
885d9e5582cSBaolin Wang 	struct snd_rawmidi_status64 status;
886d9e5582cSBaolin Wang 
887d9e5582cSBaolin Wang 	if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status64)))
888d9e5582cSBaolin Wang 		return -EFAULT;
889d9e5582cSBaolin Wang 
890d9e5582cSBaolin Wang 	switch (status.stream) {
891d9e5582cSBaolin Wang 	case SNDRV_RAWMIDI_STREAM_OUTPUT:
892d9e5582cSBaolin Wang 		if (rfile->output == NULL)
893d9e5582cSBaolin Wang 			return -EINVAL;
894d9e5582cSBaolin Wang 		err = snd_rawmidi_output_status(rfile->output, &status);
895d9e5582cSBaolin Wang 		break;
896d9e5582cSBaolin Wang 	case SNDRV_RAWMIDI_STREAM_INPUT:
897d9e5582cSBaolin Wang 		if (rfile->input == NULL)
898d9e5582cSBaolin Wang 			return -EINVAL;
899d9e5582cSBaolin Wang 		err = snd_rawmidi_input_status(rfile->input, &status);
900d9e5582cSBaolin Wang 		break;
901d9e5582cSBaolin Wang 	default:
902d9e5582cSBaolin Wang 		return -EINVAL;
903d9e5582cSBaolin Wang 	}
904d9e5582cSBaolin Wang 	if (err < 0)
905d9e5582cSBaolin Wang 		return err;
906d9e5582cSBaolin Wang 	if (copy_to_user(argp, &status,
907d9e5582cSBaolin Wang 			 sizeof(struct snd_rawmidi_status64)))
908d9e5582cSBaolin Wang 		return -EFAULT;
909d9e5582cSBaolin Wang 	return 0;
910d9e5582cSBaolin Wang }
911d9e5582cSBaolin Wang 
snd_rawmidi_ioctl(struct file * file,unsigned int cmd,unsigned long arg)9121da177e4SLinus Torvalds static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
9131da177e4SLinus Torvalds {
91448c9d417STakashi Iwai 	struct snd_rawmidi_file *rfile;
915fb3bd121STakashi Iwai 	struct snd_rawmidi *rmidi;
9161da177e4SLinus Torvalds 	void __user *argp = (void __user *)arg;
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds 	rfile = file->private_data;
9191da177e4SLinus Torvalds 	if (((cmd >> 8) & 0xff) != 'W')
9201da177e4SLinus Torvalds 		return -ENOTTY;
9211da177e4SLinus Torvalds 	switch (cmd) {
9221da177e4SLinus Torvalds 	case SNDRV_RAWMIDI_IOCTL_PVERSION:
9231da177e4SLinus Torvalds 		return put_user(SNDRV_RAWMIDI_VERSION, (int __user *)argp) ? -EFAULT : 0;
9241da177e4SLinus Torvalds 	case SNDRV_RAWMIDI_IOCTL_INFO:
9251da177e4SLinus Torvalds 	{
92648c9d417STakashi Iwai 		int stream;
92748c9d417STakashi Iwai 		struct snd_rawmidi_info __user *info = argp;
9285bed9139STakashi Iwai 
9291da177e4SLinus Torvalds 		if (get_user(stream, &info->stream))
9301da177e4SLinus Torvalds 			return -EFAULT;
9311da177e4SLinus Torvalds 		switch (stream) {
9321da177e4SLinus Torvalds 		case SNDRV_RAWMIDI_STREAM_INPUT:
9331da177e4SLinus Torvalds 			return snd_rawmidi_info_user(rfile->input, info);
9341da177e4SLinus Torvalds 		case SNDRV_RAWMIDI_STREAM_OUTPUT:
9351da177e4SLinus Torvalds 			return snd_rawmidi_info_user(rfile->output, info);
9361da177e4SLinus Torvalds 		default:
9371da177e4SLinus Torvalds 			return -EINVAL;
9381da177e4SLinus Torvalds 		}
9391da177e4SLinus Torvalds 	}
94009d23174SJaroslav Kysela 	case SNDRV_RAWMIDI_IOCTL_USER_PVERSION:
94109d23174SJaroslav Kysela 		if (get_user(rfile->user_pversion, (unsigned int __user *)arg))
94209d23174SJaroslav Kysela 			return -EFAULT;
94309d23174SJaroslav Kysela 		return 0;
94409d23174SJaroslav Kysela 
9451da177e4SLinus Torvalds 	case SNDRV_RAWMIDI_IOCTL_PARAMS:
9461da177e4SLinus Torvalds 	{
94748c9d417STakashi Iwai 		struct snd_rawmidi_params params;
9485bed9139STakashi Iwai 
94948c9d417STakashi Iwai 		if (copy_from_user(&params, argp, sizeof(struct snd_rawmidi_params)))
9501da177e4SLinus Torvalds 			return -EFAULT;
95109d23174SJaroslav Kysela 		if (rfile->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 2)) {
95209d23174SJaroslav Kysela 			params.mode = 0;
95309d23174SJaroslav Kysela 			memset(params.reserved, 0, sizeof(params.reserved));
95409d23174SJaroslav Kysela 		}
9551da177e4SLinus Torvalds 		switch (params.stream) {
9561da177e4SLinus Torvalds 		case SNDRV_RAWMIDI_STREAM_OUTPUT:
9571da177e4SLinus Torvalds 			if (rfile->output == NULL)
9581da177e4SLinus Torvalds 				return -EINVAL;
9591da177e4SLinus Torvalds 			return snd_rawmidi_output_params(rfile->output, &params);
9601da177e4SLinus Torvalds 		case SNDRV_RAWMIDI_STREAM_INPUT:
9611da177e4SLinus Torvalds 			if (rfile->input == NULL)
9621da177e4SLinus Torvalds 				return -EINVAL;
9631da177e4SLinus Torvalds 			return snd_rawmidi_input_params(rfile->input, &params);
9641da177e4SLinus Torvalds 		default:
9651da177e4SLinus Torvalds 			return -EINVAL;
9661da177e4SLinus Torvalds 		}
9671da177e4SLinus Torvalds 	}
968d9e5582cSBaolin Wang 	case SNDRV_RAWMIDI_IOCTL_STATUS32:
969d9e5582cSBaolin Wang 		return snd_rawmidi_ioctl_status32(rfile, argp);
970d9e5582cSBaolin Wang 	case SNDRV_RAWMIDI_IOCTL_STATUS64:
971d9e5582cSBaolin Wang 		return snd_rawmidi_ioctl_status64(rfile, argp);
9721da177e4SLinus Torvalds 	case SNDRV_RAWMIDI_IOCTL_DROP:
9731da177e4SLinus Torvalds 	{
9741da177e4SLinus Torvalds 		int val;
9755bed9139STakashi Iwai 
9761da177e4SLinus Torvalds 		if (get_user(val, (int __user *) argp))
9771da177e4SLinus Torvalds 			return -EFAULT;
9781da177e4SLinus Torvalds 		switch (val) {
9791da177e4SLinus Torvalds 		case SNDRV_RAWMIDI_STREAM_OUTPUT:
9801da177e4SLinus Torvalds 			if (rfile->output == NULL)
9811da177e4SLinus Torvalds 				return -EINVAL;
9821da177e4SLinus Torvalds 			return snd_rawmidi_drop_output(rfile->output);
9831da177e4SLinus Torvalds 		default:
9841da177e4SLinus Torvalds 			return -EINVAL;
9851da177e4SLinus Torvalds 		}
9861da177e4SLinus Torvalds 	}
9871da177e4SLinus Torvalds 	case SNDRV_RAWMIDI_IOCTL_DRAIN:
9881da177e4SLinus Torvalds 	{
9891da177e4SLinus Torvalds 		int val;
9905bed9139STakashi Iwai 
9911da177e4SLinus Torvalds 		if (get_user(val, (int __user *) argp))
9921da177e4SLinus Torvalds 			return -EFAULT;
9931da177e4SLinus Torvalds 		switch (val) {
9941da177e4SLinus Torvalds 		case SNDRV_RAWMIDI_STREAM_OUTPUT:
9951da177e4SLinus Torvalds 			if (rfile->output == NULL)
9961da177e4SLinus Torvalds 				return -EINVAL;
9971da177e4SLinus Torvalds 			return snd_rawmidi_drain_output(rfile->output);
9981da177e4SLinus Torvalds 		case SNDRV_RAWMIDI_STREAM_INPUT:
9991da177e4SLinus Torvalds 			if (rfile->input == NULL)
10001da177e4SLinus Torvalds 				return -EINVAL;
10011da177e4SLinus Torvalds 			return snd_rawmidi_drain_input(rfile->input);
10021da177e4SLinus Torvalds 		default:
10031da177e4SLinus Torvalds 			return -EINVAL;
10041da177e4SLinus Torvalds 		}
10051da177e4SLinus Torvalds 	}
10061da177e4SLinus Torvalds 	default:
1007fb3bd121STakashi Iwai 		rmidi = rfile->rmidi;
1008fb3bd121STakashi Iwai 		if (rmidi->ops && rmidi->ops->ioctl)
1009fb3bd121STakashi Iwai 			return rmidi->ops->ioctl(rmidi, cmd, argp);
1010fb3bd121STakashi Iwai 		rmidi_dbg(rmidi, "rawmidi: unknown command = 0x%x\n", cmd);
10111da177e4SLinus Torvalds 	}
10121da177e4SLinus Torvalds 	return -ENOTTY;
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds 
1015127ae6f6STakashi Iwai /* ioctl to find the next device; either legacy or UMP depending on @find_ump */
snd_rawmidi_next_device(struct snd_card * card,int __user * argp,bool find_ump)1016127ae6f6STakashi Iwai static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp,
1017127ae6f6STakashi Iwai 				   bool find_ump)
1018127ae6f6STakashi Iwai 
1019127ae6f6STakashi Iwai {
1020127ae6f6STakashi Iwai 	struct snd_rawmidi *rmidi;
1021127ae6f6STakashi Iwai 	int device;
1022127ae6f6STakashi Iwai 	bool is_ump;
1023127ae6f6STakashi Iwai 
1024127ae6f6STakashi Iwai 	if (get_user(device, argp))
1025127ae6f6STakashi Iwai 		return -EFAULT;
1026127ae6f6STakashi Iwai 	if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
1027127ae6f6STakashi Iwai 		device = SNDRV_RAWMIDI_DEVICES - 1;
1028127ae6f6STakashi Iwai 	mutex_lock(&register_mutex);
1029127ae6f6STakashi Iwai 	device = device < 0 ? 0 : device + 1;
1030127ae6f6STakashi Iwai 	for (; device < SNDRV_RAWMIDI_DEVICES; device++) {
1031127ae6f6STakashi Iwai 		rmidi = snd_rawmidi_search(card, device);
1032127ae6f6STakashi Iwai 		if (!rmidi)
1033127ae6f6STakashi Iwai 			continue;
1034127ae6f6STakashi Iwai 		is_ump = rawmidi_is_ump(rmidi);
1035127ae6f6STakashi Iwai 		if (find_ump == is_ump)
1036127ae6f6STakashi Iwai 			break;
1037127ae6f6STakashi Iwai 	}
1038127ae6f6STakashi Iwai 	if (device == SNDRV_RAWMIDI_DEVICES)
1039127ae6f6STakashi Iwai 		device = -1;
1040127ae6f6STakashi Iwai 	mutex_unlock(&register_mutex);
1041127ae6f6STakashi Iwai 	if (put_user(device, argp))
1042127ae6f6STakashi Iwai 		return -EFAULT;
1043127ae6f6STakashi Iwai 	return 0;
1044127ae6f6STakashi Iwai }
1045127ae6f6STakashi Iwai 
104630fc1392STakashi Iwai #if IS_ENABLED(CONFIG_SND_UMP)
104730fc1392STakashi Iwai /* inquiry of UMP endpoint and block info via control API */
snd_rawmidi_call_ump_ioctl(struct snd_card * card,int cmd,void __user * argp)104830fc1392STakashi Iwai static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd,
104930fc1392STakashi Iwai 				      void __user *argp)
105030fc1392STakashi Iwai {
105130fc1392STakashi Iwai 	struct snd_ump_endpoint_info __user *info = argp;
105230fc1392STakashi Iwai 	struct snd_rawmidi *rmidi;
105330fc1392STakashi Iwai 	int device, ret;
105430fc1392STakashi Iwai 
105530fc1392STakashi Iwai 	if (get_user(device, &info->device))
105630fc1392STakashi Iwai 		return -EFAULT;
105730fc1392STakashi Iwai 	mutex_lock(&register_mutex);
105830fc1392STakashi Iwai 	rmidi = snd_rawmidi_search(card, device);
105930fc1392STakashi Iwai 	if (rmidi && rmidi->ops && rmidi->ops->ioctl)
106030fc1392STakashi Iwai 		ret = rmidi->ops->ioctl(rmidi, cmd, argp);
106130fc1392STakashi Iwai 	else
106230fc1392STakashi Iwai 		ret = -ENXIO;
106330fc1392STakashi Iwai 	mutex_unlock(&register_mutex);
106430fc1392STakashi Iwai 	return ret;
106530fc1392STakashi Iwai }
106630fc1392STakashi Iwai #endif
106730fc1392STakashi Iwai 
snd_rawmidi_control_ioctl(struct snd_card * card,struct snd_ctl_file * control,unsigned int cmd,unsigned long arg)106848c9d417STakashi Iwai static int snd_rawmidi_control_ioctl(struct snd_card *card,
106948c9d417STakashi Iwai 				     struct snd_ctl_file *control,
10701da177e4SLinus Torvalds 				     unsigned int cmd,
10711da177e4SLinus Torvalds 				     unsigned long arg)
10721da177e4SLinus Torvalds {
10731da177e4SLinus Torvalds 	void __user *argp = (void __user *)arg;
10741da177e4SLinus Torvalds 
10751da177e4SLinus Torvalds 	switch (cmd) {
10761da177e4SLinus Torvalds 	case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
1077127ae6f6STakashi Iwai 		return snd_rawmidi_next_device(card, argp, false);
1078127ae6f6STakashi Iwai #if IS_ENABLED(CONFIG_SND_UMP)
1079127ae6f6STakashi Iwai 	case SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE:
1080127ae6f6STakashi Iwai 		return snd_rawmidi_next_device(card, argp, true);
108130fc1392STakashi Iwai 	case SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO:
108230fc1392STakashi Iwai 		return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_ENDPOINT_INFO, argp);
108330fc1392STakashi Iwai 	case SNDRV_CTL_IOCTL_UMP_BLOCK_INFO:
108430fc1392STakashi Iwai 		return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_BLOCK_INFO, argp);
1085127ae6f6STakashi Iwai #endif
10861da177e4SLinus Torvalds 	case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
10871da177e4SLinus Torvalds 	{
10881da177e4SLinus Torvalds 		int val;
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds 		if (get_user(val, (int __user *)argp))
10911da177e4SLinus Torvalds 			return -EFAULT;
109223c18d4bSTakashi Iwai 		control->preferred_subdevice[SND_CTL_SUBDEV_RAWMIDI] = val;
10931da177e4SLinus Torvalds 		return 0;
10941da177e4SLinus Torvalds 	}
10951da177e4SLinus Torvalds 	case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
10961da177e4SLinus Torvalds 		return snd_rawmidi_info_select_user(card, argp);
10971da177e4SLinus Torvalds 	}
10981da177e4SLinus Torvalds 	return -ENOIOCTLCMD;
10991da177e4SLinus Torvalds }
11001da177e4SLinus Torvalds 
receive_with_tstamp_framing(struct snd_rawmidi_substream * substream,const unsigned char * buffer,int src_count,const struct timespec64 * tstamp)110108fdced6SDavid Henningsson static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
110208fdced6SDavid Henningsson 			const unsigned char *buffer, int src_count, const struct timespec64 *tstamp)
110308fdced6SDavid Henningsson {
110408fdced6SDavid Henningsson 	struct snd_rawmidi_runtime *runtime = substream->runtime;
110508fdced6SDavid Henningsson 	struct snd_rawmidi_framing_tstamp *dest_ptr;
110608fdced6SDavid Henningsson 	struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec };
110708fdced6SDavid Henningsson 	int orig_count = src_count;
110808fdced6SDavid Henningsson 	int frame_size = sizeof(struct snd_rawmidi_framing_tstamp);
1109e3a8a5b7STakashi Iwai 	int align = get_align(runtime);
111008fdced6SDavid Henningsson 
111108fdced6SDavid Henningsson 	BUILD_BUG_ON(frame_size != 0x20);
111208fdced6SDavid Henningsson 	if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
111308fdced6SDavid Henningsson 		return -EINVAL;
111408fdced6SDavid Henningsson 
1115e3a8a5b7STakashi Iwai 	while (src_count > align) {
111608fdced6SDavid Henningsson 		if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
111708fdced6SDavid Henningsson 			runtime->xruns += src_count;
111808fdced6SDavid Henningsson 			break;
111908fdced6SDavid Henningsson 		}
112008fdced6SDavid Henningsson 		if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
112108fdced6SDavid Henningsson 			frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH;
112208fdced6SDavid Henningsson 		else {
1123e3a8a5b7STakashi Iwai 			frame.length = get_aligned_size(runtime, src_count);
1124e3a8a5b7STakashi Iwai 			if (!frame.length)
1125e3a8a5b7STakashi Iwai 				break;
112608fdced6SDavid Henningsson 			memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH);
112708fdced6SDavid Henningsson 		}
112808fdced6SDavid Henningsson 		memcpy(frame.data, buffer, frame.length);
112908fdced6SDavid Henningsson 		buffer += frame.length;
113008fdced6SDavid Henningsson 		src_count -= frame.length;
113108fdced6SDavid Henningsson 		dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr);
113208fdced6SDavid Henningsson 		*dest_ptr = frame;
113308fdced6SDavid Henningsson 		runtime->avail += frame_size;
113408fdced6SDavid Henningsson 		runtime->hw_ptr += frame_size;
113508fdced6SDavid Henningsson 		runtime->hw_ptr %= runtime->buffer_size;
113608fdced6SDavid Henningsson 	}
113708fdced6SDavid Henningsson 	return orig_count - src_count;
113808fdced6SDavid Henningsson }
113908fdced6SDavid Henningsson 
get_framing_tstamp(struct snd_rawmidi_substream * substream)114008fdced6SDavid Henningsson static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
114108fdced6SDavid Henningsson {
114208fdced6SDavid Henningsson 	struct timespec64 ts64 = {0, 0};
114308fdced6SDavid Henningsson 
114408fdced6SDavid Henningsson 	switch (substream->clock_type) {
114508fdced6SDavid Henningsson 	case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW:
114608fdced6SDavid Henningsson 		ktime_get_raw_ts64(&ts64);
114708fdced6SDavid Henningsson 		break;
114808fdced6SDavid Henningsson 	case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC:
114908fdced6SDavid Henningsson 		ktime_get_ts64(&ts64);
115008fdced6SDavid Henningsson 		break;
115108fdced6SDavid Henningsson 	case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME:
115208fdced6SDavid Henningsson 		ktime_get_real_ts64(&ts64);
115308fdced6SDavid Henningsson 		break;
115408fdced6SDavid Henningsson 	}
115508fdced6SDavid Henningsson 	return ts64;
115608fdced6SDavid Henningsson }
115708fdced6SDavid Henningsson 
11581da177e4SLinus Torvalds /**
11591da177e4SLinus Torvalds  * snd_rawmidi_receive - receive the input data from the device
11601da177e4SLinus Torvalds  * @substream: the rawmidi substream
11611da177e4SLinus Torvalds  * @buffer: the buffer pointer
11621da177e4SLinus Torvalds  * @count: the data size to read
11631da177e4SLinus Torvalds  *
11641da177e4SLinus Torvalds  * Reads the data from the internal buffer.
11651da177e4SLinus Torvalds  *
1166eb7c06e8SYacine Belkadi  * Return: The size of read data, or a negative error code on failure.
11671da177e4SLinus Torvalds  */
snd_rawmidi_receive(struct snd_rawmidi_substream * substream,const unsigned char * buffer,int count)116848c9d417STakashi Iwai int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
116948c9d417STakashi Iwai 			const unsigned char *buffer, int count)
11701da177e4SLinus Torvalds {
11711da177e4SLinus Torvalds 	unsigned long flags;
117208fdced6SDavid Henningsson 	struct timespec64 ts64 = get_framing_tstamp(substream);
11731da177e4SLinus Torvalds 	int result = 0, count1;
1174463a20fdSTakashi Iwai 	struct snd_rawmidi_runtime *runtime;
117508fdced6SDavid Henningsson 
1176f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
1177463a20fdSTakashi Iwai 	if (!substream->opened) {
1178463a20fdSTakashi Iwai 		result = -EBADFD;
1179463a20fdSTakashi Iwai 		goto unlock;
1180463a20fdSTakashi Iwai 	}
1181463a20fdSTakashi Iwai 	runtime = substream->runtime;
1182463a20fdSTakashi Iwai 	if (!runtime || !runtime->buffer) {
1183463a20fdSTakashi Iwai 		rmidi_dbg(substream->rmidi,
1184463a20fdSTakashi Iwai 			  "snd_rawmidi_receive: input is not active!!!\n");
1185463a20fdSTakashi Iwai 		result = -EINVAL;
1186463a20fdSTakashi Iwai 		goto unlock;
1187463a20fdSTakashi Iwai 	}
1188463a20fdSTakashi Iwai 
1189e3a8a5b7STakashi Iwai 	count = get_aligned_size(runtime, count);
1190e3a8a5b7STakashi Iwai 	if (!count)
1191e3a8a5b7STakashi Iwai 		goto unlock;
1192e3a8a5b7STakashi Iwai 
119308fdced6SDavid Henningsson 	if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
119408fdced6SDavid Henningsson 		result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
119508fdced6SDavid Henningsson 	} else if (count == 1) {	/* special case, faster code */
11961da177e4SLinus Torvalds 		substream->bytes++;
11971da177e4SLinus Torvalds 		if (runtime->avail < runtime->buffer_size) {
11981da177e4SLinus Torvalds 			runtime->buffer[runtime->hw_ptr++] = buffer[0];
11991da177e4SLinus Torvalds 			runtime->hw_ptr %= runtime->buffer_size;
12001da177e4SLinus Torvalds 			runtime->avail++;
12011da177e4SLinus Torvalds 			result++;
12021da177e4SLinus Torvalds 		} else {
12031da177e4SLinus Torvalds 			runtime->xruns++;
12041da177e4SLinus Torvalds 		}
12051da177e4SLinus Torvalds 	} else {
12061da177e4SLinus Torvalds 		substream->bytes += count;
12071da177e4SLinus Torvalds 		count1 = runtime->buffer_size - runtime->hw_ptr;
12081da177e4SLinus Torvalds 		if (count1 > count)
12091da177e4SLinus Torvalds 			count1 = count;
12101da177e4SLinus Torvalds 		if (count1 > (int)(runtime->buffer_size - runtime->avail))
12111da177e4SLinus Torvalds 			count1 = runtime->buffer_size - runtime->avail;
1212e3a8a5b7STakashi Iwai 		count1 = get_aligned_size(runtime, count1);
1213e3a8a5b7STakashi Iwai 		if (!count1)
1214e3a8a5b7STakashi Iwai 			goto unlock;
12151da177e4SLinus Torvalds 		memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
12161da177e4SLinus Torvalds 		runtime->hw_ptr += count1;
12171da177e4SLinus Torvalds 		runtime->hw_ptr %= runtime->buffer_size;
12181da177e4SLinus Torvalds 		runtime->avail += count1;
12191da177e4SLinus Torvalds 		count -= count1;
12201da177e4SLinus Torvalds 		result += count1;
12211da177e4SLinus Torvalds 		if (count > 0) {
12221da177e4SLinus Torvalds 			buffer += count1;
12231da177e4SLinus Torvalds 			count1 = count;
12241da177e4SLinus Torvalds 			if (count1 > (int)(runtime->buffer_size - runtime->avail)) {
12251da177e4SLinus Torvalds 				count1 = runtime->buffer_size - runtime->avail;
12261da177e4SLinus Torvalds 				runtime->xruns += count - count1;
12271da177e4SLinus Torvalds 			}
12281da177e4SLinus Torvalds 			if (count1 > 0) {
12291da177e4SLinus Torvalds 				memcpy(runtime->buffer, buffer, count1);
12301da177e4SLinus Torvalds 				runtime->hw_ptr = count1;
12311da177e4SLinus Torvalds 				runtime->avail += count1;
12321da177e4SLinus Torvalds 				result += count1;
12331da177e4SLinus Torvalds 			}
12341da177e4SLinus Torvalds 		}
12351da177e4SLinus Torvalds 	}
12361da177e4SLinus Torvalds 	if (result > 0) {
12371da177e4SLinus Torvalds 		if (runtime->event)
1238b3c705aaSTakashi Iwai 			schedule_work(&runtime->event_work);
123988a06d6fSTakashi Iwai 		else if (__snd_rawmidi_ready(runtime))
12401da177e4SLinus Torvalds 			wake_up(&runtime->sleep);
12411da177e4SLinus Torvalds 	}
1242463a20fdSTakashi Iwai  unlock:
1243f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
12441da177e4SLinus Torvalds 	return result;
12451da177e4SLinus Torvalds }
12466776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_receive);
12471da177e4SLinus Torvalds 
snd_rawmidi_kernel_read1(struct snd_rawmidi_substream * substream,unsigned char __user * userbuf,unsigned char * kernelbuf,long count)124848c9d417STakashi Iwai static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
124917596a80SMarcin Ślusarz 				     unsigned char __user *userbuf,
125017596a80SMarcin Ślusarz 				     unsigned char *kernelbuf, long count)
12511da177e4SLinus Torvalds {
12521da177e4SLinus Torvalds 	unsigned long flags;
12531da177e4SLinus Torvalds 	long result = 0, count1;
125448c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
125581f57754STakashi Iwai 	unsigned long appl_ptr;
1256c1f6e3c8STakashi Iwai 	int err = 0;
12571da177e4SLinus Torvalds 
1258f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
1259c1f6e3c8STakashi Iwai 	snd_rawmidi_buffer_ref(runtime);
12601da177e4SLinus Torvalds 	while (count > 0 && runtime->avail) {
12611da177e4SLinus Torvalds 		count1 = runtime->buffer_size - runtime->appl_ptr;
12621da177e4SLinus Torvalds 		if (count1 > count)
12631da177e4SLinus Torvalds 			count1 = count;
12641da177e4SLinus Torvalds 		if (count1 > (int)runtime->avail)
12651da177e4SLinus Torvalds 			count1 = runtime->avail;
126681f57754STakashi Iwai 
126781f57754STakashi Iwai 		/* update runtime->appl_ptr before unlocking for userbuf */
126881f57754STakashi Iwai 		appl_ptr = runtime->appl_ptr;
126981f57754STakashi Iwai 		runtime->appl_ptr += count1;
127081f57754STakashi Iwai 		runtime->appl_ptr %= runtime->buffer_size;
127181f57754STakashi Iwai 		runtime->avail -= count1;
127281f57754STakashi Iwai 
127317596a80SMarcin Ślusarz 		if (kernelbuf)
127481f57754STakashi Iwai 			memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
127517596a80SMarcin Ślusarz 		if (userbuf) {
1276f1d40433STakashi Iwai 			spin_unlock_irqrestore(&substream->lock, flags);
127717596a80SMarcin Ślusarz 			if (copy_to_user(userbuf + result,
1278c1f6e3c8STakashi Iwai 					 runtime->buffer + appl_ptr, count1))
1279c1f6e3c8STakashi Iwai 				err = -EFAULT;
1280f1d40433STakashi Iwai 			spin_lock_irqsave(&substream->lock, flags);
1281c1f6e3c8STakashi Iwai 			if (err)
1282c1f6e3c8STakashi Iwai 				goto out;
12831da177e4SLinus Torvalds 		}
12841da177e4SLinus Torvalds 		result += count1;
12851da177e4SLinus Torvalds 		count -= count1;
12861da177e4SLinus Torvalds 	}
1287c1f6e3c8STakashi Iwai  out:
1288c1f6e3c8STakashi Iwai 	snd_rawmidi_buffer_unref(runtime);
1289f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
1290c1f6e3c8STakashi Iwai 	return result > 0 ? result : err;
12911da177e4SLinus Torvalds }
12921da177e4SLinus Torvalds 
snd_rawmidi_kernel_read(struct snd_rawmidi_substream * substream,unsigned char * buf,long count)129348c9d417STakashi Iwai long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
129448c9d417STakashi Iwai 			     unsigned char *buf, long count)
12951da177e4SLinus Torvalds {
12961da177e4SLinus Torvalds 	snd_rawmidi_input_trigger(substream, 1);
129717596a80SMarcin Ślusarz 	return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
12981da177e4SLinus Torvalds }
12996776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_kernel_read);
13001da177e4SLinus Torvalds 
snd_rawmidi_read(struct file * file,char __user * buf,size_t count,loff_t * offset)130148c9d417STakashi Iwai static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count,
130248c9d417STakashi Iwai 				loff_t *offset)
13031da177e4SLinus Torvalds {
13041da177e4SLinus Torvalds 	long result;
13051da177e4SLinus Torvalds 	int count1;
130648c9d417STakashi Iwai 	struct snd_rawmidi_file *rfile;
130748c9d417STakashi Iwai 	struct snd_rawmidi_substream *substream;
130848c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime;
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds 	rfile = file->private_data;
13111da177e4SLinus Torvalds 	substream = rfile->input;
13121da177e4SLinus Torvalds 	if (substream == NULL)
13131da177e4SLinus Torvalds 		return -EIO;
13141da177e4SLinus Torvalds 	runtime = substream->runtime;
13151da177e4SLinus Torvalds 	snd_rawmidi_input_trigger(substream, 1);
13161da177e4SLinus Torvalds 	result = 0;
13171da177e4SLinus Torvalds 	while (count > 0) {
1318f1d40433STakashi Iwai 		spin_lock_irq(&substream->lock);
131988a06d6fSTakashi Iwai 		while (!__snd_rawmidi_ready(runtime)) {
1320ac6424b9SIngo Molnar 			wait_queue_entry_t wait;
13215bed9139STakashi Iwai 
13221da177e4SLinus Torvalds 			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
1323f1d40433STakashi Iwai 				spin_unlock_irq(&substream->lock);
13241da177e4SLinus Torvalds 				return result > 0 ? result : -EAGAIN;
13251da177e4SLinus Torvalds 			}
13261da177e4SLinus Torvalds 			init_waitqueue_entry(&wait, current);
13271da177e4SLinus Torvalds 			add_wait_queue(&runtime->sleep, &wait);
13281da177e4SLinus Torvalds 			set_current_state(TASK_INTERRUPTIBLE);
1329f1d40433STakashi Iwai 			spin_unlock_irq(&substream->lock);
13301da177e4SLinus Torvalds 			schedule();
13311da177e4SLinus Torvalds 			remove_wait_queue(&runtime->sleep, &wait);
13320914f796STakashi Iwai 			if (rfile->rmidi->card->shutdown)
13330914f796STakashi Iwai 				return -ENODEV;
13341da177e4SLinus Torvalds 			if (signal_pending(current))
13351da177e4SLinus Torvalds 				return result > 0 ? result : -ERESTARTSYS;
1336f1d40433STakashi Iwai 			spin_lock_irq(&substream->lock);
133788a06d6fSTakashi Iwai 			if (!runtime->avail) {
1338f1d40433STakashi Iwai 				spin_unlock_irq(&substream->lock);
133988a06d6fSTakashi Iwai 				return result > 0 ? result : -EIO;
134088a06d6fSTakashi Iwai 			}
13411da177e4SLinus Torvalds 		}
1342f1d40433STakashi Iwai 		spin_unlock_irq(&substream->lock);
13434d23359bSClemens Ladisch 		count1 = snd_rawmidi_kernel_read1(substream,
134417596a80SMarcin Ślusarz 						  (unsigned char __user *)buf,
134517596a80SMarcin Ślusarz 						  NULL/*kernelbuf*/,
134617596a80SMarcin Ślusarz 						  count);
13471da177e4SLinus Torvalds 		if (count1 < 0)
13481da177e4SLinus Torvalds 			return result > 0 ? result : count1;
13491da177e4SLinus Torvalds 		result += count1;
13501da177e4SLinus Torvalds 		buf += count1;
13511da177e4SLinus Torvalds 		count -= count1;
13521da177e4SLinus Torvalds 	}
13531da177e4SLinus Torvalds 	return result;
13541da177e4SLinus Torvalds }
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds /**
13571da177e4SLinus Torvalds  * snd_rawmidi_transmit_empty - check whether the output buffer is empty
13581da177e4SLinus Torvalds  * @substream: the rawmidi substream
13591da177e4SLinus Torvalds  *
1360eb7c06e8SYacine Belkadi  * Return: 1 if the internal output buffer is empty, 0 if not.
13611da177e4SLinus Torvalds  */
snd_rawmidi_transmit_empty(struct snd_rawmidi_substream * substream)136248c9d417STakashi Iwai int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
13631da177e4SLinus Torvalds {
1364463a20fdSTakashi Iwai 	struct snd_rawmidi_runtime *runtime;
13651da177e4SLinus Torvalds 	int result;
13661da177e4SLinus Torvalds 	unsigned long flags;
13671da177e4SLinus Torvalds 
1368463a20fdSTakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
1369463a20fdSTakashi Iwai 	runtime = substream->runtime;
1370463a20fdSTakashi Iwai 	if (!substream->opened || !runtime || !runtime->buffer) {
1371ca20d292STakashi Iwai 		rmidi_dbg(substream->rmidi,
1372ca20d292STakashi Iwai 			  "snd_rawmidi_transmit_empty: output is not active!!!\n");
1373463a20fdSTakashi Iwai 		result = 1;
1374463a20fdSTakashi Iwai 	} else {
13751da177e4SLinus Torvalds 		result = runtime->avail >= runtime->buffer_size;
1376463a20fdSTakashi Iwai 	}
1377f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
13781da177e4SLinus Torvalds 	return result;
13791da177e4SLinus Torvalds }
13806776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
13811da177e4SLinus Torvalds 
1382cd76175aSTakashi Iwai /*
138306ab3003STakashi Iwai  * __snd_rawmidi_transmit_peek - copy data from the internal buffer
13841da177e4SLinus Torvalds  * @substream: the rawmidi substream
13851da177e4SLinus Torvalds  * @buffer: the buffer pointer
13861da177e4SLinus Torvalds  * @count: data size to transfer
13871da177e4SLinus Torvalds  *
138806ab3003STakashi Iwai  * This is a variant of snd_rawmidi_transmit_peek() without spinlock.
13891da177e4SLinus Torvalds  */
__snd_rawmidi_transmit_peek(struct snd_rawmidi_substream * substream,unsigned char * buffer,int count)1390cd76175aSTakashi Iwai static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
139148c9d417STakashi Iwai 				       unsigned char *buffer, int count)
13921da177e4SLinus Torvalds {
13931da177e4SLinus Torvalds 	int result, count1;
139448c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
13951da177e4SLinus Torvalds 
13961da177e4SLinus Torvalds 	if (runtime->buffer == NULL) {
1397ca20d292STakashi Iwai 		rmidi_dbg(substream->rmidi,
1398ca20d292STakashi Iwai 			  "snd_rawmidi_transmit_peek: output is not active!!!\n");
13991da177e4SLinus Torvalds 		return -EINVAL;
14001da177e4SLinus Torvalds 	}
14011da177e4SLinus Torvalds 	result = 0;
14021da177e4SLinus Torvalds 	if (runtime->avail >= runtime->buffer_size) {
14031da177e4SLinus Torvalds 		/* warning: lowlevel layer MUST trigger down the hardware */
14041da177e4SLinus Torvalds 		goto __skip;
14051da177e4SLinus Torvalds 	}
14061da177e4SLinus Torvalds 	if (count == 1) {	/* special case, faster code */
14071da177e4SLinus Torvalds 		*buffer = runtime->buffer[runtime->hw_ptr];
14081da177e4SLinus Torvalds 		result++;
14091da177e4SLinus Torvalds 	} else {
14101da177e4SLinus Torvalds 		count1 = runtime->buffer_size - runtime->hw_ptr;
14111da177e4SLinus Torvalds 		if (count1 > count)
14121da177e4SLinus Torvalds 			count1 = count;
14131da177e4SLinus Torvalds 		if (count1 > (int)(runtime->buffer_size - runtime->avail))
14141da177e4SLinus Torvalds 			count1 = runtime->buffer_size - runtime->avail;
1415e3a8a5b7STakashi Iwai 		count1 = get_aligned_size(runtime, count1);
1416e3a8a5b7STakashi Iwai 		if (!count1)
1417e3a8a5b7STakashi Iwai 			goto __skip;
14181da177e4SLinus Torvalds 		memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
14191da177e4SLinus Torvalds 		count -= count1;
14201da177e4SLinus Torvalds 		result += count1;
14211da177e4SLinus Torvalds 		if (count > 0) {
14221da177e4SLinus Torvalds 			if (count > (int)(runtime->buffer_size - runtime->avail - count1))
14231da177e4SLinus Torvalds 				count = runtime->buffer_size - runtime->avail - count1;
1424e3a8a5b7STakashi Iwai 			count = get_aligned_size(runtime, count);
1425e3a8a5b7STakashi Iwai 			if (!count)
1426e3a8a5b7STakashi Iwai 				goto __skip;
14271da177e4SLinus Torvalds 			memcpy(buffer + count1, runtime->buffer, count);
14281da177e4SLinus Torvalds 			result += count;
14291da177e4SLinus Torvalds 		}
14301da177e4SLinus Torvalds 	}
14311da177e4SLinus Torvalds       __skip:
143206ab3003STakashi Iwai 	return result;
143306ab3003STakashi Iwai }
143406ab3003STakashi Iwai 
143506ab3003STakashi Iwai /**
143606ab3003STakashi Iwai  * snd_rawmidi_transmit_peek - copy data from the internal buffer
143706ab3003STakashi Iwai  * @substream: the rawmidi substream
143806ab3003STakashi Iwai  * @buffer: the buffer pointer
143906ab3003STakashi Iwai  * @count: data size to transfer
144006ab3003STakashi Iwai  *
144106ab3003STakashi Iwai  * Copies data from the internal output buffer to the given buffer.
144206ab3003STakashi Iwai  *
144306ab3003STakashi Iwai  * Call this in the interrupt handler when the midi output is ready,
144406ab3003STakashi Iwai  * and call snd_rawmidi_transmit_ack() after the transmission is
144506ab3003STakashi Iwai  * finished.
144606ab3003STakashi Iwai  *
144706ab3003STakashi Iwai  * Return: The size of copied data, or a negative error code on failure.
144806ab3003STakashi Iwai  */
snd_rawmidi_transmit_peek(struct snd_rawmidi_substream * substream,unsigned char * buffer,int count)144906ab3003STakashi Iwai int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
145006ab3003STakashi Iwai 			      unsigned char *buffer, int count)
145106ab3003STakashi Iwai {
145206ab3003STakashi Iwai 	int result;
145306ab3003STakashi Iwai 	unsigned long flags;
145406ab3003STakashi Iwai 
1455f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
1456463a20fdSTakashi Iwai 	if (!substream->opened || !substream->runtime)
1457463a20fdSTakashi Iwai 		result = -EBADFD;
1458463a20fdSTakashi Iwai 	else
145906ab3003STakashi Iwai 		result = __snd_rawmidi_transmit_peek(substream, buffer, count);
1460f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
14611da177e4SLinus Torvalds 	return result;
14621da177e4SLinus Torvalds }
14636776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
14641da177e4SLinus Torvalds 
1465cd76175aSTakashi Iwai /*
146606ab3003STakashi Iwai  * __snd_rawmidi_transmit_ack - acknowledge the transmission
146706ab3003STakashi Iwai  * @substream: the rawmidi substream
146806ab3003STakashi Iwai  * @count: the transferred count
146906ab3003STakashi Iwai  *
147006ab3003STakashi Iwai  * This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
147106ab3003STakashi Iwai  */
__snd_rawmidi_transmit_ack(struct snd_rawmidi_substream * substream,int count)1472cd76175aSTakashi Iwai static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
1473cd76175aSTakashi Iwai 				      int count)
147406ab3003STakashi Iwai {
147506ab3003STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
147606ab3003STakashi Iwai 
147706ab3003STakashi Iwai 	if (runtime->buffer == NULL) {
147806ab3003STakashi Iwai 		rmidi_dbg(substream->rmidi,
147906ab3003STakashi Iwai 			  "snd_rawmidi_transmit_ack: output is not active!!!\n");
148006ab3003STakashi Iwai 		return -EINVAL;
148106ab3003STakashi Iwai 	}
148206ab3003STakashi Iwai 	snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
1483e3a8a5b7STakashi Iwai 	count = get_aligned_size(runtime, count);
148406ab3003STakashi Iwai 	runtime->hw_ptr += count;
148506ab3003STakashi Iwai 	runtime->hw_ptr %= runtime->buffer_size;
148606ab3003STakashi Iwai 	runtime->avail += count;
148706ab3003STakashi Iwai 	substream->bytes += count;
148806ab3003STakashi Iwai 	if (count > 0) {
148988a06d6fSTakashi Iwai 		if (runtime->drain || __snd_rawmidi_ready(runtime))
149006ab3003STakashi Iwai 			wake_up(&runtime->sleep);
149106ab3003STakashi Iwai 	}
149206ab3003STakashi Iwai 	return count;
149306ab3003STakashi Iwai }
149406ab3003STakashi Iwai 
149506ab3003STakashi Iwai /**
14961da177e4SLinus Torvalds  * snd_rawmidi_transmit_ack - acknowledge the transmission
14971da177e4SLinus Torvalds  * @substream: the rawmidi substream
14980a11458cSMasanari Iida  * @count: the transferred count
14991da177e4SLinus Torvalds  *
15001da177e4SLinus Torvalds  * Advances the hardware pointer for the internal output buffer with
15011da177e4SLinus Torvalds  * the given size and updates the condition.
15021da177e4SLinus Torvalds  * Call after the transmission is finished.
15031da177e4SLinus Torvalds  *
1504eb7c06e8SYacine Belkadi  * Return: The advanced size if successful, or a negative error code on failure.
15051da177e4SLinus Torvalds  */
snd_rawmidi_transmit_ack(struct snd_rawmidi_substream * substream,int count)150648c9d417STakashi Iwai int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
15071da177e4SLinus Torvalds {
150806ab3003STakashi Iwai 	int result;
150906ab3003STakashi Iwai 	unsigned long flags;
15101da177e4SLinus Torvalds 
1511f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
1512463a20fdSTakashi Iwai 	if (!substream->opened || !substream->runtime)
1513463a20fdSTakashi Iwai 		result = -EBADFD;
1514463a20fdSTakashi Iwai 	else
151506ab3003STakashi Iwai 		result = __snd_rawmidi_transmit_ack(substream, count);
1516f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
151706ab3003STakashi Iwai 	return result;
15181da177e4SLinus Torvalds }
15196776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
15201da177e4SLinus Torvalds 
15211da177e4SLinus Torvalds /**
15221da177e4SLinus Torvalds  * snd_rawmidi_transmit - copy from the buffer to the device
15231da177e4SLinus Torvalds  * @substream: the rawmidi substream
1524df8db936STakashi Iwai  * @buffer: the buffer pointer
15251da177e4SLinus Torvalds  * @count: the data size to transfer
15261da177e4SLinus Torvalds  *
15271da177e4SLinus Torvalds  * Copies data from the buffer to the device and advances the pointer.
15281da177e4SLinus Torvalds  *
1529eb7c06e8SYacine Belkadi  * Return: The copied size if successful, or a negative error code on failure.
15301da177e4SLinus Torvalds  */
snd_rawmidi_transmit(struct snd_rawmidi_substream * substream,unsigned char * buffer,int count)153148c9d417STakashi Iwai int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
153248c9d417STakashi Iwai 			 unsigned char *buffer, int count)
15331da177e4SLinus Torvalds {
153406ab3003STakashi Iwai 	int result;
153506ab3003STakashi Iwai 	unsigned long flags;
153606ab3003STakashi Iwai 
1537f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
1538219df32fSTakashi Iwai 	if (!substream->opened)
153906ab3003STakashi Iwai 		result = -EBADFD;
154006ab3003STakashi Iwai 	else {
154106ab3003STakashi Iwai 		count = __snd_rawmidi_transmit_peek(substream, buffer, count);
154206ab3003STakashi Iwai 		if (count <= 0)
154306ab3003STakashi Iwai 			result = count;
154406ab3003STakashi Iwai 		else
154506ab3003STakashi Iwai 			result = __snd_rawmidi_transmit_ack(substream, count);
154606ab3003STakashi Iwai 	}
1547f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
154806ab3003STakashi Iwai 	return result;
15491da177e4SLinus Torvalds }
15506776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_transmit);
15511da177e4SLinus Torvalds 
15526aea5702STakashi Iwai /**
15536aea5702STakashi Iwai  * snd_rawmidi_proceed - Discard the all pending bytes and proceed
15546aea5702STakashi Iwai  * @substream: rawmidi substream
15556aea5702STakashi Iwai  *
15566aea5702STakashi Iwai  * Return: the number of discarded bytes
15576aea5702STakashi Iwai  */
snd_rawmidi_proceed(struct snd_rawmidi_substream * substream)15586aea5702STakashi Iwai int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream)
15596aea5702STakashi Iwai {
1560463a20fdSTakashi Iwai 	struct snd_rawmidi_runtime *runtime;
15616aea5702STakashi Iwai 	unsigned long flags;
15626aea5702STakashi Iwai 	int count = 0;
15636aea5702STakashi Iwai 
1564f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
1565463a20fdSTakashi Iwai 	runtime = substream->runtime;
1566463a20fdSTakashi Iwai 	if (substream->opened && runtime &&
1567463a20fdSTakashi Iwai 	    runtime->avail < runtime->buffer_size) {
15686aea5702STakashi Iwai 		count = runtime->buffer_size - runtime->avail;
15696aea5702STakashi Iwai 		__snd_rawmidi_transmit_ack(substream, count);
15706aea5702STakashi Iwai 	}
1571f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
15726aea5702STakashi Iwai 	return count;
15736aea5702STakashi Iwai }
15746aea5702STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_proceed);
15756aea5702STakashi Iwai 
snd_rawmidi_kernel_write1(struct snd_rawmidi_substream * substream,const unsigned char __user * userbuf,const unsigned char * kernelbuf,long count)157648c9d417STakashi Iwai static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
157717596a80SMarcin Ślusarz 				      const unsigned char __user *userbuf,
157817596a80SMarcin Ślusarz 				      const unsigned char *kernelbuf,
157917596a80SMarcin Ślusarz 				      long count)
15801da177e4SLinus Torvalds {
15811da177e4SLinus Torvalds 	unsigned long flags;
15821da177e4SLinus Torvalds 	long count1, result;
158348c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime = substream->runtime;
158481f57754STakashi Iwai 	unsigned long appl_ptr;
15851da177e4SLinus Torvalds 
1586cc85f7a6STakashi Iwai 	if (!kernelbuf && !userbuf)
15877eaa943cSTakashi Iwai 		return -EINVAL;
15887eaa943cSTakashi Iwai 	if (snd_BUG_ON(!runtime->buffer))
15897eaa943cSTakashi Iwai 		return -EINVAL;
15901da177e4SLinus Torvalds 
15911da177e4SLinus Torvalds 	result = 0;
1592f1d40433STakashi Iwai 	spin_lock_irqsave(&substream->lock, flags);
15931da177e4SLinus Torvalds 	if (substream->append) {
15941da177e4SLinus Torvalds 		if ((long)runtime->avail < count) {
1595f1d40433STakashi Iwai 			spin_unlock_irqrestore(&substream->lock, flags);
15961da177e4SLinus Torvalds 			return -EAGAIN;
15971da177e4SLinus Torvalds 		}
15981da177e4SLinus Torvalds 	}
1599c1f6e3c8STakashi Iwai 	snd_rawmidi_buffer_ref(runtime);
16001da177e4SLinus Torvalds 	while (count > 0 && runtime->avail > 0) {
16011da177e4SLinus Torvalds 		count1 = runtime->buffer_size - runtime->appl_ptr;
16021da177e4SLinus Torvalds 		if (count1 > count)
16031da177e4SLinus Torvalds 			count1 = count;
16041da177e4SLinus Torvalds 		if (count1 > (long)runtime->avail)
16051da177e4SLinus Torvalds 			count1 = runtime->avail;
160681f57754STakashi Iwai 
160781f57754STakashi Iwai 		/* update runtime->appl_ptr before unlocking for userbuf */
160881f57754STakashi Iwai 		appl_ptr = runtime->appl_ptr;
160981f57754STakashi Iwai 		runtime->appl_ptr += count1;
161081f57754STakashi Iwai 		runtime->appl_ptr %= runtime->buffer_size;
161181f57754STakashi Iwai 		runtime->avail -= count1;
161281f57754STakashi Iwai 
161317596a80SMarcin Ślusarz 		if (kernelbuf)
161481f57754STakashi Iwai 			memcpy(runtime->buffer + appl_ptr,
161517596a80SMarcin Ślusarz 			       kernelbuf + result, count1);
161617596a80SMarcin Ślusarz 		else if (userbuf) {
1617f1d40433STakashi Iwai 			spin_unlock_irqrestore(&substream->lock, flags);
161881f57754STakashi Iwai 			if (copy_from_user(runtime->buffer + appl_ptr,
161917596a80SMarcin Ślusarz 					   userbuf + result, count1)) {
1620f1d40433STakashi Iwai 				spin_lock_irqsave(&substream->lock, flags);
16211da177e4SLinus Torvalds 				result = result > 0 ? result : -EFAULT;
16221da177e4SLinus Torvalds 				goto __end;
16231da177e4SLinus Torvalds 			}
1624f1d40433STakashi Iwai 			spin_lock_irqsave(&substream->lock, flags);
16251da177e4SLinus Torvalds 		}
16261da177e4SLinus Torvalds 		result += count1;
16271da177e4SLinus Torvalds 		count -= count1;
16281da177e4SLinus Torvalds 	}
16291da177e4SLinus Torvalds       __end:
16301da177e4SLinus Torvalds 	count1 = runtime->avail < runtime->buffer_size;
1631c1f6e3c8STakashi Iwai 	snd_rawmidi_buffer_unref(runtime);
1632f1d40433STakashi Iwai 	spin_unlock_irqrestore(&substream->lock, flags);
16331da177e4SLinus Torvalds 	if (count1)
16341da177e4SLinus Torvalds 		snd_rawmidi_output_trigger(substream, 1);
16351da177e4SLinus Torvalds 	return result;
16361da177e4SLinus Torvalds }
16371da177e4SLinus Torvalds 
snd_rawmidi_kernel_write(struct snd_rawmidi_substream * substream,const unsigned char * buf,long count)163848c9d417STakashi Iwai long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
163948c9d417STakashi Iwai 			      const unsigned char *buf, long count)
16401da177e4SLinus Torvalds {
164117596a80SMarcin Ślusarz 	return snd_rawmidi_kernel_write1(substream, NULL, buf, count);
16421da177e4SLinus Torvalds }
16436776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_kernel_write);
16441da177e4SLinus Torvalds 
snd_rawmidi_write(struct file * file,const char __user * buf,size_t count,loff_t * offset)164548c9d417STakashi Iwai static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
164648c9d417STakashi Iwai 				 size_t count, loff_t *offset)
16471da177e4SLinus Torvalds {
16481da177e4SLinus Torvalds 	long result, timeout;
16491da177e4SLinus Torvalds 	int count1;
165048c9d417STakashi Iwai 	struct snd_rawmidi_file *rfile;
165148c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime;
165248c9d417STakashi Iwai 	struct snd_rawmidi_substream *substream;
16531da177e4SLinus Torvalds 
16541da177e4SLinus Torvalds 	rfile = file->private_data;
16551da177e4SLinus Torvalds 	substream = rfile->output;
16561da177e4SLinus Torvalds 	runtime = substream->runtime;
16571da177e4SLinus Torvalds 	/* we cannot put an atomic message to our buffer */
16581da177e4SLinus Torvalds 	if (substream->append && count > runtime->buffer_size)
16591da177e4SLinus Torvalds 		return -EIO;
16601da177e4SLinus Torvalds 	result = 0;
16611da177e4SLinus Torvalds 	while (count > 0) {
1662f1d40433STakashi Iwai 		spin_lock_irq(&substream->lock);
16631da177e4SLinus Torvalds 		while (!snd_rawmidi_ready_append(substream, count)) {
1664ac6424b9SIngo Molnar 			wait_queue_entry_t wait;
16655bed9139STakashi Iwai 
16661da177e4SLinus Torvalds 			if (file->f_flags & O_NONBLOCK) {
1667f1d40433STakashi Iwai 				spin_unlock_irq(&substream->lock);
16681da177e4SLinus Torvalds 				return result > 0 ? result : -EAGAIN;
16691da177e4SLinus Torvalds 			}
16701da177e4SLinus Torvalds 			init_waitqueue_entry(&wait, current);
16711da177e4SLinus Torvalds 			add_wait_queue(&runtime->sleep, &wait);
16721da177e4SLinus Torvalds 			set_current_state(TASK_INTERRUPTIBLE);
1673f1d40433STakashi Iwai 			spin_unlock_irq(&substream->lock);
16741da177e4SLinus Torvalds 			timeout = schedule_timeout(30 * HZ);
16751da177e4SLinus Torvalds 			remove_wait_queue(&runtime->sleep, &wait);
16760914f796STakashi Iwai 			if (rfile->rmidi->card->shutdown)
16770914f796STakashi Iwai 				return -ENODEV;
16781da177e4SLinus Torvalds 			if (signal_pending(current))
16791da177e4SLinus Torvalds 				return result > 0 ? result : -ERESTARTSYS;
1680f1d40433STakashi Iwai 			spin_lock_irq(&substream->lock);
168188a06d6fSTakashi Iwai 			if (!runtime->avail && !timeout) {
1682f1d40433STakashi Iwai 				spin_unlock_irq(&substream->lock);
168388a06d6fSTakashi Iwai 				return result > 0 ? result : -EIO;
168488a06d6fSTakashi Iwai 			}
16851da177e4SLinus Torvalds 		}
1686f1d40433STakashi Iwai 		spin_unlock_irq(&substream->lock);
168717596a80SMarcin Ślusarz 		count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
16881da177e4SLinus Torvalds 		if (count1 < 0)
16891da177e4SLinus Torvalds 			return result > 0 ? result : count1;
16901da177e4SLinus Torvalds 		result += count1;
16911da177e4SLinus Torvalds 		buf += count1;
16921da177e4SLinus Torvalds 		if ((size_t)count1 < count && (file->f_flags & O_NONBLOCK))
16931da177e4SLinus Torvalds 			break;
16941da177e4SLinus Torvalds 		count -= count1;
16951da177e4SLinus Torvalds 	}
16966b2f3d1fSChristoph Hellwig 	if (file->f_flags & O_DSYNC) {
1697f1d40433STakashi Iwai 		spin_lock_irq(&substream->lock);
16981da177e4SLinus Torvalds 		while (runtime->avail != runtime->buffer_size) {
1699ac6424b9SIngo Molnar 			wait_queue_entry_t wait;
17001da177e4SLinus Torvalds 			unsigned int last_avail = runtime->avail;
17015bed9139STakashi Iwai 
17021da177e4SLinus Torvalds 			init_waitqueue_entry(&wait, current);
17031da177e4SLinus Torvalds 			add_wait_queue(&runtime->sleep, &wait);
17041da177e4SLinus Torvalds 			set_current_state(TASK_INTERRUPTIBLE);
1705f1d40433STakashi Iwai 			spin_unlock_irq(&substream->lock);
17061da177e4SLinus Torvalds 			timeout = schedule_timeout(30 * HZ);
17071da177e4SLinus Torvalds 			remove_wait_queue(&runtime->sleep, &wait);
17081da177e4SLinus Torvalds 			if (signal_pending(current))
17091da177e4SLinus Torvalds 				return result > 0 ? result : -ERESTARTSYS;
17101da177e4SLinus Torvalds 			if (runtime->avail == last_avail && !timeout)
17111da177e4SLinus Torvalds 				return result > 0 ? result : -EIO;
1712f1d40433STakashi Iwai 			spin_lock_irq(&substream->lock);
17131da177e4SLinus Torvalds 		}
1714f1d40433STakashi Iwai 		spin_unlock_irq(&substream->lock);
17151da177e4SLinus Torvalds 	}
17161da177e4SLinus Torvalds 	return result;
17171da177e4SLinus Torvalds }
17181da177e4SLinus Torvalds 
snd_rawmidi_poll(struct file * file,poll_table * wait)1719680ef72aSAl Viro static __poll_t snd_rawmidi_poll(struct file *file, poll_table *wait)
17201da177e4SLinus Torvalds {
172148c9d417STakashi Iwai 	struct snd_rawmidi_file *rfile;
172248c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime;
1723680ef72aSAl Viro 	__poll_t mask;
17241da177e4SLinus Torvalds 
17251da177e4SLinus Torvalds 	rfile = file->private_data;
17261da177e4SLinus Torvalds 	if (rfile->input != NULL) {
17271da177e4SLinus Torvalds 		runtime = rfile->input->runtime;
17281da177e4SLinus Torvalds 		snd_rawmidi_input_trigger(rfile->input, 1);
17291da177e4SLinus Torvalds 		poll_wait(file, &runtime->sleep, wait);
17301da177e4SLinus Torvalds 	}
17311da177e4SLinus Torvalds 	if (rfile->output != NULL) {
17321da177e4SLinus Torvalds 		runtime = rfile->output->runtime;
17331da177e4SLinus Torvalds 		poll_wait(file, &runtime->sleep, wait);
17341da177e4SLinus Torvalds 	}
17351da177e4SLinus Torvalds 	mask = 0;
17361da177e4SLinus Torvalds 	if (rfile->input != NULL) {
17371da177e4SLinus Torvalds 		if (snd_rawmidi_ready(rfile->input))
1738a9a08845SLinus Torvalds 			mask |= EPOLLIN | EPOLLRDNORM;
17391da177e4SLinus Torvalds 	}
17401da177e4SLinus Torvalds 	if (rfile->output != NULL) {
17411da177e4SLinus Torvalds 		if (snd_rawmidi_ready(rfile->output))
1742a9a08845SLinus Torvalds 			mask |= EPOLLOUT | EPOLLWRNORM;
17431da177e4SLinus Torvalds 	}
17441da177e4SLinus Torvalds 	return mask;
17451da177e4SLinus Torvalds }
17461da177e4SLinus Torvalds 
17471da177e4SLinus Torvalds /*
17481da177e4SLinus Torvalds  */
17491da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
17501da177e4SLinus Torvalds #include "rawmidi_compat.c"
17511da177e4SLinus Torvalds #else
17521da177e4SLinus Torvalds #define snd_rawmidi_ioctl_compat	NULL
17531da177e4SLinus Torvalds #endif
17541da177e4SLinus Torvalds 
17551da177e4SLinus Torvalds /*
17561da177e4SLinus Torvalds  */
17571da177e4SLinus Torvalds 
snd_rawmidi_proc_info_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)175848c9d417STakashi Iwai static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
175948c9d417STakashi Iwai 				       struct snd_info_buffer *buffer)
17601da177e4SLinus Torvalds {
176148c9d417STakashi Iwai 	struct snd_rawmidi *rmidi;
176248c9d417STakashi Iwai 	struct snd_rawmidi_substream *substream;
176348c9d417STakashi Iwai 	struct snd_rawmidi_runtime *runtime;
176488a06d6fSTakashi Iwai 	unsigned long buffer_size, avail, xruns;
176508fdced6SDavid Henningsson 	unsigned int clock_type;
176608fdced6SDavid Henningsson 	static const char *clock_names[4] = { "none", "realtime", "monotonic", "monotonic raw" };
17671da177e4SLinus Torvalds 
17681da177e4SLinus Torvalds 	rmidi = entry->private_data;
17691da177e4SLinus Torvalds 	snd_iprintf(buffer, "%s\n\n", rmidi->name);
1770e3a8a5b7STakashi Iwai 	if (IS_ENABLED(CONFIG_SND_UMP))
1771e3a8a5b7STakashi Iwai 		snd_iprintf(buffer, "Type: %s\n",
1772e3a8a5b7STakashi Iwai 			    rawmidi_is_ump(rmidi) ? "UMP" : "Legacy");
1773*b2ce0027STakashi Iwai 	if (rmidi->ops && rmidi->ops->proc_read)
1774fa030f66STakashi Iwai 		rmidi->ops->proc_read(entry, buffer);
17751a60d4c5SIngo Molnar 	mutex_lock(&rmidi->open_mutex);
17761da177e4SLinus Torvalds 	if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
17779244b2c3SJohannes Berg 		list_for_each_entry(substream,
17789244b2c3SJohannes Berg 				    &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
17799244b2c3SJohannes Berg 				    list) {
17801da177e4SLinus Torvalds 			snd_iprintf(buffer,
17811da177e4SLinus Torvalds 				    "Output %d\n"
17821da177e4SLinus Torvalds 				    "  Tx bytes     : %lu\n",
17831da177e4SLinus Torvalds 				    substream->number,
17841da177e4SLinus Torvalds 				    (unsigned long) substream->bytes);
17851da177e4SLinus Torvalds 			if (substream->opened) {
17867584af10SClemens Ladisch 				snd_iprintf(buffer,
17877584af10SClemens Ladisch 				    "  Owner PID    : %d\n",
17887584af10SClemens Ladisch 				    pid_vnr(substream->pid));
17891da177e4SLinus Torvalds 				runtime = substream->runtime;
1790f1d40433STakashi Iwai 				spin_lock_irq(&substream->lock);
179188a06d6fSTakashi Iwai 				buffer_size = runtime->buffer_size;
179288a06d6fSTakashi Iwai 				avail = runtime->avail;
1793f1d40433STakashi Iwai 				spin_unlock_irq(&substream->lock);
17941da177e4SLinus Torvalds 				snd_iprintf(buffer,
17951da177e4SLinus Torvalds 				    "  Mode         : %s\n"
17961da177e4SLinus Torvalds 				    "  Buffer size  : %lu\n"
17971da177e4SLinus Torvalds 				    "  Avail        : %lu\n",
17981da177e4SLinus Torvalds 				    runtime->oss ? "OSS compatible" : "native",
179988a06d6fSTakashi Iwai 				    buffer_size, avail);
18001da177e4SLinus Torvalds 			}
18011da177e4SLinus Torvalds 		}
18021da177e4SLinus Torvalds 	}
18031da177e4SLinus Torvalds 	if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) {
18049244b2c3SJohannes Berg 		list_for_each_entry(substream,
18059244b2c3SJohannes Berg 				    &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams,
18069244b2c3SJohannes Berg 				    list) {
18071da177e4SLinus Torvalds 			snd_iprintf(buffer,
18081da177e4SLinus Torvalds 				    "Input %d\n"
18091da177e4SLinus Torvalds 				    "  Rx bytes     : %lu\n",
18101da177e4SLinus Torvalds 				    substream->number,
18111da177e4SLinus Torvalds 				    (unsigned long) substream->bytes);
18121da177e4SLinus Torvalds 			if (substream->opened) {
18137584af10SClemens Ladisch 				snd_iprintf(buffer,
18147584af10SClemens Ladisch 					    "  Owner PID    : %d\n",
18157584af10SClemens Ladisch 					    pid_vnr(substream->pid));
18161da177e4SLinus Torvalds 				runtime = substream->runtime;
1817f1d40433STakashi Iwai 				spin_lock_irq(&substream->lock);
181888a06d6fSTakashi Iwai 				buffer_size = runtime->buffer_size;
181988a06d6fSTakashi Iwai 				avail = runtime->avail;
182088a06d6fSTakashi Iwai 				xruns = runtime->xruns;
1821f1d40433STakashi Iwai 				spin_unlock_irq(&substream->lock);
18221da177e4SLinus Torvalds 				snd_iprintf(buffer,
18231da177e4SLinus Torvalds 					    "  Buffer size  : %lu\n"
18241da177e4SLinus Torvalds 					    "  Avail        : %lu\n"
18251da177e4SLinus Torvalds 					    "  Overruns     : %lu\n",
182688a06d6fSTakashi Iwai 					    buffer_size, avail, xruns);
182708fdced6SDavid Henningsson 				if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
182808fdced6SDavid Henningsson 					clock_type = substream->clock_type >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT;
1829a6de7b32SColin Ian King 					if (!snd_BUG_ON(clock_type >= ARRAY_SIZE(clock_names)))
183008fdced6SDavid Henningsson 						snd_iprintf(buffer,
183108fdced6SDavid Henningsson 							    "  Framing      : tstamp\n"
183208fdced6SDavid Henningsson 							    "  Clock type   : %s\n",
183308fdced6SDavid Henningsson 							    clock_names[clock_type]);
183408fdced6SDavid Henningsson 				}
18351da177e4SLinus Torvalds 			}
18361da177e4SLinus Torvalds 		}
18371da177e4SLinus Torvalds 	}
18381a60d4c5SIngo Molnar 	mutex_unlock(&rmidi->open_mutex);
18391da177e4SLinus Torvalds }
18401da177e4SLinus Torvalds 
18411da177e4SLinus Torvalds /*
18421da177e4SLinus Torvalds  *  Register functions
18431da177e4SLinus Torvalds  */
18441da177e4SLinus Torvalds 
18455bed9139STakashi Iwai static const struct file_operations snd_rawmidi_f_ops = {
18461da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
18471da177e4SLinus Torvalds 	.read =		snd_rawmidi_read,
18481da177e4SLinus Torvalds 	.write =	snd_rawmidi_write,
18491da177e4SLinus Torvalds 	.open =		snd_rawmidi_open,
18501da177e4SLinus Torvalds 	.release =	snd_rawmidi_release,
185102f4865fSTakashi Iwai 	.llseek =	no_llseek,
18521da177e4SLinus Torvalds 	.poll =		snd_rawmidi_poll,
18531da177e4SLinus Torvalds 	.unlocked_ioctl =	snd_rawmidi_ioctl,
18541da177e4SLinus Torvalds 	.compat_ioctl =	snd_rawmidi_ioctl_compat,
18551da177e4SLinus Torvalds };
18561da177e4SLinus Torvalds 
snd_rawmidi_alloc_substreams(struct snd_rawmidi * rmidi,struct snd_rawmidi_str * stream,int direction,int count)185748c9d417STakashi Iwai static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
185848c9d417STakashi Iwai 					struct snd_rawmidi_str *stream,
18591da177e4SLinus Torvalds 					int direction,
18601da177e4SLinus Torvalds 					int count)
18611da177e4SLinus Torvalds {
186248c9d417STakashi Iwai 	struct snd_rawmidi_substream *substream;
18631da177e4SLinus Torvalds 	int idx;
18641da177e4SLinus Torvalds 
18651da177e4SLinus Torvalds 	for (idx = 0; idx < count; idx++) {
1866ca2c0966STakashi Iwai 		substream = kzalloc(sizeof(*substream), GFP_KERNEL);
1867ec0e9937STakashi Iwai 		if (!substream)
18681da177e4SLinus Torvalds 			return -ENOMEM;
18691da177e4SLinus Torvalds 		substream->stream = direction;
18701da177e4SLinus Torvalds 		substream->number = idx;
18711da177e4SLinus Torvalds 		substream->rmidi = rmidi;
18721da177e4SLinus Torvalds 		substream->pstr = stream;
1873f1d40433STakashi Iwai 		spin_lock_init(&substream->lock);
18741da177e4SLinus Torvalds 		list_add_tail(&substream->list, &stream->substreams);
18751da177e4SLinus Torvalds 		stream->substream_count++;
18761da177e4SLinus Torvalds 	}
18771da177e4SLinus Torvalds 	return 0;
18781da177e4SLinus Torvalds }
18791da177e4SLinus Torvalds 
1880e3a8a5b7STakashi Iwai /* used for both rawmidi and ump */
snd_rawmidi_init(struct snd_rawmidi * rmidi,struct snd_card * card,char * id,int device,int output_count,int input_count,unsigned int info_flags)1881e3a8a5b7STakashi Iwai int snd_rawmidi_init(struct snd_rawmidi *rmidi,
1882e3a8a5b7STakashi Iwai 		     struct snd_card *card, char *id, int device,
1883e3a8a5b7STakashi Iwai 		     int output_count, int input_count,
1884e3a8a5b7STakashi Iwai 		     unsigned int info_flags)
1885e3a8a5b7STakashi Iwai {
1886e3a8a5b7STakashi Iwai 	int err;
1887e3a8a5b7STakashi Iwai 	static const struct snd_device_ops ops = {
1888e3a8a5b7STakashi Iwai 		.dev_free = snd_rawmidi_dev_free,
1889e3a8a5b7STakashi Iwai 		.dev_register = snd_rawmidi_dev_register,
1890e3a8a5b7STakashi Iwai 		.dev_disconnect = snd_rawmidi_dev_disconnect,
1891e3a8a5b7STakashi Iwai 	};
1892e3a8a5b7STakashi Iwai 
1893e3a8a5b7STakashi Iwai 	rmidi->card = card;
1894e3a8a5b7STakashi Iwai 	rmidi->device = device;
1895e3a8a5b7STakashi Iwai 	mutex_init(&rmidi->open_mutex);
1896e3a8a5b7STakashi Iwai 	init_waitqueue_head(&rmidi->open_wait);
1897e3a8a5b7STakashi Iwai 	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
1898e3a8a5b7STakashi Iwai 	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
1899e3a8a5b7STakashi Iwai 	rmidi->info_flags = info_flags;
1900e3a8a5b7STakashi Iwai 
1901e3a8a5b7STakashi Iwai 	if (id != NULL)
1902e3a8a5b7STakashi Iwai 		strscpy(rmidi->id, id, sizeof(rmidi->id));
1903e3a8a5b7STakashi Iwai 
1904ea29a02fSTakashi Iwai 	err = snd_device_alloc(&rmidi->dev, card);
1905ea29a02fSTakashi Iwai 	if (err < 0)
1906ea29a02fSTakashi Iwai 		return err;
1907e3a8a5b7STakashi Iwai 	if (rawmidi_is_ump(rmidi))
1908ea29a02fSTakashi Iwai 		dev_set_name(rmidi->dev, "umpC%iD%i", card->number, device);
1909e3a8a5b7STakashi Iwai 	else
1910ea29a02fSTakashi Iwai 		dev_set_name(rmidi->dev, "midiC%iD%i", card->number, device);
1911e3a8a5b7STakashi Iwai 
1912e3a8a5b7STakashi Iwai 	err = snd_rawmidi_alloc_substreams(rmidi,
1913e3a8a5b7STakashi Iwai 					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
1914e3a8a5b7STakashi Iwai 					   SNDRV_RAWMIDI_STREAM_INPUT,
1915e3a8a5b7STakashi Iwai 					   input_count);
1916e3a8a5b7STakashi Iwai 	if (err < 0)
1917e3a8a5b7STakashi Iwai 		return err;
1918e3a8a5b7STakashi Iwai 	err = snd_rawmidi_alloc_substreams(rmidi,
1919e3a8a5b7STakashi Iwai 					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
1920e3a8a5b7STakashi Iwai 					   SNDRV_RAWMIDI_STREAM_OUTPUT,
1921e3a8a5b7STakashi Iwai 					   output_count);
1922e3a8a5b7STakashi Iwai 	if (err < 0)
1923e3a8a5b7STakashi Iwai 		return err;
1924e3a8a5b7STakashi Iwai 	err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
1925e3a8a5b7STakashi Iwai 	if (err < 0)
1926e3a8a5b7STakashi Iwai 		return err;
1927e3a8a5b7STakashi Iwai 	return 0;
1928e3a8a5b7STakashi Iwai }
1929e3a8a5b7STakashi Iwai EXPORT_SYMBOL_GPL(snd_rawmidi_init);
1930e3a8a5b7STakashi Iwai 
19311da177e4SLinus Torvalds /**
19321da177e4SLinus Torvalds  * snd_rawmidi_new - create a rawmidi instance
19331da177e4SLinus Torvalds  * @card: the card instance
19341da177e4SLinus Torvalds  * @id: the id string
19351da177e4SLinus Torvalds  * @device: the device index
19361da177e4SLinus Torvalds  * @output_count: the number of output streams
19371da177e4SLinus Torvalds  * @input_count: the number of input streams
19381da177e4SLinus Torvalds  * @rrawmidi: the pointer to store the new rawmidi instance
19391da177e4SLinus Torvalds  *
19401da177e4SLinus Torvalds  * Creates a new rawmidi instance.
19411da177e4SLinus Torvalds  * Use snd_rawmidi_set_ops() to set the operators to the new instance.
19421da177e4SLinus Torvalds  *
1943eb7c06e8SYacine Belkadi  * Return: Zero if successful, or a negative error code on failure.
19441da177e4SLinus Torvalds  */
snd_rawmidi_new(struct snd_card * card,char * id,int device,int output_count,int input_count,struct snd_rawmidi ** rrawmidi)194548c9d417STakashi Iwai int snd_rawmidi_new(struct snd_card *card, char *id, int device,
19461da177e4SLinus Torvalds 		    int output_count, int input_count,
194748c9d417STakashi Iwai 		    struct snd_rawmidi **rrawmidi)
19481da177e4SLinus Torvalds {
194948c9d417STakashi Iwai 	struct snd_rawmidi *rmidi;
19501da177e4SLinus Torvalds 	int err;
19511da177e4SLinus Torvalds 
19527eaa943cSTakashi Iwai 	if (rrawmidi)
19531da177e4SLinus Torvalds 		*rrawmidi = NULL;
1954ca2c0966STakashi Iwai 	rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
1955ec0e9937STakashi Iwai 	if (!rmidi)
19561da177e4SLinus Torvalds 		return -ENOMEM;
1957e3a8a5b7STakashi Iwai 	err = snd_rawmidi_init(rmidi, card, id, device,
1958e3a8a5b7STakashi Iwai 			       output_count, input_count, 0);
1959e3a8a5b7STakashi Iwai 	if (err < 0) {
1960e3a8a5b7STakashi Iwai 		snd_rawmidi_free(rmidi);
1961e3a8a5b7STakashi Iwai 		return err;
1962e3a8a5b7STakashi Iwai 	}
19637eaa943cSTakashi Iwai 	if (rrawmidi)
19641da177e4SLinus Torvalds 		*rrawmidi = rmidi;
19651da177e4SLinus Torvalds 	return 0;
19661da177e4SLinus Torvalds }
19676776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_new);
19681da177e4SLinus Torvalds 
snd_rawmidi_free_substreams(struct snd_rawmidi_str * stream)196948c9d417STakashi Iwai static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
19701da177e4SLinus Torvalds {
197148c9d417STakashi Iwai 	struct snd_rawmidi_substream *substream;
19721da177e4SLinus Torvalds 
19731da177e4SLinus Torvalds 	while (!list_empty(&stream->substreams)) {
197448c9d417STakashi Iwai 		substream = list_entry(stream->substreams.next, struct snd_rawmidi_substream, list);
19751da177e4SLinus Torvalds 		list_del(&substream->list);
19761da177e4SLinus Torvalds 		kfree(substream);
19771da177e4SLinus Torvalds 	}
19781da177e4SLinus Torvalds }
19791da177e4SLinus Torvalds 
1980e3a8a5b7STakashi Iwai /* called from ump.c, too */
snd_rawmidi_free(struct snd_rawmidi * rmidi)1981e3a8a5b7STakashi Iwai int snd_rawmidi_free(struct snd_rawmidi *rmidi)
19821da177e4SLinus Torvalds {
19837eaa943cSTakashi Iwai 	if (!rmidi)
19847eaa943cSTakashi Iwai 		return 0;
1985c461482cSTakashi Iwai 
1986c461482cSTakashi Iwai 	snd_info_free_entry(rmidi->proc_entry);
1987c461482cSTakashi Iwai 	rmidi->proc_entry = NULL;
1988c461482cSTakashi Iwai 	if (rmidi->ops && rmidi->ops->dev_unregister)
1989c461482cSTakashi Iwai 		rmidi->ops->dev_unregister(rmidi);
1990c461482cSTakashi Iwai 
19911da177e4SLinus Torvalds 	snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
19921da177e4SLinus Torvalds 	snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
19931da177e4SLinus Torvalds 	if (rmidi->private_free)
19941da177e4SLinus Torvalds 		rmidi->private_free(rmidi);
1995ea29a02fSTakashi Iwai 	put_device(rmidi->dev);
1996ea29a02fSTakashi Iwai 	kfree(rmidi);
19971da177e4SLinus Torvalds 	return 0;
19981da177e4SLinus Torvalds }
1999e3a8a5b7STakashi Iwai EXPORT_SYMBOL_GPL(snd_rawmidi_free);
20001da177e4SLinus Torvalds 
snd_rawmidi_dev_free(struct snd_device * device)200148c9d417STakashi Iwai static int snd_rawmidi_dev_free(struct snd_device *device)
20021da177e4SLinus Torvalds {
200348c9d417STakashi Iwai 	struct snd_rawmidi *rmidi = device->device_data;
20045bed9139STakashi Iwai 
20051da177e4SLinus Torvalds 	return snd_rawmidi_free(rmidi);
20061da177e4SLinus Torvalds }
20071da177e4SLinus Torvalds 
2008111b0cdbSTakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
snd_rawmidi_dev_seq_free(struct snd_seq_device * device)200948c9d417STakashi Iwai static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device)
20101da177e4SLinus Torvalds {
201148c9d417STakashi Iwai 	struct snd_rawmidi *rmidi = device->private_data;
20125bed9139STakashi Iwai 
20131da177e4SLinus Torvalds 	rmidi->seq_dev = NULL;
20141da177e4SLinus Torvalds }
20151da177e4SLinus Torvalds #endif
20161da177e4SLinus Torvalds 
snd_rawmidi_dev_register(struct snd_device * device)201748c9d417STakashi Iwai static int snd_rawmidi_dev_register(struct snd_device *device)
20181da177e4SLinus Torvalds {
2019f87135f5SClemens Ladisch 	int err;
202048c9d417STakashi Iwai 	struct snd_info_entry *entry;
20211da177e4SLinus Torvalds 	char name[16];
202248c9d417STakashi Iwai 	struct snd_rawmidi *rmidi = device->device_data;
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds 	if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
20251da177e4SLinus Torvalds 		return -ENOMEM;
20267fdc9b08STakashi Iwai 	err = 0;
20271a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
20287fdc9b08STakashi Iwai 	if (snd_rawmidi_search(rmidi->card, rmidi->device))
20297fdc9b08STakashi Iwai 		err = -EBUSY;
20307fdc9b08STakashi Iwai 	else
2031f87135f5SClemens Ladisch 		list_add_tail(&rmidi->list, &snd_rawmidi_devices);
2032816f318bSTakashi Iwai 	mutex_unlock(&register_mutex);
20337fdc9b08STakashi Iwai 	if (err < 0)
20347fdc9b08STakashi Iwai 		return err;
20357fdc9b08STakashi Iwai 
203640a4b263STakashi Iwai 	err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
20371da177e4SLinus Torvalds 				  rmidi->card, rmidi->device,
2038ea29a02fSTakashi Iwai 				  &snd_rawmidi_f_ops, rmidi, rmidi->dev);
2039aee5012fSTakashi Iwai 	if (err < 0) {
2040aee5012fSTakashi Iwai 		rmidi_err(rmidi, "unable to register\n");
20417fdc9b08STakashi Iwai 		goto error;
20421da177e4SLinus Torvalds 	}
20437fdc9b08STakashi Iwai 	if (rmidi->ops && rmidi->ops->dev_register) {
20447fdc9b08STakashi Iwai 		err = rmidi->ops->dev_register(rmidi);
20457fdc9b08STakashi Iwai 		if (err < 0)
20467fdc9b08STakashi Iwai 			goto error_unregister;
20471da177e4SLinus Torvalds 	}
20481da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
20491da177e4SLinus Torvalds 	rmidi->ossreg = 0;
2050e3a8a5b7STakashi Iwai 	if (!rawmidi_is_ump(rmidi) &&
2051e3a8a5b7STakashi Iwai 	    (int)rmidi->device == midi_map[rmidi->card->number]) {
20521da177e4SLinus Torvalds 		if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
2053f87135f5SClemens Ladisch 					    rmidi->card, 0, &snd_rawmidi_f_ops,
205480d7d771STakashi Iwai 					    rmidi) < 0) {
2055ca20d292STakashi Iwai 			rmidi_err(rmidi,
2056ca20d292STakashi Iwai 				  "unable to register OSS rawmidi device %i:%i\n",
2057ca20d292STakashi Iwai 				  rmidi->card->number, 0);
20581da177e4SLinus Torvalds 		} else {
20591da177e4SLinus Torvalds 			rmidi->ossreg++;
20601da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_MIDI
20611da177e4SLinus Torvalds 			snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name);
20621da177e4SLinus Torvalds #endif
20631da177e4SLinus Torvalds 		}
20641da177e4SLinus Torvalds 	}
2065e3a8a5b7STakashi Iwai 	if (!rawmidi_is_ump(rmidi) &&
2066e3a8a5b7STakashi Iwai 	    (int)rmidi->device == amidi_map[rmidi->card->number]) {
20671da177e4SLinus Torvalds 		if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
2068f87135f5SClemens Ladisch 					    rmidi->card, 1, &snd_rawmidi_f_ops,
206980d7d771STakashi Iwai 					    rmidi) < 0) {
2070ca20d292STakashi Iwai 			rmidi_err(rmidi,
2071ca20d292STakashi Iwai 				  "unable to register OSS rawmidi device %i:%i\n",
2072ca20d292STakashi Iwai 				  rmidi->card->number, 1);
20731da177e4SLinus Torvalds 		} else {
20741da177e4SLinus Torvalds 			rmidi->ossreg++;
20751da177e4SLinus Torvalds 		}
20761da177e4SLinus Torvalds 	}
20771da177e4SLinus Torvalds #endif /* CONFIG_SND_OSSEMUL */
20781da177e4SLinus Torvalds 	sprintf(name, "midi%d", rmidi->device);
20791da177e4SLinus Torvalds 	entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
20801da177e4SLinus Torvalds 	if (entry) {
20811da177e4SLinus Torvalds 		entry->private_data = rmidi;
20821da177e4SLinus Torvalds 		entry->c.text.read = snd_rawmidi_proc_info_read;
20831da177e4SLinus Torvalds 		if (snd_info_register(entry) < 0) {
20841da177e4SLinus Torvalds 			snd_info_free_entry(entry);
20851da177e4SLinus Torvalds 			entry = NULL;
20861da177e4SLinus Torvalds 		}
20871da177e4SLinus Torvalds 	}
20881da177e4SLinus Torvalds 	rmidi->proc_entry = entry;
2089111b0cdbSTakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
2090e3a8a5b7STakashi Iwai 	/* no own registration mechanism? */
2091e3a8a5b7STakashi Iwai 	if (!rmidi->ops || !rmidi->ops->dev_register) {
20921da177e4SLinus Torvalds 		if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
20931da177e4SLinus Torvalds 			rmidi->seq_dev->private_data = rmidi;
20941da177e4SLinus Torvalds 			rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
20951da177e4SLinus Torvalds 			sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device);
20961da177e4SLinus Torvalds 			snd_device_register(rmidi->card, rmidi->seq_dev);
20971da177e4SLinus Torvalds 		}
20981da177e4SLinus Torvalds 	}
20991da177e4SLinus Torvalds #endif
21001da177e4SLinus Torvalds 	return 0;
21017fdc9b08STakashi Iwai 
21027fdc9b08STakashi Iwai  error_unregister:
2103ea29a02fSTakashi Iwai 	snd_unregister_device(rmidi->dev);
21047fdc9b08STakashi Iwai  error:
21057fdc9b08STakashi Iwai 	mutex_lock(&register_mutex);
21067fdc9b08STakashi Iwai 	list_del(&rmidi->list);
21077fdc9b08STakashi Iwai 	mutex_unlock(&register_mutex);
21087fdc9b08STakashi Iwai 	return err;
21091da177e4SLinus Torvalds }
21101da177e4SLinus Torvalds 
snd_rawmidi_dev_disconnect(struct snd_device * device)211148c9d417STakashi Iwai static int snd_rawmidi_dev_disconnect(struct snd_device *device)
21121da177e4SLinus Torvalds {
211348c9d417STakashi Iwai 	struct snd_rawmidi *rmidi = device->device_data;
21140914f796STakashi Iwai 	int dir;
21151da177e4SLinus Torvalds 
21161a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
21170914f796STakashi Iwai 	mutex_lock(&rmidi->open_mutex);
21180914f796STakashi Iwai 	wake_up(&rmidi->open_wait);
2119f87135f5SClemens Ladisch 	list_del_init(&rmidi->list);
21200914f796STakashi Iwai 	for (dir = 0; dir < 2; dir++) {
21210914f796STakashi Iwai 		struct snd_rawmidi_substream *s;
21225bed9139STakashi Iwai 
21230914f796STakashi Iwai 		list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
21240914f796STakashi Iwai 			if (s->runtime)
21250914f796STakashi Iwai 				wake_up(&s->runtime->sleep);
21260914f796STakashi Iwai 		}
21270914f796STakashi Iwai 	}
21280914f796STakashi Iwai 
21291da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
21301da177e4SLinus Torvalds 	if (rmidi->ossreg) {
21311da177e4SLinus Torvalds 		if ((int)rmidi->device == midi_map[rmidi->card->number]) {
21321da177e4SLinus Torvalds 			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0);
21331da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_MIDI
21341da177e4SLinus Torvalds 			snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number);
21351da177e4SLinus Torvalds #endif
21361da177e4SLinus Torvalds 		}
21371da177e4SLinus Torvalds 		if ((int)rmidi->device == amidi_map[rmidi->card->number])
21381da177e4SLinus Torvalds 			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1);
21391da177e4SLinus Torvalds 		rmidi->ossreg = 0;
21401da177e4SLinus Torvalds 	}
21411da177e4SLinus Torvalds #endif /* CONFIG_SND_OSSEMUL */
2142ea29a02fSTakashi Iwai 	snd_unregister_device(rmidi->dev);
21430914f796STakashi Iwai 	mutex_unlock(&rmidi->open_mutex);
21441a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
2145c461482cSTakashi Iwai 	return 0;
21461da177e4SLinus Torvalds }
21471da177e4SLinus Torvalds 
21481da177e4SLinus Torvalds /**
21491da177e4SLinus Torvalds  * snd_rawmidi_set_ops - set the rawmidi operators
21501da177e4SLinus Torvalds  * @rmidi: the rawmidi instance
21511da177e4SLinus Torvalds  * @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX
21521da177e4SLinus Torvalds  * @ops: the operator table
21531da177e4SLinus Torvalds  *
21541da177e4SLinus Torvalds  * Sets the rawmidi operators for the given stream direction.
21551da177e4SLinus Torvalds  */
snd_rawmidi_set_ops(struct snd_rawmidi * rmidi,int stream,const struct snd_rawmidi_ops * ops)215648c9d417STakashi Iwai void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
21576ba79b85STakashi Iwai 			 const struct snd_rawmidi_ops *ops)
21581da177e4SLinus Torvalds {
215948c9d417STakashi Iwai 	struct snd_rawmidi_substream *substream;
21601da177e4SLinus Torvalds 
21619244b2c3SJohannes Berg 	list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)
21621da177e4SLinus Torvalds 		substream->ops = ops;
21631da177e4SLinus Torvalds }
21646776a5d7STakashi Iwai EXPORT_SYMBOL(snd_rawmidi_set_ops);
21651da177e4SLinus Torvalds 
21661da177e4SLinus Torvalds /*
21671da177e4SLinus Torvalds  *  ENTRY functions
21681da177e4SLinus Torvalds  */
21691da177e4SLinus Torvalds 
alsa_rawmidi_init(void)21701da177e4SLinus Torvalds static int __init alsa_rawmidi_init(void)
21711da177e4SLinus Torvalds {
21721da177e4SLinus Torvalds 
21731da177e4SLinus Torvalds 	snd_ctl_register_ioctl(snd_rawmidi_control_ioctl);
21741da177e4SLinus Torvalds 	snd_ctl_register_ioctl_compat(snd_rawmidi_control_ioctl);
21751da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
21761da177e4SLinus Torvalds 	{ int i;
21771da177e4SLinus Torvalds 	/* check device map table */
21781da177e4SLinus Torvalds 	for (i = 0; i < SNDRV_CARDS; i++) {
21791da177e4SLinus Torvalds 		if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
2180ca20d292STakashi Iwai 			pr_err("ALSA: rawmidi: invalid midi_map[%d] = %d\n",
2181ca20d292STakashi Iwai 			       i, midi_map[i]);
21821da177e4SLinus Torvalds 			midi_map[i] = 0;
21831da177e4SLinus Torvalds 		}
21841da177e4SLinus Torvalds 		if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
2185ca20d292STakashi Iwai 			pr_err("ALSA: rawmidi: invalid amidi_map[%d] = %d\n",
2186ca20d292STakashi Iwai 			       i, amidi_map[i]);
21871da177e4SLinus Torvalds 			amidi_map[i] = 1;
21881da177e4SLinus Torvalds 		}
21891da177e4SLinus Torvalds 	}
21901da177e4SLinus Torvalds 	}
21911da177e4SLinus Torvalds #endif /* CONFIG_SND_OSSEMUL */
21921da177e4SLinus Torvalds 	return 0;
21931da177e4SLinus Torvalds }
21941da177e4SLinus Torvalds 
alsa_rawmidi_exit(void)21951da177e4SLinus Torvalds static void __exit alsa_rawmidi_exit(void)
21961da177e4SLinus Torvalds {
21971da177e4SLinus Torvalds 	snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl);
21981da177e4SLinus Torvalds 	snd_ctl_unregister_ioctl_compat(snd_rawmidi_control_ioctl);
21991da177e4SLinus Torvalds }
22001da177e4SLinus Torvalds 
22011da177e4SLinus Torvalds module_init(alsa_rawmidi_init)
22021da177e4SLinus Torvalds module_exit(alsa_rawmidi_exit)
2203