/* ss-hw/rfid.c * * RFID tag reader functions. * * Copyright (C) 2003, 2004 * Andy Goth * * This code is available under the GNU General Public License; see COPYING. */ #include #include #include #include #include #include #include "evlist/evlist.h" #include "prjlibs/vec.h" #include "ss-hw/rfid.h" /* Default amount of time between inventories. */ #define INVENTORY_MSECS 5000 /* Amount of time before giving up and starting over. Set to 0 to disable. */ #define WATCHDOG_MSECS 10000 * 0 /* Number of inventories until a tag not present is considered to be removed * (min 1). */ #define GRACE_PERIOD 2 /* I type this so often it pays to abbreviate. */ #define BUF VEC_ELTS(byte_t, &dev->buf_in) #if WATCHDOG_MSECS > 0 /* After the timer expires, cancel the inventory in progress, discard the I/O * buffers, and try again. */ #define WATCHDOG_RESET() \ do { \ evlist_sched_del(dev->evlist, rfid->inv_id); \ evlist_sched_add(dev->evlist, WATCHDOG_MSECS, rfid_inv_reset, \ dev, &rfid->inv_id); \ } while (0) #else /* Watchdog is disabled. */ #define WATCHDOG_RESET() #endif typedef struct tag { /* The tag's uid is universally unique. It's used as the primary key for * the inventory database. */ uint64_t uid; /* The tag's type indicates what type of item it is attached to. */ uint32_t type; /* If true, the type field is known. The moment the type is read, the * tag is reported to stdout, so if seen_type is true and the tag leaves * the shelf, the removal must also be reported. */ int seen_type; /* It's definitely possible to add more fields to the tag to describe, say, * expiration date and lot number. */ /* After the grace period expires, the tag is considered to be removed. * This takes care of flaky tags that only respond to every other read. */ int grace; } tag_t; typedef struct rfid { /* List of all tags currently in the RF field. Tags are removed from this * list as they are discovered to have been removed from the field, and * tags are added as soon as they appear in an inventory, even though their * full type information isn't available until after further queries. */ VEC_T(tag_t) tags; /* Device serving as the user interface. All status updates are sent to * this device via the user_send_*() functions. */ device_t* user; /* State of the inventory in progress. See rfid_inventory() for details on * the precise meaning of this field. */ int inv_state; /* Inventory iterator. This is used while getting type information for * multiple tags. */ int inv_iter; /* Id of the inventory/watchdog scheduled callback. If 0, no inventory * is scheduled. */ unsigned int inv_id; /* Number of milliseconds between the end of one inventory operation and * the beginning of the next. If zero or less, automatic inventorying is * disabled. Don't set this too low, or your food will get cancer. :^) */ int inv_delay; /* Desired state of the RF field. If true, the field is switched on. * Disabling the field is deferred until after the current inventory * completes. */ int field; /* Expected command type of the next packet to be received from the RFID * reader. This is set after sending a command. The assumption is that * the reader responds to every last command packet it receives. Sadly, it * sometimes it fails to do so; to cope we use a watchdog which, God * willing, will allow things to get back on track. */ int next_cmd; } rfid_t; static int rfid_open (device_t* dev); static int rfid_close (device_t* dev); static int rfid_cleanup (device_t* dev); static int rfid_incoming(device_t* dev); static int rfid_send (device_t* dev, int ctrl, ...); static int rfid_recv_0x01(device_t* dev); static int rfid_recv_0x23(device_t* dev); static int rfid_inv_reset(void* arg); static int rfid_inv_start(void* dev); static int rfid_inv (device_t* dev); static int rfid_tag_compar(const void* lhs, const void* rhs); static uint16_t rfid_buf_to_crc(byte_t* buf, int len); static uint16_t rfid_buf_to_u16(byte_t* buf); static uint32_t rfid_buf_to_u32(byte_t* buf); static uint64_t rfid_buf_to_u64(byte_t* buf); static void rfid_u16_to_buf(byte_t* buf, uint16_t val); static void rfid_u32_to_buf(byte_t* buf, uint32_t val); static void rfid_u64_to_buf(byte_t* buf, uint64_t val); int rfid_init(device_t* dev, device_t* user) { rfid_t* rfid; if ((rfid = malloc(sizeof(rfid_t))) == NULL) return -1; VEC_INIT(tag_t, &rfid->tags); rfid->user = user; rfid->inv_state = 0; rfid->inv_iter = -1; rfid->inv_id = 0; rfid->inv_delay = INVENTORY_MSECS; rfid->field = 1; rfid->next_cmd = -1; dev->sw_state = rfid ; dev->buf_size = 0 ; dev->sw_open = rfid_open ; dev->sw_close = rfid_close ; dev->sw_cleanup = rfid_cleanup ; dev->sw_incoming = rfid_incoming; dev->sw_outgoing = NULL ; return 0; } int rfid_inv_delay(device_t* dev, int delay) { rfid_t* rfid = dev->sw_state; rfid->inv_delay = delay; if (rfid->inv_state == 0) { if (rfid->inv_id == 0) { /* No inventory in progress; no inventory scheduled. */ } else { /* No inventory in progress; inventory scheduled to happen later. */ evlist_sched_del(dev->evlist, rfid->inv_id); rfid->inv_id = 0; } if (rfid->field) { /* Start an inventory now. */ if (delay >= 0) rfid_inv(dev); } else { /* RF field is off, so don't start an inventory. */ } } else { /* Inventory already in progress. */ } return 0; } int rfid_rf_field(device_t* dev, int enable) { rfid_t* rfid = dev->sw_state; if (rfid->field != enable) { if (enable) { /* Turn the field on. */ rfid_send(dev, 0x6a, 1); if (rfid->inv_delay > 0) { /* Initiate an inventory. */ rfid_inv(dev); } } else if (rfid->inv_state != 0) { /* Defer field deactivation to end of inventory in progress. */ } else { /* Turn field off now. */ rfid_send(dev, 0x6a, 0); if (rfid->inv_id != 0) { /* Cancel upcoming inventory. */ evlist_sched_del(dev->evlist, rfid->inv_id); rfid->inv_id = 0; } } rfid->field = enable; } return 0; } static int rfid_open(device_t* dev) { rfid_inv(dev); return 0; } static int rfid_close(device_t* dev) { rfid_t* rfid = dev->sw_state; evlist_sched_del(dev->evlist, rfid->inv_id); rfid->inv_id = 0; return 0; } static int rfid_cleanup(device_t* dev) { rfid_t* rfid = dev->sw_state; VEC_FREE(tag_t, &rfid->tags); free(rfid); return 0; } static int rfid_incoming(device_t* dev) { rfid_t* rfid = dev->sw_state; int cmd; while (1) { if (VEC_LEN(byte_t, &dev->buf_in) == 0 || VEC_LEN(byte_t, &dev->buf_in) < BUF[0]) { /* Not enough data yet. Later, perhaps. */ return 0; } if (BUF[0] < 6) { /* Runt packet? Very bad. Discard the first byte. */ BUF[0] = 1; } else if (rfid_buf_to_u16(BUF + BUF[0] - 2) != rfid_buf_to_crc(BUF, BUF[0] - 2)) { /* Bad CRC. Discard the whole packet. */ } else { /* Good CRC. */ switch (BUF[2]) { case 0x6a: /* RF on/off. */ if (BUF[3] == 0x00) { /* OK. */ } else { /* Ack! */ fprintf(stderr, "%s (%s): Control = 0x%02x; status = " "0x%02x\n", dev->name, dev->path, BUF[2], BUF[3]); } break; case 0xb0: /* ISO-15693 host command (mandatory/optional). */ cmd = rfid->next_cmd; rfid->next_cmd = -1; switch (cmd) { case 0x01: rfid_recv_0x01(dev); break; case 0x23: rfid_recv_0x23(dev); break; case -1: return -1; } break; default: /* Unexpected. */ fprintf(stderr, "%s (%s): Control = 0x%02x; status = 0x%02x\n", dev->name, dev->path, BUF[2], BUF[3]); } } /* Discard consumed bytes. */ VEC_SHIFTN(byte_t, &dev->buf_in, BUF[0]); } return 0; } static int rfid_send(device_t* dev, int ctrl, ...) { rfid_t* rfid = dev->sw_state; byte_t buf[32]; /* XXX */ int ret = 0; int len; va_list ap; /* XXX: Avoid writing to read-only devices. TODO: make this not suck! */ if (dev->pollfd_out == NULL) return 0; va_start(ap, ctrl); switch (ctrl) { case 0x6a: { /* RF on/off. */ int enable = va_arg(ap, int); len = 6; buf[3] = enable ? 0x01 : 0x00; break; } case 0xb0: { /* ISO-15693 host command (mandatory/optional). */ int cmd = va_arg(ap, int); if (rfid->next_cmd != -1) { /* Ack, disallow sending a second packet before receiving a * response to the first. */ ret = -1; goto end; } else { rfid->next_cmd = cmd; } buf[3] = cmd; switch (cmd) { case 0x01: { /* Inventory. */ int more = va_arg(ap, int); len = 7; buf[4] = more ? 0x80 : 0x00; break; } case 0x23: { /* Read multiple blocks. */ uint64_t uid = va_arg(ap, uint64_t); len = 17; buf[ 4] = 0x01; rfid_u64_to_buf(buf + 5, uid); buf[13] = 0; buf[14] = 1; break; } default: /* Unknown/unsupported. */ ret = -1; goto end; } break; } default: /* Unknown/unsupported. */ ret = -1; goto end; } /* Prepend the header. */ buf[0] = len; buf[1] = 0xfe; buf[2] = ctrl; /* Append the CRC. */ rfid_u16_to_buf(&buf[len - 2], rfid_buf_to_crc(buf, len - 2)); /* Put this packet in the write buffer. */ VEC_PUSHN(byte_t, &dev->buf_out, buf, len); dev->pollfd_out->events |= POLLOUT; end: va_end(ap); return ret; } static int rfid_recv_0x01(device_t* dev) { int i, j; uint64_t uid; tag_t tag; rfid_t* rfid = dev->sw_state; switch (BUF[3]) { case 0x00: /* OK. */ case 0x94: /* More data. */ /* Good data. */ for (i = 0; i < BUF[4]; ++i) { tag.uid = rfid_buf_to_u64(BUF + 7 + i * 10); j = VEC_BSRCHF(tag_t, &rfid->tags, &tag, rfid_tag_compar); if (j < VEC_LEN(tag_t, &rfid->tags) && VEC_ELTS(tag_t, &rfid->tags)[j].uid == tag.uid) { /* Existing tag is still in shelf. Renew grace. */ VEC_ELTS(tag_t, &rfid->tags)[j].grace = GRACE_PERIOD; } else { /* New tag! */ tag.grace = GRACE_PERIOD; tag.seen_type = 0; VEC_INS(tag_t, &rfid->tags, j, &tag, 1); } } break; case 0x01: /* No transponder. */ /* Do nothing. */ break; default: /* Other. */ /* Other error. */ fprintf(stderr, "%s (%s): Control = 0x%02x; command = 0x01; " "status = 0x%02x\n", dev->name, dev->path, BUF[2], BUF[3]); return 0; } if (BUF[3] == 0x94) { /* More data available; request the next packet. */ rfid_send(dev, 0xb0, 0x01, 1); } else if (BUF[3] == 0x00 || BUF[3] == 0X01) { /* No more data; move on to the next phase of the inventory. */ rfid_inv(dev); } return 0; } static int rfid_recv_0x23(device_t* dev) { rfid_t* rfid = dev->sw_state; tag_t* tag = &VEC_ELTS(tag_t, &rfid->tags)[rfid->inv_iter]; switch (BUF[3]) { case 0x00: /* OK. */ /* Store and report type. */ tag->type = rfid_buf_to_u32(BUF + 7); tag->seen_type = 1; user_send_tag_add(rfid->user, tag->uid, tag->type); /* Next! */ rfid_inv(dev); return 0; case 0x01: /* No transponder. */ /* Wait for next inventory to check tag. */ rfid_inv(dev); return 0; case 0x95: /* ISO error. */ fprintf(stderr, "%s (%s): Control = 0x%02x; command = 0x23; " "status = 0x%02x; iso-error = 0x%02x\n", dev->name, dev->path, BUF[2], BUF[3], BUF[4]); return 0; default: /* Other error. */ fprintf(stderr, "%s (%s): Control = 0x%02x; command = 0x23; " "status = 0x%02x\n", dev->name, dev->path, BUF[2], BUF[3]); return 0; } } static int rfid_inv_reset(void* arg) { device_t* dev = arg; rfid_t* rfid = dev->sw_state; VEC_TRUNC(byte_t, &dev->buf_in, 0); VEC_TRUNC(byte_t, &dev->buf_out, 0); rfid->inv_state = 0; rfid->next_cmd = -1; /* XXX: is this right? */ if (rfid->field) { if (rfid->inv_delay > 0) { /* Start inventory over again. */ rfid_inv(dev); } else { /* Inventorying disabled, for the time being. */ } } else { /* Deactivate the RF field. Don't reschedule the inventory. */ rfid_send(dev, 0x6a, 0); } fprintf(stderr, "Watchdog timeout expired.\n"); /* XXX: Should I send a RESET of some sort? */ return 0; } static int rfid_inv_start(void* dev) { return rfid_inv(dev); } static int rfid_inv(device_t* dev) { int i; rfid_t* rfid = dev->sw_state; tag_t* tag; switch (rfid->inv_state) { case 0: /* Start the inventory by querying the uids. */ for (i = 0; i < VEC_LEN(tag_t, &rfid->tags); ++i) { --VEC_ELTS(tag_t, &rfid->tags)[i].grace; } rfid_send(dev, 0xb0, 0x01, 0); /* Wait for response(s) to the 0x01. */ rfid->inv_state = 1; WATCHDOG_RESET(); return 0; case 1: /* Find tag removals. */ for (i = 0; i < VEC_LEN(tag_t, &rfid->tags); ++i) { tag = &VEC_ELTS(tag_t, &rfid->tags)[i]; if (tag->grace == 0) { /* Tag has left the RF field. */ if (tag->seen_type) { /* Tag previously reported with an ADD. */ user_send_tag_del(rfid->user, tag->uid); } VEC_CUT(tag_t, &rfid->tags, i, NULL, 1); --i; } } /* Fall through. */ rfid->inv_state = 2; rfid->inv_iter = -1; case 2: /* Find tags with unknown types. */ ++rfid->inv_iter; for (i = rfid->inv_iter; i < VEC_LEN(tag_t, &rfid->tags); ++i) { tag = &VEC_ELTS(tag_t, &rfid->tags)[i]; if (!tag->seen_type) { /* New tag, or type otherwise not yet known. */ rfid->inv_iter = i; rfid_send(dev, 0xb0, 0x23, tag->uid); /* Wait for response to 0x23. */ WATCHDOG_RESET(); return 0; } } /* Done with inventory! Schedule it to happen again in a while. */ rfid->inv_state = 0; rfid->inv_iter = -1; evlist_sched_del(dev->evlist, rfid->inv_id); if (rfid->field) { /* RF field is active. */ if (rfid->inv_delay > 0) { evlist_sched_add(dev->evlist, rfid->inv_delay, rfid_inv_start, dev, &rfid->inv_id); } else { /* Well, okay, don't schedule it after all. :^) */ rfid->inv_id = 0; } } else { /* Deactivate the RF field. Don't reschedule the inventory. */ rfid_send(dev, 0x6a, 0); } return 0; } } static int rfid_tag_compar(const void* lhs, const void* rhs) { uint64_t x = ((const tag_t*)lhs)->uid; uint64_t y = ((const tag_t*)rhs)->uid; if (x < y) return -1; else if (x > y) return 1; else return 0; } static uint16_t rfid_buf_to_crc(byte_t* buf, int len) { uint16_t crc = ~0; int i, j; for (i = 0; i < len; ++i) { crc ^= buf[i]; for (j = 0; j < 8; ++j) { if (crc & 1) crc = (crc >> 1) ^ 0x8408; else crc = crc >> 1; } } return (((crc >> 8) & 255) << 0) | (((crc >> 0) & 255) << 8); } static uint16_t rfid_buf_to_u16(byte_t* buf) { return ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1] << 0); } static uint32_t rfid_buf_to_u32(byte_t* buf) { return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | ((uint32_t)buf[3] << 0); } static uint64_t rfid_buf_to_u64(byte_t* buf) { return ((uint64_t)buf[0] << 56) | ((uint64_t)buf[1] << 48) | ((uint64_t)buf[2] << 40) | ((uint64_t)buf[3] << 32) | ((uint64_t)buf[4] << 24) | ((uint64_t)buf[5] << 16) | ((uint64_t)buf[6] << 8) | ((uint64_t)buf[7] << 0); } static void rfid_u16_to_buf(byte_t* buf, uint16_t val) { buf[0] = (val >> 8) & 255; buf[1] = (val >> 0) & 255; } static void rfid_u32_to_buf(byte_t* buf, uint32_t val) { buf[0] = (val >> 24) & 255; buf[1] = (val >> 16) & 255; buf[2] = (val >> 8) & 255; buf[3] = (val >> 0) & 255; } static void rfid_u64_to_buf(byte_t* buf, uint64_t val) { buf[0] = (val >> 56) & 255; buf[1] = (val >> 48) & 255; buf[2] = (val >> 40) & 255; buf[3] = (val >> 32) & 255; buf[4] = (val >> 24) & 255; buf[5] = (val >> 16) & 255; buf[6] = (val >> 8) & 255; buf[7] = (val >> 0) & 255; } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */