brintos

brintos / linux-shallow public Read only

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