// 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 Panda::listAvailablePandas() { std::vector 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::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(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& can_msgs) { std::vector 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{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_recv() { std::vector 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; }