xref: /openbmc/linux/sound/core/seq/seq_system.c (revision ecca82b4)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *   ALSA sequencer System services Client
31da177e4SLinus Torvalds  *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
71da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
81da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
91da177e4SLinus Torvalds  *   (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
121da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
131da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141da177e4SLinus Torvalds  *   GNU General Public License for more details.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
171da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
181da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include <sound/driver.h>
231da177e4SLinus Torvalds #include <linux/init.h>
241da177e4SLinus Torvalds #include <sound/core.h>
251da177e4SLinus Torvalds #include "seq_system.h"
261da177e4SLinus Torvalds #include "seq_timer.h"
271da177e4SLinus Torvalds #include "seq_queue.h"
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /* internal client that provide system services, access to timer etc. */
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds /*
321da177e4SLinus Torvalds  * Port "Timer"
331da177e4SLinus Torvalds  *      - send tempo /start/stop etc. events to this port to manipulate the
341da177e4SLinus Torvalds  *        queue's timer. The queue address is specified in
351da177e4SLinus Torvalds  *	  data.queue.queue.
361da177e4SLinus Torvalds  *      - this port supports subscription. The received timer events are
371da177e4SLinus Torvalds  *        broadcasted to all subscribed clients. The modified tempo
381da177e4SLinus Torvalds  *	  value is stored on data.queue.value.
391da177e4SLinus Torvalds  *	  The modifier client/port is not send.
401da177e4SLinus Torvalds  *
411da177e4SLinus Torvalds  * Port "Announce"
421da177e4SLinus Torvalds  *      - does not receive message
431da177e4SLinus Torvalds  *      - supports supscription. For each client or port attaching to or
441da177e4SLinus Torvalds  *        detaching from the system an announcement is send to the subscribed
451da177e4SLinus Torvalds  *        clients.
461da177e4SLinus Torvalds  *
471da177e4SLinus Torvalds  * Idea: the subscription mechanism might also work handy for distributing
481da177e4SLinus Torvalds  * synchronisation and timing information. In this case we would ideally have
491da177e4SLinus Torvalds  * a list of subscribers for each type of sync (time, tick), for each timing
501da177e4SLinus Torvalds  * queue.
511da177e4SLinus Torvalds  *
521da177e4SLinus Torvalds  * NOTE: the queue to be started, stopped, etc. must be specified
531da177e4SLinus Torvalds  *	 in data.queue.addr.queue field.  queue is used only for
541da177e4SLinus Torvalds  *	 scheduling, and no longer referred as affected queue.
551da177e4SLinus Torvalds  *	 They are used only for timer broadcast (see above).
561da177e4SLinus Torvalds  *							-- iwai
571da177e4SLinus Torvalds  */
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds /* client id of our system client */
611da177e4SLinus Torvalds static int sysclient = -1;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds /* port id numbers for this client */
641da177e4SLinus Torvalds static int announce_port = -1;
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds /* fill standard header data, source port & channel are filled in */
691da177e4SLinus Torvalds static int setheader(snd_seq_event_t * ev, int client, int port)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	if (announce_port < 0)
721da177e4SLinus Torvalds 		return -ENODEV;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	memset(ev, 0, sizeof(snd_seq_event_t));
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
771da177e4SLinus Torvalds 	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds 	ev->source.client = sysclient;
801da177e4SLinus Torvalds 	ev->source.port = announce_port;
811da177e4SLinus Torvalds 	ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	/* fill data */
841da177e4SLinus Torvalds 	/*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/
851da177e4SLinus Torvalds 	ev->data.addr.client = client;
861da177e4SLinus Torvalds 	ev->data.addr.port = port;
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	return 0;
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds /* entry points for broadcasting system events */
931da177e4SLinus Torvalds void snd_seq_system_broadcast(int client, int port, int type)
941da177e4SLinus Torvalds {
951da177e4SLinus Torvalds 	snd_seq_event_t ev;
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	if (setheader(&ev, client, port) < 0)
981da177e4SLinus Torvalds 		return;
991da177e4SLinus Torvalds 	ev.type = type;
1001da177e4SLinus Torvalds 	snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds /* entry points for broadcasting system events */
1041da177e4SLinus Torvalds int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev)
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds 	ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
1071da177e4SLinus Torvalds 	ev->source.client = sysclient;
1081da177e4SLinus Torvalds 	ev->source.port = announce_port;
1091da177e4SLinus Torvalds 	ev->dest.client = client;
1101da177e4SLinus Torvalds 	ev->dest.port = port;
1111da177e4SLinus Torvalds 	return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds /* call-back handler for timer events */
1151da177e4SLinus Torvalds static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
1161da177e4SLinus Torvalds {
1171da177e4SLinus Torvalds 	return snd_seq_control_queue(ev, atomic, hop);
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds /* register our internal client */
1211da177e4SLinus Torvalds int __init snd_seq_system_client_init(void)
1221da177e4SLinus Torvalds {
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	snd_seq_client_callback_t callbacks;
1251da177e4SLinus Torvalds 	snd_seq_port_callback_t pcallbacks;
1261da177e4SLinus Torvalds 	snd_seq_client_info_t *inf;
1271da177e4SLinus Torvalds 	snd_seq_port_info_t *port;
1281da177e4SLinus Torvalds 
129ecca82b4STakashi Iwai 	inf = kzalloc(sizeof(*inf), GFP_KERNEL);
130ecca82b4STakashi Iwai 	port = kzalloc(sizeof(*port), GFP_KERNEL);
1311da177e4SLinus Torvalds 	if (! inf || ! port) {
1321da177e4SLinus Torvalds 		kfree(inf);
1331da177e4SLinus Torvalds 		kfree(port);
1341da177e4SLinus Torvalds 		return -ENOMEM;
1351da177e4SLinus Torvalds 	}
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	memset(&callbacks, 0, sizeof(callbacks));
1381da177e4SLinus Torvalds 	memset(&pcallbacks, 0, sizeof(pcallbacks));
1391da177e4SLinus Torvalds 	pcallbacks.owner = THIS_MODULE;
1401da177e4SLinus Torvalds 	pcallbacks.event_input = event_input_timer;
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	/* register client */
1431da177e4SLinus Torvalds 	callbacks.allow_input = callbacks.allow_output = 1;
1441da177e4SLinus Torvalds 	sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks);
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	/* set our name */
1471da177e4SLinus Torvalds 	inf->client = 0;
1481da177e4SLinus Torvalds 	inf->type = KERNEL_CLIENT;
1491da177e4SLinus Torvalds 	strcpy(inf->name, "System");
1501da177e4SLinus Torvalds 	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, inf);
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	/* register timer */
1531da177e4SLinus Torvalds 	strcpy(port->name, "Timer");
1541da177e4SLinus Torvalds 	port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
1551da177e4SLinus Torvalds 	port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
1561da177e4SLinus Torvalds 	port->kernel = &pcallbacks;
1571da177e4SLinus Torvalds 	port->type = 0;
1581da177e4SLinus Torvalds 	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
1591da177e4SLinus Torvalds 	port->addr.client = sysclient;
1601da177e4SLinus Torvalds 	port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
1611da177e4SLinus Torvalds 	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	/* register announcement port */
1641da177e4SLinus Torvalds 	strcpy(port->name, "Announce");
1651da177e4SLinus Torvalds 	port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
1661da177e4SLinus Torvalds 	port->kernel = NULL;
1671da177e4SLinus Torvalds 	port->type = 0;
1681da177e4SLinus Torvalds 	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
1691da177e4SLinus Torvalds 	port->addr.client = sysclient;
1701da177e4SLinus Torvalds 	port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
1711da177e4SLinus Torvalds 	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
1721da177e4SLinus Torvalds 	announce_port = port->addr.port;
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	kfree(inf);
1751da177e4SLinus Torvalds 	kfree(port);
1761da177e4SLinus Torvalds 	return 0;
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds /* unregister our internal client */
1811da177e4SLinus Torvalds void __exit snd_seq_system_client_done(void)
1821da177e4SLinus Torvalds {
1831da177e4SLinus Torvalds 	int oldsysclient = sysclient;
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	if (oldsysclient >= 0) {
1861da177e4SLinus Torvalds 		sysclient = -1;
1871da177e4SLinus Torvalds 		announce_port = -1;
1881da177e4SLinus Torvalds 		snd_seq_delete_kernel_client(oldsysclient);
1891da177e4SLinus Torvalds 	}
1901da177e4SLinus Torvalds }
191