xref: /openbmc/linux/sound/core/seq/seq_ump_convert.c (revision b89c2c56)
1e9e02819STakashi Iwai // SPDX-License-Identifier: GPL-2.0-or-later
2e9e02819STakashi Iwai /*
3e9e02819STakashi Iwai  * ALSA sequencer event conversion between UMP and legacy clients
4e9e02819STakashi Iwai  */
5e9e02819STakashi Iwai 
6e9e02819STakashi Iwai #include <linux/init.h>
7e9e02819STakashi Iwai #include <linux/errno.h>
8e9e02819STakashi Iwai #include <linux/string.h>
9e9e02819STakashi Iwai #include <sound/core.h>
10e9e02819STakashi Iwai #include <sound/ump.h>
11e9e02819STakashi Iwai #include <sound/ump_msg.h>
12e9e02819STakashi Iwai #include "seq_ump_convert.h"
13e9e02819STakashi Iwai 
14e9e02819STakashi Iwai /*
15e9e02819STakashi Iwai  * Upgrade / downgrade value bits
16e9e02819STakashi Iwai  */
downscale_32_to_7bit(u32 src)17e9e02819STakashi Iwai static u8 downscale_32_to_7bit(u32 src)
18e9e02819STakashi Iwai {
19e9e02819STakashi Iwai 	return src >> 25;
20e9e02819STakashi Iwai }
21e9e02819STakashi Iwai 
downscale_32_to_14bit(u32 src)22e9e02819STakashi Iwai static u16 downscale_32_to_14bit(u32 src)
23e9e02819STakashi Iwai {
24e9e02819STakashi Iwai 	return src >> 18;
25e9e02819STakashi Iwai }
26e9e02819STakashi Iwai 
downscale_16_to_7bit(u16 src)27e9e02819STakashi Iwai static u8 downscale_16_to_7bit(u16 src)
28e9e02819STakashi Iwai {
29e9e02819STakashi Iwai 	return src >> 9;
30e9e02819STakashi Iwai }
31e9e02819STakashi Iwai 
upscale_7_to_16bit(u8 src)32e9e02819STakashi Iwai static u16 upscale_7_to_16bit(u8 src)
33e9e02819STakashi Iwai {
34e9e02819STakashi Iwai 	u16 val, repeat;
35e9e02819STakashi Iwai 
36e9e02819STakashi Iwai 	val = (u16)src << 9;
37e9e02819STakashi Iwai 	if (src <= 0x40)
38e9e02819STakashi Iwai 		return val;
39e9e02819STakashi Iwai 	repeat = src & 0x3f;
40e9e02819STakashi Iwai 	return val | (repeat << 3) | (repeat >> 3);
41e9e02819STakashi Iwai }
42e9e02819STakashi Iwai 
upscale_7_to_32bit(u8 src)43e9e02819STakashi Iwai static u32 upscale_7_to_32bit(u8 src)
44e9e02819STakashi Iwai {
45e9e02819STakashi Iwai 	u32 val, repeat;
46e9e02819STakashi Iwai 
47e9e02819STakashi Iwai 	val = src << 25;
48e9e02819STakashi Iwai 	if (src <= 0x40)
49e9e02819STakashi Iwai 		return val;
50e9e02819STakashi Iwai 	repeat = src & 0x3f;
51e9e02819STakashi Iwai 	return val | (repeat << 19) | (repeat << 13) |
52e9e02819STakashi Iwai 		(repeat << 7) | (repeat << 1) | (repeat >> 5);
53e9e02819STakashi Iwai }
54e9e02819STakashi Iwai 
upscale_14_to_32bit(u16 src)55e9e02819STakashi Iwai static u32 upscale_14_to_32bit(u16 src)
56e9e02819STakashi Iwai {
57e9e02819STakashi Iwai 	u32 val, repeat;
58e9e02819STakashi Iwai 
59e9e02819STakashi Iwai 	val = src << 18;
60e9e02819STakashi Iwai 	if (src <= 0x2000)
61e9e02819STakashi Iwai 		return val;
62e9e02819STakashi Iwai 	repeat = src & 0x1fff;
63e9e02819STakashi Iwai 	return val | (repeat << 5) | (repeat >> 8);
64e9e02819STakashi Iwai }
65e9e02819STakashi Iwai 
get_ump_group(struct snd_seq_client_port * port)66e9e02819STakashi Iwai static unsigned char get_ump_group(struct snd_seq_client_port *port)
67e9e02819STakashi Iwai {
68e9e02819STakashi Iwai 	return port->ump_group ? (port->ump_group - 1) : 0;
69e9e02819STakashi Iwai }
70e9e02819STakashi Iwai 
71e9e02819STakashi Iwai /* create a UMP header */
72e9e02819STakashi Iwai #define make_raw_ump(port, type) \
73e9e02819STakashi Iwai 	ump_compose(type, get_ump_group(port), 0, 0)
74e9e02819STakashi Iwai 
75e9e02819STakashi Iwai /*
76e9e02819STakashi Iwai  * UMP -> MIDI1 sequencer event
77e9e02819STakashi Iwai  */
78e9e02819STakashi Iwai 
79e9e02819STakashi Iwai /* MIDI 1.0 CVM */
80e9e02819STakashi Iwai 
81e9e02819STakashi Iwai /* encode note event */
ump_midi1_to_note_ev(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)82e9e02819STakashi Iwai static void ump_midi1_to_note_ev(const union snd_ump_midi1_msg *val,
83e9e02819STakashi Iwai 				 struct snd_seq_event *ev)
84e9e02819STakashi Iwai {
85e9e02819STakashi Iwai 	ev->data.note.channel = val->note.channel;
86e9e02819STakashi Iwai 	ev->data.note.note = val->note.note;
87e9e02819STakashi Iwai 	ev->data.note.velocity = val->note.velocity;
88e9e02819STakashi Iwai }
89e9e02819STakashi Iwai 
90e9e02819STakashi Iwai /* encode one parameter controls */
ump_midi1_to_ctrl_ev(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)91e9e02819STakashi Iwai static void ump_midi1_to_ctrl_ev(const union snd_ump_midi1_msg *val,
92e9e02819STakashi Iwai 				 struct snd_seq_event *ev)
93e9e02819STakashi Iwai {
94e9e02819STakashi Iwai 	ev->data.control.channel = val->caf.channel;
95e9e02819STakashi Iwai 	ev->data.control.value = val->caf.data;
96e9e02819STakashi Iwai }
97e9e02819STakashi Iwai 
98e9e02819STakashi Iwai /* encode pitch wheel change */
ump_midi1_to_pitchbend_ev(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)99e9e02819STakashi Iwai static void ump_midi1_to_pitchbend_ev(const union snd_ump_midi1_msg *val,
100e9e02819STakashi Iwai 				      struct snd_seq_event *ev)
101e9e02819STakashi Iwai {
102e9e02819STakashi Iwai 	ev->data.control.channel = val->pb.channel;
103e9e02819STakashi Iwai 	ev->data.control.value = (val->pb.data_msb << 7) | val->pb.data_lsb;
104e9e02819STakashi Iwai 	ev->data.control.value -= 8192;
105e9e02819STakashi Iwai }
106e9e02819STakashi Iwai 
107e9e02819STakashi Iwai /* encode midi control change */
ump_midi1_to_cc_ev(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)108e9e02819STakashi Iwai static void ump_midi1_to_cc_ev(const union snd_ump_midi1_msg *val,
109e9e02819STakashi Iwai 			       struct snd_seq_event *ev)
110e9e02819STakashi Iwai {
111e9e02819STakashi Iwai 	ev->data.control.channel = val->cc.channel;
112e9e02819STakashi Iwai 	ev->data.control.param = val->cc.index;
113e9e02819STakashi Iwai 	ev->data.control.value = val->cc.data;
114e9e02819STakashi Iwai }
115e9e02819STakashi Iwai 
116e9e02819STakashi Iwai /* Encoding MIDI 1.0 UMP packet */
117e9e02819STakashi Iwai struct seq_ump_midi1_to_ev {
118e9e02819STakashi Iwai 	int seq_type;
119e9e02819STakashi Iwai 	void (*encode)(const union snd_ump_midi1_msg *val, struct snd_seq_event *ev);
120e9e02819STakashi Iwai };
121e9e02819STakashi Iwai 
122e9e02819STakashi Iwai /* Encoders for MIDI1 status 0x80-0xe0 */
123e9e02819STakashi Iwai static struct seq_ump_midi1_to_ev midi1_msg_encoders[] = {
124e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NOTEOFF,	ump_midi1_to_note_ev},	/* 0x80 */
125e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NOTEON,	ump_midi1_to_note_ev},	/* 0x90 */
126e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_KEYPRESS,	ump_midi1_to_note_ev},	/* 0xa0 */
127e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_CONTROLLER,	ump_midi1_to_cc_ev},	/* 0xb0 */
128e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_PGMCHANGE,	ump_midi1_to_ctrl_ev},	/* 0xc0 */
129e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_CHANPRESS,	ump_midi1_to_ctrl_ev},	/* 0xd0 */
130e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_PITCHBEND,	ump_midi1_to_pitchbend_ev}, /* 0xe0 */
131e9e02819STakashi Iwai };
132e9e02819STakashi Iwai 
cvt_ump_midi1_to_event(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)133e9e02819STakashi Iwai static int cvt_ump_midi1_to_event(const union snd_ump_midi1_msg *val,
134e9e02819STakashi Iwai 				  struct snd_seq_event *ev)
135e9e02819STakashi Iwai {
136e9e02819STakashi Iwai 	unsigned char status = val->note.status;
137e9e02819STakashi Iwai 
138e9e02819STakashi Iwai 	if (status < 0x8 || status > 0xe)
139e9e02819STakashi Iwai 		return 0; /* invalid - skip */
140e9e02819STakashi Iwai 	status -= 8;
141e9e02819STakashi Iwai 	ev->type = midi1_msg_encoders[status].seq_type;
142e9e02819STakashi Iwai 	ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
143e9e02819STakashi Iwai 	midi1_msg_encoders[status].encode(val, ev);
144e9e02819STakashi Iwai 	return 1;
145e9e02819STakashi Iwai }
146e9e02819STakashi Iwai 
147e9e02819STakashi Iwai /* MIDI System message */
148e9e02819STakashi Iwai 
149e9e02819STakashi Iwai /* encode one parameter value*/
ump_system_to_one_param_ev(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)150e9e02819STakashi Iwai static void ump_system_to_one_param_ev(const union snd_ump_midi1_msg *val,
151e9e02819STakashi Iwai 				       struct snd_seq_event *ev)
152e9e02819STakashi Iwai {
153e9e02819STakashi Iwai 	ev->data.control.value = val->system.parm1;
154e9e02819STakashi Iwai }
155e9e02819STakashi Iwai 
156e9e02819STakashi Iwai /* encode song position */
ump_system_to_songpos_ev(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)157e9e02819STakashi Iwai static void ump_system_to_songpos_ev(const union snd_ump_midi1_msg *val,
158e9e02819STakashi Iwai 				     struct snd_seq_event *ev)
159e9e02819STakashi Iwai {
160b7c4ef7bSTakashi Iwai 	ev->data.control.value = (val->system.parm2 << 7) | val->system.parm1;
161e9e02819STakashi Iwai }
162e9e02819STakashi Iwai 
163e9e02819STakashi Iwai /* Encoders for 0xf0 - 0xff */
164e9e02819STakashi Iwai static struct seq_ump_midi1_to_ev system_msg_encoders[] = {
165e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},	 /* 0xf0 */
166e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_QFRAME,	ump_system_to_one_param_ev}, /* 0xf1 */
167e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_SONGPOS,	ump_system_to_songpos_ev}, /* 0xf2 */
168e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_SONGSEL,	ump_system_to_one_param_ev}, /* 0xf3 */
169e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL}, /* 0xf4 */
170e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL}, /* 0xf5 */
171e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_TUNE_REQUEST,	NULL}, /* 0xf6 */
172e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL}, /* 0xf7 */
173e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_CLOCK,		NULL}, /* 0xf8 */
174e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL}, /* 0xf9 */
175e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_START,		NULL}, /* 0xfa */
176e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_CONTINUE,	NULL}, /* 0xfb */
177e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_STOP,		NULL}, /* 0xfc */
178e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL}, /* 0xfd */
179e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_SENSING,	NULL}, /* 0xfe */
180e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_RESET,		NULL}, /* 0xff */
181e9e02819STakashi Iwai };
182e9e02819STakashi Iwai 
cvt_ump_system_to_event(const union snd_ump_midi1_msg * val,struct snd_seq_event * ev)183e9e02819STakashi Iwai static int cvt_ump_system_to_event(const union snd_ump_midi1_msg *val,
184e9e02819STakashi Iwai 				   struct snd_seq_event *ev)
185e9e02819STakashi Iwai {
186e9e02819STakashi Iwai 	unsigned char status = val->system.status;
187e9e02819STakashi Iwai 
188e9e02819STakashi Iwai 	if ((status & 0xf0) != UMP_MIDI1_MSG_REALTIME)
189e9e02819STakashi Iwai 		return 0; /* invalid status - skip */
190e9e02819STakashi Iwai 	status &= 0x0f;
191e9e02819STakashi Iwai 	ev->type = system_msg_encoders[status].seq_type;
192e9e02819STakashi Iwai 	ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
193e9e02819STakashi Iwai 	if (ev->type == SNDRV_SEQ_EVENT_NONE)
194e9e02819STakashi Iwai 		return 0;
195e9e02819STakashi Iwai 	if (system_msg_encoders[status].encode)
196e9e02819STakashi Iwai 		system_msg_encoders[status].encode(val, ev);
197e9e02819STakashi Iwai 	return 1;
198e9e02819STakashi Iwai }
199e9e02819STakashi Iwai 
200e9e02819STakashi Iwai /* MIDI 2.0 CVM */
201e9e02819STakashi Iwai 
202e9e02819STakashi Iwai /* encode note event */
ump_midi2_to_note_ev(const union snd_ump_midi2_msg * val,struct snd_seq_event * ev)203e9e02819STakashi Iwai static int ump_midi2_to_note_ev(const union snd_ump_midi2_msg *val,
204e9e02819STakashi Iwai 				struct snd_seq_event *ev)
205e9e02819STakashi Iwai {
206e9e02819STakashi Iwai 	ev->data.note.channel = val->note.channel;
207e9e02819STakashi Iwai 	ev->data.note.note = val->note.note;
208e9e02819STakashi Iwai 	ev->data.note.velocity = downscale_16_to_7bit(val->note.velocity);
209e9e02819STakashi Iwai 	/* correct note-on velocity 0 to 1;
210e9e02819STakashi Iwai 	 * it's no longer equivalent as not-off for MIDI 2.0
211e9e02819STakashi Iwai 	 */
212e9e02819STakashi Iwai 	if (ev->type == SNDRV_SEQ_EVENT_NOTEON &&
213e9e02819STakashi Iwai 	    !ev->data.note.velocity)
214e9e02819STakashi Iwai 		ev->data.note.velocity = 1;
215e9e02819STakashi Iwai 	return 1;
216e9e02819STakashi Iwai }
217e9e02819STakashi Iwai 
218e9e02819STakashi Iwai /* encode pitch wheel change */
ump_midi2_to_pitchbend_ev(const union snd_ump_midi2_msg * val,struct snd_seq_event * ev)219e9e02819STakashi Iwai static int ump_midi2_to_pitchbend_ev(const union snd_ump_midi2_msg *val,
220e9e02819STakashi Iwai 				     struct snd_seq_event *ev)
221e9e02819STakashi Iwai {
222e9e02819STakashi Iwai 	ev->data.control.channel = val->pb.channel;
223e9e02819STakashi Iwai 	ev->data.control.value = downscale_32_to_14bit(val->pb.data);
224e9e02819STakashi Iwai 	ev->data.control.value -= 8192;
225e9e02819STakashi Iwai 	return 1;
226e9e02819STakashi Iwai }
227e9e02819STakashi Iwai 
228e9e02819STakashi Iwai /* encode midi control change */
ump_midi2_to_cc_ev(const union snd_ump_midi2_msg * val,struct snd_seq_event * ev)229e9e02819STakashi Iwai static int ump_midi2_to_cc_ev(const union snd_ump_midi2_msg *val,
230e9e02819STakashi Iwai 			      struct snd_seq_event *ev)
231e9e02819STakashi Iwai {
232e9e02819STakashi Iwai 	ev->data.control.channel = val->cc.channel;
233e9e02819STakashi Iwai 	ev->data.control.param = val->cc.index;
234e9e02819STakashi Iwai 	ev->data.control.value = downscale_32_to_7bit(val->cc.data);
235e9e02819STakashi Iwai 	return 1;
236e9e02819STakashi Iwai }
237e9e02819STakashi Iwai 
238e9e02819STakashi Iwai /* encode midi program change */
ump_midi2_to_pgm_ev(const union snd_ump_midi2_msg * val,struct snd_seq_event * ev)239e9e02819STakashi Iwai static int ump_midi2_to_pgm_ev(const union snd_ump_midi2_msg *val,
240e9e02819STakashi Iwai 			       struct snd_seq_event *ev)
241e9e02819STakashi Iwai {
242e9e02819STakashi Iwai 	int size = 1;
243e9e02819STakashi Iwai 
244e9e02819STakashi Iwai 	ev->data.control.channel = val->pg.channel;
245e9e02819STakashi Iwai 	if (val->pg.bank_valid) {
246e9e02819STakashi Iwai 		ev->type = SNDRV_SEQ_EVENT_CONTROL14;
247e9e02819STakashi Iwai 		ev->data.control.param = UMP_CC_BANK_SELECT;
248e9e02819STakashi Iwai 		ev->data.control.value = (val->pg.bank_msb << 7) | val->pg.bank_lsb;
249e9e02819STakashi Iwai 		ev[1] = ev[0];
250e9e02819STakashi Iwai 		ev++;
251e9e02819STakashi Iwai 		ev->type = SNDRV_SEQ_EVENT_PGMCHANGE;
252e9e02819STakashi Iwai 		size = 2;
253e9e02819STakashi Iwai 	}
254e9e02819STakashi Iwai 	ev->data.control.value = val->pg.program;
255e9e02819STakashi Iwai 	return size;
256e9e02819STakashi Iwai }
257e9e02819STakashi Iwai 
258e9e02819STakashi Iwai /* encode one parameter controls */
ump_midi2_to_ctrl_ev(const union snd_ump_midi2_msg * val,struct snd_seq_event * ev)259e9e02819STakashi Iwai static int ump_midi2_to_ctrl_ev(const union snd_ump_midi2_msg *val,
260e9e02819STakashi Iwai 				struct snd_seq_event *ev)
261e9e02819STakashi Iwai {
262e9e02819STakashi Iwai 	ev->data.control.channel = val->caf.channel;
263e9e02819STakashi Iwai 	ev->data.control.value = downscale_32_to_7bit(val->caf.data);
264e9e02819STakashi Iwai 	return 1;
265e9e02819STakashi Iwai }
266e9e02819STakashi Iwai 
267e9e02819STakashi Iwai /* encode RPN/NRPN */
ump_midi2_to_rpn_ev(const union snd_ump_midi2_msg * val,struct snd_seq_event * ev)268e9e02819STakashi Iwai static int ump_midi2_to_rpn_ev(const union snd_ump_midi2_msg *val,
269e9e02819STakashi Iwai 			       struct snd_seq_event *ev)
270e9e02819STakashi Iwai {
271e9e02819STakashi Iwai 	ev->data.control.channel = val->rpn.channel;
272e9e02819STakashi Iwai 	ev->data.control.param = (val->rpn.bank << 7) | val->rpn.index;
273e9e02819STakashi Iwai 	ev->data.control.value = downscale_32_to_14bit(val->rpn.data);
274e9e02819STakashi Iwai 	return 1;
275e9e02819STakashi Iwai }
276e9e02819STakashi Iwai 
277e9e02819STakashi Iwai /* Encoding MIDI 2.0 UMP Packet */
278e9e02819STakashi Iwai struct seq_ump_midi2_to_ev {
279e9e02819STakashi Iwai 	int seq_type;
280e9e02819STakashi Iwai 	int (*encode)(const union snd_ump_midi2_msg *val, struct snd_seq_event *ev);
281e9e02819STakashi Iwai };
282e9e02819STakashi Iwai 
283e9e02819STakashi Iwai /* Encoders for MIDI2 status 0x00-0xf0 */
284e9e02819STakashi Iwai static struct seq_ump_midi2_to_ev midi2_msg_encoders[] = {
285e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},			/* 0x00 */
286e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},			/* 0x10 */
287e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_REGPARAM,	ump_midi2_to_rpn_ev},	/* 0x20 */
288e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONREGPARAM,	ump_midi2_to_rpn_ev},	/* 0x30 */
289e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},			/* 0x40 */
290e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},			/* 0x50 */
291e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},			/* 0x60 */
292e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},			/* 0x70 */
293e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NOTEOFF,	ump_midi2_to_note_ev},	/* 0x80 */
294e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NOTEON,	ump_midi2_to_note_ev},	/* 0x90 */
295e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_KEYPRESS,	ump_midi2_to_note_ev},	/* 0xa0 */
296e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_CONTROLLER,	ump_midi2_to_cc_ev},	/* 0xb0 */
297e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_PGMCHANGE,	ump_midi2_to_pgm_ev},	/* 0xc0 */
298e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_CHANPRESS,	ump_midi2_to_ctrl_ev},	/* 0xd0 */
299e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_PITCHBEND,	ump_midi2_to_pitchbend_ev}, /* 0xe0 */
300e9e02819STakashi Iwai 	{SNDRV_SEQ_EVENT_NONE,		NULL},			/* 0xf0 */
301e9e02819STakashi Iwai };
302e9e02819STakashi Iwai 
cvt_ump_midi2_to_event(const union snd_ump_midi2_msg * val,struct snd_seq_event * ev)303e9e02819STakashi Iwai static int cvt_ump_midi2_to_event(const union snd_ump_midi2_msg *val,
304e9e02819STakashi Iwai 				  struct snd_seq_event *ev)
305e9e02819STakashi Iwai {
306e9e02819STakashi Iwai 	unsigned char status = val->note.status;
307e9e02819STakashi Iwai 
308e9e02819STakashi Iwai 	ev->type = midi2_msg_encoders[status].seq_type;
309e9e02819STakashi Iwai 	if (ev->type == SNDRV_SEQ_EVENT_NONE)
310e9e02819STakashi Iwai 		return 0; /* skip */
311e9e02819STakashi Iwai 	ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
312e9e02819STakashi Iwai 	return midi2_msg_encoders[status].encode(val, ev);
313e9e02819STakashi Iwai }
314e9e02819STakashi Iwai 
315e9e02819STakashi Iwai /* parse and compose for a sysex var-length event */
cvt_ump_sysex7_to_event(const u32 * data,unsigned char * buf,struct snd_seq_event * ev)316e9e02819STakashi Iwai static int cvt_ump_sysex7_to_event(const u32 *data, unsigned char *buf,
317e9e02819STakashi Iwai 				   struct snd_seq_event *ev)
318e9e02819STakashi Iwai {
319e9e02819STakashi Iwai 	unsigned char status;
320e9e02819STakashi Iwai 	unsigned char bytes;
321e9e02819STakashi Iwai 	u32 val;
322e9e02819STakashi Iwai 	int size = 0;
323e9e02819STakashi Iwai 
324e9e02819STakashi Iwai 	val = data[0];
325e9e02819STakashi Iwai 	status = ump_sysex_message_status(val);
326e9e02819STakashi Iwai 	bytes = ump_sysex_message_length(val);
327e9e02819STakashi Iwai 	if (bytes > 6)
328e9e02819STakashi Iwai 		return 0; // skip
329e9e02819STakashi Iwai 
330e9e02819STakashi Iwai 	if (status == UMP_SYSEX_STATUS_SINGLE ||
331e9e02819STakashi Iwai 	    status == UMP_SYSEX_STATUS_START) {
332e9e02819STakashi Iwai 		buf[0] = UMP_MIDI1_MSG_SYSEX_START;
333e9e02819STakashi Iwai 		size = 1;
334e9e02819STakashi Iwai 	}
335e9e02819STakashi Iwai 
336e9e02819STakashi Iwai 	if (bytes > 0)
337e9e02819STakashi Iwai 		buf[size++] = (val >> 8) & 0x7f;
338e9e02819STakashi Iwai 	if (bytes > 1)
339e9e02819STakashi Iwai 		buf[size++] = val & 0x7f;
340e9e02819STakashi Iwai 	val = data[1];
341e9e02819STakashi Iwai 	if (bytes > 2)
342e9e02819STakashi Iwai 		buf[size++] = (val >> 24) & 0x7f;
343e9e02819STakashi Iwai 	if (bytes > 3)
344e9e02819STakashi Iwai 		buf[size++] = (val >> 16) & 0x7f;
345e9e02819STakashi Iwai 	if (bytes > 4)
346e9e02819STakashi Iwai 		buf[size++] = (val >> 8) & 0x7f;
347e9e02819STakashi Iwai 	if (bytes > 5)
348e9e02819STakashi Iwai 		buf[size++] = val & 0x7f;
349e9e02819STakashi Iwai 
350e9e02819STakashi Iwai 	if (status == UMP_SYSEX_STATUS_SINGLE ||
351e9e02819STakashi Iwai 	    status == UMP_SYSEX_STATUS_END)
352e9e02819STakashi Iwai 		buf[size++] = UMP_MIDI1_MSG_SYSEX_END;
353e9e02819STakashi Iwai 
354e9e02819STakashi Iwai 	ev->type = SNDRV_SEQ_EVENT_SYSEX;
355e9e02819STakashi Iwai 	ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
356e9e02819STakashi Iwai 	ev->data.ext.len = size;
357e9e02819STakashi Iwai 	ev->data.ext.ptr = buf;
358e9e02819STakashi Iwai 	return 1;
359e9e02819STakashi Iwai }
360e9e02819STakashi Iwai 
361e9e02819STakashi Iwai /* convert UMP packet from MIDI 1.0 to MIDI 2.0 and deliver it */
cvt_ump_midi1_to_midi2(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * __event,int atomic,int hop)362e9e02819STakashi Iwai static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest,
363e9e02819STakashi Iwai 				  struct snd_seq_client_port *dest_port,
364e9e02819STakashi Iwai 				  struct snd_seq_event *__event,
365e9e02819STakashi Iwai 				  int atomic, int hop)
366e9e02819STakashi Iwai {
367e9e02819STakashi Iwai 	struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event;
368e9e02819STakashi Iwai 	struct snd_seq_ump_event ev_cvt;
369e9e02819STakashi Iwai 	const union snd_ump_midi1_msg *midi1 = (const union snd_ump_midi1_msg *)event->ump;
370e9e02819STakashi Iwai 	union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)ev_cvt.ump;
3715c3df8c4STakashi Iwai 	struct snd_seq_ump_midi2_bank *cc;
372e9e02819STakashi Iwai 
373e9e02819STakashi Iwai 	ev_cvt = *event;
374e9e02819STakashi Iwai 	memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
375e9e02819STakashi Iwai 
376e9e02819STakashi Iwai 	midi2->note.type = UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE;
377e9e02819STakashi Iwai 	midi2->note.group = midi1->note.group;
378e9e02819STakashi Iwai 	midi2->note.status = midi1->note.status;
379e9e02819STakashi Iwai 	midi2->note.channel = midi1->note.channel;
380e9e02819STakashi Iwai 	switch (midi1->note.status) {
381e9e02819STakashi Iwai 	case UMP_MSG_STATUS_NOTE_ON:
382e9e02819STakashi Iwai 	case UMP_MSG_STATUS_NOTE_OFF:
383e9e02819STakashi Iwai 		midi2->note.note = midi1->note.note;
384e9e02819STakashi Iwai 		midi2->note.velocity = upscale_7_to_16bit(midi1->note.velocity);
385e9e02819STakashi Iwai 		break;
386e9e02819STakashi Iwai 	case UMP_MSG_STATUS_POLY_PRESSURE:
387e9e02819STakashi Iwai 		midi2->paf.note = midi1->paf.note;
388e9e02819STakashi Iwai 		midi2->paf.data = upscale_7_to_32bit(midi1->paf.data);
389e9e02819STakashi Iwai 		break;
390e9e02819STakashi Iwai 	case UMP_MSG_STATUS_CC:
3915c3df8c4STakashi Iwai 		cc = &dest_port->midi2_bank[midi1->note.channel];
3925c3df8c4STakashi Iwai 		switch (midi1->cc.index) {
3935c3df8c4STakashi Iwai 		case UMP_CC_BANK_SELECT:
3945c3df8c4STakashi Iwai 			cc->bank_set = 1;
3955c3df8c4STakashi Iwai 			cc->cc_bank_msb = midi1->cc.data;
3965c3df8c4STakashi Iwai 			return 0; // skip
3975c3df8c4STakashi Iwai 		case UMP_CC_BANK_SELECT_LSB:
3985c3df8c4STakashi Iwai 			cc->bank_set = 1;
3995c3df8c4STakashi Iwai 			cc->cc_bank_lsb = midi1->cc.data;
4005c3df8c4STakashi Iwai 			return 0; // skip
4015c3df8c4STakashi Iwai 		}
402e9e02819STakashi Iwai 		midi2->cc.index = midi1->cc.index;
403e9e02819STakashi Iwai 		midi2->cc.data = upscale_7_to_32bit(midi1->cc.data);
404e9e02819STakashi Iwai 		break;
405e9e02819STakashi Iwai 	case UMP_MSG_STATUS_PROGRAM:
406e9e02819STakashi Iwai 		midi2->pg.program = midi1->pg.program;
4075c3df8c4STakashi Iwai 		cc = &dest_port->midi2_bank[midi1->note.channel];
4085c3df8c4STakashi Iwai 		if (cc->bank_set) {
4095c3df8c4STakashi Iwai 			midi2->pg.bank_valid = 1;
4105c3df8c4STakashi Iwai 			midi2->pg.bank_msb = cc->cc_bank_msb;
4115c3df8c4STakashi Iwai 			midi2->pg.bank_lsb = cc->cc_bank_lsb;
4125c3df8c4STakashi Iwai 			cc->bank_set = 0;
4135c3df8c4STakashi Iwai 		}
414e9e02819STakashi Iwai 		break;
415e9e02819STakashi Iwai 	case UMP_MSG_STATUS_CHANNEL_PRESSURE:
416e9e02819STakashi Iwai 		midi2->caf.data = upscale_7_to_32bit(midi1->caf.data);
417e9e02819STakashi Iwai 		break;
418e9e02819STakashi Iwai 	case UMP_MSG_STATUS_PITCH_BEND:
419e9e02819STakashi Iwai 		midi2->pb.data = upscale_14_to_32bit((midi1->pb.data_msb << 7) |
420e9e02819STakashi Iwai 						     midi1->pb.data_lsb);
421e9e02819STakashi Iwai 		break;
422e9e02819STakashi Iwai 	default:
423e9e02819STakashi Iwai 		return 0;
424e9e02819STakashi Iwai 	}
425e9e02819STakashi Iwai 
426e9e02819STakashi Iwai 	return __snd_seq_deliver_single_event(dest, dest_port,
427e9e02819STakashi Iwai 					      (struct snd_seq_event *)&ev_cvt,
428e9e02819STakashi Iwai 					      atomic, hop);
429e9e02819STakashi Iwai }
430e9e02819STakashi Iwai 
431e9e02819STakashi Iwai /* convert UMP packet from MIDI 2.0 to MIDI 1.0 and deliver it */
cvt_ump_midi2_to_midi1(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * __event,int atomic,int hop)432e9e02819STakashi Iwai static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest,
433e9e02819STakashi Iwai 				  struct snd_seq_client_port *dest_port,
434e9e02819STakashi Iwai 				  struct snd_seq_event *__event,
435e9e02819STakashi Iwai 				  int atomic, int hop)
436e9e02819STakashi Iwai {
437e9e02819STakashi Iwai 	struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event;
438e9e02819STakashi Iwai 	struct snd_seq_ump_event ev_cvt;
439e9e02819STakashi Iwai 	union snd_ump_midi1_msg *midi1 = (union snd_ump_midi1_msg *)ev_cvt.ump;
440e9e02819STakashi Iwai 	const union snd_ump_midi2_msg *midi2 = (const union snd_ump_midi2_msg *)event->ump;
4415c3df8c4STakashi Iwai 	int err;
442e9e02819STakashi Iwai 	u16 v;
443e9e02819STakashi Iwai 
444e9e02819STakashi Iwai 	ev_cvt = *event;
445e9e02819STakashi Iwai 	memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
446e9e02819STakashi Iwai 
447e9e02819STakashi Iwai 	midi1->note.type = UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE;
448e9e02819STakashi Iwai 	midi1->note.group = midi2->note.group;
449e9e02819STakashi Iwai 	midi1->note.status = midi2->note.status;
450e9e02819STakashi Iwai 	midi1->note.channel = midi2->note.channel;
45102d32d5aSTakashi Iwai 	switch (midi2->note.status) {
452e9e02819STakashi Iwai 	case UMP_MSG_STATUS_NOTE_ON:
453e9e02819STakashi Iwai 	case UMP_MSG_STATUS_NOTE_OFF:
454e9e02819STakashi Iwai 		midi1->note.note = midi2->note.note;
455e9e02819STakashi Iwai 		midi1->note.velocity = downscale_16_to_7bit(midi2->note.velocity);
456e9e02819STakashi Iwai 		break;
457e9e02819STakashi Iwai 	case UMP_MSG_STATUS_POLY_PRESSURE:
458e9e02819STakashi Iwai 		midi1->paf.note = midi2->paf.note;
459e9e02819STakashi Iwai 		midi1->paf.data = downscale_32_to_7bit(midi2->paf.data);
460e9e02819STakashi Iwai 		break;
461e9e02819STakashi Iwai 	case UMP_MSG_STATUS_CC:
462e9e02819STakashi Iwai 		midi1->cc.index = midi2->cc.index;
463e9e02819STakashi Iwai 		midi1->cc.data = downscale_32_to_7bit(midi2->cc.data);
464e9e02819STakashi Iwai 		break;
465e9e02819STakashi Iwai 	case UMP_MSG_STATUS_PROGRAM:
4665c3df8c4STakashi Iwai 		if (midi2->pg.bank_valid) {
4675c3df8c4STakashi Iwai 			midi1->cc.status = UMP_MSG_STATUS_CC;
4685c3df8c4STakashi Iwai 			midi1->cc.index = UMP_CC_BANK_SELECT;
4695c3df8c4STakashi Iwai 			midi1->cc.data = midi2->pg.bank_msb;
4705c3df8c4STakashi Iwai 			err = __snd_seq_deliver_single_event(dest, dest_port,
4715c3df8c4STakashi Iwai 							     (struct snd_seq_event *)&ev_cvt,
4725c3df8c4STakashi Iwai 							     atomic, hop);
4735c3df8c4STakashi Iwai 			if (err < 0)
4745c3df8c4STakashi Iwai 				return err;
4755c3df8c4STakashi Iwai 			midi1->cc.index = UMP_CC_BANK_SELECT_LSB;
4765c3df8c4STakashi Iwai 			midi1->cc.data = midi2->pg.bank_lsb;
4775c3df8c4STakashi Iwai 			err = __snd_seq_deliver_single_event(dest, dest_port,
4785c3df8c4STakashi Iwai 							     (struct snd_seq_event *)&ev_cvt,
4795c3df8c4STakashi Iwai 							     atomic, hop);
4805c3df8c4STakashi Iwai 			if (err < 0)
4815c3df8c4STakashi Iwai 				return err;
4825c3df8c4STakashi Iwai 			midi1->note.status = midi2->note.status;
4835c3df8c4STakashi Iwai 		}
484e9e02819STakashi Iwai 		midi1->pg.program = midi2->pg.program;
485e9e02819STakashi Iwai 		break;
486e9e02819STakashi Iwai 	case UMP_MSG_STATUS_CHANNEL_PRESSURE:
487e9e02819STakashi Iwai 		midi1->caf.data = downscale_32_to_7bit(midi2->caf.data);
488e9e02819STakashi Iwai 		break;
489e9e02819STakashi Iwai 	case UMP_MSG_STATUS_PITCH_BEND:
490e9e02819STakashi Iwai 		v = downscale_32_to_14bit(midi2->pb.data);
491e9e02819STakashi Iwai 		midi1->pb.data_msb = v >> 7;
492e9e02819STakashi Iwai 		midi1->pb.data_lsb = v & 0x7f;
493e9e02819STakashi Iwai 		break;
494e9e02819STakashi Iwai 	default:
495e9e02819STakashi Iwai 		return 0;
496e9e02819STakashi Iwai 	}
497e9e02819STakashi Iwai 
498e9e02819STakashi Iwai 	return __snd_seq_deliver_single_event(dest, dest_port,
499e9e02819STakashi Iwai 					      (struct snd_seq_event *)&ev_cvt,
500e9e02819STakashi Iwai 					      atomic, hop);
501e9e02819STakashi Iwai }
502e9e02819STakashi Iwai 
503e9e02819STakashi Iwai /* convert UMP to a legacy ALSA seq event and deliver it */
cvt_ump_to_any(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * event,unsigned char type,int atomic,int hop)504e9e02819STakashi Iwai static int cvt_ump_to_any(struct snd_seq_client *dest,
505e9e02819STakashi Iwai 			  struct snd_seq_client_port *dest_port,
506e9e02819STakashi Iwai 			  struct snd_seq_event *event,
507e9e02819STakashi Iwai 			  unsigned char type,
508e9e02819STakashi Iwai 			  int atomic, int hop)
509e9e02819STakashi Iwai {
510e9e02819STakashi Iwai 	struct snd_seq_event ev_cvt[2]; /* up to two events */
511e9e02819STakashi Iwai 	struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event;
512e9e02819STakashi Iwai 	/* use the second event as a temp buffer for saving stack usage */
513e9e02819STakashi Iwai 	unsigned char *sysex_buf = (unsigned char *)(ev_cvt + 1);
514e9e02819STakashi Iwai 	unsigned char flags = event->flags & ~SNDRV_SEQ_EVENT_UMP;
515e9e02819STakashi Iwai 	int i, len, err;
516e9e02819STakashi Iwai 
517e9e02819STakashi Iwai 	ev_cvt[0] = ev_cvt[1] = *event;
518e9e02819STakashi Iwai 	ev_cvt[0].flags = flags;
519e9e02819STakashi Iwai 	ev_cvt[1].flags = flags;
520e9e02819STakashi Iwai 	switch (type) {
521e9e02819STakashi Iwai 	case UMP_MSG_TYPE_SYSTEM:
522e9e02819STakashi Iwai 		len = cvt_ump_system_to_event((union snd_ump_midi1_msg *)ump_ev->ump,
523e9e02819STakashi Iwai 					      ev_cvt);
524e9e02819STakashi Iwai 		break;
525e9e02819STakashi Iwai 	case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE:
526e9e02819STakashi Iwai 		len = cvt_ump_midi1_to_event((union snd_ump_midi1_msg *)ump_ev->ump,
527e9e02819STakashi Iwai 					     ev_cvt);
528e9e02819STakashi Iwai 		break;
529e9e02819STakashi Iwai 	case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE:
530e9e02819STakashi Iwai 		len = cvt_ump_midi2_to_event((union snd_ump_midi2_msg *)ump_ev->ump,
531e9e02819STakashi Iwai 					     ev_cvt);
532e9e02819STakashi Iwai 		break;
533e9e02819STakashi Iwai 	case UMP_MSG_TYPE_DATA:
534e9e02819STakashi Iwai 		len = cvt_ump_sysex7_to_event(ump_ev->ump, sysex_buf, ev_cvt);
535e9e02819STakashi Iwai 		break;
536e9e02819STakashi Iwai 	default:
537e9e02819STakashi Iwai 		return 0;
538e9e02819STakashi Iwai 	}
539e9e02819STakashi Iwai 
540e9e02819STakashi Iwai 	for (i = 0; i < len; i++) {
541e9e02819STakashi Iwai 		err = __snd_seq_deliver_single_event(dest, dest_port,
542e9e02819STakashi Iwai 						     &ev_cvt[i], atomic, hop);
543e9e02819STakashi Iwai 		if (err < 0)
544e9e02819STakashi Iwai 			return err;
545e9e02819STakashi Iwai 	}
546e9e02819STakashi Iwai 
547e9e02819STakashi Iwai 	return 0;
548e9e02819STakashi Iwai }
549e9e02819STakashi Iwai 
550e9e02819STakashi Iwai /* Replace UMP group field with the destination and deliver */
deliver_with_group_convert(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_ump_event * ump_ev,int atomic,int hop)551e9e02819STakashi Iwai static int deliver_with_group_convert(struct snd_seq_client *dest,
552e9e02819STakashi Iwai 				      struct snd_seq_client_port *dest_port,
553e9e02819STakashi Iwai 				      struct snd_seq_ump_event *ump_ev,
554e9e02819STakashi Iwai 				      int atomic, int hop)
555e9e02819STakashi Iwai {
556e9e02819STakashi Iwai 	struct snd_seq_ump_event ev = *ump_ev;
557e9e02819STakashi Iwai 
558e9e02819STakashi Iwai 	/* rewrite the group to the destination port */
559e9e02819STakashi Iwai 	ev.ump[0] &= ~(0xfU << 24);
560e9e02819STakashi Iwai 	/* fill with the new group; the dest_port->ump_group field is 1-based */
561e9e02819STakashi Iwai 	ev.ump[0] |= ((dest_port->ump_group - 1) << 24);
562e9e02819STakashi Iwai 
563e9e02819STakashi Iwai 	return __snd_seq_deliver_single_event(dest, dest_port,
564e9e02819STakashi Iwai 					      (struct snd_seq_event *)&ev,
565e9e02819STakashi Iwai 					      atomic, hop);
566e9e02819STakashi Iwai }
567e9e02819STakashi Iwai 
568d2b70607STakashi Iwai /* apply the UMP event filter; return true to skip the event */
ump_event_filtered(struct snd_seq_client * dest,const struct snd_seq_ump_event * ev)569d2b70607STakashi Iwai static bool ump_event_filtered(struct snd_seq_client *dest,
570d2b70607STakashi Iwai 			       const struct snd_seq_ump_event *ev)
571d2b70607STakashi Iwai {
572d2b70607STakashi Iwai 	unsigned char group;
573d2b70607STakashi Iwai 
574d2b70607STakashi Iwai 	group = ump_message_group(ev->ump[0]);
5755437ac9bSTakashi Iwai 	if (ump_is_groupless_msg(ump_message_type(ev->ump[0])))
5765437ac9bSTakashi Iwai 		return dest->group_filter & (1U << 0);
577d2b70607STakashi Iwai 	/* check the bitmap for 1-based group number */
578d2b70607STakashi Iwai 	return dest->group_filter & (1U << (group + 1));
579d2b70607STakashi Iwai }
580d2b70607STakashi Iwai 
581e9e02819STakashi Iwai /* Convert from UMP packet and deliver */
snd_seq_deliver_from_ump(struct snd_seq_client * source,struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * event,int atomic,int hop)582e9e02819STakashi Iwai int snd_seq_deliver_from_ump(struct snd_seq_client *source,
583e9e02819STakashi Iwai 			     struct snd_seq_client *dest,
584e9e02819STakashi Iwai 			     struct snd_seq_client_port *dest_port,
585e9e02819STakashi Iwai 			     struct snd_seq_event *event,
586e9e02819STakashi Iwai 			     int atomic, int hop)
587e9e02819STakashi Iwai {
588e9e02819STakashi Iwai 	struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event;
589e9e02819STakashi Iwai 	unsigned char type;
590e9e02819STakashi Iwai 
591e9e02819STakashi Iwai 	if (snd_seq_ev_is_variable(event))
592e9e02819STakashi Iwai 		return 0; // skip, no variable event for UMP, so far
593d2b70607STakashi Iwai 	if (ump_event_filtered(dest, ump_ev))
594d2b70607STakashi Iwai 		return 0; // skip if group filter is set and matching
595e9e02819STakashi Iwai 	type = ump_message_type(ump_ev->ump[0]);
596e9e02819STakashi Iwai 
597e9e02819STakashi Iwai 	if (snd_seq_client_is_ump(dest)) {
598e9e02819STakashi Iwai 		if (snd_seq_client_is_midi2(dest) &&
599e9e02819STakashi Iwai 		    type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE)
600e9e02819STakashi Iwai 			return cvt_ump_midi1_to_midi2(dest, dest_port,
601e9e02819STakashi Iwai 						      event, atomic, hop);
602e9e02819STakashi Iwai 		else if (!snd_seq_client_is_midi2(dest) &&
603e9e02819STakashi Iwai 			 type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE)
604e9e02819STakashi Iwai 			return cvt_ump_midi2_to_midi1(dest, dest_port,
605e9e02819STakashi Iwai 						      event, atomic, hop);
606e9e02819STakashi Iwai 		/* non-EP port and different group is set? */
607e9e02819STakashi Iwai 		if (dest_port->ump_group &&
6085437ac9bSTakashi Iwai 		    !ump_is_groupless_msg(type) &&
609e9e02819STakashi Iwai 		    ump_message_group(*ump_ev->ump) + 1 != dest_port->ump_group)
610e9e02819STakashi Iwai 			return deliver_with_group_convert(dest, dest_port,
611e9e02819STakashi Iwai 							  ump_ev, atomic, hop);
612e9e02819STakashi Iwai 		/* copy as-is */
613e9e02819STakashi Iwai 		return __snd_seq_deliver_single_event(dest, dest_port,
614e9e02819STakashi Iwai 						      event, atomic, hop);
615e9e02819STakashi Iwai 	}
616e9e02819STakashi Iwai 
617e9e02819STakashi Iwai 	return cvt_ump_to_any(dest, dest_port, event, type, atomic, hop);
618e9e02819STakashi Iwai }
619e9e02819STakashi Iwai 
620e9e02819STakashi Iwai /*
621e9e02819STakashi Iwai  * MIDI1 sequencer event -> UMP conversion
622e9e02819STakashi Iwai  */
623e9e02819STakashi Iwai 
624e9e02819STakashi Iwai /* Conversion to UMP MIDI 1.0 */
625e9e02819STakashi Iwai 
626e9e02819STakashi Iwai /* convert note on/off event to MIDI 1.0 UMP */
note_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)627e9e02819STakashi Iwai static int note_ev_to_ump_midi1(const struct snd_seq_event *event,
628e9e02819STakashi Iwai 				struct snd_seq_client_port *dest_port,
629e9e02819STakashi Iwai 				union snd_ump_midi1_msg *data,
630e9e02819STakashi Iwai 				unsigned char status)
631e9e02819STakashi Iwai {
632e9e02819STakashi Iwai 	if (!event->data.note.velocity)
633e9e02819STakashi Iwai 		status = UMP_MSG_STATUS_NOTE_OFF;
634e9e02819STakashi Iwai 	data->note.status = status;
635e9e02819STakashi Iwai 	data->note.channel = event->data.note.channel & 0x0f;
636e9e02819STakashi Iwai 	data->note.velocity = event->data.note.velocity & 0x7f;
637e9e02819STakashi Iwai 	data->note.note = event->data.note.note & 0x7f;
638e9e02819STakashi Iwai 	return 1;
639e9e02819STakashi Iwai }
640e9e02819STakashi Iwai 
641e9e02819STakashi Iwai /* convert CC event to MIDI 1.0 UMP */
cc_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)642e9e02819STakashi Iwai static int cc_ev_to_ump_midi1(const struct snd_seq_event *event,
643e9e02819STakashi Iwai 			      struct snd_seq_client_port *dest_port,
644e9e02819STakashi Iwai 			      union snd_ump_midi1_msg *data,
645e9e02819STakashi Iwai 			      unsigned char status)
646e9e02819STakashi Iwai {
647e9e02819STakashi Iwai 	data->cc.status = status;
648e9e02819STakashi Iwai 	data->cc.channel = event->data.control.channel & 0x0f;
649e9e02819STakashi Iwai 	data->cc.index = event->data.control.param;
650e9e02819STakashi Iwai 	data->cc.data = event->data.control.value;
651e9e02819STakashi Iwai 	return 1;
652e9e02819STakashi Iwai }
653e9e02819STakashi Iwai 
654e9e02819STakashi Iwai /* convert one-parameter control event to MIDI 1.0 UMP */
ctrl_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)655e9e02819STakashi Iwai static int ctrl_ev_to_ump_midi1(const struct snd_seq_event *event,
656e9e02819STakashi Iwai 				struct snd_seq_client_port *dest_port,
657e9e02819STakashi Iwai 				union snd_ump_midi1_msg *data,
658e9e02819STakashi Iwai 				unsigned char status)
659e9e02819STakashi Iwai {
660e9e02819STakashi Iwai 	data->caf.status = status;
661e9e02819STakashi Iwai 	data->caf.channel = event->data.control.channel & 0x0f;
662e9e02819STakashi Iwai 	data->caf.data = event->data.control.value & 0x7f;
663e9e02819STakashi Iwai 	return 1;
664e9e02819STakashi Iwai }
665e9e02819STakashi Iwai 
666e9e02819STakashi Iwai /* convert pitchbend event to MIDI 1.0 UMP */
pitchbend_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)667e9e02819STakashi Iwai static int pitchbend_ev_to_ump_midi1(const struct snd_seq_event *event,
668e9e02819STakashi Iwai 				     struct snd_seq_client_port *dest_port,
669e9e02819STakashi Iwai 				     union snd_ump_midi1_msg *data,
670e9e02819STakashi Iwai 				     unsigned char status)
671e9e02819STakashi Iwai {
672e9e02819STakashi Iwai 	int val = event->data.control.value + 8192;
673e9e02819STakashi Iwai 
674e9e02819STakashi Iwai 	val = clamp(val, 0, 0x3fff);
675e9e02819STakashi Iwai 	data->pb.status = status;
676e9e02819STakashi Iwai 	data->pb.channel = event->data.control.channel & 0x0f;
677e9e02819STakashi Iwai 	data->pb.data_msb = (val >> 7) & 0x7f;
678e9e02819STakashi Iwai 	data->pb.data_lsb = val & 0x7f;
679e9e02819STakashi Iwai 	return 1;
680e9e02819STakashi Iwai }
681e9e02819STakashi Iwai 
682e9e02819STakashi Iwai /* convert 14bit control event to MIDI 1.0 UMP; split to two events */
ctrl14_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)683e9e02819STakashi Iwai static int ctrl14_ev_to_ump_midi1(const struct snd_seq_event *event,
684e9e02819STakashi Iwai 				  struct snd_seq_client_port *dest_port,
685e9e02819STakashi Iwai 				  union snd_ump_midi1_msg *data,
686e9e02819STakashi Iwai 				  unsigned char status)
687e9e02819STakashi Iwai {
688e9e02819STakashi Iwai 	data->cc.status = UMP_MSG_STATUS_CC;
689e9e02819STakashi Iwai 	data->cc.channel = event->data.control.channel & 0x0f;
690e9e02819STakashi Iwai 	data->cc.index = event->data.control.param & 0x7f;
691e9e02819STakashi Iwai 	if (event->data.control.param < 0x20) {
692e9e02819STakashi Iwai 		data->cc.data = (event->data.control.value >> 7) & 0x7f;
693e9e02819STakashi Iwai 		data[1] = data[0];
694e9e02819STakashi Iwai 		data[1].cc.index = event->data.control.param | 0x20;
695e9e02819STakashi Iwai 		data[1].cc.data = event->data.control.value & 0x7f;
696e9e02819STakashi Iwai 		return 2;
697e9e02819STakashi Iwai 	}
698e9e02819STakashi Iwai 
699e9e02819STakashi Iwai 	data->cc.data = event->data.control.value & 0x7f;
700e9e02819STakashi Iwai 	return 1;
701e9e02819STakashi Iwai }
702e9e02819STakashi Iwai 
703e9e02819STakashi Iwai /* convert RPN/NRPN event to MIDI 1.0 UMP; split to four events */
rpn_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)704e9e02819STakashi Iwai static int rpn_ev_to_ump_midi1(const struct snd_seq_event *event,
705e9e02819STakashi Iwai 			       struct snd_seq_client_port *dest_port,
706e9e02819STakashi Iwai 			       union snd_ump_midi1_msg *data,
707e9e02819STakashi Iwai 			       unsigned char status)
708e9e02819STakashi Iwai {
709e9e02819STakashi Iwai 	bool is_rpn = (status == UMP_MSG_STATUS_RPN);
710e9e02819STakashi Iwai 
711e9e02819STakashi Iwai 	data->cc.status = UMP_MSG_STATUS_CC;
712e9e02819STakashi Iwai 	data->cc.channel = event->data.control.channel & 0x0f;
713e9e02819STakashi Iwai 	data[1] = data[2] = data[3] = data[0];
714e9e02819STakashi Iwai 
715e9e02819STakashi Iwai 	data[0].cc.index = is_rpn ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB;
716e9e02819STakashi Iwai 	data[0].cc.data = (event->data.control.param >> 7) & 0x7f;
717e9e02819STakashi Iwai 	data[1].cc.index = is_rpn ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB;
718e9e02819STakashi Iwai 	data[1].cc.data = event->data.control.param & 0x7f;
719e9e02819STakashi Iwai 	data[2].cc.index = UMP_CC_DATA;
720e9e02819STakashi Iwai 	data[2].cc.data = (event->data.control.value >> 7) & 0x7f;
721e9e02819STakashi Iwai 	data[3].cc.index = UMP_CC_DATA_LSB;
722e9e02819STakashi Iwai 	data[3].cc.data = event->data.control.value & 0x7f;
723e9e02819STakashi Iwai 	return 4;
724e9e02819STakashi Iwai }
725e9e02819STakashi Iwai 
726e9e02819STakashi Iwai /* convert system / RT message to UMP */
system_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)727e9e02819STakashi Iwai static int system_ev_to_ump_midi1(const struct snd_seq_event *event,
728e9e02819STakashi Iwai 				  struct snd_seq_client_port *dest_port,
729e9e02819STakashi Iwai 				  union snd_ump_midi1_msg *data,
730e9e02819STakashi Iwai 				  unsigned char status)
731e9e02819STakashi Iwai {
7325f1b01a8STakashi Iwai 	data->system.type = UMP_MSG_TYPE_SYSTEM; // override
733e9e02819STakashi Iwai 	data->system.status = status;
734e9e02819STakashi Iwai 	return 1;
735e9e02819STakashi Iwai }
736e9e02819STakashi Iwai 
737e9e02819STakashi Iwai /* convert system / RT message with 1 parameter to UMP */
system_1p_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)738e9e02819STakashi Iwai static int system_1p_ev_to_ump_midi1(const struct snd_seq_event *event,
739e9e02819STakashi Iwai 				     struct snd_seq_client_port *dest_port,
740e9e02819STakashi Iwai 				     union snd_ump_midi1_msg *data,
741e9e02819STakashi Iwai 				     unsigned char status)
742e9e02819STakashi Iwai {
743b48f81d2STakashi Iwai 	data->system.type = UMP_MSG_TYPE_SYSTEM; // override
744e9e02819STakashi Iwai 	data->system.status = status;
745e9e02819STakashi Iwai 	data->system.parm1 = event->data.control.value & 0x7f;
746e9e02819STakashi Iwai 	return 1;
747e9e02819STakashi Iwai }
748e9e02819STakashi Iwai 
749e9e02819STakashi Iwai /* convert system / RT message with two parameters to UMP */
system_2p_ev_to_ump_midi1(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi1_msg * data,unsigned char status)750e9e02819STakashi Iwai static int system_2p_ev_to_ump_midi1(const struct snd_seq_event *event,
751e9e02819STakashi Iwai 				     struct snd_seq_client_port *dest_port,
752e9e02819STakashi Iwai 				     union snd_ump_midi1_msg *data,
753e9e02819STakashi Iwai 				     unsigned char status)
754e9e02819STakashi Iwai {
755b48f81d2STakashi Iwai 	data->system.type = UMP_MSG_TYPE_SYSTEM; // override
756e9e02819STakashi Iwai 	data->system.status = status;
757b7c4ef7bSTakashi Iwai 	data->system.parm1 = event->data.control.value & 0x7f;
758b7c4ef7bSTakashi Iwai 	data->system.parm2 = (event->data.control.value >> 7) & 0x7f;
759e9e02819STakashi Iwai 	return 1;
760e9e02819STakashi Iwai }
761e9e02819STakashi Iwai 
762e9e02819STakashi Iwai /* Conversion to UMP MIDI 2.0 */
763e9e02819STakashi Iwai 
764e9e02819STakashi Iwai /* convert note on/off event to MIDI 2.0 UMP */
note_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)765e9e02819STakashi Iwai static int note_ev_to_ump_midi2(const struct snd_seq_event *event,
766e9e02819STakashi Iwai 				struct snd_seq_client_port *dest_port,
767e9e02819STakashi Iwai 				union snd_ump_midi2_msg *data,
768e9e02819STakashi Iwai 				unsigned char status)
769e9e02819STakashi Iwai {
770e9e02819STakashi Iwai 	if (!event->data.note.velocity)
771e9e02819STakashi Iwai 		status = UMP_MSG_STATUS_NOTE_OFF;
772e9e02819STakashi Iwai 	data->note.status = status;
773e9e02819STakashi Iwai 	data->note.channel = event->data.note.channel & 0x0f;
774e9e02819STakashi Iwai 	data->note.note = event->data.note.note & 0x7f;
775e9e02819STakashi Iwai 	data->note.velocity = upscale_7_to_16bit(event->data.note.velocity & 0x7f);
776e9e02819STakashi Iwai 	return 1;
777e9e02819STakashi Iwai }
778e9e02819STakashi Iwai 
779e9e02819STakashi Iwai /* convert PAF event to MIDI 2.0 UMP */
paf_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)780e9e02819STakashi Iwai static int paf_ev_to_ump_midi2(const struct snd_seq_event *event,
781e9e02819STakashi Iwai 			       struct snd_seq_client_port *dest_port,
782e9e02819STakashi Iwai 			       union snd_ump_midi2_msg *data,
783e9e02819STakashi Iwai 			       unsigned char status)
784e9e02819STakashi Iwai {
785e9e02819STakashi Iwai 	data->paf.status = status;
786e9e02819STakashi Iwai 	data->paf.channel = event->data.note.channel & 0x0f;
787e9e02819STakashi Iwai 	data->paf.note = event->data.note.note & 0x7f;
788e9e02819STakashi Iwai 	data->paf.data = upscale_7_to_32bit(event->data.note.velocity & 0x7f);
789e9e02819STakashi Iwai 	return 1;
790e9e02819STakashi Iwai }
791e9e02819STakashi Iwai 
792e9e02819STakashi Iwai /* set up the MIDI2 RPN/NRPN packet data from the parsed info */
fill_rpn(struct snd_seq_ump_midi2_bank * cc,union snd_ump_midi2_msg * data)793e9e02819STakashi Iwai static void fill_rpn(struct snd_seq_ump_midi2_bank *cc,
794e9e02819STakashi Iwai 		     union snd_ump_midi2_msg *data)
795e9e02819STakashi Iwai {
796e9e02819STakashi Iwai 	if (cc->rpn_set) {
797e9e02819STakashi Iwai 		data->rpn.status = UMP_MSG_STATUS_RPN;
798e9e02819STakashi Iwai 		data->rpn.bank = cc->cc_rpn_msb;
799e9e02819STakashi Iwai 		data->rpn.index = cc->cc_rpn_lsb;
800e9e02819STakashi Iwai 		cc->rpn_set = 0;
801e9e02819STakashi Iwai 		cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
802e9e02819STakashi Iwai 	} else {
803e9e02819STakashi Iwai 		data->rpn.status = UMP_MSG_STATUS_NRPN;
804e9e02819STakashi Iwai 		data->rpn.bank = cc->cc_nrpn_msb;
805e9e02819STakashi Iwai 		data->rpn.index = cc->cc_nrpn_lsb;
806e9e02819STakashi Iwai 		cc->nrpn_set = 0;
807e9e02819STakashi Iwai 		cc->cc_nrpn_msb = cc->cc_nrpn_lsb = 0;
808e9e02819STakashi Iwai 	}
809e9e02819STakashi Iwai 	data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
810e9e02819STakashi Iwai 					     cc->cc_data_lsb);
811e9e02819STakashi Iwai 	cc->cc_data_msb = cc->cc_data_lsb = 0;
812e9e02819STakashi Iwai }
813e9e02819STakashi Iwai 
814e9e02819STakashi Iwai /* convert CC event to MIDI 2.0 UMP */
cc_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)815e9e02819STakashi Iwai static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
816e9e02819STakashi Iwai 			      struct snd_seq_client_port *dest_port,
817e9e02819STakashi Iwai 			      union snd_ump_midi2_msg *data,
818e9e02819STakashi Iwai 			      unsigned char status)
819e9e02819STakashi Iwai {
820e9e02819STakashi Iwai 	unsigned char channel = event->data.control.channel & 0x0f;
821e9e02819STakashi Iwai 	unsigned char index = event->data.control.param & 0x7f;
822e9e02819STakashi Iwai 	unsigned char val = event->data.control.value & 0x7f;
823e9e02819STakashi Iwai 	struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
824e9e02819STakashi Iwai 
825e9e02819STakashi Iwai 	/* process special CC's (bank/rpn/nrpn) */
826e9e02819STakashi Iwai 	switch (index) {
827e9e02819STakashi Iwai 	case UMP_CC_RPN_MSB:
828e9e02819STakashi Iwai 		cc->rpn_set = 1;
829e9e02819STakashi Iwai 		cc->cc_rpn_msb = val;
830e9e02819STakashi Iwai 		return 0; // skip
831e9e02819STakashi Iwai 	case UMP_CC_RPN_LSB:
832e9e02819STakashi Iwai 		cc->rpn_set = 1;
833e9e02819STakashi Iwai 		cc->cc_rpn_lsb = val;
834e9e02819STakashi Iwai 		return 0; // skip
835e9e02819STakashi Iwai 	case UMP_CC_NRPN_MSB:
836e9e02819STakashi Iwai 		cc->nrpn_set = 1;
837e9e02819STakashi Iwai 		cc->cc_nrpn_msb = val;
838e9e02819STakashi Iwai 		return 0; // skip
839e9e02819STakashi Iwai 	case UMP_CC_NRPN_LSB:
840e9e02819STakashi Iwai 		cc->nrpn_set = 1;
841e9e02819STakashi Iwai 		cc->cc_nrpn_lsb = val;
842e9e02819STakashi Iwai 		return 0; // skip
843e9e02819STakashi Iwai 	case UMP_CC_DATA:
844e9e02819STakashi Iwai 		cc->cc_data_msb = val;
845e9e02819STakashi Iwai 		return 0; // skip
846e9e02819STakashi Iwai 	case UMP_CC_BANK_SELECT:
847e9e02819STakashi Iwai 		cc->bank_set = 1;
848e9e02819STakashi Iwai 		cc->cc_bank_msb = val;
849e9e02819STakashi Iwai 		return 0; // skip
850e9e02819STakashi Iwai 	case UMP_CC_BANK_SELECT_LSB:
851e9e02819STakashi Iwai 		cc->bank_set = 1;
852e9e02819STakashi Iwai 		cc->cc_bank_lsb = val;
853e9e02819STakashi Iwai 		return 0; // skip
854e9e02819STakashi Iwai 	case UMP_CC_DATA_LSB:
855e9e02819STakashi Iwai 		cc->cc_data_lsb = val;
856e9e02819STakashi Iwai 		if (!(cc->rpn_set || cc->nrpn_set))
857e9e02819STakashi Iwai 			return 0; // skip
858e9e02819STakashi Iwai 		fill_rpn(cc, data);
859e9e02819STakashi Iwai 		return 1;
860e9e02819STakashi Iwai 	}
861e9e02819STakashi Iwai 
862e9e02819STakashi Iwai 	data->cc.status = status;
863e9e02819STakashi Iwai 	data->cc.channel = channel;
864e9e02819STakashi Iwai 	data->cc.index = index;
865e9e02819STakashi Iwai 	data->cc.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
866e9e02819STakashi Iwai 	return 1;
867e9e02819STakashi Iwai }
868e9e02819STakashi Iwai 
869e9e02819STakashi Iwai /* convert one-parameter control event to MIDI 2.0 UMP */
ctrl_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)870e9e02819STakashi Iwai static int ctrl_ev_to_ump_midi2(const struct snd_seq_event *event,
871e9e02819STakashi Iwai 				struct snd_seq_client_port *dest_port,
872e9e02819STakashi Iwai 				union snd_ump_midi2_msg *data,
873e9e02819STakashi Iwai 				unsigned char status)
874e9e02819STakashi Iwai {
875e9e02819STakashi Iwai 	data->caf.status = status;
876e9e02819STakashi Iwai 	data->caf.channel = event->data.control.channel & 0x0f;
877e9e02819STakashi Iwai 	data->caf.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
878e9e02819STakashi Iwai 	return 1;
879e9e02819STakashi Iwai }
880e9e02819STakashi Iwai 
881e9e02819STakashi Iwai /* convert program change event to MIDI 2.0 UMP */
pgm_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)882e9e02819STakashi Iwai static int pgm_ev_to_ump_midi2(const struct snd_seq_event *event,
883e9e02819STakashi Iwai 			       struct snd_seq_client_port *dest_port,
884e9e02819STakashi Iwai 			       union snd_ump_midi2_msg *data,
885e9e02819STakashi Iwai 			       unsigned char status)
886e9e02819STakashi Iwai {
887e9e02819STakashi Iwai 	unsigned char channel = event->data.control.channel & 0x0f;
888e9e02819STakashi Iwai 	struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
889e9e02819STakashi Iwai 
890e9e02819STakashi Iwai 	data->pg.status = status;
891e9e02819STakashi Iwai 	data->pg.channel = channel;
892e9e02819STakashi Iwai 	data->pg.program = event->data.control.value & 0x7f;
893e9e02819STakashi Iwai 	if (cc->bank_set) {
894e9e02819STakashi Iwai 		data->pg.bank_valid = 1;
895e9e02819STakashi Iwai 		data->pg.bank_msb = cc->cc_bank_msb;
896e9e02819STakashi Iwai 		data->pg.bank_lsb = cc->cc_bank_lsb;
897e9e02819STakashi Iwai 		cc->bank_set = 0;
898e9e02819STakashi Iwai 	}
899e9e02819STakashi Iwai 	return 1;
900e9e02819STakashi Iwai }
901e9e02819STakashi Iwai 
902e9e02819STakashi Iwai /* convert pitchbend event to MIDI 2.0 UMP */
pitchbend_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)903e9e02819STakashi Iwai static int pitchbend_ev_to_ump_midi2(const struct snd_seq_event *event,
904e9e02819STakashi Iwai 				     struct snd_seq_client_port *dest_port,
905e9e02819STakashi Iwai 				     union snd_ump_midi2_msg *data,
906e9e02819STakashi Iwai 				     unsigned char status)
907e9e02819STakashi Iwai {
908e9e02819STakashi Iwai 	int val = event->data.control.value + 8192;
909e9e02819STakashi Iwai 
910e9e02819STakashi Iwai 	val = clamp(val, 0, 0x3fff);
911e9e02819STakashi Iwai 	data->pb.status = status;
912e9e02819STakashi Iwai 	data->pb.channel = event->data.control.channel & 0x0f;
913e9e02819STakashi Iwai 	data->pb.data = upscale_14_to_32bit(val);
914e9e02819STakashi Iwai 	return 1;
915e9e02819STakashi Iwai }
916e9e02819STakashi Iwai 
917e9e02819STakashi Iwai /* convert 14bit control event to MIDI 2.0 UMP; split to two events */
ctrl14_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)918e9e02819STakashi Iwai static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event,
919e9e02819STakashi Iwai 				  struct snd_seq_client_port *dest_port,
920e9e02819STakashi Iwai 				  union snd_ump_midi2_msg *data,
921e9e02819STakashi Iwai 				  unsigned char status)
922e9e02819STakashi Iwai {
923e9e02819STakashi Iwai 	unsigned char channel = event->data.control.channel & 0x0f;
924e9e02819STakashi Iwai 	unsigned char index = event->data.control.param & 0x7f;
925e9e02819STakashi Iwai 	struct snd_seq_ump_midi2_bank *cc = &dest_port->midi2_bank[channel];
926e9e02819STakashi Iwai 	unsigned char msb, lsb;
927e9e02819STakashi Iwai 
928e9e02819STakashi Iwai 	msb = (event->data.control.value >> 7) & 0x7f;
929e9e02819STakashi Iwai 	lsb = event->data.control.value & 0x7f;
930e9e02819STakashi Iwai 	/* process special CC's (bank/rpn/nrpn) */
931e9e02819STakashi Iwai 	switch (index) {
932e9e02819STakashi Iwai 	case UMP_CC_BANK_SELECT:
933e9e02819STakashi Iwai 		cc->cc_bank_msb = msb;
934e9e02819STakashi Iwai 		fallthrough;
935e9e02819STakashi Iwai 	case UMP_CC_BANK_SELECT_LSB:
936e9e02819STakashi Iwai 		cc->bank_set = 1;
937e9e02819STakashi Iwai 		cc->cc_bank_lsb = lsb;
938e9e02819STakashi Iwai 		return 0; // skip
939e9e02819STakashi Iwai 	case UMP_CC_RPN_MSB:
940e9e02819STakashi Iwai 		cc->cc_rpn_msb = msb;
941e9e02819STakashi Iwai 		fallthrough;
942e9e02819STakashi Iwai 	case UMP_CC_RPN_LSB:
943e9e02819STakashi Iwai 		cc->rpn_set = 1;
944e9e02819STakashi Iwai 		cc->cc_rpn_lsb = lsb;
945e9e02819STakashi Iwai 		return 0; // skip
946e9e02819STakashi Iwai 	case UMP_CC_NRPN_MSB:
947e9e02819STakashi Iwai 		cc->cc_nrpn_msb = msb;
948e9e02819STakashi Iwai 		fallthrough;
949e9e02819STakashi Iwai 	case UMP_CC_NRPN_LSB:
950e9e02819STakashi Iwai 		cc->nrpn_set = 1;
951e9e02819STakashi Iwai 		cc->cc_nrpn_lsb = lsb;
952e9e02819STakashi Iwai 		return 0; // skip
953e9e02819STakashi Iwai 	case UMP_CC_DATA:
954e9e02819STakashi Iwai 		cc->cc_data_msb = msb;
955e9e02819STakashi Iwai 		fallthrough;
956e9e02819STakashi Iwai 	case UMP_CC_DATA_LSB:
957e9e02819STakashi Iwai 		cc->cc_data_lsb = lsb;
958e9e02819STakashi Iwai 		if (!(cc->rpn_set || cc->nrpn_set))
959e9e02819STakashi Iwai 			return 0; // skip
960e9e02819STakashi Iwai 		fill_rpn(cc, data);
961e9e02819STakashi Iwai 		return 1;
962e9e02819STakashi Iwai 	}
963e9e02819STakashi Iwai 
964e9e02819STakashi Iwai 	data->cc.status = UMP_MSG_STATUS_CC;
965e9e02819STakashi Iwai 	data->cc.channel = channel;
966e9e02819STakashi Iwai 	data->cc.index = index;
967e9e02819STakashi Iwai 	if (event->data.control.param < 0x20) {
968e9e02819STakashi Iwai 		data->cc.data = upscale_7_to_32bit(msb);
969e9e02819STakashi Iwai 		data[1] = data[0];
970e9e02819STakashi Iwai 		data[1].cc.index = event->data.control.param | 0x20;
971e9e02819STakashi Iwai 		data[1].cc.data = upscale_7_to_32bit(lsb);
972e9e02819STakashi Iwai 		return 2;
973e9e02819STakashi Iwai 	}
974e9e02819STakashi Iwai 
975e9e02819STakashi Iwai 	data->cc.data = upscale_7_to_32bit(lsb);
976e9e02819STakashi Iwai 	return 1;
977e9e02819STakashi Iwai }
978e9e02819STakashi Iwai 
979e9e02819STakashi Iwai /* convert RPN/NRPN event to MIDI 2.0 UMP */
rpn_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)980e9e02819STakashi Iwai static int rpn_ev_to_ump_midi2(const struct snd_seq_event *event,
981e9e02819STakashi Iwai 			       struct snd_seq_client_port *dest_port,
982e9e02819STakashi Iwai 			       union snd_ump_midi2_msg *data,
983e9e02819STakashi Iwai 			       unsigned char status)
984e9e02819STakashi Iwai {
985e9e02819STakashi Iwai 	data->rpn.status = status;
986e9e02819STakashi Iwai 	data->rpn.channel = event->data.control.channel;
987e9e02819STakashi Iwai 	data->rpn.bank = (event->data.control.param >> 7) & 0x7f;
988e9e02819STakashi Iwai 	data->rpn.index = event->data.control.param & 0x7f;
989e9e02819STakashi Iwai 	data->rpn.data = upscale_14_to_32bit(event->data.control.value & 0x3fff);
990e9e02819STakashi Iwai 	return 1;
991e9e02819STakashi Iwai }
992e9e02819STakashi Iwai 
993e9e02819STakashi Iwai /* convert system / RT message to UMP */
system_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)994e9e02819STakashi Iwai static int system_ev_to_ump_midi2(const struct snd_seq_event *event,
995e9e02819STakashi Iwai 				  struct snd_seq_client_port *dest_port,
996e9e02819STakashi Iwai 				  union snd_ump_midi2_msg *data,
997e9e02819STakashi Iwai 				  unsigned char status)
998e9e02819STakashi Iwai {
999e9e02819STakashi Iwai 	return system_ev_to_ump_midi1(event, dest_port,
1000e9e02819STakashi Iwai 				      (union snd_ump_midi1_msg *)data,
1001e9e02819STakashi Iwai 				      status);
1002e9e02819STakashi Iwai }
1003e9e02819STakashi Iwai 
1004e9e02819STakashi Iwai /* convert system / RT message with 1 parameter to UMP */
system_1p_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)1005e9e02819STakashi Iwai static int system_1p_ev_to_ump_midi2(const struct snd_seq_event *event,
1006e9e02819STakashi Iwai 				     struct snd_seq_client_port *dest_port,
1007e9e02819STakashi Iwai 				     union snd_ump_midi2_msg *data,
1008e9e02819STakashi Iwai 				     unsigned char status)
1009e9e02819STakashi Iwai {
1010e9e02819STakashi Iwai 	return system_1p_ev_to_ump_midi1(event, dest_port,
1011e9e02819STakashi Iwai 					 (union snd_ump_midi1_msg *)data,
1012e9e02819STakashi Iwai 					 status);
1013e9e02819STakashi Iwai }
1014e9e02819STakashi Iwai 
1015e9e02819STakashi Iwai /* convert system / RT message with two parameters to UMP */
system_2p_ev_to_ump_midi2(const struct snd_seq_event * event,struct snd_seq_client_port * dest_port,union snd_ump_midi2_msg * data,unsigned char status)1016e9e02819STakashi Iwai static int system_2p_ev_to_ump_midi2(const struct snd_seq_event *event,
1017e9e02819STakashi Iwai 				     struct snd_seq_client_port *dest_port,
1018e9e02819STakashi Iwai 				     union snd_ump_midi2_msg *data,
1019e9e02819STakashi Iwai 				     unsigned char status)
1020e9e02819STakashi Iwai {
1021e9e02819STakashi Iwai 	return system_1p_ev_to_ump_midi1(event, dest_port,
1022e9e02819STakashi Iwai 					 (union snd_ump_midi1_msg *)data,
1023e9e02819STakashi Iwai 					 status);
1024e9e02819STakashi Iwai }
1025e9e02819STakashi Iwai 
1026e9e02819STakashi Iwai struct seq_ev_to_ump {
1027e9e02819STakashi Iwai 	int seq_type;
1028e9e02819STakashi Iwai 	unsigned char status;
1029e9e02819STakashi Iwai 	int (*midi1_encode)(const struct snd_seq_event *event,
1030e9e02819STakashi Iwai 			    struct snd_seq_client_port *dest_port,
1031e9e02819STakashi Iwai 			    union snd_ump_midi1_msg *data,
1032e9e02819STakashi Iwai 			    unsigned char status);
1033e9e02819STakashi Iwai 	int (*midi2_encode)(const struct snd_seq_event *event,
1034e9e02819STakashi Iwai 			    struct snd_seq_client_port *dest_port,
1035e9e02819STakashi Iwai 			    union snd_ump_midi2_msg *data,
1036e9e02819STakashi Iwai 			    unsigned char status);
1037e9e02819STakashi Iwai };
1038e9e02819STakashi Iwai 
1039e9e02819STakashi Iwai static const struct seq_ev_to_ump seq_ev_ump_encoders[] = {
1040e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_NOTEON, UMP_MSG_STATUS_NOTE_ON,
1041e9e02819STakashi Iwai 	  note_ev_to_ump_midi1, note_ev_to_ump_midi2 },
1042e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_NOTEOFF, UMP_MSG_STATUS_NOTE_OFF,
1043e9e02819STakashi Iwai 	  note_ev_to_ump_midi1, note_ev_to_ump_midi2 },
1044e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_KEYPRESS, UMP_MSG_STATUS_POLY_PRESSURE,
1045e9e02819STakashi Iwai 	  note_ev_to_ump_midi1, paf_ev_to_ump_midi2 },
1046e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_CONTROLLER, UMP_MSG_STATUS_CC,
1047e9e02819STakashi Iwai 	  cc_ev_to_ump_midi1, cc_ev_to_ump_midi2 },
1048e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_PGMCHANGE, UMP_MSG_STATUS_PROGRAM,
1049e9e02819STakashi Iwai 	  ctrl_ev_to_ump_midi1, pgm_ev_to_ump_midi2 },
1050e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_CHANPRESS, UMP_MSG_STATUS_CHANNEL_PRESSURE,
1051e9e02819STakashi Iwai 	  ctrl_ev_to_ump_midi1, ctrl_ev_to_ump_midi2 },
1052e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_PITCHBEND, UMP_MSG_STATUS_PITCH_BEND,
1053e9e02819STakashi Iwai 	  pitchbend_ev_to_ump_midi1, pitchbend_ev_to_ump_midi2 },
1054e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_CONTROL14, 0,
1055e9e02819STakashi Iwai 	  ctrl14_ev_to_ump_midi1, ctrl14_ev_to_ump_midi2 },
1056e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_NONREGPARAM, UMP_MSG_STATUS_NRPN,
1057e9e02819STakashi Iwai 	  rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 },
1058e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_REGPARAM, UMP_MSG_STATUS_RPN,
1059e9e02819STakashi Iwai 	  rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 },
1060e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_QFRAME, UMP_SYSTEM_STATUS_MIDI_TIME_CODE,
1061e9e02819STakashi Iwai 	  system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 },
1062e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_SONGPOS, UMP_SYSTEM_STATUS_SONG_POSITION,
1063e9e02819STakashi Iwai 	  system_2p_ev_to_ump_midi1, system_2p_ev_to_ump_midi2 },
1064e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_SONGSEL, UMP_SYSTEM_STATUS_SONG_SELECT,
1065e9e02819STakashi Iwai 	  system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 },
1066e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_TUNE_REQUEST, UMP_SYSTEM_STATUS_TUNE_REQUEST,
1067e9e02819STakashi Iwai 	  system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
1068e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_CLOCK, UMP_SYSTEM_STATUS_TIMING_CLOCK,
1069e9e02819STakashi Iwai 	  system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
1070e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_START, UMP_SYSTEM_STATUS_START,
1071e9e02819STakashi Iwai 	  system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
1072e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_CONTINUE, UMP_SYSTEM_STATUS_CONTINUE,
1073e9e02819STakashi Iwai 	  system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
1074e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_STOP, UMP_SYSTEM_STATUS_STOP,
1075e9e02819STakashi Iwai 	  system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
1076e9e02819STakashi Iwai 	{ SNDRV_SEQ_EVENT_SENSING, UMP_SYSTEM_STATUS_ACTIVE_SENSING,
1077e9e02819STakashi Iwai 	  system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
1078*b89c2c56STakashi Iwai 	{ SNDRV_SEQ_EVENT_RESET, UMP_SYSTEM_STATUS_RESET,
1079*b89c2c56STakashi Iwai 	  system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
1080e9e02819STakashi Iwai };
1081e9e02819STakashi Iwai 
find_ump_encoder(int type)1082e9e02819STakashi Iwai static const struct seq_ev_to_ump *find_ump_encoder(int type)
1083e9e02819STakashi Iwai {
1084e9e02819STakashi Iwai 	int i;
1085e9e02819STakashi Iwai 
1086e9e02819STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(seq_ev_ump_encoders); i++)
1087e9e02819STakashi Iwai 		if (seq_ev_ump_encoders[i].seq_type == type)
1088e9e02819STakashi Iwai 			return &seq_ev_ump_encoders[i];
1089e9e02819STakashi Iwai 
1090e9e02819STakashi Iwai 	return NULL;
1091e9e02819STakashi Iwai }
1092e9e02819STakashi Iwai 
setup_ump_event(struct snd_seq_ump_event * dest,const struct snd_seq_event * src)1093e9e02819STakashi Iwai static void setup_ump_event(struct snd_seq_ump_event *dest,
1094e9e02819STakashi Iwai 			    const struct snd_seq_event *src)
1095e9e02819STakashi Iwai {
1096e9e02819STakashi Iwai 	memcpy(dest, src, sizeof(*src));
1097e9e02819STakashi Iwai 	dest->type = 0;
1098e9e02819STakashi Iwai 	dest->flags |= SNDRV_SEQ_EVENT_UMP;
1099e9e02819STakashi Iwai 	dest->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
1100e9e02819STakashi Iwai 	memset(dest->ump, 0, sizeof(dest->ump));
1101e9e02819STakashi Iwai }
1102e9e02819STakashi Iwai 
1103e9e02819STakashi Iwai /* Convert ALSA seq event to UMP MIDI 1.0 and deliver it */
cvt_to_ump_midi1(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * event,int atomic,int hop)1104e9e02819STakashi Iwai static int cvt_to_ump_midi1(struct snd_seq_client *dest,
1105e9e02819STakashi Iwai 			    struct snd_seq_client_port *dest_port,
1106e9e02819STakashi Iwai 			    struct snd_seq_event *event,
1107e9e02819STakashi Iwai 			    int atomic, int hop)
1108e9e02819STakashi Iwai {
1109e9e02819STakashi Iwai 	const struct seq_ev_to_ump *encoder;
1110e9e02819STakashi Iwai 	struct snd_seq_ump_event ev_cvt;
1111e9e02819STakashi Iwai 	union snd_ump_midi1_msg data[4];
1112e9e02819STakashi Iwai 	int i, n, err;
1113e9e02819STakashi Iwai 
1114e9e02819STakashi Iwai 	encoder = find_ump_encoder(event->type);
1115e9e02819STakashi Iwai 	if (!encoder)
1116e9e02819STakashi Iwai 		return __snd_seq_deliver_single_event(dest, dest_port,
1117e9e02819STakashi Iwai 						      event, atomic, hop);
1118e9e02819STakashi Iwai 
1119e9e02819STakashi Iwai 	data->raw = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE);
1120e9e02819STakashi Iwai 	n = encoder->midi1_encode(event, dest_port, data, encoder->status);
1121e9e02819STakashi Iwai 	if (!n)
1122e9e02819STakashi Iwai 		return 0;
1123e9e02819STakashi Iwai 
1124e9e02819STakashi Iwai 	setup_ump_event(&ev_cvt, event);
1125e9e02819STakashi Iwai 	for (i = 0; i < n; i++) {
1126e9e02819STakashi Iwai 		ev_cvt.ump[0] = data[i].raw;
1127e9e02819STakashi Iwai 		err = __snd_seq_deliver_single_event(dest, dest_port,
1128e9e02819STakashi Iwai 						     (struct snd_seq_event *)&ev_cvt,
1129e9e02819STakashi Iwai 						     atomic, hop);
1130e9e02819STakashi Iwai 		if (err < 0)
1131e9e02819STakashi Iwai 			return err;
1132e9e02819STakashi Iwai 	}
1133e9e02819STakashi Iwai 
1134e9e02819STakashi Iwai 	return 0;
1135e9e02819STakashi Iwai }
1136e9e02819STakashi Iwai 
1137e9e02819STakashi Iwai /* Convert ALSA seq event to UMP MIDI 2.0 and deliver it */
cvt_to_ump_midi2(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * event,int atomic,int hop)1138e9e02819STakashi Iwai static int cvt_to_ump_midi2(struct snd_seq_client *dest,
1139e9e02819STakashi Iwai 			    struct snd_seq_client_port *dest_port,
1140e9e02819STakashi Iwai 			    struct snd_seq_event *event,
1141e9e02819STakashi Iwai 			    int atomic, int hop)
1142e9e02819STakashi Iwai {
1143e9e02819STakashi Iwai 	const struct seq_ev_to_ump *encoder;
1144e9e02819STakashi Iwai 	struct snd_seq_ump_event ev_cvt;
1145e9e02819STakashi Iwai 	union snd_ump_midi2_msg data[2];
1146e9e02819STakashi Iwai 	int i, n, err;
1147e9e02819STakashi Iwai 
1148e9e02819STakashi Iwai 	encoder = find_ump_encoder(event->type);
1149e9e02819STakashi Iwai 	if (!encoder)
1150e9e02819STakashi Iwai 		return __snd_seq_deliver_single_event(dest, dest_port,
1151e9e02819STakashi Iwai 						      event, atomic, hop);
1152e9e02819STakashi Iwai 
1153e9e02819STakashi Iwai 	data->raw[0] = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE);
1154e9e02819STakashi Iwai 	data->raw[1] = 0;
1155e9e02819STakashi Iwai 	n = encoder->midi2_encode(event, dest_port, data, encoder->status);
1156e9e02819STakashi Iwai 	if (!n)
1157e9e02819STakashi Iwai 		return 0;
1158e9e02819STakashi Iwai 
1159e9e02819STakashi Iwai 	setup_ump_event(&ev_cvt, event);
1160e9e02819STakashi Iwai 	for (i = 0; i < n; i++) {
1161e9e02819STakashi Iwai 		memcpy(ev_cvt.ump, &data[i], sizeof(data[i]));
1162e9e02819STakashi Iwai 		err = __snd_seq_deliver_single_event(dest, dest_port,
1163e9e02819STakashi Iwai 						     (struct snd_seq_event *)&ev_cvt,
1164e9e02819STakashi Iwai 						     atomic, hop);
1165e9e02819STakashi Iwai 		if (err < 0)
1166e9e02819STakashi Iwai 			return err;
1167e9e02819STakashi Iwai 	}
1168e9e02819STakashi Iwai 
1169e9e02819STakashi Iwai 	return 0;
1170e9e02819STakashi Iwai }
1171e9e02819STakashi Iwai 
1172e9e02819STakashi Iwai /* Fill up a sysex7 UMP from the byte stream */
fill_sysex7_ump(struct snd_seq_client_port * dest_port,u32 * val,u8 status,u8 * buf,int len)1173e9e02819STakashi Iwai static void fill_sysex7_ump(struct snd_seq_client_port *dest_port,
1174e9e02819STakashi Iwai 			    u32 *val, u8 status, u8 *buf, int len)
1175e9e02819STakashi Iwai {
1176e9e02819STakashi Iwai 	memset(val, 0, 8);
1177e9e02819STakashi Iwai 	memcpy((u8 *)val + 2, buf, len);
1178e9e02819STakashi Iwai #ifdef __LITTLE_ENDIAN
1179e9e02819STakashi Iwai 	swab32_array(val, 2);
1180e9e02819STakashi Iwai #endif
1181e9e02819STakashi Iwai 	val[0] |= ump_compose(UMP_MSG_TYPE_DATA, get_ump_group(dest_port),
1182e9e02819STakashi Iwai 			      status, len);
1183e9e02819STakashi Iwai }
1184e9e02819STakashi Iwai 
1185e9e02819STakashi Iwai /* Convert sysex var event to UMP sysex7 packets and deliver them */
cvt_sysex_to_ump(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * event,int atomic,int hop)1186e9e02819STakashi Iwai static int cvt_sysex_to_ump(struct snd_seq_client *dest,
1187e9e02819STakashi Iwai 			    struct snd_seq_client_port *dest_port,
1188e9e02819STakashi Iwai 			    struct snd_seq_event *event,
1189e9e02819STakashi Iwai 			    int atomic, int hop)
1190e9e02819STakashi Iwai {
1191e9e02819STakashi Iwai 	struct snd_seq_ump_event ev_cvt;
1192e9e02819STakashi Iwai 	unsigned char status;
1193e9e02819STakashi Iwai 	u8 buf[6], *xbuf;
1194e9e02819STakashi Iwai 	int offset = 0;
1195e9e02819STakashi Iwai 	int len, err;
1196e9e02819STakashi Iwai 
1197e9e02819STakashi Iwai 	if (!snd_seq_ev_is_variable(event))
1198e9e02819STakashi Iwai 		return 0;
1199e9e02819STakashi Iwai 
1200e9e02819STakashi Iwai 	setup_ump_event(&ev_cvt, event);
1201e9e02819STakashi Iwai 	for (;;) {
1202e9e02819STakashi Iwai 		len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset);
1203e9e02819STakashi Iwai 		if (len <= 0)
1204e9e02819STakashi Iwai 			break;
1205e9e02819STakashi Iwai 		if (WARN_ON(len > 6))
1206e9e02819STakashi Iwai 			break;
1207e9e02819STakashi Iwai 		offset += len;
1208e9e02819STakashi Iwai 		xbuf = buf;
1209e9e02819STakashi Iwai 		if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) {
1210e9e02819STakashi Iwai 			status = UMP_SYSEX_STATUS_START;
1211e9e02819STakashi Iwai 			xbuf++;
1212e9e02819STakashi Iwai 			len--;
1213e9e02819STakashi Iwai 			if (len > 0 && xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
1214e9e02819STakashi Iwai 				status = UMP_SYSEX_STATUS_SINGLE;
1215e9e02819STakashi Iwai 				len--;
1216e9e02819STakashi Iwai 			}
1217e9e02819STakashi Iwai 		} else {
1218e9e02819STakashi Iwai 			if (xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
1219e9e02819STakashi Iwai 				status = UMP_SYSEX_STATUS_END;
1220e9e02819STakashi Iwai 				len--;
1221e9e02819STakashi Iwai 			} else {
1222e9e02819STakashi Iwai 				status = UMP_SYSEX_STATUS_CONTINUE;
1223e9e02819STakashi Iwai 			}
1224e9e02819STakashi Iwai 		}
1225e9e02819STakashi Iwai 		fill_sysex7_ump(dest_port, ev_cvt.ump, status, xbuf, len);
1226e9e02819STakashi Iwai 		err = __snd_seq_deliver_single_event(dest, dest_port,
1227e9e02819STakashi Iwai 						     (struct snd_seq_event *)&ev_cvt,
1228e9e02819STakashi Iwai 						     atomic, hop);
1229e9e02819STakashi Iwai 		if (err < 0)
1230e9e02819STakashi Iwai 			return err;
1231e9e02819STakashi Iwai 	}
1232e9e02819STakashi Iwai 	return 0;
1233e9e02819STakashi Iwai }
1234e9e02819STakashi Iwai 
1235e9e02819STakashi Iwai /* Convert to UMP packet and deliver */
snd_seq_deliver_to_ump(struct snd_seq_client * source,struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * event,int atomic,int hop)1236e9e02819STakashi Iwai int snd_seq_deliver_to_ump(struct snd_seq_client *source,
1237e9e02819STakashi Iwai 			   struct snd_seq_client *dest,
1238e9e02819STakashi Iwai 			   struct snd_seq_client_port *dest_port,
1239e9e02819STakashi Iwai 			   struct snd_seq_event *event,
1240e9e02819STakashi Iwai 			   int atomic, int hop)
1241e9e02819STakashi Iwai {
124222eefaeaSTakashi Iwai 	if (dest->group_filter & (1U << dest_port->ump_group))
124322eefaeaSTakashi Iwai 		return 0; /* group filtered - skip the event */
1244e9e02819STakashi Iwai 	if (event->type == SNDRV_SEQ_EVENT_SYSEX)
1245e9e02819STakashi Iwai 		return cvt_sysex_to_ump(dest, dest_port, event, atomic, hop);
1246e9e02819STakashi Iwai 	else if (snd_seq_client_is_midi2(dest))
1247e9e02819STakashi Iwai 		return cvt_to_ump_midi2(dest, dest_port, event, atomic, hop);
1248e9e02819STakashi Iwai 	else
1249e9e02819STakashi Iwai 		return cvt_to_ump_midi1(dest, dest_port, event, atomic, hop);
1250e9e02819STakashi Iwai }
1251