zoukankan      html  css  js  c++  java
  • Linux网络设备驱动之设备驱动的注册与注销(二)

      网络设备驱动的注册与注销由 register_netdev( ) 和 unregister_netdev( ) 函数完成,这两个函数的原型为:

    /**
     *    register_netdev    - register a network device
     *    @dev: device to register
     *
     *    Take a completed network device structure and add it to the kernel
     *    interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
     *    chain. 0 is returned on success. A negative errno code is returned
     *    on a failure to set up the device, or if the name is a duplicate.
     *
     *    This is a wrapper around register_netdevice that takes the rtnl semaphore
     *    and expands the device name if you passed a format string to
     *    alloc_netdev.
     */
    int register_netdev(struct net_device *dev)
    {
        int err;
    
        rtnl_lock();
        err = register_netdevice(dev);
        rtnl_unlock();
        return err;
    }
    /**
     *    unregister_netdev - remove device from the kernel
     *    @dev: device
     *
     *    This function shuts down a device interface and removes it
     *    from the kernel tables.
     *
     *    This is just a wrapper for unregister_netdevice that takes
     *    the rtnl semaphore.  In general you want to use this and not
     *    unregister_netdevice.
     */
    void unregister_netdev(struct net_device *dev)
    {
        rtnl_lock();
        unregister_netdevice(dev);
        rtnl_unlock();
    }

      这两个函数都接收一个 net_device 结构体指针为参数,可见 net_device 数据结构在网络设备驱动中的核心地位。

      net_device 的生成和成员的赋值并不一定要由我们亲自动手逐个完成,可以利用如下宏帮助填充:

    #define alloc_netdev(sizeof_priv, name, name_assign_type, setup) 
            alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)
    
    #define alloc_netdev_mq(sizeof_priv, name, name_assign_type, setup, count) 
            alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, count, count)
    
    #define alloc_etherdev(sizeof_priv)
          alloc_etherdev_mq(sizeof_priv, 1) #define alloc_etherdev_mq(sizeof_priv, count)
          alloc_etherdev_mqs(sizeof_priv, count, count)

      alloc_etherdev_mq 宏引用的 alloc_etherdev_mqs( ) 函数的原型为:

    /**
     * alloc_etherdev_mqs - Allocates and sets up an Ethernet device
     * @sizeof_priv: Size of additional driver-private structure to be allocated
     *    for this Ethernet device
     * @txqs: The number of TX queues this device has.
     * @rxqs: The number of RX queues this device has.
     *
     * Fill in the fields of the device structure with Ethernet-generic
     * values. Basically does everything except registering the device.
     *
     * Constructs a new net device, complete with a private data area of
     * size (sizeof_priv).  A 32-byte (not bit) alignment is enforced for
     * this private data area.
     */
    
    struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
                          unsigned int rxqs)
    {
        return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN,
                    ether_setup, txqs, rxqs);
    }

      alloc_netdev 以及 alloc_netdev_mq 宏引用的 alloc_netdev_mqs( ) 函数的原型为:

    /**
     *    alloc_netdev_mqs - allocate network device
     *    @sizeof_priv:        size of private data to allocate space for
     *    @name:            device name format string
     *    @name_assign_type:     origin of device name
     *    @setup:            callback to initialize device
     *    @txqs:            the number of TX subqueues to allocate
     *    @rxqs:            the number of RX subqueues to allocate
     *
     *    Allocates a struct net_device with private data area for driver use
     *    and performs basic initialization.  Also allocates subqueue structs
     *    for each queue on the device.
     */
    struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
            unsigned char name_assign_type,
            void (*setup)(struct net_device *),
            unsigned int txqs, unsigned int rxqs)
    {
        struct net_device *dev;
        size_t alloc_size;
        struct net_device *p;
    
        BUG_ON(strlen(name) >= sizeof(dev->name));
    
        if (txqs < 1) {
            pr_err("alloc_netdev: Unable to allocate device with zero queues
    ");
            return NULL;
        }
    
    #ifdef CONFIG_SYSFS
        if (rxqs < 1) {
            pr_err("alloc_netdev: Unable to allocate device with zero RX queues
    ");
            return NULL;
        }
    #endif
    
        alloc_size = sizeof(struct net_device);
        if (sizeof_priv) {
            /* ensure 32-byte alignment of private area */
            alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
            alloc_size += sizeof_priv;
        }
        /* ensure 32-byte alignment of whole construct */
        alloc_size += NETDEV_ALIGN - 1;
    
        p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
        if (!p)
            p = vzalloc(alloc_size);
        if (!p)
            return NULL;
    
        dev = PTR_ALIGN(p, NETDEV_ALIGN);
        dev->padded = (char *)dev - (char *)p;
    
        dev->pcpu_refcnt = alloc_percpu(int);
        if (!dev->pcpu_refcnt)
            goto free_dev;
    
        if (dev_addr_init(dev))
            goto free_pcpu;
    
        dev_mc_init(dev);
        dev_uc_init(dev);
    
        dev_net_set(dev, &init_net);
    
        dev->gso_max_size = GSO_MAX_SIZE;
        dev->gso_max_segs = GSO_MAX_SEGS;
        dev->gso_min_segs = 0;
    
        INIT_LIST_HEAD(&dev->napi_list);
        INIT_LIST_HEAD(&dev->unreg_list);
        INIT_LIST_HEAD(&dev->close_list);
        INIT_LIST_HEAD(&dev->link_watch_list);
        INIT_LIST_HEAD(&dev->adj_list.upper);
        INIT_LIST_HEAD(&dev->adj_list.lower);
        INIT_LIST_HEAD(&dev->all_adj_list.upper);
        INIT_LIST_HEAD(&dev->all_adj_list.lower);
        INIT_LIST_HEAD(&dev->ptype_all);
        INIT_LIST_HEAD(&dev->ptype_specific);
        dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
        setup(dev);
    
        dev->num_tx_queues = txqs;
        dev->real_num_tx_queues = txqs;
        if (netif_alloc_netdev_queues(dev))
            goto free_all;
    
    #ifdef CONFIG_SYSFS
        dev->num_rx_queues = rxqs;
        dev->real_num_rx_queues = rxqs;
        if (netif_alloc_rx_queues(dev))
            goto free_all;
    #endif
    
        strcpy(dev->name, name);
        dev->name_assign_type = name_assign_type;
        dev->group = INIT_NETDEV_GROUP;
        if (!dev->ethtool_ops)
            dev->ethtool_ops = &default_ethtool_ops;
        return dev;
    
    free_all:
        free_netdev(dev);
        return NULL;
    
    free_pcpu:
        free_percpu(dev->pcpu_refcnt);
    free_dev:
        netdev_freemem(dev);
        return NULL;
    }

      alloc_netdev_mqs( ) 函数生成一个 net_device 结构体,对其成员赋值并返回该结构体的指针。第一个参数为设备私有成员大小,第二个参数为设备名,第三个参数为设备名称来源, 第四个参数为 net_device 的 setup( ) 函数指针,第五、六个参数为要分配的发送和接收子队列的数量。

      setup( ) 接收的参数也为  struct net_device  指针,用于预置 net_device 成员的值。

      free_netdev( ) 完成与 alloc_netdev( ) 和 alloc_etherdev( ) 函数相反的功能,即释放 net_device 结构体的函数:

    /**
     *    free_netdev - free network device
     *    @dev: device
     *
     *    This function does the last stage of destroying an allocated device
     *     interface. The reference to the device object is released.
     *    If this is the last reference then it will be freed.
     */
    void free_netdev(struct net_device *dev)
    {
        struct napi_struct *p, *n;
    
        release_net(dev_net(dev));
    
        netif_free_tx_queues(dev);
    #ifdef CONFIG_SYSFS
        kvfree(dev->_rx);
    #endif
    
        kfree(rcu_dereference_protected(dev->ingress_queue, 1));
    
        /* Flush device addresses */
        dev_addr_flush(dev);
    
        list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
            netif_napi_del(p);
    
        free_percpu(dev->pcpu_refcnt);
        dev->pcpu_refcnt = NULL;
    
        /*  Compatibility with error handling in drivers */
        if (dev->reg_state == NETREG_UNINITIALIZED) {
            netdev_freemem(dev);
            return;
        }
    
        BUG_ON(dev->reg_state != NETREG_UNREGISTERED);
        dev->reg_state = NETREG_RELEASED;
    
        /* will free via device release */
        put_device(&dev->dev);
    }

      net_device 结构体的分配 和 网络设备驱动的注册 需在网络设备驱动程序初始化时进行,而 net_device 结构体的释放 和 网络设备驱动的注销 在设备或驱动被移除的时候执行,如下所示:

    /*  
     *  网络设备驱动程序的注册和注销
     *
     */
    
    static int xxx_register(void)
    {
        ```
        /* 分配 net_device 结构体并对其成员赋值 */
        xxx_dev = alloc_netdev(sizeof(struct xxx_priv), "sn%d", xxx_init);
        if (xxx_dev == NULL)
        ``` /* 分配 net_device 失败 */
    
        /* 注册 net_device 结构体 */
        if ((result = register_netdev(xxx_dev))) 
        ```    
    }
    
    static void xxx_unregister(void)
    {
        ```
        /* 注销 net_device 结构体 */
        unregister_netdev(xxx_dev);
    
        /* 释放 net_device 结构体 */ 
        free_netdev(xxx_dev);
    }   
  • 相关阅读:
    I2C总线驱动框架详解
    Allegro封装的制作
    轮询与中断 简单分析
    SMI#、SCI#信号在OS、BIOS、EC中的中断方式(Linux)
    var
    集合元素重复问题
    子类重写父类属性和方法
    内存(转)
    DesiredSize,RenderSize&& Width ,ActualWidth
    sql select(A.B)拼接
  • 原文地址:https://www.cnblogs.com/wanglouxiaozi/p/13375328.html
Copyright © 2011-2022 走看看