_b ? _a : _b; })
+
+char ssid[32];
+static const int pin = 2;
+
+// Structure holding the TCP connection information.
+struct espconn tcp_conn;
+// TCP specific protocol structure.
+esp_tcp tcp_proto;
+
+// interrupt communication on port 1338, UDP!
+struct espconn inter_conn;
+esp_udp inter_proto;
+
+uint32_t sendData[0x14] = {0};
+uint32_t recvData[0x40] = {0};
+
+static int ICACHE_FLASH_ATTR __spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen) {
+ unsigned int length = 0;
+
+ SpiData spiData;
+
+ spiData.cmd = 2;
+ spiData.cmdLen = 0;
+ spiData.addr = NULL;
+ spiData.addrLen = 0;
+
+ // float boot pin
+ gpio_output_set(0, 0, 0, (1 << 4));
+
+ // manual CS pin
+ gpio_output_set(0, (1 << 5), 0, 0);
+ memset(sendData, 0xCC, 0x14);
+
+ // wait for ST to respond to CS interrupt
+ os_delay_us(50);
+
+ // send request
+ memcpy(((void*)sendData), dat, len);
+ spiData.data = sendData;
+ spiData.dataLen = 0x14;
+ SPIMasterSendData(SpiNum_HSPI, &spiData);
+
+ #define SPI_TIMEOUT 50000
+ // give the ST time to be ready, up to 500ms
+ int i;
+ for (i = 0; (gpio_input_get() & (1 << 4)) && i < SPI_TIMEOUT; i++) {
+ os_delay_us(10);
+ system_soft_wdt_feed();
+ }
+
+ // TODO: handle this better
+ if (i == SPI_TIMEOUT) {
+ os_printf("ERROR: SPI receive failed\n");
+ goto fail;
+ }
+
+ // blank out recvData
+ memset(recvData, 0x00, 0x44);
+
+ // receive the length
+ spiData.data = recvData;
+ spiData.dataLen = 4;
+ if(SPIMasterRecvData(SpiNum_HSPI, &spiData) == -1) {
+ // TODO: Handle gracefully. Maybe fail if len read fails?
+ os_printf("SPI: Failed to recv length\n");
+ goto fail;
+ }
+
+ length = recvData[0];
+ if (length > 0x40) {
+ os_printf("SPI: BAD LENGTH RECEIVED %x\n", length);
+ length = 0;
+ goto fail;
+ }
+
+ // got response, 0x40 works, 0x44 does not
+ spiData.data = recvData+1;
+ spiData.dataLen = (length+3)&(~3); // recvDataLen;
+ if(SPIMasterRecvData(SpiNum_HSPI, &spiData) == -1) {
+ // TODO: Handle gracefully. Maybe retry if payload failed.
+ os_printf("SPI: Failed to recv payload\n");
+ length = 0;
+ goto fail;
+ }
+
+fail:
+ // clear CS
+ gpio_output_set((1 << 5), 0, 0, 0);
+
+ // set boot pin back
+ gpio_output_set((1 << 4), 0, (1 << 4), 0);
+
+ return length;
+}
+
+int ICACHE_FLASH_ATTR spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen) {
+ // blink the led during SPI comm
+ if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & (1 << pin)) {
+ // set gpio low
+ gpio_output_set(0, (1 << pin), 0, 0);
+ } else {
+ // set gpio high
+ gpio_output_set((1 << pin), 0, 0, 0);
+ }
+
+ return __spi_comm(dat, len, recvData, recvDataLen);
+}
+
+static void ICACHE_FLASH_ATTR tcp_rx_cb(void *arg, char *data, uint16_t len) {
+ // nothing too big
+ if (len > 0x14) return;
+
+ // do the SPI comm
+ spi_comm(data, len, recvData, 0x40);
+
+ espconn_send(&tcp_conn, recvData, 0x44);
+}
+
+void ICACHE_FLASH_ATTR tcp_connect_cb(void *arg) {
+ struct espconn *conn = (struct espconn *)arg;
+ espconn_set_opt(&tcp_conn, ESPCONN_NODELAY);
+ espconn_regist_recvcb(conn, tcp_rx_cb);
+}
+
+// must be 0x44, because we can fit 4 more
+uint8_t buf[0x44*0x10];
+int queue_send_len = -1;
+
+void ICACHE_FLASH_ATTR poll_can(void *arg) {
+ uint8_t timerRecvData[0x44] = {0};
+ int i = 0;
+ int j;
+
+ while (i < 0x40) {
+ int len = spi_comm("\x01\x00\x00\x00", 4, timerRecvData, 0x40);
+ if (len == 0) break;
+ if (len > 0x40) { os_printf("SPI LENGTH ERROR!"); break; }
+
+ // if it sends it, assume it's valid CAN
+ for (j = 0; j < len; j += 0x10) {
+ memcpy(buf + i*0x10, (timerRecvData+4)+j, 0x10);
+ i++;
+ }
+ }
+
+ if (i != 0) {
+ int ret = espconn_sendto(&inter_conn, buf, i*0x10);
+ if (ret != 0) {
+ os_printf("send failed: %d\n", ret);
+ queue_send_len = i*0x10;
+ } else {
+ queue_send_len = -1;
+ }
+ }
+}
+
+int udp_countdown = 0;
+
+static volatile os_timer_t udp_callback;
+void ICACHE_FLASH_ATTR udp_callback_func(void *arg) {
+ if (queue_send_len == -1) {
+ poll_can(NULL);
+ } else {
+ int ret = espconn_sendto(&inter_conn, buf, queue_send_len);
+ if (ret == 0) {
+ queue_send_len = -1;
+ }
+ }
+ if (udp_countdown > 0) {
+ os_timer_arm(&udp_callback, 5, 0);
+ udp_countdown--;
+ } else {
+ os_printf("UDP timeout\n");
+ }
+}
+
+void ICACHE_FLASH_ATTR inter_recv_cb(void *arg, char *pusrdata, unsigned short length) {
+ remot_info *premot = NULL;
+ if (espconn_get_connection_info(&inter_conn,&premot,0) == ESPCONN_OK) {
+ inter_conn.proto.udp->remote_port = premot->remote_port;
+ inter_conn.proto.udp->remote_ip[0] = premot->remote_ip[0];
+ inter_conn.proto.udp->remote_ip[1] = premot->remote_ip[1];
+ inter_conn.proto.udp->remote_ip[2] = premot->remote_ip[2];
+ inter_conn.proto.udp->remote_ip[3] = premot->remote_ip[3];
+
+
+ if (udp_countdown == 0) {
+ os_printf("UDP recv\n");
+ udp_countdown = 200*5;
+
+ // start 5 second timer
+ os_timer_disarm(&udp_callback);
+ os_timer_setfn(&udp_callback, (os_timer_func_t *)udp_callback_func, NULL);
+ os_timer_arm(&udp_callback, 5, 0);
+ } else {
+ udp_countdown = 200*5;
+ }
+ }
+}
+
+void ICACHE_FLASH_ATTR wifi_init() {
+ // default ssid and password
+ memset(ssid, 0, 32);
+ os_sprintf(ssid, "panda-%08x-BROKEN", system_get_chip_id());
+ char password[] = "testing123";
+
+ // fetch secure ssid and password
+ // update, try 3 times
+ for (int i = 0; i < 3; i++) {
+ uint8_t digest[SHA_DIGEST_SIZE];
+ char resp[0x20];
+ __spi_comm("\x00\x00\x00\x00\x40\xD0\x00\x00\x00\x00\x20\x00", 0xC, recvData, 0x40);
+ memcpy(resp, recvData+1, 0x20);
+
+ SHA_hash(resp, 0x1C, digest);
+ if (memcmp(digest, resp+0x1C, 4) == 0) {
+ // OTP is valid
+ memcpy(ssid+6, resp, 0x10);
+ memcpy(password, resp+0x10, 10);
+ break;
+ }
+ os_delay_us(50000);
+ }
+
+ // start wifi AP
+ wifi_set_opmode(SOFTAP_MODE);
+ struct softap_config config;
+ wifi_softap_get_config(&config);
+ strcpy(config.ssid, ssid);
+ strcpy(config.password, password);
+ config.ssid_len = strlen(ssid);
+ config.authmode = AUTH_WPA2_PSK;
+ config.beacon_interval = 100;
+ config.max_connection = 10;
+ wifi_softap_set_config(&config);
+
+ //set IP
+ wifi_softap_dhcps_stop(); //stop DHCP before setting static IP
+ struct ip_info ip_config;
+ IP4_ADDR(&ip_config.ip, 192, 168, 0, 10);
+ IP4_ADDR(&ip_config.gw, 0, 0, 0, 0);
+ IP4_ADDR(&ip_config.netmask, 255, 255, 255, 0);
+ wifi_set_ip_info(SOFTAP_IF, &ip_config);
+ int stupid_gateway = 0;
+ wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &stupid_gateway);
+ wifi_softap_dhcps_start();
+
+ // setup tcp server
+ tcp_proto.local_port = 1337;
+ tcp_conn.type = ESPCONN_TCP;
+ tcp_conn.state = ESPCONN_NONE;
+ tcp_conn.proto.tcp = &tcp_proto;
+ espconn_regist_connectcb(&tcp_conn, tcp_connect_cb);
+ espconn_accept(&tcp_conn);
+ espconn_regist_time(&tcp_conn, 60, 0); // 60s timeout for all connections
+
+ // setup inter server
+ inter_proto.local_port = 1338;
+ const char udp_remote_ip[4] = {255, 255, 255, 255};
+ os_memcpy(inter_proto.remote_ip, udp_remote_ip, 4);
+ inter_proto.remote_port = 1338;
+
+ inter_conn.type = ESPCONN_UDP;
+ inter_conn.proto.udp = &inter_proto;
+
+ espconn_regist_recvcb(&inter_conn, inter_recv_cb);
+
+ espconn_create(&inter_conn);
+}
+
+#define LOOP_PRIO 2
+#define QUEUE_SIZE 1
+static os_event_t my_queue[QUEUE_SIZE];
+void loop();
+
+void ICACHE_FLASH_ATTR web_init();
+void ICACHE_FLASH_ATTR elm327_init();
+
+void ICACHE_FLASH_ATTR user_init() {
+ // init gpio subsystem
+ gpio_init();
+
+ // configure UART TXD to be GPIO1, set as output
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
+ gpio_output_set(0, 0, (1 << pin), 0);
+
+ // configure SPI
+ SpiAttr hSpiAttr;
+ hSpiAttr.bitOrder = SpiBitOrder_MSBFirst;
+ hSpiAttr.speed = SpiSpeed_10MHz;
+ hSpiAttr.mode = SpiMode_Master;
+ hSpiAttr.subMode = SpiSubMode_0;
+
+ // TODO: is one of these CS?
+ WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105);
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); // configure io to spi mode
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); // configure io to spi mode
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); // configure io to spi mode
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); // configure io to spi mode
+ SPIInit(SpiNum_HSPI, &hSpiAttr);
+ //SPICsPinSelect(SpiNum_HSPI, SpiPinCS_1);
+
+ // configure UART TXD to be GPIO1, set as output
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
+ gpio_output_set(0, 0, (1 << 5), 0);
+ gpio_output_set((1 << 5), 0, 0, 0);
+
+ // uart init
+ uart_init(BIT_RATE_115200, BIT_RATE_115200);
+
+ // led init
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
+ gpio_output_set(0, (1 << pin), (1 << pin), 0);
+
+ os_printf("hello\n");
+
+ // needs SPI
+ wifi_init();
+
+ // support ota upgrades
+ elm327_init();
+ web_init();
+
+ // set gpio high, so LED is off by default
+ for (int i = 0; i < 5; i++) {
+ gpio_output_set(0, (1 << pin), 0, 0);
+ os_delay_us(50000);
+ gpio_output_set((1 << pin), 0, 0, 0);
+ os_delay_us(50000);
+ }
+
+ // jump to OS
+ system_os_task(loop, LOOP_PRIO, my_queue, QUEUE_SIZE);
+ system_os_post(LOOP_PRIO, 0, 0);
+}
+
+
+void ICACHE_FLASH_ATTR loop(os_event_t *events) {
+ system_os_post(LOOP_PRIO, 0, 0);
+}
+
diff --git a/panda/boardesp/user_config.h b/panda/boardesp/user_config.h
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/panda/boardesp/webserver.c b/panda/boardesp/webserver.c
new file mode 100644
index 0000000000..66b1185fef
--- /dev/null
+++ b/panda/boardesp/webserver.c
@@ -0,0 +1,373 @@
+#include "stdlib.h"
+#include "ets_sys.h"
+#include "osapi.h"
+#include "gpio.h"
+#include "mem.h"
+#include "os_type.h"
+#include "user_interface.h"
+#include "espconn.h"
+#include "upgrade.h"
+
+#include "crypto/rsa.h"
+#include "crypto/sha.h"
+
+#include "obj/gitversion.h"
+#include "obj/cert.h"
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#define espconn_send_string(conn, x) espconn_send(conn, x, strlen(x))
+
+#define MAX_RESP 0x800
+char resp[MAX_RESP];
+char pageheader[] = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n"
+"\n"
+"\n"
+"\n"
+"Panda \n"
+"\n"
+"\n"
+"This is your comma.ai panda\n\n"
+"It's open source. Find the code here \n"
+"Designed to work with our dashcam, chffr \n";
+
+char pagefooter[] = " \n"
+"\n"
+"\n";
+
+char OK_header[] = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n";
+
+static struct espconn web_conn;
+static esp_tcp web_proto;
+extern char ssid[];
+
+char *st_firmware;
+int real_content_length, content_length = 0;
+char *st_firmware_ptr;
+LOCAL os_timer_t ota_reboot_timer;
+
+#define FIRMWARE_SIZE 503808
+
+typedef struct {
+ uint16_t ep;
+ uint16_t extra_len;
+ union {
+ struct {
+ uint8_t request_type;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+ } control;
+ uint8_t data[0x10];
+ } u;
+} usb_msg;
+
+int ICACHE_FLASH_ATTR usb_cmd(int ep, int len, int request,
+ int value, int index, char *data) {
+ usb_msg usb = {0};
+
+ usb.ep = ep;
+ usb.extra_len = (ep == 0) ? 0 : len;
+ if (ep == 0) {
+ usb.u.control.request_type = 0xc0;
+ usb.u.control.request = request;
+ usb.u.control.value = value;
+ usb.u.control.index = index;
+ } else {
+ memcpy(&usb.u.data, data, usb.extra_len);
+ }
+
+ uint32_t recv[0x44/4];
+ spi_comm(&usb, sizeof(usb), recv, 0x40);
+
+ return recv[0];
+}
+
+
+void ICACHE_FLASH_ATTR st_flash() {
+ if (st_firmware != NULL) {
+ // boot mode
+ os_printf("st_flash: enter boot mode\n");
+ st_set_boot_mode(1);
+
+ // echo
+ os_printf("st_flash: wait for echo\n");
+ for (int i = 0; i < 10; i++) {
+ os_printf(" attempt: %d\n", i);
+ if (usb_cmd(0, 0, 0xb0, 0, 0, NULL) > 0) break;
+ }
+
+ // unlock flash
+ os_printf("st_flash: unlock flash\n");
+ usb_cmd(0, 0, 0xb1, 0, 0, NULL);
+
+ // erase sector 1
+ os_printf("st_flash: erase sector 1\n");
+ usb_cmd(0, 0, 0xb2, 1, 0, NULL);
+
+ if (real_content_length >= 16384) {
+ // erase sector 2
+ os_printf("st_flash: erase sector 2\n");
+ usb_cmd(0, 0, 0xb2, 2, 0, NULL);
+ }
+
+ // real content length will always be 0x10 aligned
+ os_printf("st_flash: flashing\n");
+ for (int i = 0; i < real_content_length; i += 0x10) {
+ int rl = min(0x10, real_content_length-i);
+ usb_cmd(2, rl, 0, 0, 0, &st_firmware[i]);
+ system_soft_wdt_feed();
+ }
+
+ // reboot into normal mode
+ os_printf("st_flash: rebooting\n");
+ usb_cmd(0, 0, 0xd8, 0, 0, NULL);
+
+ // done with this
+ os_free(st_firmware);
+ st_firmware = NULL;
+ }
+}
+
+typedef enum {
+ NOT_STARTED,
+ CONNECTION_ESTABLISHED,
+ RECEIVING_HEADER,
+ RECEIVING_ST_FIRMWARE,
+ RECEIVING_ESP_FIRMWARE,
+ REBOOTING,
+ ERROR
+} web_state_t;
+
+web_state_t state = NOT_STARTED;
+int esp_address, esp_address_erase_limit, start_address;
+
+void ICACHE_FLASH_ATTR hexdump(char *data, int len) {
+ int i;
+ for (i=0;iClient"
+ "CDP "
+ "DCP \n");
+
+ ets_strcat(resp, pagefooter);
+
+ espconn_send_string(&web_conn, resp);
+ espconn_disconnect(conn);
+
+ } else if (memcmp(data, "GET /set_property?usb_mode=", 27) == 0) {
+ char mode_value = data[27] - '0';
+ if (mode_value >= '\x00' && mode_value <= '\x02') {
+ memset(resp, 0, MAX_RESP);
+ char set_usb_mode_packet[] = "\x00\x00\x00\x00\x40\xE6\x00\x00\x00\x00\x40\x00";
+ set_usb_mode_packet[6] = mode_value;
+ uint32_t recvData[1];
+ spi_comm(set_usb_mode_packet, 0xC, recvData, 0);
+ os_sprintf(resp, "%sUSB Mode set to %02x\n\n", OK_header, mode_value);
+ espconn_send_string(&web_conn, resp);
+ espconn_disconnect(conn);
+ }
+ } else if (memcmp(data, "PUT /stupdate ", 14) == 0) {
+ os_printf("init st firmware\n");
+ char *cl = strstr(data, "Content-Length: ");
+ if (cl != NULL) {
+ // get content length
+ cl += strlen("Content-Length: ");
+ content_length = skip_atoi(&cl);
+ os_printf("with content length %d\n", content_length);
+
+ // should be small enough to fit in RAM
+ real_content_length = (content_length+0xF)&(~0xF);
+ st_firmware_ptr = st_firmware = os_malloc(real_content_length);
+ memset(st_firmware, 0, real_content_length);
+ state = RECEIVING_ST_FIRMWARE;
+ }
+
+ } else if ((memcmp(data, "PUT /espupdate1 ", 16) == 0) ||
+ (memcmp(data, "PUT /espupdate2 ", 16) == 0)) {
+ // 0x1000 = user1.bin
+ // 0x81000 = user2.bin
+ // 0x3FE000 = blank.bin
+ os_printf("init esp firmware\n");
+ char *cl = strstr(data, "Content-Length: ");
+ if (cl != NULL) {
+ // get content length
+ cl += strlen("Content-Length: ");
+ content_length = skip_atoi(&cl);
+ os_printf("with content length %d\n", content_length);
+
+ // setup flashing
+ uint8_t current = system_upgrade_userbin_check();
+ if (data[14] == '2' && current == UPGRADE_FW_BIN1) {
+ os_printf("flashing boot2.bin\n");
+ state = RECEIVING_ESP_FIRMWARE;
+ esp_address = 4*1024 + FIRMWARE_SIZE + 16*1024 + 4*1024;
+ } else if (data[14] == '1' && current == UPGRADE_FW_BIN2) {
+ os_printf("flashing boot1.bin\n");
+ state = RECEIVING_ESP_FIRMWARE;
+ esp_address = 4*1024;
+ } else {
+ espconn_send_string(&web_conn, "HTTP/1.0 404 Not Found\nContent-Type: text/html\n\nwrong!\n");
+ espconn_disconnect(conn);
+ }
+ esp_address_erase_limit = esp_address;
+ start_address = esp_address;
+ }
+ } else {
+ espconn_send_string(&web_conn, "HTTP/1.0 404 Not Found\nContent-Type: text/html\n\n404 Not Found!\n");
+ espconn_disconnect(conn);
+ }
+ } else if (state == RECEIVING_ST_FIRMWARE) {
+ os_printf("receiving st firmware: %d/%d\n", len, content_length);
+ memcpy(st_firmware_ptr, data, min(content_length, len));
+ st_firmware_ptr += len;
+ content_length -= len;
+
+ if (content_length <= 0 && real_content_length > 1000) {
+ state = NOT_STARTED;
+ os_printf("done!\n");
+ espconn_send_string(&web_conn, "HTTP/1.0 200 OK\nContent-Type: text/html\n\nsuccess!\n");
+ espconn_disconnect(conn);
+
+ // reboot
+ os_printf("Scheduling st_flash in 100ms.\n");
+ os_timer_disarm(&ota_reboot_timer);
+ os_timer_setfn(&ota_reboot_timer, (os_timer_func_t *)st_flash, NULL);
+ os_timer_arm(&ota_reboot_timer, 100, 0);
+ }
+ } else if (state == RECEIVING_ESP_FIRMWARE) {
+ if ((esp_address+len) < (start_address + FIRMWARE_SIZE)) {
+ os_printf("receiving esp firmware: %d/%d -- 0x%x - 0x%x\n", len, content_length,
+ esp_address, esp_address_erase_limit);
+ content_length -= len;
+ while (esp_address_erase_limit < (esp_address + len)) {
+ os_printf("erasing 0x%X\n", esp_address_erase_limit);
+ spi_flash_erase_sector(esp_address_erase_limit / SPI_FLASH_SEC_SIZE);
+ esp_address_erase_limit += SPI_FLASH_SEC_SIZE;
+ }
+ SpiFlashOpResult res = spi_flash_write(esp_address, data, len);
+ if (res != SPI_FLASH_RESULT_OK) {
+ os_printf("flash fail @ 0x%x\n", esp_address);
+ }
+ esp_address += len;
+
+ if (content_length == 0) {
+
+ char digest[SHA_DIGEST_SIZE];
+ uint32_t rsa[RSANUMBYTES/4];
+ uint32_t dat[0x80/4];
+ int ll;
+ spi_flash_read(esp_address-RSANUMBYTES, rsa, RSANUMBYTES);
+
+ // 32-bit aligned accesses only
+ SHA_CTX ctx;
+ SHA_init(&ctx);
+ for (ll = start_address; ll < esp_address-RSANUMBYTES; ll += 0x80) {
+ spi_flash_read(ll, dat, 0x80);
+ SHA_update(&ctx, dat, min((esp_address-RSANUMBYTES)-ll, 0x80));
+ }
+ memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
+
+ if (RSA_verify(&releaseesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE) ||
+ #ifdef ALLOW_DEBUG
+ RSA_verify(&debugesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE)
+ #else
+ false
+ #endif
+ ) {
+ os_printf("RSA verify success!\n");
+ espconn_send_string(&web_conn, "HTTP/1.0 200 OK\nContent-Type: text/html\n\nsuccess!\n");
+ system_upgrade_flag_set(UPGRADE_FLAG_FINISH);
+
+ // reboot
+ os_printf("Scheduling reboot.\n");
+ os_timer_disarm(&ota_reboot_timer);
+ os_timer_setfn(&ota_reboot_timer, (os_timer_func_t *)system_upgrade_reboot, NULL);
+ os_timer_arm(&ota_reboot_timer, 2000, 0);
+ } else {
+ os_printf("RSA verify FAILURE\n");
+ espconn_send_string(&web_conn, "HTTP/1.0 500 Internal Server Error\nContent-Type: text/html\n\nrsa verify fail\n");
+ }
+ espconn_disconnect(conn);
+ }
+ }
+ }
+}
+
+void ICACHE_FLASH_ATTR web_tcp_connect_cb(void *arg) {
+ state = CONNECTION_ESTABLISHED;
+ struct espconn *conn = (struct espconn *)arg;
+ espconn_set_opt(&web_conn, ESPCONN_NODELAY);
+ espconn_regist_recvcb(conn, web_rx_cb);
+}
+
+void ICACHE_FLASH_ATTR web_init() {
+ web_proto.local_port = 80;
+ web_conn.type = ESPCONN_TCP;
+ web_conn.state = ESPCONN_NONE;
+ web_conn.proto.tcp = &web_proto;
+ espconn_regist_connectcb(&web_conn, web_tcp_connect_cb);
+ espconn_accept(&web_conn);
+}
+
diff --git a/panda/buy.png b/panda/buy.png
new file mode 100644
index 0000000000..0d4f2225b0
Binary files /dev/null and b/panda/buy.png differ
diff --git a/panda/certs/debug b/panda/certs/debug
new file mode 100644
index 0000000000..39864b6b1a
--- /dev/null
+++ b/panda/certs/debug
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC948lnRo4x44Rd7Y8bQAML4aKDC4XRx958fHV8K6+FbCaP1Z42
+U2kX0yygak0LjoDutpgObmGHZA+Iz3HeUD6VGjr/teN24vPk+A95cRsjt8rgmGQ9
+6HNjaNgjR+gl1F9XxFimMzir82Xpl1ekTueJNXa7ia5HVH1nFdiksOKHGQIDAQAB
+AoGAQuPw2I6EHJLW1/eNB75e1FqhUqRGeYV8nEGDaUBCTi+wzc4kM2LijF/5QnDv
+vvht9qkfm0XK2VSoHDtnEzcVM/l1ksb68n4R/1nUooAWY6cQI7dCSk/A6yS1EJFg
+BXsgGbT/65khw9pzBW2zVtMVcVNWFayqfCO1I9WcDdA1x1kCQQDfrhoZTZNoDEUE
+JKM4fiUdWr1h3Aw8KLJFFexSWeGDwo+qqnujYcKWkHa9qaH1RG5x8Kir9s9Oi4Js
+mzKwov8fAkEA2VPJPWxJ4vVQpXle6wC1nyoL7s739yxMWFcabvkzDDhlIVBNdVJd
+gZKsFWV7QnVNdDMjn9D27FwKu3i2D+kKxwJBANp1SMojqO765MEKI1t+YDNONx6H
+cm+i85Fjuv4nCIjOEdCGVuCYDxtMFpxgO2y3HAMuHx5sm8XDnWsDHLvFRdMCQD7V
+XqWHnYUk8AAnqy2+ssQl3/VXmZG5GQmhhV74Za3u0C5ljT+SZL6FrYMyKAT67T3f
+WzllrT6BDglNyTWoZxkCQQCt0XSoGM3603GGYNt6AUlGSgtXSo/2Px7odGUtQoKA
+FH9q6FVMYpQJ38spZxIGufZJmLP8LLg6YIWJj1F+akxr
+-----END RSA PRIVATE KEY-----
diff --git a/panda/certs/debug.pub b/panda/certs/debug.pub
new file mode 100644
index 0000000000..00e219d7bb
--- /dev/null
+++ b/panda/certs/debug.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC948lnRo4x44Rd7Y8bQAML4aKDC4XRx958fHV8K6+FbCaP1Z42U2kX0yygak0LjoDutpgObmGHZA+Iz3HeUD6VGjr/teN24vPk+A95cRsjt8rgmGQ96HNjaNgjR+gl1F9XxFimMzir82Xpl1ekTueJNXa7ia5HVH1nFdiksOKHGQ== batman@y840
diff --git a/panda/certs/debugesp b/panda/certs/debugesp
new file mode 100644
index 0000000000..789beaac19
--- /dev/null
+++ b/panda/certs/debugesp
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCjIHvrSCWN0Nec6ozbImYik30PIF7JSWgdwDKTxSJ05RM3pj5E
+LQEGt3qcaVrTokO68tpt5Gu1p6ZsNqWg7iVTW9M7Qj7IH45YDzQP/PSRjgSosQA6
+6f5Gokba5QrW38myqimvj+0p+YH+CNGCBRlTUQGCO8uLCspMZneRSLPW9QIDAQAB
+AoGADaUn+HRef9BaWMvd4G6uMHI54cwJYbj8NpDfKjExQqnuw5bqWnWRQmiSnwbJ
+DC7kj3zE/LBAuj890ot3q1CAWqh47ZICZfoX9Qbi5TpvIHFCGy6YkOliF6iIQhR2
+4+zNKTAA0zNKskOM25PdI+grK1Ni/bEofSA6TrqvEwsmxnkCQQDVp9FUUor2Bo/h
+/3oAIP51LTw7vfpztYbJr+BDV63czV2DLXzSwzeNrwH4sA3oy1mjUgMBBgAarNGE
+DYlc4H5jAkEAw3UCHzzXPlxkw2QGp7nBly5y3p80Uqc31NuYz8rdX/U8KTngi2No
+Ft/SGCEXNpeYbToj+WK3RJJ2Ey0mK8+IxwJAcpGd/5CPsaQNLcw4WK9Yo+8Q2Jxk
+G/4gfDCSmqn+smNxnLEcuUwzkwdgkEGgA9BfjeOhdsAH+EXpx90WZrZ/LwJBAK0k
+jq+rTqUQZbZsejTEKYjJ/bnV4BzDwoKN0Q1pkLc7X4LJoW74rTFuLgdv8MdMfRtt
+IIb/eoeFEpGkMicnHesCQHgR7BTUGBM6Uxam7RCdsgVsxoHBma21E/44ivWUMZzN
+3oVt0mPnjS4speOlqwED5pCJ7yw7jwLPFMs8kNxuIKU=
+-----END RSA PRIVATE KEY-----
diff --git a/panda/certs/debugesp.pub b/panda/certs/debugesp.pub
new file mode 100644
index 0000000000..3afcf3988e
--- /dev/null
+++ b/panda/certs/debugesp.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCjIHvrSCWN0Nec6ozbImYik30PIF7JSWgdwDKTxSJ05RM3pj5ELQEGt3qcaVrTokO68tpt5Gu1p6ZsNqWg7iVTW9M7Qj7IH45YDzQP/PSRjgSosQA66f5Gokba5QrW38myqimvj+0p+YH+CNGCBRlTUQGCO8uLCspMZneRSLPW9Q== batman@y840
diff --git a/panda/certs/release.pub b/panda/certs/release.pub
new file mode 100644
index 0000000000..19066e29a7
--- /dev/null
+++ b/panda/certs/release.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGN9GU2nOc0kKq6vdZI5qUMzHt234ngqofrgCFFxL0D2Whex0zACp9gar0HZp+bvtpoSgU/Ev8wexNKr+A9QTradljiuxi5ctrOra9k+wxqNj63Wrcu4+wU5UnJEVf/buV4jCOFffMT8z3PO4imt8LzHuEIC/m/ASKVYyvuvBRQQ== batman@y840
diff --git a/panda/certs/releaseesp.pub b/panda/certs/releaseesp.pub
new file mode 100644
index 0000000000..1d1d54bb7e
--- /dev/null
+++ b/panda/certs/releaseesp.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDN4pVyGuJJSde1l3Fjay8qPxog09DsAJZtYPk+armoYO1L6YKReUTcMNyHQYZZMZFmhCdgjCgTIF2QYWMoP4KSe8l6JF04YPP51dIgefc6UXjtlSI8Pyutr0v9xXjSfsVm3RAJxDSHgzs9AoMsluKCL+LhAR1nd7cuHXITJ80O4w== batman@y840
diff --git a/panda/common/version.mk b/panda/common/version.mk
new file mode 100644
index 0000000000..cc42223da3
--- /dev/null
+++ b/panda/common/version.mk
@@ -0,0 +1,18 @@
+ifeq ($(RELEASE),1)
+ BUILD_TYPE = "RELEASE"
+else
+ BUILD_TYPE = "DEBUG"
+endif
+
+ifneq ($(wildcard ../.git/HEAD),)
+obj/gitversion.h: ../VERSION ../.git/HEAD ../.git/index
+ echo "const uint8_t gitversion[] = \"$(shell cat ../VERSION)-$(shell git rev-parse --short=8 HEAD)-$(BUILD_TYPE)\";" > $@
+else
+ifneq ($(wildcard ../../.git/modules/panda/HEAD),)
+obj/gitversion.h: ../VERSION ../../.git/modules/panda/HEAD ../../.git/modules/panda/index
+ echo "const uint8_t gitversion[] = \"$(shell cat ../VERSION)-$(shell git rev-parse --short=8 HEAD)-$(BUILD_TYPE)\";" > $@
+else
+obj/gitversion.h: ../VERSION
+ echo "const uint8_t gitversion[] = \"$(shell cat ../VERSION)-unknown-$(BUILD_TYPE)\";" > $@
+endif
+endif
diff --git a/panda/crypto/getcertheader.py b/panda/crypto/getcertheader.py
new file mode 100755
index 0000000000..75d04e977a
--- /dev/null
+++ b/panda/crypto/getcertheader.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+import sys
+import struct
+from Crypto.PublicKey import RSA
+
+def egcd(a, b):
+ if a == 0:
+ return (b, 0, 1)
+ else:
+ g, y, x = egcd(b % a, a)
+ return (g, x - (b // a) * y, y)
+
+def modinv(a, m):
+ g, x, y = egcd(a, m)
+ if g != 1:
+ raise Exception('modular inverse does not exist')
+ else:
+ return x % m
+
+def to_c_string(x):
+ mod = (hex(x)[2:-1].rjust(0x100, '0'))
+ hh = ''.join('\\x'+mod[i:i+2] for i in range(0, 0x100, 2))
+ return hh
+
+def to_c_uint32(x):
+ nums = []
+ for i in range(0x20):
+ nums.append(x%(2**32))
+ x /= (2**32)
+ return "{"+'U,'.join(map(str, nums))+"U}"
+
+for fn in sys.argv[1:]:
+ rsa = RSA.importKey(open(fn).read())
+ rr = pow(2**1024, 2, rsa.n)
+ n0inv = 2**32 - modinv(rsa.n, 2**32)
+
+ cname = fn.split("/")[-1].split(".")[0] + "_rsa_key"
+
+ print 'RSAPublicKey '+cname+' = {.len = 0x20,'
+ print ' .n0inv = %dU,' % n0inv
+ print ' .n = %s,' % to_c_uint32(rsa.n)
+ print ' .rr = %s,' % to_c_uint32(rr)
+ print ' .exponent = %d,' % rsa.e
+ print '};'
+
+
diff --git a/panda/crypto/hash-internal.h b/panda/crypto/hash-internal.h
new file mode 100644
index 0000000000..05ec3ec9fd
--- /dev/null
+++ b/panda/crypto/hash-internal.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+
+#include "stdint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+struct HASH_CTX; // forward decl
+
+typedef struct HASH_VTAB {
+ void (* const init)(struct HASH_CTX*);
+ void (* const update)(struct HASH_CTX*, const void*, int);
+ const uint8_t* (* const final)(struct HASH_CTX*);
+ const uint8_t* (* const hash)(const void*, int, uint8_t*);
+ int size;
+} HASH_VTAB;
+
+typedef struct HASH_CTX {
+ const HASH_VTAB * f;
+ uint64_t count;
+ uint8_t buf[64];
+ uint32_t state[8]; // upto SHA2
+} HASH_CTX;
+
+#define HASH_init(ctx) (ctx)->f->init(ctx)
+#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
+#define HASH_final(ctx) (ctx)->f->final(ctx)
+#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
+#define HASH_size(ctx) (ctx)->f->size
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
diff --git a/panda/crypto/rsa.c b/panda/crypto/rsa.c
new file mode 100644
index 0000000000..24171e8790
--- /dev/null
+++ b/panda/crypto/rsa.c
@@ -0,0 +1,294 @@
+/* rsa.c
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of Google Inc. nor the names of its contributors may
+** be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "rsa.h"
+#include "sha.h"
+
+// a[] -= mod
+static void subM(const RSAPublicKey* key,
+ uint32_t* a) {
+ int64_t A = 0;
+ int i;
+ for (i = 0; i < key->len; ++i) {
+ A += (uint64_t)a[i] - key->n[i];
+ a[i] = (uint32_t)A;
+ A >>= 32;
+ }
+}
+
+// return a[] >= mod
+static int geM(const RSAPublicKey* key,
+ const uint32_t* a) {
+ int i;
+ for (i = key->len; i;) {
+ --i;
+ if (a[i] < key->n[i]) return 0;
+ if (a[i] > key->n[i]) return 1;
+ }
+ return 1; // equal
+}
+
+// montgomery c[] += a * b[] / R % mod
+static void montMulAdd(const RSAPublicKey* key,
+ uint32_t* c,
+ const uint32_t a,
+ const uint32_t* b) {
+ uint64_t A = (uint64_t)a * b[0] + c[0];
+ uint32_t d0 = (uint32_t)A * key->n0inv;
+ uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
+ int i;
+
+ for (i = 1; i < key->len; ++i) {
+ A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+ B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
+ c[i - 1] = (uint32_t)B;
+ }
+
+ A = (A >> 32) + (B >> 32);
+
+ c[i - 1] = (uint32_t)A;
+
+ if (A >> 32) {
+ subM(key, c);
+ }
+}
+
+// montgomery c[] = a[] * b[] / R % mod
+static void montMul(const RSAPublicKey* key,
+ uint32_t* c,
+ const uint32_t* a,
+ const uint32_t* b) {
+ int i;
+ for (i = 0; i < key->len; ++i) {
+ c[i] = 0;
+ }
+ for (i = 0; i < key->len; ++i) {
+ montMulAdd(key, c, a[i], b);
+ }
+}
+
+// In-place public exponentiation.
+// Input and output big-endian byte array in inout.
+static void modpow(const RSAPublicKey* key,
+ uint8_t* inout) {
+ uint32_t a[RSANUMWORDS];
+ uint32_t aR[RSANUMWORDS];
+ uint32_t aaR[RSANUMWORDS];
+ uint32_t* aaa = 0;
+ int i;
+
+ // Convert from big endian byte array to little endian word array.
+ for (i = 0; i < key->len; ++i) {
+ uint32_t tmp =
+ (inout[((key->len - 1 - i) * 4) + 0] << 24) |
+ (inout[((key->len - 1 - i) * 4) + 1] << 16) |
+ (inout[((key->len - 1 - i) * 4) + 2] << 8) |
+ (inout[((key->len - 1 - i) * 4) + 3] << 0);
+ a[i] = tmp;
+ }
+
+ if (key->exponent == 65537) {
+ aaa = aaR; // Re-use location.
+ montMul(key, aR, a, key->rr); // aR = a * RR / R mod M
+ for (i = 0; i < 16; i += 2) {
+ montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M
+ montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M
+ }
+ montMul(key, aaa, aR, a); // aaa = aR * a / R mod M
+ } else if (key->exponent == 3) {
+ aaa = aR; // Re-use location.
+ montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
+ montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
+ montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */
+ }
+
+ // Make sure aaa < mod; aaa is at most 1x mod too large.
+ if (geM(key, aaa)) {
+ subM(key, aaa);
+ }
+
+ // Convert to bigendian byte array
+ for (i = key->len - 1; i >= 0; --i) {
+ uint32_t tmp = aaa[i];
+ *inout++ = tmp >> 24;
+ *inout++ = tmp >> 16;
+ *inout++ = tmp >> 8;
+ *inout++ = tmp >> 0;
+ }
+}
+
+// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature.
+// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the
+// other flavor which omits the optional parameter entirely). This code does not
+// accept signatures without the optional parameter.
+
+/*
+static const uint8_t sha_padding[RSANUMBYTES] = {
+ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30,
+ 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+ 0x05, 0x00, 0x04, 0x14,
+
+ // 20 bytes of hash go here.
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+*/
+
+static const uint8_t sha_padding_1024[RSANUMBYTES] = {
+ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00,
+
+ // 20 bytes of hash go here.
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above.
+// At the location of the bytes of the hash all 00 are hashed.
+/*static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = {
+ 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e,
+ 0x6e, 0xfc, 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68,
+ 0x7c, 0xfb, 0xf1, 0x67
+};*/
+
+// Verify a 2048-bit RSA PKCS1.5 signature against an expected hash.
+// Both e=3 and e=65537 are supported. hash_len may be
+// SHA_DIGEST_SIZE (== 20) to indicate a SHA-1 hash, or
+// SHA256_DIGEST_SIZE (== 32) to indicate a SHA-256 hash. No other
+// values are supported.
+//
+// Returns 1 on successful verification, 0 on failure.
+int RSA_verify(const RSAPublicKey *key,
+ const uint8_t *signature,
+ const int len,
+ const uint8_t *hash,
+ const int hash_len) {
+ uint8_t buf[RSANUMBYTES];
+ int i;
+ //const uint8_t* padding_hash;
+
+ if (key->len != RSANUMWORDS) {
+ return 0; // Wrong key passed in.
+ }
+
+ if (len != sizeof(buf)) {
+ return 0; // Wrong input length.
+ }
+
+ if (hash_len != SHA_DIGEST_SIZE) {
+ return 0; // Unsupported hash.
+ }
+
+ if (key->exponent != 3 && key->exponent != 65537) {
+ return 0; // Unsupported exponent.
+ }
+
+ for (i = 0; i < len; ++i) { // Copy input to local workspace.
+ buf[i] = signature[i];
+ }
+
+ modpow(key, buf); // In-place exponentiation.
+
+#ifdef TEST_RSA
+ printf("sig\n");
+ for (i=0;i> (32 - (bits))))
+
+static void SHA1_Transform(SHA_CTX* ctx) {
+ uint32_t W[80];
+ uint32_t A, B, C, D, E;
+ uint8_t* p = ctx->buf;
+ int t;
+
+ for(t = 0; t < 16; ++t) {
+ uint32_t tmp = *p++ << 24;
+ tmp |= *p++ << 16;
+ tmp |= *p++ << 8;
+ tmp |= *p++;
+ W[t] = tmp;
+ }
+
+ for(; t < 80; t++) {
+ W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+ E = ctx->state[4];
+
+ for(t = 0; t < 80; t++) {
+ uint32_t tmp = rol(5,A) + E + W[t];
+
+ if (t < 20)
+ tmp += (D^(B&(C^D))) + 0x5A827999;
+ else if ( t < 40)
+ tmp += (B^C^D) + 0x6ED9EBA1;
+ else if ( t < 60)
+ tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
+ else
+ tmp += (B^C^D) + 0xCA62C1D6;
+
+ E = D;
+ D = C;
+ C = rol(30,B);
+ B = A;
+ A = tmp;
+ }
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+ ctx->state[4] += E;
+}
+
+static const HASH_VTAB SHA_VTAB = {
+ SHA_init,
+ SHA_update,
+ SHA_final,
+ SHA_hash,
+ SHA_DIGEST_SIZE
+};
+
+void SHA_init(SHA_CTX* ctx) {
+ ctx->f = &SHA_VTAB;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+ ctx->state[4] = 0xC3D2E1F0;
+ ctx->count = 0;
+}
+
+
+void SHA_update(SHA_CTX* ctx, const void* data, int len) {
+ int i = (int) (ctx->count & 63);
+ const uint8_t* p = (const uint8_t*)data;
+
+ ctx->count += len;
+
+ while (len--) {
+ ctx->buf[i++] = *p++;
+ if (i == 64) {
+ SHA1_Transform(ctx);
+ i = 0;
+ }
+ }
+}
+
+
+const uint8_t* SHA_final(SHA_CTX* ctx) {
+ uint8_t *p = ctx->buf;
+ uint64_t cnt = ctx->count * 8;
+ int i;
+
+ SHA_update(ctx, (uint8_t*)"\x80", 1);
+ while ((ctx->count & 63) != 56) {
+ SHA_update(ctx, (uint8_t*)"\0", 1);
+ }
+ for (i = 0; i < 8; ++i) {
+ uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
+ SHA_update(ctx, &tmp, 1);
+ }
+
+ for (i = 0; i < 5; i++) {
+ uint32_t tmp = ctx->state[i];
+ *p++ = tmp >> 24;
+ *p++ = tmp >> 16;
+ *p++ = tmp >> 8;
+ *p++ = tmp >> 0;
+ }
+
+ return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest) {
+ SHA_CTX ctx;
+ SHA_init(&ctx);
+ SHA_update(&ctx, data, len);
+ memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
+ return digest;
+}
diff --git a/panda/crypto/sha.h b/panda/crypto/sha.h
new file mode 100644
index 0000000000..4b51a531bf
--- /dev/null
+++ b/panda/crypto/sha.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
+
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX SHA_CTX;
+
+void SHA_init(SHA_CTX* ctx);
+void SHA_update(SHA_CTX* ctx, const void* data, int len);
+const uint8_t* SHA_final(SHA_CTX* ctx);
+
+// Convenience method. Returns digest address.
+// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes.
+const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest);
+
+#define SHA_DIGEST_SIZE 20
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
diff --git a/panda/crypto/sign.py b/panda/crypto/sign.py
new file mode 100755
index 0000000000..159299271e
--- /dev/null
+++ b/panda/crypto/sign.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+import os
+import sys
+import struct
+import hashlib
+from Crypto.PublicKey import RSA
+
+rsa = RSA.importKey(open(sys.argv[3]).read())
+
+with open(sys.argv[1]) as f:
+ dat = f.read()
+
+print "signing", len(dat), "bytes"
+
+with open(sys.argv[2], "wb") as f:
+ if os.getenv("SETLEN") is not None:
+ x = struct.pack("I", len(dat)) + dat[4:]
+ # mock signature of dat[4:]
+ dd = hashlib.sha1(dat[4:]).digest()
+ else:
+ x = dat
+ dd = hashlib.sha1(dat).digest()
+ print "hash:",dd.encode("hex")
+ dd = "\x00\x01" + "\xff"*0x69 + "\x00" + dd
+ rsa_out = pow(int(dd.encode("hex"), 16), rsa.d, rsa.n)
+ sig = (hex(rsa_out)[2:-1].rjust(0x100, '0')).decode("hex")
+ x += sig
+ f.write(x)
+
diff --git a/panda/crypto/stdint.h b/panda/crypto/stdint.h
new file mode 100644
index 0000000000..67ac93ed75
--- /dev/null
+++ b/panda/crypto/stdint.h
@@ -0,0 +1,4 @@
+#define uint8_t unsigned char
+#define uint32_t unsigned int
+#define int64_t long long
+#define uint64_t unsigned long long
diff --git a/panda/docs/guide.pdf b/panda/docs/guide.pdf
new file mode 100644
index 0000000000..5dbc95680a
Binary files /dev/null and b/panda/docs/guide.pdf differ
diff --git a/panda/drivers/linux/.gitignore b/panda/drivers/linux/.gitignore
new file mode 100644
index 0000000000..93a6cde763
--- /dev/null
+++ b/panda/drivers/linux/.gitignore
@@ -0,0 +1,6 @@
+.*.cmd
+*.ko
+.tmp_versions
+Module.symvers
+modules.order
+*.mod.c
diff --git a/panda/drivers/linux/Makefile b/panda/drivers/linux/Makefile
new file mode 100644
index 0000000000..e5b1ec4219
--- /dev/null
+++ b/panda/drivers/linux/Makefile
@@ -0,0 +1,18 @@
+VERSION=0.0.1
+obj-m+=panda.o
+
+link:
+ sudo dkms add `pwd`
+
+build:
+ sudo dkms build panda/$(VERSION)
+
+install:
+ sudo dkms install panda/$(VERSION)
+
+all: build install
+
+uninstall:
+ sudo dkms uninstall panda/$(VERSION)
+ sudo dkms remove panda/$(VERSION) --all
+
diff --git a/panda/drivers/linux/README.md b/panda/drivers/linux/README.md
new file mode 100644
index 0000000000..81e95523ac
--- /dev/null
+++ b/panda/drivers/linux/README.md
@@ -0,0 +1,19 @@
+Installs the panda linux kernel driver using DKMS.
+
+This will allow the panda to work with tools such as `can-utils`
+
+prerequisites:
+ - `apt-get install dkms gcc linux-headers-$(uname -r) make sudo`
+
+installation:
+ - `make link` (only needed the first time. It will report an error on subsequent attempts to link)
+ - `make all`
+ - `make install`
+
+uninstall:
+ - `make uninstall`
+
+usage:
+
+You will need to bring it up using `sudo ifconfig can0 up` or
+`sudo ip link set dev can0 up`, depending on your platform.
diff --git a/panda/drivers/linux/dkms.conf b/panda/drivers/linux/dkms.conf
new file mode 100644
index 0000000000..da9cba04a2
--- /dev/null
+++ b/panda/drivers/linux/dkms.conf
@@ -0,0 +1,6 @@
+PACKAGE_NAME="panda"
+PACKAGE_VERSION="0.0.1"
+BUILT_MODULE_NAME[0]="panda"
+DEST_MODULE_LOCATION[0]="/kernel/drivers/net/panda/"
+AUTOINSTALL="yes"
+
diff --git a/panda/drivers/linux/panda.c b/panda/drivers/linux/panda.c
new file mode 100644
index 0000000000..4c5980a9d2
--- /dev/null
+++ b/panda/drivers/linux/panda.c
@@ -0,0 +1,613 @@
+/**
+ * @file panda.c
+ * @author Jessy Diamond Exum
+ * @date 16 June 2017
+ * @version 0.1
+ * @brief Driver for the Comma.ai Panda CAN adapter to allow it to be controlled via
+ * the Linux SocketCAN interface.
+ * @see https://github.com/commaai/panda for the full project.
+ * @see Inspired by net/can/usb/mcba_usb.c from Linux Kernel 4.12-rc4.
+ */
+
+#include
+#include
+#include
+#include // Macros used to mark up functions e.g., __init __exit
+#include // Contains types, macros, functions for the kernel
+#include // Core header for loading LKMs into the kernel
+#include
+#include
+
+/* vendor and product id */
+#define PANDA_MODULE_NAME "panda"
+#define PANDA_VENDOR_ID 0XBBAA
+#define PANDA_PRODUCT_ID 0XDDCC
+
+#define PANDA_MAX_TX_URBS 20
+#define PANDA_CTX_FREE PANDA_MAX_TX_URBS
+
+#define PANDA_USB_RX_BUFF_SIZE 0x40
+#define PANDA_USB_TX_BUFF_SIZE (sizeof(struct panda_usb_can_msg))
+
+#define PANDA_NUM_CAN_INTERFACES 3
+
+#define PANDA_CAN_TRANSMIT 1
+#define PANDA_CAN_EXTENDED 4
+
+#define PANDA_BITRATE 500000
+
+#define PANDA_DLC_MASK 0x0F
+
+struct panda_usb_ctx {
+ struct panda_inf_priv *priv;
+ u32 ndx;
+ u8 dlc;
+};
+
+struct panda_dev_priv;
+
+struct panda_inf_priv {
+ struct can_priv can;
+ struct panda_usb_ctx tx_context[PANDA_MAX_TX_URBS];
+ struct net_device *netdev;
+ struct usb_anchor tx_submitted;
+ atomic_t free_ctx_cnt;
+ u8 interface_num;
+ u8 mcu_can_ifnum;
+ struct panda_dev_priv *priv_dev;
+};
+
+struct panda_dev_priv {
+ struct usb_device *udev;
+ struct device *dev;
+ struct usb_anchor rx_submitted;
+ struct panda_inf_priv *interfaces[PANDA_NUM_CAN_INTERFACES];
+};
+
+struct __packed panda_usb_can_msg {
+ u32 rir;
+ u32 bus_dat_len;
+ u8 data[8];
+};
+
+static const struct usb_device_id panda_usb_table[] = {
+ { USB_DEVICE(PANDA_VENDOR_ID, PANDA_PRODUCT_ID) },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, panda_usb_table);
+
+
+// panda: CAN1 = 0 CAN2 = 1 CAN3 = 4
+const int can_numbering[] = {0,1,4};
+
+struct panda_inf_priv *
+panda_get_inf_from_bus_id(struct panda_dev_priv *priv_dev, int bus_id){
+ int inf_num;
+ for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++)
+ if(can_numbering[inf_num] == bus_id)
+ return priv_dev->interfaces[inf_num];
+ return NULL;
+}
+
+// CTX handling shamlessly ripped from mcba_usb.c linux driver
+static inline void panda_init_ctx(struct panda_inf_priv *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < PANDA_MAX_TX_URBS; i++) {
+ priv->tx_context[i].ndx = PANDA_CTX_FREE;
+ priv->tx_context[i].priv = priv;
+ }
+
+ atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context));
+}
+
+static inline struct panda_usb_ctx *panda_usb_get_free_ctx(struct panda_inf_priv *priv,
+ struct can_frame *cf)
+{
+ int i = 0;
+ struct panda_usb_ctx *ctx = NULL;
+
+ for (i = 0; i < PANDA_MAX_TX_URBS; i++) {
+ if (priv->tx_context[i].ndx == PANDA_CTX_FREE) {
+ ctx = &priv->tx_context[i];
+ ctx->ndx = i;
+ ctx->dlc = cf->can_dlc;
+
+ atomic_dec(&priv->free_ctx_cnt);
+ break;
+ }
+ }
+
+ printk("CTX num %d\n", atomic_read(&priv->free_ctx_cnt));
+ if (!atomic_read(&priv->free_ctx_cnt)){
+ /* That was the last free ctx. Slow down tx path */
+ printk("SENDING TOO FAST\n");
+ netif_stop_queue(priv->netdev);
+ }
+
+ return ctx;
+}
+
+/* panda_usb_free_ctx and panda_usb_get_free_ctx are executed by different
+ * threads. The order of execution in below function is important.
+ */
+static inline void panda_usb_free_ctx(struct panda_usb_ctx *ctx)
+{
+ /* Increase number of free ctxs before freeing ctx */
+ atomic_inc(&ctx->priv->free_ctx_cnt);
+
+ ctx->ndx = PANDA_CTX_FREE;
+
+ /* Wake up the queue once ctx is marked free */
+ netif_wake_queue(ctx->priv->netdev);
+}
+
+
+
+static void panda_urb_unlink(struct panda_inf_priv *priv)
+{
+ usb_kill_anchored_urbs(&priv->priv_dev->rx_submitted);
+ usb_kill_anchored_urbs(&priv->tx_submitted);
+}
+
+static int panda_set_output_enable(struct panda_inf_priv* priv, bool enable){
+ return usb_control_msg(priv->priv_dev->udev,
+ usb_sndctrlpipe(priv->priv_dev->udev, 0),
+ 0xDC, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ enable ? 0x1337 : 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static void panda_usb_write_bulk_callback(struct urb *urb)
+{
+ struct panda_usb_ctx *ctx = urb->context;
+ struct net_device *netdev;
+
+ WARN_ON(!ctx);
+
+ netdev = ctx->priv->netdev;
+
+ /* free up our allocated buffer */
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+
+ if (!netif_device_present(netdev))
+ return;
+
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += ctx->dlc;
+
+ can_get_echo_skb(netdev, ctx->ndx);
+
+ if (urb->status)
+ netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
+
+ /* Release the context */
+ panda_usb_free_ctx(ctx);
+}
+
+
+static netdev_tx_t panda_usb_xmit(struct panda_inf_priv *priv,
+ struct panda_usb_can_msg *usb_msg,
+ struct panda_usb_ctx *ctx)
+{
+ struct urb *urb;
+ u8 *buf;
+ int err;
+
+ /* create a URB, and a buffer for it, and copy the data to the URB */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = usb_alloc_coherent(priv->priv_dev->udev,
+ PANDA_USB_TX_BUFF_SIZE, GFP_ATOMIC,
+ &urb->transfer_dma);
+ if (!buf) {
+ err = -ENOMEM;
+ goto nomembuf;
+ }
+
+ memcpy(buf, usb_msg, PANDA_USB_TX_BUFF_SIZE);
+
+ usb_fill_bulk_urb(urb, priv->priv_dev->udev,
+ usb_sndbulkpipe(priv->priv_dev->udev, 3), buf,
+ PANDA_USB_TX_BUFF_SIZE, panda_usb_write_bulk_callback,
+ ctx);
+
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err))
+ goto failed;
+
+ /* Release our reference to this URB, the USB core will eventually free it entirely. */
+ usb_free_urb(urb);
+
+ return 0;
+
+ failed:
+ usb_unanchor_urb(urb);
+ usb_free_coherent(priv->priv_dev->udev, PANDA_USB_TX_BUFF_SIZE, buf, urb->transfer_dma);
+
+ if (err == -ENODEV)
+ netif_device_detach(priv->netdev);
+ else
+ netdev_warn(priv->netdev, "failed tx_urb %d\n", err);
+
+ nomembuf:
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void panda_usb_process_can_rx(struct panda_dev_priv *priv_dev,
+ struct panda_usb_can_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ int bus_num;
+ struct panda_inf_priv *priv_inf;
+ struct net_device_stats *stats;
+
+ bus_num = (msg->bus_dat_len >> 4) & 0xf;
+ priv_inf = panda_get_inf_from_bus_id(priv_dev, bus_num);
+ if(!priv_inf){
+ printk("Got something on an unused interface %d\n", bus_num);
+ return;
+ }
+ printk("Recv bus %d\n", bus_num);
+
+ stats = &priv_inf->netdev->stats;
+ //u16 sid;
+
+ if (!netif_device_present(priv_inf->netdev))
+ return;
+
+ skb = alloc_can_skb(priv_inf->netdev, &cf);
+ if (!skb)
+ return;
+
+ if(msg->rir & PANDA_CAN_EXTENDED){
+ cf->can_id = (msg->rir >> 3) | CAN_EFF_FLAG;
+ }else{
+ cf->can_id = (msg->rir >> 21);
+ }
+
+ // TODO: Handle Remote Frames
+ //if (msg->dlc & MCBA_DLC_RTR_MASK)
+ // cf->can_id |= CAN_RTR_FLAG;
+
+ cf->can_dlc = get_can_dlc(msg->bus_dat_len & PANDA_DLC_MASK);
+
+ memcpy(cf->data, msg->data, cf->can_dlc);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ netif_rx(skb);
+}
+
+static void panda_usb_read_int_callback(struct urb *urb)
+{
+ struct panda_dev_priv *priv_dev = urb->context;
+ int retval;
+ int pos = 0;
+ int inf_num;
+
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default:
+ dev_info(priv_dev->dev, "Rx URB aborted (%d)\n", urb->status);
+ goto resubmit_urb;
+ }
+
+ while (pos < urb->actual_length) {
+ struct panda_usb_can_msg *msg;
+
+ if (pos + sizeof(struct panda_usb_can_msg) > urb->actual_length) {
+ dev_err(priv_dev->dev, "format error\n");
+ break;
+ }
+
+ msg = (struct panda_usb_can_msg *)(urb->transfer_buffer + pos);
+
+ panda_usb_process_can_rx(priv_dev, msg);
+
+ pos += sizeof(struct panda_usb_can_msg);
+ }
+
+ resubmit_urb:
+ usb_fill_int_urb(urb, priv_dev->udev,
+ usb_rcvintpipe(priv_dev->udev, 1),
+ urb->transfer_buffer, PANDA_USB_RX_BUFF_SIZE,
+ panda_usb_read_int_callback, priv_dev, 5);
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (retval == -ENODEV){
+ for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++)
+ if(priv_dev->interfaces[inf_num])
+ netif_device_detach(priv_dev->interfaces[inf_num]->netdev);
+ }else if (retval)
+ dev_err(priv_dev->dev, "failed resubmitting read bulk urb: %d\n", retval);
+}
+
+
+static int panda_usb_start(struct panda_dev_priv *priv_dev)
+{
+ int err;
+ struct urb *urb = NULL;
+ u8 *buf;
+ int inf_num;
+
+ for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++)
+ panda_init_ctx(priv_dev->interfaces[inf_num]);
+
+ err = usb_set_interface(priv_dev->udev, 0, 1);
+ if (err) {
+ dev_err(priv_dev->dev, "Can not set alternate setting to 1, error: %i", err);
+ return err;
+ }
+
+ /* create a URB, and a buffer for it */
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ return -ENOMEM;
+ }
+
+ buf = usb_alloc_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ dev_err(priv_dev->dev, "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ usb_fill_int_urb(urb, priv_dev->udev,
+ usb_rcvintpipe(priv_dev->udev, 1),
+ buf, PANDA_USB_RX_BUFF_SIZE,
+ panda_usb_read_int_callback, priv_dev, 5);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_anchor_urb(urb, &priv_dev->rx_submitted);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ usb_free_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE,
+ buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ dev_err(priv_dev->dev, "Failed in start, while submitting urb.\n");
+ return err;
+ }
+
+ /* Drop reference, USB core will take care of freeing it */
+ usb_free_urb(urb);
+
+
+ return 0;
+}
+
+/* Open USB device */
+static int panda_usb_open(struct net_device *netdev)
+{
+ struct panda_inf_priv *priv = netdev_priv(netdev);
+ int err;
+
+ /* common open */
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ //priv->can_speed_check = true;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ netif_start_queue(netdev);
+
+ return 0;
+}
+
+/* Close USB device */
+static int panda_usb_close(struct net_device *netdev)
+{
+ struct panda_inf_priv *priv = netdev_priv(netdev);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ netif_stop_queue(netdev);
+
+ /* Stop polling */
+ panda_urb_unlink(priv);
+
+ close_candev(netdev);
+
+ return 0;
+}
+
+static netdev_tx_t panda_usb_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct panda_inf_priv *priv_inf = netdev_priv(netdev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct panda_usb_ctx *ctx = NULL;
+ struct net_device_stats *stats = &priv_inf->netdev->stats;
+ int err;
+ struct panda_usb_can_msg usb_msg = {};
+ int bus = priv_inf->mcu_can_ifnum;
+
+ if (can_dropped_invalid_skb(netdev, skb)){
+ printk("Invalid CAN packet");
+ return NETDEV_TX_OK;
+ }
+
+ ctx = panda_usb_get_free_ctx(priv_inf, cf);
+
+ //Warning: cargo cult. Can't tell what this is for, but it is
+ //everywhere and encouraged in the documentation.
+ can_put_echo_skb(skb, priv_inf->netdev, ctx->ndx);
+
+ if(cf->can_id & CAN_EFF_FLAG){
+ usb_msg.rir = cpu_to_le32(((cf->can_id & 0x1FFFFFFF) << 3) |
+ PANDA_CAN_TRANSMIT | PANDA_CAN_EXTENDED);
+ }else{
+ usb_msg.rir = cpu_to_le32(((cf->can_id & 0x7FF) << 21) | PANDA_CAN_TRANSMIT);
+ }
+ usb_msg.bus_dat_len = cpu_to_le32((cf->can_dlc & 0x0F) | (bus << 4));
+
+ memcpy(usb_msg.data, cf->data, cf->can_dlc);
+
+ //TODO Handle Remote Frames
+ //if (cf->can_id & CAN_RTR_FLAG)
+ // usb_msg.dlc |= PANDA_DLC_RTR_MASK;
+
+ netdev_err(netdev, "Received data from socket. canid: %x; len: %d\n", cf->can_id, cf->can_dlc);
+
+ err = panda_usb_xmit(priv_inf, &usb_msg, ctx);
+ if (err)
+ goto xmit_failed;
+
+ return NETDEV_TX_OK;
+
+ xmit_failed:
+ can_free_echo_skb(priv_inf->netdev, ctx->ndx);
+ panda_usb_free_ctx(ctx);
+ dev_kfree_skb(skb);
+ stats->tx_dropped++;
+
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops panda_netdev_ops = {
+ .ndo_open = panda_usb_open,
+ .ndo_stop = panda_usb_close,
+ .ndo_start_xmit = panda_usb_start_xmit,
+};
+
+static int panda_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct net_device *netdev;
+ struct panda_inf_priv *priv_inf;
+ int err = -ENOMEM;
+ int inf_num;
+ struct panda_dev_priv *priv_dev;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ priv_dev = kzalloc(sizeof(struct panda_dev_priv), GFP_KERNEL);
+ if (!priv_dev) {
+ dev_err(&intf->dev, "Couldn't alloc priv_dev\n");
+ return -ENOMEM;
+ }
+ priv_dev->udev = usbdev;
+ priv_dev->dev = &intf->dev;
+ usb_set_intfdata(intf, priv_dev);
+
+ ////// Interface privs
+ for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){
+ netdev = alloc_candev(sizeof(struct panda_inf_priv), PANDA_MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Couldn't alloc candev\n");
+ goto cleanup_candev;
+ }
+ netdev->netdev_ops = &panda_netdev_ops;
+ netdev->flags |= IFF_ECHO; /* we support local echo */
+
+ priv_inf = netdev_priv(netdev);
+ priv_inf->netdev = netdev;
+ priv_inf->priv_dev = priv_dev;
+ priv_inf->interface_num = inf_num;
+ priv_inf->mcu_can_ifnum = can_numbering[inf_num];
+
+ init_usb_anchor(&priv_dev->rx_submitted);
+ init_usb_anchor(&priv_inf->tx_submitted);
+
+ /* Init CAN device */
+ priv_inf->can.state = CAN_STATE_STOPPED;
+ priv_inf->can.bittiming.bitrate = PANDA_BITRATE;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ err = register_candev(netdev);
+ if (err) {
+ netdev_err(netdev, "couldn't register PANDA CAN device: %d\n", err);
+ free_candev(priv_inf->netdev);
+ goto cleanup_candev;
+ }
+
+ priv_dev->interfaces[inf_num] = priv_inf;
+ }
+
+ err = panda_usb_start(priv_dev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to initialize Comma.ai Panda CAN controller\n");
+ goto cleanup_candev;
+ }
+
+ err = panda_set_output_enable(priv_inf, true);
+ if (err) {
+ dev_info(&intf->dev, "Failed to initialize send enable message to Panda.\n");
+ goto cleanup_candev;
+ }
+
+ dev_info(&intf->dev, "Comma.ai Panda CAN controller connected\n");
+
+ return 0;
+
+ cleanup_candev:
+ for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){
+ priv_inf = priv_dev->interfaces[inf_num];
+ if(priv_inf){
+ unregister_candev(priv_inf->netdev);
+ free_candev(priv_inf->netdev);
+ }else
+ break;
+ }
+
+ kfree(priv_dev);
+
+ return err;
+}
+
+/* Called by the usb core when driver is unloaded or device is removed */
+static void panda_usb_disconnect(struct usb_interface *intf)
+{
+ struct panda_dev_priv *priv_dev = usb_get_intfdata(intf);
+ struct panda_inf_priv *priv_inf;
+ int inf_num;
+
+ usb_set_intfdata(intf, NULL);
+
+ for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){
+ priv_inf = priv_dev->interfaces[inf_num];
+ if(priv_inf){
+ netdev_info(priv_inf->netdev, "device disconnected\n");
+ unregister_candev(priv_inf->netdev);
+ free_candev(priv_inf->netdev);
+ }else
+ break;
+ }
+
+ panda_urb_unlink(priv_inf);
+ kfree(priv_dev);
+}
+
+static struct usb_driver panda_usb_driver = {
+ .name = PANDA_MODULE_NAME,
+ .probe = panda_usb_probe,
+ .disconnect = panda_usb_disconnect,
+ .id_table = panda_usb_table,
+};
+
+module_usb_driver(panda_usb_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jessy Diamond Exum ");
+MODULE_DESCRIPTION("SocketCAN driver for Comma.ai's Panda Adapter.");
+MODULE_VERSION("0.1");
diff --git a/panda/drivers/linux/test/Makefile b/panda/drivers/linux/test/Makefile
new file mode 100644
index 0000000000..c73945e4cd
--- /dev/null
+++ b/panda/drivers/linux/test/Makefile
@@ -0,0 +1,2 @@
+all:
+ gcc main.c -o cantest -pthread -lpthread
diff --git a/panda/drivers/linux/test/main.c b/panda/drivers/linux/test/main.c
new file mode 100644
index 0000000000..1f44efc76e
--- /dev/null
+++ b/panda/drivers/linux/test/main.c
@@ -0,0 +1,120 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+const char *ifname = "can0";
+
+static unsigned char payload[] = {0xAA, 0xAA, 0xAA, 0xAA, 0x07, 0x00, 0x00, 0x00};
+int packet_len = 8;
+int dir = 0;
+
+void *write_thread( void *dat ){
+ int nbytes;
+ struct can_frame frame;
+ int s = *((int*) dat);
+
+ while(1){
+ for(int i = 0; i < 1; i ++){
+ if(packet_len % 2){
+ frame.can_id = 0x8AA | CAN_EFF_FLAG;
+ }else{
+ frame.can_id = 0xAA;
+ }
+
+ frame.can_dlc = packet_len;
+ memcpy(frame.data, payload, frame.can_dlc);
+
+ nbytes = write(s, &frame, sizeof(struct can_frame));
+
+ printf("Wrote %d bytes; addr: %lx; datlen: %d\n", nbytes, frame.can_id, frame.can_dlc);
+
+ if(dir){
+ packet_len++;
+ if(packet_len >= 8)
+ dir = 0;
+ }else{
+ packet_len--;
+ if(packet_len <= 0)
+ dir = 1;
+ }
+ }
+ sleep(2);
+ }
+}
+
+
+int main(void)
+{
+ pthread_t sndthread;
+ int err, s, nbytes;
+ struct sockaddr_can addr;
+ struct can_frame frame;
+ struct ifreq ifr;
+
+ if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
+ perror("Error while opening socket");
+ return -1;
+ }
+
+ strcpy(ifr.ifr_name, ifname);
+ ioctl(s, SIOCGIFINDEX, &ifr);
+
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = ifr.ifr_ifindex;
+
+ printf("%s at index %d\n", ifname, ifr.ifr_ifindex);
+
+ if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("Error in socket bind");
+ return -2;
+ }
+
+ /////// Create Write Thread
+
+ err = pthread_create( &sndthread, NULL, write_thread, (void*) &s);
+ if(err){
+ fprintf(stderr,"Error - pthread_create() return code: %d\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ /////// Listen to socket
+ while (1) {
+ struct can_frame framein;
+
+ // Read in a CAN frame
+ int numBytes = read(s, &framein, CANFD_MTU);
+ switch (numBytes) {
+ case CAN_MTU:
+ if(framein.can_id & 0x80000000)
+ printf("Received %u byte payload; canid 0x%lx (EXT)\n",
+ framein.can_dlc, framein.can_id & 0x7FFFFFFF);
+ else
+ printf("Received %u byte payload; canid 0x%lx\n", framein.can_dlc, framein.can_id);
+ break;
+ case CANFD_MTU:
+ // TODO: Should make an example for CAN FD
+ break;
+ case -1:
+ // Check the signal value on interrupt
+ //if (EINTR == errno)
+ // continue;
+
+ // Delay before continuing
+ sleep(1);
+ default:
+ continue;
+ }
+ }
+
+ return 0;
+}
diff --git a/panda/drivers/linux/test/run.sh b/panda/drivers/linux/test/run.sh
new file mode 100755
index 0000000000..5301719b49
--- /dev/null
+++ b/panda/drivers/linux/test/run.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+sudo ifconfig can0 up
+make
+./cantest
diff --git a/panda/drivers/windows/.gitignore b/panda/drivers/windows/.gitignore
new file mode 100644
index 0000000000..dbe7ad5a91
--- /dev/null
+++ b/panda/drivers/windows/.gitignore
@@ -0,0 +1,306 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+Debug_x86/
+Debug_x64/
+Release_x86/
+Release_x64/
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# installer
+*.exe
diff --git a/panda/drivers/windows/ECUsim CLI/ECUsim CLI.cpp b/panda/drivers/windows/ECUsim CLI/ECUsim CLI.cpp
new file mode 100644
index 0000000000..6c8a469acc
--- /dev/null
+++ b/panda/drivers/windows/ECUsim CLI/ECUsim CLI.cpp
@@ -0,0 +1,38 @@
+// ECUsim CLI.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include "ECUsim DLL\ECUsim.h"
+
+std::unique_ptr sim;
+
+BOOL CtrlHandler(DWORD fdwCtrlType)
+{
+ if (fdwCtrlType != CTRL_C_EVENT) return FALSE;
+
+ sim->stop();
+ sim->join();
+
+ return(TRUE);
+}
+
+int main(int argc, // Number of strings in array argv
+ char *argv[], // Array of command-line argument strings
+ char *envp[]) // Array of environment variable strings
+{
+
+ int count;
+
+ // Display each command-line argument.
+ std::cout << "\nCommand-line arguments:\n";
+ for (count = 0; count < argc; count++)
+ std::cout << " argv[" << count << "] " << argv[count] << "\n";
+
+ SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
+
+ sim.reset(new ECUsim("", 500000));
+ sim->join();
+
+ return 0;
+}
+
diff --git a/panda/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj b/panda/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj
new file mode 100644
index 0000000000..9ac10e8bc1
--- /dev/null
+++ b/panda/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj
@@ -0,0 +1,178 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}
+ Win32Proj
+ ECUsimCLI
+ 8.1
+
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ true
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ false
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ false
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+
+ Use
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Console
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib
+
+
+
+
+ Use
+ Level3
+ Disabled
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Console
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Console
+ true
+ true
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Console
+ true
+ true
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+ {96e0e646-ee76-444d-9a77-a0cd7f781deb}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters b/panda/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters
new file mode 100644
index 0000000000..ea223e30b2
--- /dev/null
+++ b/panda/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters
@@ -0,0 +1,36 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/ECUsim CLI/stdafx.cpp b/panda/drivers/windows/ECUsim CLI/stdafx.cpp
new file mode 100644
index 0000000000..d4a23c3cf7
--- /dev/null
+++ b/panda/drivers/windows/ECUsim CLI/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// ECUsim CLI.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/panda/drivers/windows/ECUsim CLI/stdafx.h b/panda/drivers/windows/ECUsim CLI/stdafx.h
new file mode 100644
index 0000000000..b005a839de
--- /dev/null
+++ b/panda/drivers/windows/ECUsim CLI/stdafx.h
@@ -0,0 +1,15 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include
+#include
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/panda/drivers/windows/ECUsim CLI/targetver.h b/panda/drivers/windows/ECUsim CLI/targetver.h
new file mode 100644
index 0000000000..87c0086de7
--- /dev/null
+++ b/panda/drivers/windows/ECUsim CLI/targetver.h
@@ -0,0 +1,8 @@
+#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
diff --git a/panda/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj b/panda/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj
new file mode 100644
index 0000000000..d50a0a2918
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj
@@ -0,0 +1,197 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}
+ Win32Proj
+ ECUsimDLL
+ 8.1
+
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ ecusim
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ true
+ ecusim
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ false
+ ecusim
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ false
+ ecusim
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+
+ Use
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Windows
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib
+
+
+
+
+ Use
+ Level3
+ Disabled
+ _DEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Windows
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Windows
+ true
+ true
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ NDEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)
+
+
+ Windows
+ true
+ true
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+ {5528aefb-638d-49af-b9d4-965154e7d531}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters b/panda/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters
new file mode 100644
index 0000000000..299d654451
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters
@@ -0,0 +1,42 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/ECUsim DLL/ECUsim.cpp b/panda/drivers/windows/ECUsim DLL/ECUsim.cpp
new file mode 100644
index 0000000000..19f2bf360e
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/ECUsim.cpp
@@ -0,0 +1,261 @@
+#include "stdafx.h"
+#include "ECUsim.h"
+
+ECUsim::ECUsim(std::string sn, unsigned long can_baud, bool ext_addr) :
+ doloop(TRUE), verbose(TRUE), can11b_enabled(TRUE), can29b_enabled(TRUE), ext_addr(ext_addr){
+ this->panda = panda::Panda::openPanda(sn);
+ this->panda->set_can_speed_cbps(panda::PANDA_CAN1, can_baud / 100); //Don't pass in baud where baud%100 != 0
+ this->panda->set_safety_mode(panda::SAFETY_ALLOUTPUT);
+ this->panda->set_can_loopback(FALSE);
+ this->panda->can_clear(panda::PANDA_CAN_RX);
+
+ DWORD threadid;
+ this->thread_can = CreateThread(NULL, 0, _canthreadBootstrap, (LPVOID)this, 0, &threadid);
+}
+
+ECUsim::~ECUsim() {
+ this->stop();
+ this->join();
+}
+
+void ECUsim::stop() {
+ this->doloop = FALSE;
+}
+
+void ECUsim::join() {
+ WaitForSingleObject(this->thread_can, INFINITE);
+}
+
+DWORD WINAPI ECUsim::_canthreadBootstrap(LPVOID This) {
+ return ((ECUsim*)This)->can_recv_thread_function();
+}
+
+DWORD ECUsim::can_recv_thread_function() {
+ while (this->doloop) {
+ auto msgs = this->panda->can_recv();
+ for (auto& msg : msgs) {
+ if (msg.is_receipt) continue;
+ if (msg.bus == 0 && !msg.is_receipt /*&& msg.len == 8*/ && msg.dat[0] >= 2) {
+ if (this->verbose) {
+ printf("Processing message (bus: %d; addr: %X; 29b: %d):\n ", msg.bus, msg.addr, msg.addr_29b);
+ for (int i = 0; i < msg.len; i++) printf("%02X ", msg.dat[i]);
+ printf("\n");
+ }
+ this->_CAN_process_msg(msg);
+ } else {
+ if (this->verbose) {
+ printf("Rejecting message (bus: %d; addr: %X; 29b: %d):\n ", msg.bus, msg.addr, msg.addr_29b);
+ for (int i = 0; i < msg.len; i++) printf("%02X ", msg.dat[i]);
+ printf("\n");
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+BOOL ECUsim::_can_addr_matches(panda::PANDA_CAN_MSG& msg) {
+ if (this->can11b_enabled && !msg.addr_29b && (msg.addr == 0x7DF || (msg.addr & 0x7F8) == 0x7E0)) {
+ if (!this->ext_addr) {
+ return TRUE;
+ } else {
+ return msg.len >= 1 && msg.dat[0] == 0x13;//13 is an arbitrary address picked to test ext addresses
+ }
+ }
+ if (this->can29b_enabled && msg.addr_29b && ((msg.addr & 0x1FFF00FF) == 0x18DB00F1 || (msg.addr & 0x1FFF00FF) == 0x18da00f1)) {
+ if (!this->ext_addr) {
+ return TRUE;
+ } else {
+ return msg.len >= 1 && msg.dat[0] == 0x13;//13 is an arbitrary address picked to test ext addresses
+ }
+ }
+ return FALSE;
+}
+
+void ECUsim::_CAN_process_msg(panda::PANDA_CAN_MSG& msg) {
+ std::string outmsg;
+ uint32_t outaddr;
+ uint8_t formatted_msg_buff[8];
+ bool doreply = FALSE;
+
+ if (this->_can_addr_matches(msg)) {// && msg.len == 8) {
+ uint8_t *dat = (this->ext_addr) ? &msg.dat[1] : &msg.dat[0];
+ if ((dat[0] & 0xF0) == 0x10) {
+ printf("Got a multiframe write request\n");
+ outaddr = (msg.addr_29b) ? 0x18DAF1EF : 0x7E8;
+ this->panda->can_send(outaddr, msg.addr_29b, (const uint8_t*)"\x30\x00\x00", 3, panda::PANDA_CAN1);
+ return;
+ }
+
+ /////////// Check if Flow Control Msg
+ if ((dat[0] & 0xF0) == 0x30 && msg.len >= 3 && this->can_multipart_data.size() > 0) {
+ if (this->verbose) printf("More data requested\n");
+ uint8_t block_size = dat[1], sep_time_min = dat[2];
+ outaddr = (msg.addr == 0x7DF || msg.addr == 0x7E0) ? 0x7E8 : 0x18DAF1EF; //ext addr 5th byte is just always 0x13 for simplicity
+
+ unsigned int msgnum = 1;
+ while (this->can_multipart_data.size()) {
+ unsigned int datalen = this->ext_addr ?
+ min(6, this->can_multipart_data.size()): //EXT ADDR VALUE
+ min(7, this->can_multipart_data.size()); //NORMAL ADDR VALUE
+
+ unsigned int idx = 0;
+ if (this->ext_addr)
+ formatted_msg_buff[idx++] = 0x13; //EXT ADDR
+ formatted_msg_buff[idx++] = 0x20 | msgnum;
+ for (int i = 0; i < datalen; i++) {
+ formatted_msg_buff[i + idx] = this->can_multipart_data.front();
+ this->can_multipart_data.pop();
+ }
+ for (int i = datalen + idx; i < sizeof(formatted_msg_buff); i++)
+ formatted_msg_buff[i] = 0;
+
+ if (this->verbose) {
+ printf("Multipart reply to %X.\n ", outaddr);
+ for (int i = 0; i < datalen + idx; i++) printf("%02X ", formatted_msg_buff[i]);
+ printf("\n");
+ }
+
+ this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, datalen + idx, panda::PANDA_CAN1);
+ msgnum = (msgnum + 1) % 0x10;
+ Sleep(10);
+ }
+ return;
+ }
+
+ /////////// Normal message in
+ outmsg = this->process_obd_msg(dat[1], dat[2], doreply);
+ if (doreply) {
+ outaddr = (msg.addr_29b) ? 0x18DAF1EF : 0x7E8;
+
+ if (outmsg.size() <= (this->ext_addr ? 4 : 5)) {
+ unsigned int idx = 0;
+ if(this->ext_addr)
+ formatted_msg_buff[idx++] = 0x13; //EXT ADDR
+ formatted_msg_buff[idx++] = outmsg.size() + 2;
+ formatted_msg_buff[idx++] = 0x40 | dat[1];
+ formatted_msg_buff[idx++] = dat[2]; //PID
+ memcpy_s(&formatted_msg_buff[idx], sizeof(formatted_msg_buff) - idx, outmsg.c_str(), outmsg.size());
+ for (int i = idx + outmsg.size(); i < 8; i++)
+ formatted_msg_buff[i] = 0;
+
+ if (this->verbose) {
+ printf("Replying to %X.\n ", outaddr);
+ for (int i = 0; i < 8; i++) printf("%02X ", formatted_msg_buff[i]);
+ printf("\n");
+ }
+
+ this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, 8, panda::PANDA_CAN1); //outmsg.size() + 3
+ } else {
+ uint8_t first_msg_len = this->ext_addr ?
+ min(2, outmsg.size() % 7) : //EXT ADDR VALUES
+ min(3, outmsg.size() % 7); //NORMAL ADDR VALUES
+ uint8_t payload_len = outmsg.size() + 3;
+
+ unsigned int idx = 0;
+ if (this->ext_addr)
+ formatted_msg_buff[idx++] = 0x13; //EXT ADDR
+ formatted_msg_buff[idx++] = 0x10 | ((payload_len >> 8) & 0xF);
+ formatted_msg_buff[idx++] = payload_len & 0xFF;
+ formatted_msg_buff[idx++] = 0x40 | dat[1];
+ formatted_msg_buff[idx++] = dat[2]; //PID
+ formatted_msg_buff[idx++] = 1;
+ memcpy_s(&formatted_msg_buff[idx], sizeof(formatted_msg_buff) - idx, outmsg.c_str(), first_msg_len);
+
+ if (this->verbose) {
+ printf("Replying FIRST FRAME to %X.\n ", outaddr);
+ for (int i = 0; i < 8; i++) printf("%02X ", formatted_msg_buff[i]);
+ printf("\n");
+ }
+
+ this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, 8, panda::PANDA_CAN1);
+ for (int i = first_msg_len; i < outmsg.size(); i++)
+ this->can_multipart_data.push(outmsg[i]);
+ }
+ }
+ }
+}
+
+std::string ECUsim::process_obd_msg(UCHAR mode, UCHAR pid, bool& return_data) {
+ std::string tmp;
+ return_data = TRUE;
+
+ switch (mode) {
+ case 0x01: // Mode : Show current data
+ switch (pid) {
+ case 0x00: //List supported things
+ return "\xff\xff\xff\xfe"; //b"\xBE\x1F\xB8\x10" #Bitfield, random features
+ case 0x01: // Monitor Status since DTC cleared
+ return std::string("\x00\x00\x00\x00", 4); //Bitfield, random features
+ case 0x04: // Calculated engine load
+ return "\x2f";
+ case 0x05: // Engine coolant temperature
+ return "\x3c";
+ case 0x0B: // Intake manifold absolute pressure
+ return "\x90";
+ case 0x0C: // Engine RPM
+ return "\x1A\xF8";
+ case 0x0D: // Vehicle Speed
+ return "\x53";
+ case 0x10: // MAF air flow rate
+ return "\x01\xA0";
+ case 0x11: // Throttle Position
+ return "\x90";
+ case 0x33: // Absolute Barometric Pressure
+ return "\x90";
+ default:
+ return_data = FALSE;
+ return "";
+ }
+ case 0x09: // Mode : Request vehicle information
+ switch (pid) {
+ case 0x02: // Show VIN
+ return "1D4GP00R55B123456";
+ case 0xFC: // test long multi message.Ligned up for LIN responses
+ for (int i = 0; i < 80; i++) {
+ tmp += "\xAA\xAA";
+ }
+ return tmp;//">BBH", 0xAA, 0xAA, num + 1)
+ case 0xFD: // test long multi message
+ for (int i = 0; i < 80; i++) {
+ tmp += "\xAA\xAA\xAA";
+ tmp.push_back(i >> 24);
+ tmp.push_back((i >> 16) & 0xFF);
+ tmp.push_back((i >> 8) & 0xFF);
+ tmp.push_back(i & 0xFF);
+ }
+ return "\xAA\xAA\xAA" + tmp;
+ case 0xFE: // test very long multi message
+ tmp = "\xAA\xAA\xAA";
+ for (int i = 0; i < 584; i++) {
+ tmp += "\xAA\xAA\xAA";
+ tmp.push_back(i >> 24);
+ tmp.push_back((i >> 16) & 0xFF);
+ tmp.push_back((i >> 8) & 0xFF);
+ tmp.push_back(i & 0xFF);
+ }
+ return tmp + "\xAA";
+ case 0xFF:
+ for (int i = 0; i < 584; i++) {
+ tmp += "\xAA\xAA\xAA\xAA\xAA";
+ tmp.push_back(((i + 1) >> 8) & 0xFF);
+ tmp.push_back((i + 1) & 0xFF);
+ }
+ return std::string("\xAA\x00\x00", 3) + tmp;
+ default:
+ return_data = FALSE;
+ return "";
+ }
+ case 0x3E:
+ if (pid == 0) {
+ return_data = TRUE;
+ return "";
+ }
+ return_data = FALSE;
+ return "";
+ default:
+ return_data = FALSE;
+ return "";
+ }
+}
diff --git a/panda/drivers/windows/ECUsim DLL/ECUsim.h b/panda/drivers/windows/ECUsim DLL/ECUsim.h
new file mode 100644
index 0000000000..7378e2c581
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/ECUsim.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include
+#include "panda\panda.h"
+#include
+
+// 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 ECUSIMDLL_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
+// ECUSIMDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
+// defined with this macro as being exported.
+#ifdef ECUSIMDLL_EXPORTS
+#define ECUSIMDLL_API __declspec(dllexport)
+#else
+#define ECUSIMDLL_API __declspec(dllimport)
+#endif
+
+// This class is exported from the ECUsim DLL.dll
+class ECUSIMDLL_API ECUsim {
+public:
+ ECUsim(std::string sn, unsigned long can_baud, bool ext_addr = FALSE);
+ ECUsim(panda::Panda && p, unsigned long can_baud, bool ext_addr = FALSE);
+ ~ECUsim();
+
+ void stop();
+ void join();
+
+ // Flag determines if verbose output is enabled
+ volatile bool verbose;
+ BOOL ext_addr;
+private:
+ std::unique_ptr panda;
+
+ static DWORD WINAPI _canthreadBootstrap(LPVOID This);
+ DWORD can_recv_thread_function();
+
+ BOOL _can_addr_matches(panda::PANDA_CAN_MSG & msg);
+
+ void _CAN_process_msg(panda::PANDA_CAN_MSG & msg);
+
+ std::string process_obd_msg(UCHAR mode, UCHAR pid, bool& return_data);
+
+ HANDLE thread_can;
+ volatile bool doloop;
+ std::queue can_multipart_data;
+
+ BOOL can11b_enabled;
+ BOOL can29b_enabled;
+};
diff --git a/panda/drivers/windows/ECUsim DLL/dllmain.cpp b/panda/drivers/windows/ECUsim DLL/dllmain.cpp
new file mode 100644
index 0000000000..69b58914b3
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/dllmain.cpp
@@ -0,0 +1,19 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "stdafx.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/panda/drivers/windows/ECUsim DLL/stdafx.cpp b/panda/drivers/windows/ECUsim DLL/stdafx.cpp
new file mode 100644
index 0000000000..b4056ecd06
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// ECUsim DLL.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/panda/drivers/windows/ECUsim DLL/stdafx.h b/panda/drivers/windows/ECUsim DLL/stdafx.h
new file mode 100644
index 0000000000..f3a07375c7
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/stdafx.h
@@ -0,0 +1,16 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/panda/drivers/windows/ECUsim DLL/targetver.h b/panda/drivers/windows/ECUsim DLL/targetver.h
new file mode 100644
index 0000000000..87c0086de7
--- /dev/null
+++ b/panda/drivers/windows/ECUsim DLL/targetver.h
@@ -0,0 +1,8 @@
+#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
diff --git a/panda/drivers/windows/README.md b/panda/drivers/windows/README.md
new file mode 100644
index 0000000000..7219abda24
--- /dev/null
+++ b/panda/drivers/windows/README.md
@@ -0,0 +1,177 @@
+```
+ ;" ^; ;' ",
+______/\\\\\\\\\\\____/\\\\\\\\\_______/\\\\\\\\\\\\\\\______/\\\\\\\\\\_____________/\\\____ ; s$$$$$$$s ;
+ _____\/////\\\///___/\\\///////\\\____\/\\\///////////_____/\\\///////\\\__________/\\\\\____ , ss$$$$$$$$$$s ,'
+ _________\/\\\_____\///______\//\\\___\/\\\_______________\///______/\\\_________/\\\/\\\____ ;s$$$$$$$$$$$$$$$
+ _________\/\\\_______________/\\\/____\/\\\\\\\\\\\\_____________/\\\//________/\\\/\/\\\____ $$$$$$$$$$$$$$$$$$
+ _________\/\\\____________/\\\//______\////////////\\\__________\////\\\_____/\\\/__\/\\\____ $$$$P""Y$$$Y""W$$$$$
+ _________\/\\\_________/\\\//____________________\//\\\____________\//\\\__/\\\\\\\\\\\\\\\\_ $$$$ p"$$$"q $$$$$
+ __/\\\___\/\\\_______/\\\/____________/\\\________\/\\\___/\\\______/\\\__\///////////\\\//__ $$$$ .$$$$$. $$$$
+ _\//\\\\\\\\\_______/\\\\\\\\\\\\\\\_\//\\\\\\\\\\\\\/___\///\\\\\\\\\/_____________\/\\\____ _ $$$$$$$$$$$$$$$$
+ __\/////////_______\///////////////___\/////////////_______\/////////_______________\///_____| | "Y$$$"*"$$$Y"
+ _ __ __ _ _ __ __| | __ _"$b.$$"
+ | '_ \ / _` | '_ \ / _` |/ _` |
+ | |_) | (_| | | | | (_| | (_| |
+ | .__/ \__,_|_| |_|\__,_|\__,_|
+ | | A comma.ai product.
+ |_| (Code by Jessy Diamond Exum)
+```
+
+
+# What is J2534?
+
+J2534 is an API that tries to provide a consistent way to send/receive
+messages over the many different protocols supported by the OBD II
+port. The place this is perhaps most obvious, is sending data over
+different protocols (each using unique packetizing methods) using the
+same data format.
+
+For each PassThru Device that should be used with J2534 (in this case,
+the panda), a 'driver' has to be written that can be loaded by a
+client application wanting to send/receive data.
+
+A lot of J2534 has good ideas behind it, but the standard has some odd choices:
+
+* Platform Locked: Requires using the Windows Registry to find installed J2534 libraries/drivers. Drivers have to be DLLs.
+* Architecture Locked: So far there is only support for x86.
+* No device autodetect, and poor support for selecting from multiple devices.
+* Constant vague language about important behavior (small differences between vendors).
+* Most common differences become standard in later revisions.
+
+# Why use J2534 with the panda?
+
+J2534 is the only interface supported by most professional grade
+vehicle diagnostics systems (such as HDS). These tools are useful for
+diagnosing vehicles, as well as reverse engineering some lesser known
+features.
+
+# What parts are supported with panda?
+
+- [ ] **J1850VPW** *(Outdated, and not physically supported by the panda)*
+- [ ] **J1850PWM** *(Outdated, and not physically supported by the panda)*
+- [X] **CAN**
+- [X] **ISO15765**
+- [ ] **ISO9141** *(This protocol could be implemented if 5 BAUD init support is added to the panda.)*
+- [ ] **ISO14230/KWP2000** *(Could be supported with FAST init, 5baud init if panda adds support for 5bps serial)*
+
+# Building the Project:
+
+This project was developed with Visual Studio 2015, the Windows SDK,
+and the Windows Driver Kit (WDK). At the time of writing, there is not
+a stable WDK for Visual Studio 2017, but this project should build
+with the new WDK and Visual Studio when it is available.
+
+The WDK is only required for creating the signed WinUSB inf file. The
+WDK may also provide the headers for WinUSB.
+
+To build all the projects required for the installer, in Visual
+Studio, select **Build->Batch Build.** In the project list select:
+
+- **"panda"** *Release|x86*
+- **"panda"** *Release|x64*
+- **"panda Driver Package"** Debug|x86 (Note this inf file works with x86/amd64).
+- **"pandaJ2534DLL"** *Release|x86*
+
+The installer is generated with [NullSoft NSIS](http://nsis.sourceforge.net/Main_Page).
+Use NSIS to run panda_install.nsi after building all the required projects.
+
+Before generating the installer, you must go to copy vscruntimeinfo.nsh.sample to
+vscruntimeinfo.nsh and follow the instructions to bundle in the Visual Studio C
+Runtime required by your version of Visual Studio. Without this runtime, the panda
+code will not work, so without this file, the installer will refuse to build.
+
+# Installing:
+
+Either build the software yourself by following the steps in the
+'Developing' section, or get the panda_installer.exe file and run
+it. The wizard should correctly set up the drivers.
+
+Since this driver is still in development, there are some issues
+that may occur. If after you install the driver and then plug in your
+panda (unplug it first if it was already plugged in), Windows says
+the driver is missing, refer to the section below 'Dealing with self
+signed drivers.'
+
+# Using J2534:
+
+After installing the J2534 drivers for the panda, you can do... nothing.
+You first need to get a J2534 client that can load the drivers and talk to
+the panda for you.
+
+A simple tool for testing J2534 drivers is DrewTech's 'J2534-1 Bus Analysis
+Tool' available in the 'Other Support Applications' section of their
+[Download Page](http://www.drewtech.com/downloads/).
+
+# Dealing with self signed drivers:
+
+Installation would be straightforward were it not for the USB Driver
+that needs to be setup. The driver itself is only a WinUSB inf file
+(no actual driver), but it still needs to be signed.
+
+Driver signing is a requirement of Windows starting in 8 (64 bit
+versions only for some reason). If your Windows refuses to install
+the driver, there are three choices:
+
+- Self Sign the Driver.
+- Disable Driver Signature Verification
+- Purchase a certificate signed by a trusted authority.
+
+Since self signed certificates have no chain of trust to a known
+certificate authority, if you self sign, you will have to add your
+cert to the root certificate store of your Windows' installation. This
+is dangerous because it means anything signed with your cert will be
+trusted. If you make your own cert, add a password so someone can't
+copy it and screw with your computer's trust.
+
+Disabling Signature Verification allows you to temporarily install
+drivers without a trusted signature. Once you reboot, new drivers will
+need to be verified again, but any installed drivers will stay where
+they are. This option is irritating if you are installing and
+uninstalling the inf driver multiple times, but overall, is safer than
+the custom root certificate described above.
+
+Purchasing a signed certificate is the best long term option, but it
+is not useful for open source contributors, since the certificate will
+be kept safe by the comma.ai team. Developers should use one of the
+other two options.
+
+**Note that certificate issues apply no matter if you are building
+ from source or running an insaller .exe file.**
+
+Some people have reported that the driver installs without needing to
+disable driver signing, or that visual studio correctly sets up a
+temporary signing key for them. I call witchcraft because I have not
+had that happen to me. The signed certificate is still the correct
+thing to do in the end.
+
+Windows 7 will not force driver signing. This software is not tested
+on anything before 7.
+
+# Developing:
+
+- Edit and merge pandaJ2534DLL\J2534register_x64.reg to register your development J2534 DLL.
+- Add your output directory (panda\drivers\windows\Debug_x86) to your system PATH to avoid insanity.
+
+# ToDo Items:
+
+- Get official signing key for WinUSB driver inf file.
+- Implement TxClear and RxClear. (Requires reading vague specifications).
+- Apply a style-guide and consistent naming convention for Classes/Functions/Variables.
+- Send multiple messages (each with a different address) from a given connection at the same time.
+- Implement ISO14230/KWP2000 FAST (LIN communication is already supported with the raw panda USB driver).
+- Find more documentation about SW_CAN_PS (Single Wire CAN, aka GMLAN).
+- Find example of client using a _PS version of a protocol (PS is pin select, and may support using different CAN buses).
+
+
+# Known Issues:
+
+- ISO15765 Multi-frame TX: Hardware delays make transmission overshoot
+ STMIN by several milliseconds. This does not violate the requirements
+ of STMIN, it just means it is a little slower than it could be.
+
+- All Tx messages from a single Connection are serialized. This can be
+ relaxed to allow serialization of messages based on their address
+ (making multiple queues, effectively one queue per address).
+
+# Other:
+Panda head ASCII art by dcau
\ No newline at end of file
diff --git a/panda/drivers/windows/docs/Message_Size.png b/panda/drivers/windows/docs/Message_Size.png
new file mode 100644
index 0000000000..3a20a2ef39
Binary files /dev/null and b/panda/drivers/windows/docs/Message_Size.png differ
diff --git a/panda/drivers/windows/docs/RxBits_defs.jpg b/panda/drivers/windows/docs/RxBits_defs.jpg
new file mode 100644
index 0000000000..db01f62c95
Binary files /dev/null and b/panda/drivers/windows/docs/RxBits_defs.jpg differ
diff --git a/panda/drivers/windows/docs/RxBits_valid.png b/panda/drivers/windows/docs/RxBits_valid.png
new file mode 100644
index 0000000000..6f7c4c326d
Binary files /dev/null and b/panda/drivers/windows/docs/RxBits_valid.png differ
diff --git a/panda/drivers/windows/docs/bus_init_signla.png b/panda/drivers/windows/docs/bus_init_signla.png
new file mode 100644
index 0000000000..9aa71faa27
Binary files /dev/null and b/panda/drivers/windows/docs/bus_init_signla.png differ
diff --git a/panda/drivers/windows/docs/connection_flags.png b/panda/drivers/windows/docs/connection_flags.png
new file mode 100644
index 0000000000..dd578a2cb9
Binary files /dev/null and b/panda/drivers/windows/docs/connection_flags.png differ
diff --git a/panda/drivers/windows/docs/iso15765_ioctls.png b/panda/drivers/windows/docs/iso15765_ioctls.png
new file mode 100644
index 0000000000..7de4a118e0
Binary files /dev/null and b/panda/drivers/windows/docs/iso15765_ioctls.png differ
diff --git a/panda/drivers/windows/docs/message_send.png b/panda/drivers/windows/docs/message_send.png
new file mode 100644
index 0000000000..112aab14cb
Binary files /dev/null and b/panda/drivers/windows/docs/message_send.png differ
diff --git a/panda/drivers/windows/docs/msg_filter_passfail.png b/panda/drivers/windows/docs/msg_filter_passfail.png
new file mode 100644
index 0000000000..4a91facb45
Binary files /dev/null and b/panda/drivers/windows/docs/msg_filter_passfail.png differ
diff --git a/panda/drivers/windows/docs/other notes.txt b/panda/drivers/windows/docs/other notes.txt
new file mode 100644
index 0000000000..a066d26465
--- /dev/null
+++ b/panda/drivers/windows/docs/other notes.txt
@@ -0,0 +1,347 @@
+When using the ISO 15765-4 protocol, only SingleFrame messages can be transmitted without a matching
+flow control filter. Also, PCI bytes are transparently added by the API. See PassThruStartMsgFilter
+and Appendix A for a discussion of flow control filters.
+
+
+
+PassThruReadMsgs
+This function reads messages and indications from the receive buffer. All messages and indications shall
+be read in the order that they occurred on the bus. If a transmit message generated a loopback message
+and TxDone indication, the TxDone indication shall always be queued first. Except for loopback messages
+and indications, no messages shall be queued for reception without matching a PASS_FILTER
+(for non-ISO 15765) or FLOW_CONTROL filter (for ISO 15765). On ISO 15765, PCI bytes are transparently
+removed by the API. If the function is successful, a value of STATUS_NOERROR is returned.
+
+
+PassThruWriteMsgs
+Write timeout (in milliseconds). When a value of 0 is specified, the function queues as
+many of the specified messages as possible and returns immediately. When a value
+greater than 0 is specified, the function will block until the Timeout has expired, an error
+has occurred, or the desired number of messages have been transmitted on the vehicle
+network. Even if the device can buffer only one packet at a time, this function shall be
+able to send an arbitrary number of packets if a Timeout value is supplied. Since the
+function returns early if all the messages have been sent, there is normally no penalty for
+having a large timeout (several seconds). If the number of messages requested have
+been written, the function shall not return ERR_TIMEOUT, even if the timeout value is
+zero.
+
+When an ERR_TIMEOUT is returned, only the number of messages that were sent on
+the vehicle network is known. The number of messages queued is unknown. Application
+writers should avoid this ambiguity by using a Timeout value large enough to work on
+slow devices and networks with arbitration delays.
+
+
+
+PassThruStartPeriodicMsg
+This function will immediately queue the specified message for transmission, and repeat at the specified
+interval. Periodic messages are limited in length to a single frame message of 12 bytes or less, including
+header or CAN ID. Periodic messages shall have priority over messages queued with
+PassThruWriteMsgs, but periodic messages must not violate bus idle timing parameters (e.g. P3_MIN).
+Periodic messages shall generate TxDone indications (ISO 15765) and loopback messages (on any
+protocol, if enabled). On ISO 15765, periodic messages can be sent during a multi-frame transmission or
+reception. If the function is successful, a value of STATUS_NOERROR is returned. The Pass-Thru
+device must support a minimum of ten periodic messages.
+
+PassThruDisconnect shall delete all periodic messages on that channel. PassThruClose shall delete all
+periodic messages on all channels for the device. All periodic messages will be stopped on a
+PassThruDisconnect for the associated protocol or a PassThruClose for the device.
+
+
+
+PASSTHRUSTARTMSGFILTER
+This function starts filtering of incoming messages. If the function is successful, a value of
+STATUS_NOERROR is returned. A minimum of ten message filters shall be supported by the interface
+for each supported protocol. PassThruDisconnect shall delete all message filters on that channel.
+
+PassThruClose shall delete all filters on all channels for the device. Pattern and Mask messages shall
+follow the protocol formats specified in Section 8. However, only the first twelve (12) bytes, including
+header or CAN ID, are used by the filter. ERR_INVALID_MSG shall be returned if the filter length
+exceeds 12. Note that this function does not clear any messages that may have been received and
+queued before the filter was set. Users are cautioned to consider performing a CLEAR_RX_BUFFER
+after starting a message filter to be sure that unwanted frames are purged from any receive buffers.
+
+
+
+
+
+
+
+
+FILTER RELATED STUFF
+For all protocols except ISO 15765:
+• PASS_FILTERs and BLOCK_FILTERs will be applied to all received messages. They shall not be
+applied to indications or loopback messages
+
+• FLOW_CONTROL_FILTERs must not be used and shall cause the interface to return
+ERR_INVALID_FILTER_ID
+
+• Both pMaskMsg and pPatternMsg must have the same DataSize and TxFlags. Otherwise, the
+interface shall return ERR_INVALID_MSG
+
+• The default filter behavior after PassThruConnect is to block all messages, which means no messages
+will be placed in the receive queue until a PASS_FILTER has been set. Messages that match a
+PASS_FILTER can still be blocked by a BLOCK_FILTER
+
+• Figure 16 and Figure 17 show how the message filtering mechanism operates
+
+For ISO 15765:
+• PASS_FILTERs and BLOCK_FILTERs must not be used and shall cause the interface to return
+ERR_INVALID_FILTER_ID
+
+• Filters shall not be applied to indications or loopback messages. When loopback is on, the original
+message shall be copied to the receive queue upon the last segment being transmitted on the bus.
+
+• Non-segmented messages do not need to match a FLOW_CONTROL_FILTER.
+
+• No segmented messages can be transmitted without matching an appropriate FLOW_CONTROL_FILTER.
+An appropriate filter is one in which the pFlowControlMsg CAN ID matches the messages to be
+transmitted. Also, the ISO 15765_ADDR_TYPE (reference TxFlags in Section 8.7.3) bits must match.
+If that bit is set, the first byte after the CAN IDs (the extended address)
+must match too.
+
+• No message (segmented or unsegmented) shall be received without matching an appropriate
+FLOW_CONTROL_FILTER. An appropriate filter is one in which the pPatternMsg CAN ID matches
+the incoming message ID. If the ISO 15765_ADDR_TYPE (reference TxFlags in Section 8.7.3) bit is
+set in the filter, the first byte after the CAN IDs (the extended address) must match too.
+
+• All 3 message pointers must have the same DataSize and TxFlags. Otherwise, the interface shall
+return ERR_INVALID_MSG.
+
+• Both the pFlowControlMsg ID and the pPatternMsg ID must be unique (not match any IDs in any other
+filters). The only exception is that pPatternMsg can equal pFlowControlMsg to allow for receiving
+functionally addressed messages. In this case, only non-segmented messages can be received.
+
+• See Appendix A for a detailed description of flow control filter usage.
+
+
+
+
+8.4 Format Checks for Messages Passed to the API
+The vendor DLL shall validate all PASSTHRU_MSG structures, and return an ERR_INVALID_MSG in the following cases:
+• DataSize violates Min Tx or Max Tx columns in Figure 42
+
+• Source address (Data[3]) is different from the Node ID (Ioctl SET_CONFIG, Parameter NODE_ADDRESS) on J1850PWM
+
+• The header length field is incorrect for the number of bytes in the message on ISO14230
+
+• The CAN_29_BIT flag of the message does not match the CAN_29_BIT flag passed to
+PassThruConnect, unless the CAN_ID_BOTH bit was set on connect
+
+The vendor DLL shall return ERR_MSG_PROTOCOL_ID when the ProtocolID field in the message does
+not match the Protocol ID specified when opening the channel.
+
+
+
+8.5 Conventions for Returning Messages from the API
+When returning a message in PassThruReadMsg:
+– DataSize shall tell the application how many bytes in the Data array are valid. ExtraDataIndex will be
+the (non-zero) index of the last byte of the message. If ExtraDataIndex is not equal to DataSize there
+are extra data bytes after the message. If loopback is on, RxStatus must be consulted to tell if the
+message came via loopback.
+
+– DataSize will be in the range shown in the Min Rx and Max Rx columns of Figure 42. If the device
+receives a message from the vehicle bus that is too long or too short, the message shall be discarded
+with no error.
+
+– For received messages, ExtraDataIndex shall be equal to DataSize, except when the interface is
+returning SAE J1850 PWM IFR bytes. In no case shall ExtraDataIndex be larger than DataSize.
+
+– When receiving a message on an SAE J1850 PWM channel, the message shall have any IFR bytes
+appended. In this case, ExtraDataIndex shall be the index of the first IFR byte, and DataSize shall be
+the total length of the original message plus all IFR bytes. For example, if there are two IFR bytes,
+DataSize will be incremented by two, and ExtraDataIndex will be DataSize - 2. When loopback is on,
+the loopback message shall contain any IFR bytes.
+
+
+
+8.6 Conventions for Retuning Indications from the API
+When returning an indication in PassThruReadMsg:
+– ExtraDataIndex must be zero
+
+– DataSize shall tell the application how many bytes in the Data array are valid
+
+– RxStatus must be consulted to determine the indication type (See Section 8.4).
+
+– A TxDone indication (ISO 15765 only) is generated by the DLL after a SingleFrame message is sent,
+or the last frame of a multi-segment transmission is sent. DataSize shall be 4 (or 5 when the message
+was using Extended Addressing). Data shall contain the CAN ID (and possible Extended Address) of
+the message just sent. If loopback is on, the TxDone indication shall precede the loopback message in
+the receive queue.
+
+– An RxBreak indication (SAE J2610/SCI and SAE J1850VPW only) is generated by the DLL if a break
+is received.
+
+– An RxStart indication is generated by the DLL when starting to receive a message on ISO9141 or
+ISO14230, or when receiving the FirstFrame signal of a multi-segment ISO 15765 message.
+
+
+
+9.1 Naming of Files
+Each vendor will provide a different name implementation of the API DLL and a number of these
+implementations could simultaneously reside on the same PC. No vendor shall name its implementation
+“J2534.DLL”. All implementations shall have the string “32” suffixed to end of the name of the API DLL to
+indicate 32-bit. For example, if the company name is “Vendor X” the name could be VENDRX32.DLL.
+
+For simplicity, an API DLL shall be named in accordance with the file allocation table (FAT) file system
+naming convention (which allows up to eight characters for the file name and three characters for the
+extension with no spaces anywhere). Note that, given this criteria, the major name of an API DLL can be
+no greater than six characters. The OEM application can determine the name of the appropriate vendor’s
+DLL using the Win32 Registry mechanism described in this section.
+
+
+
+
+A.1 Flow Control Overview
+ISO 15765-2 was designed to send blocks of up to 4095 bytes on top of the limited 8-byte payload of raw
+CAN frames. If the data is small enough, it can fit in a single frame and be transmitted like a raw CAN
+message with additional headers. Otherwise, the block is broken up into segments and becomes a
+segmented transmission, generating CAN frames in both directions. For flexibility, the receiver of the
+segments can control the rate at which the segments are sent.
+
+Each transmission is actually part of a conversation between two nodes. There is no discovery
+mechanism for conversation partners. Therefore, each desired conversation must be pre-defined on each
+side before the conversation can start. Conversations are symmetric, meaning that either side can send a
+block of data to the other. A conversation can only have one transfer (in one direction) in progress at a
+time. One transfer must complete before the next transfer (in the same or in a different direction) can
+start. The device must support multiple transfers at once, as long as each one is part of a different
+conversation. Raw CAN frames are not allowed when using ISO15765-2.
+
+A key feature of a conversation is that each side has a unique CAN ID, and each side uses their unique
+CAN ID for all transmissions during the conversation. No other CAN IDs are part of the conversation.
+Even though the useful data is only flowing in one direction, both sides are transmitting. One side is
+sending the flow control message to pace the segments of data coming from the other side.
+
+For example, during OBD communication, a pass-thru device and an ECU might have a conversation.
+The pass-thru device will use the "Tester1" physical CAN ID ($241), and the first ECU will use the
+"ECU1" physical CAN ID ($641). During a multi-segment transfer, both sides will be transmitting using
+only their respective IDs. It does not matter if the data is being sent by the ECU or by the Tester, the IDs
+remain the same.
+
+It is important to understand the difference between OBD Requests/Responses and ISO 15765-2
+transfers. The OBD Request is transmitted from the Tester to the ECU using functional addressing.
+Because segmented transfer is not possible on functional addresses, the message must fit in a single
+frame. The OBD Response is a message from the ECU to the Tester using physical addressing. Unlike
+other protocols, the responses are not sequential. In fact, the responses can overlap, as if each ECU
+were having a private conversation with the Tester. Some of the responses may fit in a single frame,
+while others will require a segmented transfer from the ECU to the tester.
+
+
+A.2 Transmitting a Segmented Message
+When PassThruWrite is called, the API will search the list of flow control filters, looking for a
+pFlowControlMsg that matches the CAN ID (and possible extended address) of the message being sent.
+Upon matching a filter, the pass-thru device will:
+
+• Start the ISO 15765 transfer by sending a FirstFrame on the bus. The CAN ID of this segment was
+specified in both the message and the matching pFlowControlMsg. In our example, this is $241.
+
+• Wait for a FlowControl frame from the conversation partner. The CAN ID to look for is specified in the
+corresponding pPatternMsg. In our example, this is $641.
+
+• Transmit the message data in ConsecutiveFrames according to the FlowControl frame’s instructions
+for BS (BlockSize) and STmin (SeparationTime minimum). Again, the pass-thru device transmits using
+CAN ID specified in pFlowControlMsg. In our example, this is $241.
+
+• Repeat the previous two steps as required.
+
+• When finished, the pass-thru device will place a TxDone indication in the API receive queue. The data
+will contain the CAN ID specified in pFlowControlMsg. In our example, this is $241.
+
+• If loopback is on, the entire message sent will appear in the API receive queue with the
+TX_MSG_TYPE bit set to 1 in RxStatus. The loopback shall not precede the TxDone indication.
+
+Before any multi-segment transfer can take place, the conversation must be set up on both sides. It’s
+assumed that the ECU is already setup. The application is responsible for setting up the pass-thru device.
+This setup must be done once (and only once) per conversation. The setup involves a single call to
+PassThruStartMsgFilter, with the following parameters:
+
+A.2.2 Data Transmission
+Once the conversation is set up, any number of messages (to the conversation partner) can be
+transmitted using PassThruWriteMsg. The interface shall handle all aspects of the transfer, including
+pacing (slowing) the transmission to the requirements of the receiver.
+
+When there are multiple conversations setup, the pass-thru device will search all of the flow control filters
+for a matching pFlowControlMsg. If there is no match, the message cannot be sent because the pass-
+thru device doesn’t know which partner will be pacing the conversation.
+
+When doing blocking writes, it is important to pick a timeout long enough to cover entire transfer, even if
+the ECU is pacing things slowly. Otherwise PassThruWriteMsg will return with a timeout, even though the
+transmission is proceeding normally.
+
+
+A.3 Transmitting an Unsegmented Message
+As a special case, transfers that fit in a single frame can be transmitted without setting up a conversation.
+This is useful during an OBD Request, which is a functionally addressed message that is broadcast to all
+ECUs. This message must be small enough to fit into a single frame (including headers) because it is not
+possible to do one segmented transfer to multiple ECUs.
+
+When using functional addressing for an OBD Request, it is important to remember that there can be no
+direct reply. Instead, each ECU will send their OBD Response using physical addressing to their
+conversation partner (e.g. ECU1 to Tester1, ECU2 to Tester2) as defined by ISO 15765-4. The OBD
+Response may be a segmented transfer, or it may be a single frame.
+
+In this case, no conversation setup is necessary. The call to PassThruWriteMsg is the same as above,
+except that the DataSize must be 7 bytes or less (6 bytes or less if extended addressing is turned on).
+The pass-thru device will automatically insert a PCI byte before transmission.
+
+
+A.4 Receiving a Segmented Message
+Message reception is asynchronous to the application. When a FirstFrame is seen on the bus, the pass-
+thru device will search the list of flow control filters, looking for a pPatternMsg message with the same
+CAN ID (and possible extended address) as the FirstFrame. Upon matching a filter, the pass-thru device will:
+
+• Place an RxStart indication in the API receive queue. This indication has the START_OF_MESSAGE
+bit set in RxFlags. The message data will contain the CAN ID of the sender. In our example, this is
+$641. DataSize will be 4 bytes (5 with extended addressing), and ExtraDataIndex will be zero.
+
+• Send a FlowControl frame to the conversation partner. The FlowStatus field shall be set to
+ontinueToSend. The CAN ID of this segment comes from the filter’s corresponding
+pFlowControlMsg. In our example, this CAN ID is $241. The BS (BlockSize) and STmin
+(SeparationTime minimum) parameters default to zero, but can be changed with the SET_CONFIGIoctl.
+
+• Wait for the conversation partner to send C
+onsecutiveFrames containing the actual data. The
+partner’s CAN ID is specified in pPatternMsg. In our example, this CAN ID is $641.
+
+• Repeat as necessary until the entire block has been received. When finished, the pass-thru device will
+put the assembled message into the API receive queue. The CAN ID of the assembled message will
+be the CAN ID of the sender. In our example, this CAN ID is $641.
+
+If the FirstFrame does not match any flow control filters, then the message must be ignored by the
+device.
+
+Segmented messages cause the API to generate an RxStart indication. This lets the application know
+that the device has started message reception. It may take a while before message reception is
+complete, especially if the application has increased BS and STmin.
+
+Once the transfer is complete, the entire message can be read like on any other protocol. Usually,
+applications will call PassThruReadMsg again immediately after getting an RxStart indication. Application
+writers should not assume that the complete message will always follow the RxStart indication. If multiple
+conversations are setup, indications and messages from other conversations can be received in between
+the RxStart indication and the actual message. The parameters for PassThruReadMsg are exactly the
+same as in the previous section. The only difference is that the DataSize will be larger and
+ExtraDataIndex will be non-zero.
+
+
+
+A.5 Receiving an Unsegmented Message
+No messages can be received until a conversation is setup. Each conversation setup will receive
+messages from exactly one CAN ID (and extended address if present). Because setup is bi-directional,
+the same PassThruStartMsgFilter call used for transmission will allow for message reception.
+
+When a SingleFrame is seen on the bus, the pass-thru device will search the list of flow control filters,
+looking for a pPatternMsg message with the same C
+AN ID (and possible extended address) as the
+SingleFrame. Upon matching a filter, the pass-thru device will strip the PCI byte and queue the packet for
+reception. If the SingleFrame does not match a flow control filter, it must be discarded.
+
+The only difference between the previous cases is that single-frame messages do not generate an
+RxStart indication.
+
+
+
+
+
+
+
+
+
diff --git a/panda/drivers/windows/docs/read_msg_flags.png b/panda/drivers/windows/docs/read_msg_flags.png
new file mode 100644
index 0000000000..6d0a1f8742
Binary files /dev/null and b/panda/drivers/windows/docs/read_msg_flags.png differ
diff --git a/panda/drivers/windows/docs/reginfo.txt b/panda/drivers/windows/docs/reginfo.txt
new file mode 100644
index 0000000000..03182832b2
--- /dev/null
+++ b/panda/drivers/windows/docs/reginfo.txt
@@ -0,0 +1,2 @@
+#32 bit: HKEY_LOCAL_MACHINE\SOFTWARE\PassThruSupport
+#64 bit: HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\PassThruSupport
diff --git a/panda/drivers/windows/docs/start_msg_filter.png b/panda/drivers/windows/docs/start_msg_filter.png
new file mode 100644
index 0000000000..7cb1a7a4c8
Binary files /dev/null and b/panda/drivers/windows/docs/start_msg_filter.png differ
diff --git a/panda/drivers/windows/docs/start_msg_filter2.png b/panda/drivers/windows/docs/start_msg_filter2.png
new file mode 100644
index 0000000000..54063a5074
Binary files /dev/null and b/panda/drivers/windows/docs/start_msg_filter2.png differ
diff --git a/panda/drivers/windows/docs/start_msg_filter3.png b/panda/drivers/windows/docs/start_msg_filter3.png
new file mode 100644
index 0000000000..ffc8e34820
Binary files /dev/null and b/panda/drivers/windows/docs/start_msg_filter3.png differ
diff --git a/panda/drivers/windows/docs/start_msg_filter4.png b/panda/drivers/windows/docs/start_msg_filter4.png
new file mode 100644
index 0000000000..9c21fb014d
Binary files /dev/null and b/panda/drivers/windows/docs/start_msg_filter4.png differ
diff --git a/panda/drivers/windows/docs/timeout_info.txt b/panda/drivers/windows/docs/timeout_info.txt
new file mode 100644
index 0000000000..22cfa2c5d5
--- /dev/null
+++ b/panda/drivers/windows/docs/timeout_info.txt
@@ -0,0 +1,42 @@
+From focum information on NI hardware: https://forums.ni.com/t5/Automotive-and-Embedded-Networks/15765-2-with-NI-products/td-p/1454256
+
+/////////////////////////////////////////////////////////////////////
+Timeout Diag Command is the timeout in milliseconds the master
+waits for the response to a diagnostic request message. The default is
+1000 ms.
+
+Timeout FC (Bs) is the timeout in milliseconds the master waits
+for a Flow Control frame after sending a First Frame or the last
+Consecutive Frame of a block. The default is 250 ms.
+
+Timeout CF (Cr) is the timeout in milliseconds the master waits
+for a Consecutive Frame in a multiframe response. The default is
+250 ms.
+
+Receive Block Size (BS) is the number of Consecutive Frames the
+slave sends in one block before waiting for the next Flow Control
+frame. A value of 0 (default) means all Consecutive Frames are sent
+in one run without interruption.
+
+Wait Time CF (STmin) defines the minimum time for the slave to
+wait between sending two Consecutive Frames of a block. Values
+from 0 to 127 are wait times in milliseconds. Values 241 to 249
+(Hex F1 to F9) mean wait times of 100 μs to 900 μs, respectively.
+All other values are reserved. The default is 5 ms.
+
+Max Wait Frames (N_WFTmax) is the maximum number of WAIT
+frames the master accepts before terminating the connection. The
+default is 10.
+
+
+There are no defined lower limits for these values; you can specify any
+value down to 0. However, as you correctly pointed out, the timing is
+done by Windows, and will be subject to the jitter introduced by the OS
+which can easily be in the order of 10s of milliseconds. It is however
+hard to give more accurate numbers as the actual jitter is dependent on
+the workload of the computer
+/////////////////////////////////////////////////////////////////////
+
+J2534 04.04 does not appear to have default adjustable parameters for
+the timeout related fields. For now, these default values shall be used
+in the Panda J2534 implementation.
diff --git a/panda/drivers/windows/panda Driver Package/panda Driver Package.vcxproj b/panda/drivers/windows/panda Driver Package/panda Driver Package.vcxproj
new file mode 100644
index 0000000000..5b448e96a8
--- /dev/null
+++ b/panda/drivers/windows/panda Driver Package/panda Driver Package.vcxproj
@@ -0,0 +1,99 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}
+ {a1357fe7-03e0-4d61-85f4-09c7ed38c0c1}
+ v4.5
+ 12.0
+ $driverCurrentWindowsConfigurationName$ Debug
+ Win32
+ panda_Driver_Package
+ $(LatestTargetPlatformVersion)
+
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ Utility
+ Package
+ true
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ Utility
+ Package
+ true
+
+
+
+
+
+
+
+
+
+
+
+ DbgengRemoteDebugger
+
+
+
+ False
+ False
+ True
+
+ 133563
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+
+ DbgengRemoteDebugger
+
+
+
+ False
+ False
+ True
+
+ 133563
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(KMDF_VERSION_MAJOR).$(KMDF_VERSION_MINOR)
+
+
+
+
+
+
+ $(KMDF_VERSION_MAJOR).$(KMDF_VERSION_MINOR)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters b/panda/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters
new file mode 100644
index 0000000000..b4cf077981
--- /dev/null
+++ b/panda/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters
@@ -0,0 +1,14 @@
+
+
+
+
+ {8E41214B-6785-4CFE-B992-037D68949A14}
+ inf;inv;inx;mof;mc;
+
+
+
+
+ Driver Files
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/panda Driver Package/panda.inf b/panda/drivers/windows/panda Driver Package/panda.inf
new file mode 100644
index 0000000000..69390dcbc0
Binary files /dev/null and b/panda/drivers/windows/panda Driver Package/panda.inf differ
diff --git a/panda/drivers/windows/panda.ico b/panda/drivers/windows/panda.ico
new file mode 100644
index 0000000000..593a5cd33c
Binary files /dev/null and b/panda/drivers/windows/panda.ico differ
diff --git a/panda/drivers/windows/panda.sln b/panda/drivers/windows/panda.sln
new file mode 100644
index 0000000000..a74e4022bf
--- /dev/null
+++ b/panda/drivers/windows/panda.sln
@@ -0,0 +1,90 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pandaJ2534DLL", "pandaJ2534DLL\pandaJ2534DLL.vcxproj", "{A2BB18A5-F26B-48D6-BBB5-B83D64473C77}"
+ ProjectSection(ProjectDependencies) = postProject
+ {5528AEFB-638D-49AF-B9D4-965154E7D531} = {5528AEFB-638D-49AF-B9D4-965154E7D531}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda", "panda\panda.vcxproj", "{5528AEFB-638D-49AF-B9D4-965154E7D531}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda_playground", "panda_playground\panda_playground.vcxproj", "{691DB635-C272-4B98-897E-0505B970DCA9}"
+ ProjectSection(ProjectDependencies) = postProject
+ {5528AEFB-638D-49AF-B9D4-965154E7D531} = {5528AEFB-638D-49AF-B9D4-965154E7D531}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda Driver Package", "panda Driver Package\panda Driver Package.vcxproj", "{BD34DB24-F5DC-4992-A74F-05FAF731ABED}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tests", "pandaJ2534DLL Test\pandaJ2534DLL Test.vcxproj", "{7912F978-B48C-4C5D-8BFD-5D1E22158E47}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECUsim DLL", "ECUsim DLL\ECUsim DLL.vcxproj", "{96E0E646-EE76-444D-9A77-A0CD7F781DEB}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECUsim CLI", "ECUsim CLI\ECUsim CLI.vcxproj", "{D99E2FCD-21A4-4065-949A-31E34E0E69D1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x64.ActiveCfg = Debug|Win32
+ {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x86.ActiveCfg = Debug|Win32
+ {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x86.Build.0 = Debug|Win32
+ {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x64.ActiveCfg = Release|Win32
+ {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x86.ActiveCfg = Release|Win32
+ {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x86.Build.0 = Release|Win32
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x64.ActiveCfg = Debug|x64
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x64.Build.0 = Debug|x64
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x86.ActiveCfg = Debug|Win32
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x86.Build.0 = Debug|Win32
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x64.ActiveCfg = Release|x64
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x64.Build.0 = Release|x64
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x86.ActiveCfg = Release|Win32
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x86.Build.0 = Release|Win32
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x64.ActiveCfg = Debug|x64
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x64.Build.0 = Debug|x64
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x86.ActiveCfg = Debug|Win32
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x86.Build.0 = Debug|Win32
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x64.ActiveCfg = Release|x64
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x64.Build.0 = Release|x64
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x86.ActiveCfg = Release|Win32
+ {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x86.Build.0 = Release|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x64.ActiveCfg = Debug|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x86.ActiveCfg = Debug|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x86.Build.0 = Debug|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x86.Deploy.0 = Debug|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x64.ActiveCfg = Release|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x86.ActiveCfg = Release|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x86.Build.0 = Release|Win32
+ {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x86.Deploy.0 = Release|Win32
+ {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x64.ActiveCfg = Debug|Win32
+ {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x86.ActiveCfg = Debug|Win32
+ {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x86.Build.0 = Debug|Win32
+ {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x64.ActiveCfg = Release|Win32
+ {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x86.ActiveCfg = Release|Win32
+ {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x86.Build.0 = Release|Win32
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x64.ActiveCfg = Debug|x64
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x64.Build.0 = Debug|x64
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x86.ActiveCfg = Debug|Win32
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x86.Build.0 = Debug|Win32
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x64.ActiveCfg = Release|x64
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x64.Build.0 = Release|x64
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x86.ActiveCfg = Release|Win32
+ {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x86.Build.0 = Release|Win32
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x64.ActiveCfg = Debug|x64
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x64.Build.0 = Debug|x64
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x86.ActiveCfg = Debug|Win32
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x86.Build.0 = Debug|Win32
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x64.ActiveCfg = Release|x64
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x64.Build.0 = Release|x64
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x86.ActiveCfg = Release|Win32
+ {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/panda/drivers/windows/panda/device.cpp b/panda/drivers/windows/panda/device.cpp
new file mode 100644
index 0000000000..a204610000
--- /dev/null
+++ b/panda/drivers/windows/panda/device.cpp
@@ -0,0 +1,166 @@
+#include "stdafx.h"
+#include
+#include
+
+#include
+#include
+
+#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 panda::detect_pandas() {
+ HDEVINFO deviceInfo;
+ HRESULT hr;
+ SP_DEVINFO_DATA deviceInfoData;
+ SP_DEVICE_INTERFACE_DATA interfaceData;
+ unsigned int deviceIndex;
+
+ std::unordered_map 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;
+}
diff --git a/panda/drivers/windows/panda/device.h b/panda/drivers/windows/panda/device.h
new file mode 100644
index 0000000000..3b404857cf
--- /dev/null
+++ b/panda/drivers/windows/panda/device.h
@@ -0,0 +1,33 @@
+#ifndef __PANDA_DEVICE
+#define __PANDA_DEVICE
+
+//
+// Define below GUIDs
+//
+#include "stdafx.h"
+#include
+#include
+
+#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 __declspec(dllexport) detect_pandas();
+}
+#endif
diff --git a/panda/drivers/windows/panda/dllmain.cpp b/panda/drivers/windows/panda/dllmain.cpp
new file mode 100644
index 0000000000..69b58914b3
--- /dev/null
+++ b/panda/drivers/windows/panda/dllmain.cpp
@@ -0,0 +1,19 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "stdafx.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/panda/drivers/windows/panda/main.cpp b/panda/drivers/windows/panda/main.cpp
new file mode 100644
index 0000000000..621c600b82
--- /dev/null
+++ b/panda/drivers/windows/panda/main.cpp
@@ -0,0 +1,79 @@
+#include "stdafx.h"
+
+#include
+
+LONG __cdecl
+_tmain(
+ LONG Argc,
+ LPTSTR * Argv
+ )
+/*++
+
+Routine description:
+
+ Sample program that communicates with a USB device using WinUSB
+
+--*/
+{
+ DEVICE_DATA deviceData;
+ HRESULT hr;
+ USB_DEVICE_DESCRIPTOR deviceDesc;
+ BOOL bResult;
+ BOOL noDevice;
+ ULONG lengthReceived;
+
+ UNREFERENCED_PARAMETER(Argc);
+ UNREFERENCED_PARAMETER(Argv);
+
+ //
+ // Find a device connected to the system that has WinUSB installed using our
+ // INF
+ //
+ hr = OpenDevice(&deviceData, &noDevice);
+
+ if (FAILED(hr)) {
+
+ if (noDevice) {
+
+ printf(_T("Device not connected or driver not installed\n"));
+
+ } else {
+
+ printf(_T("Failed looking for device, HRESULT 0x%x\n"), hr);
+ }
+
+ return 0;
+ }
+
+ //
+ // Get device descriptor
+ //
+ bResult = WinUsb_GetDescriptor(deviceData.WinusbHandle,
+ USB_DEVICE_DESCRIPTOR_TYPE,
+ 0,
+ 0,
+ (PBYTE) &deviceDesc,
+ sizeof(deviceDesc),
+ &lengthReceived);
+
+ if (FALSE == bResult || lengthReceived != sizeof(deviceDesc)) {
+
+ printf(_T("Error among LastError %d or lengthReceived %d\n"),
+ FALSE == bResult ? GetLastError() : 0,
+ lengthReceived);
+ CloseDevice(&deviceData);
+ return 0;
+ }
+
+ //
+ // Print a few parts of the device descriptor
+ //
+ printf(_T("Device found: VID_%04X&PID_%04X; bcdUsb %04X; path: %s\n"),
+ deviceDesc.idVendor,
+ deviceDesc.idProduct,
+ deviceDesc.bcdUSB,
+ deviceData.DevicePath);
+
+ CloseDevice(&deviceData);
+ return 0;
+}
diff --git a/panda/drivers/windows/panda/panda.cpp b/panda/drivers/windows/panda/panda.cpp
new file mode 100644
index 0000000000..79ec08edc2
--- /dev/null
+++ b/panda/drivers/windows/panda/panda.cpp
@@ -0,0 +1,479 @@
+// 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;
+
+#pragma pack(1)
+typedef struct _PANDA_CAN_MSG_INTERNAL {
+ uint32_t rir;
+ uint32_t f2;
+ uint8_t dat[8];
+} PANDA_CAN_MSG_INTERNAL;
+
+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_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);
+ for (int i = 0; i < 2; 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);
+
+ std::vector msg_recv;
+ if (alt_setting == 1) {
+ //Read the messages so they do not contaimnate the real message stream.
+ auto err = this->can_recv_async(NULL, msg_recv, 1000);
+ }
+ else {
+ msg_recv = 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;
+}
+
+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});
+}
+
+void Panda::parse_can_recv(std::vector& msg_recv, char *buff, int retcount) {
+ 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);
+ 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;
+ }
+ msg_recv.push_back(in_msg);
+ }
+}
+
+bool Panda::can_recv_async(HANDLE kill_event, std::vector& msg_buff, DWORD timeoutms) {
+ int retcount;
+ char buff[sizeof(PANDA_CAN_MSG_INTERNAL) * 4];
+
+ // Overlapped structure required for async read.
+ HANDLE m_hReadFinishedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ OVERLAPPED m_overlappedData;
+ memset(&m_overlappedData, sizeof(OVERLAPPED), 0);
+ m_overlappedData.hEvent = m_hReadFinishedEvent;
+
+ HANDLE phSignals[2] = { m_hReadFinishedEvent, kill_event };
+
+ if (!WinUsb_ReadPipe(this->usbh, 0x81, (PUCHAR)buff, sizeof(buff), (PULONG)&retcount, &m_overlappedData)) {
+ // An overlapped read will return true if done, or false with an
+ // error of ERROR_IO_PENDING if the transfer is still in process.
+ DWORD dwError = GetLastError();
+ if (dwError == ERROR_IO_PENDING) {
+ 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, &m_overlappedData, (PULONG)&retcount, FALSE)) {
+ // TODO: handle other error cases better.
+ dwError = GetLastError();
+ printf("Got overlap error %d\n", dwError);
+
+ return TRUE;
+ }
+ } 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;
+ }
+ return TRUE;
+ }
+ } else { // ERROR_BAD_COMMAND happens when device is unplugged.
+ return FALSE;
+ }
+ }
+
+ parse_can_recv(msg_buff, buff, retcount);
+ return TRUE;
+}
+
+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;
+
+ parse_can_recv(msg_recv, buff, retcount);
+
+ 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;
+}
diff --git a/panda/drivers/windows/panda/panda.h b/panda/drivers/windows/panda/panda.h
new file mode 100644
index 0000000000..12a4fbb318
--- /dev/null
+++ b/panda/drivers/windows/panda/panda.h
@@ -0,0 +1,214 @@
+#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 __declspec(dllimport)
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#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
+
+//template class __declspec(dllexport) std::basic_string;
+
+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 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(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(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(clock::now());
+
+ // return the difference of the times
+ auto startms = std::chrono::time_point_cast(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 listAvailablePandas();
+ static std::unique_ptr openPanda(std::string sn);
+
+ ~Panda();
+
+ std::string get_usb_sn();
+ bool set_alt_setting(UCHAR alt_setting);
+ UCHAR get_current_alt_setting();
+
+ 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& can_msgs);
+ bool can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus);
+ void parse_can_recv(std::vector& msg_recv, char *buff, int retcount);
+ bool can_recv_async(HANDLE kill_event, std::vector& msg_buff, DWORD timeoutms = INFINITE);
+ std::vector can_recv();
+ 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
+ );
+
+ WINUSB_INTERFACE_HANDLE usbh;
+ HANDLE devh;
+ tstring devPath;
+ std::string sn;
+ bool loopback;
+
+ Timer runningTime;
+ };
+
+}
diff --git a/panda/drivers/windows/panda/panda.ico b/panda/drivers/windows/panda/panda.ico
new file mode 100644
index 0000000000..ff0e071f54
Binary files /dev/null and b/panda/drivers/windows/panda/panda.ico differ
diff --git a/panda/drivers/windows/panda/panda.rc b/panda/drivers/windows/panda/panda.rc
new file mode 100644
index 0000000000..88cf9f7678
Binary files /dev/null and b/panda/drivers/windows/panda/panda.rc differ
diff --git a/panda/drivers/windows/panda/panda.vcxproj b/panda/drivers/windows/panda/panda.vcxproj
new file mode 100644
index 0000000000..147c58ca14
--- /dev/null
+++ b/panda/drivers/windows/panda/panda.vcxproj
@@ -0,0 +1,193 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {5528AEFB-638D-49AF-B9D4-965154E7D531}
+ Win32Proj
+ panda
+ 8.1
+
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ true
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ false
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ false
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+
+ Use
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions)
+ true
+ false
+
+
+ Windows
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib
+
+
+
+
+ Use
+ Level3
+ Disabled
+ _DEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ true
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ NDEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ true
+ true
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/panda/panda.vcxproj.filters b/panda/drivers/windows/panda/panda.vcxproj.filters
new file mode 100644
index 0000000000..cded701a31
--- /dev/null
+++ b/panda/drivers/windows/panda/panda.vcxproj.filters
@@ -0,0 +1,58 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/panda/resource.h b/panda/drivers/windows/panda/resource.h
new file mode 100644
index 0000000000..bf006ff4b3
Binary files /dev/null and b/panda/drivers/windows/panda/resource.h differ
diff --git a/panda/drivers/windows/panda/stdafx.cpp b/panda/drivers/windows/panda/stdafx.cpp
new file mode 100644
index 0000000000..8793e09b40
--- /dev/null
+++ b/panda/drivers/windows/panda/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// panda.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/panda/drivers/windows/panda/stdafx.h b/panda/drivers/windows/panda/stdafx.h
new file mode 100644
index 0000000000..cc3d3b9667
--- /dev/null
+++ b/panda/drivers/windows/panda/stdafx.h
@@ -0,0 +1,19 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#endif
+// Windows Header Files:
+#include
+
+#include
+#include
+#include
+#include
diff --git a/panda/drivers/windows/panda/targetver.h b/panda/drivers/windows/panda/targetver.h
new file mode 100644
index 0000000000..1bf4ee6fee
--- /dev/null
+++ b/panda/drivers/windows/panda/targetver.h
@@ -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
+
+#define WINVER _WIN32_WINNT_WIN7
+#define _WIN32_WINNT _WIN32_WINNT_WIN7
+
+#include
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp b/panda/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp
new file mode 100644
index 0000000000..1effc49145
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp
@@ -0,0 +1,87 @@
+#include "stdafx.h"
+#include "Loader4.h"
+#include "pandaJ2534DLL/J2534_v0404.h"
+#include "panda/panda.h"
+#include "Timer.h"
+#include "ECUsim DLL\ECUsim.h"
+#include "TestHelpers.h"
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace pandaWCUsimTest
+{
+
+ TEST_CLASS(ECUsimTests)
+ {
+ public:
+
+ TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bStandardAddrPad500k)
+ {
+ ECUsim sim("", 500000);
+ auto p = getPanda(500);
+
+ p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x01\x00", 3, panda::PANDA_CAN1);
+ auto msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x01\x00", 3), LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x06\x41\x00\xff\xff\xff\xfe\x00", 8), LINE_INFO());
+ }
+
+ TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bStandardAddrPad250k)
+ {
+ ECUsim sim("", 250000);
+ auto p = getPanda(250);
+
+ p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x01\x00", 3, panda::PANDA_CAN1);
+ auto msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x01\x00", 3), LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x06""\x41\x00""\xff\xff\xff\xfe""\x00", 8), LINE_INFO());
+ }
+
+ TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bExtAddrPad500k)
+ {
+ ECUsim sim("", 500000, TRUE);
+ auto p = getPanda(500);
+
+ p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x02\x01\x00", 4, panda::PANDA_CAN1);
+ auto msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x02\x01\x00", 4), LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x06""\x41\x00""\xff\xff\xff\xfe", 8), LINE_INFO());
+ }
+
+ TEST_METHOD(ECUsim_ISO15765_MultiFrameTx_29bStandardAddrPad500k)
+ {
+ ECUsim sim("", 500000);
+ auto p = getPanda(500);
+
+ p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x09\x02", 3, panda::PANDA_CAN1);
+ auto msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x09\x02", 3), LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x10\x14""\x49\x02\x01""1D4", 8), LINE_INFO());
+
+ p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x30\x00\x00", 3, panda::PANDA_CAN1);
+ msg_recv = panda_recv_loop(p, 3);
+ check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x30\x0\x0", 3), LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x21""GP00R55", 8), LINE_INFO());
+ check_panda_can_msg(msg_recv[2], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x22""B123456", 8), LINE_INFO());
+ }
+
+ TEST_METHOD(ECUsim_ISO15765_MultiFrameTx_29bExtAddrPad500k)
+ {
+ ECUsim sim("", 500000, TRUE);
+ auto p = getPanda(500);
+
+ p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x02\x09\x02", 4, panda::PANDA_CAN1);
+ auto msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x02\x09\x02", 4), LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x10\x14""\x49\x02\x01""1D", 8), LINE_INFO());
+
+ p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x30\x00\x00", 4, panda::PANDA_CAN1);
+ msg_recv = panda_recv_loop(p, 4);
+ check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x30\x0\x0", 4), LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x21""4GP00R", 8), LINE_INFO());
+ check_panda_can_msg(msg_recv[2], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x22""55B123", 8), LINE_INFO());
+ check_panda_can_msg(msg_recv[3], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x23""456", 5), LINE_INFO());
+ }
+ };
+
+}
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/Loader4.cpp b/panda/drivers/windows/pandaJ2534DLL Test/Loader4.cpp
new file mode 100644
index 0000000000..f4a0b70fa5
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/Loader4.cpp
@@ -0,0 +1,240 @@
+// Loader4.cpp
+// (c) 2005 National Control Systems, Inc.
+// Portions (c) 2004 Drew Technologies, Inc.
+// Dynamic J2534 v04.04 dll loader for VB
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to:
+// the Free Software Foundation, Inc.
+// 51 Franklin Street, Fifth Floor
+// Boston, MA 02110-1301, USA
+
+// National Control Systems, Inc.
+// 10737 Hamburg Rd
+// Hamburg, MI 48139
+// 810-231-2901
+
+// Drew Technologies, Inc.
+// 7012 E.M -36, Suite 3B
+// Whitmore Lake, MI 48189
+// 810-231-3171
+
+#define STRICT
+#include "stdafx.h"
+#include
+#include "Loader4.h"
+
+PTOPEN LocalOpen;
+PTCLOSE LocalClose;
+PTCONNECT LocalConnect;
+PTDISCONNECT LocalDisconnect;
+PTREADMSGS LocalReadMsgs;
+PTWRITEMSGS LocalWriteMsgs;
+PTSTARTPERIODICMSG LocalStartPeriodicMsg;
+PTSTOPPERIODICMSG LocalStopPeriodicMsg;
+PTSTARTMSGFILTER LocalStartMsgFilter;
+PTSTOPMSGFILTER LocalStopMsgFilter;
+PTSETPROGRAMMINGVOLTAGE LocalSetProgrammingVoltage;
+PTREADVERSION LocalReadVersion;
+PTGETLASTERROR LocalGetLastError;
+PTIOCTL LocalIoctl;
+
+HINSTANCE hDLL = NULL;
+//BOOL bIsCorrectVersion = FALSE;
+
+BOOL WINAPI DllMain(HINSTANCE hInstA, DWORD dwReason, LPVOID lpvReserved)
+{
+ switch (dwReason) {
+ case DLL_PROCESS_ATTACH:
+ // The DLL is being mapped into the process's address space
+
+ case DLL_THREAD_ATTACH:
+ // A thread is being created
+ break;
+
+ case DLL_THREAD_DETACH:
+ // A thread is exiting cleanly
+ break;
+
+ case DLL_PROCESS_DETACH:
+ // The DLL is being unmapped from the process's address space
+ break;
+ }
+
+ return TRUE;
+}
+
+
+long WINAPI LoadJ2534Dll(char *sLib)
+{
+ long lFuncList = 0;
+
+ if (hDLL != NULL) UnloadJ2534Dll();
+ hDLL = LoadLibraryA (sLib);
+ if (hDLL == NULL) return ERR_NO_DLL;
+
+ LocalOpen = (PTOPEN)(GetProcAddress(hDLL, "PassThruOpen"));
+ if (LocalOpen == NULL) lFuncList = lFuncList | ERR_NO_PTOPEN;
+
+ LocalClose = (PTCLOSE)(GetProcAddress(hDLL, "PassThruClose"));
+ if (LocalClose == NULL) lFuncList = lFuncList | ERR_NO_PTCLOSE;
+
+ LocalConnect = (PTCONNECT)(GetProcAddress(hDLL,"PassThruConnect"));
+ if (LocalConnect == NULL) lFuncList = lFuncList | ERR_NO_PTCONNECT;
+
+ LocalDisconnect = (PTDISCONNECT)(GetProcAddress(hDLL,"PassThruDisconnect"));
+ if (LocalDisconnect == NULL) lFuncList = lFuncList | ERR_NO_PTDISCONNECT;
+
+ LocalReadMsgs = (PTREADMSGS)(GetProcAddress(hDLL,"PassThruReadMsgs"));
+ if (LocalReadMsgs == NULL) lFuncList = lFuncList | ERR_NO_PTREADMSGS;
+
+ LocalWriteMsgs = (PTWRITEMSGS)(GetProcAddress(hDLL,"PassThruWriteMsgs"));
+ if (LocalWriteMsgs == NULL) lFuncList = lFuncList | ERR_NO_PTWRITEMSGS;
+
+ LocalStartPeriodicMsg = (PTSTARTPERIODICMSG)(GetProcAddress(hDLL,"PassThruStartPeriodicMsg"));
+ if (LocalStartPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTARTPERIODICMSG;
+
+ LocalStopPeriodicMsg = (PTSTOPPERIODICMSG)(GetProcAddress(hDLL,"PassThruStopPeriodicMsg"));
+ if (LocalStopPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTOPPERIODICMSG;
+
+ LocalStartMsgFilter = (PTSTARTMSGFILTER)(GetProcAddress(hDLL,"PassThruStartMsgFilter"));
+ if (LocalStartPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTARTMSGFILTER;
+
+ LocalStopMsgFilter = (PTSTOPMSGFILTER)(GetProcAddress(hDLL,"PassThruStopMsgFilter"));
+ if (LocalStopMsgFilter == NULL) lFuncList = lFuncList | ERR_NO_PTSTOPMSGFILTER;
+
+ LocalSetProgrammingVoltage = (PTSETPROGRAMMINGVOLTAGE)(GetProcAddress(hDLL,"PassThruSetProgrammingVoltage"));
+ if (LocalSetProgrammingVoltage == NULL) lFuncList = lFuncList | ERR_NO_PTSETPROGRAMMINGVOLTAGE;
+
+ LocalReadVersion = (PTREADVERSION)(GetProcAddress(hDLL,"PassThruReadVersion"));
+ if (LocalReadVersion == NULL) lFuncList = lFuncList | ERR_NO_PTREADVERSION;
+
+ LocalGetLastError = (PTGETLASTERROR)(GetProcAddress(hDLL,"PassThruGetLastError"));
+ if (LocalGetLastError == NULL) lFuncList = lFuncList | ERR_NO_PTGETLASTERROR;
+
+ LocalIoctl = (PTIOCTL)(GetProcAddress(hDLL,"PassThruIoctl"));
+ if (LocalIoctl == NULL) lFuncList = lFuncList | ERR_NO_PTIOCTL;
+
+ if (lFuncList == ERR_NO_FUNCTIONS) return ERR_WRONG_DLL_VER;
+
+ return lFuncList;
+}
+
+long WINAPI UnloadJ2534Dll()
+{
+ if (FreeLibrary(hDLL))
+ {
+ hDLL = NULL;
+ LocalOpen = NULL;
+ LocalClose = NULL;
+ LocalConnect = NULL;
+ LocalDisconnect = NULL;
+ LocalReadMsgs = NULL;
+ LocalWriteMsgs = NULL;
+ LocalStartPeriodicMsg = NULL;
+ LocalStopPeriodicMsg = NULL;
+ LocalStartMsgFilter = NULL;
+ LocalStopMsgFilter = NULL;
+ LocalSetProgrammingVoltage = NULL;
+ LocalReadVersion = NULL;
+ LocalGetLastError = NULL;
+ LocalIoctl = NULL;
+ return 0;
+ }
+ return ERR_NO_DLL;
+}
+
+long WINAPI PassThruOpen(void *pName, unsigned long *pDeviceID)
+{
+ if (LocalOpen == NULL) return ERR_FUNC_MISSING;
+ return LocalOpen(pName, pDeviceID);
+}
+
+long WINAPI PassThruClose(unsigned long DeviceID)
+{
+ if (LocalOpen == NULL) return ERR_FUNC_MISSING;
+ return LocalClose(DeviceID);
+}
+
+long WINAPI PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long Baudrate, unsigned long *pChannelID)
+{
+ if (LocalConnect == NULL) return ERR_FUNC_MISSING;
+ return LocalConnect(DeviceID, ProtocolID, Flags, Baudrate, pChannelID);
+}
+
+long WINAPI PassThruDisconnect(unsigned long ChannelID)
+{
+ if (LocalDisconnect == NULL) return ERR_FUNC_MISSING;
+ return LocalDisconnect(ChannelID);
+}
+
+long WINAPI PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
+{
+ if (LocalReadMsgs == NULL) return ERR_FUNC_MISSING;
+ return LocalReadMsgs(ChannelID, pMsg, pNumMsgs, Timeout);
+}
+
+long WINAPI PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
+{
+ if (LocalWriteMsgs == NULL) return ERR_FUNC_MISSING;
+ return LocalWriteMsgs(ChannelID, pMsg, pNumMsgs, Timeout);
+}
+
+long WINAPI PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval)
+{
+ if (LocalStartPeriodicMsg == NULL) return ERR_FUNC_MISSING;
+ return LocalStartPeriodicMsg(ChannelID, pMsg, pMsgID, TimeInterval);
+}
+
+long WINAPI PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID)
+{
+ if (LocalStopPeriodicMsg == NULL) return ERR_FUNC_MISSING;
+ return LocalStopPeriodicMsg(ChannelID, MsgID);
+}
+
+long WINAPI PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType,
+ PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID)
+{
+ if (LocalStartMsgFilter == NULL) return ERR_FUNC_MISSING;
+ return LocalStartMsgFilter(ChannelID, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID);
+}
+
+long WINAPI PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID)
+{
+ if (LocalStopMsgFilter == NULL) return ERR_FUNC_MISSING;
+ return LocalStopMsgFilter(ChannelID, FilterID);
+}
+
+long WINAPI PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage)
+{
+ if (LocalSetProgrammingVoltage == NULL) return ERR_FUNC_MISSING;
+ return LocalSetProgrammingVoltage(DeviceID, PinNumber, Voltage);
+}
+
+long WINAPI PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion)
+{
+ if (LocalReadVersion == NULL) return ERR_FUNC_MISSING;
+ return LocalReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion);
+}
+
+long WINAPI PassThruGetLastError(char *pErrorDescription)
+{
+ if (LocalGetLastError == NULL) return ERR_FUNC_MISSING;
+ return LocalGetLastError(pErrorDescription);
+}
+
+long WINAPI PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput)
+{
+ if (LocalIoctl == NULL) return ERR_FUNC_MISSING;
+ return LocalIoctl(ChannelID, IoctlID, pInput, pOutput);
+}
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/Loader4.h b/panda/drivers/windows/pandaJ2534DLL Test/Loader4.h
new file mode 100644
index 0000000000..9710144141
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/Loader4.h
@@ -0,0 +1,55 @@
+// Loader4.h
+// (c) 2005 National Control Systems, Inc.
+// Portions (c) 2004 Drew Technologies, Inc.
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to:
+// the Free Software Foundation, Inc.
+// 51 Franklin Street, Fifth Floor
+// Boston, MA 02110-1301, USA
+
+// National Control Systems, Inc.
+// 10737 Hamburg Rd
+// Hamburg, MI 48139
+// 810-231-2901
+
+// Drew Technologies, Inc.
+// 7012 E.M -36, Suite 3B
+// Whitmore Lake, MI 48189
+// 810-231-3171
+
+#include "pandaJ2534DLL/J2534_v0404.h"
+
+//Other Functions
+long WINAPI LoadJ2534Dll(char *);
+long WINAPI UnloadJ2534Dll();
+
+// NCS Returns of any functions not found
+#define ERR_NO_PTOPEN 0x0001
+#define ERR_NO_PTCLOSE 0x0002
+#define ERR_NO_PTCONNECT 0x0004
+#define ERR_NO_PTDISCONNECT 0x0008
+#define ERR_NO_PTREADMSGS 0x0010
+#define ERR_NO_PTWRITEMSGS 0x0020
+#define ERR_NO_PTSTARTPERIODICMSG 0x0040
+#define ERR_NO_PTSTOPPERIODICMSG 0x0080
+#define ERR_NO_PTSTARTMSGFILTER 0x0100
+#define ERR_NO_PTSTOPMSGFILTER 0x0200
+#define ERR_NO_PTSETPROGRAMMINGVOLTAGE 0x0400
+#define ERR_NO_PTREADVERSION 0x0800
+#define ERR_NO_PTGETLASTERROR 0x1000
+#define ERR_NO_PTIOCTL 0x2000
+#define ERR_NO_FUNCTIONS 0x3fff
+#define ERR_NO_DLL -1
+#define ERR_WRONG_DLL_VER -2
+#define ERR_FUNC_MISSING -3
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp b/panda/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp
new file mode 100644
index 0000000000..62096d1181
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp
@@ -0,0 +1,254 @@
+#include "stdafx.h"
+#include "TestHelpers.h"
+#include "Loader4.h"
+#include "pandaJ2534DLL/J2534_v0404.h"
+#include "panda/panda.h"
+#include "Timer.h"
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+void write_ioctl(unsigned int chanid, unsigned int param, unsigned int val, const __LineInfo* pLineInfo) {
+ SCONFIG config = { param, val };
+ SCONFIG_LIST inconfig = { 1, &config };
+
+ Assert::AreEqual(STATUS_NOERROR, PassThruIoctl(chanid, SET_CONFIG, &inconfig, NULL), _T("Failed to set IOCTL."), pLineInfo);
+}
+
+std::vector panda_recv_loop_loose(std::unique_ptr& p, unsigned int min_num, unsigned long timeout_ms) {
+ std::vector ret_messages;
+ Timer t = Timer();
+
+ while (t.getTimePassed() < timeout_ms) {
+ Sleep(10);
+ std::vectormsg_recv = p->can_recv();
+ if (msg_recv.size() > 0) {
+ ret_messages.insert(std::end(ret_messages), std::begin(msg_recv), std::end(msg_recv));
+ }
+ }
+
+ Assert::IsTrue(min_num <= ret_messages.size(), _T("Received too few messages."));
+ return ret_messages;
+}
+
+std::vector panda_recv_loop(std::unique_ptr& p, unsigned int num_expected, unsigned long timeout_ms) {
+ std::vector ret_messages;
+ Timer t = Timer();
+
+ while (t.getTimePassed() < timeout_ms) {
+ Sleep(10);
+ std::vectormsg_recv = p->can_recv();
+ if (msg_recv.size() > 0) {
+ ret_messages.insert(std::end(ret_messages), std::begin(msg_recv), std::end(msg_recv));
+ }
+ if (ret_messages.size() >= num_expected) break;
+ }
+
+ std::ostringstream stringStream;
+
+ stringStream << "j2534_recv_loop Broke at " << t.getTimePassed() << " ms size is " << ret_messages.size() << std::endl;
+
+ if (num_expected != ret_messages.size()) {
+ stringStream << "Incorrect number of messages received. Displaying the messages:" << std::endl;
+ for (auto msg : ret_messages) {
+ stringStream << " TS: " << msg.recv_time << "; Dat: ";
+ for (int i = 0; i < msg.len; i++) stringStream << std::hex << std::setw(2) << std::setfill('0') << int(msg.dat[i] & 0xFF) << " ";
+ stringStream << std::endl;
+ }
+ }
+
+ Logger::WriteMessage(stringStream.str().c_str());
+
+ Assert::AreEqual(num_expected, ret_messages.size(), _T("Received wrong number of messages."));
+ return ret_messages;
+}
+
+void check_panda_can_msg(panda::PANDA_CAN_MSG& msgin, uint8_t bus, unsigned long addr, bool addr_29b,
+ bool is_receipt, std::string dat, const __LineInfo* pLineInfo) {
+ Assert::AreEqual(bus, msgin.bus, _T("Wrong msg bus"), pLineInfo);
+ Assert::AreEqual(addr, msgin.addr, _T("Wrong msg addr"), pLineInfo);
+ Assert::AreEqual(addr_29b, msgin.addr_29b, _T("Wrong msg 29b flag"), pLineInfo);
+ Assert::AreEqual(is_receipt, msgin.is_receipt, _T("Wrong msg receipt flag"), pLineInfo);
+
+ std::ostringstream logmsg;
+ logmsg << "Expected Hex (";
+ for (int i = 0; i < dat.size(); i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(dat[i] & 0xFF) << " ";
+ logmsg << "); Actual Hex (";
+ for (int i = 0; i < msgin.len; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(((char*)msgin.dat)[i] & 0xFF) << " ";
+ logmsg << ")";
+ Logger::WriteMessage(logmsg.str().c_str());
+
+ Assert::AreEqual(dat.size(), msgin.len, _T("Wrong msg len"), pLineInfo);
+ Assert::AreEqual(dat, std::string((char*)msgin.dat, msgin.len), _T("Wrong msg payload"), pLineInfo);
+}
+
+unsigned long J2534_start_periodic_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize,
+ unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, const __LineInfo * pLineInfo) {
+ PASSTHRU_MSG msg = { ProtocolID, 0, TxFlags, 0, DataSize, ExtraDataIndex };
+ memcpy_s(msg.Data, 4128, Data, DataSize);
+ unsigned long msgID;
+ Assert::AreEqual(STATUS_NOERROR, J2534_start_periodic_msg(chanid, ProtocolID, TxFlags, DataSize,
+ ExtraDataIndex, Data, TimeInterval, &msgID, pLineInfo), _T("Failed to start Periodic Message."), pLineInfo);
+ return msgID;
+}
+
+unsigned long J2534_start_periodic_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize,
+ unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, unsigned long* msgID, const __LineInfo * pLineInfo) {
+ PASSTHRU_MSG msg = { ProtocolID, 0, TxFlags, 0, DataSize, ExtraDataIndex };
+ memcpy_s(msg.Data, 4128, Data, DataSize);
+ return PassThruStartPeriodicMsg(chanid, &msg, msgID, TimeInterval);
+}
+
+void J2534_send_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags,
+ unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo) {
+
+ PASSTHRU_MSG msg = { ProtocolID, RxStatus, TxFlags, Timestamp, DataSize, ExtraDataIndex };
+ memcpy_s(msg.Data, 4128, Data, DataSize);
+ unsigned long msgcount = 1;
+ Assert::AreEqual(STATUS_NOERROR, PassThruWriteMsgs(chanid, &msg, &msgcount, 0), _T("Failed to write message."), pLineInfo);
+ Assert::AreEqual(1, msgcount, _T("Wrong message count after tx."), pLineInfo);
+}
+
+long J2534_send_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags,
+ unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data) {
+
+ PASSTHRU_MSG msg = { ProtocolID, RxStatus, TxFlags, Timestamp, DataSize, ExtraDataIndex };
+ memcpy_s(msg.Data, 4128, Data, DataSize);
+ unsigned long msgcount = 1;
+ return PassThruWriteMsgs(chanid, &msg, &msgcount, 0);
+}
+
+//Allow more messages to come in than the min.
+std::vector j2534_recv_loop_loose(unsigned int chanid, unsigned int min_num, unsigned long timeout_ms) {
+ std::vector ret_messages;
+ PASSTHRU_MSG recvbuff[4] = {};
+ Timer t = Timer();
+
+ while (t.getTimePassed() < timeout_ms) {
+ unsigned long msgcount = 4;
+ unsigned int res = PassThruReadMsgs(chanid, recvbuff, &msgcount, 0);
+ if (res == ERR_BUFFER_EMPTY) continue;
+ Assert::IsFalse(msgcount > 4, _T("PassThruReadMsgs returned more data than the buffer could hold."));
+ Assert::AreEqual(STATUS_NOERROR, res, _T("Failed to read message."));
+ if (msgcount > 0) {
+ for (unsigned int i = 0; i < msgcount; i++) {
+ ret_messages.push_back(recvbuff[i]);
+ }
+ }
+ }
+
+ Assert::IsTrue(min_num <= ret_messages.size(), _T("Received too few messages."));
+ return ret_messages;
+}
+
+std::vector j2534_recv_loop(unsigned int chanid, unsigned int num_expected, unsigned long timeout_ms) {
+ std::vector ret_messages;
+ PASSTHRU_MSG recvbuff[4] = {};
+ Timer t = Timer();
+
+ while (t.getTimePassed() < timeout_ms) {
+ unsigned long msgcount = 4;
+ unsigned int res = PassThruReadMsgs(chanid, recvbuff, &msgcount, 0);
+ if (res == ERR_BUFFER_EMPTY) continue;
+ Assert::IsFalse(msgcount > 4, _T("PassThruReadMsgs returned more data than the buffer could hold."));
+ Assert::AreEqual(STATUS_NOERROR, res, _T("Failed to read message."));
+ if (msgcount > 0) {
+ for (unsigned int i = 0; i < msgcount; i++) {
+ ret_messages.push_back(recvbuff[i]);
+ }
+ }
+ if (ret_messages.size() >= num_expected) break;
+ }
+
+ std::ostringstream stringStream;
+ stringStream << "j2534_recv_loop Broke at " << t.getTimePassed() << " ms size is " << ret_messages.size() << std::endl;
+
+ if (num_expected != ret_messages.size()) {
+ stringStream << "Incorrect number of messages received. Displaying the messages:" << std::endl;
+ for (auto msg : ret_messages) {
+ stringStream << " TS: " << msg.Timestamp << "; Dat: ";
+ for (int i = 0; i < msg.DataSize; i++) stringStream << std::hex << std::setw(2) << std::setfill('0') << int(msg.Data[i] & 0xFF) << " ";
+ stringStream << std::endl;
+ }
+ }
+
+ Logger::WriteMessage(stringStream.str().c_str());
+
+ Assert::AreEqual(num_expected, ret_messages.size(), _T("Received wrong number of messages."));
+ return ret_messages;
+}
+
+void check_J2534_can_msg(PASSTHRU_MSG& msgin, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags,
+ unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo) {
+ Assert::AreEqual(DataSize, msgin.DataSize, _T("Wrong msg len"), pLineInfo);
+
+ std::ostringstream logmsg;
+ logmsg << "Expected Hex (";
+ for (int i = 0; i < DataSize; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(Data[i] & 0xFF) << " ";
+ logmsg << "); Actual Hex (";
+ for (int i = 0; i < msgin.DataSize; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(((char*)msgin.Data)[i] & 0xFF) << " ";
+ logmsg << ")";
+ Logger::WriteMessage(logmsg.str().c_str());
+ Assert::AreEqual(std::string(Data, DataSize), std::string((char*)msgin.Data, msgin.DataSize), _T("Wrong msg payload"), pLineInfo);
+
+ Assert::AreEqual(ProtocolID, msgin.ProtocolID, _T("Wrong msg protocol"), pLineInfo);
+ Assert::AreEqual(RxStatus, msgin.RxStatus, _T("Wrong msg receipt rxstatus"), pLineInfo);
+ Assert::AreEqual(TxFlags, msgin.TxFlags, _T("Wrong msg receipt txflag"), pLineInfo);
+ Assert::AreEqual(ExtraDataIndex, msgin.ExtraDataIndex, _T("Wrong msg ExtraDataIndex"), pLineInfo);
+}
+
+unsigned long J2534_set_PASS_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx,
+ unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo) {
+ unsigned long filterid;
+ PASSTHRU_MSG mask_msg = { ProtocolID, 0, tx, 0, len, 0, 0 };
+ PASSTHRU_MSG pattern_msg = { ProtocolID, 0, tx, 0, len, 0, 0 };
+ memcpy(mask_msg.Data, mask, len);
+ memcpy(pattern_msg.Data, pattern, len);
+ Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, PASS_FILTER, &mask_msg, &pattern_msg, NULL, &filterid),
+ _T("Failed to create filter."), pLineInfo);
+ return filterid;
+}
+
+unsigned long J2534_set_BLOCK_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx,
+ unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo) {
+ unsigned long filterid;
+ PASSTHRU_MSG mask_msg = { ProtocolID, 0, tx, 0, len, 0, 0 };
+ PASSTHRU_MSG pattern_msg = { ProtocolID, 0, tx, 0, len, 0, 0 };
+ memcpy(mask_msg.Data, mask, len);
+ memcpy(pattern_msg.Data, pattern, len);
+ Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, BLOCK_FILTER, &mask_msg, &pattern_msg, NULL, &filterid),
+ _T("Failed to create filter."), pLineInfo);
+ return filterid;
+}
+
+unsigned long J2534_set_flowctrl_filter(unsigned long chanid, unsigned long tx,
+ unsigned long len, char* mask, char* pattern, char* flow, const __LineInfo* pLineInfo) {
+ unsigned long filterid;
+ PASSTHRU_MSG mask_msg = { ISO15765, 0, tx, 0, len, 0, 0 };
+ PASSTHRU_MSG pattern_msg = { ISO15765, 0, tx, 0, len, 0, 0 };
+ PASSTHRU_MSG flow_msg = { ISO15765, 0, tx, 0, len, 0, 0 };
+ memcpy(mask_msg.Data, mask, len);
+ memcpy(pattern_msg.Data, pattern, len);
+ memcpy(flow_msg.Data, flow, len);
+ Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, FLOW_CONTROL_FILTER, &mask_msg, &pattern_msg, &flow_msg, &filterid),
+ _T("Failed to create filter."), pLineInfo);
+ return filterid;
+}
+
+std::unique_ptr getPanda(unsigned long kbaud, BOOL loopback) {
+ auto p = panda::Panda::openPanda("");
+ Assert::IsTrue(p != nullptr, _T("Could not open raw panda device to test communication."));
+ p->set_can_speed_kbps(panda::PANDA_CAN1, kbaud);
+ p->set_safety_mode(panda::SAFETY_ALLOUTPUT);
+ p->set_can_loopback(loopback);
+ p->can_clear(panda::PANDA_CAN_RX);
+ return p;
+}
+
+std::vector checked_panda_send(std::unique_ptr& p, uint32_t addr, bool is_29b,
+ char* msg, uint8_t len, unsigned int num_expected, const __LineInfo* pLineInfo, unsigned long timeout_ms) {
+ Assert::IsTrue(p->can_send(addr, is_29b, (const uint8_t*)msg, len, panda::PANDA_CAN1), _T("Panda send says it failed."), pLineInfo);
+ auto panda_msg_recv = panda_recv_loop(p, 1 + num_expected, timeout_ms);
+ check_panda_can_msg(panda_msg_recv[0], 0, addr, is_29b, TRUE, std::string(msg, len), pLineInfo);
+ panda_msg_recv.erase(panda_msg_recv.begin());
+ return panda_msg_recv;
+}
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/TestHelpers.h b/panda/drivers/windows/pandaJ2534DLL Test/TestHelpers.h
new file mode 100644
index 0000000000..17dcee5f54
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/TestHelpers.h
@@ -0,0 +1,48 @@
+#pragma once
+#include "stdafx.h"
+#include "pandaJ2534DLL/J2534_v0404.h"
+#include "panda/panda.h"
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+extern void write_ioctl(unsigned int chanid, unsigned int param, unsigned int val, const __LineInfo* pLineInfo = NULL);
+
+extern std::vector panda_recv_loop_loose(std::unique_ptr& p, unsigned int min_num, unsigned long timeout_ms = 100);
+
+extern std::vector panda_recv_loop(std::unique_ptr& p, unsigned int num_expected, unsigned long timeout_ms = 100);
+
+extern void check_panda_can_msg(panda::PANDA_CAN_MSG& msgin, uint8_t bus, unsigned long addr, bool addr_29b,
+ bool is_receipt, std::string dat, const __LineInfo* pLineInfo = NULL);
+
+extern unsigned long J2534_start_periodic_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize,
+ unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, const __LineInfo * pLineInfo);
+
+extern unsigned long J2534_start_periodic_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize,
+ unsigned long ExtraDataIndex, const char* Data, unsigned long TimeInterval, unsigned long* msgID, const __LineInfo* pLineInfo = NULL);
+
+extern void J2534_send_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags,
+ unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo = NULL);
+
+extern long J2534_send_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags,
+ unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data);
+
+extern std::vector j2534_recv_loop_loose(unsigned int chanid, unsigned int min_num, unsigned long timeout_ms = 100);
+
+extern std::vector j2534_recv_loop(unsigned int chanid, unsigned int num_expected, unsigned long timeout_ms = 100);
+
+extern void check_J2534_can_msg(PASSTHRU_MSG& msgin, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags,
+ unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo = NULL);
+
+extern unsigned long J2534_set_PASS_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx,
+ unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo = NULL);
+
+extern unsigned long J2534_set_BLOCK_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx,
+ unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo = NULL);
+
+extern unsigned long J2534_set_flowctrl_filter(unsigned long chanid, unsigned long tx,
+ unsigned long len, char* mask, char* pattern, char* flow, const __LineInfo* pLineInfo = NULL);
+
+extern std::unique_ptr getPanda(unsigned long kbaud = 500, BOOL loopback = FALSE);
+
+extern std::vector checked_panda_send(std::unique_ptr& p, uint32_t addr, bool is_29b,
+ char* msg, uint8_t len, unsigned int num_expected=0, const __LineInfo* pLineInfo = NULL, unsigned long timeout_ms = 100);
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/Timer.cpp b/panda/drivers/windows/pandaJ2534DLL Test/Timer.cpp
new file mode 100644
index 0000000000..33d029e844
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/Timer.cpp
@@ -0,0 +1,21 @@
+#include "stdafx.h"
+#include "Timer.h"
+
+
+Timer::Timer()
+{
+ reset();
+}
+
+// gets the time elapsed from construction.
+unsigned long long /*milliseconds*/ Timer::getTimePassed(){
+ // get the new time
+ auto end = std::chrono::time_point_cast(clock::now());
+
+ // return the difference of the times
+ return (end - start).count();
+}
+
+void Timer::reset() {
+ start = std::chrono::time_point_cast(clock::now());
+}
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/Timer.h b/panda/drivers/windows/pandaJ2534DLL Test/Timer.h
new file mode 100644
index 0000000000..cbf5579a5c
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/Timer.h
@@ -0,0 +1,20 @@
+#pragma once
+#include
+
+//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::milliseconds >;
+public:
+ Timer();
+
+ // gets the time elapsed from construction.
+ unsigned long long /*milliseconds*/ getTimePassed();
+
+ void reset();
+
+private:
+ time_point_type start;
+};
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp b/panda/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp
new file mode 100644
index 0000000000..774b8ed31c
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp
@@ -0,0 +1,1602 @@
+#include "stdafx.h"
+#include "Loader4.h"
+#include "pandaJ2534DLL/J2534_v0404.h"
+#include "panda/panda.h"
+#include "Timer.h"
+#include "ECUsim DLL\ECUsim.h"
+#include "TestHelpers.h"
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace pandaJ2534DLLTest
+{
+ TEST_CLASS(J2534DLLInitialization)
+ {
+ public:
+
+ TEST_CLASS_CLEANUP(deinit) {
+ UnloadJ2534Dll();
+ }
+
+ TEST_METHOD(J2534_Driver_Init)
+ {
+ long err = LoadJ2534Dll("pandaJ2534_0404_32.dll");
+ Assert::IsTrue(err == 0, _T("Library failed to load properly. Check the export names and library location."));
+ }
+
+ };
+
+ TEST_CLASS(J2534DeviceInitialization)
+ {
+ public:
+
+ TEST_METHOD_INITIALIZE(init) {
+ LoadJ2534Dll("pandaJ2534_0404_32.dll");
+ }
+
+ TEST_METHOD_CLEANUP(deinit) {
+ if (didopen) {
+ PassThruClose(devid);
+ didopen = FALSE;
+ }
+ UnloadJ2534Dll();
+ }
+
+ TEST_METHOD(J2534_Device_OpenDevice__Empty)
+ {
+ Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_Device_OpenDevice__J2534_2)
+ {
+ Assert::AreEqual(STATUS_NOERROR, open_dev("J2534-2:"), _T("Failed to open device."), LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_Device_OpenDevice__SN)
+ {
+ auto pandas_available = panda::Panda::listAvailablePandas();
+ Assert::IsTrue(pandas_available.size() > 0, _T("No pandas detected."));
+
+ Assert::AreEqual(STATUS_NOERROR, open_dev(pandas_available[0].c_str()), _T("Failed to open device."), LINE_INFO());
+
+ auto pandas_available_2 = panda::Panda::listAvailablePandas();
+ for (auto panda_sn : pandas_available_2)
+ Assert::AreNotEqual(panda_sn, pandas_available[0]);
+ }
+
+ TEST_METHOD(J2534_Device_CloseDevice)
+ {
+ Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO());
+ Assert::AreEqual(STATUS_NOERROR, close_dev(devid), _T("Failed to close device."), LINE_INFO());
+ Assert::AreEqual(ERR_INVALID_DEVICE_ID, PassThruClose(devid), _T("The 2nd close should have failed with ERR_INVALID_DEVICE_ID."), LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_Device_ConnectDisconnect)
+ {
+ unsigned long chanid;
+ Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO());
+ Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, CAN, 0, 500000, &chanid), _T("Failed to open channel."), LINE_INFO());
+
+ Assert::AreEqual(STATUS_NOERROR, PassThruDisconnect(chanid), _T("Failed to close channel."), LINE_INFO());
+ Assert::AreEqual(ERR_INVALID_CHANNEL_ID, PassThruDisconnect(chanid), _T("The 2nd disconnect should have failed with ERR_INVALID_CHANNEL_ID."), LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_Device_ConnectInvalidProtocol)
+ {
+ unsigned long chanid;
+ Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO());
+ Assert::AreEqual(ERR_INVALID_PROTOCOL_ID, PassThruConnect(devid, 999, 0, 500000, &chanid),
+ _T("Did not report ERR_INVALID_PROTOCOL_ID."), LINE_INFO());
+ Assert::AreEqual(ERR_INVALID_CHANNEL_ID, PassThruDisconnect(chanid), _T("The channel should not have been created."), LINE_INFO());
+ }
+
+ bool didopen = FALSE;
+ unsigned long devid;
+
+ unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) {
+ unsigned int res = PassThruOpen((void*)name, &devid);
+ if (res == STATUS_NOERROR) didopen = TRUE;
+ return res;
+ }
+
+ unsigned long close_dev(unsigned long devid) {
+ unsigned long res = PassThruClose(devid);
+ if (res == STATUS_NOERROR) didopen = FALSE;
+ return res;
+ }
+
+ };
+
+ TEST_CLASS(J2534DeviceCAN)
+ {
+ public:
+
+ TEST_METHOD_INITIALIZE(init) {
+ LoadJ2534Dll("pandaJ2534_0404_32.dll");
+ }
+
+ TEST_METHOD_CLEANUP(deinit) {
+ if (didopen) {
+ PassThruClose(devid);
+ didopen = FALSE;
+ }
+ UnloadJ2534Dll();
+ }
+
+ //Test that the BAUD rate of a CAN connection can be changed.
+ TEST_METHOD(J2534_CAN_SetBaud)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK
+ auto p = getPanda(250);
+
+ J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ panda_recv_loop(p, 0);
+
+ write_ioctl(chanid, DATA_RATE, 250000, LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO());
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_CAN_11b_Tx)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO());
+
+ std::vector msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+
+ j2534_recv_loop(chanid, 0, 50); // Check no message is returned (since loopback is off)
+ }
+
+ TEST_METHOD(J2534_CAN_29b_Tx)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, CAN_29BIT_ID, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ Assert::AreEqual(ERR_INVALID_MSG, J2534_send_msg(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI"), _T("11b address should fail to tx."), LINE_INFO());
+ J2534_send_msg_checked(chanid, CAN, 0, CAN_29BIT_ID, 0, 6, 6, "\x0\x0\x3\xAB""YO", LINE_INFO());
+
+ std::vector msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, TRUE, FALSE, "YO", LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_CAN_11b29b_Tx)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, CAN_ID_BOTH, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO());
+ J2534_send_msg_checked(chanid, CAN, 0, CAN_29BIT_ID, 0, 6, 6, "\x0\x0\x3\xAB""YO", LINE_INFO());
+
+ std::vector msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x3AB, TRUE, FALSE, "YO", LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_CAN_TxEcho)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 9, 9, "\x0\x0\x3\xAB""HIDOG", LINE_INFO());
+
+ auto msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HIDOG", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 0);
+
+ /////////////////////////////////
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK
+
+ J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 7, 7, "\x0\x0\x3\xAB""SUP", LINE_INFO());
+
+ msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "SUP", LINE_INFO());
+
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 3 + 4, 0, "\x0\x0\x3\xAB""SUP", LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_CAN_RxAndPassAllFilters)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO());
+ auto p = getPanda(500);
+
+ p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1);
+ p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1);
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_CAN_RxAndLimitedPassFilter)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ J2534_set_PASS_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO());
+ auto p = getPanda(500);
+
+ p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1);
+ p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1);
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_CAN_RxAndPassBlockFilter)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO());
+ J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO());
+ auto p = getPanda(500);
+
+ p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1);
+ p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1);
+ p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1);
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2, 1000);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO());
+ }
+
+ //Check that the order of the pass and block filter do not matter
+ TEST_METHOD(J2534_CAN_RxAndFilterBlockPass)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO());
+ J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO());
+ auto p = getPanda(500);
+
+ p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1);
+ p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); // Should not pass filter
+ p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1);
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2, 2000);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO());
+ }
+
+ //Check that the order of the pass and block filter do not matter
+ TEST_METHOD(J2534_CAN_RxAndFilterRemoval)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ auto filterid0 = J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO());
+ auto filterid1 = J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO());
+
+ Assert::AreEqual(STATUS_NOERROR, PassThruStopMsgFilter(chanid, filterid0), _T("Failed to delete filter."), LINE_INFO());
+
+ auto p = getPanda(500);
+
+ p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1);
+ p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1);
+ p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1);
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 3, 1000);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[2], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO());
+ }
+
+ //Check that the order of the pass and block filter do not matter
+ TEST_METHOD(J2534_CAN_RxWithTimeout)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO());
+ auto p = getPanda(500);
+
+ PASSTHRU_MSG recvbuff;
+ unsigned long msgcount = 1;
+ unsigned int res = PassThruReadMsgs(chanid, &recvbuff, &msgcount, 100); // Here is where we test the timeout
+ Assert::AreEqual(ERR_BUFFER_EMPTY, res, _T("No message should be found"), LINE_INFO());
+ Assert::AreEqual(0, msgcount, _T("Received wrong number of messages."));
+
+ //TODO Test that the timings work right instead of just testing it doesn't crash.
+ }
+
+ TEST_METHOD(J2534_CAN_Baud)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 250000, LINE_INFO());
+ auto p = getPanda(250);
+
+ J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO());
+
+ std::vector msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ }
+
+ TEST_METHOD(J2534_CAN_PeriodicMessageStartStop)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO());
+
+ std::vector msg_recv = panda_recv_loop(p, 3, 250);
+ Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO());
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+
+ auto timediff_1_0 = msg_recv[1].recv_time - msg_recv[0].recv_time;
+ auto timediff_2_1 = msg_recv[2].recv_time - msg_recv[1].recv_time;
+
+ std::ostringstream stringStream1;
+ stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << std::endl;
+ Logger::WriteMessage(stringStream1.str().c_str());
+
+ Assert::IsTrue(timediff_1_0 > 90000);
+ Assert::IsTrue(timediff_1_0 < 110000);
+ Assert::IsTrue(timediff_2_1 > 90000);
+ Assert::IsTrue(timediff_2_1 < 110000);
+
+ msg_recv = panda_recv_loop(p, 0, 300);
+ }
+
+ TEST_METHOD(J2534_CAN_PeriodicMessageMultipleStartStop)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ auto msgid0 = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO());
+ auto msgid1 = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x1\x23""YO", 80, LINE_INFO());
+
+ std::vector msg_recv = panda_recv_loop(p, 9, 370);
+ Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid0), _T("Failed to delete filter."), LINE_INFO());
+ Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid1), _T("Failed to delete filter."), LINE_INFO());
+ //time diagram. 10 ms per character. * is send event. : is termination of periodic messages.
+ //*---------*---------*---------*-----:----* HI
+ //*-------*-------*-------*-------*---:----* YO
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO());
+ check_panda_can_msg(msg_recv[2], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO());
+ check_panda_can_msg(msg_recv[3], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[4], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO());
+ check_panda_can_msg(msg_recv[5], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[6], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO());
+ check_panda_can_msg(msg_recv[7], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[8], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO());
+
+ auto timediff_HI_3_0 = msg_recv[3].recv_time - msg_recv[0].recv_time;
+ auto timediff_HI_5_3 = msg_recv[5].recv_time - msg_recv[3].recv_time;
+ auto timediff_HI_7_5 = msg_recv[7].recv_time - msg_recv[5].recv_time;
+
+ auto timediff_YO_2_1 = msg_recv[2].recv_time - msg_recv[1].recv_time;
+ auto timediff_YO_4_2 = msg_recv[4].recv_time - msg_recv[2].recv_time;
+ auto timediff_YO_6_4 = msg_recv[6].recv_time - msg_recv[4].recv_time;
+ auto timediff_YO_8_6 = msg_recv[8].recv_time - msg_recv[6].recv_time;
+
+ std::ostringstream stringStreamHi;
+ stringStreamHi << "HiTimes: " << timediff_HI_3_0 << ", " << timediff_HI_5_3 << ", " << timediff_HI_7_5 << std::endl;
+ Logger::WriteMessage(stringStreamHi.str().c_str());
+
+ std::ostringstream stringStreamYo;
+ stringStreamYo << "HiTimes: " << timediff_YO_2_1 << ", " << timediff_YO_4_2 << ", " << timediff_YO_6_4 << ", " << timediff_YO_8_6 << std::endl;
+ Logger::WriteMessage(stringStreamYo.str().c_str());
+
+ Assert::IsTrue(timediff_HI_3_0 > 90000);
+ Assert::IsTrue(timediff_HI_3_0 < 110000);
+ Assert::IsTrue(timediff_HI_5_3 > 90000);
+ Assert::IsTrue(timediff_HI_5_3 < 110000);
+ Assert::IsTrue(timediff_HI_7_5 > 90000);
+ Assert::IsTrue(timediff_HI_7_5 < 110000);
+
+ Assert::IsTrue(timediff_YO_2_1 > 80000-10000);
+ Assert::IsTrue(timediff_YO_2_1 < 80000+1000);
+ Assert::IsTrue(timediff_YO_4_2 > 80000 - 10000);
+ Assert::IsTrue(timediff_YO_4_2 < 80000 + 10000);
+ Assert::IsTrue(timediff_YO_6_4 > 80000 - 10000);
+ Assert::IsTrue(timediff_YO_6_4 < 80000 + 10000);
+ Assert::IsTrue(timediff_YO_8_6 > 80000 - 10000);
+ Assert::IsTrue(timediff_YO_8_6 < 80000 + 10000);
+
+ msg_recv = panda_recv_loop(p, 0, 300);
+ }
+
+ TEST_METHOD(J2534_CAN_PeriodicMessageStartStop_Loopback)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK
+ auto p = getPanda(500);
+ auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO());
+
+ std::vector msg_recv = panda_recv_loop(p, 3, 250);
+ Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO());
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 3);
+ check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[2], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO());
+
+ auto timediff_1_0 = j2534_msg_recv[1].Timestamp - j2534_msg_recv[0].Timestamp;
+ auto timediff_2_1 = j2534_msg_recv[2].Timestamp - j2534_msg_recv[1].Timestamp;
+
+ std::ostringstream stringStream1;
+ stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << std::endl;
+ Logger::WriteMessage(stringStream1.str().c_str());
+
+ Assert::IsTrue(timediff_1_0 > 90000);
+ Assert::IsTrue(timediff_1_0 < 110000);
+ Assert::IsTrue(timediff_2_1 > 90000);
+ Assert::IsTrue(timediff_2_1 < 110000);
+
+ msg_recv = panda_recv_loop(p, 0, 300);
+ }
+
+ TEST_METHOD(J2534_CAN_PeriodicMessageWithTx)
+ {
+ auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO());
+ auto p = getPanda(500);
+ auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO());
+
+ J2534_send_msg(chanid, CAN, 0, 0, 0, 7, 0, "\x0\x0\x3\xAB""LOL");
+
+ std::vector msg_recv = panda_recv_loop(p, 4, 250);
+ Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO());
+ check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "LOL", LINE_INFO());//Staggered write inbetween multiple scheduled TXs
+ check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+ check_panda_can_msg(msg_recv[3], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO());
+
+ auto timediff_2_0 = msg_recv[2].recv_time - msg_recv[0].recv_time;
+ auto timediff_3_2 = msg_recv[3].recv_time - msg_recv[2].recv_time;
+
+ std::ostringstream stringStream1;
+ stringStream1 << "times1: " << timediff_2_0 << ", " << timediff_3_2 << std::endl;
+ Logger::WriteMessage(stringStream1.str().c_str());
+
+ Assert::IsTrue(timediff_2_0 > 90000);
+ Assert::IsTrue(timediff_2_0 < 110000);
+ Assert::IsTrue(timediff_3_2 > 90000);
+ Assert::IsTrue(timediff_3_2 < 110000);
+
+ msg_recv = panda_recv_loop(p, 0, 300);
+ }
+
+ TEST_METHOD(J2534_CAN_BaudInvalid)
+ {
+ unsigned long chanid;
+ Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO());
+ Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 6000000, &chanid), _T("Baudrate should have been invalid."), LINE_INFO());
+ Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 200, &chanid), _T("Baudrate should have been invalid."), LINE_INFO());
+ Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 250010, &chanid), _T("Baudrate should have been invalid."), LINE_INFO());
+ }
+
+ bool didopen = FALSE;
+ unsigned long devid;
+
+ unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) {
+ unsigned int res = PassThruOpen((void*)name, &devid);
+ if (res == STATUS_NOERROR) didopen = TRUE;
+ return res;
+ }
+
+ unsigned long J2534_open_and_connect(const char* name, unsigned long ProtocolID, unsigned long Flags, unsigned long bps, const __LineInfo* pLineInfo = NULL) {
+ unsigned long chanid;
+ Assert::AreEqual(STATUS_NOERROR, open_dev(name), _T("Failed to open device."), pLineInfo);
+ Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, ProtocolID, Flags, bps, &chanid), _T("Failed to open channel."), pLineInfo);
+ write_ioctl(chanid, LOOPBACK, FALSE, LINE_INFO()); // DISABLE J2534 ECHO/LOOPBACK
+ return chanid;
+ }
+
+ };
+
+ TEST_CLASS(J2534DeviceISO15765)
+ {
+ public:
+
+ TEST_METHOD_INITIALIZE(init) {
+ LoadJ2534Dll("pandaJ2534_0404_32.dll");
+ }
+
+ TEST_METHOD_CLEANUP(deinit) {
+ if (didopen) {
+ PassThruClose(devid);
+ didopen = FALSE;
+ }
+ UnloadJ2534Dll();
+ }
+
+ //Test that the BAUD rate of a ISO15765 connection can be changed.
+ TEST_METHOD(J2534_ISO15765_SetBaud)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK
+ auto p = getPanda(250);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, 0, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ panda_recv_loop(p, 0);
+
+ write_ioctl(chanid, DATA_RATE, 250000, LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, TX_INDICATION, 0, 4, 0, "\x0\x0\x3\xAB", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, TX_MSG_TYPE, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO());
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x3AB, FALSE, FALSE, "\x2""HI", LINE_INFO());
+ }
+
+ ///////////////////// Tests checking things don't send/receive /////////////////////
+
+ //Check tx PASSES and rx FAIL WITHOUT a filter. 29 bit. NO Filter. NoPadding. STD address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_PassTxFailRx_29b_NoFilter_NoPad_STD_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ //TX: works because all single frame writes should work (with or without a flow contorl filter)
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO());
+
+ //RX: Reads require a flow control filter, and should fail without one.
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ }
+
+ //Check tx and rx FAIL WITHOUT a filter. 29 bit. NO Filter. NoPadding. STD address. First Frame.
+ TEST_METHOD(J2534_ISO15765_FailTxRx_29b_NoFilter_NoPad_STD_FF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ auto p = getPanda(500);
+
+ //TX
+ Assert::AreEqual(ERR_NO_FLOW_CONTROL, J2534_send_msg(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 12, 0, "\x18\xda\xef\xf1\xA1\xB2\xC3\xD4\xE5\xF6\x09\x1A"),
+ _T("Should fail to tx without a filter."), LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ panda_recv_loop(p, 0);
+
+ //RX; Send full response and check didn't receive flow control from J2534 device
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x14\x49\x02\x01""1D4", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GP00R55", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""B123456", 8, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);//Check a full message is not accepted.
+ }
+
+ //Check tx PASSES and rx FAIL with a MISMATCHED filter. 29 bit. Mismatch Filter. NoPadding. STD address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_PassTxFailRx_29b_MismatchFilter_NoPad_STD_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //TX: works because all single frame writes should work (with or without a flow contorl filter)
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 6, 0, "\x18\xda\xe0\xf1""\x11\x22", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xe0\xf1", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAE0F1, TRUE, FALSE, "\x02""\x11\x22", LINE_INFO());
+
+ //RX. Send ISO15765 single frame to device. Address still doesn't match filter, so should not be received.
+ checked_panda_send(p, 0x18DAF1E0, TRUE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ }
+
+ //Check tx and rx FAIL with a MISMATCHED filter. 29 bit. Mismatch Filter. NoPadding. STD address. First Frame.
+ TEST_METHOD(J2534_ISO15765_FailTxRx_29b_MismatchFilter_NoPad_STD_FF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //TX
+ Assert::AreEqual(ERR_NO_FLOW_CONTROL, J2534_send_msg(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 12, 0, "\x18\xda\xe0\xf1""USELESS STUFF"),
+ _T("Should fail to tx without a filter."), LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ panda_recv_loop(p, 0);
+
+ //RX; Send a full response and check didn't receive flow control from J2534 device
+ checked_panda_send(p, 0x18DAF1E0, TRUE, "\x10\x14\x49\x02\x01""1D4", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1E0, TRUE, "\x21""GP00R55", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1E0, TRUE, "\x22""B123456", 8, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);//Check a full message is not accepted.
+ }
+
+ //Check tx FAILS with a MISMATCHED filter 29bit flag. 29 bit. Mismatch Filter. NoPadding. STD address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_FailTxRx_29b_MismatchFilterFlag29b_NoPad_STD_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO());
+ auto p = getPanda(500);
+
+ //TX
+ Assert::AreEqual(ERR_INVALID_MSG, J2534_send_msg(chanid, ISO15765, 0, 0, 0, 6, 0, "\x0/x0/x1/xcd\x01\x00"),
+ _T("mismatched address should fail to tx."), LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ panda_recv_loop(p, 0);
+
+ //RX. Send ISO15765 single frame to device. Address still doesn't match filter, so should not be received.
+ checked_panda_send(p, 0x1ab, FALSE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ }
+
+ ///////////////////// Tests checking things actually send/receive. Standard Addressing /////////////////////
+
+ //Check rx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_STD_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x07""ABCD123", 8, 0, LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 11, 11, "\x18\xda\xf1\xef""ABCD123", LINE_INFO());
+ }
+
+ //Check tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO());
+ }
+
+ //Check tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame. Loopback.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SF_LOOPBACK)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO());
+ }
+
+ //Check rx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_STD_FFCF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the rest of the message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO());
+
+ //Check J2534 constructed the whole message
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""nineteen bytes here", LINE_INFO());
+ }
+
+ //Check multi frame tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_FFCF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 14, 0, "\x18\xda\xef\xf1""\xAA\xBB\xCC\xDD\xEE\xFF\x11\x22\x33\x44", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 0); // No TxDone msg until after the final tx frame is sent
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x0A""\xAA\xBB\xCC\xDD\xEE\xFF", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""\x11\x22\x33\x44", LINE_INFO());
+
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ }
+
+ //Check rx passes with filter. 11 bit. Good Filter. NoPadding. STD address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessRx_11b_Filter_NoPad_STD_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, 0, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO());
+ auto p = getPanda(500);
+
+ checked_panda_send(p, 0x1ab, FALSE, "\x07""ABCD123", 8, 0, LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, 0, 0, 11, 11, "\x0\x0\x1\xab""ABCD123", LINE_INFO());
+ }
+
+ //Check tx passes with filter. 11 bit. Good Filter. NoPadding. STD address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_11b_Filter_NoPad_STD_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, 0, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, 0, 0, 11, 0, "\x0\x0\x1\xcd""TX_TEST", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, TX_INDICATION, 0, 4, 0, "\x0\x0\x1\xcd", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x1CD, FALSE, FALSE, "\x07""TX_TEST", LINE_INFO());
+ }
+
+ //Check tx passes with filter multiple times. 29 bit. Good Filter. NoPadding. STD address. Multiple Single Frames.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MultipleSF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO());
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 4);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[2], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[3], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO());
+ }
+
+ //Check that receiver's flow control block size requests are respected. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with multiple flow control.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLBlockSize)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO());
+
+ // [flow_status, block_size, st_min]
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x00", 3, 2, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 3, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ }
+
+ //Check that receiver's flow control separation time requests are respected. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with multiple flow control.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLSTMinMultiFc)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO());
+
+ // [flow_status, block_size, st_min]
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x03\x0A", 3, 3, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+ auto timediff0_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time;
+ auto timediff0_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time;
+
+ std::ostringstream stringStream0;
+ stringStream0 << "times0: " << timediff0_1_0 << ", " << timediff0_2_1 << std::endl;
+ Logger::WriteMessage(stringStream0.str().c_str());
+
+ Assert::IsTrue(timediff0_1_0 > 10000);
+ Assert::IsTrue(timediff0_1_0 < 32000);//Flexible, but trying to make sure things don't just all lag for a second or something
+ Assert::IsTrue(timediff0_2_1 > 10000);
+ Assert::IsTrue(timediff0_2_1 < 32000);
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x04\x20", 3, 4, LINE_INFO(), 500);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO());
+ auto timediff1_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time;
+ auto timediff1_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time;
+ auto timediff1_3_2 = panda_msg_recv[3].recv_time - panda_msg_recv[2].recv_time;
+
+ std::ostringstream stringStream1;
+ stringStream1 << "times1: " << timediff1_1_0 << ", " << timediff1_2_1 << ", " << timediff1_3_2 << std::endl;
+ Logger::WriteMessage(stringStream1.str().c_str());
+
+ Assert::IsTrue(timediff1_1_0 > 32000);
+ Assert::IsTrue(timediff1_2_1 > 32000);
+ Assert::IsTrue(timediff1_3_2 > 32000);
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ }
+
+ //Check that receiver's flow control separation time requests are respected 2. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with one flow control.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLSTMinSingleFc)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO());
+
+ // [flow_status, block_size, st_min]
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x07\x0A", 3, 7, LINE_INFO(), 500);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[6], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO());
+
+ auto timediff_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time;
+ auto timediff_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time;
+ auto timediff_3_2 = panda_msg_recv[3].recv_time - panda_msg_recv[2].recv_time;
+ auto timediff_4_3 = panda_msg_recv[4].recv_time - panda_msg_recv[3].recv_time;
+ auto timediff_5_4 = panda_msg_recv[5].recv_time - panda_msg_recv[4].recv_time;
+ auto timediff_6_5 = panda_msg_recv[6].recv_time - panda_msg_recv[5].recv_time;
+
+ std::ostringstream stringStream1;
+ stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << ", " << timediff_3_2 <<
+ ", " << timediff_4_3 << ", " << timediff_5_4 << ", " << timediff_6_5 << std::endl;
+ Logger::WriteMessage(stringStream1.str().c_str());
+
+ Assert::IsTrue(timediff_1_0 > 10000);
+ Assert::IsTrue(timediff_2_1 > 10000);
+ Assert::IsTrue(timediff_3_2 > 10000);
+ Assert::IsTrue(timediff_4_3 > 10000);
+ Assert::IsTrue(timediff_5_4 > 10000);
+ Assert::IsTrue(timediff_6_5 > 10000);
+ }
+
+ //Check that tx works for messages with more than 16 frames. 29 bit. Good Filter. NoPadding. STD address. Large multiframe message.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_LotsOfFrames)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 125, 0,
+ "\x18\xda\xef\xf1"
+ "AABBCC""DDEEFFG""GHHIIJJ""KKLLMMN""NOOPPQQ""RRSSTTU""UVVWWXX""YYZZ112""2334455""6677889"
+ "900abcd""efghijk""lmnopqr""stuvwxy""z!@#$%^""&*()_+-""=`~ABCD""EFGHIJK", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x7D""AABBCC", LINE_INFO());
+
+ // [flow_status, block_size, st_min]
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 17, LINE_INFO(), 1000);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[6], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ112", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[7], 0, 0x18DAEFF1, TRUE, FALSE, "\x28""2334455", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[8], 0, 0x18DAEFF1, TRUE, FALSE, "\x29""6677889", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[9], 0, 0x18DAEFF1, TRUE, FALSE, "\x2A""900abcd", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[10], 0, 0x18DAEFF1, TRUE, FALSE, "\x2B""efghijk", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[11], 0, 0x18DAEFF1, TRUE, FALSE, "\x2C""lmnopqr", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[12], 0, 0x18DAEFF1, TRUE, FALSE, "\x2D""stuvwxy", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[13], 0, 0x18DAEFF1, TRUE, FALSE, "\x2E""z!@#$%^", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[14], 0, 0x18DAEFF1, TRUE, FALSE, "\x2F""&*()_+-", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[15], 0, 0x18DAEFF1, TRUE, FALSE, "\x20""=`~ABCD", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[16], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""EFGHIJK", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ }
+
+ //Check tx passes with filter multiple times. 29 bit. Good Filter. NoPadding. STD address. Multiple Single Frames.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MultipleMFSF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO());
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 4, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""ata bec", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""ause I ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""can", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 4);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[2], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[3], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+ }
+
+ //Check tx passes after message timeout. 29 bit. Good Filter. NoPadding. STD address. Multiple Frame timeout then Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFTimeoutSFSuccess)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO());
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 2, 1000);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); //First Frame. Not replying so it needs to time out.
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); //Reply to the next message.
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+ }
+
+ //Check tx passes after mid-message timeout. 29 bit. Good Filter. NoPadding. STD address. Multiple Frame mid-timeout then Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFMidTimeoutSFSuccess)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO());
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); //First Frame. Not replying so it needs to time out.
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 2, LINE_INFO(), 1000);//Start a conversation
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""ata bec", LINE_INFO());//Check passthru device sent more data, but don't reply to it
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); //Reply to the next message.
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO());
+ }
+
+ //Check slow tx passes without hitting FC timeout. 29 bit. Good Filter. NoPadding. STD address. Long STmin, catches if FC timeout applies before needed.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SLOWMF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x7F", 3, 6, LINE_INFO(), 3000);//Start a conversation... but slow. FC timeout is 250 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());//Check this convo doesn't trigger that timeout.
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue.
+ check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+ }
+
+ //Check MF tx can be sent along side of a periodic message. 29 bit. Good Filter. NoPadding. STD address. Long STmin, checks that MF tx and periodic TX don't break each other.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SLOWMF_WithPeriodicMsg)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ //Timing diagram of this test.
+ //* is a periodic msg transfer; F is first frame, L is Flow control, C is Consecutive Frame.
+ // *~~~~~~~*~~~~~~~*~~~~~~~* (The alignment here is unimportant. The exact order is not checked.
+ //F C----C----C----C----C----C (100 ms between Cs)
+ // L
+
+ auto msgid = J2534_start_periodic_msg_checked(chanid, ISO15765, CAN_29BIT_ID, 6, 0, "\x18\xda\xef\xf1""HI", 130, LINE_INFO());
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x02""HI", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO());
+
+ Assert::IsTrue(p->can_send(0x18DAF1EF, TRUE, (const uint8_t*)"\x30\x06\x64", 3, panda::PANDA_CAN1), _T("Panda send says it failed."), LINE_INFO());
+
+ Timer t_permsg = Timer();
+ Timer t_MFmsg = Timer();
+ unsigned int MFframesReceived = 0;
+ unsigned int PeriodicMsgReceived = 1; //Because of the first panda_recv_loop above.
+ std::array const mfMsgExpectedParts{ "\x21""DDEEFFG", "\x22""GHHIIJJ", "\x23""KKLLMMN", "\x24""NOOPPQQ", "\x25""RRSSTTU", "\x26""UVVWWXX" };
+
+ while (TRUE) {
+ std::vectormsg_recv = p->can_recv();
+ for (auto msg : msg_recv) {
+ if (msg.is_receipt) continue;
+ if ((msg.dat[0] & 0xf0) == 0x20) {
+ Assert::AreEqual(mfMsgExpectedParts[MFframesReceived], std::string((const char*)msg.dat, msg.len), _T("Got wrong part of MF msg."), LINE_INFO());
+ MFframesReceived++;
+ t_MFmsg.reset();
+ } else if (std::string((const char*)msg.dat, msg.len) == "\x02HI") {
+ PeriodicMsgReceived++;
+ t_permsg.reset();
+ } else {
+ Assert::IsTrue(FALSE, _T("Got impossible message. Something is very wrong. Check other tests."), LINE_INFO());
+ }
+ }
+
+ if (MFframesReceived >= 6) break;
+ Assert::IsTrue(300 > t_permsg.getTimePassed(), _T("Timed out waiting for periodic msessage frame."), LINE_INFO());
+ Assert::IsTrue(300 > t_MFmsg.getTimePassed(), _T("Timed out waiting for multiframe msessage frame."), LINE_INFO());
+
+ if (msg_recv.size() == 0)
+ Sleep(10);
+ }
+
+ //Stop the periodic message and grab any data it may have sent since we last checked.
+ //Not sure if this is needed.
+ Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO());
+ auto extra_panda_msg = panda_recv_loop_loose(p, 0, 200);
+ for (auto msg : extra_panda_msg) {
+ if (std::string((const char*)msg.dat, msg.len) == "\x02HI") {
+ PeriodicMsgReceived++;
+ Logger::WriteMessage("Received extra periodic message.");
+ } else {
+ Assert::IsTrue(FALSE, _T("Got impossible message. Something is very wrong. Check other tests."), LINE_INFO());
+ }
+ }
+
+ Assert::IsTrue(PeriodicMsgReceived > 3, _T("Did not receive enough periodic messages. Likely canceled or delayed."), LINE_INFO());
+
+ std::ostringstream stringStream;
+ stringStream << "PeriodicMsgReceived = " << PeriodicMsgReceived << std::endl;
+ Logger::WriteMessage(stringStream.str().c_str());
+
+ unsigned int periodicTxIndicationCount = 0;
+ unsigned int TxIndicationCount = 0;
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2 + (PeriodicMsgReceived * 2));
+ for (int i = 0; i < PeriodicMsgReceived + 1; i++) {
+ check_J2534_can_msg(j2534_msg_recv[(i * 2) + 0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ switch (j2534_msg_recv[(i * 2) + 1].DataSize) {
+ case 4 + 2:
+ check_J2534_can_msg(j2534_msg_recv[(i * 2) + 1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 2, 0, "\x18\xda\xef\xf1""HI", LINE_INFO());
+ break;
+ case 4 + 48:
+ check_J2534_can_msg(j2534_msg_recv[(i * 2) + 1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+ break;
+ default:
+ Assert::IsTrue(FALSE, _T("Got unexpected data!"), LINE_INFO());
+ }
+ }
+
+ Assert::AreNotEqual(PeriodicMsgReceived, periodicTxIndicationCount, _T("Wrong number of periodic msgs reported by passthru device."), LINE_INFO());
+ Assert::AreNotEqual(1, TxIndicationCount, _T("Wrong number of TX msgs reported by passthru device."), LINE_INFO());
+ }
+
+ ///////////////////// Tests checking things break or recover during send/receive /////////////////////
+
+ //Check rx FAILS when frame is dropped. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_FailRx_29b_Filter_NoPad_STD_FFCF_DropFrame)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the rest of the message
+ //Missing the 2nd frame "\x21""en byte"
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO());
+
+ //Check J2534 DOES NOT construct the incomplete message
+ j2534_recv_loop(chanid, 0);
+ }
+
+ //Check rx ignores frames that arrive out of order. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_FFCF_FrameNumSkip)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the rest of the message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""XXXXXX", 7, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x23""ZZZZZZ", 7, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO());
+
+ //Check J2534 constructa the complete message from the correctly numbered frames.
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""ABCDEFGHIJKLMNOPQRS", LINE_INFO());
+ }
+
+ //Check Single Frame rx RESETS ongoing multiframe transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_SFRxResetsMFRx)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the next part of the message multi message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO());
+
+ //ABORTING MESSAGE
+ //Send a NEW single frame message and check the J2534 device gets it (but not the original message it was receiving.
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x06""ABC123", 7, 0, LINE_INFO());
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 10, 10, "\x18\xda\xf1\xef""ABC123", LINE_INFO());
+
+ //Resume sending the old message, and check th eJ2534 device didn't get a message.
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ }
+
+ //The documentation says that a s ingle channel can not send and receive messages trhough a
+ //single conversation (flow control filter) at the same time. However, the required behavior
+ //when this is detected is not described. This test was my best understanding of how it was
+ //wanted, but I no longer see the point. For now I am disabling it.
+ /*//Check Single Frame tx RESETS ongoing multiframe rx transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_SFTxResetsMFRx)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the next part of the message multi message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+
+ //ABORTING MESSAGE
+ //Send a NEW single frame message and check the J2534 device gets it (but not the original message it was receiving.
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO());
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+
+ panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO());
+ ///////////////////////////
+
+ //Resume sending the old message, and check th eJ2534 device didn't get a message.
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ }*/
+
+ //TODO check rx is cleared by tx (multi). Or not.... read above note.
+
+ //Check multiframe rx RESETS ongoing multiframe transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_FFCF_MFRxResetsMFRx)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the next part of the multi message A
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO());
+
+ //ABORTING MESSAGE A
+ //Send a NEW multi frame message (B) and check the J2534 device gets it (but not the original message it was receiving.
+ //Send first frame, then check we get a flow control frame
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the rest of the message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO());
+
+ //Check J2534 constructed the whole message
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""nineteen bytes here", LINE_INFO());
+ //////////////////////// End sending B
+
+ //Resume sending the multi message A, and check th eJ2534 device didn't get a message.
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+ }
+
+ //Check rx fails gracefully if final CF of MF rx is too short. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_FailRxFinalCFTooShort_29b_Filter_NoPad_STD_FFCF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO());
+
+ //Send the rest of the message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s her", 6, 0, LINE_INFO()); //The transaction should reset here because more data could have been sent.
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x23""e", 2, 0, LINE_INFO());
+
+ //Check J2534 constructed the whole message
+ j2534_msg_recv = j2534_recv_loop(chanid, 0);
+ }
+
+ //Check rx fails gracefully if first frame is too short. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_FailRxFFTooShort_29b_Filter_NoPad_STD_FFCF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame. The transaction should reset immediately because more data could have been sent in this frame.
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninet", 7, 0, LINE_INFO());
+ j2534_recv_loop(chanid, 0);
+
+ //Send the rest of the message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""een byt", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""es here", 8, 0, LINE_INFO());
+
+ //Check J2534 constructed the whole message
+ j2534_recv_loop(chanid, 0);
+ }
+
+ //Check MF tx will stop upon receiving a flow control ABORT. 29 bit. Good Filter. NoPadding. STD address. Large STmin, then abort, then send SF.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD__MF_FCAbort_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x20", 3, 2, LINE_INFO());//Start a conversation. FC timeout is 32 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x32\x0\x0", 3, 0, LINE_INFO());//Abort the conversation
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO());
+ }
+
+ //Check MF tx will stop upon receiving a flow control ABORT during valid blocksize. 29 bit. Good Filter. NoPadding. STD address. Large STmin, then mid tx abort, then send SF.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD__MF_FCMixTXAbort_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x7F", 3, 1, LINE_INFO(), 200);//Start a conversation. FC timeout is 127 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x32\x0\x0", 3, 0, LINE_INFO());//Abort the conversation
+ panda_recv_loop(p, 0, 200);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO());
+ }
+
+ //Check slow tx can be stalled past timeout with CF WAIT frames. 29 bit. Good Filter. NoPadding. STD address. MF tx that would timeout without WAIT frames.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFWithWaitFrames)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ write_ioctl(chanid, ISO15765_WFT_MAX, 10, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x40", 3, 2, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 4, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue.
+ check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+ }
+
+ //Check slow tx can be stalled past timeout with CF WAIT frames during normal TX. 29 bit. Good Filter. NoPadding. STD address. Stalling working MF tx.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFWithMidTXWaitFrames)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ write_ioctl(chanid, ISO15765_WFT_MAX, 10, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x64", 3, 2, LINE_INFO(), 120);//Start a conversation. STmin 100. FC timeout is 250 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 4, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue.
+ check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+ }
+
+ //Check that too many WAIT frames will abort the transfer. 29 bit. Good Filter. NoPadding. STD address. Too much stalling causes abort.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFTooManyWaitFrames)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO());
+ write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO());
+ write_ioctl(chanid, ISO15765_WFT_MAX, 2, LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x64", 3, 2, LINE_INFO(), 120);//Start a conversation. STmin 100. FC timeout is 250 ms.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x64", 3, 2, LINE_INFO(), 120);//Resume the conversation.
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO());
+
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation.
+ Sleep(100);
+
+ //Should not resume because the conversation has been delayed too long.
+ panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 0, LINE_INFO(), 300);
+
+ //Send a SF message to check the tubes are not clogged.
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 2);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO());
+ check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO());
+ }
+
+ ///////////////////// Tests checking things actually send/receive. Ext 5 byte Addressing /////////////////////
+
+ //Check rx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_EXT_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO());
+ auto p = getPanda(500);
+
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x06""ABC123", 8, 0, LINE_INFO());
+
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 11, 11, "\x18\xda\xf1\xef\x13""ABC123", LINE_INFO());
+ }
+
+ //Check tx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Single Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_EXT_SF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO());
+ auto p = getPanda(500);
+
+ J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 11, 0, "\x18\xda\xef\xf1\x13""DERP!!", LINE_INFO());
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xef\xf1\x13", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x13""\x06""DERP!!", LINE_INFO());
+ }
+
+ //Check rx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_EXT_FFCF)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ Assert::IsTrue(p->can_send(0x18DAF1EF, TRUE, (const uint8_t*)"\x13""\x10\x13""ninet", 8, panda::PANDA_CAN1), _T("Panda send says it failed."), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xf1\xef\x13", LINE_INFO());
+
+ auto panda_msg_recv = panda_recv_loop(p, 2);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAF1EF, TRUE, TRUE, std::string("\x13""\x10\x13""ninet", 8), LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x13""\x30\x00\x00", 4), LINE_INFO());
+
+ //Send the rest of the message
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x21""een by", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x22""tes he", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x23""re", 8, 0, LINE_INFO());
+
+ //Check J2534 constructed the whole message
+ j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 5 + 0x13, 5 + 0x13, "\x18\xda\xf1\xef\x13""nineteen bytes here", LINE_INFO());
+ }
+
+ //Check tx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Multi Frame.
+ /*TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_EXT_FFCF)
+ { //TODO when TX works with flow control}*/
+
+ ///////////////////// Tests checking things break or recover during send/receive. Ext 5 byte Addressing /////////////////////
+
+ //Check rx FAILS when frame is dropped. 29 bit. Good Filter. NoPadding. STD address. Multi Frame.
+ TEST_METHOD(J2534_ISO15765_FailRx_29b_Filter_NoPad_EXT_FFCF_DropFrame)
+ {
+ auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO());
+ J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO());
+ auto p = getPanda(500);
+
+ //Send first frame, then check we get a flow control frame
+ auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13\x10\x13""ninet", 8, 1, LINE_INFO());
+ check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x13\x30\x00\x00", 4), LINE_INFO());
+
+ //Check first frame is registered with J2534
+ auto j2534_msg_recv = j2534_recv_loop(chanid, 1);
+ check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xf1\xef\x13", LINE_INFO());
+
+ //Send the rest of the message
+ //Missing the 2nd frame "\x13""\x21""een by"
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x22""tes he", 8, 0, LINE_INFO());
+ checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x23""re", 8, 0, LINE_INFO());
+
+ //Check J2534 DOES NOT construct the incomplete message
+ j2534_recv_loop(chanid, 0);
+ }
+
+ bool didopen = FALSE;
+ unsigned long devid;
+
+ unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) {
+ unsigned int res = PassThruOpen((void*)name, &devid);
+ if (res == STATUS_NOERROR) didopen = TRUE;
+ return res;
+ }
+
+ unsigned long J2534_open_and_connect(const char* name, unsigned long ProtocolID, unsigned long Flags, unsigned long bps, const __LineInfo* pLineInfo = NULL) {
+ unsigned long chanid;
+ Assert::AreEqual(STATUS_NOERROR, open_dev(name), _T("Failed to open device."), pLineInfo);
+ Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, ProtocolID, Flags, bps, &chanid), _T("Failed to open channel."), pLineInfo);
+ write_ioctl(chanid, LOOPBACK, FALSE, LINE_INFO()); // DISABLE J2534 ECHO/LOOPBACK
+ return chanid;
+ }
+
+ };
+}
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj b/panda/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj
new file mode 100644
index 0000000000..56923823ba
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj
@@ -0,0 +1,125 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {7912F978-B48C-4C5D-8BFD-5D1E22158E47}
+ Win32Proj
+ pandaJ2534DLLTest
+ 8.1
+ Tests
+
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+ false
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+ true
+ $(SolutionDir)$(Configuration)_$(PlatformShortName)\
+
+
+
+ Use
+ Level3
+ Disabled
+ $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir)
+ WIN32;_DEBUG;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir)
+ WIN32;NDEBUG;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ true
+ $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)
+ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+
+
+
+
+
+
+
+ {96e0e646-ee76-444d-9a77-a0cd7f781deb}
+
+
+ {a2bb18a5-f26b-48d6-bbb5-b83d64473c77}
+
+
+ {5528aefb-638d-49af-b9d4-965154e7d531}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters b/panda/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters
new file mode 100644
index 0000000000..476ce458d3
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters
@@ -0,0 +1,63 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp b/panda/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp
new file mode 100644
index 0000000000..65e549c50c
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp
@@ -0,0 +1,187 @@
+#include "stdafx.h"
+#include "panda/panda.h"
+#include "TestHelpers.h"
+
+#include
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+using namespace panda;
+
+namespace pandaTestNative
+{
+ TEST_CLASS(DeviceDiscovery)
+ {
+ public:
+
+ TEST_METHOD(Panda_DevDiscover_ListDevices)
+ {
+ auto pandas_available = Panda::listAvailablePandas();
+ Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found."));
+ for (auto sn : pandas_available) {
+ Assert::IsTrue(sn.size() == 24, _T("panda Serial Number not 24 characters long."));
+ }
+ }
+
+ TEST_METHOD(Panda_DevDiscover_OpenFirstDevice)
+ {
+ auto pandas_available = Panda::listAvailablePandas();
+ Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found."));
+
+ auto p1 = Panda::openPanda(pandas_available[0]);
+ Assert::IsFalse(p1 == nullptr, _T("Could not open panda."));
+ }
+
+ TEST_METHOD(Panda_DevDiscover_OpenDeviceNoName)
+ {
+ auto pandas_available = Panda::listAvailablePandas();
+ Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found."));
+
+ auto p1 = Panda::openPanda("");
+ Assert::IsFalse(p1 == nullptr, _T("Could not open panda."));
+ Assert::IsTrue(p1->get_usb_sn() == pandas_available[0], _T("Could not open panda."));
+ }
+
+ TEST_METHOD(Panda_DevDiscover_OpenDeviceUnavailable)
+ {
+ auto p1 = Panda::openPanda("ZZZZZZZZZZZZZZZZZZZZZZZZ");
+ Assert::IsTrue(p1 == nullptr, _T("Invalid sn still worked."));
+ }
+
+ TEST_METHOD(Panda_DevDiscover_WillNotOpenAlreadyOpenedDevice)
+ {
+ auto pandas_available = Panda::listAvailablePandas();
+ Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found."));
+
+ auto p1 = Panda::openPanda(pandas_available[0]);
+ Assert::IsFalse(p1 == nullptr, _T("Could not open panda."));
+
+ auto p2 = Panda::openPanda(pandas_available[0]);
+ Assert::IsTrue(p2 == nullptr, _T("Opened an already open panda."));
+ }
+
+ TEST_METHOD(Panda_DevDiscover_OpenedDeviceNotListed)
+ {
+ auto pandas_available = Panda::listAvailablePandas();
+ Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found."));
+
+ auto p1 = Panda::openPanda(pandas_available[0]);
+ Assert::IsFalse(p1 == nullptr, _T("Could not open panda."));
+
+ auto pandas_available2 = Panda::listAvailablePandas();
+ for (auto sn : pandas_available2) {
+ Assert::IsFalse(p1->get_usb_sn() == sn, _T("Opened panda appears in list of available pandas."));
+ }
+
+ }
+ };
+
+ TEST_CLASS(CANOperations)
+ {
+ public:
+
+ TEST_METHOD(Panda_CAN_Echo)
+ {
+ auto p0 = getPanda(500, TRUE);
+
+ uint32_t addr = 0xAA;
+ bool is_29b = FALSE;
+ uint8_t candata[8];
+
+ for (auto canbus : { PANDA_CAN1, PANDA_CAN2, PANDA_CAN3 }) {
+ uint8_t len = (rand() % 8) + 1;
+ for (size_t i = 0; i < len; i++)
+ candata[i] = rand() % 256;
+
+ p0->can_send(addr, is_29b, candata, len, canbus);
+ Sleep(10);
+
+ auto can_msgs = p0->can_recv();
+
+ Assert::AreEqual(2, can_msgs.size(), _T("Received the wrong number of CAN messages."), LINE_INFO());
+
+ for (auto msg : can_msgs) {
+ Assert::IsTrue(msg.addr == addr, _T("Wrong addr."));
+ Assert::IsTrue(msg.bus == canbus, _T("Wrong bus."));
+ Assert::IsTrue(msg.len == len, _T("Wrong len."));
+ Assert::AreEqual(memcmp(msg.dat, candata, msg.len), 0, _T("Received CAN data not equal"));
+ for (int i = msg.len; i < 8; i++)
+ Assert::IsTrue(msg.dat[i] == 0, _T("Received CAN data not trailed by 0s"));
+ }
+
+ Assert::IsTrue(can_msgs[0].is_receipt, _T("Didn't get receipt."));
+ Assert::IsFalse(can_msgs[1].is_receipt, _T("Didn't get echo."));
+ }
+ }
+
+ TEST_METHOD(Panda_CAN_ChangeBaud)
+ {
+ auto p0 = getPanda(250);
+ auto p1 = getPanda(500);
+
+ p0->can_send(0xAA, FALSE, (const uint8_t*)"\x1\x2\x3\x4\x5\x6\x7\x8", 8, panda::PANDA_CAN1);
+ panda_recv_loop(p0, 0);
+ panda_recv_loop(p1, 0);
+
+ p0->set_can_speed_kbps(panda::PANDA_CAN1, 500);
+
+ auto panda_msg_recv = panda_recv_loop(p0, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0xAA, FALSE, TRUE, "\x1\x2\x3\x4\x5\x6\x7\x8", LINE_INFO());
+ panda_msg_recv = panda_recv_loop(p1, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0xAA, FALSE, FALSE, "\x1\x2\x3\x4\x5\x6\x7\x8", LINE_INFO());
+
+ //////////////////
+
+ p0->set_can_speed_kbps(panda::PANDA_CAN1, 250);
+ p0->can_send(0xC4, FALSE, (const uint8_t*)"\xA\B\xC\xD\xE\xF\x10\x11", 8, panda::PANDA_CAN1);
+ panda_recv_loop(p0, 0);
+ panda_recv_loop(p1, 0);
+
+ p1->set_can_speed_kbps(panda::PANDA_CAN1, 250);
+
+ panda_msg_recv = panda_recv_loop(p0, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0xC4, FALSE, TRUE, "\xA\B\xC\xD\xE\xF\x10\x11", LINE_INFO());
+ panda_msg_recv = panda_recv_loop(p1, 1);
+ check_panda_can_msg(panda_msg_recv[0], 0, 0xC4, FALSE, FALSE, "\xA\B\xC\xD\xE\xF\x10\x11", LINE_INFO());
+ }
+
+ TEST_METHOD(Panda_CAN_ClearClears)
+ {
+ auto p0 = getPanda(500, TRUE);
+ p0->can_send(0xAA, FALSE, (const uint8_t*)"\x0\x1\x2\x3\x4\x5\x6\x7", 8, panda::PANDA_CAN1);
+ Sleep(100);
+ p0->can_clear(PANDA_CAN_RX);
+
+ auto can_msgs = p0->can_recv();
+ Assert::IsTrue(can_msgs.size() == 0, _T("Received messages after a clear."));
+ }
+ };
+
+ TEST_CLASS(SerialOperations)
+ {
+ public:
+
+ TEST_METHOD(Panda_LIN_Echo)
+ {
+ auto p0 = getPanda(500);
+
+ for (auto lin_port : { SERIAL_LIN1, SERIAL_LIN2 }) {
+ p0->serial_clear(lin_port);
+
+ for (int i = 0; i < 10; i++) {
+ uint8_t len = (rand() % LIN_MSG_MAX_LEN) + 1;
+ std::string lindata;
+ lindata.reserve(len);
+
+ for (size_t j = 0; j < len; j++)
+ lindata += (const char)(rand() % 256);
+
+ p0->serial_write(lin_port, lindata.c_str(), len);
+ Sleep(10);
+
+ auto retdata = p0->serial_read(lin_port);
+ Assert::AreEqual(retdata, lindata);
+ }
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/stdafx.cpp b/panda/drivers/windows/pandaJ2534DLL Test/stdafx.cpp
new file mode 100644
index 0000000000..84a1f0aaf3
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// pandaJ2534DLL Test.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/stdafx.h b/panda/drivers/windows/pandaJ2534DLL Test/stdafx.h
new file mode 100644
index 0000000000..1ac8bd8dcc
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/stdafx.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include
+#include
+
+// Headers for CppUnitTest
+#include "CppUnitTest.h"
+#include //Used for formatting in TestHelpers.cpp
+#include
+#include
diff --git a/panda/drivers/windows/pandaJ2534DLL Test/targetver.h b/panda/drivers/windows/pandaJ2534DLL Test/targetver.h
new file mode 100644
index 0000000000..87c0086de7
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL Test/targetver.h
@@ -0,0 +1,8 @@
+#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
diff --git a/panda/drivers/windows/pandaJ2534DLL/Action.h b/panda/drivers/windows/pandaJ2534DLL/Action.h
new file mode 100644
index 0000000000..e9721c2eda
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL/Action.h
@@ -0,0 +1,57 @@
+#pragma once
+#include
+
+#include "J2534Frame.h"
+
+class J2534Connection;
+
+/**
+An Action represents a unit of work that can be scheduled for execution at a later time.
+Actions are not guaranteed to be run at their specified time, but a best effort is made.
+An Action will never execute early, but can execute later depending on what is in the
+queus.
+Many different operations are based on this base class. Instead of making a thread,
+consider if the work can be offloaded to the Task Queue.
+*/
+class Action
+{
+public:
+ Action(
+ std::weak_ptr connection,
+ std::chrono::microseconds delay
+ ) : connection(connection), delay(delay) { };
+
+ Action(
+ std::weak_ptr connection
+ ) : connection(connection), delay(std::chrono::microseconds(0)) { };
+
+ //The function called by the task runner when this action is to be invoked.
+ virtual void execute() = 0;
+
+ //Reschedule this Action for now().
+ void scheduleImmediate() {
+ expire = std::chrono::steady_clock::now();
+ }
+
+ //Reschedule this Action relative to its last expiration time.
+ void scheduleDelay() {
+ expire += this->delay;
+ }
+
+ //Reschedule this action {delay} after now().
+ void scheduleImmediateDelay() {
+ expire = std::chrono::steady_clock::now() + this->delay;
+ }
+
+ //Reschedule this Action based on a specific base time.
+ void schedule(std::chrono::time_point starttine, BOOL adddelayed) {
+ this->expire = starttine;
+ if (adddelayed)
+ expire += this->delay;
+ }
+
+ std::weak_ptr connection;
+ std::chrono::microseconds delay;
+ //The timestamp at which point this Action is ready to be executed.
+ std::chrono::time_point expire;
+};
diff --git a/panda/drivers/windows/pandaJ2534DLL/J2534Connection.cpp b/panda/drivers/windows/pandaJ2534DLL/J2534Connection.cpp
new file mode 100644
index 0000000000..358158bcb4
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL/J2534Connection.cpp
@@ -0,0 +1,257 @@
+#include "stdafx.h"
+#include "J2534Connection.h"
+#include "Timer.h"
+
+J2534Connection::J2534Connection(
+ std::shared_ptr panda_dev,
+ unsigned long ProtocolID,
+ unsigned long Flags,
+ unsigned long BaudRate
+) : panda_dev(panda_dev), ProtocolID(ProtocolID), Flags(Flags), BaudRate(BaudRate), port(0) { }
+
+unsigned long J2534Connection::validateTxMsg(PASSTHRU_MSG* msg) {
+ if (msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen())
+ return ERR_INVALID_MSG;
+ return STATUS_NOERROR;
+}
+
+long J2534Connection::PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) {
+ //Timeout of 0 means return immediately. Non zero means WAIT for that time then return. Dafuk.
+ long err_code = STATUS_NOERROR;
+ Timer t = Timer();
+
+ unsigned long msgnum = 0;
+ while (msgnum < *pNumMsgs) {
+ if (Timeout > 0 && t.getTimePassed() >= Timeout) {
+ err_code = ERR_TIMEOUT;
+ break;
+ }
+
+ //Synchronized won't work where we have to break out of a loop
+ messageRxBuff_mutex.lock();
+ if (this->messageRxBuff.empty()) {
+ messageRxBuff_mutex.unlock();
+ if (Timeout == 0)
+ break;
+ continue;
+ }
+
+ auto msg_in = this->messageRxBuff.front();
+ this->messageRxBuff.pop();
+ messageRxBuff_mutex.unlock();
+
+ PASSTHRU_MSG *msg_out = &pMsg[msgnum++];
+ msg_out->ProtocolID = this->ProtocolID;
+ msg_out->DataSize = msg_in.Data.size();
+ memcpy(msg_out->Data, msg_in.Data.c_str(), msg_in.Data.size());
+ msg_out->Timestamp = msg_in.Timestamp;
+ msg_out->RxStatus = msg_in.RxStatus;
+ msg_out->ExtraDataIndex = msg_in.ExtraDataIndex;
+ msg_out->TxFlags = 0;
+ if (msgnum == *pNumMsgs) break;
+ }
+
+ if (msgnum == 0)
+ err_code = ERR_BUFFER_EMPTY;
+ *pNumMsgs = msgnum;
+ return err_code;
+}
+
+long J2534Connection::PassThruWriteMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) {
+ //There doesn't seem to be much reason to implement the timeout here.
+ for (int msgnum = 0; msgnum < *pNumMsgs; msgnum++) {
+ PASSTHRU_MSG* msg = &pMsg[msgnum];
+ if (msg->ProtocolID != this->ProtocolID) {
+ *pNumMsgs = msgnum;
+ return ERR_MSG_PROTOCOL_ID;
+ }
+
+ auto retcode = this->validateTxMsg(msg);
+ if (retcode != STATUS_NOERROR) {
+ *pNumMsgs = msgnum;
+ return retcode;
+ }
+
+ auto msgtx = this->parseMessageTx(*pMsg);
+ if (msgtx != nullptr) //Nullptr is supported for unimplemented connection types.
+ this->schedultMsgTx(std::dynamic_pointer_cast(msgtx));
+ }
+ return STATUS_NOERROR;
+}
+
+//The docs say that a device has to support 10 periodic messages, though more is ok.
+//It is easier to store them on the connection, so 10 per connection it is.
+long J2534Connection::PassThruStartPeriodicMsg(PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval) {
+ if (pMsg->DataSize < getMinMsgLen() || pMsg->DataSize > getMaxMsgSingleFrameLen()) return ERR_INVALID_MSG;
+ if (pMsg->ProtocolID != this->ProtocolID) return ERR_MSG_PROTOCOL_ID;
+ if (TimeInterval < 5 || TimeInterval > 65535) return ERR_INVALID_TIME_INTERVAL;
+
+ for (int i = 0; i < this->periodicMessages.size(); i++) {
+ if (periodicMessages[i] != nullptr) continue;
+
+ *pMsgID = i;
+ auto msgtx = this->parseMessageTx(*pMsg);
+ if (msgtx != nullptr) {
+ periodicMessages[i] = std::make_shared(std::chrono::microseconds(TimeInterval*1000), msgtx);
+ periodicMessages[i]->scheduleImmediate();
+ if (auto panda_dev = this->getPandaDev()) {
+ panda_dev->insertActionIntoTaskList(periodicMessages[i]);
+ }
+ }
+ return STATUS_NOERROR;
+ }
+ return ERR_EXCEEDED_LIMIT;
+}
+
+long J2534Connection::PassThruStopPeriodicMsg(unsigned long MsgID) {
+ if (MsgID >= this->periodicMessages.size() || this->periodicMessages[MsgID] == nullptr)
+ return ERR_INVALID_MSG_ID;
+ this->periodicMessages[MsgID]->cancel();
+ this->periodicMessages[MsgID] = nullptr;
+ return STATUS_NOERROR;
+}
+
+long J2534Connection::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg,
+ PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) {
+ for (int i = 0; i < this->filters.size(); i++) {
+ if (filters[i] == nullptr) {
+ try {
+ auto newfilter = std::make_shared(this, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg);
+ for (int check_idx = 0; check_idx < filters.size(); check_idx++) {
+ if (filters[check_idx] == nullptr) continue;
+ if (filters[check_idx] == newfilter) {
+ filters[i] = nullptr;
+ return ERR_NOT_UNIQUE;
+ }
+ }
+ *pFilterID = i;
+ filters[i] = newfilter;
+ return STATUS_NOERROR;
+ } catch (int e) {
+ return e;
+ }
+ }
+ }
+ return ERR_EXCEEDED_LIMIT;
+}
+
+long J2534Connection::PassThruStopMsgFilter(unsigned long FilterID) {
+ if (FilterID >= this->filters.size() || this->filters[FilterID] == nullptr)
+ return ERR_INVALID_FILTER_ID;
+ this->filters[FilterID] = nullptr;
+ return STATUS_NOERROR;
+}
+
+long J2534Connection::PassThruIoctl(unsigned long IoctlID, void *pInput, void *pOutput) {
+ return STATUS_NOERROR;
+}
+
+long J2534Connection::init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput) { return STATUS_NOERROR; }
+long J2534Connection::initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput) { return STATUS_NOERROR; }
+long J2534Connection::clearTXBuff() { return STATUS_NOERROR; }
+long J2534Connection::clearRXBuff() {
+ synchronized(messageRxBuff_mutex) {
+ this->messageRxBuff = {};
+ }
+ return STATUS_NOERROR;
+}
+long J2534Connection::clearPeriodicMsgs() { return STATUS_NOERROR; }
+long J2534Connection::clearMsgFilters() {
+ for (auto& filter : this->filters) filter = nullptr;
+ return STATUS_NOERROR;
+}
+
+void J2534Connection::setBaud(unsigned long baud) {
+ this->BaudRate = baud;
+}
+
+void J2534Connection::schedultMsgTx(std::shared_ptr msgout) {
+ if (auto panda_ps = this->panda_dev.lock()) {
+ synchronized(staged_writes_lock) {
+ this->txbuff.push(msgout);
+ panda_ps->registerConnectionTx(shared_from_this());
+ }
+ }
+}
+
+void J2534Connection::rescheduleExistingTxMsgs() {
+ if (auto panda_ps = this->panda_dev.lock()) {
+ synchronized(staged_writes_lock) {
+ panda_ps->unstallConnectionTx(shared_from_this());
+ }
+ }
+}
+
+//Works well as long as the protocol doesn't support flow control.
+void J2534Connection::processMessage(const J2534Frame& msg) {
+ FILTER_RESULT filter_res = FILTER_RESULT_NEUTRAL;
+
+ for (auto filter : this->filters) {
+ if (filter == nullptr) continue;
+ FILTER_RESULT current_check_res = filter->check(msg);
+ if (current_check_res == FILTER_RESULT_BLOCK) return;
+ if (current_check_res == FILTER_RESULT_PASS) filter_res = FILTER_RESULT_PASS;
+ }
+
+ if (filter_res == FILTER_RESULT_PASS) {
+ addMsgToRxQueue(msg);
+ }
+}
+
+void J2534Connection::processIOCTLSetConfig(unsigned long Parameter, unsigned long Value) {
+ switch (Parameter) {
+ case DATA_RATE: // 5-500000
+ this->setBaud(Value);
+ case LOOPBACK: // 0 (OFF), 1 (ON) [0]
+ this->loopback = (Value != 0);
+ break;
+ case ISO15765_WFT_MAX:
+ break;
+ case NODE_ADDRESS: // J1850PWM Related (Not supported by panda). HDS requires these to 'work'.
+ case NETWORK_LINE:
+ case P1_MIN: // A bunch of stuff relating to ISO9141 and ISO14230 that the panda
+ case P1_MAX: // currently doesn't support. Don't let HDS know we can't use these.
+ case P2_MIN:
+ case P2_MAX:
+ case P3_MIN:
+ case P3_MAX:
+ case P4_MIN:
+ case P4_MAX:
+ case W0:
+ case W1:
+ case W2:
+ case W3:
+ case W4:
+ case W5:
+ case TIDLE:
+ case TINIL:
+ case TWUP:
+ case PARITY:
+ case T1_MAX: // SCI related options. The panda does not appear to support this
+ case T2_MAX:
+ case T3_MAX:
+ case T4_MAX:
+ case T5_MAX:
+ break; // Just smile and nod.
+ default:
+ printf("Got unknown SET code %X\n", Parameter);
+ }
+}
+
+unsigned long J2534Connection::processIOCTLGetConfig(unsigned long Parameter) {
+ switch (Parameter) {
+ case DATA_RATE:
+ return this->getBaud();
+ case LOOPBACK:
+ return this->loopback;
+ break;
+ case BIT_SAMPLE_POINT:
+ return 80;
+ case SYNC_JUMP_WIDTH:
+ return 15;
+ default:
+ // HDS rarely reads off values through ioctl GET_CONFIG, but it often
+ // just wants the call to pass without erroring, so just don't do anything.
+ printf("Got unknown code %X\n", Parameter);
+ }
+}
diff --git a/panda/drivers/windows/pandaJ2534DLL/J2534Connection.h b/panda/drivers/windows/pandaJ2534DLL/J2534Connection.h
new file mode 100644
index 0000000000..cdb215018b
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL/J2534Connection.h
@@ -0,0 +1,141 @@
+#pragma once
+#include "panda/panda.h"
+#include "J2534_v0404.h"
+#include "synchronize.h"
+#include "J2534Frame.h"
+#include "PandaJ2534Device.h"
+#include "J2534MessageFilter.h"
+#include "MessagePeriodic.h"
+
+class J2534Frame;
+class Action;
+class PandaJ2534Device;
+class J2534MessageFilter;
+
+#define check_bmask(num, mask)(((num) & mask) == mask)
+
+/**
+Class representing a generic J2534 Connection created by PassThruConnect,
+and is associated with a channelID given to the J2534 API user.
+Subclasses implement specific J2534 supported protocols.
+*/
+class J2534Connection : public std::enable_shared_from_this {
+ friend class PandaJ2534Device;
+
+public:
+ J2534Connection(
+ std::shared_ptr panda_dev,
+ unsigned long ProtocolID,
+ unsigned long Flags,
+ unsigned long BaudRate
+ );
+ virtual ~J2534Connection() {};
+
+ //J2534 API functions
+
+ virtual long PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout);
+ long PassThruWriteMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout);
+ virtual long PassThruStartPeriodicMsg(PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval);
+ virtual long PassThruStopPeriodicMsg(unsigned long MsgID);
+
+ virtual long PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg,
+ PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID);
+
+ virtual long PassThruStopMsgFilter(unsigned long FilterID);
+ virtual long PassThruIoctl(unsigned long IoctlID, void *pInput, void *pOutput);
+
+ //Functions for parsing messages to be send with PassThruWriteMsgs.
+
+ virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg);
+ virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& msg) { return nullptr; };
+
+ //IOCTL functions
+
+ long init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput);
+ long initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput);
+ long clearTXBuff();
+ long clearRXBuff();
+ long clearPeriodicMsgs();
+ long clearMsgFilters();
+
+ virtual void setBaud(unsigned long baud);
+
+ unsigned long getBaud() {
+ return this->BaudRate;
+ }
+
+ unsigned long getProtocol() {
+ return this->ProtocolID;
+ };
+
+ virtual bool isProtoCan() {
+ return FALSE;
+ }
+
+ //Port is used in a protocol specific way to differentiate tranceivers.
+ unsigned long getPort() {
+ return this->port;
+ }
+
+ virtual void processIOCTLSetConfig(unsigned long Parameter, unsigned long Value);
+
+ virtual unsigned long processIOCTLGetConfig(unsigned long Parameter);
+
+ //Called when the passthru device has received a message for this connection
+ //Loopback messages are processed separately.
+ virtual void processMessage(const J2534Frame& msg);
+
+ //Limitations on message size. Override in every subclass.
+
+ virtual unsigned long getMinMsgLen() {
+ return 1;
+ }
+
+ virtual unsigned long getMaxMsgLen() {
+ return 4128;
+ }
+
+ virtual unsigned long getMaxMsgSingleFrameLen() {
+ return 12;
+ }
+
+ //Add an Action to the Task Queue for future processing.
+ //The task should be set its expire time before being submitted.
+ void schedultMsgTx(std::shared_ptr msgout);
+
+ void rescheduleExistingTxMsgs();
+
+ std::shared_ptr getPandaDev() {
+ if (auto panda_dev_sp = this->panda_dev.lock())
+ return panda_dev_sp;
+ return nullptr;
+ }
+
+ //Add a message to the queue read by PassThruReadMsgs().
+ void addMsgToRxQueue(const J2534Frame& frame) {
+ synchronized(messageRxBuff_mutex) {
+ messageRxBuff.push(frame);
+ }
+ }
+
+ bool loopback = FALSE;
+
+protected:
+ unsigned long ProtocolID;
+ unsigned long Flags;
+ unsigned long BaudRate;
+ unsigned long port;
+
+ std::weak_ptr panda_dev;
+
+ Mutex messageRxBuff_mutex;
+ std::queue messageRxBuff;
+
+ std::array, 10> filters;
+ std::queue> txbuff;
+
+ std::array, 10> periodicMessages;
+
+private:
+ Mutex staged_writes_lock;
+};
diff --git a/panda/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp
new file mode 100644
index 0000000000..342616900d
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp
@@ -0,0 +1,41 @@
+#include "stdafx.h"
+#include "J2534Connection_CAN.h"
+#include "MessageTx_CAN.h"
+#include "Timer.h"
+
+J2534Connection_CAN::J2534Connection_CAN(
+ std::shared_ptr panda_dev,
+ unsigned long ProtocolID,
+ unsigned long Flags,
+ unsigned long BaudRate
+ ) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate) {
+ this->port = 0;
+
+ if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000)
+ throw ERR_INVALID_BAUDRATE;
+
+ panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, BaudRate/100);
+};
+
+unsigned long J2534Connection_CAN::validateTxMsg(PASSTHRU_MSG* msg) {
+ if ((msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen() ||
+ (val_is_29bit(msg->TxFlags) != this->_is_29bit() && !check_bmask(this->Flags, CAN_ID_BOTH))))
+ return ERR_INVALID_MSG;
+ return STATUS_NOERROR;
+}
+
+std::shared_ptr J2534Connection_CAN::parseMessageTx(PASSTHRU_MSG& msg) {
+ return std::dynamic_pointer_cast(std::make_shared(shared_from_this(), msg));
+}
+
+void J2534Connection_CAN::setBaud(unsigned long BaudRate) {
+ if (auto panda_dev = this->getPandaDev()) {
+ if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000)
+ throw ERR_NOT_SUPPORTED;
+
+ panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100));
+ return J2534Connection::setBaud(BaudRate);
+ } else {
+ throw ERR_DEVICE_NOT_CONNECTED;
+ }
+}
diff --git a/panda/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h
new file mode 100644
index 0000000000..4dd950b390
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "J2534Connection.h"
+#include "panda/panda.h"
+
+#define val_is_29bit(num) check_bmask(num, CAN_29BIT_ID)
+
+class J2534Connection_CAN : public J2534Connection {
+public:
+ J2534Connection_CAN(
+ std::shared_ptr panda_dev,
+ unsigned long ProtocolID,
+ unsigned long Flags,
+ unsigned long BaudRate
+ );
+
+ virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg);
+
+ virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& pMsg);
+
+ virtual void setBaud(unsigned long baud);
+
+ virtual unsigned long getMinMsgLen() {
+ return 4;
+ }
+
+ virtual unsigned long getMaxMsgLen() {
+ return 12;
+ }
+
+ virtual unsigned long getMaxMsgSingleFrameLen() {
+ return 12;
+ }
+
+ virtual bool isProtoCan() {
+ return TRUE;
+ }
+
+ bool _is_29bit() {
+ return (this->Flags & CAN_29BIT_ID) == CAN_29BIT_ID;
+ }
+
+};
\ No newline at end of file
diff --git a/panda/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp
new file mode 100644
index 0000000000..2b9d97cb64
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp
@@ -0,0 +1,229 @@
+#include "stdafx.h"
+#include "J2534Connection_ISO15765.h"
+#include "Timer.h"
+#include "constants_ISO15765.h"
+#include
+
+J2534Connection_ISO15765::J2534Connection_ISO15765(
+ std::shared_ptr panda_dev,
+ unsigned long ProtocolID,
+ unsigned long Flags,
+ unsigned long BaudRate
+) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate), wftMax(0) {
+ this->port = 0;
+
+ if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000)
+ throw ERR_INVALID_BAUDRATE;
+
+ panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100));
+}
+
+unsigned long J2534Connection_ISO15765::validateTxMsg(PASSTHRU_MSG* msg) {
+ if ((msg->DataSize < this->getMinMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) ||
+ msg->DataSize > this->getMaxMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) ||
+ (val_is_29bit(msg->TxFlags) != this->_is_29bit() && !check_bmask(this->Flags, CAN_ID_BOTH))))
+ return ERR_INVALID_MSG;
+
+ int fid = get_matching_out_fc_filter_id(std::string((const char*)msg->Data, msg->DataSize), msg->TxFlags, 0xFFFFFFFF);
+ if (msg->DataSize > getMaxMsgSingleFrameLen() && fid == -1) return ERR_NO_FLOW_CONTROL; //11 bytes (4 for CANid, 7 payload) is max length of input frame.
+
+ return STATUS_NOERROR;
+}
+
+std::shared_ptr J2534Connection_ISO15765::parseMessageTx(PASSTHRU_MSG& msg) {
+ int fid = get_matching_out_fc_filter_id(std::string((const char*)msg.Data, msg.DataSize), msg.TxFlags, 0xFFFFFFFF);
+ if (msg.DataSize > getMaxMsgSingleFrameLen() && fid == -1) 1;
+
+ return std::dynamic_pointer_cast(
+ std::make_shared(shared_from_this(), msg, (fid == -1) ? nullptr : this->filters[fid])
+ );
+}
+
+//https://happilyembedded.wordpress.com/2016/02/15/can-multiple-frame-transmission/
+void J2534Connection_ISO15765::processMessage(const J2534Frame& msg) {
+ if (msg.ProtocolID != CAN) return;
+
+ int fid = get_matching_in_fc_filter_id(msg);
+ if (fid == -1) return;
+
+ auto filter = this->filters[fid];
+ bool is_ext_addr = check_bmask(filter->flags, ISO15765_ADDR_TYPE);
+ uint8_t addrlen = is_ext_addr ? 5 : 4;
+
+ switch (msg_get_type(msg, addrlen)) {
+ case FRAME_FLOWCTRL:
+ {
+ if (this->txbuff.size() == 0)
+ return;
+ if (msg.Data.size() < addrlen + 3) return;
+ uint8_t flow_status = msg.Data[addrlen] & 0x0F;
+ uint8_t block_size = msg.Data[addrlen + 1];
+ uint8_t st_min = msg.Data[addrlen + 2];
+
+ auto txConvo = std::static_pointer_cast(this->txbuff.front());
+ switch (flow_status) {
+ case FLOWCTRL_CONTINUE: {
+ if (st_min > 0xF9) break;
+ if (st_min >= 0xf1 && st_min <= 0xf9) {
+ txConvo->flowControlContinue(block_size, std::chrono::microseconds((st_min & 0x0F) * 100));
+ } else if(st_min <= 0x7f) {
+ txConvo->flowControlContinue(block_size, std::chrono::microseconds(st_min * 1000));
+ } else {
+ break;
+ }
+ txConvo->scheduleImmediate();
+ this->rescheduleExistingTxMsgs();
+ break;
+ }
+ case FLOWCTRL_WAIT:
+ txConvo->flowControlWait(this->wftMax);
+ break;
+ case FLOWCTRL_ABORT:
+ txConvo->flowControlAbort();
+ break;
+ }
+ break;
+ }
+ case FRAME_SINGLE:
+ {
+ this->rxConversations[fid] = nullptr; //Reset any current transaction.
+
+ if (is_ext_addr) {
+ if ((msg.Data[5] & 0x0F) > 6) return;
+ } else {
+ if ((msg.Data[4] & 0x0F) > 7) return;
+ }
+
+ J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp);
+ if (msg.Data.size() != 8 && check_bmask(this->Flags, ISO15765_FRAME_PAD))
+ outframe.RxStatus |= ISO15765_PADDING_ERROR;
+ if (is_ext_addr)
+ outframe.RxStatus |= ISO15765_ADDR_TYPE;
+ outframe.Data = msg.Data.substr(0, addrlen) + msg.Data.substr(addrlen + 1, msg.Data[addrlen]);
+ outframe.ExtraDataIndex = outframe.Data.size();
+
+ addMsgToRxQueue(outframe);
+ break;
+ }
+ case FRAME_FIRST:
+ {
+ if (msg.Data.size() < 12) {
+ //A frame was received that could have held more data.
+ //No examples of this protocol show that happening, so
+ //it will be assumed that it is grounds to reset rx.
+ this->rxConversations[fid] = nullptr;
+ return;
+ }
+
+ J2534Frame outframe(ISO15765, msg.RxStatus | START_OF_MESSAGE, 0, msg.Timestamp);
+ if (is_ext_addr)
+ outframe.RxStatus |= ISO15765_ADDR_TYPE;
+ outframe.Data = msg.Data.substr(0, addrlen);
+
+ addMsgToRxQueue(outframe);
+
+ this->rxConversations[fid] = std::make_shared(
+ ((msg.Data[addrlen] & 0x0F) << 8) | msg.Data[addrlen + 1],
+ msg.Data.substr(addrlen + 2, 12 - (addrlen + 2)),
+ msg.RxStatus, filter);
+
+ //TODO maybe the flow control should also be scheduled in the TX list.
+ //Doing it this way because the filter can be 5 bytes in ext address mode.
+ std::string flowfilter = filter->get_flowctrl();
+ uint32_t flow_addr = (((uint8_t)flowfilter[0]) << 24) | ((uint8_t)(flowfilter[1]) << 16) | ((uint8_t)(flowfilter[2]) << 8) | ((uint8_t)flowfilter[3]);
+
+ std::string flowstrlresp;
+ if (flowfilter.size() > 4)
+ flowstrlresp += flowfilter[4];
+ flowstrlresp += std::string("\x30\x00\x00", 3);
+
+ if (auto panda_dev_sp = this->panda_dev.lock()) {
+ panda_dev_sp->panda->can_send(flow_addr, val_is_29bit(msg.RxStatus), (const uint8_t *)flowstrlresp.c_str(), (uint8_t)flowstrlresp.size(), panda::PANDA_CAN1);
+ }
+ break;
+ }
+ case FRAME_CONSEC:
+ {
+ auto& convo = this->rxConversations[fid];
+ if (convo == nullptr) return;
+
+ if (!convo->rx_add_frame(msg.Data[addrlen], (is_ext_addr ? 6 : 7), msg.Data.substr(addrlen + 1))) {
+ //Delete this conversation.
+ convo = nullptr;
+ return;
+ }
+
+ std::string final_msg;
+ if (convo->flush_result(final_msg)) {
+ convo = nullptr;
+ J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp);
+ if (is_ext_addr)
+ outframe.RxStatus |= ISO15765_ADDR_TYPE;
+ outframe.Data = msg.Data.substr(0, addrlen) + final_msg;
+ outframe.ExtraDataIndex = outframe.Data.size();
+
+ addMsgToRxQueue(outframe);
+ }
+ break;
+ }
+ }
+}
+
+void J2534Connection_ISO15765::setBaud(unsigned long BaudRate) {
+ if (auto panda_dev = this->getPandaDev()) {
+ if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000)
+ throw ERR_NOT_SUPPORTED;
+
+ panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100));
+ return J2534Connection::setBaud(BaudRate);
+ } else {
+ throw ERR_DEVICE_NOT_CONNECTED;
+ }
+}
+
+long J2534Connection_ISO15765::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg,
+ PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) {
+
+ if (FilterType != FLOW_CONTROL_FILTER) return ERR_INVALID_FILTER_ID;
+ return J2534Connection::PassThruStartMsgFilter(FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID);
+}
+
+int J2534Connection_ISO15765::get_matching_out_fc_filter_id(const std::string& msgdata, unsigned long flags, unsigned long flagmask) {
+ for (unsigned int i = 0; i < this->filters.size(); i++) {
+ if (this->filters[i] == nullptr) continue;
+ auto filter = this->filters[i]->get_flowctrl();
+ if (filter == msgdata.substr(0, filter.size()) &&
+ (this->filters[i]->flags & flagmask) == (flags & flagmask))
+ return i;
+ }
+ return -1;
+}
+
+int J2534Connection_ISO15765::get_matching_in_fc_filter_id(const J2534Frame& msg, unsigned long flagmask) {
+ for (unsigned int i = 0; i < this->filters.size(); i++) {
+ if (this->filters[i] == nullptr) continue;
+ if (this->filters[i]->check(msg) == FILTER_RESULT_MATCH &&
+ (this->filters[i]->flags & flagmask) == (msg.RxStatus & flagmask))
+ return i;
+ }
+ return -1;
+}
+
+void J2534Connection_ISO15765::processIOCTLSetConfig(unsigned long Parameter, unsigned long Value) {
+ switch (Parameter) {
+ case ISO15765_WFT_MAX:
+ this->wftMax = Value;
+ break;
+ default:
+ J2534Connection::processIOCTLSetConfig(Parameter, Value);
+ }
+}
+
+unsigned long J2534Connection_ISO15765::processIOCTLGetConfig(unsigned long Parameter) {
+ switch (Parameter) {
+ case ISO15765_WFT_MAX:
+ return this->wftMax;
+ default:
+ return J2534Connection::processIOCTLGetConfig(Parameter);
+ }
+}
diff --git a/panda/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h
new file mode 100644
index 0000000000..ddbf2dc2aa
--- /dev/null
+++ b/panda/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h
@@ -0,0 +1,65 @@
+#pragma once
+#include
+#include "J2534Connection.h"
+#include "J2534Connection_CAN.h"
+#include "MessageTx_ISO15765.h"
+#include "MessageRx.h"
+
+class MessageTx_ISO15765;
+
+typedef struct {
+ std::string dispatched_msg;
+ std::string remaining_payload;
+} PRESTAGED_WRITE;
+
+class J2534Connection_ISO15765 : public J2534Connection {
+public:
+ J2534Connection_ISO15765(
+ std::shared_ptr panda_dev,
+ unsigned long ProtocolID,
+ unsigned long Flags,
+ unsigned long BaudRate
+ );
+
+ virtual long PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG * pMaskMsg, PASSTHRU_MSG * pPatternMsg, PASSTHRU_MSG * pFlowControlMsg, unsigned long * pFilterID);
+
+ int get_matching_out_fc_filter_id(const std::string & msgdata, unsigned long flags, unsigned long flagmask);
+
+ int get_matching_in_fc_filter_id(const J2534Frame& msg, unsigned long flagmask = CAN_29BIT_ID);
+
+ virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg);
+
+ virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& msg);
+
+ virtual void processMessage(const J2534Frame& msg);
+
+ virtual void setBaud(unsigned long baud);
+
+ virtual void processIOCTLSetConfig(unsigned long Parameter, unsigned long Value);
+
+ virtual unsigned long processIOCTLGetConfig(unsigned long Parameter);
+
+ virtual unsigned long getMinMsgLen() {
+ return 4;
+ }
+
+ virtual unsigned long getMaxMsgLen() {
+ return 4099;
+ };
+
+ virtual unsigned long getMaxMsgSingleFrameLen() {
+ return 11;
+ }
+
+ virtual bool _is_29bit() {
+ return (this->Flags & CAN_29BIT_ID) == CAN_29BIT_ID;
+ }
+
+ virtual bool isProtoCan() {
+ return TRUE;
+ }
+
+private:
+ std::array