PHY State detection and control of NIC under Linux
This article turns from: http://blog.sina.com.cn/s/blog_79453a7e0100p9t7.html
Recently, in a project, the integration of a switch chip, encountered some trouble, found that the performance of the switch is always on
No, 100M switch, the actual exchange capacity is only 10M. With the hardware colleagues, spent a few weeks debugging,
Only to find the problem. It turns out to be a few subsystems on the switch chip, using the Micrel 8041PHY chip, which is closed by default
The hardware flow control, resulting in the switch can not control the network data exchange through the flow control, the result makes its performance drop. and exchange
Machine each port PHY and subsystem PHY are used by the auto negotiation to negotiate link state, subsystem default does not
Stream control is supported, and the switch also shuts down the flow control.
The solution is actually very simple, is to open the PHY on the subsystem side of the flow control, in fact, is a register of a bit,
It took us a couple of weeks. The reason is that most of the PHY chip by default are open flow control, so we
At first there was no doubt about it, and there was no control port for the switch to view its status.
Subsystems are running embedded Linux systems, so during these weeks of debugging, focus on looking at the Linux kernel
PHY driver, as well as some related system calls. Some experience, record down.
The Linux system provides two types of IOCTL system calls Siocethtool and siocxmiixxx to control or obtain the NIC
The state of the PHY. The implementation of these two kinds of system calls depends on the implementation of the corresponding IOCTL in the PHY driver, and the general PHY drive
Will achieve at least one of the categories. Below to get the link state of the network card to illustrate the use of these two types of system calls.
0. Create Socket Handler
Since both types of calls are to use the socket handler, we first create a socket handler for the following system
Call.
/*--------------------------------------------------------------------------*/
/**
@brief detect ETH link status from Ethtool API and MII Reg.
@param ifname--Interface name.
@return return True if link was up, return False if link was down,
or return-1 if errors occur.
This function would the ETH link status from Ethtool API.
If it fails, it would try to get ETH link status from MII Reg.
*/
/*--------------------------------------------------------------------------*/
int Get_netlink_status (char *ifname)
{
int SKFD =-1;
int link_status;
if (ifname = = NULL) {
ifname = "eth0";
}
if ((SKFD = socket (af_inet, SOCK_DGRAM, 0)) < 0) {
Nl_err ("Socket error\n");
return-1;
}
Link_status = Detect_ethtool (SKFD, ifname);
if (Link_status < 0)
Link_status = Detect_mii (SKFD, ifname);
Close (SKFD);
return link_status;
}
1. Siocethtool IOCTL
The following is the use of siocethtoll system calls.
/*--------------------------------------------------------------------------*/
/**
@brief detect ETH link status from Ethtool API.
@param SKFD--socket handler.
@param ifname--Interface name.
@return return True if link was up, return False if link was down,
or return-1 if errors occur.
*/
/*--------------------------------------------------------------------------*/
int detect_ethtool (int skfd, char *ifname)
{
struct Ifreq IFR;
struct Ethtool_value edata;
memset (&IFR, 0, sizeof (IFR));
Edata.cmd = Ethtool_glink;
strncpy (Ifr.ifr_name, ifname, sizeof (Ifr.ifr_name)-1);
Ifr.ifr_data = (char *) &edata;
if (IOCTL (SKFD, Siocethtool, &ifr) = = 1) {
Nl_err ("Ethtool_glink failed:%s\n", Strerror (errno));
return-1;
}
Return (Edata.data 1:0);
}
corresponding to the Dev_ioctl () function in the kernel code [LINUX/NET/CORE/DEV.C], the corresponding Siocethtool is called
The implementation in [LINUX/NET/CORE/ETHTOOL.C], and the Dev_ethtool () function, which will eventually call the
A function registered in the corresponding if-driven ethtool_ops structure to implement the register operation. Of course, the different PHY drivers
There are different operations on this, and even some PHY drivers do not register the structure at all, at which point we will try the following
The way out.
2. SIOCXMIIXXX IOCTL
The following is the use of siocxmiixxx system calls.
/*--------------------------------------------------------------------------*/
/**
@brief detect ETH link status from MII status Reg.
@param SKFD--socket handler.
@param ifname--Interface name.
@return return True if link was up, return False if link was down,
or return-1 if errors occur.
*/
/*--------------------------------------------------------------------------*/
static int detect_mii (int skfd, char *ifname)
{
struct Ifreq IFR;
struct Mii_ioctl_data *mii_val;
strncpy (Ifr.ifr_name, ifname, Ifnamsiz);
if (IOCTL (SKFD, siocgmiiphy, &IFR) < 0) {
fprintf (stderr, "Siocgmiiphy on%s failed:%s\n", ifname,
Strerror (errno));
(void) Close (SKFD);
return-1;
}
Mii_val = (struct Mii_ioctl_data *) (&ifr.ifr_data);
Mii_val->reg_num = MII_BMSR;
if (IOCTL (SKFD, Siocgmiireg, &IFR) < 0) {
Nl_err ("Siocgmiireg on%s failed:%s\n", Ifr.ifr_name,
Strerror (errno));
return-1;
}
if ((!) ( Mii_val->val_out & Bmsr_rfault)) &&
(Mii_val->val_out & Bmsr_lstatus) &&
(! (Mii_val->val_out & BMSR_JCD)) {
return 1;
} else {
return 0;
}
}
corresponding to the Dev_ioctl () function in the kernel code [LINUX/NET/CORE/DEV.C], the corresponding siocxmiixxx is called
The DEV_IFSIOC () function, which eventually invokes the Do_ioctl () function registered in the PHY driver to implement the PHY register
Write.