- Heavily modified, not compatible with McHCK images - Uses 4k of Flash (rather than 3k) - LED turns on when in firmware flash mode - Changed the USB IDs - Added CMake build system - Updated linker script to be closer to what is used for the rest of the projects - Removed a lot of unnecessary code - Added a license header to each file (using the same license as the McHCK had) - Updated the USB Vendor, Product and Serial strings - Using the Kiibohd initialization sequence rather than the McHCK one - Using Kiibohd interrupt vector table and other misc mk20dx setupsimple
@@ -0,0 +1,173 @@ | |||
###| CMAKE Kiibohd Controller Bootloader |### | |||
# | |||
# Jacob Alexander 2011-2014 | |||
# Due to this file's usefulness: | |||
# | |||
# Released into the Public Domain | |||
# | |||
# This bootloader is based upon the MCHCK dfu-usb bootloader. | |||
# DO NOT USE with Teensy based microcontrollers. | |||
# | |||
### | |||
### | |||
# Chip Selection | |||
# | |||
#| You _MUST_ set this to match the microcontroller you are trying to compile for | |||
#| You _MUST_ clean the build directory if you change this value | |||
#| | |||
set( CHIP | |||
"mk20dx128vlf5" # McHCK mk20dx128vlf5 | |||
) | |||
### | |||
# Bootloader Configuration | |||
# | |||
set ( BOOTLOADER 1 ) | |||
### | |||
# Compiler Intialization | |||
# | |||
set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../Lib/CMake ) | |||
include( initialize ) | |||
## | |||
# Source Defines | |||
# | |||
set( SRCS | |||
main.c | |||
dfu.c | |||
dfu.desc.c | |||
flash.c | |||
kinetis.c | |||
usb.c | |||
) | |||
message( STATUS "Bootloader Source Files:" ) | |||
message( "${SRCS}" ) | |||
#| Add Lib sources to main list | |||
foreach( SRC_FILE ${COMPILER_SRCS} ) | |||
set( SRCS ${SRCS} ${CMAKE_SOURCE_DIR}/../${SRC_FILE} ) | |||
endforeach() | |||
### | |||
# Directory Includes | |||
# | |||
include_directories( ${CMAKE_SOURCE_DIR}/../Lib ) | |||
### | |||
# Project Description | |||
# | |||
#| Project | |||
project( kiibohd_bootloader ) | |||
#| Target Name (output name) | |||
set( TARGET kiibohd_bootloader ) | |||
#| General Settings | |||
cmake_minimum_required( VERSION 2.8 ) | |||
### | |||
# Generate Header Files | |||
# | |||
configure_file( _buildvars.h buildvars.h ) | |||
include_directories( ${CMAKE_BINARY_DIR} ) | |||
### | |||
# CMake Module Checking | |||
# | |||
find_package( Git REQUIRED ) | |||
find_package( Ctags ) # Optional | |||
### | |||
# ctag Generation | |||
# | |||
if( CTAGS_EXECUTABLE ) | |||
# Generate the ctags | |||
execute_process( COMMAND ctags ${SRCS} | |||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | |||
) | |||
endif() | |||
### | |||
# Build Targets | |||
# | |||
#| Create the .ELF file | |||
set( TARGET_ELF ${TARGET}.elf ) | |||
add_executable( ${TARGET_ELF} ${SRCS} ) | |||
#| .ELF Properties | |||
set_target_properties( ${TARGET_ELF} PROPERTIES | |||
LINK_FLAGS ${LINKER_FLAGS} | |||
SUFFIX "" # XXX Force Windows to keep the .exe off | |||
) | |||
#| Convert the .ELF into a .bin to load onto the McHCK | |||
set( TARGET_BIN ${TARGET}.bin ) | |||
add_custom_command( TARGET ${TARGET_ELF} POST_BUILD | |||
COMMAND ${CMAKE_OBJCOPY} ${BIN_FLAGS} ${TARGET_ELF} ${TARGET_BIN} | |||
COMMENT "Creating binary file to load: ${TARGET_BIN}" | |||
) | |||
#| Generate the Extended .LSS | |||
set( TARGET_LSS ${TARGET}.lss ) | |||
add_custom_command( TARGET ${TARGET_ELF} POST_BUILD | |||
COMMAND ${CMAKE_OBJDUMP} ${LSS_FLAGS} ${TARGET_ELF} > ${TARGET_LSS} | |||
COMMENT "Creating Extended Listing: ${TARGET_LSS}" | |||
) | |||
#| Generate the Symbol Table .SYM | |||
set( TARGET_SYM ${TARGET}.sym ) | |||
add_custom_command( TARGET ${TARGET_ELF} POST_BUILD | |||
COMMAND ${CMAKE_NM} -n ${TARGET_ELF} > ${TARGET_SYM} | |||
COMMENT "Creating Symbol Table: ${TARGET_SYM}" | |||
) | |||
#| Compiler Selection Record | |||
add_custom_command( TARGET ${TARGET_ELF} POST_BUILD | |||
COMMAND ${CMAKE_SOURCE_DIR}/../Lib/CMake/writer compiler ${COMPILER_FAMILY} | |||
) | |||
### | |||
# Size Information | |||
# | |||
#| After Changes Size Information | |||
add_custom_target( SizeAfter ALL | |||
COMMAND ${CMAKE_SOURCE_DIR}/../Lib/CMake/sizeCalculator ${CMAKE_SIZE} ram ${TARGET_ELF} ${SIZE_RAM} " SRAM" | |||
COMMAND ${CMAKE_SOURCE_DIR}/../Lib/CMake/sizeCalculator ${CMAKE_SIZE} flash ${TARGET_ELF} ${SIZE_FLASH} "Flash" | |||
DEPENDS ${TARGET_ELF} | |||
COMMENT "Chip usage for ${CHIP}" | |||
) | |||
@@ -0,0 +1,45 @@ | |||
/* Copyright (C) 2013-2014 by Jacob Alexander | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#ifndef __buildvars_h | |||
#define __buildvars_h | |||
// ----- Includes ----- | |||
// ----- Defines ----- | |||
// You can change these to give your code its own name. | |||
#define STR_MANUFACTURER u"@MANUFACTURER@" | |||
#define STR_PRODUCT u"Kiibohd DFU Bootloader" | |||
#define STR_SERIAL u"@CHIP@" | |||
// Mac OS-X and Linux automatically load the correct drivers. On | |||
// Windows, even though the driver is supplied by Microsoft, an | |||
// INF file is needed to load the driver. These numbers need to | |||
// match the INF file. | |||
#define VENDOR_ID @BOOT_VENDOR_ID@ | |||
#define PRODUCT_ID @BOOT_PRODUCT_ID@ | |||
#endif | |||
@@ -0,0 +1,171 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Local Includes ----- | |||
#include "usb.h" | |||
#include "dfu.h" | |||
// ----- Functions ----- | |||
void dfu_write_done( enum dfu_status err, struct dfu_ctx *ctx ) | |||
{ | |||
ctx->status = err; | |||
if (ctx->status == DFU_STATUS_OK) { | |||
switch (ctx->state) { | |||
case DFU_STATE_dfuDNBUSY: | |||
ctx->state = DFU_STATE_dfuDNLOAD_IDLE; | |||
break; | |||
default: | |||
break; | |||
} | |||
} else { | |||
ctx->state = DFU_STATE_dfuERROR; | |||
} | |||
} | |||
static void dfu_dnload_complete( void *buf, ssize_t len, void *cbdata ) | |||
{ | |||
struct dfu_ctx *ctx = cbdata; | |||
if (len > 0) | |||
ctx->state = DFU_STATE_dfuDNBUSY; | |||
else | |||
ctx->state = DFU_STATE_dfuMANIFEST; | |||
ctx->status = ctx->finish_write(buf, ctx->off, len); | |||
ctx->off += len; | |||
ctx->len = len; | |||
if (ctx->status != DFU_STATUS_async) | |||
dfu_write_done(ctx->status, ctx); | |||
usb_handle_control_status(ctx->state == DFU_STATE_dfuERROR); | |||
} | |||
static void dfu_reset_system( void *buf, ssize_t len, void *cbdata ) | |||
{ | |||
SOFTWARE_RESET(); | |||
} | |||
static int dfu_handle_control( struct usb_ctrl_req_t *req, void *data ) | |||
{ | |||
struct dfu_ctx *ctx = data; | |||
int fail = 1; | |||
switch ((enum dfu_ctrl_req_code)req->bRequest) { | |||
case USB_CTRL_REQ_DFU_DNLOAD: { | |||
void *buf; | |||
switch (ctx->state) { | |||
case DFU_STATE_dfuIDLE: | |||
ctx->off = 0; | |||
break; | |||
case DFU_STATE_dfuDNLOAD_IDLE: | |||
break; | |||
default: | |||
goto err; | |||
} | |||
/** | |||
* XXX we are not allowed to STALL here, and we need to eat all transferred data. | |||
* better not allow setup_write to break the protocol. | |||
*/ | |||
ctx->status = ctx->setup_write(ctx->off, req->wLength, &buf); | |||
if (ctx->status != DFU_STATUS_OK) { | |||
ctx->state = DFU_STATE_dfuERROR; | |||
goto err_have_status; | |||
} | |||
if (req->wLength > 0) | |||
usb_ep0_rx(buf, req->wLength, dfu_dnload_complete, ctx); | |||
else | |||
dfu_dnload_complete(NULL, 0, ctx); | |||
goto out_no_status; | |||
} | |||
case USB_CTRL_REQ_DFU_GETSTATUS: { | |||
struct dfu_status_t st; | |||
st.bState = ctx->state; | |||
st.bStatus = ctx->status; | |||
st.bwPollTimeout = 1000; /* XXX */ | |||
/** | |||
* If we're in DFU_STATE_dfuMANIFEST, we just finished | |||
* the download, and we're just about to send our last | |||
* status report. Once the report has been sent, go | |||
* and reset the system to put the new firmware into | |||
* effect. | |||
*/ | |||
usb_ep0_tx_cp(&st, sizeof(st), req->wLength, NULL, NULL); | |||
if (ctx->state == DFU_STATE_dfuMANIFEST) { | |||
usb_handle_control_status_cb(dfu_reset_system); | |||
goto out_no_status; | |||
} | |||
break; | |||
} | |||
case USB_CTRL_REQ_DFU_CLRSTATUS: | |||
ctx->state = DFU_STATE_dfuIDLE; | |||
ctx->status = DFU_STATUS_OK; | |||
break; | |||
case USB_CTRL_REQ_DFU_GETSTATE: { | |||
uint8_t st = ctx->state; | |||
usb_ep0_tx_cp(&st, sizeof(st), req->wLength, NULL, NULL); | |||
break; | |||
} | |||
case USB_CTRL_REQ_DFU_ABORT: | |||
switch (ctx->state) { | |||
case DFU_STATE_dfuIDLE: | |||
case DFU_STATE_dfuDNLOAD_IDLE: | |||
/* case DFU_STATE_dfuUPLOAD_IDLE: */ | |||
ctx->state = DFU_STATE_dfuIDLE; | |||
break; | |||
default: | |||
goto err; | |||
} | |||
break; | |||
/* case USB_CTRL_REQ_DFU_UPLOAD: */ | |||
default: | |||
return (0); | |||
} | |||
fail = 0; | |||
goto out; | |||
err: | |||
ctx->status = DFU_STATUS_errSTALLEDPKT; | |||
err_have_status: | |||
ctx->state = DFU_STATE_dfuERROR; | |||
out: | |||
usb_handle_control_status(fail); | |||
out_no_status: | |||
return (1); | |||
} | |||
void dfu_init( dfu_setup_write_t setup_write, dfu_finish_write_t finish_write, struct dfu_ctx *ctx ) | |||
{ | |||
ctx->state = DFU_STATE_dfuIDLE; | |||
ctx->setup_write = setup_write; | |||
ctx->finish_write = finish_write; | |||
usb_attach_function(&dfu_function, &ctx->header); | |||
} | |||
const struct usbd_function dfu_function = { | |||
.control = dfu_handle_control, | |||
.interface_count = USB_FUNCTION_DFU_IFACE_COUNT, | |||
}; | |||
@@ -0,0 +1,115 @@ | |||
// Originally Generated from MCHCK Toolkit | |||
/* Copyright (c) Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Local Includes ----- | |||
#include "dfu.desc.h" | |||
// ----- Generated Includes ----- | |||
#include <buildvars.h> | |||
// ----- Structs ----- | |||
static const struct usb_config_1 usb_config_1 = { | |||
.config = { | |||
.bLength = sizeof(struct usb_desc_config_t), | |||
.bDescriptorType = USB_DESC_CONFIG, | |||
.wTotalLength = sizeof(struct usb_config_1), | |||
.bNumInterfaces = 1, | |||
.bConfigurationValue = 1, | |||
.iConfiguration = 0, | |||
.one = 1, | |||
.bMaxPower = 100 | |||
}, | |||
.usb_function_0 = { | |||
.iface = { | |||
.bLength = sizeof(struct usb_desc_iface_t), | |||
.bDescriptorType = USB_DESC_IFACE, | |||
.bInterfaceNumber = 0, | |||
.bAlternateSetting = 0, | |||
.bNumEndpoints = 0, | |||
.bInterfaceClass = USB_DEV_CLASS_APP, | |||
.bInterfaceSubClass = USB_DEV_SUBCLASS_APP_DFU, | |||
.bInterfaceProtocol = USB_DEV_PROTO_DFU_DFU, | |||
.iInterface = 0 | |||
}, | |||
.dfu = { | |||
.bLength = sizeof(struct dfu_desc_functional), | |||
.bDescriptorType = { | |||
.id = 0x1, | |||
.type_type = USB_DESC_TYPE_CLASS | |||
}, | |||
.will_detach = 1, | |||
.manifestation_tolerant = 0, | |||
.can_upload = 0, | |||
.can_download = 1, | |||
.wDetachTimeOut = 0, | |||
.wTransferSize = USB_DFU_TRANSFER_SIZE, | |||
.bcdDFUVersion = { .maj = 1, .min = 1 } | |||
} | |||
}, | |||
}; | |||
static const struct usbd_config usbd_config_1 = { | |||
.init = init_usb_bootloader, | |||
.suspend = NULL, | |||
.resume = NULL, | |||
.desc = &usb_config_1.config, | |||
.function = {&dfu_function}, | |||
}; | |||
static const struct usb_desc_dev_t dfu_device_dev_desc = { | |||
.bLength = sizeof(struct usb_desc_dev_t), | |||
.bDescriptorType = USB_DESC_DEV, | |||
.bcdUSB = { .maj = 2 }, | |||
.bDeviceClass = USB_DEV_CLASS_SEE_IFACE, | |||
.bDeviceSubClass = USB_DEV_SUBCLASS_SEE_IFACE, | |||
.bDeviceProtocol = USB_DEV_PROTO_SEE_IFACE, | |||
.bMaxPacketSize0 = EP0_BUFSIZE, | |||
.idVendor = VENDOR_ID, | |||
.idProduct = PRODUCT_ID, | |||
.bcdDevice = { .raw = 0 }, | |||
.iManufacturer = 1, | |||
.iProduct = 2, | |||
.iSerialNumber = 3, | |||
.bNumConfigurations = 1, | |||
}; | |||
static const struct usb_desc_string_t * const dfu_device_str_desc[] = { | |||
USB_DESC_STRING_LANG_ENUS, | |||
USB_DESC_STRING(STR_MANUFACTURER), | |||
USB_DESC_STRING(STR_PRODUCT), | |||
USB_DESC_STRING(STR_SERIAL), | |||
NULL | |||
}; | |||
const struct usbd_device dfu_device = { | |||
.dev_desc = &dfu_device_dev_desc, | |||
.string_descs = dfu_device_str_desc, | |||
.configs = { | |||
&usbd_config_1, | |||
NULL | |||
} | |||
}; | |||
@@ -0,0 +1,43 @@ | |||
// Originally Generated from MCHCK Toolkit | |||
/* Copyright (c) Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef __DFU_DESC_H | |||
#define __DFU_DESC_H | |||
// ----- Local Includes ----- | |||
#include "mchck.h" | |||
// ----- Variables ----- | |||
const struct usbd_device dfu_device; | |||
usbd_init_fun_t init_usb_bootloader; | |||
// ----- Structs ----- | |||
struct usb_config_1 { | |||
struct usb_desc_config_t config; | |||
struct dfu_function_desc usb_function_0; | |||
}; | |||
#endif | |||
@@ -0,0 +1,197 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef _USB_DFU_H | |||
#define _USB_DFU_H | |||
// ----- Compiler Includes ----- | |||
#include <sys/types.h> | |||
// ----- Defines ----- | |||
#define USB_FUNCTION_DFU_IFACE_COUNT 1 | |||
#ifndef USB_DFU_TRANSFER_SIZE | |||
#define USB_DFU_TRANSFER_SIZE FLASH_SECTOR_SIZE | |||
#endif | |||
#define USB_FUNCTION_DESC_DFU_DECL \ | |||
struct dfu_function_desc | |||
#define USB_FUNCTION_DFU_IFACE_COUNT 1 | |||
#define USB_FUNCTION_DFU_RX_EP_COUNT 0 | |||
#define USB_FUNCTION_DFU_TX_EP_COUNT 0 | |||
// ----- Macros ----- | |||
#define USB_FUNCTION_DESC_DFU(state...) \ | |||
{ \ | |||
.iface = { \ | |||
.bLength = sizeof(struct usb_desc_iface_t), \ | |||
.bDescriptorType = USB_DESC_IFACE, \ | |||
.bInterfaceNumber = USB_FUNCTION_IFACE(0, state), \ | |||
.bAlternateSetting = 0, \ | |||
.bNumEndpoints = 0, \ | |||
.bInterfaceClass = USB_DEV_CLASS_APP, \ | |||
.bInterfaceSubClass = USB_DEV_SUBCLASS_APP_DFU, \ | |||
.bInterfaceProtocol = USB_DEV_PROTO_DFU_DFU, \ | |||
.iInterface = 0, \ | |||
}, \ | |||
.dfu = { \ | |||
.bLength = sizeof(struct dfu_desc_functional), \ | |||
.bDescriptorType = { \ | |||
.id = 0x1, \ | |||
.type_type = USB_DESC_TYPE_CLASS \ | |||
}, \ | |||
.will_detach = 1, \ | |||
.manifestation_tolerant = 0, \ | |||
.can_upload = 0, \ | |||
.can_download = 1, \ | |||
.wDetachTimeOut = 0, \ | |||
.wTransferSize = USB_DFU_TRANSFER_SIZE, \ | |||
.bcdDFUVersion = { .maj = 1, .min = 1 } \ | |||
} \ | |||
} | |||
// ----- Enumerations ----- | |||
enum dfu_dev_subclass { | |||
USB_DEV_SUBCLASS_APP_DFU = 0x01 | |||
}; | |||
enum dfu_dev_proto { | |||
USB_DEV_PROTO_DFU_APP = 0x01, | |||
USB_DEV_PROTO_DFU_DFU = 0x02 | |||
}; | |||
enum dfu_ctrl_req_code { | |||
USB_CTRL_REQ_DFU_DETACH = 0, | |||
USB_CTRL_REQ_DFU_DNLOAD = 1, | |||
USB_CTRL_REQ_DFU_UPLOAD = 2, | |||
USB_CTRL_REQ_DFU_GETSTATUS = 3, | |||
USB_CTRL_REQ_DFU_CLRSTATUS = 4, | |||
USB_CTRL_REQ_DFU_GETSTATE = 5, | |||
USB_CTRL_REQ_DFU_ABORT = 6 | |||
}; | |||
enum dfu_status { | |||
DFU_STATUS_async = 0xff, | |||
DFU_STATUS_OK = 0x00, | |||
DFU_STATUS_errTARGET = 0x01, | |||
DFU_STATUS_errFILE = 0x02, | |||
DFU_STATUS_errWRITE = 0x03, | |||
DFU_STATUS_errERASE = 0x04, | |||
DFU_STATUS_errCHECK_ERASED = 0x05, | |||
DFU_STATUS_errPROG = 0x06, | |||
DFU_STATUS_errVERIFY = 0x07, | |||
DFU_STATUS_errADDRESS = 0x08, | |||
DFU_STATUS_errNOTDONE = 0x09, | |||
DFU_STATUS_errFIRMWARE = 0x0a, | |||
DFU_STATUS_errVENDOR = 0x0b, | |||
DFU_STATUS_errUSBR = 0x0c, | |||
DFU_STATUS_errPOR = 0x0d, | |||
DFU_STATUS_errUNKNOWN = 0x0e, | |||
DFU_STATUS_errSTALLEDPKT = 0x0f | |||
}; | |||
enum dfu_state { | |||
DFU_STATE_appIDLE = 0, | |||
DFU_STATE_appDETACH = 1, | |||
DFU_STATE_dfuIDLE = 2, | |||
DFU_STATE_dfuDNLOAD_SYNC = 3, | |||
DFU_STATE_dfuDNBUSY = 4, | |||
DFU_STATE_dfuDNLOAD_IDLE = 5, | |||
DFU_STATE_dfuMANIFEST_SYNC = 6, | |||
DFU_STATE_dfuMANIFEST = 7, | |||
DFU_STATE_dfuMANIFEST_WAIT_RESET = 8, | |||
DFU_STATE_dfuUPLOAD_IDLE = 9, | |||
DFU_STATE_dfuERROR = 10 | |||
}; | |||
// ----- Structs ----- | |||
struct dfu_status_t { | |||
enum dfu_status bStatus : 8; | |||
uint32_t bwPollTimeout : 24; | |||
enum dfu_state bState : 8; | |||
uint8_t iString; | |||
} __packed; | |||
CTASSERT_SIZE_BYTE(struct dfu_status_t, 6); | |||
typedef enum dfu_status (*dfu_setup_write_t)(size_t off, size_t len, void **buf); | |||
typedef enum dfu_status (*dfu_finish_write_t)(void *, size_t off, size_t len); | |||
typedef void (*dfu_detach_t)(void); | |||
struct dfu_ctx { | |||
struct usbd_function_ctx_header header; | |||
enum dfu_state state; | |||
enum dfu_status status; | |||
dfu_setup_write_t setup_write; | |||
dfu_finish_write_t finish_write; | |||
size_t off; | |||
size_t len; | |||
}; | |||
struct dfu_desc_functional { | |||
uint8_t bLength; | |||
struct usb_desc_type_t bDescriptorType; /* = class DFU/0x1 FUNCTIONAL */ | |||
union { | |||
struct { | |||
uint8_t can_download : 1; | |||
uint8_t can_upload : 1; | |||
uint8_t manifestation_tolerant : 1; | |||
uint8_t will_detach : 1; | |||
uint8_t _rsvd0 : 4; | |||
}; | |||
uint8_t bmAttributes; | |||
}; | |||
uint16_t wDetachTimeOut; | |||
uint16_t wTransferSize; | |||
struct usb_bcd_t bcdDFUVersion; | |||
} __packed; | |||
CTASSERT_SIZE_BYTE(struct dfu_desc_functional, 9); | |||
struct dfu_function_desc { | |||
struct usb_desc_iface_t iface; | |||
struct dfu_desc_functional dfu; | |||
}; | |||
extern const struct usbd_function dfu_function; | |||
extern const struct usbd_function dfu_app_function; | |||
// ----- Functions ----- | |||
void dfu_write_done(enum dfu_status, struct dfu_ctx *ctx); | |||
void dfu_init(dfu_setup_write_t setup_write, dfu_finish_write_t finish_write, struct dfu_ctx *ctx); | |||
void dfu_app_init(dfu_detach_t detachcb); | |||
#endif |
@@ -0,0 +1,93 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Local Includes ----- | |||
#include "mchck.h" | |||
// ----- Variables ----- | |||
uint32_t flash_ALLOW_BRICKABLE_ADDRESSES; | |||
// ----- Functions ----- | |||
/* This will have to live in SRAM. */ | |||
__attribute__((section(".ramtext.ftfl_submit_cmd"), long_call)) | |||
int ftfl_submit_cmd(void) | |||
{ | |||
FTFL.fstat.raw = ((struct FTFL_FSTAT_t){ | |||
.ccif = 1, | |||
.rdcolerr = 1, | |||
.accerr = 1, | |||
.fpviol = 1 | |||
}).raw; | |||
struct FTFL_FSTAT_t stat; | |||
while (!(stat = FTFL.fstat).ccif) | |||
/* NOTHING */; /* XXX maybe WFI? */ | |||
return (!!stat.mgstat0); | |||
} | |||
int flash_prepare_flashing(void) | |||
{ | |||
/* switch to FlexRAM */ | |||
if (!FTFL.fcnfg.ramrdy) { | |||
FTFL.fccob.set_flexram.fcmd = FTFL_FCMD_SET_FLEXRAM; | |||
FTFL.fccob.set_flexram.flexram_function = FTFL_FLEXRAM_RAM; | |||
return (ftfl_submit_cmd()); | |||
} | |||
return (0); | |||
} | |||
int flash_erase_sector(uintptr_t addr) | |||
{ | |||
if (addr < (uintptr_t)&_app_rom && | |||
flash_ALLOW_BRICKABLE_ADDRESSES != 0x00023420) | |||
return (-1); | |||
FTFL.fccob.erase.fcmd = FTFL_FCMD_ERASE_SECTOR; | |||
FTFL.fccob.erase.addr = addr; | |||
return (ftfl_submit_cmd()); | |||
} | |||
int flash_program_section(uintptr_t addr, size_t num_words) | |||
{ | |||
GPIOA_PSOR |= (1<<19); | |||
FTFL.fccob.program_section.fcmd = FTFL_FCMD_PROGRAM_SECTION; | |||
FTFL.fccob.program_section.addr = addr; | |||
FTFL.fccob.program_section.num_words = num_words; | |||
return (ftfl_submit_cmd()); | |||
} | |||
int flash_program_sector(uintptr_t addr, size_t len) | |||
{ | |||
return (len != FLASH_SECTOR_SIZE || | |||
(addr & (FLASH_SECTOR_SIZE - 1)) != 0 || | |||
flash_erase_sector(addr) || | |||
flash_program_section(addr, FLASH_SECTOR_SIZE/4)); | |||
} | |||
void *flash_get_staging_area(uintptr_t addr, size_t len) | |||
{ | |||
if ((addr & (FLASH_SECTOR_SIZE - 1)) != 0 || | |||
len != FLASH_SECTOR_SIZE) | |||
return (NULL); | |||
return (FlexRAM); | |||
} | |||
@@ -0,0 +1,38 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef __FLASH_H | |||
#define __FLASH_H | |||
// ----- Defines ----- | |||
#define FLASH_SECTOR_SIZE 1024 | |||
// ----- Functions ----- | |||
__attribute__((section(".ramtext.ftfl_submit_cmd"), long_call)) | |||
int ftfl_submit_cmd(void); | |||
int flash_prepare_flashing(void); | |||
int flash_erase_sector(uintptr_t); | |||
int flash_program_section(uintptr_t, size_t); | |||
int flash_program_sector(uintptr_t, size_t); | |||
void *flash_get_staging_area(uintptr_t, size_t); | |||
#endif | |||
@@ -0,0 +1,246 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef __FTFL_H | |||
#define __FTFL_H | |||
// ----- Local Includes ----- | |||
#include "mchck-cdefs.h" | |||
// ----- Structs ----- | |||
struct FTFL_FSTAT_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t mgstat0 : 1; | |||
uint8_t _rsvd0 : 3; | |||
uint8_t fpviol : 1; | |||
uint8_t accerr : 1; | |||
uint8_t rdcolerr : 1; | |||
uint8_t ccif : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct FTFL_FSTAT_t, 8); | |||
struct FTFL_FCNFG_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t eeerdy : 1; | |||
uint8_t ramrdy : 1; | |||
uint8_t pflsh : 1; | |||
uint8_t _rsvd0 : 1; | |||
uint8_t erssusp : 1; | |||
uint8_t ersareq : 1; | |||
uint8_t rdcollie : 1; | |||
uint8_t ccie : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct FTFL_FCNFG_t, 8); | |||
struct FTFL_FSEC_t { | |||
UNION_STRUCT_START(8); | |||
enum { | |||
FTFL_FSEC_SEC_UNSECURE = 2, | |||
FTFL_FSEC_SEC_SECURE = 3 | |||
} sec : 2; | |||
enum { | |||
FTFL_FSEC_FSLACC_DENY = 1, | |||
FTFL_FSEC_FSLACC_GRANT = 3 | |||
} fslacc : 2; | |||
enum { | |||
FTFL_FSEC_MEEN_DISABLE = 2, | |||
FTFL_FSEC_MEEN_ENABLE = 3 | |||
} meen : 2; | |||
enum { | |||
FTFL_FSEC_KEYEN_DISABLE = 1, | |||
FTFL_FSEC_KEYEN_ENABLE = 2 | |||
} keyen : 2; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct FTFL_FSEC_t, 8); | |||
struct FTFL_FOPT_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t lpboot : 1; | |||
uint8_t ezport_dis : 1; | |||
uint8_t nmi_dis : 1; | |||
uint8_t _rsvd0 : 5; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct FTFL_FOPT_t, 8); | |||
/** | |||
* The FCOOB is a weird register file, because it is double big endian, | |||
* which makes for odd gaps and for some data that is big endian, and for | |||
* some that is little endian. | |||
*/ | |||
union FTFL_FCCOB_t { | |||
struct ftfl_generic { | |||
uint32_t addr : 24; | |||
enum FTFL_FCMD { | |||
FTFL_FCMD_READ_1s_BLOCK = 0x00, | |||
FTFL_FCMD_READ_1s_SECTION = 0x01, | |||
FTFL_FCMD_PROGRAM_CHECK = 0x02, | |||
FTFL_FCMD_READ_RESOURCE = 0x03, | |||
FTFL_FCMD_PROGRAM_LONGWORD = 0x06, | |||
FTFL_FCMD_ERASE_BLOCK = 0x08, | |||
FTFL_FCMD_ERASE_SECTOR = 0x09, | |||
FTFL_FCMD_PROGRAM_SECTION = 0x0b, | |||
FTFL_FCMD_READ_1s_ALL_BLOCKS = 0x40, | |||
FTFL_FCMD_READ_ONCE = 0x41, | |||
FTFL_FCMD_PROGRAM_ONCE = 0x43, | |||
FTFL_FCMD_ERASE_ALL_BLOCKS = 0x44, | |||
FTFL_FCMD_VERIFY_KEY = 0x45, | |||
FTFL_FCMD_PROGRAM_PARTITION = 0x80, | |||
FTFL_FCMD_SET_FLEXRAM = 0x81 | |||
} fcmd : 8; | |||
uint8_t data_be[8]; | |||
} generic; | |||
struct { | |||
uint32_t addr : 24; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint8_t _rsvd0[3]; | |||
enum FTFL_MARGIN_CHOICE { | |||
FTFL_MARGIN_NORMAL = 0x00, | |||
FTFL_MARGIN_USER = 0x01, | |||
FTFL_MARGIN_FACTORY = 0x02 | |||
} margin : 8; | |||
} read_1s_block; | |||
struct ftfl_data_num_words { | |||
uint32_t addr : 24; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint8_t _rsvd0; | |||
enum FTFL_MARGIN_CHOICE margin : 8; | |||
uint16_t num_words; | |||
} read_1s_section; | |||
struct { | |||
uint32_t addr : 24; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint8_t _rsvd0[3]; | |||
enum FTFL_MARGIN_CHOICE margin : 8; | |||
uint8_t data_be[4]; | |||
} program_check; | |||
struct { | |||
uint32_t addr : 24; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint32_t data; | |||
uint8_t _rsvd0[3]; | |||
enum FTFL_RESOURCE_SELECT { | |||
FTFL_RESOURCE_IFR = 0x00, | |||
FTFL_RESOURCE_VERSION = 0x01 | |||
} resource_select : 8; | |||
} read_resource; | |||
struct { | |||
uint32_t addr : 24; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint8_t data_be[4]; | |||
} program_longword; | |||
struct { | |||
uint32_t addr : 24; | |||
enum FTFL_FCMD fcmd : 8; | |||
} erase; | |||
struct ftfl_data_num_words program_section; | |||
struct { | |||
uint8_t _rsvd0[2]; | |||
enum FTFL_MARGIN_CHOICE margin : 8; | |||
enum FTFL_FCMD fcmd : 8; | |||
} read_1s_all_blocks; | |||
struct ftfl_cmd_once { | |||
uint8_t _rsvd0[2]; | |||
uint8_t idx; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint8_t data_be[4]; | |||
} read_once; | |||
struct ftfl_cmd_once program_once; | |||
struct { | |||
uint8_t _rsvd0[3]; | |||
enum FTFL_FCMD fcmd : 8; | |||
} erase_all; | |||
struct { | |||
uint8_t _rsvd0[3]; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint8_t key_be[8]; | |||
} verify_key; | |||
struct { | |||
uint8_t _rsvd0[3]; | |||
enum FTFL_FCMD fcmd : 8; | |||
uint8_t _rsvd1[2]; | |||
/* the following enum is analogous to enum | |||
* SIM_FLEXNVM_PARTITION in sim.h, but this one is padded | |||
* with four 1-bits to make an 8-bit value. | |||
*/ | |||
enum FTFL_FLEXNVM_PARTITION { | |||
FTFL_FLEXNVM_DATA_32_EEPROM_0 = 0xF0, | |||
FTFL_FLEXNVM_DATA_24_EEPROM_8 = 0xF1, | |||
FTFL_FLEXNVM_DATA_16_EEPROM_16 = 0xF2, | |||
FTFL_FLEXNVM_DATA_8_EEPROM_24 = 0xF9, | |||
FTFL_FLEXNVM_DATA_0_EEPROM_32 = 0xF3 | |||
} flexnvm_partition : 8; | |||
enum FTFL_EEPROM_SIZE { | |||
FTFL_EEPROM_SIZE_0 = 0x3f, | |||
FTFL_EEPROM_SIZE_32 = 0x39, | |||
FTFL_EEPROM_SIZE_64 = 0x38, | |||
FTFL_EEPROM_SIZE_128 = 0x37, | |||
FTFL_EEPROM_SIZE_256 = 0x36, | |||
FTFL_EEPROM_SIZE_512 = 0x35, | |||
FTFL_EEPROM_SIZE_1024 = 0x34, | |||
FTFL_EEPROM_SIZE_2048 = 0x33 | |||
} eeprom_size : 8; | |||
} program_partition; | |||
struct { | |||
uint8_t _rsvd0[2]; | |||
enum FTFL_FLEXRAM_FUNCTION { | |||
FTFL_FLEXRAM_EEPROM = 0x00, | |||
FTFL_FLEXRAM_RAM = 0xff | |||
} flexram_function : 8; | |||
enum FTFL_FCMD fcmd : 8; | |||
} set_flexram; | |||
}; | |||
CTASSERT_SIZE_BYTE(union FTFL_FCCOB_t, 12); | |||
struct FTFL_t { | |||
struct FTFL_FSTAT_t fstat; | |||
struct FTFL_FCNFG_t fcnfg; | |||
struct FTFL_FSEC_t fsec; | |||
struct FTFL_FOPT_t fopt; | |||
union FTFL_FCCOB_t fccob; | |||
uint8_t fprot_be[4]; | |||
uint8_t feprot; | |||
uint8_t fdprot; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct FTFL_t, 0x18); | |||
/* Flash Configuration Field, see Sub-Family Reference Manual, section 28.3.1 */ | |||
struct FTFL_CONFIG_t { | |||
uint8_t key[8]; | |||
uint8_t fprot[4]; | |||
struct FTFL_FSEC_t fsec; | |||
struct FTFL_FOPT_t fopt; | |||
uint8_t feprot; | |||
uint8_t fdprot; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct FTFL_CONFIG_t, 16); | |||
extern volatile struct FTFL_t FTFL; | |||
extern char FlexRAM[]; | |||
extern struct FTFL_CONFIG_t FTFL_CONFIG; | |||
#endif | |||
@@ -0,0 +1,326 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Defines ----- | |||
#define usb_xfer_info USB_STAT_t | |||
// ----- Local Includes ----- | |||
#include "mchck.h" | |||
#include "usb-internal.h" | |||
// ----- Functions ----- | |||
/** | |||
* Kinetis USB driver notes: | |||
* We need to manually maintain the DATA0/1 toggling for the SIE. | |||
* SETUP transactions always start with a DATA0. | |||
* | |||
* The SIE internally uses pingpong (double) buffering, which is | |||
* easily confused with DATA0/DATA1 toggling, and I think even the | |||
* Freescale docs confuse the two notions. When BD->DTS is set, | |||
* BD->DATA01 will be used to verify/discard incoming DATAx and it | |||
* will set the DATAx PID for outgoing tokens. This is not described | |||
* as such in the Freescale Kinetis docs, but the Microchip PIC32 OTG | |||
* docs are more clear on this; it seems that both Freescale and | |||
* Microchip use different versions of the same USB OTG IP core. | |||
* | |||
* http://ww1.microchip.com/downloads/en/DeviceDoc/61126F.pdf | |||
* | |||
* Clear CTL->TOKEN_BUSY after SETUP tokens. | |||
*/ | |||
static struct USB_BD_t bdt[USB_MAX_EP * 2 *2] __attribute__((section(".usbdescriptortable"))); | |||
static struct USB_BD_t * | |||
usb_get_bd(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
return (&bdt[(s->ep_num << 2) | (s->ep_dir << 1) | s->pingpong]); | |||
} | |||
static struct USB_BD_t * | |||
usb_get_bd_stat(struct USB_STAT_t *stat) | |||
{ | |||
return (((void *)(uintptr_t)bdt + (stat->raw << 1))); | |||
} | |||
void *usb_get_xfer_data(struct usb_xfer_info *i) | |||
{ | |||
return (usb_get_bd_stat(i)->addr); | |||
} | |||
enum usb_tok_pid usb_get_xfer_pid(struct usb_xfer_info *i) | |||
{ | |||
return (usb_get_bd_stat(i)->tok_pid); | |||
} | |||
int usb_get_xfer_ep(struct usb_xfer_info *i) | |||
{ | |||
return (i->ep); | |||
} | |||
enum usb_ep_dir usb_get_xfer_dir(struct usb_xfer_info *i) | |||
{ | |||
return (i->dir); | |||
} | |||
void usb_enable_xfers(void) | |||
{ | |||
USB0.ctl.raw = ((struct USB_CTL_t){ | |||
.txd_suspend = 0, | |||
.usben = 1 | |||
}).raw; | |||
} | |||
void usb_set_addr(int addr) | |||
{ | |||
USB0.addr.raw = addr; | |||
} | |||
void usb_pipe_stall(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
volatile struct USB_BD_t *bd = usb_get_bd(s); | |||
bd->raw = ((struct USB_BD_BITS_t){ | |||
.stall = 1, | |||
.own = 1 | |||
}).raw; | |||
} | |||
void usb_pipe_unstall(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
volatile struct USB_BD_t *bd = usb_get_bd(s); | |||
struct USB_BD_BITS_t state = { .raw = bd->raw }; | |||
if (state.own && state.stall) | |||
bd->raw = 0; | |||
} | |||
void usb_pipe_enable(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
USB0.endpt[s->ep_num].raw |= ((struct USB_ENDPT_t){ | |||
.eptxen = s->ep_dir == USB_EP_TX, | |||
.eprxen = s->ep_dir == USB_EP_RX, | |||
.ephshk = 1, /* XXX ISO */ | |||
.epctldis = s->ep_num != 0 | |||
}).raw; | |||
} | |||
void usb_pipe_disable(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
USB0.endpt[s->ep_num].raw &= ~((struct USB_ENDPT_t){ | |||
.eptxen = s->ep_dir == USB_EP_TX, | |||
.eprxen = s->ep_dir == USB_EP_RX, | |||
.epctldis = 1 | |||
}).raw; | |||
} | |||
size_t usb_ep_get_transfer_size(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
struct USB_BD_t *bd = usb_get_bd(s); | |||
return (bd->bc); | |||
} | |||
void usb_queue_next(struct usbd_ep_pipe_state_t *s, void *addr, size_t len) | |||
{ | |||
volatile struct USB_BD_t *bd = usb_get_bd(s); | |||
bd->addr = addr; | |||
/* damn you bitfield problems */ | |||
bd->raw = ((struct USB_BD_BITS_t){ | |||
.dts = 1, | |||
.own = 1, | |||
.data01 = s->data01, | |||
.bc = len, | |||
}).raw; | |||
} | |||
static void usb_reset(void) | |||
{ | |||
/* reset pingpong state */ | |||
/* For some obscure reason, we need to use or here. */ | |||
USB0.ctl.raw |= ((struct USB_CTL_t){ | |||
.txd_suspend = 1, | |||
.oddrst = 1, | |||
}).raw; | |||
/* clear all interrupt bits - not sure if needed */ | |||
USB0.istat.raw = 0xff; | |||
USB0.errstat.raw = 0xff; | |||
USB0.otgistat.raw = 0xff; | |||
/* zap also BDT pingpong & queued transactions */ | |||
memset(bdt, 0, sizeof(bdt)); | |||
USB0.addr.raw = 0; | |||
usb_restart(); | |||
USB0.ctl.raw = ((struct USB_CTL_t){ | |||
.txd_suspend = 0, | |||
.usben = 1 | |||
}).raw; | |||
/* we're only interested in reset and transfers */ | |||
USB0.inten.raw = ((struct USB_ISTAT_t){ | |||
.tokdne = 1, | |||
.usbrst = 1, | |||
.stall = 1, | |||
.sleep = 1, | |||
}).raw; | |||
USB0.usbtrc0.usbresmen = 0; | |||
USB0.usbctrl.susp = 0; | |||
} | |||
void usb_enable(void) | |||
{ | |||
SIM.sopt2.usbsrc = 1; /* usb from mcg */ | |||
SIM.scgc4.usbotg = 1; /* enable usb clock */ | |||
/* reset module - not sure if needed */ | |||
USB0.usbtrc0.raw = ((struct USB_USBTRC0_t){ | |||
.usbreset = 1, | |||
.usbresmen = 1 | |||
}).raw; | |||
while (USB0.usbtrc0.usbreset) | |||
/* NOTHING */; | |||
USB0.bdtpage1 = (uintptr_t)bdt >> 8; | |||
USB0.bdtpage2 = (uintptr_t)bdt >> 16; | |||
USB0.bdtpage3 = (uintptr_t)bdt >> 24; | |||
USB0.control.raw = ((struct USB_CONTROL_t){ | |||
.dppullupnonotg = 1 /* enable pullup */ | |||
}).raw; | |||
USB0.usbctrl.raw = 0; /* resume peripheral & disable pulldowns */ | |||
usb_reset(); /* this will start usb processing */ | |||
/* really only one thing we want */ | |||
USB0.inten.raw = ((struct USB_ISTAT_t){ | |||
.usbrst = 1, | |||
}).raw; | |||
/** | |||
* Suspend transceiver now - we'll wake up at reset again. | |||
*/ | |||
// TODO - Possible removal | |||
USB0.usbctrl.susp = 1; | |||
USB0.usbtrc0.usbresmen = 1; | |||
} | |||
void USB0_Handler(void) | |||
{ | |||
struct USB_ISTAT_t stat = {.raw = USB0.istat.raw }; | |||
if (stat.usbrst) { | |||
usb_reset(); | |||
return; | |||
} | |||
if (stat.stall) { | |||
/* XXX need more work for non-0 ep */ | |||
volatile struct USB_BD_t *bd = usb_get_bd(&usb.ep_state[0].rx); | |||
if (bd->stall) | |||
usb_setup_control(); | |||
} | |||
if (stat.tokdne) { | |||
struct usb_xfer_info stat = USB0.stat; | |||
usb_handle_transaction(&stat); | |||
} | |||
if (stat.sleep) { | |||
USB0.inten.sleep = 0; | |||
USB0.inten.resume = 1; | |||
USB0.usbctrl.susp = 1; | |||
USB0.usbtrc0.usbresmen = 1; | |||
/** | |||
* Clear interrupts now so that we can detect a fresh | |||
* resume later on. | |||
*/ | |||
USB0.istat.raw = stat.raw; | |||
const struct usbd_config *c = usb_get_config_data(-1); | |||
if (c && c->suspend) | |||
c->suspend(); | |||
} | |||
/** | |||
* XXX it is unclear whether we will receive a synchronous | |||
* resume interrupt if we were in sleep. This code assumes we | |||
* do. | |||
*/ | |||
if (stat.resume || USB0.usbtrc0.usb_resume_int) { | |||
USB0.inten.resume = 0; | |||
USB0.inten.sleep = 1; | |||
USB0.usbtrc0.usbresmen = 0; | |||
USB0.usbctrl.susp = 0; | |||
const struct usbd_config *c = usb_get_config_data(-1); | |||
if (c && c->resume) | |||
c->resume(); | |||
stat.resume = 1; /* always clear bit */ | |||
} | |||
USB0.istat.raw = stat.raw; | |||
} | |||
void usb_poll(void) | |||
{ | |||
USB0_Handler(); | |||
} | |||
int usb_tx_serialno(size_t reqlen) | |||
{ | |||
struct usb_desc_string_t *d; | |||
const size_t nregs = 3; | |||
/** | |||
* actually 4, but UIDH is 0xffffffff. Also our output buffer | |||
* is only 64 bytes, and 128 bit + desc header exceeds this by | |||
* 2 bytes. | |||
*/ | |||
const size_t len = nregs * 4 * 2 * 2 + sizeof(*d); | |||
d = usb_ep0_tx_inplace_prepare(len); | |||
if (d == NULL) | |||
return (-1); | |||
d->bLength = len; | |||
d->bDescriptorType = USB_DESC_STRING; | |||
size_t bufpos = 0; | |||
for (size_t reg = 0; reg < nregs; ++reg) { | |||
/* registers run MSW first */ | |||
uint32_t val = (&SIM.uidmh)[reg]; | |||
for (size_t bits = 32; bits > 0; bits -= 4, val <<= 4) { | |||
int nibble = val >> 28; | |||
if (nibble > 9) | |||
nibble += 'a' - '9' - 1; | |||
((char16_t *)d->bString)[bufpos++] = nibble + '0'; | |||
} | |||
} | |||
usb_ep0_tx(d, len, reqlen, NULL, NULL); | |||
return (0); | |||
} | |||
@@ -0,0 +1,96 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Local Includes ----- | |||
#include "mchck.h" | |||
#include "dfu.desc.h" | |||
// ----- Variables ----- | |||
/** | |||
* Unfortunately we can't DMA directly to FlexRAM, so we'll have to stage here. | |||
*/ | |||
static char staging[FLASH_SECTOR_SIZE]; | |||
// ----- Functions ----- | |||
static enum dfu_status setup_write(size_t off, size_t len, void **buf) | |||
{ | |||
static int last = 0; | |||
if (len > sizeof(staging)) | |||
return (DFU_STATUS_errADDRESS); | |||
// We only allow the last write to be less than one sector size. | |||
if (off == 0) | |||
last = 0; | |||
if (last && len != 0) | |||
return (DFU_STATUS_errADDRESS); | |||
if (len != FLASH_SECTOR_SIZE) { | |||
last = 1; | |||
memset(staging, 0xff, sizeof(staging)); | |||
} | |||
*buf = staging; | |||
return (DFU_STATUS_OK); | |||
} | |||
static enum dfu_status finish_write( void *buf, size_t off, size_t len ) | |||
{ | |||
void *target; | |||
if (len == 0) | |||
return (DFU_STATUS_OK); | |||
target = flash_get_staging_area(off + (uintptr_t)&_app_rom, FLASH_SECTOR_SIZE); | |||
if (!target) | |||
return (DFU_STATUS_errADDRESS); | |||
memcpy(target, buf, len); | |||
if (flash_program_sector(off + (uintptr_t)&_app_rom, FLASH_SECTOR_SIZE) != 0) | |||
return (DFU_STATUS_errADDRESS); | |||
return (DFU_STATUS_OK); | |||
} | |||
static struct dfu_ctx dfu_ctx; | |||
void init_usb_bootloader( int config ) | |||
{ | |||
dfu_init(setup_write, finish_write, &dfu_ctx); | |||
} | |||
void main() | |||
{ | |||
// Enabling LED to indicate we are in the bootloader | |||
GPIOA_PDDR |= (1<<19); | |||
// Setup pin - A19 - See Lib/pin_map.mchck for more details on pins | |||
PORTA_PCR19 = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); | |||
GPIOA_PSOR |= (1<<19); | |||
flash_prepare_flashing(); | |||
usb_init( &dfu_device ); | |||
for (;;) | |||
{ | |||
usb_poll(); | |||
} | |||
} | |||
@@ -0,0 +1,138 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef _MCHCK_CDEFS_H | |||
#define _MCHCK_CDEFS_H | |||
// ----- Compiler Includes ----- | |||
#include <sys/param.h> | |||
// ----- Defines & Macros ----- | |||
#define _CONCAT(x,y) _CONCAT1(x,y) | |||
#define _CONCAT1(x,y) x ## y | |||
#define _STR(a) #a | |||
typedef __CHAR16_TYPE__ char16_t; | |||
#define __packed __attribute__((__packed__)) | |||
/* From FreeBSD: compile-time asserts */ | |||
#define CTASSERT(x) _Static_assert(x, _STR(x)) | |||
#define CTASSERT_SIZE_BYTE(t, s) CTASSERT(sizeof(t) == (s)) | |||
#define CTASSERT_SIZE_BIT(t, s) CTASSERT(sizeof(t) * 8 == (s)) | |||
#define UNION_STRUCT_START(size) \ | |||
union { \ | |||
_CONCAT(_CONCAT(uint, size), _t) raw; \ | |||
struct { \ | |||
/* just to swallow the following semicolon */ \ | |||
struct _CONCAT(_CONCAT(__dummy_, __COUNTER__), _t) {} | |||
#define UNION_STRUCT_END \ | |||
}; /* struct */ \ | |||
}; /* union */ | |||
/** | |||
* From <news:[email protected]>, | |||
* <https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s> | |||
*/ | |||
#define __PP_NARG(...) \ | |||
__PP_NARG_(__0, ## __VA_ARGS__, __PP_RSEQ_N()) | |||
#define __PP_NARG_(...) \ | |||
__PP_ARG_N(__VA_ARGS__) | |||
#define __PP_ARG_N( \ | |||
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ | |||
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ | |||
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ | |||
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ | |||
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ | |||
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ | |||
_61,_62,_63,N,...) N | |||
#define __PP_RSEQ_N() \ | |||
62,61,60, \ | |||
59,58,57,56,55,54,53,52,51,50, \ | |||
49,48,47,46,45,44,43,42,41,40, \ | |||
39,38,37,36,35,34,33,32,31,30, \ | |||
29,28,27,26,25,24,23,22,21,20, \ | |||
19,18,17,16,15,14,13,12,11,10, \ | |||
9,8,7,6,5,4,3,2,1,0 | |||
/** | |||
* From <https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms> | |||
*/ | |||
#define __CAT(a, ...) __PRIMITIVE_CAT(a, __VA_ARGS__) | |||
#define __PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ | |||
#define __IIF(c) __PRIMITIVE_CAT(__IIF_, c) | |||
#define __IIF_0(t, ...) __VA_ARGS__ | |||
#define __IIF_1(t, ...) t | |||
#define __COMPL(b) __PRIMITIVE_CAT(__COMPL_, b) | |||
#define __COMPL_0 1 | |||
#define __COMPL_1 0 | |||
#define __CHECK_N(x, n, ...) n | |||
#define __CHECK(...) __CHECK_N(__VA_ARGS__, 0,) | |||
#define __PROBE(x) x, 1, | |||
#define __NOT(x) __CHECK(__PRIMITIVE_CAT(__NOT_, x)) | |||
#define __NOT_0 __PROBE(~) | |||
#define __BOOL(x) __COMPL(__NOT(x)) | |||
#define __IF(c) __IIF(__BOOL(c)) | |||
#define __EAT(...) | |||
#define __EXPAND(...) __VA_ARGS__ | |||
#define __WHEN(c) __IF(c)(__EXPAND, __EAT) | |||
#define __HEAD(h, ...) h | |||
#define __TAIL(h, ...) __VA_ARGS__ | |||
#define __EVAL(...) __EVAL1(__EVAL1(__EVAL1(__VA_ARGS__))) | |||
#define __EVAL1(...) __EVAL2(__EVAL2(__EVAL2(__VA_ARGS__))) | |||
#define __EVAL2(...) __EVAL3(__EVAL3(__EVAL3(__VA_ARGS__))) | |||
#define __EVAL3(...) __EVAL4(__EVAL4(__EVAL4(__VA_ARGS__))) | |||
#define __EVAL4(...) __EVAL5(__EVAL5(__EVAL5(__VA_ARGS__))) | |||
#define __EVAL5(...) __EVAL6(__EVAL6(__EVAL6(__VA_ARGS__))) | |||
#define __EVAL6(...) __VA_ARGS__ | |||
#define __EMPTY() | |||
#define __DEFER(id) id __EMPTY() | |||
#define __OBSTRUCT(...) __VA_ARGS__ __DEFER(__EMPTY)() | |||
#define __CAT_ARG(f, a) __OBSTRUCT(f) a | |||
#define __REPEAT(...) __EVAL(__REPEAT_(__VA_ARGS__)) | |||
#define __REPEAT_INNER(...) __OBSTRUCT(__REPEAT_INDIRECT) () (__VA_ARGS__) | |||
#define __REPEAT_INDIRECT() __REPEAT_ | |||
#define __REPEAT_(iter, itermacro, macro, a, ...) \ | |||
__OBSTRUCT(macro)(iter, a) \ | |||
__WHEN(__PP_NARG(__VA_ARGS__)) \ | |||
( \ | |||
__OBSTRUCT(__REPEAT_INDIRECT) () ( \ | |||
itermacro(iter, a), itermacro, macro, __VA_ARGS__ \ | |||
) \ | |||
) | |||
#endif | |||
@@ -0,0 +1,49 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef __MCHCK_H | |||
#define __MCHCK_H | |||
// ----- Compiler Includes ----- | |||
#include <sys/types.h> | |||
#include <sys/cdefs.h> | |||
#include <stdfix.h> | |||
#include <stdarg.h> | |||
// ----- Project Includes ----- | |||
#include <mk20dx.h> | |||
// ----- Local Includes ----- | |||
#include "mchck-cdefs.h" | |||
extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss, _app_rom; | |||
#include "ftfl.h" | |||
#include "usbotg.h" | |||
#include "sim.h" | |||
#include "flash.h" | |||
#include "usb.h" | |||
#endif | |||
@@ -0,0 +1,332 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef __SIM_H | |||
#define __SIM_H | |||
// ----- Local Includes ----- | |||
#include "mchck.h" | |||
// ----- Structs ----- | |||
struct SIM_t { | |||
struct SIM_SOPT1_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t _rsvd0 : 12; | |||
enum { | |||
SIM_RAMSIZE_8KB = 1, | |||
SIM_RAMSIZE_16KB = 3 | |||
} ramsize : 4; | |||
uint32_t _rsvd1 : 2; | |||
enum { | |||
SIM_OSC32KSEL_SYSTEM = 0, | |||
SIM_OSC32KSEL_RTC = 2, | |||
SIM_OSC32KSEL_LPO = 3 | |||
} osc32ksel : 2; | |||
uint32_t _rsvd2 : 9; | |||
uint32_t usbvstby : 1; | |||
uint32_t usbsstby : 1; | |||
uint32_t usbregen : 1; | |||
UNION_STRUCT_END; | |||
} sopt1; | |||
struct SIM_SOPT1CFG_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t _rsvd0 : 24; | |||
uint32_t urwe : 1; | |||
uint32_t uvswe : 1; | |||
uint32_t usswe : 1; | |||
uint32_t _rsvd1 : 5; | |||
UNION_STRUCT_END; | |||
} sopt1cfg; | |||
uint32_t _pad0[(0x1004 - 0x8) / 4]; | |||
struct SIM_SOPT2_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t _rsvd0 : 4; | |||
enum { | |||
SIM_RTCCLKOUTSEL_1HZ = 0, | |||
SIM_RTCCLKOUTSEL_32KHZ = 1 | |||
} rtcclkoutsel : 1; | |||
enum { | |||
SIM_CLKOUTSEL_FLASH = 2, | |||
SIM_CLKOUTSEL_LPO = 3, | |||
SIM_CLKOUTSEL_MCG = 4, | |||
SIM_CLKOUTSEL_RTC = 5, | |||
SIM_CLKOUTSEL_OSC = 6 | |||
} clkoutsel : 3; | |||
uint32_t _rsvd1 : 3; | |||
enum { | |||
SIM_PTD7PAD_SINGLE = 0, | |||
SIM_PTD7PAD_DOUBLE = 1 | |||
} ptd7pad : 1; | |||
enum { | |||
SIM_TRACECLKSEL_MCG = 0, | |||
SIM_TRACECLKSEL_CORE = 1 | |||
} traceclksel : 1; | |||
uint32_t _rsvd2 : 3; | |||
enum { | |||
SIM_PLLFLLSEL_FLL = 0, | |||
SIM_PLLFLLSEL_PLL = 1 | |||
} pllfllsel : 1; | |||
uint32_t _rsvd3 : 1; | |||
enum { | |||
SIM_USBSRC_EXTERNAL = 0, | |||
SIM_USBSRC_MCG = 1 | |||
} usbsrc : 1; | |||
uint32_t _rsvd4 : 13; | |||
UNION_STRUCT_END; | |||
} sopt2; | |||
uint32_t _pad1; | |||
struct SIM_SOPT4_t { | |||
UNION_STRUCT_START(32); | |||
enum sim_ftmflt { | |||
SIM_FTMFLT_FTM = 0, | |||
SIM_FTMFLT_CMP = 1 | |||
} ftm0flt0 : 1; | |||
enum sim_ftmflt ftm0flt1 : 1; | |||
uint32_t _rsvd0 : 2; | |||
enum sim_ftmflt ftm1flt0 : 1; | |||
uint32_t _rsvd1 : 13; | |||
enum { | |||
SIM_FTMCHSRC_FTM = 0, | |||
SIM_FTMCHSRC_CMP0 = 1, | |||
SIM_FTMCHSRC_CMP1 = 2, | |||
SIM_FTMCHSRC_USBSOF = 3 | |||
} ftm1ch0src : 2; | |||
uint32_t _rsvd2 : 4; | |||
enum sim_ftmclksel { | |||
SIM_FTMCLKSEL_CLK0 = 0, | |||
SIM_FTMCLKSEL_CLK1 = 1 | |||
} ftm0clksel : 1; | |||
enum sim_ftmclksel ftm1clksel : 1; | |||
uint32_t _rsvd3 : 2; | |||
enum { | |||
SIM_FTMTRGSRC_HSCMP0 = 0, | |||
SIM_FTMTRGSRC_FTM1 = 1 | |||
} ftm0trg0src : 1; | |||
uint32_t _rsvd4 : 3; | |||
UNION_STRUCT_END; | |||
} sopt4; | |||
struct SIM_SOPT5_t { | |||
UNION_STRUCT_START(32); | |||
enum sim_uarttxsrc { | |||
SIM_UARTTXSRC_UART = 0, | |||
SIM_UARTTXSRC_FTM = 1 | |||
} uart0txsrc : 1; | |||
uint32_t _rsvd0 : 1; | |||
enum sim_uartrxsrc { | |||
SIM_UARTRXSRC_UART = 0, | |||
SIM_UARTRXSRC_CMP0 = 1, | |||
SIM_UARTRXSRC_CMP1 = 2 | |||
} uart0rxsrc : 2; | |||
enum sim_uarttxsrc uart1txsrc : 1; | |||
uint32_t _rsvd1 : 1; | |||
enum sim_uartrxsrc uart1rxsrc : 2; | |||
uint32_t _rsvd2 : 24; | |||
UNION_STRUCT_END; | |||
} sopt5; | |||
uint32_t _pad2; | |||
struct SIM_SOPT7_t { | |||
UNION_STRUCT_START(32); | |||
enum { | |||
SIM_ADCTRGSEL_PDB = 0, | |||
SIM_ADCTRGSEL_HSCMP0 = 1, | |||
SIM_ADCTRGSEL_HSCMP1 = 2, | |||
SIM_ADCTRGSEL_PIT0 = 4, | |||
SIM_ADCTRGSEL_PIT1 = 5, | |||
SIM_ADCTRGSEL_PIT2 = 6, | |||
SIM_ADCTRGSEL_PIT3 = 7, | |||
SIM_ADCTRGSEL_FTM0 = 8, | |||
SIM_ADCTRGSEL_FTM1 = 9, | |||
SIM_ADCTRGSEL_RTCALARM = 12, | |||
SIM_ADCTRGSEL_RTCSECS = 13, | |||
SIM_ADCTRGSEL_LPTIMER = 14 | |||
} adc0trgsel : 4; | |||
enum { | |||
SIM_ADCPRETRGSEL_A = 0, | |||
SIM_ADCPRETRGSEL_B = 1 | |||
} adc0pretrgsel : 1; | |||
uint32_t _rsvd0 : 2; | |||
enum { | |||
SIM_ADCALTTRGEN_PDB = 0, | |||
SIM_ADCALTTRGEN_ALT = 1 | |||
} adc0alttrgen : 1; | |||
uint32_t _rsvd1 : 24; | |||
UNION_STRUCT_END; | |||
} sopt7; | |||
uint32_t _pad3[(0x1024 - 0x101c) / 4]; | |||
struct SIM_SDID_t { | |||
UNION_STRUCT_START(32); | |||
enum { | |||
SIM_PINID_32 = 2, | |||
SIM_PINID_48 = 4, | |||
SIM_PINID_64 = 5 | |||
} pinid : 4; | |||
enum { | |||
SIM_FAMID_K10 = 0, | |||
SIM_FAMID_K20 = 1 | |||
} famid : 3; | |||
uint32_t _rsvd1 : 5; | |||
uint32_t revid : 4; | |||
uint32_t _rsvd2 : 16; | |||
UNION_STRUCT_END; | |||
} sdid; | |||
uint32_t _pad4[(0x1034 - 0x1028) / 4]; | |||
struct SIM_SCGC4_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t _rsvd0 : 1; | |||
uint32_t ewm : 1; | |||
uint32_t cmt : 1; | |||
uint32_t _rsvd1 : 3; | |||
uint32_t i2c0 : 1; | |||
uint32_t _rsvd2 : 3; | |||
uint32_t uart0 : 1; | |||
uint32_t uart1 : 1; | |||
uint32_t uart2 : 1; | |||
uint32_t _rsvd3 : 5; | |||
uint32_t usbotg : 1; | |||
uint32_t cmp : 1; | |||
uint32_t vref : 1; | |||
uint32_t _rsvd4 : 11; | |||
UNION_STRUCT_END; | |||
} scgc4; | |||
struct SIM_SCGC5_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t lptimer : 1; | |||
uint32_t _rsvd0 : 4; | |||
uint32_t tsi : 1; | |||
uint32_t _rsvd1 : 3; | |||
uint32_t porta : 1; | |||
uint32_t portb : 1; | |||
uint32_t portc : 1; | |||
uint32_t portd : 1; | |||
uint32_t porte : 1; | |||
uint32_t _rsvd2 : 18; | |||
UNION_STRUCT_END; | |||
} scgc5; | |||
struct SIM_SCGC6_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t ftfl : 1; | |||
uint32_t dmamux : 1; | |||
uint32_t _rsvd0 : 10; | |||
uint32_t spi0 : 1; | |||
uint32_t _rsvd1 : 2; | |||
uint32_t i2s : 1; | |||
uint32_t _rsvd2 : 2; | |||
uint32_t crc : 1; | |||
uint32_t _rsvd3 : 2; | |||
uint32_t usbdcd : 1; | |||
uint32_t pdb : 1; | |||
uint32_t pit : 1; | |||
uint32_t ftm0 : 1; | |||
uint32_t ftm1 : 1; | |||
uint32_t _rsvd4 : 1; | |||
uint32_t adc0 : 1; | |||
uint32_t _rsvd5 : 1; | |||
uint32_t rtc : 1; | |||
uint32_t _rsvd6 : 2; | |||
UNION_STRUCT_END; | |||
} scgc6; | |||
struct SIM_SCGC7_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t _rsvd0 : 1; | |||
uint32_t dma : 1; | |||
uint32_t _rsvd1 : 30; | |||
UNION_STRUCT_END; | |||
} scgc7; | |||
struct SIM_CLKDIV1_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t _rsvd0 : 16; | |||
uint32_t outdiv4 : 4; | |||
uint32_t _rsvd1 : 4; | |||
uint32_t outdiv2 : 4; | |||
uint32_t outdiv1 : 4; | |||
UNION_STRUCT_END; | |||
} clkdiv1; | |||
struct SIM_CLKDIV2_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t usbfrac : 1; | |||
uint32_t usbdiv : 3; | |||
uint32_t _rsvd0 : 28; | |||
UNION_STRUCT_END; | |||
} clkdiv2; | |||
struct SIM_FCFG1_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t flashdis : 1; | |||
uint32_t flashdoze : 1; | |||
uint32_t _rsvd0 : 6; | |||
/* the following enum is analogous to enum | |||
* FTFL_FLEXNVM_PARTITION in ftfl.h, but that one is padded | |||
* with four 1-bits to make an 8-bit value. | |||
*/ | |||
enum SIM_FLEXNVM_PARTITION { | |||
SIM_FLEXNVM_DATA_32_EEPROM_0 = 0x0, | |||
SIM_FLEXNVM_DATA_24_EEPROM_8 = 0x1, | |||
SIM_FLEXNVM_DATA_16_EEPROM_16 = 0x2, | |||
SIM_FLEXNVM_DATA_8_EEPROM_24 = 0x9, | |||
SIM_FLEXNVM_DATA_0_EEPROM_32 = 0x3 | |||
} depart : 4; | |||
uint32_t _rsvd1 : 4; | |||
enum { | |||
SIM_EESIZE_2KB = 3, | |||
SIM_EESIZE_1KB = 4, | |||
SIM_EESIZE_512B = 5, | |||
SIM_EESIZE_256B = 6, | |||
SIM_EESIZE_128B = 7, | |||
SIM_EESIZE_64B = 8, | |||
SIM_EESIZE_32B = 9, | |||
SIM_EESIZE_0B = 15 | |||
} eesize : 4; | |||
uint32_t _rsvd2 : 4; | |||
enum { | |||
SIM_PFSIZE_32KB = 3, | |||
SIM_PFSIZE_64KB = 5, | |||
SIM_PFSIZE_128KB = 7 | |||
} pfsize : 4; | |||
enum { | |||
SIM_NVMSIZE_0KB = 0, | |||
SIM_NVMSIZE_32KB = 3 | |||
} nvmsize : 4; | |||
UNION_STRUCT_END; | |||
} fcfg1; | |||
struct SIM_FCFG2_t { | |||
UNION_STRUCT_START(32); | |||
uint32_t _rsvd0 : 16; | |||
uint32_t maxaddr1 : 7; | |||
enum { | |||
SIM_PFLSH_FLEXNVM = 0, | |||
SIM_PFLSH_PROGRAM = 1 | |||
} pflsh : 1; | |||
uint32_t maxaddr0 : 7; | |||
uint32_t _rsvd1 : 1; | |||
UNION_STRUCT_END; | |||
} fcfg2; | |||
uint32_t uidh; | |||
uint32_t uidmh; | |||
uint32_t uidml; | |||
uint32_t uidl; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct SIM_t, 0x1064); | |||
extern volatile struct SIM_t SIM; | |||
#endif | |||
@@ -0,0 +1,60 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef _USB_COMMON_H | |||
#define _USB_COMMON_H | |||
// ----- Enumerations ----- | |||
/** | |||
* USB request data structures. | |||
*/ | |||
enum usb_tok_pid { | |||
USB_PID_TIMEOUT = 0, | |||
USB_PID_OUT = 1, | |||
USB_PID_ACK = 2, | |||
USB_PID_DATA0 = 3, | |||
USB_PID_IN = 9, | |||
USB_PID_NAK = 10, | |||
USB_PID_DATA1 = 11, | |||
USB_PID_SETUP = 13, | |||
USB_PID_STALL = 14, | |||
USB_PID_DATAERR = 15 | |||
}; | |||
enum usb_ep_pingpong { | |||
USB_EP_PINGPONG_EVEN = 0, | |||
USB_EP_PINGPONG_ODD = 1 | |||
}; | |||
enum usb_ep_dir { | |||
USB_EP_RX = 0, | |||
USB_EP_TX = 1 | |||
}; | |||
enum usb_data01 { | |||
USB_DATA01_DATA0 = 0, | |||
USB_DATA01_DATA1 = 1 | |||
}; | |||
enum { | |||
EP0_BUFSIZE = 64 | |||
}; | |||
#endif | |||
@@ -0,0 +1,104 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef __USB_INTERNAL_H | |||
#define __USB_INTERNAL_H | |||
/** | |||
* Internal driver structures | |||
*/ | |||
/** | |||
* USB state machine | |||
* ================= | |||
* | |||
* Device configuration states: | |||
* | |||
* Attached <-> Powered | |||
* Powered -(reset)-> Default | |||
* Default -(SET_ADDRESS)-> Address | |||
* Address -(SET_CONFIGURATION)-> Configured | |||
* Configured -(SET_CONFIGURATION 0)-> Address | |||
* Address -(SET_ADDRESS 0)-> Default | |||
* [Default, Configured, Address] -(reset)-> Default | |||
*/ | |||
// ----- Defines ----- | |||
#ifndef USB_MAX_EP | |||
#define USB_MAX_EP 16 | |||
#endif | |||
// ----- Structs ----- | |||
struct usbd_ep_pipe_state_t { | |||
enum usb_ep_pingpong pingpong; /* next desc to use */ | |||
enum usb_data01 data01; | |||
size_t transfer_size; | |||
size_t pos; | |||
uint8_t *data_buf; | |||
const uint8_t *copy_source; | |||
int short_transfer; | |||
ep_callback_t callback; | |||
void *callback_data; | |||
size_t ep_maxsize; | |||
/* constant */ | |||
int ep_num; | |||
enum usb_ep_dir ep_dir; | |||
}; | |||
struct usbd_ep_state_t { | |||
union { | |||
struct usbd_ep_pipe_state_t pipe[2]; | |||
struct { | |||
struct usbd_ep_pipe_state_t rx; | |||
struct usbd_ep_pipe_state_t tx; | |||
}; | |||
}; | |||
}; | |||
struct usbd_t { | |||
struct usbd_function_ctx_header functions; | |||
struct usbd_function control_function; | |||
const struct usbd_device *identity; | |||
int address; | |||
int config; | |||
enum usbd_dev_state { | |||
USBD_STATE_DISABLED = 0, | |||
USBD_STATE_DEFAULT, | |||
USBD_STATE_SETTING_ADDRESS, | |||
USBD_STATE_ADDRESS, | |||
USBD_STATE_CONFIGURED | |||
} state; | |||
enum usb_ctrl_req_dir ctrl_dir; | |||
struct usbd_ep_state_t ep_state[USB_MAX_EP]; | |||
}; | |||
extern struct usbd_t usb; | |||
// ----- Functions ----- | |||
void usb_restart(void); | |||
void usb_enable(void); | |||
const struct usbd_config *usb_get_config_data(int config); | |||
#endif | |||
@@ -0,0 +1,590 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Compiler Includes ----- | |||
#include <sys/types.h> | |||
#include <inttypes.h> | |||
#include <string.h> | |||
// ----- Local Includes ----- | |||
#include "usb.h" | |||
#include "usb-internal.h" | |||
// ----- Variables ----- | |||
static uint8_t ep0_buf[2][EP0_BUFSIZE] __attribute__((aligned(4))); | |||
struct usbd_t usb; | |||
// ----- Functions ----- | |||
/** | |||
* Returns: 0 when this is was the last transfer, 1 if there is still | |||
* more to go. | |||
*/ | |||
/* Defaults to EP0 for now */ | |||
static int usb_tx_next(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
/** | |||
* Us being here means the previous transfer just completed | |||
* successfully. That means the host just toggled its data | |||
* sync bit, and so do we. | |||
*/ | |||
s->data01 ^= 1; | |||
if (s->transfer_size > 0) { | |||
size_t thislen = s->transfer_size; | |||
if (thislen > s->ep_maxsize) | |||
thislen = s->ep_maxsize; | |||
void *addr = s->data_buf + s->pos; | |||
if (s->copy_source) { | |||
/* Bounce buffer mode */ | |||
addr = s->data_buf; | |||
memcpy(addr, s->copy_source + s->pos, thislen); | |||
} | |||
s->pos += thislen; | |||
s->transfer_size -= thislen; | |||
usb_queue_next(s, addr, thislen); | |||
s->pingpong ^= 1; | |||
return (1); | |||
} | |||
/** | |||
* All data has been shipped. Do we need to send a short | |||
* packet? | |||
*/ | |||
if (s->short_transfer) { | |||
s->short_transfer = 0; | |||
usb_queue_next(s, NULL, 0); | |||
s->pingpong ^= 1; | |||
return (1); | |||
} | |||
if (s->callback) | |||
s->callback(s->data_buf, s->pos, s->callback_data); | |||
return (0); | |||
} | |||
static void setup_tx(struct usbd_ep_pipe_state_t *s, const void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) | |||
{ | |||
s->data_buf = (void *)buf; | |||
s->copy_source = NULL; | |||
s->transfer_size = len; | |||
s->pos = 0; | |||
s->callback = cb; | |||
s->callback_data = cb_data; | |||
if (s->transfer_size > reqlen) | |||
s->transfer_size = reqlen; | |||
if (s->transfer_size < reqlen && s->transfer_size % s->ep_maxsize == 0) | |||
s->short_transfer = 1; | |||
else | |||
s->short_transfer = 0; | |||
} | |||
static void submit_tx(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
/* usb_tx_next() flips the data toggle, so invert this here. */ | |||
s->data01 ^= 1; | |||
usb_tx_next(s); | |||
} | |||
/** | |||
* send USB data (IN device transaction) | |||
* | |||
* So far this function is specialized for EP 0 only. | |||
* | |||
* Returns: size to be transfered, or -1 on error. | |||
*/ | |||
int usb_tx(struct usbd_ep_pipe_state_t *s, const void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) | |||
{ | |||
setup_tx(s, buf, len, reqlen, cb, cb_data); | |||
submit_tx(s); | |||
return (s->transfer_size); | |||
} | |||
/** | |||
* Returns: 0 when this is was the last transfer, 1 if there is still | |||
* more to go. | |||
*/ | |||
/* Defaults to EP0 for now */ | |||
/* XXX pass usb_stat to validate pingpong */ | |||
static int usb_rx_next(struct usbd_ep_pipe_state_t *s) | |||
{ | |||
/** | |||
* Us being here means the previous transfer just completed | |||
* successfully. That means the host just toggled its data | |||
* sync bit, and so do we. | |||
*/ | |||
s->data01 ^= 1; | |||
size_t thislen = usb_ep_get_transfer_size(s); | |||
s->transfer_size -= thislen; | |||
s->pos += thislen; | |||
/** | |||
* We're done with this buffer now. Switch the pingpong now | |||
* before we might have to receive the next piece of data. | |||
*/ | |||
s->pingpong ^= 1; | |||
/** | |||
* If this is a short transfer, or we received what we | |||
* expected, we're done. | |||
*/ | |||
if (thislen < s->ep_maxsize || s->transfer_size == 0) { | |||
if (s->callback) | |||
s->callback(s->data_buf, s->pos, s->callback_data); | |||
return (0); | |||
} | |||
/** | |||
* Otherwise we still need to receive more data. | |||
*/ | |||
size_t nextlen = s->transfer_size; | |||
if (nextlen > s->ep_maxsize) | |||
nextlen = s->ep_maxsize; | |||
void *addr = s->data_buf + s->pos; | |||
usb_queue_next(s, addr, nextlen); | |||
return (1); | |||
} | |||
/** | |||
* Receive USB data (OUT device transaction) | |||
* | |||
* Returns: size to be received, or -1 on error. | |||
*/ | |||
int usb_rx(struct usbd_ep_pipe_state_t *s, void *buf, size_t len, ep_callback_t cb, void *cb_data) | |||
{ | |||
s->data_buf = buf; | |||
s->transfer_size = len; | |||
s->pos = 0; | |||
s->callback = cb; | |||
s->callback_data = cb_data; | |||
size_t thislen = s->transfer_size; | |||
if (thislen > s->ep_maxsize) | |||
thislen = s->ep_maxsize; | |||
usb_queue_next(s, s->data_buf, thislen); | |||
return (len); | |||
} | |||
int usb_ep0_tx_cp(const void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) | |||
{ | |||
struct usbd_ep_pipe_state_t *s = &usb.ep_state[0].tx; | |||
enum usb_ep_pingpong pp = s->pingpong; | |||
setup_tx(s, ep0_buf[pp], len, reqlen, cb, cb_data); | |||
s->copy_source = buf; | |||
submit_tx(s); | |||
return (s->transfer_size); | |||
} | |||
void *usb_ep0_tx_inplace_prepare(size_t len) | |||
{ | |||
enum usb_ep_pingpong pp = usb.ep_state[0].tx.pingpong; | |||
if (len > EP0_BUFSIZE) | |||
return (NULL); | |||
return (ep0_buf[pp]); | |||
} | |||
int usb_ep0_tx(void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) | |||
{ | |||
return (usb_tx(&usb.ep_state[0].tx, buf, len, reqlen, cb, cb_data)); | |||
} | |||
int usb_ep0_rx(void *buf, size_t len, ep_callback_t cb, void *cb_data) | |||
{ | |||
return (usb_rx(&usb.ep_state[0].rx, buf, len, cb, cb_data)); | |||
} | |||
const struct usbd_config * | |||
usb_get_config_data(int config) | |||
{ | |||
if (config <= 0) | |||
config = usb.config; | |||
if (config != 0) | |||
return (usb.identity->configs[config - 1]); | |||
else | |||
return (NULL); | |||
} | |||
static int usb_set_config(int config) | |||
{ | |||
const struct usbd_config *config_data; | |||
if (usb.config != 0) { | |||
config_data = usb_get_config_data(-1); | |||
if (config_data != NULL && config_data->init != NULL) | |||
config_data->init(0); | |||
} | |||
if (config != 0) { | |||
/* XXX overflow */ | |||
config_data = usb_get_config_data(config); | |||
if (config_data != NULL && config_data->init != NULL) | |||
config_data->init(1); | |||
} | |||
usb.config = config; | |||
return (0); | |||
} | |||
static int usb_set_interface(int iface, int altsetting) | |||
{ | |||
int iface_count = 0; | |||
for (struct usbd_function_ctx_header *fh = &usb.functions; | |||
fh != NULL; | |||
fh = fh->next, iface_count += fh->function->interface_count) { | |||
if (iface - iface_count < fh->function->interface_count) { | |||
if (fh->function->configure != NULL) | |||
return (fh->function->configure(iface, | |||
iface - iface_count, | |||
altsetting, | |||
fh)); | |||
/* Default to a single altsetting */ | |||
if (altsetting != 0) | |||
return (-1); | |||
else | |||
return (0); | |||
} | |||
} | |||
return (-1); | |||
} | |||
static int usb_tx_config_desc(int idx, int reqlen) | |||
{ | |||
const struct usb_desc_config_t *d = usb.identity->configs[idx]->desc; | |||
usb_ep0_tx_cp(d, d->wTotalLength, reqlen, NULL, NULL); | |||
return (0); | |||
} | |||
static int usb_tx_string_desc(int idx, int reqlen) | |||
{ | |||
const struct usb_desc_string_t * const *d; | |||
for (d = usb.identity->string_descs; idx != 0 && *d != NULL; ++d) | |||
--idx; | |||
switch ((uintptr_t)*d) { | |||
case (uintptr_t)NULL: | |||
return (-1); | |||
case (uintptr_t)USB_DESC_STRING_SERIALNO: | |||
return (usb_tx_serialno(reqlen)); | |||
default: | |||
usb_ep0_tx_cp(*d, (*d)->bLength, reqlen, NULL, NULL); | |||
return (0); | |||
} | |||
} | |||
static void usb_handle_control_done(void *data, ssize_t len, void *cbdata) | |||
{ | |||
if (usb.state == USBD_STATE_SETTING_ADDRESS) { | |||
usb.state = USBD_STATE_ADDRESS; | |||
usb_set_addr(usb.address); | |||
} | |||
usb_setup_control(); | |||
} | |||
void usb_handle_control_status_cb(ep_callback_t cb) | |||
{ | |||
/* empty status transfer */ | |||
switch (usb.ctrl_dir) { | |||
case USB_CTRL_REQ_IN: | |||
usb.ep_state[0].rx.data01 = USB_DATA01_DATA1; | |||
usb_rx(&usb.ep_state[0].rx, NULL, 0, cb, NULL); | |||
break; | |||
default: | |||
usb.ep_state[0].tx.data01 = USB_DATA01_DATA1; | |||
usb_ep0_tx_cp(NULL, 0, 1 /* short packet */, cb, NULL); | |||
break; | |||
} | |||
} | |||
void usb_handle_control_status(int fail) | |||
{ | |||
if (fail) { | |||
usb_pipe_stall(&usb.ep_state[0].rx); | |||
usb_pipe_stall(&usb.ep_state[0].tx); | |||
} else { | |||
usb_handle_control_status_cb(usb_handle_control_done); | |||
} | |||
} | |||
/** | |||
* Dispatch non-standard request to registered USB functions. | |||
*/ | |||
static void usb_handle_control_nonstd(struct usb_ctrl_req_t *req) | |||
{ | |||
/* XXX filter by interface/endpoint? */ | |||
for (struct usbd_function_ctx_header *fh = &usb.functions; fh != NULL; fh = fh->next) { | |||
/* ->control() returns != 0 if it handled the request */ | |||
if (fh->function->control != NULL && | |||
fh->function->control(req, fh)) | |||
return; | |||
} | |||
usb_handle_control_status(-1); | |||
} | |||
/** | |||
* | |||
* Great resource: http://wiki.osdev.org/Universal_Serial_Bus | |||
* | |||
* Control Transfers | |||
* ----------------- | |||
* | |||
* A control transfer consists of a SETUP transaction (1), zero or | |||
* more data transactions (IN or OUT) (2), and a final status | |||
* transaction (3). | |||
* | |||
* Token sequence (data toggle): | |||
* 1. SETUP (0) | |||
* (2a. OUT (1) ... (toggling)) | |||
* 3a. IN (1) | |||
* | |||
* or | |||
* 1. SETUP (0) | |||
* 2b. IN (1) ... (toggling) | |||
* 3b. OUT (1) | |||
* | |||
* Report errors by STALLing the control EP after (1) or (2), so that | |||
* (3) will STALL. Seems we need to clear the STALL after that so | |||
* that the next SETUP can make it through. | |||
* | |||
* | |||
*/ | |||
/** | |||
* The following code is not written defensively, but instead only | |||
* asserts values that are essential for correct execution. It | |||
* accepts a superset of the protocol defined by the standard. We do | |||
* this to save space. | |||
*/ | |||
static void usb_handle_control(void *data, ssize_t len, void *cbdata) | |||
{ | |||
struct usb_ctrl_req_t *req = data; | |||
uint16_t zero16 = 0; | |||
int fail = 1; | |||
usb.ctrl_dir = req->in; | |||
if (req->type != USB_CTRL_REQ_STD) { | |||
usb_handle_control_nonstd(req); | |||
return; | |||
} | |||
/* Only STD requests here */ | |||
switch (req->bRequest) { | |||
case USB_CTRL_REQ_GET_STATUS: | |||
/** | |||
* Because we don't support remote wakeup or | |||
* self-powered operation, and we are specialized to | |||
* only EP 0 so far, all GET_STATUS replies are just | |||
* empty. | |||
*/ | |||
usb_ep0_tx_cp(&zero16, sizeof(zero16), req->wLength, NULL, NULL); | |||
break; | |||
case USB_CTRL_REQ_CLEAR_FEATURE: | |||
case USB_CTRL_REQ_SET_FEATURE: | |||
/** | |||
* Nothing to do. Maybe return STALLs on illegal | |||
* accesses? | |||
*/ | |||
break; | |||
case USB_CTRL_REQ_SET_ADDRESS: | |||
/** | |||
* We must keep our previous address until the end of | |||
* the status stage; therefore we can't set the | |||
* address right now. Since this is a special case, | |||
* the EP 0 handler will take care of this later on. | |||
*/ | |||
usb.address = req->wValue & 0x7f; | |||
usb.state = USBD_STATE_SETTING_ADDRESS; | |||
break; | |||
case USB_CTRL_REQ_GET_DESCRIPTOR: | |||
switch (req->wValue >> 8) { | |||
case USB_DESC_DEV: | |||
usb_ep0_tx_cp(usb.identity->dev_desc, usb.identity->dev_desc->bLength, | |||
req->wLength, NULL, NULL); | |||
fail = 0; | |||
break; | |||
case USB_DESC_CONFIG: | |||
fail = usb_tx_config_desc(req->wValue & 0xff, req->wLength); | |||
break; | |||
case USB_DESC_STRING: | |||
fail = usb_tx_string_desc(req->wValue & 0xff, req->wLength); | |||
break; | |||
default: | |||
fail = -1; | |||
break; | |||
} | |||
/* we set fail already, so we can go directly to `err' */ | |||
goto err; | |||
case USB_CTRL_REQ_GET_CONFIGURATION: | |||
usb_ep0_tx_cp(&usb.config, 1, req->wLength, NULL, NULL); /* XXX implicit LE */ | |||
break; | |||
case USB_CTRL_REQ_SET_CONFIGURATION: | |||
if (usb_set_config(req->wValue) < 0) | |||
goto err; | |||
break; | |||
case USB_CTRL_REQ_GET_INTERFACE: | |||
/* We only support iface setting 0 */ | |||
usb_ep0_tx_cp(&zero16, 1, req->wLength, NULL, NULL); | |||
break; | |||
case USB_CTRL_REQ_SET_INTERFACE: | |||
if (usb_set_interface(req->wIndex, req->wValue) < 0) | |||
goto err; | |||
break; | |||
default: | |||
goto err; | |||
} | |||
fail = 0; | |||
err: | |||
usb_handle_control_status(fail); | |||
} | |||
void usb_setup_control(void) | |||
{ | |||
void *buf = ep0_buf[usb.ep_state[0].rx.pingpong]; | |||
usb.ep_state[0].rx.data01 = USB_DATA01_DATA0; | |||
usb.ep_state[0].tx.data01 = USB_DATA01_DATA1; | |||
usb_rx(&usb.ep_state[0].rx, buf, EP0_BUFSIZE, usb_handle_control, NULL); | |||
} | |||
/** | |||
* This is called by the interrupt handler | |||
*/ | |||
void usb_handle_transaction(struct usb_xfer_info *info) | |||
{ | |||
enum usb_tok_pid pid = usb_get_xfer_pid(info); | |||
struct usbd_ep_state_t *eps = &usb.ep_state[usb_get_xfer_ep(info)]; | |||
struct usbd_ep_pipe_state_t *s = &eps->pipe[usb_get_xfer_dir(info)]; | |||
switch (pid) { | |||
case USB_PID_SETUP: | |||
case USB_PID_OUT: | |||
/** | |||
* If we receive a SETUP transaction, but don't expect | |||
* it (callback set to somewhere else), stall the EP. | |||
*/ | |||
if (pid == USB_PID_SETUP && s->callback != usb_handle_control) | |||
usb_handle_control_status(1); | |||
else | |||
usb_rx_next(s); | |||
if (pid == USB_PID_SETUP) | |||
usb_enable_xfers(); | |||
break; | |||
case USB_PID_IN: | |||
usb_tx_next(s); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
struct usbd_ep_pipe_state_t *usb_init_ep(struct usbd_function_ctx_header *ctx, int ep, enum usb_ep_dir dir, size_t size) | |||
{ | |||
struct usbd_ep_pipe_state_t *s; | |||
if (dir == USB_EP_RX) | |||
s = &usb.ep_state[ctx->ep_rx_offset + ep].rx; | |||
else | |||
s = &usb.ep_state[ctx->ep_tx_offset + ep].tx; | |||
memset(s, 0, sizeof(*s)); | |||
s->ep_maxsize = size; | |||
s->ep_num = ep; | |||
s->ep_dir = dir; | |||
usb_pipe_enable(s); | |||
return (s); | |||
} | |||
void usb_restart(void) | |||
{ | |||
const struct usbd_device *identity = usb.identity; | |||
/* XXX reset existing functions? */ | |||
memset(&usb, 0, sizeof(usb)); | |||
usb.functions.function = &usb.control_function; | |||
usb.identity = identity; | |||
usb_init_ep(&usb.functions, 0, USB_EP_RX, EP0_BUFSIZE); | |||
usb_init_ep(&usb.functions, 0, USB_EP_TX, EP0_BUFSIZE); | |||
usb_setup_control(); | |||
} | |||
void usb_attach_function(const struct usbd_function *function, struct usbd_function_ctx_header *ctx) | |||
{ | |||
/* XXX right now this requires a sequential initialization */ | |||
struct usbd_function_ctx_header *prev = &usb.functions; | |||
while (prev->next != NULL) | |||
prev = prev->next; | |||
ctx->next = NULL; | |||
ctx->function = function; | |||
ctx->interface_offset = prev->interface_offset + prev->function->interface_count; | |||
ctx->ep_rx_offset = prev->ep_rx_offset + prev->function->ep_rx_count; | |||
ctx->ep_tx_offset = prev->ep_tx_offset + prev->function->ep_tx_count; | |||
prev->next = ctx; | |||
} | |||
void usb_init(const struct usbd_device *identity) | |||
{ | |||
usb.identity = identity; | |||
usb_enable(); | |||
} | |||
@@ -0,0 +1,513 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef __USB_H | |||
#define __USB_H | |||
// ----- Compiler Includes ----- | |||
#include <sys/types.h> | |||
// ----- Local Includes ----- | |||
#include "mchck.h" | |||
#include "usb-common.h" | |||
// ----- Defines ----- | |||
#define USB_CTRL_REQ_DIR_SHIFT 0 | |||
#define USB_CTRL_REQ_TYPE_SHIFT 1 | |||
#define USB_CTRL_REQ_RECP_SHIFT 3 | |||
#define USB_CTRL_REQ_CODE_SHIFT 8 | |||
#define USB_CTRL_REQ(req_inout, req_type, req_code) \ | |||
(uint16_t) \ | |||
((USB_CTRL_REQ_##req_inout << USB_CTRL_REQ_DIR_SHIFT) \ | |||
|(USB_CTRL_REQ_##req_type << USB_CTRL_REQ_TYPE_SHIFT) \ | |||
|(USB_CTRL_REQ_##req_code << USB_CTRL_REQ_CODE_SHIFT)) | |||
// ----- Macros ----- | |||
#define USB_DESC_STRING(s) \ | |||
(const void *)&(const struct { \ | |||
struct usb_desc_string_t dsc; \ | |||
char16_t str[sizeof(s) / 2 - 1]; \ | |||
}) {{ \ | |||
.bLength = sizeof(struct usb_desc_string_t) + \ | |||
sizeof(s) - 2, \ | |||
.bDescriptorType = USB_DESC_STRING, \ | |||
}, \ | |||
s \ | |||
} | |||
#define USB_DESC_STRING_LANG_ENUS USB_DESC_STRING(u"\x0409") | |||
#define USB_DESC_STRING_SERIALNO ((const void *)1) | |||
#define USB_FUNCTION_IFACE(iface, iface_off, tx_ep_off, rx_ep_off) \ | |||
((iface_off) + (iface)) | |||
#define USB_FUNCTION_TX_EP(ep, iface_off, tx_ep_off, rx_ep_off) \ | |||
((tx_ep_off) + (ep)) | |||
#define USB_FUNCTION_RX_EP(ep, iface_off, tx_ep_off, rx_ep_off) \ | |||
((rx_ep_off) + (ep)) | |||
#define USB__INCREMENT(i, _0) (i + 1) | |||
#define USB__COUNT_IFACE_EP(i, e) \ | |||
__DEFER(USB__COUNT_IFACE_EP_)(__EXPAND i, e) | |||
#define USB__COUNT_IFACE_EP_(iface, tx_ep, rx_ep, func) \ | |||
(iface + USB_FUNCTION_ ## func ## _IFACE_COUNT, \ | |||
tx_ep + USB_FUNCTION_ ## func ## _TX_EP_COUNT, \ | |||
rx_ep + USB_FUNCTION_ ## func ## _RX_EP_COUNT) | |||
#define USB__GET_FUNCTION_IFACE_COUNT(iter, func) \ | |||
USB_FUNCTION_ ## func ## _IFACE_COUNT + | |||
#define USB__DEFINE_FUNCTION_DESC(iter, func) \ | |||
USB_FUNCTION_DESC_ ## func ## _DECL __CAT(__usb_func_desc, __COUNTER__); | |||
#define USB__INIT_FUNCTION_DESC(iter, func) \ | |||
USB_FUNCTION_DESC_ ## func iter, | |||
#define USB__DEFINE_CONFIG_DESC(confignum, name, ...) \ | |||
&((const struct name { \ | |||
struct usb_desc_config_t config; \ | |||
__REPEAT_INNER(, __EAT, USB__DEFINE_FUNCTION_DESC, __VA_ARGS__) \ | |||
}){ \ | |||
.config = { \ | |||
.bLength = sizeof(struct usb_desc_config_t), \ | |||
.bDescriptorType = USB_DESC_CONFIG, \ | |||
.wTotalLength = sizeof(struct name), \ | |||
.bNumInterfaces = __REPEAT_INNER(, __EAT, USB__GET_FUNCTION_IFACE_COUNT, __VA_ARGS__) 0, \ | |||
.bConfigurationValue = confignum, \ | |||
.iConfiguration = 0, \ | |||
.one = 1, \ | |||
.bMaxPower = 50 \ | |||
}, \ | |||
__REPEAT_INNER((0, 0, 0), USB__COUNT_IFACE_EP, USB__INIT_FUNCTION_DESC, __VA_ARGS__) \ | |||
}).config | |||
#define USB__DEFINE_CONFIG(iter, args) \ | |||
__DEFER(USB__DEFINE_CONFIG_)(iter, __EXPAND args) | |||
#define USB__DEFINE_CONFIG_(confignum, initfun, ...) \ | |||
&(const struct usbd_config){ \ | |||
.init = initfun, \ | |||
.desc = USB__DEFINE_CONFIG_DESC( \ | |||
confignum, \ | |||
__CAT(__usb_desc, __COUNTER__), \ | |||
__VA_ARGS__) \ | |||
}, | |||
#define USB_INIT_DEVICE(vid, pid, manuf, product, ...) \ | |||
{ \ | |||
.dev_desc = &(const struct usb_desc_dev_t){ \ | |||
.bLength = sizeof(struct usb_desc_dev_t), \ | |||
.bDescriptorType = USB_DESC_DEV, \ | |||
.bcdUSB = { .maj = 2 }, \ | |||
.bDeviceClass = USB_DEV_CLASS_SEE_IFACE, \ | |||
.bDeviceSubClass = USB_DEV_SUBCLASS_SEE_IFACE, \ | |||
.bDeviceProtocol = USB_DEV_PROTO_SEE_IFACE, \ | |||
.bMaxPacketSize0 = EP0_BUFSIZE, \ | |||
.idVendor = vid, \ | |||
.idProduct = pid, \ | |||
.bcdDevice = { .raw = 0 }, \ | |||
.iManufacturer = 1, \ | |||
.iProduct = 2, \ | |||
.iSerialNumber = 3, \ | |||
.bNumConfigurations = __PP_NARG(__VA_ARGS__), \ | |||
}, \ | |||
.string_descs = (const struct usb_desc_string_t * const []){ \ | |||
USB_DESC_STRING_LANG_ENUS, \ | |||
USB_DESC_STRING(manuf), \ | |||
USB_DESC_STRING(product), \ | |||
USB_DESC_STRING_SERIALNO, \ | |||
NULL \ | |||
}, \ | |||
.configs = { \ | |||
__REPEAT(1, USB__INCREMENT, USB__DEFINE_CONFIG, __VA_ARGS__) \ | |||
NULL \ | |||
} \ | |||
} | |||
// ----- Structs & Enumerations ----- | |||
/** | |||
* Note: bitfields ahead. | |||
* GCC fills the fields lsb-to-msb on little endian. | |||
*/ | |||
/** | |||
* USB descriptors | |||
*/ | |||
enum usb_desc_type { | |||
USB_DESC_DEV = 1, | |||
USB_DESC_CONFIG = 2, | |||
USB_DESC_STRING = 3, | |||
USB_DESC_IFACE = 4, | |||
USB_DESC_EP = 5, | |||
USB_DESC_DEVQUAL = 6, | |||
USB_DESC_OTHERSPEED = 7, | |||
USB_DESC_POWER = 8 | |||
}; | |||
struct usb_desc_type_t { | |||
UNION_STRUCT_START(8); | |||
enum usb_desc_type id : 5; | |||
enum usb_desc_type_type { | |||
USB_DESC_TYPE_STD = 0, | |||
USB_DESC_TYPE_CLASS = 1, | |||
USB_DESC_TYPE_VENDOR = 2 | |||
} type_type : 2; | |||
uint8_t _rsvd0 : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct usb_desc_type_t, 1); | |||
enum usb_dev_class { | |||
USB_DEV_CLASS_SEE_IFACE = 0, | |||
USB_DEV_CLASS_APP = 0xfe, | |||
USB_DEV_CLASS_VENDOR = 0xff | |||
}; | |||
enum usb_dev_subclass { | |||
USB_DEV_SUBCLASS_SEE_IFACE = 0, | |||
USB_DEV_SUBCLASS_VENDOR = 0xff | |||
}; | |||
enum usb_dev_proto { | |||
USB_DEV_PROTO_SEE_IFACE = 0, | |||
USB_DEV_PROTO_VENDOR = 0xff | |||
}; | |||
struct usb_bcd_t { | |||
UNION_STRUCT_START(16); | |||
struct { | |||
uint8_t sub : 4; | |||
uint8_t min : 4; | |||
uint16_t maj : 8; | |||
}; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct usb_bcd_t, 2); | |||
struct usb_desc_generic_t { | |||
uint8_t bLength; | |||
struct usb_desc_type_t bDescriptorType; | |||
uint8_t data[]; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct usb_desc_generic_t, 2); | |||
struct usb_desc_dev_t { | |||
uint8_t bLength; | |||
enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_DEV */ | |||
struct usb_bcd_t bcdUSB; /* = 0x0200 */ | |||
enum usb_dev_class bDeviceClass : 8; | |||
enum usb_dev_subclass bDeviceSubClass : 8; | |||
enum usb_dev_proto bDeviceProtocol : 8; | |||
uint8_t bMaxPacketSize0; | |||
uint16_t idVendor; | |||
uint16_t idProduct; | |||
struct usb_bcd_t bcdDevice; | |||
uint8_t iManufacturer; | |||
uint8_t iProduct; | |||
uint8_t iSerialNumber; | |||
uint8_t bNumConfigurations; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct usb_desc_dev_t, 18); | |||
struct usb_desc_ep_t { | |||
uint8_t bLength; | |||
enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_EP */ | |||
union { | |||
struct { | |||
uint8_t ep_num : 4; | |||
uint8_t _rsvd0 : 3; | |||
uint8_t in : 1; | |||
}; | |||
uint8_t bEndpointAddress; | |||
}; | |||
struct { | |||
enum usb_ep_type { | |||
USB_EP_CONTROL = 0, | |||
USB_EP_ISO = 1, | |||
USB_EP_BULK = 2, | |||
USB_EP_INTR = 3 | |||
} type : 2; | |||
enum usb_ep_iso_synctype { | |||
USB_EP_ISO_NOSYNC = 0, | |||
USB_EP_ISO_ASYNC = 1, | |||
USB_EP_ISO_ADAPTIVE = 2, | |||
USB_EP_ISO_SYNC = 3 | |||
} sync_type : 2; | |||
enum usb_ep_iso_usagetype { | |||
USB_EP_ISO_DATA = 0, | |||
USB_EP_ISO_FEEDBACK = 1, | |||
USB_EP_ISO_IMPLICIT = 2 | |||
} usage_type : 2; | |||
uint8_t _rsvd1 : 2; | |||
}; | |||
struct { | |||
uint16_t wMaxPacketSize : 11; | |||
uint16_t _rsvd2 : 5; | |||
}; | |||
uint8_t bInterval; | |||
} __packed; | |||
CTASSERT_SIZE_BYTE(struct usb_desc_ep_t, 7); | |||
struct usb_desc_iface_t { | |||
uint8_t bLength; | |||
enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_IFACE */ | |||
uint8_t bInterfaceNumber; | |||
uint8_t bAlternateSetting; | |||
uint8_t bNumEndpoints; | |||
enum usb_dev_class bInterfaceClass : 8; | |||
enum usb_dev_subclass bInterfaceSubClass: 8; | |||
enum usb_dev_proto bInterfaceProtocol : 8; | |||
uint8_t iInterface; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct usb_desc_iface_t, 9); | |||
struct usb_desc_config_t { | |||
uint8_t bLength; | |||
enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_CONFIG */ | |||
uint16_t wTotalLength; /* size of config, iface, ep */ | |||
uint8_t bNumInterfaces; | |||
uint8_t bConfigurationValue; | |||
uint8_t iConfiguration; | |||
struct { | |||
uint8_t _rsvd0 : 5; | |||
uint8_t remote_wakeup : 1; | |||
uint8_t self_powered : 1; | |||
uint8_t one : 1; /* = 1 for historical reasons */ | |||
}; | |||
uint8_t bMaxPower; /* units of 2mA */ | |||
} __packed; | |||
CTASSERT_SIZE_BYTE(struct usb_desc_config_t, 9); | |||
struct usb_desc_string_t { | |||
uint8_t bLength; | |||
enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_STRING */ | |||
const char16_t bString[]; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct usb_desc_string_t, 2); | |||
struct usb_ctrl_req_t { | |||
union /* reqtype and request & u16 */ { | |||
struct /* reqtype and request */ { | |||
union /* reqtype in bitfield & u8 */ { | |||
struct /* reqtype */ { | |||
enum usb_ctrl_req_recp { | |||
USB_CTRL_REQ_DEV = 0, | |||
USB_CTRL_REQ_IFACE = 1, | |||
USB_CTRL_REQ_EP = 2, | |||
USB_CTRL_REQ_OTHER = 3 | |||
} recp : 5; | |||
enum usb_ctrl_req_type { | |||
USB_CTRL_REQ_STD = 0, | |||
USB_CTRL_REQ_CLASS = 1, | |||
USB_CTRL_REQ_VENDOR = 2 | |||
} type : 2; | |||
enum usb_ctrl_req_dir { | |||
USB_CTRL_REQ_OUT = 0, | |||
USB_CTRL_REQ_IN = 1 | |||
} in : 1; | |||
}; | |||
uint8_t bmRequestType; | |||
}; /* union */ | |||
enum usb_ctrl_req_code { | |||
USB_CTRL_REQ_GET_STATUS = 0, | |||
USB_CTRL_REQ_CLEAR_FEATURE = 1, | |||
USB_CTRL_REQ_SET_FEATURE = 3, | |||
USB_CTRL_REQ_SET_ADDRESS = 5, | |||
USB_CTRL_REQ_GET_DESCRIPTOR = 6, | |||
USB_CTRL_REQ_SET_DESCRIPTOR = 7, | |||
USB_CTRL_REQ_GET_CONFIGURATION = 8, | |||
USB_CTRL_REQ_SET_CONFIGURATION = 9, | |||
USB_CTRL_REQ_GET_INTERFACE = 10, | |||
USB_CTRL_REQ_SET_INTERFACE = 11, | |||
USB_CTRL_REQ_SYNC_FRAME = 12 | |||
} bRequest : 8; | |||
}; /* struct */ | |||
uint16_t type_and_req; | |||
}; /* union */ | |||
union { | |||
uint16_t wValue; | |||
struct { | |||
uint8_t wValueLow; | |||
uint8_t wValueHigh; | |||
}; | |||
}; | |||
uint16_t wIndex; | |||
uint16_t wLength; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct usb_ctrl_req_t, 8); | |||
/** | |||
* status replies for GET_STATUS. | |||
*/ | |||
struct usb_ctrl_req_status_dev_t { | |||
uint16_t self_powered : 1; | |||
uint16_t remote_wakeup : 1; | |||
uint16_t _rsvd : 14; | |||
}; | |||
CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_dev_t, 16); | |||
struct usb_ctrl_req_status_iface_t { | |||
uint16_t _rsvd; | |||
}; | |||
CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_iface_t, 16); | |||
struct usb_ctrl_req_status_ep_t { | |||
uint16_t halt : 1; | |||
uint16_t _rsvd : 15; | |||
}; | |||
CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_ep_t, 16); | |||
/** | |||
* Descriptor type (in req->value) for GET_DESCRIPTOR. | |||
*/ | |||
struct usb_ctrl_req_desc_t { | |||
uint8_t idx; | |||
enum usb_desc_type type : 8; | |||
}; | |||
CTASSERT_SIZE_BIT(struct usb_ctrl_req_desc_t, 16); | |||
/** | |||
* Feature selector (in req->value) for CLEAR_FEATURE. | |||
*/ | |||
enum usb_ctrl_req_feature { | |||
USB_CTRL_REQ_FEAT_EP_HALT = 0, | |||
USB_CTRL_REQ_FEAT_DEV_REMOTE_WKUP = 1, | |||
USB_CTRL_REQ_FEAT_TEST_MODE = 2 | |||
}; | |||
struct usb_xfer_info; | |||
typedef void (*ep_callback_t)(void *buf, ssize_t len, void *data); | |||
/** | |||
* (Artificial) function. Aggregates one or more interfaces. | |||
*/ | |||
struct usbd_function { | |||
int (*configure)(int orig_iface, int iface, int altsetting, void *data); | |||
int (*control)(struct usb_ctrl_req_t *, void *); | |||
int interface_count; | |||
int ep_rx_count; | |||
int ep_tx_count; | |||
}; | |||
struct usbd_function_ctx_header { | |||
struct usbd_function_ctx_header *next; | |||
const struct usbd_function *function; | |||
int interface_offset; | |||
int ep_rx_offset; | |||
int ep_tx_offset; | |||
}; | |||
typedef void (usbd_init_fun_t)(int); | |||
typedef void (usbd_suspend_resume_fun_t)(void); | |||
/** | |||
* Configuration. Contains one or more functions which all will be | |||
* active concurrently. | |||
*/ | |||
struct usbd_config { | |||
usbd_init_fun_t *init; | |||
usbd_suspend_resume_fun_t *suspend; | |||
usbd_suspend_resume_fun_t *resume; | |||
/** | |||
* We will not set a config for now, because there is not much to | |||
* configure, except for power | |||
* | |||
* const struct usb_desc_config_t *config_desc; | |||
*/ | |||
const struct usb_desc_config_t *desc; | |||
const struct usbd_function *function[]; | |||
}; | |||
/** | |||
* Device. Contains one or more configurations, out of which only one | |||
* is active at a time. | |||
*/ | |||
struct usbd_device { | |||
const struct usb_desc_dev_t *dev_desc; | |||
const struct usb_desc_string_t * const *string_descs; | |||
const struct usbd_config *configs[]; | |||
}; | |||
/* Provided by MD code */ | |||
struct usbd_ep_pipe_state_t; | |||
// ----- Functions ----- | |||
void *usb_get_xfer_data(struct usb_xfer_info *); | |||
enum usb_tok_pid usb_get_xfer_pid(struct usb_xfer_info *); | |||
int usb_get_xfer_ep(struct usb_xfer_info *); | |||
enum usb_ep_dir usb_get_xfer_dir(struct usb_xfer_info *); | |||
void usb_enable_xfers(void); | |||
void usb_set_addr(int); | |||
void usb_ep_stall(int); | |||
size_t usb_ep_get_transfer_size(struct usbd_ep_pipe_state_t *); | |||
void usb_queue_next(struct usbd_ep_pipe_state_t *, void *, size_t); | |||
void usb_pipe_stall(struct usbd_ep_pipe_state_t *); | |||
void usb_pipe_unstall(struct usbd_ep_pipe_state_t *); | |||
void usb_pipe_enable(struct usbd_ep_pipe_state_t *s); | |||
void usb_pipe_disable(struct usbd_ep_pipe_state_t *s); | |||
#ifdef VUSB | |||
void vusb_main_loop(void); | |||
#else | |||
void usb_poll(void); | |||
#endif | |||
int usb_tx_serialno(size_t reqlen); | |||
/* Provided by MI code */ | |||
void usb_init(const struct usbd_device *); | |||
void usb_attach_function(const struct usbd_function *function, struct usbd_function_ctx_header *ctx); | |||
void usb_handle_transaction(struct usb_xfer_info *); | |||
void usb_setup_control(void); | |||
void usb_handle_control_status_cb(ep_callback_t cb); | |||
void usb_handle_control_status(int); | |||
struct usbd_ep_pipe_state_t *usb_init_ep(struct usbd_function_ctx_header *ctx, int ep, enum usb_ep_dir dir, size_t size); | |||
int usb_rx(struct usbd_ep_pipe_state_t *, void *, size_t, ep_callback_t, void *); | |||
int usb_tx(struct usbd_ep_pipe_state_t *, const void *, size_t, size_t, ep_callback_t, void *); | |||
int usb_ep0_rx(void *, size_t, ep_callback_t, void *); | |||
void *usb_ep0_tx_inplace_prepare(size_t len); | |||
int usb_ep0_tx(void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data); | |||
int usb_ep0_tx_cp(const void *, size_t, size_t, ep_callback_t, void *); | |||
// ----- DFU USB Additional Includes ----- | |||
#include "dfu.h" | |||
#endif | |||
@@ -0,0 +1,301 @@ | |||
/* Copyright (c) 2011,2012 Simon Schubert <[email protected]>. | |||
* Modifications by Jacob Alexander 2014 <[email protected]> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program 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 General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Local Includes ----- | |||
#include "mchck.h" | |||
#include "usb-common.h" | |||
// ----- Structs ----- | |||
/** | |||
* Hardware structures | |||
*/ | |||
struct USB_ADDINFO_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t iehost : 1; | |||
uint8_t _rsvd0 : 2; | |||
uint8_t irqnum : 5; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_ADDINFO_t, 8); | |||
struct USB_OTGSTAT_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t avbus : 1; | |||
uint8_t _rsvd0 : 1; | |||
uint8_t b_sess : 1; | |||
uint8_t sessvld : 1; | |||
uint8_t _rsvd1 : 1; | |||
uint8_t line_state : 1; | |||
uint8_t onemsec : 1; | |||
uint8_t idchg : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_OTGSTAT_t, 8); | |||
struct USB_OTGCTL_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t _rsvd0 : 2; | |||
uint8_t otgen : 1; | |||
uint8_t _rsvd1 : 1; | |||
uint8_t dmlow : 1; | |||
uint8_t dplow : 1; | |||
uint8_t _rsvd2 : 1; | |||
uint8_t dphigh : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_OTGCTL_t, 8); | |||
struct USB_ISTAT_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t usbrst : 1; | |||
uint8_t error : 1; | |||
uint8_t softok : 1; | |||
uint8_t tokdne : 1; | |||
uint8_t sleep : 1; | |||
uint8_t resume : 1; | |||
uint8_t attach : 1; | |||
uint8_t stall : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_ISTAT_t, 8); | |||
struct USB_ERRSTAT_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t piderr : 1; | |||
uint8_t crc5eof : 1; | |||
uint8_t crc16 : 1; | |||
uint8_t dfn8 : 1; | |||
uint8_t btoerr : 1; | |||
uint8_t dmaerr : 1; | |||
uint8_t _rsvd0 : 1; | |||
uint8_t btserr : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_ERRSTAT_t, 8); | |||
struct USB_STAT_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t _rsvd0 : 2; | |||
enum usb_ep_pingpong pingpong : 1; | |||
enum usb_ep_dir dir : 1; | |||
uint8_t ep : 4; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_STAT_t, 8); | |||
struct USB_CTL_t { | |||
union { | |||
struct /* common */ { | |||
uint8_t _rsvd1 : 1; | |||
uint8_t oddrst : 1; | |||
uint8_t resume : 1; | |||
uint8_t _rsvd2 : 3; | |||
uint8_t se0 : 1; | |||
uint8_t jstate : 1; | |||
}; | |||
struct /* host */ { | |||
uint8_t sofen : 1; | |||
uint8_t _rsvd3 : 2; | |||
uint8_t hostmodeen : 1; | |||
uint8_t reset : 1; | |||
uint8_t token_busy : 1; | |||
uint8_t _rsvd4 : 2; | |||
}; | |||
struct /* device */ { | |||
uint8_t usben : 1; | |||
uint8_t _rsvd5 : 4; | |||
uint8_t txd_suspend : 1; | |||
uint8_t _rsvd6 : 2; | |||
}; | |||
uint8_t raw; | |||
}; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_CTL_t, 8); | |||
struct USB_ADDR_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t addr : 7; | |||
uint8_t lsen : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_ADDR_t, 8); | |||
struct USB_TOKEN_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t endpt : 4; | |||
enum usb_tok_pid pid : 4; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_TOKEN_t, 8); | |||
struct USB_ENDPT_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t ephshk : 1; | |||
uint8_t epstall : 1; | |||
uint8_t eptxen : 1; | |||
uint8_t eprxen : 1; | |||
uint8_t epctldis : 1; | |||
uint8_t _rsvd0 : 1; | |||
uint8_t retrydis : 1; | |||
uint8_t hostwohub : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_ENDPT_t, 8); | |||
struct USB_USBCTRL_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t _rsvd0 : 6; | |||
uint8_t pde : 1; | |||
uint8_t susp : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_USBCTRL_t, 8); | |||
struct USB_OBSERVE_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t _rsvd0 : 4; | |||
uint8_t dmpd : 1; | |||
uint8_t _rsvd1 : 1; | |||
uint8_t dppd : 1; | |||
uint8_t dppu : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_OBSERVE_t, 8); | |||
struct USB_CONTROL_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t _rsvd0 : 4; | |||
uint8_t dppullupnonotg : 1; | |||
uint8_t _rsvd1 : 3; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_CONTROL_t, 8); | |||
struct USB_USBTRC0_t { | |||
UNION_STRUCT_START(8); | |||
uint8_t usb_resume_int : 1; | |||
uint8_t sync_det : 1; | |||
uint8_t _rsvd0 : 3; | |||
uint8_t usbresmen : 1; | |||
uint8_t _rsvd1 : 1; | |||
uint8_t usbreset : 1; | |||
UNION_STRUCT_END; | |||
}; | |||
CTASSERT_SIZE_BIT(struct USB_USBTRC0_t, 8); | |||
struct USB_t { | |||
uint8_t perid; | |||
uint8_t _pad0[3]; | |||
uint8_t idcomp; | |||
uint8_t _pad1[3]; | |||
uint8_t rev; | |||
uint8_t _pad2[3]; | |||
struct USB_ADDINFO_t addinfo; | |||
uint8_t _pad3[3]; | |||
struct USB_OTGSTAT_t otgistat; | |||
uint8_t _pad4[3]; | |||
struct USB_OTGSTAT_t otgicr; | |||
uint8_t _pad5[3]; | |||
struct USB_OTGSTAT_t otgstat; | |||
uint8_t _pad6[3]; | |||
struct USB_OTGCTL_t otgctl; | |||
uint8_t _pad7[3]; | |||
uint8_t _pad8[0x80 - 0x20]; | |||
struct USB_ISTAT_t istat; | |||
uint8_t _pad9[3]; | |||
struct USB_ISTAT_t inten; | |||
uint8_t _pad10[3]; | |||
struct USB_ERRSTAT_t errstat; | |||
uint8_t _pad11[3]; | |||
struct USB_ERRSTAT_t erren; | |||
uint8_t _pad12[3]; | |||
struct USB_STAT_t stat; | |||
uint8_t _pad13[3]; | |||
struct USB_CTL_t ctl; | |||
uint8_t _pad14[3]; | |||
struct USB_ADDR_t addr; | |||
uint8_t _pad15[3]; | |||
uint8_t bdtpage1; | |||
uint8_t _pad16[3]; | |||
uint8_t frmnuml; | |||
uint8_t _pad17[3]; | |||
struct { | |||
uint8_t frmnumh : 3; | |||
uint8_t _rsvd0 : 5; | |||
}; | |||
uint8_t _pad18[3]; | |||
struct USB_TOKEN_t token; | |||
uint8_t _pad19[3]; | |||
uint8_t softhld; | |||
uint8_t _pad20[3]; | |||
uint8_t bdtpage2; | |||
uint8_t _pad21[3]; | |||
uint8_t bdtpage3; | |||
uint8_t _pad22[3]; | |||
uint8_t _pad23[0xc0 - 0xb8]; | |||
struct { | |||
struct USB_ENDPT_t; | |||
uint8_t _pad24[3]; | |||
} endpt[16]; | |||
struct USB_USBCTRL_t usbctrl; | |||
uint8_t _pad25[3]; | |||
struct USB_OBSERVE_t observe; | |||
uint8_t _pad26[3]; | |||
struct USB_CONTROL_t control; | |||
uint8_t _pad27[3]; | |||
struct USB_USBTRC0_t usbtrc0; | |||
uint8_t _pad28[3]; | |||
uint8_t _pad29[4]; | |||
uint8_t usbfrmadjust; | |||
uint8_t _pad30[3]; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct USB_t, 0x118); | |||
struct USB_BD_t { | |||
struct USB_BD_BITS_t { | |||
union { | |||
struct { | |||
uint32_t _rsvd0 : 2; | |||
uint32_t stall : 1; | |||
uint32_t dts : 1; | |||
uint32_t ninc : 1; | |||
uint32_t keep : 1; | |||
enum usb_data01 data01 : 1; | |||
uint32_t own : 1; | |||
uint32_t _rsvd1 : 8; | |||
uint32_t bc : 10; | |||
uint32_t _rsvd2 : 6; | |||
}; | |||
struct /* processor */ { | |||
uint32_t _rsvd5 : 2; | |||
enum usb_tok_pid tok_pid : 4; | |||
uint32_t _rsvd6 : 26; | |||
}; | |||
uint32_t raw; | |||
}; | |||
}; | |||
void *addr; | |||
}; | |||
CTASSERT_SIZE_BYTE(struct USB_BD_t, 8); | |||
extern volatile struct USB_t USB0; | |||
@@ -0,0 +1,112 @@ | |||
/* Teensyduino Core Library | |||
* http://www.pjrc.com/teensy/ | |||
* Copyright (c) 2013 PJRC.COM, LLC. | |||
* Modifications by Jacob Alexander 2014 for use with McHCK | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining | |||
* a copy of this software and associated documentation files (the | |||
* "Software"), to deal in the Software without restriction, including | |||
* without limitation the rights to use, copy, modify, merge, publish, | |||
* distribute, sublicense, and/or sell copies of the Software, and to | |||
* permit persons to whom the Software is furnished to do so, subject to | |||
* the following conditions: | |||
* | |||
* 1. The above copyright notice and this permission notice shall be | |||
* included in all copies or substantial portions of the Software. | |||
* | |||
* 2. If the Software is incorporated into a build system that allows | |||
* selection among a list of target devices, then similar target | |||
* devices manufactured by PJRC.COM must be included in the list of | |||
* target devices and selectable in the same manner. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
* SOFTWARE. | |||
*/ | |||
MEMORY | |||
{ | |||
FLASH (rx) : ORIGIN = 0x0, LENGTH = 128K | |||
FLASH_APP (rx) : ORIGIN = 4K, LENGTH = 128K-4K | |||
RAM (rwx) : ORIGIN = 0x20000000 - 16K / 2, LENGTH = 16K | |||
} | |||
/* Starting Address of the application ROM */ | |||
_app_rom = ORIGIN( FLASH_APP ); | |||
FlexRAM = 0x14000000; | |||
FTFL = 0x40020000; | |||
SCB = 0xe000ed00; | |||
USB0 = 0x40072000; | |||
SIM = 0x40047000; | |||
/* Section Definitions */ | |||
SECTIONS | |||
{ | |||
.text : | |||
{ | |||
. = 0; | |||
KEEP(* (.vectors)) | |||
*(.startup*) | |||
*(.rodata*) | |||
. = 0x400; | |||
KEEP(* (.flashconfig)) | |||
*(.text*) | |||
. = ALIGN(4); | |||
KEEP(*(.init)) | |||
} > FLASH | |||
.ARM.exidx : | |||
{ | |||
__exidx_start = .; | |||
*(.ARM.exidx* .gnu.linkonce.armexidx.*) | |||
__exidx_end = .; | |||
} > FLASH | |||
_etext = .; | |||
.usbdescriptortable (NOLOAD) : { | |||
. = ALIGN(512); | |||
*(.usbdescriptortable*) | |||
} > RAM | |||
.dmabuffers (NOLOAD) : { | |||
. = ALIGN(4); | |||
*(.dmabuffers*) | |||
} > RAM | |||
.usbbuffers (NOLOAD) : { | |||
. = ALIGN(4); | |||
*(.usbbuffers*) | |||
} > RAM | |||
.data : AT (_etext) { | |||
. = ALIGN(4); | |||
_sdata = .; | |||
*(SORT_BY_ALIGNMENT(.ramtext.*) SORT_BY_ALIGNMENT(.data*)) | |||
*(.data*) | |||
. = ALIGN(4); | |||
_edata = .; | |||
} > RAM | |||
.noinit (NOLOAD) : { | |||
*(.noinit*) | |||
} > RAM | |||
.bss : { | |||
. = ALIGN(4); | |||
_sbss = .; | |||
*(.bss*) | |||
*(COMMON) | |||
. = ALIGN(4); | |||
_ebss = .; | |||
__bss_end = .; | |||
} > RAM | |||
_estack = ORIGIN(RAM) + LENGTH(RAM); | |||
} | |||