iwpriv 兼容高版本内核
# 问题
最近在 ubuntu16.04 上调 MTK 的 WiFi 驱动,发现 iwpriv 命令不能正常使用,现象如下:
wifi_group@ubuntu16:~/liyongjun/MT7615_onPC_Lq$ iwpriv rao
rao no private ioctls.
修改驱动,使 CONFIG_APSTA_MIXED_SUPPORT
宏生效,iwpriv 能够列出功能参数,但是具体功能仍然无法正常使用,现象如下:
wifi_group@ubuntu16:~/liyongjun/MT7615_onPC_Lq$ iwpriv ra0
ra0 Available private ioctls :
set (8BE2) : set 1536 char & get 0
show (8BF1) : set 1024 char & get 0
get_site_survey (8BED) : set 1024 char & get 1024 char
set_wsc_oob (8BF9) : set 1024 char & get 1024 char
get_mac_table (8BEF) : set 1024 char & get 1024 char
e2p (8BE7) : set 1024 char & get 1024 char
bbp (8BE3) : set 1024 char & get 1024 char
mac (8BE5) : set 1024 char & get 1024 char
rf (8BF3) : set 1024 char & get 1024 char
get_wsc_profile (8BF2) : set 1024 char & get 1024 char
get_ba_table (8BF6) : set 1024 char & get 1024 char
stat (8BE9) : set 1024 char & get 1024 char
rx (8BFB) : set 1024 char & get 1024 char
wifi_group@ubuntu16:~/liyongjun/MT7615_onPC_Lq$ iwpriv ra0 set
Interface doesn't accept private ioctl...
set (8BE2): Operation not permitted
wifi_group@ubuntu16:~/liyongjun/MT7615_onPC_Lq$ sudo iwpriv ra0 set
Interface doesn't accept private ioctl...
set (8BE2): Operation not supported
# 追寻
同样的驱动在 ubuntu14.04 中可以正常使用 iwpriv,由此,怀疑是内核版本问题。
ubuntu14.04 内核版本是 4.4.0
ubuntu16.04 内核版本是 4.15.0
比较两份内核代码的差异,只需比较 net/ 部分即可
并通过函数调用追溯
发现 net/wireless/wext-core.c 中,函数 wireless_process_ioctl 在 4.4.0 kernel 中为:
/*
* Main IOCTl dispatcher.
* Check the type of IOCTL and call the appropriate wrapper...
*/
static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
unsigned int cmd,
struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{
struct iwreq *iwr = (struct iwreq *) ifr;
struct net_device *dev;
iw_handler handler;
/* Permissions are already checked in dev_ioctl() before calling us.
* The copy_to/from_user() of ifr is also dealt with in there */
/* Make sure the device exist */
if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL)
return -ENODEV;
/* A bunch of special cases, then the generic case...
* Note that 'cmd' is already filtered in dev_ioctl() with
* (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
if (cmd == SIOCGIWSTATS)
return standard(dev, iwr, cmd, info,
&iw_handler_get_iwstats);
#ifdef CONFIG_WEXT_PRIV
if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
return standard(dev, iwr, cmd, info,
iw_handler_get_private);
#endif
/* Basic check */
if (!netif_device_present(dev))
return -ENODEV;
/* New driver API : try to find the handler */
handler = get_handler(dev, cmd);
if (handler) {
/* Standard and private are not the same */
if (cmd < SIOCIWFIRSTPRIV)
return standard(dev, iwr, cmd, info, handler);
else if (private)
return private(dev, iwr, cmd, info, handler);
}
/* Old driver API : call driver ioctl handler */
if (dev->netdev_ops->ndo_do_ioctl)
return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
return -EOPNOTSUPP;
}
在 4.15.0 kernel 中为:
/*
* Main IOCTl dispatcher.
* Check the type of IOCTL and call the appropriate wrapper...
*/
static int wireless_process_ioctl(struct net *net, struct iwreq *iwr,
unsigned int cmd,
struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{
struct net_device *dev;
iw_handler handler;
/* Permissions are already checked in dev_ioctl() before calling us.
* The copy_to/from_user() of ifr is also dealt with in there */
/* Make sure the device exist */
if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL)
return -ENODEV;
/* A bunch of special cases, then the generic case...
* Note that 'cmd' is already filtered in dev_ioctl() with
* (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
if (cmd == SIOCGIWSTATS)
return standard(dev, iwr, cmd, info,
&iw_handler_get_iwstats);
#ifdef CONFIG_WEXT_PRIV
if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
return standard(dev, iwr, cmd, info,
iw_handler_get_private);
#endif
/* Basic check */
if (!netif_device_present(dev))
return -ENODEV;
/* New driver API : try to find the handler */
handler = get_handler(dev, cmd);
if (handler) {
/* Standard and private are not the same */
if (cmd < SIOCIWFIRSTPRIV)
return standard(dev, iwr, cmd, info, handler);
else if (private)
return private(dev, iwr, cmd, info, handler);
}
return -EOPNOTSUPP;
}
去掉了老的 API
/* Old driver API : call driver ioctl handler */
if (dev->netdev_ops->ndo_do_ioctl)
return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
不知道它为什么这样做,从 iwpriv 返回的结果也可以看出,这里直接返回了 EOPNOTSUPP
,因为没有相应的 ioctl 去接手。
通过 strace 也能追踪到相应出错位置
strace iwpriv ra0
……
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
brk(NULL) = 0x563a1154a000
brk(0x563a1156b000) = 0x563a1156b000
ioctl(3, SIOCGIWPRIV, 0x7ffc2176f390) = -1 EOPNOTSUPP (不支持的操作)
……
# 解决
把新版内核中删掉的部分再给他加回去,如下:
/*
* Main IOCTl dispatcher.
* Check the type of IOCTL and call the appropriate wrapper...
*/
static int wireless_process_ioctl(struct net *net, struct iwreq *iwr,
unsigned int cmd,
struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{
struct net_device *dev;
iw_handler handler;
/* Permissions are already checked in dev_ioctl() before calling us.
* The copy_to/from_user() of ifr is also dealt with in there */
/* Make sure the device exist */
if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL)
return -ENODEV;
/* A bunch of special cases, then the generic case...
* Note that 'cmd' is already filtered in dev_ioctl() with
* (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
if (cmd == SIOCGIWSTATS)
return standard(dev, iwr, cmd, info,
&iw_handler_get_iwstats);
#ifdef CONFIG_WEXT_PRIV
if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
return standard(dev, iwr, cmd, info,
iw_handler_get_private);
#endif
/* Basic check */
if (!netif_device_present(dev))
return -ENODEV;
/* New driver API : try to find the handler */
handler = get_handler(dev, cmd);
if (handler) {
/* Standard and private are not the same */
if (cmd < SIOCIWFIRSTPRIV)
return standard(dev, iwr, cmd, info, handler);
else if (private)
return private(dev, iwr, cmd, info, handler);
}
// add begin
/* Old driver API : call driver ioctl handler */
if (dev->netdev_ops->ndo_do_ioctl)
return dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)iwr, cmd);
// add end
return -EOPNOTSUPP;
}
注意:ndo_do_ioctl 的第二个参数 ifr 要改为 (struct ifreq *)iwr。