免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1155 | 回复: 0
打印 上一主题 下一主题

Linux设备驱动开发学习笔记 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-08-30 14:32 |只看该作者 |倒序浏览
出处:ZAPPER'S
时间:Mon, 24 Apr 2006 20:35:40 +0000
作者:zapper
地址:
http://gz.greatzapper.net/read.php/112.htm
内容:
只读了几章ldd,先总结一下
代码:
Linux设备驱动开发学习笔记
内核版本:2.6.x

Major and Minor Numbers
内核通过major号来识别设备,下面的命令列出的是系统上所连接的设备及其major number,第一列就是设备的major number.
$ cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
116 alsa
128 ptm
136 pts
180 usb
195 nvidia
226 drm
254 devfs
Block devices:
3 ide0
22 ide1
$ls -l /dev
..............
crw------- 1 root root 119,  0 Apr 24 11:34 vmnet0
crw------- 1 root root 119,  1 Apr 24 11:34 vmnet1
crw------- 1 root root 119,  2 Apr 24 11:34 vmnet2
crw------- 1 root root 119,  3 Apr 24 11:34 vmnet3
crw------- 1 root root 119,  4 Apr 24 11:34 vmnet4
crw------- 1 root root 119,  5 Apr 24 11:34 vmnet5
crw------- 1 root root 119,  6 Apr 24 11:34 vmnet6
crw------- 1 root root 119,  7 Apr 24 11:34 vmnet7
crw------- 1 root root 119,  8 Apr 24 11:34 vmnet8
..............
可以看到他们的major number 是119,但他们的minor number不同.分别是0~8.
内核只关心major number,而minor number 是由设备驱动来区别的.
内核内部,类型dev_t存储着设备号,且定义了一组宏来维护它.
MKDEV(int major,int minor);//return dev_t
MAJOR( dev_t dev);
MINOR (dev_t dev);
比如,我们用mknod建立一个新的设备文件
#mknod /dev/newchr c 50 0
建立/dev/newchr设备文件,类型是c(char,字符型),major number 是50,minor number 是0.mknod的用法可以用man来查看.
在内核内部,我们用上面的宏来维护:
dev_t mydev;
mydev=MKDEV(50,0);
我们也可以由mydev得到major 和minor number.
int major,minor;
major=MAJOR(mydev);
minor=MINOR(mydev);
注册设备号
我们定义好major和minor number 后就可以在内核中注册一个设备了.注册一个字符设备需要用到下面几个函数:
int register_chrdev_region(dev_t first,unsigned int count,
char *name);
first是你要注册的设备号范围的开始(其中minor号一般设置为0),count是你所申请的连续设备号的总数.name是设备的名称.它会在/proc/devices中出现.
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,
unsigned int count,char *name);
这个函数是用来动态分配设备号的.有余开发者不知道所要用的major号是多少,便让内核动态分配一个.参数dev是个output-only参数.
void unregister_chrdev_region(dev_t first,unsigned int count);
一般在模块清除函数中调用.
重要的数据结构
正如你所想象的,注册只是第一步,后面还有更重要的部分,其中一个就是我们一开始提到的如何实现open/read/write/ioctl等用户接口.
首先看一下几个重要的数据结构:
file_operations,file,inode
中定义了这三个结构.
struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
    ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
    ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*dir_notify)(struct file *filp, unsigned long arg);
}
file_operations结构定义了一组函数指针,每一个打开的文件(用struct file表示)和他自己的一组函数(包含在一个叫f_op的域中,它指向一个struct file_operations结构)相联系.这些操作都是来实现系统调用的,所以才被命名为open,read,等等.对于那些不需要的功能(比如你的设备不需要write功能,即不需要向设备写数据),可以给write指针付NULL.
struct file {
    struct list_head    f_list;
    struct dentry      *f_dentry;
    struct vfsmount     *f_vfsmnt;
    struct file_operations *f_op;
    atomic_t        f_count;
    unsigned int      f_flags;
    mode_t         f_mode;
    int           f_error;
    loff_t         f_pos;
    struct fown_struct   f_owner;
    unsigned int      f_uid, f_gid;
    struct file_ra_state  f_ra;
    unsigned long      f_version;
    void          *f_security;
    /* needed for tty driver, and maybe others */
    void          *private_data;
#ifdef CONFIG_EPOLL
    /* Used by fs/eventpoll.c to link all the hooks to this file */
    struct list_head    f_ep_links;
    spinlock_t       f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
    struct address_space  *f_mapping;
};
每个打开的文件对应一个struct file.它在被打开时由内核创建,并传给它所有可以操作该文件的函数,到文件被关闭时才被删除.
The inode Structure.
Inode结构是用来在内核内部表示文件的.同一个文件可以被打开好多次,所以可以对应很多struct file,但是只对应一个struct inode.该结构里面包含了很多信息,但是,驱动开发者只关心里面两个重要的域:
dev_t i_rdev;//含有真正的设备号
struct cdev *i_cdev;//struct cdev是内核内部表示字符设备的结构.
注册字符设备
可以有两种方法注册
struct cdev *my_cdev=cdev_alloc();
my_cdev->ops=&my_fops;

void cdev_init(struct cdev *cdev,struct file_operations *fops);
注册完后还要通知内核一声,通过调用
int cdev_add(struct cdev *dev,dev_t num,unsigned int count);
count 一般是 1.
删除字符设备,调用
void cdev_del(struct cdev *dev);
说了那么多,现在可以来个例子了.
#include
#include
#include
#include
#include
#include
#define DP_MAJOR 50
#define DP_MINOR 0
static int char_read(struct file *filp,char __user *buffer,size_t,loff_t *);
static int char_open(struct inode *,struct file *);
static int char_write(struct file *filp,const char __user *buffer,size_t ,loff_t*);
static int char_release(struct inode *,struct file *);
static char *arr,*p;
static int chropen;
struct cdev *my_cdev;
static int len;
struct file_operations Fops = {
.read = char_read,
.write = char_write,
.open = char_open,
.release = char_release, /* a.k.a. close */
};
static int __init char_init(void)
{
printk(KERN_ALERT"Initing......\n");
dev_t dev;
dev=MKDEV(DP_MAJOR,DP_MINOR);
my_cdev = cdev_alloc( );
arr=kmalloc(1024,GFP_KERNEL);
if(arr==NULL){
printk(KERN_ALERT"kmalloc error\n");
}
sprintf(arr,"Hello,Pid=%d\n",current->pid);
if(my_cdev==NULL){
return -1;
}
if(register_chrdev_region(dev,10,"dpchr")ops = &Fops;
cdev_init(my_cdev,&Fops);
cdev_add(my_cdev,dev,1);
return 0;
}
static int char_open(struct inode *inode,struct file *file)
{
if(chropen==0)
chropen++;
else{
printk(KERN_ALERT"Another process open the char device\n");
return -1;
}
p=arr;
try_module_get(THIS_MODULE);
return 0;
}
static int char_release(struct inode *inode,struct file *file)
{
chropen--;
module_put(THIS_MODULE);
return 0;
}
static int char_read(struct file *filp,char __user *buffer,size_t length,loff_t *offset)
{
int i=0;
if(*p=='\0')
return 0;
while(length&&*p){
put_user(*(p++),buffer++);
length--;
i++;
}
return i;
}
static int char_write(struct file *filp,const char __user *buffer,size_t length,loff_t *offset)
{
int i;
for(i=0;i
Generated by Bo-blog 2.0.2

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/22712/showart_162437.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP