Read Felica using RC-S380 (PaSoRi) in Java

Overview

I want to use PaSoRi other than Windows! When I think about it, I think Python's nfcpy is the first choice, but I want to do it in Java instead of Python! At that time, I couldn't find the sample code (or almost all Python samples ... !!), so I'd like to summarize up to the point where it works.

reference

As a reference first, I have greatly referred to the following articles. We thank our ancestors for implementing the specifications, etc., as they cannot be obtained without purchasing them as a corporation.

A brief summary of how to handle USB from Java

The following projects seem to be new and prosperous, so I will adopt them without thinking about anything. http://usb4java.org/

Looking at the site, it seems that there are Low level API (libusb) and High Level API (javax.usb), and this time it will be easier to understand, so I tried using javax.usb. In this case, the following page will be very helpful. --Accessing USB devices from Java applications

The normal procedure for accessing a USB device using the JSR-80 API is as follows:

  1. Get the appropriate UsbServices from UsbHostManager and bootstrap it.
  2. Access the root hub with Usb Services. The root hub is considered UsbHub in your application.
  3. Get a list of UsbDevices connected to the root hub. Examine all lower tier hubs to find the right UsbDevice.
  4. Use a control message (UsbControlIrp) to communicate directly with the UsbDevice, or ask for the UsbInterface from the appropriate UsbConfiguration on the UsbDevice and perform I / O using the UsbEndpoint available on the UsbInterface.
  5. If UsbEndpoint is used to perform I / O, open the associated UsbPipe. Upstream data (USB device to host computer) and downstream data (host computer to USB device) are submitted synchronously or asynchronously by UsbPipe.
  6. Close UsbPipe and release the appropriate UsbInterface if the application no longer needs access to the UsbDevice.

Based on the above article, a brief summary of the procedure for communication

  1. Detect USB from vendor ID and product ID. Each ID is uniquely published (should). For example, for this RC-S380, Vendor ID is 0x054C, Product ID is 0x06C3 ..
  2. From there, get Configuration, Interface, Endpoint. It should be noted that there are two places where data is exchanged when Endpoint actually communicates with USB, inward and outward with respect to USB.
  3. Send arbitrary communication to the device and receive a response

As mentioned above, if you know the control command to the device, it is not a difficult procedure.

Practice

Device detection

Since USB can be connected in a tree format with a hub, it is necessary to search for USB in a Imozuru style and find the desired USB. You can get the virtual route below.

RCS380.java


UsbServices services = UsbHostManager.getUsbServices();
UsbHub rootHub = services.getRootUsbHub();

Then look for the device by the vendor and product ID you looked up earlier. This is the sample. Search recursively even if there is a hub in the middle.

RC380.java


public UsbDevice findDevice(UsbHub hub, int vendorId, int productId) throws UsbException, UnsupportedEncodingException {
        for (UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices()) {
            UsbDeviceDescriptor desc = device.getUsbDeviceDescriptor();

            if (desc.idVendor() == vendorId && desc.idProduct() == productId) return device;
            if (device.isUsbHub()) {
                device = findDevice((UsbHub) device, vendorId, productId);
                if (device != null) return device;
            }
        }
        return null;
    }

Device detection is easy!

I wanted to say ..., but there is a point I was addicted to personally here, and if I proceed as it is, the Configuration is not Active, so I fell into a situation where UsbPipe for exchanging at Endpoint 2 could not be opened. It was. I checked if there is a method to setConfiguration or activateConfiguration with javax, but I could not find it, so for later, I will set Configuration using Low Level API (libusb) only in one place here.

//javax.usb.*If it is just, Configuration is not Active, so activate Configuration with LibUSB ...
DeviceHandle dh = LibUsb.openDeviceWithVidPid(null, (short) RCS380.VENDOR_ID, (short) RCS380.PRODUCT_ID);
LibUsb.setAutoDetachKernelDriver(dh, true);
LibUsb.setConfiguration(dh, 1);

Get up to UsbPipe at once

From here, you can get up to UsbPipe at once in the Imozuru style.

RCS380.java


            rcs380 = this.findDevice(rootHub, RCS380.VENDOR_ID, RCS380.PRODUCT_ID);
            UsbConfiguration configuration = (UsbConfiguration) rcs380.getUsbConfigurations().get(0);
            this.iface = (UsbInterface) configuration.getUsbInterfaces().get(0);


            UsbEndpoint endpointOut = null, endpointIn = null;
            for (int i = 0; i < iface.getUsbEndpoints().size(); i++) {
                byte endpointAddr = (byte) ((UsbEndpoint) (iface.getUsbEndpoints().get(i))).getUsbEndpointDescriptor().bEndpointAddress();
                if (((endpointAddr & 0x80) == 0x80)) {
                    endpointIn = (UsbEndpoint) (iface.getUsbEndpoints().get(i));
                } else if ((endpointAddr & 0x80) == 0x00) {
                    endpointOut = (UsbEndpoint) (iface.getUsbEndpoints().get(i))
                    ;
                }
            }

            this.pipeOut = endpointOut.getUsbPipe();
            this.pipeIn = endpointIn.getUsbPipe();

The point here is that there seems to be a rule that the first 4 bits of the EndPoint address in the direction from USB to the host (IN) is 0x8. So, if you take AND at 0x80 and it remains, it is for IN, otherwise it is for OUT.

