Symbian3/PDK/Source/GUID-42F8FA5A-BBE4-50DE-917E-D05755019FEC.dita
author Graeme Price <GRAEME.PRICE@NOKIA.COM>
Fri, 15 Oct 2010 14:32:18 +0100
changeset 15 307f4279f433
parent 14 578be2adaf3e
permissions -rw-r--r--
Initial contribution of the Adaptation Documentation.

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. -->
<!-- This component and the accompanying materials are made available under the terms of the License 
"Eclipse Public License v1.0" which accompanies this distribution, 
and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". -->
<!-- Initial Contributors:
    Nokia Corporation - initial contribution.
Contributors: 
-->
<!DOCTYPE concept
  PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
<concept id="GUID-42F8FA5A-BBE4-50DE-917E-D05755019FEC" xml:lang="en"><title>Personality
Layer Design</title><shortdesc>Provides some guidelines for the design of a personality layer
for real time Applications for the Kernel.</shortdesc><prolog><metadata><keywords/></metadata></prolog><conbody>
<section id="GUID-A0EC87BF-170D-46B4-AEFF-F21D7483149B"><title>Memory management</title> <p>The personality layer assumes
that the RTA will run in a single flat address space in which there is neither
protection between different parts of the application nor protection of any
hardware or CPU resource from any part of the application. For example, any
part of the application code can access any I/O port, and can disable interrupts. </p> <p>To
get this behaviour under the Kernel Architecture 2, the RTA must run in supervisor
mode in the kernel address space. The obvious way to do this is to make the
RTA together with the personality layer a kernel extension. This also ensures
that it is started automatically early on in the boot process. </p> <p>In
general the RTA will have its own memory management strategy and will not
wish to use the standard Symbian platform memory management system. To achieve
this, the personality layer will allocate a certain fixed amount of RAM for
use by the real time application at boot time. For a telephony stack this
will be around 128K - 256K. This can be done either by including it in the
kernel extension's <filepath>.bss</filepath> section, or by making a one-time
allocation on the kernel heap at boot time. Depending on the RTA requirements,
the personality layer can manage this area of RAM (if the RTOS being emulated
provides memory management primitives) or the RTA can manage it. </p> </section>
<section id="GUID-FAA30849-282F-4E2D-ABDA-63E130A2D2FB"><title>Threads and mapping thread priorities</title> <p>A nanokernel
thread will be used for each RTOS thread </p> <p>A priority mapping scheme
will be required to map RTOS priorities, of which there are typically 64 to
256 distinct values, to nanokernel priorities. As long as the RTA does not
use more than 35 threads running simultaneously, which is usually the case,
it should be possible to produce a mapping scheme that allows each thread
to have a distinct priority, if needed. If this limit is exceeded, it will
be necessary to fold some priorities together. </p> <p>Note that any attempt
to increase the number of priorities supported by both the nanokernel and
the Symbian platform kernel would be <i>prohibitively</i> expensive in terms
of RAM usage. </p> </section>
<section id="GUID-169604BD-A22A-5B56-8279-13F875AB4AB6"><title>Communication
between Symbian platform and the RTOS Environments</title> <p>To allow the
functionality of the RTA to be available to Symbian platform applications,
it is necessary that a mechanism exist by which Symbian platform code and
the RTA may communicate with each other. In practice this means: </p> <ul>
<li id="GUID-DC8AF97D-EA8A-5852-96FF-CCA19D69F614"><p>it must be possible
for a Symbian platform thread to cause an RTOS thread to be scheduled and
vice-versa </p> </li>
<li id="GUID-EF671D7A-28B6-505D-823C-5B51C378225E"><p>it must be possible
for data to be transferred between Symbian platform and RTOS threads in both
directions. </p> </li>
</ul> <p>It will usually be possible for a Symbian platform thread to make
standard personality layer calls (the same calls that RTOS threads would make)
in order to cause an RTOS thread to be scheduled. This is because the nanokernel
underlies both types of thread and most 'signal' type operations (i.e. those
that make threads ready rather than blocking them) can be implemented using
operations which make no reference to the calling thread, and which are therefore
not sensitive to which type of thread they are called from. </p> <p>The standard
personality layer calls will not work in the other direction, since it will
not be possible for a Symbian platform thread to wait on a personality layer
wait object. The most straightforward way for RTOS threads to trigger scheduling
of a Symbian platform thread would be to enque a DFC on a queue operated by
a Symbian platform thread. Another possibility would be for the Symbian platform
thread to wait on a fast semaphore which could then be signalled by the RTOS
thread. However the DFC method fits better with the way device drivers are
generally written. A device driver will be necessary to mediate communication
between Symbian platform user mode processes and the RTA since the latter
runs kernel side. </p> <p>All data transfer between the two environments must
occur kernel side. It will not be possible for any RTOS thread to access normal
user side memory since the functions provided for doing so access parts of
the <xref href="GUID-38D1534C-AA01-33AF-9937-CDD818A85F97.dita"><apiname>DThread</apiname></xref> structure representing the Symbian platform
calling thread, for example to perform exception trapping. Some possibilities
for the data transfer mechanism are: </p> <ul>
<li id="GUID-96A247AA-BA1C-50E5-8400-5487DFF5991D"><p>A fairly common architecture
for real time applications involves a fixed block size memory manager and
message queues for inter-thread communication. The memory manager supports
allocation and freeing of memory in constant time. The sending thread allocates
a memory block, places data in it and posts it to the receiving thread's message
queue. The receiving thread then processes the data and frees the memory block,
or possibly passes the block to yet another thread. It would be a simple proposition
to produce such a system in which the memory manager could be used by any
thread. In that case a Symbian platform thread could pass messages to RTOS
threads in the same way as other RTOS threads. Passing data back would involve
a special type of message queue implemented in the personality layer. When
a message was sent to a Symbian platform thread a DFC would be enqueued. That
DFC would then process the message data and free the memory block as usual.
This scheme combines the data transfer and scheduling aspects of communication. </p> </li>
<li id="GUID-1D2B6085-7B3A-51EF-BFF6-865D4AF94E5A"><p>Any standard buffering
arrangement could be used between the RTA and the device driver (e.g. circular
buffers). Contention between threads could be prevented using nanokernel fast
mutexes, on which any thread can wait, or by the simpler means of disabling
preemption or disabling interrupts. It will also be possible for RTOS threads
to make use of shared I/O buffers for transfer direct to user mode clients,
provided that these buffers are set up by the Symbian platform device driver
thread. This may be useful as a way to reduce copying overhead if bulk data
transfer is necessary between the two domains. </p> </li>
</ul> </section>
<section id="GUID-09CC8168-50FD-49ED-8376-05C00C3243FA"><title>Synchronisation/communication primitives</title> <p>The nanokernel
does not support most of the synchronisation and communication primitives
provided by a standard RTOS. Any such primitives required by the RTA will
have to be implemented in the personality layer. This means that the personality
layer needs to define new types of object on which threads can wait. This
in turn means that new nanokernel thread states (N-state) must be defined
to signify that a thread is waiting on an object of the new type. In general,
each new type of wait object requires an accompanying new nanokernel thread
state. </p> <p><b>Blocking
a thread on a wait object</b> </p> <p>To make a thread block on a new type
of wait object, call the nanokernel function <xref href="GUID-C6946ECB-775F-3EC2-A56F-78F25B9FBE3D.dita#GUID-C6946ECB-775F-3EC2-A56F-78F25B9FBE3D/GUID-72F2E4E0-B498-32D2-BB24-E79AC66EFDDB"><apiname>Kern::NanoBlock()</apiname></xref>,
passing the maximum time for which the thread should block, the new N-state
value, and a pointer to the wait object. </p> <p>Use the <codeph>TPriListLink</codeph> base
class of <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita"><apiname>NThreadBase</apiname></xref> to attach the thread to a list of
threads waiting on the object. Note that this must be done after calling <xref href="GUID-C6946ECB-775F-3EC2-A56F-78F25B9FBE3D.dita#GUID-C6946ECB-775F-3EC2-A56F-78F25B9FBE3D/GUID-72F2E4E0-B498-32D2-BB24-E79AC66EFDDB"><apiname>Kern::NanoBlock()</apiname></xref>.
As preemption is disabled before this function is called, a reschedule will
not occur immediately, but will be deferred until preemption is reenabled. </p> <p id="GUID-5017FB0A-5C1E-5C88-9315-E12C12D9639F"><b>State handler</b> </p> <p>Every
thread that can use a new type of wait object, must have a nanokernel state
handler installed to handle operations on that thread when it is waiting on
that wait object. A nanokernel state handler is a function that has the following
signature: </p> <codeblock id="GUID-31E807F7-250B-552D-8480-5F282C151565" xml:space="preserve">void StateHandler(NThread* aThread, TInt aOp, TInt aParam);</codeblock> <ul>
<li id="GUID-B8ECADB3-0345-56CB-8C7A-6D4E8CE15726"><p> <codeph> aThread</codeph> is
a pointer to the thread involved. </p> </li>
<li id="GUID-711957A1-4D01-5F8B-BF67-70EB8F39D20B"><p> <codeph> aOp</codeph> is
one of the <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-FF52640A-87C9-319B-B4C6-F6B860171229"><apiname>NThreadBase::NThreadOperation</apiname></xref> values that indicates
which operation is being performed on the thread. </p> </li>
<li id="GUID-FF54386A-D163-5F53-B230-296BE402C9E6"><p> <codeph> aParam</codeph> is
a parameter that depends on <codeph>aOp</codeph>. </p> </li>
</ul> <p>Note that the state handler is always called with preemption disabled. </p> <p>The
possible values of <codeph>aOp</codeph> are: </p> <table id="GUID-F4372C47-F491-55E2-B06E-2C68628B5BC5">
<tgroup cols="2"><colspec colname="col0"/><colspec colname="col1"/>
<tbody>
<row>
<entry><p> <codeph>aOp</codeph> value </p> </entry>
<entry><p>Description </p> </entry>
</row>
<row>
<entry><p> <codeph>ESuspend</codeph>  </p> </entry>
<entry><p>Called if the thread is suspended while not in a critical section
and not holding a fast mutex. Called in whichever context <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-FF94D458-C2D0-3D20-ADD6-AAE68A3296C3"><apiname>NThreadBase::Suspend()</apiname></xref> is
called from. </p> <p> <codeph>aParam</codeph> contains the requested suspension
count. </p> </entry>
</row>
<row>
<entry><p> <codeph>EResume</codeph>  </p> </entry>
<entry><p>Called if the thread is resumed while actually suspended, and the
last suspension has been removed. Called in whichever context <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-C0A6E734-7DE6-37B9-AAB2-A2A0E2664731"><apiname>NThreadBase::Resume()</apiname></xref> is
called from. </p> <p> <codeph>aParam</codeph> contains no additional information. </p> </entry>
</row>
<row>
<entry><p> <codeph>EForceResume</codeph>  </p> </entry>
<entry><p>Called if the thread has all suspensions cancelled while actually
suspended. Called in whichever context <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-BE92FBC3-A7D9-3576-A1A9-7BBA6EE64226"><apiname>NThreadBase::ForceResume()</apiname></xref> is
called from. </p> <p> <codeph>aParam</codeph> contains no additional information. </p> </entry>
</row>
<row>
<entry><p> <codeph>ERelease</codeph>  </p> </entry>
<entry><p>Called if the thread is released from its wait. This call should
make the thread ready if necessary. Called in whichever context <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-1B24AC4E-C4A1-32AE-BC6E-DC3131116EF8"><apiname>NThreadBase::Release()</apiname></xref> was
called from. </p> <p> <codeph>aParam</codeph> is the value passed into <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-1B24AC4E-C4A1-32AE-BC6E-DC3131116EF8"><apiname>NThreadBase::Release()</apiname></xref> to
be used as a return code. </p> <p>If <codeph>aParam</codeph> is non-negative,
this indicates normal termination of the wait condition. </p> <p>If <codeph>aParam</codeph> is
negative, it indicates early or abnormal termination of the wait; in this
case the wait object should be rolled back as if the wait had never occurred.
For example, a semaphore's count needs to be incremented if <codeph>aParam</codeph> is
negative, since in that case the waiting thread never acquired the semaphore. </p> </entry>
</row>
<row>
<entry><p> <codeph>EChangePriority</codeph>  </p> </entry>
<entry><p>Called if the thread's priority is changed. Called in whichever
context <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-A92E7B01-C1D0-3997-B3E1-2E54229FFA75"><apiname>NThreadBase::SetPriority()</apiname></xref> is called from. This
function should set the <codeph>iPriority</codeph> field of the thread, after
doing any necessary priority queue manipulations. </p> <p> <codeph>aParam</codeph> contains
the new priority. </p> </entry>
</row>
<row>
<entry><p> <codeph>ELeaveCS</codeph>  </p> </entry>
<entry><p>Called in the context of the thread concerned if the thread calls <xref href="GUID-3A3C08F3-3D33-3D9E-80E7-7855C7B21E02.dita#GUID-3A3C08F3-3D33-3D9E-80E7-7855C7B21E02/GUID-2C897BA5-2BD7-3ABA-9F2B-F0B1AC14D1AE"><apiname>NKern::ThreadLeaveCS()</apiname></xref> with
an unknown <codeph>iCsFunction</codeph> that is negative but not equal to <codeph>ECsExitPending</codeph>. </p> <p>aParam
contains the value of <codeph>iCsFunction</codeph>. </p> </entry>
</row>
<row>
<entry><p> <codeph>ETimeout</codeph>  </p> </entry>
<entry><p>Called if the thread's wait time-out expires and no time-out handler
is defined for that thread. Called in the context of the nanokernel timer
thread (DfcThread1). This should cancel the wait and arrange for an appropriate
error code to be returned. The handler for this condition will usually do
the same thing as the handler for <codeph>ERelease</codeph> with a parameter
of <xref href="GUID-BAC2386E-8168-3CDB-9F9F-180319EF6920.dita"><apiname>KErrTimedOut</apiname></xref>. </p> <p> <codeph>aParam</codeph> contains
no additional information. </p> </entry>
</row>
</tbody>
</tgroup>
</table> <p>See the code in <filepath>...\e32\personality\example\...</filepath> for
practical examples. </p> <p><b>Releasing
the thread</b> </p> <p>When a thread's wait condition is resolved, the nanokernel
function <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-1B24AC4E-C4A1-32AE-BC6E-DC3131116EF8"><apiname>NThreadBase::Release()</apiname></xref> should be called. This
takes a single <xref href="GUID-7A2A43EC-6125-3BFE-834B-23C37F7B40D5.dita"><apiname>TInt</apiname></xref> parameter. </p> <p>The parameter is
usually <xref href="GUID-6CA4F1ED-7947-3087-B618-D35858FAA3BC.dita"><apiname>KErrNone</apiname></xref>, if the wait condition is resolved normally.
A typical example is where the semaphore on which the thread is waiting, is
signalled. A negative parameter value is used for an abnormal termination,
and in this case, the wait object may need to be rolled back. </p> <p> <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-1B24AC4E-C4A1-32AE-BC6E-DC3131116EF8"><apiname>NThreadBase::Release()</apiname></xref> should
be called with preemption disabled. It performs the following actions: </p> <ul>
<li id="GUID-6697024F-ED05-5C5A-A834-EA89402AE173"><p>sets the <codeph>NThreadBase::iWaitObj</codeph> field
to NULL </p> </li>
<li id="GUID-C1DF3120-4FD3-5B6C-90F8-6D8C7CAD0B88"><p>cancels the wait timer
if it is still running </p> </li>
<li id="GUID-6E346E2B-4F94-56FB-8372-8260BCC4CA8C"><p>stores the supplied
return code in <codeph>NThreadBase::iReturnCode</codeph>  </p> </li>
<li id="GUID-3AF577DB-41A9-553E-9EFD-6F0DA2FADCEA"><p>calls the <xref href="GUID-42F8FA5A-BBE4-50DE-917E-D05755019FEC.dita#GUID-42F8FA5A-BBE4-50DE-917E-D05755019FEC/GUID-5017FB0A-5C1E-5C88-9315-E12C12D9639F">state handler</xref> passing <codeph>ERelease</codeph> and the return code.
If the return code is negative this should remove the thread from any wait
queues and roll back the state of the wait object. In any case it should call <xref href="GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC.dita#GUID-379D9320-AC3C-3206-8A5D-EE6E5983EBDC/GUID-82E43D79-721D-31A9-B9ED-1277F2300914"><apiname>NThreadBase::CheckSuspendThenReady()</apiname></xref> to
make the thread ready again if necessary. </p> </li>
</ul> </section>
<section id="GUID-1FF1CFE8-41E3-4912-AE28-39D289442BB7"><title>Thread scheduling following a hardware interrupt</title> <p>Most
RTOS allow interrupt service routines (ISRs) to perform operations such as
semaphore signal, queue post, set event flag directly, usually using the same
API as would be used in a thread context. The Kernel Architecture 2 nanokernel
does not allow this; ISRs can only queue an IDFC or DFC. </p> <p>The way to
get round this limitation is to incorporate an IDFC into each personality
layer wait object. The personality layer API involved then needs to check
whether it is being invoked from an ISR or a thread and, in the first case,
it queues the IDFC. It may need to save some other information for use by
the IDFC, for example, it may need to maintain a list of messages queued from
ISRs, a count of semaphore signals from ISRs or a bit mask of event flags
set by ISRs. Checking for invocation from an ISR can be done either by using <xref href="GUID-3A3C08F3-3D33-3D9E-80E7-7855C7B21E02.dita#GUID-3A3C08F3-3D33-3D9E-80E7-7855C7B21E02/GUID-9542588A-2920-3CB0-A2C0-A55FA6BC29A2"><apiname>NKern::CurrentContext()</apiname></xref>,
or by checking the CPSR directly; if doing the latter note that any mode other
than USR or SVC on the ARM counts as interrupt context. </p> <p>Hardware interrupts
serviced by the RTA will need to conform to the same pattern as those serviced
by Symbian platform extensions or device drivers. This means that the standard
preamble must run before the actual service routine and the nanokernel interrupt
postamble must run after the service routine to enable reschedules to occur
if necessary. This can be done by calling <codeph>Interrupt::Bind()</codeph> as
provided by the base port during RTA initialisation (possibly via a personality
layer call if it must be called from C code). See also <xref href="GUID-3C34724F-B476-5329-B0B1-6D5A34294979.dita">Interrupt
Dispatcher Tutorial</xref>. </p> </section>
</conbody><related-links>
<link href="GUID-5B1D8D9C-90EF-5FCC-8D7D-01B13D6C2E68.dita"><linktext>Nanokernel</linktext>
</link>
<link href="GUID-2700AAC8-A034-5E7D-B0E0-26B49E68BB18.dita"><linktext>Personality
Layer for Real                 Time Applications</linktext></link>
</related-links></concept>