You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							522 lines
						
					
					
						
							16 KiB
						
					
					
				
			
		
		
	
	
							522 lines
						
					
					
						
							16 KiB
						
					
					
				// 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;
 | 
						|
}
 | 
						|
 |