blob: 1efa7eb20545b34ff4179e0451c689c21cb57c27 [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <ftdi.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "ftdi_common.h"
#include "ftdigpio.h"
#include "ftdiuart.h"
#include "ftdii2c.h"
#define MAX_BUF 512
#define GPIO_FIELDS 3
void usage(char *progname) {
printf("%s [common ftdi args]\n\n", progname);
exit(-1);
}
// parse_ul_element - parse element return non-zero for error else properly
// converted value returned in value and buf set to character beyond delimeter
char *parse_ul_element(unsigned long *value, char *buf, char delim) {
char *eptr;
*value = strtoul(buf, &eptr, 0);
if (!delim) {
return eptr;
} else if (eptr[0] != delim) {
return NULL;
}
return eptr + 1;
}
// parse_buffer_gpio - parse gpio command from client
// <interface>,<direction>,<value>[,<mask>]
// ex) 1,0xff,0xff -- set all gpios to output '1' on interface 1
int parse_buffer_gpio(char *buf, struct gpio_s *gpio,
unsigned int *interface) {
char *bptr = buf;
unsigned long tmp_ul;
if (((bptr = parse_ul_element(&tmp_ul, bptr, ',')) == NULL) ||
(!tmp_ul || (tmp_ul > 4))) {
prn_error("Malformed interface argument\n");
return 1;
}
*interface = (unsigned int)tmp_ul;
if (((bptr = parse_ul_element(&tmp_ul, bptr, ',')) == NULL) ||
(tmp_ul > 0xff)) {
prn_error("Malformed direction argument\n");
return 1;
}
gpio->direction = (uint8_t)tmp_ul;
if (((bptr = parse_ul_element(&tmp_ul, bptr, 0)) == NULL) ||
(tmp_ul > 0xff)) {
prn_error("Malformed value argument\n");
return 1;
}
gpio->value = (uint8_t)tmp_ul;
if (bptr[0] == ',') {
bptr++;
// there's a mask
if (((bptr = parse_ul_element(&tmp_ul, bptr, 0)) == NULL) ||
(tmp_ul > 0xff)) {
prn_error("Malformed mask argument\n");
return 1;
}
gpio->mask = (uint8_t)tmp_ul;
} else {
gpio->mask = 0xff;
}
prn_dbg("Done parsing gpio buffer i:%d d:0x%02x v:0x%02x m:0x%02x\n",
*interface, gpio->direction, gpio->value, gpio->mask);
return 0;
}
// parse_buffer_i2c - parse i2c command from client
// ex) 0x40,1,0x0,2 -- for i2c slave 0x40 write 1byte 0, read 2bytes
int parse_buffer_i2c(char *buf, int *argc, uint8_t *argv) {
int argcnt = 0;
char *field;
field = strtok(buf, ",");
while (field) {
argv[argcnt++] = strtoul(field, NULL, 0);
field = strtok(NULL, ",");
}
// do some checking
if ((argcnt > 2) && (argcnt < argv[1] + 2)) {
prn_error("looks like i2c write w/o enough data\n");
return -1;
} else if (argcnt < 2) {
prn_error("Must have at least 2 arguments to i2c cmd=%s\n", buf);
return -1;
}
*argc = argcnt;
return 0;
}
// process_client - interact w/ client connection
//
// response to client looks like:
// I:<read value>
// A:
//
// or in case of error
// E:<msg>
//
// where:
// read value == value read for that bank of 8 gpios
// msg == detailed message of error condition
int process_client(struct ftdi_itype *interfaces, int int_cnt, int client) {
char buf[MAX_BUF];
char *rsp = buf;
int blen, bcnt, err;
struct gpio_s new_gpio;
uint8_t rd_val;
struct ftdi_itype *interface;
memset(rsp, 0, MAX_BUF);
if ((blen = read(client,buf,MAX_BUF-1)) <= 0) {
if (blen == 0) {
prn_info("client connection (fd=%d) hung up\n", client);
return 1;
} else {
perror("reading from client ... guess he disappeared");
return 1;
}
}
prn_dbg("client cmd: %s",buf);
if ((buf[0] == 'g') && (buf[1] == ',')) {
unsigned int interface_num;
if (parse_buffer_gpio(&buf[2], &new_gpio, &interface_num)) {
snprintf(buf, MAX_BUF,
"E:parsing client request. Should be\n\t%s\n",
"<interface>,<dir>,<val>[,<mask>]");
goto CLIENT_RSP;
}
interface = fcom_lookup_interface(interfaces, int_cnt, interface_num, ANY);
if (interface == NULL) {
snprintf(rsp, MAX_BUF, "E:No gpio at interface %d\n", interface_num);
goto CLIENT_RSP;
}
struct fgpio_context *fgc = NULL;
fgc = (struct fgpio_context *)interface->context;
assert(fgc);
if ((err = fgpio_wr_rd(fgc, &new_gpio, &rd_val, interface->type))) {
if (err == FGPIO_ERR_MASK) {
snprintf(rsp, MAX_BUF, "E:Illegal gpio mask. Bits avail are 0x%02x\n",
fgc->gpio.mask);
} else {
snprintf(rsp, MAX_BUF, "E:writing/reading gpio\n");
}
goto CLIENT_RSP;
}
snprintf(rsp, MAX_BUF, "I:0x%02x\nA:\n", rd_val);
} else if ((buf[0] == 'i') && (buf[1] == ',')) {
int argcnt = 0;
uint8_t args[128];
uint8_t rbuf[128];
if (parse_buffer_i2c(&buf[2], &argcnt, args)) {
snprintf(rsp, MAX_BUF, "E:parsing client request. Should be\n\t%s\n",
"<slv>,[<bytes to Wr>,<Wr0>,<Wr1>,<WrN>],[<bytes to Rd>]");
goto CLIENT_RSP;
}
interface = fcom_lookup_interface(interfaces, int_cnt, 2, I2C);
if (interface == NULL) {
snprintf(rsp, MAX_BUF, "E:No i2c at interface 2\n");
goto CLIENT_RSP;
}
struct fi2c_context *fic = NULL;
fic = (struct fi2c_context *)interface->context;
assert(fic);
// defaults if read-only
uint8_t *wbuf = NULL;
int wcnt = 0;
int rcnt = args[argcnt-1];
fic->slv = args[0];
if (argcnt > 2) {
wbuf = &args[2];
wcnt = args[1];
if (argcnt != wcnt + 3) {
// its just a write
rcnt = 0;
}
}
if (fi2c_wr_rd(fic, wbuf, wcnt, rbuf, rcnt)) {
snprintf(rsp, MAX_BUF, "E:writing/reading i2c\n");
goto CLIENT_RSP;
}
int bytes_remaining = MAX_BUF;
if (rcnt) {
snprintf(rsp, bytes_remaining, "I:0x");
rsp = rsp + 4;
bytes_remaining-=4;
int i;
for (i = 0; i < rcnt; i++) {
if (i && !(i % 4)) {
snprintf(rsp, bytes_remaining, "\nI:0x");
rsp+=5;
bytes_remaining-=5;
}
snprintf(rsp, bytes_remaining, "%02x", rbuf[i]);
rsp+=2;
bytes_remaining-=2;
if (bytes_remaining < 5) {
snprintf(buf, MAX_BUF, "E: i2c request too large. See developer\n");
goto CLIENT_RSP;
}
}
}
snprintf(rsp, bytes_remaining, "\nA:\n");
} else {
snprintf(rsp, MAX_BUF,
"E:parsing client request. Should be\n\t%s\n\t%s\n",
"g,<interface>,<dir>,<val>",
"i,<slv>,<bytes to write(4max)>,<write word>,<bytes to read>");
}
CLIENT_RSP:
blen = strlen(buf);
bcnt = write(client, buf, blen);
if (bcnt != blen)
perror("writing to client");
return 0;
}
int init_socket(int port) {
struct sockaddr_in server_addr;
prn_dbg("Initializing server\n");
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
perror("opening socket");
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
int tr = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &tr, sizeof(int)) == -1) {
perror("setting sockopt");
exit(-1);
}
if (bind(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
perror("binding socket");
exit(-1);
}
return sock;
}
int init_server(int port) {
int sock = init_socket(port);
assert(listen(sock, 5) == 0);
prn_dbg("Server initialized\n");
return sock;
}
void *run_uart(void *ptr) {
struct fuart_context *fcc = (struct fuart_context *)ptr;
while (1) {
if (fuart_wr_rd(fcc, 10000)) {
prn_error("fuart_wr_rd");
break;
}
}
// should never reach
return NULL;
}
void run_server(struct ftdi_itype *interfaces, int int_cnt, int server_fd) {
struct sockaddr_in client_addr;
fd_set read_fds, master_fds;
unsigned int client_len = sizeof(client_addr);
FD_ZERO(&read_fds);
FD_ZERO(&master_fds);
int max_fd = server_fd;
FD_SET(server_fd, &master_fds);
prn_dbg("Running server fd=%d\n", server_fd);
while (1) {
read_fds = master_fds;
if(select(max_fd+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("Select test failed");
exit(1);
}
prn_dbg("select ok\n");
int i;
for (i = 0; i <= max_fd; i++) {
if (FD_ISSET(i, &read_fds)) {
if (i == server_fd) {
// add new clients
int new_client = accept(server_fd, (struct sockaddr *)&client_addr,
&client_len);
if (new_client < 0) {
perror("accepting connection");
exit(1);
} else {
prn_info("Client connected %s fd:%d\n",
inet_ntoa(client_addr.sin_addr), new_client);
FD_SET(new_client, &master_fds);
if (new_client > max_fd) {
max_fd = new_client;
prn_dbg("max_fd increased to %d\n", max_fd);
}
}
} else {
// process connected clients
if (process_client(interfaces, int_cnt, i)) {
close(i);
FD_CLR(i, &master_fds);
}
}
}
}
}
}
#define NUM_GPIOS 2
#define NUM_INTERFACES 4
int main(int argc, char **argv) {
int sock;
struct ftdi_common_args fargs = {
.interface = 0,
//.vendor_id = 0x18d1,
//.product_id = 0x5001,
.vendor_id = 0x0403,
.product_id = 0x6011,
.speed = 115200,
.bits = BITS_8,
.parity = NONE,
.sbits = STOP_BIT_1
};
int args_consumed;
if ((args_consumed = fcom_args(&fargs, argc, argv)) < 0) {
usage(argv[0]);
}
int i;
struct ftdi_context fc[NUM_INTERFACES];
struct fgpio_context fgs[NUM_GPIOS];
for (i = 0; i < NUM_INTERFACES; i++) {
if (ftdi_init(&fc[i]) < 0) {
ERROR_FTDI("Initializing ftdi context", &fc[i]);
return 1;
}
}
struct ftdi_itype interfaces[NUM_INTERFACES];
for (i = 0; i < NUM_GPIOS; i++) {
if (fgpio_init(&fgs[i], &fc[i])) {
prn_error("fgpio_init\n");
return FCOM_ERR;
}
}
// should be JTAG/SPI ... but GPIOs for now or use flashrom/openocd separately
fargs.interface = 1;
if (fgpio_open(&fgs[0], &fargs)) {
prn_error("fgpio_open\n");
return FCOM_ERR;
}
interfaces[fargs.interface - 1].context = (void *)&fgs[0];
interfaces[fargs.interface - 1].type = GPIO;
// i2c master
struct fi2c_context fic;
fargs.interface = 2;
if (fi2c_init(&fic, &fc[3])) {
prn_error("fi2c_init\n");
return FCOM_ERR;
}
if (fi2c_open(&fic, &fargs)) {
prn_error("fi2c_open\n");
return FCOM_ERR;
}
// 100khz
if (fi2c_setclock(&fic, 100000)) {
prn_error("fi2c_setclock\n");
return FCOM_ERR;
}
interfaces[fargs.interface - 1].context = (void *)&fic;
interfaces[fargs.interface - 1].type = I2C;
// DUT console uart
fargs.interface = 3;
struct fuart_context fcc;
if (fuart_init(&fcc, &fc[2])) {
prn_error("fuart_init\n");
return FCOM_ERR;
}
if (fuart_open(&fcc, &fargs)) {
prn_error("fuart_open\n");
return FCOM_ERR;
}
printf("ftdi uart connected to pty at %s\n", fcc.name);
pthread_t fcc_thread;
if (pthread_create(&fcc_thread, NULL, run_uart, (void *)&fcc)) {
perror("threading fuart");
return errno;
}
interfaces[fargs.interface - 1].context = (void *)&fcc;
interfaces[fargs.interface - 1].type = UART;
// legit gpios
fargs.interface = 4;
if (fgpio_open(&fgs[1], &fargs)) {
prn_error("fgpio_open\n");
return FCOM_ERR;
}
interfaces[fargs.interface - 1].context = (void *)&fgs[1];
interfaces[fargs.interface - 1].type = GPIO;
sock = init_server(9999);
prn_info("%s running accepting connections at port %d\n", argv[0], 9999);
run_server(interfaces, NUM_INTERFACES, sock);
return 0;
}