Linux中国 Linux中国门户站!
设为主页 设为主页
收藏本站 收藏本站
 
当前位置 :首页 ->Linux技术 ->嵌入式系统 ->正文

Linux串口上网的简单实现

来源:IBM DW中国 作者:李素科  时间:2007-04-22 点击: [收藏] [投稿]

在头文件中,我们定义ED_REC_DEVICE为receiving device,名字是ed_rec;定义ED_TX_DEVICE为sending device,名字是ed_tx。


#define MAJOR_NUM_REC 200
#define MAJOR_NUM_TX  201
#define IOCTL_SET_BUSY _IOWR(MAJOR_NUM_TX,1,int)

200和201分别代表 receiving device 和 sending device的主设备号。在内核空间,驱动程序是根据主、次设备号识别设备的,而不是设备名;本文的字符设备的次设备号都是0,主设备号是用户定义的且不能和系统已有的设备的主设备有冲突。IOCTL_SET_BUSY _IOWR(MAJOR_NUM_TX,1,int)是ioctl的操作函数定义(从用户空间发送命令到内核空间),主要作用是使得每次在同一时间,同一字符设备上,只可进行一次操作。我们可以使用mknod来建立这两个字符设备:


[root@localhost]#mknod c 200 0 /dev/ed_rec
[root@localhost]#mknod c 201 0 /dev/ed_tx

设备建立后,编译好的模块就可以动态加载了:


[root@localhost]#insmod ed_device.o

为了方便对设备编程,我们还需要一个字符设备管理的数据结构:


struct ed_device{
	int magic;
	char name[8]; 	
	int busy;
	unsigned char *buffer;
    #ifdef LINUX_24
wait_queue_head_t rwait;
#endif
	int mtu;
	spinlock_t lock;
	int data_len;
    int buffer_size;
	struct file *file;
    ssize_t (*kernel_write)(const char *buffer,size_t length,int buffer_size);
};

这个数据结构是用来保存字符设备的一些基本状态信息。ssize_t (*kernel_write)(const char *buffer,size_t length,int buffer_size) 是一个指向函数的指针,它的作用是为伪网络驱动程序提供写字符设备数据的系统调用接口。magic字段主要是标志设备类型号的,这里没有别的特殊意义; busy字段用来说明字符设备是否是处于忙状态,buffer指向内核缓存区,用来存放读写数据;mtu保存当前可发送的网络数据包最大传输单位,以字节为单位;lock的类型是自旋锁类型spinlock_t,它实际以一个整数域作为锁,在同一时刻对同一字符设备,只能有一个操作,所以使用内核锁机制保护防止数据污染;data_len是当前缓存区内保存的数据实际大小,以字节为单位;file是指向设备文件结构struct file的一个指针,其作用主要是定位设备的私有数据 file-> private_data。定义字符设备struct ed_device ed[2],其中ed[ED_REC_DEVICE]就是receving device,ed[ED_TX_DEVICE]就是sending device。如果sending device ED_TX_DEVICE没有数据,用户空间的read调用将被阻塞,并把进程信息放于rwait队列中。当有数据的时候,kernel_write() 中的wake_up_interruptible()将唤醒等待进程。kernel_write()函数定义如下:


ssize_t kernel_write(const char *buffer,size_t length,int buffer_size)
{
    if(length > buffer_size )
        length = buffer_size;
    memset(ed[ED_TX_DEVICE].buffer,0,buffer_size);
    memcpy(ed[ED_TX_DEVICE].buffer,buffer,buffer_size);
    ed[ED_TX_DEVICE].tx_len = length;
    #ifdef LINUX_24
    wake_up_interruptible(&ed[ED_TX_DEVICE].rwait);	
    #endif   
    return length;
}

字符设备的操作及其相关函数调用过程如图3 所示。


图 3

当ed_device模块被加载的时候,eddev_module_init()调用register_chrdev()内核API注册ed_tx和ed_rec两个字符设备。这个函数定义在<linux/fs.h>:


int register_chdev(unsigned int major, const char *, struct fle_operations *fops)

字符设备被注册成功后,内核把这两个字符设备加入到内核字符设备驱动表中。内核字符设备驱动表保留指向struct file_operations的一个数据指针。用户进程调用设备读写操作时,通过这个指针访问设备的操作函数, struct file_operations中的域大部分是指向函数的函数指针,指向用户自己编写的设备操作函数。


struct file_operations ed_ops ={
#ifdef LINUX_24
    NULL,
#endif
    NULL,
    device_read,
    device_write,
    NULL,
    NULL,
    device_ioctl,
    NULL,
    device_open,
    NULL,
    device_release,    	
};

注意到Linux2.4.x和 Linux2.2.x内核中定义的struct file_operations是不一样的。device_read()、device_write()、device_ioctl()、 device_open()、device_release()就是需要用户自己定义的函数操作了,这几个函数是最基本的操作,如果需要设备驱动程序完成更复杂的任务,还必须编写其他struct file_operations中定义的操作。eddev_module_init()除了注册设备及其操作外,它还有初始化字符设备结构struct ed_device,分配内核缓存区所需要的空间的作用。在内核空间,分配内存空间的API函数是kmalloc()。

 如果您对本文有任何疑问或者建议,请到讨论区发表您的意见: >> 论坛入口 <<



上一篇:Linux下PCI设备驱动程序开发   下一篇:ucLinux下sqlite数据库移植全攻略

文章评论】 【收藏本文】 【推荐好友】 【打印本文】 【我要投稿】 【论坛讨论
更多相关文章
Power by linux-cn.com 粤ICP备05006655号