[PATCH 4/4] ALSA: seq: ump: Fix RPN/NRPN state handling at MIDI 1.0 CC conversion

From: HyeongJun An

Date: Sun Nov 08 2026 - 07:50:56 EST


The sequencer converts MIDI 1.0 CC and CONTROL14 events to MIDI 2.0 UMP
with the same RPN/NRPN state handling as the UMP core helper. It has
the same problems:

- Data Entry without a selection is sent at the next RPN/NRPN
selection, with the number only half updated.
- A repeated Data Entry for the same RPN is not sent right away and
ends up as above.
- A Null NRPN is not handled.

Apply the same fix as for the UMP core helper to both the CC and the
CONTROL14 paths.

Fixes: e9e02819a98a ("ALSA: seq: Automatic conversion of UMP events")
Fixes: a4ff92ff0bdd ("ALSA: seq: ump: Transmit RPN/NRPN message at each MSB/LSB data reception")
Cc: <stable@xxxxxxxxxxxxxxx>
Link: https://lore.kernel.org/all/20261104121508.3972314-1-sammiee5311@xxxxxxxxx/
Signed-off-by: HyeongJun An <sammiee5311@xxxxxxxxx>
---
sound/core/seq/seq_ump_convert.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c
index bac37d4ef43c..62bdef6dca58 100644
--- a/sound/core/seq/seq_ump_convert.c
+++ b/sound/core/seq/seq_ump_convert.c
@@ -790,13 +790,22 @@ static int paf_ev_to_ump_midi2(const struct snd_seq_event *event,
return 1;
}

+static void reset_rpn_data(struct ump_cvt_to_ump_bank *cc)
+{
+ cc->cc_data_msb = cc->cc_data_lsb = 0;
+ cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
+}
+
+/*
+ * Deselect on a Null RPN/NRPN, but keep the 7f/7f numbers as a MIDI 1.0
+ * receiver does. Otherwise, when the LSB was already 127, the second
+ * message of a Null would select RPN or NRPN 0/127.
+ */
static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
{
cc->rpn_set = 0;
cc->nrpn_set = 0;
- cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
- cc->cc_data_msb = cc->cc_data_lsb = 0;
- cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
+ reset_rpn_data(cc);
}

/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
@@ -820,6 +829,8 @@ static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
data->rpn.bank = cc->cc_nrpn_msb;
data->rpn.index = cc->cc_nrpn_lsb;
} else {
+ /* no RPN/NRPN selected (e.g. after a Null), drop the data */
+ reset_rpn_data(cc);
return 0; // skip
}

@@ -827,7 +838,8 @@ static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
cc->cc_data_lsb);
data->rpn.channel = channel;

- reset_rpn(cc);
+ /* keep the RPN/NRPN selection for a following Data Entry */
+ reset_rpn_data(cc);
return 1;
}

@@ -866,12 +878,16 @@ static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
cc->rpn_set = 0;
cc->nrpn_set = 1;
cc->cc_nrpn_msb = val;
+ if (cc->cc_nrpn_msb == 0x7f && cc->cc_nrpn_lsb == 0x7f)
+ reset_rpn(cc);
break;
case UMP_CC_NRPN_LSB:
ret = fill_rpn(cc, data, channel, true);
cc->rpn_set = 0;
cc->nrpn_set = 1;
cc->cc_nrpn_lsb = val;
+ if (cc->cc_nrpn_msb == 0x7f && cc->cc_nrpn_lsb == 0x7f)
+ reset_rpn(cc);
break;
case UMP_CC_DATA:
cc->cc_data_msb_set = 1;
@@ -993,6 +1009,8 @@ static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event,
cc->cc_nrpn_msb = msb;
cc->nrpn_set = 1;
cc->cc_nrpn_lsb = lsb;
+ if (cc->cc_nrpn_msb == 0x7f && cc->cc_nrpn_lsb == 0x7f)
+ reset_rpn(cc);
return ret;
case UMP_CC_DATA:
case UMP_CC_DATA_LSB:
--
2.43.0