| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Universal MIDI Packet (UMP) support
- */
- #include <linux/list.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/export.h>
- #include <linux/mm.h>
- #include <sound/core.h>
- #include <sound/rawmidi.h>
- #include <sound/ump.h>
- #include <sound/ump_convert.h>
- #define ump_err(ump, fmt, args...) dev_err((ump)->core.dev, fmt, ##args)
- #define ump_warn(ump, fmt, args...) dev_warn((ump)->core.dev, fmt, ##args)
- #define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args)
- #define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args)
- static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
- static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
- static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
- void __user *argp);
- static void snd_ump_proc_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer);
- static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream);
- static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream);
- static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
- int up);
- static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream);
- static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
- const u32 *buf, int size);
- #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
- static int process_legacy_output(struct snd_ump_endpoint *ump,
- u32 *buffer, int count);
- static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
- int words);
- static void update_legacy_names(struct snd_ump_endpoint *ump);
- #else
- static inline int process_legacy_output(struct snd_ump_endpoint *ump,
- u32 *buffer, int count)
- {
- return 0;
- }
- static inline void process_legacy_input(struct snd_ump_endpoint *ump,
- const u32 *src, int words)
- {
- }
- static inline void update_legacy_names(struct snd_ump_endpoint *ump)
- {
- }
- #endif
- static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
- .dev_register = snd_ump_dev_register,
- .dev_unregister = snd_ump_dev_unregister,
- .ioctl = snd_ump_ioctl,
- .proc_read = snd_ump_proc_read,
- };
- static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
- .open = snd_ump_rawmidi_open,
- .close = snd_ump_rawmidi_close,
- .trigger = snd_ump_rawmidi_trigger,
- };
- static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
- .open = snd_ump_rawmidi_open,
- .close = snd_ump_rawmidi_close,
- .trigger = snd_ump_rawmidi_trigger,
- .drain = snd_ump_rawmidi_drain,
- };
- static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
- {
- struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
- struct snd_ump_block *fb;
- while (!list_empty(&ump->block_list)) {
- fb = list_first_entry(&ump->block_list, struct snd_ump_block,
- list);
- list_del(&fb->list);
- if (fb->private_free)
- fb->private_free(fb);
- kfree(fb);
- }
- if (ump->private_free)
- ump->private_free(ump);
- #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
- kfree(ump->out_cvts);
- #endif
- }
- /**
- * 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)
- {
- unsigned int 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;
- ump = kzalloc(sizeof(*ump), GFP_KERNEL);
- if (!ump)
- return -ENOMEM;
- INIT_LIST_HEAD(&ump->block_list);
- mutex_init(&ump->open_mutex);
- init_waitqueue_head(&ump->stream_wait);
- #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
- spin_lock_init(&ump->legacy_locks[0]);
- spin_lock_init(&ump->legacy_locks[1]);
- #endif
- err = snd_rawmidi_init(&ump->core, card, id, device,
- output, input, info_flags);
- if (err < 0) {
- snd_rawmidi_free(&ump->core);
- return err;
- }
- ump->info.card = card->number;
- ump->info.device = device;
- ump->core.private_free = snd_ump_endpoint_free;
- ump->core.ops = &snd_ump_rawmidi_ops;
- if (input)
- snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
- &snd_ump_rawmidi_input_ops);
- if (output)
- snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
- &snd_ump_rawmidi_output_ops);
- ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
- *ump_ret = ump;
- return 0;
- }
- EXPORT_SYMBOL_GPL(snd_ump_endpoint_new);
- /*
- * Device register / unregister hooks;
- * do nothing, placeholders for avoiding the default rawmidi handling
- */
- #if IS_ENABLED(CONFIG_SND_SEQUENCER)
- static void snd_ump_dev_seq_free(struct snd_seq_device *device)
- {
- struct snd_ump_endpoint *ump = device->private_data;
- ump->seq_dev = NULL;
- }
- #endif
- static int snd_ump_dev_register(struct snd_rawmidi *rmidi)
- {
- #if IS_ENABLED(CONFIG_SND_SEQUENCER)
- struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
- int err;
- err = snd_seq_device_new(ump->core.card, ump->core.device,
- SNDRV_SEQ_DEV_ID_UMP, 0, &ump->seq_dev);
- if (err < 0)
- return err;
- ump->seq_dev->private_data = ump;
- ump->seq_dev->private_free = snd_ump_dev_seq_free;
- snd_device_register(ump->core.card, ump->seq_dev);
- #endif
- return 0;
- }
- static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi)
- {
- return 0;
- }
- static struct snd_ump_block *
- snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
- {
- struct snd_ump_block *fb;
- list_for_each_entry(fb, &ump->block_list, list) {
- if (fb->info.block_id == id)
- return fb;
- }
- return NULL;
- }
- /*
- * rawmidi ops for UMP endpoint
- */
- static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
- {
- struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
- int dir = substream->stream;
- int err;
- if (ump->substreams[dir])
- return -EBUSY;
- err = ump->ops->open(ump, dir);
- if (err < 0)
- return err;
- ump->substreams[dir] = substream;
- return 0;
- }
- static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
- {
- struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
- int dir = substream->stream;
- ump->substreams[dir] = NULL;
- ump->ops->close(ump, dir);
- return 0;
- }
- static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
- int up)
- {
- struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
- int dir = substream->stream;
- ump->ops->trigger(ump, dir, up);
- }
- static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
- {
- struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
- if (ump->ops->drain)
- ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
- }
- /* number of 32bit words per message type */
- static unsigned char 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);
- }
- substream = ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
- if (!substream)
- return 0;
- return snd_rawmidi_receive(substream, (const char *)buffer, count);
- }
- EXPORT_SYMBOL_GPL(snd_ump_receive);
- /**
- * 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, unsigned int blk,
- unsigned int direction, unsigned int first_group,
- unsigned int num_groups, struct snd_ump_block **blk_ret)
- {
- struct snd_ump_block *fb, *p;
- if (blk < 0 || 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 %d-%d",
- 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);
- added:
- ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name);
- *blk_ret = fb;
- return 0;
- }
- EXPORT_SYMBOL_GPL(snd_ump_block_new);
- static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump,
- struct snd_ump_block_info __user *argp)
- {
- struct snd_ump_block *fb;
- unsigned char id;
- if (get_user(id, &argp->block_id))
- return -EFAULT;
- fb = snd_ump_get_block(ump, id);
- if (!fb)
- return -ENOENT;
- if (copy_to_user(argp, &fb->info, sizeof(fb->info)))
- return -EFAULT;
- return 0;
- }
- /*
- * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl()
- */
- static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
- void __user *argp)
- {
- struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
- switch (cmd) {
- case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
- if (copy_to_user(argp, &ump->info, sizeof(ump->info)))
- return -EFAULT;
- return 0;
- case SNDRV_UMP_IOCTL_BLOCK_INFO:
- return snd_ump_ioctl_block(ump, argp);
- default:
- ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd);
- return -ENOTTY;
- }
- }
- static const char *ump_direction_string(int dir)
- {
- switch (dir) {
- case SNDRV_UMP_DIR_INPUT:
- return "input";
- case SNDRV_UMP_DIR_OUTPUT:
- return "output";
- case SNDRV_UMP_DIR_BIDIRECTION:
- return "bidirection";
- default:
- return "unknown";
- }
- }
- static const char *ump_ui_hint_string(int dir)
- {
- switch (dir) {
- case SNDRV_UMP_BLOCK_UI_HINT_RECEIVER:
- return "receiver";
- case SNDRV_UMP_BLOCK_UI_HINT_SENDER:
- return "sender";
- case SNDRV_UMP_BLOCK_UI_HINT_BOTH:
- return "both";
- default:
- return "unknown";
- }
- }
- /* Additional proc file output */
- static void snd_ump_proc_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
- {
- struct snd_rawmidi *rmidi = entry->private_data;
- struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
- struct snd_ump_block *fb;
- snd_iprintf(buffer, "EP Name: %s\n", ump->info.name);
- snd_iprintf(buffer, "EP Product ID: %s\n", ump->info.product_id);
- snd_iprintf(buffer, "UMP Version: 0x%04x\n", ump->info.version);
- snd_iprintf(buffer, "Protocol Caps: 0x%08x\n", ump->info.protocol_caps);
- snd_iprintf(buffer, "Protocol: 0x%08x\n", ump->info.protocol);
- if (ump->info.version) {
- snd_iprintf(buffer, "Manufacturer ID: 0x%08x\n",
- ump->info.manufacturer_id);
- snd_iprintf(buffer, "Family ID: 0x%04x\n", ump->info.family_id);
- snd_iprintf(buffer, "Model ID: 0x%04x\n", ump->info.model_id);
- snd_iprintf(buffer, "SW Revision: 0x%4phN\n", ump->info.sw_revision);
- }
- snd_iprintf(buffer, "Static Blocks: %s\n",
- (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) ? "Yes" : "No");
- snd_iprintf(buffer, "Num Blocks: %d\n\n", ump->info.num_blocks);
- list_for_each_entry(fb, &ump->block_list, list) {
- snd_iprintf(buffer, "Block %d (%s)\n", fb->info.block_id,
- fb->info.name);
- snd_iprintf(buffer, " Direction: %s\n",
- ump_direction_string(fb->info.direction));
- snd_iprintf(buffer, " Active: %s\n",
- fb->info.active ? "Yes" : "No");
- snd_iprintf(buffer, " Groups: %d-%d\n",
- fb->info.first_group + 1,
- fb->info.first_group + fb->info.num_groups);
- snd_iprintf(buffer, " Is MIDI1: %s%s\n",
- (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1) ? "Yes" : "No",
- (fb->info.flags & SNDRV_UMP_BLOCK_IS_LOWSPEED) ? " (Low Speed)" : "");
- if (ump->info.version) {
- snd_iprintf(buffer, " MIDI-CI Version: %d\n",
- fb->info.midi_ci_version);
- snd_iprintf(buffer, " Sysex8 Streams: %d\n",
- fb->info.sysex8_streams);
- snd_iprintf(buffer, " UI Hint: %s\n",
- ump_ui_hint_string(fb->info.ui_hint));
- }
- snd_iprintf(buffer, "\n");
- }
- }
- /* 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) {
- /* store the first matching name */
- strscpy(group->name, fb->info.name,
- sizeof(group->name));
- } else {
- /* when overlapping, concat names */
- strlcat(group->name, ", ", sizeof(group->name));
- strlcat(group->name, fb->info.name,
- sizeof(group->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 */
- static int ump_request_open(struct snd_ump_endpoint *ump)
- {
- return snd_rawmidi_kernel_open(&ump->core, 0,
- SNDRV_RAWMIDI_LFLG_OUTPUT,
- &ump->stream_rfile);
- }
- static void ump_request_close(struct snd_ump_endpoint *ump)
- {
- snd_rawmidi_kernel_release(&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
- */
- static int ump_req_msg(struct snd_ump_endpoint *ump, u32 req1, u32 req2,
- u32 reply)
- {
- u32 buf[4];
- ump_dbg(ump, "%s: request %08x %08x, wait-for %08x\n",
- __func__, req1, req2, reply);
- memset(buf, 0, sizeof(buf));
- buf[0] = req1;
- buf[1] = req2;
- ump->stream_finished = 0;
- ump->stream_wait_for = reply;
- snd_rawmidi_kernel_write(ump->stream_rfile.output,
- (unsigned char *)&buf, 16);
- wait_event_timeout(ump->stream_wait, ump->stream_finished,
- msecs_to_jiffies(500));
- if (!READ_ONCE(ump->stream_finished)) {
- ump_dbg(ump, "%s: request timed out\n", __func__);
- return -ETIMEDOUT;
- }
- ump->stream_finished = 0;
- ump_dbg(ump, "%s: reply: %08x %08x %08x %08x\n",
- __func__, buf[0], buf[1], buf[2], buf[3]);
- return 0;
- }
- /* append the received letters via UMP packet to the given string buffer;
- * return 1 if the full string is received or 0 to continue
- */
- static int ump_append_string(struct snd_ump_endpoint *ump, char *dest,
- int maxsize, const u32 *buf, int offset)
- {
- unsigned char 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;
- }
- for (; offset < 16; offset++) {
- dest[c] = buf[offset / 4] >> (3 - (offset % 4)) * 8;
- if (!dest[c])
- break;
- if (++c >= maxsize - 1)
- break;
- }
- dest[c] = 0;
- return (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
- format == UMP_STREAM_MSG_FORMAT_END);
- }
- /* Choose the default protocol */
- static void choose_default_protocol(struct snd_ump_endpoint *ump)
- {
- if (ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK)
- return;
- if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
- ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI2;
- else
- ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1;
- }
- /* handle EP info stream message; update the UMP attributes */
- static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
- const union snd_ump_stream_msg *buf)
- {
- ump->info.version = (buf->ep_info.ump_version_major << 8) |
- buf->ep_info.ump_version_minor;
- ump->info.num_blocks = buf->ep_info.num_function_blocks;
- if (ump->info.num_blocks > SNDRV_UMP_MAX_BLOCKS) {
- ump_info(ump, "Invalid function blocks %d, fallback to 1\n",
- ump->info.num_blocks);
- ump->info.num_blocks = 1;
- }
- if (buf->ep_info.static_function_block)
- ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
- ump->info.protocol_caps = (buf->ep_info.protocol << 8) |
- buf->ep_info.jrts;
- ump_dbg(ump, "EP info: version=%x, num_blocks=%x, proto_caps=%x\n",
- ump->info.version, ump->info.num_blocks, ump->info.protocol_caps);
- ump->info.protocol &= ump->info.protocol_caps;
- choose_default_protocol(ump);
- return 1; /* finished */
- }
- /* handle EP device info stream message; update the UMP attributes */
- static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,
- const union snd_ump_stream_msg *buf)
- {
- ump->info.manufacturer_id = buf->device_info.manufacture_id & 0x7f7f7f;
- ump->info.family_id = (buf->device_info.family_msb << 8) |
- buf->device_info.family_lsb;
- ump->info.model_id = (buf->device_info.model_msb << 8) |
- buf->device_info.model_lsb;
- ump->info.sw_revision[0] = (buf->device_info.sw_revision >> 24) & 0x7f;
- ump->info.sw_revision[1] = (buf->device_info.sw_revision >> 16) & 0x7f;
- ump->info.sw_revision[2] = (buf->device_info.sw_revision >> 8) & 0x7f;
- ump->info.sw_revision[3] = buf->device_info.sw_revision & 0x7f;
- ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%4phN\n",
- ump->info.manufacturer_id,
- ump->info.family_id,
- ump->info.model_id,
- ump->info.sw_revision);
- return 1; /* finished */
- }
- /* handle EP name stream message; update the UMP name string */
- static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump,
- const union snd_ump_stream_msg *buf)
- {
- return ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
- buf->raw, 2);
- }
- /* handle EP product id stream message; update the UMP product_id string */
- static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
- const union snd_ump_stream_msg *buf)
- {
- return ump_append_string(ump, ump->info.product_id,
- sizeof(ump->info.product_id),
- buf->raw, 2);
- }
- /* notify the protocol change to sequencer */
- static void 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, unsigned int protocol)
- {
- unsigned int 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;
- ump->info.protocol = protocol;
- ump_dbg(ump, "New protocol = %x (caps = %x)\n",
- protocol, ump->info.protocol_caps);
- seq_notify_protocol(ump);
- return 1;
- }
- EXPORT_SYMBOL_GPL(snd_ump_switch_protocol);
- /* handle EP stream config message; update the UMP protocol */
- static int ump_handle_stream_cfg_msg(struct snd_ump_endpoint *ump,
- const union snd_ump_stream_msg *buf)
- {
- unsigned int protocol =
- (buf->stream_cfg.protocol << 8) | buf->stream_cfg.jrts;
- snd_ump_switch_protocol(ump, protocol);
- return 1; /* finished */
- }
- /* Extract Function Block info from UMP packet */
- static void fill_fb_info(struct snd_ump_endpoint *ump,
- struct snd_ump_block_info *info,
- const union snd_ump_stream_msg *buf)
- {
- info->direction = buf->fb_info.direction;
- info->ui_hint = buf->fb_info.ui_hint;
- info->first_group = buf->fb_info.first_group;
- info->num_groups = buf->fb_info.num_groups;
- if (buf->fb_info.midi_10 < 2)
- info->flags = buf->fb_info.midi_10;
- else
- info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED;
- info->active = buf->fb_info.active;
- info->midi_ci_version = buf->fb_info.midi_ci_version;
- info->sysex8_streams = buf->fb_info.sysex8_streams;
- ump_dbg(ump, "FB %d: dir=%d, active=%d, first_gp=%d, num_gp=%d, midici=%d, sysex8=%d, flags=0x%x\n",
- info->block_id, info->direction, info->active,
- info->first_group, info->num_groups, info->midi_ci_version,
- info->sysex8_streams, info->flags);
- if ((info->flags & SNDRV_UMP_BLOCK_IS_MIDI1) && info->num_groups != 1) {
- info->num_groups = 1;
- ump_dbg(ump, "FB %d: corrected groups to 1 for MIDI1\n",
- info->block_id);
- }
- }
- /* check whether the FB info gets updated by the current message */
- static bool is_fb_info_updated(struct snd_ump_endpoint *ump,
- struct snd_ump_block *fb,
- const union snd_ump_stream_msg *buf)
- {
- char tmpbuf[offsetof(struct snd_ump_block_info, name)];
- if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
- ump_info(ump, "Skipping static FB info update (blk#%d)\n",
- fb->info.block_id);
- return 0;
- }
- memcpy(tmpbuf, &fb->info, sizeof(tmpbuf));
- fill_fb_info(ump, (struct snd_ump_block_info *)tmpbuf, buf);
- return memcmp(&fb->info, tmpbuf, sizeof(tmpbuf)) != 0;
- }
- /* notify the FB info/name change to sequencer */
- static void seq_notify_fb_change(struct snd_ump_endpoint *ump,
- struct snd_ump_block *fb)
- {
- #if IS_ENABLED(CONFIG_SND_SEQUENCER)
- if (ump->seq_ops && ump->seq_ops->notify_fb_change)
- ump->seq_ops->notify_fb_change(ump, fb);
- #endif
- }
- /* handle FB info message; update FB info if the block is present */
- static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump,
- const union snd_ump_stream_msg *buf)
- {
- unsigned char blk;
- struct snd_ump_block *fb;
- blk = buf->fb_info.function_block_id;
- fb = snd_ump_get_block(ump, blk);
- /* 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 */
- static int ump_handle_fb_name_msg(struct snd_ump_endpoint *ump,
- const union snd_ump_stream_msg *buf)
- {
- unsigned char blk;
- struct snd_ump_block *fb;
- int ret;
- blk = buf->fb_name.function_block_id;
- fb = snd_ump_get_block(ump, blk);
- if (!fb)
- return -ENODEV;
- if (ump->parsed &&
- (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS)) {
- ump_dbg(ump, "Skipping static FB name update (blk#%d)\n",
- fb->info.block_id);
- return 0;
- }
- ret = ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
- buf->raw, 3);
- /* notify the FB name update to sequencer, too */
- if (ret > 0 && ump->parsed) {
- snd_ump_update_group_attrs(ump);
- update_legacy_names(ump);
- seq_notify_fb_change(ump, fb);
- }
- return ret;
- }
- static int create_block_from_fb_info(struct snd_ump_endpoint *ump, int blk)
- {
- struct snd_ump_block *fb;
- unsigned char direction, first_group, num_groups;
- const union snd_ump_stream_msg *buf =
- (const union snd_ump_stream_msg *)ump->input_buf;
- u32 msg;
- int err;
- /* 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;
- err = snd_ump_block_new(ump, blk, direction, first_group, num_groups,
- &fb);
- if (err < 0)
- return err;
- fill_fb_info(ump, &fb->info, buf);
- msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
- (blk << 8) | UMP_STREAM_MSG_REQUEST_FB_NAME;
- err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_NAME);
- if (err)
- ump_dbg(ump, "Unable to get UMP FB name string #%d\n", blk);
- return 0;
- }
- /* handle stream messages, called from snd_ump_receive() */
- static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
- const u32 *buf, int size)
- {
- const union snd_ump_stream_msg *msg;
- unsigned int status;
- int ret;
- /* UMP stream message suppressed (for gadget UMP)? */
- if (ump->no_process_stream)
- return;
- BUILD_BUG_ON(sizeof(*msg) != 16);
- ump_dbg(ump, "Stream msg: %08x %08x %08x %08x\n",
- buf[0], buf[1], buf[2], buf[3]);
- if (size != 4 || ump_message_type(*buf) != UMP_MSG_TYPE_STREAM)
- return;
- msg = (const union 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");
- /* 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);
- error:
- ump->parsed = true;
- ump_request_close(ump);
- if (err == -ETIMEDOUT)
- err = -ENODEV;
- return err;
- }
- EXPORT_SYMBOL_GPL(snd_ump_parse_endpoint);
- #if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
- /*
- * Legacy rawmidi support
- */
- static int 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;
- }
- static int 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;
- }
- static void snd_ump_legacy_trigger(struct snd_rawmidi_substream *substream,
- int up)
- {
- struct snd_ump_endpoint *ump = substream->rmidi->private_data;
- int dir = substream->stream;
- ump->ops->trigger(ump, dir, up);
- }
- static void snd_ump_legacy_drain(struct snd_rawmidi_substream *substream)
- {
- struct snd_ump_endpoint *ump = substream->rmidi->private_data;
- if (ump->ops->drain)
- ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
- }
- static int snd_ump_legacy_dev_register(struct snd_rawmidi *rmidi)
- {
- /* dummy, just for avoiding create superfluous seq clients */
- return 0;
- }
- static const struct snd_rawmidi_ops snd_ump_legacy_input_ops = {
- .open = snd_ump_legacy_open,
- .close = snd_ump_legacy_close,
- .trigger = snd_ump_legacy_trigger,
- };
- static const struct snd_rawmidi_ops snd_ump_legacy_output_ops = {
- .open = snd_ump_legacy_open,
- .close = snd_ump_legacy_close,
- .trigger = snd_ump_legacy_trigger,
- .drain = snd_ump_legacy_drain,
- };
- static const struct snd_rawmidi_global_ops snd_ump_legacy_ops = {
- .dev_register = snd_ump_legacy_dev_register,
- };
- static int process_legacy_output(struct snd_ump_endpoint *ump,
- u32 *buffer, int count)
- {
- struct snd_rawmidi_substream *substream;
- struct ump_cvt_to_ump *ctx;
- const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
- unsigned int protocol;
- unsigned char c;
- int group, size = 0;
- if (!ump->out_cvts || !ump->legacy_out_opens)
- return 0;
- guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
- for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
- substream = ump->legacy_substreams[dir][group];
- if (!substream)
- continue;
- ctx = &ump->out_cvts[group];
- protocol = ump->info.protocol;
- if ((protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) &&
- ump->groups[group].is_midi1)
- protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
- while (!ctx->ump_bytes &&
- snd_rawmidi_transmit(substream, &c, 1) > 0)
- snd_ump_convert_to_ump(ctx, group, protocol, c);
- if (ctx->ump_bytes && ctx->ump_bytes <= count) {
- size = ctx->ump_bytes;
- memcpy(buffer, ctx->ump, size);
- ctx->ump_bytes = 0;
- break;
- }
- }
- return size;
- }
- static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
- int words)
- {
- struct snd_rawmidi_substream *substream;
- unsigned char buf[16];
- unsigned char group;
- const int 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 */
- static int fill_legacy_mapping(struct snd_ump_endpoint *ump)
- {
- struct snd_ump_block *fb;
- unsigned int 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;
- }
- static void fill_substream_names(struct snd_ump_endpoint *ump,
- struct snd_rawmidi *rmidi, int dir)
- {
- struct snd_rawmidi_substream *s;
- const char *name;
- int idx;
- list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
- idx = ump->legacy_mapping[s->number];
- name = ump->groups[idx].name;
- if (!*name)
- name = ump->info.name;
- scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s",
- idx + 1, name,
- ump->groups[idx].active ? "" : " [Inactive]");
- }
- }
- static void update_legacy_names(struct snd_ump_endpoint *ump)
- {
- struct snd_rawmidi *rmidi = ump->legacy_rmidi;
- fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
- fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
- }
- int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
- char *id, int device)
- {
- struct snd_rawmidi *rmidi;
- bool input, output;
- int err, num;
- ump->out_cvts = kcalloc(SNDRV_UMP_MAX_GROUPS,
- sizeof(*ump->out_cvts), GFP_KERNEL);
- if (!ump->out_cvts)
- return -ENOMEM;
- num = fill_legacy_mapping(ump);
- input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT;
- output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT;
- err = snd_rawmidi_new(ump->core.card, id, device,
- output ? num : 0, input ? num : 0,
- &rmidi);
- if (err < 0) {
- kfree(ump->out_cvts);
- return err;
- }
- if (input)
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
- &snd_ump_legacy_input_ops);
- if (output)
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
- &snd_ump_legacy_output_ops);
- snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
- ump->info.name);
- rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
- rmidi->ops = &snd_ump_legacy_ops;
- rmidi->private_data = ump;
- ump->legacy_rmidi = rmidi;
- update_legacy_names(ump);
- ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
- return 0;
- }
- EXPORT_SYMBOL_GPL(snd_ump_attach_legacy_rawmidi);
- #endif /* CONFIG_SND_UMP_LEGACY_RAWMIDI */
- MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver");
- MODULE_LICENSE("GPL");
|