Skip to content

Commit

Permalink
Init project
Browse files Browse the repository at this point in the history
  • Loading branch information
mdeverdelhan committed May 8, 2020
1 parent 94dfb47 commit 2bda763
Show file tree
Hide file tree
Showing 7 changed files with 618 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,22 @@

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# Eclipse
/.classpath
/.project
/.settings/
/.metadata/
/.sonarlint/

# Intellij
.idea/
*.iml
*.iws

# Maven
log/
target/

# Misc
bin/
Binary file added doc/editor_66_TOPAZ-BCM20203T512_wafer_ds.pdf
Binary file not shown.
41 changes: 41 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>eu.verdelhan</groupId>
<artifactId>topaz512-driver</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>topaz512-driver</name>
<description>Driver for Innovision/Broadcom Topaz512 NFC tags</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>

</project>
65 changes: 65 additions & 0 deletions src/main/java/eu/verdelhan/topaz512/Crc16.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package eu.verdelhan.topaz512;

import java.util.Objects;

/**
* CRC-16 reference (ISO/IEC 14443-3)
* Crc-16 G(x) = x^16 + x^12 + x^5 + 1
*/
public class Crc16 {

/**
* CRC type
*/
public enum CrcType {
CRC_A((short) 0x6363), // ITU-V.41
CRC_B((short) 0xFFFF); // ISO/IEC 13239 (formerly ISO/IEC 3309)
short seed;
CrcType(short seed) {
this.seed = seed;
}
}

/**
* @param data a byte array
* @return the CRC-A, computed on the provided byte array
*/
public static byte[] computeCrcA(byte[] data) {
return computeCrc(CrcType.CRC_A, data);
}

/**
* @param data a byte array
* @return the CRC-B, computed on the provided byte array
*/
public static byte[] computeCrcB(byte[] data) {
return computeCrc(CrcType.CRC_B, data);
}

/**
* Compute the CRC of type {@link CrcType} on the provided byte array.
* @param crcType a CRC type
* @param data a byte array
* @return the CRC, computed on the provided byte array
*/
public static byte[] computeCrc(CrcType crcType, byte[] data) {
Objects.requireNonNull(crcType, "CRC type cannot be null");
Objects.requireNonNull(data, "Byte array cannot be null");
short wCrcU = crcType.seed;
for (int i = 0; i < data.length; i++) {
wCrcU = updateCrcU(data[i], wCrcU);
}
if (CrcType.CRC_B.equals(crcType)) {
wCrcU = (short) ~wCrcU; // ISO/IEC 13239 (formerly ISO/IEC 3309)
}
return new byte[] { (byte) wCrcU, (byte) (wCrcU >> 8) };
}

private static short updateCrcU(byte charU, short lpwCrcU) {
charU = (byte) (charU ^ lpwCrcU);
charU = (byte) (charU ^ charU << 4);
return (short) (Short.toUnsignedInt(lpwCrcU) >> 8 ^ Byte.toUnsignedInt(charU) << 8 ^ Byte.toUnsignedInt(charU) << 3 ^ Byte.toUnsignedInt(charU) >> 4);
}

private Crc16() {}
}
262 changes: 262 additions & 0 deletions src/main/java/eu/verdelhan/topaz512/Topaz512.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
package eu.verdelhan.topaz512;

