diff options
Diffstat (limited to 'usb/generic.h')
-rw-r--r-- | usb/generic.h | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/usb/generic.h b/usb/generic.h new file mode 100644 index 0000000..915b25a --- /dev/null +++ b/usb/generic.h @@ -0,0 +1,155 @@ +#ifndef GENERIC_H +#define GENERIC_H + +#include <stdint.h> + +struct desc_t { + uint32_t size; + void* data; +}; + +enum SetupStatus {Unhandled, Ok, Stall}; + +class USB_class_driver { + friend class USB_generic; + + protected: + virtual SetupStatus handle_setup(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + return SetupStatus::Unhandled; + } +}; + +class USB_generic { + private: + desc_t dev_desc; + desc_t conf_desc; + + USB_class_driver* control_handlers[4]; + + public: + USB_generic(desc_t dev, desc_t conf) : dev_desc(dev), conf_desc(conf) {} + + virtual bool ep_ready(uint32_t ep) = 0; + virtual void write(uint32_t ep, uint32_t* bufp, uint32_t len) = 0; + virtual void hw_set_address(uint8_t addr) = 0; + virtual void hw_conf_ep(uint8_t ep, uint32_t conf) = 0; + virtual void hw_set_stall(uint8_t ep) = 0; + + bool register_control_handler(USB_class_driver* control_handler) { + for(USB_class_driver*& handler : control_handlers) { + if(!handler || handler == control_handler) { + handler = control_handler; + return true; + } + } + + // Handler table is full. + return false; + } + + protected: + bool get_descriptor(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + desc_t* descp; + + switch(wValue) { + case 0x100: + descp = &dev_desc; + break; + case 0x200: + descp = &conf_desc; + break; + default: + return false; + } + + uint32_t* dp = (uint32_t*)descp->data; + uint32_t length = wLength > descp->size ? descp->size : wLength; + + while(length > 64) { + write(0, dp, 64); + dp += 16; + length -= 64; + + while(!ep_ready(0)); + } + + write(0, dp, length); + + return true; + } + + bool set_address(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + hw_set_address(wValue); + write(0, 0, 0); + return true; + } + + bool set_configuration(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + switch(wValue) { + case 0: + hw_conf_ep(1, 0); + break; + + case 1: + hw_conf_ep(1, (1 << 22) | (2 << 18) | (1 << 15) | 64); + break; + + default: + return false; + } + + write(0, 0, 0); + return true; + } + + + void handle_setup(const uint32_t* bufp) { + uint8_t bmRequestType = bufp[0] & 0xff; + uint8_t bRequest = (bufp[0] >> 8) & 0xff; + uint16_t wValue = (bufp[0] >> 16) & 0xffff; + uint16_t wIndex = bufp[1] & 0xffff; + uint16_t wLength = (bufp[1] >> 16) & 0xffff; + + // GET_DESCRIPTOR + if(bmRequestType == 0x80 && bRequest == 0x06) { + if(get_descriptor(wValue, wIndex, wLength)) { + return; + } + } + + // SET_ADDRESS + if(bmRequestType == 0x00 && bRequest == 0x05) { + if(set_address(wValue, wIndex, wLength)) { + return; + } + } + + // SET_CONFIGURATION + if(bmRequestType == 0x00 && bRequest == 0x09) { + if(set_configuration(wValue, wIndex, wLength)) { + return; + } + } + + SetupStatus res = SetupStatus::Unhandled; + + for(USB_class_driver*& handler : control_handlers) { + if(handler) { + res = handler->handle_setup(bmRequestType, bRequest, wValue, wIndex, wLength); + + if(res != SetupStatus::Unhandled) { + break; + } + } + } + + if(res == SetupStatus::Ok) { + return; + } + + hw_set_stall(0); + } + +}; + +#endif |