mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 16:31:25 +01:00 
			
		
		
		
	The dev->halt() func can be called at any time, and the dev->recv() func does not need to use NetRxPackets[] when calling NetReceive(). Signed-off-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Ben Warren <biggerbadderben@gmail.com>
		
			
				
	
	
		
			186 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| -----------------------
 | |
|  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.
 | |
| 
 | |
| ------------------
 | |
|  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.
 | |
| Any such information that is specific to an interface should be stored in a
 | |
| private, driver-defined data structure and pointed to by eth->priv (see below).
 | |
| 
 | |
| 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 1;
 | |
| }
 | |
| 
 | |
| 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.
 | |
| 
 | |
| The return value for this function should be as follows:
 | |
| < 0 - failure (hardware failure, not probe failure)
 | |
| >=0 - number of interfaces detected
 | |
| 
 | |
| 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.
 | |
| For each packet you receive, you should call the NetReceive() function on it
 | |
| along with the packet length.  The common code sets up packet buffers for you
 | |
| already in the .bss (NetRxPackets), so there should be no need to allocate your
 | |
| own.  This doesn't mean you must use the NetRxPackets array however; you're
 | |
| free to call the NetReceive() function with any buffer you wish.  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.  It can be called at any time (before any call to the related
 | |
| init function), so make sure it can handle this sort of thing.
 | |
| 
 | |
| 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_MII / 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:
 | |
| #if defined(CONFIG_MII) || defined(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.
 |