summaryrefslogtreecommitdiff
path: root/include/sound/ump.h
blob: 91238dabe3075b4e0b5cda2abec140ede4cf6ea5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Universal MIDI Packet (UMP) Support
 */
#ifndef __SOUND_UMP_H
#define __SOUND_UMP_H

#include <sound/rawmidi.h>

struct snd_ump_endpoint;
struct snd_ump_block;
struct snd_ump_ops;
struct ump_cvt_to_ump;
struct snd_seq_ump_ops;

struct snd_ump_endpoint {
	struct snd_rawmidi core;	/* raw UMP access */

	struct snd_ump_endpoint_info info;

	const struct snd_ump_ops *ops;	/* UMP ops set by the driver */
	struct snd_rawmidi_substream *substreams[2];	/* opened substreams */

	void *private_data;
	void (*private_free)(struct snd_ump_endpoint *ump);

	/* UMP Stream message processing */
	u32 stream_wait_for;	/* expected stream message status */
	bool stream_finished;	/* set when message has been processed */
	bool parsed;		/* UMP / FB parse finished? */
	bool no_process_stream;	/* suppress UMP stream messages handling */
	wait_queue_head_t stream_wait;
	struct snd_rawmidi_file stream_rfile;

	struct list_head block_list;	/* list of snd_ump_block objects */

	/* intermediate buffer for UMP input */
	u32 input_buf[4];
	int input_buf_head;
	int input_pending;

	struct mutex open_mutex;

#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
	spinlock_t legacy_locks[2];
	struct snd_rawmidi *legacy_rmidi;
	struct snd_rawmidi_substream *legacy_substreams[2][SNDRV_UMP_MAX_GROUPS];
	unsigned char legacy_mapping[SNDRV_UMP_MAX_GROUPS];

	/* for legacy output; need to open the actual substream unlike input */
	int legacy_out_opens;
	struct snd_rawmidi_file legacy_out_rfile;
	struct ump_cvt_to_ump *out_cvts;
#endif

#if IS_ENABLED(CONFIG_SND_SEQUENCER)
	struct snd_seq_device *seq_dev;
	const struct snd_seq_ump_ops *seq_ops;
	void *seq_client;
#endif
};

/* ops filled by UMP drivers */
struct snd_ump_ops {
	int (*open)(struct snd_ump_endpoint *ump, int dir);
	void (*close)(struct snd_ump_endpoint *ump, int dir);
	void (*trigger)(struct snd_ump_endpoint *ump, int dir, int up);
	void (*drain)(struct snd_ump_endpoint *ump, int dir);
};

/* ops filled by sequencer binding */
struct snd_seq_ump_ops {
	void (*input_receive)(struct snd_ump_endpoint *ump,
			      const u32 *data, int words);
	int (*notify_fb_change)(struct snd_ump_endpoint *ump,
				struct snd_ump_block *fb);
	int (*switch_protocol)(struct snd_ump_endpoint *ump);
};

struct snd_ump_block {
	struct snd_ump_block_info info;
	struct snd_ump_endpoint *ump;

	void *private_data;
	void (*private_free)(struct snd_ump_block *blk);

	struct list_head list;
};

#define rawmidi_to_ump(rmidi)	container_of(rmidi, struct snd_ump_endpoint, core)

int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
			 int output, int input,
			 struct snd_ump_endpoint **ump_ret);
int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump);
int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
		      unsigned int direction, unsigned int first_group,
		      unsigned int num_groups, struct snd_ump_block **blk_ret);
int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count);
int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count);

#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
				  char *id, int device);
#else
static inline int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
						char *id, int device)
{
	return 0;
}
#endif

int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val);
int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol);

/*
 * Some definitions for UMP
 */

/* MIDI 2.0 Message Type */
enum {
	UMP_MSG_TYPE_UTILITY			= 0x00,
	UMP_MSG_TYPE_SYSTEM			= 0x01,
	UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE	= 0x02,
	UMP_MSG_TYPE_DATA			= 0x03,
	UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE	= 0x04,
	UMP_MSG_TYPE_EXTENDED_DATA		= 0x05,
	UMP_MSG_TYPE_FLEX_DATA			= 0x0d,
	UMP_MSG_TYPE_STREAM			= 0x0f,
};

/* MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */
enum {
	UMP_SYSEX_STATUS_SINGLE			= 0,
	UMP_SYSEX_STATUS_START			= 1,
	UMP_SYSEX_STATUS_CONTINUE		= 2,
	UMP_SYSEX_STATUS_END			= 3,
};

/* UMP Utility Type Status (type 0x0) */
enum {
	UMP_UTILITY_MSG_STATUS_NOOP		= 0x00,
	UMP_UTILITY_MSG_STATUS_JR_CLOCK		= 0x01,
	UMP_UTILITY_MSG_STATUS_JR_TSTAMP	= 0x02,
	UMP_UTILITY_MSG_STATUS_DCTPQ		= 0x03,
	UMP_UTILITY_MSG_STATUS_DC		= 0x04,
};

/* UMP Stream Message Status (type 0xf) */
enum {
	UMP_STREAM_MSG_STATUS_EP_DISCOVERY	= 0x00,
	UMP_STREAM_MSG_STATUS_EP_INFO		= 0x01,
	UMP_STREAM_MSG_STATUS_DEVICE_INFO	= 0x02,
	UMP_STREAM_MSG_STATUS_EP_NAME		= 0x03,
	UMP_STREAM_MSG_STATUS_PRODUCT_ID	= 0x04,
	UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST = 0x05,
	UMP_STREAM_MSG_STATUS_STREAM_CFG	= 0x06,
	UMP_STREAM_MSG_STATUS_FB_DISCOVERY	= 0x10,
	UMP_STREAM_MSG_STATUS_FB_INFO		= 0x11,
	UMP_STREAM_MSG_STATUS_FB_NAME		= 0x12,
	UMP_STREAM_MSG_STATUS_START_CLIP	= 0x20,
	UMP_STREAM_MSG_STATUS_END_CLIP		= 0x21,
};

/* UMP Endpoint Discovery filter bitmap */
enum {
	UMP_STREAM_MSG_REQUEST_EP_INFO		= (1U << 0),
	UMP_STREAM_MSG_REQUEST_DEVICE_INFO	= (1U << 1),
	UMP_STREAM_MSG_REQUEST_EP_NAME		= (1U << 2),
	UMP_STREAM_MSG_REQUEST_PRODUCT_ID	= (1U << 3),
	UMP_STREAM_MSG_REQUEST_STREAM_CFG	= (1U << 4),
};

/* UMP Function Block Discovery filter bitmap */
enum {
	UMP_STREAM_MSG_REQUEST_FB_INFO		= (1U << 0),
	UMP_STREAM_MSG_REQUEST_FB_NAME		= (1U << 1),
};

/* UMP Endpoint Info capability bits (used for protocol request/notify, too) */
enum {
	UMP_STREAM_MSG_EP_INFO_CAP_TXJR		= (1U << 0), /* Sending JRTS */
	UMP_STREAM_MSG_EP_INFO_CAP_RXJR		= (1U << 1), /* Receiving JRTS */
	UMP_STREAM_MSG_EP_INFO_CAP_MIDI1	= (1U << 8), /* MIDI 1.0 */
	UMP_STREAM_MSG_EP_INFO_CAP_MIDI2	= (1U << 9), /* MIDI 2.0 */
};

/* UMP EP / FB name string format; same as SysEx string handling */
enum {
	UMP_STREAM_MSG_FORMAT_SINGLE		= 0,
	UMP_STREAM_MSG_FORMAT_START		= 1,
	UMP_STREAM_MSG_FORMAT_CONTINUE		= 2,
	UMP_STREAM_MSG_FORMAT_END		= 3,
};

/*
 * Helpers for retrieving / filling bits from UMP
 */
/* get the message type (4bit) from a UMP packet (header) */
static inline unsigned char ump_message_type(u32 data)
{
	return data >> 28;
}

/* get the group number (0-based, 4bit) from a UMP packet (header) */
static inline unsigned char ump_message_group(u32 data)
{
	return (data >> 24) & 0x0f;
}

/* get the MIDI status code (4bit) from a UMP packet (header) */
static inline unsigned char ump_message_status_code(u32 data)
{
	return (data >> 20) & 0x0f;
}

/* get the MIDI channel number (0-based, 4bit) from a UMP packet (header) */
static inline unsigned char ump_message_channel(u32 data)
{
	return (data >> 16) & 0x0f;
}

/* get the MIDI status + channel combo byte (8bit) from a UMP packet (header) */
static inline unsigned char ump_message_status_channel(u32 data)
{
	return (data >> 16) & 0xff;
}

/* compose a UMP packet (header) from type, group and status values */
static inline u32 ump_compose(unsigned char type, unsigned char group,
			      unsigned char status, unsigned char channel)
{
	return ((u32)type << 28) | ((u32)group << 24) | ((u32)status << 20) |
		((u32)channel << 16);
}

/* get SysEx message status (for both 7 and 8bits) from a UMP packet (header) */
static inline unsigned char ump_sysex_message_status(u32 data)
{
	return (data >> 20) & 0xf;
}

/* get SysEx message length (for both 7 and 8bits) from a UMP packet (header) */
static inline unsigned char ump_sysex_message_length(u32 data)
{
	return (data >> 16) & 0xf;
}

/* For Stream Messages */
static inline unsigned char ump_stream_message_format(u32 data)
{
	return (data >> 26) & 0x03;
}

static inline unsigned int ump_stream_message_status(u32 data)
{
	return (data >> 16) & 0x3ff;
}

static inline u32 ump_stream_compose(unsigned char status, unsigned short form)
{
	return (UMP_MSG_TYPE_STREAM << 28) | ((u32)form << 26) |
		((u32)status << 16);
}

#define ump_is_groupless_msg(type) \
	((type) == UMP_MSG_TYPE_UTILITY || (type) == UMP_MSG_TYPE_STREAM)

#endif /* __SOUND_UMP_H */