Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.12 555 lines 13 kB view raw
1/* 2 * OSS compatible sequencer driver 3 * 4 * open/close and reset interface 5 * 6 * Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23#include "seq_oss_device.h" 24#include "seq_oss_synth.h" 25#include "seq_oss_midi.h" 26#include "seq_oss_writeq.h" 27#include "seq_oss_readq.h" 28#include "seq_oss_timer.h" 29#include "seq_oss_event.h" 30#include <linux/init.h> 31#include <linux/moduleparam.h> 32 33/* 34 * common variables 35 */ 36static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN; 37module_param(maxqlen, int, 0444); 38MODULE_PARM_DESC(maxqlen, "maximum queue length"); 39 40static int system_client = -1; /* ALSA sequencer client number */ 41static int system_port = -1; 42 43static int num_clients; 44static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS]; 45 46 47/* 48 * prototypes 49 */ 50static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); 51static int translate_mode(struct file *file); 52static int create_port(seq_oss_devinfo_t *dp); 53static int delete_port(seq_oss_devinfo_t *dp); 54static int alloc_seq_queue(seq_oss_devinfo_t *dp); 55static int delete_seq_queue(int queue); 56static void free_devinfo(void *private); 57 58#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec) 59 60 61/* 62 * create sequencer client for OSS sequencer 63 */ 64int __init 65snd_seq_oss_create_client(void) 66{ 67 int rc; 68 snd_seq_client_callback_t callback; 69 snd_seq_client_info_t *info; 70 snd_seq_port_info_t *port; 71 snd_seq_port_callback_t port_callback; 72 73 info = kmalloc(sizeof(*info), GFP_KERNEL); 74 port = kmalloc(sizeof(*port), GFP_KERNEL); 75 if (!info || !port) { 76 rc = -ENOMEM; 77 goto __error; 78 } 79 80 /* create ALSA client */ 81 memset(&callback, 0, sizeof(callback)); 82 83 callback.private_data = NULL; 84 callback.allow_input = 1; 85 callback.allow_output = 1; 86 87 rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback); 88 if (rc < 0) 89 goto __error; 90 91 system_client = rc; 92 debug_printk(("new client = %d\n", rc)); 93 94 /* set client information */ 95 memset(info, 0, sizeof(*info)); 96 info->client = system_client; 97 info->type = KERNEL_CLIENT; 98 strcpy(info->name, "OSS sequencer"); 99 100 rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info); 101 102 /* look up midi devices */ 103 snd_seq_oss_midi_lookup_ports(system_client); 104 105 /* create annoucement receiver port */ 106 memset(port, 0, sizeof(*port)); 107 strcpy(port->name, "Receiver"); 108 port->addr.client = system_client; 109 port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ 110 port->type = 0; 111 112 memset(&port_callback, 0, sizeof(port_callback)); 113 /* don't set port_callback.owner here. otherwise the module counter 114 * is incremented and we can no longer release the module.. 115 */ 116 port_callback.event_input = receive_announce; 117 port->kernel = &port_callback; 118 119 call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port); 120 if ((system_port = port->addr.port) >= 0) { 121 snd_seq_port_subscribe_t subs; 122 123 memset(&subs, 0, sizeof(subs)); 124 subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; 125 subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; 126 subs.dest.client = system_client; 127 subs.dest.port = system_port; 128 call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs); 129 } 130 rc = 0; 131 132 __error: 133 kfree(port); 134 kfree(info); 135 return rc; 136} 137 138 139/* 140 * receive annoucement from system port, and check the midi device 141 */ 142static int 143receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop) 144{ 145 snd_seq_port_info_t pinfo; 146 147 if (atomic) 148 return 0; /* it must not happen */ 149 150 switch (ev->type) { 151 case SNDRV_SEQ_EVENT_PORT_START: 152 case SNDRV_SEQ_EVENT_PORT_CHANGE: 153 if (ev->data.addr.client == system_client) 154 break; /* ignore myself */ 155 memset(&pinfo, 0, sizeof(pinfo)); 156 pinfo.addr = ev->data.addr; 157 if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0) 158 snd_seq_oss_midi_check_new_port(&pinfo); 159 break; 160 161 case SNDRV_SEQ_EVENT_PORT_EXIT: 162 if (ev->data.addr.client == system_client) 163 break; /* ignore myself */ 164 snd_seq_oss_midi_check_exit_port(ev->data.addr.client, 165 ev->data.addr.port); 166 break; 167 } 168 return 0; 169} 170 171 172/* 173 * delete OSS sequencer client 174 */ 175int 176snd_seq_oss_delete_client(void) 177{ 178 if (system_client >= 0) 179 snd_seq_delete_kernel_client(system_client); 180 181 snd_seq_oss_midi_clear_all(); 182 183 return 0; 184} 185 186 187/* 188 * open sequencer device 189 */ 190int 191snd_seq_oss_open(struct file *file, int level) 192{ 193 int i, rc; 194 seq_oss_devinfo_t *dp; 195 196 if ((dp = kcalloc(1, sizeof(*dp), GFP_KERNEL)) == NULL) { 197 snd_printk(KERN_ERR "can't malloc device info\n"); 198 return -ENOMEM; 199 } 200 debug_printk(("oss_open: dp = %p\n", dp)); 201 202 for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) { 203 if (client_table[i] == NULL) 204 break; 205 } 206 if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { 207 snd_printk(KERN_ERR "too many applications\n"); 208 kfree(dp); 209 return -ENOMEM; 210 } 211 212 dp->index = i; 213 dp->cseq = system_client; 214 dp->port = -1; 215 dp->queue = -1; 216 dp->readq = NULL; 217 dp->writeq = NULL; 218 219 /* look up synth and midi devices */ 220 snd_seq_oss_synth_setup(dp); 221 snd_seq_oss_midi_setup(dp); 222 223 if (dp->synth_opened == 0 && dp->max_mididev == 0) { 224 /* snd_printk(KERN_ERR "no device found\n"); */ 225 rc = -ENODEV; 226 goto _error; 227 } 228 229 /* create port */ 230 debug_printk(("create new port\n")); 231 if ((rc = create_port(dp)) < 0) { 232 snd_printk(KERN_ERR "can't create port\n"); 233 goto _error; 234 } 235 236 /* allocate queue */ 237 debug_printk(("allocate queue\n")); 238 if ((rc = alloc_seq_queue(dp)) < 0) 239 goto _error; 240 241 /* set address */ 242 dp->addr.client = dp->cseq; 243 dp->addr.port = dp->port; 244 /*dp->addr.queue = dp->queue;*/ 245 /*dp->addr.channel = 0;*/ 246 247 dp->seq_mode = level; 248 249 /* set up file mode */ 250 dp->file_mode = translate_mode(file); 251 252 /* initialize read queue */ 253 debug_printk(("initialize read queue\n")); 254 if (is_read_mode(dp->file_mode)) { 255 if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) { 256 rc = -ENOMEM; 257 goto _error; 258 } 259 } 260 261 /* initialize write queue */ 262 debug_printk(("initialize write queue\n")); 263 if (is_write_mode(dp->file_mode)) { 264 dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen); 265 if (dp->writeq == NULL) { 266 rc = -ENOMEM; 267 goto _error; 268 } 269 } 270 271 /* initialize timer */ 272 debug_printk(("initialize timer\n")); 273 if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) { 274 snd_printk(KERN_ERR "can't alloc timer\n"); 275 rc = -ENOMEM; 276 goto _error; 277 } 278 debug_printk(("timer initialized\n")); 279 280 /* set private data pointer */ 281 file->private_data = dp; 282 283 /* set up for mode2 */ 284 if (level == SNDRV_SEQ_OSS_MODE_MUSIC) 285 snd_seq_oss_synth_setup_midi(dp); 286 else if (is_read_mode(dp->file_mode)) 287 snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ); 288 289 client_table[dp->index] = dp; 290 num_clients++; 291 292 debug_printk(("open done\n")); 293 return 0; 294 295 _error: 296 snd_seq_oss_synth_cleanup(dp); 297 snd_seq_oss_midi_cleanup(dp); 298 i = dp->queue; 299 delete_port(dp); 300 delete_seq_queue(i); 301 302 return rc; 303} 304 305/* 306 * translate file flags to private mode 307 */ 308static int 309translate_mode(struct file *file) 310{ 311 int file_mode = 0; 312 if ((file->f_flags & O_ACCMODE) != O_RDONLY) 313 file_mode |= SNDRV_SEQ_OSS_FILE_WRITE; 314 if ((file->f_flags & O_ACCMODE) != O_WRONLY) 315 file_mode |= SNDRV_SEQ_OSS_FILE_READ; 316 if (file->f_flags & O_NONBLOCK) 317 file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK; 318 return file_mode; 319} 320 321 322/* 323 * create sequencer port 324 */ 325static int 326create_port(seq_oss_devinfo_t *dp) 327{ 328 int rc; 329 snd_seq_port_info_t port; 330 snd_seq_port_callback_t callback; 331 332 memset(&port, 0, sizeof(port)); 333 port.addr.client = dp->cseq; 334 sprintf(port.name, "Sequencer-%d", dp->index); 335 port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */ 336 port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; 337 port.midi_channels = 128; 338 port.synth_voices = 128; 339 340 memset(&callback, 0, sizeof(callback)); 341 callback.owner = THIS_MODULE; 342 callback.private_data = dp; 343 callback.event_input = snd_seq_oss_event_input; 344 callback.private_free = free_devinfo; 345 port.kernel = &callback; 346 347 rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port); 348 if (rc < 0) 349 return rc; 350 351 dp->port = port.addr.port; 352 debug_printk(("new port = %d\n", port.addr.port)); 353 354 return 0; 355} 356 357/* 358 * delete ALSA port 359 */ 360static int 361delete_port(seq_oss_devinfo_t *dp) 362{ 363 if (dp->port < 0) 364 return 0; 365 366 debug_printk(("delete_port %i\n", dp->port)); 367 return snd_seq_event_port_detach(dp->cseq, dp->port); 368} 369 370/* 371 * allocate a queue 372 */ 373static int 374alloc_seq_queue(seq_oss_devinfo_t *dp) 375{ 376 snd_seq_queue_info_t qinfo; 377 int rc; 378 379 memset(&qinfo, 0, sizeof(qinfo)); 380 qinfo.owner = system_client; 381 qinfo.locked = 1; 382 strcpy(qinfo.name, "OSS Sequencer Emulation"); 383 if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0) 384 return rc; 385 dp->queue = qinfo.queue; 386 return 0; 387} 388 389/* 390 * release queue 391 */ 392static int 393delete_seq_queue(int queue) 394{ 395 snd_seq_queue_info_t qinfo; 396 int rc; 397 398 if (queue < 0) 399 return 0; 400 memset(&qinfo, 0, sizeof(qinfo)); 401 qinfo.queue = queue; 402 rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo); 403 if (rc < 0) 404 printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc); 405 return rc; 406} 407 408 409/* 410 * free device informations - private_free callback of port 411 */ 412static void 413free_devinfo(void *private) 414{ 415 seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private; 416 417 if (dp->timer) 418 snd_seq_oss_timer_delete(dp->timer); 419 420 if (dp->writeq) 421 snd_seq_oss_writeq_delete(dp->writeq); 422 423 if (dp->readq) 424 snd_seq_oss_readq_delete(dp->readq); 425 426 kfree(dp); 427} 428 429 430/* 431 * close sequencer device 432 */ 433void 434snd_seq_oss_release(seq_oss_devinfo_t *dp) 435{ 436 int queue; 437 438 client_table[dp->index] = NULL; 439 num_clients--; 440 441 debug_printk(("resetting..\n")); 442 snd_seq_oss_reset(dp); 443 444 debug_printk(("cleaning up..\n")); 445 snd_seq_oss_synth_cleanup(dp); 446 snd_seq_oss_midi_cleanup(dp); 447 448 /* clear slot */ 449 debug_printk(("releasing resource..\n")); 450 queue = dp->queue; 451 if (dp->port >= 0) 452 delete_port(dp); 453 delete_seq_queue(queue); 454 455 debug_printk(("release done\n")); 456} 457 458 459/* 460 * Wait until the queue is empty (if we don't have nonblock) 461 */ 462void 463snd_seq_oss_drain_write(seq_oss_devinfo_t *dp) 464{ 465 if (! dp->timer->running) 466 return; 467 if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) && 468 dp->writeq) { 469 debug_printk(("syncing..\n")); 470 while (snd_seq_oss_writeq_sync(dp->writeq)) 471 ; 472 } 473} 474 475 476/* 477 * reset sequencer devices 478 */ 479void 480snd_seq_oss_reset(seq_oss_devinfo_t *dp) 481{ 482 int i; 483 484 /* reset all synth devices */ 485 for (i = 0; i < dp->max_synthdev; i++) 486 snd_seq_oss_synth_reset(dp, i); 487 488 /* reset all midi devices */ 489 if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) { 490 for (i = 0; i < dp->max_mididev; i++) 491 snd_seq_oss_midi_reset(dp, i); 492 } 493 494 /* remove queues */ 495 if (dp->readq) 496 snd_seq_oss_readq_clear(dp->readq); 497 if (dp->writeq) 498 snd_seq_oss_writeq_clear(dp->writeq); 499 500 /* reset timer */ 501 snd_seq_oss_timer_stop(dp->timer); 502} 503 504 505/* 506 * misc. functions for proc interface 507 */ 508char * 509enabled_str(int bool) 510{ 511 return bool ? "enabled" : "disabled"; 512} 513 514static char * 515filemode_str(int val) 516{ 517 static char *str[] = { 518 "none", "read", "write", "read/write", 519 }; 520 return str[val & SNDRV_SEQ_OSS_FILE_ACMODE]; 521} 522 523 524/* 525 * proc interface 526 */ 527void 528snd_seq_oss_system_info_read(snd_info_buffer_t *buf) 529{ 530 int i; 531 seq_oss_devinfo_t *dp; 532 533 snd_iprintf(buf, "ALSA client number %d\n", system_client); 534 snd_iprintf(buf, "ALSA receiver port %d\n", system_port); 535 536 snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients); 537 for (i = 0; i < num_clients; i++) { 538 snd_iprintf(buf, "\nApplication %d: ", i); 539 if ((dp = client_table[i]) == NULL) { 540 snd_iprintf(buf, "*empty*\n"); 541 continue; 542 } 543 snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue); 544 snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n", 545 (dp->seq_mode ? "music" : "synth"), 546 filemode_str(dp->file_mode)); 547 if (dp->seq_mode) 548 snd_iprintf(buf, " timer tempo = %d, timebase = %d\n", 549 dp->timer->oss_tempo, dp->timer->oss_timebase); 550 snd_iprintf(buf, " max queue length %d\n", maxqlen); 551 if (is_read_mode(dp->file_mode) && dp->readq) 552 snd_seq_oss_readq_info_read(dp->readq, buf); 553 } 554} 555