123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- /*
- * OSS compatible sequencer driver
- *
- * open/close and reset interface
- *
- * Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include "seq_oss_device.h"
- #include "seq_oss_synth.h"
- #include "seq_oss_midi.h"
- #include "seq_oss_writeq.h"
- #include "seq_oss_readq.h"
- #include "seq_oss_timer.h"
- #include "seq_oss_event.h"
- #include <linux/init.h>
- #include <linux/export.h>
- #include <linux/moduleparam.h>
- #include <linux/slab.h>
- #include <linux/workqueue.h>
- /*
- * common variables
- */
- static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
- module_param(maxqlen, int, 0444);
- MODULE_PARM_DESC(maxqlen, "maximum queue length");
- static int system_client = -1; /* ALSA sequencer client number */
- static int system_port = -1;
- static int num_clients;
- static struct seq_oss_devinfo *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
- /*
- * prototypes
- */
- static int receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop);
- static int translate_mode(struct file *file);
- static int create_port(struct seq_oss_devinfo *dp);
- static int delete_port(struct seq_oss_devinfo *dp);
- static int alloc_seq_queue(struct seq_oss_devinfo *dp);
- static int delete_seq_queue(int queue);
- static void free_devinfo(void *private);
- #define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
- /* call snd_seq_oss_midi_lookup_ports() asynchronously */
- static void async_call_lookup_ports(struct work_struct *work)
- {
- snd_seq_oss_midi_lookup_ports(system_client);
- }
- static DECLARE_WORK(async_lookup_work, async_call_lookup_ports);
- /*
- * create sequencer client for OSS sequencer
- */
- int __init
- snd_seq_oss_create_client(void)
- {
- int rc;
- struct snd_seq_port_info *port;
- struct snd_seq_port_callback port_callback;
- port = kmalloc(sizeof(*port), GFP_KERNEL);
- if (!port) {
- rc = -ENOMEM;
- goto __error;
- }
- /* create ALSA client */
- rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS,
- "OSS sequencer");
- if (rc < 0)
- goto __error;
- system_client = rc;
- /* create annoucement receiver port */
- memset(port, 0, sizeof(*port));
- strcpy(port->name, "Receiver");
- port->addr.client = system_client;
- port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
- port->type = 0;
- memset(&port_callback, 0, sizeof(port_callback));
- /* don't set port_callback.owner here. otherwise the module counter
- * is incremented and we can no longer release the module..
- */
- port_callback.event_input = receive_announce;
- port->kernel = &port_callback;
-
- call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
- if ((system_port = port->addr.port) >= 0) {
- struct snd_seq_port_subscribe subs;
- memset(&subs, 0, sizeof(subs));
- subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
- subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
- subs.dest.client = system_client;
- subs.dest.port = system_port;
- call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
- }
- rc = 0;
- /* look up midi devices */
- schedule_work(&async_lookup_work);
- __error:
- kfree(port);
- return rc;
- }
- /*
- * receive annoucement from system port, and check the midi device
- */
- static int
- receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop)
- {
- struct snd_seq_port_info pinfo;
- if (atomic)
- return 0; /* it must not happen */
- switch (ev->type) {
- case SNDRV_SEQ_EVENT_PORT_START:
- case SNDRV_SEQ_EVENT_PORT_CHANGE:
- if (ev->data.addr.client == system_client)
- break; /* ignore myself */
- memset(&pinfo, 0, sizeof(pinfo));
- pinfo.addr = ev->data.addr;
- if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
- snd_seq_oss_midi_check_new_port(&pinfo);
- break;
- case SNDRV_SEQ_EVENT_PORT_EXIT:
- if (ev->data.addr.client == system_client)
- break; /* ignore myself */
- snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
- ev->data.addr.port);
- break;
- }
- return 0;
- }
- /*
- * delete OSS sequencer client
- */
- int
- snd_seq_oss_delete_client(void)
- {
- cancel_work_sync(&async_lookup_work);
- if (system_client >= 0)
- snd_seq_delete_kernel_client(system_client);
- snd_seq_oss_midi_clear_all();
- return 0;
- }
- /*
- * open sequencer device
- */
- int
- snd_seq_oss_open(struct file *file, int level)
- {
- int i, rc;
- struct seq_oss_devinfo *dp;
- dp = kzalloc(sizeof(*dp), GFP_KERNEL);
- if (!dp)
- return -ENOMEM;
- dp->cseq = system_client;
- dp->port = -1;
- dp->queue = -1;
- for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
- if (client_table[i] == NULL)
- break;
- }
- dp->index = i;
- if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
- pr_err("ALSA: seq_oss: too many applications\n");
- rc = -ENOMEM;
- goto _error;
- }
- /* look up synth and midi devices */
- snd_seq_oss_synth_setup(dp);
- snd_seq_oss_midi_setup(dp);
- if (dp->synth_opened == 0 && dp->max_mididev == 0) {
- /* pr_err("ALSA: seq_oss: no device found\n"); */
- rc = -ENODEV;
- goto _error;
- }
- /* create port */
- rc = create_port(dp);
- if (rc < 0) {
- pr_err("ALSA: seq_oss: can't create port\n");
- goto _error;
- }
- /* allocate queue */
- rc = alloc_seq_queue(dp);
- if (rc < 0)
- goto _error;
- /* set address */
- dp->addr.client = dp->cseq;
- dp->addr.port = dp->port;
- /*dp->addr.queue = dp->queue;*/
- /*dp->addr.channel = 0;*/
- dp->seq_mode = level;
- /* set up file mode */
- dp->file_mode = translate_mode(file);
- /* initialize read queue */
- if (is_read_mode(dp->file_mode)) {
- dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
- if (!dp->readq) {
- rc = -ENOMEM;
- goto _error;
- }
- }
- /* initialize write queue */
- if (is_write_mode(dp->file_mode)) {
- dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
- if (!dp->writeq) {
- rc = -ENOMEM;
- goto _error;
- }
- }
- /* initialize timer */
- dp->timer = snd_seq_oss_timer_new(dp);
- if (!dp->timer) {
- pr_err("ALSA: seq_oss: can't alloc timer\n");
- rc = -ENOMEM;
- goto _error;
- }
- /* set private data pointer */
- file->private_data = dp;
- /* set up for mode2 */
- if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
- snd_seq_oss_synth_setup_midi(dp);
- else if (is_read_mode(dp->file_mode))
- snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
- client_table[dp->index] = dp;
- num_clients++;
- return 0;
- _error:
- snd_seq_oss_synth_cleanup(dp);
- snd_seq_oss_midi_cleanup(dp);
- delete_seq_queue(dp->queue);
- delete_port(dp);
- return rc;
- }
- /*
- * translate file flags to private mode
- */
- static int
- translate_mode(struct file *file)
- {
- int file_mode = 0;
- if ((file->f_flags & O_ACCMODE) != O_RDONLY)
- file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
- if ((file->f_flags & O_ACCMODE) != O_WRONLY)
- file_mode |= SNDRV_SEQ_OSS_FILE_READ;
- if (file->f_flags & O_NONBLOCK)
- file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
- return file_mode;
- }
- /*
- * create sequencer port
- */
- static int
- create_port(struct seq_oss_devinfo *dp)
- {
- int rc;
- struct snd_seq_port_info port;
- struct snd_seq_port_callback callback;
- memset(&port, 0, sizeof(port));
- port.addr.client = dp->cseq;
- sprintf(port.name, "Sequencer-%d", dp->index);
- port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
- port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
- port.midi_channels = 128;
- port.synth_voices = 128;
- memset(&callback, 0, sizeof(callback));
- callback.owner = THIS_MODULE;
- callback.private_data = dp;
- callback.event_input = snd_seq_oss_event_input;
- callback.private_free = free_devinfo;
- port.kernel = &callback;
- rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
- if (rc < 0)
- return rc;
- dp->port = port.addr.port;
- return 0;
- }
- /*
- * delete ALSA port
- */
- static int
- delete_port(struct seq_oss_devinfo *dp)
- {
- if (dp->port < 0) {
- kfree(dp);
- return 0;
- }
- return snd_seq_event_port_detach(dp->cseq, dp->port);
- }
- /*
- * allocate a queue
- */
- static int
- alloc_seq_queue(struct seq_oss_devinfo *dp)
- {
- struct snd_seq_queue_info qinfo;
- int rc;
- memset(&qinfo, 0, sizeof(qinfo));
- qinfo.owner = system_client;
- qinfo.locked = 1;
- strcpy(qinfo.name, "OSS Sequencer Emulation");
- if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
- return rc;
- dp->queue = qinfo.queue;
- return 0;
- }
- /*
- * release queue
- */
- static int
- delete_seq_queue(int queue)
- {
- struct snd_seq_queue_info qinfo;
- int rc;
- if (queue < 0)
- return 0;
- memset(&qinfo, 0, sizeof(qinfo));
- qinfo.queue = queue;
- rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
- if (rc < 0)
- pr_err("ALSA: seq_oss: unable to delete queue %d (%d)\n", queue, rc);
- return rc;
- }
- /*
- * free device informations - private_free callback of port
- */
- static void
- free_devinfo(void *private)
- {
- struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private;
- snd_seq_oss_timer_delete(dp->timer);
-
- snd_seq_oss_writeq_delete(dp->writeq);
- snd_seq_oss_readq_delete(dp->readq);
-
- kfree(dp);
- }
- /*
- * close sequencer device
- */
- void
- snd_seq_oss_release(struct seq_oss_devinfo *dp)
- {
- int queue;
- client_table[dp->index] = NULL;
- num_clients--;
- snd_seq_oss_reset(dp);
- snd_seq_oss_synth_cleanup(dp);
- snd_seq_oss_midi_cleanup(dp);
- /* clear slot */
- queue = dp->queue;
- if (dp->port >= 0)
- delete_port(dp);
- delete_seq_queue(queue);
- }
- /*
- * Wait until the queue is empty (if we don't have nonblock)
- */
- void
- snd_seq_oss_drain_write(struct seq_oss_devinfo *dp)
- {
- if (! dp->timer->running)
- return;
- if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
- dp->writeq) {
- while (snd_seq_oss_writeq_sync(dp->writeq))
- ;
- }
- }
- /*
- * reset sequencer devices
- */
- void
- snd_seq_oss_reset(struct seq_oss_devinfo *dp)
- {
- int i;
- /* reset all synth devices */
- for (i = 0; i < dp->max_synthdev; i++)
- snd_seq_oss_synth_reset(dp, i);
- /* reset all midi devices */
- if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
- for (i = 0; i < dp->max_mididev; i++)
- snd_seq_oss_midi_reset(dp, i);
- }
- /* remove queues */
- if (dp->readq)
- snd_seq_oss_readq_clear(dp->readq);
- if (dp->writeq)
- snd_seq_oss_writeq_clear(dp->writeq);
- /* reset timer */
- snd_seq_oss_timer_stop(dp->timer);
- }
- #ifdef CONFIG_SND_PROC_FS
- /*
- * misc. functions for proc interface
- */
- char *
- enabled_str(int bool)
- {
- return bool ? "enabled" : "disabled";
- }
- static char *
- filemode_str(int val)
- {
- static char *str[] = {
- "none", "read", "write", "read/write",
- };
- return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
- }
- /*
- * proc interface
- */
- void
- snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
- {
- int i;
- struct seq_oss_devinfo *dp;
- snd_iprintf(buf, "ALSA client number %d\n", system_client);
- snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
- snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
- for (i = 0; i < num_clients; i++) {
- snd_iprintf(buf, "\nApplication %d: ", i);
- if ((dp = client_table[i]) == NULL) {
- snd_iprintf(buf, "*empty*\n");
- continue;
- }
- snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
- snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n",
- (dp->seq_mode ? "music" : "synth"),
- filemode_str(dp->file_mode));
- if (dp->seq_mode)
- snd_iprintf(buf, " timer tempo = %d, timebase = %d\n",
- dp->timer->oss_tempo, dp->timer->oss_timebase);
- snd_iprintf(buf, " max queue length %d\n", maxqlen);
- if (is_read_mode(dp->file_mode) && dp->readq)
- snd_seq_oss_readq_info_read(dp->readq, buf);
- }
- }
- #endif /* CONFIG_SND_PROC_FS */
|