segatools/iccard/felica.c

193 lines
3.7 KiB
C
Raw Normal View History

#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
#include "iccard/felica.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT felica_cmd_poll(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_get_system_code(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_nda_a4(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
uint64_t felica_get_generic_PMm(void)
{
/* A FeliCa PMm contains low-level protocol timing information for
communicating with a particular IC card. The exact values are not
particularly important for our purposes, so we'll just return a hard-
coded PMm. This was taken from a SuiCa train pass I bought in Japan. */
return 0x053143454682B7FFULL;
}
HRESULT felica_transact(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
uint64_t IDm;
uint8_t code;
HRESULT hr;
assert(f != NULL);
assert(req != NULL);
assert(res != NULL);
hr = iobuf_read_8(req, &code);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_write_8(res, code + 1);
if (FAILED(hr)) {
return hr;
}
if (code != FELICA_CMD_POLL) {
hr = iobuf_read_be64(req, &IDm);
if (FAILED(hr)) {
return hr;
}
if (IDm != f->IDm) {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
hr = iobuf_write_be64(res, IDm);
if (FAILED(hr)) {
return hr;
}
}
switch (code) {
case FELICA_CMD_POLL:
return felica_cmd_poll(f, req, res);
case FELICA_CMD_GET_SYSTEM_CODE:
return felica_cmd_get_system_code(f, req, res);
case FELICA_CMD_NDA_A4:
return felica_cmd_nda_a4(f, req, res);
default:
dprintf("FeliCa: Unimplemented command %02x, payload:\n", code);
dump_const_iobuf(req);
return E_NOTIMPL;
}
}
static HRESULT felica_cmd_poll(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
HRESULT hr;
/* Request: */
hr = iobuf_read_be16(req, &system_code);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_read_8(req, &request_code);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_read_8(req, &time_slot);
if (FAILED(hr)) {
return hr;
}
if (system_code != 0xFFFF && system_code != f->system_code) {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
// TODO handle other params correctly...
/* Response: */
hr = iobuf_write_be64(res, f->IDm);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_write_be64(res, f->PMm);
if (FAILED(hr)) {
return hr;
}
if (request_code == 0x01) {
hr = iobuf_write_be16(res, f->system_code);
if (FAILED(hr)) {
return hr;
}
}
return S_OK;
}
static HRESULT felica_cmd_get_system_code(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
HRESULT hr;
hr = iobuf_write_8(res, 1); /* Number of system codes */
if (FAILED(hr)) {
return hr;
}
hr = iobuf_write_be16(res, f->system_code);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
static HRESULT felica_cmd_nda_a4(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
/* The specification for this command is probably only available under NDA.
Returning what the driver seems to want. */
return iobuf_write_8(res, 0);
}