first and last commit

master
Chris Nutter 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 changed files with 636500 additions and 0 deletions

292
Makefile Normal file
View File

@ -0,0 +1,292 @@
# Copyright (C) 2007-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.
###############################################################################
# This is the top-level makefile for everything built out of the iBoot source
# tree.
# Find out what OS we're building on
export BUILD_OS := $(shell uname -s| tr '[:upper:]' '[:lower:]')
$(info %%% building on OS $(BUILD_OS))
# Find the root of the source tree from the build system's perspective.
# could be $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) with a newer make
# Note that buildit sets SRCROOT to the destination for installsrc, so it can be
# empty which breaks the VALID_APPLICATIONS probe.
ORIGINAL_SRCROOT := $(patsubst %/,%,$(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))))
export SRCROOT ?= ${ORIGINAL_SRCROOT}
# Applications listed in the APPLICATIONS argument are processed one at a
# time, and the per-application makefile in ./makefiles is invoked.
VALID_APPLICATIONS := $(filter-out .%,$(notdir $(shell find $(ORIGINAL_SRCROOT)/apps -mindepth 1 -maxdepth 1 -type d)))
# Valid applications that should not be built by default
SUPPRESSED_APPLICATIONS :=
# Options that must not be set if building default or multiple APPLICATIONS
SPECIFIC_OPTIONS := TARGETS CONFIGS
# Default SDK platform
ifeq ($(BUILD_OS),darwin)
export SDK_PLATFORM ?= iphoneos.internal
ifeq ($(SDKROOT),)
SDKROOT_PATH := $(shell xcodebuild -version -sdk $(SDK_PLATFORM) Path)
export SDKROOT := $(SDKROOT_PATH)
else
# SDKROOT may be a canonical name (iphoneos.internal). Normalize into a path.
SDKROOT_PATH := $(shell xcodebuild -version -sdk $(SDKROOT) Path)
export SDKROOT := $(SDKROOT_PATH)
export SDK_PLATFORM := $(shell xcodebuild -version -sdk $(SDKROOT) | head -1 | grep -o '(.*)' | tr -d \(\))
endif
export SDK_PRODUCT_NAME := $(shell xcodebuild -version -sdk $(SDKROOT) ProductName)
ifeq ($(SDKVERSION),)
export SDKVERSION := $(shell xcodebuild -version -sdk $(SDK_PLATFORM) SDKVersion | head -1)
endif
ifeq ($(PLATFORMROOT),)
export PLATFORMROOT := $(shell xcodebuild -version -sdk $(SDK_PLATFORM) PlatformPath)
endif
export HOST_PLATFORM ?= macosx
ifeq ($(HOST_SDKROOT),)
export HOST_SDKROOT := $(shell xcodebuild -version -sdk $(HOST_PLATFORM) Path)
endif
endif
export OBJROOT ?= .
export SYMROOT ?= .
export INSTALL_DIR ?= $(DSTROOT)/usr/local/standalone/firmware/
###############################################################################
# No user-serviceable parts below
# Check for spaces in critical paths - we can't handle that
SPACE_CHECK = $(filter ALL,$(and $(findstring $(subst ~, ,~),$(1)),$(error $(2))))
SDK_ADVICE = A path to a component of the SDK contains one or more spaces; this is not supported. Please relocate your SDK to a path without spaces
$(call SPACE_CHECK,$(SDKROOT_PATH),$(SDK_ADVICE) (SDKROOT=$(SDKROOT_PATH)))
$(call SPACE_CHECK,$(PLATFORMROOT),$(SDK_ADVICE) (PLATFORMROOT=$(PLATFORMROOT)))
$(call SPACE_CHECK,$(HOST_SDKROOT),$(SDK_ADVICE) (HOST_SDKROOT=$(HOST_SDKROOT)))
DIR_ADVICE = A path supplied in a variable contains one or more spaces; this is not supported.
$(call SPACE_CHECK,$(DSTROOT),$(DIR_ADVICE) (DSTROOT=$(DSTROOT)))
$(call SPACE_CHECK,$(OBJROOT),$(DIR_ADVICE) (OBJROOT=$(OBJROOT)))
$(call SPACE_CHECK,$(SYMROOT),$(DIR_ADVICE) (SYMROOT=$(SYMROOT)))
$(call SPACE_CHECK,$(SRCROOT),The iBoot sources are in a folder with one or more spaces in the path; this is not supported (SRCROOT=$(SRCROOT)))
# Check for the existence of required directories
DIRECTORY_CHECK = $(filter ALL,$(or $(findstring Directory,$(shell stat -Lf %HT $(1))),$(error $(2))))
DIR_MISSING_ADVICE = A path to a component of the SDK specifies a directory that does not exist
ifeq ($(BUILD_OS),darwin)
$(call DIRECTORY_CHECK,$(SDKROOT_PATH),$(DIR_MISSING_ADVICE) (SDKROOT=$(SDKROOT_PATH)))
$(call DIRECTORY_CHECK,$(PLATFORMROOT),$(DIR_MISSING_ADVICE) (PLATFORMROOT=$(PLATFORMROOT)))
$(call DIRECTORY_CHECK,$(HOST_SDKROOT),$(DIR_MISSING_ADVICE) (HOST_SDKROOT=$(HOST_SDKROOT)))
endif
# Force the tools to emit consistent punctuation in diagnostics
export LC_ALL=C
# Work out what we are building
ifneq ($(APPS),)
# APPS is a shortcut for APPLICATIONS, combine if you're dumb enough to specify both
override APPLICATIONS := $(strip $(APPLICATIONS) $(APPS))
endif
APPLICATIONS ?= $(filter-out $(SUPPRESSED_APPLICATIONS), $(VALID_APPLICATIONS))
MAKE_APPLICATIONS := $(filter $(VALID_APPLICATIONS), $(APPLICATIONS))
ERROR_APPLICATIONS := $(filter-out $(VALID_APPLICATIONS), $(APPLICATIONS))
ifneq ($(ERROR_APPLICATIONS),)
$(error Unrecognized application(s) - $(ERROR_APPLICATIONS))
endif
# Protect against application-specific options if we are building multiple
# applications
ifneq ($(strip $(foreach opt,$(SPECIFIC_OPTIONS),$(value $(opt)))),)
ifneq ($(strip $(shell echo $(MAKE_APPLICATIONS) | wc -w)),1)
$(error must not specify multiple (or default) APPLICATIONS if any of [$(strip $(SPECIFIC_OPTIONS))] is set)
endif
endif
# Export the list of standard actions, and define targets for each per application.
# Application Makefiles will in turn generate targets based on STANDARD_ACTIONS
# so that we can tunnel more or less arbitrary targets into main.mk.
export STANDARD_ACTIONS := build install library_list
APPLICATION_TEMPLATE := $(addprefix %-,$(MAKE_APPLICATIONS))
ACTIONS := $(foreach action,$(STANDARD_ACTIONS),$(addprefix $(action)-,$(MAKE_APPLICATIONS)))
# The 'help' target is special, in that applications are responsible for handling
# it directly.
HELP_APPLICATIONS := $(addprefix help-,$(MAKE_APPLICATIONS))
ACTIONS += $(HELP_APPLICATIONS)
# The 'build' targets are special, in that they depend on the non-standard
# 'libraries' target.
BUILD_APPLICATIONS := $(addprefix build-,$(MAKE_APPLICATIONS))
# Library list
export LIBLIST := $(OBJROOT)/build/library_list
# Global header list
export HDRLIST := $(OBJROOT)/build/header_list
# Where generated host tools are to be found
export TOOLS_BIN := $(OBJROOT)/build/tools
# Pick up or generate the XBS tag
ifeq ($(RC_ProjectSourceVersion),)
export XBS_BUILD_TAG ?= "$(shell echo localbuild...$$(whoami)...$$(git symbolic-ref --short HEAD 2>/dev/null || echo 'detached')_$$(git rev-parse --short HEAD 2>/dev/null)$$([[ $$(expr $$(git status --porcelain 2>/dev/null| egrep -v '^( |\?\?)' | wc -l)) == '0' ]] || echo '_dirty')...$$(date '+%Y/%m/%d-%H:%M:%S'))"
else
export XBS_BUILD_TAG := "iBoot-$(RC_ProjectSourceVersion)"
endif
# Work out whether we are building under XBS or just buildit
ifneq ($(RC_XBS),)
ifeq ($(RC_BUILDIT),)
$(warning building under XBS)
export BUILDING_UNDER_XBS := true
else
export BUILDING_UNDER_BUILDIT := true
$(warning building under BUILDIT)
endif
endif
# Find our tools, now that we know what we're building and in what environment.
include makefiles/tools.mk
# Verbosity for build rules
VERBOSE ?= NO
ifeq ($(VERBOSE),YES)
export _v =
else
export _v = @
endif
# Setup for parallel sub-makes based on 2 times number of logical CPUs
ifndef MAKEJOBS
ifeq ($(BUILD_OS),darwin)
export MAXJOBS := $(shell echo $$((`/usr/sbin//sysctl -n hw.logicalcpu` * 2)) )
else
export MAXJOBS := $(shell echo $$((`nproc` * 2)))
endif
ifeq ($(BUILDING_UNDER_XBS),true)
export MAKEJOBS := --jobs=$(MAXJOBS)
else
export MAKEJOBS := --jobs=$(shell echo $$(( $(MAXJOBS) > 8 ? 8 : $(MAXJOBS))) )
endif
endif
# Help does not look so good if built parallel...
ifeq ($(MAKECMDGOALS),help)
export MAKEJOBS = --jobs=1
endif
# debug filename hashes are needed for both library and application build phases
export DEBUG_FILENAME_HASHES := $(shell ./tools/generate_debug_hashes.py --src .)
################################################################################
# Toplevel rules
#
all: build
ifeq ($(BUILD_OS),darwin)
# By depending on libraries, we get the depended library pass out of the way
# and then application targets can build parallel.
build: build-headers-and-libraries
# Don't depend on build; the target install knows how to do that.
install: build-headers-and-libraries
else
# Linux can only build tests for now
build: tests
endif
# Global headers must be built before the libraries but after we've generated
# the libary list so order them properly with this rule.
build-headers-and-libraries: $(LIBLIST) build-headers build-libraries
build-libraries: $(LIBLIST)
@echo "%%%% building libraries"
@$(MAKE) $(MAKEJOBS) -f makefiles/libraries.mk build MAKEPHASE=libraries SDKROOT=$(SDKROOT_PATH)
$(LIBLIST): library_list
@echo "%%%% making $(LIBLIST)"
@cat /dev/null `find $(OBJROOT)/build -name *.library_list` | sort | uniq > $(LIBLIST)
@$(CHECK_LIBLIST) $(LIBLIST)
build-headers:
@echo "%%%% building global headers"
@cat /dev/null `find $(OBJROOT)/build -name *.header_list` | sort | uniq > $(HDRLIST)
@$(MAKE) $(MAKEJOBS) -f makefiles/headers.mk build MAKEPHASE=headers SDKROOT=$(SDKROOT_PATH)
clean:
$(_v)rm -rf $(OBJROOT)/build
$(_v)rm -rf $(SYMROOT)/build
pre-help:
@echo "%%% printing help"
@echo "Valid APPLICATIONS"
@echo " ${VALID_APPLICATIONS}"
@echo "Default APPLICATIONS"
@echo " ${MAKE_APPLICATIONS}"
@echo ""
help: pre-help $(HELP_APPLICATIONS)
build-tools:
@echo "%%%% generating build tools"
@$(MAKE) -f tools/Makefile build-tools
install-tools:
@echo Generating install tools
@$(MAKE) -f tools/Makefile install-tools
installsrc:
ditto . $(SRCROOT)
installhdrs:
@echo no headers installed
build-tests:
@$(MAKE) $(MAKEJOBS) -f makefiles/tests.mk SDKROOT=$(SDKROOT_PATH) build
.PHONY: tests
tests:
@$(MAKE) $(MAKEJOBS) -f makefiles/tests.mk SDKROOT=$(SDKROOT_PATH) run
cscope.files:
@echo "Building file list for cscope and tags"
@find . -name '*.h' -type f | grep -v ^..build > _cscope.files 2> /dev/null
@find . -name '*.c' -type f | grep -v ^..build >> _cscope.files 2> /dev/null
@find . -name '*.cpp' -type f | grep -v ^..build >> _cscope.files 2> /dev/null
@find . -name '*.S' -type f | grep -v ^..build >> _cscope.files 2> /dev/null
@echo > cscope.files 2> /dev/null
@sort -u < _cscope.files >> cscope.files 2> /dev/null
@rm -f _cscope.files _cscope.files2 2> /dev/null
cscope: cscope.files
@echo "Building cscope database"
@cscope -bvU 2> /dev/null
spotless realclean:
rm -rf build cscope.*
# Linux only knows how to build tests for now, so elide this on Linux
ifeq ($(BUILD_OS),darwin)
# Define this late, so that manual dependencies stated above for
# the standard actions are satisfied before the automatic depdencies.
$(STANDARD_ACTIONS) : % : $(APPLICATION_TEMPLATE)
# Build actions require the build-libraries target, which is otherwise only seen as
# a co-dependency of the build target
$(BUILD_APPLICATIONS): build-libraries
$(ACTIONS): action = $(word 1, $(subst -, ,$@))
$(ACTIONS): application = $(word 2, $(subst -, ,$@))
$(ACTIONS):
@echo "%%%%%% $(action) $(application)"
@$(MAKE) $(MAKEJOBS) -f apps/$(application)/$(application).mk $(action) SDKROOT=$(SDKROOT_PATH) APPLICATION=$(application) MAKEPHASE=$(action)
endif

0
README.md Normal file
View File

View File

@ -0,0 +1,73 @@
# Copyright (C) 2007 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.
###############################################################################
# Build rules for the EmbeddedIOP firmware
#
# Recognises values in
#
# BUILDS
# TARGETS
# CONFIGS
#
VALID_BUILDS := RELEASE DEBUG
SUPPRESSED_TARGETS:= s5l8720x
###############################################################################
# No user-serviceable parts below
CONFIG_DIR := $(patsubst %/,%,$(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))))/config
VALID_TARGETS := $(subst -config.mk,,$(notdir $(wildcard $(CONFIG_DIR)/*-config.mk)))
BUILDS ?= $(VALID_BUILDS)
MAKE_BUILDS = $(filter $(VALID_BUILDS), $(BUILDS))
TARGETS ?= $(filter-out $(SUPPRESSED_TARGETS),$(VALID_TARGETS))
MAKE_TARGETS = $(filter $(VALID_TARGETS), $(TARGETS))
ERROR_TARGETS := $(filter-out $(VALID_TARGETS), $(TARGETS))
ifneq ($(ERROR_TARGETS),)
$(error Unrecognized target(s) - $(ERROR_TARGETS))
endif
MAKE_PRODUCT = EmbeddedIOP
LIST = $(foreach build, $(MAKE_BUILDS), $(addprefix $(build)-, $(MAKE_TARGETS)))
LIST_TEMPLATE := $(addprefix %-,$(LIST))
$(STANDARD_ACTIONS):%: $(LIST_TEMPLATE)
ACTIONS := $(foreach action,$(STANDARD_ACTIONS),$(addprefix $(action)-,$(LIST)))
$(ACTIONS): action = $(word 1, $(subst -, ,$@))
$(ACTIONS): target = $(word 3, $(subst -, ,$@))
$(ACTIONS): product = $(MAKE_PRODUCT)
$(ACTIONS): build = $(word 2, $(subst -, ,$@))
$(ACTIONS): install_name = $(product).$(target)$(config).$(build).h
$(ACTIONS):
@echo %%% $(action) $(target)$(config)-$(product)-$(build)
@$(MAKE) -f makefiles/main.mk \
SUB_TARGET=$(target) \
CONFIG="" \
PRODUCT=$(product) \
BUILD=$(build) \
INSTALL_NAME=$(install_name) \
IMAGE_WITH_HEADER=$(SRCROOT)/apps/EmbeddedIOP/EmbeddedIOPFirmware.h \
BIN_INCLUDES_BSS=true \
$(action)
help:
@echo Valid TARGETS
@echo " $(VALID_TARGETS)"
@echo Valid BUILDS
@echo " $(VALID_BUILDS)"
@echo ""

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2008-2010 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.
*/
/* EATLINE
* This file is consumed to auto-generate the firmware specfication. EATLINE
* EATLINE
* A line which contains the word EATLINE will be removed. EATLINE
* EATLINE
* Substitution is performed on comment lines beginning with SUBST. Fields EATLINE
* on these lines are separated by ':' characters. EATLINE
* The second field on the line determines the form of substitution, and the fifth EATLINE
* field supplies termination. EATLINE
* EATLINE
* ENV Declares and initialiases a scalar variable with value taken from the EATLINE
* compile-time environment. The variable preamble is read EATLINE
* from the third field, and the environment variable name from EATLINE
* the fourth. EATLINE
* EATLINE
* ENVSTR Declares and initialiases a string variable with value taken from the EATLINE
* compile-time environment. The variable preamble is read EATLINE
* from the third field, and the environment variable name from EATLINE
* the fourth. EATLINE
* EATLINE
* VAR Declares a variable with value taken from the object file. EATLINE
* The variable declaration is read from the third field, and EATLINE
* the symbol name to be looked up in the object file is read EATLINE
* from the fourth. EATLINE
* EATLINE
* FILE The binary file (named in the compile-time environment) is EATLINE
* inserted in the form of lines of 16 comma-separated 8-bit EATLINE
* hex constants, suitable for use as initialisers for an array EATLINE
* of unsigned char. EATLINE
* EATLINE */
/*SUBST:ENVSTR:static const char *fwFirmwareTarget __attribute__((used)) = :SUB_TARGET:;:*/
/*SUBST:ENVSTR:static const char *fwFirmwareBuild __attribute__((used)) = :BUILD:;:*/
/*SUBST:ENVSTR:static const char *fwFirmwareVersion __attribute__((used)) = :XBS_BUILD_TAG:;:*/
/*SUBST:VAR:static unsigned long fwConfigurationOffset __attribute__((used)) = :__iop_config:;:*/
/*SUBST:VAR:static unsigned long fwHeapBaseOffset __attribute__((used)) = :_heap_base:;:*/
/*SUBST:VAR:static unsigned long fwHeapSizeOffset __attribute__((used)) = :_heap_size:;:*/
/*SUBST:VAR:static unsigned long fwPageTableBase __attribute__((used)) = :_tt:;:*/
/*SUBST:VAR:static unsigned long fwPageTableSize __attribute__((used)) = :_tt_size:;:*/
/* IOP panic debug support */
/*SUBST:VAR:static unsigned long fwPanicStr __attribute__((used)) = :_gPanicStr:;:*/
/*SUBST:VAR:static unsigned long fwPanicFunc __attribute__((used)) = :_gPanicFunc:;:*/
/*SUBST:VAR:static unsigned long fwPanicLog __attribute__((used)) = :_gIOPPanicLog:;:*/
/*SUBST:VAR:static unsigned long fwPanicBytes __attribute__((used)) = :_gIOPPanicBytes:;:*/
static unsigned long fwPanicBytesMax __attribute__((used)) =
/*SUBST:ENV::IOP_PANIC_LOG_SIZE:;:*/
/*EATLINE XXX this may no longer need the section attribute... */
static unsigned char fwFirmwareImage[] __attribute__ ((aligned(4096))) __attribute__ ((section("__DATA,fwFirmwareImage"))) = {
/*SUBST:FILE:*/
};
/* heap requirements from the IOP configuration */
static unsigned long fwHeapRequired __attribute__((used)) =
/*SUBST:ENV::IOP_HEAP_REQUIRED:;:*/
/* message channel size */
static unsigned long fwMessageChannelSize __attribute__((used)) =
/*SUBST:ENV::IOP_MESSAGE_CHANNEL_SIZE:;:*/
#endif /* _DO_NOT_DEFINE *//* EATLINE */

View File

@ -0,0 +1,212 @@
/*
* Copyright (C) 2008 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.
*/
/*
* This file defines constants and data structures that comprise the interface protocol between the
* OS driver and the generic IOP firmware.
*/
/*
* Patch structure.
*
* The IOP firmware contains the original version of this structure. The host driver
* patches it to inform the IOP firmware of its runtime environment before starting it.
*
*/
struct iop_channel_config {
uint32_t ring_base;
uint32_t ring_size;
};
struct iop_configuration {
uint32_t magic;
#define IOP_CONFIG_MAGIC 'cnfg'
uint32_t options;
#define IOP_OPTION_NO_IDLE (1<<0) /* don't idle */
#define IOP_OPTION_DO_CLOCK_MGMNT (1<<1) /* do clock management */
/* console message buffer */
uint32_t message_buffer;
/* (optional) task deep idle timeout */
uint32_t deep_idle_us;
/* control channels */
#define IOP_MAX_CHANNELS 10
struct iop_channel_config channel[IOP_MAX_CHANNELS];
#define IOP_CONTROL_CHANNEL 0
#define IOP_CONTROL_CHANNEL_SIZE 2
#define IOP_MESSAGE_CHANNEL 1
/* note the message channel size is defined on a per-firmware-image basis */
#define SDIO_CONTROL_CHANNEL 3
#define SDIO_CONTROL_CHANNEL_SIZE 2
#define FMI_CONTROL_CHANNEL0 5
#define FMI_CONTROL_CHANNEL1 6
#define FMI_CONTROL_CHANNEL_SIZE 3
#define AUDIO_CONTROL_CHANNEL 7
#define AUDIO_CONTROL_CHANNEL_SIZE 2
#define AUDIODSP_TIMER_CHANNEL 2
#define AUDIODSP_TIMER_CHANNEL_SIZE 2
#define AUDIODSP_CONTROL_CHANNEL 4
#define AUDIODSP_CONTROL_CHANNEL_SIZE 2
#define AE2_WFI_ENDPOINT_CHANNEL 8
#define AE2_WFI_ENDPOINT_CHANNEL_SIZE 2
};
/*
* Default channel definitions; these channels are prototyped by
* the endpoint driver before starting the IOP, so they are
* safe for the IOP firmware to touch at startup.
*/
static struct {
uint32_t channel_index;
uint32_t channel_size;
uint32_t producer;
} iop_default_channels[] __attribute__ ((unused)) = {
{ SDIO_CONTROL_CHANNEL, SDIO_CONTROL_CHANNEL_SIZE, true },
{ FMI_CONTROL_CHANNEL0, FMI_CONTROL_CHANNEL_SIZE, true },
{ FMI_CONTROL_CHANNEL1, FMI_CONTROL_CHANNEL_SIZE, true },
{ AUDIO_CONTROL_CHANNEL, AUDIO_CONTROL_CHANNEL_SIZE, true },
{ AUDIODSP_CONTROL_CHANNEL, AUDIODSP_CONTROL_CHANNEL_SIZE, true },
{ AUDIODSP_TIMER_CHANNEL, AUDIODSP_TIMER_CHANNEL_SIZE, false },
{ AE2_WFI_ENDPOINT_CHANNEL, AE2_WFI_ENDPOINT_CHANNEL_SIZE, true },
{ 0, 0, 0 }
};
/*
* Host control pipe commands.
*/
#define IOP_CMD_NOP 'nop '
#define IOP_CMD_TERMINATE 'halt'
#define IOP_CMD_TTYIN 'ttin'
#define IOP_CMD_SLEEP 'slep'
#define IOP_CMD_SUSPEND 'spnd'
#define IOP_CMD_RESUME 'rsum'
#define IOP_CMD_INSTRUMENT 'inst'
#define IOP_RESULT_SUCCESS 0
#define IOP_RESULT_ERROR 1
#define IOP_RESULT_IN_PROGRESS ~0
struct iop_command_generic {
uint32_t opcode;
uint32_t result;
};
/*
* Send a character to the debug input routine.
*/
struct iop_command_ttyin {
struct iop_command_generic gen;
uint32_t c;
};
/* Instrumentation */
struct iop_command_instrument {
struct iop_command_generic gen;
UInt64 uptime_ticks;
UInt64 deep_idle_ticks;
UInt64 deep_idles;
UInt64 idle_ticks;
UInt64 idles;
UInt32 threshold_us;
UInt32 ticksHz;
};
/* Ping command */
struct iop_command_ping {
uint32_t opcode;
uint32_t result;
uint32_t ping_id;
};
/*
* Command union must be a multiple of the IOP cacheline size
* because the IOP will invalidate it out of the cache before
* processing it.
*/
#define IOP_ROUNDUP(k,b) (((k) + ((b) - 1)) & ~((b) - 1))
#if defined(CPU_CACHELINE_SIZE) && (CPU_CACHELINE_SIZE > 32)
#define IOP_COMMAND_MAX IOP_ROUNDUP(sizeof(struct iop_command_instrument), CPU_CACHELINE_SIZE)
#else
#define IOP_COMMAND_MAX IOP_ROUNDUP(sizeof(struct iop_command_instrument), 32)
#endif
union iop_command {
struct iop_command_generic generic;
struct iop_command_ttyin ttyin;
struct iop_command_instrument instr;
struct iop_command_ping ping;
uint8_t _pad[IOP_COMMAND_MAX];
};
/*
* IOP to host notifications.
*/
struct iop_message_generic {
uint32_t opcode;
uint32_t size;
};
#define IOP_MSG_TTY 'tty ' /* console tty output */
#define IOP_MSG_PANIC 'pnic' /* firmware panic message */
#define IOP_MSG_TRACE 'trce' /* trace message */
#define IOP_MESSAGE_MAX 128
#define IOP_MSG_TTY_MAXLEN (IOP_MESSAGE_MAX - sizeof(struct iop_message_generic))
/*
* Console output message.
*/
struct iop_message_tty {
struct iop_message_generic gen;
char bytes[];
};
/*
* Trace message.
*/
struct iop_message_trace {
struct iop_message_generic gen;
uint32_t ident;
uint32_t arg[4];
uint64_t timestamp;
};
union iop_message {
struct iop_message_generic gen;
struct iop_message_tty tty;
struct iop_message_trace trace;
uint8_t _pad[IOP_MESSAGE_MAX];
};
struct iop_ping_tracker {
struct iop_command_ping record;
uint64_t timestamp;
};

View File

@ -0,0 +1,85 @@
# Copyright (C) 2007-2009, 2012, 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.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
#
# Global headers that are independing of product, target, etc.
#
GLOBAL_HEADERS += \
lib/mib/mib_nodes.h
ALL_OBJS += $(LOCAL_DIR)/debugcmds.o \
$(LOCAL_DIR)/main.o \
$(LOCAL_DIR)/clock_management.o \
$(LOCAL_DIR)/clock_stepping.o \
$(LOCAL_DIR)/qwi.o
OPTIONS += WITH_INTERRUPTS=1 \
QWI_MAX_CHANNELS=8 \
WITH_APPLICATION_PUTCHAR=1
WITH_NO_SECURITY := true
# basic heap allocation
export IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),28672)
ifeq ($(BUILD),DEBUG)
##############################################################################
# Debug build
#
# enable DCC console
WITH_DCC := true
OPTIONS += DEBUG_LEVEL=20 \
DCC_TX_BUFFER_SIZE=4096 \
APPLICATION_CONSOLE_BUFFER=4096 \
WITH_MENU=1
#OPTIONS += ARM_DCC_SYNCHRONOUS=1
# console 8K
# menu 5.5K
# commands ...
export IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),32768)
else
##############################################################################
# Release build
#
endif
##############################################################################
# Build configuration
MODULES += lib/heap \
sys
LIBRARY_MODULES += lib/libc \
lib/mib
GLOBAL_INCLUDES += $(LOCAL_DIR)
# defeat the default heap initialisation
OPTIONS += HEAP_SIZE=0
# configure the message channel
export IOP_MESSAGE_CHANNEL_SIZE = 8
# and the panic log buffer
export IOP_PANIC_LOG_SIZE = 32768
OPTIONS += IOP_PANIC_LOG_SIZE=$(IOP_PANIC_LOG_SIZE) \
WITH_PANIC_HOOKS=1
# basic protocol header
INSTALL_HEADERS := $(LOCAL_DIR)/EmbeddedIOPProtocol.h \
$(LOCAL_DIR)/qwi_protocol.h

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2009-2011 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 "clock_management.h"
#include <sys.h>
static bool sParticipateInClockManagement = false;
void SetParticipateInClockStateManagement(bool enable)
{
sParticipateInClockManagement = enable;
}
bool ParticipateInClockStateManagement()
{
return sParticipateInClockManagement;
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2009-2011 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.
*/
#ifndef __CLOCK_MANAGEMENT_H__
#define __CLOCK_MANAGEMENT_H__
#include <stdint.h>
void SetParticipateInClockStateManagement(bool enable);
bool ParticipateInClockStateManagement();
#endif // __CLOCK_MANAGEMENT_H__

View File

@ -0,0 +1,189 @@
/*
* Copyright (C) 2009-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.
*/
#include "clock_stepping.h"
#include "clock_management.h"
// clock management only supported on AE2 platforms (H4P, H4G, and H5P)
// On other systems, this will be a no-op
#if !(TARGET_S5L8940XAE2 || TARGET_S5L8945XAE2 || TARGET_S5L8950XAE2)
void SetClockState(clockrequest_reason_t reason, clockvalue_t value)
{
}
#else
#include <debug.h>
#include <AssertMacros.h>
#include <platform/soc/pmgr.h>
#include <drivers/audio/audio.h>
// helpful macros
#define AE2_CLK_CFG_PENDING (1 << 16)
#define AE2_PCLK_CFG_DIVIDER(_d) (((_d) & 0xFF) << 8)
#define AE2_PCLK_CFG_DIV_MASK AE2_PCLK_CFG_DIVIDER(0xFF)
#define AE2_ACLK_CFG_DIVIDER(_d) (((_d) & 0xFF) << 0)
#define AE2_ACLK_CFG_DIV_MASK AE2_ACLK_CFG_DIVIDER(0xFF)
// because PMGR.h on sl58950x does not use the rXXX syntax, define it here
#if TARGET_S5L8950XAE2
#define rPMGR_CLK_CFG_DIV_MASK PMGR_CLK_CFG_DIV_MASK
#define rPMGR_CLK_CFG_DIVIDER PMGR_CLK_CFG_DIVIDER
#define rPMGR_CLK_CFG_PENDING PMGR_CLK_CFG_PENDING
#endif
#if TARGET_S5L8940XAE2 || TARGET_S5L8945XAE2
static const size_t ClockStatesStepLowToHigh[][2] = {
{ 16, 1 },
{ 4, 1 },
{ 4, 2 },
{ 2, 2 },
};
#else
static const size_t ClockStatesStepLowToHigh[][2] = {
{ 8, 1 },
{ 2, 1 },
{ 2, 2 },
{ 1, 2 },
};
#endif
typedef enum
{
kInternalClockStateLow = 0,
kInternalClockStateHigh = 3,
} internal_clockstate_t;
uint32_t GetAudioClockDivider()
{
return rPMGR_AUDIO_CLK_CFG & rPMGR_CLK_CFG_DIV_MASK;
}
void SetAudioClockDivider(uint32_t value)
{
rPMGR_AUDIO_CLK_CFG = (rPMGR_AUDIO_CLK_CFG & ~rPMGR_CLK_CFG_DIV_MASK) | rPMGR_CLK_CFG_DIVIDER(value);
while (rPMGR_AUDIO_CLK_CFG & rPMGR_CLK_CFG_PENDING)
;
}
uint32_t GetACLKDivider()
{
return (rAE2_ACSCSR & AE2_ACLK_CFG_DIV_MASK) + 1;
}
void SetACLKDivider(uint32_t value)
{
rAE2_ACSCSR = (rAE2_ACSCSR & ~AE2_ACLK_CFG_DIV_MASK) | AE2_ACLK_CFG_DIVIDER(value - 1);
while (rAE2_ACSCSR & AE2_CLK_CFG_PENDING)
;
}
bool ConfirmClocksValid()
{
uint32_t AudioDivider = GetAudioClockDivider();
uint32_t ACLKDivider = GetACLKDivider();
dprintf(DEBUG_SPEW, "*** clocks at %d %d \n", AudioDivider, ACLKDivider);
return ((AudioDivider == ClockStatesStepLowToHigh[kInternalClockStateHigh][0]) &&
(ACLKDivider == ClockStatesStepLowToHigh[kInternalClockStateHigh][1]))
||
((AudioDivider == ClockStatesStepLowToHigh[kInternalClockStateLow][0]) &&
(ACLKDivider == ClockStatesStepLowToHigh[kInternalClockStateLow][1]));
}
void SetClocksLow()
{
if (GetAudioClockDivider() == ClockStatesStepLowToHigh[kInternalClockStateLow][0])
return;
uint32_t whichState = kInternalClockStateHigh;
while (whichState != kInternalClockStateLow)
{
// increment first, then apply the settings
--whichState;
SetAudioClockDivider(ClockStatesStepLowToHigh[whichState][0]);
SetACLKDivider(ClockStatesStepLowToHigh[whichState][1]);
}
}
void SetClocksHigh()
{
if (GetAudioClockDivider() == ClockStatesStepLowToHigh[kInternalClockStateHigh][0])
return;
uint32_t whichState = kInternalClockStateLow;
while (whichState != kInternalClockStateHigh)
{
// increment first, then apply the settings
++whichState;
SetAudioClockDivider(ClockStatesStepLowToHigh[whichState][0]);
SetACLKDivider(ClockStatesStepLowToHigh[whichState][1]);
}
}
void SetClockState(clockrequest_reason_t reason, clockvalue_t value)
{
static clockvalue_t currentRequestValue[kClockRequestMax];
clockvalue_t newValue = kClockValueHigh;
if (!ParticipateInClockStateManagement())
{
return ;
}
bool result = true;
dprintf(DEBUG_SPEW, "*** requesting clock to %d (for %d)***\n", value, reason);
// turn interrupts off
enter_critical_section();
// make sure our arguments are valid
require((result = value < kClockValueMax), Exit);
require((result = reason < kClockRequestMax), Exit);
// assert clocks in a valid state
require((result = ConfirmClocksValid()), Exit);
// We modify the actual value of the clock state depending on the value of the current requests
currentRequestValue[reason] = value;
// If all requests are for kClockValueLow, we switch to the slowest rate
uint32_t n;
for(n=0; n<kClockRequestMax; n++)
{
dprintf(DEBUG_SPEW, "*** clockReason[%d] = %d ***\n", n, currentRequestValue[n]);
if (kClockValueHigh==currentRequestValue[n])
{
break;
}
}
if (kClockRequestMax==n)
{
newValue = kClockValueLow;
}
if (newValue == kClockValueLow)
{
SetClocksLow();
}
if (newValue == kClockValueHigh)
{
SetClocksHigh();
}
// assert clocks in a valid state
require((result = ConfirmClocksValid()), Exit);
Exit:
dprintf(DEBUG_INFO, "*** setting clock to %s (result %d) ***\n", (newValue==kClockValueHigh)?"HIGH":"LOW", result);
// turn interrupts on
exit_critical_section();
}
#endif

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2009-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.
*/
#ifndef _CLOCK_STEPPING_H_
#define _CLOCK_STEPPING_H_
typedef enum
{
kClockRequestMessageProcess,
kClockRequestPowerManager,
kClockRequestLoopback,
kClockRequestMax
} clockrequest_reason_t;
typedef enum
{
kClockValueLow,
kClockValueHigh,
kClockValueMax
} clockvalue_t;
void SetClockState(clockrequest_reason_t reason, clockvalue_t value);
#endif // _CLOCK_STEPPING_H_

View File

@ -0,0 +1,33 @@
# Copyright (C) 2010 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.
#
###############################################################################
# Target configuration for the s5l8940x AE2 runtime
#
PLATFORM := s5l8940x
PLATFORM_VARIANT:= Audio
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
OPTIONS += MAP_SRAM_CACHED=1
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/audio
# We need C++ support
MODULES += lib/libc++
# functions we require
MODULES += apps/EmbeddedIOP/function_audio

View File

@ -0,0 +1,35 @@
# Copyright (C) 2009-2010 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.
#
###############################################################################
# Target configuration for the s5l8940x IOP runtime
#
PLATFORM := s5l8940x
PLATFORM_VARIANT:= IOP
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/pmgr \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/h2fmi \
drivers/arasan/sdio
# tune up the CDMA s/g list allocation
OPTIONS += CDMA_CHANNEL_CMDS=128
# functions we require
MODULES += apps/EmbeddedIOP/function_fmi \
apps/EmbeddedIOP/function_sdio

View File

@ -0,0 +1,36 @@
# Copyright (C) 2009-2010 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.
#
###############################################################################
# Target configuration for the s5l8942x IOP runtime
#
PLATFORM := s5l8940x
SUB_PLATFORM := s5l8942x
PLATFORM_VARIANT:= IOP
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/pmgr \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/h2fmi \
drivers/arasan/sdio
# tune up the CDMA s/g list allocation
OPTIONS += CDMA_CHANNEL_CMDS=128
# functions we require
MODULES += apps/EmbeddedIOP/function_fmi \
apps/EmbeddedIOP/function_sdio

View File

@ -0,0 +1,33 @@
# Copyright (C) 2010 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.
#
###############################################################################
# Target configuration for the s5l8945x AE2 runtime
#
PLATFORM := s5l8945x
PLATFORM_VARIANT:= Audio
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
OPTIONS += MAP_SRAM_CACHED=1
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/audio
# We need C++ support
MODULES += lib/libc++
# functions we require
MODULES += apps/EmbeddedIOP/function_audio

View File

@ -0,0 +1,34 @@
# Copyright (C) 2009 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.
#
###############################################################################
# Target configuration for the s5l8940x IOP runtime
#
PLATFORM := s5l8945x
PLATFORM_VARIANT:= IOP
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/pmgr \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/h2fmi \
drivers/arasan/sdio
# tune up the CDMA s/g list allocation
OPTIONS += CDMA_CHANNEL_CMDS=128
# functions we require
MODULES += apps/EmbeddedIOP/function_fmi \
apps/EmbeddedIOP/function_sdio

View File

@ -0,0 +1,34 @@
# Copyright (C) 2009-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.
#
###############################################################################
# Target configuration for the s5l8947x IOP runtime
#
PLATFORM := s5l8940x
SUB_PLATFORM := s5l8947x
PLATFORM_VARIANT:= IOP
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/pmgr \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/h2fmi
# tune up the CDMA s/g list allocation
OPTIONS += CDMA_CHANNEL_CMDS=128
# functions we require
MODULES += apps/EmbeddedIOP/function_fmi

View File

@ -0,0 +1,34 @@
# Copyright (C) 2010 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.
#
###############################################################################
# Target configuration for the s5l8950x AE2 runtime
#
PLATFORM := s5l8950x
PLATFORM_VARIANT:= Audio
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
OPTIONS += MAP_SRAM_CACHED=1
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/audio
# We need C++ support
MODULES += lib/libc++
# functions we require
MODULES += apps/EmbeddedIOP/function_audio \
apps/EmbeddedIOP/function_audiodsp

View File

@ -0,0 +1,35 @@
# Copyright (C) 2010 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.
#
###############################################################################
# Target configuration for the s5l8950x IOP runtime
#
PLATFORM := s5l8950x
PLATFORM_VARIANT:= IOP
ARCH := arm
TEXT_FOOTPRINT := 1024*1024
MODULES += platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/pmgr \
drivers/apple/a5iop \
drivers/apple/aic \
drivers/apple/h2fmi \
drivers/arasan/sdio
# tune up the CDMA s/g list allocation
OPTIONS += CDMA_CHANNEL_CMDS=128
# functions we require
MODULES += apps/EmbeddedIOP/function_fmi \
apps/EmbeddedIOP/function_sdio

View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
*
* This document is the property of Apple Computer, 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 Computer, Inc.
*/
#include <stdio.h>
#include <string.h>
#include <sys.h>
#include <sys/menu.h>
#if defined(WITH_MENU) && WITH_MENU
/* "md", "mdh", "mdb" */
int do_memdump(int argc, struct cmd_arg *args)
{
addr_t address;
size_t count;
int width;
size_t i;
/* default dump values */
static addr_t last_address = 0;
static size_t last_count = 0x100;
address = last_address;
count = last_count;
if (!strcmp(args[0].str, "md")) {
width = 32;
} else if (!strcmp(args[0].str, "mdh")) {
width = 16;
} else {
width = 8;
}
if (argc >= 2)
address = args[1].u;
if (argc >= 3)
count = args[2].u;
// printf("dumping memory at 0x%x, len 0x%x, width %d\n", address, count, width);
i = 0;
while (i < count) {
if ((i % 16) == 0) {
if (i != 0)
puts("\n");
printf("%p: ", (void *)(address + i));
}
switch (width) {
case 32:
printf("%08x ", *(uint32_t *)(address + i));
if ( 4 == (i & 0xf))
printf(" ");
i += 4;
break;
case 16:
printf("%04x ", *(uint16_t *)(address + i));
if ( 6 == (i & 0xf))
printf(" ");
i += 2;
break;
case 8:
printf("%02x ", *(uint8_t *)(address + i));
if ( 7 == (i & 0xf))
printf(" ");
i += 1;
break;
}
}
puts("\n");
/* save the values so we can continue next time */
last_count = count;
last_address = address + count;
return 0;
}
MENU_COMMAND_DEBUG(md, do_memdump, "memory display - 32bit", NULL);
MENU_COMMAND_DEBUG(mdh, do_memdump, "memory display - 16bit", NULL);
MENU_COMMAND_DEBUG(mdb, do_memdump, "memory display - 8bit", NULL);
/* "mw", "mwh", "mwb", "mws" */
int do_memwrite(int argc, struct cmd_arg *args)
{
static addr_t last_address = ~0UL;
addr_t address;
size_t length;
uint32_t data;
const char *buffer;
int width;
switch (argc) {
case 2:
if (~0UL == last_address) {
printf("%s - need an address\n", args[0].str);
return -1;
}
address = last_address;
data = args[1].u;
buffer = args[1].str;
break;
case 3:
address = args[1].u;
last_address = address;
data = args[2].u;
buffer = args[2].str;
break;
default:
printf("%s [<address>] <data>\n", args[0].str);
return -1;
}
if (!strcmp(args[0].str, "mw")) {
width = 32;
length = 4;
} else if (!strcmp(args[0].str, "mwh")) {
width = 16;
length = 2;
} else if (!strcmp(args[0].str, "mwb")) {
width = 8;
length = 1;
} else {
width = 255;
length = strlen(buffer) + 1;
}
// printf("writing memory at 0x%x, data 0x%x\n", address, data);
switch (width) {
case 32:
*(uint32_t *)address = data;
break;
case 16:
*(uint16_t *)address = data;
break;
case 8:
*(uint8_t *)address = data;
break;
case 255:
strlcpy((char *)address, buffer, length);
break;
}
return 0;
}
MENU_COMMAND_DEBUG(mw, do_memwrite, "memory write - 32bit", NULL);
MENU_COMMAND_DEBUG(mwh, do_memwrite, "memory write - 16bit", NULL);
MENU_COMMAND_DEBUG(mwb, do_memwrite, "memory write - 8bit", NULL);
MENU_COMMAND_DEBUG(mws, do_memwrite, "memory write - string", NULL);
int do_panic(int argc, struct cmd_arg *args)
{
panic("command prompt");
}
MENU_COMMAND_DEBUG(panic, do_panic, "...", NULL);
int do_hang(int argc, struct cmd_arg *args)
{
for (;;)
;
return(0);
}
MENU_COMMAND_DEBUG(hang, do_hang, "spin forever, hanging the system", NULL);
#endif

View File

@ -0,0 +1,557 @@
/*
* Copyright (C) 2009-2010 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 <AssertMacros.h>
#include <platform/memmap.h>
#include <platform.h>
#include <sys/task.h>
#include <iop.h>
#include <qwi.h>
#include <EmbeddedIOPProtocol.h>
#include <arch.h>
#include "iop_audio_protocol.h"
#include "AudioCodecA5.h"
#include "clock_stepping.h"
#ifdef DO_PROFILING
// doing peak heap check requires heap.c to support it
//#define DO_PEAKHEAPCHECK
// doing peak heap check requires task.c to support it
//#define DO_PEAKSTACKCHECK
#include <platform/timer.h>
#include <arch/arm/arm.h>
#define kPerformanceSize 16
struct PerformanceDetails
{
bool valid;
utime_t time;
uint32_t cycles;
};
struct PerformanceDetails performanceDetails[kPerformanceSize];
void PerformanceSnapShotReset()
{
memset(performanceDetails, 0, sizeof(performanceDetails));
}
void PerformanceSnapShotStart(uint32_t which)
{
if (which >= kPerformanceSize)
return;
performanceDetails[which].time = system_time();
performanceDetails[which].cycles = arm_read_pmreg(ARM_PMCCNTR);
}
void PerformanceSnapShotEnd(uint32_t which)
{
if (which >= kPerformanceSize)
return;
performanceDetails[which].valid = true;
performanceDetails[which].time = system_time() - performanceDetails[which].time;
performanceDetails[which].cycles = arm_read_pmreg(ARM_PMCCNTR) - performanceDetails[which].cycles;
}
void SetupProfiling(IOPAUDIO_METRICS* metrics)
{
// mapping from enum to event monitors from the A5 TRM
static const uint32_t eventtable[ACLKCycles-ICacheAccesses] = { 0x14, 0x1, 0x4, 0x3, 0x6, 0x7, 0xF, 0x8, 0x12, 0x10 };
// reset our user defined performance counters.
PerformanceSnapShotReset();
// select wich events we are going to monitor
uint32_t evt0 = 0x03; // by default we measure dcache fills
uint32_t evt1 = 0x04; // by default we measure dcache accesses
uint32_t i = ICacheAccesses;
// choose the event monitor to follow. We only choose the first two we find
for (;i < ACLKCycles; ++i)
{
if (metrics->mMetricsItemsValid & (1 << i))
{
evt0 = eventtable[(i - ICacheAccesses)];
break;
}
}
++i;
for (;i < ACLKCycles; ++i)
{
if (metrics->mMetricsItemsValid & (1 << i))
{
evt1 = eventtable[(i - ICacheAccesses)];
break;
}
}
#ifdef DO_PEAKSTACKCHECK
if (metrics->mMetricsItemsValid & (1<<PeakStackUsage))
reset_stack_usage("audio", 1024);
#endif // DO_PEAKSTACKCHECK
#ifdef DO_PEAKHEAPCHECK
if (metrics->mMetricsItemsValid & (1<<PeakHeapUsage))
heap_reset_peak_mem();
#endif // DO_PEAKHEAPCHECK
arm_write_pmreg(ARM_PMOVSR, ~0); /* clear overflow status */
arm_write_pmreg(ARM_PMSELR, 0); /* set event 0... */
arm_write_pmreg(ARM_PMXEVTYPER, evt0);
arm_write_pmreg(ARM_PMSELR, 1); /* set event 1... */
arm_write_pmreg(ARM_PMXEVTYPER, evt1);
arm_write_pmreg(ARM_PMCNTENSET, (1<<31) | (1<<1) | (1<<0)); /* enable cycles and events 0 and 1 */
arm_write_pmreg(ARM_PMCR, 2); /* clear counters */
arm_write_pmreg(ARM_PMCR, 1); /* enable all enabled counters */
metrics->mMetricsFields[ProcessTimeUSec] = system_time();
metrics->mMetricsFields[ProcessCycles] = arm_read_pmreg(ARM_PMCCNTR);
}
void EndProfiling(IOPAUDIO_METRICS* metrics)
{
uint32_t evt0, evt1, cycles;
arm_write_pmreg(ARM_PMCNTENCLR, ~0); /* disable all counters */
utime_t timeEnd = system_time();
arm_write_pmreg(ARM_PMSELR, 0);
evt0 = arm_read_pmreg(ARM_PMXEVCNTR); /* read event 0 */
arm_write_pmreg(ARM_PMSELR, 1);
evt1 = arm_read_pmreg(ARM_PMXEVCNTR); /* read event 1 */
cycles = arm_read_pmreg(ARM_PMCCNTR); /* read cycles */
if (arm_read_pmreg(ARM_PMOVSR))
dprintf(DEBUG_CRITICAL, "*** counter overflow ***\n");
uint32_t i = ICacheAccesses;
// choose the event monitor to follow. We only choose the first two we find
for (;i < ACLKCycles; ++i)
{
if (metrics->mMetricsItemsValid & (1 << i))
{
metrics->mMetricsFields[i] = evt0;
break;
}
}
++i;
for (;i < ACLKCycles; ++i)
{
if (metrics->mMetricsItemsValid & (1 << i))
{
metrics->mMetricsFields[i] = evt1;
break;
}
}
if (metrics->mMetricsItemsValid & (1<<ProcessTimeUSec))
metrics->mMetricsFields[ProcessTimeUSec] = timeEnd - metrics->mMetricsFields[ProcessTimeUSec];
if (metrics->mMetricsItemsValid & (1<<ProcessCycles))
metrics->mMetricsFields[ProcessCycles] = cycles - metrics->mMetricsFields[ProcessCycles];
if (metrics->mMetricsItemsValid & (1<<HeapInUsage))
metrics->mMetricsFields[HeapInUsage] = heap_get_free_mem();
#ifdef DO_PEAKHEAPCHECK
if (metrics->mMetricsItemsValid & (1<<PeakHeapUsage))
metrics->mMetricsFields[PeakHeapUsage] = heap_get_peak_mem();
#endif // DO_PEAKHEAPCHECK
#ifdef DO_PEAKSTACKCHECK
if (metrics->mMetricsItemsValid & (1<<PeakStackUsage))
metrics->mMetricsFields[PeakStackUsage] = get_stack_usage("audio");
#endif // DO_PEAKSTACKCHECK
for (uint32_t i = 0; i < kPerformanceSize; ++i)
{
if ((ReservedStart+i) >= MetricsFieldsSize)
break;
if (performanceDetails[i].valid)
{
metrics->mMetricsFields[ReservedStart+i] = performanceDetails[i].time;
metrics->mMetricsItemsValid |= 1<<(ReservedStart+i);
}
}
}
#else
void PerformanceSnapShotStart(uint32_t which) {}
void PerformanceSnapShotEnd(uint32_t which) {}
void SetupProfiling(IOPAUDIO_METRICS* metrics) {}
void EndProfiling(IOPAUDIO_METRICS* metrics) { metrics->mMetricsItemsValid = 0; }
#endif
static bool audio_message_process(void);
static int iop_audio_task(void *cfg);
static void iop_audio_sleep(int mode);
IOP_FUNCTION(audio, iop_audio_task, 64*1024, AUDIO_CONTROL_CHANNEL);
IOP_SLEEP_HOOK(audio, iop_audio_sleep);
static int audio_channel;
#define kMaxCodecs 8
static AudioCodec sTokens[kMaxCodecs] = { NULL, };
// return true if registered, false if could not.
static bool
Register(AudioCodec audioCodec)
{
for (size_t i = 0; i < kMaxCodecs; ++i)
if (!sTokens[i])
{
sTokens[i] = audioCodec;
return true;
}
return false;
}
static void
Unregister(AudioCodec audioCodec)
{
for (size_t i = 0; i < kMaxCodecs; ++i)
if (sTokens[i] == audioCodec)
sTokens[i] = NULL;
}
static bool
isAudioCodecValid(AudioCodec audioCodec)
{
for (size_t i = 0; i < kMaxCodecs; ++i)
if (sTokens[i] == audioCodec)
return true;
return false;
}
static int
iop_audio_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
struct task_event* audio_message_event = (struct task_event*) malloc(sizeof(struct task_event));
dprintf(DEBUG_SPEW, "@@ Audio task starting\n");
check(kIOPAUDIO_COMMAND_SIZE == sizeof(IOPAUDIO_Command));
/* establish the host communications channel */
event_init(audio_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "** opening audio channel\n");
audio_channel = qwi_instantiate_channel("audio command",
QWI_ROLE_CONSUMER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
(qwi_channel_hook)event_signal,
audio_message_event);
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
/* Little doubt we'll need VFP/Neon */
arch_task_fp_enable(true);
#endif
for (;;) {
dprintf(DEBUG_SPEW, "@@ waiting for message on audio channel\n");
while (audio_message_process()) {
// eat all available messages
}
event_wait(audio_message_event);
}
return(0);
}
static bool
audio_message_process(void)
{
uint32_t message;
IOPAUDIO_Command* command;
dprintf(DEBUG_SPEW, "@@ handling host message\n");
/* look to see if there's an item waiting for us */
if (qwi_receive_item(audio_channel, &message) == -1)
return(false);
// set the clocks high
SetClockState(kClockRequestMessageProcess, kClockValueHigh);
dprintf(DEBUG_SPEW, "@@ received audio message\n");
/* find the command structure based on the message */
command = (IOPAUDIO_Command*)mem_static_map_cached(message);
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the command size is a multiple of our cacheline size.
*/
platform_cache_operation(CACHE_INVALIDATE,
(void *)command,
sizeof(*command));
/*
* TODO: make this part of the API and push this
* architecture-specific command handling down into the s5l8920x
* platform directory.
*/
switch (command->iopaudio.mOpcode) {
case kIOPAUDIO_OPCODE_CREATE:
{
AudioComponent comp = NULL;
AudioComponentDescription desc;
desc.componentType = command->create.mComponentDesc.mComponentType;
desc.componentSubType = command->create.mComponentDesc.mComponentSubType;
desc.componentManufacturer = command->create.mComponentDesc.mComponentManufacturer;
desc.componentFlags = command->create.mComponentDesc.mComponentFlags;
desc.componentFlagsMask = command->create.mComponentDesc.mComponentFlagsMask;
comp = AudioComponentFindNext(comp, &desc);
if(comp == NULL)
{
dprintf(DEBUG_CRITICAL, "Could not find AudioComponent\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_CODECERROR;
break;
}
dprintf(DEBUG_CRITICAL, "found component\n");
AudioCodec audioCodec = NULL;
size_t err = AudioComponentInstanceNew(comp, &audioCodec);
command->iopaudio.Codec.mCodecStatus = err;
if (err != 0 || audioCodec == NULL)
{
dprintf(DEBUG_CRITICAL, "Could not create audioCodec\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_CODECERROR;
break;
}
const void *magicCookie = (command->create.mAdditionalParametersSizeBytes) ? command->create.mAdditionalParameters : 0;
err = AudioCodecInitialize(audioCodec, &(command->create.mInputFormat), &(command->create.mOutputFormat), magicCookie, command->create.mAdditionalParametersSizeBytes);
command->iopaudio.Codec.mCodecStatus = err;
if (err != 0)
{
dprintf(DEBUG_CRITICAL, "Could not initialize audioCodec\n");
AudioComponentInstanceDispose(audioCodec);
command->iopaudio.mStatus = kIOPAUDIO_STATUS_CODECERROR;
break;
}
if (!Register(audioCodec))
{
dprintf(DEBUG_CRITICAL, "Could not register audioCodec\n");
AudioComponentInstanceDispose(audioCodec);
command->iopaudio.mStatus = kIOPAUDIO_STATUS_CODECERROR;
break;
}
command->iopaudio.mStatus = kIOPAUDIO_STATUS_SUCCESS;
command->iopaudio.Codec.mIOPToken = (uint32_t)audioCodec;
dprintf(DEBUG_CRITICAL, "Created codec with id %x\n", command->iopaudio.Codec.mIOPToken);
}
break;
case kIOPAUDIO_OPCODE_DESTROY:
{
AudioCodec audioCodec = (AudioCodec)command->iopaudio.Codec.mIOPToken;
if (!isAudioCodecValid(audioCodec))
{
dprintf(DEBUG_CRITICAL, "Codec not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
AudioComponentInstanceDispose(audioCodec);
Unregister(audioCodec);
command->iopaudio.mStatus = kIOPAUDIO_STATUS_SUCCESS;
}
break;
case kIOPAUDIO_OPCODE_RESET:
{
AudioCodec audioCodec = (AudioCodec)command->iopaudio.Codec.mIOPToken;
if (!isAudioCodecValid(audioCodec))
{
dprintf(DEBUG_CRITICAL, "Codec not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
size_t err = AudioCodecReset(audioCodec);
command->iopaudio.Codec.mCodecStatus = err;
command->iopaudio.mStatus = kIOPAUDIO_STATUS_SUCCESS;
}
break;
case kIOPAUDIO_OPCODE_GETPROPINFO:
{
AudioCodec audioCodec = (AudioCodec)command->iopaudio.Codec.mIOPToken;
if (isAudioCodecValid(audioCodec))
{
dprintf(DEBUG_CRITICAL, "Codec not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
size_t err = AudioCodecGetPropertyInfo(audioCodec, command->get_propinfo.mPropertyID, (size_t*)&command->get_propinfo.mPropertySize, (bool*)&command->get_propinfo.mPropertyWritable);
command->iopaudio.Codec.mCodecStatus = err;
command->iopaudio.mStatus = kIOPAUDIO_STATUS_SUCCESS;
}
break;
case kIOPAUDIO_OPCODE_GETPROPERTY:
{
AudioCodec audioCodec = (AudioCodec)command->iopaudio.Codec.mIOPToken;
if (isAudioCodecValid(audioCodec))
{
dprintf(DEBUG_CRITICAL, "Codec not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
// <rdar://problem/8466788> update getProperty and setProperty to limit property address
size_t max_size_for_property = sizeof(*command) - ((uint32_t)&command->get_property.mPropertyData - (uint32_t)command);
if (max_size_for_property < command->get_property.mPropertySizeBytes)
{
dprintf(DEBUG_CRITICAL, "Parameters not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
// the message should be cleaned before and after we use it, so do not need to clean ourselves
void* property = (void*)command->get_property.mPropertyData;
size_t err = AudioCodecGetProperty(audioCodec, command->get_property.mPropertyID, (size_t*)&command->get_property.mPropertySizeBytes, property);
command->iopaudio.Codec.mCodecStatus = err;
command->iopaudio.mStatus = kIOPAUDIO_STATUS_SUCCESS;
}
break;
case kIOPAUDIO_OPCODE_SETPROPERTY:
{
AudioCodec audioCodec = (AudioCodec)command->iopaudio.Codec.mIOPToken;
if (isAudioCodecValid(audioCodec))
{
dprintf(DEBUG_CRITICAL, "Codec not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
// <rdar://problem/8466788> update getProperty and setProperty to limit property address
size_t max_size_for_property = sizeof(*command) - ((uint32_t)&command->get_property.mPropertyData - (uint32_t)command);
if (max_size_for_property < command->get_property.mPropertySizeBytes)
{
dprintf(DEBUG_CRITICAL, "Parameters not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
// the message should be cleaned before and after we use it, so do not need to clean ourselves
void* property = (void*)command->get_property.mPropertyData;
size_t err = AudioCodecSetProperty(audioCodec, command->set_property.mPropertyID, command->set_property.mPropertySizeBytes, property);
command->iopaudio.Codec.mCodecStatus = err;
command->iopaudio.mStatus = kIOPAUDIO_STATUS_SUCCESS;
}
break;
case kIOPAUDIO_OPCODE_PROCESSFRAME:
{
AudioCodec audioCodec = (AudioCodec)command->iopaudio.Codec.mIOPToken;
if (!isAudioCodecValid(audioCodec))
{
dprintf(DEBUG_CRITICAL, "Codec not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
// <rdar://problem/8486723> function_audio should make sure parameters to processSingleFrame (input, output buffers) are valid
// make sure the parameters are not-null.
if (!command->process_frame.mSrcAddr
|| !command->process_frame.mSrcSizeBytes
|| !command->process_frame.mDstAddr
|| !command->process_frame.mDstSizeBytes)
{
dprintf(DEBUG_CRITICAL, "Parameters not valid\n");
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
const void* src = (const void*)mem_static_map_cached(command->process_frame.mSrcAddr);
void* dst = (void*)mem_static_map_cached(command->process_frame.mDstAddr);
// the onus is on the caller to make sure the cache is aligned
void* lineBase = (void*)((uint32_t)src & ~(CPU_CACHELINE_SIZE-1));
size_t sizeToClean = ((((uint32_t)src & (CPU_CACHELINE_SIZE-1)) + command->process_frame.mSrcSizeBytes)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, lineBase, sizeToClean);
SetupProfiling(&command->process_frame.mMetrics);
void* packet_dst = dst;
uint16_t numPackets = command->process_frame.mNumPackets;
size_t remaining_dst_size = command->process_frame.mDstSizeBytes;
size_t total_dst_size = 0;
size_t total_src_size = 0;
//sanity check, if the multi frame fields are not used
if(0 == numPackets)
{
numPackets = 1;
command->process_frame.mPacketSize[0] = command->process_frame.mSrcSizeBytes;
}
for(uint16_t i = 0; i < numPackets; ++i)
{
size_t src_size = command->process_frame.mPacketSize[i];
//make sure we dont go pass all of the valid input data
if(total_src_size + src_size > command->process_frame.mSrcSizeBytes)
break;
size_t packet_dst_size = remaining_dst_size; //the current mp3 decoder doesn't take dst size that's bigger than the packet output size
dprintf(DEBUG_SPEW, "IOP_Audio start %d of %d, input %p size %zu, output %p, size %zu\n", i+1, numPackets, src, src_size, packet_dst, packet_dst_size);
size_t err = AudioCodecProcessSinglePacket(audioCodec, src, &src_size, packet_dst, &packet_dst_size);
command->iopaudio.Codec.mCodecStatus = err;
if(err)
break;
packet_dst += packet_dst_size;
remaining_dst_size -= packet_dst_size;
total_dst_size += packet_dst_size;
src += src_size;
total_src_size += src_size;
//for encoding, write the size back to the msg
command->process_frame.mPacketSize[i] = packet_dst_size;
}
command->process_frame.mSrcSizeBytes = total_src_size;
command->process_frame.mDstSizeBytes = total_dst_size;
//dprintf(DEBUG_CRITICAL, "Decoded/Encoded Total SrcSize %d, dstSize %d\n", command->process_frame.mSrcSizeBytes, command->process_frame.mDstSizeBytes);
EndProfiling(&command->process_frame.mMetrics);
// the onus is on the caller to make sure the cache is aligned
lineBase = (void*)((uint32_t)dst & ~(CPU_CACHELINE_SIZE-1));
sizeToClean = ((((uint32_t)dst & (CPU_CACHELINE_SIZE-1)) + command->process_frame.mDstSizeBytes)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
platform_cache_operation(CACHE_CLEAN, lineBase, sizeToClean);
command->iopaudio.mStatus = kIOPAUDIO_STATUS_SUCCESS;
}
break;
default:
dprintf(DEBUG_CRITICAL, "@@ ERROR: unrecognised audio opcode 0x%x\n",
command->iopaudio.mOpcode);
command->iopaudio.mStatus = kIOPAUDIO_STATUS_PARAM_INVALID;
break;
}
dprintf(DEBUG_SPEW, "@@ done processing audio message with status 0x%08x\n", command->iopaudio.mStatus);
platform_cache_operation(CACHE_CLEAN,
(void *)command,
sizeof(IOPAUDIO_Command));
qwi_send_item(audio_channel, message);
dprintf(DEBUG_SPEW, "@@ signaled completion of audio message to host\n");
// set the clocks low
SetClockState(kClockRequestMessageProcess, kClockValueLow);
return(true);
}
static void
iop_audio_sleep(int mode)
{
}

View File

@ -0,0 +1,292 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef _IOP_AUDIO_PROTOCOL_H_
#define _IOP_AUDIO_PROTOCOL_H_
#include <sys/types.h>
/*
* Command size is (somewhat) tunable.
*
* The principal consideration here is the maximum scatter/gather list size
* this permits.
*/
#define kIOPAUDIO_COMMAND_SIZE (512)
/*
* IOPAUDIO_opcode_t: identifies the command sent and from the AE2
*/
typedef uint32_t IOPAUDIO_opcode_t;
#define kIOPAUDIO_OPCODE_UNKNOWN ((IOPAUDIO_opcode_t) 0)
/*
* Commands for communicating with AudioCodecs library
*/
#define kIOPAUDIO_OPCODE_CREATE ((IOPAUDIO_opcode_t) 3)
#define kIOPAUDIO_OPCODE_DESTROY ((IOPAUDIO_opcode_t) 4)
#define kIOPAUDIO_OPCODE_RESET ((IOPAUDIO_opcode_t) 5)
#define kIOPAUDIO_OPCODE_PROCESSFRAME ((IOPAUDIO_opcode_t) 6)
#define kIOPAUDIO_OPCODE_GETPROPINFO ((IOPAUDIO_opcode_t) 7)
#define kIOPAUDIO_OPCODE_GETPROPERTY ((IOPAUDIO_opcode_t) 8)
#define kIOPAUDIO_OPCODE_SETPROPERTY ((IOPAUDIO_opcode_t) 9)
/*
* IOPAUDIO_status_t: result from commands
*/
typedef uint32_t IOPAUDIO_status_t;
#define kIOPAUDIO_STATUS_UNKNOWN ((IOPAUDIO_status_t) 0)
#define kIOPAUDIO_STATUS_SUCCESS ((IOPAUDIO_status_t) 1)
#define kIOPAUDIO_STATUS_FAILURE ((IOPAUDIO_status_t) 0x80000000)
#define kIOPAUDIO_STATUS_DEVICE_ERROR ((IOPAUDIO_status_t) 0x80000001)
#define kIOPAUDIO_STATUS_DEVICE_TIMEOUT ((IOPAUDIO_status_t) 0x80000002)
#define kIOPAUDIO_STATUS_DMA_TIMEOUT ((IOPAUDIO_status_t) 0x80000003)
#define kIOPAUDIO_STATUS_PARAM_INVALID ((IOPAUDIO_status_t) 0x80000004)
#define kIOPAUDIO_STATUS_UNIMPLEMENTED ((IOPAUDIO_status_t) 0x80000005)
#define kIOPAUDIO_STATUS_CODECERROR ((IOPAUDIO_status_t) 0x80000010)
/*
* IOPAUDIO_token_t: The opaque token to use for audio processing
*/
typedef uint32_t IOPAUDIO_token_t;
typedef uint32_t IOPAUDIO_codecstatus_t;
/*
* IOPAUDIO_CODEC contains the pair of token and codec status
*/
struct _IOPAUDIO_CODEC
{
IOPAUDIO_token_t mIOPToken;
IOPAUDIO_codecstatus_t mCodecStatus;
};
typedef struct _IOPAUDIO_CODEC IOPAUDIO_CODEC;
// Token for sending messages to the iop_audio system itself
#define kIOPAUDIO_system_token_t ((IOPAUDIO_opcode_t) 0xFFFFFFFF)
/*
* IOPAUDIO_status_t result from commands
*/
struct _IOPAUDIO
{
IOPAUDIO_opcode_t mOpcode;
IOPAUDIO_status_t mStatus;
IOPAUDIO_CODEC Codec;
};
typedef struct _IOPAUDIO IOPAUDIO;
struct _FunctionAudioAudioComponentDescription
{
uint32_t mComponentType;
uint32_t mComponentSubType;
uint32_t mComponentManufacturer;
uint32_t mComponentFlags;
uint32_t mComponentFlagsMask;
};
typedef struct _FunctionAudioAudioComponentDescription FunctionAudioAudioComponentDescription;
struct _FunctionAudioAudioStreamBasicDescription
{
uint64_t mSampleRate;
uint32_t mFormatID;
uint32_t mFormatFlags;
uint32_t mBytesPerPacket;
uint32_t mFramesPerPacket;
uint32_t mBytesPerFrame;
uint32_t mChannelsPerFrame;
uint32_t mBitsPerChannel;
uint32_t mReserved;
};
typedef struct _FunctionAudioAudioStreamBasicDescription FunctionAudioAudioStreamBasicDescription;
enum MetricsFields
{
ProcessTimeUSec = 0,
ProcessCycles,
PeakStackUsage,
HeapInUsage,
PeakHeapUsage,
ICacheAccesses, // these are measured by A5 performance monitors
ICacheMisses,
DCacheAccesses,
DCacheMisses,
Loads,
Stores,
UnalignedLoadStores,
InstructionsExecuted,
CorrectPredictedBranches,
IncorrectPredictedBranches,
ACLKCycles, // these are measured by AE2 performance monitors
ACLKCyclesInWFI,
SRAMBytesRead,
SRAMBytesWrite,
SRAMBytesReadWrite,
DRAMBytesRead,
DRAMBytesWrite,
DRAMBytesReadWrite,
ReservedStart,
MetricsFieldsSize = 32
};
struct _IOPAUDIO_METRICS
{
uint32_t mMetricsItemsValid; // bitmap marking which values are valid
uint32_t mMetricsFields[MetricsFieldsSize];
};
typedef struct _IOPAUDIO_METRICS IOPAUDIO_METRICS;
/*
* Messages. Message layout is dependent on the opcode. For instance, if
* the opcode is kIOPAUDIO_OPCODE_CREATE, then the message will be interpreted
* as IOPAUDIO_CREATE.
* It is the senders responsiblity to make sure a message is set correctly.
*/
/*
* IOPAUDIO_CREATE message
* Message used to create a new audio process.
* If message succeeds, iopToken will contain the token used for processing.
* Fill the input and output with necessary parameters. Additional parameters
* passed through the additional_paramters pointer.
*
* It is decoder specific how additional data is laid out.
*/
struct _IOPAUDIO_CREATE
{
IOPAUDIO mIOPHeader;
FunctionAudioAudioComponentDescription mComponentDesc;
FunctionAudioAudioStreamBasicDescription mInputFormat;
FunctionAudioAudioStreamBasicDescription mOutputFormat;
uint32_t mAdditionalParametersSizeBytes;
uint8_t mAdditionalParameters[];
};
typedef struct _IOPAUDIO_CREATE IOPAUDIO_CREATE;
/*
* IOPAUDIO_DESTROY message
* Message used to destroy a token.
* If message succeeds, iopToken is no longer valid.
*/
struct _IOPAUDIO_DESTROY
{
IOPAUDIO mIOPHeader;
};
typedef struct _IOPAUDIO_DESTROY IOPAUDIO_DESTROY;
/*
* IOPAUDIO_RESET message
* Message used to reset an audio process
* If message succeeds, iopToken is reset to the starting state.
*/
struct _IOPAUDIO_RESET
{
IOPAUDIO mIOPHeader;
};
typedef struct _IOPAUDIO_RESET IOPAUDIO_RESET;
/*
* IOPAUDIO_GETPROPINFO message
* Message used to get property information. mPropertyID is the 4-char code
* indicating what property to get info on.
* If message succeeds, size and writablility are set.
*/
struct _IOPAUDIO_GETPROPINFO
{
IOPAUDIO mIOPHeader;
uint32_t mPropertyID;
uint32_t mPropertySize;
uint32_t mPropertyWritable;
};
typedef struct _IOPAUDIO_GETPROPINFO IOPAUDIO_GETPROPINFO;
/*
* IOPAUDIO_GETPROPERTY message
* Message used to get property. mPropertyID is the 4-char code indicating what
* property to get.
* Addr passed in is the physical address where should be written. It can
* point within the message or point to some other buffer that IOP can access.
* If message succeeds, the property is written to the Addr.
*/
struct _IOPAUDIO_GETPROPERTY
{
IOPAUDIO mIOPHeader;
uint32_t mPropertyID;
uint32_t mPropertySizeBytes;
uint8_t mPropertyData[];
};
typedef struct _IOPAUDIO_GETPROPERTY IOPAUDIO_GETPROPERTY;
/*
* IOPAUDIO_SETPROPERTY message
* Message used to set property. mPropertyID is the 4-char code indicating what
* property to set.
* Addr passed in is the physical address which has the data to write. It can
* point within the message or point to some other buffer that IOP can access.
* If message succeeds, the property is written to the Addr.
*/
struct _IOPAUDIO_SETPROPERTY
{
IOPAUDIO mIOPHeader;
uint32_t mPropertyID;
uint32_t mPropertySizeBytes;
uint8_t mPropertyData[];
};
typedef struct _IOPAUDIO_SETPROPERTY IOPAUDIO_SETPROPERTY;
/*
* IOPAUDIO_PROCESSFRAME message
* Process audio from the src_addr to the dst_addr. On input, src_size is
* the size of the source data, dst_size is the size available to write into.
* On output, src_size is the amount of data consumed, dst_size is the amount
* of data written.
* Metrics may or may not be filled in, depending on the build.
*/
struct _IOPAUDIO_PROCESSFRAME
{
IOPAUDIO mIOPHeader;
uint32_t mSrcAddr;
uint32_t mDstAddr;
uint32_t mSrcSizeBytes;
uint32_t mDstSizeBytes;
IOPAUDIO_METRICS mMetrics;
//add packet information for multple packets
uint16_t mNumPackets;
uint16_t mPacketSize[];
};
typedef struct _IOPAUDIO_PROCESSFRAME IOPAUDIO_PROCESSFRAME;
union _IOPAUDIO_Command
{
IOPAUDIO iopaudio;
IOPAUDIO_CREATE create;
IOPAUDIO_DESTROY destroy;
IOPAUDIO_RESET reset;
IOPAUDIO_PROCESSFRAME process_frame;
IOPAUDIO_GETPROPINFO get_propinfo;
IOPAUDIO_GETPROPERTY get_property;
IOPAUDIO_SETPROPERTY set_property;
uint8_t _pad[kIOPAUDIO_COMMAND_SIZE];
};
typedef union _IOPAUDIO_Command IOPAUDIO_Command;
#endif // _IOP_AUDIO_PROTOCOL_H_

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2010 Apple Inc. All rights reserved.
*
* This document is the property of Apple Computer, 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 Computer, Inc.
*/
/*
* Empty namespace std stubs
*/
namespace std
{
void __throw_bad_alloc()
{}
void __throw_length_error(char const*)
{}
};

View File

@ -0,0 +1,35 @@
# Copyright (C) 2010 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.
#
#
# Audio driver
#
LOCAL_DIR := $(GET_LOCAL_DIR)
OPTIONS += WITH_FUNCTION_AUDIO=1
IOP_FUNCTIONS += AUDIO
ALL_OBJS += $(LOCAL_DIR)/iop_audio.o \
$(LOCAL_DIR)/libstd_stub.o
INSTALL_HEADERS += $(LOCAL_DIR)/iop_audio_protocol.h
# We need 64 k for stack
# and 372064 for heap
# 192512 of which will be in SRAM
export IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),245088)
GLOBAL_INCLUDES += $(SDKROOT)/usr/local/standalone/firmware \
$(SDKROOT)/usr/local/standalone/firmware/include \
# $(SDKROOT)/../../../usr/local/standalone/firmware/Accelerate.framework/Frameworks/vecLib.framework/Headers
PREBUILT_STATICLIBS += $(SDKROOT)/usr/local/standalone/firmware/libm.a \
$(SDKROOT)/usr/local/standalone/firmware/libvDSP.a \
$(SDKROOT)/usr/local/standalone/firmware/libAudioCodecsA5.a

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef __AE2_DMA_H__
#define __AE2_DMA_H__
typedef void * dma_object_t;
/*
* dma_object_t is an object that can be used to control a dma
*/
typedef enum {
kDirectionNone = 0,
kDirectionIn = 1,
kDirectionOut = 2
} DMADirection;
typedef struct LLI
{
uint32_t source;
uint32_t destination;
struct LLI *next;
uint32_t control;
} DMALinkedListItem;
typedef enum {
kI2S_0 = 0,
kI2S_1,
kI2S_2,
kI2S_3,
kMCA_0,
kMCA_1,
kAudioDevice_Last
} AudioDevice_Index;
// create a dma object that will transfer to/from a buffer from/to a device, depending
// on the direction.
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer);
void destroy_dma_object(dma_object_t dma);
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg);
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg);
typedef enum {
kFrameError = 0,
kRXOverrun,
kRXUnderrun,
kTXOverrun,
kTXUnderrun,
kError_last
} Error_Index;
static const char* const kErrorTypeStr[kError_last] = {
"Frame_Error",
"RX_Overrun",
"RX_Underrun",
"TX_Overrun",
"TX_Underrun",
};
uint32_t getErrorCount(dma_object_t dma, Error_Index which);
void startDMAObject(dma_object_t dma);
void stopDMAObject(dma_object_t dma, bool immediate);
#endif /* __AE2_DMA_H__ */

View File

@ -0,0 +1,305 @@
/*
* Copyright (C) 2010-2011 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.
*/
/**
* The PIO object pretends to be a DMA for MCA.
*/
#include "pio_ae2.h"
#include "ae2_i2s.h"
#include "ae2_mca.h"
#include <drivers/audio/audio.h>
#include <debug.h>
#include <stdlib.h>
//Per AE2 Local DMA Controller/Requests
static void* sDMAAddressRX[kAudioDevice_Last] = { (void*)rI2S0_RXDB, (void*)rI2S1_RXDB, (void*)rI2S2_RXDB, (void*)rI2S3_RXDB, (void*)rMCA0_RXDATA, (void*)rMCA1_RXDATA };
static void* sDMAAddressTX[kAudioDevice_Last] = { (void*)rI2S0_TXDB, (void*)rI2S1_TXDB, (void*)rI2S2_TXDB, (void*)rI2S3_TXDB, (void*)rMCA0_TXDATA, (void*)rMCA1_TXDATA };
static internal_pio_object_t* sPIOObjects[kNumPIOs] = { NULL };
static uint32_t sPIORunning = 0;
uint32_t readReg(uint32_t address)
{
return *(volatile uint32_t *)address;
}
void writeReg(uint32_t address, uint32_t value)
{
*(volatile uint32_t *)address = value;
}
uint32_t readMCA0Reg(uint32_t offset)
{
return readReg(rMCA0_BASE + offset);
}
void writeMCA0Reg(uint32_t offset, uint32_t value)
{
writeReg(rMCA0_BASE + offset, value);
}
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer)
{
dprintf(DEBUG_CRITICAL, "Creating a PIO object\n");
// hardcoding to support only MCA right now.
if ((device >= kAudioDevice_Last) || ((direction != kDirectionIn) && (direction != kDirectionOut)) || (device != kMCA_0))
{
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
return NULL;
}
uint32_t whichPIO = (direction == kDirectionIn) ? kPIOReceive : kPIOTransmit;
// fail if device already exists
if (sPIOObjects[whichPIO])
{
dprintf(DEBUG_CRITICAL, "PIO already exists\n");
return NULL;
}
internal_pio_object_t *This = (internal_pio_object_t*)malloc(sizeof(internal_pio_object_t));
if (This)
{
This->mWhichDevice = whichPIO;
This->mInterruptHandler = NULL;
This->mInterruptHandlerData = NULL;
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
This->mErrorCount[i] = 0;
}
if (direction == kDirectionIn)
{
setupPIO(This, sDMAAddressRX[device], buffer, chain, bytesToTransfer);
}
else
{
setupPIO(This, buffer, sDMAAddressTX[device], chain, bytesToTransfer);
}
sPIOObjects[This->mWhichDevice] = This;
// we can do this here repeatedly
install_int_handler(AE2_INT_MCA0, handleAudioDeviceInterrupt, NULL);
}
return This;
}
void destroy_dma_object(dma_object_t dma)
{
// preliminary stop
stopDMAObject(dma, true);
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
sPIOObjects[This->mWhichDevice] = NULL;
free(This);
}
}
uint32_t getErrorCount(dma_object_t dma, Error_Index which)
{
// preliminary stop
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
return This->mErrorCount[which];
}
return 0;
}
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg)
{
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
This->mInterruptHandler = handler;
This->mInterruptHandlerData = arg;
}
}
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg)
{
}
void startDMAObject(dma_object_t dma)
{
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
startPIO(This);
// we wait until two of them are ready before we unmask the int
sPIORunning |= (1 << This->mWhichDevice);
if (sPIORunning == 3)
{
unmask_int(AE2_INT_MCA0);
}
}
}
void stopDMAObject(dma_object_t dma, bool immediate)
{
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
sPIORunning &= ~(1 << This->mWhichDevice);
if (sPIORunning != 3)
{
mask_int(AE2_INT_MCA0);
}
stopPIO(This);
}
}
bool receivePIOData(DMALinkedListItem * item)
{
// we do burst sizes of 16-bit data
int16_t data[kBurstSize];
volatile uint32_t * src = (volatile uint32_t *)item->source;
for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
{
data[i] = *src;
}
uint8_t * dst = (uint8_t*)item->destination;
memcpy(dst, data, sizeof(data));
dst += sizeof(data);
item->destination = (uint32_t) dst;
item->control -= sizeof(data);
return !item->control;
}
bool transmitPIOData(DMALinkedListItem * item)
{
// we do burst sizes of 16-bit data
int16_t data[kBurstSize];
uint8_t * src = (uint8_t*)item->source;
memcpy(data, src, sizeof(data));
volatile uint32_t * dst = (volatile uint32_t *)item->destination;
for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
{
*dst = data[i];
}
src += sizeof(data);
item->source = (uint32_t) src;
item->control -= sizeof(data);
return !item->control;
}
void setupNextLLI(DMALinkedListItem * item)
{
*item = *(item->next);
}
void handleAudioDeviceInterrupt()
{
uint32_t status = readMCA0Reg(rMCASTATUS);
// <rdar://problem/13467070> Panic when setting AppleSongbirdDSP sidetone EQ
// clear any sort of sticky-bit errors.
uint32_t errors = status & ( rMCASTATUS_FRAMEEEROR_MASK | rMCASTATUS_RXOVERRUN_MASK | rMCASTATUS_RXUNDERRUN_MASK | rMCASTATUS_TXOVERRUN_MASK | rMCASTATUS_TXUNDERRUN_MASK );
writeMCA0Reg(rMCASTATUS, errors);
if (errors)
{
if (sPIOObjects[kPIOReceive])
{
if (status & rMCASTATUS_FRAMEEEROR_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kFrameError];
if (status & rMCASTATUS_RXOVERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kRXOverrun];
if (status & rMCASTATUS_RXUNDERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kRXUnderrun];
if (status & rMCASTATUS_TXOVERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kTXOverrun];
if (status & rMCASTATUS_TXUNDERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kTXUnderrun];
}
}
if (status & (1 << rMCASTATUS_RXHIGHWATER))
{
if (sPIOObjects[kPIOReceive])
{
if (receivePIOData(&sPIOObjects[kPIOReceive]->mCurrentDMAItem))
{
setupNextLLI(&sPIOObjects[kPIOReceive]->mCurrentDMAItem);
if (sPIOObjects[kPIOReceive]->mInterruptHandler)
{
sPIOObjects[kPIOReceive]->mInterruptHandler(sPIOObjects[kPIOReceive]->mInterruptHandlerData);
}
}
}
}
if (status & (1 << rMCASTATUS_TXLOWWATER))
{
if (sPIOObjects[kPIOTransmit])
{
if (transmitPIOData(&sPIOObjects[kPIOTransmit]->mCurrentDMAItem))
{
setupNextLLI(&sPIOObjects[kPIOTransmit]->mCurrentDMAItem);
if (sPIOObjects[kPIOTransmit]->mInterruptHandler)
{
sPIOObjects[kPIOTransmit]->mInterruptHandler(sPIOObjects[kPIOTransmit]->mInterruptHandlerData);
}
}
}
}
}
void setupPIO(internal_pio_object_t * This, void *src, void *dst, DMALinkedListItem *chain, size_t bytesToTransfer)
{
uint32_t transferSize = bytesToTransfer;
uint32_t control = transferSize;
DMALinkedListItem *chainElement = chain;
while(chainElement != NULL)
{
chainElement->control = (uint32_t)control;
chainElement = chainElement->next;
if(chainElement == chain)
{
//We've populated all of the control values in this circular linked list, we are done here
break;
}
}
This->mCurrentDMAItem.source = (uint32_t)src;
This->mCurrentDMAItem.destination = (uint32_t)dst;
This->mCurrentDMAItem.next = chain;
This->mCurrentDMAItem.control = control;
}
void startPIO(internal_pio_object_t * pio_object)
{
if (pio_object)
{
if (pio_object->mWhichDevice == kPIOReceive)
{
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG | (1 << rMCAUNSRXCFG_IRQ_EN));
}
else
{
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG | (1 << rMCAUNSTXCFG_IRQ_EN));
}
}
}
void stopPIO(internal_pio_object_t * pio_object)
{
if (pio_object)
{
if (pio_object->mWhichDevice == kPIOReceive)
{
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG & ~((1 << rMCAUNSRXCFG_IRQ_EN)));
}
else
{
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG & ~((1 << rMCAUNSTXCFG_IRQ_EN)));
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef __PL080DMAC_AE2__
#define __PL080DMAC_AE2__
#include <stdint.h>
#include <platform/int.h>
#include "ae2_dma.h"
typedef enum {
kPIOReceive = 0,
kPIOTransmit = 1,
kNumPIOs = 2,
} ePIOType;
enum { kBurstSize = 4 };
typedef struct
{
ePIOType mWhichDevice;
int_handler mInterruptHandler;
void * mInterruptHandlerData;
DMALinkedListItem mCurrentDMAItem;
uint32_t mErrorCount[kAudioDevice_Last];
} internal_pio_object_t;
void handleAudioDeviceInterrupt();
/*
setup pio. Set the DMA linked list for doing PIO operations.
preps the pio_object for doing it's first pio op
*/
void setupPIO(internal_pio_object_t * pio_object, void *src, void *dst, DMALinkedListItem *chain, size_t bytesToTransfer);
/*
Startup PIO
*/
void startPIO(internal_pio_object_t * pio_object);
/*
Stop PIO
*/
void stopPIO(internal_pio_object_t * pio_object);
#endif /* __PL080DMAC_AE2__ */

View File

@ -0,0 +1,380 @@
/*
* Copyright (C) 2010-2011 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 "pl080dmac_ae2.h"
#include "ae2_i2s.h"
#include "ae2_mca.h"
#include <drivers/audio/audio.h>
#include <debug.h>
#include <stdlib.h>
#ifdef MAP_SRAM_CACHED
#error DMA requires the SRAM to be uncached
#endif
typedef struct
{
uint32_t mDMAChannel;
int_handler mInterruptHandler;
void * mInterruptHandlerData;
int_handler mErrorHandler;
void * mErrorHandlerData;
} internal_dma_object_t;
#define validDMAChannel(dmaChannel) (dmaChannel!=0xffffffff)
//Per AE2 Local DMA Controller/Requests
static const uint32_t sDMARequestInterfaceRx[kAudioDevice_Last] = {0, 2, 4, 6, 10, 6};
static const uint32_t sDMARequestInterfaceTx[kAudioDevice_Last] = {1, 3, 5, 7, 11, 7};
static void* sDMAAddressRX[kAudioDevice_Last] = { (void*)rI2S0_RXDB, (void*)rI2S1_RXDB, (void*)rI2S2_RXDB, (void*)rI2S3_RXDB, (void*)rMCA0_RXDATA, (void*)rMCA1_RXDATA };
static void* sDMAAddressTX[kAudioDevice_Last] = { (void*)rI2S0_TXDB, (void*)rI2S1_TXDB, (void*)rI2S2_TXDB, (void*)rI2S3_TXDB, (void*)rMCA0_TXDATA, (void*)rMCA1_TXDATA };
static internal_dma_object_t* sDMAChannelObjects[kDMAChannelsPerController] = { NULL };
static uint32_t sDMACC0InUse = 0;
static uint32_t sDMACC0Running = 0;
uint32_t readReg(uint32_t address)
{
return *(volatile uint32_t *)address;
}
void writeReg(uint32_t address, uint32_t value)
{
*(volatile uint32_t *)address = value;
}
uint32_t readDMAReg(uint32_t offset)
{
return readReg(kDMACBase + offset);
}
void writeDMAReg(uint32_t offset, uint32_t value)
{
writeReg(kDMACBase + offset, value);
}
uint32_t readDMAChannelReg(uint32_t channel, uint32_t offset)
{
uint32_t dmaChannelOffset = kDMAC0Base + channel * kDMACRegisterSize;
return readReg(dmaChannelOffset + offset);
}
void writeDMAChannelReg(uint32_t channel, uint32_t offset, uint32_t value)
{
uint32_t dmaChannelOffset = kDMAC0Base + channel * kDMACRegisterSize;
writeReg(dmaChannelOffset + offset, value);
}
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer)
{
dprintf(DEBUG_CRITICAL, "Creating a DMA object\n");
if ((device >= kAudioDevice_Last) || ((direction != kDirectionIn) && (direction != kDirectionOut)))
{
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
return NULL;
}
internal_dma_object_t *This = (internal_dma_object_t*)malloc(sizeof(internal_dma_object_t));
if (This)
{
uint32_t dmaChannel = acquireDMAChannel();
dprintf(DEBUG_CRITICAL, "channel is %d\n", dmaChannel);
if (dmaChannel == 0xFFFFFFFF)
{
free(This);
return NULL;
}
This->mDMAChannel = dmaChannel;
This->mInterruptHandler = NULL;
This->mInterruptHandlerData = NULL;
This->mErrorHandler = NULL;
This->mErrorHandlerData = NULL;
configureDMA(This->mDMAChannel);
if (direction == kDirectionIn)
{
setupDMA(This->mDMAChannel, sDMAAddressRX[device], buffer, chain, kDirectionIn, sDMARequestInterfaceRx[device], bytesToTransfer);
}
else
{
setupDMA(This->mDMAChannel, buffer, sDMAAddressTX[device], chain, kDirectionOut, sDMARequestInterfaceTx[device], bytesToTransfer);
}
sDMAChannelObjects[This->mDMAChannel] = This;
// we can do this here repeatedly
install_int_handler(AE2_INT_DMACINTTC, handleAudioDeviceDMAInterrupt, NULL);
install_int_handler(AE2_INT_DMACINTERR, handleAudioDeviceDMAInterruptError, NULL);
}
return This;
}
void destroy_dma_object(dma_object_t dma)
{
// preliminary stop
stopDMAObject(dma, true);
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
sDMAChannelObjects[This->mDMAChannel] = NULL;
freeDMAChannel(This->mDMAChannel);
free(This);
}
}
uint32_t getErrorCount(dma_object_t, Error_Index)
{
return 0;
}
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
This->mInterruptHandler = handler;
This->mInterruptHandlerData = arg;
}
}
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
This->mErrorHandler = handler;
This->mErrorHandlerData = arg;
}
}
void startDMAObject(dma_object_t dma)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
startDMA(This->mDMAChannel);
sDMACC0Running |= (1 << This->mDMAChannel);
if (sDMACC0Running)
{
unmask_int(AE2_INT_DMACINTTC);
unmask_int(AE2_INT_DMACINTERR);
}
}
}
void stopDMAObject(dma_object_t dma, bool immediate)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
if (immediate)
{
disableDMAImmediate(This->mDMAChannel);
}
else
{
disableDMA(This->mDMAChannel);
}
sDMACC0Running &= ~(1 << This->mDMAChannel);
if (!sDMACC0Running)
{
mask_int(AE2_INT_DMACINTTC);
mask_int(AE2_INT_DMACINTERR);
}
}
}
void handleAudioDeviceDMAInterrupt()
{
uint32_t status = readDMAReg(kDMACIntTCStatus);
for (uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
{
uint32_t mask = 1 << channel;
if (status & mask)
{
writeDMAReg(kDMACIntTCClear, mask); //clear int
if(sDMAChannelObjects[channel] != NULL && sDMAChannelObjects[channel]->mInterruptHandler)
{
sDMAChannelObjects[channel]->mInterruptHandler(sDMAChannelObjects[channel]->mInterruptHandlerData);
}
}
}
}
void handleAudioDeviceDMAInterruptError()
{
uint32_t status = readDMAReg(kDMACIntErrStatus);
for (uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
{
uint32_t mask = 1 << channel;
if (status & mask)
{
writeDMAReg(kDMACIntErrClear, mask); //clear int
if(sDMAChannelObjects[channel] != NULL && sDMAChannelObjects[channel]->mInterruptHandler)
{
sDMAChannelObjects[channel]->mInterruptHandler(sDMAChannelObjects[channel]->mInterruptHandlerData);
}
}
}
}
uint32_t acquireDMAChannel(void)
{
uint32_t result = 0xFFFFFFFF;
for(uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
{
if(!(sDMACC0InUse & (1 << channel)))
{
sDMACC0InUse |= (1 << channel);
result = channel;
break;
}
}
if (sDMACC0InUse)
{
writeReg(kDMACClockGating, 0);
writeDMAReg(kDMACConfiguration, DMACConfiguration_M2_Little | DMACConfiguration_M1_Little | DMACConfiguration_E_Enable);
}
return result;
}
void freeDMAChannel(uint32_t dmaChannel)
{
sDMACC0InUse &= ~(1 << dmaChannel);
// if there are no channels in use, shut down DMA
if (!sDMACC0InUse)
{
uint32_t config = readDMAReg(kDMACConfiguration);
writeDMAReg(kDMACConfiguration, config & ~DMACConfiguration_E_Enable);
}
}
void configureDMA(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t control = DMACCxControl_I_Enable |
DMACCxControl_D_Master1 |
DMACCxControl_S_Master1 |
DMACCxControl_SWidth_Halfword |
DMACCxControl_DWidth_Halfword |
DMACCxControl_SBSize_4 |
DMACCxControl_DBSize_4;
writeDMAChannelReg(dmaChannel, kDMACCtrlOffset, control);
}
void setupDMA(uint32_t dmaChannel, void *src, void *dst, DMALinkedListItem *chain, DMADirection direction, uint32_t peripheral, size_t bytesToTransfer)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t transferSize = bytesToTransfer >> 1; //assuming burst size of DMACCxControl_DWidth_Halfword
uint32_t control = readDMAChannelReg(dmaChannel, kDMACCtrlOffset);
control &= ~(DMACCxControl_TransferSizeMask | // Clear transfer size
DMACCxControl_SI_Mask | // Clear source increment
DMACCxControl_DI_Mask); // Clear destination increment
control |= (transferSize & 0x0FFF);
uint32_t flowControl, sourcePeripheral, destinationPeripheral;
if(direction == kDirectionIn) // -> peripheral-to-memory
{
control |= DMACCxControl_SI_No_Increment;
control |= DMACCxControl_DI_Increment;
flowControl = DMACCxConfiguration_FlowCntrl_Peripheral_to_Memory_DMA;
sourcePeripheral = peripheral;
destinationPeripheral = 0; //mem
}
else if(direction == kDirectionOut) //kDirectionOut -> memory-to-peripheral
{
control |= DMACCxControl_SI_Increment;
control |= DMACCxControl_DI_No_Increment;
flowControl = DMACCxConfiguration_FlowCntrl_Memory_to_Peripheral_DMA;
sourcePeripheral = 0; //mem
destinationPeripheral = peripheral;
}
else //kDirectionNone -> mem-to-mem here
{
control |= DMACCxControl_SI_Increment;
control |= DMACCxControl_DI_Increment;
flowControl = DMACCxConfiguration_FlowCntrl_Memory_to_Memory_DMA;
sourcePeripheral = 0; //mem
destinationPeripheral = 0; //mem
}
DMALinkedListItem *chainElement = chain;
while(chainElement != NULL)
{
chainElement->control = (uint32_t)control;
chainElement = chainElement->next;
if(chainElement == chain)
{
//We've populated all of the control values in this circular linked list, we are done here
break;
}
}
writeDMAChannelReg(dmaChannel, kDMACSourceOffset, (uint32_t)src);
writeDMAChannelReg(dmaChannel, kDMACDestOffset, (uint32_t)dst);
writeDMAChannelReg(dmaChannel, kDMACLLIOffset, (uint32_t)chain);
writeDMAChannelReg(dmaChannel, kDMACCtrlOffset, control);
flowControl |= DMACCxConfiguration_ITC_Enable;
flowControl |= DMACCxConfiguration_IError_Enable;
flowControl |= ((destinationPeripheral & 0xF) << 6);
flowControl |= ((sourcePeripheral & 0xF) << 1);
// do this in the start
//flowControl |= DMACCxConfiguration_E_Enable;
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, flowControl);
}
void startDMA(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t flowControl = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, flowControl |= DMACCxConfiguration_E_Enable);
}
void disableDMA(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, config | DMACCxConfiguration_Halt);
config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
// give a chance for the DMA to timeout. But don't wait forever...
int timeToWait = 0x100;
while(config & DMACCxConfiguration_Active && (timeToWait-- > 0))
{
config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
}
disableDMAImmediate(dmaChannel);
}
void disableDMAImmediate(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, config & ~DMACCxConfiguration_E_Enable);
writeDMAReg(kDMACIntTCClear, 1 << dmaChannel); // clear any outstanding ints
writeDMAReg(kDMACIntErrClear, 1 << dmaChannel); // clear any outstanding ints
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef __PL080DMAC_AE2__
#define __PL080DMAC_AE2__
#include <stdint.h>
#include <platform/int.h>
#include "ae2_dma.h"
#define kDMACClockGating 0x341A0004
#define kDMAChannelsPerController 8
#define kDMACBase 0x341A3000
#define kDMAC0Base 0x341A3100
#define kDMACIntStatus 0x0
#define kDMACIntTCStatus 0x4
#define kDMACIntTCClear 0x8
#define kDMACIntErrStatus 0xC
#define kDMACIntErrClear 0x10
#define kDMACConfiguration 0x30
#define kDMACSourceOffset 0x0
#define kDMACDestOffset 0x4
#define kDMACLLIOffset 0x8
#define kDMACCtrlOffset 0xC
#define kDMACConfigOffset 0x10
#define kDMACRegisterSize 0x20
#define DMACConfiguration_M2_Mask (1 << 2)
#define DMACConfiguration_M2_Little (0 << 2)
#define DMACConfiguration_M2_Big (1 << 2)
#define DMACConfiguration_M1_Mask (1 << 1)
#define DMACConfiguration_M1_Little (0 << 1)
#define DMACConfiguration_M1_Big (1 << 1)
#define DMACConfiguration_E_Mask (1 << 0)
#define DMACConfiguration_E_Disable (0 << 0)
#define DMACConfiguration_E_Enable (1 << 0)
#define DMACCxConfiguration_Halt (1 << 18)
#define DMACCxConfiguration_Active (1 << 17)
#define DMACCxConfiguration_ITC_Enable (1 << 15)
#define DMACCxConfiguration_IError_Enable (1 << 14)
#define DMACCxConfiguration_E_Enable (1 << 0)
#define DMACCxControl_I_Mask (1 << 31)
#define DMACCxControl_I_Disable (0 << 31)
#define DMACCxControl_I_Enable (1 << 31)
#define DMACCxControl_SI_Mask (1 << 26)
#define DMACCxControl_SI_No_Increment (0 << 26)
#define DMACCxControl_SI_Increment (1 << 26)
#define DMACCxControl_DI_Mask (1 << 27)
#define DMACCxControl_DI_No_Increment (0 << 27)
#define DMACCxControl_DI_Increment (1 << 27)
#define DMACCxControl_D_Master1 (0 << 25)
#define DMACCxControl_S_Master1 (0 << 24)
#define DMACCxControl_SWidth_Halfword (1 << 18)
#define DMACCxControl_DWidth_Halfword (1 << 21)
#define DMACCxControl_DBSize_1 (0 << 15)
#define DMACCxControl_DBSize_4 (1 << 15)
#define DMACCxControl_SBSize_1 (0 << 12)
#define DMACCxControl_SBSize_4 (1 << 12)
#define DMACCxControl_TransferSizeMask (0x0FFF)
#define DMACCxConfiguration_FlowCntrl_Memory_to_Memory_DMA (0 << 11)
#define DMACCxConfiguration_FlowCntrl_Memory_to_Peripheral_DMA (1 << 11)
#define DMACCxConfiguration_FlowCntrl_Peripheral_to_Memory_DMA (2 << 11)
void handleAudioDeviceDMAInterrupt();
void handleAudioDeviceDMAInterruptError();
/*
Get a free DMA channel
*/
uint32_t acquireDMAChannel(void);
/*
Free a DMA channel
*/
void freeDMAChannel(uint32_t dmaChannel);
/*
Make sure the pl080 clock is enabled and initialize DMA channels 0 and 1
*/
void configureDMA(uint32_t dmaChannel);
/*
Startup DMA from address src to destination dst
If chain is not NULL, will setup that in the LLI to auto-restart DMA from the chain
*/
void setupDMA(uint32_t dmaChannel, void *src, void *dst, DMALinkedListItem *chain, DMADirection direction, uint32_t peripheral, size_t bytesToTransfer);
/*
Startup DMA from address src to destination dst
If chain is not NULL, will setup that in the LLI to auto-restart DMA from the chain
*/
void startDMA(uint32_t dmaChannel);
/*
Halts the channel, then blocks until the DMA is complete and then fully disables it
*/
void disableDMA(uint32_t dmaChannel);
/*
Blocks until the DMA is complete and then fully disables it
*/
void disableDMAImmediate(uint32_t dmaChannel);
#endif /* __PL080DMAC_AE2__ */

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef __AE2_I2S__
#define __AE2_I2S__
#include <stdint.h>
#include <platform/int.h>
#define rI2S_BASE 0x34190000
#define rI2S0_BASE (rI2S_BASE )
#define rI2S1_BASE (rI2S_BASE + 0x1000)
#define rI2S2_BASE (rI2S_BASE + 0x2000)
#define rI2S3_BASE (rI2S_BASE + 0x3000)
#define rI2SCLKCON 0
#define rI2STXCON 0x4
#define rI2STXCOM 0x8
#define rI2STXDB 0x10
#define rI2SRXCON 0x30
#define rI2SRXCOM 0x34
#define rI2SRXDB 0x38
#define rI2SSTATUS 0x3C
#define rI2SBITCLK 0x40
#define rI2SVERSION 0x44
#define rI2S0_CLKCON (rI2S0_BASE + rI2SCLKCON )
#define rI2S0_TXCON (rI2S0_BASE + rI2STXCON )
#define rI2S0_TXCOM (rI2S0_BASE + rI2STXCOM )
#define rI2S0_TXDB (rI2S0_BASE + rI2STXDB )
#define rI2S0_RXCON (rI2S0_BASE + rI2SRXCON )
#define rI2S0_RXCOM (rI2S0_BASE + rI2SRXCOM )
#define rI2S0_RXDB (rI2S0_BASE + rI2SRXDB )
#define rI2S0_STATUS (rI2S0_BASE + rI2SSTATUS )
#define rI2S0_BITCLK (rI2S0_BASE + rI2SBITCLK )
#define rI2S0_VERSION (rI2S0_BASE + rI2SVERSION)
#define rI2S1_CLKCON (rI2S1_BASE + rI2SCLKCON )
#define rI2S1_TXCON (rI2S1_BASE + rI2STXCON )
#define rI2S1_TXCOM (rI2S1_BASE + rI2STXCOM )
#define rI2S1_TXDB (rI2S1_BASE + rI2STXDB )
#define rI2S1_RXCON (rI2S1_BASE + rI2SRXCON )
#define rI2S1_RXCOM (rI2S1_BASE + rI2SRXCOM )
#define rI2S1_RXDB (rI2S1_BASE + rI2SRXDB )
#define rI2S1_STATUS (rI2S1_BASE + rI2SSTATUS )
#define rI2S1_BITCLK (rI2S1_BASE + rI2SBITCLK )
#define rI2S1_VERSION (rI2S1_BASE + rI2SVERSION)
#define rI2S2_CLKCON (rI2S2_BASE + rI2SCLKCON )
#define rI2S2_TXCON (rI2S2_BASE + rI2STXCON )
#define rI2S2_TXCOM (rI2S2_BASE + rI2STXCOM )
#define rI2S2_TXDB (rI2S2_BASE + rI2STXDB )
#define rI2S2_RXCON (rI2S2_BASE + rI2SRXCON )
#define rI2S2_RXCOM (rI2S2_BASE + rI2SRXCOM )
#define rI2S2_RXDB (rI2S2_BASE + rI2SRXDB )
#define rI2S2_STATUS (rI2S2_BASE + rI2SSTATUS )
#define rI2S2_BITCLK (rI2S2_BASE + rI2SBITCLK )
#define rI2S2_VERSION (rI2S2_BASE + rI2SVERSION)
#define rI2S3_CLKCON (rI2S3_BASE + rI2SCLKCON )
#define rI2S3_TXCON (rI2S3_BASE + rI2STXCON )
#define rI2S3_TXCOM (rI2S3_BASE + rI2STXCOM )
#define rI2S3_TXDB (rI2S3_BASE + rI2STXDB )
#define rI2S3_RXCON (rI2S3_BASE + rI2SRXCON )
#define rI2S3_RXCOM (rI2S3_BASE + rI2SRXCOM )
#define rI2S3_RXDB (rI2S3_BASE + rI2SRXDB )
#define rI2S3_STATUS (rI2S3_BASE + rI2SSTATUS )
#define rI2S3_BITCLK (rI2S3_BASE + rI2SBITCLK )
#define rI2S3_VERSION (rI2S3_BASE + rI2SVERSION)
#define rI2SSTATUS_RXOVERRUN 20
#define rI2SSTATUS_TXOVERRUN 19
#define rI2SSTATUS_RXFIFOLVL 13
#define rI2SSTATUS_TXFIFOLVL 7
#define rI2SSTATUS_RXOVERRUN_MASK ( 1 << 20)
#define rI2SSTATUS_TXOVERRUN_MASK ( 1 << 19)
#define rI2SSTATUS_RXFIFOLVL_MASK (0x3F << 13)
#define rI2SSTATUS_TXFIFOLVL_MASK (0x3F << 7 )
#endif /* __AE2_I2S__ */

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef __AE2_MCA__
#define __AE2_MCA__
#include <stdint.h>
#include <platform/int.h>
#define rMCA_BASE 0x34196000
#define rMCA0_BASE (rMCA_BASE )
#define rMCA1_BASE (rMCA_BASE + 0x1000)
#define rMCAVERSION 0
#define rMCAFIFOSIZE 0x4
#define rMCAFIFOSHIFT 0x8
#define rMCACFG 0xC
#define rMCAUNSCFG 0x10
#define rMCAIDLE 0x14
#define rMCATXCFG 0x18
#define rMCAUNSTXCFG 0x1C
#define rMCATXFIFOCFG 0x20
#define rMCATXMASK 0x24
#define rMCATXDATA 0x28
#define rMCARXCFG 0x2C
#define rMCAUNSRXCFG 0x30
#define rMCARXFIFOCFG 0x34
#define rMCARXMASK 0x38
#define rMCARXDATA 0x3C
#define rMCASTATUS 0x40
#define rMCACTL 0x44
#define rMCA0_VERSION ( rMCA0_BASE + rMCAVERSION )
#define rMCA0_FIFOSHIFT ( rMCA0_BASE + rMCAFIFOSHIFT)
#define rMCA0_CFG ( rMCA0_BASE + rMCACFG )
#define rMCA0_UNSCFG ( rMCA0_BASE + rMCAUNSCFG )
#define rMCA0_IDLE ( rMCA0_BASE + rMCAIDLE )
#define rMCA0_TXCFG ( rMCA0_BASE + rMCATXCFG )
#define rMCA0_UNSTXCFG ( rMCA0_BASE + rMCAUNSTXCFG )
#define rMCA0_TXFIFOCFG ( rMCA0_BASE + rMCATXFIFOCFG)
#define rMCA0_TXMASK ( rMCA0_BASE + rMCATXMASK )
#define rMCA0_TXDATA ( rMCA0_BASE + rMCATXDATA )
#define rMCA0_RXCFG ( rMCA0_BASE + rMCARXCFG )
#define rMCA0_UNSRXCFG ( rMCA0_BASE + rMCAUNSRXCFG )
#define rMCA0_RXFIFOCFG ( rMCA0_BASE + rMCARXFIFOCFG)
#define rMCA0_RXMASK ( rMCA0_BASE + rMCARXMASK )
#define rMCA0_RXDATA ( rMCA0_BASE + rMCARXDATA )
#define rMCA0_STATUS ( rMCA0_BASE + rMCASTATUS )
#define rMCA0_CTL ( rMCA0_BASE + rMCACTL )
#define rMCA1_VERSION ( rMCA1_BASE + rMCAVERSION )
#define rMCA1_FIFOSHIFT ( rMCA1_BASE + rMCAFIFOSHIFT)
#define rMCA1_CFG ( rMCA1_BASE + rMCACFG )
#define rMCA1_UNSCFG ( rMCA1_BASE + rMCAUNSCFG )
#define rMCA1_IDLE ( rMCA1_BASE + rMCAIDLE )
#define rMCA1_TXCFG ( rMCA1_BASE + rMCATXCFG )
#define rMCA1_UNSTXCFG ( rMCA1_BASE + rMCAUNSTXCFG )
#define rMCA1_TXFIFOCFG ( rMCA1_BASE + rMCATXFIFOCFG)
#define rMCA1_TXMASK ( rMCA1_BASE + rMCATXMASK )
#define rMCA1_TXDATA ( rMCA1_BASE + rMCATXDATA )
#define rMCA1_RXCFG ( rMCA1_BASE + rMCARXCFG )
#define rMCA1_UNSRXCFG ( rMCA1_BASE + rMCAUNSRXCFG )
#define rMCA1_RXFIFOCFG ( rMCA1_BASE + rMCARXFIFOCFG)
#define rMCA1_RXMASK ( rMCA1_BASE + rMCARXMASK )
#define rMCA1_RXDATA ( rMCA1_BASE + rMCARXDATA )
#define rMCA1_STATUS ( rMCA1_BASE + rMCASTATUS )
#define rMCA1_CTL ( rMCA1_BASE + rMCACTL )
#define rMCASTATUS_FRAMEERROR 31
#define rMCASTATUS_RXOVERRUN 29
#define rMCASTATUS_RXUNDERRUN 28
#define rMCASTATUS_RXHIGHWATER 27
#define rMCASTATUS_RXLOWWATER 26
#define rMCASTATUS_TXOVERRUN 13
#define rMCASTATUS_TXUNDERRUN 12
#define rMCASTATUS_TXHIGHWATER 11
#define rMCASTATUS_TXLOWWATER 10
#define rMCASTATUS_RXFIFOLVL 16
#define rMCASTATUS_TXFIFOLVL 0
#define rMCASTATUS_FRAMEEEROR_MASK ( 1 << rMCASTATUS_FRAMEERROR)
#define rMCASTATUS_RXOVERRUN_MASK ( 1 << rMCASTATUS_RXOVERRUN)
#define rMCASTATUS_RXUNDERRUN_MASK ( 1 << rMCASTATUS_RXUNDERRUN)
#define rMCASTATUS_TXOVERRUN_MASK ( 1 << rMCASTATUS_TXOVERRUN)
#define rMCASTATUS_TXUNDERRUN_MASK ( 1 << rMCASTATUS_TXUNDERRUN)
#define rMCASTATUS_RXFIFOLVL_MASK (0x1FF << rMCASTATUS_RXFIFOLVL)
#define rMCASTATUS_TXFIFOLVL_MASK (0x1FF << rMCASTATUS_TXFIFOLVL)
#define rMCAUNSRXCFG_IRQ_EN 1
#define rMCAUNSTXCFG_IRQ_EN 1
#endif /* __AE2_MCA__ */

View File

@ -0,0 +1,50 @@
/*
* AudioUnit_AE2.h
* CAServices-Aspen
*
* Created by richardp on 11/28/11.
* Copyright 2011 Apple, Inc. All rights reserved.
*
* Serves as the base class for AudioUnits on AE2
*/
#ifndef __AudioUnit_AE2_h__
#define __AudioUnit_AE2_h__
#include <stdint.h>
#include "AudioUnitProperties_AE2.h"
/**
* AUBase_AE2 is the wrapper to go from AE2 land to CoreAudio land.
* For instance, we assume input channels are 16-bit, whereas CoreAudio
* AudioUnit may require floats. These modules do the translation.
*
* This is not intended to be parity with AudioUnits, but a crippled
* version.
*/
class AUBase_AE2
{
public:
AUBase_AE2() {}
virtual ~AUBase_AE2() {}
virtual OSStatus Initialize() { return noErr; }
virtual void Process(const void *inSourceP,
void *inDestP,
uint32_t inFramesToProcess) {}
// for parameters
virtual OSStatus SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value) { return kAudioUnitErr_InvalidParameter; }
virtual AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID) { return 0; }
// for properties
virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable) { return kAudioUnitErr_InvalidProperty; }
virtual OSStatus GetProperty(AudioUnitPropertyID inID, void* outData) { return kAudioUnitErr_InvalidProperty; }
virtual OSStatus SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize) { return kAudioUnitErr_InvalidProperty; }
};
#endif // __AudioUnit_AE2_h__

View File

@ -0,0 +1,149 @@
/*
* AUComponent_AE2.h
* Stripped down version of AUComponent just for what AE2 needs
*
* Copyright 2010 Apple Computer. All rights reserved.
*
*/
#ifndef __AUCOMPONENT_AE2__
#define __AUCOMPONENT_AE2__
/*!
@enum Audio unit errors
@discussion These are the various errors that can be returned by AudioUnit... API calls
@constant kAudioUnitErr_InvalidProperty
The property is not supported
@constant kAudioUnitErr_InvalidParameter
The parameter is not supported
@constant kAudioUnitErr_InvalidElement
The specified element is not valid
@constant kAudioUnitErr_NoConnection
There is no connection (generally an audio unit is asked to render but it has
not input from which to gather data)
@constant kAudioUnitErr_FailedInitialization
The audio unit is unable to be initialised
@constant kAudioUnitErr_TooManyFramesToProcess
When an audio unit is initialised it has a value which specifies the max
number of frames it will be asked to render at any given time. If an audio
unit is asked to render more than this, this error is returned.
@constant kAudioUnitErr_InvalidFile
If an audio unit uses external files as a data source, this error is returned
if a file is invalid (Apple's DLS synth returns this error)
@constant kAudioUnitErr_FormatNotSupported
Returned if an input or output format is not supported
@constant kAudioUnitErr_Uninitialized
Returned if an operation requires an audio unit to be initialised and it is
not.
@constant kAudioUnitErr_InvalidScope
The specified scope is invalid
@constant kAudioUnitErr_PropertyNotWritable
The property cannot be written
@constant kAudioUnitErr_CannotDoInCurrentContext
Returned when an audio unit is in a state where it can't perform the requested
action now - but it could later. Its usually used to guard a render operation
when a reconfiguration of its internal state is being performed.
@constant kAudioUnitErr_InvalidPropertyValue
The property is valid, but the value of the property being provided is not
@constant kAudioUnitErr_PropertyNotInUse
Returned when a property is valid, but it hasn't been set to a valid value at
this time.
@constant kAudioUnitErr_Initialized
Indicates the operation cannot be performed because the audio unit is
initialized.
@constant kAudioUnitErr_InvalidOfflineRender
Used to indicate that the offline render operation is invalid. For instance,
when the audio unit needs to be pre-flighted,
but it hasn't been.
@constant kAudioUnitErr_Unauthorized
Returned by either Open or Initialise, this error is used to indicate that the
audio unit is not authorised, that it cannot be used. A host can then present
a UI to notify the user the audio unit is not able to be used in its current
state.
*/
enum
{
kAudioUnitErr_InvalidProperty = -10879,
kAudioUnitErr_InvalidParameter = -10878,
kAudioUnitErr_InvalidElement = -10877,
kAudioUnitErr_NoConnection = -10876,
kAudioUnitErr_FailedInitialization = -10875,
kAudioUnitErr_TooManyFramesToProcess = -10874,
kAudioUnitErr_InvalidFile = -10871,
kAudioUnitErr_FormatNotSupported = -10868,
kAudioUnitErr_Uninitialized = -10867,
kAudioUnitErr_InvalidScope = -10866,
kAudioUnitErr_PropertyNotWritable = -10865,
kAudioUnitErr_CannotDoInCurrentContext = -10863,
kAudioUnitErr_InvalidPropertyValue = -10851,
kAudioUnitErr_PropertyNotInUse = -10850,
kAudioUnitErr_Initialized = -10849,
kAudioUnitErr_InvalidOfflineRender = -10848,
kAudioUnitErr_Unauthorized = -10847
};
/*!
@typedef AudioUnitPropertyID
@discussion Type used for audio unit properties.
Properties are used to describe the state of an audio unit (for instance,
the input or output audio format)
*/
typedef UInt32 AudioUnitPropertyID;
/*!
@typedef AudioUnitScope
@discussion Type used for audio unit scopes. Apple reserves the 0 < 1024 range for
audio unit scope identifiers.
Scopes are used to delineate a major attribute of an audio unit
(for instance, global, input, output)
*/
typedef UInt32 AudioUnitScope;
/*!
@typedef AudioUnitElement
@discussion Type used for audio unit elements.
Scopes can have one or more member, and a member of a scope is
addressed / described by its element
For instance, input bus 1 is input scope, element 1
*/
typedef UInt32 AudioUnitElement;
/*!
@typedef AudioUnitParameterID
@discussion Type used for audio unit parameters.
Parameters are typically used to control and set render state
(for instance, filter cut-off frequency)
*/
typedef UInt32 AudioUnitParameterID;
/*!
@typedef AudioUnitParameterValue
@discussion Type used for audio unit parameter values.
The value of a given parameter is specified using this type
(typically a Float32)
*/
typedef Float32 AudioUnitParameterValue;
//-----------------------------------------------------------------------------
// Render flags
//-----------------------------------------------------------------------------
enum
{
/* these are obsolete, were never implemented: */
/* kAudioUnitRenderAction_Accumulate = (1 << 0) */
/* kAudioUnitRenderAction_UseProvidedBuffer = (1 << 1) */
kAudioUnitRenderAction_PreRender = (1 << 2),
kAudioUnitRenderAction_PostRender = (1 << 3),
kAudioUnitRenderAction_OutputIsSilence = (1 << 4),
/* Provides hint on return from Render(). if set, the buffer contains all zeroes */
kAudioOfflineUnitRenderAction_Preflight = (1 << 5),
kAudioOfflineUnitRenderAction_Render = (1 << 6),
kAudioOfflineUnitRenderAction_Complete = (1 << 7),
// this flag is set if on the post-render call an error was returned by the AUs render
// in this case, the error can be retrieved through the lastRenderError property
// and the ioData handed to the post-render notification will be invalid.
kAudioUnitRenderAction_PostRenderError = (1 << 8)
};
typedef UInt32 AudioUnitRenderActionFlags;
#endif

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2011 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 "AUNull.h"
#include "string.h"
#include <debug.h>
#define GENERATE_SAWTOOTH 0
AUNull *
AUNull::Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
AUNull *This = new AUNull;
if (This)
{
if (This->InitWith(sampleRate, numChannels, sampleSize))
{
This->Initialize();
}
else
{
delete This;
This = NULL;
}
}
return This;
}
AUNull::AUNull()
{
}
bool
AUNull::InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
mSampleRate = sampleRate;
mNumChannels = numChannels;
mSampleSize = sampleSize;
return true;
}
AUNull::~AUNull()
{
}
#if GENERATE_SAWTOOTH
static int16_t currentValue = 0;
static const uint32_t kFreq = 600;
#endif
void
AUNull::Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess)
{
#if GENERATE_SAWTOOTH
uint16_t amountToAdd = (0x10000L * kFreq) / mSampleRate;
int16_t *dst = static_cast<int16_t*>(inDestP);
size_t i = 0;
for (i = 0; i < (inFramesToProcess * mNumChannels); ++i)
{
*dst++ = currentValue;
currentValue += amountToAdd;
}
#endif
}

View File

@ -0,0 +1,46 @@
/*
* AUNull.h
*
* Created by richardp on 11/28/11.
* Copyright 2011 Apple, Inc. All rights reserved.
*
* Does necessary translation between AE2 and CA AU speaker protection
*/
#ifndef __AUNull_h__
#define __AUNull_h__
#include <stdint.h>
#include "AudioUnitProperties_AE2.h"
#include "AUBase_AE2.h"
class IIRFilter;
/**
* AUNull just passes input to output
* Assumes audio data is 16-bit stereo
*/
class AUNull : public AUBase_AE2
{
public:
static AUNull* Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
virtual ~AUNull();
virtual void Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess);
private:
AUNull();
bool InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
// some constants
typedef AUBase_AE2 super;
size_t mSampleRate;
size_t mNumChannels;
size_t mSampleSize;
};
#endif // __AUNull_h__

View File

@ -0,0 +1,458 @@
/*
* Copyright (C) 2011 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.
*/
#if USE_SIDETONE
#include "AUSidetone.h"
#include "IIRFiltersA5.h"
#include "VolumeA5.h"
#include "string.h"
#include <debug.h>
// a basic fixed 16-bit mono 44.1 description
static AudioStreamBasicDescription kDefaultMonoFormat = { 44100.0, 'lpcm', 0xC, 2, 1, 2, 1, 16, 0 };
static void SetDefaultSidetoneEQ(BiquadDescriptor *biquadValues, size_t numBands, uint32_t sampleRate)
{
size_t i = 0;
if (i < numBands)
{
biquadValues[i].filterType = kAUNBandEQFilterType_2ndOrderButterworthLowPass;
biquadValues[i].frequencyInHertz = (sampleRate == 8000) ? 3800.0f : 7400.0f;
biquadValues[i].gain = 0.f;
biquadValues[i].bw = 0.05;
biquadValues[i].bypassBand = 0.f;
++i;
}
if (i < numBands)
{
biquadValues[i].filterType = kAUNBandEQFilterType_2ndOrderButterworthHighPass;
biquadValues[i].frequencyInHertz = (sampleRate == 8000) ? 200.0f : 100.0f;
biquadValues[i].gain = 0.f;
biquadValues[i].bw = 0.05;
biquadValues[i].bypassBand = 0.f;
++i;
}
for (; i < numBands; ++i)
{
biquadValues[i].filterType = 0;
biquadValues[i].frequencyInHertz = 100.0f;
biquadValues[i].gain = 0.f;
biquadValues[i].bw = 0.05;
biquadValues[i].bypassBand = 1.f;
}
}
AUSidetone *
AUSidetone::Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
AUSidetone *This = new AUSidetone;
if (This)
{
if (This->InitWith(sampleRate, numChannels, sampleSize))
{
This->Initialize();
}
else
{
delete This;
This = NULL;
}
}
return This;
}
AUSidetone::AUSidetone() :
mSampleRate(44100),
mNumChannels(1),
mSidetoneEQ(NULL),
mVolume(NULL),
mNumberBands(kMaxNumBands),
mBiquadValues(NULL),
mVolumeInDB(kDefaultSidetoneGain),
mSidetoneEQBypass(false)
{
}
bool
AUSidetone::InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
if (sampleSize != 2)
{
return false;
}
mSampleRate = sampleRate;
mNumChannels = numChannels;
AudioStreamBasicDescription thisFormat = kDefaultMonoFormat;
thisFormat.mSampleRate = mSampleRate;
thisFormat.mBytesPerPacket *= mNumChannels;
thisFormat.mBytesPerFrame *= mNumChannels;
thisFormat.mChannelsPerFrame *= mNumChannels;
thisFormat.mBitsPerChannel *= mNumChannels;
mSidetoneEQ = NewAE2IIRFilter(&thisFormat, &thisFormat, kMaxNumBands);
dprintf(DEBUG_INFO, "sidetone eq %p\n", mSidetoneEQ);
mBiquadValues = new BiquadDescriptor[mNumberBands];
if (mBiquadValues)
{
SetDefaultSidetoneEQ(mBiquadValues, mNumberBands, mSampleRate);
if (mSidetoneEQ)
{
mVolume = NewVolume(thisFormat);
dprintf(DEBUG_INFO, "volume %p\n", mVolume);
if (mVolume)
{
if ((SetAE2IIRFilter(mSidetoneEQ, &thisFormat, mNumberBands, mBiquadValues) == noErr) &&
(SetGain(mVolume, mVolumeInDB) == noErr))
{
dprintf(DEBUG_INFO, "it is all good\n");
return true;
}
DeleteVolume(mVolume);
mVolume = NULL;
}
DeleteAE2IIRFilter(mSidetoneEQ);
mSidetoneEQ = NULL;
}
delete [] mBiquadValues;
mBiquadValues = NULL;
}
return false;
}
AUSidetone::~AUSidetone()
{
DeleteAE2IIRFilter(mSidetoneEQ);
mSidetoneEQ = NULL;
DeleteVolume(mVolume);
mVolume = NULL;
delete [] mBiquadValues;
mBiquadValues = NULL;
}
// These conversion routines are for translating from UInt32 of the AP/AE2 bridge
// to the floating point of the internal representation. Scaling and offsets applied here.
Float32 AudioUnitParameterValue_To_BypassBand(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value == 0) ? 0.f : 1.f));
return (value == 0) ? 0.f : 1.f;
}
AudioUnitParameterValue BypassBand_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value == 0.f) ? 0 : 1));
return (value == 0.f) ? 0 : 1;
}
UInt32 AudioUnitParameterValue_To_FilterType(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
AudioUnitParameterValue FilterType_To_AudioUnitParameterValue(UInt32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
Float32 AudioUnitParameterValue_To_Frequency(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
AudioUnitParameterValue Frequency_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
Float32 AudioUnitParameterValue_To_Gain(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(-96.0 + (value)));
return -96.0 + (value);
}
AudioUnitParameterValue Gain_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value) - (-96.0)));
return (value) - (-96.0);
}
Float32 AudioUnitParameterValue_To_Bandwidth(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value / 100.0f));
return value / 100.0f;
}
AudioUnitParameterValue Bandwidth_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value * 100.0f));
return value * 100.0f;
}
Float32 AudioUnitParameterValue_To_VolumeInDB(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(-96.0 + (value)));
return -96.0 + (value);
}
AudioUnitParameterValue VolumeInDB_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value) - (-96.0)));
return (value) - (-96.0);
}
OSStatus
AUSidetone::SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "SetParameter %d value %d\n", paramID, uint32_t(value));
if (paramID == kAUVolume_InDB)
{
mVolumeInDB = AudioUnitParameterValue_To_VolumeInDB(value);
return SetGain(mVolume, mVolumeInDB);
}
AudioStreamBasicDescription thisFormat = kDefaultMonoFormat;
thisFormat.mSampleRate = mSampleRate;
thisFormat.mBytesPerPacket *= mNumChannels;
thisFormat.mBytesPerFrame *= mNumChannels;
thisFormat.mChannelsPerFrame *= mNumChannels;
thisFormat.mBitsPerChannel *= mNumChannels;
// get the index
uint32_t which = paramID % 1000;
uint32_t type = (paramID / 1000)*1000;
bool success = false;
if (which < mNumberBands)
{
success = true;
switch (type)
{
case kAUNBandEQParam_BypassBand:
mBiquadValues[which].bypassBand = AudioUnitParameterValue_To_BypassBand(value);
break;
case kAUNBandEQParam_FilterType:
mBiquadValues[which].filterType = AudioUnitParameterValue_To_FilterType(value);
break;
case kAUNBandEQParam_Frequency:
mBiquadValues[which].frequencyInHertz = AudioUnitParameterValue_To_Frequency(value);
break;
case kAUNBandEQParam_Gain:
mBiquadValues[which].gain = AudioUnitParameterValue_To_Gain(value);
break;
case kAUNBandEQParam_Bandwidth:
mBiquadValues[which].bw = AudioUnitParameterValue_To_Bandwidth(value);
break;
default:
success = false;
}
}
OSStatus result = 0;
if (success)
{
result = SetAE2IIRFilter(mSidetoneEQ, &thisFormat, mNumberBands, mBiquadValues);
}
else
{
result = super::SetParameter(paramID, value);
}
dprintf(DEBUG_INFO, "result is %d\n", result);
return result;
}
AudioUnitParameterValue
AUSidetone::GetParameter(AudioUnitParameterID paramID)
{
if (paramID == kAUVolume_InDB)
{
return VolumeInDB_To_AudioUnitParameterValue(mVolumeInDB);
}
// get the index
uint32_t which = paramID % 1000;
uint32_t type = (paramID / 1000)*1000;
if (which < mNumberBands)
{
switch (type)
{
case kAUNBandEQParam_BypassBand:
return BypassBand_To_AudioUnitParameterValue(mBiquadValues[which].bypassBand);
case kAUNBandEQParam_FilterType:
return FilterType_To_AudioUnitParameterValue(mBiquadValues[which].filterType);
case kAUNBandEQParam_Frequency:
return Frequency_To_AudioUnitParameterValue(mBiquadValues[which].frequencyInHertz);
case kAUNBandEQParam_Gain:
return Gain_To_AudioUnitParameterValue(mBiquadValues[which].gain);
case kAUNBandEQParam_Bandwidth:
return Bandwidth_To_AudioUnitParameterValue(mBiquadValues[which].bw);
}
}
return super::GetParameter(paramID);
}
OSStatus
AUSidetone::GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable)
{
OSStatus result = -1;
switch (inID)
{
case kAUSongbird_SidetoneState:
result = GetPropertyInfo(kAUSongbird_SidetoneEQBlockData, outDataSize, outWritable);
if (noErr == result)
{
// leave room for the mVolume field of the AU_SidetoneState;
outDataSize += offsetof(AU_SidetoneState, mFilterDescription);
}
break;
case kAUSongbird_SidetoneEQBlockData:
outDataSize = sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter) + mNumberBands * sizeof(AU_BiquadFilter);
outWritable = true;
result = noErr;
break;
default:
result = super::GetPropertyInfo(inID, outDataSize, outWritable);
}
return result;
}
OSStatus
AUSidetone::GetProperty(AudioUnitPropertyID inID, void* outData)
{
OSStatus result = -1;
switch (inID)
{
case kAUSongbird_SidetoneState:
{
AU_SidetoneState * state = static_cast<AU_SidetoneState*>(outData);
state->mVolume = mVolumeInDB;
state->mBypass = mSidetoneEQBypass;
void * biquad_filter_description = &state->mFilterDescription;
result = GetProperty(kAUSongbird_SidetoneEQBlockData, biquad_filter_description);
}
break;
case kAUSongbird_SidetoneEQBlockData:
{
AU_BiquadFilterDescription * description = static_cast<AU_BiquadFilterDescription*>(outData);
if (description)
{
description->mBypass = mSidetoneEQBypass;
description->mNumberFilters = mNumberBands;
BiquadCoefficientsDescriptor * filter = static_cast<BiquadCoefficientsDescriptor*>(static_cast<void*>(description->mFilters));
if (filter)
{
result = GetAE2IIRFilterCoefficients(mSidetoneEQ, mNumberBands, filter);
}
}
}
break;
default:
result = super::GetProperty(inID, outData);
}
return result;
}
OSStatus
AUSidetone::SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize)
{
OSStatus result = -1;
switch (inID)
{
case kAUSongbird_SidetoneState:
{
// determine if have a valid data structure
if (inDataSize < (offsetof(AU_SidetoneState, mFilterDescription)))
{
break;
}
const AU_SidetoneState * state = static_cast<const AU_SidetoneState*>(inData);
const void * biquad_filter_description = &state->mFilterDescription;
result = SetProperty(kAUSongbird_SidetoneEQBlockData, biquad_filter_description, inDataSize - offsetof(AU_SidetoneState, mFilterDescription));
if (noErr == result)
{
mSidetoneEQBypass = state->mBypass;
mVolumeInDB = state->mVolume;
result = SetGain(mVolume, mVolumeInDB);
}
}
break;
case kAUSongbird_SidetoneEQBlockData:
{
// determine if we are talking to a valid structure
if (inDataSize < sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter))
{
break;
}
const AU_BiquadFilterDescription * description = static_cast<const AU_BiquadFilterDescription*>(inData);
if (inDataSize < ((sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter) + (sizeof(AU_BiquadFilter) * description->mNumberFilters))))
{
break;
}
if (description->mNumberFilters > kMaxNumBands)
{
break;
}
const BiquadCoefficientsDescriptor * filter = static_cast<const BiquadCoefficientsDescriptor*>(static_cast<const void*>(description->mFilters));
result = SetAE2IIRFilterCoefficients(mSidetoneEQ, description->mNumberFilters, static_cast<const BiquadCoefficientsDescriptor*>(filter));
if (!result)
{
mSidetoneEQBypass = description->mBypass;
mNumberBands = description->mNumberFilters;
}
}
break;
default:
result = super::SetProperty(inID, inData, inDataSize);
}
return result;
}
void
AUSidetone::Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess)
{
if (!mSidetoneEQBypass)
{
ProcessAE2IIRFilter(mSidetoneEQ, inFramesToProcess, inSourceP, inDestP);
}
ProcessVolumeInplace(mVolume, inFramesToProcess, inDestP);
}
#endif

View File

@ -0,0 +1,88 @@
/*
* AUSidetone.h
*
* Created by richardp on 11/28/11.
* Copyright 2011 Apple, Inc. All rights reserved.
*
* Does necessary translation between AE2 and CA AU speaker protection
*/
#ifndef __AUSidetone_h__
#define __AUSidetone_h__
#include <stdint.h>
#include "AudioUnitProperties_AE2.h"
#include "AUBase_AE2.h"
struct AE2_IIRFilter;
struct AE2_Volume;
struct BiquadDescriptor;
/**
* AUSidetone implements the sidetone eq and gain.
*
* These values are hard-coded for now.
*/
class AUSidetone : public AUBase_AE2
{
public:
static AUSidetone* Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
virtual ~AUSidetone();
virtual void Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess);
// for parameters
virtual OSStatus SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value);
virtual AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID);
// for properties
virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable);
virtual OSStatus GetProperty(AudioUnitPropertyID inID, void* outData);
virtual OSStatus SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize);
private:
AUSidetone();
bool InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
// some constants
typedef AUBase_AE2 super;
static const size_t kMaxNumBands = 5;
static const float kDefaultSidetoneGain = -32.0;
// Parameters for the AUNBandEQ unit
// Note that the parameter IDs listed correspond to band 0 (zero) of the unit. The parameter IDs for
// higher bands can be obtained by adding the zero-indexed band number to the corresponding band 0
// parameter ID up to the number of bands minus one, where the number of bands is described by the
// AUNBandEQ property kAUNBandEQProperty_NumberOfBands. For example, the parameter ID corresponding
// to the filter type of band 4 would be kAUNBandEQParam_FilterType + 3.
enum {
kAUVolume_InDB = 500,
// Global, Boolean, 0 or 1, 1
kAUNBandEQParam_BypassBand = 1000,
// Global, Indexed, 0->kNumAUNBandEQFilterTypes-1, 0
kAUNBandEQParam_FilterType = 2000,
// Global, Hz, 20->(SampleRate/2), 1000
kAUNBandEQParam_Frequency = 3000,
// Global, dB, -96->24, 0
kAUNBandEQParam_Gain = 4000,
// Global, octaves, 0.05->5.0, 0.5
kAUNBandEQParam_Bandwidth = 5000
};
uint32_t mSampleRate;
uint32_t mNumChannels;
AE2_IIRFilter *mSidetoneEQ;
AE2_Volume *mVolume;
// to hold the coefficients
uint32_t mNumberBands;
BiquadDescriptor *mBiquadValues;
float mVolumeInDB;
bool mSidetoneEQBypass;
};
#endif // __AUSidetone_h__

View File

@ -0,0 +1,309 @@
/*
File: AudioUnitProperties_AE2.h
Contains: Property constants for AudioUnits, stripped down for AE2
Copyright: (c) 2010 by Apple, Inc., all rights reserved.
*/
#ifndef __AUDIOUNITPROPERTIES_AE2__
#define __AUDIOUNITPROPERTIES_AE2__
#include "CoreAudioTypes_AE2.h"
#include "AUComponent_AE2.h"
#pragma mark -
#pragma mark Core Implementation
#pragma mark -
/*!
@enum Audio Unit scope types
@abstract The scope IDs for audio units define basic roles and contexts for an audio unit's state.
@discussion Each scope is a discrete context. Apple reserves scope IDs from 0 through 1024.
@constant kAudioUnitScope_Global The context for audio unit characteristics that apply to the audio unit as a
whole
@constant kAudioUnitScope_Input The context for audio data coming into an audio unit
@constant kAudioUnitScope_Output The context for audio data leaving an audio unit
@constant kAudioUnitScope_Group A context specific to the control scope of parameters (for instance,
MIDI Channels is an example of this scope)
@constant kAudioUnitScope_Part A distinct rendering context. For instance a single timbre in a multi-timbral
instrument, a single loop in a multi looping capable looper unit, etc.
@constant kAudioUnitScope_Layer A context which functions as a layer within a part and allows
grouped control of Cell-scope parameters.
An example is the percussive attack layer for an electric organ instrument
@constant kAudioUnitScope_Note A scope that can be used to apply changes to an individual note. The
elementID used with this scope is the unique note ID returned from
a started note (see MusicDeviceStartNote)
@constant kAudioUnitScope_Cell A scope which represents the finest level of granularity within an AU.
The individual sample zones and envelope generators within a synth are an
example of this.
*/
enum {
kAudioUnitScope_Global = 0,
kAudioUnitScope_Input = 1,
kAudioUnitScope_Output = 2
};
//=====================================================================================================================
#pragma mark - Parameter Definitions
// assume kAudioUnitParameterUnit_Generic if not found in this enum
/*!
@enum AudioUnitParameterUnit
@constant kAudioUnitParameterUnit_Generic
untyped value generally between 0.0 and 1.0
@constant kAudioUnitParameterUnit_Indexed
takes an integer value (good for menu selections)
@constant kAudioUnitParameterUnit_Boolean
0.0 means FALSE, non-zero means TRUE
@constant kAudioUnitParameterUnit_Percent
usually from 0 -> 100, sometimes -50 -> +50
@constant kAudioUnitParameterUnit_Seconds
absolute or relative time
@constant kAudioUnitParameterUnit_SampleFrames
one sample frame equals (1.0/sampleRate) seconds
@constant kAudioUnitParameterUnit_Phase
-180 to 180 degrees
@constant kAudioUnitParameterUnit_Rate
rate multiplier, for playback speed, etc. (e.g. 2.0 == twice as fast)
@constant kAudioUnitParameterUnit_Hertz
absolute frequency/pitch in cycles/second
@constant kAudioUnitParameterUnit_Cents
unit of relative pitch
@constant kAudioUnitParameterUnit_RelativeSemiTones
useful for coarse detuning
@constant kAudioUnitParameterUnit_MIDINoteNumber
absolute pitch as defined in the MIDI spec (exact freq may depend on tuning table)
@constant kAudioUnitParameterUnit_MIDIController
a generic MIDI controller value from 0 -> 127
@constant kAudioUnitParameterUnit_Decibels
logarithmic relative gain
@constant kAudioUnitParameterUnit_LinearGain
linear relative gain
@constant kAudioUnitParameterUnit_Degrees
-180 to 180 degrees, similar to phase but more general (good for 3D coord system)
@constant kAudioUnitParameterUnit_EqualPowerCrossfade
0 -> 100, crossfade mix two sources according to sqrt(x) and sqrt(1.0 - x)
@constant kAudioUnitParameterUnit_MixerFaderCurve1
0.0 -> 1.0, pow(x, 3.0) -> linear gain to simulate a reasonable mixer channel fader response
@constant kAudioUnitParameterUnit_Pan
standard left to right mixer pan
@constant kAudioUnitParameterUnit_Meters
distance measured in meters
@constant kAudioUnitParameterUnit_AbsoluteCents
absolute frequency measurement :
if f is freq in hertz then absoluteCents = 1200 * log2(f / 440) + 6900
@constant kAudioUnitParameterUnit_Octaves
octaves in relative pitch where a value of 1 is equal to 1200 cents
@constant kAudioUnitParameterUnit_BPM
beats per minute, ie tempo
@constant kAudioUnitParameterUnit_Beats
time relative to tempo, i.e., 1.0 at 120 BPM would equal 1/2 a second
@constant kAudioUnitParameterUnit_Milliseconds
parameter is expressed in milliseconds
@constant kAudioUnitParameterUnit_Ratio
for compression, expansion ratio, etc.
@constant kAudioUnitParameterUnit_CustomUnit
this is the parameter unit type for parameters that present a custom unit name
*/
enum
{
kAudioUnitParameterUnit_Generic = 0,
kAudioUnitParameterUnit_Indexed = 1,
kAudioUnitParameterUnit_Boolean = 2,
kAudioUnitParameterUnit_Percent = 3,
kAudioUnitParameterUnit_Seconds = 4,
kAudioUnitParameterUnit_SampleFrames = 5,
kAudioUnitParameterUnit_Phase = 6,
kAudioUnitParameterUnit_Rate = 7,
kAudioUnitParameterUnit_Hertz = 8,
kAudioUnitParameterUnit_Cents = 9,
kAudioUnitParameterUnit_RelativeSemiTones = 10,
kAudioUnitParameterUnit_MIDINoteNumber = 11,
kAudioUnitParameterUnit_MIDIController = 12,
kAudioUnitParameterUnit_Decibels = 13,
kAudioUnitParameterUnit_LinearGain = 14,
kAudioUnitParameterUnit_Degrees = 15,
kAudioUnitParameterUnit_EqualPowerCrossfade = 16,
kAudioUnitParameterUnit_MixerFaderCurve1 = 17,
kAudioUnitParameterUnit_Pan = 18,
kAudioUnitParameterUnit_Meters = 19,
kAudioUnitParameterUnit_AbsoluteCents = 20,
kAudioUnitParameterUnit_Octaves = 21,
kAudioUnitParameterUnit_BPM = 22,
kAudioUnitParameterUnit_Beats = 23,
kAudioUnitParameterUnit_Milliseconds = 24,
kAudioUnitParameterUnit_Ratio = 25,
kAudioUnitParameterUnit_CustomUnit = 26
};
/*!
@typedef AudioUnitParameterUnit
*/
typedef UInt32 AudioUnitParameterUnit;
/*!
@struct AudioUnitParameterInfo
@field name
UNUSED - set to zero - UTF8 encoded C string (originally).
@field unitName
only valid if kAudioUnitParameterUnit_CustomUnit is set. If kAudioUnitParameterUnit_CustomUnit
is set, this field must contain a valid CFString.
@field clumpID
only valid if kAudioUnitParameterFlag_HasClump
@field cfNameString
only valid if kAudioUnitParameterFlag_HasCFNameString
@field unit
if the "unit" field contains a value not in the enum above, then assume
kAudioUnitParameterUnit_Generic
@field minValue
@field maxValue
@field defaultValue
@field flags
Due to some vagaries about the ways in which Parameter's CFNames have been described, it was
necessary to add a flag: kAudioUnitParameterFlag_CFNameRelease
In normal usage a parameter name is essentially a static object, but sometimes an audio unit will
generate parameter names dynamically.. As these are expected to be CFStrings, in that case
the host should release those names when it is finished with them, but there was no way
to communicate this distinction in behavior.
Thus, if an audio unit will (or could) generate a name dynamically, it should set this flag in
the parameter's info. The host should check for this flag, and if present, release the parameter
name when it is finished with it.
*/
typedef struct AudioUnitParameterInfo
{
//char name[52];
//CFStringRef unitName;
UInt32 clumpID;
//CFStringRef cfNameString;
AudioUnitParameterUnit unit;
AudioUnitParameterValue minValue;
AudioUnitParameterValue maxValue;
AudioUnitParameterValue defaultValue;
UInt32 flags;
} AudioUnitParameterInfo;
/*!
@enum Audio Unit Parameter Flags
@discussion Bit positions 18, 17, and 16 are set aside for display scales. Bit 19 is reserved.
@constant kAudioUnitParameterFlag_CFNameRelease
@constant kAudioUnitParameterFlag_MeterReadOnly
@constant kAudioUnitParameterFlag_DisplayMask
@constant kAudioUnitParameterFlag_DisplaySquareRoot
@constant kAudioUnitParameterFlag_DisplaySquared
@constant kAudioUnitParameterFlag_DisplayCubed
@constant kAudioUnitParameterFlag_DisplayCubeRoot
@constant kAudioUnitParameterFlag_DisplayExponential
@constant kAudioUnitParameterFlag_HasClump
@constant kAudioUnitParameterFlag_ValuesHaveStrings
@constant kAudioUnitParameterFlag_DisplayLogarithmic
@constant kAudioUnitParameterFlag_IsHighResolution
@constant kAudioUnitParameterFlag_NonRealTime
@constant kAudioUnitParameterFlag_CanRamp
@constant kAudioUnitParameterFlag_ExpertMode
@constant kAudioUnitParameterFlag_HasCFNameString
@constant kAudioUnitParameterFlag_IsGlobalMeta
@constant kAudioUnitParameterFlag_IsElementMeta
@constant kAudioUnitParameterFlag_IsReadable
@constant kAudioUnitParameterFlag_IsWritable
*/
enum
{
kAudioUnitParameterFlag_CFNameRelease = (1L << 4),
kAudioUnitParameterFlag_MeterReadOnly = (1L << 15),
// bit positions 18,17,16 are set aside for display scales. bit 19 is reserved.
kAudioUnitParameterFlag_DisplayMask = (7L << 16) | (1L << 22),
kAudioUnitParameterFlag_DisplaySquareRoot = (1L << 16),
kAudioUnitParameterFlag_DisplaySquared = (2L << 16),
kAudioUnitParameterFlag_DisplayCubed = (3L << 16),
kAudioUnitParameterFlag_DisplayCubeRoot = (4L << 16),
kAudioUnitParameterFlag_DisplayExponential = (5L << 16),
kAudioUnitParameterFlag_HasClump = (1L << 20),
kAudioUnitParameterFlag_ValuesHaveStrings = (1L << 21),
kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22),
kAudioUnitParameterFlag_IsHighResolution = (1L << 23),
kAudioUnitParameterFlag_NonRealTime = (1L << 24),
kAudioUnitParameterFlag_CanRamp = (1L << 25),
kAudioUnitParameterFlag_ExpertMode = (1L << 26),
kAudioUnitParameterFlag_HasCFNameString = (1L << 27),
kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28),
kAudioUnitParameterFlag_IsElementMeta = (1L << 29),
kAudioUnitParameterFlag_IsReadable = (1L << 30),
kAudioUnitParameterFlag_IsWritable = (1L << 31)
};
typedef struct AUChannelInfo {
SInt16 inChannels;
SInt16 outChannels;
} AUChannelInfo;
//==================================================================================================
/*
@constant kAUSongbird_SidetoneEQBlockData
@discussion Scope: Global
Value Type: blob
Access: read/write
A property to get/set the biquad coefficients of the AE2 SidetoneEQ block as an
array of AU_BiquadFilterDescription, sized to be the number of stages.
*/
enum {
kAUSongbird_SidetoneEQBlockData = 10000,
kAUSongbird_SidetoneState = 10001
};
/*!
@struct AU_BiquadFilter
@field mBypass
Boolean to indicate if biquad is bypassed;
@field b0
@field b1
@field b2
@field a1
@field a2
Fields for biquad coefficients values;
*/
typedef struct AU_BiquadFilter {
UInt32 mBypass;
Float64 b0, b1, b2, a1, a2;
} AU_BiquadFilter;
/*!
@struct AU_BiquadFilterDescription
kAUSongbird_SidetoneEQBlockData data structure
@field mBypass
Boolean to indicate if all biquads are bypassed;
@field mNumberFilters
Number of active filters in the sidetone eq;
@field mFilters
variable length sized as mNumberFilters AU_BiquadFilter structures;
*/
typedef struct AU_BiquadFilterDescription {
UInt32 mBypass;
UInt32 mNumberFilters;
AU_BiquadFilter mFilters[1]; // this is a variable length array of mNumberFilters elements
} AU_BiquadFilterDescription;
/*!
@struct AU_SidetoneState
kAUSongbird_SidetoneState data structure
@field mVolume
Gain for Sidetone;
@field mFilterDescription
Filter description for sidetone;
*/
typedef struct AU_SidetoneState {
bool mBypass;
Float32 mVolume;
AU_BiquadFilterDescription mFilterDescription;
} AU_SidetoneState;
#endif

View File

@ -0,0 +1,248 @@
/*
* CoreAudioTypes_AE2.h
* Stripped down version of CoreAudioTypes just for what AE2 needs
*
* Copyright 2010 Apple Computer. All rights reserved.
*
*/
#ifndef __COREAUDIOTYPES_AE2__
#define __COREAUDIOTYPES_AE2__
#define CA_PREFER_FIXED_POINT 1
#include <stdint.h>
typedef double Float64;
typedef float Float32;
typedef SInt32 OSStatus;
typedef SInt32 ComponentResult;
typedef SInt16 AudioSampleType;
typedef SInt32 AudioUnitSampleType;
//XXX find the place where this is really defined
enum {
noErr = 0
};
/*!
@struct AudioStreamBasicDescription
@abstract This structure encapsulates all the information for describing the basic
format properties of a stream of audio data.
@discussion This structure is sufficient to describe any constant bit rate format that has
channels that are the same size. Extensions are required for variable bit rate
data and for constant bit rate data where the channels have unequal sizes.
However, where applicable, the appropriate fields will be filled out correctly
for these kinds of formats (the extra data is provided via separate properties).
In all fields, a value of 0 indicates that the field is either unknown, not
applicable or otherwise is inapproprate for the format and should be ignored.
Note that 0 is still a valid value for most formats in the mFormatFlags field.
In audio data a frame is one sample across all channels. In non-interleaved
audio, the per frame fields identify one channel. In interleaved audio, the per
frame fields identify the set of n channels. In uncompressed audio, a Packet is
one frame, (mFramesPerPacket == 1). In compressed audio, a Packet is an
indivisible chunk of compressed data, for example an AAC packet will contain
1024 sample frames.
@field mSampleRate
The number of sample frames per second of the data in the stream.
@field mFormatID
A four char code indicating the general kind of data in the stream.
@field mFormatFlags
Flags specific to each format.
@field mBytesPerPacket
The number of bytes in a packet of data.
@field mFramesPerPacket
The number of sample frames in each packet of data.
@field mBytesPerFrame
The number of bytes in a single sample frame of data.
@field mChannelsPerFrame
The number of channels in each frame of data.
@field mBitsPerChannel
The number of bits of sample data for each channel in a frame of data.
@field mReserved
Pads the structure out to force an even 8 byte alignment.
*/
struct AudioStreamBasicDescription
{
Float64 mSampleRate;
UInt32 mFormatID;
UInt32 mFormatFlags;
UInt32 mBytesPerPacket;
UInt32 mFramesPerPacket;
UInt32 mBytesPerFrame;
UInt32 mChannelsPerFrame;
UInt32 mBitsPerChannel;
UInt32 mReserved;
};
typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;
// SMPTETime is also defined in the CoreVideo headers.
#if !defined(__SMPTETime__)
#define __SMPTETime__
/*!
@struct SMPTETime
@abstract A structure for holding a SMPTE time.
@field mSubframes
The number of subframes in the full message.
@field mSubframeDivisor
The number of subframes per frame (typically 80).
@field mCounter
The total number of messages received.
@field mType
The kind of SMPTE time using the SMPTE time type constants.
@field mFlags
A set of flags that indicate the SMPTE state.
@field mHours
The number of hours in the full message.
@field mMinutes
The number of minutes in the full message.
@field mSeconds
The number of seconds in the full message.
@field mFrames
The number of frames in the full message.
*/
struct SMPTETime
{
SInt16 mSubframes;
SInt16 mSubframeDivisor;
UInt32 mCounter;
UInt32 mType;
UInt32 mFlags;
SInt16 mHours;
SInt16 mMinutes;
SInt16 mSeconds;
SInt16 mFrames;
};
typedef struct SMPTETime SMPTETime;
/*!
@enum SMPTE Time Types
@abstract Constants that describe the type of SMPTE time.
@constant kSMPTETimeType24
24 Frame
@constant kSMPTETimeType25
25 Frame
@constant kSMPTETimeType30Drop
30 Drop Frame
@constant kSMPTETimeType30
30 Frame
@constant kSMPTETimeType2997
29.97 Frame
@constant kSMPTETimeType2997Drop
29.97 Drop Frame
@constant kSMPTETimeType60
60 Frame
@constant kSMPTETimeType5994
59.94 Frame
@constant kSMPTETimeType60Drop
60 Drop Frame
@constant kSMPTETimeType5994Drop
59.94 Drop Frame
@constant kSMPTETimeType50
50 Frame
@constant kSMPTETimeType2398
23.98 Frame
*/
enum
{
kSMPTETimeType24 = 0,
kSMPTETimeType25 = 1,
kSMPTETimeType30Drop = 2,
kSMPTETimeType30 = 3,
kSMPTETimeType2997 = 4,
kSMPTETimeType2997Drop = 5,
kSMPTETimeType60 = 6,
kSMPTETimeType5994 = 7,
kSMPTETimeType60Drop = 8,
kSMPTETimeType5994Drop = 9,
kSMPTETimeType50 = 10,
kSMPTETimeType2398 = 11
};
/*!
@enum SMPTE State Flags
@abstract Flags that describe the SMPTE time state.
@constant kSMPTETimeValid
The full time is valid.
@constant kSMPTETimeRunning
Time is running.
*/
enum
{
kSMPTETimeValid = (1 << 0),
kSMPTETimeRunning = (1 << 1)
};
#endif
/*!
@struct AudioTimeStamp
@abstract A structure that holds different representations of the same point in time.
@field mSampleTime
The absolute sample frame time.
@field mHostTime
The host machine's time base, mach_absolute_time.
@field mRateScalar
The ratio of actual host ticks per sample frame to the nominal host ticks
per sample frame.
@field mWordClockTime
The word clock time.
@field mSMPTETime
The SMPTE time.
@field mFlags
A set of flags indicating which representations of the time are valid.
@field mReserved
Pads the structure out to force an even 8 byte alignment.
*/
struct AudioTimeStamp
{
Float64 mSampleTime;
UInt64 mHostTime;
Float64 mRateScalar;
UInt64 mWordClockTime;
SMPTETime mSMPTETime;
UInt32 mFlags;
UInt32 mReserved;
};
typedef struct AudioTimeStamp AudioTimeStamp;
/*!
@enum AudioTimeStamp Flags
@abstract The flags that indicate which fields in an AudioTimeStamp structure are valid.
@constant kAudioTimeStampSampleTimeValid
The sample frame time is valid.
@constant kAudioTimeStampHostTimeValid
The host time is valid.
@constant kAudioTimeStampRateScalarValid
The rate scalar is valid.
@constant kAudioTimeStampWordClockTimeValid
The word clock time is valid.
@constant kAudioTimeStampSMPTETimeValid
The SMPTE time is valid.
*/
enum
{
kAudioTimeStampSampleTimeValid = (1 << 0),
kAudioTimeStampHostTimeValid = (1 << 1),
kAudioTimeStampRateScalarValid = (1 << 2),
kAudioTimeStampWordClockTimeValid = (1 << 3),
kAudioTimeStampSMPTETimeValid = (1 << 4)
};
/*!
@enum Commonly Used Combinations of AudioTimeStamp Flags
@abstract Some commonly used combinations of AudioTimeStamp flags.
@constant kAudioTimeStampSampleHostTimeValid
The sample frame time and the host time are valid.
*/
enum
{
kAudioTimeStampSampleHostTimeValid = (kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid)
};
#endif

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2010 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 "iop_au_interface.h"
#include "AUSidetone.h"
#include "AUNull.h"
#include <debug.h>
extern "C" audio_unit_t Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
#if USE_SIDETONE
AUBase_AE2 *This = AUSidetone::Create_AUSidetone(sampleRate, numChannels, sampleSize);
return (audio_unit_t)This;
#else
return NULL;
#endif
}
extern "C" audio_unit_t Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
AUBase_AE2 *This = AUNull::Create_AUNull(sampleRate, numChannels, sampleSize);
return (audio_unit_t)This;
}
extern "C" void Destroy_AudioUnit(audio_unit_t au)
{
AUBase_AE2 *This = (AUBase_AE2*)au;
delete This;
}
extern "C" int32_t AudioUnit_GetParameter(audio_unit_t au, uint32_t parameter, float *parameterValue)
{
if (!au)
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
*parameterValue = This->GetParameter((AudioUnitParameterID)parameter);
return 0;
}
extern "C" int32_t AudioUnit_SetParameter(audio_unit_t au, uint32_t parameter, float parameterValue)
{
if (!au)
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->SetParameter((AudioUnitParameterID)parameter, (AudioUnitParameterValue)parameterValue);
}
extern "C" int32_t AudioUnit_GetPropertyInfo(audio_unit_t au, uint32_t propID, uint32_t *outSize, bool *outWritable)
{
if (!au)
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->GetPropertyInfo(static_cast<AudioUnitPropertyID>(propID), *outSize, *outWritable);
}
extern "C" int32_t AudioUnit_GetProperty(audio_unit_t au, uint32_t propID, void *outData, uint32_t *ioSize)
{
if (!au)
{
return -1;
}
uint32_t propSize;
bool propWritable;
int32_t result = AudioUnit_GetPropertyInfo(au, propID, &propSize, &propWritable);
if (result)
{
return result;
}
if (propSize > *ioSize)
{
return -1;
}
*ioSize = propSize;
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->GetProperty(static_cast<AudioUnitPropertyID>(propID), outData);
}
extern "C" int32_t AudioUnit_SetProperty(audio_unit_t au, uint32_t propID, const void *inData, uint32_t inSize)
{
if (!au)
{
return -1;
}
uint32_t propSize;
bool propWritable;
int32_t result = AudioUnit_GetPropertyInfo(au, propID, &propSize, &propWritable);
if (result)
{
return result;
}
if ((!propWritable))
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->SetProperty(static_cast<AudioUnitPropertyID>(propID), inData, inSize);
}
extern "C" void AudioUnit_Process(audio_unit_t au, const void *inSourceP, void *inDestP, uint32_t inFramesToProcess)
{
if (!au)
{
return ;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
This->Process(inSourceP, inDestP, inFramesToProcess);
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef _IOP_AU_INTERFACE_H_
#define _IOP_AU_INTERFACE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef void * audio_unit_t;
audio_unit_t Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
audio_unit_t Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
void Destroy_AudioUnit(audio_unit_t au);
// parameters are single values that can be adjusted
int32_t AudioUnit_GetParameter(audio_unit_t au, uint32_t parameter, float *parameterValue);
int32_t AudioUnit_SetParameter(audio_unit_t au, uint32_t parameter, float parameterValue);
// properties are blocks of data that can be adjusted, usually a private setting between user and unit
int32_t AudioUnit_GetPropertyInfo(audio_unit_t au, uint32_t propID, uint32_t *outSize, bool *outWritable);
int32_t AudioUnit_GetProperty(audio_unit_t au, uint32_t propID, void *outData, uint32_t *ioSize);
int32_t AudioUnit_SetProperty(audio_unit_t au, uint32_t propID, const void *inData, uint32_t inSize);
void AudioUnit_Process(audio_unit_t au, const void *inSourceP, void *inDestP, uint32_t inFramesToProcess);
#ifdef __cplusplus
}
#endif
#endif // _IOP_AU_INTERFACE_H_

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2010-2011 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_tap.h"
#include "timestamper.h"
#include <debug.h>
#include <AssertMacros.h>
#include <platform.h>
#include <platform/timer.h>
#include <sys/task.h>
#include "iop_au_interface.h"
typedef struct
{
uint8_t *IOBufferBegin;
uint8_t *IOBufferEnd;
uint8_t *IOBuffer;
} internal_debug_tap_t;
debug_tap_t create_debug_tap(uint8_t *inIOBegin, uint8_t *inIOEnd, uint8_t *inIO)
{
internal_debug_tap_t *This = (internal_debug_tap_t*)malloc(sizeof(debug_tap_t));
if (This)
{
This->IOBufferBegin = inIOBegin;
This->IOBufferEnd = inIOEnd;
This->IOBuffer = inIO;
}
return This;
}
void destroy_debug_tap(debug_tap_t tap)
{
free(tap);
}
static void clean_cache(void *addr, size_t size)
{
// the onus is on the caller to make sure the cache is aligned
void* lineBase = (void*)((uint32_t)addr & ~(CPU_CACHELINE_SIZE-1));
size_t sizeToDo = ((((uint32_t)addr & (CPU_CACHELINE_SIZE-1)) + size)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
platform_cache_operation(CACHE_CLEAN, lineBase, sizeToDo);
}
static void invalidate_cache(void *addr, size_t size)
{
void* lineBase = (void*)((uint32_t)addr & ~(CPU_CACHELINE_SIZE-1));
size_t sizeToDo = ((((uint32_t)addr & (CPU_CACHELINE_SIZE-1)) + size)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
platform_cache_operation(CACHE_INVALIDATE, lineBase, sizeToDo);
}
static size_t min(size_t a, size_t b) { return (a < b) ? a : b; }
size_t send_to_tap(debug_tap_t tap, const uint8_t* src, uint32_t sizeInBytes)
{
internal_debug_tap_t *This = (internal_debug_tap_t*)tap;
size_t result = 0;
// don't transfer anything to user if sizeInBytes is larger than IOBuffer:
if (sizeInBytes > (uint32_t)(This->IOBufferEnd - This->IOBufferBegin))
return result;
// make sure we don't overflow the IOBuffer
size_t dataToSend = min(This->IOBufferEnd - This->IOBuffer, sizeInBytes);
memcpy(This->IOBuffer, src, dataToSend);
clean_cache(This->IOBuffer, dataToSend);
This->IOBuffer += dataToSend;
src += dataToSend;
if (This->IOBuffer >= This->IOBufferEnd)
{
// if we've wrapped around, send the size of the IOBuffer to the caller
This->IOBuffer = This->IOBufferBegin;
result = This->IOBufferEnd - This->IOBufferBegin;
}
// now that we've sent the data, how much is left
dataToSend = sizeInBytes - dataToSend;
// if there is anything left, send it
if (dataToSend)
{
memcpy(This->IOBuffer, src, dataToSend);
clean_cache(This->IOBuffer, sizeInBytes);
This->IOBuffer += dataToSend;
}
return result;
}
size_t get_from_tap(debug_tap_t tap, uint8_t* dst, uint32_t sizeInBytes)
{
internal_debug_tap_t *This = (internal_debug_tap_t*)tap;
size_t result = 0;
// don't transfer anything to user if sizeInBytes is larger than IOBuffer:
if (sizeInBytes > (uint32_t)(This->IOBufferEnd - This->IOBufferBegin))
return result;
// make sure we don't overflow the IOBuffer
size_t dataToGet = min(This->IOBufferEnd - This->IOBuffer, sizeInBytes);
invalidate_cache(This->IOBuffer, dataToGet);
memcpy(dst, This->IOBuffer, dataToGet);
This->IOBuffer += dataToGet;
dst += dataToGet;
if (This->IOBuffer >= This->IOBufferEnd)
{
// if we've wrapped around, send the size of the IOBuffer to the caller
This->IOBuffer = This->IOBufferBegin;
result = This->IOBufferEnd - This->IOBufferBegin;
}
// now that we've got the data, how much is left
dataToGet = sizeInBytes - dataToGet;
// if there is anything left, get it
if (dataToGet)
{
invalidate_cache(This->IOBuffer, dataToGet);
memcpy(dst, This->IOBuffer, dataToGet);
This->IOBuffer += dataToGet;
}
return result;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef _DEBUG_TAP_H_
#define _DEBUG_TAP_H_
#include <platform.h>
/*
* A debug_tap is a way of tapping off or tapping in an audio signal from
* user space. Pass the pointer to the beginning, end, and start to create
* a tap. You send or get data from a tap, which causes the write or read
* head to progress around the buffer circularly. When the process wraps,
* the send or get call returns the total number of bytes sent, otherwise
* it returns 0.
*/
typedef void * debug_tap_t;
debug_tap_t create_debug_tap(uint8_t *inIOBegin, uint8_t *inIOEnd, uint8_t *inIO);
void destroy_debug_tap(debug_tap_t tap);
// return value is nonzero on calls where we wrap IO buffer. Returned
// value indicates number bytes to give to "timestamp" function
size_t send_to_tap(debug_tap_t tap, const uint8_t* src, uint32_t sizeInBytes);
size_t get_from_tap(debug_tap_t tap, uint8_t* dst, uint32_t sizeInBytes);
#endif // _DEBUG_TAP_H_

View File

@ -0,0 +1,893 @@
/*
* 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 <AssertMacros.h>
#include <platform/memmap.h>
#include <platform.h>
#include <platform/timer.h>
#include <sys/task.h>
#include <iop.h>
#include <qwi.h>
#include <qwi_protocol.h>
#include <EmbeddedIOPProtocol.h>
#include <arch.h>
#include <arch/arm/arm.h>
#include <drivers/audio/audio.h>
#include "clock_stepping.h"
#define USE_DMA 0
#include "iop_audiodsp_protocol.h"
#include "iop_au_interface.h"
#include "loopback_process.h"
#include "timestamper.h"
#include "debug_tap.h"
#include "ae2_mca.h"
#if USE_DMA
#include "ae2_i2s.h"
#include "ae2_dma.h"
#else
#include "loopback_device.h"
#endif
static const uint32_t kDefaultSampleRate = 8000;
static const uint32_t kMaxSampleRate = 48000;
// We configure for 0.004 sec of latency.
static const uint32_t kDefaultLatencyInUS = 4000;
static const uint32_t kMaxLatencyInUS = 40000;
// Describing the MCA data format.
static const uint32_t kDefaultNumChannels = 1;
static const uint32_t kMaxNumChannels = 2;
#if USE_DMA
static const uint32_t kValidChannelBitmask = 1;
#endif
static const uint32_t kDefaultSampleSize = 2;
static const size_t kDefaultDMABufferSize = 0x300;
static const AudioDevice_Index kDataDevice = kMCA_0;
#if USE_DMA
static const uint32_t kDataReceivePort = rMCA0_RXDATA;
static const uint32_t kDataTransmitPort = rMCA0_TXDATA;
#endif
static bool audiodsp_message_process(void);
static int iop_audiodsp_task(void *cfg);
static void iop_audiodsp_sleep(int mode);
IOP_FUNCTION(audiodsp, iop_audiodsp_task, 8*1024, AUDIODSP_CONTROL_CHANNEL);
IOP_SLEEP_HOOK(audiodsp, iop_audiodsp_sleep);
static int audiodsp_channel;
#if USE_DMA
static dma_object_t s_input_dma = NULL;
static dma_object_t s_output_dma = NULL;
static void audiodevice_dma_int_err_handler(void *arg);
static void audiodevice_dma_int_handler(void *arg);
static const size_t kNumDMABuffers = 4;
static int16_t *sBuffer[kNumDMABuffers];
static size_t sCurrentProcessBufferIndex;
static DMALinkedListItem *sLLIDMAInput[kNumDMABuffers], *sLLIDMAOutput[kNumDMABuffers];
#endif
#if !USE_DMA
static loopback_device_t s_loopback_device = NULL;
#endif // USE_DMA
// Loopback processing "globals"
static audio_unit_t s_loopback_au = NULL;
// Audio Unit biquad property.
static void* s_loopback_au_state_property = NULL;
static uint32_t s_loopback_au_state_property_size = 0;
// forward declare here. Make sure to change this if id changes
static const uint32_t k_loopback_au_state_property_id = 10001;
static loopback_process_t s_loopback_process = NULL;
static debug_tap_t sInputTap = NULL;
static debug_tap_t sOutputTap = NULL;
// forward declaration:
audio_unit_t Create_Loopback_AU(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess);
/***
* IMPORTANT!
* We assume that all the SRAM allocations will total less than 0x1000.
* Adjust the SRAM Heap in memmap.h if this assumption changes.
*/
static uint32_t sSRAMMallocOffset = 0;
#ifdef AUDIO_SRAM_RESERVE
static const uint32_t kSRAMMallocMax = AUDIO_SRAM_RESERVE;
#else /* !AUDIO_SRAM_RESERVE */
static const uint32_t kSRAMMallocMax = (uint32_t)-1;
#endif /* AUDIO_SRAM_RESERVE */
void* mallocFromSRAM(uint32_t byteSize)
{
//Make sure its a 4-byte aligned allocation (for DMA xfers)
byteSize = (byteSize + 3) / 4;
byteSize *= 4;
//AUDIO_BASE_ADDR is the start of SRAM in AE2
void *ptr = (void*)(AUDIO_BASE_ADDR + sSRAMMallocOffset);
sSRAMMallocOffset += byteSize;
if (sSRAMMallocOffset > kSRAMMallocMax)
panic("mallocFromSRAM beyond limit");
memset(ptr, 0, byteSize);
return ptr;
}
#if USE_DMA
static bool sDMABuffersInitialized = false;
void
initialize_dma_buffers(void)
{
if (!sDMABuffersInitialized)
{
for(uint32_t i = 0; i < kNumDMABuffers; i++)
{
sLLIDMAInput[i] = (DMALinkedListItem *)mallocFromSRAM(sizeof(DMALinkedListItem));
sLLIDMAOutput[i] = (DMALinkedListItem *)mallocFromSRAM(sizeof(DMALinkedListItem));
sBuffer[i] = (int16_t *)mallocFromSRAM(kDefaultDMABufferSize);
}
//Setup input chain for infinite quad-buffer looping, control setup handled in startDMA
for(uint32_t i = 0; i < kNumDMABuffers; i++)
{
// RX goes into buffer 2 on start, so we start the chain on 3
sLLIDMAInput[i]->destination = (uint32_t)sBuffer[(i+3)%kNumDMABuffers];
sLLIDMAInput[i]->next = sLLIDMAInput[(i+1)%kNumDMABuffers];
sLLIDMAInput[i]->source = kDataReceivePort;
// TX goes from buffer 0 on start, so we start the chain on 1
sLLIDMAOutput[i]->source = (uint32_t)sBuffer[(i+1)%kNumDMABuffers];
sLLIDMAOutput[i]->next = sLLIDMAOutput[(i+1)%kNumDMABuffers];
sLLIDMAOutput[i]->destination = kDataTransmitPort;
}
sDMABuffersInitialized = true;
}
}
void
reset_dma_buffers(void)
{
for(uint32_t i = 0; i < kNumDMABuffers; i++)
{
memset(sBuffer[i], 0, kDefaultDMABufferSize);
}
sCurrentProcessBufferIndex = 1;
}
#endif
static int
iop_audiodsp_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
struct task_event* audiodsp_message_event = (struct task_event*) malloc(sizeof(struct task_event));
dprintf(DEBUG_SPEW, "@@ AudioDSP task starting\n");
check(kIOPAUDIODSP_COMMAND_SIZE == sizeof(IOPAUDIODSP_Command));
/* establish the host communications channel */
event_init(audiodsp_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "** opening audiodsp channel\n");
audiodsp_channel = qwi_instantiate_channel("audio command",
QWI_ROLE_CONSUMER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
(qwi_channel_hook)event_signal,
audiodsp_message_event);
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
/* Little doubt we'll need VFP/Neon */
arch_task_fp_enable(true);
#endif
// create a audio unit if one does not exist
if (!s_loopback_au)
{
dprintf(DEBUG_INFO, "creating loopback\n");
s_loopback_au = Create_Loopback_AU(kDefaultSampleRate, kDefaultNumChannels, kDefaultSampleSize);
}
for (;;) {
dprintf(DEBUG_SPEW, "@@ waiting for message on audiodsp channel\n");
while (audiodsp_message_process()) {
// eat all available messages
}
event_wait(audiodsp_message_event);
}
return(0);
}
audio_unit_t Create_Loopback_AU(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
{
audio_unit_t loopback_au = NULL;
dprintf(DEBUG_INFO, "creating loopback\n");
#if USE_SIDETONE
loopback_au = Create_AUSidetone(sampleRate, channels, kDefaultSampleSize);
#else
loopback_au = Create_AUNull(sampleRate, channels, kDefaultSampleSize);
#endif
if (loopback_au && s_loopback_au_state_property && s_loopback_au_state_property_size)
{
// if we had a previous property
uint32_t propSize = s_loopback_au_state_property_size;
bool propWritable = true;
if (/*noErr*/0 == AudioUnit_GetPropertyInfo(loopback_au, k_loopback_au_state_property_id, &propSize, &propWritable))
{
if (propWritable && propSize == s_loopback_au_state_property_size)
{
dprintf(DEBUG_INFO, "Restoring previous SidetoneEQ values\n");
if (/*noErr*/0 != AudioUnit_SetProperty(loopback_au, k_loopback_au_state_property_id, s_loopback_au_state_property, s_loopback_au_state_property_size))
{
dprintf(DEBUG_INFO, "Warning, could not restore previous SidetoneEQ values\n");
// now what? Destroy the AU and start over again
Destroy_AudioUnit(loopback_au);
loopback_au = NULL;
#if USE_SIDETONE
loopback_au = Create_AUSidetone(sampleRate, channels, kDefaultSampleSize);
#else
loopback_au = Create_AUNull(sampleRate, channels, kDefaultSampleSize);
#endif
}
}
}
}
return loopback_au;
}
void Destroy_Loopback_AU(audio_unit_t loopback_au)
{
if (loopback_au)
{
uint32_t propSize = s_loopback_au_state_property_size;
bool propWritable = true;
if (/*noErr*/0 == AudioUnit_GetPropertyInfo(loopback_au, k_loopback_au_state_property_id, &propSize, &propWritable))
{
// clear the old property block if it won't fit
if (s_loopback_au_state_property && s_loopback_au_state_property_size != propSize)
{
free(s_loopback_au_state_property);
s_loopback_au_state_property = NULL;
s_loopback_au_state_property_size = 0;
}
// if we don't have somewhere to write the properties, create it:
if (!s_loopback_au_state_property && (0 != propSize))
{
s_loopback_au_state_property = malloc(propSize);
s_loopback_au_state_property_size = propSize;
}
// if we do have somewhere to write it, save off what we have
if (s_loopback_au_state_property)
{
dprintf(DEBUG_INFO, "Saving SidetoneEQ values\n");
if (/*noErr*/0 != AudioUnit_GetProperty(loopback_au, k_loopback_au_state_property_id, s_loopback_au_state_property, &s_loopback_au_state_property_size))
{
dprintf(DEBUG_INFO, "Warning, could not save SidetoneEQ values\n");
// for whatever reason, we didn't save the property. free it so we don't try to restore it
free(s_loopback_au_state_property);
s_loopback_au_state_property = NULL;
s_loopback_au_state_property_size = 0;
}
}
}
Destroy_AudioUnit(loopback_au);
loopback_au = NULL;
}
}
#if USE_DMA
// Destroy the loopback processing and AU. Assumes that loopback is not running
bool Destroy_Loopback_Processing()
{
if (s_loopback_process)
{
destroy_loopback_process(s_loopback_process);
s_loopback_process = NULL;
}
if (s_loopback_au)
{
Destroy_Loopback_AU(s_loopback_au);
s_loopback_au = NULL;
}
return true;
}
// Creates the loopback processing and AU. Assumes that loopback is not running
// will always destroy any existing loopbacks
bool Create_Loopback_Processing(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
{
Destroy_Loopback_Processing();
// initialize the loopback processing. It will be enabled later
// create a audio unit if one does not exist
if (!s_loopback_au)
{
dprintf(DEBUG_INFO, "creating loopback\n");
s_loopback_au = Create_Loopback_AU(sampleRate, channels, kDefaultSampleSize);
}
dprintf(DEBUG_INFO, "loopback is %p\n", s_loopback_au);
// if we have an audio unit, create a loopback proces
if (s_loopback_au && !s_loopback_process)
{
dprintf(DEBUG_INFO, "creating loopback process\n");
s_loopback_process = create_loopback_process(s_loopback_au, framesToProcess, channels, kDefaultSampleSize, kValidChannelBitmask);
}
dprintf(DEBUG_INFO, "loopback process is %p\n", s_loopback_process);
dprintf(DEBUG_INFO, "free mem after %d\n", heap_get_free_mem());
if (s_loopback_process)
{
return true;
}
return false;
}
#endif
static bool s_loopback_enabled = false;
// return true if loop processing stopped
bool Stop_Loopback_Processing()
{
// if loopback processing not running, done
if(!s_loopback_enabled)
{
return !s_loopback_enabled;
}
#if USE_DMA
// log any sort of errors encountered
if (s_input_dma || s_output_dma)
{
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
// from Alexei:
// Ignore the frame errors. They're not causing
// interrupts and they are expected (because L81
// masters BCLK at 12MHz always, which is more than the
// exact number of bits specified in the MCA config;
// unfortunately, there isn't a way to configure MCA to
// say "give me a frame error if there are fewer bits,
// but not if there are more", which is what you'd
// want.)
if (kFrameError == i)
{
continue;
}
uint32_t error_count = ((s_input_dma) ? getErrorCount(s_input_dma, i) : 0) + ((s_output_dma) ? getErrorCount(s_output_dma, i) : 0);
if (error_count)
{
dprintf(DEBUG_CRITICAL, "DMA encountered %d %s error%s!\n", error_count, kErrorTypeStr[i], error_count > 1 ? "s" : "");
}
}
}
dprintf(DEBUG_INFO, "Going to stop the DMA\n");
if (s_input_dma)
{
destroy_dma_object(s_input_dma);
s_input_dma = NULL;
}
if (s_output_dma)
{
destroy_dma_object(s_output_dma);
s_output_dma = NULL;
}
dprintf(DEBUG_CRITICAL, "Stopped the DMA\n");
#else
// log any sort of errors encountered
if (s_loopback_device)
{
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
// from Alexei:
// Ignore the frame errors. They're not causing
// interrupts and they are expected (because L81
// masters BCLK at 12MHz always, which is more than the
// exact number of bits specified in the MCA config;
// unfortunately, there isn't a way to configure MCA to
// say "give me a frame error if there are fewer bits,
// but not if there are more", which is what you'd
// want.)
if (kFrameError == i)
{
continue;
}
uint32_t error_count = getErrorCount(s_loopback_device, i);
if (error_count)
{
dprintf(DEBUG_CRITICAL, "loopback encountered %d %s error%s!\n", error_count, kErrorTypeStr[i], error_count > 1 ? "s" : "");
}
}
}
dprintf(DEBUG_INFO, "Going to stop the loopback\n");
if (s_loopback_device)
{
destroy_loopback_device(s_loopback_device);
s_loopback_device = NULL;
}
dprintf(DEBUG_CRITICAL, "Stopped the loopback\n");
#endif
SetClockState(kClockRequestLoopback, kClockValueLow);
s_loopback_enabled = false;
return !s_loopback_enabled;
}
// return true if loopback processing started
bool Start_Loopback_Processing(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
{
dprintf(DEBUG_INFO, "Start_Loopback_Processing(sampleRate %d, channels %d, framesToProcess %d\n", sampleRate, channels, framesToProcess);
if (!Stop_Loopback_Processing())
{
dprintf(DEBUG_CRITICAL, "Could not stop loopback\n");
return false;
}
// if loopback processing already running, done
if (s_loopback_enabled)
{
return s_loopback_enabled;
}
if (sampleRate > 32000)
{
dprintf(DEBUG_INFO, "Increasing clock rate for loopback\n");
SetClockState(kClockRequestLoopback, kClockValueHigh);
}
#if USE_DMA
// create the loopback proessing with this sample rate
if (Create_Loopback_Processing(sampleRate, channels, framesToProcess) && s_loopback_process)
{
dprintf(DEBUG_INFO, "starting up DMA\n");
initialize_dma_buffers();
reset_dma_buffers();
if (s_input_dma)
{
destroy_dma_object(s_input_dma);
s_input_dma = NULL;
}
s_input_dma = create_dma_object(sBuffer[2], kDataDevice, sLLIDMAInput[0], kDirectionIn, framesToProcess*channels*kDefaultSampleSize);
if (s_output_dma)
{
destroy_dma_object(s_output_dma);
s_output_dma = NULL;
}
s_output_dma = create_dma_object(sBuffer[0], kDataDevice, sLLIDMAOutput[0], kDirectionOut, framesToProcess*channels*kDefaultSampleSize);
if (s_input_dma && s_output_dma)
{
setupInterruptHandler(s_input_dma, audiodevice_dma_int_handler, s_loopback_process);
setupErrorHandler(s_input_dma, audiodevice_dma_int_err_handler, NULL);
setupErrorHandler(s_output_dma, audiodevice_dma_int_err_handler, NULL);
startDMAObject(s_input_dma);
startDMAObject(s_output_dma);
s_loopback_enabled = true;
}
else
{
dprintf(DEBUG_CRITICAL, "could not create DMA objects!\n");
}
}
#else
if (s_loopback_au)
{
Destroy_Loopback_AU(s_loopback_au);
s_loopback_au = NULL;
}
dprintf(DEBUG_INFO, "creating loopback\n");
s_loopback_au = Create_Loopback_AU(sampleRate, channels, kDefaultSampleSize);
// create the loopback proessing with this sample rate
if (s_loopback_au)
{
dprintf(DEBUG_INFO, "starting up loopback_device\n");
if (s_loopback_device)
{
destroy_loopback_device(s_loopback_device);
s_loopback_device = NULL;
}
s_loopback_device = create_loopback_device(kDataDevice, s_loopback_au, channels * kDefaultSampleSize);
if (s_loopback_device)
{
s_loopback_enabled = start_loopback_device(s_loopback_device);
}
else
{
dprintf(DEBUG_CRITICAL, "could not create loopback device!\n");
}
}
#endif
return s_loopback_enabled;
}
bool Check_Parameter_Message_Valid(IOPAUDIODSP_MODULE_COMMAND *command)
{
if(command->mModule != kIOPAUDIODSP_MODULE_LOOPBACK_PROCESSING)
{
dprintf(DEBUG_CRITICAL, "Invalid kIOPAUDIODSP_OPCODE_SET_PARAMETER received!\n");
return false;
}
if(!s_loopback_au)
{
dprintf(DEBUG_CRITICAL, "No existing loopback processing audio unit!\n");
return false;
}
return true;
}
bool Check_Property_Message_Valid(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Parameter_Message_Valid(command))
{
return false;
}
size_t max_size_for_property = sizeof(IOPAUDIODSP_Command) - ((uint32_t)&command->mProperty.mPropertyData - (uint32_t)command);
//make sure its formatted correctly ...
if(command->mProperty.mPropertySizeBytes > max_size_for_property)
{
dprintf(DEBUG_CRITICAL, "Invalid message size received!\n");
return false;
}
return true;
}
bool Handle_OPCODE_GET_PARAMETER(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Parameter_Message_Valid(command))
{
return false;
}
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
if (AudioUnit_GetParameter(s_loopback_au, command->mParameter.mParameterID, &command->mParameter.mParameterValue))
{
dprintf(DEBUG_CRITICAL, "FAILED getting parameter(%d) on loopback process!\n", command->mParameter.mParameterID);
return false;
}
return true;
}
bool Handle_OPCODE_SET_PARAMETER(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Parameter_Message_Valid(command))
{
return false;
}
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
if (AudioUnit_SetParameter(s_loopback_au, command->mParameter.mParameterID, command->mParameter.mParameterValue))
{
dprintf(DEBUG_CRITICAL, "FAILED setting parameter(%d) on loopback process!\n", command->mParameter.mParameterID);
return false;
}
return true;
}
bool Handle_OPCODE_GET_PROPERTY(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Property_Message_Valid(command))
{
return false;
}
uint32_t max_size_for_property = sizeof(IOPAUDIODSP_Command) - ((uint32_t)&command->mProperty.mPropertyData - (uint32_t)command);
if (AudioUnit_GetProperty(s_loopback_au, command->mProperty.mPropertyID, command->mProperty.mPropertyData, &max_size_for_property) )
{
dprintf(DEBUG_CRITICAL, "FAILED getting property(%d) on loopback process!\n", command->mProperty.mPropertyID);
return false;
}
command->mProperty.mPropertySizeBytes = max_size_for_property;
return true;
}
bool Handle_OPCODE_SET_PROPERTY(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Property_Message_Valid(command))
{
return false;
}
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
if (AudioUnit_SetProperty(s_loopback_au, command->mProperty.mPropertyID, command->mProperty.mPropertyData, command->mProperty.mPropertySizeBytes))
{
dprintf(DEBUG_CRITICAL, "FAILED setting parameter(%d) on loopback process!\n", command->mProperty.mPropertyID);
return false;
}
return true;
}
static bool
audiodsp_message_process(void)
{
uint32_t message;
IOPAUDIODSP_Command* command;
dprintf(DEBUG_SPEW, "@@ handling host message\n");
/* look to see if there's an item waiting for us */
if (qwi_receive_item(audiodsp_channel, &message) == -1)
return(false);
dprintf(DEBUG_SPEW, "@@ received audio message\n");
/* find the command structure based on the message */
command = (IOPAUDIODSP_Command*)mem_static_map_cached(message);
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the command size is a multiple of our cacheline size.
*/
platform_cache_operation(CACHE_INVALIDATE,
(void *)command,
sizeof(*command));
// assume every command is a failure unless it gets set to success
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_FAILURE;
switch (command->iopaudiodsp.mOpcode) {
case kIOPAUDIODSP_OPCODE_START_LOOPBACK_PROCESSING:
{
uint32_t sampleRate = kDefaultSampleRate;
uint32_t channels = kDefaultNumChannels;
uint32_t latency = kDefaultLatencyInUS;
// read values from the paramter block
uint32_t* parameterBlock = (uint32_t *)command->start.mAdditionalParameters;
int32_t parameterBlockSize = command->start.mAdditionalParametersSizeBytes;
if (parameterBlockSize > 0)
{
sampleRate = *parameterBlock;
sampleRate = (sampleRate < kMaxSampleRate) ? sampleRate : kMaxSampleRate;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
latency = *parameterBlock;
latency = (latency < kMaxLatencyInUS) ? latency : kMaxLatencyInUS;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
channels = *parameterBlock;
channels = (channels < kMaxNumChannels) ? channels : kMaxNumChannels;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (!sampleRate || !latency || !channels)
{
dprintf(DEBUG_CRITICAL, "Some parameter not correct (sampleRate = %d, latency = %d, channels = %d)\n", sampleRate, latency, channels);
break;
}
dprintf(DEBUG_CRITICAL, "using values sample rate %d, latency %d, channels %d\n", sampleRate, latency, channels);
uint32_t bytesToProcess = ((sampleRate * latency * channels) / 1000000) * kDefaultSampleSize;
// because we have an input buffer and process buffer, need to cut the value in half
bytesToProcess /= 2;
bytesToProcess = (bytesToProcess < kDefaultDMABufferSize) ? bytesToProcess : kDefaultDMABufferSize;
// because the DMA will do burst-4, make sure we have a valid bytes to process size:
uint32_t burstSize = kDefaultSampleSize * 4;
bytesToProcess = ((bytesToProcess + burstSize - 1)/burstSize) * burstSize;
bytesToProcess = (bytesToProcess < kDefaultDMABufferSize) ? bytesToProcess : kDefaultDMABufferSize;
uint32_t framesToProcess = bytesToProcess / (channels * kDefaultSampleSize);
dprintf(DEBUG_CRITICAL, "Start command with sample rate %d, channels %d, frames %d\n", sampleRate, channels, framesToProcess);
// now what would the latency be?
latency = (framesToProcess * 1000000) / sampleRate;
// because we had to half the latency, we need to double it.
latency *= 2;
if (Start_Loopback_Processing(sampleRate, channels, framesToProcess))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
dprintf(DEBUG_CRITICAL, "Started loopback processing!\n");
}
else
{
dprintf(DEBUG_CRITICAL, "Could not start loopback processing!\n");
}
// write values back to the paramter block
parameterBlock = (uint32_t *)command->start.mAdditionalParameters;
parameterBlockSize = command->start.mAdditionalParametersSizeBytes;
if (parameterBlockSize > 0)
{
*parameterBlock = sampleRate;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
*parameterBlock = latency;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
*parameterBlock = channels;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
}
break;
case kIOPAUDIODSP_OPCODE_STOP_LOOPBACK_PROCESSING:
{
if (Stop_Loopback_Processing())
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
dprintf(DEBUG_CRITICAL, "Stopped loopback processing!\n");
}
}
break;
case kIOPAUDIODSP_OPCODE_GET_PROPERTY:
{
if (Handle_OPCODE_GET_PROPERTY(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_SET_PROPERTY:
{
if (Handle_OPCODE_SET_PROPERTY(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_GET_PARAMETER:
{
if (Handle_OPCODE_GET_PARAMETER(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_SET_PARAMETER:
{
if (Handle_OPCODE_SET_PARAMETER(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_INITTIMESTAMP:
{
dprintf(DEBUG_CRITICAL, "Timestamp buffer is 0x%x\n", command->init_timestamp.mTimeStamperBufferAddr);
set_timestamper_message_buffer((IOPAUDIODSP_Command *)mem_static_map_cached(command->init_timestamp.mTimeStamperBufferAddr));
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
break;
case kIOPAUDIODSP_OPCODE_DO_TRANSFER:
{
dprintf(DEBUG_CRITICAL, "Starting Transfer\n");
dprintf(DEBUG_CRITICAL, "Index %d\n", command->do_transfer.mIndex);
dprintf(DEBUG_CRITICAL, "Direction %d\n", command->do_transfer.mDirection);
dprintf(DEBUG_CRITICAL, "Do transfer %d\n", command->do_transfer.mDoTransfer);
dprintf(DEBUG_CRITICAL, "Buffer boundary [0x%x,0x%x), starting at offset 0x%x\n", command->do_transfer.mBufferBegin, command->do_transfer.mBufferEnd, command->do_transfer.mBufferStart);
dprintf(DEBUG_CRITICAL, "Sample Rate %d, bytes per frame %d\n", command->do_transfer.mSampleRate, command->do_transfer.mBytesPerFrame);
bool doTransfer = command->do_transfer.mDoTransfer;
uint32_t index = command->do_transfer.mIndex;
if (s_loopback_process)
{
set_tap_point(s_loopback_process, index);
}
if (doTransfer)
{
uint8_t *IOBufferBegin = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferBegin);
uint8_t *IOBufferEnd = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferEnd);
uint8_t *IOBuffer = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferStart);
// unload and delete any tap points
set_debug_tap(s_loopback_process, NULL, NULL);
if (sInputTap)
{
destroy_debug_tap(sInputTap);
sInputTap = NULL;
}
if (sOutputTap)
{
destroy_debug_tap(sOutputTap);
sOutputTap = NULL;
}
// create the tap points
if (command->do_transfer.mDirection & 0x1)
{
sInputTap = create_debug_tap(IOBufferBegin, IOBufferEnd, IOBuffer);
}
if (command->do_transfer.mDirection & 0x2)
{
sOutputTap = create_debug_tap(IOBufferBegin, IOBufferEnd, IOBuffer);
}
// set the tap points
if (s_loopback_process)
{
set_debug_tap(s_loopback_process, sInputTap, sOutputTap);
}
}
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
break;
default:
dprintf(DEBUG_CRITICAL, "@@ ERROR: unrecognised audiodsp opcode 0x%x\n",
command->iopaudiodsp.mOpcode);
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_PARAM_INVALID;
break;
}
dprintf(DEBUG_SPEW, "@@ done processing audiodsp message with status 0x%08x\n", command->iopaudiodsp.mStatus);
platform_cache_operation(CACHE_CLEAN,
(void *)command,
sizeof(IOPAUDIODSP_Command));
qwi_send_item(audiodsp_channel, message);
dprintf(DEBUG_SPEW, "@@ signaled completion of audiodsp message to host\n");
return(true);
}
static void
iop_audiodsp_sleep(int mode)
{
}
#if USE_DMA
static void
audiodevice_dma_int_handler_wFloat(void *arg)
{
sCurrentProcessBufferIndex = (sCurrentProcessBufferIndex+1) % kNumDMABuffers;
process_data((loopback_process_t)arg, sBuffer[sCurrentProcessBufferIndex]);
}
static void
audiodevice_dma_int_handler(void *arg)
{
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
//Enables floating point ops from the ISR
enter_critical_section();
arm_call_fpsaved(arg, &audiodevice_dma_int_handler_wFloat);
exit_critical_section();
#endif
}
static void
audiodevice_dma_int_err_handler(void *arg)
{
dprintf(DEBUG_CRITICAL, "audiodevice_dma_int_err_handler!\n");
}
#endif

View File

@ -0,0 +1,219 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef _IOP_AUDIODSP_PROTOCOL_H_
#define _IOP_AUDIODSP_PROTOCOL_H_
#include <sys/types.h>
/*
* Command size is (somewhat) tunable.
*
* The principal consideration here is the maximum scatter/gather list size
* this permits.
*/
#define kIOPAUDIODSP_COMMAND_SIZE (512)
/*
* IOPAUDIODSP_opcode_t: identifies the command sent and from the AE2
*/
typedef uint32_t IOPAUDIODSP_opcode_t;
#define kIOPAUDIODSP_OPCODE_UNKNOWN ((IOPAUDIODSP_opcode_t) 0)
/*
* Commands for communicating with DSP library(ies)
*/
#define kIOPAUDIODSP_OPCODE_START_LOOPBACK_PROCESSING ((IOPAUDIODSP_opcode_t) 3)
#define kIOPAUDIODSP_OPCODE_STOP_LOOPBACK_PROCESSING ((IOPAUDIODSP_opcode_t) 4)
#define kIOPAUDIODSP_OPCODE_INITTIMESTAMP ((IOPAUDIODSP_opcode_t) 5)
#define kIOPAUDIODSP_OPCODE_TIMESTAMP ((IOPAUDIODSP_opcode_t) 6)
#define kIOPAUDIODSP_OPCODE_SET_PARAMETER ((IOPAUDIODSP_opcode_t) 7)
#define kIOPAUDIODSP_OPCODE_GET_PARAMETER ((IOPAUDIODSP_opcode_t) 8)
#define kIOPAUDIODSP_OPCODE_DO_TRANSFER ((IOPAUDIODSP_opcode_t) 9)
#define kIOPAUDIODSP_OPCODE_SET_PROPERTY ((IOPAUDIODSP_opcode_t) 10)
#define kIOPAUDIODSP_OPCODE_GET_PROPERTY ((IOPAUDIODSP_opcode_t) 11)
/*
* IOPAUDIODSP_status_t: result from commands
*/
typedef uint32_t IOPAUDIODSP_status_t;
#define kIOPAUDIODSP_STATUS_UNKNOWN ((IOPAUDIODSP_status_t) 0)
#define kIOPAUDIODSP_STATUS_SUCCESS ((IOPAUDIODSP_status_t) 1)
#define kIOPAUDIODSP_STATUS_FAILURE ((IOPAUDIODSP_status_t) 0x80000000)
#define kIOPAUDIODSP_STATUS_DEVICE_ERROR ((IOPAUDIODSP_status_t) 0x80000001)
#define kIOPAUDIODSP_STATUS_DEVICE_TIMEOUT ((IOPAUDIODSP_status_t) 0x80000002)
#define kIOPAUDIODSP_STATUS_DMA_TIMEOUT ((IOPAUDIODSP_status_t) 0x80000003)
#define kIOPAUDIODSP_STATUS_PARAM_INVALID ((IOPAUDIODSP_status_t) 0x80000004)
#define kIOPAUDIODSP_STATUS_UNIMPLEMENTED ((IOPAUDIODSP_status_t) 0x80000005)
/*
* IOPAUDIODSP_module_t: dsp modules
*/
typedef uint32_t IOPAUDIODSP_module_t;
#define kIOPAUDIODSP_MODULE_LOOPBACK_PROCESSING ((IOPAUDIODSP_module_t) 1)
/*
* IOPAUDIODSP_token_t: The opaque token to use for audio processing
*/
typedef uint32_t IOPAUDIODSP_token_t;
// Token for sending messages to the iop_audiodsp system itself
#define kIOPAUDIODSP_system_token_t ((IOPAUDIODSP_opcode_t) 0xFFFFFFFF)
/*
* IOPAUDIODSP_status_t result from commands
*/
struct _IOPAUDIODSP
{
IOPAUDIODSP_opcode_t mOpcode;
IOPAUDIODSP_status_t mStatus;
};
typedef struct _IOPAUDIODSP IOPAUDIODSP;
/*
* Messages. Message layout is dependent on the opcode. For instance, if
* the opcode is kIOPAUDIODSP_OPCODE_START, then the message will be interpreted
* as IOPAUDIODSP_START.
* It is the senders responsiblity to make sure a message is set correctly.
*/
/*
* IOPAUDIODSP_START message
* Message used to start a new audio process.
* If message succeeds, iopToken will contain the token used for processing.
* Fill the input and output with necessary parameters. Additional parameters
* passed through the additional_paramters pointer.
*/
struct _IOPAUDIODSP_START
{
IOPAUDIODSP mIOPHeader;
uint32_t mAdditionalParametersSizeBytes;
uint8_t mAdditionalParameters[];
};
typedef struct _IOPAUDIODSP_START IOPAUDIODSP_START;
/*
* IOPAUDIODSP_STOP message
* Message used to destroy a token.
* If message succeeds, iopToken is no longer valid.
*/
struct _IOPAUDIODSP_STOP
{
IOPAUDIODSP mIOPHeader;
};
typedef struct _IOPAUDIODSP_STOP IOPAUDIODSP_STOP;
/*
* IOPAUDIODSP_INITTIMESTAMP message
*/
struct _IOPAUDIODSP_INITTIMESTAMP
{
IOPAUDIODSP mIOPHeader;
uint32_t mTimeStamperBufferAddr;
};
typedef struct _IOPAUDIODSP_INITTIMESTAMP IOPAUDIODSP_INITTIMESTAMP;
/*
* IOPAUDIODSP_TIMESTAMP message
*/
struct _IOPAUDIODSP_TIMESTAMP
{
IOPAUDIODSP mIOPHeader;
uint64_t mSampleCount;
uint64_t mTimeStamp;
};
typedef struct _IOPAUDIODSP_TIMESTAMP IOPAUDIODSP_TIMESTAMP;
/*
* IOPAUDIODSP_DO_TRANSFER message
*/
struct _IOPAUDIODSP_DO_TRANSFER
{
IOPAUDIODSP mIOPHeader;
uint32_t mIndex;
uint32_t mDirection;
uint32_t mDoTransfer;
// send the address range of the buffer to work with, as well as where processing should start
uint32_t mBufferBegin;
uint32_t mBufferEnd;
uint32_t mBufferStart;
// this are necessary if we do psuedo transfer by timer
uint32_t mSampleRate;
// this is necessary to report samples transferred
uint32_t mBytesPerFrame;
};
typedef struct _IOPAUDIODSP_DO_TRANSFER IOPAUDIODSP_DO_TRANSFER;
/*
* Format of Parameters is (IOPAUDIODSP_PARAMETER_SUBCOMMAND) ...
* {
* parameter (4-bytes)
* parameter_value (float)
* }
*
*/
struct _IOPAUDIODSP_PARAMETER_SUBCOMMAND
{
uint32_t mParameterID;
float mParameterValue;
};
typedef struct _IOPAUDIODSP_PARAMETER_SUBCOMMAND IOPAUDIODSP_PARAMETER_SUBCOMMAND;
/*
* Format of Properties is (IOPAUDIODSP_PROPERTY_SUBCOMMAND) ...
* {
* Property (4-bytes)
* Property_size (4-byte)
* Property (void*)
* }
*
*/
struct _IOPAUDIODSP_PROPERTY_SUBCOMMAND
{
uint32_t mPropertyID;
uint32_t mPropertySizeBytes;
uint8_t mPropertyData[];
};
typedef struct _IOPAUDIODSP_PROPERTY_SUBCOMMAND IOPAUDIODSP_PROPERTY_SUBCOMMAND;
struct _IOPAUDIODSP_MODULE_COMMAND
{
IOPAUDIODSP mIOPHeader;
uint32_t mModule;
union
{
IOPAUDIODSP_PROPERTY_SUBCOMMAND mProperty;
IOPAUDIODSP_PARAMETER_SUBCOMMAND mParameter;
};
};
typedef struct _IOPAUDIODSP_MODULE_COMMAND IOPAUDIODSP_MODULE_COMMAND;
union _IOPAUDIODSP_Command
{
IOPAUDIODSP iopaudiodsp;
IOPAUDIODSP_START start;
IOPAUDIODSP_STOP stop;
IOPAUDIODSP_INITTIMESTAMP init_timestamp;
IOPAUDIODSP_DO_TRANSFER do_transfer;
IOPAUDIODSP_TIMESTAMP timestamp;
IOPAUDIODSP_MODULE_COMMAND module_command;
UInt8 _pad[kIOPAUDIODSP_COMMAND_SIZE];
};
typedef union _IOPAUDIODSP_Command IOPAUDIODSP_Command;
#endif // _IOP_AUDIODSP_PROTOCOL_H_

View File

@ -0,0 +1,237 @@
/*
* 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 "loopback_device.h"
#include "ae2_mca.h"
#include <arch/arm/arm.h>
#include <drivers/audio/audio.h>
#include <debug.h>
#include <stdlib.h>
#include <platform/timer.h>
enum { kBurstSize = 4 };
typedef struct
{
audio_unit_t mAudioUnit;
size_t mBytesPerFrame;
uint32_t mErrorCount[kAudioDevice_Last];
} internal_loopback_device_t;
uint32_t readReg(uint32_t address)
{
return *(volatile uint32_t *)address;
}
void writeReg(uint32_t address, uint32_t value)
{
*(volatile uint32_t *)address = value;
}
uint32_t readMCA0Reg(uint32_t offset)
{
return readReg(rMCA0_BASE + offset);
}
void writeMCA0Reg(uint32_t offset, uint32_t value)
{
writeReg(rMCA0_BASE + offset, value);
}
// if this function takes too long, bail. We cannot hang the system here.
static const utime_t kMaxTime = 1000000;
bool startPIO()
{
bool result = true;
const utime_t time_out = system_time() + kMaxTime;
// disable TX and RX
uint32_t MCATXCFG = readMCA0Reg(rMCATXCFG);
writeMCA0Reg(rMCATXCFG, MCATXCFG & ~(1));
uint32_t MCARXCFG = readMCA0Reg(rMCARXCFG);
writeMCA0Reg(rMCARXCFG, MCARXCFG & ~(1));
// Flush the RX and TX fifos
writeMCA0Reg(rMCACTL, 0x300);
uint32_t MCACTL = readMCA0Reg(rMCACTL);
while (result && (MCACTL & 0x300))
{
MCACTL = readMCA0Reg(rMCACTL);
result = system_time() < time_out;
}
// "Prime" the tx with zeros.
uint32_t tx_fifo_level = (readMCA0Reg(rMCASTATUS)) & 0x3FF;
while (result && (tx_fifo_level < 0x8))
{
writeMCA0Reg(rMCATXDATA, 0);
tx_fifo_level = (readMCA0Reg(rMCASTATUS)) & 0x3FF;
result = system_time() < time_out;
}
// enable RX IRQ (we don't need TX IRQ, we only respond when we have RX samples)
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG | (1 << rMCAUNSRXCFG_IRQ_EN));
// enable TX and RX
writeMCA0Reg(rMCATXCFG, MCATXCFG);
writeMCA0Reg(rMCARXCFG, MCARXCFG);
return result;
}
bool stopPIO()
{
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG & ~((1 << rMCAUNSRXCFG_IRQ_EN)));
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG & ~((1 << rMCAUNSTXCFG_IRQ_EN)));
return true;
}
static void
handleAudioDeviceErrors(internal_loopback_device_t * This, const uint32_t status)
{
// <rdar://problem/13467070> Panic when setting AppleSongbirdDSP sidetone EQ
// clear any sort of sticky-bit errors.
const uint32_t errors = status & ( rMCASTATUS_FRAMEEEROR_MASK | rMCASTATUS_RXOVERRUN_MASK | rMCASTATUS_RXUNDERRUN_MASK | rMCASTATUS_TXOVERRUN_MASK | rMCASTATUS_TXUNDERRUN_MASK );
writeMCA0Reg(rMCASTATUS, errors);
if (errors && This)
{
if (status & rMCASTATUS_FRAMEEEROR_MASK) ++This->mErrorCount[kFrameError];
if (status & rMCASTATUS_RXOVERRUN_MASK) ++This->mErrorCount[kRXOverrun];
if (status & rMCASTATUS_RXUNDERRUN_MASK) ++This->mErrorCount[kRXUnderrun];
if (status & rMCASTATUS_TXOVERRUN_MASK) ++This->mErrorCount[kTXOverrun];
if (status & rMCASTATUS_TXUNDERRUN_MASK) ++This->mErrorCount[kTXUnderrun];
}
}
static void
handleAudioDeviceInterrupt_wFloat(void *arg)
{
internal_loopback_device_t * This = (internal_loopback_device_t *)arg;
const uint32_t status = readMCA0Reg(rMCASTATUS);
handleAudioDeviceErrors(This, status);
// where the magic occurs
if ((status & (1 << rMCASTATUS_RXHIGHWATER)))
{
// we do burst sizes of 16-bit data
int16_t data[kBurstSize];
for (size_t i = 0; i < kBurstSize; ++i)
{
data[i] = readMCA0Reg(rMCARXDATA);
}
if (This && This->mAudioUnit)
{
AudioUnit_Process(This->mAudioUnit, data, data, sizeof(data)/This->mBytesPerFrame);
}
// <rdar://problem/14296993>, only write if there is room in the TX fifo
const uint32_t tx_fifo_level = status & 0x3FF;
const uint32_t k_tx_fifo_size = readMCA0Reg(rMCAFIFOSIZE) & 0x3FF;
if ((tx_fifo_level + kBurstSize) < k_tx_fifo_size)
{
for (size_t i = 0; i < kBurstSize; ++i)
{
writeMCA0Reg(rMCATXDATA, data[i]);
}
}
}
}
// enable floating point code
void handleAudioDeviceInterrupt(void * object)
{
enter_critical_section();
arm_call_fpsaved(object, &handleAudioDeviceInterrupt_wFloat);
exit_critical_section();
}
// create a loopback_device that will read from device rx, optional process, and send to device tx
loopback_device_t create_loopback_device(AudioDevice_Index device, audio_unit_t optional_au, size_t bytes_per_frame)
{
dprintf(DEBUG_CRITICAL, "Creating a loopback_device object\n");
// hardcoding to support only MCA right now.
if ((device != kMCA_0))
{
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
return NULL;
}
internal_loopback_device_t *This = (internal_loopback_device_t*)malloc(sizeof(internal_loopback_device_t));
if (This)
{
This->mAudioUnit = optional_au;
This->mBytesPerFrame = bytes_per_frame;
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
This->mErrorCount[i] = 0;
}
// we can do this here repeatedly
install_int_handler(AE2_INT_MCA0, handleAudioDeviceInterrupt, This);
}
return This;
}
void destroy_loopback_device(loopback_device_t device)
{
// preliminary stop
stop_loopback_device(device);
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
if (This)
{
free(This);
}
}
uint32_t getErrorCount(loopback_device_t dma, Error_Index which)
{
// preliminary stop
internal_loopback_device_t *This = (internal_loopback_device_t*)dma;
if (This)
{
return This->mErrorCount[which];
}
return 0;
}
bool start_loopback_device(loopback_device_t device)
{
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
if (This && startPIO())
{
unmask_int(AE2_INT_MCA0);
return true;
}
return false;
}
bool stop_loopback_device(loopback_device_t device)
{
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
if (This)
{
mask_int(AE2_INT_MCA0);
return stopPIO();
}
return false;
}

View File

@ -0,0 +1,61 @@
/*
* 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.
*/
#ifndef __LOOPBACK_DEVICE_H__
#define __LOOPBACK_DEVICE_H__
#include "iop_au_interface.h"
typedef void * loopback_device_t;
/*
* loopback_device_t is an object that handles looping device rx to device tx
*/
typedef enum {
kI2S_0 = 0,
kI2S_1,
kI2S_2,
kI2S_3,
kMCA_0,
kMCA_1,
kAudioDevice_Last
} AudioDevice_Index;
// create a loopback_device that will read from device rx, optional process, and send to device tx
loopback_device_t create_loopback_device(AudioDevice_Index device, audio_unit_t optional_au, size_t bytes_per_frame);
void destroy_loopback_device(loopback_device_t device);
typedef enum {
kFrameError = 0,
kRXOverrun,
kRXUnderrun,
kTXOverrun,
kTXUnderrun,
kError_last
} Error_Index;
static const char* const kErrorTypeStr[kError_last] = {
"Frame_Error",
"RX_Overrun",
"RX_Underrun",
"TX_Overrun",
"TX_Underrun",
};
uint32_t getErrorCount(loopback_device_t device, Error_Index which);
// return true on success
bool start_loopback_device(loopback_device_t device);
bool stop_loopback_device(loopback_device_t device);
#endif /* __LOOPBACK_DEVICE_H__ */

View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2010-2011 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 "loopback_process.h"
#include "timestamper.h"
#include "debug_tap.h"
#include <debug.h>
#include <AssertMacros.h>
#include <platform.h>
#include <platform/timer.h>
#include <sys/task.h>
typedef struct
{
uint32_t mNumFrames;
uint32_t mNumChannels;
uint32_t mSampleSize;
uint32_t mChannelBitmask;
// the scratch buffer is used by the AudioUnit. We assume it needs
// 8.24 Mono (channel 0) data
audio_unit_t mAudioUnit;
// for debug: which index to send to user
uint32_t mIndex;
debug_tap_t mInputTap;
debug_tap_t mOutputTap;
} internal_loopback_process_t;
// XXX channel bitmask currently not implemented. Assuming audio data is packed.
loopback_process_t create_loopback_process(audio_unit_t au, uint32_t frames_to_process, uint32_t channels_to_process, uint32_t sample_size, uint32_t channel_bitmask)
{
internal_loopback_process_t *This = (internal_loopback_process_t*)malloc(sizeof(internal_loopback_process_t));
if (This)
{
This->mNumFrames = frames_to_process;
This->mNumChannels = channels_to_process;
This->mSampleSize = sample_size;
This->mChannelBitmask = channel_bitmask;
This->mAudioUnit = au;
This->mIndex = 0;
This->mInputTap = NULL;
This->mOutputTap = NULL;
}
return This;
}
void destroy_loopback_process(loopback_process_t loopback)
{
internal_loopback_process_t *This = (internal_loopback_process_t*)loopback;
free(This);
}
void set_debug_tap(loopback_process_t loopback, debug_tap_t inputTap, debug_tap_t outputTap)
{
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
// should we turn ints off for this?
This->mInputTap = inputTap;
This->mOutputTap = outputTap;
}
void set_tap_point(loopback_process_t loopback, uint32_t index)
{
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
// should we turn ints off for this?
This->mIndex = index;
}
void process_data(loopback_process_t loopback, void *processBuffer)
{
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
uint32_t sizeOfInput = This->mNumFrames * This->mNumChannels * This->mSampleSize;
uint32_t amountToSend = 0;
// DebugTap1
if (This->mInputTap && (This->mIndex == 1))
{
size_t this_amountToSend = send_to_tap(This->mInputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
// DebugInput1
if (This->mOutputTap && (This->mIndex == 1))
{
size_t this_amountToSend = get_from_tap(This->mOutputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
AudioUnit_Process(This->mAudioUnit, processBuffer, processBuffer, This->mNumFrames);
// DebugTap3
if (This->mInputTap && (This->mIndex == 3))
{
size_t this_amountToSend = send_to_tap(This->mInputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
// DebugInput1
if (This->mOutputTap && (This->mIndex == 3))
{
size_t this_amountToSend = get_from_tap(This->mOutputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
if (amountToSend)
{
// Should this be done at the beginning?
uint64_t timestamp = timer_get_ticks();
send_timestamp(This->mNumFrames, timestamp);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef _SPEAKER_PROT_H_
#define _SPEAKER_PROT_H_
#include "debug_tap.h"
#include "iop_au_interface.h"
#include <platform.h>
typedef void * loopback_process_t;
/*
* loopback process is an object that will process an input buffer in-
* place, applying the passed Audio Unit to it.
*
* debug_taps can be added at specific tap points.
*
* It is assumed that the input number of samples to process matches
* with the call to process.
*
*/
// channel bitmask is to allow cases when audio is not packed.
loopback_process_t create_loopback_process(audio_unit_t au, uint32_t frames_to_process, uint32_t channels_to_process, uint32_t sample_size, uint32_t channel_bitmask);
void destroy_loopback_process(loopback_process_t loopback);
void set_debug_tap(loopback_process_t loopback, debug_tap_t inputTap, debug_tap_t outputTap);
void set_tap_point(loopback_process_t loopback, uint32_t index);
// Assumes that input is as described when loopback created
void process_data(loopback_process_t loopback, void *processBuffer);
#endif // _SPEAKER_PROT_H_

View File

@ -0,0 +1,48 @@
# Copyright (C) 2010-2011 Apple Inc. All rights reserved.
#
# This document is the property of Apple Computer, 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 Computer, Inc.
#
#
# Audio driver
#
LOCAL_DIR := $(GET_LOCAL_DIR)
OPTIONS += WITH_FUNCTION_AUDIODSP=1 \
USE_SIDETONE=1
IOP_FUNCTIONS += AUDIODSP
ALL_OBJS += $(LOCAL_DIR)/iop_audiodsp.o \
$(LOCAL_DIR)/loopback_process.o \
$(LOCAL_DIR)/loopback_device.o \
$(LOCAL_DIR)/timestamper.o \
$(LOCAL_DIR)/debug_tap.o \
$(LOCAL_DIR)/AudioUnits/iop_au_interface.o \
$(LOCAL_DIR)/AudioUnits/AUSidetone.o \
$(LOCAL_DIR)/AudioUnits/AUNull.o \
INSTALL_HEADERS += $(LOCAL_DIR)/iop_audiodsp_protocol.h
# Assuming 8 k for each task, with 2 task.
# Assuming 16 k heap for au process
# 32 k heap total
export IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),32768)
GLOBAL_INCLUDES += $(SDKROOT)/usr/local/standalone/firmware \
$(SDKROOT)/usr/local/standalone/firmware/include \
$(LOCAL_DIR)/AudioUnits \
$(LOCAL_DIR)/AE2_I2S \
$(LOCAL_DIR)/AE2_MCA \
$(LOCAL_DIR)/AE2_DMA \
$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework/Headers \
$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework/PrivateHeaders
PREBUILT_STATICLIBS += $(SDKROOT)/usr/local/standalone/firmware/libm.a \
$(SDKROOT)/usr/local/standalone/firmware/libvDSP.a

View File

@ -0,0 +1,174 @@
/*
* Copyright (C) 2010-2011 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 "timestamper.h"
#include <debug.h>
#include <AssertMacros.h>
#include <platform.h>
#include <platform/timer.h>
#include <sys/task.h>
#include <iop.h>
#include <qwi.h>
#include <qwi_protocol.h>
#include <EmbeddedIOPProtocol.h>
#include <arch.h>
#include <arch/arm/arm.h>
#include <drivers/audio/audio.h>
#define kLogTimestamps 0
static IOPAUDIODSP_Command *timestamper_message_buffer;
void set_timestamper_message_buffer(IOPAUDIODSP_Command *buffer)
{
timestamper_message_buffer = buffer;
}
static int timestamper_message_channel;
static struct task_event timestamper_message_event;
static struct task_event timestamper_runtask_event;
static int timestamper_task(void *cfg);
static void timestamper_sleep(int mode);
IOP_FUNCTION(timestamper, timestamper_task, 8*1024, AUDIODSP_TIMER_CHANNEL);
IOP_SLEEP_HOOK(timestamper, timestamper_sleep);
/*
* Get a message to send to the Host
*/
static IOPAUDIODSP_Command *
get_timestamper_message(utime_t allowed_delay)
{
utime_t deadline, t;
int idx;
/* nothing we can do if we don't have a buffer */
if (NULL == timestamper_message_buffer)
return(NULL);
/* try to get a slot right away */
if (-1 != (idx = qwi_next_send_index(timestamper_message_channel)))
return(timestamper_message_buffer + idx);
if (IOP_MESSAGE_NO_WAIT == allowed_delay)
return(NULL);
/* spin waiting for a free slot */
deadline = system_time() + allowed_delay;
for (;;) {
/* get the current time and see if we've timed out */
t = system_time();
if (t >= deadline)
return(NULL);
/* sleep waiting for notification or timeout */
event_wait_timeout(&timestamper_message_event, deadline - t);
/* try again to get a message slot */
if (-1 != (idx = qwi_next_send_index(timestamper_message_channel)))
return(timestamper_message_buffer + idx);
}
}
/*
* Reclaim a message the host has accepted.
*/
static void
timestamper_message_wakeup(void *arg __unused)
{
bool replies;
uint32_t message;
/* reap message replies */
replies = false;
while (qwi_receive_item(timestamper_message_channel, &message) != -1)
replies = true;
/* if we got at least one reply, wake anyone trying to send */
if (replies)
{
event_signal(&timestamper_message_event);
}
}
/*
* Try to send a trace message to the host.
*/
void
send_timestamp(uint32_t samplesTransferred, uint64_t timestamp)
{
IOPAUDIODSP_Command *msg;
/* now get a message - wait a little while for it but not forever as we might deadlock the host */
if (NULL != (msg = get_timestamper_message(1000))) {
/* populate the message */
msg->timestamp.mIOPHeader.mOpcode = kIOPAUDIODSP_OPCODE_TIMESTAMP;
msg->timestamp.mIOPHeader.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
msg->timestamp.mSampleCount = samplesTransferred;
msg->timestamp.mTimeStamp = timestamp;
#if kLogTimestamps
dprintf(DEBUG_CRITICAL, "Timestamp %ull\n", timestamp);
#endif
/* push it to memory */
platform_cache_operation(CACHE_CLEAN, msg, sizeof(*msg));
/* and give it to the host */
qwi_send_item(timestamper_message_channel, QWI_ENCODE_ORDINAL(msg - timestamper_message_buffer));
}
else
{
dprintf(DEBUG_CRITICAL, "couldn't get a timestamp message\n");
}
}
static int
timestamper_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
dprintf(DEBUG_SPEW, "@@ Timestamper task starting\n");
/* establish the host communications channel */
event_init(&timestamper_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
event_init(&timestamper_runtask_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "** opening audiodsp channel\n");
timestamper_message_channel = qwi_instantiate_channel("audio timestamp",
QWI_ROLE_PRODUCER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
timestamper_message_wakeup,
NULL);
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
/* Little doubt we'll need VFP/Neon */
arch_task_fp_enable(true);
#endif
for (;;) {
dprintf(DEBUG_SPEW, "Waiting on run event\n");
event_wait(&timestamper_runtask_event);
}
return(0);
}
static void
timestamper_sleep(int mode)
{
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef _TIMESTAMPER_H_
#define _TIMESTAMPER_H_
#include <platform.h>
#include "iop_audiodsp_protocol.h"
void set_timestamper_message_buffer(IOPAUDIODSP_Command *buffer);
void send_timestamp(uint32_t samplesTransferred, uint64_t timestamp);
#endif // _TIMESTAMPER_H_

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2008 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.
*/
typedef UInt32 CEATA_cmd_t;
/* XXX these may be just placeholders */
#define CEATA_IDENTIFY 0xec
#define CEATA_READ_DMA_EXT 0x25
#define CEATA_WRITE_DMA_EXT 0x35
#define CEATA_STANDBY_IMMEDIATE 0xe0
#define CEATA_FLUSH_CACHE_EXT 0xea

View File

@ -0,0 +1,193 @@
/*
* Copyright (C) 2008 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 <AssertMacros.h>
#include <platform/memmap.h>
#include <platform.h>
#include <sys/task.h>
#include <qwi.h>
#include <EmbeddedIOPProtocol.h>
#include "iop_ceata.h"
#include "iop_ceata_protocol.h"
#include "ceata.h"
static struct task_event ceata_message_event;
static int ceata_channel;
static bool ceata_message_process(void);
static IOPCEATA_status_t ceata_reset(void);
static IOPCEATA_status_t ceata_identify(IOPCEATA_Identify *identify);
static IOPCEATA_status_t ceata_cmd(CEATA_cmd_t cmd);
static IOPCEATA_status_t ceata_io(CEATA_cmd_t cmd,
UInt32 starting_lba,
UInt32 lba_count,
UInt32 segment_count,
IOPCEATA_dma_segment *segments);
int
iop_ceata_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
dprintf(DEBUG_SPEW, "@@ CEATA task starting\n");
check(kIOPCEATA_COMMAND_SIZE == sizeof(IOPCEATA_Command));
/* establish the host communications channel */
event_init(&ceata_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "** opening ceata channel\n");
ceata_channel = qwi_instantiate_channel(
"ceata command",
QWI_ROLE_CONSUMER,
channel->ring_size,
mem_static_map_cached(channel->ring_base),
(qwi_channel_hook)event_signal,
(void *)&ceata_message_event);
for (;;) {
dprintf(DEBUG_SPEW, "@@ waiting for message on ceata channel\n");
while (ceata_message_process()) {
// eat all available messages
}
event_wait(&ceata_message_event);
}
return(0);
}
static bool
ceata_message_process(void)
{
uint32_t message;
IOPCEATA_Command* command;
dprintf(DEBUG_SPEW, "@@ handling host message\n");
/* look to see if there's an item waiting for us */
if (qwi_receive_item(ceata_channel, &message) == -1)
return(false);
dprintf(DEBUG_SPEW, "@@ received ceata message\n");
/* find the command structure based on the message */
command = (IOPCEATA_Command*)mem_static_map_cached(message);
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the command size is a multiple of our cacheline size.
*/
platform_cache_operation(CACHE_INVALIDATE,
(void *)command,
sizeof(*command));
/*
* TODO: make this part of the API and push this
* architecture-specific command handling down into the s5l8920x
* platform directory.
*/
switch (command->iopceata.opcode) {
case kIOPCEATA_OPCODE_RESET:
dprintf(DEBUG_SPEW, "@@ RESET\n");
command->iopceata.status = ceata_reset();
break;
case kIOPCEATA_OPCODE_IDENTIFY:
dprintf(DEBUG_SPEW, "@@ IDENTIFY\n");
command->iopceata.status = ceata_identify((IOPCEATA_Identify *)command);
break;
case kIOPCEATA_OPCODE_READ:
dprintf(DEBUG_SPEW, "@@ READ\n");
command->iopceata.status = ceata_io(
CEATA_READ_DMA_EXT,
command->read_write.starting_lba,
command->read_write.lba_count,
command->read_write.segment_count,
&command->read_write.data_segments[0]);
break;
case kIOPCEATA_OPCODE_WRITE:
dprintf(DEBUG_SPEW, "@@ WRITE\n");
command->iopceata.status = ceata_io(
CEATA_WRITE_DMA_EXT,
command->read_write.starting_lba,
command->read_write.lba_count,
command->read_write.segment_count,
&command->read_write.data_segments[0]);
break;
case kIOPCEATA_OPCODE_STANDBY:
dprintf(DEBUG_SPEW, "@@ STANDBY\n");
command->iopceata.status = ceata_cmd(CEATA_STANDBY_IMMEDIATE);
break;
case kIOPCEATA_OPCODE_FLUSH:
dprintf(DEBUG_SPEW, "@@ FLUSH\n");
command->iopceata.status = ceata_cmd(CEATA_FLUSH_CACHE_EXT);
break;
default:
dprintf(DEBUG_CRITICAL, "@@ ERROR: unrecognised ceata opcode 0x%x",
command->iopceata.opcode);
command->iopceata.status = kIOPCEATA_STATUS_PARAM_INVALID;
break;
}
dprintf(DEBUG_SPEW, "@@ done processing ceata message with status 0x%08x\n", command->iopceata.status);
platform_cache_operation(CACHE_CLEAN,
(void *)command,
sizeof(IOPCEATA_Command));
qwi_send_item(ceata_channel, message);
dprintf(DEBUG_SPEW, "@@ signaled completion of ceata message to host\n");
return(true);
}
static IOPCEATA_status_t
ceata_reset(void)
{
return(kIOPCEATA_STATUS_UNIMPLEMENTED);
}
static IOPCEATA_status_t
ceata_identify(IOPCEATA_Identify *identify)
{
return(kIOPCEATA_STATUS_UNIMPLEMENTED);
}
static IOPCEATA_status_t
ceata_cmd(CEATA_cmd_t cmd)
{
return(kIOPCEATA_STATUS_UNIMPLEMENTED);
}
static IOPCEATA_status_t
ceata_io(CEATA_cmd_t cmd,
UInt32 starting_lba,
UInt32 lba_count,
UInt32 segment_count,
IOPCEATA_dma_segment *segments)
{
return(kIOPCEATA_STATUS_UNIMPLEMENTED);
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (C) 2008 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.
*/
extern int iop_ceata_task(void *arg);

View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2008 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.
*/
#ifndef _IOP_CEATA_PROTOCOL_H_
#define _IOP_CEATA_PROTOCOL_H_
#include <sys/types.h>
/*
* Command size is (somewhat) tunable.
*
* The principal consideration here is the maximum scatter/gather list size
* this permits.
*/
#define kIOPCEATA_COMMAND_SIZE (512)
typedef UInt32 IOPCEATA_opcode_t;
#define kIOPCEATA_OPCODE_UNKNOWN ((IOPCEATA_opcode_t) 0)
#define kIOPCEATA_OPCODE_RESET ((IOPCEATA_opcode_t) 1)
#define kIOPCEATA_OPCODE_IDENTIFY ((IOPCEATA_opcode_t) 2)
#define kIOPCEATA_OPCODE_READ ((IOPCEATA_opcode_t) 3)
#define kIOPCEATA_OPCODE_WRITE ((IOPCEATA_opcode_t) 4)
#define kIOPCEATA_OPCODE_STANDBY ((IOPCEATA_opcode_t) 5)
#define kIOPCEATA_OPCODE_FLUSH ((IOPCEATA_opcode_t) 6)
typedef UInt32 IOPCEATA_status_t;
#define kIOPCEATA_STATUS_UNKNOWN ((IOPCEATA_status_t) 0)
#define kIOPCEATA_STATUS_SUCCESS ((IOPCEATA_status_t) 1)
#define kIOPCEATA_STATUS_FAILURE ((IOPCEATA_status_t) 0x80000000)
#define kIOPCEATA_STATUS_DEVICE_ERROR ((IOPCEATA_status_t) 0x80000001)
#define kIOPCEATA_STATUS_DEVICE_TIMEOUT ((IOPCEATA_status_t) 0x80000002)
#define kIOPCEATA_STATUS_DMA_TIMEOUT ((IOPCEATA_status_t) 0x80000003)
#define kIOPCEATA_STATUS_PARAM_INVALID ((IOPCEATA_status_t) 0x80000004)
#define kIOPCEATA_STATUS_UNIMPLEMENTED ((IOPCEATA_status_t) 0x80000005)
/* this must match <drivers/dma.h>::struct dma_segment */
struct _IOPCEATA_dma_segment {
u_int32_t paddr;
u_int32_t length;
};
typedef struct _IOPCEATA_dma_segment IOPCEATA_dma_segment;
struct _IOPCEATA
{
IOPCEATA_opcode_t opcode;
IOPCEATA_status_t status;
};
typedef struct _IOPCEATA IOPCEATA;
struct _IOPCEATA_Identify
{
IOPCEATA iopceata;
// relevant properties from the CE-ATA Identify operation
UInt8 serial_number[20]; // ASCII bytes, not NUL-terminated
UInt8 firmware_revision[8];
UInt8 model_number[40];
UInt32 device_size; // note this is size, not max LBA
UInt32 lba_size; // LBA size in bytes
};
typedef struct _IOPCEATA_Identify IOPCEATA_Identify;
struct _IOPCEATA_ReadWrite
{
IOPCEATA iopceata;
UInt32 starting_lba;
UInt32 lba_count;
UInt32 segment_count;
IOPCEATA_dma_segment data_segments[];
};
typedef struct _IOPCEATA_ReadWrite IOPCEATA_ReadWrite;
union _IOPCEATA_Command
{
IOPCEATA iopceata;
IOPCEATA_Identify identify;
IOPCEATA_ReadWrite read_write;
UInt8 _pad[kIOPCEATA_COMMAND_SIZE];
};
typedef union _IOPCEATA_Command IOPCEATA_Command;
union _IOPCEATA_sgl_sizing_tool
{
IOPCEATA_ReadWrite io_single_dma;
};
#define kIOPCEATA_MAX_SEGMENTS ((kIOPCEATA_COMMAND_SIZE - sizeof(union _IOPCEATA_sgl_sizing_tool)) / sizeof(IOPCEATA_dma_segment))
#endif // _IOP_CEATA_PROTOCOL_H_

View File

@ -0,0 +1,26 @@
# Copyright (C) 2008 Apple Computer, Inc. All rights reserved.
#
# This document is the property of Apple Computer, 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 Computer, Inc.
#
#
# CE-ATA driver
#
LOCAL_DIR := $(GET_LOCAL_DIR)
OPTIONS += WITH_FUNCTION_CEATA=1
IOP_FUNCTIONS += CEATA
ALL_OBJS += $(LOCAL_DIR)/iop_ceata.o
export FMI_PROTOCOL_HEADER:= $(LOCAL_DIR)/iop_ceata_protocol.h
export WITH_FUNCTION_CEATA= 1
export IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),8192)

View File

@ -0,0 +1,509 @@
/*
* Copyright (C) 2008 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 <AssertMacros.h>
#include <platform/soc/hwclocks.h>
#include <platform/soc/hwregbase.h>
#include <platform/soc/pmgr.h>
#include <platform/memmap.h>
#include <platform.h>
#include <sys/task.h>
#include <iop.h>
#include <qwi.h>
#include <EmbeddedIOPProtocol.h>
#include "iop_fmi_protocol.h"
#include <H2fmi_iop.h>
static bool fmi_message_process(int channel, h2fmi_t *fmi);
static int iop_fmi_task(void *cfg);
static void iop_fmi_sleep(int mode);
IOP_FUNCTION(fmi0, iop_fmi_task, 1536, FMI_CONTROL_CHANNEL0);
IOP_FUNCTION(fmi1, iop_fmi_task, 1536, FMI_CONTROL_CHANNEL1);
IOP_SLEEP_HOOK(fmi, iop_fmi_sleep);
static h2fmi_t* g_fmi_table[kIOPFMI_MAX_NUM_OF_BUSES];
static IOPFMI_Command* g_pCurrentCommand[kIOPFMI_MAX_NUM_OF_BUSES];
static uint32_t g_fmi_count = 0;
static int
iop_fmi_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
h2fmi_t* iop_fmi = (h2fmi_t*) malloc(sizeof(h2fmi_t));
struct task_event* fmi_message_event = (struct task_event*) malloc(sizeof(struct task_event));
int fmi_channel;
check(kIOPFMI_COMMAND_SIZE == sizeof(IOPFMI_Command));
/**
* Ensure everything is zero in case we get panicked right away
* by host processor (so our panic handlers don't dereference
* invalid pointers)
*/
memset(iop_fmi,0,sizeof(*iop_fmi));
iop_fmi->bus_id = (UInt32)-1;
dprintf(DEBUG_SPEW, "**(%p) FMI task starting\n", iop_fmi);
/* register the allocated FMI in the table */
g_fmi_table[g_fmi_count++] = iop_fmi;
/* establish the host communications channel */
event_init(fmi_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "**(%p) opening fmi channel\n", iop_fmi);
fmi_channel = qwi_instantiate_channel(
"fmi command",
QWI_ROLE_CONSUMER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
(qwi_channel_hook)event_signal,
fmi_message_event);
for (;;) {
dprintf(DEBUG_SPEW, "**(%p) waiting for message on fmi channel\n", iop_fmi);
while (fmi_message_process(fmi_channel, iop_fmi))
{
// eat all available messages
}
event_wait(fmi_message_event);
}
return(0);
}
static bool
fmi_message_process(int channel, h2fmi_t *fmi)
{
uint32_t message;
IOPFMI_Command** ppCommand;
IOPFMI_Command* pCommand;
const bool is_ppn = fmi->is_ppn;
dprintf(DEBUG_SPEW, "**(%p) handling host message\n", fmi);
/* look to see if there's an item waiting for us */
if (qwi_receive_item(channel, &message) == -1)
return(false);
dprintf(DEBUG_SPEW, "**(%p) received fmi message\n", fmi);
/* find the command structure based on the message */
pCommand = mem_static_map_cached(message);
if ( ((UInt32)-1)==fmi->bus_id )
{
ppCommand = NULL;
}
else
{
ppCommand = &g_pCurrentCommand[fmi->bus_id];
*ppCommand = pCommand;
}
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the command size is a multiple of our cacheline size.
*/
WMR_PREPARE_READ_BUFFER((void *)pCommand, (sizeof(*pCommand)));
if (0 != (pCommand->iopfmi.state & kIOPFMI_STATE_WAKING_UP))
{
if (pCommand->iopfmi.opcode != kIOPFMI_OPCODE_SET_CONFIG)
{
h2fmi_restoreFmiRegs(fmi);
}
}
if (0 != (pCommand->iopfmi.state & kIOPFMI_STATE_POWER_CHANGED))
{
#if SUPPORT_PPN
if (is_ppn)
{
h2fmi_ppn_iop_power_state_changed(fmi);
}
#endif
}
/*
* TODO: make this part of the API and push this
* architecture-specific command handling down into the s5l8920x
* platform directory.
*/
switch (pCommand->iopfmi.opcode) {
case kIOPFMI_OPCODE_SET_CONFIG:
dprintf(DEBUG_SPEW, "**(%p) SET_CONFIG\n", fmi);
h2fmi_iop_set_config(fmi, &pCommand->set_config);
break;
case kIOPFMI_OPCODE_POST_RESET_OPER:
dprintf(DEBUG_SPEW, "**(%p) POST RESET_OPERATIONS\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_post_rst_pre_pwrstate_operations(fmi, &pCommand->post_reset_oper);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
break;
case kIOPFMI_OPCODE_SET_FEATURES:
dprintf(DEBUG_SPEW, "**(%p) POST RESET_OPERATIONS\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_set_feature_list(fmi, &pCommand->set_features);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
break;
case kIOPFMI_OPCODE_RESET_EVERYTHING:
dprintf(DEBUG_SPEW, "**(%p) RESET_EVERYTHING\n", fmi);
if (is_ppn)
{
#if SUPPORT_PPN
h2fmi_ppn_iop_reset_everything(fmi, &pCommand->reset_everything);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
}
else
{
h2fmi_iop_reset_everything(fmi, &pCommand->reset_everything);
}
break;
case kIOPFMI_OPCODE_ERASE_SINGLE:
dprintf(DEBUG_SPEW, "**(%p) ERASE_SINGLE\n", fmi);
if (is_ppn)
{
#if SUPPORT_PPN
h2fmi_ppn_iop_erase_single(fmi, &pCommand->erase_single);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
}
else
{
h2fmi_iop_erase_single(fmi, &pCommand->erase_single);
}
break;
case kIOPFMI_OPCODE_ERASE_MULTIPLE:
dprintf(DEBUG_SPEW, "**(%p) ERASE_MULTIPLE\n", fmi);
if (is_ppn)
{
WMR_PANIC("Erase Multiple called on PPN device with Legacy FIL");
}
else
{
h2fmi_iop_erase_multiple(fmi, &pCommand->erase_multiple);
}
break;
case kIOPFMI_OPCODE_READ_SINGLE:
dprintf(DEBUG_SPEW, "**(%p) READ_SINGLE\n", fmi);
if (is_ppn)
{
#if SUPPORT_PPN
h2fmi_ppn_iop_read_single(fmi, &pCommand->io_single);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
}
else
{
h2fmi_iop_read_single(fmi, &pCommand->io_single);
}
break;
case kIOPFMI_OPCODE_READ_MULTIPLE:
dprintf(DEBUG_SPEW, "**(%p) READ_MULTIPLE\n", fmi);
if (is_ppn)
{
WMR_PANIC("Legacy FIL used for PPN read!");
}
else
{
h2fmi_iop_read_multiple(fmi, &pCommand->io_multiple);
}
break;
case kIOPFMI_OPCODE_READ_RAW:
dprintf(DEBUG_SPEW, "**(%p) READ_RAW\n", fmi);
if (is_ppn)
{
#if SUPPORT_PPN
h2fmi_ppn_iop_read_raw(fmi, &pCommand->io_raw);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
}
else
{
h2fmi_iop_read_raw(fmi, &pCommand->io_raw);
}
break;
case kIOPFMI_OPCODE_READ_BOOTPAGE:
dprintf(DEBUG_SPEW, "**(%p) READ_BOOTLOADER\n", fmi);
if (is_ppn)
{
#if SUPPORT_PPN
h2fmi_ppn_iop_read_bootpage(fmi, &pCommand->io_bootpage);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
}
else
{
h2fmi_iop_read_bootpage(fmi, &pCommand->io_bootpage);
}
break;
case kIOPFMI_OPCODE_WRITE_SINGLE:
dprintf(DEBUG_SPEW, "**(%p) WRITE_SINGLE\n", fmi);
if (is_ppn)
{
#if SUPPORT_PPN
h2fmi_ppn_iop_write_single(fmi, &pCommand->io_single);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
}
else
{
h2fmi_iop_write_single(fmi, &pCommand->io_single);
}
break;
case kIOPFMI_OPCODE_WRITE_MULTIPLE:
dprintf(DEBUG_SPEW, "**(%p) WRITE_MULTIPLE\n", fmi);
if (is_ppn)
{
WMR_PANIC("WRITE_MULTIPLE on PPN device using legacy FIL");
}
else
{
h2fmi_iop_write_multiple(fmi, &pCommand->io_multiple);
}
break;
case kIOPFMI_OPCODE_WRITE_RAW:
dprintf(DEBUG_SPEW, "**(%p) WRITE_RAW\n", fmi);
if (is_ppn)
{
WMR_PANIC("WRITE_RAW on PPN device");
}
else
{
h2fmi_iop_write_raw(fmi, &pCommand->io_raw);
}
break;
case kIOPFMI_OPCODE_WRITE_BOOTPAGE:
dprintf(DEBUG_SPEW, "**(%p) WRITE_BOOTLOADER\n", fmi);
if (is_ppn)
{
#if SUPPORT_PPN
h2fmi_ppn_iop_write_bootpage(fmi, &pCommand->io_bootpage);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
}
else
{
h2fmi_iop_write_bootpage(fmi, &pCommand->io_bootpage);
}
break;
case kIOPFMI_OPCODE_READ_CAU_BBT:
dprintf(DEBUG_SPEW, "**(%p) READ_CAU_BBT\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_read_cau_bbt(fmi, &pCommand->io_ppn);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
break;
case kIOPFMI_OPCODE_UPDATE_FIRMWARE:
dprintf(DEBUG_SPEW, "**(%p) UPDATE_FIRMWARE\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_update_firmware(fmi, &pCommand->update_firmware);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
case kIOPFMI_OPCODE_PPN_READ:
dprintf(DEBUG_SPEW, "**(%p) PPN_READ\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_read_multiple(fmi, &pCommand->io_ppn);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
break;
case kIOPFMI_OPCODE_PPN_WRITE:
dprintf(DEBUG_SPEW, "**(%p) PPN_WRITE\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_write_multiple(fmi, &pCommand->io_ppn);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
break;
case kIOPFMI_OPCODE_PPN_ERASE:
dprintf(DEBUG_SPEW, "**(%p) PPN_ERASE\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_erase_multiple(fmi, &pCommand->io_ppn);
#else
WMR_PANIC("SUPPORT_PPN");
#endif
break;
case kIOPFMI_OPCODE_PPN_SET_POWER:
dprintf(DEBUG_SPEW, "**(%p) PPN_SET_POWER\n", fmi);
WMR_ASSERT(is_ppn);
#if SUPPORT_PPN
h2fmi_ppn_iop_set_power(fmi, &pCommand->set_power);
#endif
break;
case kIOPFMI_OPCODE_READ_CHIP_IDS:
dprintf(DEBUG_SPEW, "**(%p) READ_CHIP_IDS\n", fmi);
h2fmi_iop_read_chip_ids(fmi, &pCommand->read_chip_ids);
break;
#if SUPPORT_PPN
case kIOPFMI_OPCODE_GET_FAILURE_INFO:
dprintf(DEBUG_SPEW, "**(%p) GET_FAILURE_INFO\n", fmi);
h2fmi_ppn_iop_get_failure_info(fmi, &pCommand->get_failure_info);
break;
case kIOPFMI_OPCODE_GET_CONTROLLER_INFO:
dprintf(DEBUG_SPEW, "**(%p) GET_CONTROLLER_INFO\n", fmi);
WMR_ASSERT(is_ppn);
h2fmi_ppn_iop_get_controller_info(fmi, &pCommand->get_controller_info);
break;
case kIOPFMI_OPCODE_GET_TEMPERATURE:
dprintf(DEBUG_SPEW, "**(%p) GET_TEMPERATURE\n", fmi);
WMR_ASSERT(is_ppn);
h2fmi_ppn_iop_get_temperature(fmi, &pCommand->get_temperature);
break;
case kIOPFMI_OPCODE_GET_DIE_INFO:
dprintf(DEBUG_SPEW, "**(%p) GET_DIE_INFO\n", fmi);
WMR_ASSERT(is_ppn);
h2fmi_ppn_iop_get_die_info(fmi, &pCommand->get_die_info);
break;
#endif
default:
dprintf(DEBUG_CRITICAL, "**(%p) ERROR: unrecognised fmi opcode 0x%x", fmi,
pCommand->iopfmi.opcode);
pCommand->iopfmi.status = kIOPFMI_STATUS_PARAM_INVALID;
break;
}
dprintf(DEBUG_SPEW, "**(%p) done processing fmi message with status 0x%08x\n", fmi, pCommand->iopfmi.status);
WMR_PREPARE_WRITE_BUFFER((void *)pCommand, sizeof(*pCommand));
qwi_send_item(channel, message);
dprintf(DEBUG_SPEW, "**(%p) signaled completion of fmi message to host\n", fmi);
if ( NULL!=ppCommand )
{
*ppCommand = NULL;
}
return(true);
}
static void iop_fmi_sleep(int mode)
{
uint32_t idx;
for (idx = 0; idx < g_fmi_count; idx++)
{
if (IOP_SLEEP_MODE_SLEEPING == mode)
{
h2fmi_iop_sleep(g_fmi_table[idx]);
}
else if (IOP_SLEEP_MODE_WAKING == mode)
{
h2fmi_iop_wake(g_fmi_table[idx]);
}
else
{
dprintf(DEBUG_CRITICAL, "ERROR: unrecognized sleep mode value\n");
}
}
}
static void
do_fmi_panic(void *arg __unused)
{
uint32_t i;
printf("g_fmi_count: %d, currentTick: 0x%llx\n",g_fmi_count,WMR_CLOCK_TICKS());
for ( i=0; i<g_fmi_count; i++ )
{
IOPFMI_Command* pCommand = ( ((UInt32)-1)==g_fmi_table[i]->bus_id ? NULL : g_pCurrentCommand[g_fmi_table[i]->bus_id] );
if (NULL == pCommand)
{
printf("Not executing command\n");
// Skip HW Regs when idle in case we're gated
dump_fmi_state(g_fmi_table[i], i, FALSE32, FALSE32);
}
else
{
BOOL32 withECC;
switch (pCommand->iopfmi.opcode)
{
case kIOPFMI_OPCODE_READ_BOOTPAGE:
// fall-through
case kIOPFMI_OPCODE_WRITE_BOOTPAGE:
withECC = TRUE32;
break;
default:
withECC = (g_fmi_table[i]->is_ppn ? FALSE32 : TRUE32);
break;
}
printf("Is executing command @ %p opCode: %d\n", pCommand, pCommand->iopfmi.opcode);
dump_fmi_state(g_fmi_table[i], i, TRUE32, withECC);
}
}
}
PANIC_HOOK(fmi, do_fmi_panic, NULL);

View File

@ -0,0 +1,553 @@
/*
* Copyright (C) 2008 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.
*/
#ifndef _IOP_FMI_PROTOCOL_H_
#define _IOP_FMI_PROTOCOL_H_
#include <sys/types.h>
#define kIOPFMI_MAX_NUM_OF_BUSES (2)
#define kIOPFMI_MAX_NUM_OF_BANKS_PER_BUS (8)
#define kIOPFMI_MAX_NUM_OF_CES_PER_BUS (8)
#define kIOPFMI_MAX_NUM_OF_ENABLES (kIOPFMI_MAX_NUM_OF_CES_PER_BUS * kIOPFMI_MAX_NUM_OF_BUSES)
#define kIOPFMI_MAX_NUM_OF_BANKS (kIOPFMI_MAX_NUM_OF_ENABLES * kIOPFMI_MAX_NUM_OF_BUSES)
#define kIOPFMI_MAX_NUM_OF_CHANNELS (kIOPFMI_MAX_NUM_OF_BUSES)
#define kIOPFMI_BYTES_PER_META (10)
#define kIOPFMI_BYTES_PER_CHIPID (5)
// define the maximum number of pages that the controller is willing
// to support per I/O command; Mike says that it is generally safe to
// assume a practical upper bounds of 1 MB bursts; so, for worst case
// of 2KiB pages, this translates into 512 pages per multi-I/O command
#define kIOPFMI_MAX_IO_PAGES 512
/*
* Command size is (somewhat) tunable.
*
* The principal consideration here is the maximum scatter/gather list size
* this permits.
*/
#define kIOPFMI_COMMAND_SIZE (512)
typedef UInt32 IOPFMI_opcode_t;
#define kIOPFMI_OPCODE_UNKNOWN ((IOPFMI_opcode_t) 0)
#define kIOPFMI_OPCODE_SET_CONFIG ((IOPFMI_opcode_t) 1)
#define kIOPFMI_OPCODE_RESET_EVERYTHING ((IOPFMI_opcode_t) 2)
#define kIOPFMI_OPCODE_ERASE_SINGLE ((IOPFMI_opcode_t) 3)
#define kIOPFMI_OPCODE_READ_SINGLE ((IOPFMI_opcode_t) 4)
#define kIOPFMI_OPCODE_READ_RAW ((IOPFMI_opcode_t) 5)
#define kIOPFMI_OPCODE_WRITE_SINGLE ((IOPFMI_opcode_t) 6)
#define kIOPFMI_OPCODE_WRITE_RAW ((IOPFMI_opcode_t) 7)
#define kIOPFMI_OPCODE_READ_MULTIPLE ((IOPFMI_opcode_t) 8)
#define kIOPFMI_OPCODE_WRITE_MULTIPLE ((IOPFMI_opcode_t) 9)
#define kIOPFMI_OPCODE_WRITE_BOOTPAGE ((IOPFMI_opcode_t) 10)
#define kIOPFMI_OPCODE_READ_BOOTPAGE ((IOPFMI_opcode_t) 11)
#define kIOPFMI_OPCODE_ERASE_MULTIPLE ((IOPFMI_opcode_t) 12)
#define kIOPFMI_OPCODE_READ_CAU_BBT ((IOPFMI_opcode_t) 13)
#define kIOPFMI_OPCODE_ERASE_SCATTERED ((IOPFMI_opcode_t) 14)
#define kIOPFMI_OPCODE_UPDATE_FIRMWARE ((IOPFMI_opcode_t) 15)
#define kIOPFMI_OPCODE_PPN_READ ((IOPFMI_opcode_t) 16)
#define kIOPFMI_OPCODE_PPN_ERASE ((IOPFMI_opcode_t) 17)
#define kIOPFMI_OPCODE_PPN_WRITE ((IOPFMI_opcode_t) 18)
#define kIOPFMI_OPCODE_PPN_SET_POWER ((IOPFMI_opcode_t) 19)
#define kIOPFMI_OPCODE_READ_CHIP_IDS ((IOPFMI_opcode_t) 20)
#define kIOPFMI_OPCODE_GET_FAILURE_INFO ((IOPFMI_opcode_t) 21)
#define kIOPFMI_OPCODE_GET_CONTROLLER_INFO ((IOPFMI_opcode_t) 22)
#define kIOPFMI_OPCODE_GET_DIE_INFO ((IOPFMI_opcode_t) 23)
#define kIOPFMI_OPCODE_POST_RESET_OPER ((IOPFMI_opcode_t) 24)
#define kIOPFMI_OPCODE_GET_TEMPERATURE ((IOPFMI_opcode_t) 25)
#define kIOPFMI_OPCODE_SET_FEATURES ((IOPFMI_opcode_t) 26)
typedef UInt32 IOPFMI_flags_t;
#define kIOPFMI_FLAGS_NONE ((IOPFMI_flags_t) 0)
#define kIOPFMI_FLAGS_BLANK_CHECK ((IOPFMI_flags_t) (1<<0))
#define kIOPFMI_FLAGS_WITH_DMA_SGL ((IOPFMI_flags_t) (1<<1))
#define kIOPFMI_FLAGS_USE_AES ((IOPFMI_flags_t) (1<<2))
#define kIOPFMI_FLAGS_HOMOGENIZE ((IOPFMI_flags_t) (1<<3))
typedef UInt32 IOPFMI_status_t;
#define kIOPFMI_STATUS_UNKNOWN ((IOPFMI_status_t) 0)
#define kIOPFMI_STATUS_SUCCESS ((IOPFMI_status_t) 1)
#define kIOPFMI_STATUS_BLANK ((IOPFMI_status_t) 2)
#define kIOPFMI_STATUS_FAILURE ((IOPFMI_status_t) 0x80000000)
#define kIOPFMI_STATUS_DEVICE_ERROR ((IOPFMI_status_t) 0x80000001)
#define kIOPFMI_STATUS_DEVICE_TIMEOUT ((IOPFMI_status_t) 0x80000002)
#define kIOPFMI_STATUS_DMA_TIMEOUT ((IOPFMI_status_t) 0x80000003)
#define kIOPFMI_STATUS_PARAM_INVALID ((IOPFMI_status_t) 0x80000004)
#define kIOPFMI_STATUS_UNIMPLEMENTED ((IOPFMI_status_t) 0x80000005)
#define kIOPFMI_STATUS_IOP_NOT_READY ((IOPFMI_status_t) 0x80000006)
#define kIOPFMI_STATUS_FAILED_RESET_ALL ((IOPFMI_status_t) 0x80000011)
#define kIOPFMI_STATUS_FAILED_READ_ID ((IOPFMI_status_t) 0x80000012)
#define kIOPFMI_STATUS_FAILED_ERASE_BLOCK ((IOPFMI_status_t) 0x80000013)
#define kIOPFMI_STATUS_FAILED_READ_PAGE ((IOPFMI_status_t) 0x80000014)
#define kIOPFMI_STATUS_FAILED_WRITE_PAGE ((IOPFMI_status_t) 0x80000015)
#define kIOPFMI_STATUS_FAILED_READ_RAW ((IOPFMI_status_t) 0x80000016)
#define kIOPFMI_STATUS_FAILED_WRITE_RAW ((IOPFMI_status_t) 0x80000017)
#define kIOPFMI_STATUS_FAILED_READ_MULTI ((IOPFMI_status_t) 0x80000018)
#define kIOPFMI_STATUS_FAILED_WRITE_MULTI ((IOPFMI_status_t) 0x80000019)
#define kIOPFMI_STATUS_FAILED_WRITE_BOOTPAGE ((IOPFMI_status_t) 0x8000001A)
#define kIOPFMI_STATUS_FAILED_READ_BOOTPAGE ((IOPFMI_status_t) 0x8000001B)
#define kIOPFMI_STATUS_FMC_DONE_TIMEOUT ((IOPFMI_status_t) 0x8000001C)
#define kIOPFMI_STATUS_READY_BUSY_TIMEOUT ((IOPFMI_status_t) 0x8000001D)
#define kIOPFMI_STATUS_ECC_CLEANUP ((IOPFMI_status_t) 0x8000001E)
#define kIOPFMI_STATUS_ECC_DONE_TIMEOUT ((IOPFMI_status_t) 0x8000001F)
#define kIOPFMI_STATUS_PPN_GENERAL_ERROR ((IOPFMI_status_t) 0x80000020)
/**
* Special status -- status code from NAND is in lower 16-bits.
*/
#define kIOPFMI_STATUS_PGM_ERROR ((IOPFMI_status_t) 0x80200000)
#define kIOPFMI_STATUS_PGM_ERROR_MASK 0xffff0000
#define kIOPFMI_STATUS_ERASE_ERROR ((IOPFMI_status_t) 0x80210000)
#define kIOPFMI_STATUS_ERASE_ERROR_MASK 0xffff0000
#define kIOPFMI_STATUS_DMA_DONE_TIMEOUT ((IOPFMI_status_t) 0x80000022)
#define kIOPFMI_STATUS_NOT_ALL_CLEAN ((IOPFMI_status_t) 0x80000023)
#define kIOPFMI_STATUS_AT_LEAST_ONE_UECC ((IOPFMI_status_t) 0x80000024)
#define kIOPFMI_STATUS_FUSED ((IOPFMI_status_t) 0x80000025)
/**
* Vendor-specific codes ... (passed in via vendorProtocol
* field)
*/
#define kVS_NONE 0
#define kVS_HYNIX_2P 1
#define kVS_TOSHIBA_2P 2
#define kVS_MICRON_2P 3
#define kVS_SAMSUNG_2D 4
#define kVS_SAMSUNG_2P_2D 5
typedef UInt8 IOPFMI_chipid_t[kIOPFMI_BYTES_PER_CHIPID];
typedef UInt32 IOPFMI_correction_t;
/* this must match <drivers/dma.h>::struct dma_segment */
struct _IOPFMI_dma_segment {
u_int32_t paddr;
u_int32_t length;
};
typedef struct _IOPFMI_dma_segment IOPFMI_dma_segment;
#define kIOPFMI_AES_KEY_TYPE_USER128 (1)
#define kIOPFMI_AES_KEY_TYPE_USER192 (2)
#define kIOPFMI_AES_KEY_TYPE_USER256 (3)
#define kIOPFMI_AES_KEY_TYPE_UID0 (4)
#define kIOPFMI_AES_KEY_TYPE_GID0 (5)
#define kIOPFMI_AES_KEY_TYPE_GID1 (6)
typedef UInt32 IOPFMI_state_t;
#define kIOPFMI_STATE_NONE ((IOPFMI_state_t) 0)
#define kIOPFMI_STATE_WAKING_UP ((IOPFMI_state_t) (1UL << 0))
#define kIOPFMI_STATE_POWER_CHANGED ((IOPFMI_state_t) (1UL << 1))
struct _IOPFMI
{
IOPFMI_opcode_t opcode;
IOPFMI_flags_t flags;
IOPFMI_status_t status;
UInt32 context;
UInt32 bus;
IOPFMI_state_t state;
};
typedef struct _IOPFMI IOPFMI;
struct _IOPFMI_FailureDetails
{
/**
* The operation may have had more than
* kIOPFMI_MAX_NUM_OF_ENABLES operations -- so we tell them how
* many were executed so that the caller can then figure out
* exactly where in a large list of things we were at when
* things went wrong.
*/
UInt32 wNumCE_Executed;
IOPFMI_status_t wOverallOperationFailure;
UInt32 wSingleCEStatus;
UInt32 wFirstFailingCE;
};
typedef struct _IOPFMI_FailureDetails IOPFMI_FailureDetails;
struct _IOPFMI_PpnStatus
{
UInt8 operation_status[kIOPFMI_MAX_IO_PAGES];
UInt8 program_failed_pages[kIOPFMI_MAX_IO_PAGES];
UInt8 program_ignored_pages[kIOPFMI_MAX_IO_PAGES];
UInt8 program_retired_pages[kIOPFMI_MAX_IO_PAGES];
};
typedef struct _IOPFMI_PpnStatus IOPFMI_PpnStatus;
struct _IOPFMI_ResetEverything
{
IOPFMI iopfmi;
};
typedef struct _IOPFMI_ResetEverything IOPFMI_ResetEverything;
struct _IOPFMI_PostResetOperations
{
IOPFMI iopfmi;
};
typedef struct _IOPFMI_PostResetOperations IOPFMI_PostResetOperations;
struct _IOPFMI_EraseSingle
{
IOPFMI iopfmi;
UInt16 ce;
UInt32 block_number;
IOPFMI_FailureDetails failure_details;
};
typedef struct _IOPFMI_EraseSingle IOPFMI_EraseSingle;
struct _IOPFMI_EraseMultiple
{
IOPFMI iopfmi;
UInt32 number_of_elements;
UInt16 ce[ kIOPFMI_MAX_NUM_OF_ENABLES ];
UInt32 block_number[ kIOPFMI_MAX_NUM_OF_ENABLES ];
IOPFMI_FailureDetails failure_details;
};
typedef struct _IOPFMI_EraseMultiple IOPFMI_EraseMultiple;
struct _IOPFMI_IOSingle
{
IOPFMI iopfmi;
UInt16 ce;
UInt32 page_number;
UInt32 data_address;
UInt32 meta_address;
// if non-zero, is array of one byte per sector in page
UInt32 correction_address;
UInt32 aes_iv_array; // Array of IOPFMI_aes_iv entries
UInt32 aes_num_chains;
UInt32 aes_chain_size;
UInt32 aes_key_type;
UInt8 aes_key_bytes[32];
};
typedef struct _IOPFMI_IOSingle IOPFMI_IOSingle;
struct _IOPFMI_IOMultiple
{
IOPFMI iopfmi;
UInt32 page_count;
UInt32 chip_enable_array;
UInt32 page_number_array;
UInt32 data_segments_array; // Array of IOPFMI_dma_segment entries
UInt32 data_segment_array_length_in_bytes;
UInt32 meta_segments_array; // Array of IOPFMI_dma_segment entries
UInt32 meta_segment_array_length_in_bytes;
// if non-zero, is array of one byte per sector in page
UInt32 corrections_array;
UInt32 aes_iv_array; // Array of IOPFMI_aes_iv entries
UInt32 aes_num_chains;
UInt32 aes_chain_size;
UInt32 aes_key_type;
UInt8 aes_key_bytes[32];
UInt32 vendorProtocol;
IOPFMI_FailureDetails failure_details;
};
typedef struct _IOPFMI_IOMultiple IOPFMI_IOMultiple;
struct _IOPFMI_IOPpn
{
IOPFMI iopfmi;
UInt32 page_count;
UInt32 data_segments_array;
UInt32 data_segments_array_length;
UInt32 meta_segments_array;
UInt32 meta_segments_array_length;
UInt32 ppn_fil_command;
UInt32 ppn_fil_size;
UInt32 aes_iv_array; // Array of IOPFMI_aes_iv entries
UInt32 aes_num_chains;
UInt32 aes_chain_size;
UInt32 aes_key_type;
UInt8 aes_key_bytes[32];
UInt32 geb_ce;
UInt8 overall_status;
};
typedef struct _IOPFMI_IOPpn IOPFMI_IOPpn;
struct _IOPFMI_IORaw
{
IOPFMI iopfmi;
UInt16 ce;
UInt32 page_number;
UInt32 buffer_address;
};
typedef struct _IOPFMI_IORaw IOPFMI_IORaw;
struct _IOPFMI_IOBootPage
{
IOPFMI iopfmi;
UInt16 ce;
UInt32 page_number;
UInt32 buffer_address;
// if non-zero, is array of one byte per sector in page
UInt32 corrections_array;
};
typedef struct _IOPFMI_IOBootPage IOPFMI_IOBootPage;
struct _IOPFMI_ReadCauBbt
{
IOPFMI iopfmi;
UInt16 ce;
UInt32 cau;
UInt32 buffer_address;
};
typedef struct _IOPFMI_ReadCauBbt IOPFMI_ReadCauBbt;
struct _IOPFMI_UpdateFirmware
{
IOPFMI iopfmi;
UInt32 sgl;
UInt32 sgl_length_in_bytes;
UInt32 fw_size;
};
typedef struct _IOPFMI_UpdateFirmware IOPFMI_UpdateFirmware;
struct _IOPFMI_SetConfig
{
IOPFMI iopfmi;
UInt32 fmi;
UInt32 num_of_ce;
UInt32 valid_ces;
UInt32 pages_per_block;
UInt32 sectors_per_page;
UInt32 bytes_per_page;
UInt32 bytes_per_spare;
UInt32 logical_page_size;
UInt32 blocks_per_ce;
UInt32 read_sample_cycles;
UInt32 read_setup_cycles;
UInt32 read_hold_cycles;
UInt32 write_setup_cycles;
UInt32 write_hold_cycles;
UInt32 dqs_half_cycles;
UInt32 ce_hold_cycles;
UInt32 ce_setup_cycles;
UInt32 adl_cycles;
UInt32 whr_cycles;
UInt32 read_pre_cycles;
UInt32 read_post_cycles;
UInt32 write_pre_cycles;
UInt32 write_post_cycles;
UInt32 enable_diff_DQS;
UInt32 enable_diff_RE;
UInt32 enable_VREF;
UInt32 retire_on_invalid_refresh;
UInt32 reg_dqs_delay;
UInt32 ppn;
UInt32 ppn_version;
UInt32 toggle_system;
UInt32 toggle;
UInt32 wMaxOutstandingCEWriteOperations;
UInt32 valid_bytes_per_meta;
UInt32 total_bytes_per_meta;
UInt32 ppn_debug_flags;
UInt32 ppn_debug_flags_valid;
UInt32 ppn_vs_debug_flags;
UInt32 ppn_vs_debug_flags_valid;
UInt32 ppn_allow_saving_debug_data;
UInt32 clock_speed_khz;
};
typedef struct _IOPFMI_SetConfig IOPFMI_SetConfig;
struct _IOPFMI_ReadChipIDs
{
IOPFMI iopfmi;
UInt32 chip_id_buffer;
};
typedef struct _IOPFMI_ReadChipIDs IOPFMI_ReadChipIDs;
// another copy lives in H2fmi_ppn.h (changes should be made to both places)
#define PPN_FEATURE__POWER_STATE__LOW_POWER (0x1)
#define PPN_FEATURE__POWER_STATE__ASYNC (0x2)
#define PPN_FEATURE__POWER_STATE__STANDBY (0x4)
#define PPN_FEATURE__POWER_STATE__DDR (0xA)
// Power State transition unique identifiers and accessors
// another copy lives in H2fmi_ppn.h (changes should be made to both places)
#define PPN_PS_TRANS_LOW_POWER_TO_ASYNC PS_TRANS_SET(PPN_FEATURE__POWER_STATE__LOW_POWER, PPN_FEATURE__POWER_STATE__ASYNC)
#define PPN_PS_TRANS_STDBY_TO_ASYNC PS_TRANS_SET(PPN_FEATURE__POWER_STATE__STDBY, PPN_FEATURE__POWER_STATE__ASYNC)
#define PPN_PS_TRANS_ASYNC_TO_LOW_POWER PS_TRANS_SET(PPN_FEATURE__POWER_STATE__ASYNC, PPN_FEATURE__POWER_STATE__LOW_POWER)
#define PPN_PS_TRANS_DDR_TO_LOW_POWER PS_TRANS_SET(PPN_FEATURE__POWER_STATE__DDR, PPN_FEATURE__POWER_STATE__LOW_POWER)
#define PPN_PS_TRANS_ASYNC_TO_STDBY PS_TRANS_SET(PPN_FEATURE__POWER_STATE__ASYNC, PPN_FEATURE__POWER_STATE__STDBY)
#define PPN_PS_TRANS_DDR_TO_STDBY PS_TRANS_SET(PPN_FEATURE__POWER_STATE__DDR, PPN_FEATURE__POWER_STATE__STDBY)
#define PPN_PS_TRANS_LOW_POWER_TO_DDR PS_TRANS_SET(PPN_FEATURE__POWER_STATE__LOW_POWER, PPN_FEATURE__POWER_STATE__DDR)
#define PPN_PS_TRANS_STDBY_TO_DDR PS_TRANS_SET(PPN_FEATURE__POWER_STATE__STDBY, PPN_FEATURE__POWER_STATE__DDR)
#define PS_TRANS_SET(from, to) ((((UInt32)(from)) << 16) | ((UInt32)(to)))
#define PS_TRANS_GET_FROM(ps) ((ps) >> 16)
#define PS_TRANS_GET_TO(ps) ((ps) & 0xFFFF)
struct _IOPFMI_SetPower
{
IOPFMI iopfmi;
UInt32 power_state_trans;
};
typedef struct _IOPFMI_SetPower IOPFMI_SetPower;
struct _IOPFMI_GetFailureInfo
{
IOPFMI iopfmi;
UInt32 ce;
UInt16 type;
UInt32 rma_buffer_length;
UInt32 sgl;
UInt32 sgl_length;
};
typedef struct _IOPFMI_GetFailureInfo IOPFMI_GetFailureInfo;
#define kIOPFMI_PPN_FW_VERSION_LENGTH 16
#define kIOPFMI_PPN_PACKAGE_ASSEMBLY_CODE_LENGTH 16
#define kIOPFMI_PPN_CONTROLLER_UNIQUE_ID_LENGTH 16
#define kIOPFMI_PPN_CONTROLLER_HW_ID_LENGTH 16
#define kIOPFMI_PPN_MANUFACTURER_ID_LENGTH 8
#define kIOPFMI_PPN_NAND_MARKETING_NAME_LENGTH 10
struct _IOPFMI_GetControllerInfo
{
IOPFMI iopfmi;
UInt32 ce;
UInt8 fw_version[kIOPFMI_PPN_FW_VERSION_LENGTH];
UInt8 package_assembly_code[kIOPFMI_PPN_PACKAGE_ASSEMBLY_CODE_LENGTH];
UInt8 controller_unique_id[kIOPFMI_PPN_CONTROLLER_UNIQUE_ID_LENGTH];
UInt8 controller_hw_id[kIOPFMI_PPN_CONTROLLER_HW_ID_LENGTH];
UInt8 manufacturer_id[kIOPFMI_PPN_MANUFACTURER_ID_LENGTH];
UInt8 marketing_name[kIOPFMI_PPN_NAND_MARKETING_NAME_LENGTH];
UInt32 caus;
UInt32 cau_bits;
UInt32 blocks_per_cau;
UInt32 block_bits;
UInt32 pages_per_block_mlc;
UInt32 pages_per_block_slc;
UInt32 page_address_bits;
UInt32 bits_per_cell_bits;
UInt32 default_bits_per_cell;
UInt32 page_size;
UInt32 dies;
UInt32 tRC;
UInt32 tREA;
UInt32 tREH;
UInt32 tRHOH;
UInt32 tRHZ;
UInt32 tRLOH;
UInt32 tRP;
UInt32 tWC;
UInt32 tWH;
UInt32 tWP;
UInt32 read_queue_size;
UInt32 program_queue_size;
UInt32 erase_queue_size;
UInt32 prep_function_buffer_size;
UInt32 tRST;
UInt32 tPURST;
UInt32 tSCE;
UInt32 tCERDY;
};
typedef struct _IOPFMI_GetControllerInfo IOPFMI_GetControllerInfo;
struct _IOPFMI_GetTemperature
{
IOPFMI iopfmi;
Int16 temperature_celsius;
};
typedef struct _IOPFMI_GetTemperature IOPFMI_GetTemperature;
#define kIOPFMI_PPN_DIE_UNIQUE_ID_LENGTH 16
#define kIOPFMI_PPN_DIE_CHIP_ID_LENGTH 8
struct _IOPFMI_GetDieInfo
{
IOPFMI iopfmi;
UInt32 ce;
UInt32 die;
UInt8 unique_id[kIOPFMI_PPN_DIE_UNIQUE_ID_LENGTH];
UInt8 chip_id[kIOPFMI_PPN_DIE_CHIP_ID_LENGTH];
};
typedef struct _IOPFMI_GetDieInfo IOPFMI_GetDieInfo;
struct _IOPFMI_SetFeatures
{
IOPFMI iopfmi;
UInt32 list;
UInt32 size;
};
typedef struct _IOPFMI_SetFeatures IOPFMI_SetFeatures;
union _IOPFMI_Command
{
IOPFMI iopfmi;
IOPFMI_SetConfig set_config;
IOPFMI_ResetEverything reset_everything;
IOPFMI_PostResetOperations post_reset_oper;
IOPFMI_EraseSingle erase_single;
IOPFMI_EraseMultiple erase_multiple;
IOPFMI_IOSingle io_single;
IOPFMI_IOMultiple io_multiple;
IOPFMI_IORaw io_raw;
IOPFMI_IOBootPage io_bootpage;
IOPFMI_ReadCauBbt read_cau_bbt;
IOPFMI_UpdateFirmware update_firmware;
IOPFMI_IOPpn io_ppn;
IOPFMI_ReadChipIDs read_chip_ids;
IOPFMI_SetPower set_power;
IOPFMI_GetFailureInfo get_failure_info;
IOPFMI_GetControllerInfo get_controller_info;
IOPFMI_GetTemperature get_temperature;
IOPFMI_GetDieInfo get_die_info;
IOPFMI_SetFeatures set_features;
UInt8 _pad[kIOPFMI_COMMAND_SIZE];
};
typedef union _IOPFMI_Command IOPFMI_Command;
#endif // _IOP_FMI_PROTOCOL_H_

View File

@ -0,0 +1,25 @@
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
#
# This document is the property of Apple Computer, 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 Computer, Inc.
#
#
# Flash Memory Interface driver
#
LOCAL_DIR := $(GET_LOCAL_DIR)
OPTIONS += WITH_FUNCTION_FMI=1
IOP_FUNCTIONS += FMI
ALL_OBJS += $(LOCAL_DIR)/iop_fmi.o
INSTALL_HEADERS += $(LOCAL_DIR)/iop_fmi_protocol.h
# XXX this is probably excessive
IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),8192)

View File

@ -0,0 +1,199 @@
/*
* Copyright (C) 2008 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 <AssertMacros.h>
#include <platform/soc/hwclocks.h>
#include <platform/soc/pmgr.h>
#include <platform/memmap.h>
#include <platform.h>
#include <sys/task.h>
#include <iop.h>
#include <qwi.h>
#include <EmbeddedIOPProtocol.h>
#include "iop_sdio_protocol.h"
#include "iop_sdio_wrapper.h"
#define cache_op_size(buf_size) (((buf_size) + (CPU_CACHELINE_SIZE-1)) & ~(CPU_CACHELINE_SIZE-1))
static struct task_event sdio_message_event;
static int sdio_channel;
static void sdio_message_wakeup(void *arg);
static bool sdio_message_process(void);
static int iop_sdio_task(void *cfg);
IOP_FUNCTION(sdio, iop_sdio_task, 1024, SDIO_CONTROL_CHANNEL);
static int
iop_sdio_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
dprintf(DEBUG_SPEW, "++ SDIO task starting\n");
check(kIOPSDIO_COMMAND_SIZE == sizeof(IOPSDIO_Command));
/* establish the host communications channel */
event_init(&sdio_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "++ opening sdio channel\n");
sdio_channel = qwi_instantiate_channel(
"sdio message",
QWI_ROLE_CONSUMER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
sdio_message_wakeup,
NULL);
for (;;) {
dprintf(DEBUG_SPEW, "++ waiting for message on sdio channel\n");
while (sdio_message_process())
{
// eat all available messages
}
event_wait(&sdio_message_event);
}
return(0);
}
static void
sdio_message_wakeup(void *arg __unused)
{
event_signal(&sdio_message_event);
}
extern unsigned int do_lookahead;
extern void iopsdio_cacheTransferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC);
static bool
sdio_message_process(void)
{
uint32_t messageAddr;
dprintf(DEBUG_SPEW, "++ handling host message\n");
do_lookahead = 0;
/* look to see if there's an item waiting for us */
if (qwi_receive_item(sdio_channel, &messageAddr) == -1)
return(false);
dprintf(DEBUG_SPEW, "++ received sdio message\n");
/* find the command structure based on the message */
union IOPSDIOMessage *message = (union IOPSDIOMessage*)mem_static_map_cached(messageAddr);
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the message size is a multiple of our cacheline size.
*/
platform_cache_operation(CACHE_INVALIDATE,
(void *)message,
cache_op_size(sizeof(*message)));
switch (message->header.opcode) {
case kIOPSDIOOpcodeInit:
{
dprintf(DEBUG_INFO, "SDHC @ 0x%X: Init\n",
message->targetSDHC.basePhysicalAddr);
message->header.status = iopsdio_init(&message->targetSDHC, &message->initCmd);
break;
}
case kIOPSDIOOpcodeFree:
{
dprintf(DEBUG_INFO, "SDHC @ 0x%X: Free\n",
message->targetSDHC.basePhysicalAddr);
message->header.status = iopsdio_free(&message->targetSDHC, &message->freeCmd);
break;
}
case kIOPSDIOOpcodeReset:
{
dprintf(DEBUG_INFO, "SDHC @ 0x%X: Reset 0x%X\n",
message->targetSDHC.basePhysicalAddr, message->resetCmd.resetFlags);
message->header.status = iopsdio_reset(&message->targetSDHC, &message->resetCmd);
break;
}
case kIOPSDIOOpcodeSetBusParam:
{
/* dprintf(DEBUG_INFO, "SDHC @ 0x%X: clk = %+d, baseClk = %u, clkRate = %u Hz, width = %u, speed = %d\n",
message->targetSDHC.basePhysicalAddr,
message->setBusParamCmd.clockMode, message->setBusParamCmd.baseClockRateHz,
message->setBusParamCmd.clockRateHz, message->setBusParamCmd.busWidth,
message->setBusParamCmd.busSpeedMode);
*/
message->header.status = iopsdio_setBusConfig(&message->targetSDHC, &message->setBusParamCmd);
break;
}
case kIOPSDIOOpcodeSendCommand:
{
/* dprintf(DEBUG_INFO, "SDHC @ 0x%X: Send SDIO Cmd: index = %u, arg = 0x%X\n",
message->targetSDHC.basePhysicalAddr,
message->sendSDIOCmd.sdioCmdIndex, message->sendSDIOCmd.sdioCmdArgument);
*/
message->header.status = iopsdio_sendSDIOCmd(&message->targetSDHC, &message->commandCmd);
break;
}
case kIOPSDIOOpcodeTransferData:
{
/* dprintf(DEBUG_INFO, "SDHC @ 0x%X: Send SDIO DMA Cmd: index = %u, arg = 0x%X\n",
message->targetSDHC.basePhysicalAddr,
message->sdioDataCmd.sdioCmdIndex, message->sdioDataCmd.sdioCmdArgument);
*/
message->header.status = iopsdio_transferSDIOData(&message->targetSDHC, &message->transferCmd);
break;
}
case kIOPSDIOOpcodePing:
dprintf(DEBUG_CRITICAL, "SDHC: Ping received, IOKit <--> IOP Connection established\n");
message->header.status = kIOPSDIOStatusSuccess;
break;
default:
dprintf(DEBUG_CRITICAL, "SDHC @ 0x%X: ERROR: unrecognised sdio opcode 0x%x\n",
message->targetSDHC.basePhysicalAddr, message->header.opcode);
message->header.status = kIOPSDIOParameterInvalid;
break;
}
dprintf(DEBUG_SPEW, "++ done processing sdio message with status 0x%08x\n", message->header.status);
platform_cache_operation(CACHE_CLEAN,
(void *)message,
sizeof(union IOPSDIOMessage));
qwi_send_item(sdio_channel, messageAddr);
{
if (do_lookahead) {
iopsdio_cacheTransferSDIOData(&message->targetSDHC);
do_lookahead = 0;
}
}
dprintf(DEBUG_SPEW, "++ signaled completion of sdio message to host\n");
return(true);
}

View File

@ -0,0 +1,259 @@
/*
* Copyright (C) 2008 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.
*/
#ifndef _IOP_SDIO_PROTOCOL_H_
#define _IOP_SDIO_PROTOCOL_H_
#include <sys/types.h>
#ifdef PLATFORM_VARIANT_IOP
#include <sdiocommon/sdio_types.h>
#else
#include <IOKit/sdio/sdio_types.h>
#endif
/*
* Command size is (somewhat) tunable.
*
* The principal consideration here is the maximum scatter/gather list size
* this permits.
*/
#define kIOPSDIO_MESSAGE_SIZE (512)
typedef UInt32 IOPSDIO_opcode_t;
enum IOPSDIOOpcode
{
kIOPSDIOOpcodeUnknown = 0,
kIOPSDIOOpcodePing = 1,
kIOPSDIOOpcodeInit = 2,
kIOPSDIOOpcodeFree = 3,
kIOPSDIOOpcodeReset = 4,
kIOPSDIOOpcodeSetBusParam = 5,
kIOPSDIOOpcodeSendCommand = 6,
kIOPSDIOOpcodeTransferData = 7,
};
typedef UInt32 IOPSDIO_flags_t;
enum IOPSDIOFlags
{
kIOPSDIOFlagsNone = 0,
};
typedef UInt32 IOPSDIO_status_t;
enum IOPSDIOStatus
{
// IOP Messaging Errors
/** @brief Command succeeded. */
kIOPSDIOStatusSuccess = 0,
/** @brief Status is not yet known (e.g. message has not been sent or processed). */
kIOPSDIOStatusUnknown = 1,
/** @brief One of the IOP parameters was invalid. */
kIOPSDIOParameterInvalid = 2,
// Common IOReturn Values
// If the IOP Status is in the range [0x200 0x300) then it is a standard
// IOKit IOReturn value without the "iokit_common_err( )" bits set
// SDIOReturn_t Values
// If the IOP status is in the range [0x300 0x800) it is an SDIOReturn_t
// value. This value is the same as the base value of IOSDIOReturn in the
// IOKit SDIO family, without the "iokit_family_err(sub_iokit_sdio, )"
// bits set.
};
/* this must match <drivers/dma.h>::struct dma_segment */
struct IOPSDIO_dma_segment {
u_int32_t paddr;
u_int32_t length;
};
/** @brief Standard header on all IOP SDIO Commands. */
struct IOPSDIOHeader
{
IOPSDIO_opcode_t opcode;
IOPSDIO_flags_t flags;
IOPSDIO_status_t status;
};
/** @brief Info about the target SDIO Host Controller. */
struct IOPSDIOTargetSDHC
{
/** @brief The Base physical address of the SDIO block's register file. */
UInt32 basePhysicalAddr;
/** @brief Wake event. */
UInt32 dmaCompleteEventAddr;
};
// SDIO INIT
/** @brief Bus Parameter Command. */
struct IOPSDIOInitCmd
{
/** @brief The capabilities of the SDHC. */
UInt64 sdhcCapabilities;
/** @brief The Maximum current capabilities of the SDHC. */
UInt64 sdhcMaxCurrentCapabilities;
};
// SDIO FREE
/** @brief Bus Parameter Command. */
struct IOPSDIOFreeCmd
{
// Nothing at the moment
};
// SDIO RESET
/** @brief SDHC Reset Command. */
struct IOPSDIOResetCmd
{
/** @brief Reset flags. */
UInt32 resetFlags;
};
// BUS PARAMETERS
/** @brief Bus Parameter Command. */
struct IOPSDIOSetBusParamCmd
{
/** @brief Base clock rate input to the SD Block.
* If 0, the value from the SDHC capabilities register is used instead.
*/
UInt32 baseClockRateHz;
/** @brief New SDIO clock rate in Hz. No change if 0.
* On return, this is set the value that was actually programmed into the
* SDHC block.
*/
UInt32 clockRateHz;
/** @brief New SDIO bus width (1 or 4). No change if 0. */
UInt8 busWidth;
/** @brief 1 if the SDIO clock should be on, -1 if off, 0 to leave unchanged */
int clockMode;
/** @brief 2 for high speed mode, 1 for normal speed, 0 to leave unchanged. */
int busSpeedMode;
};
// SDIO COMMAND
/** @brief Send an SDIO Command using the command line.
* Note that the status of the command will be reflected by the iopsdio.status field.
*/
struct IOPSDIOCommandCmd
{
/** @brief The SDIO Command. */
struct SDIOCommand command;
/** @brief The command response. */
struct SDIOCommandResponse response;
};
// SDIO DATA COMMAND
/** @brief Send an SDIO Command with data (including the command line)
* Note that the status of the command will be reflected by the iopsdio.status field.
*/
struct IOPSDIOTransferCmd
{
/** @brief The SDIO Command. */
struct SDIOCommand command;
/** @brief The command response. */
struct SDIOCommandResponse response;
/** @brief The SDIO transfer parameters. */
struct SDIOTransfer transfer;
/** @brief Information about the memory segments involved in the transfer. */
struct SDIOMemorySegments memory;
/** @brief Physical segments for DMA engine. Variable Size. */
struct IOPSDIO_dma_segment segment[];
};
// IOP MESSAGES
/** @brief IOP SDIO Message. */
union IOPSDIOMessage
{
struct {
/** @brief IOP Header. */
struct IOPSDIOHeader header;
/** @brief Target SDIO block. */
struct IOPSDIOTargetSDHC targetSDHC;
/** @brief Command Data. */
union {
struct IOPSDIOInitCmd initCmd;
struct IOPSDIOFreeCmd freeCmd;
struct IOPSDIOResetCmd resetCmd;
struct IOPSDIOSetBusParamCmd setBusParamCmd;
struct IOPSDIOCommandCmd commandCmd;
struct IOPSDIOTransferCmd transferCmd;
};
};
UInt8 _pad[kIOPSDIO_MESSAGE_SIZE] SDIO_RESERVED;
};
union _IOPSDIO_sgl_sizing_tool
{
struct {
/** @brief IOP Header. */
struct IOPSDIOHeader header;
/** @brief Target SDIO block. */
struct IOPSDIOTargetSDHC targetSDHC;
/** @brief Command Data. */
union {
struct IOPSDIOTransferCmd transferCmd;
/* XXX add other sgl-using commands */
};
};
};
#define kIOPSDIO_MAX_SEGMENTS ((kIOPSDIO_MESSAGE_SIZE - sizeof(union _IOPSDIO_sgl_sizing_tool)) / sizeof(IOPSDIO_dma_segment))
#endif // _IOP_SDIO_PROTOCOL_H_

View File

@ -0,0 +1,667 @@
/*
* Copyright (c) 2008 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 "iop_sdio_wrapper.h"
#include <sys/task.h>
#include <AssertMacros.h>
#include <sdiodrv_config.h>
#include <sdiodrv_command.h>
#include <sdiodrv_transfer.h>
#include <platform.h>
#include <platform/memmap.h>
#include "iop_sdio_protocol.h"
#include <sdiocommon/sdio_cmdfields.h>
#include "debug.h"
#define cache_op_size(buf_size) (((buf_size) + (CPU_CACHELINE_SIZE-1)) & ~(CPU_CACHELINE_SIZE-1))
#define MAGIC_WRITE 0x44555544
#define MAGIC_READ 0x55555555
#define MAX_SDIO_READ_LEN (32*1024) //agreement with AppleBCMWLAN driver that there wont be any read bigger than that
#define BCOM_SDIO_CHANNEL_OFFSET 5
#define BCOM_SDIO_NEXTLEN_OFFSET 6
#define BCOM_SDIO_DATAOFFSET_OFFSET 7
#define BCOM_SDIO_GLOM_CHANNEL 3
#define BCOM_SDIO_GLOM_FRAME_DESCRIPTOR 0x80
#define MAGIC_READ_PACKET_LEN_OFFSET 1
#define MAGIC_READ_ENABLE_OFFSET 2
#define MAGIC_READ_BUFFER_LENGTH_OFFSET 3
#define LOOKAHEAD_READ_PKT_SIZE (lookahead_read_pkt_size)
struct cacheCmd {
struct IOPSDIOTransferCmd transferCmd;
struct IOPSDIO_dma_segment segment;
};
struct cacheCmd cmd;
SDIOReturn_t cachedError = kSDIOReturnSuccess;
unsigned int do_lookahead = 0;
unsigned int lookahead_read_pkt_size = 12 * 1024; //default buffer, max 12K per packet
IOPSDIO_status_t
iopsdio_init(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOInitCmd *initCmd)
{
check(targetSDHC);
check(initCmd);
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
// Create DMA completion event.
struct task_event* dmaCompleteEvent = malloc(sizeof(struct task_event));
if(!dmaCompleteEvent) {
return kSDIOReturnNoMemory;
}
event_init(dmaCompleteEvent, EVENT_FLAG_AUTO_UNSIGNAL, false);
targetSDHC->dmaCompleteEventAddr = (UInt32)dmaCompleteEvent;
#if 0
/* 7256044 */
initCmd->sdhcCapabilities = sdhc->capabilities;
initCmd->sdhcMaxCurrentCapabilities = sdhc->maxCurrentCapabilities;
#else
initCmd->sdhcCapabilities = 0;
initCmd->sdhcMaxCurrentCapabilities = 0;
#endif
return kSDIOReturnSuccess;
}
IOPSDIO_status_t
iopsdio_free(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOFreeCmd *freeCmd)
{
check(targetSDHC);
check(freeCmd);
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
// Destroy the DMA completion event
free((void*)targetSDHC->dmaCompleteEventAddr);
targetSDHC->dmaCompleteEventAddr = 0;
return kSDIOReturnSuccess;
}
IOPSDIO_status_t
iopsdio_reset(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOResetCmd *resetCmd)
{
check(targetSDHC);
check(resetCmd);
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
SDIOReturn_t retval = sdiodrv_resetSDHC(sdhc, resetCmd->resetFlags);
if(kSDIOReturnSuccess != retval) {
return retval;
}
// restore interrupt state on reset
if(resetCmd->resetFlags & kSDHCResetAll) {
sdhc_enableCommandStatus(sdhc, true);
sdhc_enableTransferStatus(sdhc, true);
}
cachedError = retval;
return retval;
}
IOPSDIO_status_t
iopsdio_setBusConfig(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOSetBusParamCmd *busParamCmd)
{
check(targetSDHC);
check(busParamCmd);
if (cachedError != kSDIOReturnSuccess)
return cachedError;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
SDIOReturn_t retval = kSDIOReturnSuccess;
if(busParamCmd->clockRateHz) {
retval = sdiodrv_setClockRate(sdhc, &busParamCmd->clockRateHz, busParamCmd->baseClockRateHz);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
if(busParamCmd->busWidth) {
retval = sdiodrv_setBusWidth(sdhc, busParamCmd->busWidth);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
if(busParamCmd->busSpeedMode) {
retval = sdiodrv_setBusSpeedMode(sdhc, busParamCmd->busSpeedMode);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
if(busParamCmd->clockMode) {
retval = sdiodrv_setClockMode(sdhc, busParamCmd->clockMode);
if(kSDIOReturnSuccess != retval) {
return retval;
}
}
return retval;
}
unsigned int nm = 0;
IOPSDIO_status_t
iopsdio_sendSDIOCmd(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOCommandCmd *commandCmd)
{
check(targetSDHC);
check(commandCmd);
if (cachedError != kSDIOReturnSuccess)
return cachedError;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
SDIOReturn_t retval = sdiodrv_sendSDIOCommand(sdhc, &commandCmd->command, &commandCmd->response);
return retval;
}
/*
* read a complete SDIO packet:
* reads are always block aligned
*
* blocks: number of blocks to read, if zero, then issue a one block read in order to get the frame header and
* obtain the packet length from this header, issue a second read in order to to get the remaining blocks
* max_blocks: max_blocks allowed
* data: virtual address if read buffer
*
*/
IOPSDIO_status_t iopsdio_read_packet(unsigned int blocks, unsigned int max_blocks, unsigned int * data, struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOTransferCmd *transferCmd)
{
IOPSDIO_status_t retval = 0;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
struct task_event *dmaCompleteEvent = (struct task_event *)targetSDHC->dmaCompleteEventAddr;
if (blocks == 0) {
transferCmd->transfer.blockCount = 1;
transferCmd->memory.dataLength = transferCmd->transfer.blockSize;
transferCmd->segment[0].length = transferCmd->transfer.blockSize;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | 1;
data = mem_static_map_cached((uint32_t)data);
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
if (transferCmd->memory.dataLength > MAX_SDIO_READ_LEN) {
//paranoia
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet1: len frame is too large %llu \n", transferCmd->memory.dataLength);
retval = kSDIOReturnBadArgument;
return retval;
}
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
if (retval)
return retval;
data = mem_static_map_cached((uint32_t)data);
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
unsigned short check = data[0]>>16;
unsigned short fflen = data[0];
if (fflen) {
if ((fflen + check) != 0xffff) {
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet error %x %d - %x blocks %u %x\n", fflen, fflen, check, blocks, data[1]);
dprintf(DEBUG_CRITICAL, " ===== %p %x %x %x\n", data, transferCmd->segment[0].paddr, data[0],*(unsigned int*)transferCmd->segment[0].paddr);
return retval;
}
unsigned short mask = transferCmd->transfer.blockSize-1;
unsigned short flen = (fflen+mask) & ~mask; //round up
unsigned int remain_blocks = flen ;
if (128 == transferCmd->transfer.blockSize) {
remain_blocks = remain_blocks/128;
} else {
remain_blocks = remain_blocks/transferCmd->transfer.blockSize;
}
if (remain_blocks == 0) {
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet: error zero blocks \n");
return kSDIOReturnBadArgument;
}
remain_blocks--;
if (remain_blocks >= max_blocks) {
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet: not enough blocks %u orig %u flen %x check %x\n", remain_blocks, max_blocks, fflen, check);
return kSDIOReturnBadArgument;
}
if (remain_blocks) {
//read remaining blocks
transferCmd->transfer.blockCount = remain_blocks;
transferCmd->memory.dataLength = transferCmd->transfer.blockSize * remain_blocks;
transferCmd->segment[0].length = transferCmd->transfer.blockSize * remain_blocks;
transferCmd->segment[0].paddr += transferCmd->transfer.blockSize;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | remain_blocks;
if (transferCmd->memory.dataLength > MAX_SDIO_READ_LEN) {
//paranoia,
dprintf(DEBUG_CRITICAL, "iopsdio_read_packet2: len frame is too large %llu remain %u size %hu\n", transferCmd->memory.dataLength, remain_blocks, transferCmd->transfer.blockSize);
retval = kSDIOReturnBadArgument;
return retval;
}
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
transferCmd->segment[0].paddr -= transferCmd->transfer.blockSize;
}
}
} else {
transferCmd->transfer.blockCount = blocks;
transferCmd->memory.dataLength = transferCmd->transfer.blockSize * blocks;
transferCmd->segment[0].length = transferCmd->transfer.blockSize * blocks;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | blocks;
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
}
return retval;
}
void iopsdio_cacheTransferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC)
{
SDIOReturn_t retval = 0;
cachedError = kSDIOReturnSuccess;
//perform access based on the cached command
struct IOPSDIOTransferCmd *transferCmd = &cmd.transferCmd;
unsigned int * data = (unsigned int*)transferCmd->segment[0].paddr;
data = mem_static_map_cached((uint32_t)data);
unsigned int max_blocks;
if (128 == transferCmd->transfer.blockSize)
max_blocks = LOOKAHEAD_READ_PKT_SIZE/128;
else
max_blocks = LOOKAHEAD_READ_PKT_SIZE/transferCmd->transfer.blockSize;
//kick SDIO transfer
retval = iopsdio_read_packet(transferCmd->transfer.blockCount, max_blocks, data, targetSDHC, transferCmd);
if (retval) {
if ((retval < kSDIOReturnDataError) || (retval > kSDIOReturnDataTimeout)) {
//paranoia, make sure we return a Data error here, which means that the host will trigger a Reset
cachedError = kSDIOReturnDataError;
} else {
cachedError = retval;
}
} else {
cachedError = kSDIOReturnSuccess;
}
unsigned int *retp = (unsigned int*)(((unsigned char*)data)+LOOKAHEAD_READ_PKT_SIZE);
//dprintf(0, "iopsdio_cacheTransferSDIOData done %x %x\n", *retp, retval);
//make sure we tell the host processor that we're done
* retp = retval;
platform_cache_operation(CACHE_CLEAN,
(void *)retp,
(32));
}
IOPSDIO_status_t
iopsdio_transferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOTransferCmd *transferCmd)
{
check(targetSDHC);
check(transferCmd);
unsigned int orig_bcount = 0;
unsigned int nlen = 0;
if (cachedError != kSDIOReturnSuccess)
return cachedError;
SDHCRegisters_t *sdhc = (SDHCRegisters_t*)targetSDHC->basePhysicalAddr;
if(!sdhc) {
return kSDIOReturnNoHostController;
}
struct task_event *dmaCompleteEvent = (struct task_event *)targetSDHC->dmaCompleteEventAddr;
transferCmd->memory.segmentList = transferCmd->segment;
unsigned int orig_data = transferCmd->segment[0].paddr;
unsigned int * data = mem_static_map_cached((uint32_t)orig_data);
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
(32));
SDIOReturn_t retval = 0;
if (( transferCmd->transfer.direction == kSDIODirectionWrite) && (*data == MAGIC_WRITE) && ((transferCmd->transfer.command.argument & kSDIOCmd53BlockMode) == kSDIOCmd53BlockMode)) {
unsigned int base_addr = transferCmd->segment[0].paddr + transferCmd->transfer.blockSize;
unsigned int * l = data + 1;
unsigned int cnt = 0;
unsigned int bcount = *l ;
// dprintf(0, "iopsdio_transferSDIOData magic wwwrite got %x %x [%x %x] %u %u %u\n",l[0],l[1], transferCmd->transfer.command.argument,transferCmd->transfer.command.index, transferCmd->transfer.blockCount,transferCmd->transfer.blockSize, transferCmd->memory.segmentCount);
//first sdio block = MAGIC_WRITE/block size of pkt1/block size of pkt 2/block size of pkt3 etc...
//second sdio blocks and so on... = packets data
if (transferCmd->transfer.blockCount == 1) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData magic write error!! %p %x %x %x\n",data, l[0],l[1],l[2]);
return kSDIOReturnBadArgument;
}
while (bcount!=0) {
unsigned int transfer_len = bcount * transferCmd->transfer.blockSize;
transferCmd->memory.dataLength = transfer_len;
transferCmd->transfer.command.argument = (transferCmd->transfer.command.argument& 0xfffffe00) | bcount;
transferCmd->transfer.blockCount = bcount;
transferCmd->segment[0].length = transfer_len;
transferCmd->segment[0].paddr = base_addr;
unsigned int *p = (unsigned int*)transferCmd->segment[0].paddr;
p = mem_static_map_cached((uint32_t)p);
platform_cache_operation(CACHE_INVALIDATE,
(void *)p,
(32));
unsigned short check = p[0]>>16;
unsigned short fflen = p[0];
if ((check + fflen) != 0xffff) {
//internal error, the sender tries to send a corrupted packet
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData detected error ---> %p : %x %x %x %x \n", p, p[0], p[1], p[2], p[3]);
retval = kSDIOReturnBadArgument;
} else {
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
}
if (retval) {
*data = cnt; //tell the sender how many packets we did send successfully
platform_cache_operation(CACHE_CLEAN,
(void *)data,
(32));
unsigned int *p = (unsigned int *)base_addr;
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData ---> %x %x %x %x \n", p[0], p[1], p[2], p[3]);
dprintf(DEBUG_CRITICAL, " --> cnt %u bc %u addr %p arg %x retval %x\n", cnt, transferCmd->transfer.blockCount, (void *)transferCmd->segment[0].paddr, transferCmd->transfer.command.argument, retval);
return retval;
}
base_addr += transfer_len;//transferCmd->transfer.blockSize * bcount;
cnt++;
l++;
bcount = *l ;
}
} else if ((transferCmd->transfer.direction == kSDIODirectionRead) && (*data == MAGIC_READ) && ((transferCmd->transfer.command.argument & kSDIOCmd53BlockMode) == kSDIOCmd53BlockMode)) {
//MAGIC READ:
//buffer1 of size LOOKAHEAD_READ_PKT_SIZE + buffer2 of size LOOKAHEAD_READ_PKT_SIZE + one
//additional block used as end of transfer marker
orig_bcount = transferCmd->transfer.blockCount;
// dprintf(0, "iopsdio_transferSDIOData read got %u %u %u \n",l[0],l[1],l[2]);
unsigned int blocks = *(data+MAGIC_READ_PACKET_LEN_OFFSET);
unsigned int lk = *(data+MAGIC_READ_ENABLE_OFFSET);
if (*(data+MAGIC_READ_BUFFER_LENGTH_OFFSET)) {
lookahead_read_pkt_size = *(data+MAGIC_READ_BUFFER_LENGTH_OFFSET);
}
retval = iopsdio_read_packet(blocks, orig_bcount, data, targetSDHC, transferCmd);
if (lk == 0) {
//Host processor doesn't want the look ahead
return retval;
}
if (retval) {
goto return_clear;
}
unsigned char nextlen = *((unsigned char*)data + BCOM_SDIO_NEXTLEN_OFFSET);
unsigned char channel = *((unsigned char*)data + BCOM_SDIO_CHANNEL_OFFSET);
unsigned char offset = *((unsigned char*)data + BCOM_SDIO_DATAOFFSET_OFFSET);
// dprintf(0, "iopsdio_transferSDIOData read got %x %x %x - %u channel %u offset %u\n",data[0],data[1],data[2], nextlen,channel, offset);
unsigned short check = data[0]>>16;
unsigned short fflen = data[0];
if (fflen) {
if ((fflen + check) != 0xffff) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData cksum error %x %d - %x \n", fflen, fflen, check);
retval = kSDIOReturnDataError;
goto return_clear;
}
} else {
if (check==0) {
//fflen and check are null, hence no more data pending
goto return_clear;
}
}
nlen = 16 * nextlen;
if (nlen == 0 && channel == (BCOM_SDIO_GLOM_FRAME_DESCRIPTOR|BCOM_SDIO_GLOM_CHANNEL)) {
//reading a Glom descriptor: calculate the length of next frame
//max number of GLom frames is tunable and typically 7, a glom frame descriptor should not be larger than 64 Bytes
#define SANE_MAX_GLOM_FRAME_LEN 64
if (fflen > SANE_MAX_GLOM_FRAME_LEN) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData glom descriptor is too large %hu \n", fflen);
goto return_clear;
}
if (fflen > 32) {
//uncache the rest of the frame
platform_cache_operation(CACHE_INVALIDATE,
(void *)data,
cache_op_size(fflen));
}
unsigned short * p = (unsigned short *)((unsigned char*)data + offset);
unsigned short * end = (unsigned short *)((unsigned char*)data + fflen);
//calculate nlen
while (p < end){
nlen += *p++;
}
if (nlen > LOOKAHEAD_READ_PKT_SIZE) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData calculated glom frame is too large %u \n", nlen);
retval = kSDIOReturnDataError;
goto return_clear;
}
}
//save parameters for the next transfer
nlen += transferCmd->transfer.blockSize-1;
nlen = nlen & ~(transferCmd->transfer.blockSize-1);
if (nlen > MAX_SDIO_READ_LEN) {
//paranoia
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData magic read calculating a bad len frame large %u \n", nlen);
retval = kSDIOReturnBadArgument;
goto return_clear;
}
cmd.transferCmd.transfer.command.argument = transferCmd->transfer.command.argument;
cmd.transferCmd.transfer.command.index = transferCmd->transfer.command.index;
if (128 == transferCmd->transfer.blockSize)
cmd.transferCmd.transfer.blockCount = nlen/128;
else
cmd.transferCmd.transfer.blockCount = nlen/transferCmd->transfer.blockSize;
cmd.transferCmd.transfer.blockSize = transferCmd->transfer.blockSize;
cmd.transferCmd.transfer.direction = transferCmd->transfer.direction;
cmd.segment.length = nlen;
cmd.segment.paddr = orig_data+LOOKAHEAD_READ_PKT_SIZE;
cmd.transferCmd.memory.segmentList = &cmd.segment;
cmd.transferCmd.memory.dataLength = nlen;
cmd.transferCmd.memory.segmentCount = 1;
if (lk > 1) {
//experimental mode, read all at once while the host processor wait - used only for debug 14July2011
iopsdio_cacheTransferSDIOData(targetSDHC);
} else {
//set lookahead to tell the sdio task handler in iop_sdio.c to generate the cached loakahead read
do_lookahead = lk;
}
} else {
if (transferCmd->transfer.direction == kSDIODirectionRead) {
//if transfer is cmd53 block read, check that the requested length matches the number of blocks requested
//and check that the total length is not overly large
nlen = transferCmd->transfer.blockCount * transferCmd->transfer.blockSize;
if (nlen != transferCmd->memory.dataLength) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData len mismatch %u %llu direction %hhu\n", nlen, transferCmd->memory.dataLength, transferCmd->transfer.direction);
return kSDIOReturnBadArgument;
}
if (nlen > MAX_SDIO_READ_LEN) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData data transfer is too large %u %llu direction %hhu\n", nlen, transferCmd->memory.dataLength, transferCmd->transfer.direction);
return kSDIOReturnBadArgument;
}
}
retval = sdiodrv_transferData(sdhc, &transferCmd->transfer, &transferCmd->memory, &transferCmd->response, dmaCompleteEvent);
if (retval) {
dprintf(DEBUG_CRITICAL, "iopsdio_transferSDIOData error %x\n",retval);
}
}
return retval;
return_clear:
{
unsigned int last_block;
if (128 == transferCmd->transfer.blockSize)
last_block = (LOOKAHEAD_READ_PKT_SIZE*2)/128;
else
last_block = (LOOKAHEAD_READ_PKT_SIZE*2)/transferCmd->transfer.blockSize;
//paranoia: check if we have enough space in the buffer, then mark it as done
if (orig_bcount >= last_block) {
unsigned int *retp = (unsigned int*)(((unsigned char*)data)+LOOKAHEAD_READ_PKT_SIZE*2);
* retp = retval;
platform_cache_operation(CACHE_CLEAN,
(void *)retp,
(32));
}
}
return retval;
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2008 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.
*/
#ifndef _IOP_SDIO_WRAPPER_H
#define _IOP_SDIO_WRAPPER_H
#include "iop_sdio_protocol.h"
IOPSDIO_status_t iopsdio_init(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOInitCmd *initCmd);
IOPSDIO_status_t iopsdio_free(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOFreeCmd *freeCmd);
IOPSDIO_status_t iopsdio_reset(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOResetCmd *resetCmd);
IOPSDIO_status_t iopsdio_setBusConfig(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOSetBusParamCmd *busParamCmd);
IOPSDIO_status_t iopsdio_sendSDIOCmd(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOCommandCmd *commandCmd);
IOPSDIO_status_t iopsdio_transferSDIOData(struct IOPSDIOTargetSDHC *targetSDHC, struct IOPSDIOTransferCmd *transferCmd);
#endif // _IOP_SDIO_WRAPPER_H

View File

@ -0,0 +1,25 @@
# Copyright (C) 2008 Apple Computer, Inc. All rights reserved.
#
# This document is the property of Apple Computer, 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 Computer, Inc.
#
#
# SDIO driver
#
LOCAL_DIR := $(GET_LOCAL_DIR)
OPTIONS += WITH_FUNCTION_SDIO=1
IOP_FUNCTIONS += SDIO
ALL_OBJS += $(LOCAL_DIR)/iop_sdio.o $(LOCAL_DIR)/iop_sdio_wrapper.o
INSTALL_HEADERS += $(LOCAL_DIR)/iop_sdio_protocol.h
# XXX tune this to suit SDIO requirements
IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),8192)

56
apps/EmbeddedIOP/iop.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2008 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 <sys/linker_set.h>
#define IOP_MESSAGE_NO_WAIT 0
#define IOP_MESSAGE_WAIT_FOREVER 0xffffffff /* actually a bit more than an hour */
extern void iop_message_trace(const char *ident, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3);
/*
* Magic for hooking up IOP functions.
*/
struct iop_function {
const char *function_name;
int (* entrypoint)(void *cfg);
int stack_allocation;
int control_channel;
};
#define IOP_FUNCTION(_function_name, _entrypoint, _stack_allocation, _control_channel) \
static const struct iop_function \
__attribute__ ((used)) \
__iop_function_##_function_name = { \
#_function_name, \
_entrypoint, \
_stack_allocation, \
_control_channel \
}; \
LINKER_SET_ENTRY(iop_function, __iop_function_##_function_name);
/*
* IOP sleep/wake notifications
*/
struct iop_sleep_hook {
void ( *func)(int mode);
};
#define IOP_SLEEP_MODE_SLEEPING 0
#define IOP_SLEEP_MODE_WAKING 1
#define IOP_SLEEP_HOOK(_name, _hook_function) \
static const struct iop_sleep_hook \
__attribute__ ((used)) \
__iop_sleep_hook_##_name = { \
_hook_function \
}; \
LINKER_SET_ENTRY(iop_sleep_hook, __iop_sleep_hook_##_name);

618
apps/EmbeddedIOP/main.c Normal file
View File

@ -0,0 +1,618 @@
/*
* Copyright (C) 2007-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 <arch.h>
#include <debug.h>
#include <cbuffer.h>
#include <platform.h>
#include <platform/memmap.h>
#include <platform/int.h>
#include <platform/soc/hwisr.h>
#include <platform/timer.h>
#include <sys.h>
#include <sys/boot.h>
#include <sys/menu.h>
#include <sys/task.h>
#include <lib/mib_def.h>
#include "iop.h"
#include "qwi.h"
#include "qwi_protocol.h"
#include "EmbeddedIOPProtocol.h"
#include "clock_management.h"
#include "clock_stepping.h"
/* XXX should get these automagically */
#if WITH_FUNCTION_SDIO
extern int iop_sdio_task(void *arg);
#endif
/* configuration, patched by host before we start */
struct iop_configuration _iop_config = {
.magic = IOP_CONFIG_MAGIC
};
static int host_channel;
static struct task_event host_command_event;
static int iop_message_channel;
static struct task_event iop_message_event;
static struct task_event iop_console_event;
static union iop_message *iop_message_buffer;
static bool iop_suspended;
struct iop_ping_tracker gControlMessages[128];
int gControlMessageCount = 0;
static int no_idle_task(void *arg __unused);
static int host_command_task(void *cfg);
static void host_command_hook(void *user_data);
static bool host_command_process(void);
static union iop_message *iop_message_alloc(utime_t allowed_delay);
static void iop_message_wakeup(void *arg __unused);
static void iop_message_tty(CBUFFER *pcb);
static int host_console_task(void *arg __unused);
/* the host command task is just another function */
IOP_FUNCTION(iop, host_command_task, 1024, IOP_CONTROL_CHANNEL);
/* sleep/wakeup hook */
static void sleep_hook(int mode);
IOP_SLEEP_HOOK(iop, sleep_hook);
/* console */
CBUFFER console_buffer;
#ifndef APPLICATION_CONSOLE_BUFFER
# define APPLICATION_CONSOLE_BUFFER 256
#endif
/* NMI */
static void iop_nmi_handler(void *junk);
#if DEBUG_BUILD
static char logo[] = {
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x2f, 0x5c, 0x2c, 0x25, 0x2c, 0x5f, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c,
0x25, 0x25, 0x25, 0x2f, 0x2c, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x2d, 0x22, 0x25,
0x25, 0x7c, 0x2f, 0x2f, 0x25, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x2e, 0x27, 0x20, 0x20, 0x2e, 0x2d, 0x22, 0x20,
0x20, 0x2f, 0x25, 0x25, 0x25, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x46, 0x4f, 0x52, 0x20, 0x50, 0x4f, 0x4e, 0x59, 0x21, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x2d, 0x27, 0x5f, 0x2e, 0x2d,
0x22, 0x20, 0x30, 0x29, 0x20, 0x20, 0x20, 0x5c, 0x25, 0x25, 0x25, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2e, 0x5c, 0x2e, 0x27, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x25, 0x25,
0x25, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x20, 0x2f, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x5f, 0x2c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x25, 0x25, 0x25, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x22,
0x2d, 0x2d, 0x2d, 0x22, 0x7e, 0x60, 0x5c, 0x20, 0x20, 0x20, 0x5f, 0x2c,
0x2a, 0x27, 0x5c, 0x25, 0x25, 0x27, 0x20, 0x20, 0x20, 0x5f, 0x2c, 0x2d,
0x2d, 0x22, 0x22, 0x22, 0x22, 0x2d, 0x2c, 0x25, 0x25, 0x2c, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x29, 0x2a, 0x5e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x22,
0x22, 0x7e, 0x7e, 0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x5c, 0x25, 0x25, 0x25, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2f, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x5c, 0x25, 0x25, 0x25, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x5f, 0x2e, 0x2d, 0x60, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7c, 0x25,
0x25, 0x2c, 0x5f, 0x5f, 0x5f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f,
0x2e, 0x2d, 0x22, 0x20, 0x20, 0x20, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x2c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x2c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2c, 0x7c,
0x25, 0x25, 0x20, 0x20, 0x20, 0x2e, 0x60, 0x5c, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x2f, 0x5c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x5c, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x5c, 0x25, 0x27, 0x20, 0x20, 0x20, 0x5c, 0x20, 0x2f, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x5c, 0x20, 0x5c, 0x20, 0x5f, 0x2c, 0x2f, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x60, 0x7e, 0x2d, 0x2e, 0x5f, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2c, 0x60, 0x5c,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x60, 0x22, 0x22, 0x7e, 0x7e,
0x60, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x22, 0x60, 0x20, 0x2f,
0x2d, 0x2e, 0x2c, 0x5f, 0x20, 0x2f, 0x27, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x60, 0x7e, 0x22, 0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x7e, 0x20, 0x20,
0x20, 0x20, 0x60, 0x5c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x0a, 0x20,
0x20, 0x20, 0x6a, 0x67, 0x73, 0x20, 0x20, 0x20, 0x5c, 0x5f, 0x5f, 0x5f,
0x2c, 0x27, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x5c, 0x2e, 0x2d, 0x22, 0x60, 0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60,
0x2d, 0x2d, 0x27, 0x0a, 0x00
};
#endif
int
_main(void)
{
void **func_cursor;
const struct iop_function *func;
#if DEBUG_BUILD
printf("\n%s", logo);
#endif
dprintf(DEBUG_INFO, "####\n#### " CONFIG_PROGNAME_STRING ": " XBS_BUILD_TAG "\n####\n");
/* initialize the cpu */
dprintf(DEBUG_INFO, "doing CPU init\n");
arch_cpu_init(false);
/* do early initialization of hardware */
dprintf(DEBUG_INFO, "doing early platform hardware init\n");
platform_early_init();
/* bring up system services (cpu, tasks, callout) */
dprintf(DEBUG_INFO, "doing system init\n");
sys_init();
#ifdef HEAP_EXT_SIZE
/*
* If we have extra memory for the heap, add it. This needs
* to happen after main memory has been initialized.
*/
dprintf(DEBUG_INFO, "Adding %llu bytes at %p to heap.\n", HEAP_EXT_SIZE, (void *)HEAP_EXT_BASE);
heap_add_chunk((void *)HEAP_EXT_BASE, HEAP_EXT_SIZE, true);
#endif
/* register our doorbell handler and enable the doorbell */
platform_init_iop_doorbell((int_handler)qwi_doorbell, NULL);
/*
* Start the host console task and give it a chance to run, so that we have
* a console past this point.
*/
task_start(task_create("console", host_console_task, NULL, 512));
task_yield();
/*
* Iterate IOP function tasks, starting each as we go.
*/
LINKER_SET_FOREACH(func_cursor, iop_function) {
func = (const struct iop_function *)*func_cursor;
if (NULL != func->entrypoint) {
dprintf(DEBUG_INFO, "starting %s\n", func->function_name);
task_start(task_create(
func->function_name,
func->entrypoint,
(void *)&_iop_config.channel[func->control_channel],
func->stack_allocation));
}
}
#if DEBUG_BUILD
dprintf(DEBUG_INFO, "starting debug console\n");
task_start(task_create("menu", menu_task, NULL, 8192));
#endif
/* start the slopsucker task if we don't want to go idle */
if (_iop_config.options & IOP_OPTION_NO_IDLE)
task_start(task_create("slopsucker", no_idle_task, NULL, 512));
/* start the slopsucker task if we don't want to go idle */
if (_iop_config.options & IOP_OPTION_DO_CLOCK_MGMNT)
SetParticipateInClockStateManagement(true);
/* if the task manager needs retuning, do so */
if (_iop_config.deep_idle_us != 0)
task_set_idle_threshold(_iop_config.deep_idle_us);
/* register our NMI handler */
platform_init_nmi(iop_nmi_handler, NULL);
dprintf(DEBUG_INFO, "bootstrap task terminating\n");
task_exit(0);
}
/*
* This task is run when we don't want the IOP to idle, normally when
* JTAG debugging is enabled on the host.
*/
static int
no_idle_task(void *arg __unused)
{
for (;;) {
task_yield();
}
return(0);
}
static int
host_command_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
dprintf(DEBUG_INFO, "## IOP control task starting\n");
/* establish the host communications channel */
event_init(&host_command_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_INFO, "## opening host channel\n");
host_channel = qwi_instantiate_channel(
"iop control",
QWI_ROLE_CONSUMER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
host_command_hook,
&host_command_event);
iop_suspended = false;
for (;;) {
/*
* Spin handling host commands
*/
while (host_command_process())
;
if (!iop_suspended) {
/*
* Normal operation; wait for an event signalling new work.
*/
dprintf(DEBUG_SPEW, "## waiting for host command\n");
event_wait(&host_command_event);
} else {
/*
* We are suspended; act as though we had received a doorbell
* interrupt.
*/
host_command_hook(&host_command_event);
}
}
return(0);
}
static void
host_command_hook(void *user_data)
{
struct task_event *event = (struct task_event *)user_data;
/* pass it through to task context */
event_signal(event);
}
static bool
host_command_process(void)
{
uint32_t message;
union iop_command *command;
bool do_sleep = false;
void **hook_cursor;
const struct iop_sleep_hook *hook;
struct idle_statistics stats; // XXX this is 'big', almost never used; ok on stack?
/* look to see if there's an item waiting for us */
if (qwi_receive_item(host_channel, &message) == -1)
return(false);
/* find the command structure based on the message */
command = (union iop_command *)mem_static_map_cached(message);
if (command == MAP_FAILED)
panic("received bad command pointer on control channel");
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the command size is a multiple of our cacheline size.
*/
platform_cache_operation(CACHE_INVALIDATE, (void *)command, sizeof(*command));
switch (command->generic.opcode) {
case IOP_CMD_NOP:
// dprintf(DEBUG_INFO, "## host NOP\n");
// Store information on ping commands received, in a global structure
// For use during debug of watchdog time outs
command->generic.result = IOP_RESULT_SUCCESS;
gControlMessages[gControlMessageCount].timestamp = timer_get_ticks();
gControlMessages[gControlMessageCount].record.opcode = command->ping.opcode;
gControlMessages[gControlMessageCount].record.result = command->ping.result;
gControlMessages[gControlMessageCount].record.ping_id = command->ping.ping_id;
gControlMessageCount = (gControlMessageCount + 1) % 128;
break;
case IOP_CMD_TTYIN:
debug_pushchar(command->ttyin.c);
command->generic.result = IOP_RESULT_SUCCESS;
break;
case IOP_CMD_SLEEP:
dprintf(DEBUG_INFO, "## host SLEEP\n");
do_sleep = true;
command->generic.result = IOP_RESULT_SUCCESS;
break;
case IOP_CMD_SUSPEND:
dprintf(DEBUG_INFO, "## host SUSPEND\n");
iop_suspended = true;
platform_cache_operation(CACHE_CLEAN, 0, 0);
command->generic.result = IOP_RESULT_SUCCESS;
break;
case IOP_CMD_RESUME:
dprintf(DEBUG_INFO, "## host RESUME\n");
iop_suspended = false;
command->generic.result = IOP_RESULT_SUCCESS;
break;
case IOP_CMD_INSTRUMENT:
dprintf(DEBUG_INFO, "## host INSTRUMENT\n");
iop_suspended = false;
task_get_statistics(& stats);
command->generic.result = IOP_RESULT_SUCCESS;
command->instr.uptime_ticks = stats.uptime_ticks;
command->instr.idles = stats.idles;
command->instr.deep_idles = stats.deep_idles;
command->instr.deep_idle_ticks = stats.deep_idle_ticks;
command->instr.idle_ticks = stats.idle_ticks;
command->instr.threshold_us = stats.threshold_us;
command->instr.ticksHz = stats.ticksHz;
break;
default:
dprintf(DEBUG_CRITICAL, "## unrecognised host opcode 0x%x in command %p\n", command->generic.opcode, command);
command->generic.result = IOP_RESULT_ERROR;
break;
}
/* this should never fail because we just pulled an item out and so we must own a slot */
platform_cache_operation(CACHE_CLEAN, (void *)command, sizeof(*command));
qwi_send_item(host_channel, message);
/* sleep *after* we have replied */
if (do_sleep) {
/* tell anyone that cares that we're sleeping */
LINKER_SET_FOREACH(hook_cursor, iop_sleep_hook) {
hook = (const struct iop_sleep_hook *)*hook_cursor;
hook->func(IOP_SLEEP_MODE_SLEEPING);
}
/* go to sleep */
platform_sleep();
/* and tell them we've woken up again */
LINKER_SET_FOREACH(hook_cursor, iop_sleep_hook) {
hook = (const struct iop_sleep_hook *)*hook_cursor;
hook->func(IOP_SLEEP_MODE_WAKING);
}
}
return(true);
}
static void
sleep_hook(int mode)
{
switch(mode) {
case IOP_SLEEP_MODE_WAKING:
dprintf(DEBUG_INFO, "enabling doorbell\n");
platform_unmask_doorbell();
// On some system (like AE2), put the system into lowest power state
// SetClockState will be no-op on other systems
SetClockState(kClockRequestPowerManager, kClockValueLow);
break;
case IOP_SLEEP_MODE_SLEEPING:
dprintf(DEBUG_INFO, "disabling doorbell\n");
// on some system(like AE2), restore clocks to high before we go to sleep
SetClockState(kClockRequestPowerManager, kClockValueHigh);
platform_mask_doorbell();
break;
}
}
/*
* Putchar hook.
*/
char gIOPPanicLog[IOP_PANIC_LOG_SIZE];
u_int32_t gIOPPanicBytes;
void
application_putchar(int c)
{
/* if we are panicking, append the data to the panic log */
if (NULL != gPanicStr) {
/*
* Printf emits nuls because it's not smart enough to tell the difference between
* strings and streams, so strip them here.
*/
if ((0 != c) && (gIOPPanicBytes < IOP_PANIC_LOG_SIZE))
gIOPPanicLog[gIOPPanicBytes++] = c;
} else {
/*
* If we have a console buffer, and this isn't a nul, try to stuff it
* there and wake up the output task.
*/
if ((true == cb_initialized(&console_buffer)) && (0 != c)) {
#if APPLICATION_CONSOLE_RELIABLE
/* Try to be lossless - yield if the buffer is full */
while (!cb_free_space(&console_buffer)) {
task_yield();
}
#endif
(void)cb_putc(&console_buffer, c);
event_signal(&iop_console_event);
}
}
}
/*
* NMI handling
*/
static void
iop_nmi_handler(void *junk)
{
panic("NMI");
}
/*******************************************************************************
* IOP-to-host messaging support
*
* N.B. Since this is used to carry console output, (d)printf should not be
* called in this code.
*/
/*
* Allocate a message to send to the host.
*
* Note that it is not safe to block between allocating a message and sending it.
*/
static union iop_message *
iop_message_alloc(utime_t allowed_delay)
{
utime_t deadline, t;
int idx;
/* nothing we can do if we don't have a buffer */
if (NULL == iop_message_buffer)
return(NULL);
/* try to get a slot right away */
if (-1 != (idx = qwi_next_send_index(iop_message_channel)))
return(iop_message_buffer + idx);
if (IOP_MESSAGE_NO_WAIT == allowed_delay)
return(NULL);
/* spin waiting for a free slot */
deadline = system_time() + allowed_delay;
for (;;) {
/* get the current time and see if we've timed out */
t = system_time();
if (t >= deadline)
return(NULL);
/* sleep waiting for notification or timeout */
event_wait_timeout(&iop_message_event, deadline - t);
/* try again to get a message slot */
if (-1 != (idx = qwi_next_send_index(iop_message_channel)))
return(iop_message_buffer + idx);
}
}
/*
* Reclaim a message the host has accepted.
*/
static void
iop_message_wakeup(void *arg __unused)
{
bool replies;
uint32_t message;
/* reap message replies */
replies = false;
while (qwi_receive_item(iop_message_channel, &message) != -1)
replies = true;
/* if we got at least one reply, wake anyone trying to send */
if (replies)
event_signal(&iop_message_event);
}
/*
* Try to send a trace message to the host.
*/
void
iop_message_trace(const char *ident, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3)
{
union iop_message *msg;
uint64_t timestamp;
/* get the raw time shared with the host */
timestamp = timer_get_ticks();
/* now get a message - wait a little while for it but not forever as we might deadlock the host */
if (NULL != (msg = iop_message_alloc(1000))) {
/* populate the message */
msg->gen.opcode = IOP_MSG_TRACE;
msg->gen.size = sizeof(msg->trace);
msg->trace.ident = mem_static_map_physical((uint32_t)ident);
msg->trace.arg[0] = arg0;
msg->trace.arg[1] = arg1;
msg->trace.arg[2] = arg2;
msg->trace.arg[3] = arg3;
msg->trace.timestamp = timestamp;
/* push it to memory */
platform_cache_operation(CACHE_CLEAN, msg, IOP_MESSAGE_MAX);
/* and give it to the host */
qwi_send_item(iop_message_channel, QWI_ENCODE_ORDINAL(msg - iop_message_buffer));
}
}
/*
* Try to send a tty message to the host.
*/
static void
iop_message_tty(CBUFFER *pcb)
{
union iop_message *msg;
unsigned int count;
/* get a message */
if (NULL == (msg = iop_message_alloc(IOP_MESSAGE_WAIT_FOREVER)))
panic("the console is dead, Jim");
/* get as many bytes from the cbuffer into our message as we can */
count = cb_read(pcb, (unsigned char *)msg->tty.bytes, IOP_MSG_TTY_MAXLEN);
msg->gen.opcode = IOP_MSG_TTY;
msg->gen.size = sizeof(msg->gen) + count;
/* push it to memory */
platform_cache_operation(CACHE_CLEAN, msg, IOP_MESSAGE_MAX);
/* and give it to the host */
qwi_send_item(iop_message_channel, QWI_ENCODE_ORDINAL(msg - iop_message_buffer));
}
/*
* Buffer task outputting to the console.
*/
static int
host_console_task(void *arg __unused)
{
/* allocate a cbuffer for host console output */
cb_create(&console_buffer, APPLICATION_CONSOLE_BUFFER);
/* establish the message channel */
iop_message_buffer = (union iop_message *)mem_static_map_cached(_iop_config.message_buffer);
event_init(&iop_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
event_init(&iop_console_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_INFO, "## opening IOP message channel\n");
iop_message_channel = qwi_instantiate_channel(
"iop message",
QWI_ROLE_PRODUCER,
_iop_config.channel[IOP_MESSAGE_CHANNEL].ring_size,
(void *)mem_static_map_cached(_iop_config.channel[IOP_MESSAGE_CHANNEL].ring_base),
iop_message_wakeup,
NULL);
for (;;) {
/* drain the console buffer */
while (cb_readable_size(&console_buffer) > 0)
iop_message_tty(&console_buffer);
/* wait for more */
event_wait(&iop_console_event);
}
return(0);
}

347
apps/EmbeddedIOP/qwi.c Normal file
View File

@ -0,0 +1,347 @@
/*
* Copyright (C) 2007-2010 Apple Inc. All rights reserved.
*
* This document is the property of Apple Computer, 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 Computer, Inc.
*/
#include <debug.h>
#include <sys/menu.h>
#include "qwi_protocol.h"
#include "qwi.h"
#ifndef QWI_MAX_CHANNELS
# error Must define QWI_MAX_CHANNELS
#endif
typedef struct qwi_channel {
const char *qwc_name;
int qwc_role;
int qwc_ring_size;
int qwc_next_in;
int qwc_next_out;
qwi_workitem_t qwc_work_items;
qwi_channel_hook qwc_user_hook;
void *qwc_user_data;
#if DEBUG_BUILD
# define QWC_STAT_INCR(_cp, _name) do {(_cp)->qwc_stat_##_name++; } while(0)
int qwc_stat_messages;
#else
# define QWC_STAT_INCR(_cp, _name) do { } while(0)
#endif
} *qwi_channel_t;
/* fetch pointer to workitem */
#define QWC_WORKITEM(_cp, _index) \
((_cp)->qwc_work_items + (_index))
/* compute next workitem index */
#define QWC_ADVANCE(_cp, _index) \
((((_index) + 1) >= (_cp)->qwc_ring_size) ? 0 : ((_index) + 1))
static int qwi_channel_count;
static struct qwi_channel qwi_channels[QWI_MAX_CHANNELS];
/******************************************************************************
* qwi_instantiate_channel
*
* Create a new channel endpoint using workitems from item_pool.
*/
int
qwi_instantiate_channel(
const char *name,
int role,
int ring_size,
void *item_pool,
qwi_channel_hook hook,
void *user_data)
{
qwi_workitem_t ip;
qwi_channel_t cp;
int i, handle;
ASSERT((role == QWI_ROLE_PRODUCER) || (role == QWI_ROLE_CONSUMER));
ASSERT(qwi_channel_count < QWI_MAX_CHANNELS);
ASSERT(item_pool != NULL);
qwi_enter_critical();
cp = &qwi_channels[qwi_channel_count];
/* init the channel */
cp->qwc_name = name;
cp->qwc_role = role;
cp->qwc_ring_size = ring_size;
cp->qwc_work_items = item_pool;
cp->qwc_user_hook = hook;
cp->qwc_user_data = user_data;
handle = qwi_channel_count++;
/* ring initially belongs to the producer, so claim it if we are */
if (role == QWI_ROLE_PRODUCER) {
for (i = 0; i < ring_size; i++) {
ip = QWC_WORKITEM(cp, i);
ip->qwi_control = QWI_OWNER_PRODUCER;
qwi_cache_store_line(ip);
}
cp->qwc_next_in = ~0;
cp->qwc_next_out = 0;
dprintf(DEBUG_INFO, "allocated producer %d with %d slots, item size %zu\n",
handle, ring_size, sizeof(*ip));
} else {
cp->qwc_next_in = 0;
cp->qwc_next_out = ~0;
dprintf(DEBUG_INFO, "allocated consumer %d with %d slots, item size %zu\n",
handle, ring_size, sizeof(*ip));
}
/* success */
qwi_exit_critical();
return(handle);
}
/******************************************************************************
* qwi_send_item
*
* Send the next work item to the other end of the channel.
*/
int
qwi_send_item(int channel_handle, uint32_t message)
{
qwi_workitem_t ip;
qwi_channel_t cp;
ASSERT((channel_handle >= 0) && (channel_handle < qwi_channel_count));
/* verify item data is aligned correctly */
ASSERT(message == (message & QWI_ADDRESS_MASK));
qwi_enter_critical();
cp = &qwi_channels[channel_handle];
/* if we have no items free to go, we can't do this */
if (cp->qwc_next_out == ~0)
goto fail;
/* find the next item for this channel */
ip = QWC_WORKITEM(cp, cp->qwc_next_out);
/* set data and assign ownership to the other end */
ip->qwi_control = QWI_CONTROL(message, cp->qwc_role ^ QWI_OWNER_MASK);
/* if we didn't have anything outstanding before, we do now */
if (cp->qwc_next_in == ~0)
cp->qwc_next_in = cp->qwc_next_out;
/* find the next item we can send */
cp->qwc_next_out = QWC_ADVANCE(cp, cp->qwc_next_out);
/* if we have sent the last item, we have nothing free to go */
if (cp->qwc_next_in == cp->qwc_next_out)
cp->qwc_next_out = ~0;
/* ensure the item is visible to the consumer */
qwi_cache_store_line(ip);
QWC_STAT_INCR(cp, messages);
qwi_exit_critical();
/* ring the doorbell */
qwi_ring_doorbell();
return(0);
fail:
qwi_exit_critical();
return(-1);
}
/******************************************************************************
* qwi_receive_item
*
* Receive the next incoming item from the other end of the channel.
*/
int
qwi_receive_item(int channel_handle, uint32_t *message)
{
qwi_workitem_t ip;
qwi_channel_t cp;
ASSERT((channel_handle >= 0) && (channel_handle < qwi_channel_count));
ASSERT(message != NULL);
qwi_enter_critical();
cp = &qwi_channels[channel_handle];
/* if we own the entire ring, there's nothing waiting for us */
if (cp->qwc_next_in == ~0)
goto fail;
/* find the next item for this channel */
ip = QWC_WORKITEM(cp, cp->qwc_next_in);
/* make sure that we own it */
qwi_cache_invalidate_line(ip);
if (!QWI_ITEM_TEST_OWNER(ip, cp->qwc_role))
goto fail;
/* we do, so pass the reply pointer back */
*message = QWI_ITEM_ADDRESS(ip);
/* if we didn't have anything free to send with, we do now */
if (cp->qwc_next_out == ~0)
cp->qwc_next_out = cp->qwc_next_in;
/* find the next item coming in */
cp->qwc_next_in = QWC_ADVANCE(cp, cp->qwc_next_in);
/* if we've received the last item, we own the entire ring again */
if (cp->qwc_next_out == cp->qwc_next_in)
cp->qwc_next_in = ~0;
QWC_STAT_INCR(cp, messages);
qwi_exit_critical();
return(0);
fail:
qwi_exit_critical();
return(-1);
}
/******************************************************************************
* qwi_peek_item
*
* Peek at the next item from the other end of the channel.
*/
int
qwi_peek_item(int channel_handle, uint32_t *message)
{
qwi_workitem_t ip;
qwi_channel_t cp;
ASSERT((channel_handle >= 0) && (channel_handle < qwi_channel_count));
qwi_enter_critical();
cp = &qwi_channels[channel_handle];
/* if we own the entire ring, there's nothing waiting for us */
if (cp->qwc_next_in == ~0)
goto fail;
/* find the next item for this channel */
ip = QWC_WORKITEM(cp, cp->qwc_next_in);
/* make sure that we own it */
qwi_cache_invalidate_line(ip);
if (!QWI_ITEM_TEST_OWNER(ip, cp->qwc_role))
goto fail;
/* we do, so pass the reply pointer back */
if (message)
*message = QWI_ITEM_ADDRESS(ip);
qwi_exit_critical();
return(0);
fail:
qwi_exit_critical();
return(-1);
}
/******************************************************************************
* qwi_next_send_index
*
* Return the ring index of the next item that will be sent for the given
* channel.
*
* For protocols that operate strictly in-order this allows the client to avoid
* separately managing their command buffers.
*/
int
qwi_next_send_index(int channel_handle)
{
qwi_channel_t cp;
int result;
ASSERT((channel_handle >= 0) && (channel_handle < qwi_channel_count));
qwi_enter_critical();
cp = &qwi_channels[channel_handle];
result = (cp->qwc_next_out == ~0) ? -1 : cp->qwc_next_out;
qwi_exit_critical();
return(result);
}
/******************************************************************************
* qwi_doorbell
*
* Called when the doorbell is rung by the other end.
*/
void
qwi_doorbell(void)
{
qwi_channel_t cp;
int i, count;
qwi_enter_critical();
count = qwi_channel_count;
qwi_exit_critical();
/*
* Iterate the channel set, and for channels that have user hooks
* and which have at least one pending item, call the hook.
*/
for (i = 0; i < count; i++) {
cp = &qwi_channels[i];
if ((cp->qwc_user_hook) && (0 == qwi_peek_item(i, NULL)))
cp->qwc_user_hook(cp->qwc_user_data);
}
}
#if WITH_MENU
static int
qwi_dump(int argc __unused, struct cmd_arg *args __unused)
{
qwi_workitem_t ip;
qwi_channel_t cp;
int chn, i;
for (chn = 0; chn < qwi_channel_count; chn++) {
cp = &qwi_channels[chn];
printf("%02d(%s): %s, 0x%x slots, next in 0x%x, next out 0x%x, ring at %p\n",
chn,
cp->qwc_name,
(cp->qwc_role == QWI_ROLE_PRODUCER) ? "producer" : "consumer",
cp->qwc_ring_size,
cp->qwc_next_in,
cp->qwc_next_out,
cp->qwc_work_items);
#if DEBUG_BUILD
printf(" %d messages passed\n", cp->qwc_stat_messages);
#endif
for (i = 0; i < cp->qwc_ring_size; i++) {
ip = QWC_WORKITEM(cp, i);
printf(" %02d@%p: 0x%08x %c %c\n",
i, ip,
ip->qwi_control & QWI_ADDRESS_MASK,
((ip->qwi_control & QWI_OWNER_MASK) == QWI_ROLE_PRODUCER) ? 'P' : 'C',
(i == cp->qwc_next_in) ? 'i' : (i == cp->qwc_next_out) ? 'o' : ' ');
}
}
return(0);
}
MENU_COMMAND_DEBUG(qwi, qwi_dump, "print QWI channels", NULL);
#endif

220
apps/EmbeddedIOP/qwi.h Normal file
View File

@ -0,0 +1,220 @@
/*
* Copyright (C) 2007-2010 Apple Inc. All rights reserved.
*
* This document is the property of Apple Computer, 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 Computer, Inc.
*/
/*
* Queued Work Item protocol library.
*
*
* QWI provides a mechanism for passing work items between processors using
* shared memory and a doorbell interrupt. It is a straightforward design using
* common techniques.
*
* This library supports multiple instances of the QWI protocol (channels)
* operating in either directioion with a single remote endpoint. There is
* assumed to be a single doorbell available at either end.
*
* A QWI channel comprises a fixed-size ring of items. The ring should be sized
* to account for doorbell latency and to permit coalescing of doorbell
* interrupts; it need not be large if the work item throughput is low.
*
* Each channel moves work items in one direction and responses in the other.
* For command/response oriented communication, one channel is thus sufficient.
* For asynchronous interactions, a channel is required in each direction. The
* parties at each end of a channel are referred to as the producer and the
* consumer respectively.
*
* Work items in the ring are owned by either the producer or the consumer.
* Ownership is tracked by a single bit, which can only be set by the current
* owner. Ownership changes always move forwards in the ring, they are never made
* out of order. For example, this ring of eight work items has three currently
* owned by the consumer:
*
* 0 1 2 3 4 5 6 7
* C C C P P P P P
*
* Work item 0 is always returned to the producer before item 1 is returned.
*
* The payload of a work item is a client-supplied pointer. QWI does not manage
* coherency for the payload; this is a client responsibility.
*
* When attention is signalled via the doorbell, the doorbell must be
* acknowledged and re-armed before any check is made of channels in order to
* prevent items being lost and the queue potentially stalling.
*
*
* The following parameters must be defined:
*
* QWI_MAX_CHANNELS
*
* Establishes the size of the channel table.
*
*/
#include <stdint.h>
/* must match the protocol definition */
#define QWI_MESSAGE_ALIGNMENT 64
/* must match QWI_OWNER_* values in the protocol definition */
#define QWI_ROLE_CONSUMER 0
#define QWI_ROLE_PRODUCER 1
typedef void (* qwi_channel_hook)(void *user_data);
/******************************************************************************
* API
*/
extern int qwi_instantiate_channel(
const char *name,
int role,
int ring_size,
void *item_pool,
qwi_channel_hook hook,
void *user_data);
/*
* Instantiates a new channel endpoint and returns a corresponding
* channel handle. Endpoints must be co-ordinated between the
* processors; there is no setup channel.
*
* role
* One of QWI_ROLE_PRODUCER or QWI_ROLE_CONSUMER depending
* on whether the endpoint is the producer or consumer.
*
* ring_size
* The number of entries in the ring.
*
* item_pool
* The buffer allocated for ring items. This can be sized by the
* client using the QWI_POOOL_SIZE() macro. The pool must be
* in uncached memory.
*
* hook
* user_data
* If a hook is supplied for the channel, when the doorbell is
* rung the hook will be called with user_data as an argument.
*/
extern int qwi_send_item(int channel_handle, uint32_t message);
/*
* Sends a work item to the other end of the channel.
*
* Any memory referenced by item_data should be in a state where it may
* be instantly consumed by the other processor.
*
* Returns -1 if there are no free work items available.
*
* channel_handle
* A handle returned from qwi_instantiate_channel().
*
* message
* The message to be passed to the other end.
*/
extern int qwi_receive_item(int channel_handle, uint32_t *message);
/*
* Attempts to receive a work item. If successful, returns the message as
* supplied by the sender.
*
* Returns -1 if there are no waiting items to be received.
*
* channel_handle
* A handle returned from qwi_instantiate_channel().
*
* message
* Local pointer storage for the value passed by the other end.
*/
extern int qwi_peek_item(int channel_handle, uint32_t *message);
/*
* Attempts to peek a work item. If successful, returns the message as
* supplied by the sender. Does not remove the item from the queue.
*
* Returns -1 if there are no waiting items to be received.
*
* channel_handle
* A handle returned from qwi_instantiate_channel().
*
* message
* Local pointer storage for the value passed by the other end.
*/
extern int qwi_next_send_index(int channel_handle);
/*
* Returns the ring index for the next item to be sent on the given
* channel. If the index returned is -1 there are no free items to send.
* For protocols where items are sent and replied in strict order, the
* index can be used by the client to avoid tracking work item buffer
* ownership separately.
*
* Note that the client is required to co-ordinate between calls to
* qwi_next_send_index() and qwi_send_item() to avoid races.
*
* channel_handle
* A handle returned from qwi_instantiate_channel()
*/
/******************************************************************************
* Dependencies
*/
#include <platform.h>
#include <sys/task.h>
extern void qwi_doorbell(void);
/*
* Must be called when the local doorbell is rung.
*/
/*extern void qwi_cache_store_line(void *address);*/
static inline void qwi_cache_store_line(void *address)
{
address = (void *)((uint32_t)address & ~(QWI_MESSAGE_ALIGNMENT - 1));
platform_cache_operation(CACHE_CLEAN, address, QWI_MESSAGE_ALIGNMENT);
}
/*
* Causes the cache line containing (address) to be stored to coherent memory.
*/
/*extern void qwi_cache_invalidate_line(void *address);*/
static inline void qwi_cache_invalidate_line(void *address)
{
address = (void *)((uint32_t)address & ~(QWI_MESSAGE_ALIGNMENT - 1));
platform_cache_operation(CACHE_INVALIDATE, address, QWI_MESSAGE_ALIGNMENT);
}
/*
* Causes the cache line containing (address) to be invalidated, forcing the
* next access to read from coherent memory.
*/
/*extern void qwi_ring_doorbell(void);*/
static inline void qwi_ring_doorbell(void)
{
platform_ring_host_doorbell();
}
/*
* Rings the other processor's doorbell.
*/
/*extern void qwi_enter_critical(void);*/
/*extern void qwi_exit_critical(void);*/
static inline void qwi_enter_critical(void)
{
enter_critical_section();
}
static inline void qwi_exit_critical(void)
{
exit_critical_section();
}
/*
* These calls are required to avoid re-entry of critical sections of the
* code. Note that the qwi_cache_*() functions must be safely callable
* from within the critical region.
*/

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2007-2010 Apple Inc. All rights reserved.
*
* This document is the property of Apple Computer, 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 Computer, Inc.
*/
/*
* Queued Work Item protocol.
*
*
* QWI provides a mechanism for passing work items between processors using
* shared memory and a doorbell interrupt. It is a straightforward design using
* common techniques.
*
* A QWI channel comprises a fixed-size ring of items. The ring should be sized
* to account for doorbell latency and to permit coalescing of doorbell
* interrupts; it need not be large if the work item throughput is low.
*
* Each channel moves work items in one direction and responses in the other.
* For command/response oriented communication, one channel is thus sufficient.
* For asynchronous interactions, a channel is required in each direction. The
* parties at each end of a channel are referred to as the producer and the
* consumer respectively.
*
* Work items in the ring are owned by either the producer or the consumer.
* Ownership is tracked by a single bit, which can only be set by the current
* owner. Ownership changes always move forwards in the ring, they are never made
* out of order. For example, this ring of eight work items has three currently
* owned by the consumer:
*
* 0 1 2 3 4 5 6 7
* C C C P P P P P
*
* Work item 0 is always returned to the producer before item 1 is returned.
*
* The payload of a work item is a client-supplied pointer. QWI does not manage
* coherency for the payload; this is a client responsibility.
*/
/* basic quantum of message passing */
#define QWI_MESSAGE_ALIGNMENT 64
/*
* In order to avoid racing, the control and payload information must be stored
* atomically.
*
* It is assumed that stores of a uint32_t (when aligned) are atomic, and that
* user data is 4-byte aligned.
*/
typedef union qwi_workitem {
uint32_t qwi_control;
char _pad[QWI_MESSAGE_ALIGNMENT];
} *qwi_workitem_t;
#define QWI_POOL_SIZE(_ring_count) ((_ring_count) * sizeof(union qwi_workitem))
/* ownership/role relation */
#define QWI_OWNER_CONSUMER 0
#define QWI_OWNER_PRODUCER 1
/* ownership mask */
#define QWI_OWNER_MASK 1
/* bit 1 in qwi_control is reserved for later use */
/* address mask */
#define QWI_ADDRESS_MASK (~(uint32_t)0x3)
/* construct the qwi_control value */
#define QWI_CONTROL(_address, _owner) \
((uint32_t)(_address) | (_owner))
/* fetch the address from a workitem */
#define QWI_ITEM_ADDRESS(_ip) \
(((_ip)->qwi_control & QWI_ADDRESS_MASK))
/* test ownership of a workitem */
#define QWI_ITEM_TEST_OWNER(_ip, _who) \
(((_ip)->qwi_control & QWI_OWNER_MASK) == (uint32_t)(_who))
/* en/decode a value so that it can be passed as though it were an item address */
#define QWI_ENCODE_ORDINAL(_n) ((uint32_t)(_n) << 2)
#define QWI_DECODE_ORDINAL(_n) ((uint32_t)(_n) >> 2)

View File

@ -0,0 +1,90 @@
# Copyright (C) 2007-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.
###############################################################################
# Build rules for the SecureROM module.
#
# Recognises values in
#
# BUILDS
# TARGETS
# CONFIGS
#
VALID_BUILDS := ROMRELEASE DEBUG
VALID_TARGETS := t8002 t8010
VALID_CONFIGS := si fpga
# support for temporarily turning off targets
SUPPRESSED_TARGETS :=
###############################################################################
# No user-serviceable parts below
# DEVICEMAP macros
include makefiles/device_map.mk
BUILDS ?= $(VALID_BUILDS)
MAKE_BUILDS = $(filter $(VALID_BUILDS), $(BUILDS))
CONFIGS ?= $(VALID_CONFIGS)
MAKE_CONFIGS = $(filter $(VALID_CONFIGS), $(CONFIGS))
# Consult the device map database for the set of platforms associated with
# at least one target.
MAP_TARGETS := $(DEVICEMAP_PLATFORMS)
TARGETS ?= $(filter-out $(SUPPRESSED_TARGETS),$(MAP_TARGETS))
MAKE_TARGETS = $(filter $(VALID_TARGETS), $(TARGETS))
MAKE_PRODUCT = SecureROM
TC_LIST = $(foreach target, $(MAKE_TARGETS), $(addprefix $(target)-, $(MAKE_CONFIGS)))
LIST = $(foreach build, $(MAKE_BUILDS), $(addprefix $(build)-, $(TC_LIST)))
LIST_TEMPLATE := $(addprefix %-,$(LIST))
$(STANDARD_ACTIONS):%: $(LIST_TEMPLATE)
ACTIONS := $(foreach action,$(STANDARD_ACTIONS),$(addprefix $(action)-,$(LIST)))
ROM_IMAGE_si := true
ROM_IMAGE_fpga := true
build: $(BUILD)
install: $(INSTALL)
clean: $(CLEAN)
$(ACTIONS): action = $(word 1, $(subst -, ,$@))
$(ACTIONS): target = $(word 3, $(subst -, ,$@))
$(ACTIONS): config = $(word 4, $(subst -, ,$@))
$(ACTIONS): product = $(MAKE_PRODUCT)
$(ACTIONS): build = $(word 2, $(subst -, ,$@))
$(ACTIONS): install_name = $(product).$(target)$(config).$(build)
$(ACTIONS):
@echo %%% $(action) $(target)$(config)-$(product)-$(build)
@$(MAKE) -f makefiles/main.mk \
SUB_TARGET=$(target) \
CONFIG=$(config) \
PRODUCT=$(product) \
BUILD=$(build) \
ROM_IMAGE=$(ROM_IMAGE_$(config)) \
ROM_IMAGE_SIZE=64k \
INSTALL_NAME=$(install_name) \
DEVMAP_EPOCH=$(call DEVICEMAP_EPOCH_FOR_PLATFORM,$(target)) \
IMAGE_FORMAT=$(or $(call DEVICEMAP_IMGFMT_FOR_PLATFORM,$(target)), $(IMAGE_FORMAT)) \
CRYPTO_HASH=$(call DEVICEMAP_CRYPTO_HASH_FOR_PLATFORM,$(target)) \
$(action)
help:
@echo Valid TARGETS
@echo " $(VALID_TARGETS)"
@echo Valid CONFIGS
@echo " $(VALID_CONFIGS)"
@echo Valid BUILDS
@echo " $(VALID_BUILDS)"
@echo ""

View File

@ -0,0 +1,69 @@
# Copyright (C) 2007-2010, 2012-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.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
#
# Global headers that are independing of product, target, etc.
#
GLOBAL_HEADERS += \
lib/mib/mib_nodes.h
OPTIONS += \
READ_ONLY=1
ifeq ($(CONFIG),si)
TEXT_AREA ?= ROM
endif
ifeq ($(CONFIG),fpga)
TEXT_AREA ?= ROM
OPTIONS += \
SUPPORT_FPGA=1
endif
ifeq ($(BUILD),ROMRELEASE)
OPTIONS += \
DEBUG_LEVEL=DEBUG_SILENT \
NO_PANIC_STRINGS=1
endif
OPTIONS += \
IMAGE_MAX_COUNT=1
ALL_OBJS += \
$(LOCAL_DIR)/main.o
MODULES += \
lib/heap \
lib/random \
platform/defaults \
platform/generic \
sys
ifeq ($(IMAGE_FORMAT),)
$(error "IMAGE_FORMAT not set")
endif
ifeq ($(IMAGE_FORMAT),im4p)
MODULES += \
lib/image/image4
OPTIONS += \
WITH_UNTRUSTED_EXECUTION_ALLOWED=1
else
$(error "IMAGE_FORMAT not supported")
endif
LIBRARY_MODULES += \
lib/libc \
lib/mib
OPTIONS += \
WITH_DFU_MODE=1

View File

@ -0,0 +1,53 @@
# Copyright (C) 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.
#
###############################################################################
# Target configuration for the t8002 SecureROM module
#
PLATFORM := t8002
ARCH := arm
ifeq ($(CONFIG),fpga)
CONFIG_FPGA := true
endif
# Memory configuration (consumed by the t8002 platform module)
ifeq ($(TEXT_AREA),ROM)
TEXT_BANK := srom
TEXT_FOOTPRINT := 512*1024
endif
MODULES += \
platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/miu \
platform/$(PLATFORM)/pmgr \
drivers/apple/a7iop \
drivers/apple/aes_v2 \
drivers/apple/aic \
drivers/apple/anc \
drivers/apple/ausb \
drivers/apple/gpio \
drivers/apple/sep \
drivers/flash_nor/spi \
drivers/samsung/spi \
drivers/synopsys/usbotg
ifeq ($(BUILD),DEBUG)
MODULES += \
drivers/samsung/uart
OPTIONS += \
WITH_PLATFORM_UARTCONFIG=1
endif
LIBRARY_MODULES += \
lib/libcorecrypto

View File

@ -0,0 +1,55 @@
# Copyright (C) 2012-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.
#
###############################################################################
# Target configuration for the t7000 SecureROM module
#
PLATFORM := t8010
ARCH := arm64
HW_TIMER := architected
# Memory configuration (consumed by the t8010 platform module)
ifeq ($(TEXT_AREA),ROM)
TEXT_BANK := srom
TEXT_FOOTPRINT := 512*1024
endif
MODULES += \
platform/$(PLATFORM) \
platform/$(PLATFORM)/apcie \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/miu \
platform/$(PLATFORM)/pmgr \
drivers/apple/a7iop \
drivers/apple/aes_v2 \
drivers/apple/aic \
drivers/apple/apcie \
drivers/apple/ausb \
drivers/apple/ccc \
drivers/apple/dart_lpae \
drivers/apple/gpio \
drivers/apple/sep \
drivers/flash_nor/spi \
drivers/nvme \
drivers/pci \
drivers/samsung/spi \
drivers/synopsys/usbotg
ifeq ($(BUILD),DEBUG)
MODULES += \
drivers/samsung/uart
OPTIONS += \
WITH_PLATFORM_UARTCONFIG=1
endif
LIBRARY_MODULES += \
lib/libcorecrypto

View File

@ -0,0 +1,17 @@
# Copyright (C) 2012-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.
#
###############################################################################
# Target configuration for the T8010 SecureROM module
#
SUB_PLATFORM := t8010
include $(GET_LOCAL_DIR)/t8010-config-base.mk

371
apps/SecureROM/main.c Normal file
View File

@ -0,0 +1,371 @@
/*
* Copyright (C) 2007-2015 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 <arch.h>
#include <debug.h>
#include <platform.h>
#include <platform/memmap.h>
#include <sys.h>
#include <sys/boot.h>
#include <sys/task.h>
#include <lib/blockdev.h>
#include <lib/image.h>
#include <lib/mib_def.h>
#include <drivers/flash_nor.h>
#include <drivers/nvme.h>
#if WITH_USB_DFU
#include <drivers/usb/usb_public.h>
#endif
#if WITH_TBT_DFU
#include <drivers/thunderbolt/thunderboot.h>
#endif
#if WITH_ANC_BOOT
# include <drivers/anc_boot.h>
#endif
#define USER_BUTTON_FORCEDFU_TIMEOUT (6 * 1000 * 1000)
static void boot_selected(enum boot_device boot_device, u_int32_t boot_flag, u_int32_t boot_arg);
static struct image_info *lookup_image_in_bdev(const char *name, uint32_t type);
static bool load_selected_image(struct image_info *loaded_image, u_int32_t type, void *load_address, size_t load_length, u_int32_t boot_flag);
static void boot_loaded_image(void *load_address);
int _main(void)
{
bool force_dfu, force_reset;
utime_t dfu_time;
enum boot_device boot_device;
u_int32_t boot_flag, boot_arg;
int32_t index;
/* initialize the cpu */
arch_cpu_init(false);
dprintf(DEBUG_CRITICAL, "\n" CONFIG_PROGNAME_STRING " start\n");
/* setup the default clock configuration */
dprintf(DEBUG_INFO, "setting up initial clock configuration\n");
platform_init_setup_clocks();
/* set up any internal memory (internal sram) */
dprintf(DEBUG_INFO, "setting up internal memory\n");
platform_init_internal_mem();
/* set up the default pin configuration */
dprintf(DEBUG_INFO, "setting up default pin configuration\n");
platform_init_hwpins();
/* Sample force_dfu and request_dfu pins. */
force_dfu = platform_get_force_dfu();
force_reset = platform_get_request_dfu1() && platform_get_request_dfu2();
/* bring up system services (cpu, tasks, callout, heap) */
sys_init();
/* generate random stack cookie on platforms without a
requirement for fast resume from suspend-to-RAM via SecureROM */
#if !WITH_NO_RANDOM_STACK_COOKIE
sys_init_stack_cookie();
#endif
/* do some early initialization of hardware */
/* timers will start firing at this point */
dprintf(DEBUG_INFO, "doing early platform hardware init\n");
platform_early_init();
/* print version info now that UART is enabled on DEBUG builds */
dprintf(DEBUG_INFO, "\n\n%s for %s\n", CONFIG_PROGNAME_STRING, CONFIG_BOARD_STRING);
dprintf(DEBUG_INFO, "%s\n", build_tag_string);
dprintf(DEBUG_INFO, "%s\n", build_style_string);
/* initialize the rest of hardware */
dprintf(DEBUG_INFO, "doing platform hardware init\n");
platform_init();
/* Check for Force DFU Mode Pin */
dprintf(DEBUG_INFO, "Checking for Force DFU Mode Pin: %x / %x\n", force_dfu, force_reset);
if (force_dfu && !platform_get_usb_cable_connected())
force_dfu = false;
/* Check for Force DFU Mode request pins. Requesting DFU mode is done
by asserting both REQUEST_DFU1 and REQUEST_DFU2, holding them for
the required time, then deasserting REQUEST_DFU1, followed by
deasserting REQUEST_DFU2 after another required hold time.
*/
dprintf(DEBUG_INFO, "Checking for Force DFU Mode request pins: %x / %x\n", force_dfu, force_reset);
if (!force_dfu && force_reset && platform_get_usb_cable_connected()) {
dfu_time = system_time();
while (platform_get_request_dfu1() && platform_get_request_dfu2() && platform_get_usb_cable_connected()) {
if (time_has_elapsed(dfu_time, USER_BUTTON_FORCEDFU_TIMEOUT))
break;
task_sleep(100 * 1000);
}
dfu_time = system_time();
while (!platform_get_request_dfu1() && platform_get_request_dfu2() && platform_get_usb_cable_connected()) {
if (time_has_elapsed(dfu_time, USER_BUTTON_FORCEDFU_TIMEOUT)) {
force_dfu = true;
break;
}
task_sleep(100 * 1000);
}
}
/* Try the boot devices in order */
index = 0;
/* if DFU is being forced, set index to -1 for force dfu */
dprintf(DEBUG_INFO, "Force DFU: %x\n", force_dfu);
if (force_dfu) index = -1;
while (1) {
/* ask the platform code for the selected boot source */
if (!platform_get_boot_device(index, &boot_device, &boot_flag, &boot_arg))
break;
/* Attempt to boot the selected device */
boot_selected(boot_device, boot_flag, boot_arg);
if (index < 0)
continue;
index++;
}
/* we are hosed */
dprintf(DEBUG_INFO, "No valid boot device, resetting.\n");
platform_reset(false);
}
/*
* Attempt to boot the seleced device.
*/
static void
boot_selected(enum boot_device boot_device, uint32_t boot_flag, uint32_t boot_arg)
{
struct image_info *loaded_image;
size_t loaded_length;
void *load_address;
size_t load_length;
bool loaded = false;
uint32_t type = IMAGE_TYPE_LLB;
dprintf(DEBUG_INFO, "boot_selected: boot_device: %08x, boot_flag: %08x, boot_arg: %08x\n",
boot_device, boot_flag, boot_arg);
/* Enable the pins for the selected boot device */
platform_enable_boot_interface(true, boot_device, boot_arg);
/* reset security and clear the insecure memory area */
security_init(true);
loaded_image = NULL;
loaded_length = 0;
load_address = (void *)INSECURE_MEMORY_BASE;
load_length = INSECURE_MEMORY_SIZE;
/* ask the boot source to fetch the boot image */
switch (boot_device) {
#if WITH_HW_FLASH_NOR && WITH_HW_FLASH_NOR_SPI
case BOOT_DEVICE_SPI:
{
dprintf(DEBUG_INFO, "SPI NOR boot selected\n");
/* initialise the NOR flash for the desired channel */
flash_nor_init(boot_arg);
loaded_image = lookup_image_in_bdev("nor0", type);
if (loaded_image != NULL)
loaded_length = loaded_image->imageLength;
break;
}
#endif /* WITH_HW_FLASH_NOR && WITH_HW_FLASH_NOR_SPI */
#if WITH_ANC_BOOT
case BOOT_DEVICE_NAND:
{
if (!anc_reset(boot_arg)) {
dprintf(DEBUG_CRITICAL, "Failed to init ASP");
break;
}
loaded_length = anc_read_llb(load_address, load_length);
if (loaded_length > load_length)
panic("load overflow");
if (loaded_length > 0) {
if ((loaded_image = image_create_from_memory(
load_address,
loaded_length,
IMAGE_OPTION_LOCAL_STORAGE)) == NULL) {
dprintf(DEBUG_INFO, "image creating failed\n");
} else {
dprintf(DEBUG_INFO, "loaded image from ANC\n");
}
}
break;
}
#endif
#if WITH_NVME
case BOOT_DEVICE_NVME:
{
loaded_image = lookup_image_in_bdev("nvme_firmware0", type);
if (loaded_image != NULL)
loaded_length = loaded_image->imageLength;
break;
}
#endif
#if WITH_USB_DFU
case BOOT_DEVICE_USBDFU:
{
int result;
/* Turn on the DFU STATUS pin */
platform_set_dfu_status(true);
/* Set type to iBSS */
type = IMAGE_TYPE_IBSS;
/* try to get an image via DFU */
if ((result = getDFUImage(load_address, load_length)) < 0) {
dprintf(DEBUG_INFO, "fatal DFU download error");
break;
}
loaded_length = (size_t)result;
if (loaded_length > load_length)
panic("load overflow");
/* wrap the image */
if ((loaded_image = image_create_from_memory(load_address, loaded_length, 0)) == NULL) {
dprintf(DEBUG_INFO, "failed to create image from DFU download\n");
break;
}
dprintf(DEBUG_INFO, "loaded image from USB-DFU\n");
break;
}
#endif /* WITH_USB_DFU */
#if WITH_TBT_DFU
case BOOT_DEVICE_TBTDFU:
{
int result;
/* Set type to iBSS */
type = IMAGE_TYPE_IBSS;
/* try to get an image via DFU */
if ((result = thunderboot_get_dfu_image(load_address, load_length)) < 0) {
dprintf(DEBUG_INFO, "fatal DFU download error");
break;
}
loaded_length = (size_t)result;
if (loaded_length > load_length)
panic("load overflow");
/* wrap the image */
if ((loaded_image = image_create_from_memory(load_address, loaded_length, 0)) == NULL) {
dprintf(DEBUG_INFO, "failed to create image from DFU download\n");
break;
}
dprintf(DEBUG_INFO, "loaded image from TBT-DFU\n");
break;
}
#endif /* WITH_USB_DFU */
/* case BOOT_DEVICE_XMODEM: */
default:
break;
}
/* if we found an image, try to load it */
if (loaded_image) {
loaded = load_selected_image(loaded_image, type, load_address, loaded_length, boot_flag);
image_free(loaded_image);
} else {
loaded = false;
}
/* Disable the pins for the selected boot device */
platform_enable_boot_interface(false, boot_device, boot_arg);
if (loaded)
boot_loaded_image(load_address);
/* load failed or boot source not supported */
dprintf(DEBUG_INFO, "failed to load from selected boot device\n");
}
static struct image_info *
lookup_image_in_bdev(const char *name, uint32_t type)
{
struct blockdev *boot_bdev;
struct image_info *loaded_image;
/* look it up */
if ((boot_bdev = lookup_blockdev(name)) == NULL)
return NULL;
dprintf(DEBUG_INFO, "boot device %s initialised\n", name);
/* look for the image directory in the expected location */
if (image_search_bdev(boot_bdev, 0, IMAGE_OPTION_LOCAL_STORAGE) == 0)
return NULL;
dprintf(DEBUG_INFO, "found image directory on %s\n", name);
/* look for the LLB */
loaded_image = image_find(type);
dprintf(DEBUG_INFO, "%s LLB image on %s\n", loaded_image != NULL ? "found" : "failed to find", name);
return loaded_image;
}
static bool
load_selected_image(struct image_info *loaded_image, u_int32_t type, void *load_address, size_t load_length, u_int32_t boot_flag)
{
/* image epoch must be greater than or equal to the SecureROM's epoch */
loaded_image->imageOptions |= IMAGE_OPTION_GREATER_EPOCH;
/* image decode library make use of this information to do various validation on image */
loaded_image->imageOptions |= IMAGE_OPTION_NEW_TRUST_CHAIN;
/* if test mode isn't set, we require the image be trusted, regardless of the security fuse */
if (!(boot_flag & BOOT_FLAG_TEST_MODE))
loaded_image->imageOptions |= IMAGE_OPTION_REQUIRE_TRUST;
/* validate the boot image */
if (image_load(loaded_image, &type, 1, NULL, &load_address, &load_length)) {
dprintf(DEBUG_INFO, "image load failed\n");
return false;
}
return true;
}
static void
boot_loaded_image(void *load_address)
{
/* consolidate environment post image validation */
security_consolidate_environment();
#if WITH_SIDP
/* Seal the ROM manifest for Silicon Data Protection */
security_sidp_seal_rom_manifest();
#endif
dprintf(DEBUG_CRITICAL, "executing image...\n");
prepare_and_jump(BOOT_UNKNOWN, (void *)load_address, NULL);
}

View File

@ -0,0 +1,118 @@
#!/bin/bash
set -e
function usage()
{
echo "build_rom_test.sh -f -p <platform> [-a <gpio address>] [-v <gpio value>]"
echo " -f: Force rebuild rather than using pre-built / audited binaries"
exit 1
}
args=$(getopt "fa:p:v" $*)
if [[ $? != 0 ]]; then
usage
fi
FORCE_REBUILD=0
GPIO_BASE=0x47500000
GPIO_VALUE=0x70203
PLATFORM="t8002"
set -- $args
for arg; do
case $arg
in
-a)
GPIO_BASE=$2; shift; shift;;
-v)
GPIO_VALUE=$2; shift; shift;;
-p)
PLATFORM=$2; shift; shift;;
-f)
FORCE_REBUILD=1; shift;;
esac
done
if [[ "$GPIO_BASE" == "" || "$GPIO_VALUE" == "" || "$PLATFORM" == "" ]]; then
usage
fi
if [[ $FORCE_REBUILD != 0 ]]; then
echo "Cleaning"
rm -f rom_test.o rom_test rom_test.bin rom_test_*.im4p
fi
if ! [[ -f rom_test.o ]]; then
echo "Running clang"
xcrun -sdk iphoneos.internal clang -arch armv7 \
-static -marm \
-std=gnu99 -Werror -W -Wall \
-DGPIO_BASE=${GPIO_BASE} -DGPIO_VALUE=${GPIO_VALUE} \
-c rom_test.c -o rom_test.o
fi
if ! [[ -f rom_test ]]; then
echo "Standalone link"
xcrun -sdk iphoneos.internal clang -arch armv7 \
-static -marm \
-Wl,-new_linker \
-Wl,-preload \
-Wl,-merge_zero_fill_sections \
-nodefaultlibs \
-nostartfiles \
-dead_strip \
-e _start \
-o rom_test rom_test.o
fi
if ! [[ -f rom_test.bin ]]; then
echo "Finding __text section"
text_offset=$(xcrun -sdk iphoneos.internal otool -l rom_test | grep -A4 'sectname __text' | grep 'offset' | awk '{print $2}')
text_size=$(xcrun -sdk iphoneos.internal otool -l rom_test | grep -A4 'sectname __text' | grep 'size' | awk '{print $2}')
echo "Found at $text_offset size $text_size"
echo "Generating bin"
dd if=rom_test ibs=1 skip=${text_offset} obs=1 count=${text_size} \
>rom_test.bin 2>/dev/null
echo "Mach-O dump"
xcrun -sdk iphoneos.internal otool -tVj rom_test
echo "Bin Dump"
hexdump -C rom_test.bin
fi
for type in ibss illb; do
for encrypt in encrypted unencrypted; do
if ! [[ -f rom_test_${type}_${encrypt}.im4p ]]; then
echo "Generating ${encrypt} ${type} Image4 payload"
xcrun -sdk iphoneos.internal img4payload \
-i rom_test.bin -o rom_test_${type}_${encrypt}.im4p \
-t ${type} -v rom_test_${type}_${encrypt}
if [ "${encrypt}" = "encrypted" ]; then
xcrun -sdk iphoneos.internal img4encrypt \
-i rom_test_${type}_${encrypt}.im4p \
-o rom_test_${type}_${encrypt}.im4p \
-p $PLATFORM
fi
fi
done
done
# Personalization, for reference:
#xcrun -sdk iphoneos.internal personalize_img4 -i rom_test_ibss.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -e 0x12345678ABCD -n 0x9A7A07F30FE84B6403AF5BB549B405BAE16461D2 -r 0x12345678AABBCCDD
#xcrun -sdk iphoneos.internal personalize_img4 -i rom_test_llb.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -e 0x12345678ABCD -n 0x9A7A07F30FE84B6403AF5BB549B405BAE16461D2 -r 0x12345678AABBCCDD
# Production signing, for reference:
#xcrun -sdk iphoneos.internal personalize_img4 -m -p -i rom_test_illb_encrypted.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -e 0x12345678ABCD -n 0xcd00fc2e1fa023796d5257b853880e18cd384f7d -s 0x1234567823456789345678904567890156789012 -r 0x12345678AABBCCDD -w -W
# Production signing with demotion, for reference:
#xcrun -sdk iphoneos.internal personalize_img4 -m -p -i rom_test_illb_encrypted.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -e 0x12345678ABCD -n 0xcd00fc2e1fa023796d5257b853880e18cd384f7d -s 0x1234567823456789345678904567890156789012 -r 0x12345678AABBCCDD -W -D 1
echo "Done"

View File

@ -0,0 +1,5 @@
void start(void)
{
*((volatile unsigned int *)GPIO_BASE) = GPIO_VALUE;
for(;;);
}

View File

@ -0,0 +1,107 @@
#!/bin/bash
set -e
function usage()
{
echo "build_rom_test.sh -p <platform> [-a <gpio address>] [-v <gpio value>]"
exit 1
}
args=$(getopt "a:p:v" $*)
if [[ $? != 0 ]]; then
usage
fi
# GPIO[0] == GPIO id 20 == 0x20f100050
GPIO_BASE=0x20f100050
GPIO_VALUE=0x70203
PLATFORM="t8010"
set -- $args
for arg; do
case $arg
in
-a)
GPIO_BASE=$2; shift; shift;;
-v)
GPIO_VALUE=$2; shift; shift;;
-p)
PLATFORM=$2; shift; shift;;
esac
done
if [[ "$GPIO_BASE" == "" || "$GPIO_VALUE" == "" || "$PLATFORM" == "" ]]; then
usage
fi
echo "Cleaning"
rm -f rom_test.o rom_test rom_test.bin rom_test_*.im4p
echo "Running clang"
xcrun -sdk iphoneos.internal clang -arch arm64 \
-static \
-std=gnu99 -Werror -W -Wall \
-DGPIO_BASE=${GPIO_BASE} -DGPIO_VALUE=${GPIO_VALUE} \
-c rom_test.c -o rom_test.o
echo "Standalone link"
xcrun -sdk iphoneos.internal clang -arch arm64 \
-static \
-Wl,-new_linker \
-Wl,-preload \
-Wl,-merge_zero_fill_sections \
-nodefaultlibs \
-nostartfiles \
-dead_strip \
-e _start \
-o rom_test rom_test.o
echo "Stripping"
xcrun -sdk iphoneos.internal strip -no_uuid rom_test
echo "Finding __text section"
text_offset=$(xcrun -sdk iphoneos.internal otool -l rom_test | grep -A4 'sectname __text' | grep 'offset' | awk '{print $2}')
text_size=$(xcrun -sdk iphoneos.internal otool -l rom_test | grep -A4 'sectname __text' | grep 'size' | awk '{print $2}')
echo "Found at $text_offset size $text_size"
echo "Generating bin"
dd if=rom_test ibs=1 skip=${text_offset} obs=1 count=${text_size} \
>rom_test.bin 2>/dev/null
echo "Mach-O dump"
xcrun -sdk iphoneos.internal otool -tVj rom_test
echo "Bin Dump"
hexdump -C rom_test.bin
for type in ibss illb; do
for encrypt in encrypted unencrypted; do
echo "Generating ${encrypt} ${type} Image4 payload"
xcrun -sdk iphoneos.internal img4payload \
-i rom_test.bin -o rom_test_${type}_${encrypt}.im4p \
-t ${type} -v rom_test_${type}_${encrypt}
if [ "${encrypt}" = "encrypted" ]; then
xcrun -sdk iphoneos.internal img4encrypt \
-i rom_test_${type}_${encrypt}.im4p \
-o rom_test_${type}_${encrypt}.im4p \
-p $PLATFORM
fi
done
done
# Personalization, for reference:
#xcrun -sdk iphoneos.internal personalize_img4 -i rom_test_ibss.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -g 384 -e 0x12345678ABCD -n 0xa4f84e2a89c2ef04a07b9f948fec7c0767c8931bd67ede924040aa4f30afe4118a2d524d687fc0db2f4a27e36c7e75a0 -r 0x12345678AABBCCDD
#xcrun -sdk iphoneos.internal personalize_img4 -i rom_test_llb.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -g 384 -e 0x12345678ABCD -n 0xa4f84e2a89c2ef04a07b9f948fec7c0767c8931bd67ede924040aa4f30afe4118a2d524d687fc0db2f4a27e36c7e75a0 -r 0x12345678AABBCCDD
# Production signing, for reference:
#xcrun -sdk iphoneos.internal personalize_img4 -m -p -i rom_test_illb_encrypted.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -g 384 -e 0x12345678ABCD -n 0xa4f84e2a89c2ef04a07b9f948fec7c0767c8931bd67ede924040aa4f30afe4118a2d524d687fc0db2f4a27e36c7e75a0 -s 0x1234567823456789345678904567890156789012 -r 0x12345678AABBCCDD -w -W
# Production signing with demotion, for reference:
#xcrun -sdk iphoneos.internal personalize_img4 -m -p -i rom_test_illb_encrypted.im4p -o /tmp -c 0x<chip id> -b 0xff -d 1 -g 384 -e 0x12345678ABCD -n 0xa4f84e2a89c2ef04a07b9f948fec7c0767c8931bd67ede924040aa4f30afe4118a2d524d687fc0db2f4a27e36c7e75a0 -s 0x1234567823456789345678904567890156789012 -r 0x12345678AABBCCDD -W -D 1
echo "Done"

View File

@ -0,0 +1,5 @@
void start(void)
{
*((volatile unsigned int *)GPIO_BASE) = GPIO_VALUE;
for(;;);
}

Binary file not shown.

View File

@ -0,0 +1 @@
0<EFBFBD>½IM4Pibssrom_test_ibss_encrypted A°ö_ÊHþ€7:@´5bJûì=}ôI¸8Y­t0r07ìȃ‡µÿ¯ƒHêo9Árø ÈcTlÅT71³¸Ž½!‚ƒû*¢³ØÐSR#祧>¡07w@#T<>Ú±ÅsRÝ“£eÒ ÇÃûñ^&ç7tÝ0M¸Ý“SÒãùeñd ûå

View File

@ -0,0 +1 @@
0<EFBFBD>½IM4Pillbrom_test_illb_encrypted C%œüQ ŠØ*Öþj6Îø<C38E>;P§†Ø3„ŸpLœ@ƒt0r07'ÀþÍ Bì>.É™ŠÈÎö æHˆ¿ÓCË`«‹ iU°š<C2B0>˜-SÕËcú{ 5æ^«07µ<>#4]l<Gï!—rôÛ;i "¦ÉÑûª mtšvàŽ0$x}Û§+T#@ú)<29>

Binary file not shown.

View File

@ -0,0 +1 @@
0<EFBFBD>½IM4Pillbrom_test_illb_encrypted ét>8ŒàÀÖ2qˆóV¤6 >ƒÓ$ÍI.ôTæP$<t0r07<džÃhqq O<>6Ú³Åï1 ßa«…úZy˜L‰ !ÞI~`÷ZžVL3Ö)·H tw07.Ñ\¦ödô2w \O‡á &¯À?”XùlЈºŠ›ö<“Ñüÿ;ŸÆËzoÈ…ˆ

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,924 @@
#!/usr/bin/python2.7
import optparse
import os
import plistlib
import textwrap
import shutil
import subprocess
import sys
import time
import hashlib
import binascii
# ECID: This is the value passed on the personalize_img4 -e option.
kDefaultEcid = 0x000012345678abcd
# BNCN: This is the value passed on the personalize_img4 -r option.
kDefaultApNonce = 0x12345678aabbccdd
# Old fashioned terminal size.
kLogWidth = 78
kOutputDirPrefix = 'test.'
kLogFileName = 'README.txt'
kDefaultPlatform = 't8002'
kDefaultChipId = 0x8002
kDefaultBoardId = 0x3f
kDefaultBlinkyGPIOReg = '0x47500000'
kDefaultBlinkyGPIOValue = '0x70203'
kDefaultDFUGPIOReg = '0x47500220'
kDefaultDFUGPIOValue = '0x00478283'
kBoardIdStrapBits = 4
kSecurityDomainDarwin = 1
# The following calculates the nonce hash and formats it correctly so that a
# person doesn't have to deal with generating the a hash, truncating it, and
# getting the endian-ness right.
kDefaultApNonceHashTruncatedSize = 256
kDefaultApNonceByteArray = bytearray.fromhex(hex(kDefaultApNonce)[2:])
kDefaultApNonceString = binascii.hexlify(kDefaultApNonceByteArray)
kDefaultApNonceLittleEndian = int("".join(reversed([kDefaultApNonceString[i:i+2] for i in range(0, len(kDefaultApNonceString), 2)])), 16)
kDefaultApNonceHash384 = hashlib.sha384(kDefaultApNonceByteArray).hexdigest()
kDefaultApNonceHash = int(kDefaultApNonceHash384[:kDefaultApNonceHashTruncatedSize/4], 16)
kDefaultSepNonceHash = 0x1234567823456789345678904567890156789012
# <rdar://problem/15199519> certificate epoch for server signing fixed at 1
kDefaultEpochForServerSigning = 1
# Supported platforms
kSupported_platforms = ['t8002']
# Image sources
kImageSourceDfu = 1
kImageSourceNand = 2
kImageSourceNor = 3
# Outcomes
kOutcomeBlinky = 1
kOutcomeDfu = 2
kOutcomeBlinkyVcoOverride = 3
kOutcomeBlinkyVcoDefault = 4
# Can be overidden by command line options.
kImg4DecodeTestTool = 'xcrun -sdk iphoneos.internal -run img4decodetest'
kPersonalizeImg4Tool = 'xcrun -sdk iphoneos.internal -run personalize_img4'
kBfnMaker = 'xcrun -sdk iphoneos.internal -run bfn_maker'
kBfnMakerPageSize = 8224
kBfnMakerFormat = 'h6'
def ErrorText(text):
RedColor = '\033[91m'
NormalColor = '\033[0m'
return RedColor + text + NormalColor
def WarningText(text):
YellowColor = '\033[93m'
NormalColor = '\033[0m'
return YellowColor + text + NormalColor
def PassText(text):
GreenColor = '\033[92m'
NormalColor = '\033[0m'
return GreenColor + text + NormalColor
def ByteSwap(value, bits):
assert (bits % 8) == 0
result = 0
for i in xrange(0, bits / 8):
result <<= 8
result |= value & 0xff
value >>= 8
return result
class LogSettings:
def __init__(self):
self._all_settings = []
def Append(self, text, settings):
self._all_settings.append((text, settings))
def Log(self, log):
# Get a format string which aligns everything.
longest_entry = 0
for settings in self._all_settings:
for s in settings[1]:
if len(s[0]) > longest_entry:
longest_entry = len(s[0])
format = ' %%-%ds %%s\n' % longest_entry
# Dump all settings.
for settings in self._all_settings:
log.write(settings[0] + ':\n')
for s in settings[1]:
if (type(s[1]) == int) or (type(s[1]) == long):
if s[1] < 16:
out = format % (s[0], '%d' % s[1])
else:
out = format % (s[0], '0x%x' % s[1])
else:
out = format % (s[0], str(s[1]))
log.write(out)
log.write('\n')
class PersonalizeImg4:
def __init__(self,
description = None,
manifest = True,
platform = kDefaultPlatform,
options = None,
log = None,
test_number = None,
outcome = None,
image_source = None,
fuse_production_status = None,
fuse_security_mode = None,
fuse_security_domain = kSecurityDomainDarwin,
fuse_epoch = None,
fuse_ecid = kDefaultEcid,
fuse_cfg_fuse9 = None,
boot_device = None,
test_mode = False,
boot_device_empty = False,
epoch = kDefaultEpochForServerSigning,
chip_id = None,
board_id = None,
force_dfu = 0,
ecid = None,
production_status = None,
security_mode = None,
security_domain = kSecurityDomainDarwin,
allow_mix_and_match = None,
ap_boot_nonce_hash = None,
sep_boot_nonce_hash = None,
enable_keys = None,
encryption = None,
crypto_hash_method = 'sha2-384',
restore_info_nonce = None,
demote_production = None,
demote_secure = None,
lock_fuses = None,
):
# Validate required arguments.
assert options is not None
assert log is not None
assert type(test_number) == int
assert outcome in [kOutcomeBlinky, kOutcomeDfu, kOutcomeBlinkyVcoOverride, kOutcomeBlinkyVcoDefault]
assert image_source in [kImageSourceDfu, kImageSourceNand, kImageSourceNor]
assert boot_device is not None
assert type(boot_device_empty) == bool
assert type(fuse_production_status) == bool
assert type(fuse_security_mode) == bool
assert ((type(fuse_security_domain) == int) and
(fuse_security_domain >= 0) and (fuse_security_domain <= 3))
assert ((type(fuse_epoch) == int) and
(fuse_epoch >= 0) and (fuse_epoch <= 0x7f))
assert ((fuse_ecid is not None) and
(fuse_ecid >= 0) and (fuse_ecid < (1 << 64)))
if manifest:
assert ((type(epoch) == int) and
(epoch >= 0) and (epoch <= 0x7f))
assert ((type(chip_id) == int) and
(chip_id >= 0) and (chip_id < (1 << 16)))
assert ((type(board_id) == int) and
(board_id >= 0) and (board_id < (1 << 8)))
assert (ecid is not None) and (ecid >= 0) and (ecid < (1 << 64))
assert type(production_status) == bool
assert type(security_mode) == bool
assert ((type(security_domain) == int) and
(security_domain >= 0) and (security_domain <= 3))
assert type(allow_mix_and_match) == bool
assert type(enable_keys) == bool
assert type(encryption) == bool
if sep_boot_nonce_hash is None:
sep_boot_nonce_hash = kDefaultSepNonceHash
# Set members.
self._description = description
self._manifest = manifest
self._platform = platform
self._images_dir = 'images_{}'.format(platform)
self._options = options
self._log = log
self._test_number = test_number
self._outcome = outcome
self._image_source = image_source
self._fuse_production_status = fuse_production_status
self._fuse_security_mode = fuse_security_mode
self._fuse_security_domain = fuse_security_domain
self._fuse_epoch = fuse_epoch
self._fuse_ecid = fuse_ecid
if fuse_cfg_fuse9 is None:
self._fuse_cfg_fuse9 = '0x00000000'
else:
assert (type(fuse_cfg_fuse9) == int)
self._fuse_cfg_fuse9 = '0x%08x' % fuse_cfg_fuse9
self._epoch = epoch
self._chip_id = chip_id
self._board_id = board_id
self._boot_device = boot_device
self._test_mode = test_mode
self._boot_device_empty = boot_device_empty
self._force_dfu = force_dfu
self._ecid = ecid
self._production_status = production_status
self._security_mode = security_mode
self._security_domain = security_domain
self._allow_mix_and_match = allow_mix_and_match
self._ap_boot_nonce_hash = ap_boot_nonce_hash
self._sep_boot_nonce_hash = sep_boot_nonce_hash
self._enable_keys = enable_keys
self._encryption = encryption
self._crypto_hash_method = crypto_hash_method
self._restore_info_nonce = restore_info_nonce
self._demote_production = demote_production
self._demote_secure = demote_secure
self._lock_fuses = lock_fuses
def Personalize(self, im4p, output_dir):
# Form args.
if self._options.personalize_img4:
args = self._options.personalize_img4.split(' ')
else:
args = kPersonalizeImg4Tool.split(' ')
args += ['-i', im4p, '-o', output_dir]
if not self._manifest:
args += ['-O']
else:
if self._chip_id is not None:
args += ['-c', '0x%04x' % self._chip_id]
if self._board_id is not None:
args += ['-b', '0x%x' % self._board_id]
if self._ecid is not None:
args += ['-e', '0x%x' % self._ecid]
if self._production_status:
args += ['-p']
if self._security_mode:
args += ['-m']
args += ['-W']
if self._security_domain is not None:
args += ['-d', '%d' % self._security_domain]
if self._allow_mix_and_match:
args += ['-a']
if self._ap_boot_nonce_hash is not None:
args += ['-n', '0x%x' % self._ap_boot_nonce_hash]
if self._sep_boot_nonce_hash is not None:
args += ['-s', '0x%x' % self._sep_boot_nonce_hash]
if self._crypto_hash_method == 'sha2-384':
args += ['-g 384']
# disabling keys or demotions require a plist file with Trusted, EPRO, and ESEC defined
if (not self._enable_keys) or (self._demote_production) or (self._demote_secure):
object_properties = {'Trusted': self._enable_keys}
if self._demote_production is not None:
object_properties['DPRO'] = True
if self._demote_production:
assert self._production_status == True
object_properties['EPRO'] = False
else:
object_properties['EPRO'] = self._production_status
if self._demote_secure:
assert self._security_mode == True
object_properties['ESEC'] = False
else:
object_properties['ESEC'] = self._security_mode
all_properties = {'OBJP': object_properties}
plist_str = plistlib.writePlistToString(all_properties)
plist_file = '/tmp/personalize-{}.plist'.format(self._test_number)
f = open(plist_file, 'w')
f.write(plist_str)
f.close()
args += ['-f', plist_file]
# personalize_img4 tool expects RestoreInfo nonce in big endian (only for LLB)
if (im4p.find('ibss') == -1) and self._restore_info_nonce is not None:
args += ['-r', '0x%x' % ByteSwap(self._restore_info_nonce, 64)]
args += ['-X']
# Convert args to an shell string.
invoke_string = ' '.join(args)
# Execute personalize_img4 utility.
print('Invoking: %s' % invoke_string)
pipe = subprocess.Popen(invoke_string, shell=True)
if pipe.wait() == 0:
return True
else:
self._log.write('Invocation of personalize_img4 failed\n')
sys.stderr.write(ErrorText('Test %d: Invocation of personalize_img4 failed\n' % self._test_number))
return False
def DecodeTest(self, img4):
if self._options.img4decodetest:
args = self._options.img4decodetest.split(' ')
else:
args = kImg4DecodeTestTool.split(' ')
args += ['-c', '-i', img4]
if self._crypto_hash_method == 'sha2-384':
args += ['-n']
invoke_string = ' '.join(args)
print('Invoking: %s' % invoke_string)
pipe = subprocess.Popen(invoke_string, shell=True)
if pipe.wait() == 0:
return True
else:
self._log.write('Invocation of img4decodetest failed\n')
sys.stderr.write(ErrorText('Test %d: Invocation of img4decodetest failed\n' % self._test_number))
return False
def BfnMaker(self, input, output):
if self._options.bfn_maker:
args = self._options.bfn_maker.split(' ')
else:
args = kBfnMaker.split(' ')
args += ['--pageSize', str(kBfnMakerPageSize)]
args += ['--format', str(kBfnMakerFormat)]
args += [input, output]
invoke_string = ' '.join(args)
pipe = subprocess.Popen(invoke_string, shell=True)
if pipe.wait() == 0:
return True
else:
self._log.write('Invocation of bfn_maker failed\n')
sys.stderr.write(ErrorText('Test %d: Invocation of bfn_maker failed\n' % self._test_number))
return False
def Execute(self):
# Log header.
self._log.write('-' * kLogWidth + '\n')
self._log.write('Test %d\n' % self._test_number)
self._log.write(textwrap.fill(self._description, width=kLogWidth) + '\n\n')
# Figure out the input file variant.
image_type, location = {kImageSourceDfu: ('ibss', 'DFU'),
kImageSourceNand: ('illb', 'NAND'),
kImageSourceNor: ('illb', 'NOR')}[self._image_source]
# FAST_NOR boot device is currenly only supported for test mode
if self._boot_device in ['FAST_NOR', 'SLOW_NOR']:
if self._test_mode == False:
self._log.write(' Skipping: FAST_NOR/SLOW_NOR boot device requires test mode\n\n')
sys.stderr.write(WarningText('Test %d: FAST_NOR/SLOW_NOR boot device requires test mode\n' % self._test_number))
return False
if self._boot_device == 'NOR':
self._boot_config = '0x%x' % (0 + (1 if self._test_mode else 0))
elif self._boot_device == 'NAND':
self._boot_config = '0x%x' % (2 + (1 if self._test_mode else 0))
elif self._boot_device == 'SLOW_NOR':
self._boot_config = '0x6'
elif self._boot_device == 'FAST_NOR':
self._boot_config = '0x7'
else:
assert 0, '_boot_device ' + self._boot_device + ' is unsupported'
if self._encryption:
encryption_name = 'encrypted'
else:
encryption_name = 'unencrypted'
im4p_basename = 'rom_test_%s_%s' % (image_type, encryption_name)
im4p_filename = im4p_basename + '.im4p'
im4p_path = os.path.join(self._images_dir, im4p_filename)
# If valid image in boot device, but force_dfu is set, we also need ibss
if self._force_dfu and self._boot_device_empty == False:
dfu_im4p_basename = 'rom_test_ibss_%s' % encryption_name
dfu_im4p_filename = dfu_im4p_basename + '.im4p'
dfu_im4p_path = os.path.join(self._images_dir, dfu_im4p_filename)
# Output filename.
output_dir = kOutputDirPrefix + self._platform
pretty_filename_base = 'test_%d_%s_%s' % (
self._test_number, image_type, encryption_name)
pretty_filename_img4 = pretty_filename_base + '.img4'
pretty_path_base = os.path.join(output_dir, pretty_filename_base)
pretty_path_img4 = pretty_path_base + '.img4'
# If valid image in boot device, but force_dfu is set, we also need ibss
if self._force_dfu and self._boot_device_empty == False:
pretty_dfu_filename_base = 'test_%d_ibss_%s' % (
self._test_number, encryption_name)
pretty_dfu_filename_img4 = pretty_dfu_filename_base + '.img4'
pretty_dfu_path_base = os.path.join(output_dir, pretty_dfu_filename_base)
pretty_dfu_path_img4 = pretty_dfu_path_base + '.img4'
# Remove old output file if it exists.
img4 = os.path.join(output_dir, im4p_basename + '.img4')
if self._force_dfu and self._boot_device_empty == False:
dfu_img4 = os.path.join(output_dir, dfu_im4p_basename + '.img4')
try:
os.unlink(img4)
if dfu_img4 is not None:
os.unlink(dfu_img4)
except:
pass
# Personalize this case.
print('Personalize %s -> %s' % (im4p_filename, img4))
if not self.Personalize(im4p_path, output_dir):
sys.stderr.write(ErrorText('Test %d failed to generate\n' % self._test_number))
# Log failure
self._log.write(' Failed to generate!\n\n')
return False
# Now, if force_dfu is set, personalize the 2nd image
if self._force_dfu and self._boot_device_empty == False:
print('Personalize %s -> %s' % (dfu_im4p_filename, dfu_img4))
if not self.Personalize(dfu_im4p_path, output_dir):
sys.stderr.write(ErrorText('Test %d failed to generate\n' % self._test_number))
# Log failure
self._log.write(' Failed to generate!\n\n')
return False
# Rename the output file to the path we want for this test case.
os.rename(img4, pretty_path_img4)
print('Test %d generated %s' % (self._test_number, pretty_filename_img4))
if self._force_dfu and self._boot_device_empty == False:
os.rename(dfu_img4, pretty_dfu_path_img4)
print('Test %d generated %s' % (self._test_number, pretty_dfu_filename_img4))
# Test that there isn't a certificate vs manifest problem, because
# it's not worth burning a huge amount of DV time running it.
if self._manifest:
if not self.DecodeTest(pretty_path_img4):
self._log.write(' Failed decode test!\n')
self._log.write(' Check manifest vs cert is compatible\n\n')
sys.stderr.write(ErrorText('Test %d: Failed decode test\n' % self._test_number))
return False
if self._force_dfu and self._boot_device_empty == False:
if not self.DecodeTest(pretty_dfu_path_img4):
self._log.write(' Failed decode test!\n')
self._log.write(' Check manifest vs cert is compatible\n\n')
sys.stderr.write(ErrorText('Test %d: Failed decode test\n' % self._test_number))
return False
# If the source is NAND, we need to pass it through bfn_maker
if self._image_source == kImageSourceNand:
nand_filename = pretty_filename_base + '.nand'
nand_path = os.path.join(output_dir, nand_filename)
if not self.BfnMaker(pretty_path_img4, nand_path):
self._log.write(' Failed to create NAND image\n')
sys.stderr.write(ErrorText('Test %d: Failed to create NAND image\n' % self._test_number))
return False
pretty_filename = nand_filename
else:
pretty_filename = pretty_filename_img4
# Log success.
settings = LogSettings()
settings.Append(
'Fuses',
[('Production Status', int(self._fuse_production_status)),
('Security Mode', int(self._fuse_security_mode)),
('Security Domain', self._fuse_security_domain),
('Board ID', '0x%x' % ((self._board_id or kDefaultBoardId) >> kBoardIdStrapBits)),
('Epoch', self._fuse_epoch),
('SE Required', 0),
('ECID_LO', '0x%08x' % (kDefaultEcid & 0xffffffff)),
('ECID_HI', '0x%08x' % ((kDefaultEcid >> 32) & 0xffffffff)),
('CFG_FUSE9', self._fuse_cfg_fuse9),
])
settings.Append(
'Straps',
[('Boot-device', self._boot_device),
('Test mode', self._test_mode),
('Boot-config', self._boot_config),
('Board-id', (self._board_id or kDefaultBoardId) & ((1 << kBoardIdStrapBits) - 1)),
('Force-dfu', self._force_dfu),
('Boot Device Empty', self._boot_device_empty),
])
if self._force_dfu and self._boot_device_empty == False:
pretty_dfu_filename = pretty_dfu_filename_img4
else:
pretty_dfu_filename = ''
settings.Append(
'Images',
[(pretty_filename, ''),
(pretty_dfu_filename, ''),
])
outcome_string = {kOutcomeBlinky: 'Blinky - AP writes {} to {}'.format(kDefaultBlinkyGPIOValue, kDefaultBlinkyGPIOReg),
kOutcomeDfu: 'DFU - AP writes {} to {}'.format(kDefaultDFUGPIOValue, kDefaultDFUGPIOReg),
kOutcomeBlinkyVcoOverride: 'Blinky: SecureROM PLLs (4, CPU) VCO_RCTRL_OW & VCO_RCTRL_SEL overridden',
kOutcomeBlinkyVcoDefault: 'Blinky: SecureROM PLLs (4, CPU) VCO_RCTRL_OW & VCO_RCTRL_SEL default values',
}[self._outcome]
if self._outcome == kOutcomeDfu:
check_usb_clk = 'USB clock frequency'
check_usbotg = 'USB20PHY_CFG0/USB20PHY_CFG1'
check_usbotg_text = 'set to tunables values for Device mode'
else:
check_usb_clk = ''
check_usbotg = ''
check_usbotg_text = ''
# JTAG gets enabled on a production part if it is demoted.
if self._fuse_production_status == True and self._demote_production == True and self._outcome == kOutcomeBlinky:
check_jtag = 'JTAG'
check_jtag_text = 'Starts disabled before test, but enabled after test'
else:
check_jtag = ''
check_jtag_text = ''
keys_state = {True: 'Enabled',
False: 'Disabled',
None: 'Disabled'}[self._enable_keys]
if self._outcome == kOutcomeDfu:
fuses_locked_default = 'Unlocked (CFG_FUSE1.AP_LOCK = 0)'
else:
fuses_locked_default = 'Locked (CFG_FUSE1.AP_LOCK = 1)'
fuses_locked_state = {True: 'Locked (CFG_FUSE1.AP_LOCK = 1)',
False: 'Unlocked (CFG_FUSE1.AP_LOCK = 0)',
None: fuses_locked_default}[self._lock_fuses]
# For LLB boot, boot nonce pre-set to 0xdeadbeefdeadbeef. For iBSS boot, set to 0xddccbbaa78563412
if pretty_filename.find('llb') != -1 and self._force_dfu == 0:
boot_nonce = 0xdeadbeefdeadbeef
else:
if self._restore_info_nonce is not None:
boot_nonce = self._restore_info_nonce
else:
boot_nonce = kDefaultApNonceLittleEndian
if self._force_dfu and self._outcome == kOutcomeBlinky:
outcome_string += ' via iBSS in DFU mode'
rom_state = {True: 'r/w access enabled',
False: 'r/w access disabled'}[self._outcome == kOutcomeDfu]
settings.Append(
'Boot Nonce in PMGR Scratch Registers',
[('PMGR_SCRATCH_10', '0x%x' % (boot_nonce & 0xffffffff)),
('PMGR_SCRATCH_11', '0x%x' % ((boot_nonce >> 32) & 0xffffffff)),
])
settings.Append(
'PASS',
[('Outcome', outcome_string),
('ROM', rom_state),
('Keys - UID and GIDs', keys_state),
('Fuses', fuses_locked_state),
(check_usb_clk, ''),
(check_usbotg, check_usbotg_text),
(check_jtag, check_jtag_text),
])
settings.Append(
'FAIL',
[('All PASS conditions not met', ''),
])
if self._manifest:
settings.Append(
'(Informational only - Image4 Personalization Settings Used)',
[('Encryption', self._encryption),
('Crypto Hash Method', self._crypto_hash_method),
('Epoch', self._epoch),
('Chip ID', self._chip_id),
('Board ID', self._board_id),
('ECID', self._ecid),
('Production Status', self._production_status),
('Security Mode', self._security_mode),
('Enable Keys', self._enable_keys),
('DPRO', self._demote_production),
('DSEC', self._demote_secure),
('Security Domain', self._security_domain),
('Allow Mix and Match', self._allow_mix_and_match),
('AP Boot Nonce Hash', self._ap_boot_nonce_hash),
('Restore Info Nonce', self._restore_info_nonce),
('SEP Boot Nonce Hash', self._sep_boot_nonce_hash),
])
settings.Log(self._log)
return True
def main():
help_str = ('Use the source.\n')
class MyParser(optparse.OptionParser):
def format_epilog(self, formatter):
return self.epilog
parser = MyParser(epilog=help_str)
parser.add_option(
'--platform',
help='Override the default platform value of ' + kDefaultPlatform)
parser.add_option(
'--personalize_img4',
help='Override path to personalize_img4 tool')
parser.add_option(
'--img4decodetest',
help='Override path to img4decodetest tool')
parser.add_option(
'--bfn_maker',
help='Override path to bfn_maker tool')
(options, args) = parser.parse_args(sys.argv[1:])
if args:
sys.stderr.write(ErrorText('Unused trailing options: ' + ' '.join(args) + '\n'))
return 1
thisPlat = options.platform
if thisPlat is None:
thisPlat = kDefaultPlatform
output_dir = kOutputDirPrefix + str(thisPlat)
# Determine the chip_id and board_id based on the platform
if thisPlat not in kSupported_platforms:
sys.stderr.write(ErrorText('Only the following platforms are supported by this script: ' + str(kSupported_platforms)))
return 1
elif thisPlat == kDefaultPlatform:
thisChip_id = kDefaultChipId
thisBoard_id = kDefaultBoardId
else:
assert false, "Unknown platform"
# Prune any existing output and re-create the directory.
shutil.rmtree(output_dir, ignore_errors=True)
os.makedirs(output_dir)
log = open(os.path.join(output_dir, kLogFileName), 'w')
log.write('This file generated by iBoot/apps/SecureROM/test_generator_img4\n')
log.write('Generated on: %s GMT\n\n' % time.asctime(time.gmtime()))
# Result code
rc = True
# Test 1:
# Boot from SPI NOR without a manifest in testmode. This facility is sometimes used in the factory
# environment and the test is to ensure that we can successfully
# boot in this environment. This is booting a non-secure image on a
# non-secure part in testmode.
rc &= PersonalizeImg4(manifest = False,
platform = thisPlat,
options = options,
log = log,
test_number = 1,
outcome = kOutcomeBlinky,
image_source = kImageSourceNor,
fuse_production_status = False,
fuse_security_mode = False,
fuse_epoch = 0,
boot_device = 'NOR',
test_mode = True,
description = ('Boot image without manifest in testmode with SPI NOR')
).Execute()
# Test 2:
# Boot from SPI NOR with manifest that disables keys. This is booting a non-secure
# image on a non-secure part.
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 2,
outcome = kOutcomeBlinky,
image_source = kImageSourceNor,
fuse_production_status = False,
fuse_security_mode = False,
fuse_epoch = 0,
production_status = False,
security_mode = False,
boot_device = 'NOR',
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = True,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = False,
encryption = False,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Boot image with manifest that disables keys on an insecure part')
).Execute()
# Test 3:
# Fail to boot a non-production image on a Production part in test mode.
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 3,
outcome = kOutcomeDfu,
image_source = kImageSourceNor,
fuse_production_status = True,
fuse_security_mode = True,
fuse_epoch = 0,
production_status = False,
security_mode = True,
boot_device = 'NOR',
test_mode = True,
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = False,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Fail to boot a non-production image on a Production part in test mode')
).Execute()
# Test 4:
# Boot a development image on a development part.
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 4,
outcome = kOutcomeBlinky,
image_source = kImageSourceNand,
fuse_production_status = False,
fuse_security_mode = True,
fuse_epoch = 0,
production_status = False,
security_mode = True,
boot_device = 'NAND',
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = False,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Boot a development image on a development part')
).Execute()
# Test 5:
# Boot a production image on a production part.
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 5,
outcome = kOutcomeBlinky,
image_source = kImageSourceNor,
fuse_production_status = True,
fuse_security_mode = True,
fuse_epoch = 1,
production_status = True,
security_mode = True,
epoch = kDefaultEpochForServerSigning,
boot_device = 'NOR',
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = True,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Boot a production image on a production part')
).Execute()
# Test 6:
# Test Epoch changes by having a chip epoch greater then the
# certificate epoch, and verifying that the object is rejected.
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 6,
outcome = kOutcomeDfu,
image_source = kImageSourceNor,
fuse_production_status = False,
fuse_security_mode = True,
fuse_epoch = 2,
production_status = False,
security_mode = True,
boot_device = 'NOR',
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = False,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Fail to boot an image with epoch less than chip epoch')
).Execute()
# Test 7:
# Test USB DFU mode on a development device.
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 7,
outcome = kOutcomeBlinky,
image_source = kImageSourceDfu,
fuse_production_status = False,
fuse_security_mode = True,
fuse_epoch = 0,
production_status = False,
security_mode = True,
boot_device = 'NOR',
boot_device_empty = True,
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = False,
description = ('Boot from USB in DFU mode with boot device set to NOR, but NOR is empty')
).Execute()
# Test 8:
# Test Force DFU when a valid image is in NOR. i.e. ignore valid NOR image
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 8,
outcome = kOutcomeBlinky,
image_source = kImageSourceNor,
fuse_production_status = False,
fuse_security_mode = True,
fuse_epoch = 0,
production_status = False,
security_mode = True,
boot_device = 'NOR',
chip_id = thisChip_id,
board_id = thisBoard_id,
force_dfu = 1,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = False,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Boot from USB in DFU mode with boot device set to NOR, and valid image in NOR, but force dfu set')
).Execute()
# Test 9:
# Boot an image in development mode on a production part (Demotion)
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 9,
outcome = kOutcomeBlinky,
image_source = kImageSourceNor,
fuse_production_status = True,
fuse_security_mode = True,
fuse_epoch = 0,
production_status = True,
security_mode = True,
epoch = kDefaultEpochForServerSigning,
boot_device = 'NOR',
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = True,
demote_production = True,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Boot an image in development mode on a production part (Demotion)')
).Execute()
# Test 10:
# Boot from NOR (slow mode) a development image on a development part.
rc &= PersonalizeImg4(platform = thisPlat,
options = options,
log = log,
test_number = 10,
outcome = kOutcomeBlinky,
image_source = kImageSourceNor,
fuse_production_status = False,
fuse_security_mode = True,
fuse_epoch = 0,
production_status = False,
security_mode = True,
boot_device = 'SLOW_NOR',
test_mode = True,
chip_id = thisChip_id,
board_id = thisBoard_id,
ecid = kDefaultEcid,
allow_mix_and_match = False,
ap_boot_nonce_hash = kDefaultApNonceHash,
enable_keys = True,
encryption = False,
restore_info_nonce = kDefaultApNonceLittleEndian,
description = ('Boot from SPI NOR in slow mode a development image on a development part')
).Execute()
log.write("In addition, provide a clock-tree dump (including spare clocks). Please pick a test from 1 to 10 for this dump.\n")
log.close()
if rc:
sys.stdout.write(PassText('\nSuccess\n'))
return 0
else:
sys.stderr.write(ErrorText('\nTest plan contains errors\n'))
return 1
if __name__ == '__main__':
sys.exit(main())

246
apps/iBoot/application.mk Normal file
View File

@ -0,0 +1,246 @@
# Copyright (C) 2007-2014 Apple Inc. All rights reserved.
# Copyright (C) 2006 Apple Computer, 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)
#
# Global headers that are independing of product, target, etc.
#
GLOBAL_HEADERS += \
lib/mib/mib_nodes.h
#
# iBoot application code.
#
ALL_OBJS += \
$(LOCAL_DIR)/boot.o \
$(LOCAL_DIR)/main.o \
$(LOCAL_DIR)/menu_commands.o \
$(LOCAL_DIR)/debugcmds.o \
$(LOCAL_DIR)/default_env.o \
$(LOCAL_DIR)/upgrade.o
#
# Target module.
#
MODULES_BASIC += \
target/$(TARGET)
OPTIONS += \
WITH_BOOT_STAGE=1 \
WITH_TARGET_CONFIG=1 \
IMAGE_MAX_COUNT=32 \
WITH_APPLICATION_PUTCHAR=1
ifeq ($(BUILD),RELEASE)
OPTIONS += \
TERSE_PANIC_STRINGS=1
endif
#
# Essential system modules.
#
MODULES_BASIC += \
lib/heap \
lib/nonce \
lib/random \
lib/paint \
sys
ifeq ($(IMAGE_FORMAT),)
$(error "IMAGE_FORMAT not set")
endif
ifeq ($(IMAGE_FORMAT),img3)
MODULES += \
lib/image/image3 \
lib/ticket
endif
ifeq ($(IMAGE_FORMAT),im4p)
MODULES += \
lib/image/image4
endif
#
# Libraries to link with.
#
LIBRARY_MODULES += \
lib/libc \
lib/mib
#
# Uncomment to enable boot time profiling. Shared memory console isn't compatible
# with boot profiling system, so it has to be ommitted when profiling
#
#MODULES_BASIC += \
# lib/profile
#MODULES_ELIDE += \
# drivers/apple/shmcon
################################################################################
#
# Define option and module lists for basic differentiating features.
#
# Note that these lists are not intended to be overridden by target or platform.
#
################################################################################
#
# Interactive console support.
#
OPTIONS_CONSOLE += \
WITH_MENU=1
MODULES_CONSOLE += \
lib/env
#
# Interactive console in release builds get the dumb menu.
#
ifeq ($(BUILD),RELEASE)
OPTIONS_CONSOLE += \
WITH_SIMPLE_MENU=1
endif
#
# Functional tests and related commands will be added to console only in debug
# builds.
#
ifeq ($(BUILD),DEBUG)
endif
#
# This option forces the restore host to bootstrap into restore OS via iBEC
# rather than immediately from current product.
#
OPTIONS_RESTORE_STRAP += \
WITH_RESTORE_STRAP=1
# iBEC is willing to tolerate any NAND epoch, as it may be used during
# restore to boot upgrade bits from an older install's storage
# device.
#
# XXX this needs to be a runtime option as this code is now in the library.
#
OPTIONS_RESTORE_BOOT += \
WITH_RESTORE_BOOT=1 \
AND_DISABLE_EPOCH_CHECK=1
################################################################################
#
# Define option and module lists for DFU-mode versus recovery mode.
#
# Note that only one of these should ever be included in any particular product.
# Also, these lists are not intended to be overridden by target or platform.
#
################################################################################
#
# DFU mode products do USB DFU rather than recovery mode.
#
OPTIONS_DFU += \
WITH_DFU_MODE=1
MODULES_DFU +=
#
# Recovery mode products get additional logic, including console support
# since it is required for recovery mode to function properly.
#
OPTIONS_RECOVERY += \
$(OPTIONS_CONSOLE) \
WITH_RECOVERY_MODE=1
MODULES_RECOVERY += \
$(MODULES_CONSOLE)
################################################################################
#
# Define option and module lists for various feature sets.
#
# It is expected that these lists are to be decorated with platform
# and/or target-specific support reflecting differences in hardware
# required to support each feature.
#
################################################################################
#
# Display drivers support.
#
MODULES_DISPLAY +=
#
# Firmware image storage support.
#
MODULES_FIRMWARE += \
lib/blockdev
#
# NVRAM storage support.
#
MODULES_NVRAM += \
lib/nvram
#
# RAMDISK support.
#
MODULES_RAMDISK += \
lib/ramdisk
#
# Syscfg storage support.
#
MODULES_SYSCFG += \
lib/syscfg
#
# Filesystem support.
#
MODULES_FILESYSTEM += \
lib/fs/hfs
OPTIONS_FILESYSTEM += \
WITH_MASS_STORAGE=1
################################################################################
#
# Define option and modules lists for composition of features required to
# boot XNU.
#
# This really shouldn't be overridden in any platform or target makefiles
#
################################################################################
#
# Products that boot the kernel get some extra options.
#
OPTIONS_BOOT += \
WITH_BOOT_XNU=1
#
# Core technology-neutral software modules required to support booting XNU.
#
MODULES_BOOT += \
lib/devicetree \
lib/macho \
lib/paniclog
#
# The following feature modules list are also needed to boot XNU.
#
MODULES_BOOT += \
$(MODULES_FIRMWARE) \
$(MODULES_NVRAM) \
$(MODULES_RAMDISK) \
$(MODULES_SYSCFG)

707
apps/iBoot/boot.c Normal file
View File

@ -0,0 +1,707 @@
/*
* Copyright (C) 2007-2014 Apple Inc. All rights reserved.
* Copyright (C) 2006 Apple Computer, 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 <arch.h>
#include <debug.h>
#include <target.h>
#include <drivers/power.h>
#include <lib/profile.h>
#include <lib/env.h>
#include <lib/fs.h>
#include <lib/image.h>
#include <lib/macho.h>
#include <lib/mib.h>
#include <lib/paint.h>
#include <lib/paniclog.h>
#include <lib/ramdisk.h>
#include <lib/syscfg.h>
#include <lib/ticket.h>
#include <platform.h>
#include <platform/memmap.h>
#include <sys.h>
#include <sys/boot.h>
#include <sys/menu.h>
#include <sys/task.h>
#if WITH_EFFACEABLE
#include <lib/effaceable.h>
#endif
#if WITH_CONSISTENT_DBG
#include <drivers/consistent_debug.h>
#endif
struct image_device {
const char *name;
off_t image_table_offset; // negative offset means from top of device
};
#include <platform/image_devices.h>
extern void boot_darwin(void *ptr, void *arg) __noreturn;
static int boot_flash(u_int32_t type, enum boot_target target);
static int boot_diagnostics_fs(void);
void find_boot_images(void)
{
PROFILE_ENTER('FBI');
#if WITH_BLOCKDEV
struct blockdev *rom_bdev;
uint32_t i;
/* iterate over each of the rom devices, looking for images and syscfg data */
for (i=0; i < sizeof(platform_image_devices)/sizeof(platform_image_devices[0]); i++) {
rom_bdev = lookup_blockdev(platform_image_devices[i].name);
if (!rom_bdev)
continue;
image_search_bdev(rom_bdev, platform_image_devices[i].image_table_offset, 0);
}
#if WITH_SYSCFG
/* hook up syscfg data */
for (i=0; i < sizeof(platform_syscfg_devices)/sizeof(platform_syscfg_devices[0]); i++) {
if (lookup_blockdev(platform_syscfg_devices[i].name)) {
if (syscfgInitWithBdev(platform_syscfg_devices[i].name))
break;
}
}
#endif /* WITH_SYSCFG */
#endif /* WITH_BLOCKDEV */
image_dump_list(false);
PROFILE_EXIT('FBI');
}
static int
boot_flash(u_int32_t type, enum boot_target target)
{
addr_t image_ptr;
size_t image_len;
image_ptr = INSECURE_MEMORY_BASE;
image_len = INSECURE_MEMORY_SIZE;
PROFILE_ENTER('ILT');
if (image_load_type(&image_ptr, &image_len, type, 0))
return -1;
PROFILE_EXIT('ILT');
/* consolidate environment */
security_consolidate_environment();
dprintf(DEBUG_INFO, "boot: loaded image at %p, len %zd\n", (void *)image_ptr, image_len);
/* boot it */
prepare_and_jump(target, (void *)image_ptr, NULL);
return 0;
}
int boot_check_stage(void)
{
#if WITH_HW_POWER
int result;
uint8_t data, boot_stage;
uint32_t boot_fail, panic_fail;
if (power_is_suspended() || power_needs_precharge()) return 0;
result = power_get_nvram(kPowerNVRAMiBootStageKey, &boot_stage);
if (result != 0) return result;
if (boot_stage == kPowerNVRAMiBootStageOff) return 0;
power_set_nvram(kPowerNVRAMiBootErrorStageKey, boot_stage);
result = power_get_nvram(kPowerNVRAMiBootErrorCountKey, &data);
if (result != 0) return result;
boot_fail = (data >> kPowerNVRAMiBootErrorBootShift) & kPowerNVRAMiBootErrorCountMask;
panic_fail = (data >> kPowerNVRAMiBootErrorPanicShift) & kPowerNVRAMiBootErrorCountMask;
if (boot_stage == kPowerNVRAMiBootStagePanicSave) {
if (panic_fail < kPowerNVRAMiBootErrorCountMask) panic_fail++;
} else {
if (boot_fail < kPowerNVRAMiBootErrorCountMask) boot_fail++;
}
data = (boot_fail << kPowerNVRAMiBootErrorBootShift) | (panic_fail << kPowerNVRAMiBootErrorPanicShift);
power_set_nvram(kPowerNVRAMiBootErrorCountKey, data);
power_cancel_buttonwait();
#endif
return 0;
}
int boot_set_stage(uint8_t boot_stage)
{
#if WITH_HW_POWER
if (power_is_suspended() || power_needs_precharge()) return 0;
power_set_nvram(kPowerNVRAMiBootStageKey, boot_stage);
#endif
return 0;
}
int boot_clear_error_count(void)
{
#if WITH_HW_POWER
uint8_t data;
data = (0 << kPowerNVRAMiBootErrorBootShift) | (0 << kPowerNVRAMiBootErrorPanicShift);
power_set_nvram(kPowerNVRAMiBootErrorCountKey, data);
#endif
return 0;
}
int boot_error_count(uint32_t *boot_fail_count, uint32_t *panic_fail_count)
{
int result = 0;
uint32_t boot_fail = 0, panic_fail = 0;
#if WITH_HW_POWER
uint8_t data = 0;
result = power_get_nvram(kPowerNVRAMiBootErrorCountKey, &data);
if (result != 0) return result;
boot_fail = (data >> kPowerNVRAMiBootErrorBootShift) & kPowerNVRAMiBootErrorCountMask;
panic_fail = (data >> kPowerNVRAMiBootErrorPanicShift) & kPowerNVRAMiBootErrorCountMask;
#endif
if (boot_fail_count != 0) *boot_fail_count = boot_fail;
if (panic_fail_count != 0) *panic_fail_count = panic_fail;
return result;
}
#if PRODUCT_IBOOT && WITH_HW_POWER && WITH_BOOT_STAGE
void boot_check_panic(void)
{
int result;
uint8_t error_stage;
/* LLB saved the original stage in ErrorStageKey */
result = power_get_nvram(kPowerNVRAMiBootErrorStageKey, &error_stage);
if (result != 0) goto panic_clear;
if (error_stage == kPowerNVRAMiBootStagePanicSave) {
/* keep alive a little longer */
platform_watchdog_tickle();
/* prevent looping in case of failure in save_panic_log */
result = power_set_nvram(kPowerNVRAMiBootErrorStageKey, 0);
if (result != 0) goto panic_clear;
boot_set_stage(kPowerNVRAMiBootStagePanicReboot);
platform_init_mass_storage_panic();
save_panic_log();
/* reset the system, boot clean */
dprintf(DEBUG_CRITICAL, "Panic saved, full reset.\n");
platform_system_reset(false);
} else if (error_stage == kPowerNVRAMiBootStageBooted) {
dprintf(DEBUG_CRITICAL, "iBoot not saving panic log.\n");
}
panic_clear:
clear_panic_region(0);
}
#endif
int boot_iboot(void)
{
return boot_flash(IMAGE_TYPE_IBOOT, BOOT_IBOOT);
}
#if WITH_DALI
int boot_dali_flash(void)
{
addr_t image_ptr;
size_t image_len;
void* dali_arg = NULL;
image_ptr = INSECURE_MEMORY_BASE;
image_len = INSECURE_MEMORY_SIZE;
PROFILE_ENTER('ILT');
if (image_load_type(&image_ptr, &image_len, IMAGE_TYPE_DALI, 0)) {
dprintf(DEBUG_CRITICAL, "Unable to load dali firmware from flash\n");
return -1;
}
PROFILE_EXIT('ILT');
dali_arg = target_prepare_dali();
/* consolidate environment */
security_consolidate_environment();
dprintf(DEBUG_INFO, "dali: loaded image at %p, len %zu\n", (void *)image_ptr, image_len);
/* boot it */
prepare_and_jump(BOOT_DALI, (void *)image_ptr, dali_arg);
}
int do_daliboot(int argc, struct cmd_arg *args)
{
u_int32_t type;
if (argc < 2) {
// After <rdar://problem/19269223>, ASP doesn't support finding Dali in the LLB blkdev. Initializing both LLB firmware
// and ASP firmware at the same time is untested, so we are no longer supporting this debug path.
printf("This variant of the dali command is no longer supported. However, you can still usb get / dali $loadaddr.\n");
return -1;
} else {
const addr_t addr = args[1].u;
#if WITH_IMAGE3
if (security_allow_memory((void *)addr, DEFAULT_KERNEL_SIZE) &&
security_allow_modes(kSecurityModeExUntrust)) {
#elif WITH_IMAGE4
if (security_allow_memory((void *)addr, DEFAULT_KERNEL_SIZE)) {
#endif
addr_t secure_addr = DEFAULT_KERNEL_ADDRESS;
size_t secure_len = DEFAULT_KERNEL_SIZE;
type = IMAGE_TYPE_DALI;
if (image_load_memory(addr, DEFAULT_KERNEL_SIZE, &secure_addr, &secure_len, &type, 1, NULL, 0)) {
printf("Memory image not valid\n");
} else {
void* dali_arg = NULL;
dali_arg = target_prepare_dali();
/* consolidate environment */
security_consolidate_environment();
printf("jumping into dali image at %p\n", (void *)secure_addr);
prepare_and_jump(BOOT_DALI, (void *)secure_addr, dali_arg);
}
} else {
printf("Permission Denied\n");
}
}
return -1;
}
MENU_COMMAND_DEVELOPMENT(dali, do_daliboot, "[$loadaddr] boot into dali.", NULL);
#endif /* WITH_DALI */
#if WITH_FS
static int
boot_diagnostics_fs(void)
{
const char *diags_paths;
addr_t image_ptr;
size_t image_len;
char bootfile[512];
u_int32_t type;
/* try booting diags from the filesystem */
diags_paths = env_get("diags-path");
/* try to mount the boot file system */
if (mount_bootfs() < 0) {
dprintf(DEBUG_INFO, "root filesystem mount failed\n");
return -1;
}
if (NULL != diags_paths) {
dprintf(DEBUG_INFO, "loading diagnostics from %s\n", diags_paths);
/* build the diags file */
bootfile[0] = 0;
strlcat(bootfile, "/boot", 512);
strlcat(bootfile, diags_paths, 512);
/* load the diags image */
image_ptr = DEFAULT_KERNEL_ADDRESS;
image_len = DEFAULT_KERNEL_SIZE;
type = IMAGE_TYPE_DIAG;
if (image_load_file(bootfile, &image_ptr, &image_len, &type, 1, NULL, 0) == 0) {
dprintf(DEBUG_INFO, "loaded diagnostics at %p, len %zd\n", (void *)image_ptr, image_len);
/* consolidate environment */
security_consolidate_environment();
/* boot it */
prepare_and_jump(BOOT_DIAGS, (void *)image_ptr, NULL);
}
}
fs_unmount("/boot");
dprintf(DEBUG_CRITICAL, "failed to load diagnostics\n");
return -1;
}
#endif /* WITH_FS */
int boot_diagnostics(void)
{
/* try booting diags from a flash image first */
boot_flash(IMAGE_TYPE_DIAG, BOOT_DIAGS);
#if WITH_FS
boot_diagnostics_fs();
#endif
return -1;
}
int do_diagboot(int argc, struct cmd_arg *args)
{
u_int32_t type;
if (argc < 2) {
boot_diagnostics();
} else {
const addr_t addr = args[1].u;
#if WITH_IMAGE3
if (security_allow_memory((void *)addr, DEFAULT_KERNEL_SIZE) &&
security_allow_modes(kSecurityModeExUntrust)) {
#elif WITH_IMAGE4
if (security_allow_memory((void *)addr, DEFAULT_KERNEL_SIZE)) {
#endif
addr_t secure_addr = DEFAULT_KERNEL_ADDRESS;
size_t secure_len = DEFAULT_KERNEL_SIZE;
type = IMAGE_TYPE_DIAG;
if (image_load_memory(addr, DEFAULT_KERNEL_SIZE, &secure_addr, &secure_len, &type, 1, NULL, 0)) {
printf("Memory image not valid\n");
} else {
/* consolidate environment */
security_consolidate_environment();
printf("jumping into diags image at %p\n", (void *)secure_addr);
prepare_and_jump(BOOT_DIAGS, (void *)secure_addr, NULL);
}
} else {
printf("Permission Denied\n");
}
}
return -1;
}
MENU_COMMAND_DEVELOPMENT(diags, do_diagboot, "boot into diagnostics (if present)", NULL);
#if WITH_MACHO
void boot_darwin(void *ptr, void *arg)
{
#if WITH_CONSISTENT_DBG
consistent_debug_update_ap_cpr(DBG_CPR_STATE_RUNNING, 0);
#endif
#if WITH_EFFACEABLE
effaceable_consume_nonce(0);
#endif
clear_panic_region(0);
prepare_and_jump(BOOT_DARWIN, ptr, arg);
}
int do_bootx(int argc, struct cmd_arg *argv)
{
int err = -1;
addr_t entry;
addr_t boot_args;
addr_t addr = DEFAULT_LOAD_ADDRESS;
u_int32_t type;
PROFILE_ENTER('Btx');
#if !RELEASE_BUILD
if (argc > 2 || ((argc > 1) && !strcmp("help", argv[1].str))) {
printf("usage:\n\t%s [<address>]\n", argv[0].str);
err = -1;
goto error;
}
if (argc > 1)
addr = argv[1].u;
#endif
if (!security_allow_memory((void *)addr, DEFAULT_KERNEL_SIZE)) {
printf("Permission Denied\n");
goto error;
}
printf("Attempting to validate kernelcache @ %p\n", (void *)addr);
// Image3 requires the non-restore types
#if WITH_IMAGE4
type = IMAGE_TYPE_KERNELCACHE_RESTORE;
#else
type = IMAGE_TYPE_KERNELCACHE;
#endif
err = load_kernelcache(addr, DEFAULT_KERNEL_SIZE, type, &entry, &boot_args);
if (err < 0) {
printf("error loading kernelcache\n");
dprintf(DEBUG_INFO, "error loading kernelcache %d\n", err);
goto error;
}
printf("kernelcache prepped at address %p\n", (void *)entry);
PROFILE_EXIT('Btx');
boot_darwin((void *)entry, (void *)boot_args);
error:
return err;
}
/* This command is not used by release products other than those allowed to perform restore boot. */
#if !RELEASE_BUILD || WITH_RESTORE_BOOT
MENU_COMMAND(bootx, do_bootx, "boot a kernel cache at specified address", NULL);
#endif
int do_memboot(int argc, struct cmd_arg *argv)
{
addr_t addr = DEFAULT_LOAD_ADDRESS;
addr_t entry;
addr_t boot_args;
addr_t ramdisk_addr;
size_t len;
size_t ramdisk_size;
struct blockdev *bdev = NULL;
struct image_info *image = NULL;
int err = -1;
len = env_get_uint("filesize", 0);
#if !RELEASE_BUILD
if ((argc > 3) || ((argc > 1) && !strcmp("help", argv[1].str))) {
printf("usage:\n\t%s [<len>] [<address>]\n", argv[0].str);
return -1;
}
addr = env_get_uint("loadaddr", (uintptr_t)DEFAULT_LOAD_ADDRESS);
if (argc > 1)
len = argv[1].u;
if (argc > 2)
addr = argv[2].u;
#endif
if (len == 0) {
printf("filesize variable invalid or not set, aborting\n");
return -1;
}
if (len > DEFAULT_RAMDISK_SIZE) {
printf("Combo image too large\n");
return -1;
}
if (!security_allow_memory((void *)addr, len)) {
printf("Permission Denied\n");
return -1;
}
bdev = create_mem_blockdev("mem", (void *)addr, len, 64);
image_search_bdev(bdev, 0, IMAGE_OPTION_MEMORY);
image_dump_list(false);
#if WITH_TICKET
err = ticket_load();
if (err < 0) {
dprintf(DEBUG_INFO, "Falling back to image3 validation\n");
}
#endif
err = load_ramdisk(&ramdisk_addr, &ramdisk_size);
if (err < 0) {
printf("error loading ramdisk\n");
dprintf(DEBUG_INFO, "error loading ramdisk %d\n", err);
goto error;
}
image = image_find(IMAGE_TYPE_KERNELCACHE);
if (image == NULL) {
printf("kernelcache not found\n");
goto error;
}
err = load_kernelcache_image(image, IMAGE_TYPE_KERNELCACHE, &entry, &boot_args);
if (err < 0) {
printf("error loading kernelcache\n");
dprintf(DEBUG_INFO, "error loading kernelcache %d\n", err);
goto error;
}
printf("kernelcache prepped at address %p\n", (void *)entry);
boot_darwin((void *)entry, (void *)boot_args);
error:
if (bdev != NULL) {
image_free_bdev(bdev);
free(bdev);
}
return err;
}
/* This command is not used by release products other than those allowed to perform restore boot. */
#if !RELEASE_BUILD || WITH_RESTORE_BOOT
MENU_COMMAND(memboot, do_memboot, "boot a combo devicetree/ramdisk/kernelcache at specified address", NULL);
#endif
#endif /* WITH_MACHO */
#if WITH_FS && WITH_ENV && WITH_MACHO && WITH_DEVICETREE
/*
* Support for booting from a filesystem.
*/
int
mount_bootfs(void)
{
const char *bootdevice;
char bootsubdevice[128];
bootdevice = env_get("boot-device");
if (!bootdevice) {
dprintf(DEBUG_INFO, "error: boot-device not set\n");
return -1;
}
/* assume the first partition is the fs but allow an override */
snprintf(bootsubdevice, 128, "%s%c", bootdevice, (char)env_get_uint("boot-partition", 0) + 'a');
return mount_bootfs_from_device(bootsubdevice);
}
int
mount_bootfs_from_device(const char *device)
{
int err;
int i;
const char *bootfs;
/* see if the subdevice exists */
if (!lookup_blockdev(device)) {
dprintf(DEBUG_INFO, "boot device '%s' does not exist\n", device);
platform_record_breadcrumb("mount_bootfs", "!lookup-blockdev");
return -1;
}
/* iterate over filesystems looking for one that will mount this partition */
for (i = 0; ; i++) {
if ((bootfs = fs_get_fsname(i)) == NULL) {
dprintf(DEBUG_INFO, "can't find a filesystem willing to mount the boot partition\n");
platform_record_breadcrumb("mount_bootfs", "unknown-fs");
return -1;
}
dprintf(DEBUG_INFO, "trying System mount from device '%s', fs '%s', at /boot\n", device, bootfs);
err = fs_mount(device, bootfs, "/boot");
if (err == 0) {
break;
}
else {
platform_record_breadcrumb_int("fs_mount_err", err);
}
}
dprintf(DEBUG_INFO, "mount successful\n");
return 0;
}
/*
* Mount the boot filesystem, load an optional ramdisk, load and boot the
* configured kernelcache.
*/
int mount_and_boot_system(void)
{
int err;
const char *bootpath;
const char *bootramdisk;
addr_t entry;
addr_t boot_args;
char *bootfile;
err = -1;
bootfile = NULL;
/* allocate storage for the kernelcache/ramdisk paths */
bootfile = (char *)malloc(FS_MAXPATHLEN);
/* find the kernelcache path */
bootpath = env_get("boot-path");
if (!bootpath) {
dprintf(DEBUG_INFO, "error: boot-path not set\n");
goto fail;
}
PROFILE_ENTER('Mfs');
/* try to mount the boot file system */
err = mount_bootfs();
if (err < 0) {
dprintf(DEBUG_CRITICAL, "root filesystem mount failed\n");
goto fail;
}
PROFILE_EXIT('Mfs');
/* look for optional ramdisk path, load it if set */
bootramdisk = env_get("boot-ramdisk");
if ((NULL != bootramdisk) && (0 != bootramdisk[0])) {
/* build the ramdisk path */
snprintf(bootfile, FS_MAXPATHLEN, "/boot%s", bootramdisk);
/* load the ramdisk */
err = load_ramdisk_file(bootfile);
if (err < 0) {
dprintf(DEBUG_CRITICAL, "ramdisk file invalid\n");
goto fail;
}
}
/* build the kernelcache path */
snprintf(bootfile, FS_MAXPATHLEN, "/boot%s", bootpath);
PROFILE_ENTER('LKF');
/* load the kernelcache */
err = load_kernelcache_file(bootfile, IMAGE_TYPE_KERNELCACHE, &entry, &boot_args);
dprintf(DEBUG_INFO, "load_kernelcache returns %d, entry at %p\n", err, (void *)entry);
if (err < 0)
goto fail;
PROFILE_ENTER('LKF');
/* boot it */
dprintf(DEBUG_INFO, "booting kernel\n");
boot_darwin((void *)entry, (void *)boot_args);
/* shouldn't get here */
panic("returned from boot!\n");
fail:
if (NULL != bootfile)
free(bootfile);
fs_unmount("/boot"); /* harmless if mount failed */
return err;
}
static int
do_fsboot(int argc, struct cmd_arg *argv)
{
if (argc >= 2 && !strcmp(argv[1].str, "help")) {
printf("%s: load file from $boot-device on partition $boot-partition at path $boot-path\n", argv[0].str);
return -1;
}
return mount_and_boot_system();
}
MENU_COMMAND_DEVELOPMENT(fsboot, do_fsboot, "boot kernelcache from filesystem", NULL);
#endif /* WITH_FS && WITH_ENV && WITH_MACHO && WITH_DEVICETREE */

View File

@ -0,0 +1,74 @@
# 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.
#
# AlkatrazRef iboot bootloader build config
PLATFORM := s5l8960x
SUB_PLATFORM := s5l8960x
ARCH := arm64
HW_TIMER := architected
BOOT_CONFIG := nand
AMC_REG_VERSION := 3
AMP_FILE_VERSION := 2
ADBE_VERSION := 1
# code modules
MODULES_BASIC += \
platform/$(PLATFORM) \
platform/$(PLATFORM)/amc \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/miu \
platform/$(PLATFORM)/pmgr \
drivers/apple/a7iop \
drivers/apple/aic \
drivers/apple/amc \
drivers/apple/amp \
drivers/apple/ausb \
drivers/apple/ccc \
drivers/apple/gpio \
drivers/apple/iic \
drivers/apple/sep \
drivers/apple/aes \
drivers/dialog/pmu \
drivers/samsung/dwi \
drivers/samsung/uart \
drivers/synopsys/usbotg \
drivers/power/hdqgauge
ifeq ($(BOOT_CONFIG), nand)
MODULES_FILESYSTEM += \
drivers/apple/asp \
drivers/apple/csi \
MODULES_FIRMWARE += \
drivers/apple/anc
endif
ifeq ($(BOOT_CONFIG), nor)
MODULES_BASIC += \
drivers/samsung/spi
MODULES_FILESYSTEM += \
drivers/flash_nor/spi
endif
LIBRARY_MODULES += \
lib/libcorecrypto
MODULES_BOOT += \
drivers/apple/adbe \
drivers/apple/dither \
drivers/apple/dpb \
drivers/apple/adfe \
drivers/apple/displaypipe \
TZ0_SIZE := 6*1024*1024
include $(APPDIR)/products.mk

View File

@ -0,0 +1,110 @@
# Copyright (C) 2009-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.
#
# j33 iboot bootloader build config
PLATFORM := s5l8940x
ARCH := arm
NO_AMP_CALIBRATION := true
# code modules
MODULES_BASIC += \
platform/$(PLATFORM) \
platform/$(PLATFORM)/amc \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/miu \
platform/$(PLATFORM)/pmgr \
drivers/apple/aic \
drivers/apple/amc \
drivers/apple/audio \
drivers/apple/cdma \
drivers/apple/gpio \
drivers/apple/iic \
drivers/apple/sha2 \
drivers/dialog/pmu \
drivers/iic \
drivers/samsung/pke \
drivers/samsung/rosc \
drivers/samsung/swi \
drivers/samsung/uart \
drivers/samsung/usbphy \
drivers/synopsys/usbotg
ifeq ($(SUB_PLATFORM),s5l8947x)
MODULES_BASIC += \
drivers/apple/amp
else
MODULES_BASIC += \
drivers/apple/amg
endif
MODULES_BOOT += \
drivers/apple/mcu
ifeq ($(SUB_PLATFORM),s5l8942x)
MODULES_DISPLAY += \
drivers/apple/adfe \
drivers/apple/displaypipe \
drivers/displayport \
drivers/samsung/rgbout \
drivers/samsung/displayport
else
MODULES_DISPLAY +=
endif
ifeq ($(SUB_PLATFORM),s5l8947x)
MODULES_DISPLAY += \
drivers/apple/adfe \
drivers/apple/displaypipe \
drivers/hdmi \
drivers/samsung/rgbout \
drivers/samsung/hdmi8947
else
MODULES_DISPLAY +=
endif
# Define NOR and NAND technology submodule lists for use only within
# the context of 'boot-from-nor-template.mk' and
# 'boot-from-nand-template.mk'.
SUBMODULES_NOR +=
SUBMODULES_NAND += \
drivers/apple/h2fmi \
drivers/flash_nand/ppn-swiss
include $(APPDIR)/config/boot-from-nand-template.mk
ifeq ($(PRODUCT),iBoot)
# Memory configuration (consumed by the S5L8940X platform module)
TEXT_BANK := sdram
TEXT_FOOTPRINT := 1008*1024
endif
ifeq ($(PRODUCT),iBEC)
# Memory configuration (consumed by the S5L8940X platform module)
TEXT_BANK := sdram
TEXT_FOOTPRINT := 1008*1024
endif
ifeq ($(PRODUCT),iBSS)
# Memory configuration (consumed by the S5L8940X platform module)
TEXT_BANK := sram
TEXT_FOOTPRINT := 256*1024
endif
ifeq ($(PRODUCT),LLB)
# Memory configuration (consumed by the S5L8940X platform module)
TEXT_BANK := sram
TEXT_FOOTPRINT := 256*1024
endif
# override default platform display region size config
DISPLAY_SIZE := 31*1024*1024
include $(APPDIR)/products.mk

View File

@ -0,0 +1,83 @@
# Copyright (C) 2012-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.
#
# appletv4 iboot bootloader build config
PLATFORM := s5l8960x
SUB_PLATFORM := s5l8960x
ARCH := arm64
HW_TIMER := architected
BOOT_CONFIG := nand
AMC_REG_VERSION := 3
AMP_FILE_VERSION := 2
ADBE_VERSION := 1
# code modules
MODULES_BASIC += \
platform/$(PLATFORM) \
platform/$(PLATFORM)/amc \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/miu \
platform/$(PLATFORM)/pmgr \
platform/$(PLATFORM)/error_handler \
drivers/apple/a7iop \
drivers/apple/aic \
drivers/apple/amc \
drivers/apple/amp \
drivers/apple/ausb \
drivers/apple/ccc \
drivers/apple/mcu \
drivers/apple/gpio \
drivers/apple/iic \
drivers/apple/sep \
drivers/apple/aes \
drivers/dialog/pmu \
drivers/samsung/dwi \
drivers/samsung/uart \
drivers/synopsys/usbotg \
drivers/ti/ths7383
ifneq ($(BUILD),RELEASE)
MODULES_BASIC += \
drivers/apple/consistent_debug
endif
ifeq ($(BOOT_CONFIG), nand)
MODULES_FILESYSTEM += \
drivers/apple/asp \
drivers/apple/csi \
MODULES_FIRMWARE += \
drivers/apple/anc
endif
ifeq ($(BOOT_CONFIG), nor)
MODULES_BASIC += \
drivers/samsung/spi
MODULES_FILESYSTEM += \
drivers/flash_nor/spi
endif
LIBRARY_MODULES += \
lib/libcorecrypto
MODULES_BOOT += \
drivers/apple/adbe \
drivers/apple/adfe \
drivers/apple/displaypipe \
drivers/apple/dpb \
drivers/apple/dither \
drivers/displayport \
drivers/samsung/displayport
include $(APPDIR)/products.mk
# You can override the default memory configuration here but you probably shouldn't.

View File

@ -0,0 +1,89 @@
# Copyright (C) 2013-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.
#
# appletv5 iBoot bootloader build config
PLATFORM := t7000
SUB_PLATFORM ?= t7000
ARCH := arm64
BOOT_CONFIG := nand
HW_TIMER := architected
AMC_REG_VERSION ?= 4
AMC_FILE_VERSION ?= 2
ADBE_VERSION := 2
LPDP_VERSION := 1
# code modules
MODULES_BASIC += \
platform/$(PLATFORM) \
platform/$(PLATFORM)/amc \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/miu \
platform/$(PLATFORM)/pmgr \
platform/$(PLATFORM)/error_handler \
drivers/apple/a7iop \
drivers/apple/aic \
drivers/apple/amc \
drivers/apple/amp_v3 \
drivers/apple/ausb \
drivers/apple/ccc \
drivers/apple/voltage_knobs \
drivers/apple/mcu \
drivers/apple/gpio \
drivers/apple/iic \
drivers/apple/sep \
drivers/apple/aes \
drivers/dialog/pmu \
drivers/samsung/dwi \
drivers/samsung/uart \
drivers/synopsys/usbotg \
drivers/ti/ths7383
ifneq ($(BUILD),RELEASE)
MODULES_BASIC += \
drivers/apple/consistent_debug
endif
ifeq ($(BOOT_CONFIG), nand)
MODULES_FILESYSTEM += \
drivers/apple/asp \
drivers/apple/csi \
MODULES_FIRMWARE += \
drivers/apple/anc
endif
ifeq ($(BOOT_CONFIG), nor)
MODULES_BASIC += \
drivers/samsung/spi
MODULES_FILESYSTEM += \
drivers/flash_nor/spi
endif
LIBRARY_MODULES += \
lib/libcorecrypto
MODULES_BOOT += \
platform/$(PLATFORM)/apcie \
drivers/apple/adbe \
drivers/apple/adfe_v2 \
drivers/apple/displaypipe \
drivers/apple/dpb \
drivers/apple/dither \
drivers/displayport \
drivers/analogix/displayport \
drivers/apple/lpdp_phy
# override platform default memory map
ASP_SIZE := 10*1024*1024
TZ0_SIZE := 8*1024*1024
include $(APPDIR)/products.mk

View File

@ -0,0 +1,82 @@
# Copyright (C) 2013-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.
#
# appletv6 iBoot bootloader build config
PLATFORM := s8000
SUB_PLATFORM := s8001
ARCH := arm64
BOOT_CONFIG := nand
HW_TIMER := architected
ADBE_VERSION := 2
LPDP_VERSION := 2
# code modules
MODULES_BASIC += \
platform/$(PLATFORM) \
platform/$(PLATFORM)/chipid \
platform/$(PLATFORM)/dcs \
platform/$(PLATFORM)/error_handler \
platform/$(PLATFORM)/miu \
platform/$(PLATFORM)/pmgr \
drivers/apple/a7iop \
drivers/apple/aes_v2 \
drivers/apple/aic \
drivers/apple/ausb \
drivers/apple/ccc \
drivers/apple/dcs \
drivers/apple/dwi \
drivers/apple/gpio \
drivers/apple/iic \
drivers/apple/sep \
drivers/apple/voltage_knobs \
drivers/dialog/pmu \
drivers/samsung/uart \
drivers/synopsys/usbotg
ifneq ($(BUILD),RELEASE)
MODULES_BASIC += \
drivers/apple/consistent_debug
endif
ifeq ($(BOOT_CONFIG), nand)
MODULES_FIRMWARE += \
platform/$(PLATFORM)/apcie \
drivers/pci \
drivers/apple/apcie \
drivers/apple/dart_lpae \
drivers/nvme
endif
ifeq ($(BOOT_CONFIG), nor)
MODULES_BASIC += \
drivers/samsung/spi
MODULES_FILESYSTEM += \
drivers/flash_nor/spi
endif
LIBRARY_MODULES += \
lib/libcorecrypto
MODULES_BOOT += \
drivers/analogix/displayport \
drivers/apple/adbe \
drivers/apple/adfe_v2 \
drivers/apple/displaypipe \
drivers/apple/dither \
drivers/apple/lpdp_phy \
drivers/apple/reconfig \
drivers/displayport
# Override platform default memory map
ASP_SIZE := 12*1024*1024
include $(APPDIR)/products.mk
# You can override the default SRAM/SDRAM configuration here but probably shouldn't.

View File

@ -0,0 +1,13 @@
# Copyright (C) 2011 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.
#
# B137 iboot bootloader build config
TARGET := iaccy1
include $(GET_LOCAL_DIR)/iaccy1-config-base.mk

View File

@ -0,0 +1,13 @@
# Copyright (C) 2011 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.
#
# B165 iboot bootloader build config
TARGET := iaccy1
include $(GET_LOCAL_DIR)/iaccy1-config-base.mk

Some files were not shown because too many files have changed in this diff Show More