From bd7a4f258643bf72d5e97f86f0f2272b381ed1ba Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Wed, 16 May 2012 02:24:19 +0100 Subject: Imported Upstream version 2.1.8.20120514 --- linux/cusblinux-1.0.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'linux') diff --git a/linux/cusblinux-1.0.c b/linux/cusblinux-1.0.c index 4b43b1d..204ef53 100644 --- a/linux/cusblinux-1.0.c +++ b/linux/cusblinux-1.0.c @@ -261,8 +261,8 @@ static int getLabelString(CPhidgetHandle phid, struct libusb_device_handle *hand { 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); + 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; @@ -407,7 +407,7 @@ int CUSBBuildList(CPhidgetList **curList) { } } - ssize_t cnt = libusb_get_device_list(NULL, &list); + ssize_t cnt = libusb_get_device_list(libusbContext, &list); if(cnt < 0) { LOG(PHIDGET_LOG_ERROR, "libusb_get_device_list failed with error code: %d", cnt); @@ -456,7 +456,7 @@ int CUSBBuildList(CPhidgetList **curList) { //LOG(PHIDGET_LOG_DEBUG,"New Device: %s",(char *)Phid_DeviceName[Phid_Device_Def[i].pdd_did]); libusb_device_handle *handle = NULL; - if (libusb_open(device, &handle) == 0) + if ((ret = libusb_open(device, &handle)) == 0) { if (desc.bcdDevice < 0x100) phid->deviceVersion = desc.bcdDevice * 100; @@ -478,7 +478,6 @@ int CUSBBuildList(CPhidgetList **curList) { if((ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string))) < 0) { LOG(PHIDGET_LOG_ERROR, "libusb_get_string_descriptor_ascii failed with error code: %d", ret); - LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root"); libusb_close(handle); free(phid); goto next; @@ -505,6 +504,8 @@ int CUSBBuildList(CPhidgetList **curList) { } //if(udev) else { + LOG(PHIDGET_LOG_WARNING, "libusb_open failed with error code: %d", ret); + LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root, or install the udev rules."); free(phid); libusb_close(handle); } @@ -537,7 +538,7 @@ int CUSBOpenHandle(CPhidgetHandle phid) int i, j,ret = EPHIDGET_OK; libusb_device **list = NULL; - ssize_t cnt = libusb_get_device_list(NULL, &list); + ssize_t cnt = libusb_get_device_list(libusbContext, &list); if(cnt < 0) { @@ -565,7 +566,7 @@ int CUSBOpenHandle(CPhidgetHandle phid) /* the vend/prod matches! */ libusb_device_handle *handle = NULL; - if (libusb_open(device, &handle) == 0) + if ((ret = libusb_open(device, &handle)) == 0) { serial = -1; if (desc.iSerialNumber) { @@ -573,7 +574,6 @@ int CUSBOpenHandle(CPhidgetHandle phid) if((ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string))) < 0) { LOG(PHIDGET_LOG_WARNING, "libusb_get_string_descriptor_ascii failed with error code: %d", ret); - LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root"); libusb_close(handle); goto next; } @@ -643,8 +643,9 @@ int CUSBOpenHandle(CPhidgetHandle phid) } /* udev open */ else { + LOG(PHIDGET_LOG_WARNING, "libusb_open failed with error code: %d", ret); + LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root, or install the udev rules."); libusb_close(handle); - LOG(PHIDGET_LOG_WARNING, "usb_open failed - bad permission or what?"); } } /* vendor/product match */ } /* deviceID matches in table */ -- cgit v1.2.3 From 8f3c91e91b634adaca77dac6cf314445cceefc78 Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Fri, 19 Oct 2012 19:38:06 +0100 Subject: Imported Upstream version 2.1.8.20120912 --- linux/cusblinux-1.0.c | 233 ++++-- linux/cusblinux.c | 144 ++-- linux/zeroconf_avahi.c | 2090 ++++++++++++++++++++++++------------------------ 3 files changed, 1295 insertions(+), 1172 deletions(-) (limited to 'linux') 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 +#include +#include 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 (pnetworkInfo->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;ideviceIDSpec == 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;ideviceIDSpec = 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;iserialNumber = strtol(name_copy+i+1, NULL, 10); - phid->specificDevice = PTRUE; - for(i = 0;ideviceIDSpec = 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;ideviceIDSpec == 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;ideviceIDSpec = 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;iserialNumber = strtol(name_copy+i+1, NULL, 10); + phid->specificDevice = PTRUE; + for(i = 0;ideviceIDSpec = 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); +} -- cgit v1.2.3