gint/src/usb/classes/ff-bulk.c

206 lines
5.0 KiB
C

#include <gint/usb.h>
#include <gint/usb-ff-bulk.h>
#include <gint/display.h>
#include <string.h>
#include <stdlib.h>
static void notify_read(int endpoint, int size);
static usb_dc_interface_t dc_interface = {
.bLength = sizeof(usb_dc_interface_t),
.bDescriptorType = USB_DC_INTERFACE,
.bInterfaceNumber = -1 /* Set by driver */,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xff, /* Vendor-Specific */
.bInterfaceSubClass = 0x77, /* (not recognized by Casio tools?) */
.bInterfaceProtocol = 0x00,
.iInterface = 0,
};
/* Endpoint for calculator -> PC communication */
static usb_dc_endpoint_t dc_endpoint1i = {
.bLength = sizeof(usb_dc_endpoint_t),
.bDescriptorType = USB_DC_ENDPOINT,
.bEndpointAddress = 0x81, /* 1 IN */
.bmAttributes = 0x02, /* Bulk transfer */
.wMaxPacketSize = htole16(512),
.bInterval = 1,
};
/* Endpoint for PC -> calculator communication */
static usb_dc_endpoint_t dc_endpoint1o = {
.bLength = sizeof(usb_dc_endpoint_t),
.bDescriptorType = USB_DC_ENDPOINT,
.bEndpointAddress = 0x02, /* 2 OUT */
.bmAttributes = 0x02, /* Bulk transfer */
.wMaxPacketSize = htole16(512),
.bInterval = 1,
};
usb_interface_t const usb_ff_bulk = {
/* List of descriptors */
.dc = (void const *[]){
&dc_interface,
&dc_endpoint1i,
&dc_endpoint1o,
NULL,
},
/* Parameters for each endpoint */
.params = (usb_interface_endpoint_t []){
{ .endpoint = 0x81, /* 1 IN */
.buffer_size = 2048, },
{ .endpoint = 0x02, /* 2 OUT */
.buffer_size = 2048, },
{ 0 },
},
.notify_read = notify_read,
};
GCONSTRUCTOR static void set_strings(void)
{
dc_interface.iInterface = usb_dc_string(u"Bulk Input", 0);
}
//---
// Direct bulk access
//---
int usb_ff_bulk_output(void)
{
return usb_interface_pipe(&usb_ff_bulk, 0x81);
}
int usb_ff_bulk_input(void)
{
return usb_interface_pipe(&usb_ff_bulk, 0x02);
}
//---
// fxlink protocol
//---
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
char const *application, char const *type, uint32_t data_size)
{
if(strlen(application) > 16 || strlen(type) > 16) return false;
memset(header, 0, sizeof *header);
header->version = htole32(0x00000100);
header->size = htole32(data_size);
/* TODO: usb_fxlink_fill_header: avoid harcoded transfer size */
header->transfer_size = htole32(2048);
strncpy(header->application, application, 16);
strncpy(header->type, type, 16);
return true;
}
static void capture_vram(GUNUSED bool onscreen, char const *type)
{
void *source = gint_vram;
int size, format;
#ifdef FX9860G
size = 1024;
format = USB_FXLINK_IMAGE_MONO;
#endif
#ifdef FXCG50
if(onscreen) {
uint16_t *main, *secondary;
dgetvram(&main, &secondary);
source = (gint_vram == main) ? secondary : main;
}
size = DWIDTH * DHEIGHT * 2;
format = USB_FXLINK_IMAGE_RGB565;
#endif
usb_fxlink_header_t header;
usb_fxlink_image_t subheader;
usb_fxlink_fill_header(&header, "fxlink", type,
size + sizeof subheader);
subheader.width = htole32(DWIDTH);
subheader.height = htole32(DHEIGHT);
subheader.pixel_format = htole32(format);
int pipe = usb_ff_bulk_output();
usb_write_sync(pipe, &header, sizeof header, false);
usb_write_sync(pipe, &subheader, sizeof subheader, false);
usb_write_sync(pipe, source, size, false);
usb_commit_sync(pipe);
}
void usb_fxlink_screenshot(bool onscreen)
{
capture_vram(onscreen, "image");
}
void usb_fxlink_text(char const *text, int size)
{
if(size == 0) size = strlen(text);
usb_fxlink_header_t header;
usb_fxlink_fill_header(&header, "fxlink", "text", size);
int pipe = usb_ff_bulk_output();
usb_write_sync(pipe, &header, sizeof header, false);
usb_write_sync(pipe, text, size, false);
usb_commit_sync(pipe);
}
void usb_fxlink_videocapture(bool onscreen)
{
capture_vram(onscreen, "video");
}
//---
// Data reception
//---
/* Copy of the header for received BULK messages */
static usb_fxlink_header_t recv_header;
/* Size of transfer not yet read (implies valid header when non-zero) */
static int recv_size = 0;
static void header_finished(void)
{
recv_header.version = le32toh(recv_header.version);
recv_header.size = le32toh(recv_header.size);
recv_header.transfer_size = le32toh(recv_header.transfer_size);
int major = (recv_header.version >> 8) & 0xff;
int minor = (recv_header.version & 0xff);
USB_LOG("[ff-bulk %d.%d] New %.16s.%.16s (%d bytes)\n", major, minor,
recv_header.application, recv_header.type, recv_header.size);
}
static void notify_read(int endpoint, int size)
{
/* We only have one endpoint for reading, the bulk OUT */
(void)endpoint;
USB_LOG("[ff-bulk] Data available on %02x (%d bytes)\n", endpoint, size);
if(recv_size <= 0) {
usb_read_sync(usb_ff_bulk_input(), &recv_header, sizeof recv_header,
false);
header_finished();
recv_size = recv_header.size;
}
else {
USB_LOG("-> malloc dropping\n");
char *data = malloc(size);
usb_read_sync(usb_ff_bulk_input(), data, size, false);
free(data);
recv_size -= size;
if(recv_size < 0)
recv_size = 0;
}
}