1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * OSS compatible sequencer driver 4 * 5 * MIDI device handlers 6 * 7 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> 8 */ 9 10 #include <sound/asoundef.h> 11 #include "seq_oss_midi.h" 12 #include "seq_oss_readq.h" 13 #include "seq_oss_timer.h" 14 #include "seq_oss_event.h" 15 #include <sound/seq_midi_event.h> 16 #include "../seq_lock.h" 17 #include <linux/init.h> 18 #include <linux/slab.h> 19 #include <linux/nospec.h> 20 21 22 /* 23 * constants 24 */ 25 #define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30 26 27 /* 28 * definition of midi device record 29 */ 30 struct seq_oss_midi { 31 int seq_device; /* device number */ 32 int client; /* sequencer client number */ 33 int port; /* sequencer port number */ 34 unsigned int flags; /* port capability */ 35 int opened; /* flag for opening */ 36 unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME]; 37 struct snd_midi_event *coder; /* MIDI event coder */ 38 struct seq_oss_devinfo *devinfo; /* assigned OSSseq device */ 39 snd_use_lock_t use_lock; 40 struct mutex open_mutex; 41 }; 42 43 44 /* 45 * midi device table 46 */ 47 static int max_midi_devs; 48 static struct seq_oss_midi *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; 49 50 static DEFINE_SPINLOCK(register_lock); 51 52 /* 53 * prototypes 54 */ 55 static struct seq_oss_midi *get_mdev(int dev); 56 static struct seq_oss_midi *get_mididev(struct seq_oss_devinfo *dp, int dev); 57 static int send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev); 58 static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev); 59 60 /* 61 * look up the existing ports 62 * this looks a very exhausting job. 63 */ 64 int 65 snd_seq_oss_midi_lookup_ports(int client) 66 { 67 struct snd_seq_client_info *clinfo __free(kfree) = NULL; 68 struct snd_seq_port_info *pinfo __free(kfree) = NULL; 69 70 clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL); 71 pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); 72 if (!clinfo || !pinfo) 73 return -ENOMEM; 74 clinfo->client = -1; 75 while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) { 76 if (clinfo->client == client) 77 continue; /* ignore myself */ 78 pinfo->addr.client = clinfo->client; 79 pinfo->addr.port = -1; 80 while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0) 81 snd_seq_oss_midi_check_new_port(pinfo); 82 } 83 return 0; 84 } 85 86 87 /* 88 */ 89 static struct seq_oss_midi * 90 get_mdev(int dev) 91 { 92 struct seq_oss_midi *mdev; 93 unsigned long flags; 94 95 spin_lock_irqsave(®ister_lock, flags); 96 mdev = midi_devs[dev]; 97 if (mdev) 98 snd_use_lock_use(&mdev->use_lock); 99 spin_unlock_irqrestore(®ister_lock, flags); 100 return mdev; 101 } 102 103 /* 104 * look for the identical slot 105 */ 106 static struct seq_oss_midi * 107 find_slot(int client, int port) 108 { 109 int i; 110 struct seq_oss_midi *mdev; 111 unsigned long flags; 112 113 spin_lock_irqsave(®ister_lock, flags); 114 for (i = 0; i < max_midi_devs; i++) { 115 mdev = midi_devs[i]; 116 if (mdev && mdev->client == client && mdev->port == port) { 117 /* found! */ 118 snd_use_lock_use(&mdev->use_lock); 119 spin_unlock_irqrestore(®ister_lock, flags); 120 return mdev; 121 } 122 } 123 spin_unlock_irqrestore(®ister_lock, flags); 124 return NULL; 125 } 126 127 128 #define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) 129 #define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) 130 /* 131 * register a new port if it doesn't exist yet 132 */ 133 int 134 snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) 135 { 136 int i; 137 struct seq_oss_midi *mdev; 138 unsigned long flags; 139 140 /* the port must include generic midi */ 141 if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC)) 142 return 0; 143 /* either read or write subscribable */ 144 if ((pinfo->capability & PERM_WRITE) != PERM_WRITE && 145 (pinfo->capability & PERM_READ) != PERM_READ) 146 return 0; 147 148 /* 149 * look for the identical slot 150 */ 151 mdev = find_slot(pinfo->addr.client, pinfo->addr.port); 152 if (mdev) { 153 /* already exists */ 154 snd_use_lock_free(&mdev->use_lock); 155 return 0; 156 } 157 158 /* 159 * allocate midi info record 160 */ 161 mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 162 if (!mdev) 163 return -ENOMEM; 164 165 /* copy the port information */ 166 mdev->client = pinfo->addr.client; 167 mdev->port = pinfo->addr.port; 168 mdev->flags = pinfo->capability; 169 mdev->opened = 0; 170 snd_use_lock_init(&mdev->use_lock); 171 mutex_init(&mdev->open_mutex); 172 173 /* copy and truncate the name of synth device */ 174 strscpy(mdev->name, pinfo->name, sizeof(mdev->name)); 175 176 /* create MIDI coder */ 177 if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) { 178 pr_err("ALSA: seq_oss: can't malloc midi coder\n"); 179 kfree(mdev); 180 return -ENOMEM; 181 } 182 /* OSS sequencer adds running status to all sequences */ 183 snd_midi_event_no_status(mdev->coder, 1); 184 185 /* 186 * look for en empty slot 187 */ 188 spin_lock_irqsave(®ister_lock, flags); 189 for (i = 0; i < max_midi_devs; i++) { 190 if (midi_devs[i] == NULL) 191 break; 192 } 193 if (i >= max_midi_devs) { 194 if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) { 195 spin_unlock_irqrestore(®ister_lock, flags); 196 snd_midi_event_free(mdev->coder); 197 kfree(mdev); 198 return -ENOMEM; 199 } 200 max_midi_devs++; 201 } 202 mdev->seq_device = i; 203 midi_devs[mdev->seq_device] = mdev; 204 spin_unlock_irqrestore(®ister_lock, flags); 205 206 return 0; 207 } 208 209 /* 210 * release the midi device if it was registered 211 */ 212 int 213 snd_seq_oss_midi_check_exit_port(int client, int port) 214 { 215 struct seq_oss_midi *mdev; 216 unsigned long flags; 217 int index; 218 219 mdev = find_slot(client, port); 220 if (mdev) { 221 spin_lock_irqsave(®ister_lock, flags); 222 midi_devs[mdev->seq_device] = NULL; 223 spin_unlock_irqrestore(®ister_lock, flags); 224 snd_use_lock_free(&mdev->use_lock); 225 snd_use_lock_sync(&mdev->use_lock); 226 snd_midi_event_free(mdev->coder); 227 kfree(mdev); 228 } 229 spin_lock_irqsave(®ister_lock, flags); 230 for (index = max_midi_devs - 1; index >= 0; index--) { 231 if (midi_devs[index]) 232 break; 233 } 234 max_midi_devs = index + 1; 235 spin_unlock_irqrestore(®ister_lock, flags); 236 return 0; 237 } 238 239 240 /* 241 * release the midi device if it was registered 242 */ 243 void 244 snd_seq_oss_midi_clear_all(void) 245 { 246 int i; 247 struct seq_oss_midi *mdev; 248 unsigned long flags; 249 250 spin_lock_irqsave(®ister_lock, flags); 251 for (i = 0; i < max_midi_devs; i++) { 252 mdev = midi_devs[i]; 253 if (mdev) { 254 snd_midi_event_free(mdev->coder); 255 kfree(mdev); 256 midi_devs[i] = NULL; 257 } 258 } 259 max_midi_devs = 0; 260 spin_unlock_irqrestore(®ister_lock, flags); 261 } 262 263 264 /* 265 * set up midi tables 266 */ 267 void 268 snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp) 269 { 270 spin_lock_irq(®ister_lock); 271 dp->max_mididev = max_midi_devs; 272 spin_unlock_irq(®ister_lock); 273 } 274 275 /* 276 * clean up midi tables 277 */ 278 void 279 snd_seq_oss_midi_cleanup(struct seq_oss_devinfo *dp) 280 { 281 int i; 282 for (i = 0; i < dp->max_mididev; i++) 283 snd_seq_oss_midi_close(dp, i); 284 dp->max_mididev = 0; 285 } 286 287 288 /* 289 * open all midi devices. ignore errors. 290 */ 291 void 292 snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode) 293 { 294 int i; 295 for (i = 0; i < dp->max_mididev; i++) 296 snd_seq_oss_midi_open(dp, i, file_mode); 297 } 298 299 300 /* 301 * get the midi device information 302 */ 303 static struct seq_oss_midi * 304 get_mididev(struct seq_oss_devinfo *dp, int dev) 305 { 306 if (dev < 0 || dev >= dp->max_mididev) 307 return NULL; 308 dev = array_index_nospec(dev, dp->max_mididev); 309 return get_mdev(dev); 310 } 311 312 313 /* 314 * open the midi device if not opened yet 315 */ 316 int 317 snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode) 318 { 319 int perm; 320 struct seq_oss_midi *mdev; 321 struct snd_seq_port_subscribe subs; 322 int err; 323 324 mdev = get_mididev(dp, dev); 325 if (!mdev) 326 return -ENODEV; 327 328 mutex_lock(&mdev->open_mutex); 329 /* already used? */ 330 if (mdev->opened && mdev->devinfo != dp) { 331 err = -EBUSY; 332 goto unlock; 333 } 334 335 perm = 0; 336 if (is_write_mode(fmode)) 337 perm |= PERM_WRITE; 338 if (is_read_mode(fmode)) 339 perm |= PERM_READ; 340 perm &= mdev->flags; 341 if (perm == 0) { 342 err = -ENXIO; 343 goto unlock; 344 } 345 346 /* already opened? */ 347 if ((mdev->opened & perm) == perm) { 348 err = 0; 349 goto unlock; 350 } 351 352 perm &= ~mdev->opened; 353 354 memset(&subs, 0, sizeof(subs)); 355 356 if (perm & PERM_WRITE) { 357 subs.sender = dp->addr; 358 subs.dest.client = mdev->client; 359 subs.dest.port = mdev->port; 360 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) 361 mdev->opened |= PERM_WRITE; 362 } 363 if (perm & PERM_READ) { 364 subs.sender.client = mdev->client; 365 subs.sender.port = mdev->port; 366 subs.dest = dp->addr; 367 subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP; 368 subs.queue = dp->queue; /* queue for timestamps */ 369 if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) 370 mdev->opened |= PERM_READ; 371 } 372 373 if (! mdev->opened) { 374 err = -ENXIO; 375 goto unlock; 376 } 377 378 mdev->devinfo = dp; 379 err = 0; 380 381 unlock: 382 mutex_unlock(&mdev->open_mutex); 383 snd_use_lock_free(&mdev->use_lock); 384 return err; 385 } 386 387 /* 388 * close the midi device if already opened 389 */ 390 int 391 snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev) 392 { 393 struct seq_oss_midi *mdev; 394 struct snd_seq_port_subscribe subs; 395 396 mdev = get_mididev(dp, dev); 397 if (!mdev) 398 return -ENODEV; 399 mutex_lock(&mdev->open_mutex); 400 if (!mdev->opened || mdev->devinfo != dp) 401 goto unlock; 402 403 memset(&subs, 0, sizeof(subs)); 404 if (mdev->opened & PERM_WRITE) { 405 subs.sender = dp->addr; 406 subs.dest.client = mdev->client; 407 subs.dest.port = mdev->port; 408 snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); 409 } 410 if (mdev->opened & PERM_READ) { 411 subs.sender.client = mdev->client; 412 subs.sender.port = mdev->port; 413 subs.dest = dp->addr; 414 snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); 415 } 416 417 mdev->opened = 0; 418 mdev->devinfo = NULL; 419 420 unlock: 421 mutex_unlock(&mdev->open_mutex); 422 snd_use_lock_free(&mdev->use_lock); 423 return 0; 424 } 425 426 /* 427 * change seq capability flags to file mode flags 428 */ 429 int 430 snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev) 431 { 432 struct seq_oss_midi *mdev; 433 int mode; 434 435 mdev = get_mididev(dp, dev); 436 if (!mdev) 437 return 0; 438 439 mode = 0; 440 if (mdev->opened & PERM_WRITE) 441 mode |= SNDRV_SEQ_OSS_FILE_WRITE; 442 if (mdev->opened & PERM_READ) 443 mode |= SNDRV_SEQ_OSS_FILE_READ; 444 445 snd_use_lock_free(&mdev->use_lock); 446 return mode; 447 } 448 449 /* 450 * reset the midi device and close it: 451 * so far, only close the device. 452 */ 453 void 454 snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) 455 { 456 struct seq_oss_midi *mdev; 457 458 mdev = get_mididev(dp, dev); 459 if (!mdev) 460 return; 461 if (! mdev->opened) { 462 snd_use_lock_free(&mdev->use_lock); 463 return; 464 } 465 466 if (mdev->opened & PERM_WRITE) { 467 struct snd_seq_event ev; 468 int c; 469 470 memset(&ev, 0, sizeof(ev)); 471 ev.dest.client = mdev->client; 472 ev.dest.port = mdev->port; 473 ev.queue = dp->queue; 474 ev.source.port = dp->port; 475 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) { 476 ev.type = SNDRV_SEQ_EVENT_SENSING; 477 snd_seq_oss_dispatch(dp, &ev, 0, 0); 478 } 479 for (c = 0; c < 16; c++) { 480 ev.type = SNDRV_SEQ_EVENT_CONTROLLER; 481 ev.data.control.channel = c; 482 ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF; 483 snd_seq_oss_dispatch(dp, &ev, 0, 0); 484 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { 485 ev.data.control.param = 486 MIDI_CTL_RESET_CONTROLLERS; 487 snd_seq_oss_dispatch(dp, &ev, 0, 0); 488 ev.type = SNDRV_SEQ_EVENT_PITCHBEND; 489 ev.data.control.value = 0; 490 snd_seq_oss_dispatch(dp, &ev, 0, 0); 491 } 492 } 493 } 494 // snd_seq_oss_midi_close(dp, dev); 495 snd_use_lock_free(&mdev->use_lock); 496 } 497 498 499 /* 500 * get client/port of the specified MIDI device 501 */ 502 void 503 snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr) 504 { 505 struct seq_oss_midi *mdev; 506 507 mdev = get_mididev(dp, dev); 508 if (!mdev) 509 return; 510 addr->client = mdev->client; 511 addr->port = mdev->port; 512 snd_use_lock_free(&mdev->use_lock); 513 } 514 515 516 /* 517 * input callback - this can be atomic 518 */ 519 int 520 snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data) 521 { 522 struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data; 523 struct seq_oss_midi *mdev; 524 int rc; 525 526 if (dp->readq == NULL) 527 return 0; 528 mdev = find_slot(ev->source.client, ev->source.port); 529 if (!mdev) 530 return 0; 531 if (! (mdev->opened & PERM_READ)) { 532 snd_use_lock_free(&mdev->use_lock); 533 return 0; 534 } 535 536 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) 537 rc = send_synth_event(dp, ev, mdev->seq_device); 538 else 539 rc = send_midi_event(dp, ev, mdev); 540 541 snd_use_lock_free(&mdev->use_lock); 542 return rc; 543 } 544 545 /* 546 * convert ALSA sequencer event to OSS synth event 547 */ 548 static int 549 send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev) 550 { 551 union evrec ossev; 552 553 memset(&ossev, 0, sizeof(ossev)); 554 555 switch (ev->type) { 556 case SNDRV_SEQ_EVENT_NOTEON: 557 ossev.v.cmd = MIDI_NOTEON; break; 558 case SNDRV_SEQ_EVENT_NOTEOFF: 559 ossev.v.cmd = MIDI_NOTEOFF; break; 560 case SNDRV_SEQ_EVENT_KEYPRESS: 561 ossev.v.cmd = MIDI_KEY_PRESSURE; break; 562 case SNDRV_SEQ_EVENT_CONTROLLER: 563 ossev.l.cmd = MIDI_CTL_CHANGE; break; 564 case SNDRV_SEQ_EVENT_PGMCHANGE: 565 ossev.l.cmd = MIDI_PGM_CHANGE; break; 566 case SNDRV_SEQ_EVENT_CHANPRESS: 567 ossev.l.cmd = MIDI_CHN_PRESSURE; break; 568 case SNDRV_SEQ_EVENT_PITCHBEND: 569 ossev.l.cmd = MIDI_PITCH_BEND; break; 570 default: 571 return 0; /* not supported */ 572 } 573 574 ossev.v.dev = dev; 575 576 switch (ev->type) { 577 case SNDRV_SEQ_EVENT_NOTEON: 578 case SNDRV_SEQ_EVENT_NOTEOFF: 579 case SNDRV_SEQ_EVENT_KEYPRESS: 580 ossev.v.code = EV_CHN_VOICE; 581 ossev.v.note = ev->data.note.note; 582 ossev.v.parm = ev->data.note.velocity; 583 ossev.v.chn = ev->data.note.channel; 584 break; 585 case SNDRV_SEQ_EVENT_CONTROLLER: 586 case SNDRV_SEQ_EVENT_PGMCHANGE: 587 case SNDRV_SEQ_EVENT_CHANPRESS: 588 ossev.l.code = EV_CHN_COMMON; 589 ossev.l.p1 = ev->data.control.param; 590 ossev.l.val = ev->data.control.value; 591 ossev.l.chn = ev->data.control.channel; 592 break; 593 case SNDRV_SEQ_EVENT_PITCHBEND: 594 ossev.l.code = EV_CHN_COMMON; 595 ossev.l.val = ev->data.control.value + 8192; 596 ossev.l.chn = ev->data.control.channel; 597 break; 598 } 599 600 snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode); 601 snd_seq_oss_readq_put_event(dp->readq, &ossev); 602 603 return 0; 604 } 605 606 /* 607 * decode event and send MIDI bytes to read queue 608 */ 609 static int 610 send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev) 611 { 612 char msg[32]; 613 int len; 614 615 snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode); 616 if (!dp->timer->running) 617 len = snd_seq_oss_timer_start(dp->timer); 618 if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { 619 snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev); 620 snd_midi_event_reset_decode(mdev->coder); 621 } else { 622 len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev); 623 if (len > 0) 624 snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len); 625 } 626 627 return 0; 628 } 629 630 631 /* 632 * dump midi data 633 * return 0 : enqueued 634 * non-zero : invalid - ignored 635 */ 636 int 637 snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev) 638 { 639 struct seq_oss_midi *mdev; 640 641 mdev = get_mididev(dp, dev); 642 if (!mdev) 643 return -ENODEV; 644 if (snd_midi_event_encode_byte(mdev->coder, c, ev)) { 645 snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port); 646 snd_use_lock_free(&mdev->use_lock); 647 return 0; 648 } 649 snd_use_lock_free(&mdev->use_lock); 650 return -EINVAL; 651 } 652 653 /* 654 * create OSS compatible midi_info record 655 */ 656 int 657 snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf) 658 { 659 struct seq_oss_midi *mdev; 660 661 mdev = get_mididev(dp, dev); 662 if (!mdev) 663 return -ENXIO; 664 inf->device = dev; 665 inf->dev_type = 0; /* FIXME: ?? */ 666 inf->capabilities = 0; /* FIXME: ?? */ 667 strscpy(inf->name, mdev->name, sizeof(inf->name)); 668 snd_use_lock_free(&mdev->use_lock); 669 return 0; 670 } 671 672 673 #ifdef CONFIG_SND_PROC_FS 674 /* 675 * proc interface 676 */ 677 static char * 678 capmode_str(int val) 679 { 680 val &= PERM_READ|PERM_WRITE; 681 if (val == (PERM_READ|PERM_WRITE)) 682 return "read/write"; 683 else if (val == PERM_READ) 684 return "read"; 685 else if (val == PERM_WRITE) 686 return "write"; 687 else 688 return "none"; 689 } 690 691 void 692 snd_seq_oss_midi_info_read(struct snd_info_buffer *buf) 693 { 694 int i; 695 struct seq_oss_midi *mdev; 696 697 snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs); 698 for (i = 0; i < max_midi_devs; i++) { 699 snd_iprintf(buf, "\nmidi %d: ", i); 700 mdev = get_mdev(i); 701 if (mdev == NULL) { 702 snd_iprintf(buf, "*empty*\n"); 703 continue; 704 } 705 snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name, 706 mdev->client, mdev->port); 707 snd_iprintf(buf, " capability %s / opened %s\n", 708 capmode_str(mdev->flags), 709 capmode_str(mdev->opened)); 710 snd_use_lock_free(&mdev->use_lock); 711 } 712 } 713 #endif /* CONFIG_SND_PROC_FS */ 714
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.