1/* 2 * Generic Instrument routines for ALSA sequencer 3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * 19 */ 20 21#include <sound/driver.h> 22#include <linux/init.h> 23#include <linux/slab.h> 24#include <sound/core.h> 25#include "seq_clientmgr.h" 26#include <sound/seq_instr.h> 27#include <sound/initval.h> 28 29MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); 30MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library."); 31MODULE_LICENSE("GPL"); 32 33 34static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list) 35{ 36 if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { 37 spin_lock_irqsave(&list->ops_lock, list->ops_flags); 38 } else { 39 mutex_lock(&list->ops_mutex); 40 } 41} 42 43static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list) 44{ 45 if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { 46 spin_unlock_irqrestore(&list->ops_lock, list->ops_flags); 47 } else { 48 mutex_unlock(&list->ops_mutex); 49 } 50} 51 52static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic) 53{ 54 struct snd_seq_kinstr *instr; 55 56 instr = kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); 57 if (instr == NULL) 58 return NULL; 59 instr->add_len = add_len; 60 return instr; 61} 62 63static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic) 64{ 65 int result = 0; 66 67 if (instr == NULL) 68 return -EINVAL; 69 if (instr->ops && instr->ops->remove) 70 result = instr->ops->remove(instr->ops->private_data, instr, 1); 71 if (!result) 72 kfree(instr); 73 return result; 74} 75 76struct snd_seq_kinstr_list *snd_seq_instr_list_new(void) 77{ 78 struct snd_seq_kinstr_list *list; 79 80 list = kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL); 81 if (list == NULL) 82 return NULL; 83 spin_lock_init(&list->lock); 84 spin_lock_init(&list->ops_lock); 85 mutex_init(&list->ops_mutex); 86 list->owner = -1; 87 return list; 88} 89 90void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr) 91{ 92 struct snd_seq_kinstr_list *list; 93 struct snd_seq_kinstr *instr; 94 struct snd_seq_kcluster *cluster; 95 int idx; 96 unsigned long flags; 97 98 if (list_ptr == NULL) 99 return; 100 list = *list_ptr; 101 *list_ptr = NULL; 102 if (list == NULL) 103 return; 104 105 for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { 106 while ((instr = list->hash[idx]) != NULL) { 107 list->hash[idx] = instr->next; 108 list->count--; 109 spin_lock_irqsave(&list->lock, flags); 110 while (instr->use) { 111 spin_unlock_irqrestore(&list->lock, flags); 112 schedule_timeout_interruptible(1); 113 spin_lock_irqsave(&list->lock, flags); 114 } 115 spin_unlock_irqrestore(&list->lock, flags); 116 if (snd_seq_instr_free(instr, 0)<0) 117 snd_printk(KERN_WARNING "instrument free problem\n"); 118 } 119 while ((cluster = list->chash[idx]) != NULL) { 120 list->chash[idx] = cluster->next; 121 list->ccount--; 122 kfree(cluster); 123 } 124 } 125 kfree(list); 126} 127 128static int instr_free_compare(struct snd_seq_kinstr *instr, 129 struct snd_seq_instr_header *ifree, 130 unsigned int client) 131{ 132 switch (ifree->cmd) { 133 case SNDRV_SEQ_INSTR_FREE_CMD_ALL: 134 /* all, except private for other clients */ 135 if ((instr->instr.std & 0xff000000) == 0) 136 return 0; 137 if (((instr->instr.std >> 24) & 0xff) == client) 138 return 0; 139 return 1; 140 case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE: 141 /* all my private instruments */ 142 if ((instr->instr.std & 0xff000000) == 0) 143 return 1; 144 if (((instr->instr.std >> 24) & 0xff) == client) 145 return 0; 146 return 1; 147 case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER: 148 /* all my private instruments */ 149 if ((instr->instr.std & 0xff000000) == 0) { 150 if (instr->instr.cluster == ifree->id.cluster) 151 return 0; 152 return 1; 153 } 154 if (((instr->instr.std >> 24) & 0xff) == client) { 155 if (instr->instr.cluster == ifree->id.cluster) 156 return 0; 157 } 158 return 1; 159 } 160 return 1; 161} 162 163int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list, 164 struct snd_seq_instr_header *ifree, 165 int client, 166 int atomic) 167{ 168 struct snd_seq_kinstr *instr, *prev, *next, *flist; 169 int idx; 170 unsigned long flags; 171 172 snd_instr_lock_ops(list); 173 for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { 174 spin_lock_irqsave(&list->lock, flags); 175 instr = list->hash[idx]; 176 prev = flist = NULL; 177 while (instr) { 178 while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) { 179 prev = instr; 180 instr = instr->next; 181 } 182 if (instr == NULL) 183 continue; 184 if (instr->ops && instr->ops->notify) 185 instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); 186 next = instr->next; 187 if (prev == NULL) { 188 list->hash[idx] = next; 189 } else { 190 prev->next = next; 191 } 192 list->count--; 193 instr->next = flist; 194 flist = instr; 195 instr = next; 196 } 197 spin_unlock_irqrestore(&list->lock, flags); 198 while (flist) { 199 instr = flist; 200 flist = instr->next; 201 while (instr->use) 202 schedule_timeout_interruptible(1); 203 if (snd_seq_instr_free(instr, atomic)<0) 204 snd_printk(KERN_WARNING "instrument free problem\n"); 205 instr = next; 206 } 207 } 208 snd_instr_unlock_ops(list); 209 return 0; 210} 211 212static int compute_hash_instr_key(struct snd_seq_instr *instr) 213{ 214 int result; 215 216 result = instr->bank | (instr->prg << 16); 217 result += result >> 24; 218 result += result >> 16; 219 result += result >> 8; 220 return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); 221} 222 223 224static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact) 225{ 226 if (exact) { 227 if (i1->cluster != i2->cluster || 228 i1->bank != i2->bank || 229 i1->prg != i2->prg) 230 return 1; 231 if ((i1->std & 0xff000000) != (i2->std & 0xff000000)) 232 return 1; 233 if (!(i1->std & i2->std)) 234 return 1; 235 return 0; 236 } else { 237 unsigned int client_check; 238 239 if (i2->cluster && i1->cluster != i2->cluster) 240 return 1; 241 client_check = i2->std & 0xff000000; 242 if (client_check) { 243 if ((i1->std & 0xff000000) != client_check) 244 return 1; 245 } else { 246 if ((i1->std & i2->std) != i2->std) 247 return 1; 248 } 249 return i1->bank != i2->bank || i1->prg != i2->prg; 250 } 251} 252 253struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list, 254 struct snd_seq_instr *instr, 255 int exact, 256 int follow_alias) 257{ 258 unsigned long flags; 259 int depth = 0; 260 struct snd_seq_kinstr *result; 261 262 if (list == NULL || instr == NULL) 263 return NULL; 264 spin_lock_irqsave(&list->lock, flags); 265 __again: 266 result = list->hash[compute_hash_instr_key(instr)]; 267 while (result) { 268 if (!compare_instr(&result->instr, instr, exact)) { 269 if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) { 270 instr = (struct snd_seq_instr *)KINSTR_DATA(result); 271 if (++depth > 10) 272 goto __not_found; 273 goto __again; 274 } 275 result->use++; 276 spin_unlock_irqrestore(&list->lock, flags); 277 return result; 278 } 279 result = result->next; 280 } 281 __not_found: 282 spin_unlock_irqrestore(&list->lock, flags); 283 return NULL; 284} 285 286void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list, 287 struct snd_seq_kinstr *instr) 288{ 289 unsigned long flags; 290 291 if (list == NULL || instr == NULL) 292 return; 293 spin_lock_irqsave(&list->lock, flags); 294 if (instr->use <= 0) { 295 snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name); 296 } else { 297 instr->use--; 298 } 299 spin_unlock_irqrestore(&list->lock, flags); 300} 301 302static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops, 303 char *instr_type) 304{ 305 while (ops) { 306 if (!strcmp(ops->instr_type, instr_type)) 307 return ops; 308 ops = ops->next; 309 } 310 return NULL; 311} 312 313static int instr_result(struct snd_seq_event *ev, 314 int type, int result, 315 int atomic) 316{ 317 struct snd_seq_event sev; 318 319 memset(&sev, 0, sizeof(sev)); 320 sev.type = SNDRV_SEQ_EVENT_RESULT; 321 sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED | 322 SNDRV_SEQ_PRIORITY_NORMAL; 323 sev.source = ev->dest; 324 sev.dest = ev->source; 325 sev.data.result.event = type; 326 sev.data.result.result = result; 327 return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0); 328} 329 330static int instr_begin(struct snd_seq_kinstr_ops *ops, 331 struct snd_seq_kinstr_list *list, 332 struct snd_seq_event *ev, 333 int atomic, int hop) 334{ 335 unsigned long flags; 336 337 spin_lock_irqsave(&list->lock, flags); 338 if (list->owner >= 0 && list->owner != ev->source.client) { 339 spin_unlock_irqrestore(&list->lock, flags); 340 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic); 341 } 342 list->owner = ev->source.client; 343 spin_unlock_irqrestore(&list->lock, flags); 344 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic); 345} 346 347static int instr_end(struct snd_seq_kinstr_ops *ops, 348 struct snd_seq_kinstr_list *list, 349 struct snd_seq_event *ev, 350 int atomic, int hop) 351{ 352 unsigned long flags; 353 354 /* TODO: timeout handling */ 355 spin_lock_irqsave(&list->lock, flags); 356 if (list->owner == ev->source.client) { 357 list->owner = -1; 358 spin_unlock_irqrestore(&list->lock, flags); 359 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic); 360 } 361 spin_unlock_irqrestore(&list->lock, flags); 362 return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic); 363} 364 365static int instr_info(struct snd_seq_kinstr_ops *ops, 366 struct snd_seq_kinstr_list *list, 367 struct snd_seq_event *ev, 368 int atomic, int hop) 369{ 370 return -ENXIO; 371} 372 373static int instr_format_info(struct snd_seq_kinstr_ops *ops, 374 struct snd_seq_kinstr_list *list, 375 struct snd_seq_event *ev, 376 int atomic, int hop) 377{ 378 return -ENXIO; 379} 380 381static int instr_reset(struct snd_seq_kinstr_ops *ops, 382 struct snd_seq_kinstr_list *list, 383 struct snd_seq_event *ev, 384 int atomic, int hop) 385{ 386 return -ENXIO; 387} 388 389static int instr_status(struct snd_seq_kinstr_ops *ops, 390 struct snd_seq_kinstr_list *list, 391 struct snd_seq_event *ev, 392 int atomic, int hop) 393{ 394 return -ENXIO; 395} 396 397static int instr_put(struct snd_seq_kinstr_ops *ops, 398 struct snd_seq_kinstr_list *list, 399 struct snd_seq_event *ev, 400 int atomic, int hop) 401{ 402 unsigned long flags; 403 struct snd_seq_instr_header put; 404 struct snd_seq_kinstr *instr; 405 int result = -EINVAL, len, key; 406 407 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) 408 goto __return; 409 410 if (ev->data.ext.len < sizeof(struct snd_seq_instr_header)) 411 goto __return; 412 if (copy_from_user(&put, (void __user *)ev->data.ext.ptr, 413 sizeof(struct snd_seq_instr_header))) { 414 result = -EFAULT; 415 goto __return; 416 } 417 snd_instr_lock_ops(list); 418 if (put.id.instr.std & 0xff000000) { /* private instrument */ 419 put.id.instr.std &= 0x00ffffff; 420 put.id.instr.std |= (unsigned int)ev->source.client << 24; 421 } 422 if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) { 423 snd_seq_instr_free_use(list, instr); 424 snd_instr_unlock_ops(list); 425 result = -EBUSY; 426 goto __return; 427 } 428 ops = instr_ops(ops, put.data.data.format); 429 if (ops == NULL) { 430 snd_instr_unlock_ops(list); 431 goto __return; 432 } 433 len = ops->add_len; 434 if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS) 435 len = sizeof(struct snd_seq_instr); 436 instr = snd_seq_instr_new(len, atomic); 437 if (instr == NULL) { 438 snd_instr_unlock_ops(list); 439 result = -ENOMEM; 440 goto __return; 441 } 442 instr->ops = ops; 443 instr->instr = put.id.instr; 444 strlcpy(instr->name, put.data.name, sizeof(instr->name)); 445 instr->type = put.data.type; 446 if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) { 447 result = ops->put(ops->private_data, 448 instr, 449 (void __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header), 450 ev->data.ext.len - sizeof(struct snd_seq_instr_header), 451 atomic, 452 put.cmd); 453 if (result < 0) { 454 snd_seq_instr_free(instr, atomic); 455 snd_instr_unlock_ops(list); 456 goto __return; 457 } 458 } 459 key = compute_hash_instr_key(&instr->instr); 460 spin_lock_irqsave(&list->lock, flags); 461 instr->next = list->hash[key]; 462 list->hash[key] = instr; 463 list->count++; 464 spin_unlock_irqrestore(&list->lock, flags); 465 snd_instr_unlock_ops(list); 466 result = 0; 467 __return: 468 instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic); 469 return result; 470} 471 472static int instr_get(struct snd_seq_kinstr_ops *ops, 473 struct snd_seq_kinstr_list *list, 474 struct snd_seq_event *ev, 475 int atomic, int hop) 476{ 477 return -ENXIO; 478} 479 480static int instr_free(struct snd_seq_kinstr_ops *ops, 481 struct snd_seq_kinstr_list *list, 482 struct snd_seq_event *ev, 483 int atomic, int hop) 484{ 485 struct snd_seq_instr_header ifree; 486 struct snd_seq_kinstr *instr, *prev; 487 int result = -EINVAL; 488 unsigned long flags; 489 unsigned int hash; 490 491 if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) 492 goto __return; 493 494 if (ev->data.ext.len < sizeof(struct snd_seq_instr_header)) 495 goto __return; 496 if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr, 497 sizeof(struct snd_seq_instr_header))) { 498 result = -EFAULT; 499 goto __return; 500 } 501 if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL || 502 ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE || 503 ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) { 504 result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic); 505 goto __return; 506 } 507 if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) { 508 if (ifree.id.instr.std & 0xff000000) { 509 ifree.id.instr.std &= 0x00ffffff; 510 ifree.id.instr.std |= (unsigned int)ev->source.client << 24; 511 } 512 hash = compute_hash_instr_key(&ifree.id.instr); 513 snd_instr_lock_ops(list); 514 spin_lock_irqsave(&list->lock, flags); 515 instr = list->hash[hash]; 516 prev = NULL; 517 while (instr) { 518 if (!compare_instr(&instr->instr, &ifree.id.instr, 1)) 519 goto __free_single; 520 prev = instr; 521 instr = instr->next; 522 } 523 result = -ENOENT; 524 spin_unlock_irqrestore(&list->lock, flags); 525 snd_instr_unlock_ops(list); 526 goto __return; 527 528 __free_single: 529 if (prev) { 530 prev->next = instr->next; 531 } else { 532 list->hash[hash] = instr->next; 533 } 534 if (instr->ops && instr->ops->notify) 535 instr->ops->notify(instr->ops->private_data, instr, 536 SNDRV_SEQ_INSTR_NOTIFY_REMOVE); 537 while (instr->use) { 538 spin_unlock_irqrestore(&list->lock, flags); 539 schedule_timeout_interruptible(1); 540 spin_lock_irqsave(&list->lock, flags); 541 } 542 spin_unlock_irqrestore(&list->lock, flags); 543 result = snd_seq_instr_free(instr, atomic); 544 snd_instr_unlock_ops(list); 545 goto __return; 546 } 547 548 __return: 549 instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic); 550 return result; 551} 552 553static int instr_list(struct snd_seq_kinstr_ops *ops, 554 struct snd_seq_kinstr_list *list, 555 struct snd_seq_event *ev, 556 int atomic, int hop) 557{ 558 return -ENXIO; 559} 560 561static int instr_cluster(struct snd_seq_kinstr_ops *ops, 562 struct snd_seq_kinstr_list *list, 563 struct snd_seq_event *ev, 564 int atomic, int hop) 565{ 566 return -ENXIO; 567} 568 569int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops, 570 struct snd_seq_kinstr_list *list, 571 struct snd_seq_event *ev, 572 int client, 573 int atomic, 574 int hop) 575{ 576 int direct = 0; 577 578 snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL); 579 if (snd_seq_ev_is_direct(ev)) { 580 direct = 1; 581 switch (ev->type) { 582 case SNDRV_SEQ_EVENT_INSTR_BEGIN: 583 return instr_begin(ops, list, ev, atomic, hop); 584 case SNDRV_SEQ_EVENT_INSTR_END: 585 return instr_end(ops, list, ev, atomic, hop); 586 } 587 } 588 if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct) 589 return -EINVAL; 590 switch (ev->type) { 591 case SNDRV_SEQ_EVENT_INSTR_INFO: 592 return instr_info(ops, list, ev, atomic, hop); 593 case SNDRV_SEQ_EVENT_INSTR_FINFO: 594 return instr_format_info(ops, list, ev, atomic, hop); 595 case SNDRV_SEQ_EVENT_INSTR_RESET: 596 return instr_reset(ops, list, ev, atomic, hop); 597 case SNDRV_SEQ_EVENT_INSTR_STATUS: 598 return instr_status(ops, list, ev, atomic, hop); 599 case SNDRV_SEQ_EVENT_INSTR_PUT: 600 return instr_put(ops, list, ev, atomic, hop); 601 case SNDRV_SEQ_EVENT_INSTR_GET: 602 return instr_get(ops, list, ev, atomic, hop); 603 case SNDRV_SEQ_EVENT_INSTR_FREE: 604 return instr_free(ops, list, ev, atomic, hop); 605 case SNDRV_SEQ_EVENT_INSTR_LIST: 606 return instr_list(ops, list, ev, atomic, hop); 607 case SNDRV_SEQ_EVENT_INSTR_CLUSTER: 608 return instr_cluster(ops, list, ev, atomic, hop); 609 } 610 return -EINVAL; 611} 612 613/* 614 * Init part 615 */ 616 617static int __init alsa_seq_instr_init(void) 618{ 619 return 0; 620} 621 622static void __exit alsa_seq_instr_exit(void) 623{ 624} 625 626module_init(alsa_seq_instr_init) 627module_exit(alsa_seq_instr_exit) 628 629EXPORT_SYMBOL(snd_seq_instr_list_new); 630EXPORT_SYMBOL(snd_seq_instr_list_free); 631EXPORT_SYMBOL(snd_seq_instr_list_free_cond); 632EXPORT_SYMBOL(snd_seq_instr_find); 633EXPORT_SYMBOL(snd_seq_instr_free_use); 634EXPORT_SYMBOL(snd_seq_instr_event); 635