uclinux-W5500 network device kernel driver based on stm32f429

uclinux-W5500 network device kernel driver based on stm32f429
ORIGINAL POST
By NA
components
Hardware Components
stm32f429
X 1
w5500
X 1
details

ucc_march2.PNG

The w5500 driver written in the above article is just a pure application driver. Although it can achieve certain purposes, it does not make full use of the Linux kernel. It seems obsolete in some applications. So I learned about the w5500 network device kernel driver Fortunately, the file for the w5500 network device driver was found in Linux kernel version 4.8, but some functions and data structures are missing to some extent with the version 2.6.33 of the kernel I’m using. It took a long time for this I went to patch the C driver file, and finally patched it to the extent that the build was bug free, but it took a certain period of debugging to achieve the desired effect, but through this, too I learned the knowledge of linux network equipment.
Here is a simple analysis and summary of some important functions and data structures driven by w5500 network devices
The W5500 network device driver is mainly included in two files: w5500.c with w5500-spi.c
w5100-spi.c compiles the spi device driver to use the spi interface to configure w5500, configures the w5500 basic read and write functions, w5100.c compiles mainly the linux kernel network device driver and establishes the network send and receive skb and other functions, pay attention to the two closely connected, one is indispensable! Modify the Kconfig and makefile in the kernel file to compile these two C files.
The basic process is:

In w5100-spi.c, we mainly use spi to configure w5500 read and write functions. The w5100_spi_probe function is eventually ported to the w5100_probe function in w5100.c. The following focuses on the w5100_spi_probe() function:

int w5100_probe(struct device *dev, const struct w5100_ops *ops,int sizeof_ops_priv, const void *mac_addr, int irq,int link_gpio)
  • 1

1. After entering this function, first allocate the registered network device
ndev = alloc_etherdev(alloc_size);
err = register_netdev(ndev);

2. Complete the network device file operation structure
ndev->netdev_ops = &w5100_netdev_ops;

const struct net_device_ops w5100_netdev_ops static = {
.ndo_open = w5100_open,
.ndo_stop = w5100_stop,
.ndo_start_xmit = w5100_start_tx,
.ndo_tx_timeout = w5100_tx_timeout,
.ndo_set_rx_mode = w5100_set_rx_mode,
.ndo_set_mac_address = w5100_set_macaddr,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
};
Complete the network device file operations, open, stop, .ndo_start_xmit and other functions, it can be seen that the network device data transmission function is w5100_start_tx

3. Complete the ethtool_ops structure
ndev->ethtool_ops = &w5100_ethtool_ops;

  static const struct ethtool_ops w5100_ethtool_ops = {
.get_drvinfo        = w5100_get_drvinfo,
.get_msglevel       = w5100_get_msglevel,
.set_msglevel       = w5100_set_msglevel,
.get_link       = w5100_get_link,
.get_regs_len       = w5100_get_regs_len,
.get_regs       = w5100_get_regs,

};
The ethtool_ops member function corresponds to the various command options of the user space ethol tool. ethtool provides network card and network card driver management capabilities, which can provide Linux network developers and administrators with network access. card hardware and drivers, and network protocol stack configuration, display and debugging functions.

4、 netif_napi_add(ndev, &priv->napi, w5100_napi_poll, 16);
This function is used to initialize a NAPI, the poll parameter of netif_napi_add() is the poll function that will be scheduled and executed by NAPI
Data reception process:

If it is a NAPI-compliant device driver, the data packet is received via poll. In this case, we need to provide the device driver with the w5100_napi_poll function as the netif_napi_add() parameter:

static int w5100_napi_poll(struct napi_struct *napi, int budget)
{
          struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
    int rx_count;

    for (rx_count = 0; rx_count < budget; rx_count++) {
        struct sk_buff *skb = w5100_rx_skb(priv->ndev); // Obtener los datos de recibir skb

        if (skb)
            netif_receive_skb(skb);// Analizar los datos en el búfer de recepción de la red
        else
            break;
    }

    if (rx_count < budget) {
        napi_complete(napi);// Elimina el dispositivo napi de la lista de sondeo
        w5100_enable_intr(priv); // Habilitar interrupción
    }

    return rx_count;
}

 

