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 < 0 || 302 fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS) 303 break; 304 group = &client->groups[fb->info.first_group]; 305 for (i = 0; i < fb->info.num_groups; i++, group++) { 306 if (fb->info.active) 307 group->active = 1; 308 switch (fb->info.direction) { 309 case SNDRV_UMP_DIR_INPUT: 310 group->dir_bits |= (1 << STR_IN); 311 break; 312 case SNDRV_UMP_DIR_OUTPUT: 313 group->dir_bits |= (1 << STR_OUT); 314 break; 315 case SNDRV_UMP_DIR_BIDIRECTION: 316 group->dir_bits |= (1 << STR_OUT) | (1 << STR_IN); 317 break; 318 } 319 if (!*fb->info.name) 320 continue; 321 if (!*group->name) { 322 /* store the first matching name */ 323 strscpy(group->name, fb->info.name, 324 sizeof(group->name)); 325 } else { 326 /* when overlapping, concat names */ 327 strlcat(group->name, ", ", sizeof(group->name)); 328 strlcat(group->name, fb->info.name, 329 sizeof(group->name)); 330 } 331 } 332 } 333 } 334 335 /* create a UMP Endpoint port */ 336 static int create_ump_endpoint_port(struct seq_ump_client *client) 337 { 338 struct snd_seq_port_info *port; 339 struct snd_seq_port_callback pcallbacks; 340 unsigned int rawmidi_info = client->ump->core.info_flags; 341 int err; 342 343 port = kzalloc(sizeof(*port), GFP_KERNEL); 344 if (!port) 345 return -ENOMEM; 346 347 port->addr.client = client->seq_client; 348 port->addr.port = 0; /* fixed */ 349 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 350 port->capability = SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT; 351 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) { 352 port->capability |= SNDRV_SEQ_PORT_CAP_READ | 353 SNDRV_SEQ_PORT_CAP_SYNC_READ | 354 SNDRV_SEQ_PORT_CAP_SUBS_READ; 355 port->direction |= SNDRV_SEQ_PORT_DIR_INPUT; 356 } 357 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) { 358 port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | 359 SNDRV_SEQ_PORT_CAP_SYNC_WRITE | 360 SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 361 port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT; 362 } 363 if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX) 364 port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; 365 port->ump_group = 0; /* no associated group, no conversion */ 366 port->type = SNDRV_SEQ_PORT_TYPE_MIDI_UMP | 367 SNDRV_SEQ_PORT_TYPE_HARDWARE | 368 SNDRV_SEQ_PORT_TYPE_PORT; 369 port->midi_channels = 16; 370 strcpy(port->name, "MIDI 2.0"); 371 memset(&pcallbacks, 0, sizeof(pcallbacks)); 372 pcallbacks.owner = THIS_MODULE; 373 pcallbacks.private_data = client; 374 if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) { 375 pcallbacks.subscribe = seq_ump_subscribe; 376 pcallbacks.unsubscribe = seq_ump_unsubscribe; 377 } 378 if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) { 379 pcallbacks.use = seq_ump_use; 380 pcallbacks.unuse = seq_ump_unuse; 381 pcallbacks.event_input = seq_ump_process_event; 382 } 383 port->kernel = &pcallbacks; 384 err = snd_seq_kernel_client_ctl(client->seq_client, 385 SNDRV_SEQ_IOCTL_CREATE_PORT, 386 port); 387 kfree(port); 388 return err; 389 } 390 391 /* release the client resources */ 392 static void seq_ump_client_free(struct seq_ump_client *client) 393 { 394 cancel_work_sync(&client->group_notify_work); 395 396 if (client->seq_client >= 0) 397 snd_seq_delete_kernel_client(client->seq_client); 398 399 client->ump->seq_ops = NULL; 400 client->ump->seq_client = NULL; 401 402 kfree(client); 403 } 404 405 /* update the MIDI version for the given client */ 406 static void setup_client_midi_version(struct seq_ump_client *client) 407 { 408 struct snd_seq_client *cptr; 409 410 cptr = snd_seq_kernel_client_get(client->seq_client); 411 if (!cptr) 412 return; 413 if (client->ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) 414 cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0; 415 else 416 cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0; 417 snd_seq_kernel_client_put(cptr); 418 } 419 420 /* UMP group change notification */ 421 static void handle_group_notify(struct work_struct *work) 422 { 423 struct seq_ump_client *client = 424 container_of(work, struct seq_ump_client, group_notify_work); 425 426 update_group_attrs(client); 427 update_port_infos(client); 428 } 429 430 /* UMP FB change notification */ 431 static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, 432 struct snd_ump_block *fb) 433 { 434 struct seq_ump_client *client = ump->seq_client; 435 436 if (!client) 437 return -ENODEV; 438 schedule_work(&client->group_notify_work); 439 return 0; 440 } 441 442 /* UMP protocol change notification; just update the midi_version field */ 443 static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump) 444 { 445 if (!ump->seq_client) 446 return -ENODEV; 447 setup_client_midi_version(ump->seq_client); 448 return 0; 449 } 450 451 static const struct snd_seq_ump_ops seq_ump_ops = { 452 .input_receive = seq_ump_input_receive, 453 .notify_fb_change = seq_ump_notify_fb_change, 454 .switch_protocol = seq_ump_switch_protocol, 455 }; 456 457 /* create a sequencer client and ports for the given UMP endpoint */ 458 static int snd_seq_ump_probe(struct device *_dev) 459 { 460 struct snd_seq_device *dev = to_seq_dev(_dev); 461 struct snd_ump_endpoint *ump = dev->private_data; 462 struct snd_card *card = dev->card; 463 struct seq_ump_client *client; 464 struct snd_ump_block *fb; 465 struct snd_seq_client *cptr; 466 int p, err; 467 468 client = kzalloc(sizeof(*client), GFP_KERNEL); 469 if (!client) 470 return -ENOMEM; 471 472 INIT_WORK(&client->group_notify_work, handle_group_notify); 473 client->ump = ump; 474 475 client->seq_client = 476 snd_seq_create_kernel_client(card, ump->core.device, 477 ump->core.name); 478 if (client->seq_client < 0) { 479 err = client->seq_client; 480 goto error; 481 } 482 483 client->ump_info[0] = &ump->info; 484 list_for_each_entry(fb, &ump->block_list, list) 485 client->ump_info[fb->info.block_id + 1] = &fb->info; 486 487 setup_client_midi_version(client); 488 update_group_attrs(client); 489 490 for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { 491 err = seq_ump_group_init(client, p); 492 if (err < 0) 493 goto error; 494 } 495 496 err = create_ump_endpoint_port(client); 497 if (err < 0) 498 goto error; 499 500 cptr = snd_seq_kernel_client_get(client->seq_client); 501 if (!cptr) { 502 err = -EINVAL; 503 goto error; 504 } 505 cptr->ump_info = client->ump_info; 506 snd_seq_kernel_client_put(cptr); 507 508 ump->seq_client = client; 509 ump->seq_ops = &seq_ump_ops; 510 return 0; 511 512 error: 513 seq_ump_client_free(client); 514 return err; 515 } 516 517 /* remove a sequencer client */ 518 static int snd_seq_ump_remove(struct device *_dev) 519 { 520 struct snd_seq_device *dev = to_seq_dev(_dev); 521 struct snd_ump_endpoint *ump = dev->private_data; 522 523 if (ump->seq_client) 524 seq_ump_client_free(ump->seq_client); 525 return 0; 526 } 527 528 static struct snd_seq_driver seq_ump_driver = { 529 .driver = { 530 .name = KBUILD_MODNAME, 531 .probe = snd_seq_ump_probe, 532 .remove = snd_seq_ump_remove, 533 }, 534 .id = SNDRV_SEQ_DEV_ID_UMP, 535 .argsize = 0, 536 }; 537 538 module_snd_seq_driver(seq_ump_driver); 539 540 MODULE_DESCRIPTION("ALSA sequencer client for UMP rawmidi"); 541 MODULE_LICENSE("GPL"); 542