0% found this document useful (0 votes)
391 views

How To Create Symbian Peripherals With Qemu

The document describes how to add peripheral models to the Symbian simulator. It provides instructions on writing a peripheral model in C code, placing it in the simulator source tree, and updating files to include the new model. It also covers creating a Symbian device driver to interface with the peripheral model by accessing simulated memory registers. An example SVPSnap peripheral and driver are described in detail to demonstrate the process.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
391 views

How To Create Symbian Peripherals With Qemu

The document describes how to add peripheral models to the Symbian simulator. It provides instructions on writing a peripheral model in C code, placing it in the simulator source tree, and updating files to include the new model. It also covers creating a Symbian device driver to interface with the peripheral model by accessing simulated memory registers. An example SVPSnap peripheral and driver are described in detail to demonstrate the process.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 4

Disclaimer; this page originates from the original SVP Wiki.

Some of the instructions might be out of date, and attached to this page files can be missing. This information is provided for information only and can highlight some design ideas that can be benefitial for the Symbian Foundation/QEMU community.

1.

Introduction

This page describes how to add peripherals to the prototype simulator. The basic idea of the simulator is to provide an execution environment for Symbian ROMs that is fast and pretty similar to a real development board. This means that you access peripherals in the same manner as you would on a 'real' Symbian system. The Symbian software stack is kept intact from application to device driver. The simulator contains a set of peripheral model (i.e. uart, display etc) that have the same registers and behaviour as a peripheral on a development board. Since this is all C code, it's simple to extend the board model with new peripherals.

2.

Adding a peripheral

First of all you need to get the development environment for the simulator. See the main Wiki page on how to do that. You will need to recompile the simulator each time you make a change to your peripheral model. You also need to familiarize yourself with the Symbian device driver model.

3.

Creating a Simulator peripheral model


Write the model and place it in qemu/devel/hw/ Update the file qemu/devel/hw/versatile.c Update the QEmu makefile to include your new model

The interaction between the device driver and the simulator peripheral model is done by simulated memory accesses. Just as a device driver would access a peripheral on a real system (by reading and

writing to registers somewhere in the memory map) the simulator provides the exact same mechanism. At simulator start-up you register each peripheral to QEmu with a given address (note, this is physical addresses) and a couple of function pointer for read and write accesses.
/* Add SVPSnap Device */ svpsnap_init(0x101f5000, NULL); ... void svpsnap_init(uint32_t base, qemu_irq irq) { iomemtype = cpu_register_io_memory(0, svpsnap_readfn, svpsnap_writefn, s); cpu_register_physical_memory(base, 0x00001000, iomemtype); }

The simulator can now translate memory access from the CPU to the given addresses to function calls to the supplied function pointers.

4.

Creating the Symbian device driver


Change the syborg/bld.inf file to get the device driver compiled into the ROM Change the syborg.h file to give your peripheral a base address Create a oby file the rombuild directory that will put your device driver in a ROM

The application passes on an operation code and a couple of memory pointers to the device driver. The device driver translates the code to an operation, access the peripheral and reads/writes data in the memory dictated by the pointers. Remember that in order to safely access this memory you should use the Kern::ThreadRawRead/Write() and Kern::ThreadDesRead() functions. For simulator peripherals you should always use DoRequest() rather than DoControl() to make sure all accesses to the device driver are "atomic". This will prevent problems when a device driver has many users that may access it as the same time. You should also wait for the request to complete before returning to the application.
inline TInt DoSVPRequest(TInt aReqNo, TAny * a1) { TRequestStatus status; DoRequest(aReqNo, status, a1); User::WaitForRequest(status); return status.Int(); }

