1 /* 2 * ALSA sequencer System services Client 3 * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl> 4 * 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * 20 */ 21 22 #include <linux/init.h> 23 #include <linux/slab.h> 24 #include <sound/core.h> 25 #include "seq_system.h" 26 #include "seq_timer.h" 27 #include "seq_queue.h" 28 29 /* internal client that provide system services, access to timer etc. */ 30 31 /* 32 * Port "Timer" 33 * - send tempo /start/stop etc. events to this port to manipulate the 34 * queue's timer. The queue address is specified in 35 * data.queue.queue. 36 * - this port supports subscription. The received timer events are 37 * broadcasted to all subscribed clients. The modified tempo 38 * value is stored on data.queue.value. 39 * The modifier client/port is not send. 40 * 41 * Port "Announce" 42 * - does not receive message 43 * - supports supscription. For each client or port attaching to or 44 * detaching from the system an announcement is send to the subscribed 45 * clients. 46 * 47 * Idea: the subscription mechanism might also work handy for distributing 48 * synchronisation and timing information. In this case we would ideally have 49 * a list of subscribers for each type of sync (time, tick), for each timing 50 * queue. 51 * 52 * NOTE: the queue to be started, stopped, etc. must be specified 53 * in data.queue.addr.queue field. queue is used only for 54 * scheduling, and no longer referred as affected queue. 55 * They are used only for timer broadcast (see above). 56 * -- iwai 57 */ 58 59 60 /* client id of our system client */ 61 static int sysclient = -1; 62 63 /* port id numbers for this client */ 64 static int announce_port = -1; 65 66 67 68 /* fill standard header data, source port & channel are filled in */ 69 static int setheader(struct snd_seq_event * ev, int client, int port) 70 { 71 if (announce_port < 0) 72 return -ENODEV; 73 74 memset(ev, 0, sizeof(struct snd_seq_event)); 75 76 ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; 77 ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; 78 79 ev->source.client = sysclient; 80 ev->source.port = announce_port; 81 ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; 82 83 /* fill data */ 84 /*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/ 85 ev->data.addr.client = client; 86 ev->data.addr.port = port; 87 88 return 0; 89 } 90 91 92 /* entry points for broadcasting system events */ 93 void snd_seq_system_broadcast(int client, int port, int type) 94 { 95 struct snd_seq_event ev; 96 97 if (setheader(&ev, client, port) < 0) 98 return; 99 ev.type = type; 100 snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0); 101 } 102 103 /* entry points for broadcasting system events */ 104 int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev) 105 { 106 ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; 107 ev->source.client = sysclient; 108 ev->source.port = announce_port; 109 ev->dest.client = client; 110 ev->dest.port = port; 111 return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0); 112 } 113 114 /* call-back handler for timer events */ 115 static int event_input_timer(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop) 116 { 117 return snd_seq_control_queue(ev, atomic, hop); 118 } 119 120 /* register our internal client */ 121 int __init snd_seq_system_client_init(void) 122 { 123 struct snd_seq_port_callback pcallbacks; 124 struct snd_seq_port_info *port; 125 126 port = kzalloc(sizeof(*port), GFP_KERNEL); 127 if (!port) 128 return -ENOMEM; 129 130 memset(&pcallbacks, 0, sizeof(pcallbacks)); 131 pcallbacks.owner = THIS_MODULE; 132 pcallbacks.event_input = event_input_timer; 133 134 /* register client */ 135 sysclient = snd_seq_create_kernel_client(NULL, 0, "System"); 136 137 /* register timer */ 138 strcpy(port->name, "Timer"); 139 port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */ 140 port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */ 141 port->kernel = &pcallbacks; 142 port->type = 0; 143 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 144 port->addr.client = sysclient; 145 port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; 146 snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port); 147 148 /* register announcement port */ 149 strcpy(port->name, "Announce"); 150 port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ 151 port->kernel = NULL; 152 port->type = 0; 153 port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 154 port->addr.client = sysclient; 155 port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; 156 snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port); 157 announce_port = port->addr.port; 158 159 kfree(port); 160 return 0; 161 } 162 163 164 /* unregister our internal client */ 165 void __exit snd_seq_system_client_done(void) 166 { 167 int oldsysclient = sysclient; 168 169 if (oldsysclient >= 0) { 170 sysclient = -1; 171 announce_port = -1; 172 snd_seq_delete_kernel_client(oldsysclient); 173 } 174 } 175