/*1* ALSA sequencer System services Client2* Copyright (c) 1998-1999 by Frank van de Pol <[email protected]>3*4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (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 of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with this program; if not, write to the Free Software17* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA18*19*/2021#include <linux/init.h>22#include <linux/slab.h>23#include <sound/core.h>24#include "seq_system.h"25#include "seq_timer.h"26#include "seq_queue.h"2728/* internal client that provide system services, access to timer etc. */2930/*31* Port "Timer"32* - send tempo /start/stop etc. events to this port to manipulate the33* queue's timer. The queue address is specified in34* data.queue.queue.35* - this port supports subscription. The received timer events are36* broadcasted to all subscribed clients. The modified tempo37* value is stored on data.queue.value.38* The modifier client/port is not send.39*40* Port "Announce"41* - does not receive message42* - supports supscription. For each client or port attaching to or43* detaching from the system an announcement is send to the subscribed44* clients.45*46* Idea: the subscription mechanism might also work handy for distributing47* synchronisation and timing information. In this case we would ideally have48* a list of subscribers for each type of sync (time, tick), for each timing49* queue.50*51* NOTE: the queue to be started, stopped, etc. must be specified52* in data.queue.addr.queue field. queue is used only for53* scheduling, and no longer referred as affected queue.54* They are used only for timer broadcast (see above).55* -- iwai56*/575859/* client id of our system client */60static int sysclient = -1;6162/* port id numbers for this client */63static int announce_port = -1;64656667/* fill standard header data, source port & channel are filled in */68static int setheader(struct snd_seq_event * ev, int client, int port)69{70if (announce_port < 0)71return -ENODEV;7273memset(ev, 0, sizeof(struct snd_seq_event));7475ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;76ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;7778ev->source.client = sysclient;79ev->source.port = announce_port;80ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;8182/* fill data */83/*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/84ev->data.addr.client = client;85ev->data.addr.port = port;8687return 0;88}899091/* entry points for broadcasting system events */92void snd_seq_system_broadcast(int client, int port, int type)93{94struct snd_seq_event ev;9596if (setheader(&ev, client, port) < 0)97return;98ev.type = type;99snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);100}101102/* entry points for broadcasting system events */103int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev)104{105ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;106ev->source.client = sysclient;107ev->source.port = announce_port;108ev->dest.client = client;109ev->dest.port = port;110return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);111}112113/* call-back handler for timer events */114static int event_input_timer(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop)115{116return snd_seq_control_queue(ev, atomic, hop);117}118119/* register our internal client */120int __init snd_seq_system_client_init(void)121{122struct snd_seq_port_callback pcallbacks;123struct snd_seq_port_info *port;124125port = kzalloc(sizeof(*port), GFP_KERNEL);126if (!port)127return -ENOMEM;128129memset(&pcallbacks, 0, sizeof(pcallbacks));130pcallbacks.owner = THIS_MODULE;131pcallbacks.event_input = event_input_timer;132133/* register client */134sysclient = snd_seq_create_kernel_client(NULL, 0, "System");135136/* register timer */137strcpy(port->name, "Timer");138port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */139port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */140port->kernel = &pcallbacks;141port->type = 0;142port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;143port->addr.client = sysclient;144port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;145snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);146147/* register announcement port */148strcpy(port->name, "Announce");149port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */150port->kernel = NULL;151port->type = 0;152port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;153port->addr.client = sysclient;154port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;155snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);156announce_port = port->addr.port;157158kfree(port);159return 0;160}161162163/* unregister our internal client */164void __exit snd_seq_system_client_done(void)165{166int oldsysclient = sysclient;167168if (oldsysclient >= 0) {169sysclient = -1;170announce_port = -1;171snd_seq_delete_kernel_client(oldsysclient);172}173}174175176