科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Linux系统串口上网的简单实现(上) (3)

Linux系统串口上网的简单实现(上) (3)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文主要说明某些简易 Linux 环境或者嵌入式 Linux 中实现串口上网的简单实现,这在工业控制中有着广泛的应用。希望对实现无网卡设备上网的方法有抛砖引玉的作用。

作者:shanyaun 来源:赛迪网技术社区 2007年10月19日

关键字: 操作系统 实现 串口 Linux

  • 评论
  • 分享微博
  • 分享邮件

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两个字符设备。这个函数定义在

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()。

下面介绍一下字符设备的主要操作例程device_open()、device_release()、device_read()、devie_write()。字符设备文件操作结构ed_ops中定义的指向以上函数的函数指针的原形:

device_open:  int(*open)(struct inode *,struct file *)     
     device_release: int (*release) (struct inode *, struct file *);
     device_read:  ssize_t (*read) 
     (struct file *, char *, size_t, loff_t *);
     device_write: ssize_t (*write) 
     (struct file *, const char *, size_t, loff_t *);
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章