Browse Source

[0.99.14] jack_client_open() part 2

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@806 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
joq 21 years ago
parent
commit
6dcf31179c
27 changed files with 1937 additions and 1062 deletions
  1. +1
    -1
      configure.ac
  2. +9
    -3
      doc/Makefile.am
  3. +3
    -1
      doc/mainpage.dox
  4. +5
    -4
      doc/reference.doxygen.in
  5. +4
    -4
      example-clients/inprocess.c
  6. +4
    -4
      example-clients/intime.c
  7. +155
    -20
      example-clients/ipload.c
  8. +66
    -1
      example-clients/ipunload.c
  9. +13
    -3
      example-clients/lsp.c
  10. +1
    -1
      example-clients/simple_client.c
  11. +2
    -0
      jack/Makefile.am
  12. +13
    -0
      jack/engine.h
  13. +130
    -0
      jack/intclient.h
  14. +16
    -9
      jack/internal.h
  15. +34
    -24
      jack/jack.h
  16. +66
    -24
      jack/types.h
  17. +64
    -0
      jack/varargs.h
  18. +3
    -2
      jackd/Makefile.am
  19. +933
    -0
      jackd/clientengine.c
  20. +51
    -0
      jackd/clientengine.h
  21. +91
    -891
      jackd/engine.c
  22. +2
    -2
      jackd/jackd.c
  23. +6
    -0
      libjack/ChangeLog
  24. +4
    -3
      libjack/Makefile.am
  25. +69
    -63
      libjack/client.c
  26. +187
    -0
      libjack/intclient.c
  27. +5
    -2
      libjack/local.h

+ 1
- 1
configure.ac View File

@@ -15,7 +15,7 @@ dnl changes are made
dnl ---
JACK_MAJOR_VERSION=0
JACK_MINOR_VERSION=99
JACK_MICRO_VERSION=13
JACK_MICRO_VERSION=14

dnl ---
dnl HOWTO: updating the jack protocol version


+ 9
- 3
doc/Makefile.am View File

@@ -5,9 +5,15 @@ CLEANFILES=doxygen-build.stamp

DOX=reference.doxygen
DOXSOURCES=mainpage.dox transport.dox porting.dox fsm.png fsm.eps \
../jack/jack.h ../jack/types.h ../jack/transport.h \
../jack/ringbuffer.h ../jack/thread.h ../jack/port.h \
../example-clients/simple_client.c ../example-clients/inprocess.c
../example-clients/inprocess.c \
../example-clients/simple_client.c \
../jack/intclient.h \
../jack/jack.h \
../jack/port.h \
../jack/ringbuffer.h \
../jack/thread.h \
../jack/transport.h \
../jack/types.h

EXTRA_DIST=mainpage.dox transport.dox fsm.png fsm.eps porting.dox



+ 3
- 1
doc/mainpage.dox View File

@@ -63,7 +63,7 @@ programs are not all running synchronously.
Using JACK within your program is very simple, and typically consists
of just:

- calling @ref jack_client_open to connect to the JACK server.
- calling @ref jack_client_open() to connect to the JACK server.
- registering "ports" to enable data to be moved to and from
your application.
- registering a "process callback" which will be called at the
@@ -83,6 +83,8 @@ internal client "plugin" that runs within the JACK server process.
The JACK programming interfaces are described in several header files:

- @ref jack.h "<jack/jack.h>" defines most of the JACK interfaces.
- @ref intclient.h "<jack/intclient.h>" defines interfaces for
loading and unloading JACK internal clients.
- @ref ringbuffer.h "<jack/ringbuffer.h>" defines a simple API for
using lock-free ringbuffers. These are a good way to pass data
between threads, when streaming realtime data to slower media, like


+ 5
- 4
doc/reference.doxygen.in View File

@@ -365,13 +365,14 @@ WARN_LOGFILE =
INPUT = @top_srcdir@/doc/mainpage.dox \
@top_srcdir@/doc/transport.dox \
@top_srcdir@/doc/porting.dox \
@top_srcdir@/example-clients/inprocess.c \
@top_srcdir@/example-clients/simple_client.c \
@top_srcdir@/jack/intclient.h \
@top_srcdir@/jack/jack.h \
@top_srcdir@/jack/types.h \
@top_srcdir@/jack/transport.h \
@top_srcdir@/jack/ringbuffer.h \
@top_srcdir@/jack/thread.h \
@top_srcdir@/example-clients/simple_client.c \
@top_srcdir@/example-clients/inprocess.c
@top_srcdir@/jack/transport.h \
@top_srcdir@/jack/types.h

# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp


+ 4
- 4
example-clients/inprocess.c View File

@@ -48,16 +48,16 @@ inprocess (jack_nframes_t nframes, void *arg)

/**
* This required entry point is called after the client is loaded by
* jack_internal_client_new().
* jack_internal_client_load().
*
* @param client pointer to JACK client structure.
* @param so_data character string passed from jack_internal_client_new().
* @param load_init character string passed to the load operation.
*
* @return 0 if successful; otherwise jack_finish() will be called and
* the client terminated immediately.
*/
int
jack_initialize (jack_client_t *client, const char *so_data)
jack_initialize (jack_client_t *client, const char *load_init)
{
port_pair_t *pp = malloc (sizeof (port_pair_t));

@@ -96,7 +96,7 @@ jack_initialize (jack_client_t *client, const char *so_data)
/**
* This required entry point is called immediately before the client
* is unloaded, which could happen due to a call to
* jack_internal_client_close(), or a nonzero return from either
* jack_internal_client_unload(), or a nonzero return from either
* jack_initialize() or inprocess().
*
* @param arg the same parameter provided to inprocess().


+ 4
- 4
example-clients/intime.c View File

@@ -122,11 +122,11 @@ timecode (jack_transport_state_t state, jack_nframes_t nframes,

/* after internal client loaded */
int
jack_initialize (jack_client_t *client, const char *arg)
jack_initialize (jack_client_t *client, const char *load_init)
{
JackTimebaseCallback callback = timebbt;

int rc = sscanf(arg, " %f/%f, %lf bpm ", &time_beats_per_bar,
int rc = sscanf(load_init, " %f/%f, %lf bpm ", &time_beats_per_bar,
&time_beat_type, &time_beats_per_minute);

if (rc > 0) {
@@ -134,8 +134,8 @@ jack_initialize (jack_client_t *client, const char *arg)
time_beats_per_bar, time_beat_type,
time_beats_per_minute);
} else {
int len = strlen(arg);
if ((len > 0) && (strncmp(arg, "timecode", len) == 0))
int len = strlen(load_init);
if ((len > 0) && (strncmp(load_init, "timecode", len) == 0))
callback = timecode;
}



+ 155
- 20
example-clients/ipload.c View File

@@ -1,34 +1,169 @@
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <getopt.h>
#include <jack/jack.h>
#include <jack/intclient.h>

jack_client_t *client;
jack_intclient_t intclient;
char *client_name;
char *intclient_name;
char *load_name;
char *load_init = NULL;
char *server_name = NULL;
int wait_opt = 0;

void
signal_handler (int sig)
{
jack_status_t status;

fprintf (stderr, "signal received, unloading...");
status = jack_internal_client_unload (client, intclient);
if (status & JackFailure)
fprintf (stderr, "(failed), status = 0x%x\n", status);
else
fprintf (stderr, "(succeeded)\n");
jack_client_close (client);
exit (0);
}

void
show_usage ()
{
fprintf (stderr, "usage: %s [ options ] client-name [ load-name "
"[ init-string]]\n\noptions:\n", client_name);
fprintf (stderr,
"\t-h, --help \t\t print help message\n"
"\t-i, --init string\t initialize string\n"
"\t-s, --server name\t select JACK server\n"
"\t-w, --wait \t\t wait for signal, then unload\n"
"\n"
);
}

int
parse_args (int argc, char *argv[])
{
int c;
int option_index = 0;
char *short_options = "hi:s:w";
struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "init", required_argument, 0, 'i' },
{ "server", required_argument, 0, 's' },
{ "wait", 0, 0, 'w' },
{ 0, 0, 0, 0 }
};

client_name = strrchr(argv[0], '/');
if (client_name == NULL) {
client_name = argv[0];
} else {
client_name++;
}

while ((c = getopt_long (argc, argv, short_options, long_options,
&option_index)) >= 0) {
switch (c) {
case 'i':
load_init = optarg;
break;
case 's':
server_name = optarg;
break;
case 'w':
wait_opt = 1;
break;
case 'h':
default:
show_usage ();
return 1;
}
}

if (optind == argc) { /* no positional args? */
show_usage ();
return 1;
}
if (optind < argc)
load_name = intclient_name = argv[optind++];

if (optind < argc)
load_name = argv[optind++];

if (optind < argc)
load_init = argv[optind++];

//fprintf (stderr, "client-name = `%s', load-name = `%s', "
// "load-init = `%s', wait = %d\n",
// intclient_name, load_name, load_init, wait_opt);

return 0; /* args OK */
}

