[U-Boot] [PATCH 2/2] document network driver framework

Mike Frysinger vapier at gentoo.org
Sun Jul 19 03:04:09 CEST 2009


Signed-off-by: Mike Frysinger <vapier at gentoo.org>
---
Ben: some things to note:
	- i adopted Jean's proposed naming scheme in the CONFIG section
	- i deprecated calling the driver-specific entry point
	  "xxx_initialization()" in favor of "xxx_register()" because the
	  former is way too confusing with everyone also having "xxx_init()"

 doc/README.drivers.eth |  199 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 199 insertions(+), 0 deletions(-)
 create mode 100644 doc/README.drivers.eth

diff --git a/doc/README.drivers.eth b/doc/README.drivers.eth
new file mode 100644
index 0000000..00b4eb1
--- /dev/null
+++ b/doc/README.drivers.eth
@@ -0,0 +1,199 @@
+-----------------------
+ Ethernet Driver Guide
+-----------------------
+
+The networking stack in Das U-Boot is designed for multiple network devices
+to be easily added and controlled at runtime.  This guide is meant for people
+who wish to review the net driver stack with an eye towards implementing your
+own ethernet device driver.  Here we will describe a new pseudo 'APE' driver.
+
+----------------
+ CONFIG Options
+----------------
+
+The common config defines your device should respect (if applicable):
+	CONFIG_MII  - configure in MII mode
+	CONFIG_RMII - configure in RMII mode
+
+	CONFIG_CMD_MII - support the MII command
+
+If you want to add config options specific to your driver, you should use the
+naming convention:
+	CONFIG_NET_<DRIVERNAME>_<OPTION>
+For example, if the APE driver could operate in either 16bit or 32bit mode,
+there would probably be the option:
+	CONFIG_NET_APE_32BIT - operate in 32bit mode rather than 16bit default
+
+And to control whether your driver is even enabled, you should use:
+	CONFIG_SYS_NET_<DRIVERNAME>
+So the APE driver would look like:
+	CONFIG_SYS_NET_APE
+
+------------------
+ Driver Functions
+------------------
+
+All functions you will be implementing in this document have the return value
+meaning of 0 for success and non-zero for failure.
+
+ ----------
+  Register
+ ----------
+
+When U-Boot initializes, it will call the common function eth_initialize().
+This will in turn call the board-specific board_eth_init() (or if that fails,
+the cpu-specific cpu_eth_init()).  These board-specific functions can do random
+system handling, but ultimately they will call the driver-specific register
+function which in turn takes care of initializing that particular instance.
+
+Keep in mind that you should code the driver to avoid storing state in global
+data as someone might want to hook up two of the same devices to one board.  If
+the state is maintained as global data, it makes using both of those devices
+impossible.
+
+So the call graph at this stage would look something like:
+board_init()
+	eth_initialize()
+		board_eth_init() / cpu_eth_init()
+			driver_register()
+				initialize eth_device
+				eth_register()
+
+At this point in time, the only thing you need to worry about is the driver's
+register function.  The pseudo code would look something like:
+int ape_register(bd_t *bis, int iobase)
+{
+	struct ape_priv *priv;
+	struct eth_device *dev;
+
+	priv = malloc(sizeof(*priv));
+	if (priv == NULL)
+		return 1;
+
+	dev = malloc(sizeof(*dev));
+	if (dev == NULL) {
+		free(priv);
+		return 1;
+	}
+
+	/* setup whatever private state you need */
+
+	memset(dev, 0, sizeof(*dev));
+	sprintf(dev->name, "APE");
+
+	/* if your device has dedicated hardware storage for the
+	 * MAC, read it and initialize dev->enetaddr with it
+	 */
+	ape_mac_read(dev->enetaddr);
+
+	dev->iobase = iobase;
+	dev->priv = priv;
+	dev->init = ape_init;
+	dev->halt = ape_halt;
+	dev->send = ape_send;
+	dev->recv = ape_recv;
+
+	eth_register(dev);
+
+#ifdef CONFIG_CMD_MII)
+	miiphy_register(dev->name, ape_mii_read, ape_mii_write);
+#endif
+
+	return 0;
+}
+
+The exact arguments needed to initialize your device are up to you.  If you
+need to pass more/less arguments, that's fine.  You should also add the
+prototype for your new register function to include/netdev.h.  You might notice
+that many drivers seem to use xxx_initialize() rather than xxx_register().
+This is the old naming convention and should be avoided as it causes confusion
+with the driver-specific init function.
+
+Other than locating the MAC address in dedicated hardware storage, you should
+not touch the hardware in anyway.  That step is handled in the driver-specific
+init function.  Remember that we are only registering the device here, we are
+not checking its state or doing random probing.
+
+ -----------
+  Callbacks
+ -----------
+
+Now that we've registered with the ethernet layer, we can start getting some
+real work done.  You will need four functions:
+	int ape_init(struct eth_device *dev, bd_t *bis);
+	int ape_send(struct eth_device *dev, volatile void *packet, int length);
+	int ape_recv(struct eth_device *dev);
+	int ape_halt(struct eth_device *dev);
+
+The init function checks the hardware (probing/identifying) and gets it ready
+for send/recv operations.  You often do things here such as resetting the MAC
+and/or PHY, and waiting for the link to autonegotiate.  You should also take
+the opportunity to program the device's MAC address with the dev->enetaddr
+member.  This allows the rest of U-Boot to dynamically change the MAC address
+and have the new settings be respected.
+
+The send function does what you think -- transmit the specified packet whose
+size is specified by length (in bytes).  You should not return until the
+transmission is complete, and you should leave the state such that the send
+function can be called multiple times in a row.
+
+The recv function should process packets as long as the hardware has them
+readily available before returning.  i.e. you should drain the hardware fifo.
+The common code sets up packet buffers for you already (NetRxPackets), so there
+is no need to allocate your own.  For each packet you receive, you should call
+the NetReceive() function on it with the packet length.  So the pseudo code
+here would look something like:
+int ape_recv(struct eth_device *dev)
+{
+	int length, i = 0;
+	...
+	while (packets_are_available()) {
+		...
+		length = ape_get_packet(&NetRxPackets[i]);
+		...
+		NetReceive(&NetRxPackets[i], length);
+		...
+		if (++i >= PKTBUFSRX)
+			i = 0;
+		...
+	}
+	...
+	return 0;
+}
+
+The halt function should turn off / disable the hardware and place it back in
+its reset state.
+
+So the call graph at this stage would look something like:
+some net operation (ping / tftp / whatever...)
+	eth_init()
+		dev->init()
+	eth_send()
+		dev->send()
+	eth_rx()
+		dev->recv()
+	eth_halt()
+		dev->halt()
+
+----------------
+ CONFIG_CMD_MII
+----------------
+
+If your device supports banging arbitrary values on the MII bus (pretty much
+every device does), you should add support for the mii command.  Doing so is
+fairly trivial and makes debugging mii issues a lot easier at runtime.
+
+After you have called eth_register() in your driver's register function, add
+a call to miiphy_register() like so:
+#ifdef CONFIG_CMD_MII
+	miiphy_register(dev->name, mii_read, mii_write);
+#endif
+
+And then define the mii_read and mii_write functions if you haven't already.
+Their syntax is straightforward:
+	int mii_read(char *devname, uchar addr, uchar reg, ushort *val);
+	int mii_write(char *devname, uchar addr, uchar reg, ushort val);
+
+The read function should read the register 'reg' from the phy at address 'addr'
+and store the result in the pointer 'val'.  The implementation for the write
+function should logically follow.
-- 
1.6.3.3



More information about the U-Boot mailing list