5Copyright (C) 2003 - 2020 Jasem Mutlaq
6Copyright (C) 2003 - 2006 Elwood C. Downey
8This library is free software;
9you can redistribute it and / or
10modify it under the terms of the GNU Lesser General Public
11License as published by the Free Software Foundation;
13version 2.1 of the License, or (at your option) any later version.
15This library is distributed in the hope that it will be useful,
16 but WITHOUT
ANY WARRANTY;
17without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19Lesser General Public License
for more details.
21You should have received a copy of the GNU Lesser General Public
22License along with
this library;
23if not, write to the Free Software
24Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
77static pthread_mutex_t rosc_mutex = PTHREAD_MUTEX_INITIALIZER;
79static ROSC *propCache = NULL;
80static int nPropCache = 0;
82static ROSC *rosc_new()
84 assert_mem(propCache = (
ROSC *)(realloc(propCache, (nPropCache + 1) *
sizeof *propCache)));
85 return &propCache[nPropCache++];
88static void rosc_add(
const char *propName,
const char *devName,
IPerm perm,
const void *ptr,
int type)
90 ROSC *SC = rosc_new();
99static ROSC *rosc_find(
const char *propName,
const char *devName)
101 for (
int i = 0; i < nPropCache; i++)
102 if (!strcmp(propName, propCache[i].propName) && !strcmp(devName, propCache[i].devName))
103 return &propCache[i];
108static void rosc_add_unique(
const char *propName,
const char *devName,
IPerm perm,
const void *ptr,
int type)
110 pthread_mutex_lock(&rosc_mutex);
112 if (rosc_find(propName, devName) == NULL)
113 rosc_add(propName, devName, perm, ptr,
type);
115 pthread_mutex_unlock(&rosc_mutex);
121void IDDeleteVA(
const char *dev,
const char *name,
const char *
fmt, va_list ap)
132void IDDelete(
const char *dev,
const char *name,
const char *
fmt, ...)
146 if (snooped_device && snooped_device[0])
163 if (snooped_device && snooped_device[0])
189 if (!strcmp(rtag,
"getProperties"))
198 fprintf(stderr,
"%s: getProperties missing version\n",
me);
204 fprintf(stderr,
"%s: client version %g > %g\n",
me, v,
INDIV);
216 pthread_mutex_lock(&rosc_mutex);
219 pthread_mutex_unlock(&rosc_mutex);
259 if (!strcmp(rtag,
"setNumberVector") || !strcmp(rtag,
"setTextVector") || !strcmp(rtag,
"setLightVector") ||
260 !strcmp(rtag,
"setSwitchVector") || !strcmp(rtag,
"setBLOBVector") || !strcmp(rtag,
"defNumberVector") ||
261 !strcmp(rtag,
"defTextVector") || !strcmp(rtag,
"defLightVector") || !strcmp(rtag,
"defSwitchVector") ||
262 !strcmp(rtag,
"defBLOBVector") || !strcmp(rtag,
"message") || !strcmp(rtag,
"delProperty"))
270 if (
crackDN(root, &dev, &name, msg) < 0)
273 pthread_mutex_lock(&rosc_mutex);
274 if (rosc_find(name, dev) == NULL)
276 pthread_mutex_unlock(&rosc_mutex);
277 snprintf(msg,
MAXRBUF,
"Property %s is not defined in %s.", name, dev);
280 pthread_mutex_unlock(&rosc_mutex);
283 for (
int i = 0; i < nPropCache; i++)
285 if (!strcmp(propCache[i].propName, name) && !strcmp(propCache[i].devName, dev))
287 if (propCache[i].perm ==
IP_RO)
289 snprintf(msg,
MAXRBUF,
"Cannot set read-only property %s", name);
299 if (!strcmp(rtag,
"newNumberVector"))
301 static double *doubles = NULL;
302 static char **names = NULL;
311 if (strcmp(
tagXMLEle(ep),
"oneNumber") == 0)
320 assert_mem(doubles = (
double *)realloc(doubles, maxn *
sizeof *doubles));
321 assert_mem(names = (
char **)realloc(names, maxn *
sizeof *names));
332 indi_locale_C_numeric_pop(orig);
338 IDMessage(dev,
"[ERROR] %s: newNumberVector with no valid members", name);
342 if (!strcmp(rtag,
"newSwitchVector"))
345 static char **names = NULL;
352 if (strcmp(
tagXMLEle(ep),
"oneSwitch") == 0)
361 assert_mem(names = (
char **)realloc(names, maxn *
sizeof *names));
385 IDMessage(dev,
"[ERROR] %s: newSwitchVector with no valid members", name);
389 if (!strcmp(rtag,
"newTextVector"))
391 static char **texts = NULL;
392 static char **names = NULL;
398 if (strcmp(
tagXMLEle(ep),
"oneText") == 0)
406 assert_mem(texts = (
char **)realloc(texts, maxn *
sizeof *texts));
407 assert_mem(names = (
char **)realloc(names, maxn *
sizeof *names));
420 IDMessage(dev,
"[ERROR] %s: set with no valid members", name);
424 if (!strcmp(rtag,
"newBLOBVector"))
426 static char **blobs = NULL;
427 static char **names = NULL;
428 static char **formats = NULL;
429 static int *blobsizes = NULL;
430 static int *sizes = NULL;
437 if (strcmp(
tagXMLEle(ep),
"oneBLOB") == 0)
448 assert_mem(blobs = (
char **)realloc(blobs, maxn *
sizeof *blobs));
449 assert_mem(names = (
char **)realloc(names, maxn *
sizeof *names));
450 assert_mem(formats = (
char **)realloc(formats, maxn *
sizeof *formats));
451 assert_mem(sizes = (
int *)realloc(sizes, maxn *
sizeof *sizes));
452 assert_mem(blobsizes = (
int *)realloc(blobsizes, maxn *
sizeof *blobsizes));
459 assert_mem(blobs[n] = (
char*)malloc(3 * bloblen / 4));
472 ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
473 for (
int i = 0; i < n; i++)
477 IDMessage(dev,
"[ERROR] %s: newBLOBVector with no valid members", name);
481 snprintf(msg,
MAXRBUF,
"Unknown command: %s", rtag);
485int IUReadConfig(
const char *filename,
const char *dev,
const char *property,
int silent,
char errmsg[])
488 XMLEle *root = NULL, *fproot = NULL;
503 snprintf(errmsg,
MAXRBUF,
"Unable to parse config XML: %s", whynot);
508 if (
nXMLEle(fproot) > 0 && silent != 1)
509 IDMessage(dev,
"[INFO] Loading device configuration...");
514 if (
crackDN(root, &rdev, &rname, errmsg) < 0)
522 if (strcmp(dev, rdev))
525 if ((property && !strcmp(property, rname)) || property == NULL)
533 if (
nXMLEle(fproot) > 0 && silent != 1)
534 IDMessage(dev,
"[INFO] Device configuration applied.");
547 snprintf(configFileName,
MAXRBUF,
"%s", source_config);
550 if (getenv(
"INDICONFIG"))
551 snprintf(configFileName,
MAXRBUF,
"%s", getenv(
"INDICONFIG"));
553 snprintf(configFileName,
MAXRBUF,
"%s/.indi/%s_config.xml", getenv(
"HOME"), dev);
557 snprintf(configDefaultFileName,
MAXRBUF,
"%s", dest_config);
558 else if (getenv(
"INDICONFIG"))
559 snprintf(configDefaultFileName,
MAXRBUF,
"%s.default", getenv(
"INDICONFIG"));
561 snprintf(configDefaultFileName,
MAXRBUF,
"%s/.indi/%s_config.xml.default", getenv(
"HOME"), dev);
564 if (access(configDefaultFileName, F_OK))
566 FILE *fpin = fopen(configFileName,
"r");
569 FILE *fpout = fopen(configDefaultFileName,
"w");
573 while ((ch = getc(fpin)) != EOF)
595 XMLEle *root = NULL, *fproot = NULL;
598 int propertyFound = 0;
621 if (
crackDN(root, &rdev, &rname, errmsg) < 0)
629 if (strcmp(property->
device, rdev))
632 if (!strcmp(property->
name, rname))
636 int oneSwitchIndex = 0;
638 for (oneSwitch =
nextXMLEle(root, 1); oneSwitch != NULL; oneSwitch =
nextXMLEle(root, 0), oneSwitchIndex++)
642 *index = oneSwitchIndex;
654 return (propertyFound ? 0 : -1);
660 XMLEle *root = NULL, *fproot = NULL;
685 if (
crackDN(root, &rdev, &rname, errmsg) < 0)
693 if (strcmp(dev, rdev))
696 if ((property && !strcmp(property, rname)) || property == NULL)
716 return (valueFound == 1 ? 0 : -1);
722 XMLEle *root = NULL, *fproot = NULL;
747 if (
crackDN(root, &rdev, &rname, errmsg) < 0)
755 if (strcmp(dev, rdev))
758 if ((property && !strcmp(property, rname)) || property == NULL)
761 int currentIndex = 0;
762 for (oneSwitch =
nextXMLEle(root, 1); oneSwitch != NULL; oneSwitch =
nextXMLEle(root, 0), currentIndex++)
767 *index = currentIndex;
780 return (valueFound == 1 ? 0 : -1);
786 XMLEle *root = NULL, *fproot = NULL;
811 if (
crackDN(root, &rdev, &rname, errmsg) < 0)
819 if (strcmp(dev, rdev))
822 if ((property && !strcmp(property, rname)) || property == NULL)
849 XMLEle *root = NULL, *fproot = NULL;
874 if (
crackDN(root, &rdev, &rname, errmsg) < 0)
882 if (strcmp(dev, rdev))
885 if ((property && !strcmp(property, rname)) || property == NULL)
905 return (valueFound == 1 ? 0 : -1);
908int IUGetConfigText(
const char *dev,
const char *property,
const char *member,
char *value,
int len)
911 XMLEle *root = NULL, *fproot = NULL;
936 if (
crackDN(root, &rdev, &rname, errmsg) < 0)
944 if (strcmp(dev, rdev))
947 if ((property && !strcmp(property, rname)) || property == NULL)
967 return (valueFound == 1 ? 0 : -1);
996 snprintf(configDir,
MAXRBUF,
"%s/.indi/", getenv(
"HOME"));
999 snprintf(configFileName,
MAXRBUF,
"%s", filename);
1002 if (getenv(
"INDICONFIG"))
1003 snprintf(configFileName,
MAXRBUF,
"%s", getenv(
"INDICONFIG"));
1005 snprintf(configFileName,
MAXRBUF,
"%s%s_config.xml", configDir, dev);
1008 if (remove(configFileName) != 0)
1010 snprintf(errmsg,
MAXRBUF,
"Unable to purge configuration file %s. Error %s", configFileName, strerror(
errno));
1017FILE *
IUGetConfigFP(
const char *filename,
const char *dev,
const char *mode,
char errmsg[])
1024 snprintf(configDir,
MAXRBUF,
"%s/.indi/", getenv(
"HOME"));
1027 snprintf(configFileName,
MAXRBUF,
"%s", filename);
1030 if (getenv(
"INDICONFIG"))
1031 snprintf(configFileName,
MAXRBUF,
"%s", getenv(
"INDICONFIG"));
1033 snprintf(configFileName,
MAXRBUF,
"%s%s_config.xml", configDir, dev);
1036 if (stat(configDir, &st) != 0)
1038 if (mkdir(configDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
1040 snprintf(errmsg,
MAXRBUF,
"Unable to create config directory. Error %s: %s", configDir, strerror(
errno));
1047 if (stat(configFileName, &st) == 0 && st.st_uid == 0 && getuid() != 0)
1050 "Config file is owned by root! This will lead to serious errors. To fix this, run: sudo chown -R $USER:$USER ~/.indi",
1055 fp = fopen(configFileName, mode);
1058 snprintf(errmsg,
MAXRBUF,
"Unable to open config file. Error loading file %s: %s", configFileName,
1078 IDMessage(dev,
"[INFO] Saving device configuration...");
1083 IDMessage(dev,
"[INFO] Device configuration saved.");
1282static long lastBlobPingUid = 0;
1283#define BLOB_PING_PATTERN "SetBLOB/%ld"
1291 if (lastBlobPingUid) {
1335 assert(svp != NULL &&
"IUUpdateSwitch SVP is NULL");
1344 for (
int i = 0; i < n; i++)
1351 IDSetSwitch(svp,
"Error: %s is not a member of %s (%s) property.", names[i], svp->
label, svp->
name);
1362 for (
int i = 0; i < svp->
nsp; i++)
1375 IDSetSwitch(svp,
"Error: invalid state switch for property %s (%s). %s.", svp->
label, svp->
name,
1376 t_count == 0 ?
"No switch is on" :
"Too many switches are on");
1387 assert(nvp != NULL &&
"IUUpdateNumber NVP is NULL");
1389 for (
int i = 0; i < n; i++)
1395 IDSetNumber(nvp,
"Error: %s is not a member of %s (%s) property.", names[i], nvp->
label, nvp->
name);
1399 if (values[i] < np->min || values[i] > np->max)
1402 IDSetNumber(nvp,
"Error: Invalid range for %s (%s). Valid range is from %g to %g. Requested value is %g",
1403 np->label, np->name, np->min, np->max, values[i]);
1409 for (
int i = 0; i < n; i++)
1412 np->value = values[i];
1421 assert(tvp != NULL &&
"IUUpdateText TVP is NULL");
1423 for (
int i = 0; i < n; i++)
1429 IDSetText(tvp,
"Error: %s is not a member of %s (%s) property.", names[i], tvp->
label, tvp->
name);
1435 for (
int i = 0; i < n; i++)
1448 assert(bvp != NULL &&
"IUUpdateBLOB BVP is NULL");
1450 for (
int i = 0; i < n; i++)
1456 IDSetBLOB(bvp,
"Error: %s is not a member of %s (%s) property.", names[i], bvp->
label, bvp->
name);
1462 for (
int i = 0; i < n; i++)
1465 IUSaveBLOB(bp, sizes[i], blobsizes[i], blobs[i], formats[i]);
std::string fmt(const char *fmt,...)
int from64tobits_fast(char *out, const char *in, int inlen)
void ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Update data of an existing blob vector property.
void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
#define assert_mem(p)
Bails out if memory pointer is 0. Prints file and function.
IPerm
Permission hint, with respect to client.
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Implementations for common driver routines.
IText * IUFindText(const ITextVectorProperty *tvp, const char *name)
Find an IText member in a vector text property.
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
int crackDN(XMLEle *root, char **dev, char **name, char msg[])
Extract dev and name attributes from an XML element.
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
int IUSaveBLOB(IBLOB *bp, int size, int blobsize, char *blob, char *format)
Function to save blob metadata in the corresponding blob.
IBLOB * IUFindBLOB(const IBLOBVectorProperty *bvp, const char *name)
Find an IBLOB member in a vector BLOB property.
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
int crackISState(const char *str, ISState *ip)
Extract switch state (On or Off) from the supplied string.
Interface to the reference INDI C API device implementation on the Device Driver side.
BLOBHandling
How drivers handle BLOBs incoming from snooping drivers.
void IDMessageVA(const char *dev, const char *fmt, va_list ap)
int IUUpdateBLOB(IBLOBVectorProperty *bvp, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Update all BLOB members in a BLOB vector property.
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumberVA(const INumberVectorProperty *nvp, const char *fmt, va_list ap)
void IDDefSwitchVA(const ISwitchVectorProperty *svp, const char *fmt, va_list ap)
void IDSnoopBLOBs(const char *snooped_device, const char *snooped_property, BLOBHandling bh)
Function a Driver calls to control whether they will receive BLOBs from snooped devices.
void IDDefNumberVA(const INumberVectorProperty *nvp, const char *fmt, va_list ap)
void IDSetLight(const ILightVectorProperty *lvp, const char *fmt,...)
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
FILE * IUGetConfigFP(const char *filename, const char *dev, const char *mode, char errmsg[])
Open a configuration file for writing and return a configuration file FILE pointer.
void IDDelete(const char *dev, const char *name, const char *fmt,...)
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
void IDDefTextVA(const ITextVectorProperty *tvp, const char *fmt, va_list ap)
void IDSetBLOBVA(const IBLOBVectorProperty *bvp, const char *fmt, va_list ap)
#define BLOB_PING_PATTERN
void IUSaveConfigTag(FILE *fp, int ctag, const char *dev, int silent)
Add opening or closing tag to a configuration file. A configuration file root XML element is <INDIDri...
int IUGetConfigOnSwitch(const ISwitchVectorProperty *property, int *index)
IUGetConfigOnSwitch Opens configuration file and reads a single switch vector property to find the in...
int IUGetConfigOnSwitchName(const char *dev, const char *property, char *name, size_t size)
IUGetConfigOnSwitchLabel Opens configuration file and returns the name of the ON switch property,...
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
void IDSetTextVA(const ITextVectorProperty *tvp, const char *fmt, va_list ap)
void IDSnoopDevice(const char *snooped_device, const char *snooped_property)
Function a Driver calls to snoop on another Device. Snooped messages will then arrive via ISSnoopDevi...
void waitPingReply(const char *)
int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
IUGetConfigNumber Opens configuration file and reads single number property.
int IUReadConfig(const char *filename, const char *dev, const char *property, int silent, char errmsg[])
Loads and processes a configuration file. Once a configuration file is successful loaded,...
void IDDefLightVA(const ILightVectorProperty *lvp, const char *fmt, va_list ap)
void IDMessage(const char *dev, const char *fmt,...)
void IDDeleteVA(const char *dev, const char *name, const char *fmt, va_list ap)
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
int IUPurgeConfig(const char *filename, const char *dev, char errmsg[])
void IDDefBLOBVA(const IBLOBVectorProperty *bvp, const char *fmt, va_list ap)
int IUSaveDefaultConfig(const char *source_config, const char *dest_config, const char *dev)
Copies an existing configuration file into a default configuration file. If no default configuration ...
int IUGetConfigOnSwitchIndex(const char *dev, const char *property, int *index)
IUGetConfigOnSwitchIndex Opens configuration file and reads single switch property to find ON switch ...
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
void IDDefNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitchVA(const ISwitchVectorProperty *svp, const char *fmt, va_list ap)
void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
void IDDefText(const ITextVectorProperty *tvp, const char *fmt,...)
int dispatch(XMLEle *root, char msg[])
void IDDefLight(const ILightVectorProperty *lvp, const char *fmt,...)
void IDDefBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
int IUGetConfigSwitch(const char *dev, const char *property, const char *member, ISState *value)
IUGetConfigSwitch Opens configuration file and reads single switch property.
void IDSetLightVA(const ILightVectorProperty *lvp, const char *fmt, va_list ap)
void IDDefSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
void driverio_init(driverio *dio)
void driverio_finish(driverio *dio)
void IUUserIOSetLightVA(const userio *io, void *user, const ILightVectorProperty *lvp, const char *fmt, va_list ap)
void IUUserIODefLightVA(const userio *io, void *user, const ILightVectorProperty *lvp, const char *fmt, va_list ap)
void IUUserIOUpdateMinMax(const userio *io, void *user, const INumberVectorProperty *nvp)
void IUUserIODefBLOBVA(const userio *io, void *user, const IBLOBVectorProperty *b, const char *fmt, va_list ap)
void IUUserIODeleteVA(const userio *io, void *user, const char *dev, const char *name, const char *fmt, va_list ap)
void IUUserIOSetSwitchVA(const userio *io, void *user, const ISwitchVectorProperty *svp, const char *fmt, va_list ap)
void IUUserIODefTextVA(const userio *io, void *user, const ITextVectorProperty *tvp, const char *fmt, va_list ap)
void IUUserIOSetNumberVA(const userio *io, void *user, const INumberVectorProperty *nvp, const char *fmt, va_list ap)
void IUUserIODefSwitchVA(const userio *io, void *user, const ISwitchVectorProperty *s, const char *fmt, va_list ap)
void IUUserIOSetBLOBVA(const userio *io, void *user, const IBLOBVectorProperty *bvp, const char *fmt, va_list ap)
void IUUserIOEnableBLOB(const userio *io, void *user, const char *dev, const char *name, BLOBHandling blobH)
void IUUserIOPingRequest(const userio *io, void *user, const char *pingUid)
void IDUserIOMessageVA(const userio *io, void *user, const char *dev, const char *fmt, va_list ap)
void IUUserIOSetTextVA(const userio *io, void *user, const ITextVectorProperty *tvp, const char *fmt, va_list ap)
void IUUserIOConfigTag(const userio *io, void *user, int ctag)
void IUUserIOGetProperties(const userio *io, void *user, const char *dev, const char *name)
void IUUserIODefNumberVA(const userio *io, void *user, const INumberVectorProperty *n, const char *fmt, va_list ap)
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
int nXMLEle(XMLEle *ep)
Return the number of nested XML elements in a parent XML element.
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
LilXML * newLilXML()
Create a new lilxml parser.
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
void prXMLEle(FILE *fp, XMLEle *ep, int level)
Print an XML element.
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
void delLilXML(LilXML *lp)
Delete a lilxml parser.
XMLEle * readXMLFile(FILE *fp, LilXML *lp, char ynot[])
Handy wrapper to read one xml file.
int pcdatalenXMLEle(XMLEle *ep)
Return the number of characters in pcdata in an XML element.
Namespace to encapsulate INDI client, drivers, and mediator classes.
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
void ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Update the value of an existing text vector property.
void ISGetProperties(const char *dev)
Get Device Properties.
void ISSnoopDevice(XMLEle *root)
Function defined by Drivers that is called when another Driver it is snooping (by having previously c...
One Blob (Binary Large Object) descriptor.
char devName[MAXINDIDEVICE]
char propName[MAXINDINAME]
BLOB (Binary Large Object) vector property descriptor.
char device[MAXINDIDEVICE]
Light vector property descriptor.
Number vector property descriptor.
char device[MAXINDIDEVICE]
Switch vector property descriptor.
char device[MAXINDIDEVICE]
Text vector property descriptor.
char device[MAXINDIDEVICE]
void userio_xmlv1(const userio *io, void *user)
const struct userio * userio_file()