commit dbbf1068dcef7e86fab86e42ddcaa0e38ed09f92 Author: uvos Date: Thu Jun 15 18:06:29 2023 +0200 inial commit diff --git a/90-eismultiplexer.rules b/90-eismultiplexer.rules new file mode 100644 index 0000000..0a7412b --- /dev/null +++ b/90-eismultiplexer.rules @@ -0,0 +1 @@ +SUBSYSTEM=="usb", ATTRS{idVendor}=="fe17", GROUP="uucp" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f4b8796 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0) + +project(uvosled) + +set(SRC_FILES uvosled.c usbshm.c) +set(LIBS -pthread -lusb-1.0 ) + +add_library(${PROJECT_NAME} SHARED ${SRC_FILES}) +target_link_libraries( ${PROJECT_NAME} ${LIBS}) +add_definitions("-std=c17 -Wall -O2 -fno-strict-aliasing") + +set(CMAKE_INSTALL_PREFIX "/usr") +install(TARGETS ${PROJECT_NAME} DESTINATION lib) +install(FILES ./uvosled.h DESTINATION include) + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) +set(SRC_FILES_TEST_APP main.c) +set(LIBS_TEST -l${PROJECT_NAME}) +add_executable(${PROJECT_NAME}_test ${SRC_FILES_TEST_APP}) +add_dependencies(${PROJECT_NAME}_test ${PROJECT_NAME}) +target_link_libraries( ${PROJECT_NAME}_test ${LIBS_TEST}) +add_definitions("-std=c17 -Wall -O2 -fno-strict-aliasing") + +install(TARGETS ${PROJECT_NAME}_test DESTINATION bin) + +if(CMAKE_SYSTEM_NAME MATCHES "^Linux") + set(UDEV_RULES_INSTALL_DIR /lib/udev/rules.d CACHE PATH "install directory for linux udev config") + install(FILES 90-usbled.rules DESTINATION ${UDEV_RULES_INSTALL_DIR}) +endif() diff --git a/main.c b/main.c new file mode 100644 index 0000000..ccba567 --- /dev/null +++ b/main.c @@ -0,0 +1,109 @@ +/** +* libeismultiplexer +* Copyright (C) 2023 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include "uvosled.h" + +void print_help(const char* progname) +{ + printf("usage: %s [OPERATION] [CHANNEL] [VALUE]\n", progname); + printf("available operations: set on off help\n"); +} + +int main(int argc, char* argv[]) +{ + if(argc < 2 || strcmp (argv[1], "help") == 0) + { + print_help(argv[0]); + return 0; + } + + struct uvosled led; + + if(uvosled_connect(&led)) + { + printf("Can not connect to UVOS usbled device\n"); + return -1; + } + + if(strcmp(argv[1], "on") == 0) + { + if(uvosled_poweron(&led)) + printf("cant power on \n"); + } + else if(strcmp(argv[1], "off") == 0) + { + uvosled_set_current(&led, 0xff , 0); + uvosled_poweroff(&led); + } + else if(strcmp(argv[1], "set") == 0) + { + if(argc != 4) + { + printf("usage: %s set [CHANNEL] [VALUE]\n", argv[0]); + uvosled_poweroff(&led); + return 0; + } + + uint8_t mask = 0; + + switch(argv[2][0]) + { + case 'a': + case 'A': + case '1': + mask = CHANNEL_A; + break; + case 'b': + case 'B': + case '2': + mask = CHANNEL_B; + break; + case 'c': + case 'C': + case '3': + mask = CHANNEL_C; + break; + case 'd': + case 'D': + case '4': + mask = CHANNEL_D; + break; + default: + printf("A channel must be selected out of A, B, C or D\n"); + } + + if(mask) + { + char* end; + float value = strtof(argv[3], &end); + if(isnan(value) || value < 0 || value > 1.0f) + printf("A channel value between 0 and 1.0 must be supplied\n"); + else if(uvosled_set_current(&led, mask, value)) + printf("Failed to set current\n"); + } + } + + uvosled_disconnect(&led); + return 0; +} diff --git a/usbshm.c b/usbshm.c new file mode 100644 index 0000000..35b9da3 --- /dev/null +++ b/usbshm.c @@ -0,0 +1,257 @@ +/** +* Copyright (C) 2023 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#include "usbshm.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct usbshm_priv +{ + libusb_device_handle* handle; + struct libusb_transfer* transfer; + unsigned char* buffer; +}; + +static unsigned objectCounter = 0; +static struct timeval timeout = {1, 0}; +static pthread_mutex_t *libusbDataMutex; +static pthread_t libusbThread; +static atomic_bool threadStop; + +static void usbshm_transferCallBack(struct libusb_transfer *transfer); + +void usbshm_distroy(struct usbshm* instance) +{ + while(!usbshm_ready(instance)) + sleep(1); + free(instance->priv->transfer); + libusb_close(instance->priv->handle); + free(instance->priv->buffer); + free(instance->productName); + free(instance->priv); + if(--objectCounter == 0) + { + threadStop = true; + pthread_join(libusbThread, NULL); + libusb_exit(NULL); + pthread_mutex_destroy(libusbDataMutex); + } + +} + +static void* usbshm_libusbPoll(void* arg) +{ + while(!threadStop) + { + libusb_handle_events_timeout_completed(NULL, &timeout, NULL); + } + return NULL; +} + +bool usbshm_isOpen(struct usbshm* instance) +{ + return instance->priv->handle != NULL; +} + +int usbshm_init(struct usbshm* instance, void (*dataCallback)(uint8_t request, unsigned char* data, size_t length)) +{ + int ret=0; + instance->priv = malloc(sizeof(*instance->priv)); + instance->priv->handle = NULL; + instance->priv->transfer = NULL; + instance->priv->buffer = NULL; + instance->vendorID = 0; + instance->productID = 0; + instance->productName = NULL; + instance->dataCallback = dataCallback; + if(objectCounter == 0) + { + printf("Usb Init\n"); + ret = libusb_init(NULL) < 0 ? USBSHM_ERROR_ERR : 0; + libusbDataMutex = malloc(sizeof(*libusbDataMutex)); + pthread_mutex_init(libusbDataMutex, NULL); + //libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG); + pthread_create(&libusbThread, NULL, &usbshm_libusbPoll, NULL); + } + if(ret == 0) objectCounter++; + return ret; +} + +bool usbshm_ready(struct usbshm* instance) +{ + return instance->priv->transfer == NULL; +} + +int usbshm_open(struct usbshm* instance, int vendorID, int productID, const char* productName) +{ + instance->priv->handle = NULL; + pthread_mutex_lock(libusbDataMutex); + printf("Listing Devices\n"); + libusb_device** list; + int count = libusb_get_device_list(NULL, &list); + int errorCode = 0; + if( count > 0) + { + struct libusb_device_descriptor desc = {0}; + for(int i = 0; i < count; ++i) + { + libusb_get_device_descriptor(list[i], &desc); + if(desc.idVendor == vendorID && desc.idProduct == productID) + { + errorCode = libusb_open(list[i], &instance->priv->handle) < 0 ? USBSHM_ERROR_ERR : 0; + break; + } + } + } + else + { + printf("Can not list devices\n"); + pthread_mutex_unlock(libusbDataMutex); + return USBSHM_ERROR_ERR; + } + + if(usbshm_isOpen(instance)) + { + instance->vendorID = vendorID; + instance->productID = productID; + if(productName) + { + instance->productName = malloc(strlen(productName)+1); + strcpy(instance->productName, productName); + } + else + { + instance->productName = NULL; + } + libusb_set_auto_detach_kernel_driver(instance->priv->handle, true); + } + else + { + printf("Opening usb device failed\n"); + errorCode = USBSHM_ERROR_ERR; + } + + + libusb_free_device_list(list, count); + pthread_mutex_unlock(libusbDataMutex); + return errorCode; +} + +bool usbshm_usbshm_isOpen(struct usbshm* instance) +{ + return instance->priv->handle != NULL; +} + +void usbshm_reset(struct usbshm* instance) +{ + printf("Usb transfer failed with %u\n", instance->priv->transfer->status); + libusb_reset_device(instance->priv->handle); +} + +void usbshm_reopen(struct usbshm* instance) +{ + libusb_close(instance->priv->handle); + usbshm_open(instance, instance->vendorID, instance->productID, instance->productName); +} + +int usbshm_writeControlTransfer(struct usbshm* instance, const uint8_t request, + char* buffer, const uint8_t length, + const uint16_t wValue, const uint16_t wIndex) +{ + if(!usbshm_isOpen(instance)) + return USBSHM_ERROR_NOT_CONNECTED; + if(length > 8) + return USBSHM_ERROR_PARAM; + if(instance->priv->transfer == NULL) + { + pthread_mutex_lock(libusbDataMutex); + instance->priv->buffer = malloc(length+8); + libusb_fill_control_setup(instance->priv->buffer, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + request, wValue, wIndex, length ); + for(uint8_t i = 0; i < length; ++i) + instance->priv->buffer[i+8] = buffer[i]; + instance->priv->transfer = libusb_alloc_transfer(0); + libusb_fill_control_transfer(instance->priv->transfer, instance->priv->handle, instance->priv->buffer, + &usbshm_transferCallBack, instance, 100); + int ret = libusb_submit_transfer(instance->priv->transfer); + pthread_mutex_unlock(libusbDataMutex); + if(ret < 0) + { + free(buffer); + libusb_free_transfer(instance->priv->transfer); + instance->priv->transfer = NULL; + if(ret == LIBUSB_ERROR_NO_DEVICE) + usbshm_reopen(instance); + } + return ret < 0 ? USBSHM_ERROR_ERR : 0; + } + else return USBSHM_ERROR_AGAIN; +} + +int usbshm_readControlTransfer(struct usbshm* instance, const uint8_t request, const uint8_t length) +{ + if(!usbshm_isOpen(instance)) + return USBSHM_ERROR_NOT_CONNECTED; + if(length > 8) + return USBSHM_ERROR_PARAM; + if(instance->priv->transfer == NULL) + { + pthread_mutex_lock(libusbDataMutex); + instance->priv->buffer = malloc(length+8); + libusb_fill_control_setup(instance->priv->buffer, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, request, request,request,length); + instance->priv->transfer = libusb_alloc_transfer(0); + libusb_fill_control_transfer(instance->priv->transfer, instance->priv->handle, instance->priv->buffer, &usbshm_transferCallBack, instance, 100); + int ret = libusb_submit_transfer(instance->priv->transfer); + pthread_mutex_unlock(libusbDataMutex); + if(ret < 0) + { + free(instance->priv->buffer); + libusb_free_transfer(instance->priv->transfer); + instance->priv->transfer = NULL; + if(ret == LIBUSB_ERROR_NO_DEVICE) + usbshm_reopen(instance); + } + return ret < 0 ? USBSHM_ERROR_ERR : 0; + } + else return USBSHM_ERROR_AGAIN; +} + +static void usbshm_transferCallBack(struct libusb_transfer *transfer) +{ + struct usbshm* context = (struct usbshm*)transfer->user_data; + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) + { + printf("Usb transfer failed with %d\n", transfer->status); + } + else if(transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) + { + if(context->dataCallback && transfer->length-8 >= transfer->actual_length) + context->dataCallback(transfer->buffer[1], transfer->buffer+8, transfer->actual_length); + } + free(context->priv->buffer); + libusb_free_transfer(context->priv->transfer); + context->priv->transfer = NULL; +} diff --git a/usbshm.h b/usbshm.h new file mode 100644 index 0000000..29066ed --- /dev/null +++ b/usbshm.h @@ -0,0 +1,61 @@ +/** +* Copyright (C) 2023 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include + +struct usbshm_priv; + +enum { + USBSHM_ERROR_AGAIN = -1, + USBSHM_ERROR_PARAM = -2, + USBSHM_ERROR_ERR = -3, + USBSHM_ERROR_NOT_CONNECTED = -4, +}; + +struct usbshm { + struct usbshm_priv* priv; + int vendorID; + int productID; + char* productName; + void (*dataCallback)(uint8_t request, unsigned char* data, size_t length); +}; + +void usbshm_distroy(struct usbshm* instance); + +int usbshm_init(struct usbshm* instance, void (*dataCallback)(uint8_t request, unsigned char* data, size_t length)); + +bool usbshm_ready(struct usbshm* instance); + +int usbshm_open(struct usbshm* instance, int vendorID, int productID, const char* productName); + +bool usbshm_isOpen(struct usbshm* instance); + +void usbshm_reset(struct usbshm* instance); + +void usbshm_reopen(struct usbshm* instance); + +int usbshm_writeControlTransfer(struct usbshm* instance, const uint8_t request, + char* buffer, const uint8_t length, + const uint16_t wValue, const uint16_t wIndex); + +int usbshm_readControlTransfer(struct usbshm* instance, const uint8_t request, const uint8_t length); + diff --git a/uvosled.c b/uvosled.c new file mode 100644 index 0000000..efc059f --- /dev/null +++ b/uvosled.c @@ -0,0 +1,124 @@ +/** +* Copyright (C) 2023 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#define _POSIX_C_SOURCE 199309L +#include "uvosled.h" +#include "usbshm.h" +#include +#include + +void usleep(uint64_t microseconds) +{ + struct timespec ts; + ts.tv_sec = microseconds / 1000000; + ts.tv_nsec = (microseconds % 1000000) * 1000; + nanosleep(&ts, NULL); +} + +int uvosled_connect(struct uvosled* led) +{ + int ret; + led->priv = malloc(sizeof(*led->priv)); + if(!led->priv) + return -1; + + ret = usbshm_init(led->priv, NULL); + if(ret) + return -2; + + ret = usbshm_open(led->priv, 0xfe17, 0x06dc , NULL); + if(ret) + return -3; + return 0; +} + +int uvosled_poweron(struct uvosled* led) +{ + int ret; + while((ret = usbshm_writeControlTransfer(led->priv, 0, NULL, 0, 0, 0)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + return ret; +} + +int uvosled_poweroff(struct uvosled* led) +{ + int ret; + while((ret = usbshm_writeControlTransfer(led->priv, 1, NULL, 0, 0, 0)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + return ret; +} + +int uvosled_set_current(struct uvosled* led, uint8_t channels, float current) +{ + if(current < 0) + return -1; + else if(current > 1) + return -2; + uint8_t currentU = current * 255; + // TODO: implment endianess + uint16_t wValue; + // we compile with fno-strict-aliasing + uint8_t* wValChar = (uint8_t*)&wValue; + wValChar[0] = channels; + wValChar[1] = currentU; + int ret; + while((ret = usbshm_writeControlTransfer(led->priv, 2, NULL, 0, wValue, 0)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + return ret; +} + +int uvosled_trigger(struct uvosled* led) +{ + int ret; + while((ret = usbshm_writeControlTransfer(led->priv, 1, NULL, 0, 0, 0)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + return ret; +} + +int uvosled_capture(struct uvosled* led, int channels, float current, double time, double cameraOffset) +{ + if(current < 0 || current > 1 || time > 1 || cameraOffset < -1 || cameraOffset > 1) + return USBSHM_ERROR_PARAM; + + uint8_t currentU = current * 255; + uint16_t timeU = time * 1000; + int16_t cameraOffsetU = cameraOffset * 1000; + int ret; + + while((ret = usbshm_writeControlTransfer(led->priv, 5, NULL, 0, *((uint16_t*)&cameraOffsetU), 0)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + if(ret < 0) + return ret; + + // TODO: implment endianess + uint16_t wValue; + // we compile with fno-strict-aliasing + uint8_t* wValChar = (uint8_t*)&wValue; + wValChar[0] = channels; + wValChar[1] = currentU; + while((ret = usbshm_writeControlTransfer(led->priv, 1, NULL, 0, wValue, timeU)) == USBSHM_ERROR_AGAIN) + usleep(1000000); + return ret; +} + +void uvosled_disconnect(struct uvosled* led) +{ + usbshm_distroy(led->priv); + free(led->priv); + led->priv = NULL; +} diff --git a/uvosled.h b/uvosled.h new file mode 100644 index 0000000..94784b3 --- /dev/null +++ b/uvosled.h @@ -0,0 +1,65 @@ +/** +* Copyright (C) 2023 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHANNEL_A 1 +#define CHANNEL_B (1 << 1) +#define CHANNEL_C (1 << 2) +#define CHANNEL_D (1 << 3) + +struct uvosled { + struct usbshm* priv; +}; + +// returns 0 on sucess and < 0 on failure +int uvosled_connect(struct uvosled* led); + +// power on cameras +// returns 0 on sucess and < 0 on failure +int uvosled_poweron(struct uvosled* led); + +// power off cameras +// returns 0 on sucess and < 0 on failure +int uvosled_poweroff(struct uvosled* led); + +// channels is a mask of bits, you can set it like this: CHANNEL_A | CHANNEL_C to activate channels A and C +// current is in percent of the values selected by the linear regulator resistors +// returns 0 on sucess and < 0 on failure +int uvosled_set_current(struct uvosled* led, uint8_t channels, float current); + +// causes the cameras to take an image +// returns 0 on sucess and < 0 on failure +int uvosled_trigger(struct uvosled* led); + +// leds are lit for time seconds and the camera is activated cameraOffset seconds after they are lit +// real time guarenteed by microcontroller +// returns 0 on sucess and < 0 on failure +int uvosled_capture(struct uvosled* led, int channels, float current, double time, double cameraOffset); + +void uvosled_disconnect(struct uvosled* led); + +#ifdef __cplusplus +} +#endif