2005-04-17 02:20:36 +04:00
/*
* ALSA sequencer System services Client
* Copyright ( c ) 1998 - 1999 by Frank van de Pol < fvdpol @ coil . demon . nl >
*
*
* 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 <sound/driver.h>
# include <linux/init.h>
# include <sound/core.h>
# include "seq_system.h"
# include "seq_timer.h"
# include "seq_queue.h"
/* internal client that provide system services, access to timer etc. */
/*
* Port " Timer "
* - send tempo / start / stop etc . events to this port to manipulate the
* queue ' s timer . The queue address is specified in
* data . queue . queue .
* - this port supports subscription . The received timer events are
* broadcasted to all subscribed clients . The modified tempo
* value is stored on data . queue . value .
* The modifier client / port is not send .
*
* Port " Announce "
* - does not receive message
* - supports supscription . For each client or port attaching to or
* detaching from the system an announcement is send to the subscribed
* clients .
*
* Idea : the subscription mechanism might also work handy for distributing
* synchronisation and timing information . In this case we would ideally have
* a list of subscribers for each type of sync ( time , tick ) , for each timing
* queue .
*
* NOTE : the queue to be started , stopped , etc . must be specified
* in data . queue . addr . queue field . queue is used only for
* scheduling , and no longer referred as affected queue .
* They are used only for timer broadcast ( see above ) .
* - - iwai
*/
/* client id of our system client */
static int sysclient = - 1 ;
/* port id numbers for this client */
static int announce_port = - 1 ;
/* fill standard header data, source port & channel are filled in */
2005-11-17 16:04:02 +03:00
static int setheader ( struct snd_seq_event * ev , int client , int port )
2005-04-17 02:20:36 +04:00
{
if ( announce_port < 0 )
return - ENODEV ;
2005-11-17 16:04:02 +03:00
memset ( ev , 0 , sizeof ( struct snd_seq_event ) ) ;
2005-04-17 02:20:36 +04:00
ev - > flags & = ~ SNDRV_SEQ_EVENT_LENGTH_MASK ;
ev - > flags | = SNDRV_SEQ_EVENT_LENGTH_FIXED ;
ev - > source . client = sysclient ;
ev - > source . port = announce_port ;
ev - > dest . client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS ;
/* fill data */
/*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/
ev - > data . addr . client = client ;
ev - > data . addr . port = port ;
return 0 ;
}
/* entry points for broadcasting system events */
void snd_seq_system_broadcast ( int client , int port , int type )
{
2005-11-17 16:04:02 +03:00
struct snd_seq_event ev ;
2005-04-17 02:20:36 +04:00
if ( setheader ( & ev , client , port ) < 0 )
return ;
ev . type = type ;
snd_seq_kernel_client_dispatch ( sysclient , & ev , 0 , 0 ) ;
}
/* entry points for broadcasting system events */
2005-11-17 16:04:02 +03:00
int snd_seq_system_notify ( int client , int port , struct snd_seq_event * ev )
2005-04-17 02:20:36 +04:00
{
ev - > flags = SNDRV_SEQ_EVENT_LENGTH_FIXED ;
ev - > source . client = sysclient ;
ev - > source . port = announce_port ;
ev - > dest . client = client ;
ev - > dest . port = port ;
return snd_seq_kernel_client_dispatch ( sysclient , ev , 0 , 0 ) ;
}
/* call-back handler for timer events */
2005-11-17 16:04:02 +03:00
static int event_input_timer ( struct snd_seq_event * ev , int direct , void * private_data , int atomic , int hop )
2005-04-17 02:20:36 +04:00
{
return snd_seq_control_queue ( ev , atomic , hop ) ;
}
/* register our internal client */
int __init snd_seq_system_client_init ( void )
{
2005-11-17 16:04:02 +03:00
struct snd_seq_port_callback pcallbacks ;
struct snd_seq_port_info * port ;
2005-04-17 02:20:36 +04:00
2005-09-09 16:20:49 +04:00
port = kzalloc ( sizeof ( * port ) , GFP_KERNEL ) ;
2005-12-12 11:33:37 +03:00
if ( ! port )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
memset ( & pcallbacks , 0 , sizeof ( pcallbacks ) ) ;
pcallbacks . owner = THIS_MODULE ;
pcallbacks . event_input = event_input_timer ;
/* register client */
2005-12-12 11:33:37 +03:00
sysclient = snd_seq_create_kernel_client ( NULL , 0 , " System " ) ;
2005-04-17 02:20:36 +04:00
/* register timer */
strcpy ( port - > name , " Timer " ) ;
port - > capability = SNDRV_SEQ_PORT_CAP_WRITE ; /* accept queue control */
port - > capability | = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ ; /* for broadcast */
port - > kernel = & pcallbacks ;
port - > type = 0 ;
port - > flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT ;
port - > addr . client = sysclient ;
port - > addr . port = SNDRV_SEQ_PORT_SYSTEM_TIMER ;
snd_seq_kernel_client_ctl ( sysclient , SNDRV_SEQ_IOCTL_CREATE_PORT , port ) ;
/* register announcement port */
strcpy ( port - > name , " Announce " ) ;
port - > capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ ; /* for broadcast only */
port - > kernel = NULL ;
port - > type = 0 ;
port - > flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT ;
port - > addr . client = sysclient ;
port - > addr . port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE ;
snd_seq_kernel_client_ctl ( sysclient , SNDRV_SEQ_IOCTL_CREATE_PORT , port ) ;
announce_port = port - > addr . port ;
kfree ( port ) ;
return 0 ;
}
/* unregister our internal client */
void __exit snd_seq_system_client_done ( void )
{
int oldsysclient = sysclient ;
if ( oldsysclient > = 0 ) {
sysclient = - 1 ;
announce_port = - 1 ;
snd_seq_delete_kernel_client ( oldsysclient ) ;
}
}