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 ); |
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) |
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 47.
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 ); |
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) |
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) |