⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 driver_doc

📁 eCos/RedBoot for勤研ARM AnywhereII(4510) 含全部源代码
💻
📖 第 1 页 / 共 2 页
字号:
This file provides a simple description of how to write a low-level,
hardware dependent ethernet driver.

The basic idea is that there is a high-level driver (which is only
code/functions) that is part of the stack.  There will be one or more
low-level driver tied to the actual network hardware.  Each of these
drivers contains one or more driver instances.  The principal idea is
that the low-level drivers know nothing of the details of the stack that
will be using them.  Thus, the same driver can be used by the eCos
supported TCP/IP stack, or any other, with no changes.

A driver instance is contained within a "struct eth_drv_sc".

   struct eth_hwr_funs {
      // Initialize hardware (including startup)
      void (*start)(struct eth_drv_sc *sc,
                    unsigned char *enaddr);
      // Shut down hardware
      void (*stop)(struct eth_drv_sc *sc);
      // Control interface
      int (*control)(struct eth_drv_sc *sc,
                     unsigned long cmd,
                     void *data,
                     int len);
      // Query interface - can a packet be sent?
      int (*can_send)(struct eth_drv_sc *sc);
      // Send a packet of data
      void (*send)(struct eth_drv_sc *sc,
                   struct eth_drv_sg *sg_list,
                   int sg_len,
                   int total_len,
                   unsigned long key);
      // Receive [unload] a packet of data
      void (*recv)(struct eth_drv_sc *sc,
                   struct eth_drv_sg *sg_list,
                   int sg_len);
      // Deliver data to/from device from/to stack memory space
      // (moves lots of memcpy()s out of DSRs into thread)
      void (*deliver)(struct eth_drv_sc *sc);
      // Poll for interrupts/device service
      void (*poll)(struct eth_drv_sc *sc);
      // Get interrupt information from hardware driver
      int (*int_vector)(struct eth_drv_sc *sc);
      // Logical driver interface
      struct eth_drv_funs *eth_drv, *eth_drv_old;
  };

  struct eth_drv_sc {
      struct eth_hwr_funs *funs;
      void                *driver_private;
      const char          *dev_name;
      struct arpcom       sc_arpcom; /* ethernet common */
  };

You create one of these instances using the "ETH_DRV_SC()" macro which
sets up the structure, including the prototypes for the functions, etc.
By doing things this way, if the internal design of the ethernet drivers
changes (e.g. we need to add a new low-level implementation function),
existing drivers will no longer compile until updated.  This is much
better than to have all of the definitions in the low-level drivers
themselves and have them be [quietly] broken if the interfaces change.

The "magic" which gets the drivers started [and indeed, linked] is
similar to what is used for the I/O subsystem. [Note: I may try and
make it part of the I/O subsystem later.]  This is done using the
"NETDEVTAB_ENTRY()" macro, which defines an initialization function
and the basic data structures for the low-level driver.

  typedef struct cyg_netdevtab_entry {
      const char        *name;
      bool             (*init)(struct cyg_netdevtab_entry *tab);
      void              *device_instance;
      unsigned long     status;
  } cyg_netdevtab_entry_t;

The "device_instance" entry here would point to the "struct
eth_drv_sc" entry previously defined.  This allows the network driver
setup to work with any class of driver, not just ethernet drivers.  In
the future, there will surely be serial PPP drivers, etc.  These will
use the "NETDEVTAB" setup to create the basic driver, but they will
most likely be built on top of other high-level device driver layers.

So, the bottom line is that a hardware driver will have a template
(boilerplate) which looks like this:

	#include <cyg/infra/cyg_type.h>
	#include <cyg/hal/hal_arch.h>
	#include <cyg/infra/diag.h>
	#include <cyg/hal/drv_api.h>
	#include <cyg/io/eth/netdev.h>
	#include <cyg/io/eth/eth_drv.h>

	ETH_DRV_SC(DRV_sc,
	           0,             // No driver specific data needed
	           "eth0",        // Name for this interface
	           HRDWR_start,
	           HRDWR_stop,
	           HRDWR_control,
	           HRDWR_can_send
	           HRDWR_send,
	           HRDWR_recv);

	NETDEVTAB_ENTRY(DRV_netdev, 
	                "DRV", 
	                DRV_HRDWR_init, 
	                &DRV_sc);

This, along with the referenced functions, completely define the driver.
Extensibility note: if one needed the same low-level driver to handle
multiple similar hardware interfaces, you would need multiple invocations
of the "ETH_DRV_SC()/NETDEVTAB_ENTRY()" macros.  You would add a pointer
to some instance specific data, e.g. containing base addresses, interrupt
numbers, etc, where the "0, // No driver specific data" is currently.

Now a quick waltz through the functions.  This discussion will use the
generic names from above.

static bool DRV_HDWR_init(struct cyg_netdevtab_entry *tab)
==========================================================

This function is called as part of system initialization.  Its primary
function is to decide if the hardware [as indicated via
tab->device_instance] is working and if the interface needs to be made
available in the system.  If this is the case, this function needs to
finish with a call to the ethernet driver function:

	eth_drv_init((struct eth_drv_sc *)tab->device_instance,
	             unsigned char *enaddr);

where 'enaddr' is a pointer to the ethernet station address for this
unit.  Note: the ethernet station address 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 eth driver for an example.

static void
HRDWR_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
====================================================================

This function is called, perhaps much later than system initialization
time, when the system (an application) is ready for the interface to
become active.  The purpose of this function is to set up the hardware
interface to start accepting packets from the network and be able to
send packets out.  Note: this function will be called whenever the
up/down state of the logical interface changes, e.g. when the IP address
changes.  This may occur more than one time, so this function needs to
be prepared for that case.

FUTURE: the "flags" field (currently unused) may be used to tell the
function how to start up, e.g. whether interrupts will be used,
selection of "promiscuous" mode etc.

static void HRDWR_stop(struct eth_drv_sc *sc)
=============================================

This function is the inverse of "start".  It should shut down the
hardware and keep it from interacting with the physical network.

static int
HRDWR_control(struct eth_drv_sc *sc, unsigned long key, void *data, int len)
============================================================================

This function is used to perform low-level "control" operations on the
interface.  These operations would be initiated via 'ioctl()' in the BSD
stack, and would be anything that would require the hardware setup to
possibly change (i.e.  cannot be performed totally by the
platform-independent layers).

Current operations:

ETH_DRV_SET_MAC_ADDRESS:
  This function 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.

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)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -