/** *snd_ump_transmit-transmitUMPpackets *@ump:theUMPendpoint *@buffer:thebufferpointertotransfer *@count:bytesizetotransfer * *CalledfromthedrivertoobtaintheUMPpacketsfromuser-spacetothe *device.It'sessentiallyawrapperofrawmidi_transmit(). *ThedatatotransmitisinCPU-nativeendianness.
*/ 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);
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);
/* *UMPendpointandfunctionblockhandling
*/
/* 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);
}
/* append the received letters via UMP packet to the given string buffer; *return1ifthefullstringisreceivedor0tocontinue
*/ 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) return1;
}
/* 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 */
}
/* 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)) return1; /* 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);
}
}
return1; /* 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-parseendpointandcreatefunctionblocks *@ump:UMPobject * *Returns0forsuccessfulparse,-ENODEVifdevicedoesn'trespond *(orthequeryisunsupported),orothererrorcodeforseriouserrors.
*/ 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) /* *Legacyrawmidisupport
*/ 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; return0;
}
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);
} return0;
}
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.