/* * This file is part of the libopencm3 project. * * Copyright (C) 2010 Gareth McMullin * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include //#define APP_ADDRESS 0x08002000 //#define APP_ADDRESS 0x00002000 #define APP_ADDRESS 0x00002000 //#define APP_ADDRESS 0x00004000 /* Commands sent with wBlockNum == 0 as per ST implementation. */ #define CMD_SETADDR 0x21 #define CMD_ERASE 0x41 #define BUTTON_PORT PORTA #define BUTTON_PIN GPIO27 #define BUF_SIZE 4096 static struct gclk_hw clock = { .gclk0 = SRC_DFLL48M, .gclk1 = SRC_OSC8M, /* clock 1 has 8 divider, clock should be over 1khz for 1ms timer */ .gclk1_div = 100, .gclk2 = SRC_DFLL48M, .gclk3 = SRC_DFLL48M, .gclk3_div = 1, .gclk4 = SRC_OSC8M, .gclk4_div = 1, .gclk5 = SRC_DFLL48M, .gclk6 = SRC_DFLL48M, .gclk7 = SRC_DFLL48M, }; /* We need a special large control buffer for this device: */ uint8_t usbd_control_buffer[BUF_SIZE]; static enum dfu_state usbdfu_state = STATE_DFU_IDLE; static struct { uint8_t buf[sizeof(usbd_control_buffer)]; uint16_t len; uint32_t addr; uint16_t blocknum; } prog; const struct usb_device_descriptor dev = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .idVendor = 0x1D50, .idProduct = 0x6017, .bcdDevice = 0x0200, .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 3, .bNumConfigurations = 1, }; const struct usb_dfu_descriptor dfu_function = { .bLength = sizeof(struct usb_dfu_descriptor), .bDescriptorType = DFU_FUNCTIONAL, .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, .wDetachTimeout = 255, .wTransferSize = BUF_SIZE, .bcdDFUVersion = 0x011A, }; const struct usb_interface_descriptor iface = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 0, .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */ .bInterfaceSubClass = 1, .bInterfaceProtocol = 2, /* The ST Microelectronics DfuSe application needs this string. * The format isn't documented... */ .iInterface = 4, .extra = &dfu_function, .extralen = sizeof(dfu_function), }; const struct usb_interface ifaces[] = {{ .num_altsetting = 1, .altsetting = &iface, }}; const struct usb_config_descriptor config = { .bLength = USB_DT_CONFIGURATION_SIZE, .bDescriptorType = USB_DT_CONFIGURATION, .wTotalLength = 0, .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = 0xC0, .bMaxPower = 0x32, .interface = ifaces, }; static const char *usb_strings[] = { "Black Sphere Technologies", "DFU Demo", "DEMO", /* This string is used by ST Microelectronics' DfuSe utility. */ //"@Internal Flash /0x08000000/8*001Ka,56*001Kg", "@Internal Flash /0x00000000/1*008Ka,15*008Kg", //"@Internal Flash /0x00000000/1*0016Ka,15*0016Kg", }; static uint8_t usbdfu_getstatus(uint32_t *bwPollTimeout) { switch (usbdfu_state) { case STATE_DFU_DNLOAD_SYNC: usbdfu_state = STATE_DFU_DNBUSY; *bwPollTimeout = 100; return DFU_STATUS_OK; case STATE_DFU_MANIFEST_SYNC: /* Device will reset when read is complete. */ usbdfu_state = STATE_DFU_MANIFEST; return DFU_STATUS_OK; default: return DFU_STATUS_OK; } } static void usbdfu_getstatus_complete(usbd_device *usbd_dev, struct usb_setup_data *req) { int i; (void)req; (void)usbd_dev; switch (usbdfu_state) { case STATE_DFU_DNBUSY: //flash_unlock(); if (prog.blocknum == 0) { switch (prog.buf[0]) { case CMD_ERASE: { uint32_t *dat = (uint32_t *)(prog.buf + 1); nvmctrl_erase_row(*dat); //flash_erase_page(*dat); } break; case CMD_SETADDR: { uint32_t *dat = (uint32_t *)(prog.buf + 1); prog.addr = *dat; } break; } } else { //uint32_t baseaddr = prog.addr + ((prog.blocknum - 2) * // dfu_function.wTransferSize); uint32_t baseaddr = prog.addr; //for (i = 0; i < prog.len; i += 2) { //uint16_t *dat = (uint16_t *)(prog.buf + i); //flash_program_half_word(baseaddr + i, // *dat); for (i = 0; i < BUF_SIZE; i += 256){ nvmctrl_erase_row(baseaddr+i); nvmctrl_write_row(baseaddr+i, prog.buf+i); } //} } //flash_lock(); /* Jump straight to dfuDNLOAD-IDLE, skipping dfuDNLOAD-SYNC. */ usbdfu_state = STATE_DFU_DNLOAD_IDLE; return; case STATE_DFU_MANIFEST: /* reset USB */ INSERTBF(USB_CTRLA_SWRST, 1, USB->ctrla); /* jump to app */ if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) { /* Set vector table base address. */ //SCB_VTOR = APP_ADDRESS & 0xFFFF; SCB_VTOR = APP_ADDRESS; /* Initialise master stack pointer. */ asm volatile("msr msp, %0"::"g" (*(volatile uint32_t *)APP_ADDRESS)); /* Jump to application. */ (*(void (**)())(APP_ADDRESS + 4))(); } //scb_reset_system(); return; /* Will never return. */ default: return; } } static enum usbd_request_return_codes usbdfu_control_request(usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, void (**complete)(usbd_device *dev, struct usb_setup_data *req)) { (void)dev; if ((req->bmRequestType & 0x7F) != 0x21) return 0; /* Only accept class request. */ switch (req->bRequest) { case DFU_DNLOAD: if ((len == NULL) || (*len == 0)) { usbdfu_state = STATE_DFU_MANIFEST_SYNC; return 1; } else { /* Copy download data for use on GET_STATUS. */ prog.blocknum = req->wValue; prog.len = *len; memcpy(prog.buf, *buf, *len); usbdfu_state = STATE_DFU_DNLOAD_SYNC; return 1; } case DFU_CLRSTATUS: /* Clear error and return to dfuIDLE. */ if (usbdfu_state == STATE_DFU_ERROR) usbdfu_state = STATE_DFU_IDLE; return 1; case DFU_ABORT: /* Abort returns to dfuIDLE state. */ usbdfu_state = STATE_DFU_IDLE; return 1; case DFU_UPLOAD: /* Upload not supported for now. */ return 0; case DFU_GETSTATUS: { uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec */ (*buf)[0] = usbdfu_getstatus(&bwPollTimeout); (*buf)[1] = bwPollTimeout & 0xFF; (*buf)[2] = (bwPollTimeout >> 8) & 0xFF; (*buf)[3] = (bwPollTimeout >> 16) & 0xFF; (*buf)[4] = usbdfu_state; (*buf)[5] = 0; /* iString not used here */ *len = 6; *complete = usbdfu_getstatus_complete; return 1; } case DFU_GETSTATE: /* Return state with no state transision. */ *buf[0] = usbdfu_state; *len = 1; return 1; } return 0; } static void usbdfu_set_config(usbd_device *usbd_dev, uint16_t wValue) { (void)wValue; usbd_register_control_callback( usbd_dev, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, usbdfu_control_request); } static void usb_setup(void) { /* Enable USB */ INSERTBF(PM_APBBMASK_USB, 1, PM->apbbmask); /* enable clocking to usb */ set_periph_clk(GCLK0, GCLK_ID_USB); periph_clk_en(GCLK_ID_USB, 1); gpio_config_special(PORTA, GPIO24, SOC_GPIO_PERIPH_G); gpio_config_special(PORTA, GPIO25, SOC_GPIO_PERIPH_G); } int main(void) { usbd_device *usbd_dev; gclk_init(&clock); //rcc_periph_clock_enable(RCC_GPIOA); //gpio_config_input(BUTTON_PORT,BUTTON_PIN,GPIO_IN_FLAG_PULLUP); gpio_config_input(BUTTON_PORT,BUTTON_PIN,0); nvmctrl_init(0,0); usb_setup(); if (PM->rcause != (1<<6)) if (gpio_get(BUTTON_PORT, BUTTON_PIN)) { //if (gpio_get(PORTA, GPIO27)) { /* Boot the application if it's valid. */ if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) { /* Set vector table base address. */ //SCB_VTOR = APP_ADDRESS & 0xFFFF; SCB_VTOR = APP_ADDRESS; /* Initialise master stack pointer. */ asm volatile("msr msp, %0"::"g" (*(volatile uint32_t *)APP_ADDRESS)); /* Jump to application. */ (*(void (**)())(APP_ADDRESS + 4))(); } } usbd_dev = usbd_init(&samd21_usb_driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer)); usbd_register_set_config_callback(usbd_dev, usbdfu_set_config); //nvic_enable_irq(NVIC_USB_IRQ); /* Connect USB cable */ usbd_disconnect(usbd_dev, false); //gpio_clear(GPIOC, GPIO11); while (1) usbd_poll(usbd_dev); }