first and last commit
commit
1bf4efd552
|
@ -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,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 ""
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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_
|
|
@ -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*)
|
||||
{}
|
||||
};
|
|
@ -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
|
|
@ -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__ */
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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__ */
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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__ */
|
||||
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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__
|
||||
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -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
|
||||
|
|
@ -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__
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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__ */
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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_
|
|
@ -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
|
||||
|
|
@ -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(×tamper_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(×tamper_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(×tamper_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
event_init(×tamper_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(×tamper_runtask_event);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void
|
||||
timestamper_sleep(int mode)
|
||||
{
|
||||
}
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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_
|
|
@ -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)
|
||||
|
|
@ -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);
|
||||
|
|
@ -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_
|
|
@ -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)
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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.
|
||||
*/
|
|
@ -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)
|
|
@ -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 ""
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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"
|
|
@ -0,0 +1,5 @@
|
|||
void start(void)
|
||||
{
|
||||
*((volatile unsigned int *)GPIO_BASE) = GPIO_VALUE;
|
||||
for(;;);
|
||||
}
|
|
@ -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"
|
|
@ -0,0 +1,5 @@
|
|||
void start(void)
|
||||
{
|
||||
*((volatile unsigned int *)GPIO_BASE) = GPIO_VALUE;
|
||||
for(;;);
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
0<EFBFBD>½IM4Pibssrom_test_ibss_encrypted A°ö–@Û_ÊHþ€7:@´5bJûì=}ôI¸8Yt0r07ìȃ‡µÿ¯ƒHêo9Árø ÈcTlÅT71³¸Ž½!‚ƒû*¢³ØÐSR#祧>¡07w@#T<>Ú±ÅsRÝ“£eÒ ÇÃûñ^&ç‚7tÝ0M¸Ý“SÒãùeñd
ûå
|
Binary file not shown.
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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È…ˆ
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -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())
|
|
@ -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)
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue