前言:
网卡工作在OSI七层协议中的链路层,是主机与网络对接的重要接口,它完成了网络连接的物理和电信号之间的转换,同时还对网络数据包帧进行发送、接收、校验等,是上层网络应用的基石。在计算机架构中,通常是CPU通过内部总线(AHB BUS 或者 PCI BUS)与MAC控制单元通信,然后由MAC控制单元通过MII接口协议与PHY设备进行数据交互,PHY设备则负责与外部网络通信(RJ45网络接口)。通常将MAC控制单元与PHY设备集成在一起,构成我们所熟悉的网卡(ethn设备),在嵌入式开发中,经常需要获取网卡设备的一些信息,比如说网卡的工作模式、网卡的带宽等等,虽然有现成的ethtool等开源软件来实现,但是对于存储空间有限的嵌入式设备,我们可以通过代码写socket然后ioctl来实现。下图是参考了网友的MAC控制单元与PHY设备的功能框图
网卡信息
先来了解一下网卡设备信息的结构体内容,里面包括了我们所熟知的网卡模式支持,网卡特性、网卡带宽、全双工模式、PHY地址等等网卡信息。
struct ethtool_cmd { __u32 cmd; __u32 supported; /* Features this interface supports */ __u32 advertising; /* Features this interface advertises */ __u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ __u8 duplex; /* Duplex, half or full */ __u8 port; /* Which connector port */ __u8 phy_address; __u8 transceiver; /* Which transceiver to use */ __u8 autoneg; /* Enable or disable autonegotiation */ __u32 maxtxpkt; /* Tx pkts before generating tx int */ __u32 maxrxpkt; /* Rx pkts before generating rx int */ __u32 reserved[4];
};
获取网卡信息的样例如下,它的实现也很简单,首先建立一个UDP Socket,然后对数据进行组装,最后通过ioctl接口来获取网卡的信息。
#include <string.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h> #define SPEED_10 10
#define SPEED_100 100
#define SPEED_1000 1000
#define SPEED_2500 2500
#define SPEED_10000 10000 #ifndef SIOCETHTOOL
#define SIOCETHTOOL 0x8946
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif /* CMDs currently supported */
#define ETHTOOL_GSET 0x00000001 /* Get settings. */
#define ETHTOOL_SSET 0x00000002 /* Set settings. */ typedef __uint32_t __u32; /* ditto */
typedef __uint16_t __u16; /* ditto */
typedef __uint8_t __u8; /* ditto */
/* This should work for both 32 and 64 bit userland. */
struct ethtool_cmd { __u32 cmd; __u32 supported; /* Features this interface supports */ __u32 advertising; /* Features this interface advertises */ __u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ __u8 duplex; /* Duplex, half or full */ __u8 port; /* Which connector port */ __u8 phy_address; __u8 transceiver; /* Which transceiver to use */ __u8 autoneg; /* Enable or disable autonegotiation */ __u32 maxtxpkt; /* Tx pkts before generating tx int */ __u32 maxrxpkt; /* Rx pkts before generating rx int */ __u32 reserved[4];
}; int Get_NetCard_Speed(char *eth_name)
{ struct ifreq ifr, *ifrp; int fd; int ret; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, eth_name); fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("Cannot get control socket"); return -1; } struct ethtool_cmd ep; ep.cmd = ETHTOOL_GSET; // ethtool-copy.h:380:#define ETHTOOL_GSET 0x00000001 /* Get settings. */ ifr.ifr_data = (caddr_t)&ep; ret = ioctl(fd, SIOCETHTOOL, &ifr); if (ret != 0) { printf(" ioctl is erro .\n"); close(fd);return -1; } fprintf(stdout, "%s Speed: ", devname ); switch (ep.speed) { case SPEED_10: fprintf(stdout, "10Mb/s\n"); return SPEED_10;break; case SPEED_100: fprintf(stdout, "100Mb/s\n"); return SPEED_100;break; case SPEED_1000: fprintf(stdout, "1000Mb/s\n"); return SPEED_1000;break; case SPEED_2500: fprintf(stdout, "2500Mb/s\n"); return SPEED_1000;break; case SPEED_10000: fprintf(stdout, "10000Mb/s\n"); return SPEED_10000;break; default: fprintf(stdout, "Unknown! (%i)\n", ep.speed); break; } close(fd);return 0;
} int main(int argc, char* argv[])
{Get_NetCard_Speed(argv[1]); return 0;
}
在Ubuntu16.04下运行的样例打印输出
总结
在一些嵌入式设备中常常需要对网卡设备信息获取,以对设备的网络运行环境有所了解,不过现在的网卡大多数支持全双工,10M/100M/1000M模式自适应,很少再需要用户进行手动干预。最近工作中遇到了MA控制C单元与PHY设备配置的一些问题,参考了一些资料,做了一些整理成此文,以作备忘。
参考文章
http://www.cnblogs.com/chengqi521/p/7837588.html
https://www.cnblogs.com/jason-lu/articles/3195473.html