aboutsummaryrefslogtreecommitdiffstats
path: root/linux
diff options
context:
space:
mode:
Diffstat (limited to 'linux')
-rw-r--r--linux/cusblinux-1.0.c233
-rw-r--r--linux/cusblinux.c144
-rw-r--r--linux/zeroconf_avahi.c2090
3 files changed, 1295 insertions, 1172 deletions
diff --git a/linux/cusblinux-1.0.c b/linux/cusblinux-1.0.c
index 204ef53..0e3a5e8 100644
--- a/linux/cusblinux-1.0.c
+++ b/linux/cusblinux-1.0.c
@@ -10,6 +10,8 @@
#include "stdafx.h"
#include "cusb.h"
#include <libusb-1.0/libusb.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
int CUSBCloseHandle(CPhidgetHandle phid) {
int ret = 0;
@@ -106,7 +108,10 @@ int CUSBSendPacket(CPhidgetHandle phid, unsigned char *buffer) {
LOG(PHIDGET_LOG_INFO, "Device was unplugged - detach.");
return EPHIDGET_NOTATTACHED;
default:
- LOG(PHIDGET_LOG_ERROR, "libusb_control_msg failed with error code: %d", ret);
+ if(phid->interruptOutEndpoint)
+ LOG(PHIDGET_LOG_ERROR, "libusb_interrupt_transfer failed in CUSBSendPacket with error code: %d", ret);
+ else
+ LOG(PHIDGET_LOG_ERROR, "libusb_control_msg failed in CUSBSendPacket with error code: %d", ret);
return EPHIDGET_UNEXPECTED;
}
}
@@ -123,53 +128,60 @@ int CUSBSendPacket(CPhidgetHandle phid, unsigned char *buffer) {
}
int CUSBSetLabel(CPhidgetHandle phid, char *buffer) {
- int BytesWritten = 0;
- int size = buffer[0];
-
- if(size>22) return EPHIDGET_INVALID;
-
- if (!phid)
- return EPHIDGET_INVALIDARG;
-
- if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)
- && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG))
- return EPHIDGET_NOTATTACHED;
-
- if (phid->deviceHandle == NULL)
+ if(deviceSupportsGeneralUSBProtocol(phid))
{
- LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid");
- return EPHIDGET_UNEXPECTED;
+ return CPhidgetGPP_setLabel(phid, buffer);
}
-
- BytesWritten = libusb_control_transfer((libusb_device_handle *)phid->deviceHandle,
- LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
- LIBUSB_REQUEST_SET_DESCRIPTOR,
- 0x0304, /* value */
- 0x0409, /* index*/
- (char *)buffer,
- size, /* size */
- 500); /* FIXME? timeout */
-
- if(BytesWritten < 0)
+ else
{
- switch(BytesWritten)
+ int BytesWritten = 0;
+ int size = buffer[0];
+
+ if(size>22) return EPHIDGET_INVALID;
+
+ if (!phid)
+ return EPHIDGET_INVALIDARG;
+
+ if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)
+ && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG))
+ return EPHIDGET_NOTATTACHED;
+
+ if (phid->deviceHandle == NULL)
{
- case LIBUSB_ERROR_TIMEOUT: //important case?
- default:
- LOG(PHIDGET_LOG_INFO, "usb_control_msg failed with error code: %d", BytesWritten);
- return EPHIDGET_UNSUPPORTED;
+ LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid");
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ BytesWritten = libusb_control_transfer((libusb_device_handle *)phid->deviceHandle,
+ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+ LIBUSB_REQUEST_SET_DESCRIPTOR,
+ 0x0304, /* value */
+ 0x0409, /* index*/
+ (char *)buffer,
+ size, /* size */
+ 500); /* FIXME? timeout */
+
+ if(BytesWritten < 0)
+ {
+ switch(BytesWritten)
+ {
+ case LIBUSB_ERROR_TIMEOUT: //important case?
+ default:
+ LOG(PHIDGET_LOG_INFO, "usb_control_msg failed in CUSBSetLabel with error code: %d", BytesWritten);
+ return EPHIDGET_UNSUPPORTED;
+ }
}
- }
- if (BytesWritten != size)
- {
- LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSetLabel - Report Length"
- ": %d, bytes written: %d",
- size, (int)BytesWritten);
- return EPHIDGET_UNEXPECTED;
+ if (BytesWritten != size)
+ {
+ LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSetLabel - Report Length"
+ ": %d, bytes written: %d",
+ size, (int)BytesWritten);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ return EPHIDGET_OK;
}
-
- return EPHIDGET_OK;
}
/* Buffer should be at least 8 bytes long */
@@ -202,7 +214,7 @@ int CUSBReadPacket(CPhidgetHandle phid, unsigned char *buffer) {
{
// A timeout occured, but we'll just try again
case LIBUSB_ERROR_TIMEOUT:
- LOG(PHIDGET_LOG_VERBOSE, "libusb_interrupt_transfer timeout");
+ LOG(PHIDGET_LOG_VERBOSE, "libusb_interrupt_transfer timeout in CUSBReadPacket");
return EPHIDGET_TIMEOUT;
case LIBUSB_ERROR_BUSY:
//This happens when someone else calls claim_interface on this interface (a manager for ex.) - basically just wait until they release it.
@@ -216,7 +228,7 @@ int CUSBReadPacket(CPhidgetHandle phid, unsigned char *buffer) {
case LIBUSB_ERROR_PIPE:
case LIBUSB_ERROR_OVERFLOW:
default:
- LOG(PHIDGET_LOG_ERROR, "libusb_interrupt_transfer returned: %d \"%s\"", ret);
+ LOG(PHIDGET_LOG_ERROR, "libusb_interrupt_transfer in CUSBReadPacket returned: %d", ret);
goto tryagain;
}
}
@@ -248,27 +260,42 @@ tryagain:
static int getLabelString(CPhidgetHandle phid, struct libusb_device_handle *handle)
{
- int len = 0;
+ int len = 0, ret;
char labelBuf[22];
memset(labelBuf, 0, sizeof(labelBuf));
+ libusb_device *device = libusb_get_device(handle);
+ struct libusb_device_descriptor desc;
- //Note that this returns the whole descriptor, including the length and type bytes
- len = libusb_get_string_descriptor(handle, 4, 0, (char *)labelBuf, 22);
+ if((ret = libusb_get_device_descriptor(device, &desc)) != 0)
+ {
+ LOG(PHIDGET_LOG_ERROR, "libusb_get_device_descriptor failed with error code: %d", ret);
+ return EPHIDGET_UNEXPECTED;
+ }
- if(len < 0)
+ if(desc.iSerialNumber == 3)
{
- switch(len)
+
+ //Note that this returns the whole descriptor, including the length and type bytes
+ len = libusb_get_string_descriptor(handle, 4, 0, (char *)labelBuf, 22);
+
+ if(len < 0)
{
- case LIBUSB_ERROR_TIMEOUT: //important case?
- default:
- LOG(PHIDGET_LOG_INFO, "libusb_get_string_descriptor failed in CUSBGetDeviceCapabilities with error code: %d while reading label"\
- " - this probably just means the device doesn't support labels, so this is fine.", len);
+ switch(len)
+ {
+ case LIBUSB_ERROR_TIMEOUT: //important case?
+ default:
+ LOG(PHIDGET_LOG_INFO, "libusb_get_string_descriptor failed in CUSBGetDeviceCapabilities with error code: %d while reading label"\
+ " - this probably just means the device doesn't support labels, so this is fine.", len);
+ }
+ phid->label[0]='\0';
+ return EPHIDGET_OK;
}
- phid->label[0]='\0';
- return EPHIDGET_OK;
+ else
+ return decodeLabelString(labelBuf, phid->label, phid->serialNumber);
}
- else
- return decodeLabelString(labelBuf, phid->label, phid->serialNumber);
+
+ phid->label[0]='\0';
+ return EPHIDGET_OK;
}
int CUSBRefreshLabelString(CPhidgetHandle phid)
@@ -488,6 +515,15 @@ int CUSBBuildList(CPhidgetList **curList) {
getLabelString(phid, handle);
}
}
+ if (desc.iProduct) {
+ if((ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, phid->usbProduct, sizeof(phid->usbProduct))) < 0)
+ {
+ LOG(PHIDGET_LOG_ERROR, "libusb_get_string_descriptor_ascii failed with error code: %d", ret);
+ libusb_close(handle);
+ free(phid);
+ goto next;
+ }
+ }
phid->specificDevice = TRUE;
phid->attr = Phid_Device_Def[i].pdd_attr;
@@ -520,6 +556,49 @@ done:
return ret;
}
+/*
+ * Got this from libusb-0.1 because 1.0 doesn't expose driver name!
+ */
+#define USB_MAXDRIVERNAME 255
+struct usb_getdriver {
+ unsigned int interface;
+ char driver[USB_MAXDRIVERNAME + 1];
+};
+struct linux_device_handle_priv {
+ int fd;
+};
+struct list_head {
+ struct list_head *prev, *next;
+};
+struct libusb_device_handle_internal {
+ pthread_mutex_t lock;
+ unsigned long claimed_interfaces;
+ struct list_head list;
+ void *dev;
+ unsigned char os_priv[0];
+};
+static struct linux_device_handle_priv *_device_handle_priv(struct libusb_device_handle_internal *handle)
+{
+ return (struct linux_device_handle_priv *) handle->os_priv;
+}
+#define IOCTL_USB_GETDRIVER _IOW('U', 8, struct usb_getdriver)
+static int libusb_get_driver_name(libusb_device_handle *handle, int interface, char *name, unsigned int namelen)
+{
+ int fd = _device_handle_priv((struct libusb_device_handle_internal *)handle)->fd;
+ struct usb_getdriver getdrv;
+ int ret;
+
+ getdrv.interface = interface;
+ ret = ioctl(fd, IOCTL_USB_GETDRIVER, &getdrv);
+ if (ret)
+ LOG(PHIDGET_LOG_ERROR, "could not get bound driver: %d", errno);
+
+ strncpy(name, getdrv.driver, namelen - 1);
+ name[namelen - 1] = 0;
+
+ return 0;
+}
+
void CUSBCleanup(void)
{
;
@@ -535,7 +614,7 @@ int CUSBOpenHandle(CPhidgetHandle phid)
int idVendor;
int idProduct;
int serial = 0;
- int i, j,ret = EPHIDGET_OK;
+ int i, j, ret, retVal = EPHIDGET_NOTFOUND;
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(libusbContext, &list);
@@ -582,6 +661,15 @@ int CUSBOpenHandle(CPhidgetHandle phid)
serial = atol(string);
}
}
+ if (desc.iProduct) {
+ if((ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, phid->usbProduct, sizeof(phid->usbProduct))) < 0)
+ {
+ LOG(PHIDGET_LOG_ERROR, "libusb_get_string_descriptor_ascii failed with error code: %d", ret);
+ libusb_close(handle);
+ free(phid);
+ goto next;
+ }
+ }
if (serial == phid->serialNumber) {
//Detach any Kernel Drivers
if((ret = libusb_kernel_driver_active(handle, Phid_Device_Def[i].pdd_iid)) < 0)
@@ -590,20 +678,37 @@ int CUSBOpenHandle(CPhidgetHandle phid)
}
else if(ret == 1)
{
- LOG(PHIDGET_LOG_INFO, "Kernel driver is active - will attempt a detach");
- if((ret = libusb_detach_kernel_driver(handle, Phid_Device_Def[i].pdd_iid)) != 0)
+ char name[64];
+ if((ret = libusb_get_driver_name(handle, Phid_Device_Def[i].pdd_iid, name, 32)) < 0)
{
- LOG(PHIDGET_LOG_WARNING, "libusb_detach_kernel_driver failed with error code: %d", ret);
+ LOG(PHIDGET_LOG_WARNING, "libusb_get_driver_name failed with error code: %d", ret);
}
else
{
- LOG(PHIDGET_LOG_INFO, "Successfully detached kernel driver");
+ LOG(PHIDGET_LOG_INFO, "Kernel driver name: %s", name);
+ if(strncmp(name, "usbfs", 5)) //not usbfs
+ {
+ LOG(PHIDGET_LOG_INFO, "Kernel driver is active - will attempt a detach");
+ if((ret = libusb_detach_kernel_driver(handle, Phid_Device_Def[i].pdd_iid)) != 0)
+ {
+ LOG(PHIDGET_LOG_WARNING, "libusb_detach_kernel_driver failed with error code: %d", ret);
+ }
+ else
+ {
+ LOG(PHIDGET_LOG_INFO, "Successfully detached kernel driver");
+ }
+ }
+ else
+ LOG(PHIDGET_LOG_INFO, "Not detaching kernel driver - already using usbfs.");
}
}
if((ret = libusb_claim_interface(handle, Phid_Device_Def[i].pdd_iid)) != 0)
{
- LOG(PHIDGET_LOG_WARNING, "libusb_claim_interface failed with error code: %d", ret);
+ if(ret == LIBUSB_ERROR_BUSY)
+ LOG(PHIDGET_LOG_WARNING, "libusb_claim_interface failed with BUSY - probably the device is opened by another program.");
+ else
+ LOG(PHIDGET_LOG_WARNING, "libusb_claim_interface failed with error code: %d", ret);
libusb_close(handle);
}
else
@@ -632,7 +737,7 @@ int CUSBOpenHandle(CPhidgetHandle phid)
phid->attr = Phid_Device_Def[i].pdd_attr;
- ret = EPHIDGET_OK;
+ retVal = EPHIDGET_OK;
goto done;
} /* usb_claim_interface */
} /* serial matches */
@@ -656,5 +761,5 @@ done:
if(list)
libusb_free_device_list(list, 1);
- return ret;
+ return retVal;
}
diff --git a/linux/cusblinux.c b/linux/cusblinux.c
index 5e4886c..8b15d4e 100644
--- a/linux/cusblinux.c
+++ b/linux/cusblinux.c
@@ -20,6 +20,7 @@ int CUSBCloseHandle(CPhidgetHandle phid) {
int ret = 0;
int result = EPHIDGET_OK;
+
if (!phid)
return EPHIDGET_INVALIDARG;
@@ -130,53 +131,60 @@ int CUSBSendPacket(CPhidgetHandle phid, unsigned char *buffer) {
}
int CUSBSetLabel(CPhidgetHandle phid, char *buffer) {
- int BytesWritten = 0;
- int size = buffer[0];
-
- if(size>22) return EPHIDGET_INVALID;
-
- if (!phid)
- return EPHIDGET_INVALIDARG;
-
- if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)
- && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG))
- return EPHIDGET_NOTATTACHED;
-
- if (phid->deviceHandle == NULL)
+ if(deviceSupportsGeneralUSBProtocol(phid))
{
- LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid");
- return EPHIDGET_UNEXPECTED;
+ return CPhidgetGPP_setLabel(phid, buffer);
}
-
- BytesWritten = usb_control_msg(phid->deviceHandle,
- USB_ENDPOINT_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- USB_REQ_SET_DESCRIPTOR,
- 0x0304, /* value */
- 0x0409, /* index*/
- (char *)buffer,
- size, /* size */
- 500); /* FIXME? timeout */
-
- if(BytesWritten < 0)
+ else
{
- switch(BytesWritten)
+ int BytesWritten = 0;
+ int size = buffer[0];
+
+ if(size>22) return EPHIDGET_INVALID;
+
+ if (!phid)
+ return EPHIDGET_INVALIDARG;
+
+ if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG)
+ && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG))
+ return EPHIDGET_NOTATTACHED;
+
+ if (phid->deviceHandle == NULL)
{
- case -ETIMEDOUT: //important case?
- default:
- LOG(PHIDGET_LOG_INFO, "usb_control_msg failed with error code: %d \"%s\"", BytesWritten, strerror(-BytesWritten));
- return EPHIDGET_UNSUPPORTED;
+ LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid");
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ BytesWritten = usb_control_msg(phid->deviceHandle,
+ USB_ENDPOINT_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_REQ_SET_DESCRIPTOR,
+ 0x0304, /* value */
+ 0x0409, /* index*/
+ (char *)buffer,
+ size, /* size */
+ 500); /* FIXME? timeout */
+
+ if(BytesWritten < 0)
+ {
+ switch(BytesWritten)
+ {
+ case -ETIMEDOUT: //important case?
+ default:
+ LOG(PHIDGET_LOG_INFO, "usb_control_msg failed with error code: %d \"%s\"", BytesWritten, strerror(-BytesWritten));
+ return EPHIDGET_UNSUPPORTED;
+ }
}
- }
- if (BytesWritten != size)
- {
- LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSetLabel - Report Length"
- ": %d, bytes written: %d",
- size, (int)BytesWritten);
- return EPHIDGET_UNEXPECTED;
+ if (BytesWritten != size)
+ {
+ LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSetLabel - Report Length"
+ ": %d, bytes written: %d",
+ size, (int)BytesWritten);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ return EPHIDGET_OK;
}
-
- return EPHIDGET_OK;
}
/* Buffer should be at least 8 bytes long */
@@ -258,24 +266,32 @@ static int getLabelString(CPhidgetHandle phid, struct usb_dev_handle *udev)
{
int len = 0;
char labelBuf[22];
+ struct usb_device *dev;
memset(labelBuf, 0, sizeof(labelBuf));
- //Note that this returns the whole descriptor, including the length and type bytes
- len = usb_get_string(udev, 4, 0, (char *)labelBuf, 22);
+ dev = usb_device(udev);
- if(len < 0)
+ if(dev->descriptor.iSerialNumber == 3)
{
- switch(len)
+ //Note that this returns the whole descriptor, including the length and type bytes
+ len = usb_get_string(udev, 4, 0, (char *)labelBuf, 22);
+
+ if(len < 0)
{
- case -ETIMEDOUT: //important case?
- default:
- LOG(PHIDGET_LOG_INFO, "usb_get_string_simple failed in CUSBGetDeviceCapabilities with error code: %d \"%s\" while reading label - this probably just means the device doesn't support labels, so this is fine.", len, strerror(-len));
+ switch(len)
+ {
+ case -ETIMEDOUT: //important case?
+ default:
+ LOG(PHIDGET_LOG_INFO, "usb_get_string_simple failed in CUSBGetDeviceCapabilities with error code: %d \"%s\" while reading label - this probably just means the device doesn't support labels, so this is fine.", len, strerror(-len));
+ }
+ phid->label[0]='\0';
+ return EPHIDGET_OK;
}
- phid->label[0]='\0';
- return EPHIDGET_OK;
+ else
+ return decodeLabelString(labelBuf, phid->label, phid->serialNumber);
}
- else
- return decodeLabelString(labelBuf, phid->label, phid->serialNumber);
+ phid->label[0]='\0';
+ return EPHIDGET_OK;
}
int CUSBRefreshLabelString(CPhidgetHandle phid)
@@ -504,6 +520,19 @@ int CUSBBuildList(CPhidgetList **curList) {
getLabelString(phid, udev);
}
}
+ if (dev->descriptor.iProduct) {
+ if((ret = usb_get_string_simple(udev, dev->descriptor.iProduct, phid->usbProduct, sizeof(phid->usbProduct))) < 0)
+ {
+ LOG(PHIDGET_LOG_ERROR, "usb_get_string_simple failed with error code: %d \"%s\"", ret, strerror(-ret));
+ LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root");
+ if((ret = usb_close(udev)) < 0)
+ {
+ LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret));
+ }
+ free(phid);
+ goto next;
+ }
+ }
phid->specificDevice = TRUE;
phid->attr = Phid_Device_Def[i].pdd_attr;
@@ -592,6 +621,19 @@ int CUSBOpenHandle(CPhidgetHandle phid)
serial = atol(string);
}
}
+ if (dev->descriptor.iProduct) {
+ if((ret = usb_get_string_simple(udev, dev->descriptor.iProduct, phid->usbProduct, sizeof(phid->usbProduct))) < 0)
+ {
+ LOG(PHIDGET_LOG_ERROR, "usb_get_string_simple failed with error code: %d \"%s\"", ret, strerror(-ret));
+ LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root");
+ if((ret = usb_close(udev)) < 0)
+ {
+ LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret));
+ }
+ free(phid);
+ goto next;
+ }
+ }
if (serial == phid->serialNumber) {
/* On Linux, the HID driver likes to claim Phidgets - we can disconnect it here.
Maybe the user has installed the kernel drivers for the interface kit or servo - disconnect them too (does this work)
diff --git a/linux/zeroconf_avahi.c b/linux/zeroconf_avahi.c
index 9b205b3..4b81171 100644
--- a/linux/zeroconf_avahi.c
+++ b/linux/zeroconf_avahi.c
@@ -1,1057 +1,1033 @@
-#include "stdafx.h"
-#include "csocket.h"
-#include "csocketevents.h"
-#include "cphidgetlist.h"
-#include "cphidgetmanager.h"
-#include "cphidgetdictionary.h"
-#include "cphidgetsbc.h"
-#include "zeroconf.h"
-
-#include "avahi-client/client.h"
-#include "avahi-client/lookup.h"
-
-#include "avahi-common/thread-watch.h"
-#include "avahi-common/malloc.h"
-#include "avahi-common/error.h"
-#include "avahi-common/domain.h"
-
-static int UninitializeZeroconf1(int lock);
-
-struct AvahiThreadedPoll {
- void *simple_poll;
- pthread_t thread_id;
- pthread_mutex_t mutex;
- int thread_running;
- int retval;
-} ;
-
-#ifdef ZEROCONF_RUNTIME_LINKING
-typedef AvahiClient * (* avahi_client_new_type) (
- const AvahiPoll *poll_api /**< The abstract event loop API to use */,
- AvahiClientFlags flags /**< Some flags to modify the behaviour of the client library */,
- AvahiClientCallback callback /**< A callback that is called whenever the state of the client changes. This may be NULL */,
- void *userdata /**< Some arbitrary user data pointer that will be passed to the callback function */,
- int *error /**< If creation of the client fails, this integer will contain the error cause. May be NULL if you aren't interested in the reason why avahi_client_new() failed. */);
-typedef void (* avahi_client_free_type)(AvahiClient *client);
-typedef const char * (* avahi_client_get_host_name_type) (AvahiClient *);
-typedef AvahiServiceBrowser * (* avahi_service_browser_new_type) (
- AvahiClient *client,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *type,
- const char *domain,
- AvahiLookupFlags flags,
- AvahiServiceBrowserCallback callback,
- void *userdata);
-typedef AvahiServiceResolver * (* avahi_service_resolver_new_type)(
- AvahiClient *client,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *name,
- const char *type,
- const char *domain,
- AvahiProtocol aprotocol,
- AvahiLookupFlags flags,
- AvahiServiceResolverCallback callback,
- void *userdata);
-typedef int (* avahi_service_resolver_free_type)(AvahiServiceResolver *r);
-typedef AvahiRecordBrowser * (* avahi_record_browser_new_type)(
- AvahiClient *client,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *name,
- uint16_t clazz,
- uint16_t type,
- AvahiLookupFlags flags,
- AvahiRecordBrowserCallback callback,
- void *userdata);
-typedef int (* avahi_record_browser_free_type)(AvahiRecordBrowser *);
-typedef int (* avahi_service_name_join_type)(char *p, size_t size, const char *name, const char *type, const char *domain);
-typedef const char *(* avahi_strerror_type)(int error);
-typedef int (* avahi_client_errno_type) (AvahiClient*);
-typedef AvahiThreadedPoll *(* avahi_threaded_poll_new_type)(void);
-typedef void (* avahi_threaded_poll_free_type)(AvahiThreadedPoll *p);
-typedef const AvahiPoll* (* avahi_threaded_poll_get_type)(AvahiThreadedPoll *p);
-typedef int (* avahi_threaded_poll_start_type)(AvahiThreadedPoll *p);
-typedef int (* avahi_threaded_poll_stop_type)(AvahiThreadedPoll *p);
-typedef void (* avahi_threaded_poll_quit_type)(AvahiThreadedPoll *p);
-typedef void (* avahi_threaded_poll_lock_type)(AvahiThreadedPoll *p);
-typedef void (* avahi_threaded_poll_unlock_type)(AvahiThreadedPoll *p);
-typedef const char *(* avahi_client_get_version_string_type)(AvahiClient *c);
-
-avahi_service_browser_new_type avahi_service_browser_new_ptr = NULL;
-avahi_service_resolver_new_type avahi_service_resolver_new_ptr = NULL;
-avahi_service_resolver_free_type avahi_service_resolver_free_ptr = NULL;
-avahi_record_browser_new_type avahi_record_browser_new_ptr = NULL;
-avahi_record_browser_free_type avahi_record_browser_free_ptr = NULL;
-avahi_service_name_join_type avahi_service_name_join_ptr = NULL;
-avahi_client_new_type avahi_client_new_ptr = NULL;
-avahi_client_free_type avahi_client_free_ptr = NULL;
-avahi_strerror_type avahi_strerror_ptr = NULL;
-avahi_client_errno_type avahi_client_errno_ptr = NULL;
-avahi_threaded_poll_new_type avahi_threaded_poll_new_ptr = NULL;
-avahi_threaded_poll_free_type avahi_threaded_poll_free_ptr = NULL;
-avahi_threaded_poll_get_type avahi_threaded_poll_get_ptr = NULL;
-avahi_threaded_poll_start_type avahi_threaded_poll_start_ptr = NULL;
-avahi_threaded_poll_stop_type avahi_threaded_poll_stop_ptr = NULL;
-avahi_threaded_poll_quit_type avahi_threaded_poll_quit_ptr = NULL;
-avahi_threaded_poll_lock_type avahi_threaded_poll_lock_ptr = NULL;
-avahi_threaded_poll_unlock_type avahi_threaded_poll_unlock_ptr = NULL;
-avahi_client_get_version_string_type avahi_client_get_version_string_ptr = NULL;
-#else
-#define avahi_service_browser_new_ptr avahi_service_browser_new
-#define avahi_service_resolver_new_ptr avahi_service_resolver_new
-#define avahi_service_resolver_free_ptr avahi_service_resolver_free
-#define avahi_record_browser_new_ptr avahi_record_browser_new
-#define avahi_record_browser_free_ptr avahi_record_browser_free
-#define avahi_service_name_join_ptr avahi_service_name_join
-#define avahi_client_new_ptr avahi_client_new
-#define avahi_client_free_ptr avahi_client_free
-#define avahi_strerror_ptr avahi_strerror
-#define avahi_client_errno_ptr avahi_client_errno
-#define avahi_threaded_poll_new_ptr avahi_threaded_poll_new
-#define avahi_threaded_poll_free_ptr avahi_threaded_poll_free
-#define avahi_threaded_poll_get_ptr avahi_threaded_poll_get
-#define avahi_threaded_poll_start_ptr avahi_threaded_poll_start
-#define avahi_threaded_poll_stop_ptr avahi_threaded_poll_stop
-#define avahi_threaded_poll_quit_ptr avahi_threaded_poll_quit
-#define avahi_threaded_poll_lock_ptr avahi_threaded_poll_lock
-#define avahi_threaded_poll_unlock_ptr avahi_threaded_poll_unlock
-#define avahi_client_get_version_string_ptr avahi_client_get_version_string
-#endif
-
-/*
- * TXT record version - this should be 1 for a long time
- * - only need to change if we really change the TXT record format
- */
-const char *dnssd_txt_ver = "1";
-
-int Dns_sdInitialized = FALSE;
-
-static AvahiThreadedPoll *threaded_poll = NULL;
-static AvahiClient *client = NULL;
-
-static AvahiServiceBrowser *zeroconf_browse_sbc_ref = NULL;
-static AvahiServiceBrowser *zeroconf_browse_ws_ref = NULL;
-static AvahiServiceBrowser *zeroconf_browse_phidget_ref = NULL;
-
-//pthread_t dns_thread_ws, dns_thread_phid;
-
-void *avahiLibHandle = NULL;
-
-static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
- assert(c);
-
- /* Called whenever the client or server state changes */
-
- switch (state) {
- case AVAHI_CLIENT_S_RUNNING:
-
- /* The server has startup successfully and registered its host
- * name on the network */
- Dns_sdInitialized = TRUE;
- break;
-
- case AVAHI_CLIENT_FAILURE:
-
- LOG(PHIDGET_LOG_ERROR, "Client failure: %s", avahi_strerror_ptr(avahi_client_errno_ptr(c)));
- //avahi_threaded_poll_quit_ptr(threaded_poll);
-
- break;
-
- case AVAHI_CLIENT_S_COLLISION:
-
- /* Let's drop our registered services. When the server is back
- * in AVAHI_SERVER_RUNNING state we will register them
- * again with the new host name. */
-
- case AVAHI_CLIENT_S_REGISTERING:
-
- /* The server records are now being established. This
- * might be caused by a host name change. We need to wait
- * for our own records to register until the host name is
- * properly esatblished. */
-
- //if (group)
- // avahi_entry_group_reset_ptr(group);
-
- break;
-
- case AVAHI_CLIENT_CONNECTING:
- break;
- }
-}
-
-static uint8_t *InternalTXTRecordSearch
- (
- uint16_t txtLen,
- const void *txtRecord,
- const char *key,
- unsigned long *keylen
- )
-{
- uint8_t *p = (uint8_t*)txtRecord;
- uint8_t *e = p + txtLen;
- *keylen = (unsigned long) strlen(key);
- while (p<e)
- {
- uint8_t *x = p;
- p += 1 + p[0];
- if (p <= e && *keylen <= x[0] && !strncmp(key, (char*)x+1, *keylen))
- if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
- }
- return(NULL);
-}
-
-const void * TXTRecordGetValuePtr
- (
- uint16_t txtLen,
- const void *txtRecord,
- const char *key,
- uint8_t *valueLen
- )
-{
- unsigned long keylen;
- uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
- if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL
- *valueLen = (uint8_t)(item[0] - (keylen + 1));
- return (item + 1 + keylen + 1);
-}
-
-void PhidFromTXT(CPhidgetHandle phid, uint16_t txtLen, const char *txtRecord)
-{
- int i = 0;
- short txtver;
-
- uint8_t valLen = 0;
- const char *valPtr = NULL;
-
- //txt version
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "txtvers", &valLen))) return;
- txtver = (short)strtol(valPtr, NULL, 10);
-
- //Serial Number
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "serial", &valLen))) return;
- phid->serialNumber = strtol(valPtr, NULL, 10);
- phid->specificDevice = PTRUE;
-
- //version
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "version", &valLen))) return;
- phid->deviceVersion = strtol(valPtr, NULL, 10);
-
- //label
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "label", &valLen))) return;
- if(valLen > MAX_LABEL_STORAGE-1) valLen = MAX_LABEL_STORAGE-1;
- memcpy(phid->label, valPtr, valLen);
- phid->label[valLen] = '\0';
-
- //server_id
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "server_id", &valLen))) return;
- free(phid->networkInfo->zeroconf_server_id);
- if(!(phid->networkInfo->zeroconf_server_id = malloc(valLen+1))) return;
- ZEROMEM(phid->networkInfo->zeroconf_server_id, valLen+1);
- memcpy(phid->networkInfo->zeroconf_server_id, valPtr, valLen);
-
- // things added in version 2 of the txt
- if(txtver >= 2)
- {
- //Device ID
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "id", &valLen))) return;
- phid->deviceIDSpec = strtol(valPtr, NULL, 10);
-
- for(i = 1;i<PHIDGET_DEVICE_COUNT;i++)
- if(phid->deviceIDSpec == Phid_Device_Def[i].pdd_sdid) break;
- phid->deviceDef = &Phid_Device_Def[i];
- phid->attr = Phid_Device_Def[i].pdd_attr;
-
- //Device Class
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "class", &valLen))) return;
- phid->deviceID = strtol(valPtr, NULL, 10);
- phid->deviceType = Phid_DeviceName[phid->deviceID];
- }
- //Old version uses string searching, but some devices have the same name with different IDs
- else
- {
- char *name = NULL;
- char *type = NULL;
-
- //name
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "name", &valLen))) return;
- if(!(name = malloc(valLen+1))) return;
- ZEROMEM(name, valLen+1);
- memcpy(name, valPtr, valLen);
- for(i = 0;i<PHIDGET_DEVICE_COUNT;i++)
- {
- if(!strcmp(name, Phid_Device_Def[i].pdd_name))
- {
- phid->deviceIDSpec = Phid_Device_Def[i].pdd_sdid;
- phid->deviceDef = &Phid_Device_Def[i];
- phid->attr = Phid_Device_Def[i].pdd_attr;
- break;
- }
- }
- free(name);
-
- //type
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "type", &valLen))) return;
- if(!(type = malloc(valLen+1))) return;
- ZEROMEM(type, valLen+1);
- memcpy(type, valPtr, valLen);
- phid->deviceID = phidget_type_to_id(type);
- phid->deviceType = Phid_DeviceName[phid->deviceID];
- free(type);
- }
- phid->deviceUID = CPhidget_getUID(phid->deviceIDSpec, phid->deviceVersion);
-
- phid->networkInfo->mdns = PTRUE;
-
-}
-
-void SBCFromTXT(CPhidgetSBCHandle sbc, uint16_t txtLen, const char *txtRecord)
-{
- char *hversion = NULL, *txtver = NULL;
-
- uint8_t valLen = 0;
- const char *valPtr = NULL;
-
- //txt version
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "txtvers", &valLen))) return;
- if(!(txtver = malloc(valLen+1))) return;
- ZEROMEM(txtver, valLen+1);
- memcpy(txtver, valPtr, valLen);
- sbc->txtver = (short)strtol(txtver, NULL, 10);
- free(txtver);
-
- //Firmware version
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "fversion", &valLen))) return;
- if(valLen > 12) valLen = 12;
- memcpy(sbc->fversion, valPtr, valLen);
- sbc->fversion[valLen] = '\0';
-
- //Hardware version
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "hversion", &valLen))) return;
- if(!(hversion = malloc(valLen+1))) return;
- ZEROMEM(hversion, valLen+1);
- memcpy(hversion, valPtr, valLen);
- sbc->hversion = (short)strtol(hversion, NULL, 10);
- free(hversion);
-
- // things added in version 2 of the txt
- if(sbc->txtver >= 2)
- {
- //Hostname
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "hostname", &valLen))) return;
- if(valLen > 128) valLen = 128;
- memcpy(sbc->hostname, valPtr, valLen);
- sbc->hostname[valLen] = '\0';
- }
-
- // things added in version 3 of the txt
- if(sbc->txtver >= 3)
- {
- //Device Name
- if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "name", &valLen))) return;
- if(valLen > 128) valLen = 128;
- memcpy(sbc->deviceName, valPtr, valLen);
- sbc->deviceName[valLen] = '\0';
- }
- else
- {
- sprintf(sbc->deviceName, "PhidgetSBC");
- }
-}
-
-void DNSServiceResolve_CallBack(
- AvahiServiceResolver *r,
- AVAHI_GCC_UNUSED AvahiIfIndex interface,
- AVAHI_GCC_UNUSED AvahiProtocol protocol,
- AvahiResolverEvent event,
- const char *name,
- const char *type,
- const char *domain,
- const char *host_name,
- const AvahiAddress *address,
- uint16_t port,
- AvahiStringList *txt,
- AvahiLookupResultFlags flags,
- AVAHI_GCC_UNUSED void* userdata)
-{
-
- CPhidgetRemoteHandle networkInfo = (CPhidgetRemoteHandle)userdata;
- switch (event) {
- case AVAHI_RESOLVER_FAILURE:
- LOG(PHIDGET_LOG_ERROR, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- networkInfo->zeroconf_host = strdup("err");
- break;
- case AVAHI_RESOLVER_FOUND:
- {
- LOG(PHIDGET_LOG_INFO, "DNSServiceResolve_CallBack: %s",name);
- networkInfo->zeroconf_host = strdup(host_name);
- networkInfo->zeroconf_port = malloc(10);
- snprintf(networkInfo->zeroconf_port, 9, "%d", port);
- }
- }
-
- avahi_service_resolver_free_ptr(r);
-}
-
-void DNSServiceQueryRecord_Phidget_CallBack
- (
- AvahiRecordBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *name,
- uint16_t clazz,
- uint16_t type,
- const void *rdata,
- size_t size,
- AvahiLookupResultFlags flags,
- void *userdata
- )
-{
- CPhidgetHandle phid = (CPhidgetHandle)userdata;
- CPhidgetManagerList *trav;
-
- switch(event)
- {
- case AVAHI_BROWSER_NEW:
- PhidFromTXT(phid, size, rdata);
- LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_Phidget_CallBack: %s",name);
- CThread_mutex_lock(&zeroconfPhidgetsLock);
- CThread_mutex_lock(&activeRemoteManagersLock);
-
- CPhidget_setStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock);
- CPhidget_setStatusFlag(&phid->status, PHIDGET_REMOTE_FLAG, &phid->lock);
- CPhidget_setStatusFlag(&phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &phid->lock);
-
- //now add it
- CList_addToList((CListHandle *)&zeroconfPhidgets, phid, CPhidget_areExtraEqual);
- //managers
- for (trav=activeRemoteManagers; trav; trav = trav->next)
- {
- if(trav->phidm->networkInfo->requested_address==NULL
- && (trav->phidm->networkInfo->requested_serverID == NULL || !strcmp(trav->phidm->networkInfo->requested_serverID,phid->networkInfo->zeroconf_server_id)))
- {
- CList_addToList((CListHandle *)&trav->phidm->AttachedPhidgets, phid, CPhidget_areExtraEqual);
-
- if (trav->phidm->fptrAttachChange && trav->phidm->state == PHIDGETMANAGER_ACTIVE)
- trav->phidm->fptrAttachChange((CPhidgetHandle)phid, trav->phidm->fptrAttachChangeptr);
- }
- }
- CThread_mutex_unlock(&activeRemoteManagersLock);
- CThread_mutex_unlock(&zeroconfPhidgetsLock);
- break;
- case AVAHI_BROWSER_FAILURE:
- LOG(PHIDGET_LOG_ERROR, "DNSServiceQueryRecord_Phidget_CallBack returned error: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- break;
- case AVAHI_BROWSER_ALL_FOR_NOW:
- avahi_record_browser_free_ptr(b);
- case AVAHI_BROWSER_CACHE_EXHAUSTED:
- LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_Phidget_CallBack %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
- break;
- case AVAHI_BROWSER_REMOVE:
- break;
- }
-}
-
-void DNSServiceQueryRecord_SBC_CallBack
-(
- AvahiRecordBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *name,
- uint16_t clazz,
- uint16_t type,
- const void *rdata,
- size_t size,
- AvahiLookupResultFlags flags,
- void *userdata
- )
-{
- CPhidgetSBCHandle sbc = (CPhidgetSBCHandle)userdata, found_sbc;
- CPhidgetSBCManagerList *trav;
-
- switch(event)
- {
- case AVAHI_BROWSER_NEW:
- SBCFromTXT(sbc, size, rdata);
- LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_SBC_CallBack: %s",name);
- CThread_mutex_lock(&zeroconfSBCsLock);
- CThread_mutex_lock(&activeSBCManagersLock);
-
- //Check if it's in the list and if it's different, remove it to make way for the new one
- // (Sometimes, we don't get a proper detach notification)
- if(CList_findInList((CListHandle)zeroconfSBCs, sbc, CPhidgetSBC_areEqual, (void **)&found_sbc) == EPHIDGET_OK)
- {
- if(CPhidgetSBC_areExtraEqual(found_sbc, sbc) != PTRUE) //A version number has changed
- {
- //Remove from list - don't free until after detach event
- CList_removeFromList((CListHandle *)&zeroconfSBCs, found_sbc, CPhidgetSBC_areEqual, PFALSE, NULL);
-
- for (trav=activeSBCManagers; trav; trav = trav->next)
- {
- if (trav->sbcm->fptrDetachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
- trav->sbcm->fptrDetachChange((CPhidgetSBCHandle)found_sbc, trav->sbcm->fptrDetachChangeptr);
- }
-
- CPhidgetSBC_free(found_sbc);
-
- //now we fall through and add back to new one
- }
- else //Nothing has changed, we didn't remove, don't add
- {
- CPhidgetSBC_free(sbc);
- goto dontadd;
- }
- }
-
- //now add it
- CList_addToList((CListHandle *)&zeroconfSBCs, sbc, CPhidgetSBC_areEqual);
-
- //send out events
- for (trav=activeSBCManagers; trav; trav = trav->next)
- {
- if (trav->sbcm->fptrAttachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
- trav->sbcm->fptrAttachChange((CPhidgetSBCHandle)sbc, trav->sbcm->fptrAttachChangeptr);
-
- }
- dontadd:
-
- CThread_mutex_unlock(&activeSBCManagersLock);
- CThread_mutex_unlock(&zeroconfSBCsLock);
- break;
- case AVAHI_BROWSER_FAILURE:
- LOG(PHIDGET_LOG_ERROR, "DNSServiceQueryRecord_SBC_CallBack returned error: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- break;
- case AVAHI_BROWSER_ALL_FOR_NOW:
- avahi_record_browser_free_ptr(b);
- case AVAHI_BROWSER_CACHE_EXHAUSTED:
- LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_SBC_CallBack %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
- break;
- case AVAHI_BROWSER_REMOVE:
- break;
- }
-}
-
-void DNSServiceBrowse_Phidget_CallBack(
- AvahiServiceBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *name,
- const char *type,
- const char *domain,
- AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
- void* userdata)
-{
-
- CPhidgetHandle phid;
- CPhidgetManagerList *trav;
-
- int ret;
-
- switch (event) {
-
- case AVAHI_BROWSER_FAILURE:
-
- LOG(PHIDGET_LOG_WARNING, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- avahi_threaded_poll_quit_ptr(threaded_poll);
- return;
-
- case AVAHI_BROWSER_NEW:
- {
- char fullname[AVAHI_DOMAIN_NAME_MAX];
-
- if((CPhidget_create(&phid))) return;
- if((CPhidgetRemote_create(&phid->networkInfo))) return;
-
- phid->networkInfo->zeroconf_name = strdup(name);
- phid->networkInfo->zeroconf_type = strdup(type);
- phid->networkInfo->zeroconf_domain = strdup(domain);
-
- LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
-
- if((ret = avahi_service_name_join_ptr(fullname, AVAHI_DOMAIN_NAME_MAX, name, type, domain)) != AVAHI_OK)
- LOG(PHIDGET_LOG_ERROR, "Failed avahi_service_name_join_ptr '%s': %s", name, avahi_strerror_ptr(ret));
-
- if(!(avahi_record_browser_new_ptr(client, interface, protocol, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, 0, DNSServiceQueryRecord_Phidget_CallBack, phid)))
- LOG(PHIDGET_LOG_ERROR, "Failed to resolve service '%s': %s", name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- //gets added to list in callback
- }
- break;
-
- case AVAHI_BROWSER_REMOVE:
- {
- if((CPhidget_create(&phid))) return;
- if((CPhidgetRemote_create(&phid->networkInfo))) return;
-
- phid->networkInfo->zeroconf_name = strdup(name);
- phid->networkInfo->zeroconf_type = strdup(type);
- phid->networkInfo->zeroconf_domain = strdup(domain);
-
- LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
-
- //have to fill in phid manually from just the name
- int i;
- CPhidgetHandle found_phid;
- char *name_copy = strdup(name);
- for(i=0;i<strlen(name_copy);i++)
- if(name_copy[i] == '(') break;
- if(i<=1) return;
- name_copy[strlen(name_copy)-1]='\0';
- name_copy[i-1] = '\0';
- phid->serialNumber = strtol(name_copy+i+1, NULL, 10);
- phid->specificDevice = PTRUE;
- for(i = 0;i<PHIDGET_DEVICE_COUNT;i++)
- if(!strcmp(name_copy, Phid_Device_Def[i].pdd_name)) break;
- phid->deviceIDSpec = 0;
- phid->deviceDef = &Phid_Device_Def[i];
- phid->attr = Phid_Device_Def[i].pdd_attr;
- phid->deviceID = Phid_Device_Def[i].pdd_did;
- phid->deviceType = Phid_DeviceName[phid->deviceID];
- phid->networkInfo->mdns = PTRUE;
-
- CThread_mutex_lock(&zeroconfPhidgetsLock);
- CThread_mutex_lock(&activeRemoteManagersLock);
-
- CPhidget_clearStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock);
- CPhidget_setStatusFlag(&phid->status, PHIDGET_DETACHING_FLAG, &phid->lock);
-
- if(!CList_findInList((CListHandle)zeroconfPhidgets, phid, CPhidget_areEqual, (void **)&found_phid))
- {
- CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_ATTACHED_FLAG, &found_phid->lock);
- CPhidget_setStatusFlag(&found_phid->status, PHIDGET_DETACHING_FLAG, &found_phid->lock);
- CPhidget_setStatusFlag(&found_phid->status, PHIDGET_REMOTE_FLAG, &found_phid->lock);
- CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &found_phid->lock);
-
- CList_removeFromList((CListHandle *)&zeroconfPhidgets, found_phid, CPhidget_areExtraEqual, FALSE, NULL);
- //managers
- for (trav=activeRemoteManagers; trav; trav = trav->next)
- {
- if(trav->phidm->networkInfo->requested_address==NULL
- && (trav->phidm->networkInfo->requested_serverID == NULL || !strcmp(trav->phidm->networkInfo->requested_serverID,found_phid->networkInfo->zeroconf_server_id)))
- {
- CList_removeFromList((CListHandle *)&trav->phidm->AttachedPhidgets, found_phid, CPhidget_areExtraEqual, PFALSE, NULL);
-
- if (trav->phidm->fptrDetachChange && trav->phidm->state == PHIDGETMANAGER_ACTIVE)
- trav->phidm->fptrDetachChange((CPhidgetHandle)found_phid, trav->phidm->fptrDetachChangeptr);
- }
- }
- CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_DETACHING_FLAG, &found_phid->lock);
- CPhidgetRemote_free(found_phid->networkInfo);
- CPhidget_free(found_phid);
- }
- CPhidgetRemote_free(phid->networkInfo);
- CPhidget_free(phid);
-
- CThread_mutex_unlock(&activeRemoteManagersLock);
- CThread_mutex_unlock(&zeroconfPhidgetsLock);
- free(name_copy);
- }
- break;
-
- case AVAHI_BROWSER_ALL_FOR_NOW:
- case AVAHI_BROWSER_CACHE_EXHAUSTED:
- LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
- break;
- }
-}
-
-void DNSServiceBrowse_SBC_CallBack(
- AvahiServiceBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *name,
- const char *type,
- const char *domain,
- AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
- void* userdata)
-{
-
- CPhidgetSBCHandle sbc, found_sbc;
- CPhidgetSBCManagerList *trav;
- int ret;
-
- switch (event) {
-
- case AVAHI_BROWSER_FAILURE:
-
- LOG(PHIDGET_LOG_WARNING, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- avahi_threaded_poll_quit_ptr(threaded_poll);
- return;
-
- case AVAHI_BROWSER_NEW:
- {
- char fullname[AVAHI_DOMAIN_NAME_MAX];
-
- if((CPhidgetSBC_create(&sbc))) return;
- if((CPhidgetRemote_create(&sbc->networkInfo))) return;
-
- sbc->networkInfo->zeroconf_name = strdup(name);
- sbc->networkInfo->zeroconf_type = strdup(type);
- sbc->networkInfo->zeroconf_domain = strdup(domain);
- sbc->networkInfo->mdns = PTRUE;
-
- strncpy(sbc->mac, name+12, 18); //name == 'PhidgetSBC (??:??:??:??:??:??)'
- sbc->mac[17] = '\0';
-
- LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
-
- if((ret = avahi_service_name_join_ptr(fullname, AVAHI_DOMAIN_NAME_MAX, name, type, domain)) != AVAHI_OK)
- LOG(PHIDGET_LOG_ERROR, "Failed avahi_service_name_join_ptr '%s': %s", name, avahi_strerror_ptr(ret));
-
- if(!(avahi_record_browser_new_ptr(client, interface, protocol, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, 0, DNSServiceQueryRecord_SBC_CallBack, sbc)))
- LOG(PHIDGET_LOG_ERROR, "Failed to resolve service '%s': %s", name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- //gets added to list in callback
- }
- break;
-
- case AVAHI_BROWSER_REMOVE:
- {
- if((CPhidgetSBC_create(&sbc))) return;
- if((CPhidgetRemote_create(&sbc->networkInfo))) return;
-
- sbc->networkInfo->zeroconf_name = strdup(name);
- sbc->networkInfo->zeroconf_type = strdup(type);
- sbc->networkInfo->zeroconf_domain = strdup(domain);
- sbc->networkInfo->mdns = PTRUE;
-
- strncpy(sbc->mac, name+12, 18); //name == 'PhidgetSBC (??:??:??:??:??:??)'
- sbc->mac[17] = '\0';
-
- LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
-
-
- CThread_mutex_lock(&zeroconfSBCsLock);
- CThread_mutex_lock(&activeSBCManagersLock);
-
- if(CList_findInList((CListHandle)zeroconfSBCs, sbc, CPhidgetSBC_areEqual, (void **)&found_sbc) == EPHIDGET_OK)
- {
- CList_removeFromList((CListHandle *)&zeroconfSBCs, found_sbc, CPhidgetSBC_areEqual, PFALSE, NULL);
- //managers
- for (trav=activeSBCManagers; trav; trav = trav->next)
- {
- if (trav->sbcm->fptrDetachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
- trav->sbcm->fptrDetachChange((CPhidgetSBCHandle)found_sbc, trav->sbcm->fptrDetachChangeptr);
- }
- CPhidgetSBC_free(found_sbc);
- }
-
- CThread_mutex_unlock(&activeSBCManagersLock);
- CThread_mutex_unlock(&zeroconfSBCsLock);
-
- CPhidgetSBC_free(sbc);
- }
- break;
-
- case AVAHI_BROWSER_ALL_FOR_NOW:
- case AVAHI_BROWSER_CACHE_EXHAUSTED:
- LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
- break;
- }
-}
-
-void DNSServiceBrowse_ws_CallBack(
- AvahiServiceBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *name,
- const char *type,
- const char *domain,
- AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
- void* userdata)
-{
-
- switch (event) {
-
- case AVAHI_BROWSER_FAILURE:
-
- LOG(PHIDGET_LOG_ERROR, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- //avahi_threaded_poll_quit_ptr(threaded_poll);
- return;
-
- case AVAHI_BROWSER_NEW:
- {
- CPhidgetRemoteHandle networkInfo;
- if((CPhidgetRemote_create(&networkInfo))) return;
-
- networkInfo->zeroconf_name = strdup(name);
- networkInfo->zeroconf_server_id = strdup(name);
- networkInfo->zeroconf_type = strdup(type);
- networkInfo->zeroconf_domain = strdup(domain);
-
- LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
-
- CThread_mutex_lock(&zeroconfServersLock);
- CList_addToList((CListHandle *)&zeroconfServers, networkInfo, CPhidgetRemote_areEqual);
- CThread_mutex_unlock(&zeroconfServersLock);
- }
- break;
-
- case AVAHI_BROWSER_REMOVE:
- {
- CPhidgetRemoteHandle networkInfo;
- if((CPhidgetRemote_create(&networkInfo))) return;
-
- networkInfo->zeroconf_name = strdup(name);
- networkInfo->zeroconf_server_id = strdup(name);
- networkInfo->zeroconf_type = strdup(type);
- networkInfo->zeroconf_domain = strdup(domain);
- LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
-
- CThread_mutex_lock(&zeroconfServersLock);
- CList_removeFromList((CListHandle *)&zeroconfServers, networkInfo, CPhidgetRemote_areEqual, TRUE, CPhidgetRemote_free);
- CThread_mutex_unlock(&zeroconfServersLock);
- }
- break;
-
- case AVAHI_BROWSER_ALL_FOR_NOW:
- case AVAHI_BROWSER_CACHE_EXHAUSTED:
- LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
- break;
- }
-}
-
-//Does nothing in Avahi
-int cancelPendingZeroconfLookups(CPhidgetRemoteHandle networkInfo)
-{
- return EPHIDGET_OK;
-}
-
-int getZeroconfHostPort(CPhidgetRemoteHandle networkInfo)
-{
- int timeout = 200; //2000ms
-
- if(networkInfo->zeroconf_host) free(networkInfo->zeroconf_host);
- networkInfo->zeroconf_host = NULL;
- if(networkInfo->zeroconf_port) free(networkInfo->zeroconf_port);
- networkInfo->zeroconf_port = NULL;
-
- //lock the thread before accessing the client - nope, it messes up if this is from the same thread (via attach/detach)
- //avahi_threaded_poll_lock_ptr(threaded_poll);
- if (!(avahi_service_resolver_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
- networkInfo->zeroconf_name, //name
- networkInfo->zeroconf_type, // service type
- networkInfo->zeroconf_domain, //domain
- AVAHI_PROTO_UNSPEC, 0, DNSServiceResolve_CallBack, networkInfo)))
- {
- LOG(PHIDGET_LOG_ERROR, "Failed to resolve service '%s': %s", networkInfo->zeroconf_name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- //avahi_threaded_poll_unlock_ptr(threaded_poll);
- return EPHIDGET_UNEXPECTED;
- }
- //avahi_threaded_poll_unlock_ptr(threaded_poll);
-
- while(!networkInfo->zeroconf_host)
- {
- usleep(10000);
- timeout--;
- if(!timeout)
- {
- LOG(PHIDGET_LOG_ERROR, "getZeroconfHostPort didn't work (timeout)");
- return EPHIDGET_UNEXPECTED;
- }
- }
-
- if(!strcmp(networkInfo->zeroconf_host, "err"))
- {
- LOG(PHIDGET_LOG_ERROR, "getZeroconfHostPort didn't work (error)");
- free(networkInfo->zeroconf_host);
- networkInfo->zeroconf_host = NULL;
- return EPHIDGET_UNEXPECTED;
- }
-
- return EPHIDGET_OK;
-}
-
-int refreshZeroconfSBC(CPhidgetSBCHandle sbc)
-{
- return EPHIDGET_OK;
-}
-
-int refreshZeroconfPhidget(CPhidgetHandle phid)
-{
- return EPHIDGET_OK;
-}
-
-int InitializeZeroconf()
-{
- int error;
- //int ret = 1;
- int timeout = 50; //500ms
- const char *avahiVersion;
-
- CThread_mutex_lock(&zeroconfInitLock);
- if(Dns_sdInitialized)
- {
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_OK;
- }
-
-#ifdef ZEROCONF_RUNTIME_LINKING
-
- avahiLibHandle = dlopen("libavahi-client.so",RTLD_LAZY);
- if(!avahiLibHandle)
- {
- avahiLibHandle = dlopen("libavahi-client.so.3",RTLD_LAZY);
- }
- if(!avahiLibHandle)
- {
- LOG(PHIDGET_LOG_WARNING, "dlopen failed with error: %s", dlerror());
- LOG(PHIDGET_LOG_WARNING, "Assuming that zeroconf is not supported on this machine.");
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNSUPPORTED;
- }
-
- //These are always in Avahi
- if(!(avahi_client_get_version_string_ptr = (avahi_client_get_version_string_type)dlsym(avahiLibHandle, "avahi_client_get_version_string"))) goto dlsym_err;
- if(!(avahi_service_browser_new_ptr = (avahi_service_browser_new_type)dlsym(avahiLibHandle, "avahi_service_browser_new"))) goto dlsym_err;
- if(!(avahi_service_resolver_new_ptr = (avahi_service_resolver_new_type)dlsym(avahiLibHandle, "avahi_service_resolver_new"))) goto dlsym_err;
- if(!(avahi_service_resolver_free_ptr = (avahi_service_resolver_free_type)dlsym(avahiLibHandle, "avahi_service_resolver_free"))) goto dlsym_err;
- if(!(avahi_record_browser_new_ptr = (avahi_record_browser_new_type)dlsym(avahiLibHandle, "avahi_record_browser_new"))) goto dlsym_err;
- if(!(avahi_record_browser_free_ptr = (avahi_record_browser_free_type)dlsym(avahiLibHandle, "avahi_record_browser_free"))) goto dlsym_err;
- if(!(avahi_service_name_join_ptr = (avahi_service_name_join_type)dlsym(avahiLibHandle, "avahi_service_name_join"))) goto dlsym_err;
- if(!(avahi_client_new_ptr = (avahi_client_new_type)dlsym(avahiLibHandle, "avahi_client_new"))) goto dlsym_err;
- if(!(avahi_client_free_ptr = (avahi_client_free_type)dlsym(avahiLibHandle, "avahi_client_free"))) goto dlsym_err;
- if(!(avahi_strerror_ptr = (avahi_strerror_type)dlsym(avahiLibHandle, "avahi_strerror"))) goto dlsym_err;
- if(!(avahi_client_errno_ptr = (avahi_client_errno_type)dlsym(avahiLibHandle, "avahi_client_errno"))) goto dlsym_err;
-
- //These are in Avahi > 0.6.4
- if(!(avahi_threaded_poll_new_ptr = (avahi_threaded_poll_new_type)dlsym(avahiLibHandle, "avahi_threaded_poll_new"))) goto dlsym_err2;
- if(!(avahi_threaded_poll_free_ptr = (avahi_threaded_poll_free_type)dlsym(avahiLibHandle, "avahi_threaded_poll_free"))) goto dlsym_err2;
- if(!(avahi_threaded_poll_get_ptr = (avahi_threaded_poll_get_type)dlsym(avahiLibHandle, "avahi_threaded_poll_get"))) goto dlsym_err2;
- if(!(avahi_threaded_poll_start_ptr = (avahi_threaded_poll_start_type)dlsym(avahiLibHandle, "avahi_threaded_poll_start"))) goto dlsym_err2;
- if(!(avahi_threaded_poll_stop_ptr = (avahi_threaded_poll_stop_type)dlsym(avahiLibHandle, "avahi_threaded_poll_stop"))) goto dlsym_err2;
- if(!(avahi_threaded_poll_quit_ptr = (avahi_threaded_poll_quit_type)dlsym(avahiLibHandle, "avahi_threaded_poll_quit"))) goto dlsym_err2;
- if(!(avahi_threaded_poll_lock_ptr = (avahi_threaded_poll_lock_type)dlsym(avahiLibHandle, "avahi_threaded_poll_lock"))) goto dlsym_err2;
- if(!(avahi_threaded_poll_unlock_ptr = (avahi_threaded_poll_unlock_type)dlsym(avahiLibHandle, "avahi_threaded_poll_unlock"))) goto dlsym_err2;
-
- goto dlsym_good;
-
-dlsym_err:
- LOG(PHIDGET_LOG_WARNING, "dlsym failed with error: %s", dlerror());
- LOG(PHIDGET_LOG_WARNING, "Assuming that zeroconf is not supported on this machine.");
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNSUPPORTED;
-
- //Old avahi didn't have the thread functions
-dlsym_err2:
- LOG(PHIDGET_LOG_WARNING, "dlsym failed with error: %s", dlerror());
- LOG(PHIDGET_LOG_WARNING, "Avahi is too old, upgrade to at least version 0.6.4.");
- LOG(PHIDGET_LOG_WARNING, "Zeroconf will not be used on this machine.");
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNSUPPORTED;
-
-dlsym_good:
-
-#endif
-
- /* Allocate main loop object */
- if (!(threaded_poll = avahi_threaded_poll_new_ptr())) {
- LOG(PHIDGET_LOG_ERROR, "Failed to create threaded poll object.");
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNEXPECTED;
- }
-
- /* Allocate a new client */
- client = avahi_client_new_ptr(avahi_threaded_poll_get_ptr(threaded_poll), 0, client_callback, NULL, &error);
-
- /* Check wether creating the client object succeeded */
- if (!client) {
- LOG(PHIDGET_LOG_ERROR, "Failed to create client: %s", avahi_strerror_ptr(error));
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNEXPECTED;
- }
-
- //get version
- avahiVersion = avahi_client_get_version_string_ptr(client);
-
- /* Create the service browsers */
- if (!(zeroconf_browse_ws_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget_ws._tcp", NULL, 0, DNSServiceBrowse_ws_CallBack, client))) {
- LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNEXPECTED;
- }
- if (!(zeroconf_browse_phidget_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget._tcp", NULL, 0, DNSServiceBrowse_Phidget_CallBack, client))) {
- LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNEXPECTED;
- }
- if (!(zeroconf_browse_sbc_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget_sbc._tcp", NULL, 0, DNSServiceBrowse_SBC_CallBack, client))) {
- LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNEXPECTED;
- }
-
- if(avahi_threaded_poll_start_ptr(threaded_poll))
- {
- LOG(PHIDGET_LOG_ERROR, "avahi_threaded_poll_start_ptr failed");
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNEXPECTED;
- }
- //Thread is started successfully
- else
- {
- //There is a bug in at least Avahi 0.6.16 (Debian Etch default) where thread_running is not set, so quit doesn't work!!!???!?!?!?!?!?!
- //This is fixed in 0.6.24
- //So I'll set it myself here
- if(strcmp(avahiVersion, "avahi 0.6.24") < 0)
- {
- LOG(PHIDGET_LOG_INFO, "Fixing thread_running bug in avahi < 0.6.24");
- threaded_poll->thread_running = 1;
- }
- }
-
- while(!Dns_sdInitialized)
- {
- usleep(10000);
- timeout--;
- if(!timeout)
- {
- UninitializeZeroconf1(PFALSE);
- LOG(PHIDGET_LOG_ERROR, "InitializeZeroconf Seems bad... Dns_sdInitialized wasn't set to true.");
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_UNEXPECTED;
- }
- }
-
- LOG(PHIDGET_LOG_INFO, "InitializeZeroconf Seems good... (%s)",avahiVersion);
-
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_OK;
-
-}
-
-
-static int UninitializeZeroconf1(int lock)
-{
- int ret;
- /* Cleanup things */
- if(lock)
- CThread_mutex_lock(&zeroconfInitLock);
- if(Dns_sdInitialized)
- {
- if (threaded_poll)
- {
- if((ret = avahi_threaded_poll_stop_ptr(threaded_poll)) == -1)
- LOG(PHIDGET_LOG_WARNING, "avahi_threaded_poll_stop failed",ret);
- avahi_client_free_ptr(client);
- avahi_threaded_poll_free_ptr(threaded_poll);
- threaded_poll = NULL;
- client = NULL;
- }
- }
-
- Dns_sdInitialized = FALSE;
- if(lock)
- CThread_mutex_unlock(&zeroconfInitLock);
- return EPHIDGET_OK;
-}
-
-int UninitializeZeroconf()
-{
- return UninitializeZeroconf1(PTRUE);
-}
+#include "stdafx.h"
+#include "csocket.h"
+#include "csocketevents.h"
+#include "cphidgetlist.h"
+#include "cphidgetmanager.h"
+#include "cphidgetdictionary.h"
+#include "cphidgetsbc.h"
+#include "zeroconf.h"
+
+#include "avahi-client/client.h"
+#include "avahi-client/lookup.h"
+
+#include "avahi-common/thread-watch.h"
+#include "avahi-common/malloc.h"
+#include "avahi-common/error.h"
+#include "avahi-common/domain.h"
+
+static int UninitializeZeroconf1(int lock);
+
+struct AvahiThreadedPoll {
+ void *simple_poll;
+ pthread_t thread_id;
+ pthread_mutex_t mutex;
+ int thread_running;
+ int retval;
+} ;
+
+#ifdef ZEROCONF_RUNTIME_LINKING
+typedef AvahiClient * (* avahi_client_new_type) (
+ const AvahiPoll *poll_api /**< The abstract event loop API to use */,
+ AvahiClientFlags flags /**< Some flags to modify the behaviour of the client library */,
+ AvahiClientCallback callback /**< A callback that is called whenever the state of the client changes. This may be NULL */,
+ void *userdata /**< Some arbitrary user data pointer that will be passed to the callback function */,
+ int *error /**< If creation of the client fails, this integer will contain the error cause. May be NULL if you aren't interested in the reason why avahi_client_new() failed. */);
+typedef void (* avahi_client_free_type)(AvahiClient *client);
+typedef const char * (* avahi_client_get_host_name_type) (AvahiClient *);
+typedef AvahiServiceBrowser * (* avahi_service_browser_new_type) (
+ AvahiClient *client,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *type,
+ const char *domain,
+ AvahiLookupFlags flags,
+ AvahiServiceBrowserCallback callback,
+ void *userdata);
+typedef AvahiServiceResolver * (* avahi_service_resolver_new_type)(
+ AvahiClient *client,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiProtocol aprotocol,
+ AvahiLookupFlags flags,
+ AvahiServiceResolverCallback callback,
+ void *userdata);
+typedef int (* avahi_service_resolver_free_type)(AvahiServiceResolver *r);
+typedef AvahiRecordBrowser * (* avahi_record_browser_new_type)(
+ AvahiClient *client,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name,
+ uint16_t clazz,
+ uint16_t type,
+ AvahiLookupFlags flags,
+ AvahiRecordBrowserCallback callback,
+ void *userdata);
+typedef int (* avahi_record_browser_free_type)(AvahiRecordBrowser *);
+typedef int (* avahi_service_name_join_type)(char *p, size_t size, const char *name, const char *type, const char *domain);
+typedef const char *(* avahi_strerror_type)(int error);
+typedef int (* avahi_client_errno_type) (AvahiClient*);
+typedef AvahiThreadedPoll *(* avahi_threaded_poll_new_type)(void);
+typedef void (* avahi_threaded_poll_free_type)(AvahiThreadedPoll *p);
+typedef const AvahiPoll* (* avahi_threaded_poll_get_type)(AvahiThreadedPoll *p);
+typedef int (* avahi_threaded_poll_start_type)(AvahiThreadedPoll *p);
+typedef int (* avahi_threaded_poll_stop_type)(AvahiThreadedPoll *p);
+typedef void (* avahi_threaded_poll_quit_type)(AvahiThreadedPoll *p);
+typedef void (* avahi_threaded_poll_lock_type)(AvahiThreadedPoll *p);
+typedef void (* avahi_threaded_poll_unlock_type)(AvahiThreadedPoll *p);
+typedef const char *(* avahi_client_get_version_string_type)(AvahiClient *c);
+//typedef char *(* avahi_address_snprint_type) (char *ret_s, size_t length, const AvahiAddress *a);
+//typedef char *(* avahi_string_list_to_string_type) (AvahiStringList *l);
+typedef void (* avahi_free_type) (void *p);
+//typedef AvahiStringList* (*avahi_string_list_find_type) (AvahiStringList *l, const char *key);
+//typedef uint8_t* (*avahi_string_list_get_text_type) ( AvahiStringList * l ) ;
+typedef AvahiStringList *(* avahi_string_list_get_next_type) (AvahiStringList *l);
+typedef int (* avahi_string_list_get_pair_type) (AvahiStringList *l, char **key, char **value, size_t *size);
+
+
+avahi_service_browser_new_type avahi_service_browser_new_ptr = NULL;
+avahi_service_resolver_new_type avahi_service_resolver_new_ptr = NULL;
+avahi_service_resolver_free_type avahi_service_resolver_free_ptr = NULL;
+avahi_record_browser_new_type avahi_record_browser_new_ptr = NULL;
+avahi_record_browser_free_type avahi_record_browser_free_ptr = NULL;
+avahi_service_name_join_type avahi_service_name_join_ptr = NULL;
+avahi_client_new_type avahi_client_new_ptr = NULL;
+avahi_client_free_type avahi_client_free_ptr = NULL;
+avahi_strerror_type avahi_strerror_ptr = NULL;
+avahi_client_errno_type avahi_client_errno_ptr = NULL;
+avahi_threaded_poll_new_type avahi_threaded_poll_new_ptr = NULL;
+avahi_threaded_poll_free_type avahi_threaded_poll_free_ptr = NULL;
+avahi_threaded_poll_get_type avahi_threaded_poll_get_ptr = NULL;
+avahi_threaded_poll_start_type avahi_threaded_poll_start_ptr = NULL;
+avahi_threaded_poll_stop_type avahi_threaded_poll_stop_ptr = NULL;
+avahi_threaded_poll_quit_type avahi_threaded_poll_quit_ptr = NULL;
+avahi_threaded_poll_lock_type avahi_threaded_poll_lock_ptr = NULL;
+avahi_threaded_poll_unlock_type avahi_threaded_poll_unlock_ptr = NULL;
+avahi_client_get_version_string_type avahi_client_get_version_string_ptr = NULL;
+avahi_free_type avahi_free_ptr = NULL;
+avahi_string_list_get_next_type avahi_string_list_get_next_ptr = NULL;
+avahi_string_list_get_pair_type avahi_string_list_get_pair_ptr = NULL;
+#else
+#define avahi_service_browser_new_ptr avahi_service_browser_new
+#define avahi_service_resolver_new_ptr avahi_service_resolver_new
+#define avahi_service_resolver_free_ptr avahi_service_resolver_free
+#define avahi_record_browser_new_ptr avahi_record_browser_new
+#define avahi_record_browser_free_ptr avahi_record_browser_free
+#define avahi_service_name_join_ptr avahi_service_name_join
+#define avahi_client_new_ptr avahi_client_new
+#define avahi_client_free_ptr avahi_client_free
+#define avahi_strerror_ptr avahi_strerror
+#define avahi_client_errno_ptr avahi_client_errno
+#define avahi_threaded_poll_new_ptr avahi_threaded_poll_new
+#define avahi_threaded_poll_free_ptr avahi_threaded_poll_free
+#define avahi_threaded_poll_get_ptr avahi_threaded_poll_get
+#define avahi_threaded_poll_start_ptr avahi_threaded_poll_start
+#define avahi_threaded_poll_stop_ptr avahi_threaded_poll_stop
+#define avahi_threaded_poll_quit_ptr avahi_threaded_poll_quit
+#define avahi_threaded_poll_lock_ptr avahi_threaded_poll_lock
+#define avahi_threaded_poll_unlock_ptr avahi_threaded_poll_unlock
+#define avahi_client_get_version_string_ptr avahi_client_get_version_string
+#define avahi_free_ptr avahi_free
+#define avahi_string_list_get_next_ptr avahi_string_list_get_next
+#define avahi_string_list_get_pair_ptr avahi_string_list_get_pair
+#endif
+
+/*
+ * TXT record version - this should be 1 for a long time
+ * - only need to change if we really change the TXT record format
+ */
+const char *dnssd_txt_ver = "1";
+
+int Dns_sdInitialized = FALSE;
+
+static AvahiThreadedPoll *threaded_poll = NULL;
+static AvahiClient *client = NULL;
+
+static AvahiServiceBrowser *zeroconf_browse_sbc_ref = NULL;
+static AvahiServiceBrowser *zeroconf_browse_ws_ref = NULL;
+static AvahiServiceBrowser *zeroconf_browse_phidget_ref = NULL;
+
+//pthread_t dns_thread_ws, dns_thread_phid;
+
+void *avahiLibHandle = NULL;
+
+static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+ assert(c);
+
+ /* Called whenever the client or server state changes */
+
+ switch (state) {
+ case AVAHI_CLIENT_S_RUNNING:
+
+ /* The server has startup successfully and registered its host
+ * name on the network */
+ Dns_sdInitialized = TRUE;
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+
+ LOG(PHIDGET_LOG_ERROR, "Client failure: %s", avahi_strerror_ptr(avahi_client_errno_ptr(c)));
+ //avahi_threaded_poll_quit_ptr(threaded_poll);
+
+ break;
+
+ case AVAHI_CLIENT_S_COLLISION:
+
+ /* Let's drop our registered services. When the server is back
+ * in AVAHI_SERVER_RUNNING state we will register them
+ * again with the new host name. */
+
+ case AVAHI_CLIENT_S_REGISTERING:
+
+ /* The server records are now being established. This
+ * might be caused by a host name change. We need to wait
+ * for our own records to register until the host name is
+ * properly esatblished. */
+
+ //if (group)
+ // avahi_entry_group_reset_ptr(group);
+
+ break;
+
+ case AVAHI_CLIENT_CONNECTING:
+ break;
+ }
+}
+
+void PhidFromTXT(CPhidgetHandle phid, AvahiStringList *txt)
+{
+ AvahiStringList *curTxt = txt;
+ short txtver;
+ int i;
+
+ do
+ {
+ char *key, *value;
+ size_t size;
+
+ avahi_string_list_get_pair_ptr(curTxt, &key, &value, &size);
+
+ if(!strcmp(key, "txtvers"))
+ {
+ txtver = (short)strtol(value, NULL, 10);
+ }
+ else if(!strcmp(key, "serial"))
+ {
+ phid->serialNumber = strtol(value, NULL, 10);
+ phid->specificDevice = PTRUE;
+ }
+ else if(!strcmp(key, "version"))
+ {
+ phid->deviceVersion = strtol(value, NULL, 10);
+ }
+ else if(!strcmp(key, "label"))
+ {
+ strncpy(phid->label, value, MAX_LABEL_STORAGE);
+ }
+ else if(!strcmp(key, "server_id"))
+ {
+ free(phid->networkInfo->zeroconf_server_id);
+ phid->networkInfo->zeroconf_server_id = strdup(value);
+ }
+ else if(!strcmp(key, "usbstr"))
+ {
+ strncpy(phid->usbProduct, value, 64);
+ }
+ else if(!strcmp(key, "id"))
+ {
+ phid->deviceIDSpec = strtol(value, NULL, 10);
+
+ for(i = 1;i<PHIDGET_DEVICE_COUNT;i++)
+ if(phid->deviceIDSpec == Phid_Device_Def[i].pdd_sdid) break;
+ phid->deviceDef = &Phid_Device_Def[i];
+ phid->attr = Phid_Device_Def[i].pdd_attr;
+ }
+ else if(!strcmp(key, "class"))
+ {
+ phid->deviceID = strtol(value, NULL, 10);
+ phid->deviceType = Phid_DeviceName[phid->deviceID];
+ }
+ else if(!strcmp(key, "name"))
+ {
+ for(i = 0;i<PHIDGET_DEVICE_COUNT;i++)
+ {
+ if(!strcmp(value, Phid_Device_Def[i].pdd_name))
+ {
+ phid->deviceIDSpec = Phid_Device_Def[i].pdd_sdid;
+ phid->deviceDef = &Phid_Device_Def[i];
+ phid->attr = Phid_Device_Def[i].pdd_attr;
+ break;
+ }
+ }
+ }
+ else if(!strcmp(key, "type"))
+ {
+ phid->deviceID = phidget_type_to_id(value);
+ phid->deviceType = Phid_DeviceName[phid->deviceID];
+ }
+
+ avahi_free_ptr(key);
+ avahi_free_ptr(value);
+ }
+ while((curTxt = avahi_string_list_get_next_ptr(curTxt)) != NULL);
+
+ phid->deviceUID = CPhidget_getUID(phid->deviceIDSpec, phid->deviceVersion);
+ phid->networkInfo->mdns = PTRUE;
+}
+
+void SBCFromTXT(CPhidgetSBCHandle sbc, AvahiStringList *txt)
+{
+ AvahiStringList *curTxt = txt;
+
+ do
+ {
+ char *key, *value;
+ size_t size;
+
+ avahi_string_list_get_pair_ptr(curTxt, &key, &value, &size);
+
+ if(!strcmp(key, "txtvers"))
+ {
+ sbc->txtver = (short)strtol(value, NULL, 10);
+ }
+ else if(!strcmp(key, "fversion"))
+ {
+ strncpy(sbc->fversion, value, 12);
+ }
+ else if(!strcmp(key, "hversion"))
+ {
+ sbc->hversion = (short)strtol(value, NULL, 10);
+ }
+ else if(!strcmp(key, "hostname"))
+ {
+ strncpy(sbc->hostname, value, 128);
+ }
+ else if(!strcmp(key, "name"))
+ {
+ strncpy(sbc->deviceName, value, 128);
+ }
+
+ avahi_free_ptr(key);
+ avahi_free_ptr(value);
+ }
+ while((curTxt = avahi_string_list_get_next_ptr(curTxt)) != NULL);
+
+ if(sbc->txtver < 3)
+ {
+ strncpy(sbc->deviceName, "PhidgetSBC", 128);
+ }
+}
+
+void DNSServiceResolve_CallBack(
+ AvahiServiceResolver *r,
+ AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ CPhidgetRemoteHandle networkInfo = (CPhidgetRemoteHandle)userdata;
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ LOG(PHIDGET_LOG_ERROR, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ networkInfo->zeroconf_host = strdup("err");
+ break;
+ case AVAHI_RESOLVER_FOUND:
+ {
+ LOG(PHIDGET_LOG_INFO, "DNSServiceResolve_CallBack: %s",name);
+ networkInfo->zeroconf_host = strdup(host_name);
+ networkInfo->zeroconf_port = malloc(10);
+ snprintf(networkInfo->zeroconf_port, 9, "%d", port);
+ }
+ }
+
+ avahi_service_resolver_free_ptr(r);
+}
+
+void DNSServiceResolve_SBC_CallBack(
+ AvahiServiceResolver *r,
+ AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ LOG(PHIDGET_LOG_ERROR, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ break;
+
+ case AVAHI_RESOLVER_FOUND: {
+ CPhidgetSBCHandle sbc = (CPhidgetSBCHandle)userdata, found_sbc;
+ CPhidgetSBCManagerList *trav;
+
+ LOG(PHIDGET_LOG_INFO, "DNSServiceResolve_SBC_CallBack: %s",name);
+
+ SBCFromTXT(sbc, txt);
+
+ sbc->networkInfo->zeroconf_host = strdup(host_name);
+ sbc->networkInfo->zeroconf_port = malloc(10);
+ snprintf(sbc->networkInfo->zeroconf_port, 9, "%d", port);
+
+ CThread_mutex_lock(&zeroconfSBCsLock);
+ CThread_mutex_lock(&activeSBCManagersLock);
+
+ //Check if it's in the list and if it's different, remove it to make way for the new one
+ // (Sometimes, we don't get a proper detach notification)
+ if(CList_findInList((CListHandle)zeroconfSBCs, sbc, CPhidgetSBC_areEqual, (void **)&found_sbc) == EPHIDGET_OK)
+ {
+ if(CPhidgetSBC_areExtraEqual(found_sbc, sbc) != PTRUE) //A version number has changed
+ {
+ //Remove from list - don't free until after detach event
+ CList_removeFromList((CListHandle *)&zeroconfSBCs, found_sbc, CPhidgetSBC_areEqual, PFALSE, NULL);
+
+ for (trav=activeSBCManagers; trav; trav = trav->next)
+ {
+ if (trav->sbcm->fptrDetachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
+ trav->sbcm->fptrDetachChange((CPhidgetSBCHandle)found_sbc, trav->sbcm->fptrDetachChangeptr);
+ }
+
+ CPhidgetSBC_free(found_sbc);
+
+ //now we fall through and add back to new one
+ }
+ else //Nothing has changed, we didn't remove, don't add
+ {
+ CPhidgetSBC_free(sbc);
+ goto dontadd;
+ }
+ }
+
+ //now add it
+ CList_addToList((CListHandle *)&zeroconfSBCs, sbc, CPhidgetSBC_areEqual);
+
+ //send out events
+ for (trav=activeSBCManagers; trav; trav = trav->next)
+ {
+ if (trav->sbcm->fptrAttachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
+ trav->sbcm->fptrAttachChange((CPhidgetSBCHandle)sbc, trav->sbcm->fptrAttachChangeptr);
+
+ }
+ dontadd:
+
+ CThread_mutex_unlock(&activeSBCManagersLock);
+ CThread_mutex_unlock(&zeroconfSBCsLock);
+ }
+ }
+
+ avahi_service_resolver_free_ptr(r);
+}
+
+void DNSServiceResolve_Phidget_CallBack(
+ AvahiServiceResolver *r,
+ AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ LOG(PHIDGET_LOG_ERROR, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ break;
+
+ case AVAHI_RESOLVER_FOUND: {
+ CPhidgetHandle phid = (CPhidgetHandle)userdata;
+ CPhidgetManagerList *trav;
+
+ LOG(PHIDGET_LOG_INFO, "DNSServiceResolve_SBC_CallBack: %s",name);
+
+ PhidFromTXT(phid, txt);
+
+ phid->networkInfo->zeroconf_host = strdup(host_name);
+ phid->networkInfo->zeroconf_port = malloc(10);
+ snprintf(phid->networkInfo->zeroconf_port, 9, "%d", port);
+
+ LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_Phidget_CallBack: %s",name);
+ CThread_mutex_lock(&zeroconfPhidgetsLock);
+ CThread_mutex_lock(&activeRemoteManagersLock);
+
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock);
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_REMOTE_FLAG, &phid->lock);
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &phid->lock);
+
+ //now add it
+ CList_addToList((CListHandle *)&zeroconfPhidgets, phid, CPhidget_areExtraEqual);
+ //managers
+ for (trav=activeRemoteManagers; trav; trav = trav->next)
+ {
+ if(trav->phidm->networkInfo->requested_address==NULL
+ && (trav->phidm->networkInfo->requested_serverID == NULL || !strcmp(trav->phidm->networkInfo->requested_serverID,phid->networkInfo->zeroconf_server_id)))
+ {
+ CList_addToList((CListHandle *)&trav->phidm->AttachedPhidgets, phid, CPhidget_areExtraEqual);
+
+ if (trav->phidm->fptrAttachChange && trav->phidm->state == PHIDGETMANAGER_ACTIVE)
+ trav->phidm->fptrAttachChange((CPhidgetHandle)phid, trav->phidm->fptrAttachChangeptr);
+ }
+ }
+ CThread_mutex_unlock(&activeRemoteManagersLock);
+ CThread_mutex_unlock(&zeroconfPhidgetsLock);
+ }
+ }
+
+ avahi_service_resolver_free_ptr(r);
+}
+
+void DNSServiceBrowse_Phidget_CallBack(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ CPhidgetHandle phid;
+ CPhidgetManagerList *trav;
+
+ int ret;
+
+ switch (event) {
+
+ case AVAHI_BROWSER_FAILURE:
+
+ LOG(PHIDGET_LOG_WARNING, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ avahi_threaded_poll_quit_ptr(threaded_poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ {
+ if((CPhidget_create(&phid))) return;
+ if((CPhidgetRemote_create(&phid->networkInfo))) return;
+
+ phid->networkInfo->zeroconf_name = strdup(name);
+ phid->networkInfo->zeroconf_type = strdup(type);
+ phid->networkInfo->zeroconf_domain = strdup(domain);
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ if (!(avahi_service_resolver_new_ptr(client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, DNSServiceResolve_Phidget_CallBack, phid)))
+ LOG(PHIDGET_LOG_ERROR, "avahi_service_resolver_new failed on service '%s': %s", name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+
+ break;
+ }
+
+ case AVAHI_BROWSER_REMOVE:
+ {
+ if((CPhidget_create(&phid))) return;
+ if((CPhidgetRemote_create(&phid->networkInfo))) return;
+
+ phid->networkInfo->zeroconf_name = strdup(name);
+ phid->networkInfo->zeroconf_type = strdup(type);
+ phid->networkInfo->zeroconf_domain = strdup(domain);
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ //have to fill in phid manually from just the name
+ int i;
+ CPhidgetHandle found_phid;
+ char *name_copy;
+ //Look to see if this is a 'firmware upgrade' device.
+ if(name[0] == '1')
+ {
+ char *realname = strchr(name, ' ');
+ if(!realname)
+ return;
+ name_copy = strdup(&realname[1]);
+ }
+ else
+ name_copy = strdup(name);
+ for(i=0;i<strlen(name_copy);i++)
+ if(name_copy[i] == '(') break;
+ if(i<=1) return;
+ name_copy[strlen(name_copy)-1]='\0';
+ name_copy[i-1] = '\0';
+ phid->serialNumber = strtol(name_copy+i+1, NULL, 10);
+ phid->specificDevice = PTRUE;
+ for(i = 0;i<PHIDGET_DEVICE_COUNT;i++)
+ if(!strcmp(name_copy, Phid_Device_Def[i].pdd_name)) break;
+ phid->deviceIDSpec = 0;
+ phid->deviceDef = &Phid_Device_Def[i];
+ phid->attr = Phid_Device_Def[i].pdd_attr;
+ phid->deviceID = Phid_Device_Def[i].pdd_did;
+ phid->deviceType = Phid_DeviceName[phid->deviceID];
+ phid->networkInfo->mdns = PTRUE;
+
+ CThread_mutex_lock(&zeroconfPhidgetsLock);
+ CThread_mutex_lock(&activeRemoteManagersLock);
+
+ CPhidget_clearStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock);
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_DETACHING_FLAG, &phid->lock);
+
+ if(!CList_findInList((CListHandle)zeroconfPhidgets, phid, CPhidget_areEqual, (void **)&found_phid))
+ {
+ CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_ATTACHED_FLAG, &found_phid->lock);
+ CPhidget_setStatusFlag(&found_phid->status, PHIDGET_DETACHING_FLAG, &found_phid->lock);
+ CPhidget_setStatusFlag(&found_phid->status, PHIDGET_REMOTE_FLAG, &found_phid->lock);
+ CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &found_phid->lock);
+
+ CList_removeFromList((CListHandle *)&zeroconfPhidgets, found_phid, CPhidget_areExtraEqual, FALSE, NULL);
+ //managers
+ for (trav=activeRemoteManagers; trav; trav = trav->next)
+ {
+ if(trav->phidm->networkInfo->requested_address==NULL
+ && (trav->phidm->networkInfo->requested_serverID == NULL || !strcmp(trav->phidm->networkInfo->requested_serverID,found_phid->networkInfo->zeroconf_server_id)))
+ {
+ CList_removeFromList((CListHandle *)&trav->phidm->AttachedPhidgets, found_phid, CPhidget_areExtraEqual, PFALSE, NULL);
+
+ if (trav->phidm->fptrDetachChange && trav->phidm->state == PHIDGETMANAGER_ACTIVE)
+ trav->phidm->fptrDetachChange((CPhidgetHandle)found_phid, trav->phidm->fptrDetachChangeptr);
+ }
+ }
+ CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_DETACHING_FLAG, &found_phid->lock);
+ CPhidgetRemote_free(found_phid->networkInfo);
+ CPhidget_free(found_phid);
+ }
+ CPhidgetRemote_free(phid->networkInfo);
+ CPhidget_free(phid);
+
+ CThread_mutex_unlock(&activeRemoteManagersLock);
+ CThread_mutex_unlock(&zeroconfPhidgetsLock);
+ free(name_copy);
+ }
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+void DNSServiceBrowse_SBC_CallBack(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ CPhidgetSBCHandle sbc, found_sbc;
+ CPhidgetSBCManagerList *trav;
+ int ret;
+
+ switch (event) {
+
+ case AVAHI_BROWSER_FAILURE:
+
+ LOG(PHIDGET_LOG_WARNING, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ avahi_threaded_poll_quit_ptr(threaded_poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ {
+ if((CPhidgetSBC_create(&sbc))) return;
+ if((CPhidgetRemote_create(&sbc->networkInfo))) return;
+
+ sbc->networkInfo->zeroconf_name = strdup(name);
+ sbc->networkInfo->zeroconf_type = strdup(type);
+ sbc->networkInfo->zeroconf_domain = strdup(domain);
+ sbc->networkInfo->mdns = PTRUE;
+
+ strncpy(sbc->mac, name+12, 18); //name == 'PhidgetSBC (??:??:??:??:??:??)'
+ sbc->mac[17] = '\0';
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ if (!(avahi_service_resolver_new_ptr(client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, DNSServiceResolve_SBC_CallBack, sbc)))
+ LOG(PHIDGET_LOG_ERROR, "avahi_service_resolver_new failed on service '%s': %s", name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ break;
+ }
+
+ case AVAHI_BROWSER_REMOVE:
+ {
+ if((CPhidgetSBC_create(&sbc))) return;
+ if((CPhidgetRemote_create(&sbc->networkInfo))) return;
+
+ sbc->networkInfo->zeroconf_name = strdup(name);
+ sbc->networkInfo->zeroconf_type = strdup(type);
+ sbc->networkInfo->zeroconf_domain = strdup(domain);
+ sbc->networkInfo->mdns = PTRUE;
+
+ strncpy(sbc->mac, name+12, 18); //name == 'PhidgetSBC (??:??:??:??:??:??)'
+ sbc->mac[17] = '\0';
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+
+ CThread_mutex_lock(&zeroconfSBCsLock);
+ CThread_mutex_lock(&activeSBCManagersLock);
+
+ if(CList_findInList((CListHandle)zeroconfSBCs, sbc, CPhidgetSBC_areEqual, (void **)&found_sbc) == EPHIDGET_OK)
+ {
+ CList_removeFromList((CListHandle *)&zeroconfSBCs, found_sbc, CPhidgetSBC_areEqual, PFALSE, NULL);
+ //managers
+ for (trav=activeSBCManagers; trav; trav = trav->next)
+ {
+ if (trav->sbcm->fptrDetachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
+ trav->sbcm->fptrDetachChange((CPhidgetSBCHandle)found_sbc, trav->sbcm->fptrDetachChangeptr);
+ }
+ CPhidgetSBC_free(found_sbc);
+ }
+
+ CThread_mutex_unlock(&activeSBCManagersLock);
+ CThread_mutex_unlock(&zeroconfSBCsLock);
+
+ CPhidgetSBC_free(sbc);
+ }
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+void DNSServiceBrowse_ws_CallBack(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ switch (event) {
+
+ case AVAHI_BROWSER_FAILURE:
+
+ LOG(PHIDGET_LOG_ERROR, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ //avahi_threaded_poll_quit_ptr(threaded_poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ {
+ CPhidgetRemoteHandle networkInfo;
+ if((CPhidgetRemote_create(&networkInfo))) return;
+
+ networkInfo->zeroconf_name = strdup(name);
+ networkInfo->zeroconf_server_id = strdup(name);
+ networkInfo->zeroconf_type = strdup(type);
+ networkInfo->zeroconf_domain = strdup(domain);
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ CThread_mutex_lock(&zeroconfServersLock);
+ CList_addToList((CListHandle *)&zeroconfServers, networkInfo, CPhidgetRemote_areEqual);
+ CThread_mutex_unlock(&zeroconfServersLock);
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ {
+ CPhidgetRemoteHandle networkInfo;
+ if((CPhidgetRemote_create(&networkInfo))) return;
+
+ networkInfo->zeroconf_name = strdup(name);
+ networkInfo->zeroconf_server_id = strdup(name);
+ networkInfo->zeroconf_type = strdup(type);
+ networkInfo->zeroconf_domain = strdup(domain);
+ LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ CThread_mutex_lock(&zeroconfServersLock);
+ CList_removeFromList((CListHandle *)&zeroconfServers, networkInfo, CPhidgetRemote_areEqual, TRUE, CPhidgetRemote_free);
+ CThread_mutex_unlock(&zeroconfServersLock);
+ }
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+//Does nothing in Avahi
+int cancelPendingZeroconfLookups(CPhidgetRemoteHandle networkInfo)
+{
+ return EPHIDGET_OK;
+}
+
+int getZeroconfHostPort(CPhidgetRemoteHandle networkInfo)
+{
+ //Don't look up if we already have it.
+ if(networkInfo->zeroconf_host && networkInfo->zeroconf_port)
+ return EPHIDGET_OK;
+
+ int timeout = 200; //2000ms
+
+ if(networkInfo->zeroconf_host) free(networkInfo->zeroconf_host);
+ networkInfo->zeroconf_host = NULL;
+ if(networkInfo->zeroconf_port) free(networkInfo->zeroconf_port);
+ networkInfo->zeroconf_port = NULL;
+
+ if (!(avahi_service_resolver_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ networkInfo->zeroconf_name, //name
+ networkInfo->zeroconf_type, // service type
+ networkInfo->zeroconf_domain, //domain
+ AVAHI_PROTO_UNSPEC, 0, DNSServiceResolve_CallBack, networkInfo)))
+ {
+ LOG(PHIDGET_LOG_ERROR, "Failed to resolve service '%s': %s", networkInfo->zeroconf_name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ while(!networkInfo->zeroconf_host)
+ {
+ usleep(10000);
+ timeout--;
+ if(!timeout)
+ {
+ LOG(PHIDGET_LOG_ERROR, "getZeroconfHostPort didn't work (timeout)");
+ return EPHIDGET_UNEXPECTED;
+ }
+ }
+
+ if(!strcmp(networkInfo->zeroconf_host, "err"))
+ {
+ LOG(PHIDGET_LOG_ERROR, "getZeroconfHostPort didn't work (error)");
+ free(networkInfo->zeroconf_host);
+ networkInfo->zeroconf_host = NULL;
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ return EPHIDGET_OK;
+}
+
+int refreshZeroconfSBC(CPhidgetSBCHandle sbc)
+{
+ return EPHIDGET_OK;
+}
+
+int refreshZeroconfPhidget(CPhidgetHandle phid)
+{
+ return EPHIDGET_OK;
+}
+
+int InitializeZeroconf()
+{
+ int error;
+ //int ret = 1;
+ int timeout = 50; //500ms
+ const char *avahiVersion;
+
+ CThread_mutex_lock(&zeroconfInitLock);
+ if(Dns_sdInitialized)
+ {
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_OK;
+ }
+
+#ifdef ZEROCONF_RUNTIME_LINKING
+
+ avahiLibHandle = dlopen("libavahi-client.so",RTLD_LAZY);
+ if(!avahiLibHandle)
+ {
+ avahiLibHandle = dlopen("libavahi-client.so.3",RTLD_LAZY);
+ }
+ if(!avahiLibHandle)
+ {
+ LOG(PHIDGET_LOG_WARNING, "dlopen failed with error: %s", dlerror());
+ LOG(PHIDGET_LOG_WARNING, "Assuming that zeroconf is not supported on this machine.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNSUPPORTED;
+ }
+
+ //These are always in Avahi
+ if(!(avahi_client_get_version_string_ptr = (avahi_client_get_version_string_type)dlsym(avahiLibHandle, "avahi_client_get_version_string"))) goto dlsym_err;
+ if(!(avahi_service_browser_new_ptr = (avahi_service_browser_new_type)dlsym(avahiLibHandle, "avahi_service_browser_new"))) goto dlsym_err;
+ if(!(avahi_service_resolver_new_ptr = (avahi_service_resolver_new_type)dlsym(avahiLibHandle, "avahi_service_resolver_new"))) goto dlsym_err;
+ if(!(avahi_service_resolver_free_ptr = (avahi_service_resolver_free_type)dlsym(avahiLibHandle, "avahi_service_resolver_free"))) goto dlsym_err;
+ if(!(avahi_record_browser_new_ptr = (avahi_record_browser_new_type)dlsym(avahiLibHandle, "avahi_record_browser_new"))) goto dlsym_err;
+ if(!(avahi_record_browser_free_ptr = (avahi_record_browser_free_type)dlsym(avahiLibHandle, "avahi_record_browser_free"))) goto dlsym_err;
+ if(!(avahi_service_name_join_ptr = (avahi_service_name_join_type)dlsym(avahiLibHandle, "avahi_service_name_join"))) goto dlsym_err;
+ if(!(avahi_client_new_ptr = (avahi_client_new_type)dlsym(avahiLibHandle, "avahi_client_new"))) goto dlsym_err;
+ if(!(avahi_client_free_ptr = (avahi_client_free_type)dlsym(avahiLibHandle, "avahi_client_free"))) goto dlsym_err;
+ if(!(avahi_strerror_ptr = (avahi_strerror_type)dlsym(avahiLibHandle, "avahi_strerror"))) goto dlsym_err;
+ if(!(avahi_client_errno_ptr = (avahi_client_errno_type)dlsym(avahiLibHandle, "avahi_client_errno"))) goto dlsym_err;
+
+ //These are in Avahi > 0.6.4
+ if(!(avahi_threaded_poll_new_ptr = (avahi_threaded_poll_new_type)dlsym(avahiLibHandle, "avahi_threaded_poll_new"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_free_ptr = (avahi_threaded_poll_free_type)dlsym(avahiLibHandle, "avahi_threaded_poll_free"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_get_ptr = (avahi_threaded_poll_get_type)dlsym(avahiLibHandle, "avahi_threaded_poll_get"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_start_ptr = (avahi_threaded_poll_start_type)dlsym(avahiLibHandle, "avahi_threaded_poll_start"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_stop_ptr = (avahi_threaded_poll_stop_type)dlsym(avahiLibHandle, "avahi_threaded_poll_stop"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_quit_ptr = (avahi_threaded_poll_quit_type)dlsym(avahiLibHandle, "avahi_threaded_poll_quit"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_lock_ptr = (avahi_threaded_poll_lock_type)dlsym(avahiLibHandle, "avahi_threaded_poll_lock"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_unlock_ptr = (avahi_threaded_poll_unlock_type)dlsym(avahiLibHandle, "avahi_threaded_poll_unlock"))) goto dlsym_err2;
+
+ //These are new ones I'm using, not sure when they were added...
+ if(!(avahi_free_ptr = (avahi_free_type)dlsym(avahiLibHandle, "avahi_free"))) goto dlsym_err3;
+ if(!(avahi_string_list_get_next_ptr = (avahi_string_list_get_next_type)dlsym(avahiLibHandle, "avahi_string_list_get_next"))) goto dlsym_err3;
+ if(!(avahi_string_list_get_pair_ptr = (avahi_string_list_get_pair_type)dlsym(avahiLibHandle, "avahi_string_list_get_pair"))) goto dlsym_err3;
+
+
+ goto dlsym_good;
+
+dlsym_err:
+ LOG(PHIDGET_LOG_WARNING, "dlsym failed with error: %s", dlerror());
+ LOG(PHIDGET_LOG_WARNING, "Assuming that zeroconf is not supported on this machine.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNSUPPORTED;
+
+ //Old avahi didn't have the thread functions
+dlsym_err2:
+ LOG(PHIDGET_LOG_WARNING, "dlsym failed with error: %s", dlerror());
+ LOG(PHIDGET_LOG_WARNING, "Avahi is too old, upgrade to at least version 0.6.4.");
+ LOG(PHIDGET_LOG_WARNING, "Zeroconf will not be used on this machine.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNSUPPORTED;
+
+dlsym_err3:
+ LOG(PHIDGET_LOG_WARNING, "dlsym failed with error: %s", dlerror());
+ LOG(PHIDGET_LOG_WARNING, "Avahi is too old, upgrade to a newer version.");
+ LOG(PHIDGET_LOG_WARNING, "Zeroconf will not be used on this machine.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNSUPPORTED;
+
+dlsym_good:
+
+#endif
+
+ /* Allocate main loop object */
+ if (!(threaded_poll = avahi_threaded_poll_new_ptr())) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create threaded poll object.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ /* Allocate a new client */
+ client = avahi_client_new_ptr(avahi_threaded_poll_get_ptr(threaded_poll), 0, client_callback, NULL, &error);
+
+ /* Check wether creating the client object succeeded */
+ if (!client) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create client: %s", avahi_strerror_ptr(error));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ //get version
+ avahiVersion = avahi_client_get_version_string_ptr(client);
+
+ /* Create the service browsers */
+ if (!(zeroconf_browse_ws_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget_ws._tcp", NULL, 0, DNSServiceBrowse_ws_CallBack, client))) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ if (!(zeroconf_browse_phidget_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget._tcp", NULL, 0, DNSServiceBrowse_Phidget_CallBack, client))) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ if (!(zeroconf_browse_sbc_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget_sbc._tcp", NULL, 0, DNSServiceBrowse_SBC_CallBack, client))) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ if(avahi_threaded_poll_start_ptr(threaded_poll))
+ {
+ LOG(PHIDGET_LOG_ERROR, "avahi_threaded_poll_start_ptr failed");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ //Thread is started successfully
+ else
+ {
+ //There is a bug in at least Avahi 0.6.16 (Debian Etch default) where thread_running is not set, so quit doesn't work!!!???!?!?!?!?!?!
+ //This is fixed in 0.6.24
+ //So I'll set it myself here
+ if(strcmp(avahiVersion, "avahi 0.6.24") < 0)
+ {
+ LOG(PHIDGET_LOG_INFO, "Fixing thread_running bug in avahi < 0.6.24");
+ threaded_poll->thread_running = 1;
+ }
+ }
+
+ while(!Dns_sdInitialized)
+ {
+ usleep(10000);
+ timeout--;
+ if(!timeout)
+ {
+ UninitializeZeroconf1(PFALSE);
+ LOG(PHIDGET_LOG_ERROR, "InitializeZeroconf Seems bad... Dns_sdInitialized wasn't set to true.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ }
+
+ LOG(PHIDGET_LOG_INFO, "InitializeZeroconf Seems good... (%s)",avahiVersion);
+
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_OK;
+
+}
+
+
+static int UninitializeZeroconf1(int lock)
+{
+ int ret;
+ /* Cleanup things */
+ if(lock)
+ CThread_mutex_lock(&zeroconfInitLock);
+ if(Dns_sdInitialized)
+ {
+ if (threaded_poll)
+ {
+ if((ret = avahi_threaded_poll_stop_ptr(threaded_poll)) == -1)
+ LOG(PHIDGET_LOG_WARNING, "avahi_threaded_poll_stop failed",ret);
+ avahi_client_free_ptr(client);
+ avahi_threaded_poll_free_ptr(threaded_poll);
+ threaded_poll = NULL;
+ client = NULL;
+ }
+ }
+
+ Dns_sdInitialized = FALSE;
+ if(lock)
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_OK;
+}
+
+int UninitializeZeroconf()
+{
+ return UninitializeZeroconf1(PTRUE);
+}