#include "usb_device.h" #include "usb.h" #include #include #include #include "registers.h" #include "delay.h" /* USB_OTG_GlobalTypeDef */ usb_device_t usb_devices[2]; void* usb_periph_addresses[] = { (void*)USB1_OTG_HS, (void*)USB2_OTG_FS }; void* usb_device_init(usb_device_slot_t slot, usb_class_vtable_t *class, uint16_t id_vendor, uint16_t id_product, char *vendor_name, char *product_name, uint16_t serial_number, char* serial_name) { usb_device_t* device = &usb_devices[slot]; void* peripheral_address = usb_periph_addresses[slot]; device->state = INIT; device->setup.detected_setup_errors = 0; device->core = peripheral_address + USB_OTG_GLOBAL_BASE; device->device = peripheral_address + USB_OTG_DEVICE_BASE; device->out = peripheral_address + USB_OTG_OUT_ENDPOINT_BASE; device->in = peripheral_address + USB_OTG_IN_ENDPOINT_BASE; device->fifos = (usb_fifo_t*)(((uint8_t*)device->core) + USB_OTG_FIFO_BASE); device->setup.received_setup_commands = queue_malloc(sizeof(usb_setup_command_t), MAX_SETUP_PACKETS); device->setup.rcvd_awaiting = 0; device->setup.rcvd_count = 0; device->setup.rcvd_data = NULL; queue_init(device->setup.received_setup_commands, sizeof(usb_setup_command_t), MAX_SETUP_PACKETS); device->vt = *class; device->class = device-> vt.init(device, id_vendor, id_product, vendor_name, product_name, serial_number, serial_name); return device; } void usb_device_reset(void* device_ptr) { usb_device_t* device = (usb_device_t*)device_ptr; // Idle while ((device->core->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0); // Reset device->core->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; // Wait for reset while (device->core->GRSTCTL & USB_OTG_GRSTCTL_CSRST); // Wait for idle while ((device->core->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0); DELAY_US(3); } void usb_device_clear_interrupts(void* device_ptr) { usb_device_t* device = (usb_device_t*)device_ptr; device->core->GINTSTS = 0; // clear interrupts // for other interrupts the rx queue has to be read } void usb_device_wait_for_handshake(void* device_ptr) { usb_device_t* device = (usb_device_t*)device_ptr; while (device->state != ENUMERATED); } void usb_device_setup(void* device_ptr) { RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN | RCC_AHB1ENR_USB1OTGHSULPIEN; volatile uint32_t dummy; dummy = RCC->AHB1ENR; dummy = RCC->AHB1ENR; NVIC_SetPriority(OTG_HS_IRQn, 2); NVIC_EnableIRQ(OTG_HS_IRQn); // 1. core initialization usb_device_t* device = (usb_device_t*)device_ptr; device->device->DCTL |= USB_OTG_DCTL_SDIS; DELAY_US(3); PWR->CR3 |= PWR_CR3_USB33DEN; while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0); device->core->GCCFG &= ~USB_OTG_GCCFG_PWRDWN; device->core->GCCFG |= USB_OTG_GCCFG_VBDEN; device->core->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; DELAY_US(3); usb_device_reset(device_ptr); /* DELAY_US(3); */ *(uint32_t*)((uint8_t*)device->core + USB_OTG_PCGCCTL_BASE) = 0; // global interrupt mask // rx fifo non empty // periodic tx fifo empty level usb_device_clear_interrupts(device_ptr); device->core->GAHBCFG = USB_OTG_GAHBCFG_GINT | USB_OTG_GAHBCFG_TXFELVL | USB_OTG_GAHBCFG_HBSTLEN_2; // hnp capable, srp capable, otg hs timeout, usb turnaround // TODO: based on enumeration speed set TOCAL - fs timeout calibration device->core->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_ULPIEVBUSI | USB_OTG_GUSBCFG_ULPIEVBUSD | (9 << USB_OTG_GUSBCFG_TRDT_Pos); // TODO: are these needed? device mode is forced... // unmask otg interrupt mask /* device->core->GINTMSK |= USB_OTG_GINTMSK_OTGINT | USB_OTG_GINTMSK_MMISM; */ // device initialization // descdma, device speed, non-zero-length status out handshake, periodic frame interval device->device->DCFG = 0; device->core->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL | USB_OTG_GOTGCTL_BVALOEN; // TODO device threshold control register IF DMA // unmask interrupts usb reset, enumeration done, early suspend, usb suspend, sof device->core->GINTMSK |= USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_RSTDEM | USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ESUSPM | USB_OTG_GINTMSK_SOFM | USB_OTG_GINTMSK_RXFLVLM; // sdis bit device->device->DCTL = ~(USB_OTG_DCTL_SDIS); // wait for usbrst interrupt - the reset /* while (((device->core->GINTSTS) & USB_OTG_GINTSTS_USBRST) == 0) {} */ // wait for enumdne interrupt - end of reset /* while (((device->core->GINTSTS) & USB_OTG_GINTSTS_ENUMDNE) == 0) {} */ } task_result_t usb_handle_setup_command(usb_device_t *device, usb_setup_command_t* cmd) { task_result_t result = RES_ERROR; switch (cmd->bRequest) { case USB_SETUP_GET_STATUS: { uint8_t size; uint8_t* packet; usb_device_status_t device_status; usb_interface_status_t interface_status; usb_endpoint_status_t endpoint_status; switch (cmd->bmRequestType.recipient) { case USB_SETUP_DEVICE: { size = sizeof(usb_device_status_t); device_status.self_powered = 0; device_status.remote_wakeup = 0; device_status.reserved_zeros = 0; packet = (uint8_t*)&device_status; } break; case USB_SETUP_INTERFACE: { size = sizeof(usb_interface_status_t); interface_status.reserved = 0; packet = (uint8_t*)&interface_status; } break; case USB_SETUP_ENDPOINT: { size = sizeof(usb_endpoint_status_t); endpoint_status.halt = 0; endpoint_status.reserved_zeros = 0; packet = (uint8_t*)&interface_status; } break; } result = usb_generic_send(device->in, packet, size, &device->fifos[0].data[0]); if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; } } break; case USB_SETUP_GET_DESCRIPTOR: { usb_descriptor_type_t descriptor_type = (cmd->wValue >> 8) & 0xFF; uint8_t descriptor_index = cmd->wValue & 0xFF; switch (descriptor_type) { case DESCRIPTOR_DEVICE: result = usb_send_device_descriptor(device->in, &device->class->device_descriptor, cmd->wLength, &device->fifos[0].data[0]); break; case DESCRIPTOR_CONFIGURATION: result = device->vt.send_configuration(device, cmd); break; case DESCRIPTOR_STRING: { if (descriptor_index == 0) { result = usb_send_string_descriptor_zero(device->in, &device->class->string_descriptor_zero, cmd->wLength, &device->fifos[0].data[0]); } else { uint8_t index = descriptor_index - 1; // NOTE: the user could potentially read different memory part!! // This has to be fixed, the length has to be stored somewhere. result = usb_send_unicode_string_descriptor(device->in, &device->class->string_descriptors[index], cmd->wLength, &device->fifos[0].data[0]); } } break; case DESCRIPTOR_INTERFACE: result = usb_send_interface_descriptor(device->in, &device->class->interfaces[descriptor_index].interface_descriptor, cmd->wLength, &device->fifos[0].data[0]); break; case DESCRIPTOR_ENDPOINT: // TODO: how to match the interface to the descriptor index? result = usb_send_endpoint_descriptor(device->in, &device->class->interfaces[0].endpoint_descriptors[descriptor_index], cmd->wLength, &device->fifos[0].data[0]); break; case DESCRIPTOR_DEVICE_QUALIFIER: result = usb_send_device_qualifier_descriptor(device->in, &device->class->device_qualifier, cmd->wLength, &device->fifos[0].data[0]); break; case DESCRIPTOR_OTHER_SPEED_CONFIGURATION: case DESCRIPTOR_INTERFACE_POWER: reg_set_bits(&device->out[0].DOEPCTL, USB_OTG_DOEPCTL_STALL); break; } if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; } } break; case USB_SETUP_GET_CONFIGURATION: { uint8_t value = device->class->configuration_descriptor.bConfigurationValue; if (device->state != ENUMERATED) { value = 0; } result = usb_generic_send(device->in, &value, sizeof(value), &device->fifos[0].data[0]); if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; } } break; case USB_SETUP_GET_INTERFACE: { result = usb_generic_send(device->in, &device->class->interfaces[cmd->wIndex].interface_descriptor.bAlternateSetting, sizeof(uint8_t), &device->fifos[0].data[0]); if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; } } break; case USB_SETUP_SET_ADDRESS: reg_write_bits_pos(&device->device->DCFG, cmd->wValue, USB_OTG_DCFG_DAD_Pos, 0x7F); result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_ACK; device->state = SET_ADDRESS_RCVD; } break; case USB_SETUP_SET_CONFIGURATION: { if (cmd->wValue == 0) { device->vt.reset_endpoints(device); result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); if (result == RES_OK) { device->state = SET_ADDRESS_RCVD; } } else if (cmd->wValue == device->class->configuration_descriptor.bConfigurationValue) { device->vt.setup_endpoints(device, cmd->wValue); result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_ACK; device->state = ENUMERATED; } } else { reg_set_bits(&device->in[0].DIEPCTL, USB_OTG_DIEPCTL_STALL); } } break; case USB_SETUP_CLEAR_FEATURE: switch (cmd->wValue) { case USB_FEATURE_ENDPOINT_HALT: { uint8_t ep_id = cmd->wIndex; reg_clear_bits(&device->in[ep_id].DIEPCTL, USB_OTG_DIEPCTL_STALL); reg_clear_bits(&device->out[ep_id].DOEPCTL, USB_OTG_DOEPCTL_STALL); } break; case USB_FEATURE_TEST_MODE: case USB_FEATURE_DEVICE_REMOTE_WAKEUP: // Do not do anything... break; } result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_ACK; } break; case USB_SETUP_SET_FEATURE: switch (cmd->wValue) { case USB_FEATURE_ENDPOINT_HALT: { uint8_t ep_id = cmd->wIndex; reg_set_bits(&device->in[ep_id].DIEPCTL, USB_OTG_DIEPCTL_STALL); reg_set_bits(&device->out[ep_id].DOEPCTL, USB_OTG_DOEPCTL_STALL); } break; case USB_FEATURE_TEST_MODE: case USB_FEATURE_DEVICE_REMOTE_WAKEUP: // Do not do anything... break; } result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); if (result == RES_OK) { device->setup.stage = SETUP_STAGE_SENDING_ACK; } break; case USB_SETUP_SET_DESCRIPTOR: case USB_SETUP_SET_INTERFACE: case USB_SETUP_SYNCH_FRAME: device->in->DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_STALL; device->state = UNKNOWN_CONTROL_COMMAND; break; case RESERVED1: case RESERVED2: default: return device->vt.setup_packet_callback(device, cmd); break; } return result; } typedef enum { USB_UNKNOWN, USB_GOT_SETUP, USB_PROCESS_SETUP, USB_GOT_ACK, USB_GOT_DATA, USB_SENT_RESPONSE, } usb_event_t; task_result_t usb_handle_setup(usb_device_t *device, usb_event_t event) { switch (device->setup.stage) { case SETUP_STAGE_RCVD_SETUP_PACKET: { if (event == USB_GOT_SETUP) { return RES_OK; } usb_setup_command_t* command = queue_peek(device->setup.received_setup_commands); if (command == NULL) { device->setup.stage = SETUP_STAGE_NONE; break; } // pre-processing of data commands - first get the data, only then proceed with handle_setup_command if (command->bmRequestType.direction == USB_SETUP_HOST_TO_DEVICE && command->wLength > 0 && device->setup.rcvd_awaiting == 0) { device->setup.rcvd_awaiting = command->wLength; device->setup.rcvd_count = 0; device->setup.rcvd_data = (uint8_t*)malloc(device->setup.rcvd_count); device->setup.stage = SETUP_STAGE_AWAITING_DATA; break; } task_result_t result = usb_handle_setup_command(device, command); if (result == RES_OK) { queue_dequeue(device->setup.received_setup_commands); uint8_t setup_packets_count = reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); if (setup_packets_count == 0) { reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); } if (device->setup.rcvd_data != NULL) { free(device->setup.rcvd_data); device->setup.rcvd_data = NULL; } device->setup.rcvd_awaiting = 0; device->setup.rcvd_count = 0; } return result; } break; case SETUP_STAGE_NONE: if (queue_peek(device->setup.received_setup_commands) != NULL) { device->setup.stage = SETUP_STAGE_RCVD_SETUP_PACKET; if (event == USB_PROCESS_SETUP) { usb_handle_setup(device, event); } } break; case SETUP_STAGE_AWAITING_DATA: if (event == USB_GOT_DATA) { if (device->setup.rcvd_count >= device->setup.rcvd_awaiting) { device->setup.stage = SETUP_STAGE_RCVD_SETUP_PACKET; } } break; case SETUP_STAGE_SENDING_RESPONSE: if (event == USB_SENT_RESPONSE) { device->setup.stage = SETUP_STAGE_AWAITING_ACK; } break; case SETUP_STAGE_SENDING_ACK: if (event == USB_SENT_RESPONSE) { device->setup.stage = SETUP_STAGE_NONE; return usb_handle_setup(device, event); } break; case SETUP_STAGE_AWAITING_ACK: if (event == USB_GOT_ACK) { device->setup.stage = SETUP_STAGE_NONE; return usb_handle_setup(device, event); } break; } return RES_OK; } void usb_handle_rxflvl_control_int(usb_device_t *device, packet_info_t *packet_info) { uint32_t dummy; uint8_t data[64]; volatile uint32_t *fifo = device->fifos[0].data; if (packet_info->packet_status == PACKET_SETUP_TRANSACTION_COMPLETED) { if (packet_info->byte_count > 0) { usb_generic_read(data, packet_info->byte_count, fifo); } } else if (packet_info->packet_status == PACKET_SETUP_DATA_PACKET_RECEIVED) { // SAVE data usb_setup_command_t command; usb_generic_read((uint8_t*)&command, 8, fifo); if (command.wLength > 60000) { dummy = 0; } if (!queue_enqueue(device->setup.received_setup_commands, &command)) { // Got a problem, setup command lost! device->setup.detected_setup_errors++; } usb_handle_setup(device, USB_GOT_SETUP); } else if (packet_info->byte_count != 0) { if (device->setup.stage == SETUP_STAGE_AWAITING_DATA && device->setup.rcvd_data != NULL) { // TODO: check if still enough space usb_generic_read(&device->setup.rcvd_data[device->setup.rcvd_count], packet_info->byte_count, fifo); device->setup.rcvd_count += packet_info->byte_count; } if (device->setup.stage == SETUP_STAGE_AWAITING_ACK) { // This is an error, since there is data in status phase... // TODO: How to handle? device->setup.detected_setup_errors++; } } } void usb_handle_rxflvl_int(usb_device_t *device) { // Disable the interrupt reg_clear_bits(&device->core->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); /* uint32_t rx_data; */ /* while ((rx_data = device->core->GRXSTSP) != 0) { */ while (device->core->GINTSTS & USB_OTG_GINTSTS_RXFLVL) { uint32_t status_data = device->core->GRXSTSP; /* uint8_t status_phase_start = reg_read_bits_pos(&status_data, USB_OTG_GRXSTSP_STSPHST); */ packet_info_t packet_info = { .status_phase_start = reg_read_bits_pos(&status_data, 27, 0x01), .packet_status = (packet_status_t)reg_read_bits_pos(&status_data, USB_OTG_GRXSTSP_PKTSTS_Pos, 0xF), .dpid = (packet_dpid_t)reg_read_bits_pos(&status_data, USB_OTG_GRXSTSP_DPID_Pos, 0x3), .byte_count = reg_read_bits_pos(&status_data, USB_OTG_GRXSTSP_BCNT_Pos, 0x7FF), .endpoint_num = reg_read_bits_pos(&status_data, USB_OTG_GRXSTSP_EPNUM_Pos, 0xF), }; if (packet_info.endpoint_num == 0) { usb_handle_rxflvl_control_int(device, &packet_info); } else { device->vt.received_data_callback(device, &packet_info); } } // Re-enable the interrupt reg_set_bits(&device->core->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); } uint8_t usb_daint_get_endpoint_number(uint32_t endpoints) { for (int i = 0; i < 15; i++) { if (endpoints & (1 << i)) { return i; } } return 0xFF; } void usb_handle_endpoint_in_int(usb_device_t* device) { uint32_t daint = reg_read_bits_pos(&device->device->DAINT, USB_OTG_DAINT_IEPINT_Pos, 0xFFFF); uint8_t ep_id = 0; while (ep_id != 0xFF) { ep_id = usb_daint_get_endpoint_number(daint); daint &= ~(1 << ep_id); if (ep_id == 0xFF) { return; } uint32_t interrupt_reg = device->in[ep_id].DIEPINT; if (interrupt_reg & USB_OTG_DIEPINT_PKTDRPSTS) { // Not generated by interrupt! So if we get this one // here, it's not from the interrupt. Just clear it, // isochronous pipes can drop data from time to time reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_PKTDRPSTS); } if (interrupt_reg & USB_OTG_DIEPINT_NAK) { // NOTE no need to do much. Hardware will resend device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_NAK; } if (interrupt_reg & USB_OTG_DIEPINT_BNA) { // NOTE this should not be reached as DMA is not used device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_BNA; } if (interrupt_reg & USB_OTG_DIEPINT_TXFIFOUDRN) { // NOTE this should not be reached as thresholding is not used device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_TXFIFOUDRN; } if (interrupt_reg & USB_OTG_DIEPINT_TXFE) { if (device->state == ENUMERATED) { device->vt.txfifo_empty_callback(device, ep_id); } device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_TXFE; } if (interrupt_reg & USB_OTG_DIEPINT_INEPNE) { // NAK effective. Okay, ack, go on. device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_INEPNE; } if (interrupt_reg & USB_OTG_DIEPINT_INEPNM) { device->state = USB_DEV_ERROR; // data on top of TxFIFO belong to endpoint other // than the one for which in token was received. device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_INEPNM; } if (interrupt_reg & USB_OTG_DIEPINT_ITTXFE) { // In token when no data. How to proceed? TODO device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_ITTXFE; } if (interrupt_reg & USB_OTG_DIEPINT_TOC) { // Timeout condition. Skip? TODO how to proceed? device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_TOC; } if (interrupt_reg & USB_OTG_DIEPINT_AHBERR) { // NOTE this should not be reached as thresholding is not used device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_AHBERR; } if (interrupt_reg & USB_OTG_DIEPINT_EPDISD) { // Endpoint is disabled, per application's request. Okay. device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_EPDISD; } if (interrupt_reg & USB_OTG_DIEPINT_XFRC) { // Transfer is completed. if (device->state == ENUMERATED) { device->vt.rx_done_callback(device, ep_id); } if (ep_id == 0) { usb_handle_setup(device, USB_SENT_RESPONSE); usb_handle_setup(device, USB_PROCESS_SETUP); } device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_XFRC; } } } #define USB_OTG_DOEPINT_BNA_Pos (9U) #define USB_OTG_DOEPINT_BNA_Msk (0x1UL << USB_OTG_DOEPINT_B2BSTUP_Pos) /*!< 0x00000040 */ #define USB_OTG_DOEPINT_BNA USB_OTG_DOEPINT_B2BSTUP_Msk /*!< Back-to-back SETUP packets received */ void usb_handle_endpoint_out_int(usb_device_t* device) { uint32_t daint = reg_read_bits_pos(&device->device->DAINT, USB_OTG_DAINT_OEPINT_Pos, 0xFFFF); uint8_t ep_id = 0; while (ep_id != 0xFF) { ep_id = usb_daint_get_endpoint_number(daint); daint &= ~(1 << ep_id); if (ep_id == 0xFF) { return; } uint32_t interrupt_reg = device->out[ep_id].DOEPINT; if (interrupt_reg & USB_OTG_DOEPINT_STPKTRX) { // NOTE This shouldn't be reached since DMA is not used device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_STPKTRX; } if (interrupt_reg & USB_OTG_DOEPINT_NYET) { // We don't really care about this one for now device->vt.nyet_callback(device, ep_id); device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_NYET; } if (interrupt_reg & USB_OTG_DOEPINT_NAK) { // We don't really care about this one for now device->vt.nak_callback(device, ep_id); device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_NAK; } if (interrupt_reg & USB_OTG_DOEPINT_BERR) { // Uh? Babble much? device->state = CONTROL_ERROR; device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_BERR; } if (interrupt_reg & USB_OTG_DOEPINT_BNA) { // NOTE This shoudln't be reached since DMA is not used device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_BNA; } if (interrupt_reg & USB_OTG_DOEPINT_OUTPKTERR) { // NOTE thresholding not enabled, so this shouldn't be reached device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_OUTPKTERR; } if (interrupt_reg & USB_OTG_DOEPINT_B2BSTUP) { // TODO: this is a problem! we couldn't capture all the packets! device->state = CONTROL_ERROR; device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_B2BSTUP; } if (interrupt_reg & USB_OTG_DOEPINT_OTEPSPR) { // TODO: is there someting to do? The data sending is usually handled prior // to this, in handling of setup / data stages. Maybe it should be // moved here instead, since it's possible other packet will be // received in the meantime? device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_OTEPSPR; } if (interrupt_reg & USB_OTG_DOEPINT_OTEPDIS) { // NOTE: Can we handle this? a callback to application? device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_OTEPDIS; } if (interrupt_reg & USB_OTG_DOEPINT_STUP) { device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_STUP; if (ep_id == 0) { usb_handle_setup(device, USB_PROCESS_SETUP); device->out[ep_id].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; } } if (interrupt_reg & USB_OTG_DOEPINT_AHBERR) { // NOTE This shoudln't be reached since DMA is not used device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_AHBERR; } if (interrupt_reg & USB_OTG_DOEPINT_EPDISD) { // NOTE endpoint has been disabled, as was instructed. So this shoudln't // need handling? device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_EPDISD; } if (interrupt_reg & USB_OTG_DOEPINT_XFRC) { device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_XFRC; if (device->state == ENUMERATED) { device->vt.rx_done_callback(device, ep_id); } if (ep_id == 0) { if (device->setup.rcvd_count > 0) { usb_handle_setup(device, USB_GOT_DATA); } else { usb_handle_setup(device, USB_GOT_ACK); } // Process next packet / data usb_handle_setup(device, USB_PROCESS_SETUP); device->out[ep_id].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; } } } } // NOTE: this is irq handler void otg_hs_handler(void) { usb_device_t* device = &usb_devices[USB_OTG_HS1]; uint8_t handled = 0; // Start of frame if (device->core->GINTSTS & USB_OTG_GINTSTS_SOF) { device->core->GINTSTS = USB_OTG_GINTSTS_SOF; handled = 1; /* usb_handle_setup(device, USB_PROCESS_SETUP); */ } // Reset detected if (device->core->GINTSTS & USB_OTG_GINTSTS_USBRST) { // clear it device->core->GINTSTS = USB_OTG_GINTSTS_USBRST; device->setup.stage = SETUP_STAGE_NONE; while (queue_dequeue(device->setup.received_setup_commands) != NULL); reg_set_bits(&device->core->GINTMSK, USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT); reg_set_bits(&device->device->DOEPMSK, USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT); for (int ep = 0; ep < 8; ep++) { USB_OTG_OUTEndpointTypeDef *out = device->out + ep; out->DOEPCTL |= USB_OTG_DOEPCTL_SNAK; } device->device->DCTL = USB_OTG_DCTL_CGINAK | USB_OTG_DCTL_CGONAK; device->device->DAINTMSK |= 1 << USB_OTG_DAINTMSK_IEPM_Pos; device->device->DAINTMSK |= 1 << USB_OTG_DAINTMSK_OEPM_Pos; device->device->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM; device->device->DIEPMSK |= USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM; // 512 bytes device->core->GRXFSIZ = 1024 / 4; // 64 bytes, beginning of ram device->core->DIEPTXF[0] = (0 << USB_OTG_DIEPTXF_INEPTXSA_Pos) | ((128 / 4) << USB_OTG_DIEPTXF_INEPTXFD_Pos); // 3 packets to receive as setup reg_write_bits_pos(&device->out->DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); device->state = USB_DEV_RESET; return; } // Reset ended if (device->core->GINTSTS & USB_OTG_GINTSTS_ENUMDNE) { device->core->GINTSTS = USB_OTG_GINTSTS_ENUMDNE; // The enumerated speed /* (device->device->DSTS & USB_OTG_DSTS_ENUMSPD_Msk) >> USB_OTG_DSTS_ENUMSPD_Pos; */ // We do not need to know the speed, it can be either full speed or high // speed, both have maximum size 64 bytes. Only low speed or super high // speed would differ, but these are not supported! reg_write_bits_pos(&device->in[0].DIEPCTL, 0, USB_OTG_DIEPCTL_MPSIZ_Pos, 0x7FFUL); device->in->DIEPCTL |= USB_OTG_DOEPCTL_SNAK; device->out->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; device->state = RESET_DONE; return; } // OTG interrupt, should not be triggered since forced device mode if (device->core->GINTSTS & USB_OTG_GINTSTS_OTGINT) { device->state = OTG_ERROR; device->core->GINTSTS = USB_OTG_GINTSTS_OTGINT; return; } // OTG interrupt, should not be triggered since forced device mode if (device->core->GINTSTS & USB_OTG_GINTSTS_MMIS) { device->state = OTG_ERROR; device->core->GINTSTS = USB_OTG_GINTSTS_MMIS; return; } // Setup, data... if (device->core->GINTSTS & USB_OTG_GINTSTS_OEPINT) { usb_handle_endpoint_out_int(device); handled = 1; } if (device->core->GINTSTS & USB_OTG_GINTSTS_IEPINT) { usb_handle_endpoint_in_int(device); handled = 1; } if (device->core->GINTSTS & (USB_OTG_GINTSTS_RXFLVL)) { usb_handle_rxflvl_int(device); handled = 1; } if (device->core->GINTSTS & (USB_OTG_GINTSTS_RSTDET)) { // Soft reset, meaning the usb is likely not connected device->core->GINTSTS = USB_OTG_GINTSTS_RSTDET; device->setup.stage = SETUP_STAGE_NONE; while (queue_dequeue(device->setup.received_setup_commands) != NULL); handled = 1; } if (device->core->GINTSTS & (USB_OTG_GINTSTS_ESUSP)) { // The usb has probably been disconnected device->core->GINTSTS = USB_OTG_GINTSTS_ESUSP; device->setup.stage = SETUP_STAGE_NONE; while (queue_dequeue(device->setup.received_setup_commands) != NULL); handled = 1; } if (handled == 0) { device->state = UNKNOWN_INTERRUPT; } }