/** * snd_ump_endpoint_new - create a UMP Endpoint object * @card: the card instance * @id: the id string for rawmidi * @device: the device index for rawmidi * @output: 1 for enabling output * @input: 1 for enabling input * @ump_ret: the pointer to store the new UMP instance * * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi * instance with one input and/or one output rawmidi stream (either uni- * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks * that consist of one or multiple UMP Groups. * * Use snd_rawmidi_set_ops() to set the operators to the new instance. * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself * depending on the given @output and @input. * * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is * created. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_ump_endpoint_new(struct snd_card *card, char *id, int device, int output, int input, struct snd_ump_endpoint **ump_ret)
{ unsignedint info_flags = SNDRV_RAWMIDI_INFO_UMP; struct snd_ump_endpoint *ump; int err;
if (input)
info_flags |= SNDRV_RAWMIDI_INFO_INPUT; if (output)
info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; if (input && output)
info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
if (ump->ops->drain)
ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
}
/* number of 32bit words per message type */ staticunsignedchar ump_packet_words[0x10] = {
1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
};
/** * snd_ump_receive_ump_val - parse the UMP packet data * @ump: UMP endpoint * @val: UMP packet data * * The data is copied onto ump->input_buf[]. * When a full packet is completed, returns the number of words (from 1 to 4). * OTOH, if the packet is incomplete, returns 0.
*/ int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val)
{ int words;
if (!ump->input_pending)
ump->input_pending = ump_packet_words[ump_message_type(val)];
ump->input_buf[ump->input_buf_head++] = val;
ump->input_pending--; if (!ump->input_pending) {
words = ump->input_buf_head;
ump->input_buf_head = 0; return words;
} return 0;
}
EXPORT_SYMBOL_GPL(snd_ump_receive_ump_val);
/** * snd_ump_receive - transfer UMP packets from the device * @ump: the UMP endpoint * @buffer: the buffer pointer to transfer * @count: byte size to transfer * * Called from the driver to submit the received UMP packets from the device * to user-space. It's essentially a wrapper of rawmidi_receive(). * The data to receive is in CPU-native endianness.
*/ int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
{ struct snd_rawmidi_substream *substream; const u32 *p = buffer; int n, words = count >> 2;
while (words--) {
n = snd_ump_receive_ump_val(ump, *p++); if (!n) continue;
ump_handle_stream_msg(ump, ump->input_buf, n); #if IS_ENABLED(CONFIG_SND_SEQUENCER) if (ump->seq_ops)
ump->seq_ops->input_receive(ump, ump->input_buf, n); #endif
process_legacy_input(ump, ump->input_buf, n);
}
/** * snd_ump_transmit - transmit UMP packets * @ump: the UMP endpoint * @buffer: the buffer pointer to transfer * @count: byte size to transfer * * Called from the driver to obtain the UMP packets from user-space to the * device. It's essentially a wrapper of rawmidi_transmit(). * The data to transmit is in CPU-native endianness.
*/ int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
{ struct snd_rawmidi_substream *substream =
ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT]; int err;
if (!substream) return -ENODEV;
err = snd_rawmidi_transmit(substream, (char *)buffer, count); /* received either data or an error? */ if (err) return err; return process_legacy_output(ump, buffer, count);
}
EXPORT_SYMBOL_GPL(snd_ump_transmit);
/** * snd_ump_block_new - Create a UMP block * @ump: UMP object * @blk: block ID number to create * @direction: direction (in/out/bidirection) * @first_group: the first group ID (0-based) * @num_groups: the number of groups in this block * @blk_ret: the pointer to store the resultant block object
*/ int snd_ump_block_new(struct snd_ump_endpoint *ump, unsignedint blk, unsignedint direction, unsignedint first_group, unsignedint num_groups, struct snd_ump_block **blk_ret)
{ struct snd_ump_block *fb, *p;
if (blk >= SNDRV_UMP_MAX_BLOCKS) return -EINVAL;
if (snd_ump_get_block(ump, blk)) return -EBUSY;
fb = kzalloc(sizeof(*fb), GFP_KERNEL); if (!fb) return -ENOMEM;
fb->ump = ump;
fb->info.card = ump->info.card;
fb->info.device = ump->info.device;
fb->info.block_id = blk; if (blk >= ump->info.num_blocks)
ump->info.num_blocks = blk + 1;
fb->info.direction = direction;
fb->info.active = 1;
fb->info.first_group = first_group;
fb->info.num_groups = num_groups; /* fill the default name, may be overwritten to a better name */
snprintf(fb->info.name, sizeof(fb->info.name), "Group %u-%u",
first_group + 1, first_group + num_groups);
/* put the entry in the ordered list */
list_for_each_entry(p, &ump->block_list, list) { if (p->info.block_id > blk) {
list_add_tail(&fb->list, &p->list); goto added;
}
}
list_add_tail(&fb->list, &ump->block_list);
/* update dir_bits and active flag for all groups in the client */ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
{ struct snd_ump_block *fb; struct snd_ump_group *group; int i;
for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
group = &ump->groups[i];
*group->name = 0;
group->dir_bits = 0;
group->active = 0;
group->group = i;
group->valid = false;
group->is_midi1 = false;
}
list_for_each_entry(fb, &ump->block_list, list) { if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS) break;
group = &ump->groups[fb->info.first_group]; for (i = 0; i < fb->info.num_groups; i++, group++) {
group->valid = true; if (fb->info.active)
group->active = 1; if (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1)
group->is_midi1 = true; switch (fb->info.direction) { case SNDRV_UMP_DIR_INPUT:
group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT); break; case SNDRV_UMP_DIR_OUTPUT:
group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_OUTPUT); break; case SNDRV_UMP_DIR_BIDIRECTION:
group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT) |
(1 << SNDRV_RAWMIDI_STREAM_OUTPUT); break;
} if (!*fb->info.name) continue; if (*group->name)
strlcat(group->name, ", ", sizeof(group->name));
safe_append_string(group->name, sizeof(group->name),
fb->info.name, sizeof(fb->info.name));
}
}
}
EXPORT_SYMBOL_GPL(snd_ump_update_group_attrs);
/* * UMP endpoint and function block handling
*/
/* open / close UMP streams for the internal stream msg communication */ staticint ump_request_open(struct snd_ump_endpoint *ump)
{ return snd_rawmidi_kernel_open(&ump->core, 0,
SNDRV_RAWMIDI_LFLG_OUTPUT,
&ump->stream_rfile);
}
/* request a command and wait for the given response; * @req1 and @req2 are u32 commands * @reply is the expected UMP stream status
*/ staticint ump_req_msg(struct snd_ump_endpoint *ump, u32 req1, u32 req2,
u32 reply)
{
u32 buf[4];
/* append the received letters via UMP packet to the given string buffer; * return 1 if the full string is received or 0 to continue
*/ staticint ump_append_string(struct snd_ump_endpoint *ump, char *dest, int maxsize, const u32 *buf, int offset)
{ unsignedchar format; int c;
format = ump_stream_message_format(buf[0]); if (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
format == UMP_STREAM_MSG_FORMAT_START) {
c = 0;
} else {
c = strlen(dest); if (c >= maxsize - 1) return 1;
}
/* set up the core rawmidi name from UMP EP name string */ staticvoid ump_set_rawmidi_name(struct snd_ump_endpoint *ump)
{
safe_copy_string(ump->core.name, sizeof(ump->core.name),
ump->info.name, sizeof(ump->info.name));
}
/* handle EP name stream message; update the UMP name string */ staticint ump_handle_ep_name_msg(struct snd_ump_endpoint *ump, constunion snd_ump_stream_msg *buf)
{ int ret;
ret = ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
buf->raw, 2); if (ret && ump->parsed) {
ump_set_rawmidi_name(ump);
ump_legacy_set_rawmidi_name(ump);
seq_notify_ep_change(ump);
}
return ret;
}
/* handle EP product id stream message; update the UMP product_id string */ staticint ump_handle_product_id_msg(struct snd_ump_endpoint *ump, constunion snd_ump_stream_msg *buf)
{ int ret;
ret = ump_append_string(ump, ump->info.product_id, sizeof(ump->info.product_id),
buf->raw, 2); if (ret)
seq_notify_ep_change(ump); return ret;
}
/* notify the protocol change to sequencer */ staticvoid seq_notify_protocol(struct snd_ump_endpoint *ump)
{ #if IS_ENABLED(CONFIG_SND_SEQUENCER) if (ump->seq_ops && ump->seq_ops->switch_protocol)
ump->seq_ops->switch_protocol(ump); #endif/* CONFIG_SND_SEQUENCER */
}
/** * snd_ump_switch_protocol - switch MIDI protocol * @ump: UMP endpoint * @protocol: protocol to switch to * * Returns 1 if the protocol is actually switched, 0 if unchanged
*/ int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsignedint protocol)
{ unsignedint type;
protocol &= ump->info.protocol_caps; if (protocol == ump->info.protocol) return 0;
type = protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK; if (type != SNDRV_UMP_EP_INFO_PROTO_MIDI1 &&
type != SNDRV_UMP_EP_INFO_PROTO_MIDI2) return 0;
/* complain only if updated after parsing */ if (!fb && ump->parsed) {
ump_info(ump, "Function Block Info Update for non-existing block %d\n",
blk); return -ENODEV;
}
/* When updated after the initial parse, check the FB info update */ if (ump->parsed && !is_fb_info_updated(ump, fb, buf)) return 1; /* no content change */
if (fb) {
fill_fb_info(ump, &fb->info, buf); if (ump->parsed) {
snd_ump_update_group_attrs(ump);
update_legacy_names(ump);
seq_notify_fb_change(ump, fb);
}
}
return 1; /* finished */
}
/* handle FB name message; update the FB name string */ staticint ump_handle_fb_name_msg(struct snd_ump_endpoint *ump, constunion snd_ump_stream_msg *buf)
{ unsignedchar blk; struct snd_ump_block *fb; int ret;
/* query the FB info once */
msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
(blk << 8) | UMP_STREAM_MSG_REQUEST_FB_INFO;
err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_INFO); if (err < 0) {
ump_dbg(ump, "Unable to get FB info for block %d\n", blk); return err;
}
/* the last input must be the FB info */ if (buf->fb_info.status != UMP_STREAM_MSG_STATUS_FB_INFO) {
ump_dbg(ump, "Inconsistent input: 0x%x\n", *buf->raw); return -EINVAL;
}
direction = buf->fb_info.direction;
first_group = buf->fb_info.first_group;
num_groups = buf->fb_info.num_groups;
if (size != 4 || ump_message_type(*buf) != UMP_MSG_TYPE_STREAM) return;
msg = (constunion snd_ump_stream_msg *)buf;
status = ump_stream_message_status(*buf); switch (status) { case UMP_STREAM_MSG_STATUS_EP_INFO:
ret = ump_handle_ep_info_msg(ump, msg); break; case UMP_STREAM_MSG_STATUS_DEVICE_INFO:
ret = ump_handle_device_info_msg(ump, msg); break; case UMP_STREAM_MSG_STATUS_EP_NAME:
ret = ump_handle_ep_name_msg(ump, msg); break; case UMP_STREAM_MSG_STATUS_PRODUCT_ID:
ret = ump_handle_product_id_msg(ump, msg); break; case UMP_STREAM_MSG_STATUS_STREAM_CFG:
ret = ump_handle_stream_cfg_msg(ump, msg); break; case UMP_STREAM_MSG_STATUS_FB_INFO:
ret = ump_handle_fb_info_msg(ump, msg); break; case UMP_STREAM_MSG_STATUS_FB_NAME:
ret = ump_handle_fb_name_msg(ump, msg); break; default: return;
}
/* when the message has been processed fully, wake up */ if (ret > 0 && ump->stream_wait_for == status) {
WRITE_ONCE(ump->stream_finished, 1);
wake_up(&ump->stream_wait);
}
}
/** * snd_ump_parse_endpoint - parse endpoint and create function blocks * @ump: UMP object * * Returns 0 for successful parse, -ENODEV if device doesn't respond * (or the query is unsupported), or other error code for serious errors.
*/ int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)
{ int blk, err;
u32 msg;
if (!(ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX)) return -ENODEV;
err = ump_request_open(ump); if (err < 0) {
ump_dbg(ump, "Unable to open rawmidi device: %d\n", err); return err;
}
/* Check Endpoint Information */
msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_EP_DISCOVERY, 0) |
0x0101; /* UMP version 1.1 */
err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_INFO,
UMP_STREAM_MSG_STATUS_EP_INFO); if (err < 0) {
ump_dbg(ump, "Unable to get UMP EP info\n"); goto error;
}
/* Request Endpoint Device Info */
err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_DEVICE_INFO,
UMP_STREAM_MSG_STATUS_DEVICE_INFO); if (err < 0)
ump_dbg(ump, "Unable to get UMP EP device info\n");
/* Request Endpoint Name */
err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_NAME,
UMP_STREAM_MSG_STATUS_EP_NAME); if (err < 0)
ump_dbg(ump, "Unable to get UMP EP name string\n");
ump_set_rawmidi_name(ump);
/* Request Endpoint Product ID */
err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID,
UMP_STREAM_MSG_STATUS_PRODUCT_ID); if (err < 0)
ump_dbg(ump, "Unable to get UMP EP product ID string\n");
/* Get the current stream configuration */
err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_STREAM_CFG,
UMP_STREAM_MSG_STATUS_STREAM_CFG); if (err < 0)
ump_dbg(ump, "Unable to get UMP EP stream config\n");
/* If no protocol is set by some reason, assume the valid one */
choose_default_protocol(ump);
/* Query and create blocks from Function Blocks */ for (blk = 0; blk < ump->info.num_blocks; blk++) {
err = create_block_from_fb_info(ump, blk); if (err < 0) continue;
}
/* initialize group attributions */
snd_ump_update_group_attrs(ump);
#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI) /* * Legacy rawmidi support
*/ staticint snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
{ struct snd_ump_endpoint *ump = substream->rmidi->private_data; int dir = substream->stream; int group = ump->legacy_mapping[substream->number]; int err;
guard(mutex)(&ump->open_mutex); if (ump->legacy_substreams[dir][group]) return -EBUSY; if (!ump->groups[group].active) return -ENODEV; if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { if (!ump->legacy_out_opens) {
err = snd_rawmidi_kernel_open(&ump->core, 0,
SNDRV_RAWMIDI_LFLG_OUTPUT |
SNDRV_RAWMIDI_LFLG_APPEND,
&ump->legacy_out_rfile); if (err < 0) return err;
}
ump->legacy_out_opens++;
snd_ump_convert_reset(&ump->out_cvts[group]);
}
guard(spinlock_irq)(&ump->legacy_locks[dir]);
ump->legacy_substreams[dir][group] = substream; return 0;
}
staticint snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
{ struct snd_ump_endpoint *ump = substream->rmidi->private_data; int dir = substream->stream; int group = ump->legacy_mapping[substream->number];
guard(mutex)(&ump->open_mutex);
scoped_guard(spinlock_irq, &ump->legacy_locks[dir])
ump->legacy_substreams[dir][group] = NULL; if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { if (!--ump->legacy_out_opens)
snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
} return 0;
}
staticvoid snd_ump_legacy_trigger(struct snd_rawmidi_substream *substream, int up)
{ struct snd_ump_endpoint *ump = substream->rmidi->private_data; int dir = substream->stream;
staticvoid process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, int words)
{ struct snd_rawmidi_substream *substream; unsignedchar buf[16]; unsignedchar group; constint dir = SNDRV_RAWMIDI_STREAM_INPUT; int size;
size = snd_ump_convert_from_ump(src, buf, &group); if (size <= 0) return;
guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
substream = ump->legacy_substreams[dir][group]; if (substream)
snd_rawmidi_receive(substream, buf, size);
}
/* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */ staticint fill_legacy_mapping(struct snd_ump_endpoint *ump)
{ struct snd_ump_block *fb; unsignedint group_maps = 0; int i, num;
if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
list_for_each_entry(fb, &ump->block_list, list) { for (i = 0; i < fb->info.num_groups; i++)
group_maps |= 1U << (fb->info.first_group + i);
} if (!group_maps)
ump_info(ump, "No UMP Group is found in FB\n");
}
/* use all groups for non-static case */ if (!group_maps)
group_maps = (1U << SNDRV_UMP_MAX_GROUPS) - 1;
num = 0; for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) if (group_maps & (1U << i))
ump->legacy_mapping[num++] = i;
return num;
}
staticvoid update_legacy_substreams(struct snd_ump_endpoint *ump, struct snd_rawmidi *rmidi, int dir)
{ struct snd_rawmidi_substream *s; constchar *name; int idx;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.