Submit data to UsbPipe and communicate

For the detailed commands and data structure of RC-S380, I think it is quick to refer to the reference mentioned at the beginning, so I will omit it here. From here to the device

  1. ACK (no return data)
  2. Set Command Type
  3. Get Firmware version
  4. Get PD DATA version
  5. Switch RF
  6. In Set RF
  7. In Set Protocol

And keep throwing the command, and finally throw the command to polling.


 buf = rcs380.sendCommand(Chipset.CMD_GET_FIRMWARE_VERSION);
            System.out.println("Firmware version: " + String.format("%d.%02d", buf.get(1), buf.get(0)));

            buf = rcs380.sendCommand(Chipset.CMD_GET_PD_DATA_VERSION);
            System.out.println("PD Data version: " + String.format("%d.%02d", buf.get(1), buf.get(0)));

            rcs380.sendCommand(Chipset.CMD_SWITCH_RF, new byte[]{0x00});

            //0x01010f01 : F
            //0x02030f03 : A
            //0x03070f07 : B
            rcs380.sendCommand(Chipset.CMD_IN_SET_RF, new byte[]{0x01, 0x01, 0x0f, 0x01});
            rcs380.sendCommand(Chipset.CMD_IN_SET_PROTOCOL, new byte[]{0x00, 0x18, 0x01, 0x01, 0x02, 0x01, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x08, 0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0e, 0x04, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x06});
            rcs380.sendCommand(Chipset.CMD_IN_SET_PROTOCOL, new byte[]{0x00, 0x18});

Again, I think it's best to see the articles and implementations of the ancestors for the command code and data structure ...!

Below is the part that is reading Felica.

 System.out.println("********** Start **********");

            boolean isLoop = true;
            while (isLoop) {
                buf = rcs380.sendCommand(Chipset.CMD_IN_COMM_RF, new byte[]{0x6e, 0x00, 0x06, 0x00, (byte) 0xff, (byte) 0xff, 0x01, 0x00});
                if (Arrays.equals(buf.array(), new byte[]{(byte) 0x80, 0x00, 0x00, 0x00})) {

                } else {
                    //Type-F
                    if (buf.get(5) == 0x14 && buf.get(6) == 0x01) {
                        System.out.println("IDm: " + Hex.encodeHexString(Arrays.copyOfRange(buf.array(), 7, 15)));
                        System.out.println("PMm: " + Hex.encodeHexString(Arrays.copyOfRange(buf.array(), 15, 23)));
                        isLoop = false;
                    }
                }

                Thread.sleep(250);
            }

When you're done, close the pipes and so on.

rcs380.close();

Below are the execution results.

/Library/Java/JavaVirtualMachi ... ...
SONY RC-S380/P
Firmware version: 1.17
PD Data version: 1.00
********** Start **********
IDm: 01120312eb18f200
PMm: 100b4b428485d0ff

Process finished with exit code 0

This source

It's dirty code for testing purposes, but it's below. https://github.com/nearprosmith/java-rcs380-test

Please do not hesitate to point out any typographical errors or mistakes.

Recommended Posts

Read Felica using RC-S380 (PaSoRi) in Java
Read JSON in Java
Read binary files in Java 1
Read binary files in Java 2
Read Java properties file in C #
Encrypt using RSA cryptography in Java
Read CSV in Java (Super CSV Annotation)
HTTPS connection using tls1.2 in Java 6
I tried using JWT in Java
I tried using Elasticsearch API in Java
Try using the Stream API in Java
Map without using an array in java
Using JavaScript from Java in Rhino 2021 version
ERRORCODE = -4471 occurs in Java application using Db2.
Try using JSON format API in Java
Read xlsx file in Java with Selenium
Read a string in a PDF file with Java
ChatWork4j for using the ChatWork API in Java
Partization in Java
[Java] API creation using Jerjey (Jax-rs) in eclipse
Send email using Amazon SES SMTP in Java
Send push notifications using Notification Hubs in Java
Changes in Java 11
Try using Sourcetrail (win version) in Java code
Rock-paper-scissors in Java
Try using Sourcetrail (macOS version) in Java code
Match IP addresses using regular expressions in Java
Add, read, and delete Excel comments in Java
[Java] Read the file in src / main / resources
Display "Hello World" in the browser using Java
Display "Hello World" in the browser using Java
Pi in Java
Try using the COTOHA API parsing in Java
NLP4J [001b] Morphological analysis in Java (using kuromoji)
FizzBuzz in Java
Convert JSON and YAML in Java (using Jackson and SnakeYAML)
I tried using Google Cloud Vision API in Java
How to convert A to a and a to A using AND and OR in Java
I tried using an extended for statement in Java
Try global hooking in Java using the JNativeHook library
Differences in code when using the length system in Java
[java] sort in list
Sorting using java comparator
Interpreter implementation in Java
Rock-paper-scissors app in Java
Constraint programming in Java
Put java8 in centos7
NVL-ish guy in Java
Combine arrays in Java
"Hello World" in Java
Read Java Property file
Callable Interface in Java
Comments in Java source
Format XML in Java
Simple htmlspecialchars in Java
Scraping practice using Java ②
Boyer-Moore implementation in Java
Read Java HashMap source
Hello World in Java
Use OpenCV in Java
webApi memorandum in java