blob: 4609be11e06c9c999ae174599fdcd6258b56ed26 [file] [log] [blame]
/*
* Copyright (C) 2012 The Chromium OS Authors.
*
* 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; version 2 of the License.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <libusb.h>
/* SPI flash chips parameters definition */
#include "em100pro_chips.h"
struct em100 {
libusb_device_handle *dev;
libusb_context *ctx;
};
int check_status(libusb_device_handle *dev);
int em100_attach(struct em100 *em100)
{
libusb_device **devs;
libusb_device_handle *dev;
libusb_context *ctx = NULL;
if (libusb_init(&ctx) < 0) {
printf("Could not init libusb.\n");
return 0;
}
libusb_set_debug(ctx, 3);
if (libusb_get_device_list(ctx, &devs) < 0) {
printf("Could not find USB devices.\n");
return 0;
}
dev = libusb_open_device_with_vid_pid(ctx, 0x4b4, 0x1235);
if (!dev) {
printf("Could not find em100pro.\n");
return 0;
}
libusb_free_device_list(devs, 1);
if (libusb_kernel_driver_active(dev, 0) == 1) {
if (libusb_detach_kernel_driver(dev, 0) != 0) {
printf("Could not detach kernel driver.\n");
return 0;
}
}
if (libusb_claim_interface(dev, 0) < 0) {
printf("Could not claim interface.\n");
return 0;
}
if (!check_status(dev)) {
printf("Device status unknown.\n");
return 0;
}
em100->dev = dev;
em100->ctx = ctx;
return 1;
}
int em100_detach(struct em100 *em100)
{
if (libusb_release_interface(em100->dev, 0) != 0) {
printf("releasing interface failed.\n");
return 1;
}
libusb_close(em100->dev);
libusb_exit(em100->ctx);
return 0;
}
int send_cmd(libusb_device_handle *dev, void *data)
{
int actual;
int length = 16; /* haven't seen any other length yet */
libusb_bulk_transfer(dev, 1 | LIBUSB_ENDPOINT_OUT, data, length, &actual, 0);
return (actual == length);
}
int get_response(libusb_device_handle *dev, void *data, int length)
{
int actual;
libusb_bulk_transfer(dev, 2 | LIBUSB_ENDPOINT_IN, data, length, &actual, 0);
return actual;
}
int set_chip_type(libusb_device_handle *dev, const chipdesc *desc)
{
unsigned char cmd[16];
/* result counts unsuccessful send_cmd()s.
* These are then converted in a boolean success value
*/
int result = 0;
int i = 0;
memset(cmd, 0, 16);
cmd[0] = 0x23;
cmd[1] = 0x32;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x3a;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x38;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x40;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x42;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x44;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x46;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x48;
cmd[2] = desc->x23[i++];
cmd[3] = desc->x23[i++];
result += !send_cmd(dev, cmd);
i = 0;
cmd[0] = 0x11;
cmd[1] = 0x02;
cmd[2] = desc->x11[i++];
cmd[3] = desc->x11[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x03;
cmd[2] = desc->x11[i++];
cmd[3] = desc->x11[i++];
result += !send_cmd(dev, cmd);
cmd[1] = 0x04;
cmd[2] = desc->x11[i++];
cmd[3] = desc->x11[i++];
result += !send_cmd(dev, cmd);
return !result;
}
int set_state(libusb_device_handle *dev, int run)
{
unsigned char cmd[16];
memset(cmd, 0, 16);
cmd[0] = 0x23;
cmd[1] = 0x28;
cmd[3] = run & 1;
return send_cmd(dev, cmd);
}
int get_state(libusb_device_handle *dev)
{
unsigned char cmd[16];
unsigned char data[3];
memset(cmd, 0, 16);
cmd[0] = 0x22;
cmd[1] = 0x28;
if (!send_cmd(dev, cmd)) {
return -1; /* error */
}
if (!get_response(dev, data, 3)) {
return -2; /* error */
}
if ((data[0] == 0x02) && (data[1] == 0x00)) {
return data[2]; /* success */
}
return -3; /* error */
}
int check_status(libusb_device_handle *dev)
{
unsigned char cmd[16];
unsigned char data[512];
memset(cmd, 0, 16);
cmd[0] = 0x30; /* status */
if (!send_cmd(dev, cmd)) {
return 0;
}
int len = get_response(dev, data, 512);
if ((len == 3) && (data[0] == 0x20) && (data[1] == 0x20) && (data[2] == 0x15))
return 1;
return 0;
}
int get_version(libusb_device_handle *dev, int *mcu, int *fpga)
{
unsigned char cmd[16];
unsigned char data[512];
memset(cmd, 0, 16);
cmd[0] = 0x10; /* version */
if (!send_cmd(dev, cmd)) {
return 0;
}
int len = get_response(dev, data, 512);
if ((len == 5) && (data[0] == 4)) {
*mcu = (data[3]*100) + data[4];
*fpga = (data[1]*100) + data[2];
return 1;
}
return 0;
}
int read_data(libusb_device_handle *dev, void *data, int length)
{
int actual;
unsigned char cmd[16];
memset(cmd, 0, 16);
cmd[0] = 0x41; /* em100-to-host eeprom data */
/* cmd 1-3 might be start address? Hard code 0 for now, haven't seen anything else in the logs */
/* when doing cmd 1-3, check if 4-6 are end address or length */
cmd[4] = length & 0xff;
cmd[5] = (length >> 8) & 0xff;
cmd[6] = (length >> 16) & 0xff;
if (!send_cmd(dev, cmd)) {
printf("error initiating host-to-em100 transfer.\n");
return 0;
}
libusb_bulk_transfer(dev, 2 | LIBUSB_ENDPOINT_IN, data, length, &actual, 0);
printf("tried reading %d bytes, got %d\n", length, actual);
return (actual == length);
}
int send_data(libusb_device_handle *dev, void *data, int length)
{
int actual;
unsigned char cmd[16];
memset(cmd, 0, 16);
cmd[0] = 0x40; /* host-to-em100 eeprom data */
/* cmd 1-3 might be start address? Hard code 0 for now, haven't seen anything else in the logs */
/* when doing cmd 1-3, check if 4-6 are end address or length */
cmd[4] = length & 0xff;
cmd[5] = (length >> 8) & 0xff;
cmd[6] = (length >> 16) & 0xff;
if (!send_cmd(dev, cmd)) {
printf("error initiating host-to-em100 transfer.\n");
return 0;
}
libusb_bulk_transfer(dev, 1 | LIBUSB_ENDPOINT_OUT, data, length, &actual, 0);
printf("tried sending %d bytes, sent %d\n", length, actual);
return (actual == length);
}
static const struct option longopts[] = {
{"set", 1, 0, 'c'},
{"download", 1, 0, 'd'},
{"start", 0, 0, 'r'},
{"stop", 0, 0, 's'},
{"verify", 0, 0, 'v'},
{"help", 0, 0, 'h'},
{NULL, 0, 0, 0}
};
void usage(void)
{
printf("em100: em100 client utility\n\n"
"-c CHIP|--set CHIP: select CHIP emulation\n"
"-d[ownload] FILE: upload FILE into em100\n"
"-r|--start: em100 shall run\n"
"-s|--stop: em100 shall stop\n"
"-v|--verify: verify EM100 content matches the file\n"
"-h|--help: this help text\n");
}
/* get MCU and FPGA version, *100 encoded */
int main(int argc, char **argv)
{
int opt, idx;
const char *desiredchip = NULL;
const char *filename = NULL;
int do_start = 0, do_stop = 0;
int verify = 0;
while ((opt = getopt_long(argc, argv, "c:d:rsvh",
longopts, &idx)) != -1) {
switch (opt) {
case 'c':
desiredchip = optarg;
break;
case 'd':
filename = optarg;
/* TODO: check that file exists */
break;
case 'r':
do_start = 1;
break;
case 's':
do_stop = 0;
break;
case 'v':
verify = 1;
break;
case 'h':
usage();
return 0;
}
}
const chipdesc *chip = chips;
if (desiredchip) {
do {
if (strcmp(desiredchip, chip->name) == 0) {
printf("will emulate '%s'\n", chip->name);
break;
}
} while ((++chip)->name);
if (chip->name == NULL) {
printf("could not find emulation for '%s'\n", desiredchip);
return 1;
}
}
struct em100 em100;
if (!em100_attach(&em100)) {
return 1;
}
int mcu, fpga;
if (!get_version(em100.dev, &mcu, &fpga)) {
printf("Failed fetching version information.\n");
return 0;
}
printf("MCU version: %d.%d\nFPGA version: %d.%d\n", mcu/100, mcu%100, fpga/100, fpga%100);
if (do_stop) {
set_state(em100.dev, 0);
}
if (desiredchip) {
if (!set_chip_type(em100.dev, chip)) {
printf("Failed configuring chip type.\n");
return 0;
}
}
if (filename) {
int maxlen = 0x800000; /* largest size, right? */
void *data = malloc(maxlen);
if (data == NULL) {
printf("FATAL: couldn't allocate memory\n");
return 1;
}
FILE *fdata = fopen(filename, "rb");
if (!fdata) {
perror("Could not open upload file");
return 1;
}
int length = 0;
while ((!feof(fdata)) && (length < maxlen)) {
int blocksize = 65536;
length += blocksize * fread(data+length, blocksize, 1, fdata);
}
fclose(fdata);
if (length > maxlen) {
printf("FATAL: length > maxlen\n");
return 1;
}
send_data(em100.dev, data, length);
if (verify) {
int done;
void *readback = malloc(length);
if (data == NULL) {
printf("FATAL: couldn't allocate memory\n");
return 1;
}
done = read_data(em100.dev, readback, length);
if (done && (memcmp(data, readback, length) == 0))
printf("Verify: PASS\n");
else
printf("Verify: FAIL\n");
free(readback);
}
free(data);
}
if (do_start) {
set_state(em100.dev, 1);
}
return em100_detach(&em100);
}