diff --git a/src/common/exec.c b/src/common/exec.c index 6cfce65f..4bd3e423 100644 --- a/src/common/exec.c +++ b/src/common/exec.c @@ -210,6 +210,7 @@ static struct cmd_map { { T_TWOWIRE, cmd_mode_init }, { T_THREEWIRE, cmd_mode_init }, { T_FLASH, cmd_mode_init }, + { T_WIEGAND, cmd_mode_init }, { 0, NULL } }; diff --git a/src/common/mode_config.h b/src/common/mode_config.h index d4d5b40c..546c0156 100644 --- a/src/common/mode_config.h +++ b/src/common/mode_config.h @@ -115,6 +115,14 @@ typedef struct { uint8_t dev_bit_lsb_msb; } onewire_config_t; +typedef struct { + mode_dev_gpio_mode_t dev_gpio_mode; + mode_dev_gpio_pull_t dev_gpio_pull; + uint8_t dev_bit_lsb_msb; + uint32_t dev_pulse_width; + uint32_t dev_pulse_gap; +} wiegand_config_t; + typedef struct { uint32_t dev_speed; mode_dev_gpio_mode_t dev_gpio_mode; @@ -151,6 +159,7 @@ typedef struct { flash_config_t flash; onewire_config_t onewire; rawwire_config_t rawwire; + wiegand_config_t wiegand; sump_config_t sump; hydranfc_config_t hydranfc; } config; diff --git a/src/hydrabus/commands.c b/src/hydrabus/commands.c index 4e506881..62c70f36 100644 --- a/src/hydrabus/commands.c +++ b/src/hydrabus/commands.c @@ -152,6 +152,7 @@ t_token_dict tl_dict[] = { { T_TS1, "ts1" }, { T_TS2, "ts2" }, { T_SJW, "sjw" }, + { T_WIEGAND, "wiegand" }, /* Developer warning add new command(s) here */ /* BP-compatible commands */ @@ -1192,11 +1193,6 @@ t_token tokens_mode_threewire[] = { .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, .help = "Read byte (repeat with :)" }, - { - T_HD, - .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, - .help = "Read byte (repeat with :) and print hexdump" - }, { T_WRITE, .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, @@ -1352,6 +1348,82 @@ t_token tokens_gpio[] = { { } }; +#define WIEGAND_PARAMETERS \ + { T_DEVICE, \ + .arg_type = T_ARG_UINT, \ + .help = "Wiegand device (1)" }, \ + { T_PULL, \ + .arg_type = T_ARG_TOKEN, \ + .subtokens = tokens_gpio_pull, \ + .help = "GPIO pull (up/down/floating)" }, +t_token tokens_mode_wiegand[] = { + { + T_SHOW, + .subtokens = tokens_mode_show, + .help = "Show wiegand parameters" + }, + WIEGAND_PARAMETERS + /* wiegand-specific commands */ + { + T_READ, + .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, + .help = "Read byte (repeat with :)" + }, + { + T_HD, + .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, + .help = "Read byte (repeat with :) and print hexdump" + }, + { + T_WRITE, + .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, + .help = "Write byte (repeat with :)" + }, + { + T_ARG_UINT, + .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, + .help = "Write byte (repeat with :)" + }, + { + T_ARG_STRING, + .help = "Write string" + }, + /* BP commands */ + { + T_MINUS, + .help = "Write one bit (D1)" + }, + { + T_UNDERSCORE, + .help = "Write one bit (D0)" + }, + { + T_AMPERSAND, + .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, + .help = "Delay 1 usec (repeat with :)" + }, + { + T_PERCENT, + .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, + .help = "Delay 1 msec (repeat with :)" + }, + { + T_TILDE, + .flags = T_FLAG_SUFFIX_TOKEN_DELIM_INT, + .help = "Write a random byte (repeat with :)" + }, + { + T_EXIT, + .help = "Exit wiegand mode" + }, + { } +}; + +t_token tokens_wiegand[] = { + WIEGAND_PARAMETERS + { } +}; + t_token tokens_adc[] = { { T_ADC1, @@ -1698,6 +1770,11 @@ t_token tl_tokens[] = { .subtokens = tokens_flash, .help = "NAND flash mode" }, + { + T_WIEGAND, + .subtokens = tokens_wiegand, + .help = "Wiegand mode" + }, { T_DEBUG, .subtokens = tokens_debug, diff --git a/src/hydrabus/commands.h b/src/hydrabus/commands.h index d941287e..35d7b86b 100644 --- a/src/hydrabus/commands.h +++ b/src/hydrabus/commands.h @@ -144,6 +144,7 @@ enum { T_TS1, T_TS2, T_SJW, + T_WIEGAND, /* Developer warning add new command(s) here */ /* BP-compatible commands */ diff --git a/src/hydrabus/hydrabus.mk b/src/hydrabus/hydrabus.mk index b5f8b422..668be49e 100644 --- a/src/hydrabus/hydrabus.mk +++ b/src/hydrabus/hydrabus.mk @@ -29,6 +29,7 @@ HYDRABUSSRC = hydrabus/hydrabus.c \ hydrabus/hydrabus_bbio_flash.c \ hydrabus/hydrabus_sd.c \ hydrabus/hydrabus_trigger.c \ + hydrabus/hydrabus_mode_wiegand.c \ # Required include directories HYDRABUSINC = ./hydrabus diff --git a/src/hydrabus/hydrabus_mode.c b/src/hydrabus/hydrabus_mode.c index 3731b751..224fb607 100644 --- a/src/hydrabus/hydrabus_mode.c +++ b/src/hydrabus/hydrabus_mode.c @@ -48,6 +48,7 @@ extern const mode_exec_t mode_twowire_exec; extern const mode_exec_t mode_threewire_exec; extern const mode_exec_t mode_can_exec; extern const mode_exec_t mode_flash_exec; +extern const mode_exec_t mode_wiegand_exec; extern t_token tokens_mode_spi[]; extern t_token tokens_mode_i2c[]; extern t_token tokens_mode_uart[]; @@ -60,6 +61,7 @@ extern t_token tokens_mode_twowire[]; extern t_token tokens_mode_threewire[]; extern t_token tokens_mode_can[]; extern t_token tokens_mode_flash[]; +extern t_token tokens_mode_wiegand[]; static struct { int token; @@ -78,6 +80,7 @@ static struct { { T_THREEWIRE, tokens_mode_threewire, &mode_threewire_exec }, { T_CAN, tokens_mode_can, &mode_can_exec }, { T_FLASH, tokens_mode_flash, &mode_flash_exec }, + { T_WIEGAND, tokens_mode_wiegand, &mode_wiegand_exec }, }; const char hydrabus_mode_str_cs_enabled[] = "/CS ENABLED\r\n"; diff --git a/src/hydrabus/hydrabus_mode_wiegand.c b/src/hydrabus/hydrabus_mode_wiegand.c new file mode 100644 index 00000000..bf35cfef --- /dev/null +++ b/src/hydrabus/hydrabus_mode_wiegand.c @@ -0,0 +1,320 @@ +/* +* HydraBus/HydraNFC +* +* Copyright (C) 2014-2015 Benjamin VERNOUX +* Copyright (C) 2018 Nicolas OBERLI +* +* 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 "common.h" +#include "tokenline.h" +#include "hydrabus.h" +#include "bsp.h" +#include "bsp_gpio.h" +#include "hydrabus_mode_wiegand.h" +#include "stm32f4xx_hal.h" +#include + +static int exec(t_hydra_console *con, t_tokenline_parsed *p, int token_pos); +static int show(t_hydra_console *con, t_tokenline_parsed *p); + +static const char* str_prompt_wiegand[] = { + "wiegand1" PROMPT, +}; + +void wiegand_init_proto_default(t_hydra_console *con) +{ + mode_config_proto_t* proto = &con->mode->proto; + + /* Defaults */ + proto->dev_num = 0; + proto->config.wiegand.dev_gpio_mode = MODE_CONFIG_DEV_GPIO_OUT_OPENDRAIN; + proto->config.wiegand.dev_gpio_pull = MODE_CONFIG_DEV_GPIO_NOPULL; + proto->config.wiegand.dev_pulse_width = 100; + proto->config.wiegand.dev_pulse_gap = 20000; +} + +static void show_params(t_hydra_console *con) +{ + mode_config_proto_t* proto = &con->mode->proto; + + cprintf(con, "Device: wiegand%d\r\nGPIO resistor: %s\r\n", + proto->dev_num + 1, + proto->config.wiegand.dev_gpio_pull == MODE_CONFIG_DEV_GPIO_PULLUP ? "pull-up" : + proto->config.wiegand.dev_gpio_pull == MODE_CONFIG_DEV_GPIO_PULLDOWN ? "pull-down" : + "floating"); + cprintf(con, "Pulse timing: %dus\r\n", + proto->config.wiegand.dev_pulse_width); + cprintf(con, "Pulse gap timing: %dus\r\n", + proto->config.wiegand.dev_pulse_gap); +} + +static void wiegand_mode_input(t_hydra_console *con) +{ + mode_config_proto_t* proto = &con->mode->proto; + + bsp_gpio_init(BSP_GPIO_PORTB, WIEGAND_D0_PIN, + MODE_CONFIG_DEV_GPIO_IN, proto->config.wiegand.dev_gpio_pull); + bsp_gpio_init(BSP_GPIO_PORTB, WIEGAND_D1_PIN, + MODE_CONFIG_DEV_GPIO_IN, proto->config.wiegand.dev_gpio_pull); +} + +static void wiegand_mode_output(t_hydra_console *con) +{ + mode_config_proto_t* proto = &con->mode->proto; + bsp_gpio_init(BSP_GPIO_PORTB, WIEGAND_D0_PIN, + proto->config.wiegand.dev_gpio_mode, proto->config.wiegand.dev_gpio_pull); + bsp_gpio_init(BSP_GPIO_PORTB, WIEGAND_D1_PIN, + proto->config.wiegand.dev_gpio_mode, proto->config.wiegand.dev_gpio_pull); + bsp_gpio_set(BSP_GPIO_PORTB, WIEGAND_D0_PIN); + bsp_gpio_set(BSP_GPIO_PORTB, WIEGAND_D1_PIN); +} + +bool wiegand_pin_init(t_hydra_console *con) +{ + wiegand_mode_input(con); + return true; +} + +inline void wiegand_d0_high(void) +{ + bsp_gpio_set(BSP_GPIO_PORTB, WIEGAND_D0_PIN); +} + +inline void wiegand_d0_low(void) +{ + bsp_gpio_clr(BSP_GPIO_PORTB, WIEGAND_D0_PIN); +} + +inline void wiegand_d1_high(void) +{ + bsp_gpio_set(BSP_GPIO_PORTB, WIEGAND_D1_PIN); +} + +inline void wiegand_d1_low(void) +{ + bsp_gpio_clr(BSP_GPIO_PORTB, WIEGAND_D1_PIN); +} + +void wiegand_write_bit(t_hydra_console *con, uint8_t bit) +{ + mode_config_proto_t* proto = &con->mode->proto; + + wiegand_mode_output(con); + if(bit){ + wiegand_d1_low(); + DelayUs(proto->config.wiegand.dev_pulse_width); + wiegand_d1_high(); + DelayUs(proto->config.wiegand.dev_pulse_gap); + }else{ + wiegand_d0_low(); + DelayUs(proto->config.wiegand.dev_pulse_width); + wiegand_d0_high(); + DelayUs(proto->config.wiegand.dev_pulse_gap); + } +} + +static void dath(t_hydra_console *con) +{ + wiegand_write_bit(con, 1); + cprintf(con, "BIT 1\r\n"); +} + +static void datl(t_hydra_console *con) +{ + wiegand_write_bit(con, 0); + cprintf(con, "BIT 0\r\n"); +} + +// Returns the status of the pins on 2 bits +// value is inverted because a logical 0 means there is a transmission +static uint8_t wiegand_sense_pins(void) +{ + uint8_t v; + + v = !bsp_gpio_pin_read(BSP_GPIO_PORTB, WIEGAND_D1_PIN)<<1; + v |= (!bsp_gpio_pin_read(BSP_GPIO_PORTB, WIEGAND_D0_PIN)); + + return v; +} + +uint8_t wiegand_read(t_hydra_console *con, uint8_t *rx_data) +{ + uint32_t start_time; + uint16_t i = 0; + uint8_t tmp = 0; + + wiegand_mode_input(con); + + //Wait for first bit + start_time = HAL_GetTick(); + while(tmp == 0 && !palReadPad(GPIOA, 0)) { + if((HAL_GetTick()-start_time) > WIEGAND_TIMEOUT_MAX) { + return 0; + } + tmp = wiegand_sense_pins(); + } + + while(i < 255 && !palReadPad(GPIOA, 0)) { + tmp = wiegand_sense_pins(); + if(tmp == 0) { + if((HAL_GetTick()-start_time) > WIEGAND_TIMEOUT_FRAME) { + return i; + } + } else { + rx_data[i++] = tmp; + while(wiegand_sense_pins() != 0) { + } + start_time = HAL_GetTick(); + } + } + return 0; +} + +void wiegand_write_u8(t_hydra_console *con, uint8_t tx_data) +{ + uint8_t i; + + wiegand_mode_output(con); + + for (i=0; i<8; i++) { + wiegand_write_bit(con, (tx_data>>(7-i)) & 1); + } +} + +static int init(t_hydra_console *con, t_tokenline_parsed *p) +{ + int tokens_used; + + /* Defaults */ + wiegand_init_proto_default(con); + + /* Process cmdline arguments, skipping "wiegand". */ + tokens_used = 1 + exec(con, p, 1); + + wiegand_pin_init(con); + + show_params(con); + + return tokens_used; +} + +static int exec(t_hydra_console *con, t_tokenline_parsed *p, int token_pos) +{ + mode_config_proto_t* proto = &con->mode->proto; + int t; + + for (t = token_pos; p->tokens[t]; t++) { + switch (p->tokens[t]) { + case T_SHOW: + t += show(con, p); + break; + case T_PULL: + switch (p->tokens[++t]) { + case T_UP: + proto->config.wiegand.dev_gpio_pull = MODE_CONFIG_DEV_GPIO_PULLUP; + break; + case T_DOWN: + proto->config.wiegand.dev_gpio_pull = MODE_CONFIG_DEV_GPIO_PULLDOWN; + break; + case T_FLOATING: + proto->config.wiegand.dev_gpio_pull = MODE_CONFIG_DEV_GPIO_NOPULL; + break; + } + wiegand_pin_init(con); + break; + default: + return t - token_pos; + } + } + + return t - token_pos; +} + +static uint32_t write(t_hydra_console *con, uint8_t *tx_data, uint8_t nb_data) +{ + int i; + for (i = 0; i < nb_data; i++) { + wiegand_write_u8(con, tx_data[i]); + } + if(nb_data == 1) { + /* Write 1 data */ + cprintf(con, hydrabus_mode_str_write_one_u8, tx_data[0]); + } else if(nb_data > 1) { + /* Write n data */ + cprintf(con, hydrabus_mode_str_mul_write); + for(i = 0; i < nb_data; i++) { + cprintf(con, hydrabus_mode_str_mul_value_u8, + tx_data[i]); + } + cprintf(con, hydrabus_mode_str_mul_br); + } + return BSP_OK; +} + +static uint32_t read(t_hydra_console *con, uint8_t *rx_data, uint8_t nb_data) +{ + uint8_t i; + uint8_t nb_bits; + + while(nb_data > 0) { + nb_bits = wiegand_read(con, rx_data); + + cprintf(con, hydrabus_mode_str_mul_read); + for(i = 0; i < nb_bits; i++) { + cprintf(con, "%d ", + rx_data[i] == 0b10 ? 1 : 0); + } + cprintf(con, hydrabus_mode_str_mul_br); + nb_data--; + } + return BSP_OK; +} + +void wiegand_cleanup(t_hydra_console *con) +{ + (void)con; +} + +static int show(t_hydra_console *con, t_tokenline_parsed *p) +{ + int tokens_used; + + tokens_used = 0; + if (p->tokens[1] == T_PINS) { + tokens_used++; + cprintf(con, "D0: PB%d\r\n", WIEGAND_D0_PIN); + cprintf(con, "D1: PB%d\r\n", WIEGAND_D1_PIN); + } else { + show_params(con); + } + return tokens_used; +} + +static const char *get_prompt(t_hydra_console *con) +{ + mode_config_proto_t* proto = &con->mode->proto; + return str_prompt_wiegand[proto->dev_num]; +} + +const mode_exec_t mode_wiegand_exec = { + .init = &init, + .exec = &exec, + .write = &write, + .read = &read, + .cleanup = &wiegand_cleanup, + .get_prompt = &get_prompt, + .dath = &dath, + .datl = &datl, +}; + diff --git a/src/hydrabus/hydrabus_mode_wiegand.h b/src/hydrabus/hydrabus_mode_wiegand.h new file mode 100644 index 00000000..ab08eae3 --- /dev/null +++ b/src/hydrabus/hydrabus_mode_wiegand.h @@ -0,0 +1,36 @@ +/* +* HydraBus/HydraNFC +* +* Copyright (C) 2015 Benjamin VERNOUX +* Copyright (C) 2018 Nicolas OBERLI +* +* 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 "hydrabus_mode.h" + +#define WIEGAND_D0_PIN 8 +#define WIEGAND_D1_PIN 9 + +#define WIEGAND_TIMEOUT_MAX 100000 // Max 200ms between bits +#define WIEGAND_TIMEOUT_FRAME 2000 // Max 200ms between bits + +void wiegand_init_proto_default(t_hydra_console *con); +bool wiegand_pin_init(t_hydra_console *con); +uint8_t wiegand_read(t_hydra_console *con, uint8_t *rx_data); +void wiegand_write_u8(t_hydra_console *con, uint8_t tx_data); +inline void wiegand_d0_high(void); +inline void wiegand_d0_low(void); +inline void wiegand_d1_high(void); +inline void wiegand_d1_low(void); +void wiegand_cleanup(t_hydra_console *con);