📄 ethdrv.sgml
字号:
A scatter-gather list (<type>struct eth_drv_sg</type>) is used once more,
just like in the send case.
</para>
</sect2>
<sect2 id="io-eth-drv-api-poll">
<title>Poll function</title>
<para>
<programlisting>
static void
<replaceable>HRDWR</replaceable>_poll(struct eth_drv_sc *sc)
</programlisting>
This function is used when in a non-interrupt driven system, e.g. when
interrupts are completely disabled. This allows the driver time to check
whether anything needs doing either for transmission, or to check if
anything has been received, or if any other processing needs doing.
</para><para>
It is perfectly correct and acceptable for the poll function to look like
this:
<programlisting>
static void
<replaceable>HRDWR</replaceable>_poll(struct eth_drv_sc *sc)
{
<replaceable>my_interrupt_ISR</replaceable>(sc);
<replaceable>HRDWR</replaceable>_deliver(struct eth_drv_sc *sc);
}
</programlisting>
provided that both the ISR and the deliver functions are idempotent and
harmless if called when there is no attention needed by the hardware. Some
devices might not need a call to the ISR here if the deliver function
contains all the “intelligence.”
</para>
</sect2>
<sect2 id="io-eth-drv-api-int-vector">
<title>Interrupt-vector function</title>
<para>
<programlisting>
static int
<replaceable>HRDWR</replaceable>_int_vector(struct eth_drv_sc *sc)
</programlisting>
This function returns the interrupt vector number used for receive
interrupts.
This is so that the common GDB stubs can detect when to check
for incoming “CTRL-C” packets (used to asynchronously
halt the application) when debugging over ethernet.
The GDB stubs need to know which interrupt the ethernet device uses
so that they can mask or unmask that interrupt as required.
</para>
</sect2>
</sect1>
<sect1 id=io-eth-drv-upper-api>
<title>Upper Layer Functions</title>
<para>
Upper layer functions are called by drivers to deliver received packets
or transmission completion status back up into the network stack.
</para><para>
These functions are defined by the hardware independent upper layers of
the networking driver support. They are present to hide the interfaces
to the actual networking stack so that the hardware drivers may
be used by different network stack implementations without change.
</para><para>
These functions require a pointer to a <type>struct eth_drv_sc</type>
which describes the interface at a logical level. It is assumed that the
low level hardware driver will keep track of this pointer so
it may be passed “up” as appropriate.
</para>
<sect2 id="io-eth-drv-upper-init">
<title>Callback Init function</title>
<para>
<programlisting>
void (sc->funs->eth_drv->init)(
struct eth_drv_sc *sc, unsigned char *enaddr)
</programlisting>
This function establishes the device at initialization time.
It should be called once per device instance only, from the
initialization function, if all is well
(see <xref linkend=io-eth-drv-api-init>).
The hardware should be totally initialized
(<emphasis>not</emphasis> “started”)
when this function is called.
</para>
</sect2>
<sect2 id="io-eth-drv-tx-done">
<title>Callback Tx-Done function</title>
<para>
<programlisting>
void (sc->funs->eth_drv->tx_done)(
struct eth_drv_sc *sc,
unsigned long key, int status)
</programlisting>
This function is called when a packet completes transmission on the
interface. The <parameter>key</parameter>
value must be one of the keys provided to
<function><replaceable>HRDWR</replaceable>_send()</function>
above. The value <parameter>status</parameter> should be non-zero
(details currently undefined) to indicate that an error occurred during the
transmission, and zero if all was well.
</para><para>
It should be called from the deliver function
(see <xref linkend=io-eth-drv-api-deliver>)
or poll function
(see <xref linkend=io-eth-drv-api-poll>).
</para>
</sect2>
<sect2 id="io-eth-drv-upper-recv">
<title>Callback Receive function</title>
<para>
<programlisting>
void (sc->funs->eth_drv->recv)(struct eth_drv_sc *sc, int len)
</programlisting>
This function is called to indicate that a packet of length
<parameter>len</parameter> has
arrived at the interface.
The callback
<function><replaceable>HRDWR</replaceable>_recv()</function> function
described above will be used to actually unload the data from the
interface into buffers used by the device independent layers.
</para><para>
It should be called from the deliver function
(see <xref linkend=io-eth-drv-api-deliver>)
or poll function
(see <xref linkend=io-eth-drv-api-poll>).
</para>
</sect2>
</sect1>
<sect1 id=io-eth-call-graph>
<title>Calling graph for Transmission and Reception</title>
<para>
It may be worth clarifying further the flow of control in the transmit and
receive cases, where the hardware driver does use interrupts and so DSRs to
tell the “foreground” when something asynchronous has occurred.
</para>
<sect2 id=io-eth-call-graph-tx>
<title>Transmission</title>
<orderedlist>
<listitem><para>
Some foreground task such as the application, SNMP “daemon”,
DHCP management thread or whatever, calls into network stack to send a
packet, or the stack decides to send a packet in response to incoming
traffic such as a “ping” or <acronym>ARP</acronym> request.
</para></listitem>
<listitem><para>
The driver calls the
<function><replaceable>HRDWR</replaceable>_can_send()</function>
function in the hardware driver.
</para></listitem>
<listitem><para>
<function><replaceable>HRDWR</replaceable>_can_send()</function>
returns the number of available "slots" in which it
can store a pending transmit packet.
If it cannot send at this time, the packet is queued outside the
hardware driver for later; in this case, the hardware is already busy
transmitting, so expect an interrupt as described below for completion
of the packet currently outgoing.
</para></listitem>
<listitem><para>
If it can send right now, <replaceable>HRDWR</replaceable>_send() is called.
<function><replaceable>HRDWR</replaceable>_send()</function> copies the
data into special hardware buffers, or instructs the hardware to
“send that.” It also remembers the key that is associated with
this tx request.
</para></listitem>
<listitem><para>
These calls return … time passes …
</para></listitem>
<listitem><para>
Asynchronously, the hardware makes an interrupt to say
“transmit is done.”
The ISR quietens the interrupt source in the hardware and
requests that the associated DSR be run.
</para></listitem>
<listitem><para>
The DSR calls (or <emphasis>is</emphasis>) the
<function>eth_drv_dsr()</function> function in the generic driver.
</para></listitem>
<listitem><para>
<function>eth_drv_dsr()</function> in the generic driver awakens the
“Network Delivery Thread” which calls the deliver function
<replaceable>HRDWR</replaceable>_deliver() in the driver.
</para></listitem>
<listitem><para>
The deliver function realizes that a transmit request has completed,
and calls the callback tx-done function
<function>(sc->funs->eth_drv->tx_done)()</function>
with the same key that it remembered for this tx.
</para></listitem>
<listitem><para>
The callback tx-done function
uses the key to find the resources associated with
this transmit request; thus the stack knows that the transmit has
completed and its resources can be freed.
</para></listitem>
<listitem><para>
The callback tx-done function
also enquires whether <replaceable>HRDWR</replaceable>_can_send() now says
“yes, we can send”
and if so, dequeues a further transmit request
which may have been queued as described above. If so, then
<replaceable>HRDWR</replaceable>_send() copies the data into the hardware buffers, or
instructs the hardware to "send that" and remembers the new key, as above.
These calls then all return to the “Network Delivery Thread”
which then sleeps, awaiting the next asynchronous event.
</para></listitem>
<listitem><para>
All done …
</para></listitem>
</orderedlist>
</sect2>
<sect2 id=io-eth-call-graph-rx>
<title>Receive</title>
<orderedlist>
<listitem><para>
Asynchronously, the hardware makes an interrupt to say
“there is ready data in a receive buffer.”
The ISR quietens the interrupt source in the hardware and
requests that the associated DSR be run.
</para></listitem>
<listitem><para>
The DSR calls (or <emphasis>is</emphasis>) the
<function>eth_drv_dsr()</function> function in the generic driver.
</para></listitem>
<listitem><para>
<function>eth_drv_dsr()</function> in the generic driver awakens the
“Network Delivery Thread” which calls the deliver function
<replaceable>HRDWR</replaceable>_deliver() in the driver.
</para></listitem>
<listitem><para>
The deliver function realizes that there is data ready and calls
the callback receive function
<function>(sc->funs->eth_drv->recv)()</function>
to tell it how many bytes to prepare for.
</para></listitem>
<listitem><para>
The callback receive function allocates memory within the stack
(eg. <type>MBUFs</type> in BSD/Unix style stacks) and prepares
a set of scatter-gather buffers that can
accommodate the packet.
</para></listitem>
<listitem><para>
It then calls back into the hardware driver routine
<replaceable>HRDWR</replaceable>_recv().
<replaceable>HRDWR</replaceable>_recv() must copy the data from the
hardware's buffers into the scatter-gather buffers provided, and return.
</para></listitem>
<listitem><para>
The network stack now has the data in-hand, and does with it what it will.
This might include recursive calls to transmit a response packet.
When this all is done, these calls return, and the
“Network Delivery Thread”
sleeps once more, awaiting the next asynchronous event.
</para></listitem>
</orderedlist>
</sect2>
</sect1>
</chapter>
</part>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -