/* Copyright (c) 2016 Hubert Denkmair This file is part of the candle windows API. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "candle.h" #include #include "candle_defs.h" #include "candle_ctrl_req.h" #include "ch_9.h" static bool candle_dev_interal_open(candle_handle hdev); static bool candle_read_di(HDEVINFO hdi, SP_DEVICE_INTERFACE_DATA interfaceData, candle_device_t *dev) { /* get required length first (this call always fails with an error) */ ULONG requiredLength=0; SetupDiGetDeviceInterfaceDetail(hdi, &interfaceData, NULL, 0, &requiredLength, NULL); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { dev->last_error = CANDLE_ERR_SETUPDI_IF_DETAILS; return false; } PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(LMEM_FIXED, requiredLength); if (detail_data != NULL) { detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); } else { dev->last_error = CANDLE_ERR_MALLOC; return false; } bool retval = true; ULONG length = requiredLength; if (!SetupDiGetDeviceInterfaceDetail(hdi, &interfaceData, detail_data, length, &requiredLength, NULL) ) { dev->last_error = CANDLE_ERR_SETUPDI_IF_DETAILS2; retval = false; } else if (FAILED(StringCchCopy(dev->path, sizeof(dev->path), detail_data->DevicePath))) { dev->last_error = CANDLE_ERR_PATH_LEN; retval = false; } LocalFree(detail_data); if (!retval) { return false; } /* try to open to read device infos and see if it is avail */ if (candle_dev_interal_open(dev)) { dev->state = CANDLE_DEVSTATE_AVAIL; candle_dev_close(dev); } else { dev->state = CANDLE_DEVSTATE_INUSE; } dev->last_error = CANDLE_ERR_OK; return true; } bool __stdcall candle_list_scan(candle_list_handle *list) { if (list==NULL) { return false; } candle_list_t *l = (candle_list_t *)calloc(1, sizeof(candle_list_t)); *list = l; if (l==NULL) { return false; } GUID guid; if (CLSIDFromString(L"{c15b4308-04d3-11e6-b3ea-6057189e6443}", &guid) != NOERROR) { l->last_error = CANDLE_ERR_CLSID; return false; } HDEVINFO hdi = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hdi == INVALID_HANDLE_VALUE) { l->last_error = CANDLE_ERR_GET_DEVICES; return false; } bool rv = false; for (unsigned i=0; idev[i])) { l->last_error = l->dev[i].last_error; rv = false; break; } } else { DWORD err = GetLastError(); if (err==ERROR_NO_MORE_ITEMS) { l->num_devices = i; l->last_error = CANDLE_ERR_OK; rv = true; } else { l->last_error = CANDLE_ERR_SETUPDI_IF_ENUM; rv = false; } break; } } SetupDiDestroyDeviceInfoList(hdi); return rv; } bool __stdcall DLL candle_list_free(candle_list_handle list) { free(list); return true; } bool __stdcall DLL candle_list_length(candle_list_handle list, uint8_t *len) { candle_list_t *l = (candle_list_t *)list; *len = l->num_devices; return true; } bool __stdcall DLL candle_dev_get(candle_list_handle list, uint8_t dev_num, candle_handle *hdev) { candle_list_t *l = (candle_list_t *)list; if (l==NULL) { return false; } if (dev_num >= CANDLE_MAX_DEVICES) { l->last_error = CANDLE_ERR_DEV_OUT_OF_RANGE; return false; } candle_device_t *dev = calloc(1, sizeof(candle_device_t)); *hdev = dev; if (dev==NULL) { l->last_error = CANDLE_ERR_MALLOC; return false; } memcpy(dev, &l->dev[dev_num], sizeof(candle_device_t)); l->last_error = CANDLE_ERR_OK; dev->last_error = CANDLE_ERR_OK; return true; } bool __stdcall DLL candle_dev_get_state(candle_handle hdev, candle_devstate_t *state) { if (hdev==NULL) { return false; } else { candle_device_t *dev = (candle_device_t*)hdev; *state = dev->state; return true; } } wchar_t __stdcall DLL *candle_dev_get_path(candle_handle hdev) { if (hdev==NULL) { return NULL; } else { candle_device_t *dev = (candle_device_t*)hdev; return dev->path; } } static bool candle_dev_interal_open(candle_handle hdev) { candle_device_t *dev = (candle_device_t*)hdev; memset(dev->rxevents, 0, sizeof(dev->rxevents)); memset(dev->rxurbs, 0, sizeof(dev->rxurbs)); dev->deviceHandle = CreateFile( dev->path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); if (dev->deviceHandle == INVALID_HANDLE_VALUE) { dev->last_error = CANDLE_ERR_CREATE_FILE; return false; } if (!WinUsb_Initialize(dev->deviceHandle, &dev->winUSBHandle)) { dev->last_error = CANDLE_ERR_WINUSB_INITIALIZE; goto close_handle; } USB_INTERFACE_DESCRIPTOR ifaceDescriptor; if (!WinUsb_QueryInterfaceSettings(dev->winUSBHandle, 0, &ifaceDescriptor)) { dev->last_error = CANDLE_ERR_QUERY_INTERFACE; goto winusb_free; } dev->interfaceNumber = ifaceDescriptor.bInterfaceNumber; unsigned pipes_found = 0; for (uint8_t i=0; iwinUSBHandle, 0, i, &pipeInfo)) { dev->last_error = CANDLE_ERR_QUERY_PIPE; goto winusb_free; } if (pipeInfo.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN(pipeInfo.PipeId)) { dev->bulkInPipe = pipeInfo.PipeId; pipes_found++; } else if (pipeInfo.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT(pipeInfo.PipeId)) { dev->bulkOutPipe = pipeInfo.PipeId; pipes_found++; } else { dev->last_error = CANDLE_ERR_PARSE_IF_DESCR; goto winusb_free; } } if (pipes_found != 2) { dev->last_error = CANDLE_ERR_PARSE_IF_DESCR; goto winusb_free; } char use_raw_io = 1; if (!WinUsb_SetPipePolicy(dev->winUSBHandle, dev->bulkInPipe, RAW_IO, sizeof(use_raw_io), &use_raw_io)) { dev->last_error = CANDLE_ERR_SET_PIPE_RAW_IO; goto winusb_free; } if (!candle_ctrl_set_host_format(dev)) { goto winusb_free; } if (!candle_ctrl_get_config(dev, &dev->dconf)) { goto winusb_free; } if (!candle_ctrl_get_capability(dev, 0, &dev->bt_const)) { dev->last_error = CANDLE_ERR_GET_BITTIMING_CONST; goto winusb_free; } dev->last_error = CANDLE_ERR_OK; return true; winusb_free: WinUsb_Free(dev->winUSBHandle); close_handle: CloseHandle(dev->deviceHandle); return false; } static bool candle_prepare_read(candle_device_t *dev, unsigned urb_num) { bool rc = WinUsb_ReadPipe( dev->winUSBHandle, dev->bulkInPipe, dev->rxurbs[urb_num].buf, sizeof(dev->rxurbs[urb_num].buf), NULL, &dev->rxurbs[urb_num].ovl ); if (rc || (GetLastError()!=ERROR_IO_PENDING)) { dev->last_error = CANDLE_ERR_PREPARE_READ; return false; } else { dev->last_error = CANDLE_ERR_OK; return true; } } static bool candle_close_rxurbs(candle_device_t *dev) { for (unsigned i=0; irxevents[i] != NULL) { CloseHandle(dev->rxevents[i]); } } return true; } bool __stdcall DLL candle_dev_open(candle_handle hdev) { candle_device_t *dev = (candle_device_t*)hdev; if (candle_dev_interal_open(dev)) { for (unsigned i=0; irxevents[i] = ev; dev->rxurbs[i].ovl.hEvent = ev; if (!candle_prepare_read(dev, i)) { candle_close_rxurbs(dev); return false; // keep last_error from prepare_read call } } dev->last_error = CANDLE_ERR_OK; return true; } else { return false; // keep last_error from open_device call } } bool __stdcall DLL candle_dev_get_timestamp_us(candle_handle hdev, uint32_t *timestamp_us) { return candle_ctrl_get_timestamp(hdev, timestamp_us); } bool __stdcall DLL candle_dev_close(candle_handle hdev) { candle_device_t *dev = (candle_device_t*)hdev; candle_close_rxurbs(dev); WinUsb_Free(dev->winUSBHandle); dev->winUSBHandle = NULL; CloseHandle(dev->deviceHandle); dev->deviceHandle = NULL; dev->last_error = CANDLE_ERR_OK; return true; } bool __stdcall DLL candle_dev_free(candle_handle hdev) { free(hdev); return true; } candle_err_t __stdcall DLL candle_dev_last_error(candle_handle hdev) { candle_device_t *dev = (candle_device_t*)hdev; return dev->last_error; } bool __stdcall DLL candle_channel_count(candle_handle hdev, uint8_t *num_channels) { // TODO check if info was already read from device; try to do so; throw error... candle_device_t *dev = (candle_device_t*)hdev; *num_channels = dev->dconf.icount+1; return true; } bool __stdcall DLL candle_channel_get_capabilities(candle_handle hdev, uint8_t ch, candle_capability_t *cap) { // TODO check if info was already read from device; try to do so; throw error... candle_device_t *dev = (candle_device_t*)hdev; memcpy(cap, &dev->bt_const.feature, sizeof(candle_capability_t)); return true; } bool __stdcall DLL candle_channel_set_timing(candle_handle hdev, uint8_t ch, candle_bittiming_t *data) { // TODO ensure device is open, check channel count.. candle_device_t *dev = (candle_device_t*)hdev; return candle_ctrl_set_bittiming(dev, ch, data); } bool __stdcall DLL candle_channel_set_bitrate(candle_handle hdev, uint8_t ch, uint32_t bitrate) { // TODO ensure device is open, check channel count.. candle_device_t *dev = (candle_device_t*)hdev; if (dev->bt_const.fclk_can != 48000000) { /* this function only works for the candleLight base clock of 48MHz */ dev->last_error = CANDLE_ERR_BITRATE_FCLK; return false; } candle_bittiming_t t; t.prop_seg = 1; t.sjw = 1; t.phase_seg1 = 13 - t.prop_seg; t.phase_seg2 = 2; switch (bitrate) { case 10000: t.brp = 300; break; case 20000: t.brp = 150; break; case 50000: t.brp = 60; break; case 83333: t.brp = 36; break; case 100000: t.brp = 30; break; case 125000: t.brp = 24; break; case 250000: t.brp = 12; break; case 500000: t.brp = 6; break; case 800000: t.brp = 4; t.phase_seg1 = 12 - t.prop_seg; t.phase_seg2 = 2; break; case 1000000: t.brp = 3; break; default: dev->last_error = CANDLE_ERR_BITRATE_UNSUPPORTED; return false; } return candle_ctrl_set_bittiming(dev, ch, &t); } bool __stdcall DLL candle_channel_start(candle_handle hdev, uint8_t ch, uint32_t flags) { // TODO ensure device is open, check channel count.. candle_device_t *dev = (candle_device_t*)hdev; flags |= CANDLE_MODE_HW_TIMESTAMP; return candle_ctrl_set_device_mode(dev, ch, CANDLE_DEVMODE_START, flags); } bool __stdcall DLL candle_channel_stop(candle_handle hdev, uint8_t ch) { // TODO ensure device is open, check channel count.. candle_device_t *dev = (candle_device_t*)hdev; return candle_ctrl_set_device_mode(dev, ch, CANDLE_DEVMODE_RESET, 0); } bool __stdcall DLL candle_frame_send(candle_handle hdev, uint8_t ch, candle_frame_t *frame) { // TODO ensure device is open, check channel count.. candle_device_t *dev = (candle_device_t*)hdev; unsigned long bytes_sent = 0; frame->echo_id = 0; frame->channel = ch; bool rc = WinUsb_WritePipe( dev->winUSBHandle, dev->bulkOutPipe, (uint8_t*)frame, sizeof(*frame), &bytes_sent, 0 ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_SEND_FRAME; return rc; } bool __stdcall DLL candle_frame_read(candle_handle hdev, candle_frame_t *frame, uint32_t timeout_ms) { // TODO ensure device is open.. candle_device_t *dev = (candle_device_t*)hdev; DWORD wait_result = WaitForMultipleObjects(CANDLE_URB_COUNT, dev->rxevents, false, timeout_ms); if (wait_result == WAIT_TIMEOUT) { dev->last_error = CANDLE_ERR_READ_TIMEOUT; return false; } if ( (wait_result < WAIT_OBJECT_0) || (wait_result >= WAIT_OBJECT_0 + CANDLE_URB_COUNT) ) { dev->last_error = CANDLE_ERR_READ_WAIT; return false; } DWORD urb_num = wait_result - WAIT_OBJECT_0; DWORD bytes_transfered; if (!WinUsb_GetOverlappedResult(dev->winUSBHandle, &dev->rxurbs[urb_num].ovl, &bytes_transfered, false)) { candle_prepare_read(dev, urb_num); dev->last_error = CANDLE_ERR_READ_RESULT; return false; } if (bytes_transfered < sizeof(*frame)-4) { candle_prepare_read(dev, urb_num); dev->last_error = CANDLE_ERR_READ_SIZE; return false; } if (bytes_transfered < sizeof(*frame)) { frame->timestamp_us = 0; } memcpy(frame, dev->rxurbs[urb_num].buf, sizeof(*frame)); return candle_prepare_read(dev, urb_num); } candle_frametype_t __stdcall DLL candle_frame_type(candle_frame_t *frame) { if (frame->echo_id != 0xFFFFFFFF) { return CANDLE_FRAMETYPE_ECHO; }; if (frame->can_id & CANDLE_ID_ERR) { return CANDLE_FRAMETYPE_ERROR; } return CANDLE_FRAMETYPE_RECEIVE; } uint32_t __stdcall DLL candle_frame_id(candle_frame_t *frame) { return frame->can_id & 0x1FFFFFFF; } bool __stdcall DLL candle_frame_is_extended_id(candle_frame_t *frame) { return (frame->can_id & CANDLE_ID_EXTENDED) != 0; } bool __stdcall DLL candle_frame_is_rtr(candle_frame_t *frame) { return (frame->can_id & CANDLE_ID_RTR) != 0; } uint8_t __stdcall DLL candle_frame_dlc(candle_frame_t *frame) { return frame->can_dlc; } uint8_t __stdcall DLL *candle_frame_data(candle_frame_t *frame) { return frame->data; } uint32_t __stdcall DLL candle_frame_timestamp_us(candle_frame_t *frame) { return frame->timestamp_us; }