SNULL指Simple Network Utility for Loading Localities,是区域装载的简单网络工具,可以用来实现串口上网。
SNULL的优点在于不和任何硬件相关,而只是操作从内核分配的一些内存。所以在任何操作系统上都能实现而不用在驱动里进行硬件配置 ,具有很好的移植性。易于理解,可以对照网卡DM9000驱动程序和LDD十七章学习。
net_device结构体
net_devic结构体描述了网络设备数据和操作。部分定义如下:
其中的open、stop就对结构体成员进行操作。priv是一个私有的数据指针,具有举足轻重的作用。它的获取是使用函数
net_device *dev;
dev->priv = netdev_priv(dev)
获取。或者类似DM9000写为:
static struct ednet_priv *GetPrivate(struct net_device *dev)
{
return netdev_priv(dev);
}
其中ednet_priv是自己定义的来封装所需私有数据的结构体:
struct ednet_priv {
struct net_device_stats stats;
struct sk_buff *skb;
spinlock_t lock;
};
相当于强制类型转换返回ednet_priv类型的指针(因为priv指针是void*类型,等着用户重定义),它指向net_device的私有数据priv区。
不能直使用dev.priv。具体原因,LDD上说法是:
priv成员的作用和字符驱动程序中的private_data指针作用类似。priv指针是于net_device结构一起分配的,出于性能和灵活性方面的考虑,不鼓励直接访问priv成员。
查看netdev_priv源代码:
static inline void *netdev_priv(const struct net_device *dev)
{
return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);
}
priv虽然是net_device的成员,但内存分配的时候其实是紧挨着net_device结构体的,因而上面这个函数返回net_device的末地址并且要满足字节对齐,也就是priv的首地址。
SNULL
SNULL参见LDD书及其源代码[7]。NULL驱动编译后ftp传送到MINI2440上,加载后ifconfig
结果如下:
sn0和sn1是两个虚拟网络设备,它们获得了局域网IP地址和其他配置信息。
NetRouter
把SNULL(相当于一个没有硬件实现的虚拟网卡)中再加入两个字符设备(相当于buffer),用来存储网卡的收发数据,实现串口上网的功能。至于为什么要加入字符设备来作buffer而不是直接在SNULL中读写串口数据,这是因为在驱动层无法类似调用read(fd,buffer,len)
,只能想一个中转数据的方法。把数据在应用层再用一个应用程序fd=open("/dev/ttySAC1",O_REWR | O_NOCTTY
读写到串口驱动里。
加载驱动ed0如下:
当上层应用需要传输数据包时,内核会自动执行下面的流程调用来从串口发出数据。画图采用可以在线协作的ProcessOn。
.ndo_start_xmit定义在net_device_ops操作函数集合结构体中,绑定到内核。
ed[ED_TX_DEVICE]是定义的作为接受缓冲区的字符设备,kernel_write相当于它的一个方法。
memcpy把驱动读到的来自上层的数据拷贝到字符设备私有buffer中。
TBD
Reference
[1].http://my.oschina.net/lvyi/blog/325785
[2].http://blog.sina.com.cn/s/blog_478ef86301007vq7.html
[3].LDD.Page491~509
[4].http://blog.csdn.net/npy_lp/article/details/7090541
[5].http://blog.csdn.net/fareast8612/article/details/7480246
[6].http://www.ibm.com/developerworks/cn/linux/l-serialnet/
[7].https://github.com/yql612679/linux-drive