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