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