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