blob: 883eb4f963b9f386a700021ceb3634179c42362f [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.
#define _BSD_SOURCE
#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"
#define MAX_BUF 512
#define CLIENT_FIELDS 2
void usage(char *progname) {
printf("%s [common ftdi args]\n\n", progname);
exit(-1);
}
// parse_buffer - parse buffer from client
//
// fmt is <direction>,<value>
// ex) 0xff,0xff -- sets all to output '1'
int parse_buffer(char *buf, struct gpio_s *gpio) {
char *eptr = NULL;
gpio->direction = strtoul(buf, &eptr, 0);
if (eptr[0] != ',') {
prn_error("Malformed direction argument\n");
return 1;
}
eptr++;
char *bptr = eptr;
gpio->value = strtoul(bptr, &eptr, 0);
if (((eptr[0] != '\r') && (eptr[0] != '\n')) ||
(bptr == eptr)) {
prn_error("Malformed value argument %c\n", eptr[0]);
return 1;
}
prn_dbg("Done parsing buffer m:0x%02x d:0x%02x v:0x%02x\n",
gpio->mask, gpio->direction, gpio->value);
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 fgpio_context *fgc, int client) {
char buf[MAX_BUF];
int blen, bcnt;
struct gpio_s new_gpio;
uint8_t rd_val;
memset(buf, 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");
exit(1);
}
}
prn_dbg("client (fd=%d) cmd: %s",client, buf);
new_gpio.mask = fgc->gpio.mask;
if (parse_buffer(buf, &new_gpio)) {
snprintf(buf, MAX_BUF,
"E:parsing client request. Should be <dir>,<val>.\n");
goto CLIENT_RSP;
}
if (fgpio_wr_rd(fgc, &new_gpio, &rd_val, GPIO)) {
snprintf(buf, MAX_BUF, "E:writing/reading gpio\n");
goto CLIENT_RSP;
}
snprintf(buf, MAX_BUF, "I:0x%02x\nA:\n", rd_val);
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) {
prn_dbg("uart checked\n");
if (fuart_wr_rd(fcc, 1000)) {
prn_error("fuart_wr_rd");
break;
}
}
// should never reach
return NULL;
}
void run_server(struct fgpio_context *fgc, 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(fgc, i)) {
close(i);
FD_CLR(i, &master_fds);
}
}
}
}
}
}
int main(int argc, char **argv) {
int sock;
struct ftdi_context fc;
struct fgpio_context fgc;
struct fuart_context fcc;
struct ftdi_common_args fargs = {
.interface = 0,
.vendor_id = 0x18d1,
.product_id = 0x5000,
.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]);
}
if (ftdi_init(&fc) < 0) {
ERROR_FTDI("Initializing ftdi context", &fc);
return 1;
}
if (fgpio_init(&fgc, &fc)) {
prn_error("fgpio_init\n");
return FCOM_ERR;
}
if (fgpio_open(&fgc, &fargs)) {
prn_error("fgpio_open\n");
return FCOM_ERR;
}
if (fuart_init(&fcc, &fc)) {
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);
// TODO(tbroch) this should be configurable
sock = init_server(9999);
pthread_t fcc_thread;
if (pthread_create(&fcc_thread, NULL, run_uart, (void *)&fcc)) {
perror("threading fuart");
return errno;
}
run_server(&fgc, sock);
return 0;
}