Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d8ef87cc0 | ||
|
|
a0698a741e | ||
|
|
adaea1d394 | ||
|
|
7d55fc7991 | ||
|
|
d8e7109d35 | ||
|
|
93dbb7ad69 | ||
|
|
f7f46cc0b3 | ||
|
|
32e2ae008a | ||
|
|
cde16baff0 | ||
|
|
38cebf5d0b | ||
|
|
c87aa5415a | ||
|
|
a4e3aa4e1a | ||
|
|
a29e52d425 | ||
|
|
0d7c0f02c2 | ||
|
|
b50ce88239 | ||
|
|
c592b20a82 | ||
|
|
2cee0001cf | ||
|
|
9ff58710dc | ||
|
|
1efd4fac28 | ||
|
|
9a16d1b224 | ||
|
|
b623ef5316 | ||
|
|
62cae19be7 | ||
|
|
19a9358da7 | ||
|
|
94d7dbdb57 | ||
|
|
04a18b4691 | ||
|
|
52b9396b85 | ||
|
|
26fb52905a | ||
|
|
fbd054a4fc | ||
|
|
3f8c24809d | ||
|
|
a914ee741e | ||
|
|
287b4f33ba | ||
|
|
51aeda9707 | ||
|
|
d9aae4f4af | ||
|
|
7e6d4c7f01 | ||
|
|
6adb8f8d7c | ||
|
|
8ff7007198 | ||
|
|
eefd289876 | ||
|
|
51865fe6dd | ||
|
|
9e0bc7a888 | ||
|
|
923144e37a | ||
|
|
fd542ea941 | ||
|
|
97c5e54cae | ||
|
|
5e9f53bfd8 | ||
|
|
2c92a18892 | ||
|
|
5a756faaa0 | ||
|
|
fd56eb635b | ||
|
|
94c88d8121 | ||
|
|
c16e8a325a | ||
|
|
e89d8fa26c | ||
|
|
3011345486 | ||
|
|
97e57b6cbb | ||
|
|
1300c08302 | ||
|
|
1b255b1bd6 | ||
|
|
3cb4e8f8d0 | ||
|
|
d13b9ed88f | ||
|
|
3e98d59d18 | ||
|
|
0186293fa8 | ||
|
|
03b479e6a3 | ||
|
|
49c58b5f2e | ||
|
|
f36b77805e | ||
|
|
b2d53838fd | ||
|
|
b52f4ffdf3 | ||
|
|
5fb1e11d77 | ||
|
|
b9529e0774 | ||
|
|
2bb40f5dd2 | ||
|
|
c7ce40a7bd | ||
|
|
ee68a07899 | ||
|
|
d106e08273 | ||
|
|
5d280d6049 | ||
|
|
96da27fe3b |
No files matched your search
@@ -1,30 +0,0 @@
|
||||
# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown
|
||||
|
||||
name: 'Repo Lockdown'
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: opened
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/repo-lockdown@v2
|
||||
with:
|
||||
pr-comment: |
|
||||
Thank you for your interest in the QEMU project.
|
||||
|
||||
This repository is a read-only mirror of the project's repostories hosted
|
||||
on https://gitlab.com/qemu-project/qemu.git.
|
||||
The project does not process merge requests filed on GitHub.
|
||||
|
||||
QEMU welcomes contributions of code (either fixing bugs or adding new
|
||||
functionality). However, we get a lot of patches, and so we have some
|
||||
guidelines about contributing on the project website:
|
||||
https://www.qemu.org/contribute/
|
||||
lock-pr: true
|
||||
close-pr: true
|
||||
@@ -20,3 +20,5 @@ GTAGS
|
||||
*.swp
|
||||
*.patch
|
||||
*.gcov
|
||||
|
||||
*.DS_Store
|
||||
@@ -0,0 +1,20 @@
|
||||
# QEMU-iOS
|
||||
|
||||
QEMU-iOS is an emulator for legacy Apple devices.
|
||||
Currently, the iPod Touch 1G and iPod Touch 2G are supported.
|
||||
|
||||
<img width="331" alt="it2g-qemu" src="https://github.com/devos50/qemu-ios/assets/1707075/9bf7f6c1-5918-47e9-bb3e-2e39ae15d519">
|
||||
|
||||
The schematic below shows the most important hardware components of the iPod Touch 2G and their interactions.
|
||||
The schematic for the iPod Touch 1G is mostly similar.
|
||||
|
||||
<img width="80%" alt="it2g-schematic" src="https://github.com/devos50/qemu-ios/assets/1707075/4b8eca9a-74b0-4590-ad23-bc056acde434">
|
||||
|
||||
### Running the iPod Touch 1G
|
||||
|
||||
Instructions on how to run the iPod Touch 1G emulator can be found [here](https://devos50.github.io/blog/2022/ipod-touch-qemu-pt2/).
|
||||
A technical blog post with more information about the peripherals and reverse engineering process is published [here](https://devos50.github.io/blog/2022/ipod-touch-qemu/).
|
||||
|
||||
### Running the iPod Touch 2G
|
||||
|
||||
Instructions on how to run the iPod Touch 2G emulator can be found [here](https://github.com/devos50/qemu-ios/blob/ipod_touch_2g/RUNNING.md).
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
===========
|
||||
QEMU README
|
||||
===========
|
||||
|
||||
QEMU is a generic and open source machine & userspace emulator and
|
||||
virtualizer.
|
||||
|
||||
QEMU is capable of emulating a complete machine in software without any
|
||||
need for hardware virtualization support. By using dynamic translation,
|
||||
it achieves very good performance. QEMU can also integrate with the Xen
|
||||
and KVM hypervisors to provide emulated hardware while allowing the
|
||||
hypervisor to manage the CPU. With hypervisor support, QEMU can achieve
|
||||
near native performance for CPUs. When QEMU emulates CPUs directly it is
|
||||
capable of running operating systems made for one machine (e.g. an ARMv7
|
||||
board) on a different machine (e.g. an x86_64 PC board).
|
||||
|
||||
QEMU is also capable of providing userspace API virtualization for Linux
|
||||
and BSD kernel interfaces. This allows binaries compiled against one
|
||||
architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a
|
||||
different architecture ABI (e.g. the Linux x86_64 ABI). This does not
|
||||
involve any hardware emulation, simply CPU and syscall emulation.
|
||||
|
||||
QEMU aims to fit into a variety of use cases. It can be invoked directly
|
||||
by users wishing to have full control over its behaviour and settings.
|
||||
It also aims to facilitate integration into higher level management
|
||||
layers, by providing a stable command line interface and monitor API.
|
||||
It is commonly invoked indirectly via the libvirt library when using
|
||||
open source applications such as oVirt, OpenStack and virt-manager.
|
||||
|
||||
QEMU as a whole is released under the GNU General Public License,
|
||||
version 2. For full licensing details, consult the LICENSE file.
|
||||
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
Documentation can be found hosted online at
|
||||
`<https://www.qemu.org/documentation/>`_. The documentation for the
|
||||
current development version that is available at
|
||||
`<https://www.qemu.org/docs/master/>`_ is generated from the ``docs/``
|
||||
folder in the source tree, and is built by `Sphinx
|
||||
<https://www.sphinx-doc.org/en/master/>`_.
|
||||
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
QEMU is multi-platform software intended to be buildable on all modern
|
||||
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
|
||||
of other UNIX targets. The simple steps to build QEMU are:
|
||||
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
../configure
|
||||
make
|
||||
|
||||
Additional information can also be found online via the QEMU website:
|
||||
|
||||
* `<https://wiki.qemu.org/Hosts/Linux>`_
|
||||
* `<https://wiki.qemu.org/Hosts/Mac>`_
|
||||
* `<https://wiki.qemu.org/Hosts/W32>`_
|
||||
|
||||
|
||||
Submitting patches
|
||||
==================
|
||||
|
||||
The QEMU source code is maintained under the GIT version control system.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://gitlab.com/qemu-project/qemu.git
|
||||
|
||||
When submitting patches, one common approach is to use 'git
|
||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||
qemu-devel@nongnu.org mailing list. All patches submitted must contain
|
||||
a 'Signed-off-by' line from the author. Patches should follow the
|
||||
guidelines set out in the `style section
|
||||
<https://www.qemu.org/docs/master/devel/style.html>`_ of
|
||||
the Developers Guide.
|
||||
|
||||
Additional information on submitting patches can be found online via
|
||||
the QEMU website
|
||||
|
||||
* `<https://wiki.qemu.org/Contribute/SubmitAPatch>`_
|
||||
* `<https://wiki.qemu.org/Contribute/TrivialPatches>`_
|
||||
|
||||
The QEMU website is also maintained under source control.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://gitlab.com/qemu-project/qemu-web.git
|
||||
|
||||
* `<https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/>`_
|
||||
|
||||
A 'git-publish' utility was created to make above process less
|
||||
cumbersome, and is highly recommended for making regular contributions,
|
||||
or even just for sending consecutive patch series revisions. It also
|
||||
requires a working 'git send-email' setup, and by default doesn't
|
||||
automate everything, so you may want to go through the above steps
|
||||
manually for once.
|
||||
|
||||
For installation instructions, please go to
|
||||
|
||||
* `<https://github.com/stefanha/git-publish>`_
|
||||
|
||||
The workflow with 'git-publish' is:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git checkout master -b my-feature
|
||||
$ # work on new commits, add your 'Signed-off-by' lines to each
|
||||
$ git publish
|
||||
|
||||
Your patch series will be sent and tagged as my-feature-v1 if you need to refer
|
||||
back to it in the future.
|
||||
|
||||
Sending v2:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git checkout my-feature # same topic branch
|
||||
$ # making changes to the commits (using 'git rebase', for example)
|
||||
$ git publish
|
||||
|
||||
Your patch series will be sent with 'v2' tag in the subject and the git tip
|
||||
will be tagged as my-feature-v2.
|
||||
|
||||
Bug reporting
|
||||
=============
|
||||
|
||||
The QEMU project uses GitLab issues to track bugs. Bugs
|
||||
found when running code built from QEMU git or upstream released sources
|
||||
should be reported via:
|
||||
|
||||
* `<https://gitlab.com/qemu-project/qemu/-/issues>`_
|
||||
|
||||
If using QEMU via an operating system vendor pre-built binary package, it
|
||||
is preferable to report bugs to the vendor's own bug tracker first. If
|
||||
the bug is also known to affect latest upstream code, it can also be
|
||||
reported via GitLab.
|
||||
|
||||
For additional information on bug reporting consult:
|
||||
|
||||
* `<https://wiki.qemu.org/Contribute/ReportABug>`_
|
||||
|
||||
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
For version history and release notes, please visit
|
||||
`<https://wiki.qemu.org/ChangeLog/>`_ or look at the git history for
|
||||
more detailed information.
|
||||
|
||||
|
||||
Contact
|
||||
=======
|
||||
|
||||
The QEMU community can be contacted in a number of ways, with the two
|
||||
main methods being email and IRC
|
||||
|
||||
* `<mailto:qemu-devel@nongnu.org>`_
|
||||
* `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_
|
||||
* #qemu on irc.oftc.net
|
||||
|
||||
Information on additional methods of contacting the community can be
|
||||
found online via the QEMU website:
|
||||
|
||||
* `<https://wiki.qemu.org/Contribute/StartHere>`_
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
## Running the iPod Touch 2G Emulator
|
||||
|
||||
This file contains the instructions on how to run the iPod Touch 2G emulator using QEMU.
|
||||
Note that this is an experimental release and the functionality of the device is still limited.
|
||||
|
||||
### Building QEMU
|
||||
|
||||
Make sure you have the required libraries installed to compile QEMU:
|
||||
|
||||
```
|
||||
# On MacOS
|
||||
brew install glib ninja pixman pkg-config sdl2
|
||||
|
||||
# On Linux (Ubuntu)
|
||||
sudo apt install make ninja-build pkg-config libssl-dev libsdl2-dev libpixman-1-dev libpixman-1-0 libglib2.0-dev
|
||||
|
||||
# On Windows (MSYS2/mingw64)
|
||||
pacman -S base-devel mingw-w64-x86_64-toolchain git python ninja mingw-w64-x86_64-glib2 mingw-w64-x86_64-pixman python-setuptools mingw-w64-x86_64-SDL2
|
||||
|
||||
```
|
||||
Compile QEMU by running the following commands from the root directory:
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
# On Intel Macs
|
||||
../configure --target-list=arm-softmmu --extra-cflags=-I/usr/local/opt/openssl@3/include --extra-ldflags='-L/usr/local/opt/openssl@3/lib -lcrypto'
|
||||
|
||||
# On Apple Silicon Macs
|
||||
../configure --enable-sdl --target-list=arm-softmmu --disable-capstone --disable-pie --disable-slirp --extra-cflags=-I/opt/homebrew/opt/openssl@3/include --extra-ldflags='-L/opt/homebrew/opt/openssl@3/lib -lcrypto'
|
||||
|
||||
# On Microsoft Windows (with MINGW64)
|
||||
../configure --enable-sdl --disable-cocoa --target-list=arm-softmmu --disable-capstone --disable-slirp --disable-pie --extra-cflags=-I/mingw64/include/openssl --extra-ldflags='-L/mingw64/lib -lcrypto' --disable-stack-protector --disable-werror
|
||||
|
||||
make
|
||||
```
|
||||
|
||||
Note that we’re explicitly enabling compilation of the SDL library which is used for interaction with the emulator (e.g., capturing keyboard and mouse events). Also, we only configure and build the ARM emulator.
|
||||
We are also linking against OpenSSL as the AES/SHA1/PKE engines use some of the library’s cryptographic functions.
|
||||
Remember to update the include and library paths to the OpenSSL library in case they are located elsewhere.
|
||||
You can speed up the `make` command by passing the number of available CPU cores with the `-j` flag, e.g., use `make -j6` to compile using six CPU cores.
|
||||
The compilation process should produce the `qemu-system-arm` binary in the `build/arm-softmmu` directory.
|
||||
|
||||
### Downloading the Required Files
|
||||
|
||||
We need a few files to successfully boot the iPod Touch emulator to the home screen, which I published [here](https://github.com/devos50/qemu-ios/releases/tag/n72ap_v1) for convenience. You can download all these files from here, and they include the following:
|
||||
- The S5L8720 bootrom binary, which is started as the very first thing when booting.
|
||||
- A NOR image that contains various auxillary files used by the bootloader. I provide some instructions on how to generate this image yourself [here](https://github.com/devos50/qemu-ios-generate-nor).
|
||||
- A NAND image that contains the root file system. I provide some instructions on how to generate this image yourself [here](https://github.com/devos50/qemu-ios-generate-nand).
|
||||
|
||||
Download all the required files and save them to a convenient location. You should unzip the `nand_n72ap.zip` file, which contains a single directory named `nand`.
|
||||
|
||||
### Running the Emulator
|
||||
|
||||
We are now ready to run the emulator from the build directory with the following command:
|
||||
|
||||
```
|
||||
./arm-softmmu/qemu-system-arm -M iPod-Touch,bootrom=<path to bootrom>,nand=<path to NAND directory>,nor=<path to NOR directory> -serial mon:stdio -cpu max -m 2G -d unimp
|
||||
```
|
||||
|
||||
If there are any issues running the above commands, please let me know by [opening an issue](https://github.com/devos50/qemu-ios/issues/new).
|
||||
@@ -44,3 +44,4 @@ CONFIG_ARM_VIRT=y
|
||||
# CONFIG_FSL_IMX7=n
|
||||
# CONFIG_FSL_IMX6UL=n
|
||||
# CONFIG_ALLWINNER_H3=n
|
||||
CONFIG_IPOD_TOUCH_2G=y
|
||||
@@ -679,3 +679,8 @@ config ARMSSE
|
||||
select UNIMP
|
||||
select SSE_COUNTER
|
||||
select SSE_TIMER
|
||||
|
||||
config IPOD_TOUCH_2G
|
||||
bool
|
||||
select IPOD_TOUCH_2G
|
||||
select PL192
|
||||
@@ -0,0 +1,564 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/arm/exynos4210.h"
|
||||
#include "hw/arm/ipod_touch_2g.h"
|
||||
#include "target/arm/cpregs.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define VMSTATE_IT2G_CPREG(name) \
|
||||
VMSTATE_UINT64(IT2G_CPREG_VAR_NAME(name), IPodTouchMachineState)
|
||||
|
||||
#define IT2G_CPREG_DEF(p_name, p_op0, p_op1, p_crn, p_crm, p_op2, p_access, p_reset) \
|
||||
{ \
|
||||
.cp = 15, \
|
||||
.name = #p_name, .opc0 = p_op0, .crn = p_crn, .crm = p_crm, \
|
||||
.opc1 = p_op1, .opc2 = p_op2, .access = p_access, .resetvalue = p_reset, \
|
||||
.state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, \
|
||||
.fieldoffset = offsetof(IPodTouchMachineState, IT2G_CPREG_VAR_NAME(p_name)) \
|
||||
- offsetof(ARMCPU, env) \
|
||||
}
|
||||
|
||||
static void allocate_ram(MemoryRegion *top, const char *name, uint32_t addr, uint32_t size)
|
||||
{
|
||||
MemoryRegion *sec = g_new(MemoryRegion, 1);
|
||||
memory_region_init_ram(sec, NULL, name, size, &error_fatal);
|
||||
memory_region_add_subregion(top, addr, sec);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo it2g_cp_reginfo_tcg[] = {
|
||||
IT2G_CPREG_DEF(REG0, 0, 0, 7, 6, 0, PL1_RW, 0),
|
||||
IT2G_CPREG_DEF(REG1, 0, 0, 15, 2, 4, PL1_RW, 0),
|
||||
IT2G_CPREG_DEF(REG1, 0, 0, 7, 14, 0, PL1_RW, 0),
|
||||
IT2G_CPREG_DEF(REG1, 0, 0, 7, 10, 0, PL1_RW, 0),
|
||||
};
|
||||
|
||||
static void ipod_touch_cpu_setup(MachineState *machine, MemoryRegion **sysmem, ARMCPU **cpu, AddressSpace **nsas)
|
||||
{
|
||||
Object *cpuobj = object_new(machine->cpu_type);
|
||||
*cpu = ARM_CPU(cpuobj);
|
||||
CPUState *cs = CPU(*cpu);
|
||||
|
||||
*sysmem = get_system_memory();
|
||||
|
||||
object_property_set_link(cpuobj, "memory", OBJECT(*sysmem), &error_abort);
|
||||
|
||||
object_property_set_bool(cpuobj, "has_el3", false, NULL);
|
||||
|
||||
object_property_set_bool(cpuobj, "has_el2", false, NULL);
|
||||
|
||||
object_property_set_bool(cpuobj, "realized", true, &error_fatal);
|
||||
|
||||
*nsas = cpu_get_address_space(cs, ARMASIdx_NS);
|
||||
|
||||
define_arm_cp_regs(*cpu, it2g_cp_reginfo_tcg);
|
||||
|
||||
object_unref(cpuobj);
|
||||
}
|
||||
|
||||
static void ipod_touch_cpu_reset(void *opaque)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE((MachineState *)opaque);
|
||||
ARMCPU *cpu = nms->cpu;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
cpu_reset(cs);
|
||||
|
||||
//env->regs[0] = nms->kbootargs_pa;
|
||||
//cpu_set_pc(CPU(cpu), 0xc00607ec);
|
||||
cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
|
||||
//env->regs[0] = 0x9000000;
|
||||
//cpu_set_pc(CPU(cpu), LLB_BASE + 0x100);
|
||||
//cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
|
||||
}
|
||||
|
||||
static void ipod_touch_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
|
||||
|
||||
allocate_ram(sysmem, "insecure_ram", INSECURE_RAM_MEM_BASE, 0x3000000);
|
||||
allocate_ram(sysmem, "secure_ram", SECURE_RAM_MEM_BASE, 0x4B04000);
|
||||
allocate_ram(sysmem, "iboot", IBOOT_MEM_BASE, 0x100000);
|
||||
allocate_ram(sysmem, "llb", 0x22000000, 0x100000);
|
||||
allocate_ram(sysmem, "sram1", SRAM1_MEM_BASE, 0x100000);
|
||||
allocate_ram(sysmem, "framebuffer", FRAMEBUFFER_MEM_BASE, 0x400000);
|
||||
allocate_ram(sysmem, "edgeic", EDGEIC_MEM_BASE, 0x1000);
|
||||
allocate_ram(sysmem, "swi", SWI_MEM_BASE, 0x1000);
|
||||
allocate_ram(sysmem, "h264", H264_MEM_BASE, 0x4000);
|
||||
|
||||
// load the bootrom (vrom)
|
||||
uint8_t *file_data = NULL;
|
||||
unsigned long fsize;
|
||||
if (g_file_get_contents(nms->bootrom_path, (char **)&file_data, &fsize, NULL)) {
|
||||
allocate_ram(sysmem, "vrom", 0x0, 0x20000);
|
||||
address_space_rw(nsas, VROM_MEM_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_bootrom_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->bootrom_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_bootrom_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
gboolean bootrom_exists = g_file_test(value, G_FILE_TEST_EXISTS);
|
||||
if(!bootrom_exists) {
|
||||
error_report("bootrom at path \"%s\" must exist", value);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->bootrom_path, value, sizeof(nms->bootrom_path));
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_nor_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->nor_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_nor_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
gboolean nor_exists = g_file_test(value, G_FILE_TEST_EXISTS);
|
||||
if(!nor_exists) {
|
||||
error_report("NOR at path \"%s\" must exist", value);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->nor_path, value, sizeof(nms->nor_path));
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_nand_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->nand_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_nand_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
gboolean nand_exists = g_file_test(value, G_FILE_TEST_IS_DIR);
|
||||
if(!nand_exists) {
|
||||
error_report("NAND at path \"%s\" must be a directory", value);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->nand_path, value, sizeof(nms->nand_path));
|
||||
}
|
||||
|
||||
static void ipod_touch_instance_init(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "bootrom", ipod_touch_get_bootrom_path, ipod_touch_set_bootrom_path);
|
||||
object_property_set_description(obj, "bootrom", "Path to the S5L8720 bootrom binary");
|
||||
|
||||
object_property_add_str(obj, "nor", ipod_touch_get_nor_path, ipod_touch_set_nor_path);
|
||||
object_property_set_description(obj, "nor", "Path to the S5L8720 NOR image");
|
||||
|
||||
object_property_add_str(obj, "nand", ipod_touch_get_nand_path, ipod_touch_set_nand_path);
|
||||
object_property_set_description(obj, "nand", "Path to the NAND files");
|
||||
}
|
||||
|
||||
static inline qemu_irq s5l8900_get_irq(IPodTouchMachineState *s, int n)
|
||||
{
|
||||
return s->irq[n / S5L8720_VIC_SIZE][n % S5L8720_VIC_SIZE];
|
||||
}
|
||||
|
||||
static void ipod_touch_key_event(void *opaque, int keycode)
|
||||
{
|
||||
bool do_irq = false;
|
||||
int gpio_group = 0, gpio_selector = 0;
|
||||
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
if(keycode == KEY_P_DOWN || keycode == KEY_P_UP) {
|
||||
// power button
|
||||
gpio_group = GPIO_BUTTON_POWER_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_POWER_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == KEY_P_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_POWER)) {
|
||||
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_POWER);
|
||||
|
||||
}
|
||||
else if(keycode == KEY_P_UP) {
|
||||
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_POWER);
|
||||
}
|
||||
}
|
||||
else if(keycode == KEY_H_DOWN || keycode == KEY_H_UP) {
|
||||
// home button
|
||||
gpio_group = GPIO_BUTTON_HOME_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_HOME_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == KEY_H_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_HOME)) {
|
||||
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_HOME);
|
||||
}
|
||||
else if(keycode == KEY_H_UP) {
|
||||
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_HOME);
|
||||
}
|
||||
}
|
||||
else if(keycode == KEY_MIN_DOWN || keycode == KEY_MIN_UP) {
|
||||
// volume down button
|
||||
gpio_group = GPIO_BUTTON_VOLDOWN_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_VOLDOWN_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == KEY_MIN_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLDOWN)) {
|
||||
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_VOLDOWN);
|
||||
}
|
||||
else if(keycode == KEY_MIN_UP) {
|
||||
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLDOWN);
|
||||
}
|
||||
}
|
||||
else if(keycode == KEY_PLUS_DOWN || keycode == KEY_PLUS_UP) {
|
||||
// volume down button
|
||||
gpio_group = GPIO_BUTTON_VOLUP_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_VOLUP_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == KEY_PLUS_DOWN && gpio_is_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLUP)) {
|
||||
gpio_set_on(s->gpio_state->gpio_state, GPIO_BUTTON_VOLUP);
|
||||
}
|
||||
else if(keycode == KEY_PLUS_UP) {
|
||||
gpio_set_off(s->gpio_state->gpio_state, GPIO_BUTTON_VOLUP);
|
||||
}
|
||||
}
|
||||
|
||||
s->sysic->gpio_int_status[gpio_group] |= (1 << gpio_selector);
|
||||
qemu_irq_raise(s->sysic->gpio_irqs[gpio_group]);
|
||||
}
|
||||
|
||||
static void ipod_touch_machine_init(MachineState *machine)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
|
||||
MemoryRegion *sysmem;
|
||||
AddressSpace *nsas;
|
||||
ARMCPU *cpu;
|
||||
|
||||
ipod_touch_cpu_setup(machine, &sysmem, &cpu, &nsas);
|
||||
|
||||
// setup clock
|
||||
nms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(nms->sysclk, 12000000ULL);
|
||||
|
||||
nms->cpu = cpu;
|
||||
nms->nsas = nsas;
|
||||
|
||||
// setup VICs
|
||||
nms->irq = g_malloc0(sizeof(qemu_irq *) * 2);
|
||||
DeviceState *dev = pl192_manual_init("vic0", qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_IRQ), qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_FIQ), NULL);
|
||||
PL192State *s = PL192(dev);
|
||||
nms->vic0 = s;
|
||||
memory_region_add_subregion(sysmem, VIC0_MEM_BASE, &nms->vic0->iomem);
|
||||
nms->irq[0] = g_malloc0(sizeof(qemu_irq) * 32);
|
||||
for (int i = 0; i < 32; i++) { nms->irq[0][i] = qdev_get_gpio_in(dev, i); }
|
||||
|
||||
dev = pl192_manual_init("vic1", NULL);
|
||||
s = PL192(dev);
|
||||
nms->vic1 = s;
|
||||
memory_region_add_subregion(sysmem, VIC1_MEM_BASE, &nms->vic1->iomem);
|
||||
nms->irq[1] = g_malloc0(sizeof(qemu_irq) * 32);
|
||||
for (int i = 0; i < 32; i++) { nms->irq[1][i] = qdev_get_gpio_in(dev, i); }
|
||||
|
||||
// // chain VICs together
|
||||
nms->vic1->daisy = nms->vic0;
|
||||
|
||||
// init clock 0
|
||||
dev = qdev_new("ipodtouch.clock");
|
||||
IPodTouchClockState *clock0_state = IPOD_TOUCH_CLOCK(dev);
|
||||
nms->clock0 = clock0_state;
|
||||
memory_region_add_subregion(sysmem, CLOCK0_MEM_BASE, &clock0_state->iomem);
|
||||
|
||||
// init clock 1
|
||||
dev = qdev_new("ipodtouch.clock");
|
||||
IPodTouchClockState *clock1_state = IPOD_TOUCH_CLOCK(dev);
|
||||
nms->clock1 = clock1_state;
|
||||
memory_region_add_subregion(sysmem, CLOCK1_MEM_BASE, &clock1_state->iomem);
|
||||
|
||||
// init the timer
|
||||
dev = qdev_new("ipodtouch.timer");
|
||||
IPodTouchTimerState *timer_state = IPOD_TOUCH_TIMER(dev);
|
||||
nms->timer1 = timer_state;
|
||||
memory_region_add_subregion(sysmem, TIMER1_MEM_BASE, &timer_state->iomem);
|
||||
SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ));
|
||||
//sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ - 1));
|
||||
timer_state->sysclk = nms->sysclk;
|
||||
|
||||
// init sysic
|
||||
dev = qdev_new("ipodtouch.sysic");
|
||||
IPodTouchSYSICState *sysic_state = IPOD_TOUCH_SYSIC(dev);
|
||||
nms->sysic = (IPodTouchSYSICState *) g_malloc0(sizeof(struct IPodTouchSYSICState));
|
||||
memory_region_add_subregion(sysmem, SYSIC_MEM_BASE, &sysic_state->iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
for(int grp = 0; grp < GPIO_NUMINTGROUPS; grp++) {
|
||||
sysbus_connect_irq(busdev, grp, s5l8900_get_irq(nms, S5L8900_GPIO_IRQS[grp]));
|
||||
}
|
||||
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G0_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G1_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G2_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G3_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G4_IRQ));
|
||||
|
||||
// init GPIO
|
||||
dev = qdev_new("ipodtouch.gpio");
|
||||
IPodTouchGPIOState *gpio_state = IPOD_TOUCH_GPIO(dev);
|
||||
nms->gpio_state = gpio_state;
|
||||
memory_region_add_subregion(sysmem, GPIO_MEM_BASE, &gpio_state->iomem);
|
||||
|
||||
// init SDIO
|
||||
dev = qdev_new("ipodtouch.sdio");
|
||||
IPodTouchSDIOState *sdio_state = IPOD_TOUCH_SDIO(dev);
|
||||
nms->sdio_state = sdio_state;
|
||||
memory_region_add_subregion(sysmem, SDIO_MEM_BASE, &sdio_state->iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_SDIO_IRQ));
|
||||
|
||||
dev = exynos4210_uart_create(UART0_MEM_BASE, 256, 0, serial_hd(0), nms->irq[0][24]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
dev = exynos4210_uart_create(UART1_MEM_BASE, 256, 1, serial_hd(1), nms->irq[0][25]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
dev = exynos4210_uart_create(UART2_MEM_BASE, 256, 2, serial_hd(2), nms->irq[0][26]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
dev = exynos4210_uart_create(UART3_MEM_BASE, 256, 3, serial_hd(3), nms->irq[0][27]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
// dev = exynos4210_uart_create(UART4_MEM_BASE, 256, 4, serial_hd(4), nms->irq[0][28]);
|
||||
// if (!dev) {
|
||||
// printf("Failed to create uart4 device!\n");
|
||||
// abort();
|
||||
// }
|
||||
|
||||
// init spis
|
||||
set_spi_base(0);
|
||||
dev = sysbus_create_simple("ipodtouch.spi", SPI0_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI0_IRQ));
|
||||
IPodTouchSPIState *spi0_state = IPOD_TOUCH_SPI(dev);
|
||||
spi0_state->nor->nor_path = nms->nor_path;
|
||||
nms->spi0_state = spi0_state;
|
||||
|
||||
set_spi_base(1);
|
||||
dev = sysbus_create_simple("ipodtouch.spi", SPI1_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI1_IRQ));
|
||||
IPodTouchSPIState *spi1_state = IPOD_TOUCH_SPI(dev);
|
||||
nms->spi1_state = spi1_state;
|
||||
|
||||
set_spi_base(2);
|
||||
sysbus_create_simple("ipodtouch.spi", SPI2_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI2_IRQ));
|
||||
|
||||
set_spi_base(3);
|
||||
sysbus_create_simple("ipodtouch.spi", SPI3_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI3_IRQ));
|
||||
|
||||
set_spi_base(4);
|
||||
dev = sysbus_create_simple("ipodtouch.spi", SPI4_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI4_IRQ));
|
||||
IPodTouchSPIState *spi4_state = IPOD_TOUCH_SPI(dev);
|
||||
spi4_state->mt->sysic = sysic_state;
|
||||
spi4_state->mt->gpio_state = gpio_state;
|
||||
nms->spi4_state = spi4_state;
|
||||
|
||||
// init the chip ID module
|
||||
dev = qdev_new("ipodtouch.chipid");
|
||||
IPodTouchChipIDState *chipid_state = IPOD_TOUCH_CHIPID(dev);
|
||||
nms->chipid_state = chipid_state;
|
||||
memory_region_add_subregion(sysmem, CHIPID_MEM_BASE, &chipid_state->iomem);
|
||||
|
||||
// init the TVOut instance
|
||||
dev = qdev_new("ipodtouch.tvout");
|
||||
IPodTouchTVOutState *tvout_state = IPOD_TOUCH_TVOUT(dev);
|
||||
nms->tvout_state = tvout_state;
|
||||
memory_region_add_subregion(sysmem, TVOUT_MIXER1_MEM_BASE, &tvout_state->mixer1_iomem);
|
||||
memory_region_add_subregion(sysmem, TVOUT_MIXER2_MEM_BASE, &tvout_state->mixer2_iomem);
|
||||
memory_region_add_subregion(sysmem, TVOUT_SDO_MEM_BASE, &tvout_state->sdo_iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TVOUT_SDO_IRQ));
|
||||
|
||||
// init the unknown1 module
|
||||
dev = qdev_new("ipodtouch.unknown1");
|
||||
IPodTouchUnknown1State *unknown1_state = IPOD_TOUCH_UNKNOWN1(dev);
|
||||
memory_region_add_subregion(sysmem, UNKNOWN1_MEM_BASE, &unknown1_state->iomem);
|
||||
|
||||
// init USB OTG
|
||||
dev = qdev_new("dwc2-usb");
|
||||
DWC2State *dwc2 = DWC2_USB(dev);
|
||||
nms->dwc2 = dwc2;
|
||||
memory_region_add_subregion(sysmem, USBOTG_MEM_BASE, &dwc2->container);
|
||||
// TODO add IRQ etc
|
||||
memory_region_init(&nms->usb_dma_container_mr, OBJECT(dev), "appleusb.dma-container-mr", UINT32_MAX);
|
||||
object_property_add_const_link(OBJECT(nms->dwc2), "dma-mr", OBJECT(&nms->usb_dma_container_mr));
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
// create the TCP host and connect the busses
|
||||
dev = qdev_new(TYPE_USB_TCP_HOST);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
BusState *bus = QLIST_FIRST(&DEVICE(busdev)->child_bus);
|
||||
qdev_realize(DEVICE(nms->dwc2->device), bus, &error_fatal);
|
||||
|
||||
// init two pl080 DMAC0 devices
|
||||
dev = qdev_new("pl080");
|
||||
PL080State *pl080_1 = PL080(dev);
|
||||
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
|
||||
memory_region_add_subregion(sysmem, DMAC0_MEM_BASE, &pl080_1->iomem1);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC0_IRQ));
|
||||
|
||||
dev = qdev_new("pl080");
|
||||
PL080State *pl080_2 = PL080(dev);
|
||||
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
|
||||
memory_region_add_subregion(sysmem, DMAC1_0_MEM_BASE, &pl080_2->iomem1);
|
||||
memory_region_add_subregion(sysmem, DMAC1_1_MEM_BASE, &pl080_2->iomem2);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC1_IRQ));
|
||||
|
||||
// Init I2C0
|
||||
dev = qdev_new("ipodtouch.i2c");
|
||||
IPodTouchI2CState *i2c_state = IPOD_TOUCH_I2C(dev);
|
||||
i2c_state->base = 0;
|
||||
nms->i2c0_state = i2c_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, I2C0_MEM_BASE, &i2c_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C0_IRQ));
|
||||
|
||||
// init the PMU
|
||||
I2CSlave * pmu = i2c_slave_create_simple(i2c_state->bus, "pcf50633", 0x73);
|
||||
|
||||
// init the accelerometer
|
||||
I2CSlave *accelerometer = i2c_slave_create_simple(i2c_state->bus, "lis302dl", 0x1D);
|
||||
|
||||
// Init I2C1
|
||||
dev = qdev_new("ipodtouch.i2c");
|
||||
i2c_state = IPOD_TOUCH_I2C(dev);
|
||||
nms->i2c1_state = i2c_state;
|
||||
i2c_state->base = 1;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, I2C1_MEM_BASE, &i2c_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C1_IRQ));
|
||||
|
||||
// init the Mikey
|
||||
I2CSlave *cd327mikey = i2c_slave_create_simple(i2c_state->bus, "cd3272mikey", 0x39);
|
||||
|
||||
// init the FMSS flash controller
|
||||
dev = qdev_new("ipodtouch.fmss");
|
||||
IPodTouchFMSSState *fmss_state = IPOD_TOUCH_FMSS(dev);
|
||||
fmss_state->nand_path = nms->nand_path;
|
||||
nms->fmss_state = fmss_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, FMSS_MEM_BASE, &fmss_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_FMSS_IRQ));
|
||||
|
||||
// init the USB module
|
||||
dev = qdev_new("ipodtouch.usbphys");
|
||||
IPodTouchUSBPhysState *usb_phys_state = IPOD_TOUCH_USB_PHYS(dev);
|
||||
nms->usb_phys_state = usb_phys_state;
|
||||
memory_region_add_subregion(sysmem, USBPHYS_MEM_BASE, &usb_phys_state->iomem);
|
||||
|
||||
ipod_touch_memory_setup(machine, sysmem, nsas);
|
||||
|
||||
// init the MIPI SDI controller
|
||||
dev = qdev_new("ipodtouch.mipidsi");
|
||||
IPodTouchMIPIDSIState *mipi_dsi_state = IPOD_TOUCH_MIPI_DSI(dev);
|
||||
nms->mipi_dsi_state = mipi_dsi_state;
|
||||
memory_region_add_subregion(sysmem, MIPI_DSI_MEM_BASE, &mipi_dsi_state->iomem);
|
||||
|
||||
// init LCD
|
||||
dev = qdev_new("ipodtouch.lcd");
|
||||
IPodTouchLCDState *lcd_state = IPOD_TOUCH_LCD(dev);
|
||||
lcd_state->sysmem = sysmem;
|
||||
lcd_state->mt = spi4_state->mt;
|
||||
nms->lcd_state = lcd_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, DISPLAY_MEM_BASE, &lcd_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_LCD_IRQ));
|
||||
|
||||
// init scaler / CSC
|
||||
dev = qdev_new("ipodtouch.scalercsc");
|
||||
IPodTouchScalerCSCState *scaler_csc_state = IPOD_TOUCH_SCALER_CSC(dev);
|
||||
nms->scaler_csc_state = scaler_csc_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, SCALER_CSC_MEM_BASE, &scaler_csc_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
// init SHA1 engine
|
||||
dev = qdev_new("ipodtouch.sha1");
|
||||
IPodTouchSHA1State *sha1_state = IPOD_TOUCH_SHA1(dev);
|
||||
nms->sha1_state = sha1_state;
|
||||
memory_region_add_subregion(sysmem, SHA1_MEM_BASE, &sha1_state->iomem);
|
||||
|
||||
// init AES engine
|
||||
dev = qdev_new("ipodtouch.aes");
|
||||
IPodTouchAESState *aes_state = IPOD_TOUCH_AES(dev);
|
||||
nms->aes_state = aes_state;
|
||||
memory_region_add_subregion(sysmem, AES_MEM_BASE, &aes_state->iomem);
|
||||
|
||||
// init PKE engine
|
||||
dev = qdev_new("ipodtouch.pke");
|
||||
IPodTouchPKEState *pke_state = IPOD_TOUCH_PKE(dev);
|
||||
nms->pke_state = pke_state;
|
||||
memory_region_add_subregion(sysmem, PKE_MEM_BASE, &pke_state->iomem);
|
||||
|
||||
// init the MBX
|
||||
dev = qdev_new("ipodtouch.mbx");
|
||||
IPodTouchMBXState *mbx_state = IPOD_TOUCH_MBX(dev);
|
||||
nms->mbx_state = mbx_state;
|
||||
memory_region_add_subregion(sysmem, MBX1_MEM_BASE, &mbx_state->iomem1);
|
||||
memory_region_add_subregion(sysmem, MBX2_MEM_BASE, &mbx_state->iomem2);
|
||||
|
||||
qemu_register_reset(ipod_touch_cpu_reset, nms);
|
||||
|
||||
qemu_add_kbd_event_handler(ipod_touch_key_event, spi4_state->mt);
|
||||
|
||||
// toggle DFU
|
||||
gpio_set_on(nms->gpio_state->gpio_state, GPIO_FORCE_DFU);
|
||||
nms->usb_phys_state->is_cable_connected = true;
|
||||
}
|
||||
|
||||
static void ipod_touch_machine_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(klass);
|
||||
mc->desc = "iPod Touch";
|
||||
mc->init = ipod_touch_machine_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1176");
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_machine_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(IPodTouchMachineState),
|
||||
.class_size = sizeof(IPodTouchMachineClass),
|
||||
.class_init = ipod_touch_machine_class_init,
|
||||
.instance_init = ipod_touch_instance_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_machine_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,203 @@
|
||||
#include "hw/arm/ipod_touch_aes.h"
|
||||
|
||||
static uint64_t ipod_touch_aes_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
struct IPodTouchAESState *aesop = (struct IPodTouchAESState *)opaque;
|
||||
|
||||
switch(offset) {
|
||||
case AES_STATUS:
|
||||
return aesop->status;
|
||||
default:
|
||||
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x\n", __FUNCTION__, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_aes_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
struct IPodTouchAESState *aesop = (struct IPodTouchAESState *)opaque;
|
||||
|
||||
uint8_t *inbuf;
|
||||
uint8_t *buf;
|
||||
|
||||
// fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
uint32_t *data = malloc(sizeof(uint16_t) * 9);
|
||||
|
||||
switch(offset) {
|
||||
case AES_GO:
|
||||
// PATCH OUT
|
||||
// in iBoot, we also patch the length check after LZSS compression since that can lead to issues
|
||||
|
||||
inbuf = (uint8_t *)malloc(aesop->insize);
|
||||
cpu_physical_memory_read((aesop->inaddr), inbuf, aesop->insize);
|
||||
|
||||
switch(aesop->keytype) {
|
||||
case AESGID:
|
||||
break;
|
||||
case AESUID:
|
||||
AES_set_decrypt_key(key_uid, sizeof(key_uid) * 8, &aesop->decryptKey);
|
||||
break;
|
||||
case AESCustom:
|
||||
AES_set_decrypt_key((uint8_t *)(&aesop->custkey[4]), 0x10 * 8, &aesop->decryptKey);
|
||||
break;
|
||||
}
|
||||
|
||||
buf = (uint8_t *) malloc(aesop->insize);
|
||||
printf("In size: %d, out size: %d, in addr: 0x%08x, in buf: 0x%08x, out addr: 0x%08x, %d\n", aesop->insize, aesop->outsize, aesop->inaddr, ((uint32_t *)inbuf)[0], aesop->outaddr, aesop->gid_encryption_count);
|
||||
|
||||
if(aesop->keytype == AESGID) {
|
||||
// Unfortunately, we don't have access to the GID key.
|
||||
// However, we know that when the AES engine is invoked with the GID key type, it's for the decryption of the IMG file.
|
||||
// Instead, we provide an hard-coded key as result and copy it to the output buffer.
|
||||
// source: https://www.theiphonewiki.com/wiki/Sugar_Bowl_5F138_(iPod2,1)
|
||||
if(aesop->gid_encryption_count == 0) { // LLB
|
||||
char key[] = {
|
||||
0xce, 0x97, 0xa7, 0xc8, 0x2e, 0xf8, 0x64, 0x67, 0x5e, 0xd3, 0x68, 0x05, 0x97, 0xec, 0x2a, 0xef, // IV
|
||||
0x27, 0x73, 0x2a, 0x6b, 0xbf, 0xb1, 0x4a, 0x07, 0x25, 0x0a, 0x2e, 0x46, 0x82, 0xbf, 0x3c, 0xba, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 1) { // iBoot
|
||||
char key[] = {
|
||||
0xb3, 0x63, 0x3a, 0xfb, 0xe0, 0x2e, 0x0e, 0x9b, 0xa4, 0xd7, 0x36, 0x6c, 0x47, 0xab, 0xe5, 0xa8, // IV
|
||||
0x2d, 0x91, 0x6d, 0xab, 0xb6, 0xdf, 0xd4, 0x59, 0x4d, 0xbe, 0x36, 0x35, 0xb4, 0xc7, 0x16, 0x62, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 2) { // apple logo
|
||||
|
||||
// very ugly - we patch out here the LZSS check
|
||||
data[0] = 0x0; // NOP
|
||||
cpu_physical_memory_write(0x0ff119f0, (uint8_t *)data, 4);
|
||||
|
||||
char key[] = {
|
||||
0x64, 0x23, 0x8f, 0xb0, 0x32, 0x91, 0x42, 0x25, 0x22, 0xb5, 0xdd, 0x28, 0x3f, 0xc3, 0x89, 0x5c, // IV
|
||||
0x85, 0x9f, 0xd4, 0xd3, 0x82, 0xb8, 0x38, 0x51, 0x56, 0xfc, 0x58, 0x1a, 0x7f, 0x1d, 0x97, 0x22, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 3) { // kernelcache
|
||||
char key[] = {
|
||||
0xa1, 0x91, 0x29, 0x12, 0x90, 0xd4, 0x87, 0xff, 0x07, 0x31, 0x96, 0x9c, 0x5f, 0xc8, 0xd9, 0x18, // IV
|
||||
0x0e, 0x4d, 0x23, 0xfa, 0x67, 0x59, 0x99, 0xd5, 0x95, 0x9d, 0xd1, 0x0c, 0x8d, 0xd7, 0x3d, 0x20, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 4) { // device tree
|
||||
char key[] = {
|
||||
0xcc, 0xff, 0x63, 0x4e, 0xe1, 0x27, 0x35, 0xf0, 0x19, 0x16, 0xc4, 0xa6, 0xb2, 0x0f, 0xf1, 0x45, // IV
|
||||
0xe1, 0x7b, 0xcd, 0x56, 0x8d, 0xf1, 0xcd, 0xdc, 0x8f, 0xec, 0xbf, 0x54, 0x87, 0xd5, 0xc3, 0xce, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
|
||||
aesop->gid_encryption_count++;
|
||||
}
|
||||
else {
|
||||
AES_cbc_encrypt(inbuf, buf, aesop->insize, &aesop->decryptKey, (uint8_t *)aesop->ivec, AES_DECRYPT);
|
||||
}
|
||||
|
||||
if(aesop->outaddr != 0x220100ac && aesop->outaddr != 0x0bf08468 && aesop->outaddr != 0x0fb9bcdc) { // TODO very ugly hack - for the RSA key decryption, it seems that doing nothing results in the correct decryption key??
|
||||
// BUG: after decrypting the kernel, we update the Adler CRC code and number of expected bytes.
|
||||
if(aesop->outaddr == 0x0b000020) {
|
||||
uint32_t *cast_buf = (uint32_t *)buf;
|
||||
cast_buf[2] = 0xA7886041; // adler
|
||||
cast_buf[3] = 0xF5D37E00; // 8311797 in big endian
|
||||
}
|
||||
|
||||
// BUG: after decrypting the device tree, the last few bytes are incorrect
|
||||
if(aesop->outaddr == 0x0bf00020) {
|
||||
uint32_t *cast_buf = (uint32_t *)buf;
|
||||
cast_buf[8429] = 0x4; // setting the size of the AAPL,phandle property to 4
|
||||
cast_buf[8430] = 0x0011C4F0; // set the right handle
|
||||
}
|
||||
|
||||
cpu_physical_memory_write((aesop->outaddr), buf, aesop->insize);
|
||||
}
|
||||
|
||||
memset(aesop->custkey, 0, 0x20);
|
||||
memset(aesop->ivec, 0, 0x10);
|
||||
free(inbuf);
|
||||
free(buf);
|
||||
aesop->outsize = aesop->insize;
|
||||
aesop->status = 0xf;
|
||||
break;
|
||||
case AES_KEYLEN:
|
||||
aesop->operation = value;
|
||||
aesop->keylen = value;
|
||||
break;
|
||||
case AES_INADDR:
|
||||
aesop->inaddr = value;
|
||||
break;
|
||||
case AES_INSIZE:
|
||||
aesop->insize = value;
|
||||
break;
|
||||
case AES_OUTSIZE:
|
||||
aesop->outsize = value;
|
||||
break;
|
||||
case AES_OUTADDR:
|
||||
aesop->outaddr = value;
|
||||
break;
|
||||
case AES_TYPE:
|
||||
aesop->keytype = value;
|
||||
break;
|
||||
case AES_KEY_REG ... ((AES_KEY_REG + AES_KEYSIZE) - 1):
|
||||
{
|
||||
uint8_t idx = (offset - AES_KEY_REG) / 4;
|
||||
aesop->custkey[idx] |= value;
|
||||
break;
|
||||
}
|
||||
case AES_IV_REG ... ((AES_IV_REG + AES_IVSIZE) -1 ):
|
||||
{
|
||||
uint8_t idx = (offset - AES_IV_REG) / 4;
|
||||
aesop->ivec[idx] |= value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x - 0x%08x\n", __FUNCTION__, offset, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aes_ops = {
|
||||
.read = ipod_touch_aes_read,
|
||||
.write = ipod_touch_aes_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_aes_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchAESState *s = IPOD_TOUCH_AES(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &aes_ops, s, "aes", 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
memset(&s->custkey, 0, 8 * sizeof(uint32_t));
|
||||
memset(&s->ivec, 0, 4 * sizeof(uint32_t));
|
||||
|
||||
s->gid_encryption_count = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_aes_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_aes_info = {
|
||||
.name = TYPE_IPOD_TOUCH_AES,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchAESState),
|
||||
.instance_init = ipod_touch_aes_init,
|
||||
.class_init = ipod_touch_aes_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_aes_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "hw/arm/ipod_touch_cd3272_mikey.h"
|
||||
|
||||
static int cd3272_mikey_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t cd3272_mikey_recv(I2CSlave *i2c)
|
||||
{
|
||||
CD3272MikeyState *s = CD3272MIKEY(i2c);
|
||||
printf("Reading mikey register %d\n", s->cmd);
|
||||
|
||||
int res = 0;
|
||||
|
||||
switch(s->cmd) {
|
||||
default:
|
||||
res = 0;
|
||||
}
|
||||
|
||||
s->cmd += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int cd3272_mikey_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
CD3272MikeyState *s = CD3272MIKEY(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cd3272_mikey_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void cd3272_mikey_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = cd3272_mikey_event;
|
||||
k->recv = cd3272_mikey_recv;
|
||||
k->send = cd3272_mikey_send;
|
||||
}
|
||||
|
||||
static const TypeInfo cd3272_mikey_info = {
|
||||
.name = TYPE_CD3272MIKEY,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = cd3272_mikey_init,
|
||||
.instance_size = sizeof(CD3272MikeyState),
|
||||
.class_init = cd3272_mikey_class_init,
|
||||
};
|
||||
|
||||
static void cd3272_mikey_register_types(void)
|
||||
{
|
||||
type_register_static(&cd3272_mikey_info);
|
||||
}
|
||||
|
||||
type_init(cd3272_mikey_register_types)
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "hw/arm/ipod_touch_chipid.h"
|
||||
|
||||
static uint64_t ipod_touch_chipid_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case CHIPID_UNKNOWN1:
|
||||
return (1 << 5); // ind5 = production mode
|
||||
case CHIPID_INFO:
|
||||
return (0x8720 << 16) | (1 << 2); // ind16 = chipid, ind2 = security domain,
|
||||
case CHIPID_UNKNOWN2:
|
||||
return 0;
|
||||
case CHIPID_UNKNOWN3:
|
||||
return 0;
|
||||
default:
|
||||
hw_error("%s: reading from unknown chip ID register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_chipid_ops = {
|
||||
.read = ipod_touch_chipid_read,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_chipid_init(Object *obj)
|
||||
{
|
||||
IPodTouchChipIDState *s = IPOD_TOUCH_CHIPID(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_chipid_ops, s, TYPE_IPOD_TOUCH_CHIPID, 0x14);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_chipid_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_chipid_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_CHIPID,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchChipIDState),
|
||||
.instance_init = ipod_touch_chipid_init,
|
||||
.class_init = ipod_touch_chipid_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_chipid_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_chipid_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_chipid_register_types)
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "hw/arm/ipod_touch_clock.h"
|
||||
|
||||
static void s5l8900_clock_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case CLOCK_CONFIG0:
|
||||
s->config0 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG1:
|
||||
s->config1 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG2:
|
||||
s->config2 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG3:
|
||||
s->config3 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG4:
|
||||
s->config4 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG5:
|
||||
s->config5 = val;
|
||||
break;
|
||||
|
||||
case CLOCK_PLL0CON:
|
||||
s->pll0con = val;
|
||||
break;
|
||||
case CLOCK_PLL1CON:
|
||||
s->pll1con = val;
|
||||
break;
|
||||
case CLOCK_PLL2CON:
|
||||
s->pll2con = val;
|
||||
break;
|
||||
case CLOCK_PLL3CON:
|
||||
s->pll3con = val;
|
||||
break;
|
||||
case CLOCK_PLL0LCNT:
|
||||
s->pll0lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLL1LCNT:
|
||||
s->pll1lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLL2LCNT:
|
||||
s->pll2lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLL3LCNT:
|
||||
s->pll3lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLLLOCK:
|
||||
hw_error("%s: Forbidden write to PLLLOCK register 0x%08x\n", __func__, addr);
|
||||
break;
|
||||
case CLOCK_PLLMODE:
|
||||
s->pllmode = val;
|
||||
break;
|
||||
case CLOCK_PWRCON0:
|
||||
s->pwrcon0 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON1:
|
||||
s->pwrcon1 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON2:
|
||||
s->pwrcon2 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON3:
|
||||
s->pwrcon3 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON4:
|
||||
s->pwrcon4 = val;
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: writing value 0x%08x to unknown clock register 0x%08x\n", __func__, val, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_clock_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case CLOCK_CONFIG0:
|
||||
return s->config0;
|
||||
case CLOCK_CONFIG1:
|
||||
return s->config1;
|
||||
case CLOCK_CONFIG2:
|
||||
return s->config2;
|
||||
case CLOCK_CONFIG3:
|
||||
return s->config3;
|
||||
case CLOCK_CONFIG4:
|
||||
return s->config4;
|
||||
case CLOCK_CONFIG5:
|
||||
return s->config5;
|
||||
|
||||
case CLOCK_PLL0CON:
|
||||
return s->pll0con;
|
||||
case CLOCK_PLL1CON:
|
||||
return s->pll1con;
|
||||
case CLOCK_PLL2CON:
|
||||
return s->pll2con;
|
||||
case CLOCK_PLL3CON:
|
||||
return s->pll3con;
|
||||
case CLOCK_PLLLOCK:
|
||||
return (1 | 2 | 4 | 8); // all PLLs are locked
|
||||
case CLOCK_PLLMODE:
|
||||
return s->pllmode;
|
||||
case CLOCK_PWRCON0:
|
||||
return s->pwrcon0;
|
||||
case CLOCK_PWRCON1:
|
||||
return s->pwrcon1;
|
||||
case CLOCK_PWRCON2:
|
||||
return s->pwrcon2;
|
||||
case CLOCK_PWRCON3:
|
||||
return s->pwrcon3;
|
||||
case CLOCK_PWRCON4:
|
||||
return s->pwrcon4;
|
||||
default:
|
||||
hw_error("%s: reading from unknown clock register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps clock_ops = {
|
||||
.read = s5l8900_clock_read,
|
||||
.write = s5l8900_clock_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_clock_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchClockState *s = IPOD_TOUCH_CLOCK(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &clock_ops, s, "clock", 0x80);
|
||||
}
|
||||
|
||||
static void s5l8900_clock_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_clock_info = {
|
||||
.name = TYPE_IPOD_TOUCH_CLOCK,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchClockState),
|
||||
.instance_init = s5l8900_clock_init,
|
||||
.class_init = s5l8900_clock_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_clock_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,199 @@
|
||||
#include "hw/arm/ipod_touch_fmss.h"
|
||||
|
||||
static uint8_t find_bit_index(uint8_t num) {
|
||||
int index = 0;
|
||||
while (num > 1) {
|
||||
num >>= 1;
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static void write_chip_info(IPodTouchFMSSState *s)
|
||||
{
|
||||
uint32_t chipid[] = { 0xb614d5ad, 0xb614d5ad, 0xb614d5ad, 0xb614d5ad };
|
||||
cpu_physical_memory_write(s->reg_cinfo_target_addr, &chipid, 0x10);
|
||||
}
|
||||
|
||||
static void dump_registers(IPodTouchFMSSState *s) {
|
||||
printf("FMSS_PAGES_IN_ADDR: 0x%08x\n", s->reg_pages_in_addr);
|
||||
printf("FMSS_CS_BUF_ADDR: 0x%08x\n", s->reg_cs_buf_addr);
|
||||
printf("FMSS_NUM_PAGES: 0x%08x\n", s->reg_num_pages);
|
||||
}
|
||||
|
||||
static void read_nand_pages(IPodTouchFMSSState *s)
|
||||
{
|
||||
// boot args
|
||||
const char *boot_args = "kextlog=0xfff debug=0x8 cpus=1 rd=disk0s1 serial=1 pmu-debug=0x1 io=0xffff8fff debug-usb=0xffffffff amfi_allow_any_signature=1"; // if not const then overwritten
|
||||
cpu_physical_memory_write(0x0ff2a584, boot_args, strlen(boot_args));
|
||||
|
||||
// patch iBoot - we want to inject the bluetooth MAC address which is located as sub-node of uart1 and not uart3 in the device tree...
|
||||
const char *chr = "arm-io/uart1/bluetooth";
|
||||
cpu_physical_memory_write(0x0ff2206c, chr, strlen(chr));
|
||||
|
||||
int page_out_buf_ind = 0;
|
||||
//dump_registers(s);
|
||||
//printf("Start CMD...\n");
|
||||
for(int page_ind = 0; page_ind < s->reg_num_pages; page_ind++) {
|
||||
uint32_t page_nr = 0;
|
||||
uint32_t page_out_addr = 0;
|
||||
uint32_t cs = 0;
|
||||
cpu_physical_memory_read(s->reg_pages_in_addr + (page_ind * sizeof(uint32_t)), &page_nr, sizeof(uint32_t));
|
||||
cpu_physical_memory_read(s->reg_cs_buf_addr + (page_ind * sizeof(uint32_t)), &cs, sizeof(uint32_t));
|
||||
uint32_t og_cs = cs;
|
||||
cs = find_bit_index(cs);
|
||||
|
||||
if(cs > 3) {
|
||||
printf("CS %d invalid! (original CS: %d, reading page %d)\n", cs, og_cs, page_nr);
|
||||
dump_registers(s);
|
||||
hw_error("CS %d invalid!", cs);
|
||||
}
|
||||
|
||||
// prepare the page
|
||||
char filename[200];
|
||||
sprintf(filename, "%s/cs%d/%d.page", s->nand_path, cs, page_nr);
|
||||
struct stat st = {0};
|
||||
if (stat(filename, &st) == -1) {
|
||||
// page storage does not exist - initialize an empty buffer
|
||||
memset(s->page_buffer, 0, NAND_BYTES_PER_PAGE);
|
||||
memset(s->page_spare_buffer, 0, NAND_BYTES_PER_SPARE);
|
||||
|
||||
uint32_t *buf_cst = (uint32_t *) s->page_spare_buffer;
|
||||
buf_cst[2] = 0x00FF00FF;
|
||||
}
|
||||
else {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (f == NULL) { hw_error("Unable to read file!"); }
|
||||
fread(s->page_buffer, sizeof(char), NAND_BYTES_PER_PAGE, f);
|
||||
fread(s->page_spare_buffer, sizeof(char), NAND_BYTES_PER_SPARE, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// we write away the page in two parts, 2048 bytes first and then the other 2048 bytes.
|
||||
int write_buf_size = NAND_BYTES_PER_PAGE / 2;
|
||||
for(int i = 0; i < 2; i++) {
|
||||
cpu_physical_memory_read(s->reg_pages_out_addr + (page_out_buf_ind * sizeof(uint32_t)), &page_out_addr, sizeof(uint32_t));
|
||||
cpu_physical_memory_write(page_out_addr, s->page_buffer + i * write_buf_size, write_buf_size);
|
||||
//printf("Will read page %d @ cs %d into address 0x%08x and spare into address 0x%08x\n", page_nr, cs, page_out_addr, s->reg_page_spare_out_addr);
|
||||
page_out_buf_ind++;
|
||||
|
||||
}
|
||||
|
||||
// finally, write the spare
|
||||
cpu_physical_memory_write(s->reg_page_spare_out_addr + page_ind * 0xc, s->page_spare_buffer, NAND_BYTES_PER_SPARE);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_fmss_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchFMSSState *s = (IPodTouchFMSSState *)opaque;
|
||||
switch(addr)
|
||||
{
|
||||
case FMSS__CS_BUF_RST_OK:
|
||||
return 0x1;
|
||||
case FMSS__CS_IRQ:
|
||||
return s->reg_cs_irq_bit;
|
||||
case FMSS__CS_IRQMASK:
|
||||
return 0x1;
|
||||
case FMSS__FMCTRL1:
|
||||
return (0x1 << 30);
|
||||
case 0xD00:
|
||||
return 42;
|
||||
default:
|
||||
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_fmss_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchFMSSState *s = (IPodTouchFMSSState *)opaque;
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch(addr) {
|
||||
case 0xC00:
|
||||
if(val == 0x0000ffb5) { s->reg_cs_irq_bit = 1; } // TODO ugly and hard-coded
|
||||
if(val == 0xfff5) { s->reg_cs_irq_bit = 1; qemu_set_irq(s->irq, 1); }
|
||||
break;
|
||||
case FMSS__CS_IRQ:
|
||||
s->reg_cs_irq_bit = 0;
|
||||
qemu_set_irq(s->irq, 0);
|
||||
break;
|
||||
case FMSS_CINFO_TARGET_ADDR:
|
||||
s->reg_cinfo_target_addr = val;
|
||||
write_chip_info(s);
|
||||
break;
|
||||
case FMSS_PAGES_IN_ADDR:
|
||||
s->reg_pages_in_addr = val;
|
||||
break;
|
||||
case FMSS_CS_BUF_ADDR:
|
||||
s->reg_cs_buf_addr = val;
|
||||
break;
|
||||
case FMSS_NUM_PAGES:
|
||||
s->reg_num_pages = val;
|
||||
break;
|
||||
case FMSS_PAGE_SPARE_OUT_ADDR:
|
||||
s->reg_page_spare_out_addr = val;
|
||||
break;
|
||||
case FMSS_PAGES_OUT_ADDR:
|
||||
s->reg_pages_out_addr = val;
|
||||
break;
|
||||
case FMSS_CSGENRC:
|
||||
s->reg_csgenrc = val;
|
||||
break;
|
||||
case 0xD38:
|
||||
if(s->reg_csgenrc == 0xa01) { read_nand_pages(s); }
|
||||
else { printf("NAND write not suported yet!\n"); }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps fmss_ops = {
|
||||
.read = ipod_touch_fmss_read,
|
||||
.write = ipod_touch_fmss_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_fmss_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_fmss_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchFMSSState *s = IPOD_TOUCH_FMSS(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &fmss_ops, s, "fmss", 0xF00);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->page_buffer = (uint8_t *)malloc(NAND_BYTES_PER_PAGE);
|
||||
s->page_spare_buffer = (uint8_t *)malloc(NAND_BYTES_PER_SPARE);
|
||||
}
|
||||
|
||||
static void ipod_touch_fmss_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = ipod_touch_fmss_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_fmss_info = {
|
||||
.name = TYPE_IPOD_TOUCH_FMSS,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchFMSSState),
|
||||
.instance_init = ipod_touch_fmss_init,
|
||||
.class_init = ipod_touch_fmss_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_fmss_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "hw/arm/ipod_touch_gpio.h"
|
||||
|
||||
static void s5l8900_gpio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
|
||||
|
||||
// TODO this should be implemented at one point
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_gpio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
case 0x4:
|
||||
return 0;
|
||||
case 0x24 ... 0x184:
|
||||
return s->gpio_state[GPIOADDR2PAD(addr)];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool gpio_is_on(uint32_t *state, uint32_t gpio)
|
||||
{
|
||||
return (state[GPIO2PAD(gpio)] & (1 << GPIO2PIN(gpio)));
|
||||
}
|
||||
|
||||
bool gpio_is_off(uint32_t *state, uint32_t gpio)
|
||||
{
|
||||
return !gpio_is_on(state, gpio);
|
||||
}
|
||||
|
||||
void gpio_set_on(uint32_t *state, uint32_t gpio)
|
||||
{
|
||||
state[GPIO2PAD(gpio)] |= (1 << GPIO2PIN(gpio));
|
||||
}
|
||||
|
||||
void gpio_set_off(uint32_t *state, uint32_t gpio)
|
||||
{
|
||||
state[GPIO2PAD(gpio)] &= ~(1 << GPIO2PIN(gpio));
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gpio_ops = {
|
||||
.read = s5l8900_gpio_read,
|
||||
.write = s5l8900_gpio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_gpio_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchGPIOState *s = IPOD_TOUCH_GPIO(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &gpio_ops, s, "gpio", 0x1000);
|
||||
}
|
||||
|
||||
static void s5l8900_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_gpio_info = {
|
||||
.name = TYPE_IPOD_TOUCH_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchGPIOState),
|
||||
.instance_init = s5l8900_gpio_init,
|
||||
.class_init = s5l8900_gpio_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_gpio_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,218 @@
|
||||
#include "hw/arm/ipod_touch_lcd.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/display/framebuffer.h"
|
||||
|
||||
static uint64_t ipod_touch_lcd_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0:
|
||||
return 2;
|
||||
case 0x4:
|
||||
return s->lcd_con;
|
||||
case 0xC:
|
||||
return 0x1; //s->unknown1;
|
||||
case 0x20:
|
||||
return s->w1_display_depth_info;
|
||||
case 0x24:
|
||||
return s->w1_framebuffer_base;
|
||||
case 0x28:
|
||||
return s->w1_hspan;
|
||||
case 0x30:
|
||||
return s->w1_display_resolution_info;
|
||||
case 0x1b10:
|
||||
return 2;
|
||||
default:
|
||||
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch(addr) {
|
||||
case 0x4:
|
||||
s->lcd_con = val;
|
||||
break;
|
||||
case 0xC:
|
||||
s->unknown1 = val;
|
||||
qemu_irq_lower(s->irq);
|
||||
break;
|
||||
case 0x20:
|
||||
s->w1_display_depth_info = val;
|
||||
break;
|
||||
case 0x24:
|
||||
s->w1_framebuffer_base = val;
|
||||
break;
|
||||
case 0x28:
|
||||
s->w1_hspan = val;
|
||||
break;
|
||||
case 0x30:
|
||||
s->w1_display_resolution_info = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lcd_invalidate(void *opaque)
|
||||
{
|
||||
IPodTouchLCDState *s = opaque;
|
||||
s->invalidate = 1;
|
||||
}
|
||||
|
||||
static void draw_line32_32(void *opaque, uint8_t *d, const uint8_t *s, int width, int deststep)
|
||||
{
|
||||
uint8_t r, g, b;
|
||||
|
||||
do {
|
||||
//v = lduw_le_p((void *) s);
|
||||
//printf("V: %d\n", *s);
|
||||
b = s[0];
|
||||
g = s[1];
|
||||
r = s[2];
|
||||
//printf("R: %d, G: %d, B: %d\n", r, g, b);
|
||||
((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
|
||||
s += 4;
|
||||
d += 4;
|
||||
} while (-- width != 0);
|
||||
}
|
||||
|
||||
static void lcd_refresh(void *opaque)
|
||||
{
|
||||
//fprintf(stderr, "%s: refreshing LCD screen\n", __func__);
|
||||
|
||||
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
|
||||
DisplaySurface *surface = qemu_console_surface(lcd->con);
|
||||
drawfn draw_line;
|
||||
int src_width, dest_width;
|
||||
int height, first, last;
|
||||
int width, linesize;
|
||||
|
||||
if (!lcd || !lcd->con || !surface_bits_per_pixel(surface))
|
||||
return;
|
||||
|
||||
dest_width = 4;
|
||||
draw_line = draw_line32_32;
|
||||
|
||||
/* Resolution */
|
||||
first = last = 0;
|
||||
width = 320;
|
||||
height = 480;
|
||||
lcd->invalidate = 1;
|
||||
|
||||
src_width = 4 * width;
|
||||
linesize = surface_stride(surface);
|
||||
|
||||
if(lcd->invalidate) {
|
||||
framebuffer_update_memory_section(&lcd->fbsection, lcd->sysmem, lcd->w1_framebuffer_base, height, 4 * width);
|
||||
}
|
||||
|
||||
framebuffer_update_display(surface, &lcd->fbsection,
|
||||
width, height,
|
||||
src_width, /* Length of source line, in bytes. */
|
||||
linesize, /* Bytes between adjacent horizontal output pixels. */
|
||||
dest_width, /* Bytes between adjacent vertical output pixels. */
|
||||
lcd->invalidate,
|
||||
draw_line, NULL,
|
||||
&first, &last);
|
||||
if (first >= 0) {
|
||||
dpy_gfx_update(lcd->con, 0, first, width, last - first + 1);
|
||||
}
|
||||
lcd->invalidate = 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps lcd_ops = {
|
||||
.read = ipod_touch_lcd_read,
|
||||
.write = ipod_touch_lcd_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const GraphicHwOps gfx_ops = {
|
||||
.invalidate = lcd_invalidate,
|
||||
.gfx_update = lcd_refresh,
|
||||
};
|
||||
|
||||
static void ipod_touch_lcd_mouse_event(void *opaque, int x, int y, int z, int buttons_state)
|
||||
{
|
||||
//printf("CLICKY %d %d %d %d\n", x, y, z, buttons_state);
|
||||
|
||||
// convert x and y to fractional numbers
|
||||
float fx = x / pow(2, 15);
|
||||
float fy = 1 - y / pow(2, 15);
|
||||
|
||||
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
|
||||
lcd->mt->prev_touch_x = lcd->mt->touch_x;
|
||||
lcd->mt->prev_touch_y = lcd->mt->touch_y;
|
||||
lcd->mt->touch_x = fx;
|
||||
lcd->mt->touch_y = fy;
|
||||
|
||||
if(buttons_state && !lcd->mt->touch_down) {
|
||||
ipod_touch_multitouch_on_touch(lcd->mt);
|
||||
}
|
||||
else if(!buttons_state && lcd->mt->touch_down) {
|
||||
ipod_touch_multitouch_on_release(lcd->mt);
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
|
||||
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY);
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
|
||||
s->con = graphic_console_init(dev, 0, &gfx_ops, s);
|
||||
qemu_console_resize(s->con, 320, 480);
|
||||
|
||||
// add mouse handler
|
||||
qemu_add_mouse_event_handler(ipod_touch_lcd_mouse_event, s, 1, "iPod Touch Touchscreen");
|
||||
|
||||
// initialize the refresh timer
|
||||
s->refresh_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, refresh_timer_tick, s);
|
||||
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY);
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &lcd_ops, s, "lcd", 0x10000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = ipod_touch_lcd_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_lcd_info = {
|
||||
.name = TYPE_IPOD_TOUCH_LCD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchLCDState),
|
||||
.instance_init = ipod_touch_lcd_init,
|
||||
.class_init = ipod_touch_lcd_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_lcd_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "hw/arm/ipod_touch_lis302dl.h"
|
||||
|
||||
static int lis302dl_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t lis302dl_recv(I2CSlave *i2c)
|
||||
{
|
||||
LIS302DLState *s = LIS302DL(i2c);
|
||||
printf("Reading accelerometer register %d\n", s->cmd);
|
||||
|
||||
switch(s->cmd) {
|
||||
case ACCEL_WHOAMI:
|
||||
return ACCEL_WHOAMI_VALUE; // whoami value
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int lis302dl_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
LIS302DLState *s = LIS302DL(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lis302dl_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void lis302dl_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = lis302dl_event;
|
||||
k->recv = lis302dl_recv;
|
||||
k->send = lis302dl_send;
|
||||
}
|
||||
|
||||
static const TypeInfo lis302dl_info = {
|
||||
.name = TYPE_LIS302DL,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = lis302dl_init,
|
||||
.instance_size = sizeof(LIS302DLState),
|
||||
.class_init = lis302dl_class_init,
|
||||
};
|
||||
|
||||
static void lis302dl_register_types(void)
|
||||
{
|
||||
type_register_static(&lis302dl_info);
|
||||
}
|
||||
|
||||
type_init(lis302dl_register_types)
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "hw/arm/ipod_touch_mbx.h"
|
||||
|
||||
static uint32_t reverse_byte_order(uint32_t value) {
|
||||
return ((value & 0x000000FF) << 24) |
|
||||
((value & 0x0000FF00) << 8) |
|
||||
((value & 0x00FF0000) >> 8) |
|
||||
((value & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_mbx1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
switch(addr)
|
||||
{
|
||||
case 0x12c:
|
||||
return 0x40;
|
||||
case 0xf00:
|
||||
return (2 << 0x10) | (1 << 0x18); // seems to be some kind of identifier
|
||||
case 0x1020:
|
||||
return 0x10000;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mbx1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
}
|
||||
|
||||
static void patch_kernel()
|
||||
{
|
||||
// patch the loading of the AppleBCM4325 driver
|
||||
char *bcm4325_vars = "test";
|
||||
|
||||
// write the pointer to our custom subroutine
|
||||
uint32_t *data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = 0xC0460000;
|
||||
cpu_physical_memory_write(0x8324aa8, (uint8_t *)data, sizeof(uint32_t) * 1);
|
||||
|
||||
// create the call to the subroutine
|
||||
data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = reverse_byte_order(0x0640A0E1); // mov r4, r6
|
||||
data[1] = reverse_byte_order(0x9C309FE5); // ldr r3, [pc, #0x9c]
|
||||
data[2] = reverse_byte_order(0x33FF2FE1); // blx r3
|
||||
data[3] = reverse_byte_order(0x00F020E3); // NOP
|
||||
data[4] = reverse_byte_order(0x00F020E3); // NOP
|
||||
data[5] = reverse_byte_order(0x00F020E3); // NOP
|
||||
cpu_physical_memory_write(0x8324a00, (uint8_t *)data, sizeof(uint32_t) * 6);
|
||||
|
||||
// fill in the driver load subroutine
|
||||
data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = reverse_byte_order(0xFE402DE9); // push on stack
|
||||
|
||||
// TODO I should clean this up
|
||||
for(int i = 1; i < 21; i++) { data[i] = reverse_byte_order(0x00F020E3); } // NOP
|
||||
|
||||
// call the IONetworkController metaclass initialization
|
||||
data[21] = reverse_byte_order(0x0100B0E3); // movs r0, #0x1
|
||||
data[22] = reverse_byte_order(0xB8109FE5); // ldr r1, [pc, #0xb8]
|
||||
data[23] = reverse_byte_order(0xB8209FE5); // ldr r2, [pc, #0xb8]
|
||||
data[24] = reverse_byte_order(0x32FF2FE1); // blx r2
|
||||
|
||||
// load the "com.apple.driver.AppleBCM4325" kext
|
||||
data[25] = reverse_byte_order(0xB4009FE5); // ldr r0, [pc, #0xb4]
|
||||
data[26] = reverse_byte_order(0x0110B0E3); // movs r1, #0x1
|
||||
data[27] = reverse_byte_order(0xB0209FE5); // ldr r2, [pc, #0xd8]
|
||||
data[28] = reverse_byte_order(0x32FF2FE1); // blx r2
|
||||
|
||||
data[29] = reverse_byte_order(0xFE80BDE8); // pop from stack
|
||||
|
||||
cpu_physical_memory_write(0x8460000, (uint8_t *)data, sizeof(uint32_t) * 50);
|
||||
|
||||
// write the data section of the driver load subroutine (0x100 items from the start of the subroutine)
|
||||
data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = 0xc0460200; // the address of the BCM4325Vars string
|
||||
data[1] = 0xc013c373; // the address of OSData::withBytes
|
||||
data[2] = 0xc013cc3d; // the address of OSDictionary::withCapacity
|
||||
data[3] = 0xc03467bc; // the "BCM4325Vars" string
|
||||
data[4] = 0xc013ad8d; // the address of OSObject::operator.new
|
||||
data[5] = 0xc032c294; // the object initialization method of AppleBCM4325
|
||||
data[6] = 0xffff; // the 2nd parameter for the call to the IONetworkController metaclass initialization
|
||||
data[7] = 0xc02f94f9; // the initialization method of the IONetworkController metaclass
|
||||
data[8] = 0xc038a320; // the "com.apple.driver.AppleBCM4325" string
|
||||
data[9] = 0xc015de01; // the kmod_load_request method
|
||||
|
||||
cpu_physical_memory_write(0x8460100, (uint8_t *)data, sizeof(uint32_t) * 10);
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_mbx2_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
switch(addr)
|
||||
{
|
||||
case 0xC:
|
||||
patch_kernel();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mbx2_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_mbx1_ops = {
|
||||
.read = ipod_touch_mbx1_read,
|
||||
.write = ipod_touch_mbx1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps ipod_touch_mbx2_ops = {
|
||||
.read = ipod_touch_mbx2_read,
|
||||
.write = ipod_touch_mbx2_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_mbx_init(Object *obj)
|
||||
{
|
||||
IPodTouchMBXState *s = IPOD_TOUCH_MBX(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem1, obj, &ipod_touch_mbx1_ops, s, TYPE_IPOD_TOUCH_MBX, 0x1000000);
|
||||
sysbus_init_mmio(sbd, &s->iomem1);
|
||||
memory_region_init_io(&s->iomem2, obj, &ipod_touch_mbx2_ops, s, TYPE_IPOD_TOUCH_MBX, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem2);
|
||||
}
|
||||
|
||||
static void ipod_touch_mbx_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_mbx_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MBX,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchMBXState),
|
||||
.instance_init = ipod_touch_mbx_init,
|
||||
.class_init = ipod_touch_mbx_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_mbx_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_mbx_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_mbx_register_types)
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "hw/arm/ipod_touch_mipi_dsi.h"
|
||||
|
||||
static uint64_t ipod_touch_mipi_dsi_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchMIPIDSIState *s = (IPodTouchMIPIDSIState *)opaque;
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0:
|
||||
return 0x103 | rDSIM_STATUS_TxReadyHsClk;
|
||||
case REG_INTSRC:
|
||||
return rDSIM_INTSRC_RxDatDone;
|
||||
case REG_RXFIFO:
|
||||
if(!s->return_panel_id) {
|
||||
s->return_panel_id = true;
|
||||
// TODO this should be rewritten as a proper queue!
|
||||
return DSIM_RSP_LONG_READ | (3 << 8); // the latter part indicates the length of the response (the panel ID)
|
||||
} else {
|
||||
s->return_panel_id = false;
|
||||
return 0x00a1d13c;
|
||||
}
|
||||
|
||||
case REG_FIFOCTRL:
|
||||
return rDSIM_FIFOCTRL_EmptyHSfr;
|
||||
default:
|
||||
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mipi_dsi_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchMIPIDSIState *s = (IPodTouchMIPIDSIState *)opaque;
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case REG_PKTHDR:
|
||||
s->pkthdr_reg = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mipi_dsi_ops = {
|
||||
.read = ipod_touch_mipi_dsi_read,
|
||||
.write = ipod_touch_mipi_dsi_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_mipi_dsi_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_mipi_dsi_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchMIPIDSIState *s = IPOD_TOUCH_MIPI_DSI(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &mipi_dsi_ops, s, "mipi_dsi", 0x10000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->return_panel_id = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mipi_dsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = ipod_touch_mipi_dsi_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_mipi_dsi_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MIPI_DSI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchMIPIDSIState),
|
||||
.instance_init = ipod_touch_mipi_dsi_init,
|
||||
.class_init = ipod_touch_mipi_dsi_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_mipi_dsi_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,414 @@
|
||||
#include "hw/arm/ipod_touch_multitouch.h"
|
||||
|
||||
static void prepare_interface_version_response(IPodTouchMultitouchState *s) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// set the interface version
|
||||
s->out_buffer[2] = MT_INTERFACE_VERSION;
|
||||
|
||||
// set the max packet size
|
||||
s->out_buffer[3] = (MT_MAX_PACKET_SIZE & 0xFF);
|
||||
s->out_buffer[4] = (MT_MAX_PACKET_SIZE >> 8) & 0xFF;
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_cmd_status_response(IPodTouchMultitouchState *s) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// TODO we should probably set some CMD status here
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_report_info_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// set the error
|
||||
s->out_buffer[2] = 0;
|
||||
|
||||
// set the report length
|
||||
uint32_t report_length = 0;
|
||||
if(report_id == MT_REPORT_UNKNOWN1) {
|
||||
report_length = MT_REPORT_UNKNOWN1_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_FAMILY_ID) {
|
||||
report_length = MT_REPORT_FAMILY_ID_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
||||
report_length = MT_REPORT_SENSOR_INFO_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
||||
report_length = MT_REPORT_SENSOR_REGION_DESC_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
||||
report_length = MT_REPORT_SENSOR_REGION_PARAM_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
||||
report_length = MT_REPORT_SENSOR_DIMENSIONS_SIZE;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown report ID 0x%02x\n", report_id);
|
||||
}
|
||||
|
||||
s->out_buffer[3] = (report_length & 0xFF);
|
||||
s->out_buffer[4] = (report_length >> 8) & 0xFF;
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_short_control_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
if(report_id == MT_REPORT_FAMILY_ID) {
|
||||
s->out_buffer[3] = MT_FAMILY_ID;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
||||
s->out_buffer[3] = MT_ENDIANNESS;
|
||||
s->out_buffer[4] = MT_SENSOR_ROWS;
|
||||
s->out_buffer[5] = MT_SENSOR_COLUMNS;
|
||||
s->out_buffer[6] = (MT_BCD_VERSION & 0xFF);
|
||||
s->out_buffer[7] = (MT_BCD_VERSION >> 8) & 0xFF;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
||||
s->out_buffer[3] = MT_SENSOR_REGION_DESC;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
||||
s->out_buffer[3] = MT_SENSOR_REGION_PARAM;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
||||
uint32_t *ob_int32 = (uint32_t *)&s->out_buffer[3];
|
||||
ob_int32[0] = MT_SENSOR_SURFACE_WIDTH;
|
||||
ob_int32[1] = MT_SENSOR_SURFACE_HEIGHT;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown report ID 0x%02x\n", report_id);
|
||||
}
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static uint32_t ipod_touch_multitouch_transfer(SSIPeripheral *dev, uint32_t value)
|
||||
{
|
||||
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(dev);
|
||||
|
||||
//printf("<MULTITOUCH> Got value: 0x%02x\n", value);
|
||||
|
||||
if(s->cur_cmd == 0) {
|
||||
//printf("Starting command 0x%02x\n", value);
|
||||
// we're currently not in a command - start a new command
|
||||
s->cur_cmd = value;
|
||||
s->out_buffer = malloc(0x100);
|
||||
s->out_buffer[0] = value; // the response header
|
||||
s->buf_ind = 0;
|
||||
s->in_buffer = malloc(0x100);
|
||||
s->in_buffer_ind = 0;
|
||||
|
||||
if(value == 0x18) { // filler packet??
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0xE1;
|
||||
}
|
||||
else if(value == 0x1A) { // HBPP ACK
|
||||
s->buf_size = 2;
|
||||
if(s->hbpp_atn_ack_response[0] == 0 && s->hbpp_atn_ack_response[1] == 0) {
|
||||
// return the default ACK response
|
||||
s->out_buffer[0] = 0x4B;
|
||||
s->out_buffer[1] = 0xC1;
|
||||
}
|
||||
else {
|
||||
s->out_buffer[0] = s->hbpp_atn_ack_response[0];
|
||||
s->out_buffer[1] = s->hbpp_atn_ack_response[1];
|
||||
}
|
||||
|
||||
}
|
||||
else if(value == 0x1C) { // read register
|
||||
s->buf_size = 8;
|
||||
memset(s->out_buffer, 0, 8); // just return zeros
|
||||
}
|
||||
else if(value == 0x1D) { // execute
|
||||
s->buf_size = 12;
|
||||
memset(s->out_buffer, 0, 12); // just return zeros
|
||||
}
|
||||
else if(value == 0x1F) { // calibration
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0x0;
|
||||
}
|
||||
else if(value == 0x1E) { // write register
|
||||
s->buf_size = 16;
|
||||
memset(s->out_buffer, 0, 16); // just return zeros
|
||||
}
|
||||
else if(value == 0x1F) { // calibration
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0x0;
|
||||
}
|
||||
else if(value == MT_CMD_HBPP_DATA_PACKET) {
|
||||
s->buf_size = 20; // should be enough initially, until we get the packet length
|
||||
memset(s->out_buffer + 1, 0, 20 - 1); // just return zeros
|
||||
}
|
||||
else if(value == 0x47) { // unknown command, probably used to clear the interrupt
|
||||
s->buf_size = 2;
|
||||
}
|
||||
else if(value == MT_CMD_GET_CMD_STATUS) {
|
||||
s->buf_size = 16;
|
||||
prepare_cmd_status_response(s);
|
||||
}
|
||||
else if(value == MT_CMD_GET_INTERFACE_VERSION) {
|
||||
s->buf_size = 16;
|
||||
prepare_interface_version_response(s);
|
||||
}
|
||||
else if(value == MT_CMD_GET_REPORT_INFO) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_SHORT_CONTROL_WRITE) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_SHORT_CONTROL_READ) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_FRAME_READ) {
|
||||
s->buf_size = sizeof(MTFrame);
|
||||
free(s->out_buffer);
|
||||
s->out_buffer = (uint8_t *) s->next_frame;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown command 0x%02x!", value);
|
||||
}
|
||||
}
|
||||
|
||||
s->in_buffer[s->in_buffer_ind] = value;
|
||||
s->in_buffer_ind++;
|
||||
|
||||
if(s->cur_cmd == MT_CMD_HBPP_DATA_PACKET && s->in_buffer_ind == 10) {
|
||||
// verify the header checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 2; i < 8; i++) {
|
||||
checksum += s->in_buffer[i];
|
||||
}
|
||||
|
||||
if(checksum != (s->in_buffer[8] << 8 | s->in_buffer[9])) {
|
||||
hw_error("HBPP data header checksum doesn't match!");
|
||||
}
|
||||
|
||||
uint32_t data_len = (s->in_buffer[2] << 10) | (s->in_buffer[3] << 2) + 5;
|
||||
// extend the lengths of the in/out buffers
|
||||
free(s->in_buffer);
|
||||
s->in_buffer = malloc(data_len + 0x10);
|
||||
|
||||
free(s->out_buffer);
|
||||
s->out_buffer = malloc(data_len);
|
||||
memset(s->out_buffer, 0, data_len);
|
||||
s->buf_size = data_len;
|
||||
s->buf_ind = 0;
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_GET_REPORT_INFO && s->in_buffer_ind == 2) {
|
||||
prepare_report_info_response(s, s->in_buffer[1]);
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_WRITE && s->in_buffer_ind == 16) {
|
||||
// TODO we should persist the report here!
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_READ && s->in_buffer_ind == 2) {
|
||||
prepare_short_control_response(s, s->in_buffer[1]);
|
||||
}
|
||||
|
||||
// TODO process register writes!
|
||||
|
||||
uint8_t ret_val = s->out_buffer[s->buf_ind];
|
||||
s->buf_ind++;
|
||||
|
||||
//printf("<MULTITOUCH> Got value: 0x%02x, returning 0x%02x (index: %d, buffer length: %d)\n", value, ret_val, s->buf_ind, s->buf_size);
|
||||
|
||||
if(s->buf_ind == s->buf_size) {
|
||||
//printf("Finished command 0x%02x\n", s->cur_cmd);
|
||||
|
||||
if(s->cur_cmd == 0x1E) {
|
||||
// make sure we return a success status on the next HBPP ACK
|
||||
s->hbpp_atn_ack_response[0] = 0x4A;
|
||||
s->hbpp_atn_ack_response[1] = 0xD1;
|
||||
}
|
||||
|
||||
// we're done with the command
|
||||
s->cur_cmd = 0;
|
||||
s->buf_size = 0;
|
||||
//free(s->out_buffer);
|
||||
//free(s->in_buffer);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static MTFrame *get_frame(IPodTouchMultitouchState *s, uint8_t event, float x, float y, uint16_t radius1, uint16_t radius2, uint16_t radius3, uint16_t contactDensity) {
|
||||
MTFrame *frame = calloc(sizeof(MTFrame), sizeof(uint8_t *));
|
||||
|
||||
uint16_t data_len = sizeof(MTFrameHeader) + sizeof(FingerData) + 2;
|
||||
|
||||
/// create the frame length packet
|
||||
frame->frame_length.cmd = MT_CMD_FRAME_READ;
|
||||
frame->frame_length.length1 = (data_len & 0xFF);
|
||||
frame->frame_length.length2 = (data_len >> 8) & 0xFF;
|
||||
|
||||
uint16_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_length)[i];
|
||||
}
|
||||
frame->frame_length.checksum1 = (checksum & 0xFF);
|
||||
frame->frame_length.checksum2 = (checksum >> 8) & 0xFF;
|
||||
|
||||
// create the frame packet
|
||||
frame->frame_packet.cmd = MT_CMD_FRAME_READ;
|
||||
frame->frame_packet.length1 = (data_len & 0xFF);
|
||||
frame->frame_packet.length2 = (data_len >> 8) & 0xFF;
|
||||
|
||||
checksum = 0;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_length)[i];
|
||||
}
|
||||
|
||||
// the first five bytes have to sum up to 0.
|
||||
frame->frame_packet.checksum_pad = 0xFF - (checksum & 0xFF) + 1;
|
||||
|
||||
frame->frame_packet.header.type = MT_FRAME_TYPE_PATH;
|
||||
frame->frame_packet.header.frameNum = s->frame_counter;
|
||||
frame->frame_packet.header.headerLen = sizeof(MTFrameHeader);
|
||||
uint64_t elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 1000000;
|
||||
frame->frame_packet.header.timestamp = elapsed_ns;
|
||||
frame->frame_packet.header.numFingers = 1;
|
||||
frame->frame_packet.header.fingerDataLen = sizeof(FingerData);
|
||||
|
||||
// create the finger data
|
||||
frame->finger_data.id = 1;
|
||||
frame->finger_data.event = event;
|
||||
frame->finger_data.unk_2 = 2;
|
||||
frame->finger_data.unk_3 = 1;
|
||||
|
||||
// compute the velocity
|
||||
int diff_x = (int)((x - s->prev_touch_x) * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
|
||||
int diff_y = (int)((x - s->prev_touch_y) * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
|
||||
frame->finger_data.velX = diff_x / (elapsed_ns + 1 - s->last_frame_timestamp) * 1000;
|
||||
frame->finger_data.velY = diff_y / (elapsed_ns + 1 - s->last_frame_timestamp) * 1000;
|
||||
|
||||
frame->finger_data.x = (int)(x * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
|
||||
frame->finger_data.y = (int)(y * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
|
||||
frame->finger_data.radius1 = radius1;
|
||||
frame->finger_data.radius2 = radius2;
|
||||
frame->finger_data.radius3 = radius3;
|
||||
frame->finger_data.angle = 19317;
|
||||
frame->finger_data.contactDensity = contactDensity; // seems to be a medium press
|
||||
|
||||
// compute the checksum over the frame data.
|
||||
checksum = 0;
|
||||
for(int i = 0; i < data_len - 2; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_packet.header)[i];
|
||||
}
|
||||
frame->checksum1 = (checksum & 0xFF);
|
||||
frame->checksum2 = (checksum >> 8) & 0xFF;
|
||||
|
||||
s->last_frame_timestamp = elapsed_ns;
|
||||
s->frame_counter += 1;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_inform_frame_ready(IPodTouchMultitouchState *s) {
|
||||
s->sysic->gpio_int_status[3] |= (1 << 13); // the multitouch interrupt bit is in group 3 (32 interrupts per group), and the 13th of the 3th group
|
||||
qemu_irq_raise(s->sysic->gpio_irqs[3]);
|
||||
}
|
||||
|
||||
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s) {
|
||||
s->touch_down = true;
|
||||
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_START, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
|
||||
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s) {
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_ENDED, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
||||
s->touch_down = false;
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
timer_del(s->touch_timer);
|
||||
timer_mod(s->touch_end_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
|
||||
static void touch_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_MOVED, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
if(s->touch_down) {
|
||||
// reschedule the timer
|
||||
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
}
|
||||
|
||||
static void touch_end_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
||||
s->touch_down = false;
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_realize(SSIPeripheral *d, Error **errp)
|
||||
{
|
||||
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(d);
|
||||
memset(s->hbpp_atn_ack_response, 0, 2);
|
||||
s->touch_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_timer_tick, s);
|
||||
s->touch_end_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_end_timer_tick, s);
|
||||
|
||||
s->prev_touch_x = 0;
|
||||
s->prev_touch_y = 0;
|
||||
s->last_frame_timestamp = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
|
||||
k->realize = ipod_touch_multitouch_realize;
|
||||
k->transfer = ipod_touch_multitouch_transfer;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_multitouch_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MULTITOUCH,
|
||||
.parent = TYPE_SSI_PERIPHERAL,
|
||||
.instance_size = sizeof(IPodTouchMultitouchState),
|
||||
.class_init = ipod_touch_multitouch_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_multitouch_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_multitouch_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_multitouch_register_types)
|
||||
@@ -0,0 +1,138 @@
|
||||
#include "hw/arm/ipod_touch_nor_spi.h"
|
||||
|
||||
static void initialize_nor(IPodTouchNORSPIState *s)
|
||||
{
|
||||
unsigned long fsize;
|
||||
if (g_file_get_contents(s->nor_path, (char **)&s->nor_data, &fsize, NULL)) {
|
||||
s->nor_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t ipod_touch_nor_spi_transfer(SSIPeripheral *dev, uint32_t value)
|
||||
{
|
||||
IPodTouchNORSPIState *s = IPOD_TOUCH_NOR_SPI(dev);
|
||||
|
||||
if(s->cur_cmd == NOR_READ_DATA_CMD && s->in_buf_cur_ind == s->in_buf_size && value != 0xFF) {
|
||||
// if we are currently reading from the NOR data and we receive a value that's not the sentinel, reset the current command.
|
||||
s->cur_cmd = 0;
|
||||
}
|
||||
|
||||
if(s->cur_cmd == 0) {
|
||||
// this is a new command -> set it
|
||||
s->cur_cmd = value;
|
||||
s->out_buf = malloc(0x1000);
|
||||
s->in_buf = malloc(0x1000);
|
||||
s->in_buf[0] = value;
|
||||
s->in_buf_size = 0;
|
||||
s->in_buf_cur_ind = 1;
|
||||
s->out_buf_cur_ind = 0;
|
||||
|
||||
if(value == NOR_WRITE_TO_STATUS_REG) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 1;
|
||||
s->out_buf[0] = 0;
|
||||
}
|
||||
else if(value == NOR_GET_STATUS_CMD) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 1;
|
||||
s->out_buf[0] = 0;
|
||||
}
|
||||
else if(value == NOR_WRITE_DATA_CMD) {
|
||||
s->in_buf_size = 4;
|
||||
s->out_buf_size = 256;
|
||||
for(int i = 0; i < 256; i++) { s->out_buf[i] = 0; }; // TODO we ignore this command for now
|
||||
}
|
||||
else if(value == NOR_READ_DATA_CMD) {
|
||||
s->in_buf_size = 4;
|
||||
s->out_buf_size = 4096;
|
||||
}
|
||||
else if(value == NOR_ERASE_BLOCK) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 3;
|
||||
for(int i = 0; i < 3; i++) { s->out_buf[i] = 0x0; } // TODO we ignore this command for now
|
||||
}
|
||||
else if(value == NOR_ENABLE_WRITE) {
|
||||
s->write_enabled = 1;
|
||||
s->cur_cmd = 0;
|
||||
}
|
||||
else if(value == NOR_DISABLE_WRITE) {
|
||||
s->write_enabled = 0;
|
||||
s->cur_cmd = 0;
|
||||
}
|
||||
else if(value == NOR_GET_JEDECID) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 3;
|
||||
s->out_buf[0] = 0x1F; // vendor: atmel, device: 0x02 -> AT25DF081A
|
||||
s->out_buf[1] = 0x45;
|
||||
s->out_buf[2] = 0x02;
|
||||
}
|
||||
else {
|
||||
printf("Unknown command %d!\n", value);
|
||||
hw_error("Unknown command 0x%02x!", value);
|
||||
}
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
else if(s->cur_cmd != 0 && s->in_buf_cur_ind < s->in_buf_size) {
|
||||
// we're reading the command
|
||||
s->in_buf[s->in_buf_cur_ind] = value;
|
||||
s->in_buf_cur_ind++;
|
||||
|
||||
if(s->cur_cmd == NOR_GET_STATUS_CMD && s->in_buf_cur_ind == s->in_buf_size) {
|
||||
s->out_buf[0] = 0x0; // indicates that the NOR is ready
|
||||
}
|
||||
else if(s->cur_cmd == NOR_READ_DATA_CMD && s->in_buf_cur_ind == s->in_buf_size) {
|
||||
if(!s->nor_initialized) { initialize_nor(s); }
|
||||
s->nor_read_ind = (s->in_buf[1] << 16) | (s->in_buf[2] << 8) | s->in_buf[3];
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
else {
|
||||
uint8_t ret_val;
|
||||
// otherwise, we're outputting the response
|
||||
if(s->cur_cmd == NOR_READ_DATA_CMD) {
|
||||
uint8_t ret_val = s->nor_data[s->nor_read_ind];
|
||||
s->nor_read_ind++;
|
||||
return ret_val;
|
||||
}
|
||||
else {
|
||||
ret_val = s->out_buf[s->out_buf_cur_ind];
|
||||
s->out_buf_cur_ind++;
|
||||
|
||||
if(s->cur_cmd != 0 && (s->out_buf_cur_ind == s->out_buf_size)) {
|
||||
// the command is done - clean up
|
||||
s->cur_cmd = 0;
|
||||
free(s->in_buf);
|
||||
free(s->out_buf);
|
||||
}
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_nor_spi_realize(SSIPeripheral *d, Error **errp)
|
||||
{
|
||||
IPodTouchNORSPIState *s = IPOD_TOUCH_NOR_SPI(d);
|
||||
s->nor_initialized = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_nor_spi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
|
||||
k->realize = ipod_touch_nor_spi_realize;
|
||||
k->transfer = ipod_touch_nor_spi_transfer;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_nor_spi_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_NOR_SPI,
|
||||
.parent = TYPE_SSI_PERIPHERAL,
|
||||
.instance_size = sizeof(IPodTouchNORSPIState),
|
||||
.class_init = ipod_touch_nor_spi_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_nor_spi_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_nor_spi_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_nor_spi_register_types)
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "hw/arm/ipod_touch_pcf50633_pmu.h"
|
||||
|
||||
static int pcf50633_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int_to_bcd(int value) {
|
||||
int shift = 0;
|
||||
int res = 0;
|
||||
while (value > 0) {
|
||||
res |= (value % 10) << (shift++ << 2);
|
||||
value /= 10;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t pcf50633_recv(I2CSlave *i2c)
|
||||
{
|
||||
Pcf50633State *s = PCF50633(i2c);
|
||||
printf("Reading PMU register %d\n", s->cmd);
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm tm = *localtime(&t);
|
||||
|
||||
int res = 0;
|
||||
|
||||
switch(s->cmd) {
|
||||
case PMU_MBCS1:
|
||||
res = 0; // battery power source
|
||||
break;
|
||||
case PMU_ADCC1:
|
||||
res = 0; // battery charge voltage
|
||||
break;
|
||||
case PMU_RTCSC: // seconds
|
||||
res = int_to_bcd(tm.tm_sec);
|
||||
break;
|
||||
case PMU_RTCMN: // minutes
|
||||
res = int_to_bcd(tm.tm_min);
|
||||
break;
|
||||
case PMU_RTCHR: // hours
|
||||
res = int_to_bcd(tm.tm_hour);
|
||||
break;
|
||||
case PMU_RTCDT: // days
|
||||
res = int_to_bcd(tm.tm_mday);
|
||||
break;
|
||||
case PMU_RTCMT: // month
|
||||
res = int_to_bcd(tm.tm_mon + 1);
|
||||
break;
|
||||
case PMU_RTCYR: // year
|
||||
res = int_to_bcd(tm.tm_year - 100); // the year counts from 1900
|
||||
break;
|
||||
case 0x67:
|
||||
res = 1; // whether we should enable debug UARTS
|
||||
break;
|
||||
case 0x69:
|
||||
res = 0; // boot count error/panic
|
||||
break;
|
||||
case 0x76:
|
||||
res = 0; // unknown register
|
||||
break;
|
||||
default:
|
||||
res = 0;
|
||||
}
|
||||
|
||||
s->cmd += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pcf50633_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
Pcf50633State *s = PCF50633(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcf50633_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void pcf50633_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = pcf50633_event;
|
||||
k->recv = pcf50633_recv;
|
||||
k->send = pcf50633_send;
|
||||
}
|
||||
|
||||
static const TypeInfo pcf50633_info = {
|
||||
.name = TYPE_PCF50633,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = pcf50633_init,
|
||||
.instance_size = sizeof(Pcf50633State),
|
||||
.class_init = pcf50633_class_init,
|
||||
};
|
||||
|
||||
static void pcf50633_register_types(void)
|
||||
{
|
||||
type_register_static(&pcf50633_info);
|
||||
}
|
||||
|
||||
type_init(pcf50633_register_types)
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "hw/arm/ipod_touch_pke.h"
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/bio.h>
|
||||
|
||||
static uint8_t *datahex(char* string) {
|
||||
|
||||
if(string == NULL)
|
||||
return NULL;
|
||||
|
||||
size_t slength = strlen(string);
|
||||
if((slength % 2) != 0) // must be even
|
||||
return NULL;
|
||||
|
||||
size_t dlength = slength / 2;
|
||||
|
||||
uint8_t* data = malloc(dlength);
|
||||
memset(data, 0, dlength);
|
||||
|
||||
size_t index = 0;
|
||||
while (index < slength) {
|
||||
char c = string[index];
|
||||
int value = 0;
|
||||
if(c >= '0' && c <= '9')
|
||||
value = (c - '0');
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
value = (10 + (c - 'A'));
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
value = (10 + (c - 'a'));
|
||||
else {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data[(index/2)] += value << (((index + 1) % 2) * 4);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_pke_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchPKEState *s = (IPodTouchPKEState *)opaque;
|
||||
|
||||
//printf("%s: offset 0x%08x\n", __FUNCTION__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case REG_PKE_SEG_SIZE:
|
||||
return s->seg_size_reg;
|
||||
case REG_PKE_SEG_START ... (REG_PKE_SEG_START + 1024):
|
||||
{
|
||||
uint32_t *res = (uint32_t *)s->segments;
|
||||
return res[(offset - REG_PKE_SEG_START) / 4];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_pke_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchPKEState *s = (IPodTouchPKEState *)opaque;
|
||||
|
||||
//printf("%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
|
||||
switch(offset) {
|
||||
case 0x0:
|
||||
s->num_started = 0;
|
||||
break;
|
||||
case 0x10:
|
||||
break;
|
||||
case REG_PKE_SEG_START ... (REG_PKE_SEG_START + 1024):
|
||||
{
|
||||
uint32_t *segments_cast = (uint32_t *)s->segments;
|
||||
segments_cast[(offset - REG_PKE_SEG_START) / 4] = value;
|
||||
break;
|
||||
}
|
||||
case REG_PKE_START:
|
||||
{
|
||||
s->num_started++;
|
||||
|
||||
if(s->num_started == 5) { // TODO this is arbitrary!
|
||||
|
||||
|
||||
BIGNUM *mod_bn = BN_lebin2bn(s->segments, s->segment_size, NULL);
|
||||
BIGNUM *base_bn = BN_lebin2bn(s->segments + s->segment_size, s->segment_size, NULL);
|
||||
BIGNUM *exp_bn = BN_new();
|
||||
BN_dec2bn(&exp_bn,"65537");
|
||||
// BN_print(BIO_new_fp(stdout, BIO_NOCLOSE), exp_bn);
|
||||
// printf("\n\n");
|
||||
|
||||
BIGNUM *res = BN_new();
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BN_mod_exp(res, base_bn, exp_bn, mod_bn, ctx);
|
||||
// BN_print(BIO_new_fp(stdout, BIO_NOCLOSE), res);
|
||||
// printf("\n\n");
|
||||
|
||||
char *res_hex = datahex(BN_bn2hex(res));
|
||||
|
||||
// copy this into SEG1 - note that the hex conversion removes the first 0x00 bytes so we add it back and shift everything to the right one place.
|
||||
for(int i = 0; i < (s->segment_size - 1); i++) { s->segments[s->segment_size + s->segment_size - 2 - i] = res_hex[i]; }
|
||||
s->segments[s->segment_size + s->segment_size - 1] = 0x0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case REG_PKE_SEG_SIZE:
|
||||
s->seg_size_reg = value;
|
||||
uint32_t size_bit = (s->seg_size_reg >> 6);
|
||||
if(size_bit == 0) { s->segment_size = 256; }
|
||||
else if(size_bit == 1) { s->segment_size = 128; }
|
||||
else { }
|
||||
break;
|
||||
case REG_PKE_SWRESET:
|
||||
s->num_started = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pke_ops = {
|
||||
.read = ipod_touch_pke_read,
|
||||
.write = ipod_touch_pke_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_pke_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchPKEState *s = IPOD_TOUCH_PKE(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &pke_ops, s, "pke", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_pke_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_pke_info = {
|
||||
.name = TYPE_IPOD_TOUCH_PKE,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchPKEState),
|
||||
.instance_init = ipod_touch_pke_init,
|
||||
.class_init = ipod_touch_pke_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_pke_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "hw/arm/ipod_touch_scaler_csc.h"
|
||||
|
||||
static uint64_t ipod_touch_scaler_csc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_scaler_csc_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||
{
|
||||
IPodTouchScalerCSCState *s = IPOD_TOUCH_SCALER_CSC(opaque);
|
||||
//printf("%s (base %d): writing 0x%08x to 0x%08x\n", __func__, s->base, data, addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_scaler_csc_ops = {
|
||||
.read = ipod_touch_scaler_csc_read,
|
||||
.write = ipod_touch_scaler_csc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_scaler_csc_init(Object *obj)
|
||||
{
|
||||
IPodTouchScalerCSCState *s = IPOD_TOUCH_SCALER_CSC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_scaler_csc_ops, s, TYPE_IPOD_TOUCH_SCALER_CSC, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_scaler_csc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_scaler_csc_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SCALER_CSC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchScalerCSCState),
|
||||
.instance_init = ipod_touch_scaler_csc_init,
|
||||
.class_init = ipod_touch_scaler_csc_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_scaler_csc_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_scaler_csc_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_scaler_csc_register_types)
|
||||
@@ -0,0 +1,252 @@
|
||||
#include "hw/arm/ipod_touch_sdio.h"
|
||||
|
||||
static void trigger_irq(void *opaque)
|
||||
{
|
||||
IPodTouchSDIOState *s = (IPodTouchSDIOState *)opaque;
|
||||
s->irq_reg = 0x2;
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
|
||||
void sdio_exec_cmd(IPodTouchSDIOState *s)
|
||||
{
|
||||
uint32_t cmd_type = s->cmd & 0x3f;
|
||||
uint32_t addr = (s->arg >> 9) & 0x1ffff;
|
||||
uint32_t func = (s->arg >> 28) & 0x7;
|
||||
//printf("SDIO CMD: %d, ADDR: %d, FUNC: %d\n", cmd_type, addr, func);
|
||||
if(cmd_type == 0x3) {
|
||||
// RCA request - ignore
|
||||
}
|
||||
else if(cmd_type == 0x5) {
|
||||
if(addr == 0) {
|
||||
// reading slot 0 - make sure there is a device here
|
||||
//s->resp0 = (1 << 31) /* indicate ready */ | (BCM4325_FUNCTIONS << CMD5_FUNC_OFFSET) /* number of functions */;
|
||||
}
|
||||
}
|
||||
else if(cmd_type == 0x7) {
|
||||
// select card - ignore
|
||||
}
|
||||
else if(cmd_type == 0x34) {
|
||||
// CMD52 - read/write from a register
|
||||
bool is_write = (s->arg >> 31) != 0;
|
||||
if(is_write) {
|
||||
uint8_t data = s->arg & 0xFF;
|
||||
s->registers[addr] = data;
|
||||
if(addr == 0x2) { s->registers[0x3] = data; } // if we write to register 2, we also write the same result to register 3 (this is the enabled functions register)
|
||||
printf("SDIO: Executing cmd52 by writing 0x%02x to register 0x%02x\n", data, addr);
|
||||
} else {
|
||||
if(addr == 0x1000e) {
|
||||
// misc register
|
||||
s->resp0 = (1 << 6) /* enable ALP clock */ | (1 << 7); /* enable HT clock */
|
||||
}
|
||||
else if(addr == 0x2020) {
|
||||
// some indication that packets are ready??
|
||||
s->resp0 = (1 << 6);
|
||||
}
|
||||
else {
|
||||
printf("SDIO: Executing cmd52 by reading from 0x%02x (value: 0x%02x)\n", addr, s->registers[addr]);
|
||||
s->resp0 = s->registers[addr];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(cmd_type == 0x35) {
|
||||
// CMD53 - block transfer
|
||||
addr = addr & 0x7fff;
|
||||
bool is_write = (s->arg >> 31) != 0;
|
||||
printf("SDIO: Executing cmd53 with block size %d and %d blocks (reg address: 0x%08x, destination address: 0x%08x, write? %d)\n", s->blklen, s->numblk, addr, s->baddr, is_write);
|
||||
|
||||
if(is_write) {
|
||||
if(func == 0x1) {
|
||||
cpu_physical_memory_read(s->baddr, &s->registers[addr], s->blklen * s->numblk);
|
||||
}
|
||||
else if(func == 0x2) {
|
||||
// this is a BCM4325 command - add a frame to the queue and schedule the IRQ request to indicate that the command has been completed
|
||||
BCM4325FrameHeaderPacket *frame_header = calloc(sizeof(BCM4325FrameHeaderPacket), sizeof(uint8_t *));
|
||||
uint16_t length = 14;
|
||||
frame_header->frame_length = length;
|
||||
frame_header->checksum = length ^ 0xffff;
|
||||
g_queue_push_tail(s->rx_fifo, frame_header);
|
||||
|
||||
timer_mod(s->irq_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 100);
|
||||
}
|
||||
} else {
|
||||
if(func == 0x1) {
|
||||
if(addr == 0x0) {
|
||||
// chip ID register
|
||||
uint32_t chipid[] = { 0x5 << 0x10 };
|
||||
cpu_physical_memory_write(s->baddr, &chipid, 0x4);
|
||||
}
|
||||
}
|
||||
else if(func == 0x2) {
|
||||
// we're reading a frame
|
||||
BCM4325FrameHeaderPacket *frame_header = (BCM4325FrameHeaderPacket *) g_queue_pop_head(s->rx_fifo);
|
||||
|
||||
if(!frame_header) {
|
||||
// create an empty frame
|
||||
frame_header = calloc(sizeof(BCM4325FrameHeaderPacket), sizeof(uint8_t *));
|
||||
}
|
||||
cpu_physical_memory_write(s->baddr, frame_header, sizeof(BCM4325FrameHeaderPacket));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// toggle IRQ register
|
||||
s->irq_reg = 0x1;
|
||||
qemu_irq_raise(s->irq);
|
||||
//printf("Raised IRQ\n");
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown SDIO command %d", cmd_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_sdio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
|
||||
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
case SDIO_CMD:
|
||||
s->cmd = value;
|
||||
if(value & (1 << 31)) { // execute bit is set
|
||||
sdio_exec_cmd(s);
|
||||
}
|
||||
break;
|
||||
case SDIO_ARGU:
|
||||
s->arg = value;
|
||||
break;
|
||||
case SDIO_STATE:
|
||||
s->state = value;
|
||||
break;
|
||||
case SDIO_STAC:
|
||||
s->stac = value;
|
||||
break;
|
||||
case SDIO_CSR:
|
||||
s->csr = value;
|
||||
break;
|
||||
case SDIO_IRQ:
|
||||
qemu_irq_lower(s->irq);
|
||||
break;
|
||||
case SDIO_IRQMASK:
|
||||
s->irq_mask = value;
|
||||
break;
|
||||
case SDIO_BADDR:
|
||||
s->baddr = value;
|
||||
break;
|
||||
case SDIO_BLKLEN:
|
||||
s->blklen = value;
|
||||
break;
|
||||
case SDIO_NUMBLK:
|
||||
s->numblk = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_sdio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//printf("%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case SDIO_CMD:
|
||||
return s->cmd;
|
||||
case SDIO_ARGU:
|
||||
return s->arg;
|
||||
case SDIO_STATE:
|
||||
return s->state;
|
||||
case SDIO_STAC:
|
||||
return s->stac;
|
||||
case SDIO_DSTA:
|
||||
return (1 << 0) | (1 << 4) ; // 0x1 indicates that the SDIO is ready for a CMD, (1 << 4) that the command is complete
|
||||
case SDIO_RESP0:
|
||||
return s->resp0;
|
||||
case SDIO_RESP1:
|
||||
return s->resp1;
|
||||
case SDIO_RESP2:
|
||||
return s->resp2;
|
||||
case SDIO_RESP3:
|
||||
return s->resp3;
|
||||
case SDIO_CSR:
|
||||
return s->csr;
|
||||
case SDIO_IRQ:
|
||||
return s->irq_reg;
|
||||
case SDIO_IRQMASK:
|
||||
return s->irq_mask;
|
||||
case SDIO_BADDR:
|
||||
return s->baddr;
|
||||
case SDIO_BLKLEN:
|
||||
return s->blklen;
|
||||
case SDIO_NUMBLK:
|
||||
return s->numblk;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_sdio_ops = {
|
||||
.read = ipod_touch_sdio_read,
|
||||
.write = ipod_touch_sdio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sdio_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchSDIOState *s = IPOD_TOUCH_SDIO(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
s->registers[0x9] = CIS_OFFSET; // registers 0x9 - 0xB contain the relative address offset, which we set to 0xC8 (200)
|
||||
s->registers[19] = 0x1; // enable support for high speed mode
|
||||
|
||||
// set the vendor information
|
||||
s->registers[CIS_OFFSET] = CIS_MANUFACTURER_ID;
|
||||
s->registers[CIS_OFFSET + 1] = 0x4;
|
||||
s->registers[CIS_OFFSET + 2] = BCM4325_MANUFACTURER & 0xFF;
|
||||
s->registers[CIS_OFFSET + 3] = (BCM4325_MANUFACTURER >> 8) & 0xFF;
|
||||
s->registers[CIS_OFFSET + 4] = BCM4325_PRODUCT_ID & 0xFF;
|
||||
s->registers[CIS_OFFSET + 5] = (BCM4325_PRODUCT_ID >> 8) & 0xFF;
|
||||
|
||||
// set the MAC address (00:23:32:6E:AA:10)
|
||||
s->registers[CIS_OFFSET + 6] = CIS_FUNCTION_EXTENSION;
|
||||
s->registers[CIS_OFFSET + 8] = 0x4; // unknown
|
||||
s->registers[CIS_OFFSET + 9] = 0x6; // the length of the MAC address
|
||||
s->registers[CIS_OFFSET + 10] = 0x0;
|
||||
s->registers[CIS_OFFSET + 11] = 0x23;
|
||||
s->registers[CIS_OFFSET + 12] = 0x32;
|
||||
s->registers[CIS_OFFSET + 13] = 0x6E;
|
||||
s->registers[CIS_OFFSET + 14] = 0xAA;
|
||||
s->registers[CIS_OFFSET + 15] = 0x10;
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_sdio_ops, s, TYPE_IPOD_TOUCH_SDIO, 4096);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->irq_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, trigger_irq, s);
|
||||
|
||||
s->rx_fifo = g_queue_new();
|
||||
}
|
||||
|
||||
static void ipod_touch_sdio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sdio_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SDIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSDIOState),
|
||||
.instance_init = ipod_touch_sdio_init,
|
||||
.class_init = ipod_touch_sdio_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_sdio_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sdio_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_sdio_register_types)
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "hw/arm/ipod_touch_sha1.h"
|
||||
|
||||
static uint64_t swapLong(void *X) {
|
||||
uint64_t x = (uint64_t) X;
|
||||
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
|
||||
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
|
||||
x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
|
||||
return x;
|
||||
}
|
||||
|
||||
static void flush_hw_buffer(IPodTouchSHA1State *s) {
|
||||
// Flush the hardware buffer to the state buffer and clear the buffer.
|
||||
memcpy(s->buffer + s->buffer_ind, (uint8_t *)s->hw_buffer, 0x40);
|
||||
memset(s->hw_buffer, 0, 0x40);
|
||||
s->hw_buffer_dirty = false;
|
||||
s->buffer_ind += 0x40;
|
||||
}
|
||||
|
||||
static void sha1_reset(IPodTouchSHA1State *s)
|
||||
{
|
||||
s->config = 0;
|
||||
s->memory_start = 0;
|
||||
s->memory_mode = 0;
|
||||
s->insize = 0;
|
||||
memset(&s->buffer, 0, SHA1_BUFFER_SIZE);
|
||||
memset(&s->hw_buffer, 0, 0x10 * sizeof(uint32_t));
|
||||
s->buffer_ind = 0;
|
||||
memset(&s->hashout, 0, 0x14);
|
||||
s->hw_buffer_dirty = false;
|
||||
s->hash_computed = false;
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_sha1_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchSHA1State *s = (IPodTouchSHA1State *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset 0x%08x\n", __FUNCTION__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case SHA_CONFIG:
|
||||
return s->config;
|
||||
case SHA_RESET:
|
||||
return 0;
|
||||
case SHA_MEMORY_START:
|
||||
return s->memory_start;
|
||||
case SHA_MEMORY_MODE:
|
||||
return s->memory_mode;
|
||||
case SHA_INSIZE:
|
||||
return s->insize;
|
||||
/* Hash result ouput */
|
||||
case 0x20 ... 0x34:
|
||||
//fprintf(stderr, "Hash out %08x\n", *(uint32_t *)&s->hashout[offset - 0x20]);
|
||||
if(!s->hash_computed) {
|
||||
// lazy compute the final hash by inspecting the last eight bytes of the buffer, which contains the length of the input data.
|
||||
uint64_t data_length = swapLong(((uint64_t *)s->buffer)[s->buffer_ind / 8 - 1]) / 8;
|
||||
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, s->buffer, data_length);
|
||||
SHA1_Final(s->hashout, &ctx);
|
||||
s->hash_computed = true;
|
||||
}
|
||||
|
||||
return *(uint32_t *)&s->hashout[offset - 0x20];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_sha1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchSHA1State *s = (IPodTouchSHA1State *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
|
||||
switch(offset) {
|
||||
case SHA_CONFIG:
|
||||
if(value == 0x2 || value == 0xa)
|
||||
{
|
||||
if(s->hw_buffer_dirty) {
|
||||
flush_hw_buffer(s);
|
||||
}
|
||||
|
||||
if(s->memory_mode)
|
||||
{
|
||||
// we are in memory mode - gradually add the memory to the buffer
|
||||
for(int i = 0; i < s->insize / 0x40; i++) {
|
||||
cpu_physical_memory_read(s->memory_start + i * 0x40, s->buffer + s->buffer_ind, 0x40);
|
||||
s->buffer_ind += 0x40;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->config = value;
|
||||
}
|
||||
break;
|
||||
case SHA_RESET:
|
||||
sha1_reset(s);
|
||||
break;
|
||||
case SHA_MEMORY_START:
|
||||
s->memory_start = value;
|
||||
break;
|
||||
case SHA_MEMORY_MODE:
|
||||
s->memory_mode = value;
|
||||
break;
|
||||
case SHA_INSIZE:
|
||||
assert(value <= SHA1_BUFFER_SIZE);
|
||||
s->insize = value;
|
||||
break;
|
||||
case 0x40 ... 0x7c:
|
||||
// write to the hardware buffer
|
||||
s->hw_buffer[(offset - 0x40) / 4] |= value;
|
||||
s->hw_buffer_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sha1_ops = {
|
||||
.read = ipod_touch_sha1_read,
|
||||
.write = ipod_touch_sha1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sha1_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchSHA1State *s = IPOD_TOUCH_SHA1(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &sha1_ops, s, "sha1", 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
sha1_reset(s);
|
||||
}
|
||||
|
||||
static void ipod_touch_sha1_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sha1_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SHA1,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSHA1State),
|
||||
.instance_init = ipod_touch_sha1_init,
|
||||
.class_init = ipod_touch_sha1_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sha1_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* S5L8900 SPI Emulation
|
||||
*
|
||||
* by cmw
|
||||
*/
|
||||
|
||||
#include "hw/arm/ipod_touch_spi.h"
|
||||
|
||||
static int apple_spi_word_size(IPodTouchSPIState *s)
|
||||
{
|
||||
switch (R_CFG_WORD_SIZE(REG(s, R_CFG))) {
|
||||
case R_CFG_WORD_SIZE_8B:
|
||||
return 1;
|
||||
case R_CFG_WORD_SIZE_16B:
|
||||
return 2;
|
||||
case R_CFG_WORD_SIZE_32B:
|
||||
return 4;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void apple_spi_update_xfer_tx(IPodTouchSPIState *s)
|
||||
{
|
||||
if (fifo8_is_empty(&s->tx_fifo)) {
|
||||
REG(s, R_STATUS) |= R_STATUS_TXEMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_xfer_rx(IPodTouchSPIState *s)
|
||||
{
|
||||
if (!fifo8_is_empty(&s->rx_fifo)) {
|
||||
REG(s, R_STATUS) |= R_STATUS_RXREADY;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_irq(IPodTouchSPIState *s)
|
||||
{
|
||||
uint32_t irq = 0;
|
||||
uint32_t mask = 0;
|
||||
|
||||
if (REG(s, R_CFG) & R_CFG_IE_RXREADY) {
|
||||
mask |= R_STATUS_RXREADY;
|
||||
}
|
||||
if (REG(s, R_CFG) & R_CFG_IE_TXEMPTY) {
|
||||
mask |= R_STATUS_TXEMPTY;
|
||||
}
|
||||
if (REG(s, R_CFG) & R_CFG_IE_COMPLETE) {
|
||||
mask |= R_STATUS_COMPLETE;
|
||||
}
|
||||
|
||||
if (REG(s, R_STATUS) & mask) {
|
||||
irq = 1;
|
||||
}
|
||||
if (irq != s->last_irq) {
|
||||
s->last_irq = irq;
|
||||
qemu_set_irq(s->irq, irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_cs(IPodTouchSPIState *s)
|
||||
{
|
||||
BusState *b = BUS(s->spi);
|
||||
BusChild *kid = QTAILQ_FIRST(&b->children);
|
||||
if (kid) {
|
||||
// TODO GPIO not properly setup yet
|
||||
//qemu_set_irq(qdev_get_gpio_in_named(kid->child, SSI_GPIO_CS, 0), (REG(s, R_PIN) & R_PIN_CS) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_cs_set(void *opaque, int pin, int level)
|
||||
{
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
|
||||
if (level) {
|
||||
REG(s, R_PIN) |= R_PIN_CS;
|
||||
} else {
|
||||
REG(s, R_PIN) &= ~R_PIN_CS;
|
||||
}
|
||||
apple_spi_update_cs(s);
|
||||
}
|
||||
|
||||
static void apple_spi_run(IPodTouchSPIState *s)
|
||||
{
|
||||
uint32_t tx;
|
||||
uint32_t rx;
|
||||
|
||||
if (!(REG(s, R_CTRL) & R_CTRL_RUN)) {
|
||||
return;
|
||||
}
|
||||
if (REG(s, R_RXCNT) == 0 && REG(s, R_TXCNT) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
apple_spi_update_xfer_tx(s);
|
||||
|
||||
//printf("TX queue: %d, RX queue: %d\n", REG(s, R_TXCNT), REG(s, R_RXCNT));
|
||||
//printf("TX buffer size: %d, RX buffer size: %d\n", fifo8_num_used(&s->tx_fifo), fifo8_num_used(&s->rx_fifo));
|
||||
|
||||
while (REG(s, R_TXCNT) && !fifo8_is_empty(&s->tx_fifo)) {
|
||||
tx = (uint32_t)fifo8_pop(&s->tx_fifo);
|
||||
rx = ssi_transfer(s->spi, tx);
|
||||
REG(s, R_TXCNT)--;
|
||||
apple_spi_update_xfer_tx(s);
|
||||
if (REG(s, R_RXCNT) > 0) {
|
||||
if (fifo8_is_full(&s->rx_fifo)) {
|
||||
hw_error("Rx buffer overflow: %d\n", fifo8_num_free(&s->rx_fifo));
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
|
||||
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
|
||||
} else {
|
||||
fifo8_push(&s->rx_fifo, (uint8_t)rx);
|
||||
REG(s, R_RXCNT)--;
|
||||
apple_spi_update_xfer_rx(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fetch the remaining bytes by sending sentinel bytes.
|
||||
while (!fifo8_is_full(&s->rx_fifo) && (REG(s, R_RXCNT) > 0) && (REG(s, R_CFG) & R_CFG_AGD)) {
|
||||
rx = ssi_transfer(s->spi, 0xff);
|
||||
if (fifo8_is_full(&s->rx_fifo)) {
|
||||
hw_error("Rx buffer overflow: %d\n", fifo8_num_free(&s->rx_fifo));
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
|
||||
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
|
||||
break;
|
||||
} else {
|
||||
fifo8_push(&s->rx_fifo, (uint8_t)rx);
|
||||
REG(s, R_RXCNT)--;
|
||||
apple_spi_update_xfer_rx(s);
|
||||
}
|
||||
}
|
||||
if (REG(s, R_RXCNT) == 0 && REG(s, R_TXCNT) == 0) {
|
||||
REG(s, R_STATUS) |= R_STATUS_COMPLETE;
|
||||
}
|
||||
|
||||
//printf("<after> TX buffer size: %d, RX buffer size: %d\n", fifo8_num_used(&s->tx_fifo), fifo8_num_used(&s->rx_fifo));
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_spi_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
|
||||
//printf("%s (base %d): read from location 0x%08x\n", __func__, s->base, addr);
|
||||
|
||||
uint32_t r;
|
||||
bool run = false;
|
||||
|
||||
r = s->regs[addr >> 2];
|
||||
switch (addr) {
|
||||
case R_RXDATA: {
|
||||
const uint8_t *buf = NULL;
|
||||
int word_size = apple_spi_word_size(s);
|
||||
uint32_t num = 0;
|
||||
if (fifo8_is_empty(&s->rx_fifo)) {
|
||||
hw_error("Rx buffer underflow\n");
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx underflow\n", __func__);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
buf = fifo8_pop_buf(&s->rx_fifo, word_size, &num);
|
||||
memcpy(&r, buf, num);
|
||||
|
||||
if (fifo8_is_empty(&s->rx_fifo)) {
|
||||
run = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_STATUS: {
|
||||
int val = 0;
|
||||
val |= (fifo8_num_used(&s->tx_fifo) << R_STATUS_TXFIFO_SHIFT);
|
||||
val |= (fifo8_num_used(&s->rx_fifo) << R_STATUS_RXFIFO_SHIFT);
|
||||
r |= val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (run) {
|
||||
apple_spi_run(s);
|
||||
}
|
||||
apple_spi_update_irq(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ipod_touch_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||
{
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
|
||||
//printf("%s (base %d): writing 0x%08x to 0x%08x\n", __func__, s->base, data, addr);
|
||||
|
||||
uint32_t r = data;
|
||||
uint32_t *mmio = ®(s, addr);
|
||||
uint32_t old = *mmio;
|
||||
bool cs_flg = false;
|
||||
bool run = false;
|
||||
|
||||
switch (addr) {
|
||||
case R_CTRL:
|
||||
if (r & R_CTRL_TX_RESET) {
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
}
|
||||
if (r & R_CTRL_RX_RESET) {
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
}
|
||||
if (r & R_CTRL_RUN && !fifo8_is_empty(&s->tx_fifo)) {
|
||||
run = true;
|
||||
}
|
||||
break;
|
||||
case R_STATUS:
|
||||
run = true;
|
||||
r = old & (~r);
|
||||
break;
|
||||
case R_PIN:
|
||||
cs_flg = true;
|
||||
break;
|
||||
case R_TXDATA ... R_TXDATA + 3: {
|
||||
int word_size = apple_spi_word_size(s);
|
||||
if (fifo8_is_full(&s->tx_fifo) || fifo8_num_free(&s->tx_fifo) < word_size) {
|
||||
hw_error("Tx buffer overflow: %d\n", fifo8_num_free(&s->tx_fifo));
|
||||
}
|
||||
fifo8_push_all(&s->tx_fifo, (uint8_t *)&r, word_size);
|
||||
break;
|
||||
case R_CFG:
|
||||
run = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*mmio = r;
|
||||
if (cs_flg) {
|
||||
apple_spi_update_cs(s);
|
||||
}
|
||||
if (run) {
|
||||
apple_spi_run(s);
|
||||
}
|
||||
|
||||
if(addr == R_STATUS) {
|
||||
apple_spi_update_xfer_tx(s);
|
||||
apple_spi_update_xfer_rx(s);
|
||||
}
|
||||
|
||||
apple_spi_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps spi_ops = {
|
||||
.read = ipod_touch_spi_read,
|
||||
.write = ipod_touch_spi_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_spi_reset(DeviceState *d)
|
||||
{
|
||||
IPodTouchSPIState *s = (IPodTouchSPIState *)d;
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
}
|
||||
|
||||
static uint32_t base_addr = 0;
|
||||
|
||||
void set_spi_base(uint32_t base)
|
||||
{
|
||||
base_addr = base;
|
||||
}
|
||||
|
||||
static void ipod_touch_spi_realize(DeviceState *dev, struct Error **errp)
|
||||
{
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
char bus_name[32] = { 0 };
|
||||
snprintf(bus_name, sizeof(bus_name), "%s.bus", dev->id);
|
||||
s->spi = ssi_create_bus(dev, (const char *)bus_name);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->cs_line);
|
||||
qdev_init_gpio_in_named(dev, apple_spi_cs_set, SSI_GPIO_CS, 1);
|
||||
char name[5];
|
||||
snprintf(name, 5, "spi%d", base_addr);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &spi_ops, s, name, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
s->base = base_addr;
|
||||
|
||||
fifo8_create(&s->tx_fifo, R_FIFO_TX_DEPTH);
|
||||
fifo8_create(&s->rx_fifo, R_FIFO_RX_DEPTH);
|
||||
|
||||
// create the peripheral
|
||||
switch(s->base) {
|
||||
case 0:
|
||||
{
|
||||
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_NOR_SPI);
|
||||
IPodTouchNORSPIState *nor = IPOD_TOUCH_NOR_SPI(dev);
|
||||
s->nor = nor;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_MULTITOUCH);
|
||||
IPodTouchMultitouchState *mt = IPOD_TOUCH_MULTITOUCH(dev);
|
||||
s->mt = mt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_spi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = ipod_touch_spi_realize;
|
||||
dc->reset = ipod_touch_spi_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_spi_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SPI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSPIState),
|
||||
.class_init = ipod_touch_spi_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_spi_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_spi_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_spi_register_types)
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "hw/arm/ipod_touch_sysic.h"
|
||||
|
||||
static uint64_t ipod_touch_sysic_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case POWER_ID:
|
||||
return s->power_id;
|
||||
case POWER_SETSTATE:
|
||||
case POWER_STATE:
|
||||
return s->power_state;
|
||||
case 0x7a:
|
||||
case 0x7c:
|
||||
return 1;
|
||||
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTLEVEL) / 4;
|
||||
return s->gpio_int_level[group];
|
||||
}
|
||||
case GPIO_INTSTAT ... (GPIO_INTSTAT + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTSTAT) / 4;
|
||||
return s->gpio_int_status[group];
|
||||
}
|
||||
case GPIO_INTEN ... (GPIO_INTEN + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTEN) / 4;
|
||||
return s->gpio_int_enabled[group];
|
||||
}
|
||||
case GPIO_INTTYPE ... (GPIO_INTTYPE + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTTYPE) / 4;
|
||||
return s->gpio_int_type[group];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_sysic_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
|
||||
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch (addr) {
|
||||
case POWER_ID:
|
||||
s->power_id = val;
|
||||
break;
|
||||
case POWER_ONCTRL:
|
||||
if((val & 0x20) != 0 || (val & 0x4) != 0 || (val & POWER_ID_ADM) != 0) { break; } // make sure that we do not record the 'on' state of some devices so it appears like they are turned on immediately.
|
||||
s->power_state = val;
|
||||
break;
|
||||
case POWER_OFFCTRL:
|
||||
s->power_state = val;
|
||||
break;
|
||||
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
break;
|
||||
}
|
||||
case GPIO_INTSTAT ... (GPIO_INTSTAT + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTSTAT) / 4;
|
||||
|
||||
// acknowledge the interrupts and clear the corresponding bits
|
||||
s->gpio_int_status[group] = s->gpio_int_status[group] & ~val;
|
||||
|
||||
qemu_irq_lower(s->gpio_irqs[group]);
|
||||
|
||||
break;
|
||||
}
|
||||
case GPIO_INTEN ... (GPIO_INTEN + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTEN) / 4;
|
||||
s->gpio_int_enabled[group] = val;
|
||||
break;
|
||||
}
|
||||
case GPIO_INTTYPE ... (GPIO_INTTYPE + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTTYPE) / 4;
|
||||
s->gpio_int_type[group] = val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_sysic_ops = {
|
||||
.read = ipod_touch_sysic_read,
|
||||
.write = ipod_touch_sysic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sysic_init(Object *obj)
|
||||
{
|
||||
IPodTouchSYSICState *s = IPOD_TOUCH_SYSIC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_sysic_ops, s, TYPE_IPOD_TOUCH_SYSIC, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
for(int grp = 0; grp < GPIO_NUMINTGROUPS; grp++) {
|
||||
sysbus_init_irq(sbd, &s->gpio_irqs[grp]);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_sysic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sysic_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SYSIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSYSICState),
|
||||
.instance_init = ipod_touch_sysic_init,
|
||||
.class_init = ipod_touch_sysic_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_sysic_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sysic_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_sysic_register_types)
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "hw/arm/ipod_touch_timer.h"
|
||||
|
||||
static void s5l8900_st_update(IPodTouchTimerState *s)
|
||||
{
|
||||
s->freq_out = 1000000000 / 100;
|
||||
s->tick_interval = /* bcount1 * get_ticks / freq + ((bcount2 * get_ticks / freq)*/
|
||||
muldiv64((s->bcount1 < 1000) ? 1000 : s->bcount1, NANOSECONDS_PER_SECOND, s->freq_out);
|
||||
s->next_planned_tick = 0;
|
||||
}
|
||||
|
||||
static void s5l8900_st_set_timer(IPodTouchTimerState *s)
|
||||
{
|
||||
uint64_t last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->base_time;
|
||||
|
||||
s->next_planned_tick = last + (s->tick_interval - last % s->tick_interval);
|
||||
timer_mod(s->st_timer, s->next_planned_tick + s->base_time);
|
||||
s->last_tick = last;
|
||||
}
|
||||
|
||||
static void s5l8900_st_tick(void *opaque)
|
||||
{
|
||||
IPodTouchTimerState *s = (IPodTouchTimerState *)opaque;
|
||||
|
||||
if (s->status & TIMER_STATE_START) {
|
||||
//fprintf(stderr, "%s: Raising irq\n", __func__);
|
||||
qemu_irq_raise(s->irq);
|
||||
|
||||
/* schedule next interrupt */
|
||||
if(!(s->status & TIMER_STATE_MANUALUPDATE)) {
|
||||
s5l8900_st_set_timer(s);
|
||||
}
|
||||
} else {
|
||||
s->next_planned_tick = 0;
|
||||
s->last_tick = 0;
|
||||
timer_del(s->st_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void s5l8900_timer1_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
|
||||
|
||||
switch(addr){
|
||||
|
||||
case TIMER_IRQSTAT:
|
||||
s->irqstat = value;
|
||||
return;
|
||||
case TIMER_IRQLATCH:
|
||||
//fprintf(stderr, "%s: lowering irq\n", __func__);
|
||||
qemu_irq_lower(s->irq);
|
||||
return;
|
||||
case TIMER_4 + TIMER_CONFIG:
|
||||
s5l8900_st_update(s);
|
||||
s->config = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_STATE:
|
||||
if (value & TIMER_STATE_START) {
|
||||
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s5l8900_st_update(s);
|
||||
s5l8900_st_set_timer(s);
|
||||
} else if (value == TIMER_STATE_STOP) {
|
||||
timer_del(s->st_timer);
|
||||
}
|
||||
s->status = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_COUNT_BUFFER:
|
||||
s->bcount1 = s->bcreload = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_COUNT_BUFFER2:
|
||||
s->bcount2 = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_timer1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
|
||||
uint64_t elapsed_ns, ticks;
|
||||
|
||||
switch (addr) {
|
||||
case TIMER_TICKSHIGH: // needs to be fixed so that read from low first works as well
|
||||
|
||||
elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 2; // the timer ticks twice as slow as the CPU frequency in the kernel
|
||||
ticks = clock_ns_to_ticks(s->sysclk, elapsed_ns);
|
||||
//printf("TICKS: %lld\n", ticks);
|
||||
s->ticks_high = (ticks >> 32);
|
||||
s->ticks_low = (ticks & 0xFFFFFFFF);
|
||||
return s->ticks_high;
|
||||
case TIMER_TICKSLOW:
|
||||
return s->ticks_low;
|
||||
case TIMER_IRQSTAT:
|
||||
return ~0; // s->irqstat;
|
||||
case TIMER_IRQLATCH:
|
||||
return 0xffffffff;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps timer1_ops = {
|
||||
.read = s5l8900_timer1_read,
|
||||
.write = s5l8900_timer1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_timer_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchTimerState *s = IPOD_TOUCH_TIMER(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &timer1_ops, s, "timer1", 0x10001);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->st_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s5l8900_st_tick, s);
|
||||
}
|
||||
|
||||
static void s5l8900_timer_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_timer_info = {
|
||||
.name = TYPE_IPOD_TOUCH_TIMER,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchTimerState),
|
||||
.instance_init = s5l8900_timer_init,
|
||||
.class_init = s5l8900_timer_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_timer_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "hw/arm/ipod_touch_tvout.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static uint64_t ipod_touch_tvout_sdo_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: offset = 0x%08x\n", __func__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case SDO_CLKCON:
|
||||
return s->sdo_clkcon;
|
||||
case SDO_CONFIG:
|
||||
return s->sdo_config;
|
||||
case SDO_IRQ:
|
||||
return s->sdo_irq;
|
||||
case SDO_IRQMASK:
|
||||
return s->sdo_irq_mask;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_sdo_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
switch(offset) {
|
||||
case SDO_CLKCON:
|
||||
s->sdo_clkcon = value;
|
||||
return;
|
||||
case SDO_CONFIG:
|
||||
s->sdo_config = value;
|
||||
return;
|
||||
case SDO_IRQ:
|
||||
s->sdo_irq = 0x0;
|
||||
qemu_irq_lower(s->irq);
|
||||
return;
|
||||
case SDO_IRQMASK:
|
||||
s->sdo_irq_mask = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_tvout_mixer1_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: offset = 0x%08x\n", __func__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
return 0x4;
|
||||
case MXR_CFG:
|
||||
return s->mixer1_cfg;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_mixer1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
if((value & 0x1) && (s->sdo_irq_mask & 1) == 0 && s->irq_count < 2) {
|
||||
s->sdo_irq = 0x1;
|
||||
s->irq_count += 1; // ugly hack for now
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
break;
|
||||
case MXR_CFG:
|
||||
s->mixer1_cfg = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_tvout_mixer2_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: offset = 0x%08x\n", __func__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
return s->mixer2_status;
|
||||
case MXR_CFG:
|
||||
return s->mixer2_cfg;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_mixer2_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
s->mixer2_status = value;
|
||||
break;
|
||||
case MXR_CFG:
|
||||
s->mixer2_cfg = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_tvout_sdo_ops = {
|
||||
.read = ipod_touch_tvout_sdo_read,
|
||||
.write = ipod_touch_tvout_sdo_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps ipod_touch_tvout_mixer1_ops = {
|
||||
.read = ipod_touch_tvout_mixer1_read,
|
||||
.write = ipod_touch_tvout_mixer1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps ipod_touch_tvout_mixer2_ops = {
|
||||
.read = ipod_touch_tvout_mixer2_read,
|
||||
.write = ipod_touch_tvout_mixer2_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_tvout_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchTVOutState *s = IPOD_TOUCH_TVOUT(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
// mixer 1
|
||||
memory_region_init_io(&s->mixer1_iomem, obj, &ipod_touch_tvout_mixer1_ops, s, "tvout_mixer1", 4096);
|
||||
sysbus_init_mmio(sbd, &s->mixer1_iomem);
|
||||
|
||||
// mixer 2
|
||||
memory_region_init_io(&s->mixer2_iomem, obj, &ipod_touch_tvout_mixer2_ops, s, "tvout_mixer2", 4096);
|
||||
sysbus_init_mmio(sbd, &s->mixer2_iomem);
|
||||
|
||||
// SDO
|
||||
memory_region_init_io(&s->sdo_iomem, obj, &ipod_touch_tvout_sdo_ops, s, "tvout_sdo", 4096);
|
||||
sysbus_init_mmio(sbd, &s->sdo_iomem);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_tvout_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_TVOUT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchTVOutState),
|
||||
.instance_init = ipod_touch_tvout_init,
|
||||
.class_init = ipod_touch_tvout_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_tvout_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_tvout_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_tvout_register_types)
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "hw/arm/ipod_touch_unknown1.h"
|
||||
|
||||
static uint64_t ipod_touch_unknown1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case 0x140:
|
||||
return 0x2;
|
||||
case 0x144:
|
||||
return 0x3;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_unknown1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_unknown1_ops = {
|
||||
.read = ipod_touch_unknown1_read,
|
||||
.write = ipod_touch_unknown1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_unknown1_init(Object *obj)
|
||||
{
|
||||
IPodTouchUnknown1State *s = IPOD_TOUCH_UNKNOWN1(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_unknown1_ops, s, TYPE_IPOD_TOUCH_UNKNOWN1, 0x200);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_unknown1_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_unknown1_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_UNKNOWN1,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchUnknown1State),
|
||||
.instance_init = ipod_touch_unknown1_init,
|
||||
.class_init = ipod_touch_unknown1_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_unknown1_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_unknown1_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_unknown1_register_types)
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "hw/arm/ipod_touch_usb_phys.h"
|
||||
|
||||
static uint64_t ipod_touch_usb_phys_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchUSBPhysState *s = (IPodTouchUSBPhysState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case REG_OPHYPWR:
|
||||
return s->usb_ophypwr;
|
||||
case REG_OPHYCLK:
|
||||
return s->usb_ophyclk;
|
||||
case REG_ORSTCON:
|
||||
return s->usb_orstcon;
|
||||
case REG_UNKNOWN1:
|
||||
return s->usb_unknown1;
|
||||
case REG_OPHYTUNE:
|
||||
return s->usb_ophytune;
|
||||
case REG_CABLE:
|
||||
return s->is_cable_connected;
|
||||
default:
|
||||
return 0x0;
|
||||
//hw_error("%s: read invalid location 0x%08x\n", __func__, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_usb_phys_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchUSBPhysState *s = (IPodTouchUSBPhysState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case REG_OPHYPWR:
|
||||
s->usb_ophypwr = val;
|
||||
return;
|
||||
case REG_OPHYCLK:
|
||||
s->usb_ophyclk = val;
|
||||
return;
|
||||
case REG_ORSTCON:
|
||||
s->usb_orstcon = val;
|
||||
return;
|
||||
case REG_UNKNOWN1:
|
||||
s->usb_unknown1 = val;
|
||||
return;
|
||||
case REG_OPHYTUNE:
|
||||
s->usb_ophytune = val;
|
||||
return;
|
||||
|
||||
default:
|
||||
//hw_error("%s: write invalid location 0x%08x.\n", __func__, addr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_usb_phys_ops = {
|
||||
.read = ipod_touch_usb_phys_read,
|
||||
.write = ipod_touch_usb_phys_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_usb_phys_init(Object *obj)
|
||||
{
|
||||
IPodTouchUSBPhysState *s = IPOD_TOUCH_USB_PHYS(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_usb_phys_ops, s, TYPE_IPOD_TOUCH_USB_PHYS, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_usb_phys_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_usb_phys_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_USB_PHYS,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchUSBPhysState),
|
||||
.instance_init = ipod_touch_usb_phys_init,
|
||||
.class_init = ipod_touch_usb_phys_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_usb_phys_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_usb_phys_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_usb_phys_register_types)
|
||||
@@ -65,6 +65,7 @@ arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c'))
|
||||
arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
|
||||
arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
|
||||
arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c'))
|
||||
arm_ss.add(when: 'CONFIG_IPOD_TOUCH_2G', if_true: files('ipod_touch_2g.c', 'ipod_touch_clock.c', 'ipod_touch_chipid.c', 'ipod_touch_gpio.c', 'ipod_touch_sysic.c', 'ipod_touch_timer.c', 'ipod_touch_usb_phys.c', 'ipod_touch_spi.c', 'ipod_touch_nor_spi.c', 'ipod_touch_sha1.c', 'ipod_touch_aes.c', 'ipod_touch_pke.c', 'ipod_touch_pcf50633_pmu.c', 'ipod_touch_unknown1.c', 'ipod_touch_lcd.c', 'ipod_touch_mipi_dsi.c', 'ipod_touch_fmss.c', 'ipod_touch_mbx.c', 'ipod_touch_cd3272_mikey.c', 'ipod_touch_lis302dl.c', 'ipod_touch_multitouch.c', 'ipod_touch_scaler_csc.c', 'ipod_touch_sdio.c', 'ipod_touch_tvout.c'))
|
||||
|
||||
system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
|
||||
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c'))
|
||||
|
||||
+19
-7
@@ -142,12 +142,12 @@ again:
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
if ((req & (1u << dest_id)) == 0)
|
||||
size = 0;
|
||||
// if ((req & (1u << dest_id)) == 0)
|
||||
// size = 0;
|
||||
break;
|
||||
case 2:
|
||||
if ((req & (1u << src_id)) == 0)
|
||||
size = 0;
|
||||
// if ((req & (1u << src_id)) == 0)
|
||||
// size = 0;
|
||||
break;
|
||||
case 3:
|
||||
if ((req & (1u << src_id)) == 0
|
||||
@@ -155,8 +155,9 @@ again:
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
if (!size)
|
||||
if (!size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Transfer one element. */
|
||||
/* ??? Should transfer multiple elements for a burst request. */
|
||||
@@ -179,6 +180,7 @@ again:
|
||||
ch->dest += swidth;
|
||||
}
|
||||
|
||||
//printf("Transfer size: %d, destination: 0x%08x\n", size, ch->dest);
|
||||
size--;
|
||||
ch->ctrl = (ch->ctrl & 0xfffff000) | size;
|
||||
if (size == 0) {
|
||||
@@ -204,6 +206,7 @@ again:
|
||||
ch->conf &= ~PL080_CCONF_E;
|
||||
}
|
||||
if (ch->ctrl & PL080_CCTRL_I) {
|
||||
//printf("Setting interrupt status of channel %d\n", c);
|
||||
s->tc_int |= 1 << c;
|
||||
}
|
||||
}
|
||||
@@ -212,6 +215,7 @@ again:
|
||||
if (--s->running)
|
||||
s->running = 1;
|
||||
}
|
||||
pl080_update(s);
|
||||
}
|
||||
|
||||
static uint64_t pl080_read(void *opaque, hwaddr offset,
|
||||
@@ -289,24 +293,30 @@ static void pl080_write(void *opaque, hwaddr offset,
|
||||
PL080State *s = (PL080State *)opaque;
|
||||
int i;
|
||||
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
if (offset >= 0x100 && offset < 0x200) {
|
||||
i = (offset & 0xe0) >> 5;
|
||||
if (i >= s->nchannels)
|
||||
goto bad_offset;
|
||||
switch ((offset >> 2) & 7) {
|
||||
case 0: /* SrcAddr */
|
||||
//printf("%s: setting source address of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].src = value;
|
||||
break;
|
||||
case 1: /* DestAddr */
|
||||
//printf("%s: setting destination address of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].dest = value;
|
||||
break;
|
||||
case 2: /* LLI */
|
||||
s->chan[i].lli = value;
|
||||
break;
|
||||
case 3: /* Control */
|
||||
//printf("%s: setting control of channel %d to 0x%08x (transfer size: %d)\n", __func__, i, value, value & 0xfff);
|
||||
s->chan[i].ctrl = value;
|
||||
break;
|
||||
case 4: /* Configuration */
|
||||
//printf("%s: setting configuration of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].conf = value;
|
||||
pl080_run(s);
|
||||
break;
|
||||
@@ -381,8 +391,10 @@ static void pl080_init(Object *obj)
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
PL080State *s = PL080(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
memory_region_init_io(&s->iomem1, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem1);
|
||||
memory_region_init_io(&s->iomem2, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem2);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->interr);
|
||||
sysbus_init_irq(sbd, &s->inttc);
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* IPod Touch I2C Bus Serial Interface Emulation
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co Ltd.
|
||||
* Maksim Kozlov, <m.kozlov@samsung.com>
|
||||
* Igor Mitsyanko, <i.mitsyanko@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/i2c/ipod_touch_i2c.h"
|
||||
|
||||
static void s5l8900_i2c_update(IPodTouchI2CState *s)
|
||||
{
|
||||
uint16_t level;
|
||||
level = (s->status & S5L8900_IICSTAT_START) &&
|
||||
(s->control & S5L8900_IICCON_IRQEN);
|
||||
|
||||
if (s->control & S5L8900_IICCON_IRQPEND)
|
||||
level = 0;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
|
||||
static int s5l8900_i2c_receive(IPodTouchI2CState *s)
|
||||
{
|
||||
int r;
|
||||
r = i2c_recv(s->bus);
|
||||
s5l8900_i2c_update(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int s5l8900_i2c_send(IPodTouchI2CState *s, uint8_t data)
|
||||
{
|
||||
if (!(s->status & S5L8900_IICSTAT_LASTBIT)) {
|
||||
s->status |= S5L8900_IICCON_ACKEN;
|
||||
s->data = data;
|
||||
s->iicreg20 |= 0x100;
|
||||
i2c_send(s->bus, s->data);
|
||||
}
|
||||
s5l8900_i2c_update(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* I2C read function */
|
||||
static uint64_t ipod_touch_i2c_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
|
||||
|
||||
//fprintf(stderr, "s5l8900_i2c_read(): offset = 0x%08x\n", offset);
|
||||
|
||||
switch (offset) {
|
||||
case I2CCON:
|
||||
return s->control;
|
||||
case I2CSTAT:
|
||||
return s->status;
|
||||
case I2CADD:
|
||||
return s->address;
|
||||
case I2CDS:
|
||||
s->iicreg20 |= 0x100;
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
return s->data;
|
||||
case I2CLC:
|
||||
return s->line_ctrl;
|
||||
case IICREG20:
|
||||
{
|
||||
// clear the flags
|
||||
uint32_t tmp_reg20 = s->iicreg20;
|
||||
s->iicreg20 &= ~0x100;
|
||||
s->iicreg20 &= ~0x2000;
|
||||
return tmp_reg20;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
//hw_error("s5l8900.i2c: bad read offset 0x" TARGET_FMT_plx "\n", offset);
|
||||
//fprintf(stderr, "%s: bad read offset 0x%08x\n", __func__, offset);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C write function */
|
||||
static void ipod_touch_i2c_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
|
||||
int mode;
|
||||
|
||||
//printf("s5l8900_i2c_write (base %d): offset = 0x%08x, val = 0x%08x\n", s->base, offset, value);
|
||||
|
||||
qemu_irq_lower(s->irq);
|
||||
|
||||
switch (offset) {
|
||||
case I2CCON:
|
||||
if(value & ~(S5L8900_IICCON_ACKEN)) {
|
||||
s->iicreg20 |= 0x100;
|
||||
}
|
||||
if((value & 0x10) && (s->status == 0x90)) {
|
||||
s->iicreg20 |= 0x2000;
|
||||
}
|
||||
s->control = value & 0xff;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
break;
|
||||
|
||||
case I2CSTAT:
|
||||
/* We have to make sure we don't miss an end transfer */
|
||||
if((!s->active) && ((s->status >> 6) != ((value >> 6)))) {
|
||||
s->status = value & 0xff;
|
||||
/* If they toggle the tx bit then we have to force an end transfer before mode update */
|
||||
} else if((s->active) && ((s->status >> 6) != ((value >> 6)))) {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active=0;
|
||||
s->status = value & 0xff;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
break;
|
||||
}
|
||||
mode = (s->status >> 6) & 0x3;
|
||||
if (value & S5L8900_IICSTAT_TXRXEN) {
|
||||
/* IIC-bus data output enable/disable bit */
|
||||
switch(mode) {
|
||||
case SR_MODE:
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
break;
|
||||
case ST_MODE:
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
break;
|
||||
case MR_MODE:
|
||||
if (value & S5L8900_IICSTAT_START) {
|
||||
/* START condition */
|
||||
s->status &= ~S5L8900_IICSTAT_LASTBIT;
|
||||
|
||||
s->iicreg20 |= 0x100;
|
||||
s->active = 1;
|
||||
i2c_start_transfer(s->bus, s->data >> 1, 1);
|
||||
} else {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active = 0;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
}
|
||||
break;
|
||||
case MT_MODE:
|
||||
if (value & S5L8900_IICSTAT_START) {
|
||||
/* START condition */
|
||||
s->status &= ~S5L8900_IICSTAT_LASTBIT;
|
||||
|
||||
s->iicreg20 |= 0x100;
|
||||
s->active = 1;
|
||||
i2c_start_transfer(s->bus, s->data >> 1, 0);
|
||||
} else {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active = 0;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
s5l8900_i2c_update(s);
|
||||
break;
|
||||
|
||||
case I2CADD:
|
||||
s->address = value & 0xff;
|
||||
break;
|
||||
|
||||
case I2CDS:
|
||||
s5l8900_i2c_send(s, value & 0xff);
|
||||
break;
|
||||
|
||||
case I2CLC:
|
||||
s->line_ctrl = value & 0xff;
|
||||
break;
|
||||
|
||||
case IICREG20:
|
||||
//s->iicreg20 &= ~value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
//hw_error("s5l8900.i2c: bad write offset 0x" TARGET_FMT_plx "\n", offset);
|
||||
//fprintf(stderr, "%s: bad write offset 0x%08x\n", __func__, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_i2c_ops = {
|
||||
.read = ipod_touch_i2c_read,
|
||||
.write = ipod_touch_i2c_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int i2c_index = 0;
|
||||
|
||||
static void ipod_touch_i2c_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchI2CState *s = IPOD_TOUCH_I2C(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_i2c_ops, s, TYPE_IPOD_TOUCH_I2C, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
char bus_name[5];
|
||||
sprintf(bus_name, "i2c%d", i2c_index);
|
||||
|
||||
s->bus = i2c_init_bus(dev, bus_name);
|
||||
i2c_index += 1;
|
||||
}
|
||||
|
||||
static void ipod_touch_i2c_reset(DeviceState *d)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_i2c_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->reset = ipod_touch_i2c_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_i2c_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_I2C,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchI2CState),
|
||||
.instance_init = ipod_touch_i2c_init,
|
||||
.class_init = ipod_touch_i2c_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_i2c_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_i2c_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_i2c_register_types)
|
||||
@@ -17,4 +17,5 @@ i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c'))
|
||||
i2c_ss.add(when: 'CONFIG_IPOD_TOUCH_2G', if_true: files('ipod_touch_i2c.c'))
|
||||
system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
|
||||
@@ -8,6 +8,9 @@ config I8259
|
||||
config PL190
|
||||
bool
|
||||
|
||||
config PL192
|
||||
bool
|
||||
|
||||
config IOAPIC
|
||||
bool
|
||||
select I8259
|
||||
|
||||
@@ -24,6 +24,7 @@ system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'))
|
||||
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c'))
|
||||
system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c'))
|
||||
system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c'))
|
||||
system_ss.add(when: 'CONFIG_PL192', if_true: files('pl192.c'))
|
||||
system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c'))
|
||||
system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c'))
|
||||
system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c'))
|
||||
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* ARM PrimeCell PL192 Vector Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2009 Samsung Electronics.
|
||||
* Contributed by Kirill Batuzov <batuzovk@ispras.ru>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/intc/pl192.h"
|
||||
|
||||
extern CPUState *getMainCpuEnv(void);
|
||||
|
||||
|
||||
static void pl192_update(PL192State *);
|
||||
|
||||
static void pl192_raise(PL192State *s, int is_fiq)
|
||||
{
|
||||
if (is_fiq) {
|
||||
if (s->fiq) {
|
||||
/* Raise parent FIQ */
|
||||
qemu_irq_raise(s->fiq);
|
||||
} else {
|
||||
if (s->daisy) {
|
||||
/* FIQ is directly propagated through daisy chain */
|
||||
pl192_raise(s->daisy, is_fiq);
|
||||
} else {
|
||||
fprintf(stderr, "pl192i cannot raise FIQ. This usually means that "
|
||||
"initialization was done incorrectly.\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (s->irq) {
|
||||
/* Raise parent IRQ */
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
if (s->daisy) {
|
||||
/* Setup daisy input of the next chained contorller and force
|
||||
it to update it's state */
|
||||
s->daisy->daisy_vectaddr = s->address;
|
||||
s->daisy->daisy_callback = s;
|
||||
s->daisy->daisy_input = 1;
|
||||
pl192_update(s->daisy);
|
||||
} else {
|
||||
// TODO Needs urgent fixing!
|
||||
hw_error("pl192: cannot raise IRQ. This usually means that initialization was done incorrectly.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pl192_lower(PL192State *s, int is_fiq)
|
||||
{
|
||||
/* Lower parrent interrupt if there is one */
|
||||
if (is_fiq && s->fiq) {
|
||||
qemu_irq_lower(s->fiq);
|
||||
}
|
||||
if (!is_fiq && s->irq) {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
/* Propagate to the previous controller in chain if needed */
|
||||
if (s->daisy) {
|
||||
if (!is_fiq) {
|
||||
s->daisy->daisy_input = 0;
|
||||
pl192_update(s->daisy);
|
||||
} else {
|
||||
pl192_lower(s->daisy, is_fiq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find interrupt of the highest priority */
|
||||
static uint32_t pl192_priority_sorter(PL192State *s)
|
||||
{
|
||||
int i;
|
||||
uint32_t prio_irq[PL192_PRIO_LEVELS];
|
||||
|
||||
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
|
||||
prio_irq[i] = PL192_NO_IRQ;
|
||||
}
|
||||
if (s->daisy_input) {
|
||||
prio_irq[s->daisy_priority] = PL192_DAISY_IRQ;
|
||||
}
|
||||
for (i = PL192_INT_SOURCES - 1; i >= 0; i--) {
|
||||
if (s->irq_status & (1 << i)) {
|
||||
prio_irq[s->vect_priority[i]] = i;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
|
||||
if ((s->sw_priority_mask & (1 << i)) &&
|
||||
prio_irq[i] <= PL192_DAISY_IRQ) {
|
||||
return prio_irq[i];
|
||||
}
|
||||
}
|
||||
return PL192_NO_IRQ;
|
||||
}
|
||||
|
||||
static void pl192_update(PL192State *s)
|
||||
{
|
||||
/* TODO: does SOFTINT affects IRQ_STATUS??? */
|
||||
s->irq_status = (s->rawintr | s->softint) & s->intenable & ~s->intselect;
|
||||
s->fiq_status = (s->rawintr | s->softint) & s->intenable & s->intselect;
|
||||
if (s->fiq_status) {
|
||||
pl192_raise(s, 1);
|
||||
} else {
|
||||
pl192_lower(s, 1);
|
||||
}
|
||||
if (s->irq_status || s->daisy_input) {
|
||||
s->current_highest = pl192_priority_sorter(s);
|
||||
if (s->current_highest < PL192_INT_SOURCES) {
|
||||
s->address = s->vect_addr[s->current_highest];
|
||||
} else {
|
||||
s->address = s->daisy_vectaddr;
|
||||
}
|
||||
if (s->current_highest != s->current) {
|
||||
if (s->current_highest < PL192_INT_SOURCES) {
|
||||
if (s->vect_priority[s->current_highest] >= s->priority) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (s->current_highest == PL192_DAISY_IRQ) {
|
||||
if (s->daisy_priority >= s->priority) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (s->current_highest <= PL192_DAISY_IRQ) {
|
||||
pl192_raise(s, 0);
|
||||
} else {
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->current_highest = PL192_NO_IRQ;
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set priority level when an interrupt have been acknoledged by CPU.
|
||||
Also save interrupt id and priority to stack so it can be restored
|
||||
lately. */
|
||||
static inline void pl192_mask_priority(PL192State *s)
|
||||
{
|
||||
if (s->stack_i >= PL192_INT_SOURCES) {
|
||||
hw_error("pl192: internal error (trying to mask when there are no more sources)\n");
|
||||
}
|
||||
s->stack_i++;
|
||||
if (s->current == PL192_DAISY_IRQ) {
|
||||
s->priority = s->daisy_priority;
|
||||
} else {
|
||||
s->priority = s->vect_priority[s->current];
|
||||
}
|
||||
s->priority_stack[s->stack_i] = s->priority;
|
||||
s->irq_stack[s->stack_i] = s->current;
|
||||
}
|
||||
|
||||
/* Set priority level when interrupt have been successfully processed by CPU.
|
||||
Also restore previous interrupt id and priority level. */
|
||||
static inline void pl192_unmask_priority(PL192State *s)
|
||||
{
|
||||
if (s->stack_i < 1) {
|
||||
return; // simply ignore this event
|
||||
//hw_error("pl192: internal error (mask stack insufficient)\n");
|
||||
}
|
||||
s->stack_i--;
|
||||
s->priority = s->priority_stack[s->stack_i];
|
||||
s->current = s->irq_stack[s->stack_i];
|
||||
}
|
||||
|
||||
/* IRQ was acknoledged by CPU. Update controller state accordingly */
|
||||
static uint32_t pl192_irq_ack(PL192State *s)
|
||||
{
|
||||
int is_daisy = (s->current_highest == PL192_DAISY_IRQ);
|
||||
uint32_t res = s->address;
|
||||
|
||||
s->current = s->current_highest;
|
||||
pl192_mask_priority(s);
|
||||
if (is_daisy) {
|
||||
pl192_mask_priority(s->daisy_callback);
|
||||
}
|
||||
pl192_update(s);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* IRQ was processed by CPU. Update controller state accrodingly */
|
||||
static void pl192_irq_fin(PL192State *s)
|
||||
{
|
||||
int is_daisy = (s->current == PL192_DAISY_IRQ);
|
||||
|
||||
pl192_unmask_priority(s);
|
||||
if (is_daisy) {
|
||||
pl192_unmask_priority(s->daisy_callback);
|
||||
}
|
||||
pl192_update(s);
|
||||
|
||||
/* hmm is this right?*/
|
||||
/*
|
||||
if (s->current == PL192_NO_IRQ && (s->current_highest >= PL192_INT_SOURCES)) {
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static uint64_t pl192_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (offset & 3) {
|
||||
fprintf(stderr, "pl192: bad read offset (1) " HWADDR_FMT_plx "\n", offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
unsigned char pl192_id[] = { 0x92, 0x11, 0x04, 0x00, 0x0D, 0xF0, 0x05, 0xB1 };
|
||||
return pl192_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
if (offset >= 0x100 && offset < 0x180) {
|
||||
return s->vect_addr[(offset - 0x100) >> 2];
|
||||
}
|
||||
if (offset >= 0x200 && offset < 0x280) {
|
||||
return s->vect_priority[(offset - 0x200) >> 2];
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case PL192_IRQSTATUS:
|
||||
//fprintf(stderr, "%s: irqstatus 0x%08x\n", __FUNCTION__, s->irq_status);
|
||||
return s->irq_status;
|
||||
case PL192_FIQSTATUS:
|
||||
return s->fiq_status;
|
||||
case PL192_RAWINTR:
|
||||
return s->rawintr;
|
||||
case PL192_INTSELECT:
|
||||
return s->intselect;
|
||||
case PL192_INTENABLE:
|
||||
return s->intenable;
|
||||
case PL192_SOFTINT:
|
||||
return s->softint;
|
||||
case PL192_PROTECTION:
|
||||
return s->protection;
|
||||
case PL192_SWPRIORITYMASK:
|
||||
return s->sw_priority_mask;
|
||||
case PL192_PRIORITYDAISY:
|
||||
return s->daisy_priority;
|
||||
case PL192_INTENCLEAR:
|
||||
return 0;
|
||||
case PL192_SOFTINTCLEAR:
|
||||
fprintf(stderr, "pl192: attempt to read write-only register (offset = "
|
||||
HWADDR_FMT_plx ")\n", offset);
|
||||
case PL192_VECTADDR:
|
||||
return pl192_irq_ack(s);
|
||||
/* Workaround for kernel code using PL190 */
|
||||
case PL190_ITCR:
|
||||
case PL190_VECTADDR:
|
||||
case PL190_DEFVECTADDR:
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "pl192: bad read offset (2) " HWADDR_FMT_plx "\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pl192_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (offset & 3) {
|
||||
hw_error("pl192: bad write offset (1) " HWADDR_FMT_plx "\n", offset);
|
||||
}
|
||||
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
hw_error("pl192: attempt to write to a read-only register (offset = "
|
||||
HWADDR_FMT_plx ")\n", offset);
|
||||
}
|
||||
if (offset >= 0x100 && offset < 0x180) {
|
||||
s->vect_addr[(offset - 0x100) >> 2] = value;
|
||||
pl192_update(s);
|
||||
return;
|
||||
}
|
||||
if (offset >= 0x200 && offset < 0x280) {
|
||||
s->vect_priority[(offset - 0x200) >> 2] = value & 0xf;
|
||||
pl192_update(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case PL192_IRQSTATUS:
|
||||
/* This is a readonly register, but linux tries to write to it
|
||||
anyway. Ignore the write. */
|
||||
return;
|
||||
case PL192_FIQSTATUS:
|
||||
case PL192_RAWINTR:
|
||||
hw_error("pl192: attempt to write to a read-only register (offset = "
|
||||
HWADDR_FMT_plx ")\n", offset);
|
||||
break;
|
||||
case PL192_INTSELECT:
|
||||
s->intselect = value;
|
||||
break;
|
||||
case PL192_INTENABLE:
|
||||
s->intenable |= value;
|
||||
break;
|
||||
case PL192_INTENCLEAR:
|
||||
s->intenable &= ~value;
|
||||
break;
|
||||
case PL192_SOFTINT:
|
||||
s->softint |= value;
|
||||
break;
|
||||
case PL192_SOFTINTCLEAR:
|
||||
s->softint &= ~value;
|
||||
break;
|
||||
case PL192_PROTECTION:
|
||||
/* TODO: implement protection */
|
||||
s->protection = value & 1;
|
||||
break;
|
||||
case PL192_SWPRIORITYMASK:
|
||||
s->sw_priority_mask = value & 0xffff;
|
||||
break;
|
||||
case PL192_PRIORITYDAISY:
|
||||
s->daisy_priority = value & 0xf;
|
||||
break;
|
||||
case PL192_VECTADDR:
|
||||
pl192_irq_fin(s);
|
||||
return;
|
||||
case PL190_ITCR:
|
||||
case PL190_VECTADDR:
|
||||
case PL190_DEFVECTADDR:
|
||||
/* NB: This thing is not present here, but linux wants to write it */
|
||||
/* Ignore written value */
|
||||
return;
|
||||
default:
|
||||
fprintf(stderr, "pl192: bad write offset (2) " HWADDR_FMT_plx "\n", offset);
|
||||
return;
|
||||
}
|
||||
|
||||
pl192_update(s);
|
||||
}
|
||||
|
||||
static void pl192_irq_handler(void *opaque, int irq, int level)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (level) {
|
||||
s->rawintr |= 1 << irq;
|
||||
} else {
|
||||
s->rawintr &= ~(1 << irq);
|
||||
}
|
||||
pl192_update(opaque);
|
||||
}
|
||||
|
||||
static void pl192_reset(DeviceState *d)
|
||||
{
|
||||
PL192State *s = PL192(d);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PL192_INT_SOURCES; i++) {
|
||||
s->vect_priority[i] = 0xf;
|
||||
}
|
||||
s->sw_priority_mask = 0xffff;
|
||||
s->daisy_priority = 0xf;
|
||||
s->current = PL192_NO_IRQ;
|
||||
s->current_highest = PL192_NO_IRQ;
|
||||
s->stack_i = 0;
|
||||
s->priority_stack[0] = 0x10;
|
||||
s->irq_stack[0] = PL192_NO_IRQ;
|
||||
s->priority = 0x10;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pl192_ops = {
|
||||
.read = pl192_read,
|
||||
.write = pl192_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
DeviceState *pl192_manual_init(char *mem_name, ...)
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_PL192);
|
||||
PL192State *s = PL192(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
va_list va;
|
||||
qemu_irq irq;
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pl192_ops, s, mem_name, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->fiq);
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
|
||||
va_start(va, mem_name);
|
||||
int n = 0;
|
||||
while (1) {
|
||||
irq = va_arg(va, qemu_irq);
|
||||
if (!irq) {
|
||||
break;
|
||||
}
|
||||
sysbus_connect_irq(sbd, n, irq);
|
||||
n++;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void pl192_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
PL192State *s = PL192(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
//memory_region_init_io(&s->iomem, obj, &pl192_ops, s, "pl192", 0x1000);
|
||||
//sysbus_init_mmio(sbd, &s->iomem);
|
||||
//qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
|
||||
//sysbus_init_irq(sbd, s->irq);
|
||||
//sysbus_init_irq(sbd, s->fiq);
|
||||
}
|
||||
|
||||
static void pl192_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = pl192_reset;
|
||||
//dc->vmsd = &vmstate_pl192;
|
||||
// TODO save VM
|
||||
}
|
||||
|
||||
static const TypeInfo pl192_info = {
|
||||
.name = TYPE_PL192,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PL192State),
|
||||
.instance_init = pl192_init,
|
||||
.class_init = pl192_class_init,
|
||||
};
|
||||
|
||||
static void pl192_register_types(void)
|
||||
{
|
||||
type_register_static(&pl192_info);
|
||||
}
|
||||
|
||||
type_init(pl192_register_types)
|
||||
@@ -137,3 +137,8 @@ config USB_DWC3
|
||||
config XLNX_USB_SUBSYS
|
||||
bool
|
||||
select USB_DWC3
|
||||
|
||||
config USB_TCP
|
||||
bool
|
||||
default y
|
||||
depends on USB
|
||||
@@ -1368,6 +1368,9 @@ static void dwc2_realize(DeviceState *dev, Error **errp)
|
||||
&dev->mem_reentrancy_guard);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->device = DWC2_USB_DEVICE(qdev_new(TYPE_DWC2_USB_DEVICE));
|
||||
s->device->dwc2 = s;
|
||||
}
|
||||
|
||||
static void dwc2_init(Object *obj)
|
||||
@@ -1387,6 +1390,56 @@ static void dwc2_init(Object *obj)
|
||||
memory_region_add_subregion(&s->container, 0x1000, &s->fifos);
|
||||
}
|
||||
|
||||
static void dwc2_usb_device_realize(USBDevice *dev, Error **errp)
|
||||
{
|
||||
dev->speed = USB_SPEED_HIGH;
|
||||
dev->speedmask = USB_SPEED_MASK_HIGH;
|
||||
dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
|
||||
dev->auto_attach = false;
|
||||
}
|
||||
|
||||
static void dwc2_usb_device_handle_attach(USBDevice *dev)
|
||||
{
|
||||
DWC2DeviceState *udev = DWC2_USB_DEVICE(dev);
|
||||
DWC2State *s = udev->dwc2;
|
||||
|
||||
/* not in host mode */
|
||||
assert(!s->uport.dev);
|
||||
|
||||
s->gotgctl |= GOTGCTL_BSESVLD | GOTGCTL_CONID_B;
|
||||
dwc2_lower_global_irq(s, GINTSTS_CURMODE_HOST);
|
||||
dwc2_raise_global_irq(s, GINTSTS_CONIDSTSCHNG);
|
||||
}
|
||||
|
||||
static void dwc2_usb_device_handle_detach(USBDevice *dev)
|
||||
{
|
||||
DWC2DeviceState *udev = DWC2_USB_DEVICE(dev);
|
||||
DWC2State *s = udev->dwc2;
|
||||
|
||||
s->gotgctl &= ~(GOTGCTL_BSESVLD | GOTGCTL_CONID_B);
|
||||
dwc2_raise_global_irq(s, GINTSTS_CURMODE_HOST | GINTSTS_CONIDSTSCHNG);
|
||||
}
|
||||
|
||||
static void dwc2_usb_device_handle_reset(USBDevice *dev)
|
||||
{
|
||||
DWC2DeviceState *udev = DWC2_USB_DEVICE(dev);
|
||||
DWC2State *s = udev->dwc2;
|
||||
|
||||
s->dcfg &= ~DCFG_DEVADDR_MASK;
|
||||
|
||||
for (int i = 1; i < DWC2_NB_EP; i++) {
|
||||
s->diepctl(i) &= ~DXEPCTL_USBACTEP;
|
||||
s->doepctl(i) &= ~DXEPCTL_USBACTEP;
|
||||
}
|
||||
|
||||
dwc2_raise_global_irq(s, GINTSTS_USBRST);
|
||||
}
|
||||
|
||||
static void dwc2_usb_device_cancel_packet(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "%s\n", __func__);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_dwc2_state_packet = {
|
||||
.name = "dwc2/packet",
|
||||
.version_id = 1,
|
||||
@@ -1448,6 +1501,29 @@ static Property dwc2_usb_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void dwc2_usb_device_class_initfn_common(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
||||
|
||||
uc->realize = dwc2_usb_device_realize;
|
||||
uc->product_desc = "DWC2 USB Device";
|
||||
uc->unrealize = NULL;
|
||||
uc->cancel_packet = dwc2_usb_device_cancel_packet;
|
||||
uc->handle_attach = dwc2_usb_device_handle_attach;
|
||||
//uc->handle_detach = dwc2_usb_device_handle_detach;
|
||||
uc->handle_reset = dwc2_usb_device_handle_reset;
|
||||
uc->handle_data = NULL;
|
||||
uc->handle_control = NULL;
|
||||
//uc->handle_packet = dwc2_usb_device_handle_packet;
|
||||
uc->flush_ep_queue = NULL;
|
||||
uc->ep_stopped = NULL;
|
||||
uc->alloc_streams = NULL;
|
||||
uc->free_streams = NULL;
|
||||
uc->usb_desc = NULL;
|
||||
set_bit(DEVICE_CATEGORY_USB, dc->categories);
|
||||
}
|
||||
|
||||
static void dwc2_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@@ -1462,6 +1538,13 @@ static void dwc2_class_init(ObjectClass *klass, void *data)
|
||||
dwc2_reset_exit, &c->parent_phases);
|
||||
}
|
||||
|
||||
static const TypeInfo dwc2_usb_device_type_info = {
|
||||
.name = TYPE_DWC2_USB_DEVICE,
|
||||
.parent = TYPE_USB_DEVICE,
|
||||
.instance_size = sizeof(DWC2DeviceState),
|
||||
.class_init = dwc2_usb_device_class_initfn_common,
|
||||
};
|
||||
|
||||
static const TypeInfo dwc2_usb_type_info = {
|
||||
.name = TYPE_DWC2_USB,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
@@ -1473,6 +1556,7 @@ static const TypeInfo dwc2_usb_type_info = {
|
||||
|
||||
static void dwc2_usb_register_types(void)
|
||||
{
|
||||
type_register_static(&dwc2_usb_device_type_info);
|
||||
type_register_static(&dwc2_usb_type_info);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
|
||||
#define DWC2_NB_CHAN 8 /* Number of host channels */
|
||||
#define DWC2_MAX_XFER_SIZE 65536 /* Max transfer size expected in HCTSIZ */
|
||||
#define DWC2_NB_EP 16 /* Number of device endpoints */
|
||||
|
||||
typedef struct DWC2Packet DWC2Packet;
|
||||
typedef struct DWC2DeviceState DWC2DeviceState;
|
||||
typedef struct DWC2State DWC2State;
|
||||
typedef struct DWC2Class DWC2Class;
|
||||
|
||||
@@ -57,12 +59,18 @@ struct DWC2Packet {
|
||||
bool needs_service;
|
||||
};
|
||||
|
||||
struct DWC2DeviceState {
|
||||
USBDevice parent_obj;
|
||||
DWC2State *dwc2;
|
||||
};
|
||||
|
||||
struct DWC2State {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
USBBus bus;
|
||||
DWC2DeviceState *device;
|
||||
qemu_irq irq;
|
||||
MemoryRegion *dma_mr;
|
||||
AddressSpace dma_as;
|
||||
@@ -141,6 +149,44 @@ struct DWC2State {
|
||||
#define hcdma(_ch) hreg1[((_ch) << 3) + 5] /* 514, 534, ... */
|
||||
#define hcdmab(_ch) hreg1[((_ch) << 3) + 7] /* 51c, 53c, ... */
|
||||
|
||||
union {
|
||||
#define DWC2_DREG_SIZE 0x38
|
||||
uint32_t dreg[DWC2_DREG_SIZE / sizeof(uint32_t)];
|
||||
struct {
|
||||
uint32_t dcfg; /* 800 */
|
||||
uint32_t dctl; /* 804 */
|
||||
uint32_t dsts; /* 808 */
|
||||
uint32_t rsvd2; /* 80c */
|
||||
uint32_t diepmsk; /* 810 */
|
||||
uint32_t doepmsk; /* 814 */
|
||||
uint32_t daint; /* 818 */
|
||||
uint32_t daintmsk; /* 81c */
|
||||
uint32_t dtknqr1; /* 820 */
|
||||
uint32_t dtknqr2; /* 824 */
|
||||
uint32_t dvbusdis; /* 828 */
|
||||
uint32_t dvbuspulse; /* 82c */
|
||||
uint32_t dtknqr3; /* 830 */
|
||||
uint32_t dtknqr4; /* 834 */
|
||||
};
|
||||
};
|
||||
|
||||
#define DWC2_DIEPREG_SIZE (0x20 * DWC2_NB_EP)
|
||||
uint32_t diepreg[DWC2_DIEPREG_SIZE / sizeof(uint32_t)];
|
||||
|
||||
#define diepctl(_ch) diepreg[((_ch) << 3) + 0] /* 900, 920, ... */
|
||||
#define diepint(_ch) diepreg[((_ch) << 3) + 2] /* 908, 928, ... */
|
||||
#define dieptsiz(_ch) diepreg[((_ch) << 3) + 4] /* 910, 930, ... */
|
||||
#define diepdma(_ch) diepreg[((_ch) << 3) + 5] /* 914, 934, ... */
|
||||
#define dtxfsts(_ch) diepreg[((_ch) << 3) + 6] /* 918, 938, ... */
|
||||
|
||||
#define DWC2_DOEPREG_SIZE (0x20 * DWC2_NB_EP)
|
||||
uint32_t doepreg[DWC2_DIEPREG_SIZE / sizeof(uint32_t)];
|
||||
|
||||
#define doepctl(_ch) doepreg[((_ch) << 3) + 0] /* b00, b20, ... */
|
||||
#define doepint(_ch) doepreg[((_ch) << 3) + 2] /* b08, b28, ... */
|
||||
#define doeptsiz(_ch) doepreg[((_ch) << 3) + 4] /* b10, b30, ... */
|
||||
#define doepdma(_ch) doepreg[((_ch) << 3) + 5] /* b14, b34, ... */
|
||||
|
||||
union {
|
||||
#define DWC2_PCGREG_SIZE 0x08
|
||||
uint32_t pcgreg[DWC2_PCGREG_SIZE / sizeof(uint32_t)];
|
||||
@@ -180,6 +226,9 @@ struct DWC2Class {
|
||||
/*< public >*/
|
||||
};
|
||||
|
||||
#define TYPE_DWC2_USB_DEVICE "dwc2-usb-device"
|
||||
OBJECT_DECLARE_TYPE(DWC2DeviceState, USBDeviceClass, DWC2_USB_DEVICE)
|
||||
|
||||
#define TYPE_DWC2_USB "dwc2-usb"
|
||||
OBJECT_DECLARE_TYPE(DWC2State, DWC2Class, DWC2_USB)
|
||||
|
||||
|
||||
@@ -0,0 +1,457 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qom/object.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "hw/usb.h"
|
||||
#include "tcp-usb.h"
|
||||
#include "hw/usb/hcd-tcp.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "io/channel.h"
|
||||
#include "io/channel-util.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/blocker.h"
|
||||
|
||||
//#define DEBUG_HCD_TCP
|
||||
|
||||
#ifdef DEBUG_HCD_TCP
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stderr, "hcd-tcp: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
static void usb_tcp_host_closed(USBTCPHostState *s)
|
||||
{
|
||||
DPRINTF("%s\n", __func__);
|
||||
if (s->ioc) {
|
||||
qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
qio_channel_close(s->ioc, NULL);
|
||||
object_unref(OBJECT(s->ioc));
|
||||
s->ioc = NULL;
|
||||
}
|
||||
s->closed = true;
|
||||
migrate_del_blocker(s->migration_blocker);
|
||||
}
|
||||
|
||||
static ssize_t tcp_usb_read(QIOChannel *ioc, void *buf, size_t len)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = len };
|
||||
bool iolock = qemu_mutex_iothread_locked();
|
||||
bool iothread = qemu_in_iothread();
|
||||
ssize_t ret = -1;
|
||||
Error *err = NULL;
|
||||
|
||||
/*
|
||||
* Dont use in IOThread out of co-routine context as
|
||||
* it will block IOThread.
|
||||
*/
|
||||
assert(qemu_in_coroutine() || !iothread);
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, NULL, 0, &err);
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
return (ret <= 0) ? ret : iov.iov_len;
|
||||
}
|
||||
|
||||
static bool tcp_usb_write(QIOChannel *ioc, void *buf, ssize_t len)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = len };
|
||||
bool iolock = qemu_mutex_iothread_locked();
|
||||
bool iothread = qemu_in_iothread();
|
||||
bool ret = false;
|
||||
Error *err = NULL;
|
||||
|
||||
/*
|
||||
* Dont use in IOThread out of co-routine context as
|
||||
* it will block IOThread.
|
||||
*/
|
||||
assert(qemu_in_coroutine() || !iothread);
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
if (!qio_channel_writev_full_all(ioc, &iov, 1, NULL, 0, 0, &err)) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static USBPort *usb_tcp_host_find_active_port(USBTCPHostState *s)
|
||||
{
|
||||
for (int i = 0; i < G_N_ELEMENTS(s->uports) - 1; i++) {
|
||||
if (s->uports[i].dev->attached) {
|
||||
return &s->uports[i];
|
||||
}
|
||||
}
|
||||
return &s->uports[0];
|
||||
}
|
||||
|
||||
static void coroutine_fn usb_tcp_host_respond_packet_co(void *opaque)
|
||||
{
|
||||
USBTCPPacket *pkt = (USBTCPPacket *)opaque;
|
||||
USBTCPHostState *s = USB_TCP_HOST(pkt->s);
|
||||
USBPacket *p = &pkt->p;
|
||||
tcp_usb_header_t hdr = { 0 };
|
||||
tcp_usb_response_header resp = { 0 };
|
||||
g_autofree void *buffer = NULL;
|
||||
USBPort *uport = usb_tcp_host_find_active_port(s);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&s->write_mutex) {
|
||||
if (!s->closed) {
|
||||
hdr.type = TCP_USB_RESPONSE;
|
||||
resp.addr = uport->dev->addr;
|
||||
resp.pid = p->pid;
|
||||
resp.ep = p->ep->nr;
|
||||
resp.id = p->id;
|
||||
resp.status = p->status;
|
||||
resp.length = p->iov.size;
|
||||
|
||||
if (resp.length > p->actual_length) {
|
||||
resp.length = p->actual_length;
|
||||
}
|
||||
|
||||
if (p->pid == USB_TOKEN_IN && p->status != USB_RET_ASYNC) {
|
||||
buffer = g_malloc(resp.length);
|
||||
iov_to_buf(p->iov.iov, p->iov.niov, 0, buffer, resp.length);
|
||||
}
|
||||
|
||||
if (!tcp_usb_write(s->ioc, &hdr, sizeof(hdr))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tcp_usb_write(s->ioc, &resp, sizeof(resp))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
if (!tcp_usb_write(s->ioc, buffer, resp.length)) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!usb_packet_is_inflight(p)) {
|
||||
if (pkt->buffer) {
|
||||
g_free(pkt->buffer);
|
||||
}
|
||||
usb_packet_cleanup(p);
|
||||
g_free(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_tcp_host_respond_packet(USBTCPHostState *s, USBTCPPacket *pkt)
|
||||
{
|
||||
Coroutine *co = NULL;
|
||||
co = qemu_coroutine_create(usb_tcp_host_respond_packet_co, pkt);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
static void coroutine_fn usb_tcp_host_msg_loop_co(void *opaque)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(opaque);
|
||||
USBPort *uport = usb_tcp_host_find_active_port(s);
|
||||
QIOChannel *ioc = s->ioc;
|
||||
|
||||
for(;;) {
|
||||
tcp_usb_header_t hdr = { 0 };
|
||||
|
||||
if (unlikely((tcp_usb_read(ioc, &hdr, sizeof(hdr)) != sizeof(hdr)))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hdr.type) {
|
||||
case TCP_USB_REQUEST: {
|
||||
/* fprintf(stderr, "%s: TCP_USB_REQUEST\n", __func__); */
|
||||
tcp_usb_request_header pkt_hdr = {0};
|
||||
g_autofree void *buffer = NULL;
|
||||
g_autofree USBTCPPacket *pkt = (USBTCPPacket *) g_malloc0(sizeof(USBTCPPacket));
|
||||
USBEndpoint *ep = NULL;
|
||||
|
||||
if (unlikely(tcp_usb_read(ioc, &pkt_hdr, sizeof(pkt_hdr)) != sizeof(pkt_hdr))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
DPRINTF("%s: TCP_USB_REQUEST pid: 0x%x ep: %d id: 0x%lx\n", __func__, pkt_hdr.pid, pkt_hdr.ep, pkt_hdr.id);
|
||||
#endif
|
||||
ep = usb_ep_get(uport->dev, pkt_hdr.pid, pkt_hdr.ep);
|
||||
if (ep == NULL) {
|
||||
DPRINTF("%s: TCP_USB_REQUEST unknown EP\n", __func__);
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_packet_init(&pkt->p);
|
||||
usb_packet_setup(&pkt->p, pkt_hdr.pid, ep, pkt_hdr.stream, pkt_hdr.id, pkt_hdr.short_not_ok, pkt_hdr.int_req);
|
||||
|
||||
if (pkt_hdr.length > 0) {
|
||||
buffer = g_malloc0(pkt_hdr.length);
|
||||
|
||||
if (pkt_hdr.pid != USB_TOKEN_IN) {
|
||||
if (unlikely(tcp_usb_read(s->ioc, buffer, pkt_hdr.length) != pkt_hdr.length)) {
|
||||
usb_tcp_host_closed(s);
|
||||
usb_packet_cleanup(&pkt->p);
|
||||
return;
|
||||
}
|
||||
/* qemu_hexdump(stderr, __func__, buffer, pkt_hdr.length); */
|
||||
}
|
||||
|
||||
usb_packet_addbuf(&pkt->p, buffer, pkt_hdr.length);
|
||||
pkt->buffer = buffer;
|
||||
g_steal_pointer(&buffer);
|
||||
}
|
||||
|
||||
if (pkt_hdr.addr != uport->dev->addr) {
|
||||
/*
|
||||
* fprintf(stderr,
|
||||
* "%s: USB_RET_NODEV: pkt_hdr.addr != uport->dev->addr: %d != %d\n",
|
||||
* __func__, pkt_hdr.addr, uport->dev->addr);
|
||||
*/
|
||||
/* Can't enforce this check because dwc2 address transition time is slow */
|
||||
}
|
||||
pkt->dev = ep->dev;
|
||||
pkt->s = s;
|
||||
pkt->addr = pkt_hdr.addr;
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
usb_handle_packet(pkt->dev, &pkt->p);
|
||||
usb_tcp_host_respond_packet(s, pkt);
|
||||
g_steal_pointer(&pkt);
|
||||
break;
|
||||
}
|
||||
case TCP_USB_RESPONSE:
|
||||
fprintf(stderr, "%s: unexpected TCP_USB_RESPONSE\n", __func__);
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
case TCP_USB_CANCEL: {
|
||||
/* DPRINTF("%s: TCP_USB_CANCEL\n", __func__); */
|
||||
tcp_usb_cancel_header pkt_hdr = {0};
|
||||
USBTCPPacket *pkt = NULL;
|
||||
USBPacket *p = NULL;
|
||||
|
||||
if (unlikely(tcp_usb_read(ioc, &pkt_hdr, sizeof(pkt_hdr)) != sizeof(pkt_hdr))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 1
|
||||
DPRINTF("%s: TCP_USB_CANCEL pid: 0x%x ep: %d\n", __func__, pkt_hdr.pid, pkt_hdr.ep);
|
||||
#endif
|
||||
|
||||
if (pkt_hdr.addr != uport->dev->addr) {
|
||||
/*
|
||||
* fprintf(stderr,
|
||||
* "%s: USB_RET_NODEV: pkt_hdr.addr != uport->dev->addr: %d != %d\n",
|
||||
* __func__, pkt_hdr.addr, uport->dev->addr);
|
||||
*/
|
||||
/* Can't enforce this check because dwc2 address transition time is slow */
|
||||
}
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
p = usb_ep_find_packet_by_id(uport->dev, pkt_hdr.pid,
|
||||
pkt_hdr.ep, pkt_hdr.id);
|
||||
if (p) {
|
||||
pkt = container_of(p, USBTCPPacket, p);
|
||||
usb_cancel_packet(&pkt->p);
|
||||
DPRINTF("%s: TCP_USB_CANCEL: packet"
|
||||
" pid: 0x%x ep: %d id: 0x%lx len: 0x%x\n",
|
||||
__func__, pkt_hdr.pid, pkt_hdr.ep, pkt_hdr.id, p->actual_length);
|
||||
usb_tcp_host_respond_packet(s, pkt);
|
||||
} else {
|
||||
warn_report("%s: TCP_USB_CANCEL: packet"
|
||||
" pid: 0x%x ep: %d id: 0x%lx not found",
|
||||
__func__, pkt_hdr.pid, pkt_hdr.ep, pkt_hdr.id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TCP_USB_RESET:
|
||||
/* fprintf(stderr, "%s: TCP_USB_RESET\n", __func__); */
|
||||
DPRINTF("%s: TCP_USB_RESET\n", __func__);
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
usb_device_reset(uport->dev);
|
||||
break;;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void usb_tcp_host_attach(USBPort *uport)
|
||||
{
|
||||
struct sockaddr_un server_addr;
|
||||
USBTCPHostState *s = USB_TCP_HOST(uport->opaque);
|
||||
int sock = -1;
|
||||
Coroutine *co = NULL;
|
||||
QIOChannel *ioc = NULL;
|
||||
int ret;
|
||||
Error *err = NULL;
|
||||
|
||||
if (uport->index >= G_N_ELEMENTS(s->uports) - 1) {
|
||||
error_report("%s: attached to unused port\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usb_tcp_host_find_active_port(s) != uport) {
|
||||
error_report("%s: Attaching to 2 proxy ports at the same time. "
|
||||
"This port might not be able to receive packets.\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uport->dev || !uport->dev->attached) {
|
||||
return;
|
||||
}
|
||||
|
||||
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (socket < 0) {
|
||||
error_report("%s: cannot open socket", __func__);
|
||||
}
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sun_family = AF_UNIX;
|
||||
strncpy(server_addr.sun_path, socket_path, sizeof(server_addr.sun_path));
|
||||
server_addr.sun_path[sizeof(server_addr.sun_path) - 1] = '\0';
|
||||
|
||||
ret = connect(sock, (const struct sockaddr *) &server_addr, sizeof(server_addr));
|
||||
if (ret < 0) {
|
||||
error_report("%s: failed to connect to server: %d\n", __func__, ret);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
ioc = qio_channel_new_fd(sock, &err);
|
||||
if (!ioc) {
|
||||
error_report_err(err);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
qio_channel_set_blocking(ioc, false, NULL);
|
||||
s->closed = 0;
|
||||
s->ioc = ioc;
|
||||
|
||||
migrate_add_blocker(s->migration_blocker, NULL);
|
||||
co = qemu_coroutine_create(usb_tcp_host_msg_loop_co, s);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
static void usb_tcp_host_detach(USBPort *uport)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(uport->opaque);
|
||||
|
||||
usb_tcp_host_closed(s);
|
||||
}
|
||||
|
||||
static void usb_tcp_host_async_packet_complete(USBPort *port, USBPacket *p)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(port->opaque);
|
||||
usb_tcp_host_respond_packet(s, container_of(p, USBTCPPacket, p));
|
||||
}
|
||||
|
||||
static USBBusOps usb_tcp_bus_ops = { };
|
||||
|
||||
static USBPortOps usb_tcp_host_port_ops = {
|
||||
.attach = usb_tcp_host_attach,
|
||||
.detach = usb_tcp_host_detach,
|
||||
.child_detach = NULL,
|
||||
.wakeup = NULL,
|
||||
.complete = usb_tcp_host_async_packet_complete,
|
||||
};
|
||||
|
||||
static void usb_tcp_host_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(dev);
|
||||
|
||||
usb_bus_new(&s->bus, sizeof(s->bus), &usb_tcp_bus_ops, dev);
|
||||
for (int i = 0; i < G_N_ELEMENTS(s->uports); i++) {
|
||||
usb_register_port(&s->bus, &s->uports[i], s, i, &usb_tcp_host_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL |
|
||||
USB_SPEED_MASK_HIGH);
|
||||
}
|
||||
|
||||
s->closed = 1;
|
||||
qemu_co_mutex_init(&s->write_mutex);
|
||||
}
|
||||
|
||||
static void usb_tcp_host_unrealize(DeviceState *dev)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(dev);
|
||||
|
||||
if (s->ioc) {
|
||||
qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
qio_channel_close(s->ioc, NULL);
|
||||
s->ioc = NULL;
|
||||
}
|
||||
|
||||
s->closed = 1;
|
||||
s->stopped = 1;
|
||||
}
|
||||
|
||||
static void usb_tcp_host_init(Object *obj)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(obj);
|
||||
s->closed = 1;
|
||||
error_setg(&s->migration_blocker, "%s does not support migration "
|
||||
"while connected", TYPE_USB_TCP_HOST);
|
||||
}
|
||||
|
||||
static Property usb_tcp_host_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void usb_tcp_host_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = usb_tcp_host_realize;
|
||||
dc->unrealize = usb_tcp_host_unrealize;
|
||||
dc->desc = "QEMU USB Passthrough Host Controller";
|
||||
device_class_set_props(dc, usb_tcp_host_properties);
|
||||
set_bit(DEVICE_CATEGORY_USB, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo usb_tcp_host_type_info = {
|
||||
.name = TYPE_USB_TCP_HOST,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(USBTCPHostState),
|
||||
.class_init = usb_tcp_host_class_init,
|
||||
.instance_init = usb_tcp_host_init,
|
||||
};
|
||||
|
||||
static void usb_tcp_host_register_types(void)
|
||||
{
|
||||
type_register_static(&usb_tcp_host_type_info);
|
||||
}
|
||||
|
||||
type_init(usb_tcp_host_register_types)
|
||||
@@ -11,6 +11,8 @@ system_ss.add(when: 'CONFIG_USB', if_true: files(
|
||||
'pcap.c',
|
||||
))
|
||||
|
||||
system_ss.add(when: 'CONFIG_USB_TCP', if_true: files('hcd-tcp.c'))
|
||||
|
||||
# usb host adapters
|
||||
system_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c'))
|
||||
system_ss.add(when: 'CONFIG_USB_OHCI', if_true: files('hcd-ohci.c'))
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef HW_USB_TCP_USB_H
|
||||
#define HW_USB_TCP_USB_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/usb.h"
|
||||
|
||||
static const char *socket_path = "/tmp/usbqemu";
|
||||
|
||||
enum {
|
||||
TCP_USB_REQUEST = (1 << 0),
|
||||
TCP_USB_RESPONSE = (1 << 1),
|
||||
TCP_USB_RESET = (1 << 2),
|
||||
TCP_USB_CANCEL = (1 << 3)
|
||||
};
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_header {
|
||||
uint8_t type;
|
||||
} tcp_usb_header_t;
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_request_header {
|
||||
uint8_t addr;
|
||||
int pid;
|
||||
uint8_t ep;
|
||||
unsigned int stream;
|
||||
uint64_t id;
|
||||
uint8_t short_not_ok;
|
||||
uint8_t int_req;
|
||||
uint16_t length;
|
||||
} tcp_usb_request_header;
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_response_header {
|
||||
uint8_t addr;
|
||||
int pid;
|
||||
uint8_t ep;
|
||||
uint64_t id;
|
||||
uint32_t status;
|
||||
uint16_t length;
|
||||
} tcp_usb_response_header;
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_cancel_header {
|
||||
uint8_t addr;
|
||||
int pid;
|
||||
uint8_t ep;
|
||||
uint64_t id;
|
||||
|
||||
} tcp_usb_cancel_header;
|
||||
|
||||
#endif //HW_USB_TCP_USB_H
|
||||
@@ -0,0 +1,181 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_H
|
||||
#define HW_ARM_IPOD_TOUCH_H
|
||||
|
||||
#include "exec/hwaddr.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/intc/pl192.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "ui/console.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/dma/pl080.h"
|
||||
#include "hw/i2c/ipod_touch_i2c.h"
|
||||
#include "hw/usb/hcd-dwc2.h"
|
||||
#include "hw/usb/hcd-tcp.h"
|
||||
#include "hw/arm/ipod_touch_timer.h"
|
||||
#include "hw/arm/ipod_touch_clock.h"
|
||||
#include "hw/arm/ipod_touch_chipid.h"
|
||||
#include "hw/arm/ipod_touch_gpio.h"
|
||||
#include "hw/arm/ipod_touch_sysic.h"
|
||||
#include "hw/arm/ipod_touch_usb_phys.h"
|
||||
#include "hw/arm/ipod_touch_spi.h"
|
||||
#include "hw/arm/ipod_touch_sha1.h"
|
||||
#include "hw/arm/ipod_touch_aes.h"
|
||||
#include "hw/arm/ipod_touch_pke.h"
|
||||
#include "hw/arm/ipod_touch_unknown1.h"
|
||||
#include "hw/arm/ipod_touch_lcd.h"
|
||||
#include "hw/arm/ipod_touch_mipi_dsi.h"
|
||||
#include "hw/arm/ipod_touch_fmss.h"
|
||||
#include "hw/arm/ipod_touch_mbx.h"
|
||||
#include "hw/arm/ipod_touch_scaler_csc.h"
|
||||
#include "hw/arm/ipod_touch_sdio.h"
|
||||
#include "hw/arm/ipod_touch_tvout.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH "iPod-Touch"
|
||||
|
||||
#define S5L8720_VIC_N 2
|
||||
#define S5L8720_VIC_SIZE 32
|
||||
|
||||
#define S5L8720_TIMER1_IRQ 0x7
|
||||
#define S5L8720_USB_OTG_IRQ 0x13
|
||||
#define S5L8720_SPI0_IRQ 0x9
|
||||
#define S5L8720_SPI1_IRQ 0xA
|
||||
#define S5L8720_SPI2_IRQ 0xB
|
||||
#define S5L8720_LCD_IRQ 0xD
|
||||
#define S5L8720_DMAC0_IRQ 0x10
|
||||
#define S5L8720_DMAC1_IRQ 0x11
|
||||
#define S5L8720_I2C0_IRQ 0x15
|
||||
#define S5L8720_SPI3_IRQ 0x1C
|
||||
#define S5L8720_I2C1_IRQ 0x16
|
||||
#define S5L8720_TVOUT_SDO_IRQ 0x1E
|
||||
#define S5L8720_SDIO_IRQ 0x2A
|
||||
#define S5L8720_FMSS_IRQ 0x36
|
||||
#define S5L8720_SPI4_IRQ 0x37
|
||||
|
||||
// GPIO interrupts
|
||||
#define S5L8900_GPIO_G0_IRQ 0x21
|
||||
#define S5L8900_GPIO_G1_IRQ 0x20
|
||||
#define S5L8900_GPIO_G2_IRQ 0x1F
|
||||
#define S5L8900_GPIO_G3_IRQ 0x03
|
||||
#define S5L8900_GPIO_G4_IRQ 0x02
|
||||
|
||||
// key codes
|
||||
#define KEY_UP (1 << 7)
|
||||
|
||||
#define KEY_P 25
|
||||
#define KEY_P_DOWN KEY_P
|
||||
#define KEY_P_UP (KEY_P_DOWN | KEY_UP)
|
||||
|
||||
#define KEY_H 35
|
||||
#define KEY_H_DOWN KEY_H
|
||||
#define KEY_H_UP (KEY_H_DOWN | KEY_UP)
|
||||
|
||||
#define KEY_MIN 12
|
||||
#define KEY_MIN_DOWN KEY_MIN
|
||||
#define KEY_MIN_UP (KEY_MIN_DOWN | KEY_UP)
|
||||
|
||||
#define KEY_PLUS 13
|
||||
#define KEY_PLUS_DOWN KEY_PLUS
|
||||
#define KEY_PLUS_UP (KEY_PLUS_DOWN | KEY_UP)
|
||||
|
||||
const int S5L8900_GPIO_IRQS[5] = { S5L8900_GPIO_G0_IRQ, S5L8900_GPIO_G1_IRQ, S5L8900_GPIO_G2_IRQ, S5L8900_GPIO_G3_IRQ, S5L8900_GPIO_G4_IRQ };
|
||||
|
||||
#define IT2G_CPREG_VAR_NAME(name) cpreg_##name
|
||||
#define IT2G_CPREG_VAR_DEF(name) uint64_t IT2G_CPREG_VAR_NAME(name)
|
||||
|
||||
#define TYPE_IPOD_TOUCH_MACHINE MACHINE_TYPE_NAME(TYPE_IPOD_TOUCH)
|
||||
#define IPOD_TOUCH_MACHINE(obj) \
|
||||
OBJECT_CHECK(IPodTouchMachineState, (obj), TYPE_IPOD_TOUCH_MACHINE)
|
||||
|
||||
// memory addresses
|
||||
#define VROM_MEM_BASE 0x0
|
||||
#define INSECURE_RAM_MEM_BASE 0x8000000
|
||||
#define SECURE_RAM_MEM_BASE 0xB000000
|
||||
#define FRAMEBUFFER_MEM_BASE 0xFB00000
|
||||
#define IBOOT_MEM_BASE 0xFF00000
|
||||
#define SRAM1_MEM_BASE 0x22020000
|
||||
#define SHA1_MEM_BASE 0x38000000
|
||||
#define DMAC0_MEM_BASE 0x38200000
|
||||
#define USBOTG_MEM_BASE 0x38400000
|
||||
#define DMAC1_0_MEM_BASE 0x38700000
|
||||
#define DISPLAY_MEM_BASE 0x38900000
|
||||
#define FMSS_MEM_BASE 0x38A00000
|
||||
#define AES_MEM_BASE 0x38C00000
|
||||
#define SDIO_MEM_BASE 0x38D00000
|
||||
#define VIC0_MEM_BASE 0x38E00000
|
||||
#define VIC1_MEM_BASE 0x38E01000
|
||||
#define EDGEIC_MEM_BASE 0x38E02000
|
||||
#define H264_MEM_BASE 0x38F00000
|
||||
#define SCALER_CSC_MEM_BASE 0x39000000
|
||||
#define TVOUT_MIXER2_MEM_BASE 0x39100000
|
||||
#define TVOUT_MIXER1_MEM_BASE 0x39200000
|
||||
#define TVOUT_SDO_MEM_BASE 0x39300000
|
||||
#define SYSIC_MEM_BASE 0x39700000
|
||||
#define DMAC1_1_MEM_BASE 0x39900000
|
||||
#define MBX1_MEM_BASE 0x3B000000
|
||||
#define MBX2_MEM_BASE 0x39400000
|
||||
#define SPI0_MEM_BASE 0x3C300000
|
||||
#define USBPHYS_MEM_BASE 0x3C400000
|
||||
#define CLOCK0_MEM_BASE 0x3C500000
|
||||
#define I2C0_MEM_BASE 0x3C600000
|
||||
#define TIMER1_MEM_BASE 0x3C700000
|
||||
#define I2C1_MEM_BASE 0x3C900000
|
||||
#define UART0_MEM_BASE 0x3CC00000
|
||||
#define SPI1_MEM_BASE 0x3CE00000
|
||||
#define GPIO_MEM_BASE 0x3CF00000
|
||||
#define PKE_MEM_BASE 0x3D000000
|
||||
#define CHIPID_MEM_BASE 0x3D100000
|
||||
#define SPI2_MEM_BASE 0x3D200000
|
||||
#define UNKNOWN1_MEM_BASE 0x3D700000
|
||||
#define MIPI_DSI_MEM_BASE 0x3D800000
|
||||
#define SPI3_MEM_BASE 0x3DA00000
|
||||
#define UART1_MEM_BASE 0x3DB00000
|
||||
#define UART2_MEM_BASE 0x3DC00000
|
||||
#define UART3_MEM_BASE 0x3DD00000
|
||||
#define SWI_MEM_BASE 0x3DE00000
|
||||
#define CLOCK1_MEM_BASE 0x3E000000
|
||||
#define SPI4_MEM_BASE 0x3E100000
|
||||
|
||||
typedef struct {
|
||||
MachineClass parent;
|
||||
} IPodTouchMachineClass;
|
||||
|
||||
typedef struct {
|
||||
MachineState parent;
|
||||
AddressSpace *nsas;
|
||||
qemu_irq **irq;
|
||||
ARMCPU *cpu;
|
||||
PL192State *vic0;
|
||||
PL192State *vic1;
|
||||
IPodTouchClockState *clock0;
|
||||
IPodTouchClockState *clock1;
|
||||
IPodTouchTimerState *timer1;
|
||||
IPodTouchSPIState *spi0_state;
|
||||
IPodTouchSPIState *spi1_state;
|
||||
IPodTouchSPIState *spi4_state;
|
||||
IPodTouchChipIDState *chipid_state;
|
||||
IPodTouchGPIOState *gpio_state;
|
||||
IPodTouchSYSICState *sysic;
|
||||
MemoryRegion usb_dma_container_mr;
|
||||
DWC2State *dwc2;
|
||||
IPodTouchUSBPhysState *usb_phys_state;
|
||||
IPodTouchSHA1State *sha1_state;
|
||||
IPodTouchAESState *aes_state;
|
||||
IPodTouchPKEState *pke_state;
|
||||
IPodTouchI2CState *i2c0_state;
|
||||
IPodTouchI2CState *i2c1_state;
|
||||
IPodTouchLCDState *lcd_state;
|
||||
IPodTouchMIPIDSIState *mipi_dsi_state;
|
||||
IPodTouchFMSSState *fmss_state;
|
||||
IPodTouchMBXState *mbx_state;
|
||||
IPodTouchScalerCSCState *scaler_csc_state;
|
||||
IPodTouchSDIOState *sdio_state;
|
||||
IPodTouchTVOutState *tvout_state;
|
||||
Clock *sysclk;
|
||||
char bootrom_path[1024];
|
||||
char nor_path[1024];
|
||||
char nand_path[1024];
|
||||
IT2G_CPREG_VAR_DEF(REG0);
|
||||
IT2G_CPREG_VAR_DEF(REG1);
|
||||
} IPodTouchMachineState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_AES_H
|
||||
#define HW_ARM_IPOD_TOUCH_AES_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
#include <openssl/aes.h>
|
||||
|
||||
#define TYPE_IPOD_TOUCH_AES "ipodtouch.aes"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchAESState, IPOD_TOUCH_AES)
|
||||
|
||||
#define key_uid ((uint8_t[]){0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF})
|
||||
|
||||
#define AES_128_CBC_BLOCK_SIZE 64
|
||||
#define AES_CONTROL 0x0
|
||||
#define AES_GO 0x4
|
||||
#define AES_UNKREG0 0x8
|
||||
#define AES_STATUS 0xC
|
||||
#define AES_UNKREG1 0x10
|
||||
#define AES_KEYLEN 0x14
|
||||
#define AES_INSIZE 0x18
|
||||
#define AES_INADDR 0x20
|
||||
#define AES_OUTSIZE 0x24
|
||||
#define AES_OUTADDR 0x28
|
||||
#define AES_AUXSIZE 0x2C
|
||||
#define AES_AUXADDR 0x30
|
||||
#define AES_SIZE3 0x34
|
||||
#define AES_KEY_REG 0x4C
|
||||
#define AES_TYPE 0x6C
|
||||
#define AES_IV_REG 0x74
|
||||
#define AES_KEYSIZE 0x20
|
||||
#define AES_IVSIZE 0x10
|
||||
|
||||
typedef enum AESKeyType {
|
||||
AESCustom = 0,
|
||||
AESGID = 1,
|
||||
AESUID = 2
|
||||
} AESKeyType;
|
||||
|
||||
typedef enum AESKeyLen {
|
||||
AES128 = 0,
|
||||
AES192 = 1,
|
||||
AES256 = 2
|
||||
} AESKeyLen;
|
||||
|
||||
typedef struct IPodTouchAESState
|
||||
{
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
AES_KEY decryptKey;
|
||||
uint32_t ivec[4];
|
||||
uint32_t insize;
|
||||
uint32_t inaddr;
|
||||
uint32_t outsize;
|
||||
uint32_t outaddr;
|
||||
uint32_t auxaddr;
|
||||
uint32_t keytype;
|
||||
uint32_t status;
|
||||
uint32_t ctrl;
|
||||
uint32_t unkreg0;
|
||||
uint32_t unkreg1;
|
||||
uint32_t operation;
|
||||
uint32_t keylen;
|
||||
uint32_t custkey[8];
|
||||
uint8_t gid_encryption_count;
|
||||
} IPodTouchAESState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef HW_CD3272_MIKEY_H
|
||||
#define HW_CD3272_MIKEY_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
#include "time.h"
|
||||
|
||||
#define TYPE_CD3272MIKEY "cd3272mikey"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(CD3272MikeyState, CD3272MIKEY)
|
||||
|
||||
typedef struct CD3272MikeyState {
|
||||
I2CSlave i2c;
|
||||
uint32_t cmd;
|
||||
} CD3272MikeyState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_CHIPID_H
|
||||
#define HW_ARM_IPOD_TOUCH_CHIPID_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_CHIPID "ipodtouch.chipid"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchChipIDState, IPOD_TOUCH_CHIPID)
|
||||
|
||||
#define CHIPID_UNKNOWN1 0x4
|
||||
#define CHIPID_INFO 0x8
|
||||
#define CHIPID_UNKNOWN2 0xC
|
||||
#define CHIPID_UNKNOWN3 0x10
|
||||
|
||||
typedef struct IPodTouchChipIDState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
} IPodTouchChipIDState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef IPOD_TOUCH_CLOCK_H
|
||||
#define IPOD_TOUCH_CLOCK_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_CLOCK "ipodtouch.clock"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchClockState, IPOD_TOUCH_CLOCK)
|
||||
|
||||
#define CLOCK_CONFIG0 0x0
|
||||
#define CLOCK_CONFIG1 0x4
|
||||
#define CLOCK_CONFIG2 0x8
|
||||
#define CLOCK_CONFIG3 0xC
|
||||
#define CLOCK_CONFIG4 0x10
|
||||
#define CLOCK_CONFIG5 0x14
|
||||
|
||||
#define CLOCK_PLL0CON 0x20
|
||||
#define CLOCK_PLL1CON 0x24
|
||||
#define CLOCK_PLL2CON 0x28
|
||||
#define CLOCK_PLL3CON 0x2C
|
||||
#define CLOCK_PLL0LCNT 0x30
|
||||
#define CLOCK_PLL1LCNT 0x34
|
||||
#define CLOCK_PLL2LCNT 0x38
|
||||
#define CLOCK_PLL3LCNT 0x3C
|
||||
#define CLOCK_PLLLOCK 0x40
|
||||
#define CLOCK_PLLMODE 0x44
|
||||
|
||||
#define CLOCK_PWRCON0 0x48
|
||||
#define CLOCK_PWRCON1 0x4C
|
||||
#define CLOCK_PWRCON2 0x58
|
||||
#define CLOCK_PWRCON3 0x68
|
||||
#define CLOCK_PWRCON4 0x6C
|
||||
|
||||
typedef struct IPodTouchClockState
|
||||
{
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t config0;
|
||||
uint32_t config1;
|
||||
uint32_t config2;
|
||||
uint32_t config3;
|
||||
uint32_t config4;
|
||||
uint32_t config5;
|
||||
|
||||
uint32_t pll0con;
|
||||
uint32_t pll1con;
|
||||
uint32_t pll2con;
|
||||
uint32_t pll3con;
|
||||
uint32_t pll0lcnt;
|
||||
uint32_t pll1lcnt;
|
||||
uint32_t pll2lcnt;
|
||||
uint32_t pll3lcnt;
|
||||
uint32_t pllmode;
|
||||
|
||||
uint32_t pwrcon0;
|
||||
uint32_t pwrcon1;
|
||||
uint32_t pwrcon2;
|
||||
uint32_t pwrcon3;
|
||||
uint32_t pwrcon4;
|
||||
|
||||
} IPodTouchClockState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef IPOD_TOUCH_FMSS_H
|
||||
#define IPOD_TOUCH_FMSS_H
|
||||
|
||||
#include <math.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_FMSS "ipodtouch.fmss"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchFMSSState, IPOD_TOUCH_FMSS)
|
||||
|
||||
#define NAND_BYTES_PER_PAGE 4096
|
||||
#define NAND_BYTES_PER_SPARE 64
|
||||
|
||||
#define FMSS__FMCTRL1 0x4
|
||||
#define FMSS__CS_IRQ 0xC0C
|
||||
#define FMSS__CS_IRQMASK 0xC10
|
||||
#define FMSS__CS_BUF_RST_OK 0xC64
|
||||
#define FMSS_CINFO_TARGET_ADDR 0xD08
|
||||
#define FMSS_PAGES_IN_ADDR 0xD0C
|
||||
#define FMSS_CS_BUF_ADDR 0xD10
|
||||
#define FMSS_NUM_PAGES 0xD18
|
||||
#define FMSS_PAGE_SPARE_OUT_ADDR 0xD1C
|
||||
#define FMSS_PAGES_OUT_ADDR 0xD20
|
||||
#define FMSS_CSGENRC 0xD30
|
||||
|
||||
typedef struct IPodTouchFMSSState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
uint8_t *page_buffer;
|
||||
uint8_t *page_spare_buffer;
|
||||
|
||||
uint32_t reg_cs_irq_bit;
|
||||
uint32_t reg_cinfo_target_addr;
|
||||
uint32_t reg_pages_in_addr;
|
||||
uint32_t reg_cs_buf_addr;
|
||||
uint32_t reg_num_pages;
|
||||
uint32_t reg_page_spare_out_addr;
|
||||
uint32_t reg_pages_out_addr;
|
||||
uint32_t reg_csgenrc;
|
||||
char *nand_path;
|
||||
} IPodTouchFMSSState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef IPOD_TOUCH_GPIO_H
|
||||
#define IPOD_TOUCH_GPIO_H
|
||||
|
||||
#include <math.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_GPIO "ipodtouch.gpio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchGPIOState, IPOD_TOUCH_GPIO)
|
||||
|
||||
#define GPIO_BUTTON_VOLUP 0x902
|
||||
#define GPIO_FORCE_DFU 0xB03
|
||||
#define GPIO_BUTTON_VOLDOWN 0xC00
|
||||
#define GPIO_BUTTON_HOME 0xC01
|
||||
#define GPIO_BUTTON_POWER 0xC02
|
||||
#define GPIO_DFU_STATUS 0xC03
|
||||
|
||||
#define GPIO_BUTTON_POWER_IRQ 0x7A
|
||||
#define GPIO_BUTTON_HOME_IRQ 0x79
|
||||
#define GPIO_BUTTON_VOLUP_IRQ 0x78
|
||||
#define GPIO_BUTTON_VOLDOWN_IRQ 0x62
|
||||
|
||||
#define NUM_GPIO_PADS 0xC
|
||||
#define NUM_GPIO_PINS 0x20
|
||||
|
||||
#define GPIO2PIN(gpio) ((gpio) & 7)
|
||||
#define GPIO2PAD(gpio) (((gpio) >> 8) & 0xFF)
|
||||
#define GPIOADDR2PAD(addr) (addr - 0x4) / 0x20
|
||||
|
||||
typedef struct IPodTouchGPIOState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
uint32_t gpio_state[NUM_GPIO_PADS];
|
||||
} IPodTouchGPIOState;
|
||||
|
||||
bool gpio_is_on(uint32_t *state, uint32_t gpio);
|
||||
bool gpio_is_off(uint32_t *state, uint32_t gpio);
|
||||
void gpio_set_on(uint32_t *state, uint32_t gpio);
|
||||
void gpio_set_off(uint32_t *state, uint32_t gpio);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef IPOD_TOUCH_LCD_H
|
||||
#define IPOD_TOUCH_LCD_H
|
||||
|
||||
#include <math.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/arm/ipod_touch_multitouch.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_LCD "ipodtouch.lcd"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchLCDState, IPOD_TOUCH_LCD)
|
||||
|
||||
#define LCD_REFRESH_RATE_FREQUENCY 10
|
||||
|
||||
typedef struct IPodTouchLCDState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion *sysmem;
|
||||
MemoryRegion iomem;
|
||||
QemuConsole *con;
|
||||
IPodTouchMultitouchState *mt;
|
||||
int invalidate;
|
||||
MemoryRegionSection fbsection;
|
||||
qemu_irq irq;
|
||||
uint32_t lcd_con;
|
||||
|
||||
uint32_t w1_display_resolution_info;
|
||||
uint32_t w1_framebuffer_base;
|
||||
uint32_t w1_hspan;
|
||||
uint32_t w1_display_depth_info;
|
||||
|
||||
uint32_t unknown1;
|
||||
|
||||
QEMUTimer *refresh_timer;
|
||||
} IPodTouchLCDState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef HW_LIS302DL_H
|
||||
#define HW_LIS302DL_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_LIS302DL "lis302dl"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LIS302DLState, LIS302DL)
|
||||
|
||||
#define ACCEL_WHOAMI 0x0F
|
||||
|
||||
#define ACCEL_WHOAMI_VALUE 0x3B
|
||||
|
||||
typedef struct LIS302DLState {
|
||||
I2CSlave i2c;
|
||||
uint32_t cmd;
|
||||
} LIS302DLState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_MBX_H
|
||||
#define HW_ARM_IPOD_TOUCH_MBX_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_MBX "ipodtouch.mbx"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMBXState, IPOD_TOUCH_MBX)
|
||||
|
||||
typedef struct IPodTouchMBXState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem1;
|
||||
MemoryRegion iomem2;
|
||||
} IPodTouchMBXState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef IPOD_TOUCH_MIPI_DSI_H
|
||||
#define IPOD_TOUCH_MIPI_DSI_H
|
||||
|
||||
#include <math.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_MIPI_DSI "ipodtouch.mipidsi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMIPIDSIState, IPOD_TOUCH_MIPI_DSI)
|
||||
|
||||
#define REG_INTSRC 0x2C
|
||||
#define REG_PKTHDR 0x34
|
||||
#define REG_RXFIFO 0x3C
|
||||
#define REG_FIFOCTRL 0x44
|
||||
|
||||
#define DSIM_RSP_LONG_READ 0x1A
|
||||
#define rDSIM_FIFOCTRL_EmptyHSfr 0x400000
|
||||
#define rDSIM_STATUS_TxReadyHsClk 0x400
|
||||
#define rDSIM_INTSRC_RxDatDone 0x00040000
|
||||
|
||||
typedef struct IPodTouchMIPIDSIState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
uint32_t pkthdr_reg;
|
||||
bool return_panel_id;
|
||||
} IPodTouchMIPIDSIState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,160 @@
|
||||
#ifndef IPOD_TOUCH_MULTITOUCH_H
|
||||
#define IPOD_TOUCH_MULTITOUCH_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "hw/arm/ipod_touch_sysic.h"
|
||||
#include "hw/arm/ipod_touch_gpio.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_MULTITOUCH "ipodtouch.multitouch"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMultitouchState, IPOD_TOUCH_MULTITOUCH)
|
||||
|
||||
#define MT_INTERFACE_VERSION 0x1
|
||||
#define MT_FAMILY_ID 81
|
||||
#define MT_ENDIANNESS 0x1
|
||||
#define MT_SENSOR_ROWS 15
|
||||
#define MT_SENSOR_COLUMNS 10
|
||||
#define MT_BCD_VERSION 51
|
||||
#define MT_SENSOR_REGION_DESC 0x0
|
||||
#define MT_SENSOR_REGION_PARAM 0x0
|
||||
#define MT_MAX_PACKET_SIZE 0x294 // 660
|
||||
#define MT_SENSOR_SURFACE_WIDTH 5000
|
||||
#define MT_SENSOR_SURFACE_HEIGHT 7500
|
||||
|
||||
// internal surface width/height
|
||||
#define MT_INTERNAL_SENSOR_SURFACE_WIDTH (9000 - MT_SENSOR_SURFACE_WIDTH) * 84 / 73
|
||||
#define MT_INTERNAL_SENSOR_SURFACE_HEIGHT (13850 - MT_SENSOR_SURFACE_HEIGHT) * 84 / 73
|
||||
|
||||
// report IDs
|
||||
#define MT_REPORT_UNKNOWN1 0x70
|
||||
#define MT_REPORT_FAMILY_ID 0xD1
|
||||
#define MT_REPORT_SENSOR_INFO 0xD3
|
||||
#define MT_REPORT_SENSOR_REGION_DESC 0xD0
|
||||
#define MT_REPORT_SENSOR_REGION_PARAM 0xA1
|
||||
#define MT_REPORT_SENSOR_DIMENSIONS 0xD9
|
||||
|
||||
// report sizes
|
||||
#define MT_REPORT_UNKNOWN1_SIZE 0x1
|
||||
#define MT_REPORT_FAMILY_ID_SIZE 0x1
|
||||
#define MT_REPORT_SENSOR_INFO_SIZE 0x5
|
||||
#define MT_REPORT_SENSOR_REGION_DESC_SIZE 0x1
|
||||
#define MT_REPORT_SENSOR_REGION_PARAM_SIZE 0x1
|
||||
#define MT_REPORT_SENSOR_DIMENSIONS_SIZE 0x8
|
||||
|
||||
#define MT_CMD_HBPP_DATA_PACKET 0x30
|
||||
#define MT_CMD_GET_CMD_STATUS 0xE1
|
||||
#define MT_CMD_GET_INTERFACE_VERSION 0xE2
|
||||
#define MT_CMD_GET_REPORT_INFO 0xE3
|
||||
#define MT_CMD_SHORT_CONTROL_WRITE 0xE4
|
||||
#define MT_CMD_SHORT_CONTROL_READ 0xE6
|
||||
#define MT_CMD_FRAME_READ 0xEA
|
||||
|
||||
// frame types
|
||||
#define MT_FRAME_TYPE_PATH 0x44
|
||||
|
||||
// frame event types
|
||||
#define MT_EVENT_TOUCH_FULL_END 0x0
|
||||
#define MT_EVENT_TOUCH_START 0x3
|
||||
#define MT_EVENT_TOUCH_MOVED 0x4
|
||||
#define MT_EVENT_TOUCH_ENDED 0x7
|
||||
|
||||
typedef struct MTFrameLengthPacket
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t length1;
|
||||
uint8_t length2;
|
||||
uint8_t unused[11];
|
||||
uint8_t checksum1;
|
||||
uint8_t checksum2;
|
||||
} __attribute__((__packed__)) MTFrameLengthPacket;
|
||||
|
||||
typedef struct MTFrameHeader
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t frameNum;
|
||||
uint8_t headerLen;
|
||||
uint8_t unk_3;
|
||||
uint32_t timestamp;
|
||||
uint8_t unk_8;
|
||||
uint8_t unk_9;
|
||||
uint8_t unk_A;
|
||||
uint8_t unk_B;
|
||||
uint16_t unk_C;
|
||||
uint16_t isImage;
|
||||
|
||||
uint8_t numFingers;
|
||||
uint8_t fingerDataLen;
|
||||
uint16_t unk_12;
|
||||
uint16_t unk_14;
|
||||
uint16_t unk_16;
|
||||
} __attribute__((__packed__)) MTFrameHeader;
|
||||
|
||||
typedef struct MTFramePacket
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t unused1;
|
||||
uint8_t length1;
|
||||
uint8_t length2;
|
||||
uint8_t checksum_pad;
|
||||
MTFrameHeader header;
|
||||
} __attribute__((__packed__)) MTFramePacket;
|
||||
|
||||
typedef struct FingerData
|
||||
{
|
||||
uint8_t id;
|
||||
uint8_t event;
|
||||
uint8_t unk_2;
|
||||
uint8_t unk_3;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t velX;
|
||||
int16_t velY;
|
||||
uint16_t radius2;
|
||||
uint16_t radius3;
|
||||
uint16_t angle;
|
||||
uint16_t radius1;
|
||||
uint16_t contactDensity;
|
||||
uint16_t unk_16;
|
||||
uint16_t unk_18;
|
||||
uint16_t unk_1A;
|
||||
} __attribute__((__packed__)) FingerData;
|
||||
|
||||
typedef struct MTFrame {
|
||||
MTFrameLengthPacket frame_length;
|
||||
MTFramePacket frame_packet;
|
||||
FingerData finger_data; // TODO we assume one finger for now
|
||||
uint8_t checksum1;
|
||||
uint8_t checksum2;
|
||||
} __attribute__((__packed__)) MTFrame;
|
||||
|
||||
typedef struct IPodTouchMultitouchState {
|
||||
SSIPeripheral ssidev;
|
||||
uint8_t cur_cmd;
|
||||
uint8_t *out_buffer;
|
||||
uint8_t *in_buffer;
|
||||
uint32_t buf_size;
|
||||
uint32_t buf_ind;
|
||||
uint32_t in_buffer_ind;
|
||||
uint8_t hbpp_atn_ack_response[2];
|
||||
MTFrame *next_frame;
|
||||
uint32_t frame_counter;
|
||||
bool touch_down;
|
||||
QEMUTimer *touch_timer;
|
||||
QEMUTimer *touch_end_timer;
|
||||
IPodTouchSYSICState *sysic;
|
||||
IPodTouchGPIOState *gpio_state;
|
||||
float touch_x;
|
||||
float touch_y;
|
||||
float prev_touch_x;
|
||||
float prev_touch_y;
|
||||
uint64_t last_frame_timestamp;
|
||||
} IPodTouchMultitouchState;
|
||||
|
||||
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s);
|
||||
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef IPOD_TOUCH_NOR_SPI_H
|
||||
#define IPOD_TOUCH_NOR_SPI_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "hw/hw.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_NOR_SPI "ipodtouch.norspi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchNORSPIState, IPOD_TOUCH_NOR_SPI)
|
||||
|
||||
#define NOR_WRITE_TO_STATUS_REG 0x1
|
||||
#define NOR_WRITE_DATA_CMD 0x2
|
||||
#define NOR_READ_DATA_CMD 0x3
|
||||
#define NOR_DISABLE_WRITE 0x4
|
||||
#define NOR_GET_STATUS_CMD 0x5
|
||||
#define NOR_ENABLE_WRITE 0x6
|
||||
#define NOR_ERASE_BLOCK 0x20
|
||||
#define NOR_GET_JEDECID 0x9F
|
||||
|
||||
typedef struct IPodTouchNORSPIState {
|
||||
SSIPeripheral ssidev;
|
||||
char *nor_path;
|
||||
uint32_t cur_cmd;
|
||||
uint8_t *in_buf;
|
||||
uint8_t *out_buf;
|
||||
uint32_t in_buf_size;
|
||||
uint32_t out_buf_size;
|
||||
uint32_t in_buf_cur_ind;
|
||||
uint32_t out_buf_cur_ind;
|
||||
uint8_t *nor_data;
|
||||
uint8_t write_enabled;
|
||||
uint32_t nor_read_ind;
|
||||
bool nor_initialized;
|
||||
} IPodTouchNORSPIState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef HW_PCF50633_PMU_H
|
||||
#define HW_PCF50633_PMU_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
#include "time.h"
|
||||
|
||||
#define TYPE_PCF50633 "pcf50633"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Pcf50633State, PCF50633)
|
||||
|
||||
#define PMU_MBCS1 0x4B
|
||||
#define PMU_ADCC1 0x57
|
||||
|
||||
// RTC registers
|
||||
#define PMU_RTCSC 0x59
|
||||
#define PMU_RTCMN 0x5A
|
||||
#define PMU_RTCHR 0x5B
|
||||
#define PMU_RTCWD 0x5C
|
||||
#define PMU_RTCDT 0x5D
|
||||
#define PMU_RTCMT 0x5E
|
||||
#define PMU_RTCYR 0x5F
|
||||
|
||||
typedef struct Pcf50633State {
|
||||
I2CSlave i2c;
|
||||
uint32_t cmd;
|
||||
} Pcf50633State;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_PKE_H
|
||||
#define HW_ARM_IPOD_TOUCH_PKE_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_PKE "ipodtouch.pke"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchPKEState, IPOD_TOUCH_PKE)
|
||||
|
||||
#define REG_PKE_START 0x8
|
||||
#define REG_PKE_SEG_SIZE 0x14
|
||||
#define REG_PKE_SWRESET 0x24
|
||||
#define REG_PKE_SEG_START 0x800
|
||||
|
||||
typedef struct IPodTouchPKEState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint8_t segments[1024];
|
||||
uint32_t seg_size_reg;
|
||||
uint32_t segment_size;
|
||||
uint8_t num_started;
|
||||
} IPodTouchPKEState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_SCALER_CSC_H
|
||||
#define HW_ARM_IPOD_TOUCH_SCALER_CSC_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SCALER_CSC "ipodtouch.scalercsc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchScalerCSCState, IPOD_TOUCH_SCALER_CSC)
|
||||
|
||||
|
||||
typedef struct IPodTouchScalerCSCState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
} IPodTouchScalerCSCState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef IPOD_TOUCH_SDIO_H
|
||||
#define IPOD_TOUCH_SDIO_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SDIO "ipodtouch.sdio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSDIOState, IPOD_TOUCH_SDIO)
|
||||
|
||||
#define SDIO_CMD 0x8
|
||||
#define SDIO_ARGU 0xC
|
||||
#define SDIO_STATE 0x10
|
||||
#define SDIO_STAC 0x14
|
||||
#define SDIO_DSTA 0x18
|
||||
#define SDIO_RESP0 0x20
|
||||
#define SDIO_RESP1 0x24
|
||||
#define SDIO_RESP2 0x28
|
||||
#define SDIO_RESP3 0x2C
|
||||
#define SDIO_CSR 0x34
|
||||
#define SDIO_IRQ 0x38
|
||||
#define SDIO_IRQMASK 0x3C
|
||||
#define SDIO_BADDR 0x44
|
||||
#define SDIO_BLKLEN 0x48
|
||||
#define SDIO_NUMBLK 0x4C
|
||||
|
||||
#define CMD5_FUNC_OFFSET 28
|
||||
#define CIS_OFFSET 0xC8
|
||||
#define CIS_MANUFACTURER_ID 0x20
|
||||
#define CIS_FUNCTION_EXTENSION 0x22
|
||||
|
||||
#define BCM4325_FUNCTIONS 0x1
|
||||
#define BCM4325_MANUFACTURER 0x4D50
|
||||
#define BCM4325_PRODUCT_ID 0x4D48
|
||||
|
||||
typedef struct BCM4325FrameHeaderPacket
|
||||
{
|
||||
uint16_t frame_length;
|
||||
uint16_t checksum;
|
||||
} __attribute__((__packed__)) BCM4325FrameHeaderPacket;
|
||||
|
||||
typedef struct IPodTouchSDIOState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
|
||||
uint32_t cmd;
|
||||
uint32_t arg;
|
||||
uint32_t state;
|
||||
uint32_t stac;
|
||||
uint32_t csr;
|
||||
uint32_t resp0;
|
||||
uint32_t resp1;
|
||||
uint32_t resp2;
|
||||
uint32_t resp3;
|
||||
uint32_t irq_reg;
|
||||
uint32_t irq_mask;
|
||||
uint32_t baddr;
|
||||
uint32_t blklen;
|
||||
uint32_t numblk;
|
||||
QEMUTimer *irq_timer;
|
||||
qemu_irq irq;
|
||||
qemu_irq irq2;
|
||||
GQueue *rx_fifo;
|
||||
uint8_t registers[0x10000];
|
||||
} IPodTouchSDIOState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_SHA1_H
|
||||
#define HW_ARM_IPOD_TOUCH_SHA1_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SHA1 "ipodtouch.sha1"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSHA1State, IPOD_TOUCH_SHA1)
|
||||
|
||||
#define SHA1_BUFFER_SIZE 1024 * 1024 * 5
|
||||
|
||||
#define SHA_CONFIG 0x0
|
||||
#define SHA_RESET 0x4
|
||||
#define SHA_MEMORY_MODE 0x80 // whether we read from the memory
|
||||
#define SHA_MEMORY_START 0x84
|
||||
#define SHA_INSIZE 0x8c
|
||||
|
||||
typedef struct IPodTouchSHA1State {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t config;
|
||||
uint32_t memory_start;
|
||||
uint32_t memory_mode;
|
||||
uint32_t insize;
|
||||
uint8_t buffer[SHA1_BUFFER_SIZE];
|
||||
uint32_t hw_buffer[0x10]; // hardware buffer
|
||||
uint32_t buffer_ind;
|
||||
uint8_t hashout[0x14];
|
||||
bool hw_buffer_dirty;
|
||||
bool hash_computed;
|
||||
SHA_CTX ctx;
|
||||
} IPodTouchSHA1State;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_SPI_H
|
||||
#define HW_ARM_IPOD_TOUCH_SPI_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/fifo8.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "hw/arm/ipod_touch_nor_spi.h"
|
||||
#include "hw/arm/ipod_touch_multitouch.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SPI "ipodtouch.spi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSPIState, IPOD_TOUCH_SPI)
|
||||
|
||||
#define R_CTRL 0x000
|
||||
#define R_CTRL_RUN (1 << 0)
|
||||
#define R_CTRL_TX_RESET (1 << 2)
|
||||
#define R_CTRL_RX_RESET (1 << 3)
|
||||
|
||||
#define R_CFG 0x004
|
||||
#define R_CFG_AGD (1 << 0)
|
||||
#define R_CFG_CPHA (1 << 1)
|
||||
#define R_CFG_CPOL (1 << 2)
|
||||
#define R_CFG_IE_RXREADY (1 << 7)
|
||||
#define R_CFG_IE_TXEMPTY (1 << 8)
|
||||
#define R_CFG_WORD_SIZE(_x) (((_x) >> 15) & 0x3)
|
||||
#define R_CFG_WORD_SIZE_8B 0
|
||||
#define R_CFG_WORD_SIZE_16B 1
|
||||
#define R_CFG_WORD_SIZE_32B 2
|
||||
#define R_CFG_IE_COMPLETE (1 << 21)
|
||||
|
||||
#define R_STATUS 0x008
|
||||
#define R_STATUS_RXREADY (1 << 0)
|
||||
#define R_STATUS_TXEMPTY (1 << 1)
|
||||
#define R_STATUS_RXOVERFLOW (1 << 3)
|
||||
#define R_STATUS_COMPLETE (1 << 22)
|
||||
#define R_STATUS_TXFIFO_SHIFT (6)
|
||||
#define R_STATUS_RXFIFO_SHIFT (11)
|
||||
|
||||
#define R_PIN 0x00c
|
||||
#define R_PIN_CS (1 << 1)
|
||||
|
||||
#define R_TXDATA 0x010
|
||||
#define R_RXDATA 0x020
|
||||
#define R_CLKDIV 0x030
|
||||
#define R_CLKDIV_MAX 0x7ff
|
||||
#define R_RXCNT 0x034
|
||||
#define R_WORD_DELAY 0x038
|
||||
#define R_TXCNT 0x04c
|
||||
|
||||
#define R_FIFO_TX_DEPTH 50000
|
||||
#define R_FIFO_RX_DEPTH 16
|
||||
|
||||
#define REG(_s,_v) ((_s)->regs[(_v)>>2])
|
||||
#define MMIO_SIZE (0x4000)
|
||||
|
||||
|
||||
typedef struct IPodTouchSPIState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
SSIBus *spi;
|
||||
IPodTouchMultitouchState *mt;
|
||||
IPodTouchNORSPIState *nor;
|
||||
|
||||
qemu_irq irq;
|
||||
uint32_t last_irq;
|
||||
qemu_irq cs_line;
|
||||
|
||||
uint32_t regs[MMIO_SIZE >> 2];
|
||||
uint8_t base;
|
||||
Fifo8 rx_fifo;
|
||||
Fifo8 tx_fifo;
|
||||
} IPodTouchSPIState;
|
||||
|
||||
void set_spi_base(uint32_t base);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef IPOD_TOUCH_SYSIC_H
|
||||
#define IPOD_TOUCH_SYSIC_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SYSIC "ipodtouch.sysic"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSYSICState, IPOD_TOUCH_SYSIC)
|
||||
|
||||
#define POWER_ID 0x44
|
||||
#define POWER_ONCTRL 0xC
|
||||
#define POWER_OFFCTRL 0x10
|
||||
#define POWER_SETSTATE 0x8
|
||||
#define POWER_STATE 0x14 // seems to be toggled by writing a 1 to the right device ID - cleared to 0 when the device has started.
|
||||
|
||||
#define POWER_ID_ADM 0x10
|
||||
|
||||
// the GPIO IC is part of the system controller
|
||||
#define GPIO_INTLEVEL 0x80
|
||||
#define GPIO_INTSTAT 0xA0
|
||||
#define GPIO_INTEN 0xC0
|
||||
#define GPIO_INTTYPE 0xE0
|
||||
|
||||
#define GPIO_NUMINTGROUPS 7
|
||||
|
||||
typedef struct IPodTouchSYSICState {
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq gpio_irqs[GPIO_NUMINTGROUPS];
|
||||
uint32_t power_id;
|
||||
uint32_t power_state;
|
||||
|
||||
// GPIO
|
||||
uint32_t gpio_int_level[GPIO_NUMINTGROUPS];
|
||||
uint32_t gpio_int_status[GPIO_NUMINTGROUPS];
|
||||
uint32_t gpio_int_enabled[GPIO_NUMINTGROUPS];
|
||||
uint32_t gpio_int_type[GPIO_NUMINTGROUPS];
|
||||
} IPodTouchSYSICState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef IPOD_TOUCH_TIMER_H
|
||||
#define IPOD_TOUCH_TIMER_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_TIMER "ipodtouch.timer"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchTimerState, IPOD_TOUCH_TIMER)
|
||||
|
||||
#define TIMER_IRQSTAT 0x10000
|
||||
#define TIMER_IRQLATCH 0x118
|
||||
#define TIMER_TICKSHIGH 0x80
|
||||
#define TIMER_TICKSLOW 0x84
|
||||
#define TIMER_STATE_START 1
|
||||
#define TIMER_STATE_STOP 0
|
||||
#define TIMER_STATE_MANUALUPDATE 2
|
||||
#define NUM_TIMERS 7
|
||||
#define TIMER_4 0xA0
|
||||
#define TIMER_CONFIG 0
|
||||
#define TIMER_STATE 0x4
|
||||
#define TIMER_COUNT_BUFFER 0x8
|
||||
#define TIMER_COUNT_BUFFER2 0xC
|
||||
|
||||
typedef struct IPodTouchTimerState
|
||||
{
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t ticks_high;
|
||||
uint32_t ticks_low;
|
||||
uint32_t status;
|
||||
uint32_t config;
|
||||
uint32_t bcount1;
|
||||
uint32_t bcount2;
|
||||
uint32_t prescaler;
|
||||
uint32_t irqstat;
|
||||
QEMUTimer *st_timer;
|
||||
Clock *sysclk;
|
||||
uint32_t bcreload;
|
||||
uint32_t freq_out;
|
||||
uint64_t tick_interval;
|
||||
uint64_t last_tick;
|
||||
uint64_t next_planned_tick;
|
||||
uint64_t base_time;
|
||||
qemu_irq irq;
|
||||
|
||||
} IPodTouchTimerState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef IPOD_TOUCH_TVOUT_H
|
||||
#define IPOD_TOUCH_TVOUT_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define SDO_CLKCON 0x0
|
||||
#define SDO_CONFIG 0x8
|
||||
#define SDO_IRQ 0x280
|
||||
#define SDO_IRQMASK 0x284
|
||||
|
||||
#define MXR_STATUS 0x0
|
||||
#define MXR_CFG 0x4
|
||||
|
||||
#define TYPE_IPOD_TOUCH_TVOUT "ipodtouch.tvout"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchTVOutState, IPOD_TOUCH_TVOUT)
|
||||
|
||||
typedef struct IPodTouchTVOutState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion mixer1_iomem;
|
||||
MemoryRegion mixer2_iomem;
|
||||
MemoryRegion sdo_iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t mixer1_status;
|
||||
uint32_t mixer1_cfg;
|
||||
|
||||
uint32_t mixer2_status;
|
||||
uint32_t mixer2_cfg;
|
||||
|
||||
uint32_t sdo_clkcon;
|
||||
uint32_t sdo_config;
|
||||
uint32_t sdo_irq;
|
||||
uint32_t sdo_irq_mask;
|
||||
|
||||
uint32_t irq_count;
|
||||
} IPodTouchTVOutState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_UNKNOWN1_H
|
||||
#define HW_ARM_IPOD_TOUCH_UNKNOWN1_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_UNKNOWN1 "ipodtouch.unknown1"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchUnknown1State, IPOD_TOUCH_UNKNOWN1)
|
||||
|
||||
typedef struct IPodTouchUnknown1State {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
} IPodTouchUnknown1State;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_USB_PHYS_H
|
||||
#define HW_ARM_IPOD_TOUCH_USB_PHYS_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_USB_PHYS "ipodtouch.usbphys"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchUSBPhysState, IPOD_TOUCH_USB_PHYS)
|
||||
|
||||
#define REG_OPHYPWR 0x0
|
||||
#define REG_OPHYCLK 0x4
|
||||
#define REG_ORSTCON 0x8
|
||||
#define REG_UNKNOWN1 0x1C
|
||||
#define REG_OPHYTUNE 0x20
|
||||
#define REG_CABLE 0x28
|
||||
|
||||
typedef struct IPodTouchUSBPhysState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
|
||||
uint32_t usb_ophypwr;
|
||||
uint32_t usb_ophyclk;
|
||||
uint32_t usb_orstcon;
|
||||
uint32_t usb_unknown1;
|
||||
uint32_t usb_ophytune;
|
||||
bool is_cable_connected;
|
||||
} IPodTouchUSBPhysState;
|
||||
|
||||
#endif
|
||||
@@ -49,7 +49,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(PL080State, PL080)
|
||||
struct PL080State {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
MemoryRegion iomem1;
|
||||
MemoryRegion iomem2;
|
||||
uint8_t tc_int;
|
||||
uint8_t tc_mask;
|
||||
uint8_t err_int;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef IPOD_TOUCH_I2C_H
|
||||
#define IPOD_TOUCH_I2C_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_I2C "ipodtouch.i2c"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchI2CState, IPOD_TOUCH_I2C)
|
||||
|
||||
#define I2CCON 0x00 /* I2C Control register */
|
||||
#define I2CSTAT 0x04 /* I2C Status register */
|
||||
#define I2CADD 0x08 /* I2C Slave Address register */
|
||||
#define I2CDS 0x0c /* I2C Data Shift register */
|
||||
#define I2CLC 0x10 /* I2C Line Control register */
|
||||
|
||||
#define IICREG20 0x20
|
||||
|
||||
#define SR_MODE 0x0 /* Slave Receive Mode */
|
||||
#define ST_MODE 0x1 /* Slave Transmit Mode */
|
||||
#define MR_MODE 0x2 /* Master Receive Mode */
|
||||
#define MT_MODE 0x3 /* Master Transmit Mode */
|
||||
|
||||
|
||||
#define S5L8900_IICCON_ACKEN (1<<7)
|
||||
#define S5L8900_IICCON_TXDIV_16 (0<<6)
|
||||
#define S5L8900_IICCON_TXDIV_512 (1<<6)
|
||||
#define S5L8900_IICCON_IRQEN (1<<5)
|
||||
#define S5L8900_IICCON_IRQPEND (1<<4)
|
||||
|
||||
#define S5L8900_IICSTAT_START (1<<5)
|
||||
#define S5L8900_IICSTAT_BUSBUSY (1<<5)
|
||||
#define S5L8900_IICSTAT_TXRXEN (1<<4)
|
||||
#define S5L8900_IICSTAT_ARBITR (1<<3)
|
||||
#define S5L8900_IICSTAT_ASSLAVE (1<<2)
|
||||
#define S5L8900_IICSTAT_ADDR0 (1<<1)
|
||||
#define S5L8900_IICSTAT_LASTBIT (1<<0)
|
||||
|
||||
#define S5L8900_I2C_REG_MEM_SIZE 0x1000
|
||||
|
||||
typedef struct IPodTouchI2CState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
I2CBus *bus;
|
||||
qemu_irq irq;
|
||||
uint8_t base;
|
||||
|
||||
uint8_t control;
|
||||
uint8_t status;
|
||||
uint8_t address;
|
||||
uint8_t datashift;
|
||||
uint8_t line_ctrl;
|
||||
uint32_t iicreg20;
|
||||
|
||||
uint8_t active;
|
||||
|
||||
uint8_t ibmr;
|
||||
uint8_t data;
|
||||
} IPodTouchI2CState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef PL192_H
|
||||
#define PL192_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#define TYPE_PL192 "pl192"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PL192State, PL192)
|
||||
|
||||
|
||||
#define PL192_INT_SOURCES 32
|
||||
#define PL192_DAISY_IRQ PL192_INT_SOURCES
|
||||
#define PL192_NO_IRQ PL192_INT_SOURCES+1
|
||||
#define PL192_PRIO_LEVELS 16
|
||||
|
||||
#define PL192_IRQSTATUS 0x00
|
||||
#define PL192_FIQSTATUS 0x04
|
||||
#define PL192_RAWINTR 0x08
|
||||
#define PL192_INTSELECT 0x0C
|
||||
#define PL192_INTENABLE 0x10
|
||||
#define PL192_INTENCLEAR 0x14
|
||||
#define PL192_SOFTINT 0x18
|
||||
#define PL192_SOFTINTCLEAR 0x1C
|
||||
#define PL192_PROTECTION 0x20
|
||||
#define PL192_SWPRIORITYMASK 0x24
|
||||
#define PL192_PRIORITYDAISY 0x28
|
||||
#define PL192_VECTADDR 0xF00
|
||||
#define PL192_IOMEM_SIZE 0x1000
|
||||
|
||||
#define PL190_ITCR 0x300
|
||||
#define PL190_VECTADDR 0x30
|
||||
#define PL190_DEFVECTADDR 0x34
|
||||
|
||||
#define PL192_IOMEM_SIZE 0x1000
|
||||
|
||||
|
||||
struct PL192State {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
|
||||
/* Control registers */
|
||||
uint32_t irq_status;
|
||||
uint32_t fiq_status;
|
||||
uint32_t rawintr;
|
||||
uint32_t intselect;
|
||||
uint32_t intenable;
|
||||
uint32_t softint;
|
||||
uint32_t protection;
|
||||
uint32_t sw_priority_mask;
|
||||
uint32_t vect_addr[PL192_INT_SOURCES];
|
||||
uint32_t vect_priority[PL192_INT_SOURCES];
|
||||
uint32_t address;
|
||||
|
||||
/* Currently processed interrupt and
|
||||
highest priority interrupt */
|
||||
uint32_t current;
|
||||
uint32_t current_highest;
|
||||
|
||||
/* Priority masking logic */
|
||||
int32_t stack_i;
|
||||
uint32_t priority_stack[PL192_PRIO_LEVELS+1];
|
||||
uint8_t irq_stack[PL192_PRIO_LEVELS+1];
|
||||
uint32_t priority;
|
||||
|
||||
/* Daisy-chain interface */
|
||||
uint32_t daisy_vectaddr;
|
||||
uint32_t daisy_priority;
|
||||
PL192State *daisy_callback;
|
||||
uint8_t daisy_input;
|
||||
|
||||
/* Parent interrupts */
|
||||
qemu_irq irq;
|
||||
qemu_irq fiq;
|
||||
|
||||
/* Next controller in chain */
|
||||
PL192State *daisy;
|
||||
};
|
||||
|
||||
DeviceState *pl192_manual_init(char *mem_name, ...);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef HW_USB_HCD_TCP_H
|
||||
#define HW_USB_HCD_TCP_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/usb.h"
|
||||
#include "io/channel.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#define TYPE_USB_TCP_HOST "usb-tcp-host"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(USBTCPHostState, USB_TCP_HOST)
|
||||
|
||||
typedef struct USBTCPPacket {
|
||||
USBPacket p;
|
||||
void *buffer;
|
||||
USBDevice *dev;
|
||||
USBTCPHostState *s;
|
||||
uint8_t addr;
|
||||
} USBTCPPacket;
|
||||
|
||||
struct USBTCPHostState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
USBBus bus;
|
||||
USBPort uports[3];
|
||||
QIOChannel *ioc;
|
||||
CoMutex write_mutex;
|
||||
Error *migration_blocker;
|
||||
bool closed;
|
||||
bool stopped;
|
||||
};
|
||||
|
||||
#endif /* HW_USB_HCD_TCP_H */
|
||||
@@ -254,7 +254,7 @@ extern "C" {
|
||||
* supports QEMU_ERROR, this will be reported at compile time; otherwise
|
||||
* this will be reported at link time due to the missing symbol.
|
||||
*/
|
||||
G_NORETURN
|
||||
G_NORETURN extern
|
||||
void QEMU_ERROR("code path is reachable")
|
||||
qemu_build_not_reached_always(void);
|
||||
#if defined(__OPTIMIZE__) && !defined(__NO_INLINE__)
|
||||
|
||||
Reference in new issue
Block a user