From 3897fabc5c94f7041c95a13ad04d2e32c944c1f1 Mon Sep 17 00:00:00 2001 From: laforge Date: Tue, 14 Jun 2005 12:41:42 +0000 Subject: - start 15693 implementation - build a real library (librfid.a) git-svn-id: https://svn.gnumonks.org/trunk/librfid@1215 e0336214-984f-0b4b-a45f-81c69e1f0ede --- Makefile | 5 +- TODO | 4 + include/rfid/rfid_asic_rc632.h | 3 + include/rfid/rfid_layer2.h | 3 +- include/rfid/rfid_layer2_iso15693.h | 48 ++++++ include/rfid/rfid_reader.h | 2 +- rfid_asic_rc632.c | 252 ++++++++++++++++++++++++++++++ rfid_layer2_iso15693.c | 302 ++++++++++++++++++++++++++++++++++++ rfid_reader_cm5121.c | 6 + 9 files changed, 622 insertions(+), 3 deletions(-) create mode 100644 include/rfid/rfid_layer2_iso15693.h create mode 100644 rfid_layer2_iso15693.c diff --git a/Makefile b/Makefile index bcf5f44..ac1ca50 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,12 @@ LDFLAGS:=-lopenct -lusb all: openct-escape -openct-escape: openct-escape.o rfid_layer2.o rfid_layer2_iso14443a.o rfid_layer2_iso14443b.o rfid_asic_rc632.o rfid_reader_cm5121.o rfid.o rfid_protocol.o rfid_proto_tcl.o rfid_iso14443_common.o rfid_reader.o +openct-escape: openct-escape.o librfid.a $(CC) $(LDFLAGS) -o $@ $^ +librfid.a: rfid_layer2.o rfid_layer2_iso14443a.o rfid_layer2_iso14443b.o rfid_layer2_iso15693.o rfid_asic_rc632.o rfid_reader_cm5121.o rfid.o rfid_protocol.o rfid_proto_tcl.o rfid_iso14443_common.o rfid_reader.o + ar r $@ $^ + %.o: %.c $(CC) $(CFLAGS) -o $@ -c $^ diff --git a/TODO b/TODO index d8045ea..6c94530 100644 --- a/TODO +++ b/TODO @@ -11,6 +11,10 @@ iso14443b: - implement 'option 2' frame markers - test anticollission (need multiple tags) +iso15693: +- implement anticollision +- implement all the rest + tcl: - test chaining - implement + test pps diff --git a/include/rfid/rfid_asic_rc632.h b/include/rfid/rfid_asic_rc632.h index f4c9fa1..4aabde2 100644 --- a/include/rfid/rfid_asic_rc632.h +++ b/include/rfid/rfid_asic_rc632.h @@ -51,6 +51,9 @@ struct rfid_asic_rc632 { struct { int (*init)(struct rfid_asic_handle *h); } iso14443b; + struct { + int (*init)(struct rfid_asic_handle *h); + } iso15693; } fn; }; diff --git a/include/rfid/rfid_layer2.h b/include/rfid/rfid_layer2.h index 40b9bff..975022a 100644 --- a/include/rfid/rfid_layer2.h +++ b/include/rfid/rfid_layer2.h @@ -6,6 +6,7 @@ struct rfid_reader_handle; #include #include +#include struct rfid_layer2 { @@ -31,7 +32,7 @@ struct rfid_layer2_handle { union { struct iso14443a_handle iso14443a; struct iso14443b_handle iso14443b; - //struct iso15693_handle iso15693; + struct iso15693_handle iso15693; } priv; struct rfid_layer2 *l2; }; diff --git a/include/rfid/rfid_layer2_iso15693.h b/include/rfid/rfid_layer2_iso15693.h new file mode 100644 index 0000000..b418ccd --- /dev/null +++ b/include/rfid/rfid_layer2_iso15693.h @@ -0,0 +1,48 @@ +#ifndef _RFID_ISO15693_H +#define _RFID_ISO15693_H + +#include + +/* protocol definitions */ + +struct iso15693_handle; + +struct iso15693_transport { + unsigned char *name; + + struct { + int (*init)(struct iso15693_handle *handle); + int (*fini)(struct iso15693_handle *handle); + +#if 0 + int (*transcieve_sf)(struct iso14443a_handle *handle, + unsigned char cmd, + struct iso14443a_atqa *atqa); + int (*transcieve_acf)(struct iso14443a_handle *handle, + struct iso14443a_anticol_cmd *acf, + unsigned int *bit_of_col); +#endif + int (*transcieve)(struct iso15693_handle *handle, + const unsigned char *tx_buf, + unsigned int tx_len, + unsigned char *rx_buf, + unsigned int *rx_len); + } fn; + + union { + } priv; +}; + +struct iso15693_handle { + unsigned int state; +}; + +enum iso15693_state { + ISO15693_STATE_ERROR, + ISO15693_STATE_NONE, +}; + +#include +extern struct rfid_layer2 rfid_layer2_iso15693; + +#endif /* _ISO15693_H */ diff --git a/include/rfid/rfid_reader.h b/include/rfid/rfid_reader.h index 11f2ca1..5f6c314 100644 --- a/include/rfid/rfid_reader.h +++ b/include/rfid/rfid_reader.h @@ -31,7 +31,7 @@ struct rfid_reader { unsigned int speed; } iso14443b; struct rfid_15693_reader { - + int (*init)(struct rfid_reader_handle *rh); } iso15693; struct rfid_reader *next; }; diff --git a/rfid_asic_rc632.c b/rfid_asic_rc632.c index 234e7ab..b77050f 100644 --- a/rfid_asic_rc632.c +++ b/rfid_asic_rc632.c @@ -830,6 +830,255 @@ static int rc632_iso14443b_init(struct rfid_asic_handle *handle) return 0; } +static int +rc632_iso15693_init(struct rfid_asic_handle *h) +{ + int ret; + + ret = rc632_reg_write(h, RC632_REG_TX_CONTROL, + (RC632_TXCTRL_MOD_SRC_INT | + RC632_TXCTRL_TX2_INV | + RC632_TXCTRL_TX2_RF_EN | + RC632_TXCTRL_TX1_RF_EN)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CW_CONDUCTANCE, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_CONDUCTANCE, 0x03); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CODER_CONTROL, + (RC632_CDRCTRL_RATE_15693 | + 0x03)); /* FIXME */ + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_TYPE_B_FRAMING, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_CONTROL1, + (RC632_RXCTRL1_SUBCP_16 | + RC632_RXCTRL1_ISO15693 | + RC632_RXCTRL1_GAIN_35DB)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_DECODER_CONTROL, + (RC632_DECCTRL_RXFR_15693 | + RC632_DECCTRL_RX_INVERT)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_BIT_PHASE, 0xe0); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_THRESHOLD, 0xff); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_BPSK_DEM_CONTROL, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_CONTROL2, + (RC632_RXCTRL2_AUTO_PD | + RC632_RXCTRL2_DECSRC_INT)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CHANNEL_REDUNDANCY, + (RC632_CR_CRC3309 | + RC632_CR_RX_CRC_ENABLE | + RC632_CR_TX_CRC_ENABLE)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_LSB, 0xff); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_MSB, 0xff); + if (ret < 0) + return ret; + + return 0; +} + +static int +rc632_iso15693_icode_init(struct rfid_asic_handle *h) +{ + int ret; + + ret = rc632_reg_write(h, RC632_REG_TX_CONTROL, + (RC632_TXCTRL_MOD_SRC_INT | + RC632_TXCTRL_TX2_INV | + RC632_TXCTRL_TX2_RF_EN | + RC632_TXCTRL_TX1_RF_EN)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CW_CONDUCTANCE, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_CONDUCTANCE, 0x02); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CODER_CONTROL, 0x2c); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_TYPE_B_FRAMING, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_CONTROL1, 0x8b); /* FIXME */ + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_DECODER_CONTROL, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_BIT_PHASE, 0x52); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_THRESHOLD, 0x66); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_BPSK_DEM_CONTROL, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_CONTROL2, + RC632_RXCTRL2_DECSRC_INT); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CHANNEL_REDUNDANCY, + (RC632_CR_RX_CRC_ENABLE | + RC632_CR_TX_CRC_ENABLE)); + ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_LSB, 0xfe); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_MSB, 0xff); + if (ret < 0) + return ret; + + return 0; +} + +static int +rc632_iso15693_icl_init(struct rfid_asic_handle *h) +{ + int ret; + + /* ICL */ + + ret = rc632_reg_write(h, RC632_REG_TX_CONTROL, + (RC632_TXCTRL_MOD_SRC_INT | + RC632_TXCTRL_TX2_INV | + RC632_TXCTRL_TX2_RF_EN | + RC632_TXCTRL_TX1_RF_EN)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CW_CONDUCTANCE, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_CONDUCTANCE, 0x11); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CODER_CONTROL, + (RC632_CDRCTRL_RATE_15693 | + RC632_CDRCTRL_TXCD_ICODE_STD | + 0x03)); /* FIXME */ + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH, 0x3f); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_MOD_WIDTH_SOF, 0x3f); + if (ret < 0) + return ret; + ret = rc632_reg_write(h, RC632_REG_RX_CONTROL1, + (RC632_RXCTRL1_SUBCP_16| + RC632_RXCTRL1_ISO15693| + RC632_RXCTRL1_GAIN_35DB)); + if (ret < 0) + return ret; + ret = rc632_reg_write(h, RC632_REG_DECODER_CONTROL, + (RC632_DECCTRL_RX_INVERT| + RC632_DECCTRL_RXFR_15693)); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_BIT_PHASE, 0xbd); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_THRESHOLD, 0xff); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_BPSK_DEM_CONTROL, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_RX_CONTROL2, + RC632_RXCTRL2_DECSRC_INT); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CHANNEL_REDUNDANCY, 0x00); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_LSB, 0x12); + if (ret < 0) + return ret; + + ret = rc632_reg_write(h, RC632_REG_CRC_PRESET_MSB, 0xe0); + if (ret < 0) + return ret; + + return 0; +} + struct rfid_asic rc632 = { .name = "Philips CL RC632", @@ -848,6 +1097,9 @@ struct rfid_asic rc632 = { .fn.iso14443b = { .init = &rc632_iso14443b_init, }, + .fn.iso15693 = { + .init = &rc632_iso15693_init, + }, }, }; diff --git a/rfid_layer2_iso15693.c b/rfid_layer2_iso15693.c new file mode 100644 index 0000000..2c9eb3b --- /dev/null +++ b/rfid_layer2_iso15693.c @@ -0,0 +1,302 @@ +/* ISO 15693 anticollision implementation + * + * (C) 2005 by Harald Welte + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +#if 0 +/* Transcieve a 7-bit short frame */ +static int +iso14443a_transcieve_sf(struct rfid_layer2_handle *handle, + unsigned char cmd, + struct iso14443a_atqa *atqa) +{ + struct rfid_reader *rdr = handle->rh->reader; + + return rdr->iso14443a.transcieve_sf(handle->rh, cmd, atqa); +} + +/* Transmit an anticollission bit frame */ +static int +iso14443a_transcieve_acf(struct rfid_layer2_handle *handle, + struct iso14443a_anticol_cmd *acf, + unsigned int *bit_of_col) +{ + struct rfid_reader *rdr = handle->rh->reader; + + return rdr->iso14443a.transcieve_acf(handle->rh, acf, bit_of_col); +} + +/* Transmit a regular frame */ +static int +iso14443a_transcieve(struct rfid_layer2_handle *handle, + const unsigned char *tx_buf, unsigned int tx_len, + unsigned char *rx_buf, unsigned int *rx_len, + unsigned int timeout, unsigned int flags) +{ + return handle->rh->reader->transcieve(handle->rh, tx_buf, tx_len, + rx_buf, rx_len, timeout, flags); +} + +static int +iso14443a_code_nvb_bits(unsigned char *nvb, unsigned int bits) +{ + unsigned int byte_count = bits / 8; + unsigned int bit_count = bits % 8; + + if (byte_count < 2 || byte_count > 7) + return -1; + + *nvb = ((byte_count & 0xf) << 4) | bit_count; + + return 0; +} + +/* first bit is '1', second bit '2' */ +static void +set_bit_in_field(unsigned char *bitfield, unsigned int bit) +{ + unsigned int byte_count = bit / 8; + unsigned int bit_count = bit % 8; + + DEBUGP("bitfield=%p, byte_count=%u, bit_count=%u\n", + bitfield, byte_count, bit_count); + DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count)); + *(bitfield+byte_count) |= 1 << (bit_count-1); + DEBUGP("%p = 0x%02x\n", (bitfield+byte_count), *(bitfield+byte_count)); +} + +static int +iso14443a_anticol(struct rfid_layer2_handle *handle) +{ + int ret; + unsigned int uid_size; + struct iso14443a_atqa atqa; + struct iso14443a_anticol_cmd acf; + unsigned int bit_of_col; + unsigned char sak[3]; + unsigned char uid[10]; // triple size equals 10 bytes; + unsigned int rx_len = sizeof(sak); + char *aqptr = (char *) &atqa; + static int first = 0; + + memset(uid, 0, sizeof(uid)); + memset(sak, 0, sizeof(sak)); + memset(&atqa, 0, sizeof(atqa)); + memset(&acf, 0, sizeof(acf)); + + if (first == 0) { + DEBUGP("Sending REQA\n"); + ret = iso14443a_transcieve_sf(handle, ISO14443A_SF_CMD_REQA, &atqa); + first = 1; + } else { + DEBUGP("Sending WUPA\n"); + ret = iso14443a_transcieve_sf(handle, ISO14443A_SF_CMD_WUPA, &atqa); + } + + if (ret < 0) { + handle->priv.iso14443a.state = ISO14443A_STATE_REQA_SENT; + DEBUGP("error during transcieve_sf: %d\n", ret); + return ret; + } + handle->priv.iso14443a.state = ISO14443A_STATE_ATQA_RCVD; + + DEBUGP("ATQA: 0x%02x 0x%02x\n", *aqptr, *(aqptr+1)); + + if (!atqa.bf_anticol) { + handle->priv.iso14443a.state =ISO14443A_STATE_NO_BITFRAME_ANTICOL; + DEBUGP("no bitframe anticollission bits set, aborting\n"); + return -1; + } + + if (atqa.uid_size == 2 || atqa.uid_size == 3) + uid_size = 3; + else if (atqa.uid_size == 1) + uid_size = 2; + else + uid_size = 1; + + acf.sel_code = ISO14443A_AC_SEL_CODE_CL1; + + handle->priv.iso14443a.state = ISO14443A_STATE_ANTICOL_RUNNING; + handle->priv.iso14443a.level = ISO14443A_LEVEL_CL1; + +cascade: + iso14443a_code_nvb_bits(&acf.nvb, 16); + + ret = iso14443a_transcieve_acf(handle, &acf, &bit_of_col); + if (ret < 0) + return ret; + DEBUGP("bit_of_col = %u\n", bit_of_col); + + while (bit_of_col != ISO14443A_BITOFCOL_NONE) { + set_bit_in_field(&acf.uid_bits[0], bit_of_col-16); + iso14443a_code_nvb_bits(&acf.nvb, bit_of_col); + ret = iso14443a_transcieve_acf(handle, &acf, &bit_of_col); + DEBUGP("bit_of_col = %u\n", bit_of_col); + if (ret < 0) + return ret; + } + + iso14443a_code_nvb_bits(&acf.nvb, 7*8); + ret = iso14443a_transcieve(handle, (unsigned char *)&acf, 7, + (unsigned char *) &sak, &rx_len, + TIMEOUT, 0); + if (ret < 0) + return ret; + + if (sak[0] & 0x04) { + /* Cascade bit set, UID not complete */ + switch (acf.sel_code) { + case ISO14443A_AC_SEL_CODE_CL1: + /* cascading from CL1 to CL2 */ + if (acf.uid_bits[0] != 0x88) { + DEBUGP("Cascade bit set, but UID0 != 0x88\n"); + return -1; + } + memcpy(&uid[0], &acf.uid_bits[1], 3); + acf.sel_code = ISO14443A_AC_SEL_CODE_CL2; + handle->priv.iso14443a.level = ISO14443A_LEVEL_CL2; + break; + case ISO14443A_AC_SEL_CODE_CL2: + /* cascading from CL2 to CL3 */ + memcpy(&uid[3], &acf.uid_bits[1], 3); + acf.sel_code = ISO14443A_AC_SEL_CODE_CL3; + handle->priv.iso14443a.level = ISO14443A_LEVEL_CL3; + break; + default: + DEBUGP("cannot cascade any further than CL3\n"); + handle->priv.iso14443a.state = ISO14443A_STATE_ERROR; + return -1; + break; + } + goto cascade; + + } else { + switch (acf.sel_code) { + case ISO14443A_AC_SEL_CODE_CL1: + /* single size UID (4 bytes) */ + memcpy(&uid[0], &acf.uid_bits[0], 4); + break; + case ISO14443A_AC_SEL_CODE_CL2: + /* double size UID (7 bytes) */ + memcpy(&uid[3], &acf.uid_bits[0], 4); + break; + case ISO14443A_AC_SEL_CODE_CL3: + /* triple size UID (10 bytes) */ + memcpy(&uid[6], &acf.uid_bits[0], 4); + break; + } + } + + handle->priv.iso14443a.level = ISO14443A_LEVEL_NONE; + handle->priv.iso14443a.state = ISO14443A_STATE_SELECTED; + + { + int uid_len; + if (uid_size == 1) + uid_len = 4; + else if (uid_size == 2) + uid_len = 7; + else + uid_len = 10; + + DEBUGP("UID %s\n", rfid_hexdump(uid, uid_len)); + } + + if (sak[0] & 0x20) { + DEBUGP("we have a T=CL compliant PICC\n"); + handle->priv.iso14443a.tcl_capable = 1; + } else { + DEBUGP("we have a T!=CL PICC\n"); + handle->priv.iso14443a.tcl_capable = 0; + } + + return 0; +} + +static int +iso14443a_hlta(struct rfid_layer2_handle *handle) +{ + int ret; + unsigned char tx_buf[2] = { 0x50, 0x00 }; + unsigned char rx_buf[10]; + unsigned int rx_len = sizeof(rx_buf); + + return 0; + + ret = iso14443a_transcieve(handle, tx_buf, sizeof(tx_buf), + rx_buf, &rx_len, 1000 /* 1ms */, 0); + if (ret < 0) { + /* "error" case: we don't get somethng back from the card */ + return 0; + } + return -1; +} +#endif + +static struct rfid_layer2_handle * +iso15693_init(struct rfid_reader_handle *rh) +{ + int ret; + struct rfid_layer2_handle *h = malloc(sizeof(*h)); + if (!h) + return NULL; + + h->l2 = &rfid_layer2_iso15693; + h->rh = rh; + h->priv.iso15693.state = ISO15693_STATE_NONE; + + ret = h->rh->reader->iso15693.init(h->rh); + if (ret < 0) { + free(h); + return NULL; + } + + return h; +} + +static int +iso15693_fini(struct rfid_layer2_handle *handle) +{ + free(handle); + return 0; +} + + +struct rfid_layer2 rfid_layer2_iso15693 = { + .id = RFID_LAYER2_ISO15693, + .name = "ISO 15693", + .fn = { + .init = &iso15693_init, + //.open = &iso15693_anticol, + //.transcieve = &iso15693_transcieve, + //.close = &iso14443a_hlta, + .fini = &iso15693_fini, + }, +}; + diff --git a/rfid_reader_cm5121.c b/rfid_reader_cm5121.c index a916c82..d77192c 100644 --- a/rfid_reader_cm5121.c +++ b/rfid_reader_cm5121.c @@ -210,6 +210,12 @@ cm5121_14443b_init(struct rfid_reader_handle *rh) return rh->ah->asic->priv.rc632.fn.iso14443b.init(rh->ah); } +static int +cm5121_15693_init(struct rfid_reader_handle *rh) +{ + return rh->ah->asic->priv.rc632.fn.iso15693.init(rh->ah); +} + struct rfid_asic_transport cm5121_ccid = { .name = "CM5121 OpenCT", -- cgit v1.2.3