10b5288f5STakashi Iwai // SPDX-License-Identifier: GPL-2.0-or-later
20b5288f5STakashi Iwai /*
30b5288f5STakashi Iwai * Helpers for UMP <-> MIDI 1.0 byte stream conversion
40b5288f5STakashi Iwai */
50b5288f5STakashi Iwai
60b5288f5STakashi Iwai #include <linux/module.h>
70b5288f5STakashi Iwai #include <linux/export.h>
80b5288f5STakashi Iwai #include <sound/core.h>
90b5288f5STakashi Iwai #include <sound/asound.h>
100b5288f5STakashi Iwai #include <sound/ump.h>
1133cd7630STakashi Iwai #include <sound/ump_convert.h>
120b5288f5STakashi Iwai
130b5288f5STakashi Iwai /*
140b5288f5STakashi Iwai * Upgrade / downgrade value bits
150b5288f5STakashi Iwai */
downscale_32_to_7bit(u32 src)160b5288f5STakashi Iwai static u8 downscale_32_to_7bit(u32 src)
170b5288f5STakashi Iwai {
180b5288f5STakashi Iwai return src >> 25;
190b5288f5STakashi Iwai }
200b5288f5STakashi Iwai
downscale_32_to_14bit(u32 src)210b5288f5STakashi Iwai static u16 downscale_32_to_14bit(u32 src)
220b5288f5STakashi Iwai {
230b5288f5STakashi Iwai return src >> 18;
240b5288f5STakashi Iwai }
250b5288f5STakashi Iwai
downscale_16_to_7bit(u16 src)260b5288f5STakashi Iwai static u8 downscale_16_to_7bit(u16 src)
270b5288f5STakashi Iwai {
280b5288f5STakashi Iwai return src >> 9;
290b5288f5STakashi Iwai }
300b5288f5STakashi Iwai
upscale_7_to_16bit(u8 src)310b5288f5STakashi Iwai static u16 upscale_7_to_16bit(u8 src)
320b5288f5STakashi Iwai {
330b5288f5STakashi Iwai u16 val, repeat;
340b5288f5STakashi Iwai
350b5288f5STakashi Iwai val = (u16)src << 9;
360b5288f5STakashi Iwai if (src <= 0x40)
370b5288f5STakashi Iwai return val;
380b5288f5STakashi Iwai repeat = src & 0x3f;
390b5288f5STakashi Iwai return val | (repeat << 3) | (repeat >> 3);
400b5288f5STakashi Iwai }
410b5288f5STakashi Iwai
upscale_7_to_32bit(u8 src)420b5288f5STakashi Iwai static u32 upscale_7_to_32bit(u8 src)
430b5288f5STakashi Iwai {
440b5288f5STakashi Iwai u32 val, repeat;
450b5288f5STakashi Iwai
460b5288f5STakashi Iwai val = src << 25;
470b5288f5STakashi Iwai if (src <= 0x40)
480b5288f5STakashi Iwai return val;
490b5288f5STakashi Iwai repeat = src & 0x3f;
500b5288f5STakashi Iwai return val | (repeat << 19) | (repeat << 13) |
510b5288f5STakashi Iwai (repeat << 7) | (repeat << 1) | (repeat >> 5);
520b5288f5STakashi Iwai }
530b5288f5STakashi Iwai
upscale_14_to_32bit(u16 src)540b5288f5STakashi Iwai static u32 upscale_14_to_32bit(u16 src)
550b5288f5STakashi Iwai {
560b5288f5STakashi Iwai u32 val, repeat;
570b5288f5STakashi Iwai
580b5288f5STakashi Iwai val = src << 18;
590b5288f5STakashi Iwai if (src <= 0x2000)
600b5288f5STakashi Iwai return val;
610b5288f5STakashi Iwai repeat = src & 0x1fff;
620b5288f5STakashi Iwai return val | (repeat << 5) | (repeat >> 8);
630b5288f5STakashi Iwai }
640b5288f5STakashi Iwai
650b5288f5STakashi Iwai /*
660b5288f5STakashi Iwai * UMP -> MIDI 1 byte stream conversion
670b5288f5STakashi Iwai */
680b5288f5STakashi Iwai /* convert a UMP System message to MIDI 1.0 byte stream */
cvt_ump_system_to_legacy(u32 data,unsigned char * buf)690b5288f5STakashi Iwai static int cvt_ump_system_to_legacy(u32 data, unsigned char *buf)
700b5288f5STakashi Iwai {
710b5288f5STakashi Iwai buf[0] = ump_message_status_channel(data);
720b5288f5STakashi Iwai switch (ump_message_status_code(data)) {
730b5288f5STakashi Iwai case UMP_SYSTEM_STATUS_MIDI_TIME_CODE:
740b5288f5STakashi Iwai case UMP_SYSTEM_STATUS_SONG_SELECT:
750b5288f5STakashi Iwai buf[1] = (data >> 8) & 0x7f;
764eecae44STakashi Iwai return 2;
770b5288f5STakashi Iwai case UMP_SYSTEM_STATUS_SONG_POSITION:
780b5288f5STakashi Iwai buf[1] = (data >> 8) & 0x7f;
790b5288f5STakashi Iwai buf[2] = data & 0x7f;
800b5288f5STakashi Iwai return 3;
810b5288f5STakashi Iwai default:
820b5288f5STakashi Iwai return 1;
830b5288f5STakashi Iwai }
840b5288f5STakashi Iwai }
850b5288f5STakashi Iwai
860b5288f5STakashi Iwai /* convert a UMP MIDI 1.0 Channel Voice message to MIDI 1.0 byte stream */
cvt_ump_midi1_to_legacy(u32 data,unsigned char * buf)870b5288f5STakashi Iwai static int cvt_ump_midi1_to_legacy(u32 data, unsigned char *buf)
880b5288f5STakashi Iwai {
890b5288f5STakashi Iwai buf[0] = ump_message_status_channel(data);
900b5288f5STakashi Iwai buf[1] = (data >> 8) & 0xff;
910b5288f5STakashi Iwai switch (ump_message_status_code(data)) {
920b5288f5STakashi Iwai case UMP_MSG_STATUS_PROGRAM:
930b5288f5STakashi Iwai case UMP_MSG_STATUS_CHANNEL_PRESSURE:
940b5288f5STakashi Iwai return 2;
950b5288f5STakashi Iwai default:
960b5288f5STakashi Iwai buf[2] = data & 0xff;
970b5288f5STakashi Iwai return 3;
980b5288f5STakashi Iwai }
990b5288f5STakashi Iwai }
1000b5288f5STakashi Iwai
1010b5288f5STakashi Iwai /* convert a UMP MIDI 2.0 Channel Voice message to MIDI 1.0 byte stream */
cvt_ump_midi2_to_legacy(const union snd_ump_midi2_msg * midi2,unsigned char * buf)1020b5288f5STakashi Iwai static int cvt_ump_midi2_to_legacy(const union snd_ump_midi2_msg *midi2,
1030b5288f5STakashi Iwai unsigned char *buf)
1040b5288f5STakashi Iwai {
1050b5288f5STakashi Iwai unsigned char status = midi2->note.status;
1060b5288f5STakashi Iwai unsigned char channel = midi2->note.channel;
1070b5288f5STakashi Iwai u16 v;
1080b5288f5STakashi Iwai
1090b5288f5STakashi Iwai buf[0] = (status << 4) | channel;
1100b5288f5STakashi Iwai switch (status) {
1110b5288f5STakashi Iwai case UMP_MSG_STATUS_NOTE_OFF:
1120b5288f5STakashi Iwai case UMP_MSG_STATUS_NOTE_ON:
1130b5288f5STakashi Iwai buf[1] = midi2->note.note;
1140b5288f5STakashi Iwai buf[2] = downscale_16_to_7bit(midi2->note.velocity);
1150b5288f5STakashi Iwai if (status == UMP_MSG_STATUS_NOTE_ON && !buf[2])
1160b5288f5STakashi Iwai buf[2] = 1;
1170b5288f5STakashi Iwai return 3;
1180b5288f5STakashi Iwai case UMP_MSG_STATUS_POLY_PRESSURE:
1190b5288f5STakashi Iwai buf[1] = midi2->paf.note;
1200b5288f5STakashi Iwai buf[2] = downscale_32_to_7bit(midi2->paf.data);
1210b5288f5STakashi Iwai return 3;
1220b5288f5STakashi Iwai case UMP_MSG_STATUS_CC:
1230b5288f5STakashi Iwai buf[1] = midi2->cc.index;
1240b5288f5STakashi Iwai buf[2] = downscale_32_to_7bit(midi2->cc.data);
1250b5288f5STakashi Iwai return 3;
1260b5288f5STakashi Iwai case UMP_MSG_STATUS_CHANNEL_PRESSURE:
1270b5288f5STakashi Iwai buf[1] = downscale_32_to_7bit(midi2->caf.data);
1280b5288f5STakashi Iwai return 2;
1290b5288f5STakashi Iwai case UMP_MSG_STATUS_PROGRAM:
1300b5288f5STakashi Iwai if (midi2->pg.bank_valid) {
1310b5288f5STakashi Iwai buf[0] = channel | (UMP_MSG_STATUS_CC << 4);
1320b5288f5STakashi Iwai buf[1] = UMP_CC_BANK_SELECT;
1330b5288f5STakashi Iwai buf[2] = midi2->pg.bank_msb;
1340b5288f5STakashi Iwai buf[3] = channel | (UMP_MSG_STATUS_CC << 4);
1350b5288f5STakashi Iwai buf[4] = UMP_CC_BANK_SELECT_LSB;
1360b5288f5STakashi Iwai buf[5] = midi2->pg.bank_lsb;
1370b5288f5STakashi Iwai buf[6] = channel | (UMP_MSG_STATUS_PROGRAM << 4);
1380b5288f5STakashi Iwai buf[7] = midi2->pg.program;
1390b5288f5STakashi Iwai return 8;
1400b5288f5STakashi Iwai }
1410b5288f5STakashi Iwai buf[1] = midi2->pg.program;
1420b5288f5STakashi Iwai return 2;
1430b5288f5STakashi Iwai case UMP_MSG_STATUS_PITCH_BEND:
1440b5288f5STakashi Iwai v = downscale_32_to_14bit(midi2->pb.data);
1450b5288f5STakashi Iwai buf[1] = v & 0x7f;
1460b5288f5STakashi Iwai buf[2] = v >> 7;
1470b5288f5STakashi Iwai return 3;
1480b5288f5STakashi Iwai case UMP_MSG_STATUS_RPN:
1490b5288f5STakashi Iwai case UMP_MSG_STATUS_NRPN:
1500b5288f5STakashi Iwai buf[0] = channel | (UMP_MSG_STATUS_CC << 4);
1510b5288f5STakashi Iwai buf[1] = status == UMP_MSG_STATUS_RPN ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB;
1520b5288f5STakashi Iwai buf[2] = midi2->rpn.bank;
1530b5288f5STakashi Iwai buf[3] = buf[0];
1540b5288f5STakashi Iwai buf[4] = status == UMP_MSG_STATUS_RPN ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB;
1550b5288f5STakashi Iwai buf[5] = midi2->rpn.index;
1560b5288f5STakashi Iwai buf[6] = buf[0];
1570b5288f5STakashi Iwai buf[7] = UMP_CC_DATA;
1580b5288f5STakashi Iwai v = downscale_32_to_14bit(midi2->rpn.data);
1590b5288f5STakashi Iwai buf[8] = v >> 7;
1600b5288f5STakashi Iwai buf[9] = buf[0];
1610b5288f5STakashi Iwai buf[10] = UMP_CC_DATA_LSB;
1620b5288f5STakashi Iwai buf[11] = v & 0x7f;
1630b5288f5STakashi Iwai return 12;
1640b5288f5STakashi Iwai default:
1650b5288f5STakashi Iwai return 0;
1660b5288f5STakashi Iwai }
1670b5288f5STakashi Iwai }
1680b5288f5STakashi Iwai
1690b5288f5STakashi Iwai /* convert a UMP 7-bit SysEx message to MIDI 1.0 byte stream */
cvt_ump_sysex7_to_legacy(const u32 * data,unsigned char * buf)1700b5288f5STakashi Iwai static int cvt_ump_sysex7_to_legacy(const u32 *data, unsigned char *buf)
1710b5288f5STakashi Iwai {
1720b5288f5STakashi Iwai unsigned char status;
1730b5288f5STakashi Iwai unsigned char bytes;
1740b5288f5STakashi Iwai int size, offset;
1750b5288f5STakashi Iwai
1760b5288f5STakashi Iwai status = ump_sysex_message_status(*data);
1770b5288f5STakashi Iwai if (status > UMP_SYSEX_STATUS_END)
1780b5288f5STakashi Iwai return 0; // unsupported, skip
1790b5288f5STakashi Iwai bytes = ump_sysex_message_length(*data);
1800b5288f5STakashi Iwai if (bytes > 6)
1810b5288f5STakashi Iwai return 0; // skip
1820b5288f5STakashi Iwai
1830b5288f5STakashi Iwai size = 0;
1840b5288f5STakashi Iwai if (status == UMP_SYSEX_STATUS_SINGLE ||
1850b5288f5STakashi Iwai status == UMP_SYSEX_STATUS_START) {
1860b5288f5STakashi Iwai buf[0] = UMP_MIDI1_MSG_SYSEX_START;
1870b5288f5STakashi Iwai size = 1;
1880b5288f5STakashi Iwai }
1890b5288f5STakashi Iwai
1900b5288f5STakashi Iwai offset = 8;
1910b5288f5STakashi Iwai for (; bytes; bytes--, size++) {
1920b5288f5STakashi Iwai buf[size] = (*data >> offset) & 0x7f;
1930b5288f5STakashi Iwai if (!offset) {
1940b5288f5STakashi Iwai offset = 24;
1950b5288f5STakashi Iwai data++;
1960b5288f5STakashi Iwai } else {
1970b5288f5STakashi Iwai offset -= 8;
1980b5288f5STakashi Iwai }
1990b5288f5STakashi Iwai }
2000b5288f5STakashi Iwai
2010b5288f5STakashi Iwai if (status == UMP_SYSEX_STATUS_SINGLE ||
2020b5288f5STakashi Iwai status == UMP_SYSEX_STATUS_END)
2030b5288f5STakashi Iwai buf[size++] = UMP_MIDI1_MSG_SYSEX_END;
2040b5288f5STakashi Iwai
2050b5288f5STakashi Iwai return size;
2060b5288f5STakashi Iwai }
2070b5288f5STakashi Iwai
20833cd7630STakashi Iwai /**
20933cd7630STakashi Iwai * snd_ump_convert_from_ump - convert from UMP to legacy MIDI
21033cd7630STakashi Iwai * @data: UMP packet
21133cd7630STakashi Iwai * @buf: buffer to store legacy MIDI data
21233cd7630STakashi Iwai * @group_ret: pointer to store the target group
21333cd7630STakashi Iwai *
21433cd7630STakashi Iwai * Convert from a UMP packet @data to MIDI 1.0 bytes at @buf.
21533cd7630STakashi Iwai * The target group is stored at @group_ret.
21633cd7630STakashi Iwai *
21733cd7630STakashi Iwai * The function returns the number of bytes of MIDI 1.0 stream.
2180b5288f5STakashi Iwai */
snd_ump_convert_from_ump(const u32 * data,unsigned char * buf,unsigned char * group_ret)21933cd7630STakashi Iwai int snd_ump_convert_from_ump(const u32 *data,
2200b5288f5STakashi Iwai unsigned char *buf,
2210b5288f5STakashi Iwai unsigned char *group_ret)
2220b5288f5STakashi Iwai {
2230b5288f5STakashi Iwai *group_ret = ump_message_group(*data);
2240b5288f5STakashi Iwai
2250b5288f5STakashi Iwai switch (ump_message_type(*data)) {
2260b5288f5STakashi Iwai case UMP_MSG_TYPE_SYSTEM:
2270b5288f5STakashi Iwai return cvt_ump_system_to_legacy(*data, buf);
2280b5288f5STakashi Iwai case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE:
2290b5288f5STakashi Iwai return cvt_ump_midi1_to_legacy(*data, buf);
2300b5288f5STakashi Iwai case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE:
2310b5288f5STakashi Iwai return cvt_ump_midi2_to_legacy((const union snd_ump_midi2_msg *)data,
2320b5288f5STakashi Iwai buf);
2330b5288f5STakashi Iwai case UMP_MSG_TYPE_DATA:
2340b5288f5STakashi Iwai return cvt_ump_sysex7_to_legacy(data, buf);
2350b5288f5STakashi Iwai }
2360b5288f5STakashi Iwai
2370b5288f5STakashi Iwai return 0;
2380b5288f5STakashi Iwai }
23933cd7630STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_convert_from_ump);
2400b5288f5STakashi Iwai
2410b5288f5STakashi Iwai /*
2420b5288f5STakashi Iwai * MIDI 1 byte stream -> UMP conversion
2430b5288f5STakashi Iwai */
2440b5288f5STakashi Iwai /* convert MIDI 1.0 SysEx to a UMP packet */
cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump * cvt,unsigned char group,u32 * data,bool finish)2450b5288f5STakashi Iwai static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt,
2460b5288f5STakashi Iwai unsigned char group, u32 *data, bool finish)
2470b5288f5STakashi Iwai {
2480b5288f5STakashi Iwai unsigned char status;
2490b5288f5STakashi Iwai bool start = cvt->in_sysex == 1;
2500b5288f5STakashi Iwai int i, offset;
2510b5288f5STakashi Iwai
2520b5288f5STakashi Iwai if (start && finish)
2530b5288f5STakashi Iwai status = UMP_SYSEX_STATUS_SINGLE;
2540b5288f5STakashi Iwai else if (start)
2550b5288f5STakashi Iwai status = UMP_SYSEX_STATUS_START;
2560b5288f5STakashi Iwai else if (finish)
2570b5288f5STakashi Iwai status = UMP_SYSEX_STATUS_END;
2580b5288f5STakashi Iwai else
2590b5288f5STakashi Iwai status = UMP_SYSEX_STATUS_CONTINUE;
2600b5288f5STakashi Iwai *data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len);
2610b5288f5STakashi Iwai offset = 8;
2620b5288f5STakashi Iwai for (i = 0; i < cvt->len; i++) {
2630b5288f5STakashi Iwai *data |= cvt->buf[i] << offset;
2640b5288f5STakashi Iwai if (!offset) {
2650b5288f5STakashi Iwai offset = 24;
2660b5288f5STakashi Iwai data++;
2670b5288f5STakashi Iwai } else
2680b5288f5STakashi Iwai offset -= 8;
2690b5288f5STakashi Iwai }
2700b5288f5STakashi Iwai cvt->len = 0;
2710b5288f5STakashi Iwai if (finish)
2720b5288f5STakashi Iwai cvt->in_sysex = 0;
2730b5288f5STakashi Iwai else
2740b5288f5STakashi Iwai cvt->in_sysex++;
2750b5288f5STakashi Iwai return 8;
2760b5288f5STakashi Iwai }
2770b5288f5STakashi Iwai
2780b5288f5STakashi Iwai /* convert to a UMP System message */
cvt_legacy_system_to_ump(struct ump_cvt_to_ump * cvt,unsigned char group,u32 * data)2790b5288f5STakashi Iwai static int cvt_legacy_system_to_ump(struct ump_cvt_to_ump *cvt,
2800b5288f5STakashi Iwai unsigned char group, u32 *data)
2810b5288f5STakashi Iwai {
2820b5288f5STakashi Iwai data[0] = ump_compose(UMP_MSG_TYPE_SYSTEM, group, 0, cvt->buf[0]);
2830b5288f5STakashi Iwai if (cvt->cmd_bytes > 1)
2840b5288f5STakashi Iwai data[0] |= cvt->buf[1] << 8;
2850b5288f5STakashi Iwai if (cvt->cmd_bytes > 2)
2860b5288f5STakashi Iwai data[0] |= cvt->buf[2];
2870b5288f5STakashi Iwai return 4;
2880b5288f5STakashi Iwai }
2890b5288f5STakashi Iwai
reset_rpn(struct ump_cvt_to_ump_bank * cc)290*4ed4e849STakashi Iwai static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
291*4ed4e849STakashi Iwai {
292*4ed4e849STakashi Iwai cc->rpn_set = 0;
293*4ed4e849STakashi Iwai cc->nrpn_set = 0;
294*4ed4e849STakashi Iwai cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
295*4ed4e849STakashi Iwai cc->cc_data_msb = cc->cc_data_lsb = 0;
296*4ed4e849STakashi Iwai cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
297*4ed4e849STakashi Iwai }
298*4ed4e849STakashi Iwai
fill_rpn(struct ump_cvt_to_ump_bank * cc,union snd_ump_midi2_msg * midi2,bool flush)2995a494fdbSTakashi Iwai static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
3005a494fdbSTakashi Iwai union snd_ump_midi2_msg *midi2,
3015a494fdbSTakashi Iwai bool flush)
3020b5288f5STakashi Iwai {
3035a494fdbSTakashi Iwai if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
3045a494fdbSTakashi Iwai return 0; // skip
3055a494fdbSTakashi Iwai /* when not flushing, wait for complete data set */
3065a494fdbSTakashi Iwai if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
3075a494fdbSTakashi Iwai return 0; // skip
3085a494fdbSTakashi Iwai
3090b5288f5STakashi Iwai if (cc->rpn_set) {
3100b5288f5STakashi Iwai midi2->rpn.status = UMP_MSG_STATUS_RPN;
3110b5288f5STakashi Iwai midi2->rpn.bank = cc->cc_rpn_msb;
3120b5288f5STakashi Iwai midi2->rpn.index = cc->cc_rpn_lsb;
3135a494fdbSTakashi Iwai } else if (cc->nrpn_set) {
3140b5288f5STakashi Iwai midi2->rpn.status = UMP_MSG_STATUS_NRPN;
3150b5288f5STakashi Iwai midi2->rpn.bank = cc->cc_nrpn_msb;
3160b5288f5STakashi Iwai midi2->rpn.index = cc->cc_nrpn_lsb;
3175a494fdbSTakashi Iwai } else {
3185a494fdbSTakashi Iwai return 0; // skip
3190b5288f5STakashi Iwai }
3205a494fdbSTakashi Iwai
3210b5288f5STakashi Iwai midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
3220b5288f5STakashi Iwai cc->cc_data_lsb);
3235a494fdbSTakashi Iwai
324*4ed4e849STakashi Iwai reset_rpn(cc);
3255a494fdbSTakashi Iwai return 1;
3260b5288f5STakashi Iwai }
3270b5288f5STakashi Iwai
3280b5288f5STakashi Iwai /* convert to a MIDI 1.0 Channel Voice message */
cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump * cvt,unsigned char group,unsigned int protocol,u32 * data,unsigned char bytes)32933cd7630STakashi Iwai static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt,
33033cd7630STakashi Iwai unsigned char group,
33133cd7630STakashi Iwai unsigned int protocol,
33233cd7630STakashi Iwai u32 *data, unsigned char bytes)
3330b5288f5STakashi Iwai {
3340b5288f5STakashi Iwai const unsigned char *buf = cvt->buf;
3350b5288f5STakashi Iwai struct ump_cvt_to_ump_bank *cc;
3360b5288f5STakashi Iwai union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)data;
3370b5288f5STakashi Iwai unsigned char status, channel;
3385a494fdbSTakashi Iwai int ret;
3390b5288f5STakashi Iwai
3400b5288f5STakashi Iwai BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4);
3410b5288f5STakashi Iwai BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg) != 8);
3420b5288f5STakashi Iwai
3430b5288f5STakashi Iwai /* for MIDI 1.0 UMP, it's easy, just pack it into UMP */
34433cd7630STakashi Iwai if (protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI1) {
3450b5288f5STakashi Iwai data[0] = ump_compose(UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE,
3460b5288f5STakashi Iwai group, 0, buf[0]);
3470b5288f5STakashi Iwai data[0] |= buf[1] << 8;
3480b5288f5STakashi Iwai if (bytes > 2)
3490b5288f5STakashi Iwai data[0] |= buf[2];
3500b5288f5STakashi Iwai return 4;
3510b5288f5STakashi Iwai }
3520b5288f5STakashi Iwai
3530b5288f5STakashi Iwai status = *buf >> 4;
3540b5288f5STakashi Iwai channel = *buf & 0x0f;
3550b5288f5STakashi Iwai cc = &cvt->bank[channel];
3560b5288f5STakashi Iwai
3570b5288f5STakashi Iwai /* special handling: treat note-on with 0 velocity as note-off */
3580b5288f5STakashi Iwai if (status == UMP_MSG_STATUS_NOTE_ON && !buf[2])
3590b5288f5STakashi Iwai status = UMP_MSG_STATUS_NOTE_OFF;
3600b5288f5STakashi Iwai
3610b5288f5STakashi Iwai /* initialize the packet */
3620b5288f5STakashi Iwai data[0] = ump_compose(UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE,
3630b5288f5STakashi Iwai group, status, channel);
3640b5288f5STakashi Iwai data[1] = 0;
3650b5288f5STakashi Iwai
3660b5288f5STakashi Iwai switch (status) {
3670b5288f5STakashi Iwai case UMP_MSG_STATUS_NOTE_ON:
3680b5288f5STakashi Iwai case UMP_MSG_STATUS_NOTE_OFF:
3690b5288f5STakashi Iwai midi2->note.note = buf[1];
3700b5288f5STakashi Iwai midi2->note.velocity = upscale_7_to_16bit(buf[2]);
3710b5288f5STakashi Iwai break;
3720b5288f5STakashi Iwai case UMP_MSG_STATUS_POLY_PRESSURE:
3730b5288f5STakashi Iwai midi2->paf.note = buf[1];
3740b5288f5STakashi Iwai midi2->paf.data = upscale_7_to_32bit(buf[2]);
3750b5288f5STakashi Iwai break;
3760b5288f5STakashi Iwai case UMP_MSG_STATUS_CC:
3770b5288f5STakashi Iwai switch (buf[1]) {
3780b5288f5STakashi Iwai case UMP_CC_RPN_MSB:
3795a494fdbSTakashi Iwai ret = fill_rpn(cc, midi2, true);
3800b5288f5STakashi Iwai cc->rpn_set = 1;
3810b5288f5STakashi Iwai cc->cc_rpn_msb = buf[2];
382*4ed4e849STakashi Iwai if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
383*4ed4e849STakashi Iwai reset_rpn(cc);
3845a494fdbSTakashi Iwai return ret;
3850b5288f5STakashi Iwai case UMP_CC_RPN_LSB:
3865a494fdbSTakashi Iwai ret = fill_rpn(cc, midi2, true);
3870b5288f5STakashi Iwai cc->rpn_set = 1;
3880b5288f5STakashi Iwai cc->cc_rpn_lsb = buf[2];
389*4ed4e849STakashi Iwai if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
390*4ed4e849STakashi Iwai reset_rpn(cc);
3915a494fdbSTakashi Iwai return ret;
3920b5288f5STakashi Iwai case UMP_CC_NRPN_MSB:
3935a494fdbSTakashi Iwai ret = fill_rpn(cc, midi2, true);
3940b5288f5STakashi Iwai cc->nrpn_set = 1;
3950b5288f5STakashi Iwai cc->cc_nrpn_msb = buf[2];
3965a494fdbSTakashi Iwai return ret;
3970b5288f5STakashi Iwai case UMP_CC_NRPN_LSB:
3985a494fdbSTakashi Iwai ret = fill_rpn(cc, midi2, true);
3990b5288f5STakashi Iwai cc->nrpn_set = 1;
4000b5288f5STakashi Iwai cc->cc_nrpn_lsb = buf[2];
4015a494fdbSTakashi Iwai return ret;
4020b5288f5STakashi Iwai case UMP_CC_DATA:
4035a494fdbSTakashi Iwai cc->cc_data_msb_set = 1;
4040b5288f5STakashi Iwai cc->cc_data_msb = buf[2];
4055a494fdbSTakashi Iwai return fill_rpn(cc, midi2, false);
4060b5288f5STakashi Iwai case UMP_CC_BANK_SELECT:
4070b5288f5STakashi Iwai cc->bank_set = 1;
4080b5288f5STakashi Iwai cc->cc_bank_msb = buf[2];
4090b5288f5STakashi Iwai return 0; // skip
4100b5288f5STakashi Iwai case UMP_CC_BANK_SELECT_LSB:
4110b5288f5STakashi Iwai cc->bank_set = 1;
4120b5288f5STakashi Iwai cc->cc_bank_lsb = buf[2];
4130b5288f5STakashi Iwai return 0; // skip
4140b5288f5STakashi Iwai case UMP_CC_DATA_LSB:
4155a494fdbSTakashi Iwai cc->cc_data_lsb_set = 1;
4160b5288f5STakashi Iwai cc->cc_data_lsb = buf[2];
4175a494fdbSTakashi Iwai return fill_rpn(cc, midi2, false);
4180b5288f5STakashi Iwai default:
4190b5288f5STakashi Iwai midi2->cc.index = buf[1];
4200b5288f5STakashi Iwai midi2->cc.data = upscale_7_to_32bit(buf[2]);
4210b5288f5STakashi Iwai break;
4220b5288f5STakashi Iwai }
4230b5288f5STakashi Iwai break;
4240b5288f5STakashi Iwai case UMP_MSG_STATUS_PROGRAM:
4250b5288f5STakashi Iwai midi2->pg.program = buf[1];
4260b5288f5STakashi Iwai if (cc->bank_set) {
4270b5288f5STakashi Iwai midi2->pg.bank_valid = 1;
4280b5288f5STakashi Iwai midi2->pg.bank_msb = cc->cc_bank_msb;
4290b5288f5STakashi Iwai midi2->pg.bank_lsb = cc->cc_bank_lsb;
4300b5288f5STakashi Iwai cc->bank_set = 0;
4310b5288f5STakashi Iwai }
4320b5288f5STakashi Iwai break;
4330b5288f5STakashi Iwai case UMP_MSG_STATUS_CHANNEL_PRESSURE:
4340b5288f5STakashi Iwai midi2->caf.data = upscale_7_to_32bit(buf[1]);
4350b5288f5STakashi Iwai break;
4360b5288f5STakashi Iwai case UMP_MSG_STATUS_PITCH_BEND:
4370b5288f5STakashi Iwai midi2->pb.data = upscale_14_to_32bit(buf[1] | (buf[2] << 7));
4380b5288f5STakashi Iwai break;
4390b5288f5STakashi Iwai default:
4400b5288f5STakashi Iwai return 0;
4410b5288f5STakashi Iwai }
4420b5288f5STakashi Iwai
4430b5288f5STakashi Iwai return 8;
4440b5288f5STakashi Iwai }
4450b5288f5STakashi Iwai
do_convert_to_ump(struct ump_cvt_to_ump * cvt,unsigned char group,unsigned int protocol,unsigned char c,u32 * data)44633cd7630STakashi Iwai static int do_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
44733cd7630STakashi Iwai unsigned int protocol, unsigned char c, u32 *data)
4480b5288f5STakashi Iwai {
4490b5288f5STakashi Iwai /* bytes for 0x80-0xf0 */
4500b5288f5STakashi Iwai static unsigned char cmd_bytes[8] = {
4510b5288f5STakashi Iwai 3, 3, 3, 3, 2, 2, 3, 0
4520b5288f5STakashi Iwai };
4530b5288f5STakashi Iwai /* bytes for 0xf0-0xff */
4540b5288f5STakashi Iwai static unsigned char system_bytes[16] = {
4550b5288f5STakashi Iwai 0, 2, 3, 2, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1
4560b5288f5STakashi Iwai };
4570b5288f5STakashi Iwai unsigned char bytes;
4580b5288f5STakashi Iwai
4590b5288f5STakashi Iwai if (c == UMP_MIDI1_MSG_SYSEX_START) {
4600b5288f5STakashi Iwai cvt->in_sysex = 1;
4610b5288f5STakashi Iwai cvt->len = 0;
4620b5288f5STakashi Iwai return 0;
4630b5288f5STakashi Iwai }
4640b5288f5STakashi Iwai if (c == UMP_MIDI1_MSG_SYSEX_END) {
4650b5288f5STakashi Iwai if (!cvt->in_sysex)
4660b5288f5STakashi Iwai return 0; /* skip */
4670b5288f5STakashi Iwai return cvt_legacy_sysex_to_ump(cvt, group, data, true);
4680b5288f5STakashi Iwai }
4690b5288f5STakashi Iwai
4700b5288f5STakashi Iwai if ((c & 0xf0) == UMP_MIDI1_MSG_REALTIME) {
4710b5288f5STakashi Iwai bytes = system_bytes[c & 0x0f];
4720b5288f5STakashi Iwai if (!bytes)
4730b5288f5STakashi Iwai return 0; /* skip */
4740b5288f5STakashi Iwai if (bytes == 1) {
4750b5288f5STakashi Iwai data[0] = ump_compose(UMP_MSG_TYPE_SYSTEM, group, 0, c);
4760b5288f5STakashi Iwai return 4;
4770b5288f5STakashi Iwai }
4780b5288f5STakashi Iwai cvt->buf[0] = c;
4790b5288f5STakashi Iwai cvt->len = 1;
4800b5288f5STakashi Iwai cvt->cmd_bytes = bytes;
4810b5288f5STakashi Iwai cvt->in_sysex = 0; /* abort SysEx */
4820b5288f5STakashi Iwai return 0;
4830b5288f5STakashi Iwai }
4840b5288f5STakashi Iwai
4850b5288f5STakashi Iwai if (c & 0x80) {
48677700b81STakashi Iwai bytes = cmd_bytes[(c >> 4) & 7];
4870b5288f5STakashi Iwai cvt->buf[0] = c;
4880b5288f5STakashi Iwai cvt->len = 1;
4890b5288f5STakashi Iwai cvt->cmd_bytes = bytes;
4900b5288f5STakashi Iwai cvt->in_sysex = 0; /* abort SysEx */
4910b5288f5STakashi Iwai return 0;
4920b5288f5STakashi Iwai }
4930b5288f5STakashi Iwai
4940b5288f5STakashi Iwai if (cvt->in_sysex) {
4950b5288f5STakashi Iwai cvt->buf[cvt->len++] = c;
4960b5288f5STakashi Iwai if (cvt->len == 6)
4970b5288f5STakashi Iwai return cvt_legacy_sysex_to_ump(cvt, group, data, false);
4980b5288f5STakashi Iwai return 0;
4990b5288f5STakashi Iwai }
5000b5288f5STakashi Iwai
5010b5288f5STakashi Iwai if (!cvt->len)
5020b5288f5STakashi Iwai return 0;
5030b5288f5STakashi Iwai
5040b5288f5STakashi Iwai cvt->buf[cvt->len++] = c;
5050b5288f5STakashi Iwai if (cvt->len < cvt->cmd_bytes)
5060b5288f5STakashi Iwai return 0;
5070b5288f5STakashi Iwai cvt->len = 1;
5080b5288f5STakashi Iwai if ((cvt->buf[0] & 0xf0) == UMP_MIDI1_MSG_REALTIME)
5090b5288f5STakashi Iwai return cvt_legacy_system_to_ump(cvt, group, data);
51033cd7630STakashi Iwai return cvt_legacy_cmd_to_ump(cvt, group, protocol, data, cvt->cmd_bytes);
5110b5288f5STakashi Iwai }
5120b5288f5STakashi Iwai
51333cd7630STakashi Iwai /**
51433cd7630STakashi Iwai * snd_ump_convert_to_ump - convert legacy MIDI byte to UMP packet
51533cd7630STakashi Iwai * @cvt: converter context
51633cd7630STakashi Iwai * @group: target UMP group
51733cd7630STakashi Iwai * @protocol: target UMP protocol
51833cd7630STakashi Iwai * @c: MIDI 1.0 byte data
51933cd7630STakashi Iwai *
52033cd7630STakashi Iwai * Feed a MIDI 1.0 byte @c and convert to a UMP packet if completed.
52133cd7630STakashi Iwai * The result is stored in the buffer in @cvt.
5220b5288f5STakashi Iwai */
snd_ump_convert_to_ump(struct ump_cvt_to_ump * cvt,unsigned char group,unsigned int protocol,unsigned char c)52333cd7630STakashi Iwai void snd_ump_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
52433cd7630STakashi Iwai unsigned int protocol, unsigned char c)
5250b5288f5STakashi Iwai {
52633cd7630STakashi Iwai cvt->ump_bytes = do_convert_to_ump(cvt, group, protocol, c, cvt->ump);
5270b5288f5STakashi Iwai }
52833cd7630STakashi Iwai EXPORT_SYMBOL_GPL(snd_ump_convert_to_ump);
529