The method of receiving data is NAPI (New API) In short, NAPI is a technology that integrates interrupt and poll methods.

Its data receiving process is:
Receive Interrupt is coming —— “Receive Interrupt Disable ——” Receive all data packets in polling mode until empty —— “Receive Interrupt Enable ——” Reception outage is coming

Register interrupt function:
err = request_irq(priv->irq, w5100_interrupt,IRQF_TRIGGER_LOW, ndev->name, ndev);
priv-> irq: hardware interrupt number to apply
w5100_interrupt: The interrupt processing function registered with the system is a callback function. When an interrupt occurs, this function is called by the system and passed the parameter ndev
IRQF_TRIGGER_LOW: Specified interrupt trigger type: active low
ndev-> name: the name of the device driver
ndev: The interrupt name can be used as an interrupt-distinguishing parameter when sharing an interrupt, or it can be used to specify the data address that the interrupt service function should refer to

Interrupt function:

static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
{
struct net_device *ndev = ndev_instance;
struct w5100_priv *priv = netdev_priv(ndev);

int ir = w5100_read(priv, W5100_S0_IR(priv));
if (!ir)
return IRQ_NONE;
w5100_write(priv, W5100_S0_IR(priv), ir);

if (ir & S0_IR_SENDOK) {
//      netif_dbg(priv, tx_done, ndev, "tx donen");
netif_wake_queue(ndev);
}

if (ir & S0_IR_RECV) {
w5100_disable_intr(priv);

if (priv->ops->may_sleep)
queue_work(priv->xfer_wq, &priv->rx_work); // Programa para ejecutar una tarea en una cola de trabajo específica. Parámetros de entrada:
 @ workqueue_struct: el puntero de la cola de trabajo especificado
 @work_struct: puntero a un objeto de tarea específico

else if (napi_schedule_prep(&priv->napi)) // Verifica si es posible programar napi
__napi_schedule(&priv->napi);// Llame a napi_schedule () en la función de procesamiento de interrupciones del controlador de la tarjeta de red para usar NAPI
}

return IRQ_HANDLED;
}

5. Create a network card work queue
priv->xfer_wq = alloc_workqueue(netdev_name(ndev), WQ_MEM_RECLAIM, 0);

Add four tasks to the job queue
INIT_WORK(&priv->rx_work, w5100_rx_work);
INIT_WORK(&priv->tx_work, w5100_tx_work);
INIT_WORK(&priv->setrx_work, w5100_setrx_work);
INIT_WORK(&priv->restart_work, w5100_restart_work);

INIT_WORK will add a work task to the _work queue you defined, which is _func. The _func task will require some data as a parameter, and this parameter is passed through _data.
⑴w5100_rx_work() function:
w5100_rx_skb():
①skb = netdev_alloc_skb_ip_align(ndev, rx_len);
netdev_alloc_skb_ip_align will request a sk_buff structure and at the same time request buffer space to store message data and associate
it ②skb_put(skb, rx_len);
skb_put() modifies the tail of the pointer pointing to the end of the data area to move down by len bytes, even if the data area expands down by len bytes, and the length of the len data area is updated, normally This function is called during device driver receive data processing.
③skb->protocol = eth_type_trans(skb, ndev);
The Linux kernel uses eth_type_trans to determine the type of data frame and the type of protocol.

w5100_enable_intr (priv): enable interrupt

w5100_tx_work() function: w5100_tx_skb, send function to write skb, and finally dev_kfree_skb (skb) frees the socket buffer.

⑶w5100_setrx_work(struct work_struct *work)
w5100_hw_start (priv): hardware start w5500
u8 mode = S0_MR_MACRAW; // Configure w5500 as a raw socket, here’s the key!

⑷w5100_restart_work
w5100_restart:
①netif_stop_queue(ndev):
Call the function netif_stop_queue() provided by the Linux kernel to prevent the device from transmitting packets.
②w5100_hw_reset(priv);
Set mac address for w5500 in function to initialize socket0
③netif_wake_queue(ndev);
When the busy data packet is sent, in the interrupt processing that ends in TX, netif_wake_queue (ndev) must be called to wake up the blocked upper layer to start it and continue driving the network device transfer data packets.

