#include #include #include #include #include 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; } }