📄 synth-new-host.html
字号:
<!-- Copyright (C) 2003 Red Hat, Inc. -->
<!-- This material may be distributed only subject to the terms -->
<!-- and conditions set forth in the Open Publication License, v1.0 -->
<!-- or later (the latest version is presently available at -->
<!-- http://www.opencontent.org/openpub/). -->
<!-- Distribution of the work or derivative of the work in any -->
<!-- standard (paper) book form is prohibited unless prior -->
<!-- permission is obtained from the copyright holder. -->
<HTML
><HEAD
><TITLE
>Writing New Devices - host</TITLE
><meta name="MSSmartTagsPreventParsing" content="TRUE">
<META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="eCos Reference Manual"
HREF="ecos-ref.html"><LINK
REL="UP"
TITLE="eCos Synthetic Target"
HREF="hal-synth-arch.html"><LINK
REL="PREVIOUS"
TITLE="Writing New Devices - target"
HREF="synth-new-target.html"><LINK
REL="NEXT"
TITLE="Porting"
HREF="synth-porting.html"></HEAD
><BODY
CLASS="REFENTRY"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>eCos Reference Manual</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="synth-new-target.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="synth-porting.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><H1
><A
NAME="SYNTH-NEW-HOST">Writing New Devices - host</H1
><DIV
CLASS="REFNAMEDIV"
><A
NAME="AEN18390"
></A
><H2
>Name</H2
>Writing New Devices -- extending the synthetic target, host-side</DIV
><DIV
CLASS="REFSECT1"
><A
NAME="SYNTH-NEW-HOST-DESCRIPTION"
></A
><H2
>Description</H2
><P
>On the host-side adding a new device means writing a Tcl/Tk script
that will handle instantiation and subsequent requests from the
target-side. These scripts all run in the same full interpreter,
extended with various commands provided by the main I/O auxiliary
code, and running in an overall GUI framework. Some knowledge of
programming with Tcl/Tk is required to implement host-side device
support.
</P
><P
>Some devices can be implemented entirely using a Tcl/Tk script. For
example, if the final system will have some buttons then those can be
emulated in the synthetic target using a few Tk widgets. A simple
emulation could just have the right number of buttons in a row. A more
advanced emulation could organize the buttons with the right layout,
perhaps even matching the colour scheme, the shapes, and the relative
sizes. With other devices it may be necessary for the Tcl script to
interact with an external program, because the required functionality
cannot easily be accessed from a Tcl script. For example interacting
with a raw ethernet device involves some <TT
CLASS="FUNCTION"
>ioctl</TT
>
calls, which is easier to do in a C program. Therefore the
<TT
CLASS="FILENAME"
>ethernet.tcl</TT
> script which implements the
host-side ethernet support spawns a separate program
<TT
CLASS="FILENAME"
>rawether</TT
>, written in C, that performs the
low-level I/O. Raw ethernet access usually also requires root
privileges, and running a small program <TT
CLASS="FILENAME"
>rawether</TT
>
with such privileges is somewhat less of a security risk than the
whole eCos application, the I/O auxiliary, and various dynamically
loaded Tcl scripts.
</P
><P
>Because all scripts run in a single interpreter, some care has
to be taken to avoid accidental sharing of global variables. The best
way to avoid problems is to have each script create its own Tcl
namespace, so for example the <TT
CLASS="FILENAME"
>ethernet.tcl</TT
> script
creates a namespace <TT
CLASS="VARNAME"
>ethernet::</TT
> and all variables
and procedures reside in this namespace. Similarly the I/O auxiliary
itself makes use of a <TT
CLASS="VARNAME"
>synth::</TT
> namespace.
</P
></DIV
><DIV
CLASS="REFSECT1"
><A
NAME="SYNTH-NEW-HOST-BUILD"
></A
><H2
>Building and Installation</H2
><P
>When an eCos device driver or application code instantiates a device,
the I/O auxiliary will attempt to load a matching Tcl script. The
third argument to <TT
CLASS="FUNCTION"
>synth_auxiliary_instantiate</TT
>
specifies the type of device, for example <TT
CLASS="LITERAL"
>ethernet</TT
>,
and the I/O auxiliary will append a <TT
CLASS="FILENAME"
>.tcl</TT
> suffix
and look for a script <TT
CLASS="FILENAME"
>ethernet.tcl</TT
>.
</P
><P
>If the device being instantiated is application-specific rather than
part of an eCos package, the I/O auxiliary will look first in the
current directory, then in <TT
CLASS="FILENAME"
>~/.ecos/synth</TT
>. If it is part of an eCos
package then the auxiliary will expect to find the Tcl script and any
support files below <TT
CLASS="FILENAME"
>libexec/ecos</TT
> in the install tree - note
that the same install tree must be used for the I/O auxiliary itself
and for any device driver support. The directory hierarchy below
<TT
CLASS="FILENAME"
>libexec/ecos</TT
> matches the
structure of the eCos repository, allowing multiple versions of a
package to be installed to allow for incompatible protocol changes.
</P
><P
>The preferred way to build host-side software is to use
<B
CLASS="COMMAND"
>autoconf</B
> and <B
CLASS="COMMAND"
>automake</B
>. Usually
this involves little more than copying the
<TT
CLASS="FILENAME"
>acinclude.m4</TT
>, <TT
CLASS="FILENAME"
>configure.in</TT
>
and <TT
CLASS="FILENAME"
>Makefile.am</TT
> files from an existing package,
for example the synthetic target ethernet driver, and then making
minor edits. In <TT
CLASS="FILENAME"
>acinclude.m4</TT
> it may be necessary
to adjust the path to the root of the repository.
<TT
CLASS="FILENAME"
>configure.in</TT
> may require a similar change, and
the <TT
CLASS="FUNCTION"
>AC_INIT</TT
> macro invocation will have to be
changed to match one of the files in the new package. A critical macro
in this file is <TT
CLASS="FILENAME"
>ECOS_PACKAGE_DIRS</TT
> which will set
up the correct install directory. <TT
CLASS="FILENAME"
>Makefile.am</TT
> may
require some more changes, for example to specify the data files that
should be installed (including the Tcl script). These files should
then be processed using <B
CLASS="COMMAND"
>aclocal</B
>,
<B
CLASS="COMMAND"
>autoconf</B
> and <B
CLASS="COMMAND"
>automake</B
> in that
order. Actually building the software then just involves
<B
CLASS="COMMAND"
>configure</B
>, <B
CLASS="COMMAND"
>make</B
> and
<B
CLASS="COMMAND"
>make install</B
>, as per the instructions in the
toplevel <TT
CLASS="FILENAME"
>README.host</TT
> file.
</P
><P
>To assist developers, if the environment variable
<TT
CLASS="ENVAR"
>ECOSYNTH_DEVEL</TT
> is set then a slightly different
algorithm is used for locating device Tcl scripts. Instead of looking
only in the install tree the I/O auxiliary will also look in the
source tree, and if the script there is more recent than the installed
version it will be used in preference. This allows developers to
modify the master copy without having to run <B
CLASS="COMMAND"
>make
install</B
> all the time.
</P
><P
>If a script needs to know where it has been installed it can examine
the Tcl variable <TT
CLASS="VARNAME"
>synth::device_install_dir</TT
> . This
variable gets updated whenever a script is loaded, so if the
value may be needed later it should be saved away in a device-specific
variable.
</P
></DIV
><DIV
CLASS="REFSECT1"
><A
NAME="SYNTH-NEW-HOST-INSTANTIATION"
></A
><H2
>Instantiation</H2
><P
>The I/O auxiliary will <B
CLASS="COMMAND"
>source</B
> the device-specific
Tcl script when the eCos application first attempts to instantiate a
device of that type. The script should return a procedure that will be
invoked to instantiate a device.
</P
><TABLE
BORDER="5"
BGCOLOR="#E0E0F0"
WIDTH="70%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>namespace eval ethernet {
…
proc instantiate { id instance data } {
…
return ethernet::handle_request
}
}
return ethernet::instantiate</PRE
></TD
></TR
></TABLE
><P
>The <TT
CLASS="VARNAME"
>id</TT
> argument is a unique identifier for this
device instance. It will also be supplied on subsequent calls to the
request handler, and will match the return value of
<TT
CLASS="FUNCTION"
>synth_auxiliary_instantiate</TT
> on the target side. A
common use for this value is as an array index to support multiple
instances of this types of device. The <TT
CLASS="VARNAME"
>instance</TT
> and
<TT
CLASS="VARNAME"
>data</TT
> arguments match the corresponding arguments to
<TT
CLASS="FUNCTION"
>synth_auxiliary_instantiate</TT
> on the target side, so
a typical value for <TT
CLASS="VARNAME"
>instance</TT
> would be
<TT
CLASS="LITERAL"
>eth0</TT
>, and <TT
CLASS="VARNAME"
>data</TT
> is used to pass
arbitrary initialization parameters from target to host.
</P
><P
>The actual work done by the instantiation procedure is obviously
device-specific. It may involve allocating an <A
HREF="synth-new-host.html#SYNTH-NEW-HOST-INTERRUPTS"
>interrupt vector</A
>, adding a
device-specific subwindow to the display, opening a real Linux device,
establishing a socket connection to some server, spawning a separate
process to handle the actual I/O, or a combination of some or all of
the above.
</P
><P
>If the device is successfully instantiated then the return value
should be a handler for subsequent I/O requests. Otherwise the return
value should be an empty string, and on the target-side the
<TT
CLASS="FUNCTION"
>synth_auxiliary_instantiate</TT
> call will return
<TT
CLASS="LITERAL"
>-1</TT
>. The script is responsible for providing
<A
HREF="synth-new-host.html#SYNTH-NEW-HOST-OUTPUT"
>diagnostics</A
> explaining
why the device could not be instantiated.
</P
></DIV
><DIV
CLASS="REFSECT1"
><A
NAME="SYNTH-NEW-HOST-REQUESTS"
></A
><H2
>Handling Requests</H2
><P
>When the target-side calls
<TT
CLASS="FUNCTION"
>synth_auxiliary_xchgmsg</TT
>, the I/O auxiliary will
end up calling the request handler for the appropriate device instance
returned during instantiation:
</P
><TABLE
BORDER="5"
BGCOLOR="#E0E0F0"
WIDTH="70%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>namespace eval ethernet {
…
proc handle_request { id request arg1 arg2 txdata txlen max_rxlen } {
…
if { <some condition> } {
synth::send_reply <error code> 0 ""
return
}
…
synth::send_reply <reply code> $packet_len $packet
}
…
}</PRE
></TD
></TR
></TABLE
><P
>The <TT
CLASS="VARNAME"
>id</TT
> argument is the same device id that was
passed to the instantiate function, and is typically used as an array
index to access per-device data. The <TT
CLASS="VARNAME"
>request</TT
>,
<TT
CLASS="VARNAME"
>arg1</TT
>, <TT
CLASS="VARNAME"
>arg2</TT
>, and
<TT
CLASS="VARNAME"
>max_rxlen</TT
> are the same values that were passed to
<TT
CLASS="FUNCTION"
>synth_auxiliary_xchgmsg</TT
> on the target-side,
although since this is a Tcl script obviously the numbers have been
converted to strings. The <TT
CLASS="VARNAME"
>txdata</TT
> buffer is raw data
as transmitted by the target, or an empty string if the I/O operation
does not involve any additional data. The Tcl procedures
<B
CLASS="COMMAND"
>binary scan</B
>, <B
CLASS="COMMAND"
>string index</B
> and
<B
CLASS="COMMAND"
>string range</B
> may be found especially useful when
manipulating this buffer. <TT
CLASS="VARNAME"
>txlen</TT
> is provided for
convenience, although <B
CLASS="COMMAND"
>string length $txdata</B
> would
give the same information.
</P
><P
>The code for actually processing the request is of course device
specific. If the target does not expect a reply then the request
handler should just return when finished. If a reply is expected then
there should be a call to <B
CLASS="COMMAND"
>synth::send_reply</B
>. The
first argument is the reply code, and will be turned into a 32-bit
integer on the target side. The second argument specifies the length
of the reply data, and the third argument is the reply data itself.
For some devices the Tcl procedure <B
CLASS="COMMAND"
>binary format</B
>
may prove useful. If the reply involves just a code and no additional
data, the second and third arguments should be <TT
CLASS="LITERAL"
>0</TT
>
and an empty string respectively.
</P
><P
>Attempts to send a reply when none is expected, fail to send a reply
when one is expected, or send a reply that is larger than the
target-side expects, will all be detected by the I/O auxiliary and
result in run-time error messages.
</P
><P
>It is not possible for the host-side code to send unsolicited messages
to the target. If host-side code needs attention from the target, for
example because some I/O operation has completed, then an interrupt
should be raised.
</P
></DIV
><DIV
CLASS="REFSECT1"
><A
NAME="SYNTH-NEW-HOST-INTERRUPTS"
></A
><H2
>Interrupts</H2
><P
>The I/O auxiliary provides a number of procedures for interrupt
handling.
</P
><TABLE
BORDER="5"
BGCOLOR="#E0E0F0"
WIDTH="70%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>synth::interrupt_allocate <name>
synth::interrupt_get_max
synth::interrupt_get_devicename <vector>
synth::interrupt_raise <vector></PRE
></TD
></TR
></TABLE
><P
><B
CLASS="COMMAND"
>synth::interrupt_allocate</B
> is normally called during
device instantiation, and returns the next free interrupt vector. This
can be passed on to the target-side device driver in response to a
suitable request, and it can then install an interrupt handler on that
vector. Interrupt vector <TT
CLASS="LITERAL"
>0</TT
> is used within the
target-side code for the real-time clock, so the allocated vectors
will start at <TT
CLASS="LITERAL"
>1</TT
>. The argument identifies the
device, for example <TT
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -