Skip to content

Device Drivers

Daniele Lacamera edited this page Dec 3, 2015 · 7 revisions

Device drivers

Similarly to what it happens for protocols, the stack accepts the registration of another type of module, describing a device driver, that can be attached to the stack. The pico_device structure is described in pico_device.h as follows:

struct pico_device {
    char name[MAX_DEVICE_NAME];
    uint32_t hash;
    uint32_t overhead;
    uint32_t mtu;
    struct pico_ethdev *eth; /* Null if non-ethernet */
    struct pico_queue *q_in;
    struct pico_queue *q_out;
    int (*link_state)(struct pico_device *self);
    int (*send)(struct pico_device *self, void *buf, int len); /* Send function. Return 0 if busy */
    int (*poll)(struct pico_device *self, int loop_score);
    void (*destroy)(struct pico_device *self);
    int (*dsr)(struct pico_device *self, int loop_score);
    int __serving_interrupt;
    /* used to signal the upper layer the number of events arrived since the last processing */
    volatile int eventCnt;
  #ifdef PICO_SUPPORT_IPV6
    struct pico_nd_hostvars hostvars;
  #endif
};

Some of the fields here are in common with the protocol structure, the name, hash, and the two queues have exactly the same meaning as for the pico_protocol descriptor. The relationship with the internal scheduler to this type of module though, is a bit different. There are two different possible strategies to implement a device driver: a poll-based implementation and a IRQ-based mechanism, slightly more efficient but also more difficult to implement.

In both cases, the stack uses the send function pointer of the driver whenever a frame has to leave the device using a link associated to the device. The send function associated by the driver during the initialization phase must implement all the mechanisms to actually put the frame out on the desired physical medium.

The polling strategy of the device driver requires the driver developer to implement the poll function. The poll function will be called by the stack at regular interval, to give the driver the possibility to inject newly received packet into the stack. This means that the smallest set of operations required to implement a device driver consists of the send and poll operations.

An asynchronous driver, on the other hand, might not implement the poll function, and instead set the __serving_interrupt flag from the interrupt handler every time a new frame is available for possible reception. This will ensure that the dsr function is called at least once to process the incoming frame when the interrupt returns. The purpose of the dsr is then the same as the poll, except that the former will only be invoked when an external, asynchronous event such as an interrupt handler has set the __serving_interrupt flag before.

The eth field is very important for two reasons: in the first place, it provides a datalink address associated to the device, but it can also assume a NULL value to indicate that the specific device associated to the driver is exchanging pure IP packets instead of full frames, and if so the stack will react accordingly. This is pretty useful in point-to-point datalink drivers, where the two endpoints don’t need any layer 2 addressing. Two examples of this kind of devices can be found in the tun driver and the PPPoS (ppp over serial line) implementation.

By implementing the link_state function, the driver can provide information regarding the state of the physical channel before any use of the device, or during the device activity. This function is not mandatory to implement in a driver (the stack will assume that the device is permanently connected if the function is not defined), but it might help if the physical device can sense the activity of the link and react accordingly. Furthermore, a global function pico_dev_link_state is exported by the stack to the public API, making it possible for the application developer to know about the physical state of the device.

The destroy function is not mandatory either: it is in fact used to free possible additional entries related to the device when the device is destroyed.

In order to create a new device instance, the driver needs to export its own init function, which must perform the specific initialization needed before calling the generic pico_device_init from the stack API. From the moment the device is created, it becomes part of the stack itself.

Clone this wiki locally