/**
* Driver for Innovision/Broadcom Topaz 512 NFC tags (BCM20203T512).
*/
public class Topaz512 {

/** Commands */
public static final byte REQA = 0x26; // Request Command, type A
public static final byte WUPA = 0x52; // Wake-up, type A
public static final byte RID = 0x78; // Read ID: Use to read the metal-mask ROM and UID0-3 from block 0
public static final byte RALL = 0x00; // Read all (only blocks 0-E covered)
public static final byte READ = 0x01; // Read (a single byte)
public static final byte WRITE_E = 0x53; // Write-with-erase (a single byte)
public static final byte WRITE_NE = 0x1A; // Write-no-erase (a single byte)
public static final byte RSEG = 0x10; // Read segment
public static final byte READ8 = 0x02; // Read (8 bytes)
public static final byte WRITE_E8 = 0x54; // Write-with-erase (8 bytes)
public static final byte WRITE_NE8 = 0x1B; // Write-no-erase (8 bytes)

/** Responses */
public static final byte[] ATQA = new byte[] { 0x0C, 0x00 };

/** Tag UID */
private byte[] uid;

/** True to add the CRC-B postfix (to commands) when it is needed, false otherwise */
private boolean addCrcPostfix;

public Topaz512() {
this(new byte[4]);
}

/**
* @param uid the tag UID
*/
public Topaz512(byte[] uid) {
this(uid, true);
}

/**
* @param addCrcPostfix true to add the CRC-B postfix when it is needed, false otherwise
*/
public Topaz512(boolean addCrcPostfix) {
this(new byte[4], addCrcPostfix);
}

/**
* @param uid the tag UID (4 bytes)
* @param addCrcPostfix true to add the CRC-B postfix when it is needed, false otherwise
*/
public Topaz512(byte[] uid, boolean addCrcPostfix) {
if (uid == null || uid.length != 4) {
throw new IllegalArgumentException("Tag UID must be 4-bytes long");
}
this.uid = uid;
this.addCrcPostfix = addCrcPostfix;
}

/**
* @param uid the tag UID
*/
public void setUid(byte[] uid) {
this.uid = uid;
}

/**
* @return the tag UID
*/
public byte[] getUid() {
return uid;
}

/**
* @return the REQA (Request) command
*/
public byte[] buildReqaCommand() {
return new byte[] {REQA};
}

/**
* @return the WUPA (Wake-up) command
*/
public byte[] buildWupaCommand() {
return new byte[] {WUPA};
}

/**
* @return the RID (Read ID) command
*/
public byte[] buildRidCommand() {
byte[] rawCmd = new byte[7];
rawCmd[0] = RID;
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @return the RALL (Read all) command (only blocks 0-E covered)
*/
public byte[] buildRallCommand() {
byte[] rawCmd = new byte[7];
rawCmd[0] = RALL;
System.arraycopy(uid, 0, rawCmd, 3, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param add the ADD address byte
* @return the READ (a single byte) command
*/
public byte[] buildReadCommand(byte add) {
byte[] rawCmd = new byte[7];
rawCmd[0] = READ;
rawCmd[1] = add;
System.arraycopy(uid, 0, rawCmd, 3, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param add the ADD address byte
* @param data the byte to be written on the tag
* @return the WRITE-E (Write-with-erase, a single byte) command
*/
public byte[] buildWriteECommand(byte add, byte data) {
byte[] rawCmd = new byte[7];
rawCmd[0] = WRITE_E;
rawCmd[1] = add;
rawCmd[2] = data;
System.arraycopy(uid, 0, rawCmd, 3, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param add the ADD address byte
* @param data the byte to be written on the tag
* @return the WRITE-NE (Write-no-erase, a single byte) command
*/
public byte[] buildWriteNECommand(byte add, byte data) {
byte[] rawCmd = new byte[7];
rawCmd[0] = WRITE_NE;
rawCmd[1] = add;
rawCmd[2] = data;
System.arraycopy(uid, 0, rawCmd, 3, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param adds the ADDS segment address byte
* @return the RSEG (Read segment) command
*/
public byte[] buildRsegCommand(byte adds) {
byte[] rawCmd = new byte[14];
rawCmd[0] = RSEG;
rawCmd[1] = adds;
System.arraycopy(uid, 0, rawCmd, 10, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param add8 the ADD8 block address byte
* @return the READ8 (Read, 8 bytes) command
*/
public byte[] buildRead8Command(byte add8) {
byte[] rawCmd = new byte[14];
rawCmd[0] = READ8;
rawCmd[1] = add8;
System.arraycopy(uid, 0, rawCmd, 10, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param add8 the ADD8 block address byte
* @param data8 the 8 bytes to be written on the tag
* @return the WRITE-E8 (Write-with-erase, 8 bytes) command
*/
public byte[] buildWriteE8Command(byte add8, byte[] data8) {
checkData8(data8);
byte[] rawCmd = new byte[14];
rawCmd[0] = WRITE_E8;
rawCmd[1] = add8;
System.arraycopy(data8, 0, rawCmd, 2, data8.length);
System.arraycopy(uid, 0, rawCmd, 10, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param add8 the ADD8 block address byte
* @param data8 the 8 bytes to be written on the tag
* @return the WRITE-NE8 (Write-no-erase, 8 bytes) command
*/
public byte[] buildWriteNE8Command(byte add8, byte[] data8) {
checkData8(data8);
byte[] rawCmd = new byte[14];
rawCmd[0] = WRITE_NE8;
rawCmd[1] = add8;
System.arraycopy(data8, 0, rawCmd, 2, data8.length);
System.arraycopy(uid, 0, rawCmd, 10, uid.length);
return addCrcPostfixIfNeeded(rawCmd);
}

/**
* @param rawCmd a raw command
* @return the command + its CRC-B postfix if it has been requested, the raw command itself otherwise
*/
protected byte[] addCrcPostfixIfNeeded(byte[] rawCmd) {
return addCrcPostfix ? addCrcBPostfix(rawCmd) : rawCmd;
}

/**
* Check the validity of a data8 byte array.
* @param data8 the data byte array to check
*/
protected static void checkData8(byte[] data8) {
if (data8 == null || data8.length != 8) {
throw new IllegalArgumentException("Data array must be 8-bytes long");
}
}

/**
* Add a CRC-B postfix to a raw command.
* @param rawCmd a raw command
* @return the command + its CRC-B postfix (2 bytes)
*/
protected static byte[] addCrcBPostfix(byte[] rawCmd) {
byte[] crc = Crc16.computeCrc(Crc16.CrcType.CRC_B, rawCmd);
byte[] cmd = new byte[rawCmd.length + crc.length];
System.arraycopy(rawCmd, 0, cmd, 0, rawCmd.length);
System.arraycopy(crc, 0, cmd, rawCmd.length, crc.length);
return cmd;
}

/**
* @param blockIdx the block index (from 0 to F)
* @param byteIdx the byte index (from 0 to 7)
* @return an ADD address byte (for READ, WRITE-E and WRITE-NE)
*/
public static byte buildAdd(char blockIdx, byte byteIdx) {
return (byte) ((Character.digit(blockIdx, 16) << 3) + byteIdx);
}

/**
* @param segmentIdx the segment index (from 0 to 3)
* @return an ADDS segment index byte (for RSEG)
*/
public static byte buildAdds(byte segmentIdx) {
if (segmentIdx < 0 || segmentIdx > 3) {
throw new IllegalArgumentException("Segment index must be between 0 and 3");
}
return (byte) (segmentIdx << 5);
}

/**
* @param globalBlockIdx the global block index (from 0x00 to 0x3F)
* @return an ADD8 block address byte (for READ8, WRITE-E8 and WRITE-NE8)
*/
public static byte buildAdd8(byte globalBlockIdx) {
if (globalBlockIdx < 0x00 || globalBlockIdx > 0x3F) {
throw new IllegalArgumentException("Global block index must be between 0x00 and 0x3F");
}
return globalBlockIdx;
}
}
Loading

0 comments on commit 2bda763

Please sign in to comment.