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