diff options
Diffstat (limited to 'linux')
-rw-r--r-- | linux/cusblinux-1.0.c | 233 | ||||
-rw-r--r-- | linux/cusblinux.c | 144 | ||||
-rw-r--r-- | linux/zeroconf_avahi.c | 2090 |
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); +} |