书籍:linux设备驱动开发详解 第6章
1 cdev结构体
在linux2.6中,使用cdev结构体描述了一个字符设备,定义如下地址:
- 12struct cdev {
-
13 struct kobject kobj; 内嵌的kobject对象
-
14 struct module *owner; 所属模块
-
15 const struct file_operations *ops;
-
16 struct list_head list; 双链表,struct list_head{struct list_head *next,*prev}
-
17 dev_t dev; 设备号
-
18 unsigned int count;
-
19};
cdev结构体的dev_t成员定义了设备号,为32bit,12bit主设备号,20bit次设备号
从下面宏从dev_t获得主设备号和次设备号
查看linux内核中,已经使用的设备号 src/Documentation/devices.txt
- 定义在include/linux/kdev_t.h
- 通过dev_t获得 主次设备号
- 7 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
- 8 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
- 通过主次设备号,获取 dev_t
- 9 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
cdev结构体的另一个重要成员 file_operations定义了字符设备驱动提供给虚拟文件系统的接口函数
- 21 void cdev_init(struct cdev *, const struct file_operations *);
-
23 struct cdev *cdev_alloc(void);
-
25 void cdev_put(struct cdev *p);
-
27 int cdev_add(struct cdev *, dev_t, unsigned);
-
29 void cdev_del(struct cdev *);
-
31 int cdev_index(struct inode *inode);
-
33 void cd_forget(struct inode *);
-
35 extern struct backing_dev_info directly_mappable_cdev_bdi;
1.1 cdev_init
函数用于初始化cdev的成员,并建立生cdev和file_operations之间连接
- /fs/char_dev.c 2.6.35
-
550 void cdev_init(struct cdev *cdev, const struct file_operations *fops)
-
551 {
-
552 memset(cdev, 0, sizeof *cdev);
-
553 INIT_LIST_HEAD(&cdev->list); //初始化链表 与 cdev_alloc 一致
-
554 kobject_init(&cdev->kobj, &ktype_cdev_default);//初始化 kobject 对象
-
555 cdev->ops = fops; //将传入的fops 传递给 cdev结构体
-
556 }
1.2 cdev_alloc()
用于动态申请一个cdev内存
- /fs/char_dev.c 2.6.35
-
532 struct cdev *cdev_alloc(void)
-
533 {
-
534 struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
-
535 if (p) {
-
536 INIT_LIST_HEAD(&p->list);
-
537 kobject_init(&p->kobj, &ktype_cdev_dynamic);
-
538 }
-
539 return p;
-
540 }
1.3cdev_add()和cdev_del()
向系统添加和删除一个cdev
- src/fs/char_dev.c
-
-
480 int cdev_add(struct cdev *p, dev_t dev, unsigned count)//count表示什么???
-
481 {
-
482 p->dev = dev;
-
483 p->count = count;
-
484 return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
-
485 }
dev_t_ num 是 12bit主设备号+20次设备号
count 表示设备的数量,通常情况下是1,我现在碰到的代码中都是 1
2012.1.3号更新注释说明count用法:
dev_t dev; 假设这个设备号=主设备250,第一个次设备号0,第二个次设备号为1
cdev_add(&cdev,dev,1);//添加第一个 c 250 0 的设备
cdev_add(&cdev,dev+1,1);//添加第二个 c 250 1 的设备
-
-
499 void cdev_del(struct cdev *p)
-
500 {
-
501 cdev_unmap(p->dev, p->count);
-
502 kobject_put(&p->kobj);
-
503 }
2.分配和释放设备号
在调用cdev_add()函数向系统注册字符设备之前,应首先调用register_chrdev_region()或alloc_chrdev_region()函数向系统申请设备号,
register_chrdev_region()函数用于已知起始设备的设备号的情况,而alloc_chrdev_region()用于设备号未知,向系统动态申请未被占用的设备号的情况。
unregister_chrdev_region(dev_t from,unsigned count);注销字符设备
函数原型为:
- src/fs/char_dev.c
- dev_t from: 设备号 ,必须包含主设备号
- unsigned count: 申请的 设备数量
- const char *name:
-
193 int register_chrdev_region(dev_t from, unsigned count, const char *name)
-
194 {
-
195 struct char_device_struct *cd;
-
196 dev_t to = from + count;
-
197 dev_t n, next;
-
198
-
199 for (n = from; n < to; n = next) {
-
200 next = MKDEV(MAJOR(n)+1, 0);
-
201 if (next > to)
-
202 next = to;
-
203 cd = __register_chrdev_region(MAJOR(n), MINOR(n),
-
204 next - n, name);
-
205 if (IS_ERR(cd))
-
206 goto fail;
-
207 }
-
208 return 0;
-
209 fail:
-
210 to = n;
-
211 for (n = from; n < to; n = next) {
-
212 next = MKDEV(MAJOR(n)+1, 0);
-
213 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
-
214 }
-
215 return PTR_ERR(cd);
-
216 }
- dev_t *dev:生成的设备号,放入*dev指针中
- unsigned baseminor:次设备号开始数值,一般为0,现在我碰到的代码中 都为 0
- ussigned count:设备数量
- dev_t devno;//主次设备号
- alloc_chrdev_region(&devno,0,2,"ywxdevice");
假如动态分配的主设备号为250:那么我将注册 c 250 0
c 250 1 两个设备节点
- 229 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
-
231 {
-
232 struct char_device_struct *cd;
-
233 cd = __register_chrdev_region(0, baseminor, count, name);
-
234 if (IS_ERR(cd))
-
235 return PTR_ERR(cd);
-
236 *dev = MKDEV(cd->major, cd->baseminor);
-
237 return 0;
-
238 }
- 304 void unregister_chrdev_region(dev_t from, unsigned count)
-
305 {
-
306 dev_t to = from + count;
-
307 dev_t n, next;
-
308
-
309 for (n = from; n < to; n = next) {
-
310 next = MKDEV(MAJOR(n)+1, 0);
-
311 if (next > to)
-
312 next = to;
-
313 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
-
314 }
-
315 }
3.file_operations结构体
- src/include/linux/fs.h
-
-
1485 struct file_operations {
-
1486 struct module *owner;
-
1487 loff_t (*llseek) (struct file *, loff_t, int);
-
1488 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
-
1489 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
-
1490 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
-
1491 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
-
1492 int (*readdir) (struct file *, void *, filldir_t);
-
1493 unsigned int (*poll) (struct file *, struct poll_table_struct *);
-
1494 int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
-
1495 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
-
1496 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
-
1497 int (*mmap) (struct file *, struct vm_area_struct *);
-
1498 int (*open) (struct inode *, struct file *);
-
1499 int (*flush) (struct file *, fl_owner_t id);
-
1500 int (*release) (struct inode *, struct file *);
-
1501 int (*fsync) (struct file *, int datasync);
-
1502 int (*aio_fsync) (struct kiocb *, int datasync);
-
1503 int (*fasync) (int, struct file *, int);
-
1504 int (*lock) (struct file *, int, struct file_lock *);
-
1505 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
-
1506 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
-
1507 int (*check_flags)(int);
-
1508 int (*flock) (struct file *, int, struct file_lock *);
-
1509 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
-
1510 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
-
1511 int (*setlease)(struct file *, long, struct file_lock **);
-
1512};
- #include <linux/module.h>//MODULE_LICENSE() module_init
-
#include <linux/init.h>//__init __exit
-
#include <linux/fs.h>//register_chrdev_region
-
#include <linux/kernel.h>//contain_of kmalloc kfree printk
-
#include <linux/cdev.h>//struct cdev cdev;
-
#include <linux/errno.h>
-
//#include <linux/mm.h>
-
//#include <linux/sched.h>
-
#include <linux/types.h>// MAJOR(dev_t dev) MKDEV
-
//#include <asm/io.h>
-
//#include <asm/system.h>
-
#include <asm/uaccess.h> //copy_to_user copy_from_user
-
-
#include <linux/stat.h>//00666
-
#include <linux/slab.h>//kmalloc kfree
-
#include <linux/moduleparam.h>//module_param()
-
-
#define GLOBALMEM_SIZE 0x1000
-
#define MEM_CLEAR 0x1
-
#define GLOBALMEM_MAJOR 240 //240-250 LOCAL/EXPERIMENTAL USE
-
-
static int globalmem_major = GLOBALMEM_MAJOR;
-
module_param(globalmem_major,int,S_IRUGO);
-
-
struct globalmem_dev{
-
struct cdev cdev;
-
unsigned char mem[GLOBALMEM_SIZE];
-
};
-
struct globalmem_dev *globalmem_devp;
-
-
int globalmem_open(struct inode *inode,struct file *filp)
-
{
-
struct globalmem_dev *dev;
- //将设备结构体指针赋值给文件私有数据指针
-
dev = container_of(inode->i_cdev,struct globalmem_dev,cdev);
-
filp->private_data = dev;
-
return 0;
-
}
-
-
int globalmem_release(struct inode *inode,struct file *filp)
-
{
-
return 0;
-
}
-
static int globalmem_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
-
{
-
struct globalmem_dev *dev = filp->private_data;
-
switch(cmd)
-
{
-
case MEM_CLEAR:
-
memset(dev->mem,0,GLOBALMEM_SIZE);
-
printk(KERN_INFO"globalmem is set to zero\n");
-
break;
-
default:
-
return -EINVAL;//22 invaild arguments
-
}
-
return 0;
-
}
- //size 要复制的数据字节数
- //loff_t *ppos :在文件中的位置
-
static ssize_t globalmem_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)
-
{
-
unsigned long p = *ppos;//p在文件中的位置,范围[0,GLOBALMEM_SIZE-1]
-
unsigned int count = size; //读取的 字节数
-
int ret = 0;
-
struct globalmem_dev *dev = filp->private_data;//获得设备结构体指针
-
if(p >= GLOBALMEM_SIZE)// 超出 GLOBALMEM_SIZE大小,
-
return 0;
-
if(count > GLOBALMEM_SIZE - p)//读取的数据 超出范围
-
count = GLOBALMEM_SIZE - p;
-
if(copy_to_user(buf,(void *)(dev->mem+p),count))
-
{
-
ret = -EFAULT;//bad address
-
}
-
else
-
{
-
*ppos += count;//ppos在文件中的位置,重新设置
-
ret = count;
-
printk(KERN_INFO"read %u byte(s) form %lu\n",count,p);
-
}
-
return ret;
-
}
-
-
static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
-
{
-
unsigned long p = *ppos;//在文件中位置,范围[0,GLOBALMEM_SIZE-1]
-
unsigned int count = size;//读数据的 字节数
-
int ret = 0;
-
struct globalmem_dev *dev = filp->private_data;
-
if(p >= GLOBALMEM_SIZE)
-
return 0;
-
if(count > GLOBALMEM_SIZE- p)
-
count = GLOBALMEM_SIZE - p;
-
if(copy_from_user(dev->mem + p,buf,count))
-
ret = -EFAULT;//bad address
-
else
-
{
-
*ppos += count;
-
ret = count;
-
printk(KERN_INFO"written %u byte(s) from %lu\n",count,p);
-
}
-
return ret;
-
}
//loff_t offset:在文件中的位置 [0,GLOBALMEM_SIZE]
//int orig:命令
-
static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig)
-
{
-
loff_t ret = 0;
-
switch(orig)
-
{
-
case 0:
-
if(offset < 0)
-
{
-
ret = -EINVAL;//
-
break;
-
}
-
if((unsigned int)offset > GLOBALMEM_SIZE)
-
{
-
ret = -EINVAL;
-
break;
-
}
-
filp->f_pos = (unsigned int)offset;
-
ret = filp->f_pos;
-
break;
-
case 1:
-
if((filp->f_pos + offset) > GLOBALMEM_SIZE)
-
{
-
ret = -EINVAL;
-
break;
-
}
-
if((filp->f_pos + offset) < 0)
-
{
-
ret = -EINVAL;
-
break;
-
}
-
filp->f_pos +=offset;
-
ret = filp->f_pos;
-
break;
-
default:
-
ret = -EINVAL;
-
break;
-
}
-
return ret;
-
}
-
-
static const struct file_operations globalmem_fops={
-
.owner = THIS_MODULE,
-
.llseek = globalmem_llseek,
-
.read = globalmem_read,
-
.write = globalmem_write,
-
.ioctl = globalmem_ioctl,
-
.open = globalmem_open,
-
.release = globalmem_release,
-
};
-
-
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
-
{
-
int err,devno = MKDEV(globalmem_major,index);
-
cdev_init(&dev->cdev,&globalmem_fops);
-
dev->cdev.owner = THIS_MODULE;
-
err = cdev_add(&dev->cdev,devno,1);
-
if(err)
-
printk(KERN_INFO"error %d ading globalmem %d",err,index);
-
}
-
-
static int __init globalmem_init(void)
-
{
-
int result;
-
dev_t devno = MKDEV(globalmem_major,0);
-
if(globalmem_major)
-
{
-
result = register_chrdev_region(devno,1,"globalmem");
-
printk("register_chrdev_region\n");
-
}
-
else
-
{
-
result = alloc_chrdev_region(&devno,0,1,"globalmem");
-
globalmem_major = MAJOR(devno);
-
printk("alloc_chrdev_region\n");
-
}
-
if(result < 0)
-
return result;
- //可以使用 zmalloc申请内核空间,这样就不需要memset重新清空内存了,zmalloc已经完成清空内存操作
- globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
-
if(!globalmem_devp)
-
{
-
result = -ENOMEM;//no memory
-
goto fail_malloc;
-
}
-
memset(globalmem_devp,0,sizeof(struct globalmem_dev));
-
globalmem_setup_cdev(globalmem_devp,0);
-
-
printk("init finished..\n");
-
return 0;
-
fail_malloc:
-
unregister_chrdev_region(devno,1);
-
return result;
-
}
-
-
static void __exit globalmem_exit(void)
-
{
-
cdev_del(&globalmem_devp->cdev);
-
kfree(globalmem_devp);
-
unregister_chrdev_region(MKDEV(globalmem_major,0),1);
-
printk("exit..\n");
-
}
-
MODULE_AUTHOR("Song");
-
MODULE_LICENSE("GPL");
-
-
module_init(globalmem_init);
-
module_exit(globalmem_exit);
操作过程:
1. 插入模块文件,在程序代码中,主设备号设置为 240
- ywx@ywx:~/Desktop/module/baohua/globalmem$ sudo insmod ./globalmem.ko
-
ywx@ywx:~/Desktop/module/baohua/globalmem$ cat /proc/devices
-
Character devices:
-
1 mem
-
4 /dev/vc/0
-
136 pts
-
180 usb
-
189 usb_device
-
240 globalmem
2.新建设备文件,并测试
- root@ywx:/home/ywx/desktop# mknod /dev/globalmem c 240 0
-
root@ywx:/home/ywx/desktop# echo "i love linux" > /dev/globalmem
-
root@ywx:/home/ywx/desktop# cat /dev/globalmem
-
i love linux
当使用 mknod /dev/globalmem c 240 1的时候,会出错。因为在程序代码中只申请了一个设备,次设备号为0,如果需要多个次设备,需要在程序代码中修改并增加设备数量及次设备号。
3. 参看/proc/modules 和 /sys/modules/globalmem
系统中多了 globalmem 这个设备
- ywx@ywx:~/desktop$ cat /proc/modules | grep glo
-
globalmem 1816 0 - Live 0xe0a27000
-
-
ywx@ywx:~/desktop$ ls /sys/module/globalmem/
-
holders initstate notes parameters refcnt sections srcversion
4. dmesg 测试程序代码 printk
- root@ywx:/home/ywx/desktop# dmesg | tail -4
-
[ 6439.327891] register_chrdev_region
-
[ 6439.327958] init finished..
-
[ 6555.013116] written 13 byte(s) from 0
-
[ 6558.571373] read 4096 byte(s) form 0
5. 测试 动态设备节点
当major主设备号为0时,系统会自动设置并分配空闲的主设备号,注意,主设备号240-250是用于 实验 使用的。所以当我们自动分配主设备号时,经常是分配250这个主设备号
- ywx@ywx:~/desktop/module/baohua/globalmem$ sudo insmod ./globalmem.ko globalmem_major=0
-
ywx@ywx:~/desktop/module/baohua/globalmem$ cat /proc/devices
-
Character devices:
-
1 mem
-
4 /dev/vc/0
-
128 ptm
-
136 pts
-
180 usb
-
189 usb_device
-
250 globalmem
6. 使用程序代码测试 将rar修改为tar.bz2
- #include <stdio.h>//printf memset
-
#include <unistd.h>
-
#include <fcntl.h>//
-
#include <linux/stat.h>//S_IRUSR=00600
-
#include <stdlib.h>//exit
-
#include <string.h>//strcpy
-
-
int main(int argc, char **argv)
-
{
-
int fd = 0;
-
int ret = 0;
-
char buf[10];
-
-
//clear memory
-
memset(buf,'a',10);
-
printf("%s\n",buf);
-
-
//open
-
fd = open("/dev/globalmem",O_RDWR,S_IRUSR|S_IWUSR);
-
-
//write
-
ret = write(fd,"yuweixian",strlen("yuweixian"));//attentions: "\n"
-
-
//lseek set to file offset
-
ret = lseek(fd,0,SEEK_SET);
-
-
//read
-
ret = read(fd,buf,strlen(buf));
-
printf("%s\n",buf);
-
-
close(fd);
-
exit(EXIT_SUCCESS);
-
}
- root@ywx:/home/ywx/desktop/module/baohua/globalmem# ./app
-
aaaaaaaaaa
-
yuweixian
当使用 cat /dev/globalmem,显示如下:
在显示 “yuweixian” 打印信息后,/dev/globalmem内存中还有很多其他混乱的数据,这是因为我们没有输入结束符
阅读(1073) | 评论(0) | 转发(0) |