/* 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 . */ /* * Управляющие USB-запросы адаптера candleLight: конфигурация, временные параметры CAN и режим * канала. Эти транзакции идут через control endpoint и отличаются от потока CAN-кадров. */ #include "candle_ctrl_req.h" #include "ch_9.h" #include enum { CANDLE_BREQ_HOST_FORMAT = 0, CANDLE_BREQ_BITTIMING = 1, CANDLE_BREQ_MODE = 2, CANDLE_BREQ_BERR = 3, CANDLE_BREQ_BT_CONST = 4, CANDLE_BREQ_DEVICE_CONFIG = 5, CANDLE_TIMESTAMP_GET = 6, /* 7: IDENTIFY, 8: GET_USER_ID, 9: SET_USER_ID (not used here) */ CANDLE_BREQ_DATA_BITTIMING = 10, CANDLE_BREQ_BT_CONST_EXT = 11, /* Расширенные пределы номинальной и быстрой фазы согласно gs_usb. */ CANDLE_BREQ_SET_TERMINATION = 12, /* Запрос gs_usb 12 переключает встроенный резистор окончания шины. */ CANDLE_BREQ_GET_TERMINATION = 13, /* Запрос gs_usb 13 читает текущее состояние терминатора. */ CANDLE_BREQ_GET_STATE = 14, /* Стандартный GET_STATE имеет номер 14; номер 12 принадлежит терминатору. */ CANDLE_BREQ_BUS_OFF_RECOVERY = 32, }; static void candle_ctrl_logf(const wchar_t *fmt, ...) { if (candle_log_fn == NULL || !candle_log_verbose) { return; } wchar_t buf[512]; va_list args; va_start(args, fmt); HRESULT hr = StringCchVPrintfW(buf, 512, fmt, args); va_end(args); if (SUCCEEDED(hr)) { candle_log_fn(buf); } } static bool usb_control_msg(WINUSB_INTERFACE_HANDLE hnd, uint8_t request, uint8_t requesttype, uint16_t value, uint16_t index, void *data, uint16_t size) { WINUSB_SETUP_PACKET packet; memset(&packet, 0, sizeof(packet)); packet.Request = request; packet.RequestType = requesttype; packet.Value = value; packet.Index = index; packet.Length = size; unsigned long bytes_sent = 0; BOOL rc = WinUsb_ControlTransfer(hnd, packet, (uint8_t*)data, size, &bytes_sent, 0); candle_ctrl_logf(L"ctrl req=0x%02x type=0x%02x value=%u index=%u size=%u rc=%u transferred=%lu winerr=%lu", request, requesttype, value, index, size, rc ? 1 : 0, bytes_sent, rc ? 0 : GetLastError()); return rc; } bool candle_ctrl_set_host_format(candle_device_t *dev) { candle_host_config_t hconf; hconf.byte_order = 0x0000beef; bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_HOST_FORMAT, USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, 1, dev->interfaceNumber, &hconf, sizeof(hconf) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_SET_HOST_FORMAT; return rc; } bool candle_ctrl_set_device_mode(candle_device_t *dev, uint8_t channel, uint32_t mode, uint32_t flags) { candle_device_mode_t dm; dm.mode = mode; dm.flags = flags; bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_MODE, USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, dev->interfaceNumber, &dm, sizeof(dm) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_SET_DEVICE_MODE; return rc; } bool candle_ctrl_get_config(candle_device_t *dev, candle_device_config_t *dconf) { bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_DEVICE_CONFIG, USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, 1, dev->interfaceNumber, dconf, sizeof(*dconf) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_GET_DEVICE_INFO; return rc; } bool candle_ctrl_get_timestamp(candle_device_t *dev, uint32_t *current_timestamp) { bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_TIMESTAMP_GET, USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, 1, dev->interfaceNumber, current_timestamp, sizeof(*current_timestamp) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_GET_TIMESTAMP; return rc; } bool candle_ctrl_get_capability(candle_device_t *dev, uint8_t channel, candle_capability_t *data) { bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_BT_CONST, USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, 0, data, sizeof(*data) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_GET_BITTIMING_CONST; return rc; } bool candle_ctrl_set_bittiming(candle_device_t *dev, uint8_t channel, candle_bittiming_t *data) { bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_BITTIMING, USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, 0, data, sizeof(*data) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_SET_BITTIMING; return rc; } bool candle_ctrl_set_data_bittiming(candle_device_t *dev, uint8_t channel, candle_bittiming_t *data) { bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_DATA_BITTIMING, USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, 0, data, sizeof(*data) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_SET_BITTIMING; return rc; } bool candle_ctrl_get_state(candle_device_t *dev, uint8_t channel, candle_device_state_t *data) { bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_GET_STATE, USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, dev->interfaceNumber, data, sizeof(*data) ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_GET_DEVICE_INFO; return rc; } bool candle_ctrl_bus_off_recover(candle_device_t *dev, uint8_t channel) { bool rc = usb_control_msg( dev->winUSBHandle, CANDLE_BREQ_BUS_OFF_RECOVERY, USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, dev->interfaceNumber, NULL, 0 ); dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_SET_DEVICE_MODE; return rc; } bool candle_ctrl_data_capability(candle_device_t *dev, uint8_t channel, candle_capability_t *cap) /* Структура gs_usb BT_CONST_EXT содержит десять общих и восемь быстрых полей uint32. */ { /* Используем массив, поскольку протокол содержит только однородные 32-битные значения без отступов. */ uint32_t fields[18] = {0}; /* 18 полей по 4 байта образуют стандартный ответ размером 72 байта. */ bool rc = usb_control_msg(dev->winUSBHandle, CANDLE_BREQ_BT_CONST_EXT, USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, 0, fields, sizeof(fields)); /* Запрашиваем пределы именно выбранного CAN-канала. */ if (rc) { /* Выходная структура должна меняться только после успешного USB-ответа. */ memcpy(cap, fields, 2 * sizeof(uint32_t)); /* Маска функций и частота CAN общие для обеих фаз. */ memcpy(&cap->tseg1_min, &fields[10], 8 * sizeof(uint32_t)); /* Поля DTSEG1…DBRP_INC заменяют номинальные пределы структуры capability. */ } /* При отказе вызывающий код получит false и не использует нулевые пределы. */ dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_GET_BITTIMING_CONST; /* Сохраняем диагностический код чтения аппаратных ограничений. */ return rc; /* Успех означает наличие полной структуры пределов быстрой фазы. */ } /* Завершение запроса расширенных возможностей FD. */ bool candle_ctrl_termination(candle_device_t *dev, uint8_t channel, uint32_t *state, bool write) /* Переключение терминатора использует стандартные SET=12 и GET=13. */ { /* Состояние передаётся одним 32-битным словом согласно протоколу gs_usb. */ bool rc = usb_control_msg(dev->winUSBHandle, write ? CANDLE_BREQ_SET_TERMINATION : CANDLE_BREQ_GET_TERMINATION, (write ? USB_DIR_OUT : USB_DIR_IN)|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, channel, 0, state, sizeof(*state)); /* Номер CAN-канала передаётся в wValue, а wIndex равен нулю по спецификации. */ dev->last_error = rc ? CANDLE_ERR_OK : CANDLE_ERR_SET_DEVICE_MODE; /* Ошибка настройки доступна Python через candle_dev_last_error. */ return rc; /* Не выдаём успешное переключение при отказе USB-контроллера. */ } /* Завершение управления встроенным окончанием шины. */