1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* ALSA sequencer binding for UMP device */ 3 4 #include <linux/init.h> 5 #include <linux/slab.h> 6 #include <linux/errno.h> 7 #include <linux/mutex.h> 8 #include <linux/string.h> 9 #include <linux/module.h> 10 #include <asm/byteorder.h> 11 #include <sound/core.h> 12 #include <sound/ump.h> 13 #include <sound/seq_kernel.h> 14 #include <sound/seq_device.h> 15 #include "seq_clientmgr.h" 16 #include "seq_system.h" 17 18 struct seq_ump_client; 19 struct seq_ump_group; 20 21 enum { 22 STR_IN = SNDRV_RAWMIDI_STREAM_INPUT, 23 STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT 24 }; 25 26 /* context for UMP input parsing, per EP */ 27 struct seq_ump_input_buffer { 28 unsigned char len; /* total length in words */ 29 unsigned char pending; /* pending words */ 30 unsigned char type; /* parsed UMP packet type */ 31 unsigned char group; /* parsed UMP packet group */ 32 u32 buf[4]; /* incoming UMP packet */ 33 }; 34 35 /* sequencer client, per UMP EP (rawmidi) */ 36 struct seq_ump_client { 37 struct snd_ump_endpoint *ump; /* assigned endpoint */ 38 int seq_client; /* sequencer client id */ 39 int opened[2]; /* current opens for each direction */ 40 struct snd_rawmidi_file out_rfile; /* rawmidi for output */ 41 /* RCU-protected shadow of out_rfile.output for the delivery hot path; 42 * out_rfile itself is only touched by open/close under open_mutex 43 */ 44 struct snd_rawmidi_substream __rcu *out_substream; 45 struct seq_ump_input_buffer input; /* input parser context */ 46 void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ 47 struct work_struct group_notify_work; /* FB change notification */ 48 }; 49 50 /* number of 32bit words for each UMP message type */ 51 static unsigned char ump_packet_words[0x10] = { 52 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4 53 }; 54 55 /* conversion between UMP group and seq port; 56 * assume the port number is equal with UMP group number (1-based) 57 */ 58 static unsigned char ump_group_to_seq_port(unsigned char group) 59 { 60 return group + 1; 61 } 62 63 /* process the incoming rawmidi stream */ 64 static void seq_ump_input_receive(struct snd_ump_endpoint *ump, 65 const u32 *val, int words) 66 { 67 struct seq_ump_client *client = ump->seq_client; 68 struct snd_seq_ump_event ev = {}; 69 70 if (!client->opened[STR_IN]) 71 return; 72 73 if (ump_is_groupless_msg(ump_message_type(*val))) 74 ev.source.port = 0; /* UMP EP port */ 75 else 76 ev.source.port = ump_group_to_seq_port(ump_message_group(*val)); 77 ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; 78 ev.flags = SNDRV_SEQ_EVENT_UMP; 79 memcpy(ev.ump, val, words << 2); 80 snd_seq_kernel_client_dispatch(client->seq_client, 81 (struct snd_seq_event *)&ev, 82 true, 0); 83 } 84 85 /* process an input sequencer event; only deal with UMP types */ 86 static int seq_ump_process_event(struct snd_seq_event *ev, int direct, 87 void *private_data, int atomic, int hop) 88 { 89 struct seq_ump_client *client = private_data; 90 struct snd_rawmidi_substream *substream; 91 struct snd_seq_ump_event *ump_ev; 92 unsigned char type; 93 int len; 94 95 guard(rcu)(); 96 substream = rcu_dereference(client->out_substream); 97 if (!substream) 98 return -ENODEV; 99 if (!snd_seq_ev_is_ump(ev)) 100 return 0; /* invalid event, skip */ 101 ump_ev = (struct snd_seq_ump_event *)ev; 102 type = ump_message_type(ump_ev->ump[0]); 103 len = ump_packet_words[type]; 104 if (len > 4) 105 return 0; // invalid - skip 106 snd_rawmidi_kernel_write(substream, ev->data.raw8.d, len << 2); 107 return 0; 108 } 109 110 /* open the rawmidi */ 111 static int seq_ump_client_open(struct seq_ump_client *client, int dir) 112 { 113 struct snd_ump_endpoint *ump = client->ump; 114 int err; 115 116 guard(mutex)(&ump->open_mutex); 117 if (dir == STR_OUT && !client->opened[dir]) { 118 /* out_rfile is only accessed under open_mutex; the delivery 119 * path reads out_substream via RCU, so open into out_rfile 120 * directly and publish the substream afterwards 121 */ 122 err = snd_rawmidi_kernel_open(&ump->core, 0, 123 SNDRV_RAWMIDI_LFLG_OUTPUT | 124 SNDRV_RAWMIDI_LFLG_APPEND, 125 &client->out_rfile); 126 if (err < 0) 127 return err; 128 rcu_assign_pointer(client->out_substream, 129 client->out_rfile.output); 130 } 131 client->opened[dir]++; 132 return 0; 133 } 134 135 /* close the rawmidi */ 136 static int seq_ump_client_close(struct seq_ump_client *client, int dir) 137 { 138 struct snd_ump_endpoint *ump = client->ump; 139 140 guard(mutex)(&ump->open_mutex); 141 if (!--client->opened[dir]) { 142 if (dir == STR_OUT && client->out_rfile.rmidi) { 143 rcu_assign_pointer(client->out_substream, NULL); 144 /* wait for a grace period so that no reader in the 145 * delivery path is still writing to the substream 146 * before it is released 147 */ 148 synchronize_rcu(); 149 snd_rawmidi_kernel_release(&client->out_rfile); 150 client->out_rfile = (struct snd_rawmidi_file){}; 151 } 152 } 153 return 0; 154 } 155 156 /* sequencer subscription ops for each client */ 157 static int seq_ump_subscribe(void *pdata, struct snd_seq_port_subscribe *info) 158 { 159 struct seq_ump_client *client = pdata; 160 161 return seq_ump_client_open(client, STR_IN); 162 } 163 164 static int seq_ump_unsubscribe(void *pdata, struct snd_seq_port_subscribe *info) 165 { 166 struct seq_ump_client *client = pdata; 167 168 return seq_ump_client_close(client, STR_IN); 169 } 170 171 static int seq_ump_use(void *pdata, struct snd_seq_port_subscribe *info) 172 { 173 struct seq_ump_client *client = pdata; 174 175 return seq_ump_client_open(client, STR_OUT); 176 } 177 178 static int seq_ump_unuse(void *pdata, struct snd_seq_port_subscribe *info) 179 { 180 struct seq_ump_client *client = pdata; 181 182 return seq_ump_client_close(client, STR_OUT); 183 } 184 185 /* fill port_info from the given UMP EP and group info */ 186 static void fill_port_info(struct snd_seq_port_info *port, 187 struct seq_ump_client *client, 188 struct snd_ump_group *group) 189 { 190 unsigned int rawmidi_info = client->ump->core.info_flags; 191 192 port->addr.client = client->seq_client; 193 port->addr.port = ump_group_to_seq_port(group->group); 194 port->capability = 0; 195 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) 196 port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | 197 SNDRV_SEQ_PORT_CAP_SYNC_WRITE | 198 SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 199 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) 200 port->capability |= SNDRV_SEQ_PORT_CAP_READ | 201 SNDRV_SEQ_PORT_CAP_SYNC_READ | 202 SNDRV_SEQ_PORT_CAP_SUBS_READ; 203 if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX) 204 port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; 205 if (group->dir_bits & (1 << STR_IN)) 206 port->direction |= SNDRV_SEQ_PORT_DIR_INPUT; 207 if (group->dir_bits & (1 << STR_OUT)) 208 port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT; 209 port->ump_group = group->group + 1; 210 if (!group->active) 211 port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE; 212 if (group->is_midi1) 213 port->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1; 214 port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | 215 SNDRV_SEQ_PORT_TYPE_MIDI_UMP | 216 SNDRV_SEQ_PORT_TYPE_HARDWARE | 217 SNDRV_SEQ_PORT_TYPE_PORT; 218 port->midi_channels = 16; 219 if (*group->name) 220 snprintf(port->name, sizeof(port->name), "Group %d (%.53s)", 221 group->group + 1, group->name); 222 else 223 sprintf(port->name, "Group %d", group->group + 1); 224 } 225 226 /* skip non-existing group for static blocks */ 227 static bool skip_group(struct seq_ump_client *client, struct snd_ump_group *group) 228 { 229 return !group->valid && 230 (client->ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS); 231 } 232 233 /* create a new sequencer port per UMP group */ 234 static int seq_ump_group_init(struct seq_ump_client *client, int group_index) 235 { 236 struct snd_ump_group *group = &client->ump->groups[group_index]; 237 struct snd_seq_port_callback pcallbacks; 238 239 if (skip_group(client, group)) 240 return 0; 241 242 struct snd_seq_port_info *port __free(kfree) = 243 kzalloc_obj(*port); 244 if (!port) 245 return -ENOMEM; 246 247 fill_port_info(port, client, group); 248 port->flags |= SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 249 memset(&pcallbacks, 0, sizeof(pcallbacks)); 250 pcallbacks.owner = THIS_MODULE; 251 pcallbacks.private_data = client; 252 pcallbacks.subscribe = seq_ump_subscribe; 253 pcallbacks.unsubscribe = seq_ump_unsubscribe; 254 pcallbacks.use = seq_ump_use; 255 pcallbacks.unuse = seq_ump_unuse; 256 pcallbacks.event_input = seq_ump_process_event; 257 port->kernel = &pcallbacks; 258 return snd_seq_kernel_client_ctl(client->seq_client, 259 SNDRV_SEQ_IOCTL_CREATE_PORT, 260 port); 261 } 262 263 /* update the sequencer ports; called from notify_fb_change callback */ 264 static void update_port_infos(struct seq_ump_client *client) 265 { 266 int i, err; 267 268 struct snd_seq_port_info *old __free(kfree) = 269 kzalloc_obj(*old); 270 struct snd_seq_port_info *new __free(kfree) = 271 kzalloc_obj(*new); 272 if (!old || !new) 273 return; 274 275 for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { 276 if (skip_group(client, &client->ump->groups[i])) 277 continue; 278 279 old->addr.client = client->seq_client; 280 old->addr.port = ump_group_to_seq_port(i); 281 err = snd_seq_kernel_client_ctl(client->seq_client, 282 SNDRV_SEQ_IOCTL_GET_PORT_INFO, 283 old); 284 if (err < 0) 285 continue; 286 fill_port_info(new, client, &client->ump->groups[i]); 287 if (old->capability == new->capability && 288 !strcmp(old->name, new->name)) 289 continue; 290 err = snd_seq_kernel_client_ctl(client->seq_client, 291 SNDRV_SEQ_IOCTL_SET_PORT_INFO, 292 new); 293 if (err < 0) 294 continue; 295 } 296 } 297 298 /* create a UMP Endpoint port */ 299 static int create_ump_endpoint_port(struct seq_ump_client *client) 300 { 301 struct snd_seq_port_callback pcallbacks; 302 unsigned int rawmidi_info = client->ump->core.info_flags; 303 int err; 304 305 struct snd_seq_port_info *port __free(kfree) = 306 kzalloc_obj(*port); 307 if (!port) 308 return -ENOMEM; 309 310 port->addr.client = client->seq_client; 311 port->addr.port = 0; /* fixed */ 312 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 313 port->capability = SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT; 314 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) { 315 port->capability |= SNDRV_SEQ_PORT_CAP_READ | 316 SNDRV_SEQ_PORT_CAP_SYNC_READ | 317 SNDRV_SEQ_PORT_CAP_SUBS_READ; 318 port->direction |= SNDRV_SEQ_PORT_DIR_INPUT; 319 } 320 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) { 321 port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | 322 SNDRV_SEQ_PORT_CAP_SYNC_WRITE | 323 SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 324 port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT; 325 } 326 if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX) 327 port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; 328 port->ump_group = 0; /* no associated group, no conversion */ 329 port->type = SNDRV_SEQ_PORT_TYPE_MIDI_UMP | 330 SNDRV_SEQ_PORT_TYPE_HARDWARE | 331 SNDRV_SEQ_PORT_TYPE_PORT; 332 port->midi_channels = 16; 333 strscpy(port->name, "MIDI 2.0"); 334 memset(&pcallbacks, 0, sizeof(pcallbacks)); 335 pcallbacks.owner = THIS_MODULE; 336 pcallbacks.private_data = client; 337 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) { 338 pcallbacks.subscribe = seq_ump_subscribe; 339 pcallbacks.unsubscribe = seq_ump_unsubscribe; 340 } 341 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) { 342 pcallbacks.use = seq_ump_use; 343 pcallbacks.unuse = seq_ump_unuse; 344 pcallbacks.event_input = seq_ump_process_event; 345 } 346 port->kernel = &pcallbacks; 347 err = snd_seq_kernel_client_ctl(client->seq_client, 348 SNDRV_SEQ_IOCTL_CREATE_PORT, 349 port); 350 return err; 351 } 352 353 /* release the client resources */ 354 static void seq_ump_client_free(struct seq_ump_client *client) 355 { 356 cancel_work_sync(&client->group_notify_work); 357 358 if (client->seq_client >= 0) 359 snd_seq_delete_kernel_client(client->seq_client); 360 361 client->ump->seq_ops = NULL; 362 client->ump->seq_client = NULL; 363 364 kfree(client); 365 } 366 367 /* update the MIDI version for the given client */ 368 static void setup_client_midi_version(struct seq_ump_client *client) 369 { 370 struct snd_seq_client *cptr; 371 372 cptr = snd_seq_kernel_client_get(client->seq_client); 373 if (!cptr) 374 return; 375 if (client->ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) 376 cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0; 377 else 378 cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0; 379 snd_seq_kernel_client_put(cptr); 380 } 381 382 /* set up client's group_filter bitmap */ 383 static void setup_client_group_filter(struct seq_ump_client *client) 384 { 385 struct snd_seq_client *cptr; 386 unsigned int filter; 387 int p; 388 389 cptr = snd_seq_kernel_client_get(client->seq_client); 390 if (!cptr) 391 return; 392 filter = SND_SEQ_GROUP_FILTER_GROUPS; /* always allow groupless messages */ 393 for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { 394 if (client->ump->groups[p].active) 395 filter &= ~(1U << (p + 1)); 396 } 397 cptr->group_filter = filter; 398 snd_seq_kernel_client_put(cptr); 399 } 400 401 /* UMP group change notification */ 402 static void handle_group_notify(struct work_struct *work) 403 { 404 struct seq_ump_client *client = 405 container_of(work, struct seq_ump_client, group_notify_work); 406 407 update_port_infos(client); 408 setup_client_group_filter(client); 409 } 410 411 /* UMP EP change notification */ 412 static int seq_ump_notify_ep_change(struct snd_ump_endpoint *ump) 413 { 414 struct seq_ump_client *client = ump->seq_client; 415 struct snd_seq_client *cptr; 416 int client_id; 417 418 if (!client) 419 return -ENODEV; 420 client_id = client->seq_client; 421 cptr = snd_seq_kernel_client_get(client_id); 422 if (!cptr) 423 return -ENODEV; 424 425 snd_seq_system_ump_notify(client_id, 0, SNDRV_SEQ_EVENT_UMP_EP_CHANGE, 426 true); 427 428 /* update sequencer client name if needed */ 429 if (*ump->core.name && strcmp(ump->core.name, cptr->name)) { 430 strscpy(cptr->name, ump->core.name, sizeof(cptr->name)); 431 snd_seq_system_client_ev_client_change(client_id); 432 } 433 434 snd_seq_kernel_client_put(cptr); 435 return 0; 436 } 437 438 /* UMP FB change notification */ 439 static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, 440 struct snd_ump_block *fb) 441 { 442 struct seq_ump_client *client = ump->seq_client; 443 444 if (!client) 445 return -ENODEV; 446 schedule_work(&client->group_notify_work); 447 snd_seq_system_ump_notify(client->seq_client, fb->info.block_id, 448 SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE, 449 true); 450 return 0; 451 } 452 453 /* UMP protocol change notification; just update the midi_version field */ 454 static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump) 455 { 456 struct seq_ump_client *client = ump->seq_client; 457 458 if (!client) 459 return -ENODEV; 460 setup_client_midi_version(client); 461 snd_seq_system_ump_notify(client->seq_client, 0, 462 SNDRV_SEQ_EVENT_UMP_EP_CHANGE, 463 true); 464 return 0; 465 } 466 467 static const struct snd_seq_ump_ops seq_ump_ops = { 468 .input_receive = seq_ump_input_receive, 469 .notify_ep_change = seq_ump_notify_ep_change, 470 .notify_fb_change = seq_ump_notify_fb_change, 471 .switch_protocol = seq_ump_switch_protocol, 472 }; 473 474 /* create a sequencer client and ports for the given UMP endpoint */ 475 static int snd_seq_ump_probe(struct snd_seq_device *dev) 476 { 477 struct snd_ump_endpoint *ump = dev->private_data; 478 struct snd_card *card = dev->card; 479 struct seq_ump_client *client; 480 struct snd_ump_block *fb; 481 struct snd_seq_client *cptr; 482 int p, err; 483 484 client = kzalloc_obj(*client); 485 if (!client) 486 return -ENOMEM; 487 488 INIT_WORK(&client->group_notify_work, handle_group_notify); 489 client->ump = ump; 490 491 client->seq_client = 492 snd_seq_create_kernel_client(card, ump->core.device, 493 ump->core.name); 494 if (client->seq_client < 0) { 495 err = client->seq_client; 496 goto error; 497 } 498 499 client->ump_info[0] = &ump->info; 500 list_for_each_entry(fb, &ump->block_list, list) 501 client->ump_info[fb->info.block_id + 1] = &fb->info; 502 503 setup_client_midi_version(client); 504 505 for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { 506 err = seq_ump_group_init(client, p); 507 if (err < 0) 508 goto error; 509 } 510 511 setup_client_group_filter(client); 512 513 err = create_ump_endpoint_port(client); 514 if (err < 0) 515 goto error; 516 517 cptr = snd_seq_kernel_client_get(client->seq_client); 518 if (!cptr) { 519 err = -EINVAL; 520 goto error; 521 } 522 cptr->ump_info = client->ump_info; 523 snd_seq_kernel_client_put(cptr); 524 525 ump->seq_client = client; 526 ump->seq_ops = &seq_ump_ops; 527 return 0; 528 529 error: 530 seq_ump_client_free(client); 531 return err; 532 } 533 534 /* remove a sequencer client */ 535 static void snd_seq_ump_remove(struct snd_seq_device *dev) 536 { 537 struct snd_ump_endpoint *ump = dev->private_data; 538 539 if (ump->seq_client) 540 seq_ump_client_free(ump->seq_client); 541 } 542 543 static struct snd_seq_driver seq_ump_driver = { 544 .probe = snd_seq_ump_probe, 545 .remove = snd_seq_ump_remove, 546 .driver = { 547 .name = KBUILD_MODNAME, 548 }, 549 .id = SNDRV_SEQ_DEV_ID_UMP, 550 .argsize = 0, 551 }; 552 553 module_snd_seq_driver(seq_ump_driver); 554 555 MODULE_DESCRIPTION("ALSA sequencer client for UMP rawmidi"); 556 MODULE_LICENSE("GPL"); 557