cardio/main.cpp

587 lines
13 KiB
C++

#include <rcc/flash.h>
#include <rcc/rcc.h>
#include <rcc/crs.h>
#include <syscfg/syscfg.h>
#include <gpio/gpio.h>
#include <os/time.h>
#include <spi/spi.h>
#include <usb/usb.h>
#include <usb/descriptor.h>
enum tag_type_t {
ISO15693,
ISO18092,
};
auto dev_desc = device_desc(0x200, 0, 0, 0, 64, 0x1d50, 0x60f8, 0, 0, 0, 0, 1);
auto conf_desc = configuration_desc(1, 1, 0, 0xc0, 0,
// MSC BBB
interface_desc(0, 0, 2, 0x08, 0x06, 0x50, 0,
endpoint_desc(0x81, 0x02, 64, 0), // IN
endpoint_desc(0x01, 0x02, 64, 0) // OUT
)
);
desc_t dev_desc_p = {sizeof(dev_desc), (void*)&dev_desc};
desc_t conf_desc_p = {sizeof(conf_desc), (void*)&conf_desc};
USB_l0 usb(USB, dev_desc_p, conf_desc_p);
Pin nfc_sck = GPIOB[3];
Pin nfc_miso = GPIOB[4];
Pin nfc_mosi = GPIOB[5];
Pin nfc_ss = GPIOB[6];
Pin nfc_irq_out = GPIOB[7];
Pin nfc_irq_in = GPIOC[13];
Pin button = GPIOA[3];
Pin led = GPIOA[4];
class CR95HF {
private:
SPI_t& spi;
Pin& ss;
public:
CR95HF(SPI_t& spi_bus, Pin& p) : spi(spi_bus), ss(p) {}
void send_cmd(uint8_t cmd, uint32_t len, uint8_t* buf) {
ss.off();
spi.transfer_byte(0);
spi.transfer_byte(cmd);
spi.transfer_byte(len);
while(len--) {
spi.transfer_byte(*buf++);
}
ss.on();
}
uint32_t get_response(uint32_t maxlen, uint8_t* buf) {
uint32_t len = 0;
while(nfc_irq_out.get());
ss.off();
spi.transfer_byte(2);
*buf++ = spi.transfer_byte();
len++;
uint32_t datalen = spi.transfer_byte();
*buf++ = datalen;
len++;
while(len < datalen + 2) {
*buf++ = spi.transfer_byte();
len++;
}
ss.on();
return len;
}
};
CR95HF cr95hf(SPI1, nfc_ss);
/*
class USB_CR95HF : public USB_class_driver {
private:
USB_generic& usb;
uint8_t pend_reg;
uint32_t pend_len;
uint8_t buf[256];
bool write(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
if(wLength > sizeof(buf)) {
return false;
}
if(wLength == 0) {
cr95hf.send_cmd(wValue, 0, nullptr);
usb.write(0, nullptr, 0);
return true;
}
pend_reg = wValue;
pend_len = wLength;
// Awaiting OUT packet.
return true;
}
bool read(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
if(wLength > sizeof(buf)) {
return false;
}
wLength = cr95hf.get_response(wLength, buf);
// TODO: wLength >= 64
usb.write(0, (uint32_t*)buf, wLength);
return true;
}
public:
USB_CR95HF(USB_generic& usbd) : usb(usbd) {
usb.register_driver(this);
}
protected:
virtual SetupStatus handle_setup(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
if(bmRequestType == 0x40 && bRequest == 0xf2) {
return write(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall;
}
if(bmRequestType == 0xc0 && bRequest == 0xf3) {
return read(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall;
}
return SetupStatus::Unhandled;
}
virtual void handle_out(uint8_t ep, uint32_t len) {
if(ep != 0 || len == 0) {
return;
}
if(len != pend_len) {
return;
}
usb.read(0, (uint32_t*)buf, len);
cr95hf.send_cmd(pend_reg, len, buf);
usb.write(0, nullptr, 0);
}
};
USB_CR95HF usb_cr95hf(usb);
*/
struct CBW {
uint32_t dCBWSignature;
uint32_t dCBWTag;
uint32_t dCBWDataTransferLength;
uint8_t bmCBWFlags;
uint8_t bCBWLUN;
uint8_t bCBWCBLength;
uint8_t CBWCB[16];
} __attribute__((packed));
struct CSW {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
uint8_t bCSWStatus;
} __attribute__((packed));
uint32_t nullbuf[16];
uint32_t fcapbuf[] = {
0x08000000,
0x00010000,
0x00020002
};
uint32_t capbuf[] = {
0x00010000,
0x00020000
};
uint32_t inquirybuf[] = {
0x02048000,
32,
0x20202020, 0x20202020,
0x20202020, 0x20202020, 0x20202020, 0x20202020,
0x34333231,
};
uint32_t sensebuf[] = {
0x00050070,
0x0a000000,
0,
0x00000020,
0
};
uint32_t modesensebuf[] = {
0x00800004
};
//uint32_t sd_buf[512];
uint8_t sectors[][512] {
{}, // NULL
{ // MBR
0xeb, 0x3c, 0x90, 0x6d, 0x6b, 0x64, 0x6f, 0x73, 0x66, 0x73, 0x00, 0x00, 0x02, 0x04, 0x01, 0x00,
0x02, 0x00, 0x02, 0x00, 0x01, 0xf8, 0x01, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xc9, 0x6a, 0x10, 0xc4, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x0e, 0x1f,
0xbe, 0x5b, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20,
0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
},
{ // FAT
0xf8, 0xff, 0xff, 0x00, 0xf0, 0xff,
},
{ // File table
0x41, 0x63, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x30, 0x00, 0x0f, 0x00, 0xa7, 0x2e, 0x00,
0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x43, 0x41, 0x52, 0x44, 0x30, 0x20, 0x20, 0x20, 0x54, 0x58, 0x54, 0x20, 0x00, 0x64, 0x53, 0x95,
0x03, 0x43, 0x03, 0x43, 0x00, 0x00, 0x53, 0x95, 0x03, 0x43, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00,
},
{},
};
uint8_t* get_sector(uint32_t sector) {
switch(sector) {
case 0:
return sectors[1];
case 1:
case 2:
return sectors[2];
case 3:
return sectors[3];
case 39:
return sectors[4];
default:
return sectors[0];
}
}
class USB_MSC_BBB : public USB_class_driver {
private:
USB_generic& usb;
uint32_t buf[16];
uint32_t pending_data_in;
bool pending_write;
uint32_t pending_write_addr;
uint32_t pending_write_num;
CBW cbw;
uint32_t pending_write_recv;
CSW csw;
public:
USB_MSC_BBB(USB_generic& usbd) : usb(usbd) {
usb.register_driver(this);
}
protected:
virtual SetupStatus handle_setup(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
// Get max LUN
if(bmRequestType == 0xa1 && bRequest == 0xfe) {
uint32_t max_lun = 0;
usb.write(0, &max_lun, 1);
return SetupStatus::Ok;
}
// Mass Storage Reset
if(bmRequestType == 0x21 && bRequest == 0xff) {
usb.write(0, nullptr, 0);
return SetupStatus::Ok;
}
return SetupStatus::Unhandled;
}
virtual void handle_set_configuration(uint8_t configuration) {
if(configuration) {
usb.register_out_handler(this, 1);
usb.hw_conf_ep(0x01, EPType::Bulk, 64);
usb.hw_conf_ep(0x81, EPType::Bulk, 64);
pending_data_in = 0;
pending_write = false;
}
}
virtual void handle_out(uint8_t ep, uint32_t len) {
if(ep == 0) {
if(len) {
usb.write(0, nullptr, 0);
}
} else if(ep == 1 && pending_data_in != 0) {
if(pending_write) {
usb_rblog.log("Handling write packet. (%d bytes)", len);
//handle_write_block_packet();
} else {
usb_rblog.log("Ignoring %d bytes of received data.", len);
}
if(len > pending_data_in) {
pending_data_in = 0;
} else {
pending_data_in -= len;
}
if(pending_data_in == 0) {
usb.write(1, (uint32_t*)&csw, sizeof(CSW));
}
} else if(ep == 1) {
// Read CBW.
if(len != sizeof(CBW) || usb.read(ep, (uint32_t*)&cbw, len) != sizeof(CBW)) {
usb_rblog.log("Invalid CBW size.");
return; // FIXME: Indicate error.
}
if(cbw.dCBWSignature != 0x43425355) {
usb_rblog.log("Invalid CBW signature.");
return; // FIXME: Indicate error.
}
usb_rblog.log("Received CBW, tl=%d, flags=%#02x", cbw.dCBWDataTransferLength, cbw.bmCBWFlags);
bool cmd_ok = handle_scsi_cmd(cbw.CBWCB, cbw.bCBWCBLength);
if(!cmd_ok && cbw.dCBWDataTransferLength != 0 && cbw.bmCBWFlags & 0x80) {
write_zero();
}
if(cmd_ok) {
csw = {0x53425355, cbw.dCBWTag, 0, 0x00};
} else {
csw = {0x53425355, cbw.dCBWTag, cbw.dCBWDataTransferLength, 0x01};
}
if(cbw.dCBWDataTransferLength != 0 && !(cbw.bmCBWFlags & 0x80)) {
pending_data_in = cbw.dCBWDataTransferLength;
return;
}
usb.write(1, (uint32_t*)&csw, sizeof(CSW));
}
}
void write_zero() {
uint32_t len = cbw.dCBWDataTransferLength;
while(len > 64) {
usb.write(1, nullbuf, 64);
while(!usb.ep_ready(1));
len -= 64;
}
usb.write(1, nullbuf, len);
while(!usb.ep_ready(1));
}
void handle_read_block() {
uint32_t block = (cbw.CBWCB[2] << 24) | (cbw.CBWCB[3] << 16) | (cbw.CBWCB[4] << 8) | cbw.CBWCB[5];
uint16_t num_blocks = (cbw.CBWCB[7] << 8) | cbw.CBWCB[8];
while(num_blocks--) {
uint32_t* bufp = (uint32_t*)get_sector(block++);
for(uint32_t i = 0; i < 512; i += 64) {
usb.write(1, bufp + i / 4, 64);
while(!usb.ep_ready(1));
}
}
}
bool handle_scsi_cmd(uint8_t* cmd, uint32_t len) {
pending_write = false;
if(!len) {
return false;
}
switch(cmd[0]) {
// TEST UNIT READY
case 0x00:
return true;
// REQUEST SENSE
case 0x03:
usb.write(1, sensebuf, cbw.dCBWDataTransferLength);
while(!usb.ep_ready(1));
return true;
// INQUIRY
case 0x12:
if(cmd[2] != 0) {
return false;
}
usb.write(1, inquirybuf, cbw.dCBWDataTransferLength);
while(!usb.ep_ready(1));
return true;
// MODE SENSE
case 0x1a:
usb.write(1, modesensebuf, cbw.dCBWDataTransferLength > 4 ? 4 : cbw.dCBWDataTransferLength);
while(!usb.ep_ready(1));
return true;
// READ FORMAT CAPACITIES
case 0x23:
usb.write(1, fcapbuf, sizeof(fcapbuf));
while(!usb.ep_ready(1));
return true;
// READ CAPACITY
case 0x25:
usb.write(1, capbuf, cbw.dCBWDataTransferLength);
while(!usb.ep_ready(1));
return true;
// READ (10)
case 0x28:
handle_read_block();
return true;
default:
return false;
}
return true;
}
};
USB_MSC_BBB usb_msc_bbb(usb);
int main() {
rcc_init();
// Initialize system timer.
STK.LOAD = 32000000 / 8 / 1000; // 1000 Hz.
STK.VAL = 0;
STK.CTRL = 0x03;
// Enable CRS.
RCC.enable(RCC.CRS);
CRS.enable();
RCC.enable(RCC.GPIOA);
RCC.enable(RCC.GPIOB);
RCC.enable(RCC.GPIOC);
led.set_mode(Pin::Output);
RCC.enable(RCC.USB);
usb.init();
Time::sleep(10);
USB.reg.BCDR |= 1 << 15;
RCC.enable(RCC.SPI1);
nfc_irq_out.set_mode(Pin::Input);
nfc_irq_in.on();
nfc_irq_in.set_mode(Pin::Output);
nfc_ss.on();
nfc_ss.set_mode(Pin::Output);
nfc_sck.set_speed(Pin::High);
nfc_sck.set_mode(Pin::AF);
nfc_miso.set_mode(Pin::AF);
nfc_mosi.set_mode(Pin::AF);
SPI1.reg.CR1 = (1 << 9) | (1 << 8) | (1 << 6) | (4 << 3) | (1 << 2) | (0 << 1) | (0 << 0); // SSM, SSI, SPE, 36/32MHz, MSTR, CPOL=0, CPHA=0
SPI1.reg.CR2 = (1 << 12) | (7 << 8); // FRXTH, DS = 8bit
Time::sleep(1);
nfc_irq_in.off();
Time::sleep(1);
nfc_irq_in.on();
Time::sleep(1);
uint8_t buf[64];
bool cmd_sent = false;
uint32_t nfc_delay_until = 0;
tag_type_t active_type = ISO15693;
Time::sleep(1000);
while(1) {
if(Time::time() < nfc_delay_until) {
// Just do nothing.
} else if(!cmd_sent) {
led.off();
if(active_type == ISO15693) {
cr95hf.send_cmd(0x02, 2, (uint8_t*)"\x01\x05"); // Select ISO 15693
cr95hf.get_response(64, buf);
cr95hf.send_cmd(0x04, 3, (uint8_t*)"\x26\x01\x00"); // INVENTORY
} else if(active_type == ISO18092) {
cr95hf.send_cmd(0x02, 2, (uint8_t*)"\x04\x51"); // Select ISO 18092
cr95hf.get_response(64, buf);
cr95hf.send_cmd(0x04, 5, (uint8_t*)"\x00\xff\xff\x00\x00"); // REQC
}
cmd_sent = true;
} else if(!nfc_irq_out.get()) {
cr95hf.get_response(64, buf);
cmd_sent = false;
if(buf[0] == 128 && buf[14] == 0) {
led.on();
USB.reg.BCDR &= ~(1 << 15);
uint8_t* id_ascii = sectors[4];
if(active_type == ISO15693) {
uint8_t* id_raw = &buf[11];
for(uint32_t i = 0; i < 8; i++) {
*id_ascii++ = "0123456789ABCDEF"[*id_raw >> 4];
*id_ascii++ = "0123456789ABCDEF"[*id_raw-- & 0xf];
}
} else if(active_type == ISO18092) {
uint8_t* id_raw = &buf[3];
for(uint32_t i = 0; i < 8; i++) {
*id_ascii++ = "0123456789ABCDEF"[*id_raw >> 4];
*id_ascii++ = "0123456789ABCDEF"[*id_raw++ & 0xf];
}
}
Time::sleep(10);
USB.reg.BCDR |= 1 << 15;
nfc_delay_until = Time::time() + 5000; // Valid for five seconds.
} else {
nfc_delay_until = Time::time() + 100; // Retry in 100ms.
if(active_type == ISO15693) {
active_type = ISO18092;
} else {
active_type = ISO15693;
}
}
}
usb.process();
}
}