// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer System services Client
* Copyright ( c ) 1998 - 1999 by Frank van de Pol < fvdpol @ coil . demon . nl >
*/
#include <linux/init.h>
#include <linux/export.h>
#include <linux/slab.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 ;
/* number of subscriptions to announce port */
static int announce_subscribed;
/* fill standard header data, source port & channel are filled in */
static int setheader(struct snd_seq_event * ev, int client, int port)
{
if (announce_port < 0 || !announce_subscribed)
return -ENODEV;
memset(ev, 0 , sizeof (struct snd_seq_event));
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, bool atomic)
{
struct snd_seq_event ev;
if (setheader(&ev, client, port) < 0 )
return ;
ev.type = type;
snd_seq_kernel_client_dispatch(sysclient, &ev, atomic, 0 );
}
EXPORT_SYMBOL_GPL(snd_seq_system_broadcast);
/* entry points for broadcasting system events */
int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev,
bool atomic)
{
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, atomic, 0 );
}
/* call-back handler for timer events */
static int event_input_timer(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop)
{
return snd_seq_control_queue(ev, atomic, hop);
}
static int sys_announce_subscribe(void *private_data,
struct snd_seq_port_subscribe *info)
{
announce_subscribed++;
return 0 ;
}
static int sys_announce_unsubscribe(void *private_data,
struct snd_seq_port_subscribe *info)
{
if (snd_BUG_ON(!announce_subscribed))
return 0 ;
announce_subscribed--;
return 0 ;
}
/* register our internal client */
int __init snd_seq_system_client_init(void )
{
struct snd_seq_port_callback pcallbacks;
struct snd_seq_port_info *port;
int err;
port = kzalloc(sizeof (*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
memset(&pcallbacks, 0 , sizeof (pcallbacks));
pcallbacks.owner = THIS_MODULE;
pcallbacks.event_input = event_input_timer;
/* register client */
sysclient = snd_seq_create_kernel_client(NULL, 0 , "System" );
if (sysclient < 0 ) {
kfree(port);
return sysclient;
}
/* register timer */
strscpy(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;
err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
port);
if (err < 0 )
goto error_port;
/* register announcement port */
strscpy(port->name, "Announce" );
port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
pcallbacks.event_input = NULL;
pcallbacks.subscribe = sys_announce_subscribe;
pcallbacks.unsubscribe = sys_announce_unsubscribe;
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_ANNOUNCE;
err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
port);
if (err < 0 )
goto error_port;
announce_port = port->addr.port;
kfree(port);
return 0 ;
error_port:
snd_seq_system_client_done();
kfree(port);
return err;
}
/* unregister our internal client */
void snd_seq_system_client_done(void )
{
int oldsysclient = sysclient;
if (oldsysclient >= 0 ) {
sysclient = -1 ;
announce_port = -1 ;
snd_seq_delete_kernel_client(oldsysclient);
}
}
Messung V0.5 in Prozent C=90 H=95 G=92
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-30)
¤
*© Formatika GbR, Deutschland