6. Send data
function In the w5100_netdev_ops function. ndo_start_xmit = w5100_start_tx, so w5100_start_tx() is the network card send data function

static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
{
    struct w5100_priv *priv = netdev_priv(ndev);

    netif_stop_queue(ndev);// Detener el paquete de transmisión del dispositivo

    if (priv->ops->may_sleep) {
        WARN_ON(priv->tx_skb);
        priv->tx_skb = skb;
        queue_work(priv->xfer_wq, &priv->tx_work);// Abrir la tarea de envío de datos
    } else {
        w5100_tx_skb(ndev, skb);
    }

    return NETDEV_TX_OK;
}
static void w5100_tx_skb(struct net_device *ndev, struct sk_buff *skb)
{
    struct w5100_priv *priv = netdev_priv(ndev);
    u16 offset;

    offset = w5100_read16(priv, W5100_S0_TX_WR(priv));// Leer socket0 enviar offset de búfer de w5500
    w5100_writebuf(priv, offset, skb->data, skb->len);
    w5100_write16(priv, W5100_S0_TX_WR(priv), offset + skb->len);// Escribe datos en w5500
    ndev->stats.tx_bytes += skb->len;
    ndev->stats.tx_packets++; // Registra la cantidad de paquetes enviados
    dev_kfree_skb(skb);

    w5100_command(priv, S0_CR_SEND);
}

The effect of the final execution:


Set the w5500 network card IP address to 192.168.1.88, use the host computer to ping, you can ping through. If the tcp/ip protocol stack is added to the linux kernel, you can connect to the host server connect tcp and udp, but instead of using the original hardware tcp/ip protocol stack of w5500, it bypasses it directly. What we notice is that the network device driver of w5500 is configured in the form of raw socket, so we should pay attention to the raw socket transmission in the network protocol in the future! !

ucc_march2.PNG

The w5500 driver written in the above article is just a pure application driver. Although it can achieve certain purposes, it does not make full use of the Linux kernel. It seems obsolete in some applications. So I learned about the w5500 network device kernel driver Fortunately, the file for the w5500 network device driver was found in Linux kernel version 4.8, but some functions and data structures are missing to some extent with the version 2.6.33 of the kernel I’m using. It took a long time for this I went to patch the C driver file, and finally patched it to the extent that the build was bug free, but it took a certain period of debugging to achieve the desired effect, but through this, too I learned the knowledge of linux network equipment.
Here is a simple analysis and summary of some important functions and data structures driven by w5500 network devices
The W5500 network device driver is mainly included in two files: w5500.c with w5500-spi.c
w5100-spi.c compiles the spi device driver to use the spi interface to configure w5500, configures the w5500 basic read and write functions, w5100.c compiles mainly the linux kernel network device driver and establishes the network send and receive skb and other functions, pay attention to the two closely connected, one is indispensable! Modify the Kconfig and makefile in the kernel file to compile these two C files.
The basic process is:

In w5100-spi.c, we mainly use spi to configure w5500 read and write functions. The w5100_spi_probe function is eventually ported to the w5100_probe function in w5100.c. The following focuses on the w5100_spi_probe() function:

int w5100_probe(struct device *dev, const struct w5100_ops *ops,int sizeof_ops_priv, const void *mac_addr, int irq,int link_gpio)
  • 1

1. After entering this function, first allocate the registered network device
ndev = alloc_etherdev(alloc_size);
err = register_netdev(ndev);

2. Complete the network device file operation structure
ndev->netdev_ops = &w5100_netdev_ops;

const struct net_device_ops w5100_netdev_ops static = {
.ndo_open = w5100_open,
.ndo_stop = w5100_stop,
.ndo_start_xmit = w5100_start_tx,
.ndo_tx_timeout = w5100_tx_timeout,
.ndo_set_rx_mode = w5100_set_rx_mode,
.ndo_set_mac_address = w5100_set_macaddr,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
};
Complete the network device file operations, open, stop, .ndo_start_xmit and other functions, it can be seen that the network device data transmission function is w5100_start_tx

3. Complete the ethtool_ops structure
ndev->ethtool_ops = &w5100_ethtool_ops;

  static const struct ethtool_ops w5100_ethtool_ops = {
.get_drvinfo        = w5100_get_drvinfo,
.get_msglevel       = w5100_get_msglevel,
.set_msglevel       = w5100_set_msglevel,
.get_link       = w5100_get_link,
.get_regs_len       = w5100_get_regs_len,
.get_regs       = w5100_get_regs,

};
The ethtool_ops member function corresponds to the various command options of the user space ethol tool. ethtool provides network card and network card driver management capabilities, which can provide Linux network developers and administrators with network access. card hardware and drivers, and network protocol stack configuration, display and debugging functions.

4、 netif_napi_add(ndev, &priv->napi, w5100_napi_poll, 16);
This function is used to initialize a NAPI, the poll parameter of netif_napi_add() is the poll function that will be scheduled and executed by NAPI
Data reception process:

If it is a NAPI-compliant device driver, the data packet is received via poll. In this case, we need to provide the device driver with the w5100_napi_poll function as the netif_napi_add() parameter:

static int w5100_napi_poll(struct napi_struct *napi, int budget)
{
          struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
    int rx_count;

    for (rx_count = 0; rx_count < budget; rx_count++) {
        struct sk_buff *skb = w5100_rx_skb(priv->ndev); // Obtener los datos de recibir skb

        if (skb)
            netif_receive_skb(skb);// Analizar los datos en el búfer de recepción de la red
        else
            break;
    }

    if (rx_count < budget) {
        napi_complete(napi);// Elimina el dispositivo napi de la lista de sondeo
        w5100_enable_intr(priv); // Habilitar interrupción
    }

    return rx_count;
}

 

The method of receiving data is NAPI (New API) In short, NAPI is a technology that integrates interrupt and poll methods.

Its data receiving process is:
Receive Interrupt is coming —— “Receive Interrupt Disable ——” Receive all data packets in polling mode until empty —— “Receive Interrupt Enable ——” Reception outage is coming

Register interrupt function:
err = request_irq(priv->irq, w5100_interrupt,IRQF_TRIGGER_LOW, ndev->name, ndev);
priv-> irq: hardware interrupt number to apply
w5100_interrupt: The interrupt processing function registered with the system is a callback function. When an interrupt occurs, this function is called by the system and passed the parameter ndev
IRQF_TRIGGER_LOW: Specified interrupt trigger type: active low
ndev-> name: the name of the device driver
ndev: The interrupt name can be used as an interrupt-distinguishing parameter when sharing an interrupt, or it can be used to specify the data address that the interrupt service function should refer to

Interrupt function:

static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
{
struct net_device *ndev = ndev_instance;
struct w5100_priv *priv = netdev_priv(ndev);

int ir = w5100_read(priv, W5100_S0_IR(priv));
if (!ir)
return IRQ_NONE;
w5100_write(priv, W5100_S0_IR(priv), ir);

if (ir & S0_IR_SENDOK) {
//      netif_dbg(priv, tx_done, ndev, "tx donen");
netif_wake_queue(ndev);
}

if (ir & S0_IR_RECV) {
w5100_disable_intr(priv);

if (priv->ops->may_sleep)
queue_work(priv->xfer_wq, &priv->rx_work); // Programa para ejecutar una tarea en una cola de trabajo específica. Parámetros de entrada:
 @ workqueue_struct: el puntero de la cola de trabajo especificado
 @work_struct: puntero a un objeto de tarea específico

else if (napi_schedule_prep(&priv->napi)) // Verifica si es posible programar napi
__napi_schedule(&priv->napi);// Llame a napi_schedule () en la función de procesamiento de interrupciones del controlador de la tarjeta de red para usar NAPI
}

return IRQ_HANDLED;
}

