Files
2016-01-21 23:42:40 +01:00

406 lines
10 KiB
C

/*
* $Header: /cvsroot/pkgsrc/sysutils/hytctl/files/sbin/hytctl.c,v 1.1 2015/09/06 06:50:53 kardel Exp $
*
* $Created: Fri May 16 20:29:08 2014 $
*/
/*-
* Copyright (c) 2015 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Frank Kardel.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: hytctl.c,v 1.1 2015/09/06 06:50:53 kardel Exp $");
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/gpio.h>
#include "hytp14reg.h"
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <util.h>
#include <dev/i2c/i2c_io.h>
#define ARG_GPIODEV 1
#define ARG_GPIOPIN 2
#define ARG_I2CDEV 3
#define ARG_I2CADDR 4
#define ARG_EEPROMADDR 5
#define ARG_EEPROMVAL 6
static int debug = 0;
__dead static void
usage(void)
{
fprintf(stderr, "usage: %s [-d] <gpio-dev> <pin#|pin-name> <i2c-dev> <i2c-addr> <eeprom-address> [<new eeprom value>]\n", getprogname());
exit(EXIT_FAILURE);
}
static void
waitfor(int usec)
{
struct timeval tv1;
struct timeval tv2;
gettimeofday(&tv1, NULL);
tv2.tv_sec = tv1.tv_sec + usec / 1000000;
tv2.tv_usec = tv1.tv_usec + usec % 1000000;
tv2.tv_sec += tv2.tv_usec / 1000000;
tv2.tv_usec %= 1000000;
while (tv2.tv_sec > tv1.tv_sec || (tv2.tv_usec > tv1.tv_usec && tv2.tv_sec == tv1.tv_sec)) {
gettimeofday(&tv1, NULL);
}
}
static int
iic_write(int fd, i2c_addr_t addr, int clen, uint8_t *cmd, int blen, uint8_t *buf)
{
i2c_ioctl_exec_t iie;
iie.iie_op = I2C_OP_WRITE_WITH_STOP;
iie.iie_addr = addr;
iie.iie_cmd = cmd;
iie.iie_cmdlen = clen;
iie.iie_buf = buf;
iie.iie_buflen = blen;
if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1) {
return errno;
}
return 0;
}
static int
iic_read(int fd, i2c_addr_t addr, int clen, uint8_t *cmd, int blen, uint8_t *buf)
{
i2c_ioctl_exec_t iie;
iie.iie_op = I2C_OP_READ_WITH_STOP;
iie.iie_addr = addr;
iie.iie_cmd = cmd;
iie.iie_cmdlen = clen;
iie.iie_buf = buf;
iie.iie_buflen = blen;
if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1) {
return errno;
}
return 0;
}
static int
exec_cmd(int fd, i2c_addr_t addr, int cmdlen, uint8_t *cmd, int datalen, uint8_t *data)
{
int error;
/* send command */
if ((error = iic_write(fd, addr, cmdlen, cmd, 0, NULL)) == 0)
{
int loops = 0;
for (;;) {
/* read reply */
memset(data, 0, datalen);
if ((error = iic_read(fd, addr, 0, NULL, sizeof data, data)) == 0)
{
if (data[0] & HYTP14_RESP_CMDMODE) {
char b[80];
snprintb(b, sizeof b, HYT_STATUS_FMT,
data[0]);
if (debug) {
fprintf(stderr, "CM_MODE command 0x%02x status=%s\n", cmd[0], b);
}
} else {
if (cmd[0] != HYTP14_CMD_START_NOM)
fprintf(stderr, "CM mode command 0x%02x unexpectedly turned off\n", cmd[0]);
return 0;
}
switch (data[0] & HYTP14_RESP_MASK)
{
case HYTP14_RESP_ACK:
return 1;
case HYTP14_RESP_NACK:
fprintf(stderr, "Command 0x%02x failed with NACK\n", cmd[0]);
return 0;
case HYTP14_RESP_BUSY:
/* do not wait forever for an ACK */
if (++loops > 100) {
fprintf(stderr, "result wait loop count exceeded\n");
return 0;
}
}
} else {
if (cmd[0] != HYTP14_CMD_START_NOM)
fprintf(stderr, "Command 0x%02x READ failed (errno=%d)\n", cmd[0], error);
return 0;
}
}
} else {
fprintf(stderr, "Command 0x%02x WRITE failed (errno=%d)\n", cmd[0], error);
return 0;
}
}
int
main(int argc, char *argv[])
{
int argidx = 0;
int gfd, fd, error;
i2c_addr_t addr = HYTP14_DEFAULT_ADDR;
int eeaddr = 0;
uint8_t data[4];
uint8_t cmd[3];
int count = 0;
char *endptr;
long gpio_pinnumber = -1;
int pinstatus;
struct gpio_info gpioinfo;
struct gpio_req gpioreq;
struct gpio_set gpioset;
setprogname(*argv);
if ((argc > 1) && (strcmp(argv[1], "-d") == 0)) {
debug = 1;
argidx = 1;
}
if ((argc-argidx) <= ARG_EEPROMADDR) {
usage();
}
fd = open(argv[argidx + ARG_I2CDEV], O_RDWR);
if (fd == -1) {
err(EXIT_FAILURE, "couldn't open %s", argv[argidx + ARG_I2CDEV]);
}
addr = strtol(argv[argidx + ARG_I2CADDR], &endptr, 0);
if (addr < 1 || addr > 0x7f || argv[argidx + ARG_I2CADDR][0] == '\0' || *endptr != '\0') {
err(EXIT_FAILURE, "I2C address (%s) is invalid", argv[argidx + ARG_I2CADDR]);
}
eeaddr = strtol(argv[argidx + ARG_EEPROMADDR], &endptr, 0);
if (eeaddr < 0 || eeaddr >= HYTP14_NUM_WORDS || argv[argidx + ARG_EEPROMADDR][0] == '\0' || *endptr != '\0') {
err(EXIT_FAILURE, "HYT EEPROM address (%s) is invalid", argv[argidx + ARG_EEPROMADDR]);
}
gpio_pinnumber = strtol(argv[argidx + ARG_GPIOPIN], &endptr, 0);
if (argv[argidx + ARG_GPIOPIN][0] != '\0' && *endptr == '\0') {
gpioreq.gp_pin = gpio_pinnumber;
gpioreq.gp_name[0] = '\0';
gpioset.gp_pin = gpio_pinnumber;
gpioset.gp_name[0] = '\0';
} else {
gpioreq.gp_pin = -1;
strncpy(gpioreq.gp_name, argv[argidx + ARG_GPIOPIN],
sizeof gpioreq.gp_name);
gpioset.gp_pin = -1;
strncpy(gpioset.gp_name, argv[argidx + ARG_GPIOPIN],
sizeof gpioset.gp_name);
}
gpioset.gp_flags = 0;
gpioset.gp_name2[0] = '\0';
gfd = open(argv[argidx + ARG_GPIODEV], O_RDWR);
if (gfd == -1) {
err(EXIT_FAILURE, "couldn't open %s", argv[argidx + ARG_GPIODEV]);
}
if (ioctl(gfd, GPIOSET, &gpioset) == -1) {
err(EXIT_FAILURE, "failed getting GPIO configuration");
}
if (debug) {
printf("GPIO: Pin %d (%s) flags: 0x%x\n",
gpioset.gp_pin, gpioset.gp_name, gpioset.gp_flags);
}
if ((gpioset.gp_flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) == 0) {
err(EXIT_FAILURE, "Pin %d (%s) not configured for output",
gpioset.gp_pin, gpioset.gp_name);
}
if (ioctl(gfd, GPIOREAD, &gpioreq) == -1) {
err(EXIT_FAILURE, "failed getting GPIO status");
}
if (debug) {
printf("GPIO: Pin %d (%s) state %s\n",
gpioreq.gp_pin, gpioreq.gp_name,
(gpioreq.gp_value == GPIO_PIN_HIGH) ? "HIGH" : "LOW");
}
pinstatus = gpioreq.gp_value;
if (gpioreq.gp_value == GPIO_PIN_HIGH) {
gpioreq.gp_value = GPIO_PIN_LOW;
if (ioctl(gfd, GPIOWRITE, &gpioreq) == -1) {
err(EXIT_FAILURE, "failed switching off gpio pin");
}
}
waitfor(10000);
gpioreq.gp_value = GPIO_PIN_HIGH;
if (ioctl(gfd, GPIOWRITE, &gpioreq) == -1) {
err(EXIT_FAILURE, "failed switching on gpio pin");
}
waitfor(1000);
memset(data, 0, sizeof data);
memset(cmd, 0, sizeof cmd);
cmd[0] = HYTP14_CMD_START_CM;
/* send START_CM command */
while (!exec_cmd(fd, addr, sizeof cmd, cmd, sizeof data, data)
&& ++count < 3)
/* empty */;
if (count >= 3) {
printf("FAILED entering CM mode\n");
} else {
cmd[0] = HYTP14_CMD_GET_REV;
if (exec_cmd(fd, addr, sizeof cmd, cmd, sizeof data, data))
{
printf("Revision = 0x%02x%02x\n", data[1], data[2]);
} else {
errc(EXIT_FAILURE, error,
"sending GET_REV command to %s address 0x%x",
argv[argidx + ARG_I2CDEV], addr);
}
cmd[0] = HYTP14_READ_EEPROM(eeaddr);
if (exec_cmd(fd, addr, sizeof cmd, cmd, sizeof data, data))
{
printf("EEPROM(0x%02x) = 0x%02x%02x\n",
eeaddr, data[1], data[2]);
} else {
errc(EXIT_FAILURE, error,
"sending READ_EEPROM command to %s address 0x%x",
argv[argidx + ARG_I2CDEV], addr);
}
if (argc > argidx + ARG_EEPROMVAL) {
long val;
val = strtol(argv[argidx + ARG_EEPROMVAL], &endptr, 0);
if (argv[argidx + ARG_EEPROMVAL][0] == '\0' || *endptr != '\0') {
err(EXIT_FAILURE,
"EEPROM value (%s) is invalid",
argv[argidx + ARG_EEPROMVAL]);
}
cmd[0] = HYTP14_WRITE_EEPROM(eeaddr);
cmd[1] = (val >> 8) & 0xFF;
cmd[2] = val & 0xFF;
if (exec_cmd(fd, addr,
sizeof cmd, cmd, sizeof data, data))
{
printf("EEPROM(0x%02x) set to 0x%02x%02x\n",
eeaddr, cmd[1], cmd[2]);
memset(cmd, 0, sizeof cmd);
cmd[0] = HYTP14_READ_EEPROM(eeaddr);
if (exec_cmd(fd, addr, sizeof cmd, cmd,
sizeof data, data))
{
printf("EEPROM(0x%02x) = 0x%02x%02x\n",
eeaddr, data[1], data[2]);
}
else
{
errc(EXIT_FAILURE, error,
"sending READ_EEPROM command to %s address 0x%x",
argv[argidx + ARG_I2CDEV], addr);
}
} else {
errc(EXIT_FAILURE, error,
"sending WRITE_EEPROM command to %s address 0x%x",
argv[argidx + ARG_I2CDEV], addr);
}
}
/* leave command mode */
memset(cmd, 0, sizeof cmd);
cmd[0] = HYTP14_CMD_START_NOM;
exec_cmd(fd, addr, sizeof cmd, cmd, sizeof data, data);
}
/* restore original pin status */
gpioreq.gp_value = pinstatus;
if (ioctl(gfd, GPIOWRITE, &gpioreq) == -1) {
err(EXIT_FAILURE, "failed restoring gpio pin status");
} else {
if (debug) {
printf("GPIO: Pin %d (%s) state %s\n",
gpioreq.gp_pin, gpioreq.gp_name,
(gpioreq.gp_value == GPIO_PIN_HIGH) ? "HIGH" : "LOW");
}
}
close(gfd);
close(fd);
return EXIT_SUCCESS;
}
/*
* $Log: hytctl.c,v $
* Revision 1.1 2015/09/06 06:50:53 kardel
* Added hytctl utility to manage HYT 221/271/939 EEPROMS
*
*/