blob: 93bd62e01a88bd422f125f9f5135ae58cb3f2ead [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* From Nugget OS */
#include <application.h>
#include <app_nugget.h>
#include <nos/device.h>
#include <nos/transport.h>
/* Our connection to Citadel */
static struct nos_device dev;
/* Our big transfer buffer. Apps may have smaller size limits. */
static uint8_t buf[0x4000];
enum {
BOARD_BINDER,
BOARD_PROTO1,
BOARD_EVT,
};
/* Global options */
static struct option_s {
/* program-specific options */
uint8_t app_id;
uint16_t param;
int more;
int ascii;
int binary;
int verbose;
int buttons;
int board;
/* generic connection options */
const char *device;
} option = {
.board = BOARD_EVT,
};
enum no_short_opts_for_these {
OPT_DEVICE = 1000,
OPT_BUTTONS,
OPT_BINDER,
OPT_PROTO1,
OPT_EVT,
};
static char *short_opts = ":hi:p:m:abv";
static const struct option long_opts[] = {
/* name hasarg *flag val */
{"id", 1, NULL, 'i'},
{"param", 1, NULL, 'p'},
{"more", 1, NULL, 'm'},
{"ascii", 0, NULL, 'a'},
{"binary", 0, NULL, 'b'},
{"verbose", 0, NULL, 'v'},
{"buttons", 0, NULL, OPT_BUTTONS},
{"binder", 0, &option.board, BOARD_BINDER},
{"proto1", 0, &option.board, BOARD_PROTO1},
{"evt", 0, &option.board, BOARD_EVT},
{"device", 1, NULL, OPT_DEVICE},
{"help", 0, NULL, 'h'},
{NULL, 0, NULL, 0},
};
static void usage(const char *progname)
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s\n"
"Usage: %s test\n\n"
" Quick test to see if Citadel is responsive\n"
"\n"
" Options:\n"
" --buttons Prompt to press buttons\n"
" --binder | --proto1 | --evt Specify the board\n\n",
progname, progname);
fprintf(stderr, "Usage: %s tpm COMMAND [BYTE ...]\n"
"\n"
" Transmit the COMMAND and possibly any BYTEs using the\n"
" TPM Wait mode driver. COMMAND and BYTEs are hex values.\n"
"\n"
" Options:\n"
" -m, --more NUM Exchange NUM additional bytes\n\n",
progname);
fprintf(stderr, "Usage: %s app [BYTE [BYTE...]]\n"
"\n"
" Call an application function, passing any BYTEs as args\n"
"\n"
" Options:\n"
" -i --id HEX App ID (default 0x00)\n"
" -p, --param HEX Set the command Param field to HEX"
" (default 0x0000)\n\n",
progname);
fprintf(stderr, "Usage: %s rw ADDRESS [VALUE]\n"
"\n"
" Read or write a memory address on Citadel. Both ADDRESS\n"
" and VALUE are 32-bit hex numbers.\n\n",
progname);
fprintf(stderr, "Common options:\n\n"
" -a, --ascii Print response as ASCII string\n"
" -b, --binary Dump binary response to stdout\n"
" -v, --verbose Increase verbosity. More is noisier\n"
" --device PATH spidev device file to open\n"
" -h, --help Show this message\n"
"\n\n");
}
/****************************************************************************/
/* Handy stuff */
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
static int errorcnt;
static void Error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "ERROR: ");
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
errorcnt++;
}
static void debug(int lvl, const char *format, ...)
{
va_list ap;
if (lvl > option.verbose)
return;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
static void debug_buf(int lvl, uint8_t *buf, int bufsize)
{
int i;
if (lvl > option.verbose)
return;
if (bufsize <= 0)
return;
if (option.binary) {
fwrite(buf, bufsize, 1, stdout);
return;
}
if (option.ascii) {
for (i = 0; i < bufsize; i++)
printf("%c", isprint(buf[i]) ? buf[i] : '.');
printf("\n");
return;
}
for (i = 0; i < bufsize;) {
if (!(i % 16))
printf("0x%06x: ", i);
printf(" %02x", buf[i]);
i++;
if (!(i % 16))
printf("\n");
}
if (i % 16)
printf("\n");
}
static void debug_retval(int lvl, uint32_t retval, uint32_t replycount)
{
if (lvl > option.verbose)
return;
printf("retval 0x%08x (", retval);
switch (retval) {
case APP_SUCCESS:
printf("success");
break;
case APP_ERROR_BOGUS_ARGS:
printf("bogus args");
break;
case APP_ERROR_INTERNAL:
printf("app is being stupid");
break;
case APP_ERROR_TOO_MUCH:
printf("caller sent too much data");
break;
default:
if (retval >= APP_SPECIFIC_ERROR && retval < APP_LINE_NUMBER_BASE)
printf("app-specific error #%d", retval - APP_SPECIFIC_ERROR);
else if (retval >= APP_LINE_NUMBER_BASE)
printf("error at line %d", retval - APP_LINE_NUMBER_BASE);
else
printf("unknown");
}
printf("), replycount 0x%x (%d)\n", replycount, replycount);
}
/****************************************************************************/
/* Functionality */
static void do_tpm(int argc, char *argv[])
{
char *e = 0;
int i, rv, argcount, optind = 1;
uint32_t command, buflen;
/* Must have a command */
if (optind < argc) {
command = strtoul(argv[optind], &e, 16);
if (e && *e)
Error("%s: Invalid COMMAND: \"%s\"", argv[0], argv[optind]);
optind++;
} else {
Error("%s: Missing required COMMAND", argv[0]);
return;
}
/* how many bytes to exchange? */
argcount = argc - optind;
buflen = option.more + argcount;
if (buflen > MAX_DEVICE_TRANSFER) {
Error("%s: Too much to send", argv[0]);
return;
}
/* preload BYTEs from command line */
for (i = 0; i < argcount; i++) {
buf[i] = 0xff & strtoul(argv[optind], &e, 16);
if (e && *e) {
Error("%s: Invalid byte value: \"%s\"", argv[0], argv[optind]);
return;
}
optind++;
}
/* Okay, let's do something */
debug(1, "Command 0x%08x, buflen 0x%x\n", command, buflen);
if (command & 0x80000000)
rv = dev.ops.read(dev.ctx, command, buf, buflen);
else
rv = dev.ops.write(dev.ctx, command, buf, buflen);
if (rv != 0)
Error("%s: nuts", argv[0]);
else if (command & 0x80000000)
debug_buf(0, buf, buflen);
}
static void do_app(int argc, char *argv[])
{
char *e = 0;
int optind = 1;
uint32_t i, buflen, replycount, retval;
/* preload BYTEs from command line */
buflen = argc - optind;
for (i = 0; i < buflen; i++) {
buf[i] = 0xff & strtoul(argv[optind], &e, 16);
if (e && *e) {
Error("%s: Invalid byte value: \"%s\"", argv[0], argv[optind]);
return;
}
optind++;
}
debug(1, "AppID 0x%02x, App param 0x%04x, buflen 0x%x\n",
option.app_id, option.param, buflen);
replycount = sizeof(buf);
retval = nos_call_application(&dev, option.app_id, option.param,
buf, buflen, buf, &replycount);
debug_retval(1, retval, replycount);
debug_buf(0, buf, replycount);
}
/****************************************************************************/
/* Available for bringup/debug only. See b/65067435 */
static uint32_t read32(uint32_t address, uint32_t *valptr)
{
uint32_t buflen, replycount, retval;
debug(2, "read from 0x%08x\n", address);
buflen = sizeof(address);
memcpy(buf, &address, buflen);
replycount = sizeof(buf);
retval = nos_call_application(&dev, APP_ID_NUGGET, NUGGET_PARAM_READ32,
buf, buflen, buf, &replycount);
debug_retval(2, retval, replycount);
if (replycount == sizeof(*valptr)) {
memcpy(valptr, buf, sizeof(*valptr));
debug(2, "value is 0x%08x\n", *valptr);
}
return retval;
}
static uint32_t write32(uint32_t address, uint32_t value)
{
uint32_t buflen, replycount, retval;
struct nugget_app_write32 w32;
/* Writing to address */
debug(2, "write to 0x%08x with value 0x%08x\n", address, value);
w32.address = address;
w32.value = value;
buflen = sizeof(w32);
memcpy(buf, &w32, buflen);
replycount = sizeof(buf);
retval = nos_call_application(&dev, APP_ID_NUGGET, NUGGET_PARAM_WRITE32,
buf, buflen, buf, &replycount);
debug_retval(2, retval, replycount);
return retval;
}
static void do_rw(int argc, char *argv[])
{
char *e = 0;
uint32_t retval, address, value;
argc = MIN(argc, 3); /* ignore any extra args */
switch (argc) {
case 3:
value = strtoul(argv[2], &e, 16);
if (e && *e) {
Error("%s: Invalid value: \"%s\"", argv[0], argv[2]);
return;
}
/* fall through */
case 2:
address = strtoul(argv[1], &e, 16);
if (e && *e) {
Error("%s: Invalid address: \"%s\"", argv[0], argv[1]);
return;
}
break;
default:
Error("%s: Missing required address", argv[0]);
return;
}
if (argc == 2) {
retval = read32(address, &value);
if (APP_SUCCESS != retval)
Error("%s: Read failed", argv[0]);
else
printf("0x%08x\n", value);
} else {
retval = write32(address, value);
if (APP_SUCCESS != retval)
Error("%s: Write failed", argv[0]);
}
}
/****************************************************************************/
/*
* This stuff is a quick dead-or-alive test for SMT. We assume that the Citadel
* chip itself will work because it's passed its own manufacturing tests, but
* we need to know that the chip is powered up and the SPI bus and GPIOs are
* working. UART passthrough will have to be tested externally.
*/
/* ARM GPIO config registers */
#define GPIO_DATA 0x40550000
#define GPIO_DATAOUT 0x40550004
#define GPIO_OUTENSET 0x40550010
/* Return true on success */
static int write_to_file(const char *filename, const char *string)
{
int fd, rv;
ssize_t len, num;
/* Assume valid input */
len = strlen(string);
fd = open(filename, O_WRONLY | O_SYNC);
if (fd < 0) {
debug(1, "can't open %s for writing: %s", filename, strerror(errno));
return 0;
}
num = write(fd, string, len);
debug(2, "%s(%s, %s) wrote %d / %d\n", __func__, filename, string, num, len);
if (len != num) {
debug(1, "can't write %d bytes to %s: %s", len, filename,
strerror(errno));
rv = close(fd);
if (rv)
debug(1, "can't close the file descriptor either: %s",
strerror(errno));
return 0;
}
rv = close(fd);
if (rv) {
debug(1, "can't close the file descriptor for %s: %s",
filename, strerror(errno));
return 0;
}
return 1;
}
/* Return true on success */
static int read_from_file(const char *filename, char *buf, ssize_t bufsize)
{
int fd, rv;
ssize_t num;
fd = open(filename, O_RDONLY);
if (fd < 0) {
debug(2, "can't open %s for reading: %s", filename, strerror(errno));
return 0;
}
num = read(fd, buf, bufsize - 1); /* leave room for '\0' */
debug(2, "%s(%s) read %d bytes\n", __func__, filename, num);
if (num < 0) {
debug(1, "can't read from %s: %s", filename, strerror(errno));
rv = close(fd);
if (rv)
debug(1, "can't close the file descriptor either: %s",
strerror(errno));
return 0;
}
if (num == 0) {
debug(1, "file %s contains no data", filename);
rv = close(fd);
if (rv)
debug(1, "can't close the file descriptor either: %s",
strerror(errno));
return 0;
}
debug_buf(2, (unsigned char *)buf, num);
rv = close(fd);
if (rv)
debug(1, "can't close the file descriptor for %s: %s",
filename, strerror(errno));
return 1;
}
/* Returns true if we're able to export this gpio */
static int is_ap_exported(uint32_t num)
{
char filename[80];
char buf[80];
debug(1, "%s(%d)\n", __func__, num);
/* It might already be exported. Try to read the value to see. */
sprintf(filename, "/sys/class/gpio/gpio%d/value", num);
memset(buf, 0, sizeof(buf));
if (read_from_file(filename, buf, sizeof(buf)))
return 1; /* yep */
/* Request it */
sprintf(buf, "%d", num);
if (!write_to_file("/sys/class/gpio/export", buf)) {
Error("%s: Can't request export of gpio %d", __func__, num);
return 0;
}
/* Try reading the value again */
memset(buf, 0, sizeof(buf));
if (read_from_file(filename, buf, sizeof(buf)))
return 1; /* yep */
Error("%s: Nuts. Can't get export of gpio %d", __func__, num);
return 0;
}
/* Returns true if we're able to set this gpio to an output */
static int is_ap_output(uint32_t num)
{
char filename[80];
char buf[80];
debug(1, "%s(%d)\n", __func__, num);
/* It might already be an output. Let's see. */
sprintf(filename, "/sys/class/gpio/gpio%d/direction", num);
memset(buf, 0, sizeof(buf));
if (!read_from_file(filename, buf, sizeof(buf))) {
debug(1, "Can't determine the direction of gpio %d", num);
return 0;
}
if (!strncmp("out", buf, 3))
return 1; /* already an output */
/* Set the direction */
sprintf(buf, "%s", "out");
if (!write_to_file(filename, buf)) {
Error("%s: Can't set the direction of gpio %d", __func__, num);
return 0;
}
/* Check again */
memset(buf, 0, sizeof(buf));
if (!read_from_file(filename, buf, sizeof(buf))) {
Error("%s: Can't determine the direction of gpio %d", __func__, num);
return 0;
}
if (!strncmp("out", buf, 3))
return 1; /* yep, it's an output */
Error("%s: Nuts. Can't set the direction of gpio %d", __func__, num);
return 0;
}
/* Return true on success */
static int set_ap_value(uint32_t num, int val)
{
char filename[80];
char buf[80];
debug(1, "%s(%d, %d)\n", __func__, num, val);
sprintf(filename, "/sys/class/gpio/gpio%d/value", num);
sprintf(buf, "%d", val);
if (!write_to_file(filename, buf)) {
Error("%s: can't set gpio %d to %d", __func__, num, val);
return 0;
}
return 1;
}
/* This wiggles a GPIO on the AP and checks to make sure Citadel saw it */
static void ap_wiggle(const char *name, uint32_t cit_gpio, uint32_t ap_gpio)
{
uint32_t prev, curr;
uint32_t cit_bit;
cit_bit = (1 << cit_gpio);
printf("Test %s AP gpio %d => Citadel gpio %d\n", name, ap_gpio, cit_gpio);
/* Configure AP for output */
if (!is_ap_exported(ap_gpio))
return;
if (!is_ap_output(ap_gpio))
return;
debug(1, "cit_bit 0x%08x\n", cit_bit);
/* drive low, confirm low */
if (!set_ap_value(ap_gpio, 0))
return;
if (0 != read32(GPIO_DATA, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
debug(1, "citadel 0x%08x\n", curr);
if (curr & cit_bit)
Error("%s: expected cit_gpio %d low, was high", __func__, cit_gpio);
prev = curr;
/* Drive high, confirm high, no other bits changed */
if (!set_ap_value(ap_gpio, 1))
return;
if (0 != read32(GPIO_DATA, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
debug(1, "citadel 0x%08x\n", curr);
if (!(curr & cit_bit))
Error("expected cit_gpio %d high, was low", cit_gpio);
if (prev != curr && (prev ^ curr) != cit_bit)
Error("unexpected GPIO change: prev 0x%08x cur 0x%08x", prev, curr);
prev = curr;
/* Drive Low, confirm low again, no other bits changed */
if (!set_ap_value(ap_gpio, 0))
return;
if (0 != read32(GPIO_DATA, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
debug(1, "citadel 0x%08x\n", curr);
if (curr & cit_bit)
Error("expected cit_gpio %d low, was high", cit_gpio);
if (prev != curr && (prev ^ curr) != cit_bit)
Error("unexpected GPIO change: prev 0x%08x cur 0x%08x", prev, curr);
}
static int stopped;
static void sig_handler(int sig)
{
stopped = 1;
printf("Signal %d recognized\n", sig);
}
/* This prompts the user to push a button and waits for Citadel to see it */
static void phys_wiggle(uint32_t cit_gpio, const char *button)
{
uint32_t prev, curr;
uint32_t cit_bit;
stopped = 0;
signal(SIGINT, sig_handler);
cit_bit = (1 << cit_gpio);
debug(1, "%s(%d) cit_bit 0x%08x\n", __func__, cit_gpio, cit_bit);
/* read initial value */
if (0 != read32(GPIO_DATA, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
debug(1, "initial value 0x%08x\n", curr);
prev = curr;
printf("\nPlease PRESS the %s button...\n", button);
do {
usleep(100 * 1000);
if (0 != read32(GPIO_DATA, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
} while (prev == curr && !stopped);
debug(1, "new value 0x%08x\n", curr);
if ((prev ^ curr) != cit_bit)
Error("%s: multiple cit_gpios changed: prev 0x%08x cur 0x%08x",
__func__, prev, curr);
prev = curr;
printf("Please RELEASE the %s button...\n", button);
do {
usleep(100 * 1000);
if (0 != read32(GPIO_DATA, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
} while (prev == curr && !stopped);
debug(1, "new value 0x%08x\n", curr);
if ((prev ^ curr) != cit_bit)
Error("%s: multiple cit_gpios changed: prev 0x%08x cur 0x%08x",
__func__, prev, curr);
signal(SIGINT, SIG_DFL);
}
/* How long to wait for an interrupt to trigger (msecs) */
#define INTERRUPT_TIMEOUT 100
/* Make Citadel wiggle its CTDL_AP_IRQ output, which we should notice */
static void cit_interrupt(const char *name, uint32_t cit_gpio)
{
uint32_t curr;
uint32_t cit_bit;
int rv;
printf("Test %s Citadel gpio %d => AP kernel driver\n", name, cit_gpio);
cit_bit = (1 << cit_gpio);
debug(1, "%s(%s, %d) cit_bit 0x%08x\n", __func__, name, cit_gpio, cit_bit);
/* First, let's see what Citadel is driving */
if (0 != read32(GPIO_DATAOUT, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
debug(1, "initial value 0x%08x\n", curr);
if (curr & cit_bit) {
/* Citadel is already driving it, so we should see it immediately */
rv = dev.ops.wait_for_interrupt(dev.ctx, INTERRUPT_TIMEOUT);
if (rv > 1) {
debug(2, "CTDL_AP_IRQ is already asserted\n");
} else {
Error("%s: CTDL_AP_IRQ is 1, but wait_for_interrupt() returned %d",
__func__, rv);
return;
}
/* Tell Citadel to stop driving it */
if (0 != write32(GPIO_DATAOUT, curr & (~cit_bit))) {
Error("%s: can't write Citadel GPIOs", __func__);
return;
}
/* Make sure it obeyed */
if (0 != read32(GPIO_DATAOUT, &curr)) {
Error("%s: can't read Citadel GPIOs", __func__);
return;
}
if (curr & cit_bit) {
Error("%s: Citadel isn't changing its GPIOs", __func__);
return;
}
}
debug(1, "there should be no immediate interrupt\n");
rv = dev.ops.wait_for_interrupt(dev.ctx, INTERRUPT_TIMEOUT);
if (rv == 0) {
debug(2, "CTDL_AP_IRQ not asserted and not triggered\n");
} else {
Error("%s: CTDL_AP_IRQ is 0, but wait_for_interrupt() returned %d",
__func__, rv);
return;
}
debug(1, "tell Citadel to trigger an interrupt\n");
if (0 != write32(GPIO_DATAOUT, curr | cit_bit)) {
Error("%s: can't write Citadel GPIOs", __func__);
return;
}
rv = dev.ops.wait_for_interrupt(dev.ctx, INTERRUPT_TIMEOUT);
if (rv > 0) {
debug(2, "CTDL_AP_IRQ is asserted and triggered\n");
} else {
Error("%s: CTDL_AP_IRQ is 1, but wait_for_interrupt() returned %d",
__func__, rv);
return;
}
/* Tell Citadel to stop driving it */
if (0 != write32(GPIO_DATAOUT, curr & (~cit_bit))) {
Error("%s: can't write Citadel GPIOs", __func__);
return;
}
}
static void do_test(void)
{
uint32_t retval, replycount, value;
int ctdl_ap_irq_is_driven = 0;
/* Using the Transport API only. Nugget OS doesn't have any Datagram apps. */
printf("Get version string...\n");
replycount = sizeof(buf);
retval = nos_call_application(&dev, APP_ID_NUGGET, NUGGET_PARAM_VERSION,
buf, 0, buf, &replycount);
if (retval != 0) {
Error("Get version failed with 0x%08x", retval);
debug_retval(0, retval, replycount);
goto done;
}
if (replycount < 4 || replycount > 1024)
Error("Get version returned %d bytes, which seems wrong", replycount);
/* might be okay, though */
debug_buf(1, buf, replycount);
/*
* We want to drive each GPIO from the AP side and just check that
* Citadel can see it wiggle. Citadel treats them all as inputs for
* now. We'll have to update our tests when that changes, of course.
*/
printf("Read GPIO direction\n");
retval = read32(GPIO_OUTENSET, &value);
if (retval != 0) {
Error("Reading GPIO direction failed with 0x%08x", retval);
goto done;
}
switch (value) {
case 0x00000000:
debug(1, "Citadel's GPIOs are all inputs\n");
break;
case 0x00000080:
debug(1, "Citadel is driving CTDL_AP_IRQ\n");
ctdl_ap_irq_is_driven = 1;
break;
default:
/* This is unexpected, but keep going */
Error("GPIO direction = 0x%08x\n", value);
}
/*
* The MSM GPIOs have moved all around with each revision.
*
* Net Name Citadel Pin BINDER B1PROTO1 B1EVT1
*
* CTDL_AP_IRQ DIOA5 7 96 96 129
* AP_CTDL_IRQ DIOA11 6 94 94 135
* AP_SEC_STATE DIOB7 4 76 76 76
* AP_PWR_STATE DIOB8 5 69 69 69
* CCD_CABLE_DET DIOA6 8 127 126 126
*/
if (ctdl_ap_irq_is_driven) {
/* Citadel should interrupt us */
cit_interrupt("CTDL_AP_IRQ", 7);
} else {
/* We'll wiggle the AP's GPIO and make sure Citadel sees it */
if (option.board == BOARD_EVT)
ap_wiggle("CTDL_AP_IRQ", 7, 129);
else
ap_wiggle("CTDL_AP_IRQ", 7, 96);
}
if (option.board == BOARD_EVT)
ap_wiggle("AP_CTDL_IRQ", 6, 135);
else
ap_wiggle("AP_CTDL_IRQ", 6, 94);
ap_wiggle("AP_SEC_STATE", 4, 76);
ap_wiggle("AP_PWR_STATE", 5, 69);
if (option.board == BOARD_BINDER)
ap_wiggle("CCD_CABLE_DET", 8, 127);
else
ap_wiggle("CCD_CABLE_DET", 8, 126);
/*
* Citadel should be able to drive all the physical buttons under
* certain circumstances, but I don't know how to confirm whether the
* AP sees them change. We'll have to prompt the user to poke them to
* verify the connectivity. That's probably tested elsewhere, though.
*/
if (option.buttons) {
if (option.board != BOARD_PROTO1)
/* We had to cut this trace on proto1 (b/66976641) */
phys_wiggle(0, "Power");
phys_wiggle(1, "Volume Down");
phys_wiggle(2, "Volume Up");
if (option.board == BOARD_BINDER)
/* There's only a button on the binder board */
phys_wiggle(10, "Forced USB Boot");
}
/*
* These are harder to test. We'll have to change the UART passthrough
* to access the Citadel console and do these manually:
*
* Citadel GPIO 3 MSM_RST_OUT_L should wiggle when the phone reboots
* Citadel GPIO 9 PM_MSM_RST_L should force the AP to reboot
*/
done:
if (errorcnt)
printf("\nFAIL FAIL FAIL\n\n");
else
printf("\nPASS PASS PASS\n\n");
}
/****************************************************************************/
/*
* Any SPI bus activity will wake Citadel from deep sleep, so we'll just send
* it a single bogus command. If Citadel's already awake, it will ignore it.
* We don't bother tracking or reporting errors. The test will report any real
* errors.
*/
#define IGNORED_COMMAND (CMD_ID(APP_ID_TEST) | CMD_PARAM(0xffff))
static void poke_citadel(void)
{
int rv;
rv = dev.ops.write(dev.ctx, IGNORED_COMMAND, 0, 0);
/* If Citadel was asleep, give it some time to wake up */
if (rv == -EAGAIN)
usleep(50000);
}
static int stopping_citadeld_fixed_it;
static int connect_to_citadel(void)
{
int rv = nos_device_open(option.device, &dev);
if (rv == -EBUSY) {
/* Try stopping citadeld */
debug(1, "citadel device is busy, stopping citadeld...\n");
if (system("setprop ctl.stop vendor.citadeld") == 0) {
/* See if that helped */
rv = nos_device_open(option.device, &dev);
if (rv == 0) {
debug(1, " okay, that worked\n");
stopping_citadeld_fixed_it = 1;
return rv;
} else {
debug(1, " nope, didn't help\n");
}
} else {
debug(1, " huh. couldn't stop it\n");
}
}
if (rv)
Error("Unable to connect to Citadel: %s", strerror(-rv));
return rv;
}
static void disconnect_from_citadel(void)
{
dev.ops.close(dev.ctx);
if (stopping_citadeld_fixed_it) {
debug(1, "We stopped citadeld earlier, so start it up again\n");
(void)system("setprop ctl.start vendor.citadeld");
}
}
int main(int argc, char *argv[])
{
int i;
int idx = 0;
char *e = 0;
char *this_prog;
this_prog= strrchr(argv[0], '/');
if (this_prog)
this_prog++;
else
this_prog = argv[0];
opterr = 0; /* quiet, you */
while ((i = getopt_long(argc, argv, short_opts, long_opts, &idx)) != -1) {
switch (i) {
/* program-specific options */
case 'i':
option.app_id = (uint8_t)strtoul(optarg, &e, 16);
if (!*optarg || (e && *e))
Error("Invalid argument: \"%s\"", optarg);
break;
case 'p':
option.param = (uint16_t)strtoul(optarg, &e, 16);
if (!*optarg || (e && *e))
Error("Invalid argument: \"%s\"", optarg);
break;
case 'm':
option.more = (uint32_t)strtoul(optarg, &e, 0);
if (!*optarg || (e && *e) || option.more < 0)
Error("Invalid argument: \"%s\"", optarg);
break;
case 'a':
option.ascii = 1;
option.binary = 0;
break;
case 'b':
option.ascii = 0;
option.binary = 1;
break;
case 'v':
option.verbose++;
break;
case OPT_BUTTONS:
option.buttons = 1;
break;
/* generic options below */
case OPT_DEVICE:
option.device = optarg;
break;
case 'h':
usage(this_prog);
return 0;
case 0:
break;
case '?':
if (optopt)
Error("Unrecognized option: -%c", optopt);
else
Error("Unrecognized option: %s", argv[optind - 1]);
usage(this_prog);
break;
case ':':
Error("Missing argument to %s", argv[optind - 1]);
break;
default:
Error("Internal error at %s:%d", __FILE__, __LINE__);
exit(1);
}
}
if (errorcnt)
return !!errorcnt;
if (connect_to_citadel() != 0)
return !!errorcnt;
/* Wake Citadel from deep sleep */
poke_citadel();
/*
* We can freely intermingle options and args, so the function should
* be the first non-option. Try to pick it out if it exists.
*/
if (argc > optind) {
if (!strcmp("tpm", argv[optind]))
do_tpm(argc - optind, argv + optind);
else if (!strcmp("app", argv[optind]))
do_app(argc - optind, argv + optind);
else if (!strcmp("rw", argv[optind]))
do_rw(argc - optind, argv + optind);
else
do_test();
/*
* "test" is the default function, but it doesn't take any args
* so anything not listed is just silently ignored. Too bad.
*/
} else {
do_test();
}
disconnect_from_citadel();
return !!errorcnt;
}