int
main (int argc, char *argv[])
{
char *name;
char *so_name;
char *so_data;
jack_status_t status;

if (argc < 3) {
fprintf (stderr, "usage: %s client-name so-name [ so-data ]\n", argv[0]);
return -1;
/* parse and validate command arguments */
if (parse_args (argc, argv))
exit (1); /* invalid command line */

/* first, become a JACK client */
client = jack_client_open (client_name, JackServerName,
&status, server_name);
if (client == NULL) {
fprintf (stderr, "jack_client_open() failed, status = 0x%x\n",
status);
if (status & JackServerFailed) {
fprintf (stderr, "Unable to connect to JACK server\n");
}
exit (1);
}
if (status & JackServerStarted) {
fprintf (stderr, "JACK server started\n");
}
if (status & JackNameNotUnique) {
client_name = jack_get_client_name(client);
fprintf (stderr, "unique name `%s' assigned\n", client_name);
}

name = argv[1];
so_name = argv[2];
if (argc < 4) {
so_data = "";
} else {
so_data = argv[3];
/* then, load the internal client */
intclient = jack_internal_client_load (client, intclient_name,
(JackLoadName|JackLoadInit),
&status, load_name, load_init);
if (status & JackFailure) {
fprintf (stderr, "could not load %s, status = %d\n",
load_name, status);
return 2;
}
if (jack_internal_client_new (name, so_name, so_data) != 0) {
fprintf (stderr, "could not load %s\n", so_name);
return -1;
} else {
fprintf (stdout, "%s is running.\n", name);
return 0;
if (status & JackNameNotUnique) {
intclient_name =
jack_get_internal_client_name (client, intclient);
fprintf (stderr, "unique internal client name `%s' assigned\n",
intclient_name);
}

fprintf (stdout, "%s is running.\n", load_name);

if (wait_opt) {
/* define a signal handler to unload the client, then
* wait for it to exit */
signal (SIGQUIT, signal_handler);
signal (SIGTERM, signal_handler);
signal (SIGHUP, signal_handler);
signal (SIGINT, signal_handler);

while (1) {
sleep (1);
}
}

return 0;
}

+ 66
- 1
example-clients/ipunload.c View File

@@ -1,9 +1,74 @@
#include <string.h>
#include <stdio.h>
#include <jack/jack.h>
#include <jack/intclient.h>

int
main (int argc, char *argv[])
{
jack_internal_client_close (argv[1]);
char *my_name;
char *client_name;
jack_client_t *client;
jack_status_t status;
jack_intclient_t intclient;

/* validate args */
if ((argc < 2) || (argc > 3)) {
fprintf (stderr, "usage: %s client-name [ server-name ]]\n",
argv[0]);
return 1;
}

/* use `basename $0` for my own client name */
my_name = strrchr(argv[0], '/');
if (my_name == 0) {
my_name = argv[0];
} else {
my_name++;
}

/* first, become a JACK client */
if (argc > 2) {
client = jack_client_open (my_name,
(JackServerName|JackNoStartServer),
&status, argv[2]);
} else {
client = jack_client_open (my_name, JackNoStartServer, &status);
}

if (client == NULL) {
if (status & JackServerFailed) {
fprintf (stderr, "JACK server not running.\n");
} else {
fprintf (stderr, "JACK open failed, status = 0x%0x\n",
status);
}
exit (1);
}

/* then, get the internal client handle */
client_name = argv[1];
intclient = jack_internal_client_handle (client, client_name, &status);
if (status & JackFailure) {
fprintf (stderr, "client %s not found.\n", client_name);
exit (2);
}

/* now, unload the internal client */
status = jack_internal_client_unload (client, intclient);
if (status & JackFailure) {
if (status & JackNoSuchClient) {
fprintf (stderr, "client %s is gone.\n",
client_name);
} else {
fprintf (stderr, "could not unload %s, returns 0x%0x\n",
client_name, status);
}
exit (3);
} else {
fprintf (stdout, "%s unloaded.\n", client_name);
}

return 0;
}


+ 13
- 3
example-clients/lsp.c View File

@@ -37,6 +37,7 @@ int
main (int argc, char *argv[])
{
jack_client_t *client;
jack_status_t status;
const char **ports, **connections;
unsigned int i, j;
int show_con = 0;
@@ -87,10 +88,19 @@ main (int argc, char *argv[])
}
}

/* try to become a client of the JACK server */
/* Open a client connection to the JACK server. Starting a
* new server only to list its ports seems pointless, so we
* specify JackNoStartServer. */
//JOQ: need a new server name option

if ((client = jack_client_new ("lsp")) == 0) {
fprintf (stderr, "jack server not running?\n");
client = jack_client_open ("lsp", JackNoStartServer, &status);
if (client == NULL) {
if (status & JackServerFailed) {
fprintf (stderr, "JACK server not running\n");
} else {
fprintf (stderr, "jack_client_open() failed, "
"status = 0x%x\n", status);
}
return 1;
}



+ 1
- 1
example-clients/simple_client.c View File

@@ -93,7 +93,7 @@ main (int argc, char *argv[])

client = jack_client_open (client_name, options, &status, server_name);
if (client == NULL) {
fprintf (stderr, "jack_client_open() failed, status = %d\n",
fprintf (stderr, "jack_client_open() failed, status = 0x%x\n",
status);
if (status & JackServerFailed) {
fprintf (stderr, "Unable to connect to JACK server\n");


+ 2
- 0
jack/Makefile.am View File

@@ -3,6 +3,7 @@ MAINTAINERCLEANFILES = Makefile.in version.h
libjackincludedir = $(includedir)/jack

libjackinclude_HEADERS = \
intclient.h \
jack.h \
ringbuffer.h \
thread.h \
@@ -26,4 +27,5 @@ noinst_HEADERS = \
shm.h \
start.h \
unlock.h \
varargs.h \
version.h

+ 13
- 0
jack/engine.h View File

@@ -162,6 +162,12 @@ int jack_engine_load_driver (jack_engine_t *engine,
JSList * driver_params);
void jack_dump_configuration(jack_engine_t *engine, int take_lock);

/* private engine functions */
void jack_engine_reset_rolling_usecs (jack_engine_t *engine);
int internal_client_request (void* ptr, jack_request_t *request);
int jack_get_fifo_fd (jack_engine_t *engine,
unsigned int which_fifo);

extern jack_client_internal_t *
jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id);

@@ -187,4 +193,11 @@ static inline unsigned int jack_power_of_two (unsigned int n)
return !(n & (n - 1));
}

/* Internal port handling interfaces for JACK engine. */
void jack_port_clear_connections (jack_engine_t *engine,
jack_port_internal_t *port);
void jack_port_registration_notify (jack_engine_t *, jack_port_id_t, int);
void jack_port_release (jack_engine_t *engine, jack_port_internal_t *);
void jack_sort_graph (jack_engine_t *engine);

#endif /* __jack_engine_h__ */

+ 130
- 0
jack/intclient.h View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2004 Jack O'Quin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*
* $Id$
*/

#ifndef __jack_intclient_h__
#define __jack_intclient_h__

#ifdef __cplusplus
extern "C" {
#endif

#include <jack/types.h>

/**
* Get an internal client's name. This is useful when @ref
* JackUseExactName was not specified on jack_internal_client_load()
* and @ref JackNameNotUnique status was returned. In that case, the
* actual name will differ from the @a client_name requested.
*
* @param client requesting JACK client's handle.
*
* @param intclient handle returned from jack_internal_client_load()
* or jack_internal_client_handle().
*
* @return NULL if unsuccessful, otherwise pointer to the internal
* client name obtained from the heap via malloc(). The caller should
* free() this storage when no longer needed.
*/
char *jack_get_internal_client_name (jack_client_t *client,
jack_intclient_t intclient);

/**
* Return the @ref jack_intclient_t handle for an internal client
* running in the JACK server.
*
* @param client requesting JACK client's handle.
*
* @param client_name for the internal client of no more than
* jack_client_name_size() characters. The name scope is local to the
* current server.
*
* @param status (if non-NULL) an address for JACK to return
* information from this operation. This status word is formed by
* OR-ing together the relevant @ref JackStatus bits.
*
* @return Opaque internal client handle if successful. If 0, the
* internal client was not found, and @a *status includes the @ref
* JackNoSuchClient and @ref JackFailure bits.
*/
jack_intclient_t jack_internal_client_handle (jack_client_t *client,
const char *client_name,
jack_status_t *status);

/**
* Load an internal client into the JACK server.
*
* Internal clients run inside the JACK server process. They can use
* most of the same functions as external clients. Each internal
* client is built as a shared object module, which must declare
* jack_initialize() and jack_finish() entry points called at load and
* unload times. See @ref inprocess.c for an example.
*
* @param client loading JACK client's handle.
*
* @param client_name of at most jack_client_name_size() characters
* for the internal client to load. The name scope is local to the
* current server.
*
* @param options formed by OR-ing together @ref JackOptions bits.
* Only the @ref JackLoadOptions bits are valid.
*
* @param status (if non-NULL) an address for JACK to return
* information from the load operation. This status word is formed by
* OR-ing together the relevant @ref JackStatus bits.
*
* <b>Optional parameters:</b> depending on corresponding [@a options
* bits] additional parameters may follow @a status (in this order).
*
* @arg [@ref JackLoadName] <em>(char *) load_name</em> is the shared
* object file from which to load the new internal client (otherwise
* use the @a client_name).
*
* @arg [@ref JackLoadInit] <em>(char *) load_init</em> an arbitary
* string passed to the internal client's jack_initialize() routine
* (otherwise NULL), of no more than @ref JACK_LOAD_INIT_LIMIT bytes.
*
* @return Opaque internal client handle if successful. If this is 0,
* the load operation failed, the internal client was not loaded, and
* @a *status includes the @ref JackFailure bit.
*/
jack_intclient_t jack_internal_client_load (jack_client_t *client,
const char *client_name,
jack_options_t options,
jack_status_t *status, ...);
/**
* Unload an internal client from a JACK server. This calls the
* intclient's jack_finish() entry point then removes it. See @ref
* inprocess.c for an example.
*
* @param client unloading JACK client's handle.
*
* @param intclient handle returned from jack_internal_client_load() or
* jack_internal_client_attach().
*
* @return 0 if successful, otherwise @ref JackStatus bits.
*/
jack_status_t jack_internal_client_unload (jack_client_t *client,
jack_intclient_t intclient);

#ifdef __cplusplus
}
#endif

#endif /* __jack_intclient_h__ */

+ 16
- 9
jack/internal.h View File

@@ -217,7 +217,7 @@ typedef volatile struct {
/* external clients: set by libjack
* internal clients: set by engine */
int (*deliver_request)(void*, jack_request_t*);
int (*deliver_request)(void*, jack_request_t*); /* JOQ: 64/32 bug! */
void *deliver_arg;

/* for engine use only */
@@ -239,10 +239,9 @@ typedef struct {

typedef struct {

int32_t status; /* messy name overloading */
uint32_t protocol_v;
jack_status_t status;

jack_status_t open_status; /* used for open() */
jack_shm_info_t client_shm;
jack_shm_info_t engine_shm;

@@ -253,12 +252,9 @@ typedef struct {

char name[JACK_CLIENT_NAME_SIZE]; /* unique name, if assigned */

/* these two are valid only if the connect request
was for type == ClientDriver.
*/

jack_client_control_t *client_control; /* JOQ: 64/32 problem */
jack_control_t *engine_control; /* JOQ: 64/32 problem */
/* these two are valid only for internal clients */
jack_client_control_t *client_control;
jack_control_t *engine_control;

#ifdef JACK_USE_MACH_THREADS
/* specific resources for server/client real-time thread communication */
@@ -294,6 +290,10 @@ typedef enum {
SetBufferSize = 16,
FreeWheel = 17,
StopFreeWheel = 18,
IntClientHandle = 19,
IntClientLoad = 20,
IntClientName = 21,
IntClientUnload = 22
} RequestType;

struct _jack_request {
@@ -320,6 +320,13 @@ struct _jack_request {
jack_client_id_t client_id;
int32_t conditional;
} timebase;
struct {
jack_options_t options;
jack_client_id_t id;
char name[JACK_CLIENT_NAME_SIZE];
char path[PATH_MAX+1];
char init[JACK_LOAD_INIT_LIMIT];
} intclient;
jack_client_id_t client_id;
jack_nframes_t nframes;
jack_time_t timeout;


+ 34
- 24
jack/jack.h View File

@@ -47,22 +47,25 @@ extern "C" {
* @ref JackUseExactName option, the server will modify this name to
* create a unique variant, if needed.
*
* @param options formed by OR-ing together @ref JackOpenOptions bits.
* @param options formed by OR-ing together @ref JackOptions bits.
* Only the @ref JackOpenOptions bits are allowed.
*
* @param status (if non-NULL) an address for JACK to return
* information from the open operation. This status word is formed by
* OR-ing together the relevant @ref JackOpenStatus bits.
* OR-ing together the relevant @ref JackStatus bits.
*
* <b>Optional parameters:</b>
*
* @arg @a server_name <tt>(char *)</tt> selects from among several
* possible concurrent server instances. This parameter must follow
* @a status if the @ref JackServerName option is specified.
* Otherwise, "default" is assumed. Server names are unique to each
* user.
* <b>Optional parameters:</b> depending on corresponding [@a options
* bits] additional parameters may follow @a status (in this order).
*
* @arg [@ref JackServerName] <em>(char *) server_name</em> selects
* from among several possible concurrent server instances. Server
* names are unique to each user. If unspecified, use "default"
* unless \$JACK_DEFAULT_SERVER is defined in the process environment.
*
* @return Opaque client handle if successful. If this is NULL, the
* open operation failed, and the caller is not a JACK client.
* open operation failed, @a *status includes @ref JackFailure and the
* caller is not a JACK client.
*/
jack_client_t *jack_client_open (const char *client_name,
jack_options_t options,
@@ -110,30 +113,37 @@ int jack_client_name_size (void);
* JackNameNotUnique status was returned. In that case, the actual
* name will differ from the @a client_name requested.
*/
char *jack_get_client_name (jack_client_t* client);
char *jack_get_client_name (jack_client_t *client);

/**
* Attempt to load an internal client into the Jack server.
* Load an internal client into the Jack server.
*
* Internal clients run inside the JACK server process. They can use
* most of the same functions as external clients. Each internal
* client must declare jack_initialize() and jack_finish() entry
* points, called at load and unload times. See inprocess.c for an
* example of how to write an internal client.
*
* Internal clients run within the JACK server process. They can use
* of the same functions as external clients. Each internal client
* must declare jack_initialize() and jack_finish() entry points,
* called at load and unload times. See inprocess.c for an example of
* how to write an internal client.
* @deprecated Please use jack_internal_client_load().
*
* @param client_name of at most jack_client_name_size() characters.
* @param so_name A path to a shared object file containing the code
* for the new client.
* @param so_data An arbitary string containing information to be
* passed to the jack_initialize() routine of the new client.
*
* @param load_name of a shared object file containing the code for
* the new client.
*
* @param load_init an arbitary string passed to the jack_initialize()
* routine of the new client (may be NULL).
*
* @return 0 if successful.
*/
int jack_internal_client_new (const char *client_name, const char *so_name,
const char *so_data);
int jack_internal_client_new (const char *client_name,
const char *load_name,
const char *load_init);

/**
* Removes an internal client from a JACK server.
* Remove an internal client from a JACK server.
*
* @return 0 on success, otherwise a non-zero error code
* @deprecated Please use jack_internal_client_load().
*/
void jack_internal_client_close (const char *client_name);



+ 66
- 24
jack/types.h View File

@@ -1,5 +1,6 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004 Jack O'Quin
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -43,6 +44,19 @@ typedef uint32_t jack_nframes_t;
*/
typedef uint64_t jack_time_t;

/**
* Maximum size of @a load_init string passed to an internal client
* jack_initialize() function via jack_internal_client_load().
*/
#define JACK_LOAD_INIT_LIMIT 1024

/**
* jack_intclient_t is an opaque type representing a loaded internal
* client. You may only access it using the API provided in @ref
* intclient.h "<jack/intclient.h>".
*/
typedef uint64_t jack_intclient_t;

/**
* jack_port_t is an opaque type. You may only access it using the
* API provided.
@@ -227,9 +241,9 @@ enum JackPortFlags {
};

/**
* jack_client_open() option bits
* @ref jack_options_t bits
*/
enum JackOpenOptions {
enum JackOptions {

/**
* Null value to use when no option bits are needed.
@@ -251,32 +265,50 @@ enum JackOpenOptions {
JackUseExactName = 0x02,

/**
* Open with optional @a char @a *server_name parameter.
*
* @warning This option has not yet been implemented.
* Open with optional <em>(char *) server_name</em> parameter.
* <b>Warning: this option is not yet implemented.</b>
*/
JackServerName = 0x04,

/**
* Load internal client from optional <em>(char *)
* load_name</em>. Otherwise use the @a client_name.
*/
JackServerName = 0x04
JackLoadName = 0x08,

/**
* Pass optional <em>(char *) load_init</em> string to the
* jack_initialize() entry point of an internal client.
*/
JackLoadInit = 0x10
};

/* options mask does not include unimplemented features */
#define JackValidOptions (JackNoStartServer|JackUseExactName)
/** Valid options for opening an external client. */
#define JackOpenOptions (JackServerName|JackNoStartServer|JackUseExactName)

/** Valid options for loading an internal client. */
#define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName)

/**
* jack_client_open() request options are formed by AND-ing together
* @ref JackOpenOptions bits.
* Options for several JACK operations, formed by OR-ing together the
* relevant @ref JackOptions bits.
*/
typedef enum JackOpenOptions jack_options_t;
typedef enum JackOptions jack_options_t;

/**
* jack_client_open() status bits
* @ref jack_status_t bits
*/
enum JackOpenStatus {
enum JackStatus {

/**
* Overall operation failed.
*/
JackFailure = 0x01,

/**
* The open request failed because it contained an invalid or
* unsupported option.
* The operation contained an invalid or unsupported option.
*/
JackInvalidOption = 0x01,
JackInvalidOption = 0x02,

/**
* The desired client name was not unique. With the @ref
@@ -287,26 +319,36 @@ enum JackOpenStatus {
* that was used. If the specified @a client_name plus these
* extra characters would be too long, the open fails instead.
*/
JackNameNotUnique = 0x02,
JackNameNotUnique = 0x04,

/**
* The JACK server was started as a result of this open.
* The JACK server was started as a result of this operation.
* Otherwise, it was running already. In either case the caller
* is now connected to jackd, so there is no race condition.
* When the server shuts down, the client will find out.
*/
JackServerStarted = 0x04,
JackServerStarted = 0x08,

/**
* Unable to connect to the JACK server.
*/
JackServerFailed = 0x10,

/**
* Communication error with the JACK server.
*/
JackServerError = 0x20,

/**
* Unable to connect to the JACK server, open failed.
* Requested client does not exist.
*/
JackServerFailed = 0x08
JackNoSuchClient = 0x40
};

/**
* The status word returned from jack_client_open() is formed by
* AND-ing together the relevant @ref JackOpenStatus bits.
* Status word returned from several JACK operations, formed by
* OR-ing together the relevant @ref JackStatus bits.
*/
typedef enum JackOpenStatus jack_status_t;
typedef enum JackStatus jack_status_t;

#endif /* __jack_types_h__ */

+ 64
- 0
jack/varargs.h View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2004 Jack O'Quin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*
* $Id$
*/

#ifndef __jack_varargs_h__
#define __jack_varargs_h__

#ifdef __cplusplus
extern "C" {
#endif

/* variable argument structure */
typedef struct {
char *server_name; /* server name */
char *load_name; /* load module name */
char *load_init; /* initialization string */
} jack_varargs_t;

static inline void
jack_varargs_init (jack_varargs_t *va)
{
memset (va, 0, sizeof(jack_varargs_t));
if ((va->server_name = getenv("JACK_DEFAULT_SERVER")) == NULL)
va->server_name = "default";
}

static inline void
jack_varargs_parse (jack_options_t options, va_list ap, jack_varargs_t *va)
{
/* initialize default settings */
jack_varargs_init (va);

if ((options & JackServerName)) {
char *sn = va_arg(ap, char *);
if (sn)
va->server_name = sn;
}
if ((options & JackLoadName))
va->load_name = va_arg(ap, char *);
if ((options & JackLoadInit))
va->load_init = va_arg(ap, char *);
}

#ifdef __cplusplus
}
#endif

#endif /* __jack_varargs_h__ */

+ 3
- 2
jackd/Makefile.am View File

@@ -23,9 +23,10 @@ bin_PROGRAMS = jackd $(CAP_PROGS)

AM_CFLAGS = $(JACK_CFLAGS) -DJACK_LOCATION=\"$(bindir)\"

jackd_SOURCES = jackd.c engine.c transengine.c
jackd_SOURCES = jackd.c engine.c clientengine.c transengine.c
jackd_LDADD = ../libjack/libjack.la $(CAP_LIBS) @OS_LDFLAGS@
noinst_HEADERS = jack_md5.h md5.h md5_loc.h transengine.h
noinst_HEADERS = jack_md5.h md5.h md5_loc.h \
clientengine.h transengine.h

BUILT_SOURCES = jack_md5.h



+ 933
- 0
jackd/clientengine.c View File

@@ -0,0 +1,933 @@
/* -*- mode: c; c-file-style: "bsd"; -*- */
/*
* Internal client handling interfaces for JACK engine.
*
* Copyright (C) 2001-2003 Paul Davis
* Copyright (C) 2004 Jack O'Quin
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/

#include <config.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include <jack/internal.h>
#include <jack/engine.h>
#include <jack/version.h>
#include <sysdeps/poll.h>

#include "clientengine.h"
#include "transengine.h"

#define JACK_ERROR_WITH_SOCKETS 10000000

static void
jack_client_disconnect (jack_engine_t *engine, jack_client_internal_t *client)

{
JSList *node;
jack_port_internal_t *port;

/* call tree **** MUST HOLD *** engine->client_lock */

for (node = client->ports; node; node = jack_slist_next (node)) {
port = (jack_port_internal_t *) node->data;
jack_port_clear_connections (engine, port);
jack_port_registration_notify (engine, port->shared->id, FALSE);
jack_port_release (engine, port);
}

jack_slist_free (client->ports);
jack_slist_free (client->fed_by);
client->fed_by = 0;
client->ports = 0;
}

int
jack_client_do_deactivate (jack_engine_t *engine,
jack_client_internal_t *client, int sort_graph)
{
/* caller must hold engine->client_lock and must have checked for and/or
* cleared all connections held by client. */
client->control->active = FALSE;

jack_transport_client_exit (engine, client);

if (!jack_client_is_internal (client) &&
engine->external_client_cnt > 0) {
engine->external_client_cnt--;
}

if (sort_graph) {
jack_sort_graph (engine);
}
return 0;
}

static void
jack_zombify_client (jack_engine_t *engine, jack_client_internal_t *client)
{
VERBOSE (engine, "removing client \"%s\" from the processing chain\n",
client->control->name);

/* caller must hold the client_lock */

/* this stops jack_deliver_event() from doing anything */

client->control->dead = TRUE;
jack_client_disconnect (engine, client);
jack_client_do_deactivate (engine, client, FALSE);
}

static void
jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
{
/* called *without* the request_lock */
unsigned int i;
JSList *node;

/* caller must hold the client_lock */

VERBOSE (engine, "removing client \"%s\"\n", client->control->name);

/* if its not already a zombie, make it so */

if (!client->control->dead) {
jack_zombify_client (engine, client);
}

if (client->control->type == ClientExternal) {

/* try to force the server thread to return from poll */
close (client->event_fd);
close (client->request_fd);

/* rearrange the pollfd array so that things work right the
next time we go into poll(2).
*/
for (i = 0; i < engine->pfd_max; i++) {
if (engine->pfd[i].fd == client->request_fd) {
if (i+1 < engine->pfd_max) {
memmove (&engine->pfd[i],
&engine->pfd[i+1],
sizeof (struct pollfd)
* (engine->pfd_max - i));
}
engine->pfd_max--;
}
}
}

for (node = engine->clients; node; node = jack_slist_next (node)) {
if (((jack_client_internal_t *) node->data)->control->id
== client->control->id) {
engine->clients =
jack_slist_remove_link (engine->clients, node);
jack_slist_free_1 (node);
break;
}
}
jack_client_delete (engine, client);

/* ignore the driver, which counts as a client. */
if (engine->temporary && (jack_slist_length(engine->clients) <= 1)) {
exit(0);
}
}

void
jack_remove_clients (jack_engine_t* engine)
{
JSList *tmp, *node;
int need_sort = FALSE;
jack_client_internal_t *client;
/* remove all dead clients */

for (node = engine->clients; node; ) {
tmp = jack_slist_next (node);
client = (jack_client_internal_t *) node->data;
if (client->error) {
/* if we have a communication problem with the
client, remove it. otherwise, turn it into
a zombie. the client will/should realize
this and will close its sockets. then
we'll end up back here again and will
finally remove the client.
*/
if (client->error >= JACK_ERROR_WITH_SOCKETS) {
VERBOSE (engine, "removing failed "
"client %s state = %s errors"
" = %d\n",
client->control->name,
client_state_names[
client->control->state
],
client->error);
jack_remove_client (engine,
(jack_client_internal_t *)
node->data);
} else {
VERBOSE (engine, "client failure: "
"client %s state = %s errors"
" = %d\n",
client->control->name,
client_state_names[
client->control->state
],
client->error);
jack_zombify_client (engine,
(jack_client_internal_t *)
node->data);
client->error = 0;
}
need_sort = TRUE;
}
node = tmp;
}

if (need_sort) {
jack_sort_graph (engine);
}
jack_engine_reset_rolling_usecs (engine);
}

static int
jack_load_client (jack_engine_t *engine, jack_client_internal_t *client,
const char *so_name)
{
const char *errstr;
char path_to_so[PATH_MAX+1];

snprintf (path_to_so, sizeof (path_to_so), ADDON_DIR "/%s.so", so_name);
client->handle = dlopen (path_to_so, RTLD_NOW|RTLD_GLOBAL);
if (client->handle == 0) {
if ((errstr = dlerror ()) != 0) {
jack_error ("%s", errstr);
} else {
jack_error ("bizarre error loading %s", so_name);
}
return -1;
}

client->initialize = dlsym (client->handle, "jack_initialize");

if ((errstr = dlerror ()) != 0) {
jack_error ("%s has no initialize() function\n", so_name);
dlclose (client->handle);
client->handle = 0;
return -1;
}

client->finish = (void (*)(void *)) dlsym (client->handle,
"jack_finish");
if ((errstr = dlerror ()) != 0) {
jack_error ("%s has no finish() function", so_name);
dlclose (client->handle);
client->handle = 0;
return -1;
}

return 0;
}

static void
jack_client_unload (jack_client_internal_t *client)
{
if (client->handle) {
if (client->finish) {
client->finish (client->control->process_arg);
}
dlclose (client->handle);
}
}

static jack_client_internal_t *
jack_client_by_name (jack_engine_t *engine, const char *name)
{
jack_client_internal_t *client = NULL;
JSList *node;

jack_lock_graph (engine);

for (node = engine->clients; node; node = jack_slist_next (node)) {
if (strcmp ((const char *) ((jack_client_internal_t *)
node->data)->control->name,
name) == 0) {
client = (jack_client_internal_t *) node->data;
break;
}
}

jack_unlock_graph (engine);
return client;
}

static jack_client_id_t
jack_client_id_by_name (jack_engine_t *engine, const char *name)
{
jack_client_id_t id = 0; /* NULL client ID */
JSList *node;

jack_lock_graph (engine);

for (node = engine->clients; node; node = jack_slist_next (node)) {
if (strcmp ((const char *) ((jack_client_internal_t *)
node->data)->control->name,
name) == 0) {
jack_client_internal_t *client =
(jack_client_internal_t *) node->data;
id = client->control->id;
break;
}
}

jack_unlock_graph (engine);
return id;
}

jack_client_internal_t *
jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id)
{
jack_client_internal_t *client = NULL;
JSList *node;

/* call tree ***MUST HOLD*** the graph lock */

for (node = engine->clients; node; node = jack_slist_next (node)) {
if (((jack_client_internal_t *) node->data)->control->id
== id) {
client = (jack_client_internal_t *) node->data;
break;
}
}

return client;
}

/* generate a unique client name
*
* returns 0 if successful, updates name in place
*/
static inline int
jack_generate_unique_name (jack_engine_t *engine, char *name)
{
int tens, ones;
int length = strlen (name);

if (length > JACK_CLIENT_NAME_SIZE - 4) {
jack_error ("%s exists and is too long to make unique", name);
return 1; /* failure */
}

/* generate a unique name by appending "-01".."-99" */
name[length++] = '-';
tens = length++;
ones = length++;
name[tens] = '0';
name[ones] = '1';
name[length] = '\0';
while (jack_client_by_name (engine, name)) {
if (name[ones] == '9') {
if (name[tens] == '9') {
jack_error ("client %s has 99 extra"
" instances already", name);
return 1; /* give up */
}
name[tens]++;
name[ones] = '0';
} else {
name[ones]++;
}
}
return 0;
}

static int
jack_client_name_invalid (jack_engine_t *engine, char *name,
jack_options_t options, jack_status_t *status)
{
/* Since this is always called from the server thread, no
* other new client will be created at the same time. So,
* testing a name for uniqueness is valid here. When called
* from jack_engine_load_driver() this is not strictly true,
* but that seems to be adequately serialized due to engine
* startup. There are no other clients at that point, anyway.
*/

if (jack_client_by_name (engine, name)) {

*status |= JackNameNotUnique;

if (options & JackUseExactName) {
jack_error ("cannot create new client; %s already"
" exists", name);
*status |= JackFailure;
return TRUE;
}

if (jack_generate_unique_name(engine, name)) {
*status |= JackFailure;
return TRUE;
}
}

return FALSE;
}

/* Set up the engine's client internal and control structures for both
* internal and external clients. */
static jack_client_internal_t *
jack_setup_client_control (jack_engine_t *engine, int fd,
ClientType type, const char *name)
{
jack_client_internal_t *client;

client = (jack_client_internal_t *)
malloc (sizeof (jack_client_internal_t));

client->request_fd = fd;
client->event_fd = -1;
client->ports = 0;
client->fed_by = 0;
client->execution_order = UINT_MAX;
client->next_client = NULL;
client->handle = NULL;
client->finish = NULL;
client->error = 0;

if (type != ClientExternal) {
client->control = (jack_client_control_t *)
malloc (sizeof (jack_client_control_t));

} else {

char shm_name[PATH_MAX+1];

snprintf (shm_name, sizeof (shm_name), "/jack-c-%s", name);
if (jack_shmalloc (shm_name,
sizeof (jack_client_control_t),
&client->control_shm)) {
jack_error ("cannot create client control block for %s",
name);
free (client);
return 0;
}

if (jack_attach_shm (&client->control_shm)) {
jack_error ("cannot attach to client control block "
"for %s (%s)", name, strerror (errno));
jack_destroy_shm (&client->control_shm);
free (client);
return 0;
}

client->control = (jack_client_control_t *)
jack_shm_addr (&client->control_shm);
}

client->control->type = type;
client->control->active = 0;
client->control->dead = FALSE;
client->control->timed_out = 0;
client->control->id = engine->next_client_id++;
strcpy ((char *) client->control->name, name);
client->subgraph_start_fd = -1;
client->subgraph_wait_fd = -1;

client->control->process = NULL;
client->control->process_arg = NULL;
client->control->bufsize = NULL;
client->control->bufsize_arg = NULL;
client->control->srate = NULL;
client->control->srate_arg = NULL;
client->control->xrun = NULL;
client->control->xrun_arg = NULL;
client->control->port_register = NULL;
client->control->port_register_arg = NULL;
client->control->graph_order = NULL;
client->control->graph_order_arg = NULL;

jack_transport_client_new (client);
#ifdef JACK_USE_MACH_THREADS
/* specific resources for server/client real-time thread
* communication */
allocate_mach_serverport(engine, client);
client->running = FALSE;
#endif

return client;
}

/* set up all types of clients */
static jack_client_internal_t *
setup_client (jack_engine_t *engine, ClientType type, char *name,
jack_options_t options, jack_status_t *status, int client_fd,
const char *object_path, const char *object_data)
{
/* called with the request_lock */
jack_client_internal_t *client;

/* validate client name, generate a unique one if appropriate */
if (jack_client_name_invalid (engine, name, options, status))
return NULL;

/* create a client struct for this name */
if ((client = jack_setup_client_control (engine, client_fd,
type, name)) == NULL) {
jack_error ("cannot create new client object");
return NULL;
}

/* only for internal clients, driver is already loaded */
if (type == ClientInternal) {
if (jack_load_client (engine, client, object_path)) {
jack_error ("cannot dynamically load client from"
" \"%s\"", object_path);
jack_client_delete (engine, client);
return NULL;
}
}

VERBOSE (engine, "new client: %s, id = %" PRIu32
" type %d @ %p fd = %d\n",
client->control->name, client->control->id,
type, client->control, client_fd);

if (jack_client_is_internal(client)) {

/* Set up the pointers necessary for the request
* system to work. The client is in the same address
* space */

client->control->deliver_request = internal_client_request;
client->control->deliver_arg = engine;
}

/* add new client to the clients list */
jack_lock_graph (engine);
engine->clients = jack_slist_prepend (engine->clients, client);
jack_engine_reset_rolling_usecs (engine);
if (jack_client_is_internal(client)) {

/* Internal clients need to make regular JACK API
* calls, which need a jack_client_t structure.
* Create one here.
*/
client->control->private_client =
jack_client_alloc_internal (client->control, engine);

jack_unlock_graph (engine);

/* Call its initialization function. This function
* may make requests of its own, so we temporarily
* release and then reacquire the request_lock. */
if (client->control->type == ClientInternal) {

pthread_mutex_unlock (&engine->request_lock);
if (client->initialize (client->control->private_client,
object_data)) {

/* failed: clean up client data */
VERBOSE (engine,
"%s jack_initialize() failed!\n",
client->control->name);
jack_lock_graph (engine);
jack_remove_client (engine, client);
jack_unlock_graph (engine);
client = NULL;
//JOQ: not clear that all allocated
//storage has been cleaned up properly.
}
pthread_mutex_lock (&engine->request_lock);
}

} else { /* external client */
if (engine->pfd_max >= engine->pfd_size) {
engine->pfd = (struct pollfd *)
realloc (engine->pfd, sizeof (struct pollfd)
* (engine->pfd_size + 16));
engine->pfd_size += 16;
}
engine->pfd[engine->pfd_max].fd = client->request_fd;
engine->pfd[engine->pfd_max].events =
POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL;
engine->pfd_max++;

jack_unlock_graph (engine);
}
return client;
}

jack_client_internal_t *
jack_setup_driver_client (jack_engine_t *engine, char *name)
{
jack_client_connect_request_t req;
jack_status_t status;
jack_client_internal_t *client;

snprintf (req.name, sizeof (req.name), "%s", name);

pthread_mutex_lock (&engine->request_lock);
client = setup_client (engine, ClientDriver, name, JackUseExactName,
&status, -1, NULL, NULL);
pthread_mutex_unlock (&engine->request_lock);

return client;
}

static jack_status_t
handle_unload_client (jack_engine_t *engine, jack_client_id_t id)
{
/* called *without* the request_lock */
jack_client_internal_t *client;
jack_status_t status = (JackNoSuchClient|JackFailure);

jack_lock_graph (engine);

if ((client = jack_client_internal_by_id (engine, id))) {
VERBOSE (engine, "unloading client \"%s\"\n",
client->control->name);
jack_remove_client (engine, client);
status = 0;
}

jack_unlock_graph (engine);

return status;
}

int
jack_new_client_request (jack_engine_t *engine, int client_fd)
{
/* called *without* the request_lock */
jack_client_internal_t *client;
jack_client_connect_request_t req;
jack_client_connect_result_t res;

res.status = 0;

if (read (client_fd, &req, sizeof (req)) != sizeof (req)) {
jack_error ("cannot read connection request from client");
return -1;
}

if (!req.load) { /* internal client close? */

int rc = -1;
jack_client_id_t id;

if ((id = jack_client_id_by_name(engine, req.name))) {
rc = handle_unload_client (engine, id);
}
/* close does not send a reply */
return rc;
}
pthread_mutex_lock (&engine->request_lock);
client = setup_client (engine, req.type, req.name,
req.options, &res.status, client_fd,
req.object_path, req.object_data);
pthread_mutex_unlock (&engine->request_lock);
if (client == NULL) {
return -1;
}
res.protocol_v = jack_protocol_version;
res.client_shm = client->control_shm;
res.engine_shm = engine->control_shm;
res.realtime = engine->control->real_time;
res.realtime_priority = engine->rtpriority - 1;
strncpy (res.name, req.name, sizeof(res.name));

#ifdef JACK_USE_MACH_THREADS
/* Mach port number for server/client communication */
res.portnum = client->portnum;
#endif
if (jack_client_is_internal(client)) {
res.client_control = client->control;
res.engine_control = engine->control;
} else {
strcpy (res.fifo_prefix, engine->fifo_prefix);
}
if (write (client->request_fd, &res, sizeof (res)) != sizeof (res)) {
jack_error ("cannot write connection response to client");
jack_client_delete (engine, client);
return -1;
}

if (jack_client_is_internal (client)) {
close (client_fd);
}

return 0;
}

int
jack_client_activate (jack_engine_t *engine, jack_client_id_t id)
{
jack_client_internal_t *client;
JSList *node;
int ret = -1;

jack_lock_graph (engine);

for (node = engine->clients; node; node = jack_slist_next (node)) {

if (((jack_client_internal_t *) node->data)->control->id
== id) {
client = (jack_client_internal_t *) node->data;
client->control->active = TRUE;

jack_transport_activate(engine, client);

/* we call this to make sure the FIFO is
* built+ready by the time the client needs
* it. we don't care about the return value at
* this point.
*/

jack_get_fifo_fd (engine,
++engine->external_client_cnt);
jack_sort_graph (engine);

ret = 0;
break;
}
}

jack_unlock_graph (engine);
return ret;
}

int
jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id)
{
JSList *node;
int ret = -1;

jack_lock_graph (engine);

for (node = engine->clients; node; node = jack_slist_next (node)) {

jack_client_internal_t *client =
(jack_client_internal_t *) node->data;

if (client->control->id == id) {
JSList *portnode;
jack_port_internal_t *port;

for (portnode = client->ports; portnode;
portnode = jack_slist_next (portnode)) {
port = (jack_port_internal_t *) portnode->data;
jack_port_clear_connections (engine, port);
}

ret = jack_client_do_deactivate (engine, client, TRUE);
break;
}
}

jack_unlock_graph (engine);

return ret;
}

int
jack_client_socket_error (jack_engine_t *engine, int fd)
{
jack_client_internal_t *client = 0;
JSList *node;

#ifndef DEFER_CLIENT_REMOVE_TO_AUDIO_THREAD

jack_lock_graph (engine);

for (node = engine->clients; node; node = jack_slist_next (node)) {

if (jack_client_is_internal((jack_client_internal_t *)
node->data)) {
continue;
}

if (((jack_client_internal_t *) node->data)->request_fd == fd) {
client = (jack_client_internal_t *) node->data;
break;
}
}

if (client) {
VERBOSE (engine, "removing disconnected client %s state = "
"%s errors = %d\n", client->control->name,
client_state_names[client->control->state],
client->error);
jack_remove_client(engine, client);
jack_sort_graph (engine);
}

jack_unlock_graph (engine);

#else /* DEFER_CLIENT_REMOVE_TO_AUDIO_THREAD */

jack_lock_graph (engine);

for (node = engine->clients; node; node = jack_slist_next (node)) {

if (jack_client_is_internal((jack_client_internal_t *)
node->data)) {
continue;
}

if (((jack_client_internal_t *) node->data)->request_fd == fd) {
client = (jack_client_internal_t *) node->data;
if (client->error < JACK_ERROR_WITH_SOCKETS) {
client->error += JACK_ERROR_WITH_SOCKETS;
}
break;
}
}

jack_unlock_graph (engine);

#endif /* DEFER_CLIENT_REMOVE_TO_AUDIO_THREAD */

return 0;
}

void
jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client)
{
if (jack_client_is_internal (client)) {

jack_client_unload (client);
free (client->control->private_client);
free ((void *) client->control);

} else {
/* release the client segment, mark it for
destruction, and free up the shm registry
information so that it can be reused.
*/

jack_release_shm (&client->control_shm);
jack_destroy_shm (&client->control_shm);
}

free (client);
}

void
jack_intclient_handle_request (jack_engine_t *engine, jack_request_t *req)
{
jack_client_internal_t *client;

req->status = 0;
if ((client = jack_client_by_name (engine, req->x.intclient.name))) {
req->x.intclient.id = client->control->id;
} else {
req->status |= (JackNoSuchClient|JackFailure);
}
}

void
jack_intclient_load_request (jack_engine_t *engine, jack_request_t *req)
{
/* called with the request_lock */
jack_client_internal_t *client;
jack_status_t status = 0;

VERBOSE (engine, "load internal client %s from %s, init `%s', "
"options: 0x%x\n", req->x.intclient.name,
req->x.intclient.path, req->x.intclient.init,
req->x.intclient.options);

client = setup_client (engine, ClientInternal, req->x.intclient.name,
req->x.intclient.options, &status, -1,
req->x.intclient.path, req->x.intclient.init);

if (status & JackFailure) {
req->x.intclient.id = 0;
VERBOSE (engine, "load failed, status = 0x%x\n", status);
} else {
req->x.intclient.id = client->control->id;
}
req->status = status;
}

void
jack_intclient_name_request (jack_engine_t *engine, jack_request_t *req)
{
jack_client_internal_t *client;

jack_lock_graph (engine);
if ((client = jack_client_internal_by_id (engine,
req->x.intclient.id))) {
strncpy ((char *) req->x.intclient.name,
(char *) client->control->name,
sizeof (req->x.intclient.name));
req->status = 0;
} else {
req->status = (JackNoSuchClient|JackFailure);
}
jack_unlock_graph (engine);
}

void
jack_intclient_unload_request (jack_engine_t *engine, jack_request_t *req)
{
/* Called with the request_lock, but we need to call
* handle_unload_client() *without* it. */

if (req->x.intclient.id) {
pthread_mutex_unlock (&engine->request_lock);
req->status =
handle_unload_client (engine, req->x.intclient.id);
pthread_mutex_lock (&engine->request_lock);
} else {
VERBOSE (engine, "invalid unload request\n");
req->status = JackFailure;
}
}

+ 51
- 0
jackd/clientengine.h View File

@@ -0,0 +1,51 @@
/*
* Internal client handling interfaces for JACK engine.
*
* Copyright (C) 2004 Jack O'Quin
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

static char *client_state_names[] = {
"Not triggered",
"Triggered",
"Running",
"Finished"
};

static inline int
jack_client_is_internal (jack_client_internal_t *client)
{
return (client->control->type == ClientInternal) ||
(client->control->type == ClientDriver);
}

int jack_client_activate (jack_engine_t *engine, jack_client_id_t id);
int jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id);
void jack_client_delete (jack_engine_t *engine,
jack_client_internal_t *client);
int jack_client_socket_error (jack_engine_t *engine, int fd);
void jack_intclient_handle_request (jack_engine_t *engine,
jack_request_t *req);
void jack_intclient_load_request (jack_engine_t *engine,
jack_request_t *req);
void jack_intclient_name_request (jack_engine_t *engine,
jack_request_t *req);
void jack_intclient_unload_request (jack_engine_t *engine,
jack_request_t *req);
int jack_new_client_request (jack_engine_t *engine, int client_fd);
void jack_remove_clients (jack_engine_t* engine);
jack_client_internal_t *jack_setup_driver_client (jack_engine_t *engine,
char *name);

+ 91
- 891
jackd/engine.c
File diff suppressed because it is too large
View File


+ 2
- 2
jackd/jackd.c View File

@@ -138,8 +138,8 @@ jack_main (jack_driver_desc_t * driver_desc, JSList * driver_params)

if ((engine = jack_engine_new (realtime, realtime_priority,
do_mlock, do_unlock,
temporary, verbose, client_timeout, port_max,
getpid(), drivers)) == 0) {
temporary, verbose, client_timeout,
port_max, getpid(), drivers)) == 0) {
fprintf (stderr, "cannot create engine\n");
return -1;
}


+ 6
- 0
libjack/ChangeLog View File

@@ -1,3 +1,9 @@
2004-11-27 Jack O'Quin <joq@io.com>

* new API functions: jack_internal_client_handle(),
jack_internal_client_load(), jack_get_internal_client_name(),
jack_internal_client_unload(). See: <jack/intclient.h>.

2004-10-14 Jack O'Quin <joq@io.com>

* new API function: jack_get_xrun_delayed_usecs()


+ 4
- 3
libjack/Makefile.am View File

@@ -1,16 +1,17 @@
MAINTAINERCLEANFILES = Makefile.in

SOURCE_FILES = \
shm.c \
client.c \
driver.c \
unlock.c \
intclient.c \
pool.c \
port.c \
ringbuffer.c \
shm.c \
thread.c \
timestamps.c \
transclient.c
transclient.c \
unlock.c

lib_LTLIBRARIES = libjack.la



+ 69
- 63
libjack/client.c View File

@@ -45,6 +45,7 @@
#include <jack/shm.h>
#include <jack/unlock.h>
#include <jack/thread.h>
#include <jack/varargs.h>

#include <sysdeps/time.h>
JACK_TIME_GLOBAL_DECL; /* One instance per process. */
@@ -146,12 +147,13 @@ oop_client_deliver_request (void *ptr, jack_request_t *req)
int
jack_client_deliver_request (const jack_client_t *client, jack_request_t *req)
{
/* indirect through the function pointer that was set
either by jack_client_new() (external) or handle_new_client()
in the server.
*/
/* indirect through the function pointer that was set either
* by jack_client_open() or by jack_new_client_request() in
* the server.
*/

return client->control->deliver_request (client->control->deliver_arg, req);
return client->control->deliver_request (client->control->deliver_arg,
req);
}

jack_client_t *
@@ -344,7 +346,7 @@ server_connect (const char *server_name)
}

//JOQ: temporary debug code
// fprintf (stderr, "connecting to %s server\n", server_name);
//fprintf (stderr, "DEBUG: connecting to `%s' server\n", server_name);

//JOQ: use server_name as part of socket path
addr.sun_family = AF_UNIX;
@@ -412,7 +414,7 @@ server_event_connect (jack_client_t *client)

/* Exec the JACK server in this process. Does not return. */
static void
_start_server (const char *server_cmd)
_start_server (void)
{
FILE* fp = 0;
char filename[255];
@@ -426,11 +428,6 @@ _start_server (const char *server_cmd)
int good = 0;
int ret;

//JOQ:
// if (start_command) {
// parse user-supplied command
// } else {

snprintf(filename, 255, "%s/.jackdrc", getenv("HOME"));
fp = fopen(filename, "r");

@@ -501,7 +498,7 @@ _start_server (const char *server_cmd)
}

int
start_server (jack_options_t options, const char *server_cmd)
start_server (jack_options_t options)
{
if ((options & JackNoStartServer)
|| (getenv("JACK_NO_START_SERVER") != NULL)) {
@@ -521,7 +518,7 @@ start_server (jack_options_t options, const char *server_cmd)
case 0: /* child process */
switch (fork()) {
case 0: /* grandchild process */
_start_server(server_cmd);
_start_server();
_exit (99); /* exec failed */
case -1:
_exit (98);
@@ -537,18 +534,15 @@ start_server (jack_options_t options, const char *server_cmd)
}

static int
jack_request_client (ClientType type, const char* client_name,
const char* so_name, const char* so_data,
jack_client_connect_result_t *res, int *req_fd,
jack_options_t options, jack_status_t *status,
const char *server_name, const char *server_cmd)
jack_request_client (ClientType type,
const char* client_name, jack_options_t options,
jack_status_t *status, jack_varargs_t *va,
jack_client_connect_result_t *res, int *req_fd)
{
jack_client_connect_request_t req;

*req_fd = -1;

memset (&req, 0, sizeof (req));

req.options = options;

if (strlen (client_name) >= sizeof (req.name)) {
@@ -559,48 +553,54 @@ jack_request_client (ClientType type, const char* client_name,
return -1;
}

if (strlen (so_name) > sizeof (req.object_path) - 1) {
if (va->load_name
&& (strlen (va->load_name) > sizeof (req.object_path) - 1)) {
jack_error ("\"%s\" is too long to be used as a JACK shared"
" object name.\n"
"Please use %lu characters or less.",
so_name, sizeof (req.object_path) - 1);
va->load_name, sizeof (req.object_path) - 1);
return -1;
}

if (strlen (so_data) > sizeof (req.object_data) - 1) {
if (va->load_init
&& (strlen (va->load_init) > sizeof (req.object_data) - 1)) {
jack_error ("\"%s\" is too long to be used as a JACK shared"
" object data string.\n"
"Please use %lu characters or less.",
so_data, sizeof (req.object_data) - 1);
va->load_init, sizeof (req.object_data) - 1);
return -1;
}

if ((*req_fd = server_connect (server_name)) < 0) {
if ((*req_fd = server_connect (va->server_name)) < 0) {
int trys;
if (start_server(options, server_cmd)) {
*status |= JackServerFailed;
if (start_server(options)) {
*status |= (JackFailure|JackServerFailed);
goto fail;
}
trys = 5;
do {
sleep(1);
if (--trys < 0) {
*status |= JackServerFailed;
*status |= (JackFailure|JackServerFailed);
goto fail;
}
} while ((*req_fd = server_connect (server_name)) < 0);
} while ((*req_fd = server_connect (va->server_name)) < 0);
*status |= JackServerStarted;
}

req.load = TRUE;
req.type = type;
snprintf (req.name, sizeof (req.name), "%s", client_name);
snprintf (req.object_path, sizeof (req.object_path), "%s", so_name);
snprintf (req.object_data, sizeof (req.object_data), "%s", so_data);
snprintf (req.name, sizeof (req.name),
"%s", client_name);
snprintf (req.object_path, sizeof (req.object_path),
"%s", va->load_name);
snprintf (req.object_data, sizeof (req.object_data),
"%s", va->load_init);

if (write (*req_fd, &req, sizeof (req)) != sizeof (req)) {
jack_error ("cannot send request to jack server (%s)",
strerror (errno));
*status |= (JackFailure|JackServerError);
goto fail;
}

@@ -608,33 +608,36 @@ jack_request_client (ClientType type, const char* client_name,

if (errno == 0) {
/* server shut the socket */
jack_error ("could not attach as client "
"(duplicate client name?)");
jack_error ("could not attach as client");
*status |= (JackFailure|JackServerError);
goto fail;
}
if (errno == ECONNRESET) {
jack_error ("could not attach as jack client (server has exited)");
jack_error ("could not attach as JACK client "
"(server has exited)");
*status |= (JackFailure|JackServerError);
goto fail;
}
jack_error ("cannot read regsponse from jack server (%s)",
jack_error ("cannot read response from jack server (%s)",
strerror (errno));
*status |= (JackFailure|JackServerError);
goto fail;
}

*status |= res->open_status; /* return server status bits */
*status |= res->status; /* return server status bits */

//JOQ: fixme overloading confusion
if (res->status) {
jack_error ("could not attach as client "
"(duplicate client name?)");
if (*status & JackFailure) {
jack_error ("could not attach as client");
*status |= JackServerError;
goto fail;
}

if (res->protocol_v != jack_protocol_version){
jack_error ("application linked against incompatible libjack"
" version.");
*status |= (JackFailure|JackServerError);
goto fail;
}

@@ -735,9 +738,8 @@ jack_client_open (const char *client_name,
jack_status_t *status, ...)
{
/* optional arguments: */
char *server_name = "default"; /* server name */
char *server_cmd = NULL; /* server command (not implemented) */
va_list ap; /* variable argument pointer */
jack_varargs_t va; /* variable arguments */

int req_fd = -1;
int ev_fd = -1;
@@ -748,21 +750,17 @@ jack_client_open (const char *client_name,

if (status == NULL) /* no status from caller? */
status = &my_status; /* use local status word */

*status = 0;

/* validate parameters */
if ((options & ~JackValidOptions)) {
*status |= JackInvalidOption;
if ((options & ~JackOpenOptions)) {
*status |= (JackFailure|JackInvalidOption);
return NULL;
}

/* parse optional arguments */
/* parse variable arguments */
va_start (ap, status);
if ((options & JackServerName))
server_name = va_arg(ap, char *);
//if ((options & JackServerCmd)) /* not implemented */
// server_cmd = va_arg(ap, char *);
jack_varargs_parse (options, ap, &va);
va_end (ap);

/* External clients need this initialized. It is already set
@@ -775,9 +773,8 @@ jack_client_open (const char *client_name,
return NULL;
}

if (jack_request_client (ClientExternal, client_name, "", "",
&res, &req_fd, options, status,
server_name, server_cmd)) {
if (jack_request_client (ClientExternal, client_name, options, status,
&va, &res, &req_fd)) {
return NULL;
}

@@ -893,19 +890,24 @@ jack_get_client_name (jack_client_t *client)
}

int
jack_internal_client_new (const char *client_name, const char *so_name, const char *so_data)
jack_internal_client_new (const char *client_name,
const char *so_name, const char *so_data)
{
jack_client_connect_result_t res;
int req_fd;
jack_varargs_t va;
jack_status_t status;
jack_options_t options = JackUseExactName;

if (getenv("JACK_START_SERVER") == NULL)
options |= JackNoStartServer;

return jack_request_client (ClientInternal, client_name, so_name,
so_data, &res, &req_fd,
options, &status, NULL, NULL);
jack_varargs_init (&va);
va.load_name = (char *) so_name;
va.load_init = (char *) so_data;

return jack_request_client (ClientInternal, client_name,
options, &status, &va, &res, &req_fd);
}

void
@@ -913,16 +915,21 @@ jack_internal_client_close (const char *client_name)
{
jack_client_connect_request_t req;
int fd;
char *server_name;

if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL)
server_name = "default";

req.load = FALSE;
snprintf (req.name, sizeof (req.name), "%s", client_name);
if ((fd = server_connect (NULL)) < 0) {
if ((fd = server_connect (server_name)) < 0) {
return;
}

if (write (fd, &req, sizeof (req)) != sizeof(req)) {
jack_error ("cannot deliver ClientUnload request to JACK server.");
jack_error ("cannot deliver ClientUnload request to JACK "
"server.");
}
/* no response to this request */
@@ -931,7 +938,6 @@ jack_internal_client_close (const char *client_name)
return;
}


int
jack_set_freewheel (jack_client_t* client, int onoff)
{
@@ -1489,7 +1495,7 @@ jack_activate (jack_client_t *client)

req.type = SetClientCapabilities;
req.x.client_id = client->control->id;
jack_client_deliver_request (client, &req);

if (req.status) {


+ 187
- 0
libjack/intclient.c View File

@@ -0,0 +1,187 @@
/* -*- mode: c; c-file-style: "bsd"; -*- */
/*
* Copyright (C) 2004 Jack O'Quin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*
* $Id$
*/

#include <config.h>

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include <jack/internal.h>
#include <jack/intclient.h>
#include <jack/varargs.h>

#include "local.h"

static jack_intclient_t
jack_intclient_request(RequestType type, jack_client_t *client,
const char* client_name, jack_options_t options,
jack_status_t *status, jack_varargs_t *va)
{
jack_request_t req;

memset (&req, 0, sizeof (req));

if (strlen (client_name) >= sizeof (req.x.intclient.name)) {
jack_error ("\"%s\" is too long for a JACK client name.\n"
"Please use %lu characters or less.",
client_name, sizeof (req.x.intclient.name));
return 0;
}

if (va->load_name
&& (strlen (va->load_name) > sizeof (req.x.intclient.path) - 1)) {
jack_error ("\"%s\" is too long for a shared object name.\n"
"Please use %lu characters or less.",
va->load_name, sizeof (req.x.intclient.path) - 1);
*status |= (JackFailure|JackInvalidOption);
return 0;
}

if (va->load_init
&& (strlen (va->load_init) > sizeof (req.x.intclient.init) - 1)) {
jack_error ("\"%s\" is too long for internal client init "
"string.\nPlease use %lu characters or less.",
va->load_init, sizeof (req.x.intclient.init) - 1);
*status |= (JackFailure|JackInvalidOption);
return 0;
}

req.type = type;
req.x.intclient.options = options;
strncpy (req.x.intclient.name, client_name,
sizeof (req.x.intclient.name));
if (va->load_name)
strncpy (req.x.intclient.path, va->load_name,
sizeof (req.x.intclient.path));
if (va->load_init)
strncpy (req.x.intclient.init, va->load_init,
sizeof (req.x.intclient.init));

jack_client_deliver_request (client, &req);

*status |= req.status;

if (*status & JackFailure)
return 0;

return req.x.intclient.id;
}

char *
jack_get_internal_client_name (jack_client_t *client,
jack_intclient_t intclient)
{
jack_request_t req;
char *name;

memset (&req, 0, sizeof (req));
req.type = IntClientName;
req.x.intclient.options = JackNullOption;
req.x.intclient.id = intclient;

jack_client_deliver_request (client, &req);

if (req.status & JackFailure)
return NULL;

/* allocate storage for returning the name */
name = malloc (strlen (req.x.intclient.name));
strcpy (name, req.x.intclient.name);

return name;
}

jack_intclient_t
jack_internal_client_handle (jack_client_t *client,
const char *client_name,
jack_status_t *status)
{
jack_request_t req;
jack_status_t my_status;

if (status == NULL) /* no status from caller? */
status = &my_status; /* use local status word */
*status = 0;

memset (&req, 0, sizeof (req));
req.type = IntClientHandle;
req.x.intclient.options = JackNullOption;
strncpy (req.x.intclient.name, client_name,
sizeof (req.x.intclient.name));

*status = jack_client_deliver_request (client, &req);

return req.x.intclient.id;
}

jack_intclient_t
jack_internal_client_load (jack_client_t *client,
const char *client_name,
jack_options_t options,
jack_status_t *status, ...)
{
va_list ap;
jack_varargs_t va;
jack_status_t my_status;

if (status == NULL) /* no status from caller? */
status = &my_status; /* use local status word */
*status = 0;

/* validate parameters */
if ((options & ~JackLoadOptions)) {
*status |= (JackFailure|JackInvalidOption);
return 0;
}

/* parse variable arguments */
va_start (ap, status);
jack_varargs_parse (options, ap, &va);
va_end (ap);

return jack_intclient_request (IntClientLoad, client, client_name,
options, status, &va);
}

jack_status_t
jack_internal_client_unload (jack_client_t *client,
jack_intclient_t intclient)
{
jack_request_t req;
jack_status_t status;

if (intclient) {

memset (&req, 0, sizeof (req));
req.type = IntClientUnload;
req.x.intclient.options = JackNullOption;
req.x.intclient.id = intclient;
jack_client_deliver_request (client, &req);
status = req.status;

} else { /* intclient is null */
status = (JackNoSuchClient|JackFailure);
}

return status;
}

+ 5
- 2
libjack/local.h View File

@@ -43,8 +43,11 @@ struct _jack_client {

};

extern int jack_client_deliver_request (const jack_client_t *client, jack_request_t *req);
extern jack_port_t *jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control);
extern int jack_client_deliver_request (const jack_client_t *client,
jack_request_t *req);
extern jack_port_t *jack_port_new (const jack_client_t *client,
jack_port_id_t port_id,
jack_control_t *control);

extern void *jack_zero_filled_buffer;



Loading…
Cancel
Save