To extend the amount of information passed to and read back from the device driver you should create a container class which pointer you pass to DoRequest().
TInt DSVPHostFsChannel::FileOpen(TSVPHostFsFileOpenInfo* aInfo) { TSVPHostFsFileOpenInfo info; TInt err = KErrNone; RET_IF_ERROR(err, Kern::ThreadRawRead(iClientThread, aInfo, (TUint8*)&info, sizeof(TSVPHostFsFileOpenInfo)));

This example shows both the use of a container class TSVPHostFsFileOpenInfo and how to access data from the user space thread.

5.

Example, SVPSnapDriver

The SVPSnapDriver is a device driver which makes it possible to trigger the storing and loading of snapshots. It provides 2 functions LoadVM and SaveVM, both takes the name of the snapshot as

parameter. Please note that you will probably end up in the kernel's crash debugger when you do LoadVM() if you are not careful.

6.

Peripheral Model

The file is called qemu/devel/qemu/gw/svpsnap.c svpsnap_init() is called from hw/versatilepb.c at board initialization time. This functions registers read/write access function pointers tied to a spot in the memory map with QEmu.
void svpsnap_init(uint32_t base, qemu_irq irq) { int iomemtype; svpsnap_state *s; s = (svpsnap_state *)qemu_mallocz(sizeof(svpsnap_state)); iomemtype = cpu_register_io_memory(0, svpsnap_readfn, svpsnap_writefn, s); cpu_register_physical_memory(base, 0x00001000, iomemtype); s->base = base;

The peripheral contains a set of registers decoded from the memory address
uint32_t reg = (offset - s->base) >> 2;

These registers are used by the device driver for communication with the device. Both for setting parameters, reading results and invoking a operation. In the svpsnap_write() function register 9 acts as the invoke register. When written to is executes an peripheral operation.
switch (reg) { ... case 9: /* (last) command */ { s->op = value; if ((sizeof(svpsnap_ops) / sizeof(svpsnap_op_t)) <= (uint32_t)value) s->result = SVP_SNAP_UNSUPPORTED; else s->result = svpsnap_ops[value](s); break; }

The LoadVM() and SaveVM() functions expects a string pointer to be written into register 0 (arg[0]) before invocation. This gives a very simple SaveVM() function;
static int svpsnap_savevm(svpsnap_state * s) { char name[256]; cpu_physical_memory_read(s->arg[0], name, 256); do_savevm(name); return s->result = SVP_SNAP_SUCCESS; }

7.
8.

Symbian Device Driver


Kernel-side driver

The kernel side driver is implemented in the file svpsnapdriver.cpp. The snapdriver is written as a Logical Device Driver (LDD). It consits of a DriverFactory class and the main DLogicalChannel. When a request comes, HandleMsg() calls DoRequest() which decodes the operation code.

switch(aReqNo) { case RSVPSnapDriver::ESaveVM: // DP("RSVPSnapDriver::ESaveVM"); err = SaveVM((char*)a1); break; case RSVPSnapDriver::ELoadVM: // DP("RSVPSnapDriver::ELoadVM"); err = LoadVM((char*)a1); break;

The function SaveVM() copies the parameter string from the calling threads context and uses the function WriteReg() to pass parameters to the peripheral (in this case a memory pointer) and Invoke() to 'prod' the peripheral to run the operation. Finally it uses ReadReg() to read back the result of the operation.
TInt DSVPSnapChannel::SaveVM(char* aName) { ... RET_IF_ERROR(err, Kern::ThreadRawRead(iClientThread, aName, (TUint8*)buf, 256)); WriteReg(EArg0, Epoc::LinearToPhysical((TUint32)buf)); Invoke(RSVPSnapDriver::ESaveVM); } return ReadReg(EResult);

The WriteReg()/ReadReg() operations does memory reads and writes directly to the peripheral.
static inline TUint32 ReadReg(ESVPSnapReg aReg) { return *(volatile TUint32 *)(KHwSVPSnapDevice + (aReg << 2)); } static inline void WriteReg(ESVPSnapReg aReg, TUint32 aVal) { *(volatile TUint32*)(KHwSVPSnapDevice + (aReg << 2)) = aVal; }

9.

User-side driver

The user-side device driver simply calls DoRequest(). It will block for the request to complete before returning. The reason for using DoRequest is to make Device Driver calls atomic (we don't want to be interrupted by another call in the middle of WriteReg()'s as could be the case if we used DoControl().
inline TInt DoSVPRequest(TInt aReqNo, TAny * a1) { TRequestStatus status; DoRequest(aReqNo, status, a1); User::WaitForRequest(status); return status.Int(); }

Labels parameters

You might also like