Zephyr网络传输Offloading--概览

Creative Commons
本作品采用知识共享署名

本文简单介绍Zephyr网络传输offloading的架构。

本文说明网络传输offloading的大体框架,包含阅读代码的方向,不包括具体实现的分析。后续会有文章对具体实现进行分析。

概述

Zephyr有完备的TCP/IP协议栈,在TCP/IP之上使用socket作为应用层的标准接口。此外Zephyr提供了网络传输offloading框架,将设备供应商的TCP/IP协议栈或者socket挂接到Zephyr内,对于Zephyr应用来说使用使用标准的socket编程后,Zephyr替换了底层的TCP/IP的来源也不用修改应用代码。

架构

下面图示是基于Zephyr官方的网络栈架构图(参考文末链接)修改而来,可以比较清楚的说明offload的架构:

在Zephyr中网络传输offloading有两种方式:

  1. 网络offloading API
  2. Socket offloading API
    下面分别进行介绍

网络offloading

网络offloadin让Zephyr使用设备供应商提供的TCP/IP协议栈。网络连接创建,数据传输等动作是在供应商的HAL中完成,而不是在Zephyr网络协议栈中完成。
上图中蓝色部分是网络offloading的路线,在驱动中使用设备供应商的HAL对struct net_offload中定义的函数指针进行函数实现。注册这些函数后,net_context层就可以使用这些函数来完成网络传输。函数原型如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
struct net_offload {
/**
* This function is called when the socket is to be opened.
*/
int (*get)(sa_family_t family,
enum net_sock_type type,
enum net_ip_protocol ip_proto,
struct net_context **context);

/**
* This function is called when user wants to bind to local IP address.
*/
int (*bind)(struct net_context *context,
const struct sockaddr *addr,
socklen_t addrlen);

/**
* This function is called when user wants to mark the socket
* to be a listening one.
*/
int (*listen)(struct net_context *context, int backlog);

/**
* This function is called when user wants to create a connection
* to a peer host.
*/
int (*connect)(struct net_context *context,
const struct sockaddr *addr,
socklen_t addrlen,
net_context_connect_cb_t cb,
int32_t timeout,
void *user_data);

/**
* This function is called when user wants to accept a connection
* being established.
*/
int (*accept)(struct net_context *context,
net_tcp_accept_cb_t cb,
int32_t timeout,
void *user_data);

/**
* This function is called when user wants to send data to peer host.
*/
int (*send)(struct net_pkt *pkt,
net_context_send_cb_t cb,
int32_t timeout,
void *user_data);

/**
* This function is called when user wants to send data to peer host.
*/
int (*sendto)(struct net_pkt *pkt,
const struct sockaddr *dst_addr,
socklen_t addrlen,
net_context_send_cb_t cb,
int32_t timeout,
void *user_data);

/**
* This function is called when user wants to receive data from peer
* host.
*/
int (*recv)(struct net_context *context,
net_context_recv_cb_t cb,
int32_t timeout,
void *user_data);

/**
* This function is called when user wants to close the socket.
*/
int (*put)(struct net_context *context);
};

esp8266 AT就使用了该种形式,可以参考代码drivers/wifi/esp, 主要是esp.c

Socket offloading

Socket的offloading, 提供设备供应商socket和Zephyr集成方式,让Zephyr可以直接使用设备供应商的socket。上图中黄色部分是socket offloading的路线,在驱动中使用设备供应商的socket对struct socket_op_vtable中定义的函数指针进行函数实现。这一组函数被注册到Zephyr的socket的虚方法表中,在实际socket进行调用时会通虚方法表查找到实际的供应商socket API并执行,要实现虚方法表内的socket的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct socket_op_vtable {
struct fd_op_vtable fd_vtable;
int (*bind)(void *obj, const struct sockaddr *addr, socklen_t addrlen);
int (*connect)(void *obj, const struct sockaddr *addr,
socklen_t addrlen);
int (*listen)(void *obj, int backlog);
int (*accept)(void *obj, struct sockaddr *addr, socklen_t *addrlen);
ssize_t (*sendto)(void *obj, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t (*recvfrom)(void *obj, void *buf, size_t max_len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
int (*getsockopt)(void *obj, int level, int optname,
void *optval, socklen_t *optlen);
int (*setsockopt)(void *obj, int level, int optname,
const void *optval, socklen_t optlen);
ssize_t (*sendmsg)(void *obj, const struct msghdr *msg, int flags);
int (*getsockname)(void *obj, struct sockaddr *addr,
socklen_t *addrlen);
};

Inventek的esWifi就使用了该种形式,可以参考代码drivers/wifi/eswifi, 主要是eswifi_socket_offload.c

参考

https://docs.zephyrproject.org/latest/reference/networking/net_offload.html
https://docs.zephyrproject.org/latest/guides/networking/net-stack-architecture.html