Добавить очереди операций PBUS/SBUS, общую консоль, настройки Candle и SLCAN, декодеры DSView и обработку плотных графиков. Обновить протокол Бальзам и проверки общих компонентов.
261 lines
10 KiB
C
261 lines
10 KiB
C
/*
|
||
|
||
Copyright (c) 2016 Hubert Denkmair <hubert@denkmair.de>
|
||
|
||
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 <http://www.gnu.org/licenses/>.
|
||
|
||
*/
|
||
|
||
/*
|
||
* Управляющие USB-запросы адаптера candleLight: конфигурация, временные параметры CAN и режим
|
||
* канала. Эти транзакции идут через control endpoint и отличаются от потока CAN-кадров.
|
||
*/
|
||
|
||
#include "candle_ctrl_req.h"
|
||
#include "ch_9.h"
|
||
|
||
#include <stdarg.h>
|
||
|
||
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-контроллера. */
|
||
} /* Завершение управления встроенным окончанием шины. */
|