-
Notifications
You must be signed in to change notification settings - Fork 15
Example device driver
We will be showing how to write a very simple Ethernet device driver.
Before you start, please read the page on the architecture of device drivers first:
[Device Drivers] (Device-Drivers)
A device driver exposes at minimum one API call to the user: The create function.
The amount of parameters may vary, depending on the kind of driver you are writing.
struct pico_device *pico_eth_create(char *name, uint8_t *mac);
A destroy function is optional, as explained in the [Device Drivers] (Device-Drivers) architecture page.
In the create function you should:
- Create a new device struct
- Initialize the hardware
- Attach function your drivers' function pointers to the picoTCP device
- Register the device in picoTCP
- Return a pointer to the device struct
picoTCP provides a pico_device struct, that should be allocated, and registered. It can be extended (wrapped in your own struct) if needed.
struct pico_device* eth_dev = PICO_ZALLOC(sizeof(struct pico_device));
if(!eth_dev) {
return NULL;
}
This is device-specific. It could be a call to some HAL or BSP provided by the board manufacturer. E.g.:
BSP_ethernet_init(ETH_BASE, ETH_OPTION_1 | ETH_OPTION_2);
BSP_ethernet_set_mac(ETH_BASE, mac);
BSP_ethernet_enable(ETH_BASE);
A device driver needs at least a function for sending, and a function for receiving packets.
In this case we use the polling option for receiving packet.
This means we must implement a send
and poll
function (implementation below) and attach them to the right function pointers:
eth_dev->send = driver_eth_send;
eth_dev->poll = driver_eth_poll;
Once initialization went fine, you should register the new device in picoTCP.
if( 0 != pico_device_init((struct pico_device *)eth_dev, name, mac)) {
dbg ("Device init failed.\n");
PICO_FREE(eth_dev);
return NULL;
}
If initialization fails, return a NULL pointer; otherwise, return a pointer to the newly allocated device struct.
return eth_dev;
###The complete pico_eth_create function
struct pico_device *pico_eth_create(const char *name, const uint8_t *mac)
{
/* Create device struct */
struct pico_device* eth_dev = PICO_ZALLOC(sizeof(struct pico_device));
if(!eth_dev) {
return NULL;
}
/* Initialize hardware */
BSP_ethernet_init(ETH_BASE, ETH_OPTION_1 | ETH_OPTION_2);
BSP_ethernet_set_mac(ETH_BASE, mac);
BSP_ethernet_enable(ETH_BASE);
/* Attach function pointers */
eth_dev->send = driver_eth_send;
eth_dev->poll = driver_eth_poll;
/* Register the device in picoTCP */
if( 0 != pico_device_init((struct pico_device *)dev_eth, name, mac)) {
dbg("Device init failed.\n");
PICO_FREE(stellaris);
return NULL;
}
/* Return a pointer to the device struct */
return (struct pico_device *)dev_eth;
}
The previous function initialized the driver, and you are almost ready to go. Two more functions must be implemented:
- Sending packets
- Receiving packets
For sending, there is only one possibility: The stack calls your eth_dev->send
function, whenever a packet must be put on the network. You should take a copy of this packet, because the stack frees it immediately after the send()
function returns.
static int pico_eth_send(struct pico_device *dev, void *buf, int len)
{
int retval = BSP_ethernet_send_packet(ETH_BASE, buf, len);
/* send function must return amount of bytes put on the network - no negative values! */
if(retval < 0)
return 0;
return retval;
}
For receiving, there are two possibilities: Polling and asynchronous using an interrupt. (See [Device Drivers] (Device-Drivers) architecture page). Polling is the simplest case, which we demonstrate here.
Then, there are more possibilities:
- Copy the frame from the device driver's buffers into a newly allocated frame in picoTCP
- Do not copy the frame, but rather pass a reference to it; free it later, when the stack does not need it anymore. Technique called "zerocopy".
We demonstrate option #1 here.
The stack will poll every tick for new packets using eth_dev->poll.
static int pico_eth_poll(struct pico_device *dev, int loop_score)
{
uint8_t *buf = NULL;
uint32_t len = 0;
while (loop_score > 0);
if (!BSP_ethernet_packet_available(ETH_BASE)) {
break;
}
len = BSP_ethernet_packet_get(ETH_BASE, &buf);
if (len == 0) {
break;
}
pico_stack_recv(dev, buf, len); /* this will copy the frame into the stack */
loop_score--;
}
/* return (original_loop_score - amount_of_packets_received) */
return loop_score;
}
This is all you need to implement a basic device driver.
For more possibilities and more information about the specifics, see the [Device Drivers] (Device-Drivers) architecture page.
Getting Started
- Setting up the environment
- Testing
- Configuring and compiling
- Running picoTCP on Linux - Deprecated (see setting up)
- Running picoTCP on Windows
Porting
- Build process explained
- Porting the build to another compiler or IDE
- Porting picoTCP to your favorite embedded target
- Porting picoTCP to your favorite Operating System
- Example device driver
Development