radare2/libr/io/p/io_gprobe.c
2020-06-27 17:20:43 +02:00

1250 lines
26 KiB
C

/* radare - LGPL - Copyright 2018 - Dirk Eibach, Guntermann & Drunck GmbH */
#include <r_io.h>
#include <r_lib.h>
#include <r_util.h>
#include <r_util/r_print.h>
#if __UNIX__
#include <errno.h>
#include <fcntl.h>
#endif
#define USE_OWNTIMER 1
#if USE_OWNTIMER
#include "io_gprobe.h"
#else
#define Timersub timersub
#define Timeradd timeradd
#define Timercmp timercmp
#endif
#if __WINDOWS__
#include <cfgmgr32.h>
#include <setupapi.h>
#include <tchar.h>
#include <windows.h>
#else
#if __linux__ || __APPLE__ || __OpenBSD__ || __FreeBSD__ || __NetBSD__ || __DragonFly__ || __HAIKU__
#include <sys/ioctl.h>
#include <termios.h>
#else
#include <stropts.h>
#endif
#endif
#define GPROBE_SIZE (1LL << 32)
#define GPROBE_I2C_ADDR 0x6e
#define I2C_SLAVE 0x0703
/* serial port code adapted from git://sigrok.org/libserialport */
struct gport {
const char *name;
#if __WINDOWS__
HANDLE hdl;
COMMTIMEOUTS timeouts;
OVERLAPPED write_ovl;
OVERLAPPED read_ovl;
OVERLAPPED wait_ovl;
DWORD events;
BYTE pending_byte;
BOOL writing;
BOOL wait_running;
#else
int fd;
#endif
int (*send_request) (struct gport *port, RBuffer *request);
int (*get_reply) (struct gport *port, ut8 cmd, RBuffer *reply);
void (*frame) (RBuffer *frame);
ut32 max_rx_size;
ut32 max_tx_size;
};
typedef struct {
struct gport gport;
ut64 offset;
} RIOGprobe;
enum {
GPROBE_DEBUGON = 0x09,
GPROBE_DEBUGOFF = 0x0a,
GPROBE_ACK = 0x0c,
GPROBE_RESET = 0x20,
GPROBE_GET_DEVICE_ID = 0x30,
GPROBE_GET_INFORMATION = 0x40,
GPROBE_RAM_READ_2 = 0x52,
GPROBE_RAM_WRITE_2 = 0x53,
GPROBE_RUN_CODE_2 = 0x54,
};
#if __UNIX__
static ut8 gprobe_checksum_i2c (const ut8 *p, unsigned int size, ut8 initial) {
ut8 res = initial;
unsigned int k;
for (k = 0; k < size; k++) {
res ^= p[k];
}
return res;
}
static void gprobe_frame_i2c(RBuffer *frame) {
ut8 size = r_buf_size (frame) + 1;
ut8 header[] = {0x51, 0x80 + size + 3, 0xc2, 0x00, 0x00};
r_buf_prepend_bytes (frame, &size, 1);
r_buf_prepend_bytes (frame, header, sizeof (header));
ut64 tmpsz;
const ut8 *tmp = r_buf_data (frame, &tmpsz);
ut8 checksum = gprobe_checksum_i2c (tmp, tmpsz, GPROBE_I2C_ADDR);
r_buf_append_bytes (frame, &checksum, 1);
}
static int gprobe_get_reply_i2c(struct gport *port, ut8 cmd, RBuffer *reply) {
ut8 buf[131];
int count;
int ddc2bi3_len;
ut8 addr = 0x50;
ut8 checksum;
r_sys_usleep (40000);
count = read (port->fd, buf, sizeof (buf));
if (count != sizeof (buf)) {
return -1;
}
ddc2bi3_len = buf[1] & ~0x80;
if (((buf[0] & 0xfe) != GPROBE_I2C_ADDR)
|| !(buf[1] & 0x80)
|| (buf[2] != 0xc2)
|| (buf[3] != 0x00)
|| (buf[4] != 0x00)
|| (cmd != buf[6])
|| !(buf[5] - 2)
|| (buf[5] != ddc2bi3_len - 2)) {
return -1;
}
checksum = gprobe_checksum_i2c (&addr, 1, 0);
if (gprobe_checksum_i2c (buf, ddc2bi3_len + 2, checksum) != buf[ddc2bi3_len + 2]) {
eprintf ("gprobe rx checksum error\n");
}
r_buf_append_bytes (reply, buf + 7, buf[5] - 3);
return 0;
}
static int gprobe_send_request_i2c(struct gport *port, RBuffer *request) {
ut64 tmpsz;
const ut8 *tmp = r_buf_data (request, &tmpsz);
if (write (port->fd, tmp, tmpsz) != r_buf_size (request)) {
return -1;
}
return 0;
}
static int i2c_open(struct gport *port) {
char *end, filename[32];
int i2cbus = strtol (port->name + 4, &end, 0);
if (*end) {
return -1;
}
snprintf (filename, sizeof (filename), "/dev/i2c/%d", i2cbus);
filename[sizeof (filename) - 1] = '\0';
int file = r_sandbox_open (filename, O_RDWR, 0);
if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
sprintf (filename, "/dev/i2c-%d", i2cbus);
file = r_sandbox_open (filename, O_RDWR, 0);
}
if (file < 0) {
return -1;
}
if (ioctl (file, I2C_SLAVE, GPROBE_I2C_ADDR >> 1) < 0) {
r_sandbox_close (file);
port->fd = -1;
return -1;
}
port->fd = file;
return 0;
}
#endif
static int sp_close(struct gport *port) {
#if __WINDOWS__
/* Returns non-zero upon success, 0 upon failure. */
if (CloseHandle (port->hdl) == 0){
return -1;
}
port->hdl = INVALID_HANDLE_VALUE;
/* Close event handles for overlapped structures. */
#define CLOSE_OVERLAPPED(ovl) \
do { \
if (port->ovl.hEvent != INVALID_HANDLE_VALUE && \
CloseHandle (port->ovl.hEvent) == 0) \
return -1; \
} while (0)
CLOSE_OVERLAPPED (read_ovl);
CLOSE_OVERLAPPED (write_ovl);
CLOSE_OVERLAPPED (wait_ovl);
#else
if (close (port->fd) == -1) {
return -1;
}
port->fd = -1;
#endif
return 0;
}
#if __WINDOWS__
/* To be called after port receive buffer is emptied. */
static int restart_wait (struct gport *port) {
DWORD wait_result;
if (port->wait_running) {
/* Check status of running wait operation. */
if (GetOverlappedResult (port->hdl, &port->wait_ovl,
&wait_result, FALSE)) {
port->wait_running = FALSE;
} else if (GetLastError () == ERROR_IO_INCOMPLETE) {
return 0;
}
return -1;
}
if (!port->wait_running) {
/* Start new wait operation. */
if (WaitCommEvent (port->hdl, &port->events,
&port->wait_ovl)) {
} else if (GetLastError () == ERROR_IO_PENDING) {
port->wait_running = TRUE;
}
return -1;
}
return 0;
}
#endif
static int sp_open (struct gport *port) {
#if __WINDOWS__
int ret;
DWORD errors;
char *escaped_port_name;
COMSTAT status;
DCB dcb;
LPTSTR filename_;
/* Prefix port name with '\\.\' to work with ports above COM9. */
if (!(escaped_port_name = malloc (strlen (port->name) + 5))) {
return -1;
}
sprintf (escaped_port_name, "\\\\.\\%s", port->name);
filename_ = r_sys_conv_utf8_to_win (escaped_port_name);
port->hdl = CreateFile (filename_, GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);
free (escaped_port_name);
if (port->hdl == INVALID_HANDLE_VALUE) {
return -1;
}
/* All timeouts initially disabled. */
port->timeouts.ReadIntervalTimeout = 0;
port->timeouts.ReadTotalTimeoutMultiplier = 0;
port->timeouts.ReadTotalTimeoutConstant = 0;
port->timeouts.WriteTotalTimeoutMultiplier = 0;
port->timeouts.WriteTotalTimeoutConstant = 0;
if (SetCommTimeouts (port->hdl, &port->timeouts) == 0) {
sp_close (port);
return -1;
}
/* Prepare OVERLAPPED structures. */
#define INIT_OVERLAPPED(ovl) \
do { \
memset (&port->ovl, 0, sizeof (port->ovl)); \
port->ovl.hEvent = INVALID_HANDLE_VALUE; \
if ((port->ovl.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL)) == INVALID_HANDLE_VALUE) { \
sp_close (port); \
return -1; \
} \
} while (0)
INIT_OVERLAPPED (read_ovl);
INIT_OVERLAPPED (write_ovl);
INIT_OVERLAPPED (wait_ovl);
/* Set event mask for RX and error events. */
if (SetCommMask (port->hdl, EV_RXCHAR | EV_ERR) == 0) {
sp_close (port);
return -1;
}
port->writing = FALSE;
port->wait_running = FALSE;
ret = restart_wait (port);
if (ret < 0) {
sp_close (port);
return -1;
}
dcb.fBinary = TRUE;
dcb.fDsrSensitivity = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fAbortOnError = FALSE;
if (ClearCommError (port->hdl, &errors, &status) == 0) {
return -1;
}
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fOutxCtsFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fOutxDsrFlow = FALSE;
dcb.fInX = FALSE;
dcb.fOutX = FALSE;
if (!SetCommState (port->hdl, &dcb)) {
return -1;
}
return 0;
#else
struct termios tty = {0};
if ((port->fd = r_sandbox_open (port->name, O_NONBLOCK | O_NOCTTY | O_RDWR, 0)) < 0) {
return -1;
}
if (tcgetattr (port->fd, &tty) != 0) {
sp_close (port);
return -1;
}
cfsetospeed (&tty, B115200);
cfsetispeed (&tty, B115200);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_iflag &= ~IGNBRK;
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag &= ~CSTOPB;
#ifdef CRTSCTS
tty.c_cflag &= ~CRTSCTS;
#else
tty.c_cflag &= ~020000000000;
#endif
if (tcsetattr (port->fd, TCSANOW, &tty) != 0) {
return -1;
}
return 0;
#endif
}
#if __WINDOWS__
/* Restart wait operation if buffer was emptied. */
static int restart_wait_if_needed (struct gport *port, unsigned int bytes_read) {
DWORD errors;
COMSTAT comstat;
if (bytes_read == 0) {
return 0;
}
if (ClearCommError (port->hdl, &errors, &comstat) == 0) {
return -1;
}
if (comstat.cbInQue == 0) {
if (restart_wait (port)) {
return -1;
}
}
return 0;
}
#endif
static int sp_blocking_read (struct gport *port, void *buf,
size_t count, unsigned int timeout_ms) {
#if __WINDOWS__
DWORD bytes_read = 0;
/* Set timeout. */
if (port->timeouts.ReadIntervalTimeout != 0 ||
port->timeouts.ReadTotalTimeoutMultiplier != 0 ||
port->timeouts.ReadTotalTimeoutConstant != timeout_ms) {
port->timeouts.ReadIntervalTimeout = 0;
port->timeouts.ReadTotalTimeoutMultiplier = 0;
port->timeouts.ReadTotalTimeoutConstant = timeout_ms;
if (SetCommTimeouts (port->hdl, &port->timeouts) == 0) {
return -1;
}
}
/* Start read. */
if (ReadFile (port->hdl, buf, count, NULL, &port->read_ovl)) {
bytes_read = count;
} else if (GetLastError () == ERROR_IO_PENDING) {
if (GetOverlappedResult (port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0)
return -1;
} else {
return -1;
}
if (restart_wait_if_needed (port, bytes_read)) {
return -1;
}
return bytes_read;
#else
size_t bytes_read = 0;
unsigned char *ptr = (unsigned char *)buf;
struct timeval start, delta, now, end = {0, 0};
int started = 0;
fd_set fds;
int result;
if (timeout_ms) {
/* Get time at start of operation. */
gettimeofday (&start, NULL);
/* Define duration of timeout. */
delta.tv_sec = timeout_ms / 1000;
delta.tv_usec = (timeout_ms % 1000) * 1000;
/* Calculate time at which we should give up. */
Timeradd (&start, &delta, &end);
}
FD_ZERO (&fds);
FD_SET (port->fd, &fds);
/* Loop until we have the requested number of bytes. */
while (bytes_read < count) {
/*
* Check timeout only if we have run select() at least once,
* to avoid any issues if a short timeout is reached before
* select() is even run.
*/
if (timeout_ms && started) {
gettimeofday (&now, NULL);
if (Timercmp (&now, &end, >)) {
/* Timeout has expired. */
break;
}
Timersub (&end, &now, &delta);
}
result = select (port->fd + 1, &fds, NULL, NULL, timeout_ms ? &delta : NULL);
started = 1;
if (result < 0) {
if (errno == EINTR) {
continue;
} else {
return -1;
}
} else if (result == 0) {
/* Timeout has expired. */
break;
}
/* Do read. */
result = read (port->fd, ptr, count - bytes_read);
if (result < 0) {
if (errno == EAGAIN) {
/*
* This shouldn't happen because we did a
* select() first, but handle anyway.
*/
continue;
} else {
/* This is an actual failure. */
return -1;
}
}
bytes_read += result;
ptr += result;
}
return bytes_read;
#endif
}
static int sp_flush (struct gport *port) {
#if __WINDOWS__
/* Returns non-zero upon success, 0 upon failure. */
if (PurgeComm (port->hdl, PURGE_RXCLEAR) == 0) {
return -1;
}
if (restart_wait (port)) {
return -1;
}
#else
if (tcflush (port->fd, TCIFLUSH) < 0) {
return -1;
}
#endif
return 0;
}
#if __WINDOWS__
static int await_write_completion (struct gport *port) {
DWORD bytes_written;
BOOL result;
/* Wait for previous non-blocking write to complete, if any. */
if (port->writing) {
result = GetOverlappedResult (port->hdl, &port->write_ovl, &bytes_written, TRUE);
port->writing = 0;
if (!result) {
return -1;
}
}
return 0;
}
#endif
static int sp_blocking_write (struct gport *port, const void *buf,
size_t count, unsigned int timeout_ms) {
#if __WINDOWS__
DWORD bytes_written = 0;
if (await_write_completion (port)) {
return -1;
}
/* Set timeout. */
if (port->timeouts.WriteTotalTimeoutConstant != timeout_ms) {
port->timeouts.WriteTotalTimeoutConstant = timeout_ms;
if (SetCommTimeouts (port->hdl, &port->timeouts) == 0) {
return -1;
}
}
/* Start write. */
if (WriteFile (port->hdl, buf, count, NULL, &port->write_ovl)) {
return count;
} else if (GetLastError () == ERROR_IO_PENDING) {
if (GetOverlappedResult (port->hdl, &port->write_ovl, &bytes_written, TRUE) == 0) {
if (GetLastError () == ERROR_SEM_TIMEOUT)
return 0;
else
return -1;
}
return bytes_written;
} else {
return -1;
}
#else
size_t bytes_written = 0;
unsigned char *ptr = (unsigned char *)buf;
struct timeval start, delta, now, end = {0, 0};
int started = 0;
fd_set fds;
int result;
if (timeout_ms) {
/* Get time at start of operation. */
gettimeofday (&start, NULL);
/* Define duration of timeout. */
delta.tv_sec = timeout_ms / 1000;
delta.tv_usec = (timeout_ms % 1000) * 1000;
/* Calculate time at which we should give up. */
Timeradd (&start, &delta, &end);
}
FD_ZERO (&fds);
FD_SET (port->fd, &fds);
/* Loop until we have written the requested number of bytes. */
while (bytes_written < count) {
/*
* Check timeout only if we have run select() at least once,
* to avoid any issues if a short timeout is reached before
* select() is even run.
*/
if (timeout_ms && started) {
gettimeofday (&now, NULL);
if (Timercmp (&now, &end, >)) {
/* Timeout has expired. */
break;
}
Timersub (&end, &now, &delta);
}
result = select (port->fd + 1, NULL, &fds, NULL, timeout_ms ? &delta : NULL);
started = 1;
if (result < 0) {
if (errno == EINTR) {
continue;
} else {
return -1;
}
} else if (result == 0) {
/* Timeout has expired. */
break;
}
/* Do write. */
result = write (port->fd, ptr, count - bytes_written);
if (result < 0) {
if (errno == EAGAIN) {
/* This shouldn't happen because we did a select() first, but handle anyway. */
continue;
} else {
/* This is an actual failure. */
return -1;
}
}
bytes_written += result;
ptr += result;
}
return bytes_written;
#endif
}
static ut8 gprobe_checksum (const ut8 *p, unsigned int size) {
ut8 res = 0;
unsigned int k;
for (k = 0; k < size; k++) {
res += p[k];
}
res = ~res + 1;
return res;
}
static void gprobe_frame_sp(RBuffer *frame) {
ut64 size;
const ut8 *tmp = r_buf_data (frame, &size);
size += 2;
ut8 checksum;
r_buf_prepend_bytes (frame, (const ut8 *)&size, 1);
checksum = gprobe_checksum (tmp, size - 1);
r_buf_append_bytes (frame, &checksum, 1);
}
static int gprobe_get_reply_sp(struct gport *port, ut8 cmd, RBuffer *reply) {
ut8 buf[256];
int count = sp_blocking_read (port, buf, 2, 50);
if (count < 2) {
return -1;
}
if (cmd != buf[1]) {
return -1;
}
if (!(buf[0] - 2)) {
return 0;
}
count = sp_blocking_read (port, buf + 2, buf[0] - 2, 50) + 2;
if (count != buf[0]) {
return -1;
}
/* checksumming answers does not work reliably */
#if 0
if (gprobe_checksum(buf, count - 1) != buf[count - 1]) {
printf("### CHECKSUM FAILED\n");
}
#endif
r_buf_append_bytes (reply, buf + 2, count - 3);
return 0;
}
static int gprobe_send_request_sp(struct gport *port, RBuffer *request) {
sp_flush (port);
ut64 tmpsz;
const ut8 *tmp = r_buf_data (request, &tmpsz);
if (sp_blocking_write (port, tmp, tmpsz, 100) != tmpsz) {
return -1;
}
return 0;
}
static int gprobe_read(struct gport *port, ut32 addr, ut8 *buf, ut32 count) {
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_RAM_READ_2;
ut8 addr_be[4];
ut8 count_be[4];
int res;
if (!request || !reply) {
r_buf_free (request);
r_buf_free (reply);
return -1;
}
count = R_MIN (port->max_rx_size, count);
r_write_be32 (addr_be, addr);
r_write_be32 (count_be, count);
r_buf_append_bytes (request, &cmd, 1);
r_buf_append_bytes (request, addr_be, 4);
r_buf_append_bytes (request, count_be, 4);
port->frame (request);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, cmd, reply)) {
goto fail;
}
res = r_buf_read_at (reply, 0, buf, r_buf_size (reply));
r_buf_free (request);
r_buf_free (reply);
return res;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int gprobe_write (struct gport *port, ut32 addr, const ut8 *buf, ut32 count) {
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_RAM_WRITE_2;
ut8 addr_be[4];
ut8 count_be[4];
if (!request || !reply) {
r_buf_free (request);
r_buf_free (reply);
return -1;
}
count = R_MIN (port->max_tx_size, count);
r_write_be32 (addr_be, addr);
r_write_be32 (count_be, count);
r_buf_append_bytes (request, &cmd, 1);
r_buf_append_bytes (request, addr_be, 4);
r_buf_append_bytes (request, buf, count);
port->frame (request);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, GPROBE_ACK, reply)) {
goto fail;
}
r_buf_free (request);
r_buf_free (reply);
return count;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int gprobe_reset (struct gport *port, ut8 code) {
if (!port) {
return -1;
}
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_RESET;
if (!request || !reply) {
goto fail;
}
r_buf_append_bytes (request, &cmd, 1);
r_buf_append_bytes (request, &code, 1);
port->frame (request);
sp_flush (port);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, GPROBE_ACK, reply)) {
goto fail;
}
r_buf_free (request);
r_buf_free (reply);
return 0;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int gprobe_debugon (struct gport *port) {
if (!port) {
return -1;
}
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_DEBUGON;
if (!request || !reply) {
goto fail;
}
r_buf_append_bytes (request, &cmd, 1);
port->frame (request);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, GPROBE_ACK, reply)) {
goto fail;
}
r_buf_free (request);
r_buf_free (reply);
return 0;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int gprobe_debugoff (struct gport *port) {
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_DEBUGOFF;
if (!request || !reply) {
goto fail;
}
r_buf_append_bytes (request, &cmd, 1);
port->frame (request);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, GPROBE_ACK, reply)) {
goto fail;
}
r_buf_free (request);
r_buf_free (reply);
return 0;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int gprobe_runcode (struct gport *port, ut32 addr) {
if (!port) {
return -1;
}
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_RUN_CODE_2;
ut8 addr_be[4];
if (!request || !reply) {
goto fail;
}
r_write_be32 (addr_be, addr);
r_buf_append_bytes (request, &cmd, 1);
r_buf_append_bytes (request, addr_be, 4);
port->frame (request);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, GPROBE_ACK, reply)) {
goto fail;
}
r_buf_free (request);
r_buf_free (reply);
return 0;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int gprobe_getdeviceid (struct gport *port, ut8 index) {
if (!port) {
return -1;
}
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_GET_DEVICE_ID;
if (!request || !reply) {
goto fail;
}
r_buf_append_bytes (request, &cmd, 1);
r_buf_append_bytes (request, &index, 1);
port->frame (request);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, cmd, reply)) {
goto fail;
}
char *s = r_buf_to_string (reply);
if (s) {
printf ("%s\n", s);
free (s);
}
r_buf_free (request);
r_buf_free (reply);
return 0;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int gprobe_getinformation (struct gport *port) {
if (!port) {
return -1;
}
RBuffer *request = r_buf_new ();
RBuffer *reply = r_buf_new ();
const ut8 cmd = GPROBE_GET_INFORMATION;
const ut8 index = 0;
if (!request || !reply) {
goto fail;
}
r_buf_append_bytes (request, &cmd, 1);
r_buf_append_bytes (request, &index, 1);
port->frame (request);
if (port->send_request (port, request)) {
goto fail;
}
if (port->get_reply (port, cmd, reply)) {
goto fail;
}
ut64 tmpsz;
const ut8 *tmp = r_buf_data (reply, &tmpsz);
r_print_hexdump (NULL, 0, tmp, tmpsz, 16, 1, 1);
r_buf_free (request);
r_buf_free (reply);
return 0;
fail:
r_buf_free (request);
r_buf_free (reply);
return -1;
}
static int __write(RIO *io, RIODesc *fd, const ut8 *buf, int count) {
RIOGprobe *gprobe;
int res;
int has_written = 0;
if (!fd || !fd->data || !buf) {
return -1;
}
gprobe = (RIOGprobe *)fd->data;
if ((gprobe->offset + count) > GPROBE_SIZE) {
count = GPROBE_SIZE - gprobe->offset;
}
while (has_written < count) {
res = gprobe_write (&gprobe->gport, gprobe->offset, buf + has_written, count - has_written);
if (res <= 0) {
return -1;
}
gprobe->offset += res;
has_written += res;
}
return count;
}
static int __read (RIO *io, RIODesc *fd, ut8 *buf, int count) {
int res;
RIOGprobe *gprobe;
int has_read = 0;
if (!fd || !fd->data || !buf) {
return -1;
}
gprobe = (RIOGprobe *)fd->data;
if ((gprobe->offset + count) > GPROBE_SIZE) {
count = GPROBE_SIZE - gprobe->offset;
}
while (has_read < count) {
res = gprobe_read (&gprobe->gport, gprobe->offset, buf + has_read, count - has_read);
if (res <= 0) {
return -1;
}
gprobe->offset += res;
has_read += res;
}
return has_read;
}
static int __close (RIODesc *fd) {
RIOGprobe *gprobe;
if (!fd || !fd->data) {
return -1;
}
gprobe = (RIOGprobe *)fd->data;
sp_close (&gprobe->gport);
R_FREE (fd->data);
return 0;
}
static ut64 __lseek (RIO *io, RIODesc *fd, ut64 offset, int whence) {
RIOGprobe *gprobe;
if (!fd || !fd->data) {
return offset;
}
gprobe = (RIOGprobe *)fd->data;
switch (whence) {
case SEEK_SET:
if (offset >= GPROBE_SIZE) {
return gprobe->offset = GPROBE_SIZE - 1;
}
return gprobe->offset = offset;
case SEEK_CUR:
if ((gprobe->offset + offset) >= GPROBE_SIZE) {
return gprobe->offset = GPROBE_SIZE - 1;
}
return gprobe->offset += offset;
case SEEK_END:
return gprobe->offset = GPROBE_SIZE - 1;
}
return offset;
}
static bool __plugin_open (RIO *io, const char *pathname, bool many) {
return pathname && r_str_startswith (pathname, "gprobe://") && strlen (pathname + strlen ("gprobe://"));
}
static RIODesc *__open (RIO *io, const char *pathname, int rw, int mode) {
if (__plugin_open (io, pathname, 0)) {
RIOGprobe *gprobe = R_NEW0 (RIOGprobe);
gprobe->offset = 0LL;
gprobe->gport.name = pathname + strlen ("gprobe://");
if (r_str_startswith (gprobe->gport.name, "i2c-")) {
#if __UNIX__
gprobe->gport.send_request = gprobe_send_request_i2c;
gprobe->gport.get_reply = gprobe_get_reply_i2c;
gprobe->gport.frame = gprobe_frame_i2c;
gprobe->gport.max_tx_size = 117;
gprobe->gport.max_rx_size = 121;
if (i2c_open (&gprobe->gport)) {
R_FREE (gprobe);
return NULL;
}
#else
R_FREE (gprobe);
return NULL;
#endif
} else {
gprobe->gport.send_request = gprobe_send_request_sp;
gprobe->gport.get_reply = gprobe_get_reply_sp;
gprobe->gport.frame = gprobe_frame_sp;
gprobe->gport.max_tx_size = 248;
gprobe->gport.max_rx_size = 252;
if (sp_open (&gprobe->gport)) {
R_FREE (gprobe);
return NULL;
}
}
return r_io_desc_new (io, &r_io_plugin_gprobe, pathname, rw, mode, gprobe);
}
return NULL;
}
static char *__system (RIO *io, RIODesc *fd, const char *cmd) {
RIOGprobe *gprobe;
if (!fd || !fd->data) {
return NULL;
}
gprobe = (RIOGprobe *)fd->data;
if (!cmd[0] || cmd[0] == '?' || !strcmp (cmd, "help")) {
printf ("Usage: =!cmd args\n"
" =!reset code\n"
" =!debugon\n"
" =!debugoff\n"
" =!runcode address\n"
" =!getdeviceid\n"
" =!getinformation\n");
return NULL;
}
if (r_str_startswith (cmd, "reset") && (strlen (cmd) > 6)) {
ut32 code = (ut32)strtoul (cmd + 6, NULL, 10);
gprobe_reset (&gprobe->gport, code);
return NULL;
}
if (r_str_startswith (cmd, "debugon")) {
gprobe_debugon (&gprobe->gport);
return NULL;
}
if (r_str_startswith (cmd, "debugoff")) {
gprobe_debugoff (&gprobe->gport);
return NULL;
}
if (r_str_startswith (cmd, "runcode") && (strlen (cmd) > 8)) {
ut32 address = (ut32)strtoul (cmd + 8, NULL, 0);
gprobe_runcode (&gprobe->gport, address);
return NULL;
}
if (r_str_startswith (cmd, "getdeviceid")) {
ut8 index = 0;
while (!gprobe_getdeviceid (&gprobe->gport, index++)) {
};
return NULL;
}
if (r_str_startswith (cmd, "getinformation")) {
gprobe_getinformation (&gprobe->gport);
return NULL;
}
printf ("Try: '=!?'\n");
return NULL;
}
RIOPlugin r_io_plugin_gprobe = {
.name = "gprobe",
.desc = "Open gprobe connection",
.license = "LGPL3",
.uris = "gprobe://",
.open = __open,
.close = __close,
.read = __read,
.check = __plugin_open,
.lseek = __lseek,
.write = __write,
.system = __system,
};
#ifndef R2_PLUGIN_INCORE
R_API RLibStruct radare_plugin = {
.type = R_LIB_TYPE_IO,
.data = &r_io_plugin_gprobe,
.version = R2_VERSION};
#endif