5. Create a network card work queue
priv->xfer_wq = alloc_workqueue(netdev_name(ndev), WQ_MEM_RECLAIM, 0);

Add four tasks to the job queue
INIT_WORK(&priv->rx_work, w5100_rx_work);
INIT_WORK(&priv->tx_work, w5100_tx_work);
INIT_WORK(&priv->setrx_work, w5100_setrx_work);
INIT_WORK(&priv->restart_work, w5100_restart_work);

INIT_WORK will add a work task to the _work queue you defined, which is _func. The _func task will require some data as a parameter, and this parameter is passed through _data.
⑴w5100_rx_work() function:
w5100_rx_skb():
①skb = netdev_alloc_skb_ip_align(ndev, rx_len);
netdev_alloc_skb_ip_align will request a sk_buff structure and at the same time request buffer space to store message data and associate
it ②skb_put(skb, rx_len);
skb_put() modifies the tail of the pointer pointing to the end of the data area to move down by len bytes, even if the data area expands down by len bytes, and the length of the len data area is updated, normally This function is called during device driver receive data processing.
③skb->protocol = eth_type_trans(skb, ndev);
The Linux kernel uses eth_type_trans to determine the type of data frame and the type of protocol.

w5100_enable_intr (priv): enable interrupt

w5100_tx_work() function: w5100_tx_skb, send function to write skb, and finally dev_kfree_skb (skb) frees the socket buffer.

⑶w5100_setrx_work(struct work_struct *work)
w5100_hw_start (priv): hardware start w5500
u8 mode = S0_MR_MACRAW; // Configure w5500 as a raw socket, here’s the key!

⑷w5100_restart_work
w5100_restart:
①netif_stop_queue(ndev):
Call the function netif_stop_queue() provided by the Linux kernel to prevent the device from transmitting packets.
②w5100_hw_reset(priv);
Set mac address for w5500 in function to initialize socket0
③netif_wake_queue(ndev);
When the busy data packet is sent, in the interrupt processing that ends in TX, netif_wake_queue (ndev) must be called to wake up the blocked upper layer to start it and continue driving the network device transfer data packets.

6. Send data
function In the w5100_netdev_ops function. ndo_start_xmit = w5100_start_tx, so w5100_start_tx() is the network card send data function

static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
{
    struct w5100_priv *priv = netdev_priv(ndev);

    netif_stop_queue(ndev);// Detener el paquete de transmisión del dispositivo

    if (priv->ops->may_sleep) {
        WARN_ON(priv->tx_skb);
        priv->tx_skb = skb;
        queue_work(priv->xfer_wq, &priv->tx_work);// Abrir la tarea de envío de datos
    } else {
        w5100_tx_skb(ndev, skb);
    }

    return NETDEV_TX_OK;
}
static void w5100_tx_skb(struct net_device *ndev, struct sk_buff *skb)
{
    struct w5100_priv *priv = netdev_priv(ndev);
    u16 offset;

    offset = w5100_read16(priv, W5100_S0_TX_WR(priv));// Leer socket0 enviar offset de búfer de w5500
    w5100_writebuf(priv, offset, skb->data, skb->len);
    w5100_write16(priv, W5100_S0_TX_WR(priv), offset + skb->len);// Escribe datos en w5500
    ndev->stats.tx_bytes += skb->len;
    ndev->stats.tx_packets++; // Registra la cantidad de paquetes enviados
    dev_kfree_skb(skb);

    w5100_command(priv, S0_CR_SEND);
}

The effect of the final execution:


Set the w5500 network card IP address to 192.168.1.88, use the host computer to ping, you can ping through. If the tcp/ip protocol stack is added to the linux kernel, you can connect to the host server connect tcp and udp, but instead of using the original hardware tcp/ip protocol stack of w5500, it bypasses it directly. What we notice is that the network device driver of w5500 is configured in the form of raw socket, so we should pay attention to the raw socket transmission in the network protocol in the future! !
documents
Code
W5100.c
c file for w5100
w5100_spi.c
c file for w5100-spi

COMMENTS

Please Login to comment
  Subscribe  
Notify of
POSTED BY
Reusable S/W