新闻中心

EEPW首页>嵌入式系统>设计应用> Linux串口上网的程序实现方法

Linux串口上网的程序实现方法

作者: 时间:2011-05-10 来源:网络 收藏
操作int device_open(struct inode *inode,struct file *file)是设备节点上的第一个操作,如果多个设备共享这一个操作函数,必须区分设备的设备号。我们使用inode->i_rdev >> 8 语句获得设备的主设备号,本文中的接收设备主设备号是200,发送设备号是201。每个字符设备的file>private_data指向打开设备时候使用的file结构,private_data实际上可以指向用户定义的任何结构,这里只指向我们自己定义的struct ed_device,用来保存字符设备的一些基本信息,比如设备名、内核缓存区等。 操作ssize_t device_read(struct file *file,char *buffer,size_t length, loff_t *offset)是读取设备数据的操作。device_read()结构如图4所示。 图4 从设备中读取数据(用户空间调用read()系统调用)的时候,需要从内核空间把数据拷贝到用户空间,copy_to_user()可完成此功能,它和memcpy()此类函数有本质的区别,memcpy()不能完成不同用户空间数据的交换。如果需要数据临界区的保护,使用spin_lock()内核API负责加锁,spin_unlock()负责解锁,防止数据污染。由于守候进程server需要不断轮询设备,以查询是否有数据可读,如果用户进程不处于休眠状态,在用户空间查看进程使用资源情况,发现server占用了很多CPU资源。所以我们改进device_read(),使之在内核中轮询,当发现当前设备没有数据可读取,那么就阻塞用户进程,使用内核API add_wait_queue()可完成此功能,这时候用户进程并没有占用很多CPU资源,而是处于休眠状态。当内核发现有数据可读的时候,调用remove_wait_queue()即可唤醒等待进程,这段 代码如下: DECLARE_WAITQUEUE(wait,current);

本文引用地址://m.amcfsurvey.com/article/150775.htm

add_wait_queue(edp->rwait,wait);

for(;;){

set_current_state(TASK_INTERRUPTIBLE);

if ( file->f_flags O_NONBLOCK)

break;

/*其他代码 */

if ( signal_pending(current))

break;

schedule();

}

set_current_state(TASK_RUNNING);

remove_wait_queue(edp->rwait,wait);

操作ssize_t device_write(struct file *file,const char *buffer, size_t length,loff_t *offset)向设备写入数据。拷贝数据的copy_from_user()和copy_to_user()的功能恰恰相反,它是从用户空间拷贝数据到内核空间,如图5所示。

图 5

编写伪网络设备驱动

伪网络驱动和字符设备驱动一样,也必须初始化和注册。网络驱动需记录其发送和接收数据量的统计信息,所以我们定义一个记录这些信息的数据结构。

struct ednet_priv {

#ifdef LINUX_24

struct net_device_stats stats;

#else

struct enet_statistics stats;

#endif

struct sk_buff *skb;

spinlock_t lock;

};

struct ednet_priv只有3个数据成员。2.4.x 使用的网络数据状态统计结构是struct net_device_stats,而2.2.x则使用的是struct enet_statistics。同样,对控制网络接口设备的设备结构也有不同的定义:2.4.x使用的是struct net_device,而Linux2.2.x却是struct device。

#ifdef LINUX_24

struct net_device ednet_dev;

#else

struct device ednet_dev;

#endif

伪网络驱动程序的也需要初始化和注册。和字符设备的注册不同之处是,它使用的是register_netdev(net_device *) kernel API。

int ednet_module_init(void)

{

int err;

strcpy(ednet_dev.name, ed0);

ednet_dev.init = ednet_init;

if ( (err = register_netdev(ednet_dev)) )

printk(ednet: error %i registering pseudo network device %sn,

err, ednet_dev.name);

return err;

}

ednet_dev的name域是接口名,ednet_module_init()中赋予网络接口的名字为ed0,如果本网络设备被加载,使用ifconfig命令可以看到ed0。

[root@localhost pku]# /sbin/ifconfig

ed0 Link encap:Ethernet HWaddr 00:45:44:30:30:30

inet addr:192.168.3.9 Bcast:192.168.3.255 Mask:255.255.255.0

UP BROADCAST RUNNING NOARP MULTICAST MTU:1500 Metric:1

RX packets:0 errors:0 dropped:0 overruns:0 frame:0

TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)

我们看到我们的伪网络接口没有Interrupt和Base address,这是因为这个伪网络接口不和硬件打交道,也没有分配中断号和IO基址。否则,如果你看一个实实在在的网络接口(如下面的eth1),可以看到它的Interrupt号是11和IO Base address是0xa000。

eth1 Link encap:Ethernet HWaddr 50:78:4C:43:1D:01

inet addr:192.168.21.202 Bcast:192.168.21.255 Mask:255.255.255.0

UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

RX packets:356523 errors:0 dropped:0 overruns:0 frame:0

TX packets:266 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

RX bytes:21542043 (20.5 Mb) TX bytes:19510 (19.0 Kb)

Interrupt:11 Base address:0xa000

ednet_dev的init域是一个函数指针,指向用户定义的ednet_init()例程。ednet_init()添充net_device结构,只有ednet_init()初始化成功后,系统才被加入到设备链表中。ednet_dev的初始化例程ednet_init()如下:

#ifdef LINUX_24

int ednet_init(struct net_device *dev)

#else

int ednet_init(struct device *dev)

#endif

{

ether_setup(dev);

dev->open = ednet_open;

dev->stop = ednet_release;

dev->hard_start_xmit = ednet_tx;

dev->get_stats = ednet_stats;

dev->change_mtu = ednet_change_mtu;

#ifdef LINUX_24

dev->hard_header = ednet_header;

#endif

dev->rebuild_header = ednet_rebuild_header;

#ifdef LINUX_24

dev->tx_timeout = ednet_tx_timeout;

dev->watchdog_timeo = timeout;

#endif

/* We do not need the ARP protocol. */

dev->flags |= IFF_NOARP;

#ifndef LINUX_20

dev->hard_header_cache = NULL;

#endif

#ifdef LINUX_24

SET_MODULE_OWNER(dev);

#endif

dev->priv = kmalloc(sizeof(struct ednet_priv), GFP_KERNEL);

if (dev->priv == NULL)

return -ENOMEM;

memset(dev->priv, 0, sizeof(struct ednet_priv));

spin_lock_init( ((struct ednet_priv *) dev->priv)->lock);

return 0;

}

linux操作系统文章专题:linux操作系统详解(linux不再难懂)

linux相关文章:linux教程




评论


相关推荐

技术专区

关闭