浅析ethx网卡控制函数ioctl实现具体流程
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
1.
应用层程序iwpriv
wireless tools网络配置应用程序iwpriv命令格式:
iwpriv ethX private
-
command [
parameters]
iwpriv部分实现源码如下:
int
main(
int
argc,
char
*
argv[
]
)
{
.
.
.
sockfd =
socket
(
AF_INET
,
SOCK_STREAM
,
0)
;
.
.
.
ioctl(
sockfd,
ioctl_val,
&
iwr)
;
//将控制命令通过ioctl发送到无线网卡
.
.
.
}
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
2.
系统调用sys_ioctl
应用层通过ioctl(
sockfd,
ioctl_val,
&
iwr)
;
触发sys_ioctl系统调用,
实际流程:
sys_ioctl=
>
vfs_ioctl=
>
do_ioctl=
最后调用
filp-
>
f_op-
>
unlocked_ioctl执行具体的ioctl操作,
该操作就是sock_ioctl,
至于为什么是sock_ioctl,
后边作了进一步分析
sock_ioctl=
>
{
.
.
.
#
ifdef
CONFIG_WIRELESS_EXT
if
(
cmd >
=
SIOCIWFIRST &
&
cmd <
=
SIOCIWLAST)
{
err =
dev_ioctl(
net,
cmd,
argp)
;
//
}
else
#
endif
.
.
.
}
dev_ioctl=
>
wext_handle_ioctl
{
.
.
.
/* Take care of Wireless Extensions */
if
(
cmd >
=
SIOCIWFIRST &
&
cmd <
=
SIOCIWLAST)
return
wext_handle_ioctl(
net,
&
ifr,
cmd,
arg
)
;
.
.
.
}
wext_handle_ioctl=
>
wireless_process_ioctl=
>
然后通过if (
(
dev =
__dev_get_by_name(
net,
ifr-
>
ifr_name)
)
=
=
NULL
)
函数,
从系统管理的net链表中,
把ioctl指定的ethX对应的struct net_device摘出来,
最后调用ioctl_private_call(
handler)
或者调用dev-
>
do_ioctl(
dev,
ifr,
cmd)
来处理该ioctl,
这两个函数分别指向wlan_handler_def和wlan_do_ioctl
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
3.
wifi网卡是怎么登记到kernel上的
wlan_probe(
)
=
>
wlan_add_card(
)
=
>
alloc_etherdev(
)
=
>
之后将操作方法添加到struct net_device *
dev=
alloc_etherdev(
)
申请的dev上去,
其中包括:
.
.
.
/* Setup the OS Interface to our functions */
dev-
>
open
=
wlan_open;
dev-
>
hard_start_xmit =
wlan_hard_start_xmit;
dev-
>
stop =
wlan_close;
dev-
>
do_ioctl =
wlan_do_ioctl;
dev-
>
set_mac_address =
wlan_set_mac_address;
dev-
>
tx_timeout =
wlan_tx_timeout;
dev-
>
get_stats =
wlan_get_stats;
dev-
>
watchdog_timeo =
MRVDRV_DEFAULT_WATCHDOG_TIMEOUT;
dev-
>
wireless_handlers =
(
struct
iw_handler_def *
)
&
wlan_handler_def;
dev-
>
set_multicast_list =
wlan_set_multicast_list;
.
.
.
4.
socket系统调用如何关联上ioctl和ethX设备
asmlinkage long
sys_socket(
int
family,
int
type,
int
protocol)
;
sys_socket=
>
sock_create=
>
__sock_create=
>
sock =
sock_alloc(
)
;
通过sock_mnt-
>
mnt_sb从socket文件系统的超级块上申请一个inode节点,
这样也就同时获得了由该inode描述的一个sock结构体单元,
所以sokcet和dentry目录项等效,
接下来从net_families全局管理结构体中找到当前family对应的ops操作集,
net_proto_family *
pf=
net_families[
family]
;
pf-
>
create(
net,
sock,
protocol)
;
//核心调用,对于ipv4,就是inet_create
以ipv4为例
static
struct
net_proto_family inet_family_ops =
{
.
family =
PF_INET
,
.
create =
inet_create,
.
owner =
THIS_MODULE,
}
;
还记得上面应用层创建sokcet的函数吧,
sockfd =
socket
(
AF_INET
,
SOCK_STREAM
,
0)
;
//AF_INET虽然等于PF_INET,但是因为种种原因我们提倡使用PF_INET
可见family等于AF_INET,
type等于SOCK_STREAM,
协议protocol为0,
也就是采用IP协议,
inet_create=
>
inetsw[
sock-
>
type]
也就是inetsw[
SOCK_STREAM
]
,
从inetsw[
sock-
>
type]
中找到已经登记的protocol网络协议处理函数,
inetsw[
]
是怎么填充的呢?
inet_init(
)
=
>
inet_register_protosw(
inetsw_array)
=
>
这样inetsw_array中的所有protocol处理模块都将登记到inetsw中了,
static
struct
inet_protosw inetsw_array[
]
=
{
{
.
type =
SOCK_STREAM
,
.
protocol =
IPPROTO_TCP
,
.
prot =
&
tcp_prot,
.
ops =
&
inet_stream_ops,
.
capability =
-
1,
.
no_check =
0,
.
flags =
INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
}
,
{
.
type =
SOCK_DGRAM
,
.
protocol =
IPPROTO_UDP
,
.
prot =
&
udp_prot,
.
ops =
&
inet_dgram_ops,
.
capability =
-
1,
.
no_check =
UDP_CSUM_DEFAULT,
.
flags =
INET_PROTOSW_PERMANENT,
}
,
{
.
type =
SOCK_RAW
,
.
protocol =
IPPROTO_IP
,
/* wild card */
.
prot =
&
raw_prot,
.
ops =
&
inet_sockraw_ops,
.
capability =
CAP_NET_RAW,
.
no_check =
UDP_CSUM_DEFAULT,
.
flags =
INET_PROTOSW_REUSE,
}
}
;
至于inet_init,
则是以fs_initcall(
inet_init)
方式,
以5号优先级被build in到了内核中,
当kernel启动时会在start_kernel=
>
rest_init=
>
kernel_init=
>
do_basic_setup=
>
do_initcalls中依据优先级号优先于其他module驱动被调用.
这样sock-
>
ops =
answer-
>
ops;
对于ipv4也就等于inet_stream_ops,
接下来就是将ops填充到file操作指针中了,
sys_socket=
>
sock_map_fd=
>
sock_attach_fd=
>
dentry-
>
d_op =
&
sockfs_dentry_operations;
init_file(
file
,
sock_mnt,
dentry,
FMODE_READ |
FMODE_WRITE,
&
socket_file_ops)
;
file
-
>
private_data =
sock;
其中init_file=
>
file
-
>
f_op =
fop;
也就是file-
>
f_op =
socket_file_ops;
所以read(
)
,
wirte(
)
,
poll(
)
和ioctl(
)
应用程序调用的file-
>
f_op就是socket_file_ops了,
比如:
read
(
)
对应sock_aio_read网络异步读
write
(
)
对应sock_aio_write网络异步写
ioctl(
)
对应sock_ioctl
socket_file_ops结构体具体实现如下:
static
const
struct
file_operations socket_file_ops =
{
.
owner =
THIS_MODULE,
.
llseek =
no_llseek,
.
aio_read =
sock_aio_read,
.
aio_write =
sock_aio_write,
.
poll =
sock_poll,
.
unlocked_ioctl =
sock_ioctl,
#
ifdef
CONFIG_COMPAT
.
compat_ioctl =
compat_sock_ioctl,
#
endif
.
mmap =
sock_mmap,
.
open
=
sock_no_open,
/* special open code to disallow open via /proc */
.
release =
sock_close,
.
fasync =
sock_fasync,
.
sendpage =
sock_sendpage,
.
splice_write =
generic_splice_sendpage,
}
;
网卡控制因为涉及到的知识点比较多,
上面只是从宏观上对数据流程做了一个简单的介绍,
深入到其中的每个知识点,
都会牵扯出一系列文章,
读者需要自己去一个个的慢慢深入,
希望本文能够对刚刚接触网络驱动的读者有所帮助和启发【gliethttp.
Leith】
浅析ethx网卡控制函数ioctl实现具体流程
最新推荐文章于 2023-04-02 20:48:59 发布