root/sound/core/seq/oss/seq_oss_init.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. async_call_lookup_ports
  2. snd_seq_oss_create_client
  3. receive_announce
  4. snd_seq_oss_delete_client
  5. snd_seq_oss_open
  6. translate_mode
  7. create_port
  8. delete_port
  9. alloc_seq_queue
  10. delete_seq_queue
  11. free_devinfo
  12. snd_seq_oss_release
  13. snd_seq_oss_reset
  14. enabled_str
  15. filemode_str
  16. snd_seq_oss_system_info_read

   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  */
  26 static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
  27 module_param(maxqlen, int, 0444);
  28 MODULE_PARM_DESC(maxqlen, "maximum queue length");
  29 
  30 static int system_client = -1; /* ALSA sequencer client number */
  31 static int system_port = -1;
  32 
  33 static int num_clients;
  34 static struct seq_oss_devinfo *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
  35 
  36 
  37 /*
  38  * prototypes
  39  */
  40 static int receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop);
  41 static int translate_mode(struct file *file);
  42 static int create_port(struct seq_oss_devinfo *dp);
  43 static int delete_port(struct seq_oss_devinfo *dp);
  44 static int alloc_seq_queue(struct seq_oss_devinfo *dp);
  45 static int delete_seq_queue(int queue);
  46 static 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 */
  52 static void async_call_lookup_ports(struct work_struct *work)
  53 {
  54         snd_seq_oss_midi_lookup_ports(system_client);
  55 }
  56 
  57 static DECLARE_WORK(async_lookup_work, async_call_lookup_ports);
  58 
  59 /*
  60  * create sequencer client for OSS sequencer
  61  */
  62 int __init
  63 snd_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 = kmalloc(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 annoucement receiver port */
  84         memset(port, 0, sizeof(*port));
  85         strcpy(port->name, "Receiver");
  86         port->addr.client = system_client;
  87         port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
  88         port->type = 0;
  89 
  90         memset(&port_callback, 0, sizeof(port_callback));
  91         /* don't set port_callback.owner here. otherwise the module counter
  92          * is incremented and we can no longer release the module..
  93          */
  94         port_callback.event_input = receive_announce;
  95         port->kernel = &port_callback;
  96         
  97         call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
  98         if ((system_port = port->addr.port) >= 0) {
  99                 struct snd_seq_port_subscribe subs;
 100 
 101                 memset(&subs, 0, sizeof(subs));
 102                 subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
 103                 subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
 104                 subs.dest.client = system_client;
 105                 subs.dest.port = system_port;
 106                 call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
 107         }
 108         rc = 0;
 109 
 110         /* look up midi devices */
 111         schedule_work(&async_lookup_work);
 112 
 113  __error:
 114         kfree(port);
 115         return rc;
 116 }
 117 
 118 
 119 /*
 120  * receive annoucement from system port, and check the midi device
 121  */
 122 static int
 123 receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop)
 124 {
 125         struct snd_seq_port_info pinfo;
 126 
 127         if (atomic)
 128                 return 0; /* it must not happen */
 129 
 130         switch (ev->type) {
 131         case SNDRV_SEQ_EVENT_PORT_START:
 132         case SNDRV_SEQ_EVENT_PORT_CHANGE:
 133                 if (ev->data.addr.client == system_client)
 134                         break; /* ignore myself */
 135                 memset(&pinfo, 0, sizeof(pinfo));
 136                 pinfo.addr = ev->data.addr;
 137                 if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
 138                         snd_seq_oss_midi_check_new_port(&pinfo);
 139                 break;
 140 
 141         case SNDRV_SEQ_EVENT_PORT_EXIT:
 142                 if (ev->data.addr.client == system_client)
 143                         break; /* ignore myself */
 144                 snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
 145                                                 ev->data.addr.port);
 146                 break;
 147         }
 148         return 0;
 149 }
 150 
 151 
 152 /*
 153  * delete OSS sequencer client
 154  */
 155 int
 156 snd_seq_oss_delete_client(void)
 157 {
 158         cancel_work_sync(&async_lookup_work);
 159         if (system_client >= 0)
 160                 snd_seq_delete_kernel_client(system_client);
 161 
 162         snd_seq_oss_midi_clear_all();
 163 
 164         return 0;
 165 }
 166 
 167 
 168 /*
 169  * open sequencer device
 170  */
 171 int
 172 snd_seq_oss_open(struct file *file, int level)
 173 {
 174         int i, rc;
 175         struct seq_oss_devinfo *dp;
 176 
 177         dp = kzalloc(sizeof(*dp), GFP_KERNEL);
 178         if (!dp)
 179                 return -ENOMEM;
 180 
 181         dp->cseq = system_client;
 182         dp->port = -1;
 183         dp->queue = -1;
 184 
 185         for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
 186                 if (client_table[i] == NULL)
 187                         break;
 188         }
 189 
 190         dp->index = i;
 191         if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
 192                 pr_debug("ALSA: seq_oss: too many applications\n");
 193                 rc = -ENOMEM;
 194                 goto _error;
 195         }
 196 
 197         /* look up synth and midi devices */
 198         snd_seq_oss_synth_setup(dp);
 199         snd_seq_oss_midi_setup(dp);
 200 
 201         if (dp->synth_opened == 0 && dp->max_mididev == 0) {
 202                 /* pr_err("ALSA: seq_oss: no device found\n"); */
 203                 rc = -ENODEV;
 204                 goto _error;
 205         }
 206 
 207         /* create port */
 208         rc = create_port(dp);
 209         if (rc < 0) {
 210                 pr_err("ALSA: seq_oss: can't create port\n");
 211                 goto _error;
 212         }
 213 
 214         /* allocate queue */
 215         rc = alloc_seq_queue(dp);
 216         if (rc < 0)
 217                 goto _error;
 218 
 219         /* set address */
 220         dp->addr.client = dp->cseq;
 221         dp->addr.port = dp->port;
 222         /*dp->addr.queue = dp->queue;*/
 223         /*dp->addr.channel = 0;*/
 224 
 225         dp->seq_mode = level;
 226 
 227         /* set up file mode */
 228         dp->file_mode = translate_mode(file);
 229 
 230         /* initialize read queue */
 231         if (is_read_mode(dp->file_mode)) {
 232                 dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
 233                 if (!dp->readq) {
 234                         rc = -ENOMEM;
 235                         goto _error;
 236                 }
 237         }
 238 
 239         /* initialize write queue */
 240         if (is_write_mode(dp->file_mode)) {
 241                 dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
 242                 if (!dp->writeq) {
 243                         rc = -ENOMEM;
 244                         goto _error;
 245                 }
 246         }
 247 
 248         /* initialize timer */
 249         dp->timer = snd_seq_oss_timer_new(dp);
 250         if (!dp->timer) {
 251                 pr_err("ALSA: seq_oss: can't alloc timer\n");
 252                 rc = -ENOMEM;
 253                 goto _error;
 254         }
 255 
 256         /* set private data pointer */
 257         file->private_data = dp;
 258 
 259         /* set up for mode2 */
 260         if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
 261                 snd_seq_oss_synth_setup_midi(dp);
 262         else if (is_read_mode(dp->file_mode))
 263                 snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
 264 
 265         client_table[dp->index] = dp;
 266         num_clients++;
 267 
 268         return 0;
 269 
 270  _error:
 271         snd_seq_oss_synth_cleanup(dp);
 272         snd_seq_oss_midi_cleanup(dp);
 273         delete_seq_queue(dp->queue);
 274         delete_port(dp);
 275 
 276         return rc;
 277 }
 278 
 279 /*
 280  * translate file flags to private mode
 281  */
 282 static int
 283 translate_mode(struct file *file)
 284 {
 285         int file_mode = 0;
 286         if ((file->f_flags & O_ACCMODE) != O_RDONLY)
 287                 file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
 288         if ((file->f_flags & O_ACCMODE) != O_WRONLY)
 289                 file_mode |= SNDRV_SEQ_OSS_FILE_READ;
 290         if (file->f_flags & O_NONBLOCK)
 291                 file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
 292         return file_mode;
 293 }
 294 
 295 
 296 /*
 297  * create sequencer port
 298  */
 299 static int
 300 create_port(struct seq_oss_devinfo *dp)
 301 {
 302         int rc;
 303         struct snd_seq_port_info port;
 304         struct snd_seq_port_callback callback;
 305 
 306         memset(&port, 0, sizeof(port));
 307         port.addr.client = dp->cseq;
 308         sprintf(port.name, "Sequencer-%d", dp->index);
 309         port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
 310         port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
 311         port.midi_channels = 128;
 312         port.synth_voices = 128;
 313 
 314         memset(&callback, 0, sizeof(callback));
 315         callback.owner = THIS_MODULE;
 316         callback.private_data = dp;
 317         callback.event_input = snd_seq_oss_event_input;
 318         callback.private_free = free_devinfo;
 319         port.kernel = &callback;
 320 
 321         rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
 322         if (rc < 0)
 323                 return rc;
 324 
 325         dp->port = port.addr.port;
 326 
 327         return 0;
 328 }
 329 
 330 /*
 331  * delete ALSA port
 332  */
 333 static int
 334 delete_port(struct seq_oss_devinfo *dp)
 335 {
 336         if (dp->port < 0) {
 337                 kfree(dp);
 338                 return 0;
 339         }
 340 
 341         return snd_seq_event_port_detach(dp->cseq, dp->port);
 342 }
 343 
 344 /*
 345  * allocate a queue
 346  */
 347 static int
 348 alloc_seq_queue(struct seq_oss_devinfo *dp)
 349 {
 350         struct snd_seq_queue_info qinfo;
 351         int rc;
 352 
 353         memset(&qinfo, 0, sizeof(qinfo));
 354         qinfo.owner = system_client;
 355         qinfo.locked = 1;
 356         strcpy(qinfo.name, "OSS Sequencer Emulation");
 357         if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
 358                 return rc;
 359         dp->queue = qinfo.queue;
 360         return 0;
 361 }
 362 
 363 /*
 364  * release queue
 365  */
 366 static int
 367 delete_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  */
 386 static void
 387 free_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  */
 404 void
 405 snd_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  */
 428 void
 429 snd_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  */
 457 char *
 458 enabled_str(int bool)
 459 {
 460         return bool ? "enabled" : "disabled";
 461 }
 462 
 463 static char *
 464 filemode_str(int val)
 465 {
 466         static char *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  */
 476 void
 477 snd_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                 if ((dp = client_table[i]) == NULL) {
 489                         snd_iprintf(buf, "*empty*\n");
 490                         continue;
 491                 }
 492                 snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
 493                 snd_iprintf(buf, "  sequencer mode = %s : file open mode = %s\n",
 494                             (dp->seq_mode ? "music" : "synth"),
 495                             filemode_str(dp->file_mode));
 496                 if (dp->seq_mode)
 497                         snd_iprintf(buf, "  timer tempo = %d, timebase = %d\n",
 498                                     dp->timer->oss_tempo, dp->timer->oss_timebase);
 499                 snd_iprintf(buf, "  max queue length %d\n", maxqlen);
 500                 if (is_read_mode(dp->file_mode) && dp->readq)
 501                         snd_seq_oss_readq_info_read(dp->readq, buf);
 502         }
 503 }
 504 #endif /* CONFIG_SND_PROC_FS */

/* [<][>][^][v][top][bottom][index][help] */