common/recipes-utils/ftdicmd/files/cmd-control.c (280 lines of code) (raw):
/*
* ftdi-bitbang
*
* License: MIT
* Authors: Antti Partanen <aehparta@iki.fi>
* https://github.com/aehparta/ftdi-bitbang
*
* Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com)
*
* This program file 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 in a file named COPYING; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb-1.0/libusb.h>
#include <libftdi1/ftdi.h>
#include "ftdi-bitbang.h"
#include "cmd-common.h"
const char opts[] = COMMON_SHORT_OPTS "ENom:d:s:l:x:n:p:A:B:C:D:";
struct option longopts[] = {
COMMON_LONG_OPTS
{ "ee-erase", no_argument, NULL, 'E' },
{ "ee-init", no_argument, NULL, 'N' },
{ "ee-decode", no_argument, NULL, 'o' },
{ "ee-manufacturer", required_argument, NULL, 'm' },
{ "ee-description", required_argument, NULL, 'd' },
{ "ee-serial", required_argument, NULL, 's' },
{ "ee-serial-len", required_argument, NULL, 'l' },
{ "ee-serial-hex", required_argument, NULL, 'x' },
{ "ee-serial-dec", required_argument, NULL, 'n' },
{ "ee-bus-power", required_argument, NULL, 'p' },
{ "ee-ch-a-rs485", required_argument, NULL, 'A' },
{ "ee-ch-b-rs485", required_argument, NULL, 'B' },
{ "ee-ch-c-rs485", required_argument, NULL, 'C' },
{ "ee-ch-d-rs485", required_argument, NULL, 'D' },
{ 0, 0, 0, 0 },
};
/* ftdi device context */
struct ftdi_context *ftdi = NULL;
/* if eeprom should be erased first */
int ee_erase = 0;
/* initialize eeprom to default values, erases eeprom */
int ee_initialize = 0;
/* if eeprom should be decoded */
int ee_decode = 0;
/* if eeprom should be read first */
int ee_rd = 0;
/* if eeprom should be written after */
int ee_wr = 0;
char *set_description = NULL;
char *set_serial = NULL;
int set_serial_len = 0;
int set_serial_mode = 0;
char *set_manufacturer = NULL;
int set_bus_power = 0;
int set_A_rs485 = -1;
int set_B_rs485 = -1;
int set_C_rs485 = -1;
int set_D_rs485 = -1;
int value=0;
/**
* Free resources allocated by process, quit using libraries, terminate
* connections and so on. This function will use exit() to quit the process.
*
* @param return_code Value to be returned to parent process.
*/
void p_exit(int return_code)
{
if (set_description) {
free(set_description);
}
if (set_serial) {
free(set_serial);
}
if (set_manufacturer) {
free(set_manufacturer);
}
if (ftdi) {
ftdi_free(ftdi);
}
/* terminate program instantly */
exit(return_code);
}
void p_help()
{
printf(
" -E, --ee-erase erase eeprom, sometimes needed if eeprom has already been initialized\n"
" -N, --ee-init erase and initialize eeprom with defaults\n"
" -o, --ee-decode read eeprom and print decoded information\n"
" -m, --ee-manufacturer=STRING\n"
" write manufacturer string\n"
" -d, --ee-description=STRING\n"
" write description (product) string\n"
" -s, --ee-serial=STRING write serial string\n"
" -l, --ee-serial-len=LENGTH pad serial with randomized ascii letters and numbers to this length (upper case)\n"
" -x, --ee-serial-hex=LENGTH pad serial with randomized hex to this length (upper case)\n"
" -n, --ee-serial-dec=LENGTH pad serial with randomized numbers to this length\n"
" -p, --ee-bus-power=INT bus power drawn by the device (100-500 mA)\n"
" -A, --ee-ch-a-rs485=(0,1) enable/disable rs485 mode for channel A\n"
" -B, --ee-ch-b-rs485=(0,1) enable/disable rs485 mode for channel B\n"
" -C, --ee-ch-c-rs485=(0,1) enable/disable rs485 mode for channel C\n"
" -D, --ee-ch-d-rs485=(0,1) enable/disable rs485 mode for channel D\n"
"\n"
"Basic control and eeprom routines for FTDI FTx232 chips.\n"
"\n");
}
int p_options(int c, char *optarg)
{
switch (c) {
case 'E':
ee_erase = 1;
return 1;
case 'N':
ee_erase = 1;
ee_initialize = 1;
ee_wr = 1;
return 1;
case 'o':
ee_decode = 1;
ee_rd = 1;
return 1;
case 'm':
set_manufacturer = strdup(optarg);
ee_rd = 1;
ee_wr = 1;
return 1;
case 'd':
set_description = strdup(optarg);
ee_rd = 1;
ee_wr = 1;
return 1;
case 's':
if (set_serial) {
free(set_serial);
}
set_serial = strdup(optarg);
ee_rd = 1;
ee_wr = 1;
return 1;
case 'l': /* mode 0 */
case 'x': /* mode 1 */
case 'n': /* mode 2 */
if (!set_serial) {
set_serial = strdup("");
}
set_serial_len = atoi(optarg);
set_serial_mode = c == 'n' ? 2 : (c == 'x' ? 1 : 0);
return 1;
case 'p':
set_bus_power = atoi(optarg);
if (set_bus_power < 100 || set_bus_power > 500) {
fprintf(stderr, "invalid bus power value, not within 100-500 mA\n");
return -1;
}
ee_rd = 1;
ee_wr = 1;
return 1;
case 'A':
set_A_rs485 = atoi(optarg);
if (set_A_rs485 != 0 && set_A_rs485 != 1) {
fprintf(stderr, "invalid value, not is (0 or 1)\n");
return -1;
}
printf("Set Channel A set RS485 to %d\n", set_A_rs485);
ee_rd = 1;
ee_wr = 1;
return 1;
case 'B':
set_B_rs485 = atoi(optarg);
if (set_B_rs485 != 0 && set_B_rs485 != 1) {
fprintf(stderr, "invalid value, not is (0 or 1)\n");
return -1;
}
printf("Set Channel B set RS485 to %d\n", set_B_rs485);
ee_rd = 1;
ee_wr = 1;
return 1;
case 'C':
set_C_rs485 = atoi(optarg);
if (set_C_rs485 != 0 && set_C_rs485 != 1) {
fprintf(stderr, "invalid value, not is (0 or 1)\n");
return -1;
}
printf("Set Channel C set RS485 to %d\n", set_C_rs485);
ee_rd = 1;
ee_wr = 1;
return 1;
case 'D':
set_D_rs485 = atoi(optarg);
if (set_D_rs485 != 0 && set_D_rs485 != 1) {
fprintf(stderr, "invalid value, not is (0 or 1)\n");
return -1;
}
printf("Set Channel D set RS485 to %d\n", set_D_rs485);
ee_rd = 1;
ee_wr = 1;
return 1;
}
return 0;
}
int main(int argc, char *argv[])
{
/* random seed */
srand(time(NULL));
/* parse command line options */
if (common_options(argc, argv, opts, longopts, 0, 0)) {
fprintf(stderr, "invalid command line option(s)\n");
p_exit(EXIT_FAILURE);
}
/* init ftdi things */
ftdi = common_ftdi_init();
if (!ftdi) {
p_exit(EXIT_FAILURE);
}
if (ee_initialize) {
/* initialize eeprom to defaults */
if (ftdi_eeprom_initdefaults(ftdi, NULL, NULL, NULL)) {
fprintf(stderr, "failed to init defaults to eeprom: %s\n", ftdi_get_error_string(ftdi));
p_exit(EXIT_FAILURE);
}
} else if (ee_rd) {
/* read eeprom */
if (ftdi_read_eeprom(ftdi)) {
fprintf(stderr, "failed to read eeprom: %s\n", ftdi_get_error_string(ftdi));
p_exit(EXIT_FAILURE);
}
if (ftdi_eeprom_decode(ftdi, 0)) {
fprintf(stderr, "failed to decode eeprom after read: %s\n", ftdi_get_error_string(ftdi));
p_exit(EXIT_FAILURE);
}
}
/* erase eeprom */
if (ee_erase) {
if (ftdi_erase_eeprom(ftdi)) {
fprintf(stderr, "failed to erase eeprom: %s\n", ftdi_get_error_string(ftdi));
p_exit(EXIT_FAILURE);
}
}
/* set strings to eeprom */
if (set_manufacturer || set_description || set_serial) {
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(libusb_get_device(ftdi->usb_dev), &desc);
if (!set_manufacturer) {
set_manufacturer = calloc(1, 128);
libusb_get_string_descriptor_ascii(ftdi->usb_dev, desc.iManufacturer, (unsigned char*)set_manufacturer, 127);
}
if (!set_description) {
set_description = calloc(1, 128);
libusb_get_string_descriptor_ascii(ftdi->usb_dev, desc.iProduct, (unsigned char*)set_description, 127);
}
if (!set_serial) {
set_serial = calloc(1, 128);
libusb_get_string_descriptor_ascii(ftdi->usb_dev, desc.iSerialNumber, (unsigned char*)set_serial, 127);
}
if (strlen(set_serial) < set_serial_len) {
int i;
set_serial = realloc(set_serial, set_serial_len + 1);
for (i = strlen(set_serial); i < set_serial_len; i++) {
char c;
char max = set_serial_mode == 2 ? 0 : (set_serial_mode == 1 ? 'F' : 'Z');
/* generate random padding */
do {
c = rand() & 0x7f;
} while ((c < '0' || c > '9') && (c < 'A' || c > max));
set_serial[i] = c;
}
set_serial[set_serial_len] = '\0';
}
ftdi_eeprom_set_strings(ftdi, set_manufacturer, set_description, set_serial);
}
/* bus power */
if (set_bus_power > 100) {
ftdi_set_eeprom_value(ftdi, MAX_POWER, set_bus_power);
}
/* set RS485 Enable/Disable*/
if (set_A_rs485 == 0 || set_A_rs485 == 1){
ftdi_set_eeprom_value(ftdi, CHANNEL_A_RS485, set_A_rs485);
}
if (set_B_rs485 == 0 || set_B_rs485 == 1){
ftdi_set_eeprom_value(ftdi, CHANNEL_B_RS485, set_B_rs485);
}
if (set_C_rs485 == 0 || set_C_rs485 == 1){
ftdi_set_eeprom_value(ftdi, CHANNEL_C_RS485, set_C_rs485);
}
if (set_D_rs485 == 0 || set_D_rs485 == 1){
ftdi_set_eeprom_value(ftdi, CHANNEL_D_RS485, set_D_rs485);
}
/* write eeprom data */
if (ee_wr) {
if (ftdi_eeprom_build(ftdi) < 0) {
fprintf(stderr, "failed to build eeprom: %s\n", ftdi_get_error_string(ftdi));
p_exit(EXIT_FAILURE);
}
if (ftdi_write_eeprom(ftdi)) {
fprintf(stderr, "failed to write eeprom: %s\n", ftdi_get_error_string(ftdi));
p_exit(EXIT_FAILURE);
}
}
/* decode eeprom */
if (ee_decode) {
if (ftdi_eeprom_decode(ftdi, 1)) {
fprintf(stderr, "failed to decode eeprom: %s\n", ftdi_get_error_string(ftdi));
p_exit(EXIT_FAILURE);
}
/* display RS485 mode */
ftdi_get_eeprom_value(ftdi, CHANNEL_A_RS485, &value);
printf("CHANNEL_A_RS485 %d\n", value ? 1 : 0);
ftdi_get_eeprom_value(ftdi, CHANNEL_B_RS485, &value);
printf("CHANNEL_B_RS485 %d\n", value ? 1 : 0);
ftdi_get_eeprom_value(ftdi, CHANNEL_C_RS485, &value);
printf("CHANNEL_C_RS485 %d\n", value ? 1 : 0);
ftdi_get_eeprom_value(ftdi, CHANNEL_D_RS485, &value);
printf("CHANNEL_D_RS485 %d\n", value ? 1 : 0);
}
p_exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
}