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