#include #include #include #include #include "board.h" #include "usb_lld.h" #include "vrsp.h" extern uint16_t screen_image[12800]; static chopstx_intr_t usb_intr; struct vrsp { struct usb_dev dev; chopstx_mutex_t mtx; chopstx_cond_t cnd; uint32_t device_state : 3; /* USB device status */ uint32_t screen_mem_owner : 1; /* 0: driver, 1: app */ uint32_t mode : 8; uint16_t *screen_ptr; }; static struct vrsp vrsp0; /* * Locate VRSP structure from interface number or endpoint number. * Currently, it always returns vrsp0, because we only have the one. */ static struct vrsp * vrsp_get (int interface, uint8_t ep_num) { struct vrsp *v = &vrsp0; (void)interface; (void)ep_num; return v; } static void vrsp_update_finish (struct vrsp *v, int value); /* USB Device Descriptor */ static const uint8_t vrsp_device_desc[18] = { 18, /* bLength */ DEVICE_DESCRIPTOR, /* bDescriptorType */ 0x10, 0x01, /* bcdUSB = 1.1 */ 0x00, /* bDeviceClass. */ 0x00, /* bDeviceSubClass. */ 0x00, /* bDeviceProtocol. */ 0x40, /* bMaxPacketSize. */ 0xFF, 0xFF, /* idVendor */ 0x01, 0x00, /* idProduct */ 0x00, 0x01, /* bcdDevice */ 1, /* iManufacturer. */ 2, /* iProduct. */ 3, /* iSerialNumber. */ 1 /* bNumConfigurations. */ }; #define FEATURE_BUS_POWERED 0x80 /* Configuration Descriptor */ static const uint8_t vrsp_config_desc[32] = { 9, CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ /* Configuration Descriptor.*/ 32, 0x00, /* wTotalLength. */ 0x01, /* bNumInterfaces. */ 0x01, /* bConfigurationValue. */ 0, /* iConfiguration. */ FEATURE_BUS_POWERED, /* bmAttributes. */ 50, /* bMaxPower (100mA). */ /* Interface Descriptor.*/ 9, INTERFACE_DESCRIPTOR, 0x00, /* bInterfaceNumber. */ 0x00, /* bAlternateSetting. */ 0x02, /* bNumEndpoints. */ 0xFF, /* bInterfaceClass (Vendor specific). */ 0x00, /* bInterfaceSubClass. */ 0x00, /* bInterfaceProtocol. */ 0, /* iInterface. */ /* Endpoint 1-OUT Descriptor.*/ 7, ENDPOINT_DESCRIPTOR, ENDP1, /* bEndpointAddress. */ 0x02, /* bmAttributes (Bulk). */ 0x40, 0x00, /* wMaxPacketSize. */ 0x00, /* bInterval. */ /* Endpoint 1-IN Descriptor.*/ 7, ENDPOINT_DESCRIPTOR, ENDP1|0x80, /* bEndpointAddress. */ 0x02, /* bmAttributes (Bulk). */ 0x40, 0x00, /* wMaxPacketSize. */ 0x00, /* bInterval. */ }; /* * U.S. English language identifier. */ static const uint8_t vrsp_string0[4] = { 4, /* bLength */ STRING_DESCRIPTOR, 0x09, 0x04 /* LangID = 0x0409: US-English */ }; static const uint8_t vrsp_string1[] = { 23*2+2, /* bLength */ STRING_DESCRIPTOR, /* bDescriptorType */ /* Manufacturer: "Flying Stone Technology" */ 'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0, 't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0, 'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0, }; static const uint8_t vrsp_string2[] = { 14*2+2, /* bLength */ STRING_DESCRIPTOR, /* bDescriptorType */ /* Product name: "Chopstx Sample" */ 'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0, 'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, }; /* * Serial Number string. */ static const uint8_t vrsp_string3[10] = { 10, /* bLength */ STRING_DESCRIPTOR, /* bDescriptorType */ '0', 0, '.', 0, '0', 0, '0', 0, /* Version number */ }; #define NUM_INTERFACES 1 static void usb_device_reset (struct usb_dev *dev) { usb_lld_reset (dev, FEATURE_BUS_POWERED); chopstx_mutex_lock (&vrsp0.mtx); vrsp0.device_state = USB_DEVICE_STATE_ATTACHED; vrsp0.screen_mem_owner = 0; vrsp0.screen_ptr = screen_image; vrsp0.mode = 2; chopstx_cond_signal (&vrsp0.cnd); chopstx_mutex_unlock (&vrsp0.mtx); } static void usb_ctrl_write_finish (struct usb_dev *dev) { (void)dev; } static int usb_setup (struct usb_dev *dev) { struct device_req *arg = &dev->dev_req; uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); if (USB_SETUP_SET (arg->type)) { if (type_rcp == (VENDOR_REQUEST | INTERFACE_RECIPIENT) && arg->request == 0x00 && arg->index == 0 /* interface 0 */) { struct vrsp *v = vrsp_get (arg->index, 0); /* Finish the screen update. */ vrsp_update_finish (v, arg->value); return usb_lld_ctrl_ack (dev); } } return -1; } static int usb_get_descriptor (struct usb_dev *dev) { struct device_req *arg = &dev->dev_req; uint8_t rcp = arg->type & RECIPIENT; uint8_t desc_type = (arg->value >> 8); uint8_t desc_index = (arg->value & 0xff); if (rcp != DEVICE_RECIPIENT) return -1; if (desc_type == DEVICE_DESCRIPTOR) return usb_lld_ctrl_send (dev, vrsp_device_desc, sizeof (vrsp_device_desc)); else if (desc_type == CONFIG_DESCRIPTOR) return usb_lld_ctrl_send (dev, vrsp_config_desc, sizeof (vrsp_config_desc)); else if (desc_type == STRING_DESCRIPTOR) { const uint8_t *str; int size; switch (desc_index) { case 0: str = vrsp_string0; size = sizeof (vrsp_string0); break; case 1: str = vrsp_string1; size = sizeof (vrsp_string1); break; case 2: str = vrsp_string2; size = sizeof (vrsp_string2); break; case 3: str = vrsp_string3; size = sizeof (vrsp_string3); break; default: return -1; } return usb_lld_ctrl_send (dev, str, size); } return -1; } static const uint8_t status_zero; static void vrsp_setup_endpoints_for_interface (struct usb_dev *dev, uint16_t interface, int stop) { if (interface == 0) { if (!stop) { struct vrsp *v = vrsp_get (interface, 0); usb_lld_setup_endp (dev, ENDP1, EP_BULK, 1, 1); if (v->screen_mem_owner == 0) usb_lld_tx_enable_buf (&v->dev, ENDP1, &status_zero, 1); } else { usb_lld_stall_rx (dev, ENDP1); usb_lld_stall_tx (dev, ENDP1); } } } static int usb_set_configuration (struct usb_dev *dev) { uint8_t current_conf; current_conf = usb_lld_current_configuration (dev); if (current_conf == 0) { if (dev->dev_req.value != 1) return -1; usb_lld_set_configuration (dev, 1); vrsp_setup_endpoints_for_interface (dev, 0, 0); } else if (current_conf != dev->dev_req.value) { if (dev->dev_req.value != 0) return -1; usb_lld_set_configuration (dev, 0); vrsp_setup_endpoints_for_interface (dev, 0, 1); } return usb_lld_ctrl_ack (dev); } static int usb_set_interface (struct usb_dev *dev) { uint16_t interface = dev->dev_req.index; uint16_t alt = dev->dev_req.value; if (interface >= NUM_INTERFACES) return -1; if (alt != 0) return -1; else { vrsp_setup_endpoints_for_interface (dev, interface, 0); return usb_lld_ctrl_ack (dev); } } static int usb_get_interface (struct usb_dev *dev) { const uint8_t zero = 0; uint16_t interface = dev->dev_req.index; if (interface >= NUM_INTERFACES) return -1; /* We don't have alternate interface, so, always return 0. */ return usb_lld_ctrl_send (dev, &zero, 1); } static int usb_get_status_interface (struct usb_dev *dev) { const uint16_t status_info = 0; uint16_t interface = dev->dev_req.index; if (interface >= NUM_INTERFACES) return -1; return usb_lld_ctrl_send (dev, &status_info, 2); } static void vrsp_update_receive (struct vrsp *v, int len) { chopstx_mutex_lock (&v->mtx); if (v->screen_mem_owner == 0 && v->screen_ptr) { v->screen_ptr += len / 2; if (v->screen_ptr < screen_image + 12800) usb_lld_rx_enable_buf (&v->dev, ENDP1, v->screen_ptr, 64); } chopstx_mutex_unlock (&v->mtx); } static void vrsp_update_finish (struct vrsp *v, int value) { chopstx_mutex_lock (&v->mtx); if (v->screen_mem_owner == 0 && v->screen_ptr) { v->screen_ptr = NULL; v->mode = value; chopstx_cond_signal (&v->cnd); } chopstx_mutex_unlock (&v->mtx); } static void usb_tx_done (uint8_t ep_num, uint16_t len) { struct vrsp *v = vrsp_get (-1, ep_num); (void)len; /* * Coming here means that notification ("it's ready to accept screen * data") is retrieved by host. Enable the receive endpoint to * accept data. */ /* EP_NUM should be ENDP1 in this implementation. */ usb_lld_rx_enable_buf (&v->dev, ENDP1, v->screen_ptr, 64); } static void usb_rx_ready (uint8_t ep_num, uint16_t len) { struct vrsp *v = vrsp_get (-1, ep_num); if (ep_num == ENDP1) vrsp_update_receive (v, len); } #define PRIO_VRSP 4 #define STACK_PROCESS_2 #include "stack-def.h" #define STACK_ADDR_VRSP ((uint32_t)process2_base) #define STACK_SIZE_VRSP (sizeof process2_base) static void * vrsp_main (void *arg) { struct vrsp *v = arg; struct usb_dev *dev = &v->dev; int e; chopstx_claim_irq (&usb_intr, INTR_REQ_USB); usb_lld_init (dev, FEATURE_BUS_POWERED); while (1) { chopstx_intr_wait (&usb_intr); if (usb_intr.ready) { uint8_t ep_num; /* * When interrupt is detected, call usb_lld_event_handler. * The event may be one of following: * (1) Transfer to endpoint (bulk or interrupt) * In this case EP_NUM is encoded in the variable E. * (2) "NONE" event: some trasfer was done, but all was * done by lower layer, no other work is needed in * upper layer. * (3) Device events: Reset or Suspend * (4) Device requests to the endpoint zero. * */ e = usb_lld_event_handler (dev); chopstx_intr_done (&usb_intr); ep_num = USB_EVENT_ENDP (e); if (ep_num != 0) { if (USB_EVENT_TXRX (e)) usb_tx_done (ep_num, USB_EVENT_LEN (e)); else usb_rx_ready (ep_num, USB_EVENT_LEN (e)); } else switch (USB_EVENT_ID (e)) { case USB_EVENT_DEVICE_RESET: usb_device_reset (dev); continue; case USB_EVENT_DEVICE_ADDRESSED: /* The addres is assigned to the device. We don't * need to do anything for this actually, but in this * application, we maintain the USB status of the * device. Usually, just "continue" as EVENT_OK is * OK. */ chopstx_mutex_lock (&v->mtx); v->device_state = USB_DEVICE_STATE_ADDRESSED; chopstx_cond_signal (&v->cnd); chopstx_mutex_unlock (&v->mtx); continue; case USB_EVENT_GET_DESCRIPTOR: if (usb_get_descriptor (dev) < 0) usb_lld_ctrl_error (dev); continue; case USB_EVENT_SET_CONFIGURATION: if (usb_set_configuration (dev) < 0) usb_lld_ctrl_error (dev); continue; case USB_EVENT_SET_INTERFACE: if (usb_set_interface (dev) < 0) usb_lld_ctrl_error (dev); continue; case USB_EVENT_CTRL_REQUEST: /* Device specific device request. */ if (usb_setup (dev) < 0) usb_lld_ctrl_error (dev); continue; case USB_EVENT_GET_STATUS_INTERFACE: if (usb_get_status_interface (dev) < 0) usb_lld_ctrl_error (dev); continue; case USB_EVENT_GET_INTERFACE: if (usb_get_interface (dev) < 0) usb_lld_ctrl_error (dev); continue; case USB_EVENT_SET_FEATURE_DEVICE: case USB_EVENT_SET_FEATURE_ENDPOINT: case USB_EVENT_CLEAR_FEATURE_DEVICE: case USB_EVENT_CLEAR_FEATURE_ENDPOINT: usb_lld_ctrl_ack (dev); continue; case USB_EVENT_CTRL_WRITE_FINISH: /* Control WRITE transfer finished. */ usb_ctrl_write_finish (dev); continue; case USB_EVENT_OK: case USB_EVENT_DEVICE_SUSPEND: default: continue; } } } return NULL; } static int vrsp_ready_check (void *arg) { struct vrsp *v = arg; return v->screen_mem_owner == 0 && v->screen_ptr == NULL; } /* API exposed to application. */ struct vrsp * vrsp_open (void) { struct vrsp *v = &vrsp0; /* Now, we only have a single VRSP. */ chopstx_mutex_init (&v->mtx); chopstx_cond_init (&v->cnd); v->device_state = USB_DEVICE_STATE_UNCONNECTED; v->screen_mem_owner = 0; v->screen_ptr = screen_image; v->mode = 1; chopstx_create (PRIO_VRSP, STACK_ADDR_VRSP, STACK_SIZE_VRSP, vrsp_main, v); return v; } void vrsp_prepare_poll (struct vrsp *v, chopstx_poll_cond_t *poll_desc) { poll_desc->type = CHOPSTX_POLL_COND; poll_desc->ready = 0; poll_desc->cond = &v->cnd; poll_desc->mutex = &v->mtx; poll_desc->check = vrsp_ready_check; poll_desc->arg = v; } /* * vrsp_screen_acquire: Acquire the screen memory by the application to * update the LCD. * * Returns < 0 on error * >= 0 on success */ int vrsp_screen_acquire (struct vrsp *v) { int r = 0; chopstx_mutex_lock (&v->mtx); while (1) if (v->screen_mem_owner != 0) { /* something goes wrong, it's your turn already. */ r = -1; break; } else if (v->screen_ptr) /* The use of screen has not been finished yet. */ chopstx_cond_wait (&v->cnd, &v->mtx); else { v->screen_mem_owner = 1; r = v->mode; break; } chopstx_mutex_unlock (&v->mtx); return r; } /* * vrsp_screen_release: release the screen memory by the application */ void vrsp_screen_release (struct vrsp *v) { chopstx_mutex_lock (&v->mtx); v->screen_mem_owner = 0; v->screen_ptr = screen_image; chopstx_mutex_unlock (&v->mtx); /* Put the notification to host. */ usb_lld_tx_enable_buf (&v->dev, ENDP1, &status_zero, 1); }