mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-14 19:44:05 +08:00
Squashed 'panda/' content from commit ae816c104
git-subtree-dir: panda git-subtree-split: ae816c104a99a8cd4d508ccd6abdc7b93053529c
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <SetupAPI.h>
|
||||
#include <Devpkey.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
#include <winusb.h>
|
||||
|
||||
#include "device.h"
|
||||
|
||||
using namespace panda;
|
||||
|
||||
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
|
||||
tstring GetLastErrorAsString(){
|
||||
//Get the error message, if any.
|
||||
DWORD errorMessageID = ::GetLastError();
|
||||
if (errorMessageID == 0)
|
||||
return tstring(); //No error message has been recorded
|
||||
|
||||
_TCHAR *messageBuffer = nullptr;
|
||||
size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (_TCHAR*)&messageBuffer, 0, NULL);
|
||||
|
||||
tstring message(messageBuffer, size);
|
||||
|
||||
//Free the buffer.
|
||||
LocalFree(messageBuffer);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, tstring> panda::detect_pandas() {
|
||||
HDEVINFO deviceInfo;
|
||||
HRESULT hr;
|
||||
SP_DEVINFO_DATA deviceInfoData;
|
||||
SP_DEVICE_INTERFACE_DATA interfaceData;
|
||||
unsigned int deviceIndex;
|
||||
|
||||
std::unordered_map<std::string, tstring> map_sn_to_devpath;
|
||||
|
||||
deviceInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_panda,
|
||||
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); //DIGCF_ALLCLASSES
|
||||
|
||||
if (deviceInfo == INVALID_HANDLE_VALUE) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
_tprintf(_T("Failed to get dev handle. HR: %d\n"), hr);
|
||||
return map_sn_to_devpath;
|
||||
}
|
||||
|
||||
ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA));
|
||||
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
deviceIndex = 0;
|
||||
|
||||
while (SetupDiEnumDeviceInfo(deviceInfo, deviceIndex, &deviceInfoData)) {
|
||||
deviceIndex++;
|
||||
_tprintf(_T("Device info index %d\n"), deviceIndex);
|
||||
|
||||
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
if (SetupDiEnumDeviceInterfaces(deviceInfo, &deviceInfoData,
|
||||
&GUID_DEVINTERFACE_panda, 0, &interfaceData) == FALSE) {
|
||||
_tprintf(_T(" Got unexpected error while accessing interface %d\n"), GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
DWORD requiredLength;
|
||||
if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL) == FALSE
|
||||
&& ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
|
||||
_tprintf(_T(" Got unexpected error while reading interface details %d\n"), GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, requiredLength);
|
||||
if (NULL == detailData) {
|
||||
_tprintf(_T(" Failed to allocate %d bytes for interface data\n"), requiredLength);
|
||||
continue;
|
||||
}
|
||||
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
DWORD length = requiredLength;
|
||||
if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, length, &requiredLength, NULL) == FALSE) {
|
||||
_tprintf(_T(" Got unexpected error while reading interface details (2nd time) %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
LocalFree(detailData);
|
||||
continue;
|
||||
}
|
||||
|
||||
//_tprintf(_T(" Path: '%s'\n"), detailData->DevicePath);
|
||||
HANDLE deviceHandle = CreateFile(detailData->DevicePath,
|
||||
GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (INVALID_HANDLE_VALUE == deviceHandle) {
|
||||
_tprintf(_T(" Error opening Device Handle %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
LocalFree(detailData);
|
||||
continue;
|
||||
}
|
||||
|
||||
WINUSB_INTERFACE_HANDLE winusbHandle;
|
||||
if (WinUsb_Initialize(deviceHandle, &winusbHandle) == FALSE) {
|
||||
_tprintf(_T(" Error initializing WinUSB %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
CloseHandle(deviceHandle);
|
||||
LocalFree(detailData);
|
||||
continue;
|
||||
}
|
||||
|
||||
USB_DEVICE_DESCRIPTOR deviceDesc;
|
||||
unsigned long lengthReceived;
|
||||
if (WinUsb_GetDescriptor(winusbHandle, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0,
|
||||
(PBYTE)&deviceDesc, sizeof(deviceDesc), &lengthReceived) == FALSE
|
||||
|| lengthReceived != sizeof(deviceDesc)) {
|
||||
_tprintf(_T(" Error getting device descriptor %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
WinUsb_Free(winusbHandle);
|
||||
CloseHandle(deviceHandle);
|
||||
LocalFree(detailData);
|
||||
continue;
|
||||
}
|
||||
|
||||
#define SNDESCLEN 64
|
||||
PUSB_STRING_DESCRIPTOR psnDesc = (PUSB_STRING_DESCRIPTOR)LocalAlloc(LMEM_FIXED, SNDESCLEN);
|
||||
if (NULL == psnDesc) {
|
||||
_tprintf(_T(" Failed to allocate %d bytes for sn data\n"), SNDESCLEN);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (WinUsb_GetDescriptor(winusbHandle, USB_STRING_DESCRIPTOR_TYPE, deviceDesc.iSerialNumber,
|
||||
0x0409 /*Eng*/, (PBYTE)psnDesc, SNDESCLEN, &lengthReceived) == FALSE || lengthReceived == 0) {
|
||||
_tprintf(_T(" Error getting serial number %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
LocalFree(psnDesc);
|
||||
WinUsb_Free(winusbHandle);
|
||||
CloseHandle(deviceHandle);
|
||||
LocalFree(detailData);
|
||||
continue;
|
||||
}
|
||||
//The minus 2 is for the two numbers, not the null term.
|
||||
psnDesc->bString[(psnDesc->bLength - 2) / sizeof(_TCHAR)] = 0;
|
||||
|
||||
char w_to_m_buff[256];
|
||||
size_t mbuff_len;
|
||||
if (wcstombs_s(&mbuff_len, w_to_m_buff, sizeof(w_to_m_buff), psnDesc->bString, 24) != 0) {
|
||||
_tprintf(_T(" Error generating mb SN string %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
LocalFree(psnDesc);
|
||||
WinUsb_Free(winusbHandle);
|
||||
CloseHandle(deviceHandle);
|
||||
LocalFree(detailData);
|
||||
continue;
|
||||
}
|
||||
std::string serialnum(w_to_m_buff, mbuff_len-1);
|
||||
printf(" Device found: seriallen: %d; serial: %s\n", lengthReceived, serialnum.c_str());
|
||||
|
||||
map_sn_to_devpath[serialnum] = tstring(detailData->DevicePath);
|
||||
|
||||
LocalFree(psnDesc);
|
||||
WinUsb_Free(winusbHandle);
|
||||
CloseHandle(deviceHandle);
|
||||
LocalFree(detailData);
|
||||
}
|
||||
|
||||
if(deviceInfo)
|
||||
SetupDiDestroyDeviceInfoList(deviceInfo);
|
||||
|
||||
return map_sn_to_devpath;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef __PANDA_DEVICE
|
||||
#define __PANDA_DEVICE
|
||||
|
||||
//
|
||||
// Define below GUIDs
|
||||
//
|
||||
#include <initguid.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#if defined(UNICODE)
|
||||
#define _tcout std::wcout
|
||||
#define tstring std::wstring
|
||||
#else
|
||||
#define _tcout std::cout
|
||||
#define tstring std::string
|
||||
#endif
|
||||
|
||||
//
|
||||
// Device Interface GUID.
|
||||
// Used by all WinUsb devices that this application talks to.
|
||||
// Must match "DeviceInterfaceGUIDs" registry value specified in the INF file.
|
||||
// cce5291c-a69f-4995-a4c2-2ae57a51ade9
|
||||
//
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_panda,
|
||||
0xcce5291c,0xa69f,0x4995,0xa4,0xc2,0x2a,0xe5,0x7a,0x51,0xad,0xe9);
|
||||
|
||||
tstring GetLastErrorAsString();
|
||||
|
||||
namespace panda {
|
||||
std::unordered_map<std::string, tstring> __declspec(dllexport) detect_pandas();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,522 @@
|
||||
// panda.cpp : Defines the exported functions for the DLL application.
|
||||
//
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "device.h"
|
||||
#include "panda.h"
|
||||
|
||||
#define REQUEST_IN 0xC0
|
||||
#define REQUEST_OUT 0x40
|
||||
|
||||
#define CAN_TRANSMIT 1
|
||||
#define CAN_EXTENDED 4
|
||||
|
||||
using namespace panda;
|
||||
|
||||
Panda::Panda(
|
||||
WINUSB_INTERFACE_HANDLE WinusbHandle,
|
||||
HANDLE DeviceHandle,
|
||||
tstring devPath_,
|
||||
std::string sn_
|
||||
) : usbh(WinusbHandle), devh(DeviceHandle), devPath(devPath_), sn(sn_) {
|
||||
printf("CREATED A PANDA %s\n", this->sn.c_str());
|
||||
this->set_can_loopback(FALSE);
|
||||
this->set_raw_io(TRUE);
|
||||
this->set_alt_setting(0);
|
||||
}
|
||||
|
||||
Panda::~Panda() {
|
||||
WinUsb_Free(this->usbh);
|
||||
CloseHandle(this->devh);
|
||||
printf("Cleanup Panda %s\n", this->sn.c_str());
|
||||
}
|
||||
|
||||
std::vector<std::string> Panda::listAvailablePandas() {
|
||||
std::vector<std::string> ret;
|
||||
auto map_sn_to_devpath = detect_pandas();
|
||||
|
||||
for (auto kv : map_sn_to_devpath) {
|
||||
ret.push_back(std::string(kv.first));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr<Panda> Panda::openPanda(std::string sn)
|
||||
{
|
||||
auto map_sn_to_devpath = detect_pandas();
|
||||
|
||||
if (map_sn_to_devpath.empty()) return nullptr;
|
||||
if (map_sn_to_devpath.find(sn) == map_sn_to_devpath.end() && sn != "") return nullptr;
|
||||
|
||||
tstring devpath;
|
||||
if (sn.empty()) {
|
||||
sn = map_sn_to_devpath.begin()->first;
|
||||
devpath = map_sn_to_devpath.begin()->second;
|
||||
} else {
|
||||
devpath = map_sn_to_devpath[sn];
|
||||
}
|
||||
|
||||
HANDLE deviceHandle = CreateFile(devpath.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (INVALID_HANDLE_VALUE == deviceHandle) {
|
||||
_tprintf(_T(" Error opening Device Handle %d.\n"),// Msg: '%s'\n"),
|
||||
GetLastError());// , GetLastErrorAsString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WINUSB_INTERFACE_HANDLE winusbHandle;
|
||||
if (WinUsb_Initialize(deviceHandle, &winusbHandle) == FALSE) {
|
||||
_tprintf(_T(" Error initializing WinUSB %d.\n"),// Msg: '%s'\n"),
|
||||
GetLastError());// , GetLastErrorAsString().c_str());
|
||||
CloseHandle(deviceHandle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<Panda>(new Panda(winusbHandle, deviceHandle, map_sn_to_devpath[sn], sn));
|
||||
}
|
||||
|
||||
std::string Panda::get_usb_sn() {
|
||||
return std::string(this->sn);
|
||||
}
|
||||
|
||||
int Panda::control_transfer(
|
||||
uint8_t bmRequestType,
|
||||
uint8_t bRequest,
|
||||
uint16_t wValue,
|
||||
uint16_t wIndex,
|
||||
void * data,
|
||||
uint16_t wLength,
|
||||
unsigned int timeout
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(timeout);
|
||||
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
|
||||
//Create the setup packet
|
||||
SetupPacket.RequestType = bmRequestType;
|
||||
SetupPacket.Request = bRequest;
|
||||
SetupPacket.Value = wValue;
|
||||
SetupPacket.Index = wIndex;
|
||||
SetupPacket.Length = wLength;
|
||||
|
||||
//ULONG timeout = 10; // ms
|
||||
//WinUsb_SetPipePolicy(interfaceHandle, pipeID, PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout);
|
||||
|
||||
if (WinUsb_ControlTransfer(this->usbh, SetupPacket, (PUCHAR)data, wLength, &cbSent, 0) == FALSE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cbSent;
|
||||
}
|
||||
|
||||
int Panda::bulk_write(UCHAR endpoint, const void * buff, ULONG length, PULONG transferred, ULONG timeout) {
|
||||
if (this->usbh == INVALID_HANDLE_VALUE || !buff || !length || !transferred) return FALSE;
|
||||
|
||||
if (WinUsb_WritePipe(this->usbh, endpoint, (PUCHAR)buff, length, transferred, NULL) == FALSE) {
|
||||
_tprintf(_T(" Got error during bulk xfer: %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int Panda::bulk_read(UCHAR endpoint, void * buff, ULONG buff_size, PULONG transferred, ULONG timeout) {
|
||||
if (this->usbh == INVALID_HANDLE_VALUE || !buff || !buff_size || !transferred) return FALSE;
|
||||
|
||||
if (WinUsb_ReadPipe(this->usbh, endpoint, (PUCHAR)buff, buff_size, transferred, NULL) == FALSE) {
|
||||
_tprintf(_T(" Got error during bulk xfer: %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool Panda::set_alt_setting(UCHAR alt_setting) {
|
||||
if (WinUsb_AbortPipe(this->usbh, 0x81) == FALSE) {
|
||||
_tprintf(_T(" Error abobrting pipe before setting altsetting. continue. %d, Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
}
|
||||
if (WinUsb_SetCurrentAlternateSetting(this->usbh, alt_setting) == FALSE) {
|
||||
_tprintf(_T(" Error setting usb altsetting %d, Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Either the panda or the windows usb stack can drop messages
|
||||
// if an odd number of messages are sent before an interrupt IN
|
||||
// message is canceled. There are some other odd behaviors, but
|
||||
// the best solution so far has been to send a few messages
|
||||
// before using the device to clear out the pipe. No, the windows
|
||||
// functions for clearing/resetting/etc the pipe did not work.
|
||||
// This took way too to figure out a workaround.
|
||||
// New info. The most repeatable behavior is losing the first
|
||||
// message sent after setting alt setting to 1 (even without
|
||||
// receiving). Something like this happened on linux sometimes.
|
||||
bool loopback_backup = this->loopback;
|
||||
this->set_can_loopback(TRUE);
|
||||
Sleep(20); // Give time for any sent messages to appear in the RX buffer.
|
||||
this->can_clear(PANDA_CAN_RX);
|
||||
// send 4 messages becaus can_recv reads 4 messages at a time
|
||||
for (int i = 0; i < 4; i++) {
|
||||
printf("Sending PAD %d\n", i);
|
||||
if (this->can_send(0x7FF, FALSE, {}, 0, PANDA_CAN1) == FALSE) {
|
||||
auto err = GetLastError();
|
||||
printf("Got err on first send: %d\n", err);
|
||||
}
|
||||
}
|
||||
Sleep(10);
|
||||
//this->can_clear(PANDA_CAN_RX);
|
||||
|
||||
//Read the messages so they do not contaimnate the real message stream.
|
||||
this->can_recv();
|
||||
|
||||
//this->set_can_loopback(FALSE);
|
||||
this->set_can_loopback(loopback_backup);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UCHAR Panda::get_current_alt_setting() {
|
||||
UCHAR alt_setting;
|
||||
if (WinUsb_GetCurrentAlternateSetting(this->usbh, &alt_setting) == FALSE) {
|
||||
_tprintf(_T(" Error getting usb altsetting %d, Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return alt_setting;
|
||||
}
|
||||
|
||||
bool Panda::set_raw_io(bool val) {
|
||||
UCHAR raw_io = val;
|
||||
if (!WinUsb_SetPipePolicy(this->usbh, 0x81, RAW_IO, sizeof(raw_io), &raw_io)) {
|
||||
_tprintf(_T(" Error setting usb raw I/O pipe policy %d, Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PANDA_HEALTH Panda::get_health()
|
||||
{
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
|
||||
//Create the setup packet
|
||||
SetupPacket.RequestType = REQUEST_IN;
|
||||
SetupPacket.Request = 0xD2;
|
||||
SetupPacket.Value = 0;
|
||||
SetupPacket.Index = 0;
|
||||
SetupPacket.Length = sizeof(UCHAR);
|
||||
|
||||
//uint8_t health[13];
|
||||
PANDA_HEALTH health;
|
||||
|
||||
if (WinUsb_ControlTransfer(this->usbh, SetupPacket, (PUCHAR)&health, sizeof(health), &cbSent, 0) == FALSE) {
|
||||
_tprintf(_T(" Got unexpected error while reading panda health (2nd time) %d. Msg: '%s'\n"),
|
||||
GetLastError(), GetLastErrorAsString().c_str());
|
||||
}
|
||||
|
||||
return health;
|
||||
}
|
||||
|
||||
bool Panda::enter_bootloader() {
|
||||
return this->control_transfer(REQUEST_OUT, 0xd1, 0, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
std::string Panda::get_version() {
|
||||
char buff[0x40];
|
||||
ZeroMemory(&buff, sizeof(buff));
|
||||
|
||||
int xferCount = this->control_transfer(REQUEST_IN, 0xd6, 0, 0, buff, 0x40, 0);
|
||||
if (xferCount == -1) return std::string();
|
||||
return std::string(buff);
|
||||
}
|
||||
|
||||
//TODO: Do hash stuff for calculating the serial.
|
||||
std::string Panda::get_serial() {
|
||||
char buff[0x20];
|
||||
ZeroMemory(&buff, sizeof(buff));
|
||||
|
||||
int xferCount = this->control_transfer(REQUEST_IN, 0xD0, 0, 0, buff, 0x20, 0);
|
||||
if (xferCount == -1) return std::string();
|
||||
return std::string(buff);
|
||||
|
||||
//dat = self._handle.controlRead(REQUEST_IN, 0xd0, 0, 0, 0x20);
|
||||
//hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4]
|
||||
// assert(hashsig == calc_hash)
|
||||
// return[dat[0:0x10], dat[0x10:0x10 + 10]]
|
||||
}
|
||||
|
||||
//Secret appears to by raw bytes, not a string. TODO: Change returned type.
|
||||
std::string Panda::get_secret() {
|
||||
char buff[0x10];
|
||||
ZeroMemory(&buff, sizeof(buff));
|
||||
|
||||
int xferCount = this->control_transfer(REQUEST_IN, 0xd0, 1, 0, buff, 0x10, 0);
|
||||
if (xferCount == -1) return std::string();
|
||||
return std::string(buff);
|
||||
}
|
||||
|
||||
bool Panda::set_usb_power(bool on) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xe6, (int)on, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::set_esp_power(bool on) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xd9, (int)on, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::esp_reset(uint16_t bootmode = 0) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xda, bootmode, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::set_safety_mode(PANDA_SAFETY_MODE mode = SAFETY_NOOUTPUT) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xdc, mode, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::set_can_forwarding(PANDA_CAN_PORT from_bus, PANDA_CAN_PORT to_bus) {
|
||||
if (from_bus == PANDA_CAN_UNK) return FALSE;
|
||||
return this->control_transfer(REQUEST_OUT, 0xdd, from_bus, to_bus, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::set_gmlan(PANDA_GMLAN_HOST_PORT bus = PANDA_GMLAN_CAN3) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xdb, 1, (bus == PANDA_GMLAN_CLEAR) ? 0 : bus, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::set_can_loopback(bool enable) {
|
||||
this->loopback = enable;
|
||||
return this->control_transfer(REQUEST_OUT, 0xe5, enable, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
//Can not use the full range of 16 bit speed.
|
||||
//cbps means centa bits per second (tento of kbps)
|
||||
bool Panda::set_can_speed_cbps(PANDA_CAN_PORT bus, uint16_t speed) {
|
||||
if (bus == PANDA_CAN_UNK) return FALSE;
|
||||
return this->control_transfer(REQUEST_OUT, 0xde, bus, speed, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
//Can not use the full range of 16 bit speed.
|
||||
bool Panda::set_can_speed_kbps(PANDA_CAN_PORT bus, uint16_t speed) {
|
||||
return set_can_speed_cbps(bus, speed * 10);
|
||||
}
|
||||
|
||||
//Can not use full 32 bit range of rate
|
||||
bool Panda::set_uart_baud(PANDA_SERIAL_PORT uart, uint32_t rate) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xe4, uart, rate / 300, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::set_uart_parity(PANDA_SERIAL_PORT uart, PANDA_SERIAL_PORT_PARITY parity) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xe2, uart, parity, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
bool Panda::can_send_many(const std::vector<PANDA_CAN_MSG>& can_msgs) {
|
||||
std::vector<PANDA_CAN_MSG_INTERNAL> formatted_msgs;
|
||||
formatted_msgs.reserve(can_msgs.size());
|
||||
|
||||
for (auto msg : can_msgs) {
|
||||
if (msg.bus == PANDA_CAN_UNK) continue;
|
||||
if (msg.len > 8) continue;
|
||||
PANDA_CAN_MSG_INTERNAL tmpmsg = {};
|
||||
tmpmsg.rir = (msg.addr_29b) ?
|
||||
((msg.addr << 3) | CAN_TRANSMIT | CAN_EXTENDED) :
|
||||
(((msg.addr & 0x7FF) << 21) | CAN_TRANSMIT);
|
||||
tmpmsg.f2 = msg.len | (msg.bus << 4);
|
||||
memcpy(tmpmsg.dat, msg.dat, msg.len);
|
||||
formatted_msgs.push_back(tmpmsg);
|
||||
}
|
||||
|
||||
if (formatted_msgs.size() == 0) return FALSE;
|
||||
|
||||
unsigned int retcount;
|
||||
return this->bulk_write(3, formatted_msgs.data(),
|
||||
sizeof(PANDA_CAN_MSG_INTERNAL)*formatted_msgs.size(), (PULONG)&retcount, 0);
|
||||
}
|
||||
|
||||
bool Panda::can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus) {
|
||||
if (bus == PANDA_CAN_UNK) return FALSE;
|
||||
if (len > 8) return FALSE;
|
||||
PANDA_CAN_MSG msg;
|
||||
msg.addr_29b = addr_29b;
|
||||
msg.addr = addr;
|
||||
msg.len = len;
|
||||
memcpy(msg.dat, dat, msg.len);
|
||||
msg.bus = bus;
|
||||
return this->can_send_many(std::vector<PANDA_CAN_MSG>{msg});
|
||||
}
|
||||
|
||||
PANDA_CAN_MSG Panda::parse_can_recv(PANDA_CAN_MSG_INTERNAL *in_msg_raw) {
|
||||
PANDA_CAN_MSG in_msg;
|
||||
|
||||
in_msg.addr_29b = (bool)(in_msg_raw->rir & CAN_EXTENDED);
|
||||
in_msg.addr = (in_msg.addr_29b) ? (in_msg_raw->rir >> 3) : (in_msg_raw->rir >> 21);
|
||||
in_msg.recv_time = this->runningTime.getTimePassedUS();
|
||||
in_msg.recv_time_point = std::chrono::steady_clock::now();
|
||||
//The timestamp from the device is (in_msg_raw->f2 >> 16),
|
||||
//but this 16 bit value is a little hard to use. Using a
|
||||
//timer since the initialization of this device.
|
||||
in_msg.len = in_msg_raw->f2 & 0xF;
|
||||
memcpy(in_msg.dat, in_msg_raw->dat, 8);
|
||||
|
||||
in_msg.is_receipt = ((in_msg_raw->f2 >> 4) & 0x80) == 0x80;
|
||||
switch ((in_msg_raw->f2 >> 4) & 0x7F) {
|
||||
case PANDA_CAN1:
|
||||
in_msg.bus = PANDA_CAN1;
|
||||
break;
|
||||
case PANDA_CAN2:
|
||||
in_msg.bus = PANDA_CAN2;
|
||||
break;
|
||||
case PANDA_CAN3:
|
||||
in_msg.bus = PANDA_CAN3;
|
||||
break;
|
||||
default:
|
||||
in_msg.bus = PANDA_CAN_UNK;
|
||||
}
|
||||
return in_msg;
|
||||
}
|
||||
|
||||
bool Panda::can_rx_q_push(HANDLE kill_event, DWORD timeoutms) {
|
||||
while (1) {
|
||||
auto w_ptr = this->w_ptr;
|
||||
auto n_ptr = w_ptr + 1;
|
||||
if (n_ptr == CAN_RX_QUEUE_LEN) {
|
||||
n_ptr = 0;
|
||||
}
|
||||
|
||||
// Pause if there is not a slot available in the queue
|
||||
if (n_ptr == this->r_ptr) {
|
||||
printf("RX queue full!\n");
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this->can_rx_q[n_ptr].complete) {
|
||||
// TODO: is ResetEvent() faster?
|
||||
CloseHandle(this->can_rx_q[n_ptr].complete);
|
||||
}
|
||||
|
||||
// Overlapped structure required for async read.
|
||||
this->can_rx_q[n_ptr].complete = CreateEvent(NULL, TRUE, TRUE, NULL);
|
||||
memset(&this->can_rx_q[n_ptr].overlapped, sizeof(OVERLAPPED), 0);
|
||||
this->can_rx_q[n_ptr].overlapped.hEvent = this->can_rx_q[n_ptr].complete;
|
||||
this->can_rx_q[n_ptr].error = 0;
|
||||
|
||||
if (!WinUsb_ReadPipe(this->usbh, 0x81, this->can_rx_q[n_ptr].data, sizeof(this->can_rx_q[n_ptr].data), &this->can_rx_q[n_ptr].count, &this->can_rx_q[n_ptr].overlapped)) {
|
||||
// An overlapped read will return true if done, or false with an
|
||||
// error of ERROR_IO_PENDING if the transfer is still in process.
|
||||
this->can_rx_q[n_ptr].error = GetLastError();
|
||||
}
|
||||
|
||||
// Process the pipe read call from the previous invocation of this function
|
||||
if (this->can_rx_q[w_ptr].error == ERROR_IO_PENDING) {
|
||||
HANDLE phSignals[2] = { this->can_rx_q[w_ptr].complete, kill_event };
|
||||
auto dwError = WaitForMultipleObjects(kill_event ? 2 : 1, phSignals, FALSE, timeoutms);
|
||||
|
||||
// Check if packet, timeout (nope), or break
|
||||
if (dwError == WAIT_OBJECT_0) {
|
||||
// Signal came from our usb object. Read the returned data.
|
||||
if (!GetOverlappedResult(this->usbh, &this->can_rx_q[w_ptr].overlapped, &this->can_rx_q[w_ptr].count, TRUE)) {
|
||||
// TODO: handle other error cases better.
|
||||
dwError = GetLastError();
|
||||
printf("Got overlap error %d\n", dwError);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
WinUsb_AbortPipe(this->usbh, 0x81);
|
||||
|
||||
// Return FALSE to show that the optional signal
|
||||
// was set instead of the wait breaking from a
|
||||
// message or recoverable error.
|
||||
if (dwError == (WAIT_OBJECT_0 + 1)) {
|
||||
return FALSE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (this->can_rx_q[w_ptr].error != 0) { // ERROR_BAD_COMMAND happens when device is unplugged.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
this->w_ptr = n_ptr;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void Panda::can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count) {
|
||||
count = 0;
|
||||
|
||||
// No data left in queue
|
||||
if (this->r_ptr == this->w_ptr) {
|
||||
Sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
auto r_ptr = this->r_ptr;
|
||||
for (int i = 0; i < this->can_rx_q[r_ptr].count; i += sizeof(PANDA_CAN_MSG_INTERNAL)) {
|
||||
auto in_msg_raw = (PANDA_CAN_MSG_INTERNAL *)(this->can_rx_q[r_ptr].data + i);
|
||||
msg_out[count] = parse_can_recv(in_msg_raw);
|
||||
++count;
|
||||
}
|
||||
|
||||
// Advance read pointer (wrap around if needed)
|
||||
++r_ptr;
|
||||
this->r_ptr = (r_ptr == CAN_RX_QUEUE_LEN ? 0 : r_ptr);
|
||||
}
|
||||
|
||||
std::vector<PANDA_CAN_MSG> Panda::can_recv() {
|
||||
std::vector<PANDA_CAN_MSG> msg_recv;
|
||||
int retcount;
|
||||
char buff[sizeof(PANDA_CAN_MSG_INTERNAL) * 4];
|
||||
|
||||
if (this->bulk_read(0x81, buff, sizeof(buff), (PULONG)&retcount, 0) == FALSE)
|
||||
return msg_recv;
|
||||
|
||||
for (int i = 0; i < retcount; i += sizeof(PANDA_CAN_MSG_INTERNAL)) {
|
||||
PANDA_CAN_MSG_INTERNAL *in_msg_raw = (PANDA_CAN_MSG_INTERNAL *)(buff + i);
|
||||
auto in_msg = parse_can_recv(in_msg_raw);
|
||||
msg_recv.push_back(in_msg);
|
||||
}
|
||||
|
||||
return msg_recv;
|
||||
}
|
||||
|
||||
bool Panda::can_clear(PANDA_CAN_PORT_CLEAR bus) {
|
||||
/*Clears all messages from the specified internal CAN ringbuffer as though it were drained.
|
||||
bus(int) : can bus number to clear a tx queue, or 0xFFFF to clear the global can rx queue.*/
|
||||
return this->control_transfer(REQUEST_OUT, 0xf1, bus, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
|
||||
std::string Panda::serial_read(PANDA_SERIAL_PORT port_number) {
|
||||
std::string result;
|
||||
char buff[0x40];
|
||||
while (TRUE) {
|
||||
int retlen = this->control_transfer(REQUEST_IN, 0xe0, port_number, 0, &buff, 0x40, 0);
|
||||
if (retlen <= 0)
|
||||
break;
|
||||
result += std::string(buff, retlen);
|
||||
if (retlen < 0x40) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int Panda::serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len) {
|
||||
std::string dat;
|
||||
dat += port_number;
|
||||
dat += std::string((char*)buff, len);
|
||||
int retcount;
|
||||
if (this->bulk_write(2, dat.c_str(), len+1, (PULONG)&retcount, 0) == FALSE) return -1;
|
||||
return retcount;
|
||||
}
|
||||
|
||||
bool Panda::serial_clear(PANDA_SERIAL_PORT port_number) {
|
||||
return this->control_transfer(REQUEST_OUT, 0xf2, port_number, 0, NULL, 0, 0) != -1;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
#pragma once
|
||||
|
||||
// The following ifdef block is the standard way of creating macros which make exporting
|
||||
// from a DLL simpler. All files within this DLL are compiled with the PANDA_EXPORTS
|
||||
// symbol defined on the command line. This symbol should not be defined on any project
|
||||
// that uses this DLL. This way any other project whose source files include this file see
|
||||
// PANDA_API functions as being imported from a DLL, whereas this DLL sees symbols
|
||||
// defined with this macro as being exported.
|
||||
#ifdef PANDA_EXPORTS
|
||||
#define PANDA_API __declspec(dllexport)
|
||||
#else
|
||||
#define PANDA_API
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
#include <windows.h>
|
||||
#include <winusb.h>
|
||||
|
||||
#if defined(UNICODE)
|
||||
#define _tcout std::wcout
|
||||
#define tstring std::wstring
|
||||
#else
|
||||
#define _tcout std::cout
|
||||
#define tstring std::string
|
||||
#endif
|
||||
|
||||
#define LIN_MSG_MAX_LEN 10
|
||||
#define CAN_RX_QUEUE_LEN 10000
|
||||
#define CAN_RX_MSG_LEN 1000
|
||||
|
||||
//template class __declspec(dllexport) std::basic_string<char>;
|
||||
|
||||
namespace panda {
|
||||
typedef enum _PANDA_SAFETY_MODE : uint16_t {
|
||||
SAFETY_NOOUTPUT = 0,
|
||||
SAFETY_HONDA = 1,
|
||||
SAFETY_ALLOUTPUT = 0x1337,
|
||||
} PANDA_SAFETY_MODE;
|
||||
|
||||
typedef enum _PANDA_SERIAL_PORT : uint8_t {
|
||||
SERIAL_DEBUG = 0,
|
||||
SERIAL_ESP = 1,
|
||||
SERIAL_LIN1 = 2,
|
||||
SERIAL_LIN2 = 3,
|
||||
} PANDA_SERIAL_PORT;
|
||||
|
||||
typedef enum _PANDA_SERIAL_PORT_PARITY : uint8_t {
|
||||
PANDA_PARITY_OFF = 0,
|
||||
PANDA_PARITY_EVEN = 1,
|
||||
PANDA_PARITY_ODD = 2,
|
||||
} PANDA_SERIAL_PORT_PARITY;
|
||||
|
||||
typedef enum _PANDA_CAN_PORT : uint8_t {
|
||||
PANDA_CAN1 = 0,
|
||||
PANDA_CAN2 = 1,
|
||||
PANDA_CAN3 = 2,
|
||||
PANDA_CAN_UNK = 0xFF,
|
||||
} PANDA_CAN_PORT;
|
||||
|
||||
typedef enum _PANDA_CAN_PORT_CLEAR : uint16_t {
|
||||
PANDA_CAN1_TX = 0,
|
||||
PANDA_CAN2_TX = 1,
|
||||
PANDA_CAN3_TX = 2,
|
||||
PANDA_CAN_RX = 0xFFFF,
|
||||
} PANDA_CAN_PORT_CLEAR;
|
||||
|
||||
typedef enum _PANDA_GMLAN_HOST_PORT : uint8_t {
|
||||
PANDA_GMLAN_CLEAR = 0,
|
||||
PANDA_GMLAN_CAN2 = 1,
|
||||
PANDA_GMLAN_CAN3 = 2,
|
||||
} PANDA_GMLAN_HOST_PORT;
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct _PANDA_HEALTH {
|
||||
uint32_t voltage;
|
||||
uint32_t current;
|
||||
uint8_t started;
|
||||
uint8_t controls_allowed;
|
||||
uint8_t gas_interceptor_detected;
|
||||
uint8_t started_signal_detected;
|
||||
uint8_t started_alt;
|
||||
} PANDA_HEALTH, *PPANDA_HEALTH;
|
||||
|
||||
typedef struct _PANDA_CAN_MSG {
|
||||
uint32_t addr;
|
||||
unsigned long long recv_time; //In microseconds since device initialization
|
||||
std::chrono::time_point<std::chrono::steady_clock> recv_time_point;
|
||||
uint8_t dat[8];
|
||||
uint8_t len;
|
||||
PANDA_CAN_PORT bus;
|
||||
bool is_receipt;
|
||||
bool addr_29b;
|
||||
} PANDA_CAN_MSG;
|
||||
|
||||
//Copied from https://stackoverflow.com/a/31488113
|
||||
class Timer
|
||||
{
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point_type = std::chrono::time_point < clock, std::chrono::microseconds >;
|
||||
public:
|
||||
Timer() {
|
||||
start = std::chrono::time_point_cast<std::chrono::microseconds>(clock::now());
|
||||
}
|
||||
|
||||
// gets the time elapsed from construction.
|
||||
unsigned long long /*microseconds*/ Timer::getTimePassedUS() {
|
||||
// get the new time
|
||||
auto end = std::chrono::time_point_cast<std::chrono::microseconds>(clock::now());
|
||||
|
||||
// return the difference of the times
|
||||
return (end - start).count();
|
||||
}
|
||||
|
||||
// gets the time elapsed from construction.
|
||||
unsigned long long /*milliseconds*/ Timer::getTimePassedMS() {
|
||||
// get the new time
|
||||
auto end = std::chrono::time_point_cast<std::chrono::milliseconds>(clock::now());
|
||||
|
||||
// return the difference of the times
|
||||
auto startms = std::chrono::time_point_cast<std::chrono::milliseconds>(start);
|
||||
return (end - startms).count();
|
||||
}
|
||||
private:
|
||||
time_point_type start;
|
||||
};
|
||||
|
||||
// This class is exported from the panda.dll
|
||||
class PANDA_API Panda {
|
||||
public:
|
||||
static std::vector<std::string> listAvailablePandas();
|
||||
static std::unique_ptr<Panda> openPanda(std::string sn);
|
||||
|
||||
~Panda();
|
||||
|
||||
std::string get_usb_sn();
|
||||
bool set_alt_setting(UCHAR alt_setting);
|
||||
UCHAR get_current_alt_setting();
|
||||
bool Panda::set_raw_io(bool val);
|
||||
|
||||
PANDA_HEALTH get_health();
|
||||
bool enter_bootloader();
|
||||
std::string get_version();
|
||||
std::string get_serial();
|
||||
std::string get_secret();
|
||||
|
||||
bool set_usb_power(bool on);
|
||||
bool set_esp_power(bool on);
|
||||
bool esp_reset(uint16_t bootmode);
|
||||
bool set_safety_mode(PANDA_SAFETY_MODE mode);
|
||||
bool set_can_forwarding(PANDA_CAN_PORT from_bus, PANDA_CAN_PORT to_bus);
|
||||
bool set_gmlan(PANDA_GMLAN_HOST_PORT bus);
|
||||
bool set_can_loopback(bool enable);
|
||||
bool set_can_speed_cbps(PANDA_CAN_PORT bus, uint16_t speed);
|
||||
bool set_can_speed_kbps(PANDA_CAN_PORT bus, uint16_t speed);
|
||||
bool set_uart_baud(PANDA_SERIAL_PORT uart, uint32_t rate);
|
||||
bool set_uart_parity(PANDA_SERIAL_PORT uart, PANDA_SERIAL_PORT_PARITY parity);
|
||||
|
||||
bool can_send_many(const std::vector<PANDA_CAN_MSG>& can_msgs);
|
||||
bool can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus);
|
||||
std::vector<PANDA_CAN_MSG> can_recv();
|
||||
bool can_rx_q_push(HANDLE kill_event, DWORD timeoutms = INFINITE);
|
||||
void can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count);
|
||||
bool can_clear(PANDA_CAN_PORT_CLEAR bus);
|
||||
|
||||
std::string serial_read(PANDA_SERIAL_PORT port_number);
|
||||
int serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len);
|
||||
bool serial_clear(PANDA_SERIAL_PORT port_number);
|
||||
private:
|
||||
Panda(
|
||||
WINUSB_INTERFACE_HANDLE WinusbHandle,
|
||||
HANDLE DeviceHandle,
|
||||
tstring devPath_,
|
||||
std::string sn_
|
||||
);
|
||||
|
||||
int control_transfer(
|
||||
uint8_t bmRequestType,
|
||||
uint8_t bRequest,
|
||||
uint16_t wValue,
|
||||
uint16_t wIndex,
|
||||
void * data,
|
||||
uint16_t wLength,
|
||||
unsigned int timeout
|
||||
);
|
||||
|
||||
int bulk_write(
|
||||
UCHAR endpoint,
|
||||
const void * buff,
|
||||
ULONG length,
|
||||
PULONG transferred,
|
||||
ULONG timeout
|
||||
);
|
||||
|
||||
int Panda::bulk_read(
|
||||
UCHAR endpoint,
|
||||
void * buff,
|
||||
ULONG buff_size,
|
||||
PULONG transferred,
|
||||
ULONG timeout
|
||||
);
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct _PANDA_CAN_MSG_INTERNAL {
|
||||
uint32_t rir;
|
||||
uint32_t f2;
|
||||
uint8_t dat[8];
|
||||
} PANDA_CAN_MSG_INTERNAL;
|
||||
|
||||
typedef struct _CAN_RX_PIPE_READ {
|
||||
unsigned char data[sizeof(PANDA_CAN_MSG_INTERNAL) * CAN_RX_MSG_LEN];
|
||||
unsigned long count;
|
||||
OVERLAPPED overlapped;
|
||||
HANDLE complete;
|
||||
DWORD error;
|
||||
} CAN_RX_PIPE_READ;
|
||||
|
||||
PANDA_CAN_MSG parse_can_recv(PANDA_CAN_MSG_INTERNAL *in_msg_raw);
|
||||
|
||||
WINUSB_INTERFACE_HANDLE usbh;
|
||||
HANDLE devh;
|
||||
tstring devPath;
|
||||
std::string sn;
|
||||
bool loopback;
|
||||
|
||||
Timer runningTime;
|
||||
CAN_RX_PIPE_READ can_rx_q[CAN_RX_QUEUE_LEN];
|
||||
unsigned long w_ptr = 0;
|
||||
unsigned long r_ptr = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
<HasSharedItems>true</HasSharedItems>
|
||||
<ItemsProjectGuid>{0c843279-68c7-4679-ae51-9bc463d50d1c}</ItemsProjectGuid>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ProjectCapability Include="SourceItemsFromImports" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)device.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)panda.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)device.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)panda.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)targetver.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <WinSDKVer.h>
|
||||
|
||||
#define WINVER _WIN32_WINNT_WIN7
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WIN7
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
Reference in New Issue
Block a user