summaryrefslogtreecommitdiff
path: root/libyhy.c
diff options
context:
space:
mode:
Diffstat (limited to 'libyhy.c')
-rw-r--r--libyhy.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/libyhy.c b/libyhy.c
new file mode 100644
index 0000000..041b0b7
--- /dev/null
+++ b/libyhy.c
@@ -0,0 +1,288 @@
+#include "libyhy.h"
+#include <stdint.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <stdio.h>
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#ifdef DEBUG
+ #define DBG(x) (x)
+#else
+ #define DBG(x)
+#endif
+
+#define DEFAULT_NODE_ID 0
+
+//TODO
+//0x AA substitution where applicable
+//Confirm checksum on receive
+//Handle status flags on return
+
+#pragma pack(push, 1)
+typedef struct {
+ uint16_t magic; //0xAABB
+ uint16_t csum_span;
+ uint16_t node;
+ uint16_t command;
+} yhy_transmit_t;
+
+typedef struct {
+ uint16_t magic; //0xAABB
+ uint16_t csum_span;
+ uint16_t node;
+ uint16_t command;
+ uint8_t status; //0 on success
+} yhy_response_t;
+#pragma pack(pop)
+
+enum /*_yhy_command*/ {
+ YHY_SET_BAUDRATE = 0x0101,
+ /* uint8_t baudrate_id
+ * 0 -> 4800
+ * 1 -> 9600
+ * 2 -> 14400
+ * 3 -> 19200
+ * 4 -> 38800
+ * 5 -> 38400
+ * 6 -> 57600
+ * 7 -> 115200
+ */
+ YHY_SET_NODEID = 0x0102,
+ /* uint16_t nodeid */
+ YHY_GET_NODEID = 0x0103,
+ /* No payload */
+ YHY_READ_VERSION = 0x0104,
+ /* No payload */
+ YHY_BEEP = 0x0106,
+ /* uint8_t beeptime in 10ms increments */
+ YHY_LED = 0x0107,
+ /* uint8_t LED status bitfield
+ * Bit 0 -> Green LED
+ * Bit 1 -> Red LED
+ */
+ YHY_ANTENNA = 0x010C,
+ /* uint8_t anntenna state
+ * * 0 -> Antenna off
+ * * 1 -> Antenna on
+ */
+ YHY_MIFARE_REQUEST = 0x0201,
+ /* uint8_t command
+ * * 0x52 -> Wake up all cards (WUPA)
+ * * 0x26 -> Request idle cards (REQA)
+ */
+ YHY_MIFARE_ANTICOLL = 0x0202,
+ /* No payload */
+ YHY_MIFARE_SELECT = 0x0203,
+ /* uint32_t Card UID */
+ YHY_MIFARE_HLTA = 0x0204,
+ /* No payload */
+ YHY_MIFARE_AUTH = 0x0207,
+ /* uint8_t auth_mode
+ * * 0x60 -> Key A
+ * * 0x61 -> Key B
+ * uint8_t block
+ * uint8_t[6] key
+ */
+ YHY_MIFARE_READ = 0x0208,
+ /* Payload uint8_t block */
+ YHY_MIFARE_WRITE = 0x0209,
+ /* uint8_t block
+ * uint8_t[16] data */
+ YHY_ULTRALIGHT_SELECT = 0x0212,
+ /* No payload */
+ YHY_ULTRALIGHT_WRITE = 0x0213
+ /* uint8_t page
+ * uint8_t[4] data */
+};
+
+static void xxd(uint8_t* buf, size_t len){
+ size_t n;
+ for(n = 0; n < len; n++){
+ printf("%02X ", buf[n]);
+ }
+}
+
+static ssize_t yhy_send_command(int fd, uint16_t node, uint16_t cmd, uint8_t* payload, size_t len){
+ size_t n;
+ uint8_t checksum = 0x00;
+ yhy_transmit_t header = {
+ .magic = htobe16(0xAABB),
+ .csum_span = htole16(len + 5),
+ .node = node,
+ .command = htole16(cmd)
+ };
+
+ for(n = 4; n < sizeof(header); n++){
+ checksum ^= ((uint8_t*)(&header))[n];
+ }
+
+ for(n = 0; n < len; n++){
+ checksum ^= payload[n];
+ }
+
+ DBG(printf("Out: "));
+ DBG(xxd((uint8_t*) &header, sizeof(header)));
+ DBG(xxd(payload, len));
+ DBG(xxd(&checksum, 1));
+ DBG(printf("\n"));
+
+ write(fd, &header, sizeof(header));
+ if(len){
+ write(fd, payload, len);
+ }
+ write(fd, &checksum, 1);
+ return 0;
+}
+
+static ssize_t yhy_receive_response(int fd, uint8_t* response, size_t max_len){
+ //maximum received data is probably firmware version, assume 1024 bytes
+ uint8_t recv_buffer[1024] = "";
+ yhy_response_t* resp = (yhy_response_t*) recv_buffer;
+ read(fd, recv_buffer, sizeof(yhy_response_t));
+
+ //UL_SELECT returns wrong length from device
+ if(resp->command == YHY_ULTRALIGHT_SELECT && resp->status == 0){
+ DBG(printf("Fixing UL_SELECT response length (was %d)\n", le16toh(resp->csum_span)));
+ resp->csum_span = htole16(0x0D);
+ }
+
+ uint16_t bytes_left = le16toh(resp->csum_span) - 5;
+ read(fd, recv_buffer + sizeof(yhy_response_t), bytes_left);
+
+ DBG(printf("In: "));
+ DBG(xxd(recv_buffer, sizeof(yhy_response_t)));
+ DBG(printf(" | "));
+ DBG(xxd(recv_buffer + sizeof(yhy_response_t), bytes_left));
+ DBG(printf("\n"));
+
+ if(bytes_left > 1 && response && max_len){
+ memcpy(response, recv_buffer + sizeof(yhy_response_t), MIN(bytes_left - 1, max_len));
+ }
+ return bytes_left - 1;
+}
+
+static ssize_t yhy_sync(int fd, uint16_t node, uint16_t cmd, uint8_t* payload, size_t payload_len, uint8_t* response, size_t response_len){
+ yhy_send_command(fd, node, cmd, payload, payload_len);
+ return yhy_receive_response(fd, response, response_len);
+}
+
+int yhy_open(char* device){
+ struct termios tty;
+
+ int fd = open(device, O_RDWR);
+ if(fd < 0) {
+ return -1;
+ }
+
+ if(tcgetattr(fd, &tty)){
+ return -1;
+ }
+
+ cfsetospeed(&tty, B115200);
+ cfsetispeed(&tty, B115200);
+
+ tty.c_iflag &= ~(IXON | IXOFF | IXANY);
+ tty.c_cflag |= (CLOCAL | CREAD);
+ tty.c_cflag &= ~PARENB;
+ tty.c_cflag &= ~CSTOPB;
+ tty.c_cflag &= ~CSIZE;
+ tty.c_cflag &= ~CRTSCTS;
+ tty.c_cflag |= CS8;
+ tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+
+ if(tcsetattr(fd, TCSAFLUSH, &tty)){
+ return -1;
+ }
+
+ return fd;
+}
+
+int yhy_sync_read_version(int fd, char* version, size_t max_len){
+ ssize_t bytes = yhy_sync(fd, DEFAULT_NODE_ID, YHY_READ_VERSION, NULL, 0, (uint8_t*) version, max_len);
+ if(bytes >= max_len){
+ version[max_len] = 0;
+ }
+ else{
+ version[bytes] = 0;
+ }
+ return bytes;
+}
+
+int yhy_sync_beep(int fd, uint8_t time){
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_BEEP, &time, 1, NULL, 0);
+}
+
+int yhy_sync_led(int fd, uint8_t led_bits){
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_LED, &led_bits, 1, NULL, 0);
+}
+
+int yhy_sync_antenna(int fd, uint8_t enable){
+ uint8_t status = enable ? 1 : 0;
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_ANTENNA, &status, 1, NULL, 0);
+}
+
+int yhy_sync_request(int fd, uint8_t wakeup, uint16_t* atqa){
+ *atqa = 0;
+
+ //Select WUPA or REQA
+ uint8_t wup = wakeup ? 0x52 : 0x26;
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_MIFARE_REQUEST, &wup, 1, (uint8_t*) atqa, 2);
+}
+
+int yhy_sync_anticoll(int fd, uint8_t* serial, size_t length){
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_MIFARE_ANTICOLL, NULL, 0, serial, length);
+}
+
+int yhy_sync_select(int fd, uint8_t* serial, size_t length, uint8_t* sak){
+ *sak = 0;
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_MIFARE_SELECT, serial, length, sak, 1);
+}
+
+int yhy_sync_hlta(int fd){
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_MIFARE_HLTA, NULL, 0, NULL, 0);
+}
+
+int yhy_sync_auth(int fd, uint8_t key_a, uint8_t block, uint8_t key[6]){
+ struct {
+ uint8_t mode;
+ uint8_t block;
+ uint8_t key[6];
+ } auth_payload = {
+ .mode = (key_a ? 0x60 : 0x61),
+ .block = block
+ };
+ memcpy(&(auth_payload.key), key, 6);
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_MIFARE_AUTH, (uint8_t*) (&auth_payload), sizeof(auth_payload), NULL, 0);
+}
+
+int yhy_sync_read(int fd, uint8_t block, uint8_t* data, size_t max_len){
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_MIFARE_READ, &block, 1, data, max_len);
+}
+
+int yhy_sync_write(int fd, uint8_t block, uint8_t data[16]){
+ struct {
+ uint8_t block;
+ uint8_t data[16];
+ } write_payload = {
+ .block = block
+ };
+ memcpy(&(write_payload.data), data, 16);
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_MIFARE_WRITE, (uint8_t*) (&write_payload), sizeof(write_payload), NULL, 0);
+}
+
+int yhy_sync_ulselect(int fd, uint8_t* serial, size_t length){
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_ULTRALIGHT_SELECT, NULL, 0, serial, length);
+}
+
+int yhy_sync_ulwrite(int fd, uint8_t block, uint8_t data[4]){
+ struct {
+ uint8_t block;
+ uint8_t data[4];
+ } write_payload = {
+ .block = block
+ };
+ memcpy(&(write_payload.data), data, 4);
+ return yhy_sync(fd, DEFAULT_NODE_ID, YHY_ULTRALIGHT_WRITE, (uint8_t*) (&write_payload), sizeof(write_payload), NULL, 0);
+}