xref: /openbmc/linux/sound/core/ump_convert.c (revision ac8f933664c3a0e2d42f6ee9a2a6d25f87cb23f6)
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