|  | ----------------------- | 
|  | 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. |