Now a brief review of the functions. This discussion will use generic names for the functions — your driver should use hardware-specific names to maintain uniqueness against any other drivers.
static bool DRV_HDWR_init(struct cyg_netdevtab_entry *tab) |
struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance; ....initialization code.... // Initialize upper level driver (sc->funs->eth_drv->init)( sc, unsigned char *enaddr ); |
enaddr
is a pointer to the ethernet station address for this unit, to inform
the stack of this device's readiness and availability.Note: The ethernet station address (ESA) is supposed to be a world-unique, 48 bit address for this particular ethernet interface. Typically it is provided by the board/hardware manufacturer in ROM.
In many packages it is possible for the ESA to be set from RedBoot, (perhaps from 'fconfig' data), hard-coded from CDL, or from an EPROM. A driver should choose a run-time specified ESA (e.g. from RedBoot) preferentially, otherwise (in order) it should use a CDL specified ESA if one has been set, otherwise an EPROM set ESA, or otherwise fail. See the cl/cs8900a ethernet driver for an example.
static void HRDWR_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) |
Note: This function will be called whenever the up/down state of the logical interface changes, e.g. when the IP address changes, or when promiscuous mode is selected by means of an
ioctl()
call in the application. This may occur more than once, so this function needs to be prepared for that case.
Note: In future, the
flags
field (currently unused) may be used to tell the function how to start up, e.g. whether interrupts will be used, alternate means of selecting promiscuous mode etc.
static void HRDWR_stop(struct eth_drv_sc *sc) |
static int HRDWR_control( struct eth_drv_sc *sc, unsigned long key, void *data, int len) |
ioctl()
calls in the BSD
stack, and would be anything that might require the hardware setup to
change (i.e. cannot be performed totally by the
platform-independent layers).The key
parameter selects the operation, and the
data
and len
params point describe,
as required, some data for the operation in question.
Available Operations:
This operation sets the ethernet station address (ESA or MAC) for the device. Normally this address is kept in non-volatile memory and is unique in the world. This function must at least set the interface to use the new address. It may also update the NVM as appropriate.
These acquire a set of statistical counters from the interface, and write
the information into the memory pointed to by data
.
The “UD” variant explicitly instructs the driver to acquire
up-to-date values. This is a separate option because doing so may take
some time, depending on the hardware.
The definition of the data structure is in cyg/io/eth/eth_drv_stats.h.
This call is typically made by SNMP, see Chapter 62.
This entry instructs the device to set up multicast packet filtering
to receive only packets addressed to the multicast ESAs in the list pointed
to by data
.
The format of the data is a 32-bit count of the ESAs in the list, followed by packed bytes which are the ESAs themselves, thus:
#define ETH_DRV_MAX_MC 8 struct eth_drv_mc_list { int len; unsigned char addrs[ETH_DRV_MAX_MC][ETHER_ADDR_LEN]; }; |
This entry instructs the device to receive all multicast packets, and delete any explicit filtering which had been set up.
This function should return zero if the specified operation was completed successfully. It should return non-zero if the operation could not be performed, for any reason.
static int HRDWR_can_send(struct eth_drv_sc *sc) |
Return the number of packets which could be accepted at this time, zero implies that the interface is saturated/busy.
struct eth_drv_sg { CYG_ADDRESS buf; CYG_ADDRWORD len; }; static void HRDWR_send( struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key) |
Note: All data in/out of the driver is specified via a “scatter-gather” list. This is just an array of address/length pairs which describe sections of data to move (in the order given by the array), as in the struct eth_drv_sg defined above and pointed to by
sg_list
.
Once the data has been successfully sent by the interface (or if an
error occurs), the driver should call
(sc->funs->eth_drv->tx_done)()
(see the Section called Callback Tx-Done function)
using the specified key
.
Only then will the upper layers release the resources
for that packet and start another transmission.
Note: In future, this function may be extended so that the data need not be copied by having the function return a “disposition” code (done, send pending, etc). At this point, you should move the data to some “safe” location before returning.
static void HRDWR_deliver(struct eth_drv_sc *sc) |
After handling any outstanding incoming packets or pending transmission status, it can unmask the device's interrupts, and free any relevant resources so it can process further packets.
It will be called when the interrupt handler for the network device has called
eth_drv_dsr( vector, count, (cyg_addrword_t)sc ); |
eth_drv_dsr()
call must occur from within the
interrupt handler's DSR (not the ISR) or actually be
the DSR, whenever it is determined that
the device needs attention from the foreground. The third parameter
(data
in the prototype of
eth_drv_dsr()
must
be a valid struct eth_drv_sc pointer sc
.The reason for this slightly convoluted train of events is to keep the DSR (and ISR) execution time as short as possible, so that other activities of higher priority than network servicing are not denied the CPU by network traffic.
To deliver a newly-received packet into the network stack, the deliver routine must call
(sc->funs->eth_drv->recv)(sc, len); |
static void HRDWR_recv( struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) |
(sc->funs->eth_drv->recv)(struct eth_drv_sc *sc, int total_len) |
(sc->funs->eth_drv->recv)()
function then arranges network buffers
and structures for the data and then calls
HRDWR_recv()
to actually
move the data from the interface.A scatter-gather list (struct eth_drv_sg) is used once more, just like in the send case.
static void HRDWR_poll(struct eth_drv_sc *sc) |
It is perfectly correct and acceptable for the poll function to look like this:
static void HRDWR_poll(struct eth_drv_sc *sc) { my_interrupt_ISR(sc); HRDWR_deliver(struct eth_drv_sc *sc); } |
static int HRDWR_int_vector(struct eth_drv_sc *sc) |