first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,55 @@
|
||||
# Copyright (C) 2007-2013 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
# Base USB Device stack
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/usb.o \
|
||||
$(LOCAL_DIR)/usb_controller.o \
|
||||
$(LOCAL_DIR)/usb_core.o
|
||||
|
||||
ifneq ($(filter $(DOCKFIFOS), bulk),)
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/usb_dockfifo_controller.o
|
||||
endif
|
||||
|
||||
# SecureROM and LLB-DFU support
|
||||
ifneq ($(filter $(OPTIONS), WITH_DFU_MODE=1), )
|
||||
|
||||
OPTIONS += \
|
||||
WITH_USB_DFU=1 \
|
||||
WITH_USB_MODE_DFU=1
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/usb_dfu.o
|
||||
|
||||
endif
|
||||
|
||||
# Recovery mode support
|
||||
ifneq ($(filter $(OPTIONS), WITH_RECOVERY_MODE=1), )
|
||||
|
||||
OPTIONS += \
|
||||
WITH_USB_MODE_RECOVERY=1
|
||||
|
||||
ifeq ($(filter RELEASE ROMRELEASE,$(BUILD)),)
|
||||
OPTIONS += \
|
||||
WITH_BULK_UPLOAD=1
|
||||
endif
|
||||
|
||||
MODULES += \
|
||||
lib/cbuf \
|
||||
lib/cksum
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/usb_debug.o \
|
||||
$(LOCAL_DIR)/usb_serial.o \
|
||||
$(LOCAL_DIR)/usb_transfer.o
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
#include <drivers/usb/usb_public.h>
|
||||
#include <drivers/usb/usb_controller.h>
|
||||
#include <drivers/usb/usb_core.h>
|
||||
|
||||
#define require(assertion, exception_label) \
|
||||
do { \
|
||||
if (__builtin_expect(!(assertion), 0)) \
|
||||
{ \
|
||||
goto exception_label; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
#define DFU_MODE_CONFIG_STRING "Apple Mobile Device (DFU Mode)"
|
||||
#define RECOVERY_MODE_CONFIG_STRING "Apple Mobile Device (Recovery Mode)"
|
||||
|
||||
///////////////////////////////////
|
||||
// USB DFU mode - SecureROM only
|
||||
// USB Recovery mode - iBoot only
|
||||
///////////////////////////////////
|
||||
|
||||
static bool usb_inited;
|
||||
|
||||
static void usb_free()
|
||||
{
|
||||
if(!usb_inited) goto early_inited_free;
|
||||
|
||||
#if WITH_USB_MODE_DFU
|
||||
|
||||
usb_dfu_exit();
|
||||
|
||||
#elif WITH_USB_MODE_RECOVERY
|
||||
|
||||
usb_transfer_exit();
|
||||
usb_serial_exit();
|
||||
|
||||
#endif
|
||||
|
||||
return;
|
||||
|
||||
early_inited_free:
|
||||
|
||||
#if WITH_USB_MODE_RECOVERY
|
||||
usb_serial_exit();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
int usb_early_init()
|
||||
{
|
||||
#if WITH_USB_MODE_RECOVERY
|
||||
if(usb_serial_early_init() != 0) {
|
||||
usb_free();
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_init_with_controller(struct usb_controller_functions *controller_funcs)
|
||||
{
|
||||
if(usb_inited) return 0;
|
||||
|
||||
usb_controller_register(controller_funcs);
|
||||
|
||||
#if WITH_USB_MODE_DFU
|
||||
|
||||
require((usb_core_init(DFU_MODE_CONFIG_STRING) != -1), error);
|
||||
require((usb_dfu_init() != -1), error);
|
||||
|
||||
#elif WITH_USB_MODE_RECOVERY
|
||||
|
||||
require((usb_core_init(RECOVERY_MODE_CONFIG_STRING) != -1), error);
|
||||
require((usb_transfer_init() != -1), error);
|
||||
require((usb_serial_init() != -1), error);
|
||||
|
||||
#endif
|
||||
|
||||
require((usb_core_start() != -1), error);
|
||||
|
||||
usb_inited = true;
|
||||
|
||||
dprintf(DEBUG_INFO, "USB inited \n");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dprintf(DEBUG_INFO, "failed to start the core\n");
|
||||
usb_free();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_init()
|
||||
{
|
||||
// default to Synopsys OTG controller
|
||||
#if WITH_HW_SYNOPSYS_OTG
|
||||
extern struct usb_controller_functions *synopsys_otg_controller_init();
|
||||
|
||||
return usb_init_with_controller(synopsys_otg_controller_init());
|
||||
#else
|
||||
# error "usb_init used without defined USB controller";
|
||||
#endif
|
||||
}
|
||||
|
||||
void usb_quiesce()
|
||||
{
|
||||
if (usb_inited == false) return;
|
||||
|
||||
usb_core_stop();
|
||||
usb_free();
|
||||
usb_controller_register(NULL);
|
||||
|
||||
usb_inited = false;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <drivers/usb/usb_controller.h>
|
||||
|
||||
static struct usb_controller_functions usb_controller_funcs;
|
||||
static bool usb_controller_registered;
|
||||
|
||||
int usb_controller_register(const struct usb_controller_functions *controller_functions)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "usb_controller_register, %p\n", controller_functions);
|
||||
if (controller_functions != NULL) {
|
||||
bcopy((const void *)controller_functions, (void *)&usb_controller_funcs, sizeof(struct usb_controller_functions));
|
||||
usb_controller_registered = true;
|
||||
}
|
||||
else {
|
||||
bzero((void *)&usb_controller_funcs, sizeof(struct usb_controller_functions));
|
||||
usb_controller_registered = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_controller_init(void)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.init != NULL);
|
||||
return usb_controller_funcs.init();
|
||||
}
|
||||
|
||||
void usb_controller_free(void)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.free_func != NULL);
|
||||
usb_controller_funcs.free_func();
|
||||
}
|
||||
|
||||
int usb_controller_start(void)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.start != NULL);
|
||||
return usb_controller_funcs.start();
|
||||
}
|
||||
|
||||
void usb_controller_stop(void)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.stop != NULL);
|
||||
usb_controller_funcs.stop();
|
||||
}
|
||||
|
||||
void usb_controller_set_address(u_int32_t new_address)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.set_address != NULL);
|
||||
usb_controller_funcs.set_address(new_address);
|
||||
}
|
||||
|
||||
int usb_controller_get_connection_speed(void)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.get_connection_speed != NULL);
|
||||
return usb_controller_funcs.get_connection_speed();
|
||||
}
|
||||
|
||||
void usb_controller_activate_endpoint(u_int32_t endpoint, int type, int max_packet_size, int interval)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.activate_endpoint != NULL);
|
||||
usb_controller_funcs.activate_endpoint(endpoint, type, max_packet_size, interval);
|
||||
}
|
||||
|
||||
void usb_controller_do_endpoint_io(struct usb_device_io_request *req)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.do_endpoint_io != NULL);
|
||||
usb_controller_funcs.do_endpoint_io(req);
|
||||
}
|
||||
|
||||
void usb_controller_stall_endpoint(u_int32_t endpoint, bool stall)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.stall_endpoint != NULL);
|
||||
usb_controller_funcs.stall_endpoint(endpoint, stall);
|
||||
}
|
||||
|
||||
void usb_controller_reset_endpoint_data_toggle(u_int32_t endpoint)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.reset_endpoint_data_toggle != NULL);
|
||||
usb_controller_funcs.reset_endpoint_data_toggle(endpoint);
|
||||
}
|
||||
|
||||
bool usb_controller_is_endpoint_stalled(u_int32_t endpoint)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.is_endpoint_stalled != NULL);
|
||||
return usb_controller_funcs.is_endpoint_stalled(endpoint);
|
||||
}
|
||||
|
||||
void usb_controller_do_test_mode(u_int32_t selector)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.do_test_mode != NULL);
|
||||
usb_controller_funcs.do_test_mode(selector);
|
||||
}
|
||||
|
||||
void usb_controller_abort_endpoint(u_int32_t endpoint)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.abort_endpoint != NULL);
|
||||
usb_controller_funcs.abort_endpoint(endpoint);
|
||||
}
|
||||
|
||||
void usb_controller_deactivate_endpoint(u_int32_t endpoint)
|
||||
{
|
||||
RELEASE_ASSERT(usb_controller_funcs.deactivate_endpoint != NULL);
|
||||
usb_controller_funcs.deactivate_endpoint(endpoint);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <sys/menu.h>
|
||||
#include <lib/cksum.h>
|
||||
#include <lib/env.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <drivers/usb/usb_core.h>
|
||||
#include <drivers/usb/usb_public.h>
|
||||
#include <drivers/usb/usb_debug.h>
|
||||
|
||||
//===================================================================================
|
||||
// USB command stuff
|
||||
//===================================================================================
|
||||
|
||||
#if defined(WITH_MENU) && WITH_MENU
|
||||
|
||||
#define USB_CMD_STRING_LEN 1024
|
||||
|
||||
#define USB_SERIAL_CMD_START 0xfc
|
||||
#define USB_SERIAL_CMD_END 0xfd
|
||||
#define USB_SERIAL_CMD_START_OFFSET 1
|
||||
#define USB_SERIAL_CMD_END_OFFSET 1
|
||||
|
||||
static u_int8_t usb_cmd_string[USB_CMD_STRING_LEN] __attribute__ ((aligned(CPU_CACHELINE_SIZE)));
|
||||
|
||||
static int usb_cmd_main(int argc, struct cmd_arg * args);
|
||||
|
||||
|
||||
MENU_COMMAND_DEVELOPMENT(usb, usb_cmd_main, "run a USB command", NULL);
|
||||
|
||||
static void usb_print_usage(const char * s)
|
||||
{
|
||||
printf("Usage: \n");
|
||||
printf("\t%s get <filename> [address] [amount] \n", s);
|
||||
printf( "\t%s get tftp://servername.com/file.bin [address] [amount] \n", s );
|
||||
#if WITH_BULK_UPLOAD
|
||||
printf( "\t%s put <filename> <amount> [address] \n", s );
|
||||
#endif // WITH_BULK_UPLOAD
|
||||
}
|
||||
|
||||
#if WITH_BULK_UPLOAD
|
||||
|
||||
static int usb_cmd_put(const char *fileName, uint64_t amount, uintptr_t address, int printCompletionStatus)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if(!fileName) {
|
||||
printf("File Name is NULL!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(!amount) {
|
||||
printf("Amount is 0!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(usb_serial_is_active() == false) {
|
||||
printf("PUT failed, run usbterm\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *prefix = "put ";
|
||||
int notifyHostLen = strlen(prefix) + strlen(fileName) +
|
||||
USB_SERIAL_CMD_START_OFFSET + USB_SERIAL_CMD_END_OFFSET;
|
||||
|
||||
if(notifyHostLen > USB_CMD_STRING_LEN) {
|
||||
printf("File length too long \n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(address == 0) {
|
||||
address = env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
|
||||
printf("Defaulting to address %p\n", (void *)address);
|
||||
}
|
||||
|
||||
if(!security_allow_memory((void *)address, amount)) {
|
||||
dprintf(DEBUG_INFO, "Permission Denied\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
usb_transfer_prepare(USB_TRANSFER_TYPE_BULK_PUT, (void *)address, amount);
|
||||
|
||||
bzero(usb_cmd_string, USB_CMD_STRING_LEN);
|
||||
usb_cmd_string[0] = USB_SERIAL_CMD_START;
|
||||
memcpy(usb_cmd_string + USB_SERIAL_CMD_START_OFFSET, prefix, strlen(prefix));
|
||||
memcpy(usb_cmd_string + USB_SERIAL_CMD_START_OFFSET + strlen(prefix), fileName,
|
||||
notifyHostLen - USB_SERIAL_CMD_START_OFFSET - USB_SERIAL_CMD_END_OFFSET);
|
||||
usb_cmd_string[notifyHostLen - 1] = USB_SERIAL_CMD_END;
|
||||
|
||||
ret = usb_serial_send_cmd_string(usb_cmd_string, notifyHostLen);
|
||||
if(ret != 0)
|
||||
goto error;
|
||||
|
||||
ret = usb_transfer_start();
|
||||
|
||||
if(ret <= 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
amount = ret;
|
||||
|
||||
if (printCompletionStatus)
|
||||
printf("PUT complete, amount %llu\n", amount);
|
||||
|
||||
// set the file size and return code
|
||||
env_set_uint("filesize", amount, 0 );
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
printf("PUT failed. \n");
|
||||
env_set_uint("filesize", 0, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_send_data_to_file(const char *filename, size_t amount, uintptr_t address, int printCompletionStatus)
|
||||
{
|
||||
return usb_cmd_put(filename, amount, address, printCompletionStatus);
|
||||
}
|
||||
|
||||
#endif // WITH_BULK_UPLOAD
|
||||
|
||||
static int usb_cmd_get(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if(usb_core_get_cable_state() == CABLE_DISCONNECTED) {
|
||||
printf("GET failed, cable missing\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if(usb_serial_is_active() == false) {
|
||||
printf("GET failed, run usbterm\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *prefix = "get ";
|
||||
const char * fileName = args[0].str;
|
||||
int notifyHostLen = strlen(prefix) + strlen(fileName) +
|
||||
USB_SERIAL_CMD_START_OFFSET + USB_SERIAL_CMD_END_OFFSET;
|
||||
|
||||
addr_t address = 0;
|
||||
uint64_t amount = 0;
|
||||
|
||||
if(notifyHostLen > USB_CMD_STRING_LEN) {
|
||||
printf("File length too long \n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(argc >= 2) {
|
||||
address = args[1].u;
|
||||
if(argc >= 3) {
|
||||
amount = args[2].u;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 0) {
|
||||
address = env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
|
||||
printf("Defaulting to address %p\n", (void *)address);
|
||||
}
|
||||
|
||||
if(amount > DEFAULT_RAMDISK_SIZE) {
|
||||
printf("Amount too large \n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(amount == 0) {
|
||||
amount = DEFAULT_RAMDISK_SIZE;
|
||||
}
|
||||
|
||||
if(!security_allow_memory((void *)address, amount)) {
|
||||
dprintf(DEBUG_INFO, "Permission Denied\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
usb_transfer_prepare(USB_TRANSFER_TYPE_BULK_GET, (void *)address, amount);
|
||||
bzero((void *)usb_cmd_string, USB_CMD_STRING_LEN);
|
||||
usb_cmd_string[0] = USB_SERIAL_CMD_START;
|
||||
memcpy(usb_cmd_string + USB_SERIAL_CMD_START_OFFSET, prefix, strlen(prefix));
|
||||
memcpy(usb_cmd_string + USB_SERIAL_CMD_START_OFFSET + strlen(prefix), fileName,
|
||||
notifyHostLen - USB_SERIAL_CMD_START_OFFSET - USB_SERIAL_CMD_END_OFFSET);
|
||||
usb_cmd_string[notifyHostLen - 1] = USB_SERIAL_CMD_END;
|
||||
|
||||
ret = usb_serial_send_cmd_string(usb_cmd_string, notifyHostLen);
|
||||
|
||||
if(ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = usb_transfer_start();
|
||||
|
||||
if(ret <= 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
amount = ret;
|
||||
|
||||
printf("GET complete, amount %llu\n", amount);
|
||||
|
||||
// set the file size and return code
|
||||
env_set_uint( "filesize", amount, 0 );
|
||||
|
||||
ret = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
printf("GET failed. \n");
|
||||
env_set_uint("filesize", 0, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int usb_cmd_main(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *s = args[1].str;
|
||||
|
||||
if(!s) {
|
||||
ret = -1;
|
||||
}
|
||||
else {
|
||||
if(!strcmp(s, "get")) {
|
||||
|
||||
if((argc < 3) || (argc > 5)) {
|
||||
goto print_usage;
|
||||
}
|
||||
|
||||
ret = usb_cmd_get(argc - 2, args + 2);
|
||||
}
|
||||
#if WITH_BULK_UPLOAD
|
||||
else if(!strcmp(s, "put")) {
|
||||
|
||||
if((argc < 4) || (argc > 5)) {
|
||||
goto print_usage;
|
||||
}
|
||||
|
||||
ret = usb_cmd_put(args[2].str, args[3].u, (argc == 5) ? args[4].u : 0, 1);
|
||||
}
|
||||
#endif // WITH_BULK_UPLOAD
|
||||
else {
|
||||
goto print_usage;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
print_usage:
|
||||
usb_print_usage( args[0].str );
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2012 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// USB DFU
|
||||
//
|
||||
// This file implements usb dfu protocol (just download). This is used
|
||||
// in SecureROM.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <debug.h>
|
||||
#include <sys/task.h>
|
||||
#include <sys/security.h>
|
||||
#include <drivers/usb/usb_chap9.h>
|
||||
#include <drivers/usb/usb_public.h>
|
||||
#include <drivers/usb/usb_core.h>
|
||||
#include <platform.h>
|
||||
|
||||
#define DEBUG 0
|
||||
#if DEBUG
|
||||
#define print(fmt, args...) printf("%s --- " fmt, __FUNCTION__, ##args)
|
||||
#else
|
||||
#define print(fmt, args...) (void)0
|
||||
#endif
|
||||
|
||||
|
||||
struct usb_dfu_run_time_descriptor {
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t bmAttributes;
|
||||
uint16_t wDetachTimeOut;
|
||||
uint16_t wTransferSize;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct usb_dfu_status_request
|
||||
{
|
||||
UInt8 bStatus;
|
||||
UInt8 bwPollTimeout[3];
|
||||
UInt8 bState;
|
||||
UInt8 iString;
|
||||
} __attribute__((packed));
|
||||
|
||||
enum {
|
||||
DFU_DETACH = 0,
|
||||
DFU_DNLOAD,
|
||||
DFU_UPLOAD,
|
||||
DFU_GETSTATUS,
|
||||
DFU_CLR_STATUS,
|
||||
DFU_GETSTATE,
|
||||
DFU_ABORT
|
||||
};
|
||||
|
||||
enum {
|
||||
appIDLE,
|
||||
appDETACH,
|
||||
dfuIDLE,
|
||||
dfuDNLOAD_SYNC,
|
||||
dfuDNBUSY,
|
||||
dfuDNLOAD_IDLE,
|
||||
dfuMANIFEST_SYNC,
|
||||
dfuMANIFEST,
|
||||
dfuMANIFEST_WAIT_RESET,
|
||||
dfuUPLOAD_IDLE,
|
||||
dfuERROR
|
||||
};
|
||||
|
||||
enum {
|
||||
errOK = 0,
|
||||
errADDRESS = 0x08,
|
||||
errNOTDONE = 0x09,
|
||||
errSTALLEDPKT = 0x0f
|
||||
};
|
||||
|
||||
#define DD_DFU (0x21)
|
||||
#define USB_DFU_RUN_TIME_DT_SIZE (7)
|
||||
#define DFU_MAX_TRANSFER_SIZE (2 * 1024)
|
||||
#define DETACH_TIMEOUT_MS (10)
|
||||
#define DFU_FILE_SUFFIX_LENGTH (16)
|
||||
|
||||
//the polling delay we generally report in GETSTATUS requests
|
||||
#define STANDARD_DELAY_MS 50
|
||||
|
||||
//the delay we report after completing the file download. I don't think it needs to be different
|
||||
//from the standard delay but this is what m68 h1 rom does.
|
||||
#define DL_DONE_DELAY_MS 3000
|
||||
|
||||
#define DFU_BMATTRIBUTES (1 << 0) // download only
|
||||
|
||||
struct usb_dfu_io_buffer {
|
||||
u_int8_t bytes[DFU_MAX_TRANSFER_SIZE];
|
||||
};
|
||||
|
||||
static const struct usb_interface_descriptor usb_dfu_interface_descriptor = {
|
||||
USB_DT_INTERFACE_SIZE,
|
||||
USB_DT_INTERFACE,
|
||||
APPLE_USB_DFU_INTF,
|
||||
0,
|
||||
0,
|
||||
APPLE_USB_DFU_CLASS,
|
||||
APPLE_USB_DFU_SUBCLASS,
|
||||
APPLE_USB_DFU_PROTOCOL,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct usb_dfu_run_time_descriptor usb_dfu_run_time_desc = {
|
||||
USB_DFU_RUN_TIME_DT_SIZE,
|
||||
DD_DFU,
|
||||
DFU_BMATTRIBUTES,
|
||||
DETACH_TIMEOUT_MS,
|
||||
DFU_MAX_TRANSFER_SIZE
|
||||
};
|
||||
|
||||
static bool usb_dfu_inited;
|
||||
static struct usb_interface_instance usb_dfu_interface_instance;
|
||||
static struct usb_dfu_status_request status_req;
|
||||
static struct task_event dfu_event;
|
||||
static volatile uint8_t dfu_state;
|
||||
static volatile bool dfu_done;
|
||||
static struct usb_dfu_io_buffer *io_buffer;
|
||||
static int completion_status;
|
||||
static u_int8_t *image_buffer;
|
||||
static u_int32_t image_buffer_size;
|
||||
static u_int32_t total_received;
|
||||
static u_int32_t expecting;
|
||||
|
||||
static void set_status(u_int8_t bStatus, int bwPollTimeout, u_int8_t bState, u_int8_t iString);
|
||||
static int handle_interface_request(struct usb_device_request *request, uint8_t **out_buffer);
|
||||
static int handle_dfu_request(struct usb_device_request *setup, uint8_t **buffer);
|
||||
static void handle_bus_reset(void);
|
||||
static void data_received(u_int32_t received);
|
||||
|
||||
|
||||
int getDFUImage(void* buffer, int maxlen)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Trying to get a file with DFU.\n" );
|
||||
|
||||
// save the info provided by our caller
|
||||
image_buffer = buffer;
|
||||
image_buffer_size = maxlen;
|
||||
|
||||
print( "Initializing USB...\n" );
|
||||
#if WITH_PLATFORM_INIT_USB
|
||||
if(platform_init_usb() != 0)
|
||||
goto exit;
|
||||
#else
|
||||
if(usb_init() != 0)
|
||||
goto exit;
|
||||
#endif
|
||||
|
||||
// wait here till dfu download is finished
|
||||
while(!dfu_done) event_wait(&dfu_event);
|
||||
dprintf(DEBUG_INFO, "DFU complete, status=%d\n", completion_status);
|
||||
|
||||
exit:
|
||||
print( "Exiting USB...\n" );
|
||||
usb_quiesce();
|
||||
|
||||
return completion_status;
|
||||
}
|
||||
|
||||
int usb_dfu_init()
|
||||
{
|
||||
if(usb_dfu_inited) return 0;
|
||||
|
||||
io_buffer = memalign(sizeof(*io_buffer), CPU_CACHELINE_SIZE);
|
||||
bzero((void *)io_buffer, sizeof(*io_buffer));
|
||||
|
||||
set_status(errOK, STANDARD_DELAY_MS, dfuIDLE, 0);
|
||||
|
||||
completion_status = -1;
|
||||
total_received = 0;
|
||||
dfu_done = false;
|
||||
|
||||
event_init(&dfu_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
usb_dfu_interface_instance.total_interfaces = APPLE_USB_DFU_TOTAL_INTFS;
|
||||
usb_dfu_interface_instance.interface_descs = (struct usb_interface_descriptor *)&usb_dfu_interface_descriptor;
|
||||
usb_dfu_interface_instance.total_other_descs = 1;
|
||||
usb_dfu_interface_instance.other_descs = (void *)&usb_dfu_run_time_desc;
|
||||
usb_dfu_interface_instance.handle_request = handle_interface_request;
|
||||
usb_dfu_interface_instance.non_setup_data_phase_finished_callback = data_received;
|
||||
usb_dfu_interface_instance.handle_bus_reset = handle_bus_reset;
|
||||
|
||||
usb_core_register_interface(&usb_dfu_interface_instance);
|
||||
|
||||
usb_dfu_inited = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_dfu_exit()
|
||||
{
|
||||
if(!usb_dfu_inited) return;
|
||||
|
||||
bzero((void*)&usb_dfu_interface_instance, sizeof(struct usb_interface_instance));
|
||||
|
||||
if(io_buffer) {
|
||||
free(io_buffer);
|
||||
io_buffer = NULL;
|
||||
}
|
||||
|
||||
usb_dfu_inited = false;
|
||||
}
|
||||
|
||||
|
||||
static void set_status(u_int8_t bStatus, int bwPollTimeout, u_int8_t bState, u_int8_t iString)
|
||||
{
|
||||
status_req.bStatus = bStatus;
|
||||
status_req.bwPollTimeout[0]= bwPollTimeout & 0xff;
|
||||
status_req.bwPollTimeout[1]= (bwPollTimeout >> 8) & 0xff;
|
||||
status_req.bwPollTimeout[2]= (bwPollTimeout >> 16) & 0xff;
|
||||
dfu_state = status_req.bState = bState;
|
||||
status_req.iString = iString;
|
||||
}
|
||||
|
||||
static int handle_interface_request(struct usb_device_request *request, uint8_t **out_buffer)
|
||||
{
|
||||
u_int8_t bRequest = request->bRequest;
|
||||
u_int16_t wLength = request->wLength;
|
||||
int ret;
|
||||
|
||||
print("type: %d\n", (request->bmRequestType & USB_REQ_DIRECTION_MASK));
|
||||
print("request=%02x wValue=%04x index=%d length=%d\n", bRequest,
|
||||
request->wValue, request->wIndex, wLength);
|
||||
|
||||
ret = -1;
|
||||
|
||||
// DFU_GETSTATE and DFU_GETSTATUS request are DEVICE2HOST
|
||||
// request. Response for these requests are copied into
|
||||
// io_buffer.
|
||||
// DFU_DNLOAD request is HOST2DEVICE type request. We pass our
|
||||
// io_buffer to the stack to copy the data from the host.
|
||||
//
|
||||
// io_buffer is gauranteed to be larger than any data needs to
|
||||
// be copied into it.
|
||||
if((request->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE)
|
||||
{
|
||||
if(out_buffer == NULL)
|
||||
return -1;
|
||||
|
||||
switch(bRequest)
|
||||
{
|
||||
case DFU_DNLOAD:
|
||||
{
|
||||
if(wLength == 0) {
|
||||
print("File transfer complete. Waiting for status sync.\n");
|
||||
set_status(errOK, STANDARD_DELAY_MS, dfuMANIFEST_SYNC, 0);
|
||||
}
|
||||
else {
|
||||
if(wLength > sizeof(*io_buffer)) {
|
||||
// cleanest way to recover is to stall here
|
||||
set_status(errSTALLEDPKT, STANDARD_DELAY_MS, dfuIDLE, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_buffer = (uint8_t *)io_buffer;
|
||||
}
|
||||
expecting = wLength;
|
||||
ret = wLength;
|
||||
break;
|
||||
}
|
||||
case DFU_CLR_STATUS :
|
||||
case DFU_ABORT:
|
||||
{
|
||||
set_status(errOK, STANDARD_DELAY_MS, dfuIDLE, 0);
|
||||
|
||||
total_received = 0;
|
||||
|
||||
if(!dfu_done) {
|
||||
completion_status = -1;
|
||||
dfu_done = true;
|
||||
event_signal(&dfu_event);
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
else if((request->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_DEVICE2HOST)
|
||||
{
|
||||
switch(bRequest) {
|
||||
|
||||
case DFU_GETSTATE:
|
||||
print("Returning state = %d\n", dfu_state);
|
||||
*(u_int8_t *)io_buffer = dfu_state;
|
||||
ret = 1;
|
||||
break;
|
||||
|
||||
case DFU_GETSTATUS:
|
||||
memcpy((u_int8_t *)io_buffer, (void *)&status_req, sizeof(struct usb_dfu_status_request));
|
||||
|
||||
if(dfu_state == dfuMANIFEST_SYNC) {
|
||||
set_status(errOK, DL_DONE_DELAY_MS, dfuMANIFEST, 0);
|
||||
}
|
||||
else if(dfu_state == dfuMANIFEST) {
|
||||
// XXX TODO: validate DFU File Suffix? For now erase them
|
||||
RELEASE_ASSERT(total_received >= DFU_FILE_SUFFIX_LENGTH);
|
||||
RELEASE_ASSERT(image_buffer != NULL);
|
||||
completion_status = total_received - DFU_FILE_SUFFIX_LENGTH;
|
||||
bzero((void *)&image_buffer[completion_status], DFU_FILE_SUFFIX_LENGTH);
|
||||
total_received = 0;
|
||||
|
||||
// transitioning to this state will break us out of the file wait loop
|
||||
set_status(errOK, STANDARD_DELAY_MS, dfuMANIFEST_WAIT_RESET, 0);
|
||||
}
|
||||
|
||||
ret = sizeof(struct usb_dfu_status_request);
|
||||
break;
|
||||
}
|
||||
|
||||
if(ret < 0)
|
||||
return -1;
|
||||
|
||||
usb_core_do_transfer(EP0_IN, (u_int8_t *)io_buffer, ret, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void handle_bus_reset(void)
|
||||
{
|
||||
if(dfu_state == dfuMANIFEST_WAIT_RESET) {
|
||||
dfu_done = true;
|
||||
event_signal(&dfu_event);
|
||||
}
|
||||
}
|
||||
|
||||
static void data_received(u_int32_t received)
|
||||
{
|
||||
print("DFU received %d of %d\n", received, expecting);
|
||||
|
||||
if(received != expecting) {
|
||||
set_status(errNOTDONE, STANDARD_DELAY_MS, dfuERROR, 0);
|
||||
}
|
||||
else if((total_received + received) > image_buffer_size) {
|
||||
set_status(errADDRESS, STANDARD_DELAY_MS, dfuERROR, 0);
|
||||
}
|
||||
else {
|
||||
if(!security_allow_memory(&image_buffer[total_received], received)) {
|
||||
dprintf(DEBUG_INFO, "Permission Denied\n");
|
||||
completion_status = -1;
|
||||
dfu_done = true;
|
||||
event_signal(&dfu_event);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&image_buffer[total_received], (void *)io_buffer, received);
|
||||
total_received += received;
|
||||
expecting = 0;
|
||||
set_status(errOK, STANDARD_DELAY_MS, dfuDNLOAD_IDLE, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,904 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <drivers/usb/usb_core.h>
|
||||
#include <drivers/dockfifo/dockfifo.h>
|
||||
#include <drivers/usb/usb_controller.h>
|
||||
#include <sys/task.h>
|
||||
#include <endian_tools.h>
|
||||
#include <platform.h>
|
||||
|
||||
// #undef DEBUG_LEVEL
|
||||
// #define DEBUG_LEVEL DEBUG_SPEW
|
||||
|
||||
/*
|
||||
* 3 bytes header:
|
||||
* 1byte: endpoint and flags
|
||||
* bit 7 is direction:
|
||||
* 0: Host to Device (OUT in USB terms)
|
||||
* 1: Device to Host (IN in USB terms)
|
||||
* bit 6 is control packet phase:
|
||||
* 0: data or N/A
|
||||
* 1: setup
|
||||
* bit 5 is fragment continuation:
|
||||
* 0: last or only packet in fragment
|
||||
* 1: more packets follow, continuing data
|
||||
* ep 31: reserved for link control management
|
||||
* ep 0-30: available
|
||||
*
|
||||
* Controller management requests: 1 byte + data
|
||||
* 1byte: command
|
||||
* nbytes: data
|
||||
*/
|
||||
|
||||
#define EP_DIR_OUT (0 << 7)
|
||||
#define EP_DIR_IN (1 << 7)
|
||||
#define EP_DIR_MASK (1 << 7)
|
||||
|
||||
#define EP_PHASE_DATA (0 << 6)
|
||||
#define EP_PHASE_SETUP (1 << 6)
|
||||
#define EP_PHASE_MASK (1 << 6)
|
||||
|
||||
#define EP_FRAG_LAST (0 << 5)
|
||||
#define EP_FRAG_MORE (1 << 5)
|
||||
#define EP_FRAG_MASK (1 << 5)
|
||||
|
||||
#define EP_NUM_MASK 0x1f
|
||||
#define EP_LINK_CONTROL 0x1f
|
||||
|
||||
#define MAX_EP_NUMBER 5
|
||||
#define MAX_EP_NEEDED (MAX_EP_NUMBER * 2 + 2) // 2*EP and EP0 IN/OUT
|
||||
|
||||
#define ep_to_epnum(ep) ((ep) & EP_NUM_MASK)
|
||||
#define ep_to_epdir(ep) ((ep) & EP_DIR_MASK)
|
||||
#define ep_to_epindex(ep) ((2 * ep_to_epnum((ep))) + (ep_to_epdir((ep)) ? 0 : 1))
|
||||
|
||||
enum {
|
||||
// Host to Device
|
||||
kCommand_LinkReady = 0x01, // 4 bytes: nonce
|
||||
kCommand_HostConnect = 0x02, // no data
|
||||
kCommand_HostDisconnect = 0x03, // no data
|
||||
kCommand_DeviceBusReset = 0x04, // no data
|
||||
kCommand_DeviceConnectionSpeed = 0x05, // 1 byte: FS or HS
|
||||
|
||||
// Device to Host
|
||||
kCommand_LinkReadyAck = 0x81, // 4 bytes: repeat nonce from LinkReady command
|
||||
kCommand_DeviceConnect = 0x82, // no data
|
||||
kCommand_DeviceDisconnect = 0x83, // no data
|
||||
kCommand_ConfigureEndpoint = 0x84, // 2 bytes: ep# (msb is direction), XXX config data to be defined
|
||||
kCommand_EnableEndpoint = 0x85, // 2 byte: ep#, enable (1) or disable (0)
|
||||
kCommand_StallEndpoint = 0x86, // 2 byte: ep#, set (1) or clear (0) stall
|
||||
kCommand_Error = 0xff // 1+ bytes: code, data
|
||||
};
|
||||
|
||||
enum {
|
||||
kError_OK = 0x00, // No error
|
||||
kError_LinkNotReady = 0x01, // Need LinkReady first
|
||||
};
|
||||
|
||||
#define MAX_PAYLOAD_SIZE (512)
|
||||
|
||||
enum {
|
||||
kEndpointState_Free = 0,
|
||||
kEndpointState_Allocated_Idle = (1 << 0),
|
||||
kEndpointState_Allocated_Busy = (1 << 1),
|
||||
kEndpointState_Allocated_AbortRequested = (1 << 2),
|
||||
kEndpointState_Allocated_Stalled = (1 << 3),
|
||||
};
|
||||
|
||||
struct usb_endpoint_instance
|
||||
{
|
||||
uint32_t endpoint_address;
|
||||
uint32_t max_packet_size;
|
||||
uint32_t attributes;
|
||||
uint32_t bInterval;
|
||||
|
||||
uint32_t state;
|
||||
struct task_event idle_state_event;
|
||||
uint32_t remote_available;
|
||||
|
||||
struct usb_device_io_request *io_request;
|
||||
};
|
||||
|
||||
static void usb_dockfifo_stall_endpoint(uint32_t endpoint, bool stall);
|
||||
|
||||
static bool usb_dockfifo_link_ready;
|
||||
static uint8_t usb_connection_speed;
|
||||
static bool usb_ep0_data_phase_expected;
|
||||
static struct usb_endpoint_instance usb_dockfifo_endpoints[MAX_EP_NEEDED];
|
||||
static struct task *usb_dockfifo_main_task_handle;
|
||||
static struct task *usb_dockfifo_writer_task_handle;
|
||||
static struct task_event usb_dockfifo_writer_task_event;
|
||||
static uint8_t usb_dockfifo_read_buffer_data[MAX_PAYLOAD_SIZE + 1];
|
||||
static size_t usb_dockfifo_read_buffer_bytes;
|
||||
|
||||
//=======================
|
||||
// Local funtions
|
||||
//=======================
|
||||
|
||||
static void dump_bytes(uint8_t *buffer, size_t size)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
dprintf(DEBUG_INFO, "===\n");
|
||||
for(i = 0; i < (int32_t)size; i++) {
|
||||
dprintf(DEBUG_INFO, "%02x ", buffer[i]);
|
||||
}
|
||||
dprintf(DEBUG_INFO, "\n===\n");
|
||||
}
|
||||
|
||||
static int32_t usb_dockfifo_read_data()
|
||||
{
|
||||
static int frame_count = 0;
|
||||
int32_t ret = dockfifo_bulk_read_frame(
|
||||
usb_dockfifo_read_buffer_data,
|
||||
sizeof(usb_dockfifo_read_buffer_data),
|
||||
&usb_dockfifo_read_buffer_bytes);
|
||||
if ((ret != 0) || (usb_dockfifo_read_buffer_bytes < 1)) {
|
||||
dprintf(DEBUG_INFO, "failed to read frame: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dprintf(DEBUG_SPEW, "Receive frame %d\n", ++frame_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t usb_dockfifo_send_bytes(const uint8_t *data, size_t size)
|
||||
{
|
||||
static int frame_count = 0;
|
||||
dprintf(DEBUG_SPEW, "Send frame %d\n", ++frame_count);
|
||||
int32_t ret = dockfifo_bulk_write_frame(data, size);
|
||||
if (ret != 0) {
|
||||
dprintf(DEBUG_INFO, "failed to write frame: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_dockfifo_go_on_bus(bool on)
|
||||
{
|
||||
uint8_t data[2];
|
||||
|
||||
data[0] = EP_LINK_CONTROL | EP_DIR_IN;
|
||||
if (on)
|
||||
data[1] = kCommand_DeviceConnect;
|
||||
else
|
||||
data[1] = kCommand_DeviceDisconnect;
|
||||
usb_dockfifo_send_bytes(data, sizeof(data));
|
||||
}
|
||||
|
||||
static void usb_dockfifo_send_error(uint8_t error_code)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Responding with error 0x%02x\n", error_code);
|
||||
|
||||
uint8_t data[3] = {
|
||||
EP_LINK_CONTROL | EP_DIR_IN,
|
||||
kCommand_Error,
|
||||
error_code
|
||||
};
|
||||
usb_dockfifo_send_bytes(data, sizeof(data));
|
||||
}
|
||||
|
||||
static void usb_dockfifo_send_available(uint8_t epnum)
|
||||
{
|
||||
dprintf(DEBUG_SPEW, "ep%02xOUT available\n", epnum);
|
||||
|
||||
uint8_t data[3] = {
|
||||
epnum | EP_DIR_OUT,
|
||||
MAX_PAYLOAD_SIZE & 0xff,
|
||||
(MAX_PAYLOAD_SIZE >> 8) & 0xff
|
||||
};
|
||||
usb_dockfifo_send_bytes(data, sizeof(data));
|
||||
}
|
||||
|
||||
static void usb_dockfifo_handle_ready(void)
|
||||
{
|
||||
dprintf(DEBUG_SPEW, "Got READY packet. Replying.\n");
|
||||
|
||||
// Send ReadyAck.
|
||||
uint8_t data[8];
|
||||
data[0] = EP_LINK_CONTROL | EP_DIR_IN;
|
||||
data[1] = kCommand_LinkReadyAck;
|
||||
// Ping back with the same nonce value we received.
|
||||
memcpy(data + 2, usb_dockfifo_read_buffer_data + 2, 4);
|
||||
// Inform the host about the max packet size (not including
|
||||
// endpoint byte) we can receive.
|
||||
ASSERT(DOCKFIFO_BULK_MRU >= MAX_PAYLOAD_SIZE + 1);
|
||||
write_le_16(data, 6, MAX_PAYLOAD_SIZE);
|
||||
|
||||
usb_dockfifo_send_bytes(data, sizeof(data));
|
||||
|
||||
// Accept other packets.
|
||||
usb_dockfifo_link_ready = true;
|
||||
}
|
||||
|
||||
static void usb_dockfifo_handle_link_control(void)
|
||||
{
|
||||
// Handle commands from the host.
|
||||
if (usb_dockfifo_read_buffer_bytes < 2) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"Expected LinkControl bytes >= 2, got %d\n",
|
||||
(int) usb_dockfifo_read_buffer_bytes);
|
||||
return;
|
||||
}
|
||||
uint8_t command = usb_dockfifo_read_buffer_data[1];
|
||||
if ((!usb_dockfifo_link_ready) && (command != kCommand_LinkReady)) {
|
||||
dprintf(DEBUG_INFO, "Require Ready command first, got 0x%02x\n",
|
||||
command);
|
||||
// Tell host it needs to reset its session.
|
||||
usb_dockfifo_send_error(kError_LinkNotReady);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case kCommand_LinkReady:
|
||||
dprintf(DEBUG_INFO, "Got Ready command\n");
|
||||
usb_dockfifo_handle_ready();
|
||||
break;
|
||||
|
||||
case kCommand_HostConnect:
|
||||
{
|
||||
dprintf(DEBUG_INFO, "Got HostConnected command\n");
|
||||
usb_core_event_handler(CABLE_CONNECTED);
|
||||
usb_dockfifo_go_on_bus(true);
|
||||
break;
|
||||
}
|
||||
|
||||
case kCommand_DeviceBusReset:
|
||||
dprintf(DEBUG_INFO, "Got DeviceBusReset command\n");
|
||||
usb_core_event_handler(USB_RESET);
|
||||
break;
|
||||
|
||||
case kCommand_DeviceConnectionSpeed:
|
||||
dprintf(DEBUG_INFO, "Got DeviceConnectionSpeed command\n");
|
||||
if (usb_dockfifo_read_buffer_bytes < 3) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"Expected 3 bytes for DeviceConnectionSpeed\n");
|
||||
break;
|
||||
}
|
||||
usb_connection_speed = usb_dockfifo_read_buffer_data[2] ?
|
||||
CONNECTION_SPEED_HIGH : CONNECTION_SPEED_FULL;
|
||||
usb_core_event_handler(USB_ENUM_DONE);
|
||||
break;
|
||||
|
||||
case kCommand_HostDisconnect:
|
||||
dprintf(DEBUG_INFO, "Got HostDisconnected command\n");
|
||||
usb_core_event_handler(CABLE_DISCONNECTED);
|
||||
usb_dockfifo_go_on_bus(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintf(DEBUG_INFO, "Unknown command 0x%02x\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_dockfifo_handle_endpoint_out(uint32_t endpoint, const uint8_t *buffer, size_t bytes)
|
||||
{
|
||||
dprintf(DEBUG_SPEW, "handle epout %02x %d\n", endpoint, (int) bytes);
|
||||
// Get endpoint.
|
||||
size_t epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
uint32_t ep_num = ep_to_epnum(endpoint);
|
||||
ASSERT((ep_num <= EP_NUM_MASK) && (ep_num != EP_LINK_CONTROL));
|
||||
struct usb_endpoint_instance *ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
if ((ep->io_request == NULL) || (ep->state & kEndpointState_Allocated_AbortRequested)) {
|
||||
dprintf(DEBUG_INFO, "Dropping unexpected data received for endpoint %x, state:%d io_request:%p\n", endpoint, ep->state, ep->io_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get IO request, transfer size.
|
||||
struct usb_device_io_request *req = ep->io_request;
|
||||
ASSERT(req != NULL);
|
||||
uint32_t xfer_size = req->io_length - req->return_count;
|
||||
|
||||
// Make sure we have space left to copy this data
|
||||
RELEASE_ASSERT(bytes <= xfer_size);
|
||||
|
||||
// Save data received
|
||||
memcpy((void *) req->io_buffer + req->return_count, buffer, bytes);
|
||||
|
||||
// Update IO request.
|
||||
req->return_count += bytes;
|
||||
|
||||
dprintf(DEBUG_SPEW, "ep%02xout now %d/%d\n",
|
||||
ep_num, (int) req->return_count, (int) req->io_length);
|
||||
|
||||
if ((bytes == ep->max_packet_size) &&
|
||||
(req->return_count < req->io_length) &&
|
||||
((ep->state & kEndpointState_Allocated_AbortRequested) == 0)) {
|
||||
// More data to be received
|
||||
dprintf(DEBUG_SPEW, "More to be received\n");
|
||||
usb_dockfifo_send_available(ep_to_epnum(req->endpoint));
|
||||
return;
|
||||
} else {
|
||||
// Completed transaction, update status and return
|
||||
dprintf(DEBUG_SPEW, "Completed a transaction\n");
|
||||
ep->io_request = NULL;
|
||||
uint32_t old_state = ep->state;
|
||||
ep->state = kEndpointState_Allocated_Idle;
|
||||
event_signal(&ep->idle_state_event);
|
||||
if (!(old_state & kEndpointState_Allocated_AbortRequested)) {
|
||||
req->status = USB_IO_SUCCESS;
|
||||
usb_core_complete_endpoint_io(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_dockfifo_start_endpoint_in(uint32_t endpoint)
|
||||
{
|
||||
static uint8_t buf[MAX_PAYLOAD_SIZE + 1];
|
||||
|
||||
// Get endpoint.
|
||||
size_t epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
uint32_t ep_num = ep_to_epnum(endpoint);
|
||||
ASSERT((ep_num <= EP_NUM_MASK) && (ep_num != EP_LINK_CONTROL));
|
||||
struct usb_endpoint_instance *ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
// Get IO request, transfer size.
|
||||
struct usb_device_io_request *req = ep->io_request;
|
||||
ASSERT(req != NULL);
|
||||
uint32_t xfer_size = req->io_length - req->return_count;
|
||||
|
||||
// Fragment the request if it exceeds max buffer size.
|
||||
bool fragment = false;
|
||||
if (xfer_size > MAX_PAYLOAD_SIZE) {
|
||||
xfer_size = MAX_PAYLOAD_SIZE;
|
||||
fragment = true;
|
||||
}
|
||||
|
||||
// Should be some remote buffer space available before this function
|
||||
// was invoked.
|
||||
ASSERT(ep->remote_available > 0);
|
||||
if (xfer_size > ep->remote_available) {
|
||||
xfer_size = ep->remote_available;
|
||||
fragment = true;
|
||||
}
|
||||
ep->remote_available = 0;
|
||||
|
||||
// Encode the endpoint and transfer flags.
|
||||
uint8_t encoded_ep = ep_num | EP_DIR_IN;
|
||||
if (fragment) {
|
||||
encoded_ep |= EP_FRAG_MORE;
|
||||
}
|
||||
|
||||
dprintf(DEBUG_SPEW, "ep_num:0x%02x sent:%d io_len:%d xfer:%d frag:%d\n",
|
||||
ep_num,
|
||||
(int) req->return_count,
|
||||
(int) req->io_length,
|
||||
(int) xfer_size,
|
||||
(int) fragment);
|
||||
|
||||
// Send encoded endpoint|flags, followed by payload.
|
||||
buf[0] = encoded_ep;
|
||||
memcpy(buf + 1, (void *) req->io_buffer + req->return_count, xfer_size);
|
||||
usb_dockfifo_send_bytes(buf, xfer_size + 1);
|
||||
dprintf(DEBUG_SPEW, "Sent\n");
|
||||
|
||||
// Update IO request.
|
||||
req->return_count += xfer_size;
|
||||
|
||||
if (fragment && ((ep->state & kEndpointState_Allocated_AbortRequested) == 0)) {
|
||||
// More data needs transmitting for this packet.
|
||||
event_signal(&usb_dockfifo_writer_task_event);
|
||||
} else {
|
||||
// Completed this packet, update status and return
|
||||
uint32_t old_state = ep->state;
|
||||
ep->state = kEndpointState_Allocated_Idle;
|
||||
event_signal(&ep->idle_state_event);
|
||||
if (!(old_state & kEndpointState_Allocated_AbortRequested)) {
|
||||
req->status = USB_IO_SUCCESS;
|
||||
ep->io_request = NULL;
|
||||
usb_core_complete_endpoint_io(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool usb_dockfifo_handle_ep0_data(uint8_t *buf, size_t bytes,
|
||||
bool *data_phase)
|
||||
{
|
||||
// Fragment into EP0_MAX_PACKET_SIZE chunks.
|
||||
while (bytes > 0) {
|
||||
size_t to_receive = EP0_MAX_PACKET_SIZE;
|
||||
if (to_receive > bytes) {
|
||||
to_receive = bytes;
|
||||
}
|
||||
usb_core_handle_usb_control_receive(buf, false, to_receive,
|
||||
data_phase);
|
||||
buf += to_receive;
|
||||
bytes -= to_receive;
|
||||
if (bytes > 0 && !*data_phase) {
|
||||
dprintf(DEBUG_INFO, "Began setup phase in fragment\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool usb_dockfifo_dispatch_ep_out(void)
|
||||
{
|
||||
ASSERT(usb_dockfifo_read_buffer_bytes >= 1);
|
||||
|
||||
uint8_t endpoint = usb_dockfifo_read_buffer_data[0];
|
||||
ASSERT((endpoint & EP_DIR_MASK) == EP_DIR_OUT);
|
||||
|
||||
switch (endpoint & ~EP_FRAG_MASK) {
|
||||
case EP_LINK_CONTROL | EP_DIR_OUT:
|
||||
usb_dockfifo_handle_link_control();
|
||||
return true;
|
||||
|
||||
case 0 | EP_PHASE_SETUP | EP_DIR_OUT:
|
||||
dprintf(DEBUG_SPEW, "Setup packet received\n");
|
||||
if (usb_dockfifo_read_buffer_bytes < 1 + SETUP_PACKET_LEN) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"Setup packet too small: %d\n",
|
||||
(int) usb_dockfifo_read_buffer_bytes);
|
||||
return false;
|
||||
}
|
||||
if (usb_dockfifo_read_buffer_bytes > 1 + EP0_MAX_PACKET_SIZE) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"Setup packet too big: %d\n",
|
||||
(int) usb_dockfifo_read_buffer_bytes);
|
||||
return false;
|
||||
}
|
||||
// auto-clear stall if endpoint was previously stalled
|
||||
usb_dockfifo_stall_endpoint(0x80, false);
|
||||
usb_ep0_data_phase_expected = false;
|
||||
usb_core_handle_usb_control_receive(
|
||||
usb_dockfifo_read_buffer_data + 1, true,
|
||||
SETUP_PACKET_LEN,
|
||||
&usb_ep0_data_phase_expected);
|
||||
return true;
|
||||
|
||||
case 0 | EP_PHASE_DATA | EP_DIR_OUT:
|
||||
dprintf(DEBUG_SPEW, "Data phase for setup packet\n");
|
||||
usb_ep0_data_phase_expected = false;
|
||||
if (!usb_dockfifo_handle_ep0_data(
|
||||
usb_dockfifo_read_buffer_data + 1,
|
||||
usb_dockfifo_read_buffer_bytes - 1,
|
||||
&usb_ep0_data_phase_expected)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
usb_dockfifo_handle_endpoint_out(
|
||||
(endpoint & ~EP_FRAG_MASK),
|
||||
(usb_dockfifo_read_buffer_data + 1),
|
||||
(usb_dockfifo_read_buffer_bytes - 1));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool usb_dockfifo_dispatch_ep_in(void)
|
||||
{
|
||||
// Update remote_available for this endpoint.
|
||||
|
||||
ASSERT(usb_dockfifo_read_buffer_bytes >= 1);
|
||||
uint8_t endpoint = usb_dockfifo_read_buffer_data[0];
|
||||
ASSERT((endpoint & EP_DIR_MASK) == EP_DIR_IN);
|
||||
uint32_t ep_num = ep_to_epnum(endpoint);
|
||||
ASSERT((ep_num <= EP_NUM_MASK) && (ep_num != EP_LINK_CONTROL));
|
||||
size_t epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
struct usb_endpoint_instance *ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
if (usb_dockfifo_read_buffer_bytes < 3) {
|
||||
dprintf(DEBUG_INFO, "Runt EPIN packet\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t available = 0;
|
||||
memcpy(&available, &usb_dockfifo_read_buffer_data[1],
|
||||
sizeof(available));
|
||||
ep->remote_available = available;
|
||||
dprintf(DEBUG_SPEW, "ep%02x remote_available %d\n",
|
||||
(int) ep_num, (int) available);
|
||||
|
||||
if (available > 0) {
|
||||
// Writer task may have been waiting to send data.
|
||||
event_signal(&usb_dockfifo_writer_task_event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void usb_dockfifo_main_task_session(void)
|
||||
{
|
||||
dprintf(DEBUG_INFO, "DockFifo USB session started\n");
|
||||
|
||||
// Reset USB state.
|
||||
usb_core_event_handler(USB_RESET);
|
||||
|
||||
// Require a Ready packet first.
|
||||
usb_dockfifo_link_ready = false;
|
||||
|
||||
// Wait for DOCK_CONNECT.
|
||||
while (platform_get_dock_connect() == false) {
|
||||
dprintf(DEBUG_INFO, "Waiting for dock connect\n");
|
||||
task_sleep(100*1000);
|
||||
}
|
||||
|
||||
// Loop while in a good state. Breaking out of the loop breaks
|
||||
// the link until a new Ready packet is received.
|
||||
for (;;) {
|
||||
dprintf(DEBUG_SPEW, "main loop\n");
|
||||
// Read a packet.
|
||||
usb_dockfifo_read_data();
|
||||
if (usb_dockfifo_read_buffer_bytes < 1) {
|
||||
dprintf(DEBUG_INFO, "Runt packet\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Get endpoint number.
|
||||
uint8_t endpoint = usb_dockfifo_read_buffer_data[0];
|
||||
dprintf(DEBUG_SPEW, "Got EP%s%02x %s %s %d\n",
|
||||
(endpoint & EP_DIR_MASK) == EP_DIR_OUT ? "OUT" : "IN",
|
||||
endpoint & EP_NUM_MASK,
|
||||
(endpoint & EP_PHASE_MASK) == EP_PHASE_DATA ?
|
||||
"data" : "setup",
|
||||
(endpoint & EP_FRAG_MASK) == EP_FRAG_MORE ?
|
||||
"frag" : "no frag",
|
||||
(int) (usb_dockfifo_read_buffer_bytes - 1));
|
||||
|
||||
// If we're not Link-Ready yet, any packet other than
|
||||
// LinkReady will be responded with an error, but we
|
||||
// don't need to exit the loop.
|
||||
if (!usb_dockfifo_link_ready &&
|
||||
endpoint != (EP_LINK_CONTROL | EP_DIR_OUT)) {
|
||||
dprintf(DEBUG_INFO, "Non-link packet before Ready\n");
|
||||
// Tell host it needs to reset its session.
|
||||
usb_dockfifo_send_error(kError_LinkNotReady);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dispatch based on endpoint.
|
||||
bool ok;
|
||||
if ((endpoint & EP_DIR_MASK) == EP_DIR_OUT) {
|
||||
ok = usb_dockfifo_dispatch_ep_out();
|
||||
} else {
|
||||
ok = usb_dockfifo_dispatch_ep_in();
|
||||
}
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dprintf(DEBUG_INFO, "dockfifo USB session ended\n");
|
||||
}
|
||||
|
||||
static int usb_dockfifo_main_task(void *args)
|
||||
{
|
||||
for (;;) {
|
||||
// Keep re-trying sessions. If there are protocol
|
||||
// errors, restart from scratch.
|
||||
usb_dockfifo_main_task_session();
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_dockfifo_writer_task(void *args)
|
||||
{
|
||||
for (;;) {
|
||||
static int32_t current_ep_in;
|
||||
|
||||
event_wait(&usb_dockfifo_writer_task_event);
|
||||
|
||||
// walk through IN endpoints for any pending io's, if nothing pending wait for more work
|
||||
for (current_ep_in = 0; current_ep_in < MAX_EP_NEEDED; current_ep_in += 2) {
|
||||
if ((usb_dockfifo_endpoints[current_ep_in].state == kEndpointState_Allocated_Busy) &&
|
||||
(usb_dockfifo_endpoints[current_ep_in].io_request != NULL) &&
|
||||
(usb_dockfifo_endpoints[current_ep_in].remote_available > 0))
|
||||
usb_dockfifo_start_endpoint_in(usb_dockfifo_endpoints[current_ep_in].endpoint_address);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_dockfifo_init()
|
||||
{
|
||||
bzero(usb_dockfifo_endpoints, sizeof(struct usb_endpoint_instance) * MAX_EP_NEEDED);
|
||||
|
||||
// main task to do initial handshake and enumeration
|
||||
usb_dockfifo_main_task_handle = task_create("usb_dockfifo main", usb_dockfifo_main_task, NULL, 0x1000);
|
||||
|
||||
// writer task
|
||||
usb_dockfifo_writer_task_handle = task_create("usb_dockfifo writer", usb_dockfifo_writer_task, NULL, 0x1000);
|
||||
|
||||
event_init(&usb_dockfifo_writer_task_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_dockfifo_free()
|
||||
{
|
||||
// dockfifo_bulk_quiesce();
|
||||
}
|
||||
|
||||
static int usb_dockfifo_start()
|
||||
{
|
||||
event_init(&usb_dockfifo_endpoints[ep_to_epindex(EP0_IN)].idle_state_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
usb_dockfifo_endpoints[ep_to_epindex(EP0_IN)].endpoint_address = 0x80;
|
||||
usb_dockfifo_endpoints[ep_to_epindex(EP0_IN)].max_packet_size = EP0_MAX_PACKET_SIZE;
|
||||
usb_dockfifo_endpoints[ep_to_epindex(EP0_IN)].state = kEndpointState_Allocated_Idle;
|
||||
|
||||
event_init(&usb_dockfifo_endpoints[ep_to_epindex(EP0_OUT)].idle_state_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
usb_dockfifo_endpoints[ep_to_epindex(EP0_OUT)].endpoint_address = 0;
|
||||
usb_dockfifo_endpoints[ep_to_epindex(EP0_OUT)].max_packet_size = EP0_MAX_PACKET_SIZE;
|
||||
usb_dockfifo_endpoints[ep_to_epindex(EP0_OUT)].state = kEndpointState_Allocated_Idle;
|
||||
|
||||
dockfifo_bulk_init();
|
||||
|
||||
task_start(usb_dockfifo_main_task_handle);
|
||||
task_start(usb_dockfifo_writer_task_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_dockfifo_stop()
|
||||
{
|
||||
usb_dockfifo_go_on_bus(false);
|
||||
}
|
||||
|
||||
static void usb_dockfifo_set_address(uint32_t new_address)
|
||||
{
|
||||
}
|
||||
|
||||
static int usb_dockfifo_get_connection_speed()
|
||||
{
|
||||
return usb_connection_speed;
|
||||
}
|
||||
|
||||
static void usb_dockfifo_activate_endpoint(uint32_t endpoint, int type, int max_packet_size, int interval)
|
||||
{
|
||||
uint32_t epindex;
|
||||
struct usb_endpoint_instance *ep;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
ASSERT(ep_to_epnum(endpoint) <= MAX_EP_NUMBER);
|
||||
epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
if(ep->state != kEndpointState_Free)
|
||||
goto exit;
|
||||
|
||||
ep->endpoint_address = endpoint;
|
||||
ep->attributes = type;
|
||||
ep->max_packet_size = max_packet_size;
|
||||
ep->bInterval = interval;
|
||||
event_init(&ep->idle_state_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
ep->state = kEndpointState_Allocated_Idle;
|
||||
|
||||
exit:
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
static void usb_dockfifo_do_endpoint_io(struct usb_device_io_request *req)
|
||||
{
|
||||
struct usb_endpoint_instance *ep;
|
||||
uint32_t epindex;
|
||||
|
||||
dprintf(DEBUG_SPEW, "endpoint %02x len %d\n", req->endpoint,
|
||||
(int) req->io_length);
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
ASSERT(ep_to_epnum(req->endpoint) <= MAX_EP_NUMBER);
|
||||
epindex = ep_to_epindex(req->endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
req->return_count = 0;
|
||||
req->status = USB_IO_ERROR;
|
||||
req->next = NULL;
|
||||
|
||||
// make sure endpoint is idle
|
||||
if(ep->state != kEndpointState_Allocated_Idle) {
|
||||
dprintf(DEBUG_INFO, "endpoint %x is not idle, state:%x\n", req->endpoint, ep->state);
|
||||
event_wait(&ep->idle_state_event);
|
||||
dprintf(DEBUG_INFO, "endpoint %x got idle state\n",
|
||||
req->endpoint);
|
||||
}
|
||||
|
||||
// save IO
|
||||
RELEASE_ASSERT(ep->io_request == NULL);
|
||||
ep->io_request = req;
|
||||
|
||||
// mark endpoint busy
|
||||
ep->state = kEndpointState_Allocated_Busy;
|
||||
event_unsignal(&ep->idle_state_event);
|
||||
|
||||
// start transmitting/receiving the data
|
||||
if (ep_to_epdir(req->endpoint)) {
|
||||
dprintf(DEBUG_SPEW, "EP%02x start write len %d\n",
|
||||
ep_to_epnum(req->endpoint), (int) req->io_length);
|
||||
event_signal(&usb_dockfifo_writer_task_event);
|
||||
} else {
|
||||
dprintf(DEBUG_SPEW, "EP%02x advertise available\n",
|
||||
ep_to_epnum(req->endpoint));
|
||||
usb_dockfifo_send_available(ep_to_epnum(req->endpoint));
|
||||
}
|
||||
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
static void usb_dockfifo_stall_endpoint(uint32_t endpoint, bool stall)
|
||||
{
|
||||
struct usb_endpoint_instance *ep;
|
||||
uint32_t epindex;
|
||||
|
||||
// Nothing to do for EP0_OUT
|
||||
if(endpoint == 0)
|
||||
return;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
ASSERT(ep_to_epnum(endpoint) <= MAX_EP_NUMBER);
|
||||
epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
dprintf(DEBUG_SPEW, "stall endpoint %x, state:%x, stall:%d\n", endpoint, ep->state, stall);
|
||||
|
||||
if (stall) {
|
||||
// if already stalled, nothing to do
|
||||
if ((ep->state & kEndpointState_Allocated_Stalled) != 0)
|
||||
return;
|
||||
|
||||
// wait for endpoint to be idle
|
||||
if (ep->state != kEndpointState_Allocated_Idle)
|
||||
event_wait(&ep->idle_state_event);
|
||||
|
||||
ep->state |= kEndpointState_Allocated_Stalled;
|
||||
}
|
||||
else {
|
||||
// if not stalled, nothing to do
|
||||
if ((ep->state & kEndpointState_Allocated_Stalled) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify Dock
|
||||
uint8_t data[] = {
|
||||
EP_LINK_CONTROL | EP_DIR_IN,
|
||||
kCommand_StallEndpoint,
|
||||
endpoint,
|
||||
stall
|
||||
};
|
||||
usb_dockfifo_send_bytes(data, sizeof(data));
|
||||
|
||||
if (!stall) {
|
||||
ep->state = kEndpointState_Allocated_Idle;
|
||||
event_signal(&ep->idle_state_event);
|
||||
}
|
||||
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
static void usb_dockfifo_reset_endpoint_data_toggle(uint32_t endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
static bool usb_dockfifo_is_endpoint_stalled(uint32_t endpoint)
|
||||
{
|
||||
struct usb_endpoint_instance *ep;
|
||||
uint32_t epindex;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
ASSERT(ep_to_epnum(endpoint) <= MAX_EP_NUMBER);
|
||||
epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
dprintf(DEBUG_SPEW, "endpoint %x, state:%x\n", endpoint, ep->state);
|
||||
|
||||
exit_critical_section();
|
||||
|
||||
return ((ep->state & kEndpointState_Allocated_Stalled) != 0);
|
||||
}
|
||||
|
||||
static void usb_dockfifo_do_test_mode(uint32_t selector)
|
||||
{
|
||||
}
|
||||
|
||||
static void usb_dockfifo_abort_endpoint(uint32_t endpoint)
|
||||
{
|
||||
uint32_t epindex;
|
||||
struct usb_endpoint_instance *ep;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
ASSERT(ep_to_epnum(endpoint) <= MAX_EP_NUMBER);
|
||||
epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
dprintf(DEBUG_SPEW, "abort endpoint %x, state:%x\n", endpoint, ep->state);
|
||||
|
||||
if (ep->state != kEndpointState_Allocated_Busy)
|
||||
goto exit;
|
||||
|
||||
ep->state |= kEndpointState_Allocated_AbortRequested;
|
||||
|
||||
// wait for endpoint to be idle
|
||||
if (ep->state != kEndpointState_Allocated_Idle)
|
||||
event_wait(&ep->idle_state_event);
|
||||
|
||||
ep->io_request->status = USB_IO_ABORTED;
|
||||
usb_core_complete_endpoint_io(ep->io_request);
|
||||
ep->io_request = NULL;
|
||||
|
||||
dprintf(DEBUG_SPEW, "abort endpoint %x finished, state:%x\n", endpoint, ep->state);
|
||||
|
||||
exit:
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
static void usb_dockfifo_deactivate_endpoint(uint32_t endpoint)
|
||||
{
|
||||
uint32_t epindex;
|
||||
struct usb_endpoint_instance *ep;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
ASSERT(ep_to_epnum(endpoint) <= MAX_EP_NUMBER);
|
||||
epindex = ep_to_epindex(endpoint);
|
||||
ASSERT(epindex < MAX_EP_NEEDED);
|
||||
ep = &usb_dockfifo_endpoints[epindex];
|
||||
ASSERT(ep != NULL);
|
||||
|
||||
if(ep->state == kEndpointState_Free)
|
||||
goto exit;
|
||||
|
||||
usb_dockfifo_abort_endpoint(endpoint);
|
||||
|
||||
bzero(&usb_dockfifo_endpoints[ep_to_epindex(endpoint)], sizeof(struct usb_endpoint_instance));
|
||||
|
||||
exit:
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
static const struct usb_controller_functions usb_dockfifo_controller_functions = {
|
||||
.init = usb_dockfifo_init,
|
||||
.free = usb_dockfifo_free,
|
||||
.start = usb_dockfifo_start,
|
||||
.stop = usb_dockfifo_stop,
|
||||
.set_address = usb_dockfifo_set_address,
|
||||
.get_connection_speed = usb_dockfifo_get_connection_speed,
|
||||
.activate_endpoint = usb_dockfifo_activate_endpoint,
|
||||
.do_endpoint_io = usb_dockfifo_do_endpoint_io,
|
||||
.stall_endpoint = usb_dockfifo_stall_endpoint,
|
||||
.reset_endpoint_data_toggle = usb_dockfifo_reset_endpoint_data_toggle,
|
||||
.is_endpoint_stalled = usb_dockfifo_is_endpoint_stalled,
|
||||
.do_test_mode = usb_dockfifo_do_test_mode,
|
||||
.abort_endpoint = usb_dockfifo_abort_endpoint,
|
||||
.deactivate_endpoint = usb_dockfifo_deactivate_endpoint,
|
||||
};
|
||||
|
||||
//=======================
|
||||
// Global funtions
|
||||
//=======================
|
||||
|
||||
const struct usb_controller_functions *usb_dockfifo_controller_init()
|
||||
{
|
||||
return &usb_dockfifo_controller_functions;
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2012 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// USB Serial
|
||||
// This file implements usb serial emulation protocol. We have 'usbterm'
|
||||
// tool on the Mac to enable this.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <debug.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <drivers/usb/usb_chap9.h>
|
||||
#include <drivers/usb/usb_public.h>
|
||||
#include <drivers/usb/usb_core.h>
|
||||
#include <platform/memmap.h>
|
||||
|
||||
#include <sys.h>
|
||||
#include <sys/task.h>
|
||||
|
||||
#include <lib/cbuf.h>
|
||||
#include <sys/callout.h>
|
||||
|
||||
//==============================================================================================
|
||||
// Local conts, macors, structs, typedefs
|
||||
//==============================================================================================
|
||||
#define WRITE_CBUF_MAX_CAPACITY (4096)
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define print(fmt, args...) printf("%s --- " fmt, __FUNCTION__, ##args)
|
||||
#else
|
||||
#define print(fmt, args...) (void)0
|
||||
#endif
|
||||
|
||||
struct usb_serial_buffer {
|
||||
u_int8_t bytes[HS_BULK_EP_MAX_PACKET_SIZE];
|
||||
};
|
||||
|
||||
//==============================================================================================
|
||||
// Global and Local variables
|
||||
//==============================================================================================
|
||||
|
||||
static const struct usb_interface_descriptor usb_serial_interface_descriptors[APPLE_USB_SERIAL_TOTAL_INTFS] =
|
||||
{
|
||||
{
|
||||
USB_DT_INTERFACE_SIZE,
|
||||
USB_DT_INTERFACE,
|
||||
APPLE_USB_SERIAL_INTF,
|
||||
0,
|
||||
0,
|
||||
APPLE_USB_SERIAL_CLASS,
|
||||
APPLE_USB_SERIAL_SUBCLASS,
|
||||
APPLE_USB_SERIAL_PROTOCOL,
|
||||
0
|
||||
},
|
||||
{
|
||||
USB_DT_INTERFACE_SIZE,
|
||||
USB_DT_INTERFACE,
|
||||
APPLE_USB_SERIAL_INTF,
|
||||
1,
|
||||
APPLE_USB_SERIAL_TOTAL_EPS,
|
||||
APPLE_USB_SERIAL_CLASS,
|
||||
APPLE_USB_SERIAL_SUBCLASS,
|
||||
APPLE_USB_SERIAL_PROTOCOL,
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
static const struct usb_endpoint_descriptor usb_serial_endpoint_descriptors[APPLE_USB_SERIAL_TOTAL_EPS] = {
|
||||
{
|
||||
USB_DT_ENDPOINT_SIZE,
|
||||
USB_DT_ENDPOINT,
|
||||
APPLE_USB_SERIAL_EP_BULK_IN,
|
||||
USB_ENDPOINT_BULK,
|
||||
HS_BULK_EP_MAX_PACKET_SIZE,
|
||||
0
|
||||
},
|
||||
{
|
||||
USB_DT_ENDPOINT_SIZE,
|
||||
USB_DT_ENDPOINT,
|
||||
APPLE_USB_SERIAL_EP_BULK_OUT,
|
||||
USB_ENDPOINT_BULK,
|
||||
HS_BULK_EP_MAX_PACKET_SIZE,
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
static const char *usb_serial_string_descriptors[APPLE_USB_SERIAL_TOTAL_INTFS] =
|
||||
{
|
||||
NULL,
|
||||
"Apple USB Serial Interface"
|
||||
};
|
||||
|
||||
static struct usb_interface_instance usb_serial_interface_instance;
|
||||
|
||||
static struct usb_serial_buffer *out_buffer;
|
||||
static struct usb_serial_buffer *in_buffer;
|
||||
static struct task_event in_buffer_event;
|
||||
|
||||
static struct cbuf *usb_serial_output_cbuf;
|
||||
|
||||
static bool usb_serial_inited;
|
||||
static volatile bool usb_serial_activated;
|
||||
|
||||
static int current_alt_setting;
|
||||
|
||||
//==============================================================================================
|
||||
// Functions Prototypes
|
||||
//==============================================================================================
|
||||
|
||||
static int usb_serial_flush_write_cbuf (bool start_transfer, u_int32_t length);
|
||||
static void usb_serial_transfer_finished_cb (struct usb_device_io_request *io_request);
|
||||
static void usb_serial_activate_endpoints (void);
|
||||
static void usb_serial_deactivate_endpoints (void);
|
||||
static int usb_serial_get_max_packet_size(void);
|
||||
static int usb_serial_task_entry(void *arg);
|
||||
|
||||
//==============================================================================================
|
||||
// Local functions
|
||||
//==============================================================================================
|
||||
|
||||
static void usb_serial_activate_interface (void)
|
||||
{
|
||||
usb_serial_activate_endpoints();
|
||||
usb_core_do_transfer(APPLE_USB_SERIAL_EP_BULK_OUT, (u_int8_t *)out_buffer, sizeof(*out_buffer), usb_serial_transfer_finished_cb);
|
||||
usb_serial_activated = true;
|
||||
task_start(task_create("usb_serial", usb_serial_task_entry, NULL, 0x400));
|
||||
}
|
||||
|
||||
|
||||
static void usb_serial_deactivate_interface (void)
|
||||
{
|
||||
usb_serial_activated = false;
|
||||
usb_serial_deactivate_endpoints();
|
||||
}
|
||||
|
||||
static void usb_serial_activate_endpoints (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < APPLE_USB_SERIAL_TOTAL_EPS; i++) {
|
||||
usb_core_activate_endpoint(usb_serial_endpoint_descriptors[i].bEndpointAddress,
|
||||
usb_serial_endpoint_descriptors[i].bmAttributes,
|
||||
usb_serial_get_max_packet_size(),
|
||||
usb_serial_endpoint_descriptors[i].bInterval);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_serial_deactivate_endpoints (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < APPLE_USB_SERIAL_TOTAL_EPS; i++) {
|
||||
usb_core_deactivate_endpoint(usb_serial_endpoint_descriptors[i].bEndpointAddress);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void usb_serial_transfer_finished_cb (struct usb_device_io_request *io_request)
|
||||
{
|
||||
print("callback %x\n", io_request->endpoint);
|
||||
|
||||
if(io_request->endpoint == APPLE_USB_SERIAL_EP_BULK_OUT) {
|
||||
if (io_request->status == USB_IO_SUCCESS) {
|
||||
u_int32_t i = 0;
|
||||
u_int32_t count;
|
||||
|
||||
count = __min(io_request->return_count, sizeof(*out_buffer));
|
||||
|
||||
while(i < count) {
|
||||
debug_pushchar(out_buffer->bytes[i++]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dprintf(DEBUG_INFO, "failed to read\n");
|
||||
}
|
||||
|
||||
usb_core_do_transfer(APPLE_USB_SERIAL_EP_BULK_OUT, (u_int8_t *)out_buffer, sizeof(*out_buffer), usb_serial_transfer_finished_cb);
|
||||
}
|
||||
else if (io_request->endpoint == APPLE_USB_SERIAL_EP_BULK_IN) {
|
||||
event_signal(&in_buffer_event);
|
||||
|
||||
if (io_request->status != USB_IO_SUCCESS) {
|
||||
dprintf(DEBUG_INFO, "failed to write\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int usb_serial_handle_set_interface (int value)
|
||||
{
|
||||
switch(value) {
|
||||
case 0 :
|
||||
usb_serial_deactivate_interface();
|
||||
current_alt_setting = 0;
|
||||
break;
|
||||
|
||||
case 1 :
|
||||
usb_serial_activate_interface();
|
||||
current_alt_setting = 1;
|
||||
break;
|
||||
|
||||
default :
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int usb_serial_handle_get_interface (void)
|
||||
{
|
||||
return (current_alt_setting);
|
||||
}
|
||||
|
||||
static int usb_serial_task_entry(void *arg __unused)
|
||||
{
|
||||
while (usb_serial_activated) {
|
||||
if(usb_serial_flush_write_cbuf(true, sizeof(*in_buffer)) == 0)
|
||||
{
|
||||
task_sleep(15 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_serial_flush_write_cbuf (bool start_transfer, u_int32_t length)
|
||||
{
|
||||
u_int32_t i = 0;
|
||||
char c = 0;
|
||||
u_int32_t max_len = __min(length, sizeof(*in_buffer));
|
||||
|
||||
while(false == event_wait_timeout(&in_buffer_event, 5000 * 1000)) {
|
||||
if(usb_serial_activated) {
|
||||
usb_core_abort_endpoint(APPLE_USB_SERIAL_EP_BULK_IN);
|
||||
}
|
||||
}
|
||||
|
||||
while(i < max_len) {
|
||||
if(cbuf_read_char(usb_serial_output_cbuf, &c) == 0) {
|
||||
break;
|
||||
}
|
||||
in_buffer->bytes[i++] = c;
|
||||
}
|
||||
|
||||
if(i && start_transfer) {
|
||||
usb_core_do_transfer(APPLE_USB_SERIAL_EP_BULK_IN, (u_int8_t *)in_buffer, i, usb_serial_transfer_finished_cb);
|
||||
}
|
||||
else {
|
||||
event_signal(&in_buffer_event);
|
||||
}
|
||||
|
||||
return i; // Number of characters written to USB Buffer
|
||||
}
|
||||
|
||||
|
||||
static int usb_serial_get_max_packet_size(void)
|
||||
{
|
||||
int mps;
|
||||
|
||||
if(usb_core_get_connection_speed() == CONNECTION_SPEED_HIGH) {
|
||||
mps = HS_BULK_EP_MAX_PACKET_SIZE;
|
||||
}
|
||||
else {
|
||||
mps = FS_EP_MAX_PACKET_SIZE;
|
||||
}
|
||||
|
||||
return mps;
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// Global functions
|
||||
//==============================================================================================
|
||||
|
||||
int usb_serial_early_init (void)
|
||||
{
|
||||
in_buffer = memalign(sizeof(*in_buffer), CPU_CACHELINE_SIZE);
|
||||
bzero(in_buffer, sizeof(*in_buffer));
|
||||
|
||||
usb_serial_output_cbuf = cbuf_create(WRITE_CBUF_MAX_CAPACITY, NULL);
|
||||
if(usb_serial_output_cbuf == NULL) {
|
||||
usb_serial_inited = false;
|
||||
dprintf(DEBUG_INFO, "usb_serial_output_cbuf create failed \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_init(&in_buffer_event, EVENT_FLAG_AUTO_UNSIGNAL, true);
|
||||
usb_serial_inited = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_serial_init (void)
|
||||
{
|
||||
if(!usb_serial_inited) {
|
||||
dprintf(DEBUG_INFO, "usb_serial_early_init failed \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
usb_serial_activated = false;
|
||||
current_alt_setting = 0;
|
||||
|
||||
out_buffer = memalign(sizeof(*out_buffer), CPU_CACHELINE_SIZE);
|
||||
if(out_buffer == NULL) {
|
||||
dprintf(DEBUG_INFO, "usb_serial:usb_serial_init --- out_buffer memalign failed \n");
|
||||
return -1;
|
||||
}
|
||||
bzero(out_buffer, sizeof(*out_buffer));
|
||||
|
||||
usb_serial_interface_instance.total_interfaces = APPLE_USB_SERIAL_TOTAL_INTFS;
|
||||
usb_serial_interface_instance.interface_descs = (struct usb_interface_descriptor *)&usb_serial_interface_descriptors;
|
||||
usb_serial_interface_instance.total_endpoints = APPLE_USB_SERIAL_TOTAL_EPS;
|
||||
usb_serial_interface_instance.endpoint_descs = (struct usb_endpoint_descriptor *)&usb_serial_endpoint_descriptors;
|
||||
usb_serial_interface_instance.total_string_descs = APPLE_USB_SERIAL_TOTAL_INTFS;
|
||||
usb_serial_interface_instance.string_descs = (char **)usb_serial_string_descriptors;
|
||||
usb_serial_interface_instance.deactivate_interface = usb_serial_deactivate_interface;
|
||||
usb_serial_interface_instance.handle_set_interface = usb_serial_handle_set_interface;
|
||||
usb_serial_interface_instance.handle_get_interface = usb_serial_handle_get_interface;
|
||||
|
||||
usb_core_register_interface(&usb_serial_interface_instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_serial_putchar(int c)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if((ret = cbuf_write_char(usb_serial_output_cbuf, c)) == 0) {
|
||||
bool start_transfer = false;
|
||||
u_int32_t len = 1;
|
||||
|
||||
if(usb_serial_activated) {
|
||||
len = usb_serial_get_max_packet_size();
|
||||
start_transfer = true;
|
||||
}
|
||||
|
||||
usb_serial_flush_write_cbuf(start_transfer, len);
|
||||
|
||||
cbuf_write_char(usb_serial_output_cbuf, c);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_serial_exit (void)
|
||||
{
|
||||
if(usb_serial_output_cbuf) {
|
||||
cbuf_destroy(usb_serial_output_cbuf);
|
||||
usb_serial_output_cbuf = NULL;
|
||||
}
|
||||
|
||||
if(in_buffer) {
|
||||
free(in_buffer);
|
||||
in_buffer = NULL;
|
||||
}
|
||||
|
||||
if(out_buffer) {
|
||||
free(out_buffer);
|
||||
out_buffer = NULL;
|
||||
}
|
||||
|
||||
usb_serial_inited = false;
|
||||
}
|
||||
|
||||
int usb_serial_send_cmd_string(u_int8_t *buffer, u_int32_t len)
|
||||
{
|
||||
if(!usb_serial_activated) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
usb_core_do_transfer(APPLE_USB_SERIAL_EP_BULK_IN, buffer, len, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool usb_serial_is_active()
|
||||
{
|
||||
return ((current_alt_setting == 1) ? true : false);
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// USB Bulk Upload + File transfer
|
||||
//
|
||||
// This file implements USB Bulk protocol for file uploads;
|
||||
// and it also implements vendor specific file transfer
|
||||
// protocol using a bulk pipe.
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <debug.h>
|
||||
#include <sys/task.h>
|
||||
#include <lib/profile.h>
|
||||
#include <lib/env.h>
|
||||
#include <sys/security.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <drivers/usb/usb_chap9.h>
|
||||
#include <drivers/usb/usb_public.h>
|
||||
#include <drivers/usb/usb_core.h>
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define print(fmt, args...) printf("%s - " fmt, __FUNCTION__, ##args)
|
||||
#else
|
||||
#define print(fmt, args...) (void)0
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_FPGA
|
||||
#define TIMEOUT_SCALER (10)
|
||||
#else
|
||||
#define TIMEOUT_SCALER (1)
|
||||
#endif
|
||||
|
||||
#define BULK_HS_TRNSFR_TIMEOUT (15 * 1000 * 1000 * TIMEOUT_SCALER)
|
||||
#define BULK_FS_TRNSFR_TIMEOUT (30 * 1000 * 1000 * TIMEOUT_SCALER)
|
||||
|
||||
#define ERR_PACKET_LEN (4)
|
||||
#define ERR_PKT_START (0xfb)
|
||||
#define ERR_PKT_END (0xfa)
|
||||
|
||||
// File transfer error codes
|
||||
#define ERR_FILE_SUCCESS 0
|
||||
#define ERR_FILE_UNKNOWN_ERROR 1
|
||||
#define ERR_FILE_UNEXPECTED_ERROR 2
|
||||
#define ERR_FILE_NOT_FOUND 3
|
||||
#define ERR_FILE_READ_ERROR 4
|
||||
#define ERR_FILE_WRITE_ERROR 5
|
||||
|
||||
// Personalization related file transfer error codes
|
||||
#define ERR_PFILE_NOT_IM4P 11
|
||||
#define ERR_PFILE_CREATE_FAILED 12
|
||||
#define ERR_PFILE_READ_ERROR 13
|
||||
|
||||
static const struct usb_interface_descriptor usb_trnsfr_interface_descriptor = {
|
||||
USB_DT_INTERFACE_SIZE,
|
||||
USB_DT_INTERFACE,
|
||||
APPLE_USB_TRNSFR_INTF,
|
||||
0,
|
||||
APPLE_USB_TRNSFR_TOTAL_EPS,
|
||||
APPLE_USB_TRNSFR_CLASS,
|
||||
APPLE_USB_TRNSFR_SUBCLASS,
|
||||
APPLE_USB_TRNSFR_PROTOCOL,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct usb_endpoint_descriptor usb_trnsfr_endpoint_descriptors [] = {
|
||||
{
|
||||
USB_DT_ENDPOINT_SIZE,
|
||||
USB_DT_ENDPOINT,
|
||||
APPLE_USB_TRNSFR_EP_BULK_OUT,
|
||||
USB_ENDPOINT_BULK,
|
||||
HS_BULK_EP_MAX_PACKET_SIZE,
|
||||
0
|
||||
}
|
||||
#if WITH_BULK_UPLOAD
|
||||
,
|
||||
{
|
||||
USB_DT_ENDPOINT_SIZE,
|
||||
USB_DT_ENDPOINT,
|
||||
APPLE_USB_TRNSFR_EP_BULK_IN,
|
||||
USB_ENDPOINT_BULK,
|
||||
HS_BULK_EP_MAX_PACKET_SIZE,
|
||||
0
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct usb_interface_instance usb_trnsfr_interface_instance;
|
||||
static struct task_event trnsfr_event;
|
||||
static volatile bool trnsfr_done;
|
||||
static int completion_status;
|
||||
static u_int8_t *image_buffer;
|
||||
static u_int32_t image_buffer_size;
|
||||
static struct callout trnsfr_callout;
|
||||
static u_int32_t max_transfer_len;
|
||||
|
||||
#if WITH_BULK_UPLOAD
|
||||
static u_int8_t __aligned(CPU_CACHELINE_SIZE) bulk_in_zlp_buffer[sizeof(u_int32_t)];
|
||||
static u_int8_t __aligned(CPU_CACHELINE_SIZE) bulk_upload_status_buffer[ERR_PACKET_LEN];
|
||||
static void bulk_in_callback(struct usb_device_io_request *io_request);
|
||||
static void bulk_upload_status_callback(struct usb_device_io_request *io_request);
|
||||
#endif // WITH_BULK_UPLOAD
|
||||
|
||||
static void activate_interface(void);
|
||||
static void deactivate_interface(void);
|
||||
static void bulk_transfer_callback(struct usb_device_io_request *io_request);
|
||||
static void handle_trnsfr_callout(struct callout *co, void *args);
|
||||
static int handle_vendor_request(struct usb_device_request *request);
|
||||
static void transfer_failed();
|
||||
|
||||
int usb_transfer_init()
|
||||
{
|
||||
completion_status = -1;
|
||||
image_buffer = NULL;
|
||||
image_buffer_size = 0;
|
||||
trnsfr_done = false;
|
||||
|
||||
event_init(&trnsfr_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
usb_trnsfr_interface_instance.total_interfaces = APPLE_USB_TRNSFR_TOTAL_INTFS;
|
||||
usb_trnsfr_interface_instance.interface_descs = (struct usb_interface_descriptor *)&usb_trnsfr_interface_descriptor;
|
||||
usb_trnsfr_interface_instance.total_endpoints = APPLE_USB_TRNSFR_TOTAL_EPS;
|
||||
usb_trnsfr_interface_instance.endpoint_descs = (struct usb_endpoint_descriptor *)&usb_trnsfr_endpoint_descriptors;
|
||||
usb_trnsfr_interface_instance.deactivate_interface = deactivate_interface;
|
||||
usb_trnsfr_interface_instance.activate_interface = activate_interface;
|
||||
#if WITH_RECOVERY_MODE
|
||||
usb_trnsfr_interface_instance.handle_vendor_request = handle_vendor_request;
|
||||
#endif // WITH_RECOVERY_MODE
|
||||
|
||||
usb_core_register_interface(&usb_trnsfr_interface_instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_transfer_exit()
|
||||
{
|
||||
}
|
||||
|
||||
void usb_transfer_prepare(int type, void *address, u_int32_t length)
|
||||
{
|
||||
u_int32_t timeout;
|
||||
|
||||
print("\n");
|
||||
|
||||
timeout = 0;
|
||||
|
||||
completion_status = -1;
|
||||
image_buffer = address;
|
||||
image_buffer_size = length;
|
||||
trnsfr_done = false;
|
||||
|
||||
#if WITH_ENV
|
||||
env_set_uint("filesize", 0, 0);
|
||||
#endif // WITH_ENV
|
||||
|
||||
if(type == USB_TRANSFER_TYPE_BULK_GET) {
|
||||
|
||||
max_transfer_len = DEFAULT_RAMDISK_SIZE;
|
||||
|
||||
if(!security_allow_memory((u_int8_t *)image_buffer, max_transfer_len)) {
|
||||
dprintf(DEBUG_INFO, "Permission denied\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(usb_core_get_connection_speed() == CONNECTION_SPEED_HIGH)
|
||||
timeout = BULK_HS_TRNSFR_TIMEOUT;
|
||||
else
|
||||
timeout = BULK_FS_TRNSFR_TIMEOUT;
|
||||
|
||||
usb_core_abort_endpoint(APPLE_USB_TRNSFR_EP_BULK_OUT);
|
||||
|
||||
usb_core_do_transfer(APPLE_USB_TRNSFR_EP_BULK_OUT, image_buffer, max_transfer_len,
|
||||
bulk_transfer_callback);
|
||||
}
|
||||
#if WITH_BULK_UPLOAD
|
||||
else if(type == USB_TRANSFER_TYPE_BULK_PUT) {
|
||||
|
||||
if(!security_allow_memory((u_int8_t *)image_buffer, image_buffer_size)) {
|
||||
dprintf(DEBUG_INFO, "Permission denied\n");
|
||||
return;
|
||||
}
|
||||
|
||||
timeout = (usb_core_get_connection_speed() == CONNECTION_SPEED_HIGH) ? BULK_HS_TRNSFR_TIMEOUT : BULK_FS_TRNSFR_TIMEOUT;
|
||||
usb_core_abort_endpoint(APPLE_USB_TRNSFR_EP_BULK_OUT);
|
||||
usb_core_abort_endpoint(APPLE_USB_TRNSFR_EP_BULK_IN);
|
||||
|
||||
// Queue BULK IN request to upload data to host
|
||||
usb_core_do_transfer(APPLE_USB_TRNSFR_EP_BULK_IN, image_buffer, image_buffer_size, bulk_in_callback);
|
||||
|
||||
// Keep an active BULK OUT request so that host can communcate failure at any time or communicate success at the end.
|
||||
// There is no timeout for this request
|
||||
usb_core_do_transfer(APPLE_USB_TRNSFR_EP_BULK_OUT, bulk_upload_status_buffer, ERR_PACKET_LEN, bulk_upload_status_callback);
|
||||
}
|
||||
#endif // WITH_BULK_UPLOAD
|
||||
|
||||
callout_enqueue(&trnsfr_callout, timeout, handle_trnsfr_callout, NULL);
|
||||
}
|
||||
|
||||
u_int32_t usb_transfer_start()
|
||||
{
|
||||
print("starting\n");
|
||||
|
||||
while(!trnsfr_done) event_wait(&trnsfr_event);
|
||||
#if DEBUG_BUILD
|
||||
dprintf(DEBUG_SPEW, "transfer complete, status=%d\n", completion_status);
|
||||
#else
|
||||
dprintf(DEBUG_INFO, "transfer complete, status=%d\n", completion_status);
|
||||
#endif
|
||||
|
||||
return completion_status;
|
||||
}
|
||||
|
||||
static void deactivate_interface(void)
|
||||
{
|
||||
usb_core_deactivate_endpoint(APPLE_USB_TRNSFR_EP_BULK_OUT);
|
||||
#if WITH_BULK_UPLOAD
|
||||
usb_core_deactivate_endpoint(APPLE_USB_TRNSFR_EP_BULK_IN);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void activate_interface(void)
|
||||
{
|
||||
int mps;
|
||||
|
||||
mps = (usb_core_get_connection_speed() == CONNECTION_SPEED_HIGH) ? HS_BULK_EP_MAX_PACKET_SIZE : FS_EP_MAX_PACKET_SIZE;
|
||||
usb_core_activate_endpoint(APPLE_USB_TRNSFR_EP_BULK_OUT, USB_ENDPOINT_BULK, mps, 0);
|
||||
|
||||
#if WITH_BULK_UPLOAD
|
||||
usb_core_activate_endpoint(APPLE_USB_TRNSFR_EP_BULK_IN, USB_ENDPOINT_BULK, mps, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void handle_trnsfr_callout(struct callout *co, void *args)
|
||||
{
|
||||
print("\n");
|
||||
|
||||
transfer_failed();
|
||||
}
|
||||
|
||||
#if WITH_RECOVERY_MODE
|
||||
static int handle_vendor_request(struct usb_device_request *request)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = -1;
|
||||
|
||||
print("req: %d\n", request->wValue);
|
||||
|
||||
if(request->wValue == PR_RESET_REQUEST) {
|
||||
PROFILE_1('UHVR');
|
||||
usb_transfer_prepare(USB_TRANSFER_TYPE_BULK_GET, (void *)DEFAULT_LOAD_ADDRESS, DEFAULT_RAMDISK_SIZE);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // WITH_RECOVERY_MODE
|
||||
|
||||
static void transfer_failed()
|
||||
{
|
||||
print("\n");
|
||||
|
||||
callout_dequeue(&trnsfr_callout);
|
||||
completion_status = -1;
|
||||
#if WITH_ENV
|
||||
env_set_uint("filesize", 0, 0);
|
||||
#endif // WITH_ENV
|
||||
trnsfr_done = true;
|
||||
event_signal(&trnsfr_event);
|
||||
}
|
||||
|
||||
static void bulk_transfer_callback(struct usb_device_io_request *io_request)
|
||||
{
|
||||
print("status: %d, count: %d\n", io_request->status, io_request->return_count);
|
||||
|
||||
if(io_request->status != USB_IO_SUCCESS) {
|
||||
transfer_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// special case, Error packet handling.
|
||||
// this length and pattern should not appear in
|
||||
// normal case, if someone will hit this often
|
||||
// will make a change.
|
||||
if(io_request->return_count == ERR_PACKET_LEN) {
|
||||
u_int8_t *buf = (u_int8_t *)image_buffer;
|
||||
|
||||
if((buf[0] == ERR_PKT_START) && (buf[3] == ERR_PKT_END)) {
|
||||
switch(buf[2])
|
||||
{
|
||||
case ERR_FILE_UNEXPECTED_ERROR:
|
||||
dprintf(DEBUG_CRITICAL, "Unexpected error, exiting\n");
|
||||
break;
|
||||
case ERR_FILE_NOT_FOUND:
|
||||
dprintf(DEBUG_CRITICAL, "File not found or could not be accessed, exiting\n");
|
||||
break;
|
||||
case ERR_FILE_READ_ERROR:
|
||||
dprintf(DEBUG_CRITICAL, "File read error, exiting\n");
|
||||
break;
|
||||
case ERR_PFILE_NOT_IM4P:
|
||||
dprintf(DEBUG_CRITICAL, "Not an im4p file, exiting\n");
|
||||
break;
|
||||
case ERR_PFILE_CREATE_FAILED:
|
||||
dprintf(DEBUG_CRITICAL, "Could not personalize file, exiting\n");
|
||||
break;
|
||||
case ERR_PFILE_READ_ERROR:
|
||||
dprintf(DEBUG_CRITICAL, "Error reading personalized file, exiting\n");
|
||||
break;
|
||||
case ERR_FILE_UNKNOWN_ERROR:
|
||||
default:
|
||||
dprintf(DEBUG_CRITICAL, "Unknown error - %d, exiting\n", buf[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
transfer_failed();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_ENV
|
||||
env_set_uint("filesize", io_request->return_count, 0);
|
||||
#endif // WITH_ENV
|
||||
completion_status = io_request->return_count;
|
||||
callout_dequeue(&trnsfr_callout);
|
||||
trnsfr_done = true;
|
||||
event_signal(&trnsfr_event);
|
||||
}
|
||||
|
||||
#ifdef WITH_BULK_UPLOAD
|
||||
static void bulk_in_callback(struct usb_device_io_request *io_request)
|
||||
{
|
||||
int mps;
|
||||
int amount;
|
||||
|
||||
amount = io_request->return_count;
|
||||
print("status: %d, count: %d\n", io_request->status, amount);
|
||||
|
||||
if(io_request->status == USB_IO_SUCCESS) {
|
||||
completion_status = amount;
|
||||
|
||||
mps = (usb_core_get_connection_speed() == CONNECTION_SPEED_HIGH) ? HS_BULK_EP_MAX_PACKET_SIZE : FS_EP_MAX_PACKET_SIZE;
|
||||
if((amount > 0) && ((amount & (mps - 1)) == 0))
|
||||
{
|
||||
// Send an empty packet to indicate end of transfer
|
||||
usb_core_do_transfer(APPLE_USB_TRNSFR_EP_BULK_IN, bulk_in_zlp_buffer, 0, NULL);
|
||||
}
|
||||
|
||||
// Restart the timer waiting for the status packet
|
||||
callout_reset(&trnsfr_callout, 0);
|
||||
}
|
||||
else {
|
||||
completion_status = -1;
|
||||
}
|
||||
}
|
||||
#endif // WITH_BULK_UPLOAD
|
||||
|
||||
#ifdef WITH_BULK_UPLOAD
|
||||
static void bulk_upload_status_callback(struct usb_device_io_request *io_request)
|
||||
{
|
||||
bool upload_success = false;
|
||||
|
||||
print("status: %d, count: %d\n", io_request->status, io_request->return_count);
|
||||
if(io_request->status == USB_IO_SUCCESS) {
|
||||
if(io_request->return_count == ERR_PACKET_LEN) {
|
||||
if((bulk_upload_status_buffer[0] == ERR_PKT_START) && (bulk_upload_status_buffer[3] == ERR_PKT_END)) {
|
||||
switch(bulk_upload_status_buffer[2]) {
|
||||
case ERR_FILE_SUCCESS:
|
||||
upload_success = true;
|
||||
break;
|
||||
case ERR_FILE_UNEXPECTED_ERROR:
|
||||
printf("Unexpected error, exiting\n");
|
||||
break;
|
||||
case ERR_FILE_NOT_FOUND:
|
||||
printf("File could not be opened, exiting\n");
|
||||
break;
|
||||
case ERR_FILE_WRITE_ERROR:
|
||||
printf("File write error, exiting\n");
|
||||
break;
|
||||
case ERR_FILE_UNKNOWN_ERROR:
|
||||
default:
|
||||
printf("Unknown error - %d, exiting\n", bulk_upload_status_buffer[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dprintf(DEBUG_INFO, "Invalid format for status packet\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
dprintf(DEBUG_INFO, "Invalid status packet length - %d\n", io_request->return_count);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dprintf(DEBUG_INFO, "Status packet failed %x\n", io_request->status);
|
||||
}
|
||||
|
||||
if(!upload_success) {
|
||||
usb_core_abort_endpoint(APPLE_USB_TRNSFR_EP_BULK_IN);
|
||||
}
|
||||
|
||||
trnsfr_done = true;
|
||||
callout_dequeue(&trnsfr_callout);
|
||||
event_signal(&trnsfr_event);
|
||||
}
|
||||
#endif // WITH_BULK_UPLOAD
|
||||
Reference in new issue
Block a user