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