字符设备驱动模块与测试代码编写。

设备驱动程序:以内核模块的形式存在也就是*.ko

设备驱动程序和系统调用关系.
系统调用:应有程序和操作系统(内核) 之间的接口(应用程序与内核的交互)
设备驱动程序:内核和设备硬件之间接口(内核与硬件的交互)

整个过程实现了应用程序间接访问了底层的硬件。

test.c中调用open-----》系统调用(sys_open())----->file_operation{.open = led_open}:调用驱动函数中的自定义的open

---------------------------------------------------------------

linux设备驱动的三种类型:

1.字符设备:LED,显卡,声卡,键盘,触摸屏。。。
2.块设备:硬盘,nandflash,SD,U盘....
3.网络设备:网卡(无线,有线)

---------------------------------------------------------------

二。字符设备特点
1.应用程序和设备进行数据交互时,是以字节单位进行的。
2。访问的数据是连续时,实时。
3.不带缓存,块设备驱动带有缓存.

三。字符设备的描述
1。cdev结构
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list; //内核链表
dev_t dev;
unsigned int count;//当前设备下有多少个次设备
};
cdev是描述一个字符设备,每写一字符设备,创建一个cdev结构,每个字符设备都有自已的cdev

--struct kobject kobj;
不关心的,是由内核管理设备使用,
--struct module *owner;
cdev属于那个体module,一般写THIS_MODULE;
--const struct file_operations *ops;
操作集(设备驱动),是应用程序访问设备的驱动接口

--struct list_head list;
内核链表:将当前cdev放到一链表,方便内核管理cdev

--dev_t dev;
typedef u_long dev_t;
typedef unsigned long u_long;
设备号.每个字符设备都有自已的设备号,设备号在当内核中是唯一,在32 BITs,32无符号整形
--unsigned int count;
在当设备下有多少个次设备...

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 (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, 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);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
-----------------------------------------------
};

------------------------------------------------------------------

cdev中的设备号:

2.1定义
typedef u_long dev_t;
typedef unsigned long u_long;
设备号.每个字符设备都有自已的设备号,设备号在当内核中是唯一,在32 BITs,32无符号整形。

2.2设备号由主设备号和次设备号组成:
主设备号:某类型类的设备 [31:20]
次设备号:该类型的设备下,具体那个设备实例.[19:0]

2.3相关函数(宏):由设备号的得到主次设备号

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))

  由主次设备号得到设备号:

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
ma-->主设备号
mi-->次设备号

  向内核中注册设备号:

定义一个CDEV时候,首先在内核中申请一个设备号,获得设备号的方法有两种。

1、向内核中静态注册一个设备号:自已定制一个设备号,向内核注册(申请),如果内核中没有占用设备号,代注册成功。

* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
参数说明:
from-->设备号的值
count-->次设备的数量
name->设备的名称.
返回值:
成功:0
失败:负数

例:
[@GEC2103 /]# ls /dev/s3c241* -l
crw-rw---- 1 0 0 204, 64 Jan 1 00:00 /dev/s3c2410_serial0
crw-rw---- 1 0 0 204, 65 Jan 1 00:00 /dev/s3c2410_serial1
crw-rw---- 1 0 0 204, 66 Jan 1 00:00 /dev/s3c2410_serial2
crw-rw---- 1 0 0 204, 67 Jan 1 00:00 /dev/s3c2410_serial3

register_chrdev_region(MKDEV(204,64),4,“s3c2410_serial”)


#cat /proc/devices
204 s3c2410_serial

2、让内核动态分配一个空闲设备号

/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
参数:
*dev:-->申请设备号
baseminor-->申请设备号第一个次设备号
count-->申请次设备的数量
name-->设备名称

返回值:
成功:0
失败:负数

注意:一定判断申请是成功


dev_t det;
alloc_chrdev_region(&det,0,2,"test");

3、注销设备号

3.3注销一个已经注册的设备号
当驱动程序移除时,一定先注销设备号
/**
* unregister_chrdev_region() - return a range of device numbers
* @from: the first in the range of numbers to unregister
* @count: the number of device numbers to unregister
*
* This function will unregister a range of @count device numbers,
* starting with @from. The caller should normally be the one who
* allocated those numbers in the first place...
*/
void unregister_chrdev_region(dev_t from, unsigned count)
参数:from-->设备号的值,第一个设备号
count-->次设备号的数量

--------------------------------------------------------------------------

3.文件操作集
struct file_operations

文件操作集是内核管理cdev那设备的驱动接口, 驱动程序提供应用程序一个接口

int testopen(struct inode *inode, struct file *file)
{



}
int testclose (struct inode *inode, struct file *file);
{


}
ssize_t testread(struct file *, char __user *, size_t, loff_t *)
{


}

struct file_operations fops= --->结构体初始化 .---???????????????
{ .owner=THIS_MODULE,
.open=testopen,
.read=testread,
.release=testclose,
}

----------------------------------------------------------------------

4.cdev初始化和注册
4.1.cdev的初始化函数
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数:
cdev-->要初始化cdev
fops-->cdev的文件操作集


4.2.注册cdev到内核的函数
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
参数:p --->注册那个cdev
dev--->设备号
count-->次设备个数


返回值:
成功:0
失败:负数

例:
strcut cdev cd;
cdev_init(&cd,&fops)
cd.owner=THIS_MODULE;
cdev_add(&cd,dev,1);

--------------------------------------------------------------

4.3cdev的注销函数
/**
* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed
*
* cdev_del() removes @p from the system, possibly freeing the structure
* itself.
*/
void cdev_del(struct cdev *p)
p-->要注销那个cdev .

注意:在linux驱动设计时候,我们使用的函数,一般都成对,申请<--->释放 注册--->注销

------------------------------------------------------------------------

5。字符设备的设计流程
5.1定义一个cdev
struct cdev chrdev3;
5.2申请一个设备号
例:
unsigned int TestMajor=0;
unsigned int TestMinor=0;
dev_t dev_no;
int ret;
dev_no =MKDEV(TestMajor,TestMinor)


if(dev_no>0)
{
ret=register_chrdev_region(dev_no, 1,"chrdev_test");//静态注册设备号

}
else //动态申请设备号
{

alloc_chrdev_region(&dev_no,TestMinor, 1,"chrdev_test");

}


if(ret<0)
{
return ret;
}

5.3定义文件操作集
int testopen(struct inode *inode, struct file *file)
{



}
int testclose (struct inode *inode, struct file *file);
{


}
ssize_t testwrtie(struct file *, char __user *, size_t, loff_t *)
{


}

struct file_operations fops= --->结构体初始化 .---???????????????
{ .owner=THIS_MODULE,
.open=testopen,
.write=testwrite,
.release=testclose,
}

5.4cdev初始化
cdev_init(&chrdev3,&fops);

5.5cdev注册到内核
cdev_add(&chrdev3,dev_no,1);先初始化设备号,然后注册cdev

5.6设备号和cdev注销
unregister_chrdev_region(dev_no, 1);
cdev_dev(&chrdev3);

-------------------------------------------------------------------------

.结合模块规则:写字符设备驱动程序
test.c

#include <linux/module.h>
#include <linux/kernel.h>
#include<linux/cdev.h>

struct cdev chrdev3;
unsigned int TestMajor=0;
unsigned int TestMinor=0;
dev_t dev_no;

int testopen(struct inode *inode, struct file *file)
{
//LED输出
printk("led init ");

}
int testclose (struct inode *inode, struct file *file);
{
printk("close");
return 0;

}
ssize_t testwrtie(struct file *, char __user *usr, size_t len, loff_t *offf)
{
char buf[12];
copy_from_user(buf,usr,);
buf[12];
//if(buf[]=='1')
// led点明
printk(,buf);


}
ssize_t testread(struct file *, char __user *usr, size_t len, loff_t *);
{
char buf='r';
read_led;
copy_to_user(usr,buf,);
}

struct file_operations fops= --->结构体初始化 .---???????????????
{ .owner=THIS_MODULE,
.open=testopen,
.write=testwrite,
.release=testclose,
}


static int __init test_init(void) //入口函数
{
printk("hello world! "); //相当于printf()
int ret;
dev_no =MKDEV(TestMajor,TestMinor)


if(dev_no>0)
{
ret=register_chrdev_region(dev_no, 1,"chrdev_test");//静态注册设备号

}
else //动态申请设备号
{

alloc_chrdev_region(&dev_no,TestMinor, 1,"chrdev_test");

}


if(ret<0)
{
return ret;
}


cdev_init(&chrdev3,&fops);
cdev.owner=THIS_MODULE;
cdev_add(&chrdev3,dev_no,1);
return 0;
}

static void __exit test_exit(void) //出口函数
{
unregister_chrdev_region(dev_no, 1);
cdev_del(&chrdev3);
}


module_init(test_init); //驱动的入口 #insmod *.ko
module_exit(test_exit); //驱动的出口 #rmmod *.ko

//#modinfo *.ko 可以查看module的信息
MODULE_AUTHOR("fbx@GEC");
MODULE_DESCRIPTION("the first module of drivers");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");

---------------------------------------------------------------------

8.linux用户空间与内核空间交互函数。
8.1将内核空间数据copy用户空间
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

8.2用户空间数据copy内核空间
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) ---> write


练习:应用调用write写一个字符到内核空间,内核那驱动程序的.write---》 PRINTK();
应用调用read把一个数据从内核读到用户,用户空间打印出来

chrdev.c

 1 #include<linux/module.h>
 2 #include<linux/kernel.h>
 3 #include<linux/cdev.h>
 4 #include<linux/fs.h>
 5 #include<linux/kdev_t.h>
 6 #include<linux/types.h>
 7 #include<linux/uaccess.h>
 8 #include<linux/string.h>
 9 struct cdev chrdev;
10 unsigned int TestMajor=0;
11 unsigned int TestMinor=0;
12 dev_t dev_no;
13 int ret;
14 
15 int testopen(struct inode *inode,struct file *file)
16 {
17     printk("cdev init
");
18     return 0;
19     
20 }
21 ssize_t testwrite(struct file *file, const char __user *usr, size_t len, loff_t *off)
22 {
23     char buf[12];
24     
25     copy_from_user(buf,usr,strlen(usr));
26     printk("%s
",buf);
27 
28 }
29 ssize_t testread(struct file *file, char __user *usr, size_t len, loff_t *off)
30 {
31     char *buf = "hello,user!";
32     copy_to_user(usr,buf,20);
33 
34 
35 }
36 int testrelease(struct inode *inode, struct file *file)
37 {
38     printk("close
");
39     return 0;
40 
41 }
42 
43 struct file_operations fops=
44 {
45     .owner=THIS_MODULE,
46     .open = testopen,
47     .write = testwrite,
48     .read = testread,
49     .release = testrelease,
50 };
51 static int __init test_init(void)
52 {
53     dev_no = MKDEV(TestMajor,TestMinor);
54     if(dev_no>0)
55     {
56         ret = register_chrdev_region(dev_no,1,"chrdev_test");    
57     }
58     else
59     {
60         alloc_chrdev_region(&dev_no,0,1,"chrdev_test");
61     }
62     if(ret<0)
63     {
64         return ret;
65     }
66     cdev_init(&chrdev,&fops);
67     chrdev.owner=THIS_MODULE;
68     cdev_add(&chrdev,dev_no,1);
69     return 0;
70 }
71 
72 static int __exit test_exit(void)
73 {
74     unregister_chrdev_region(dev_no,1);
75     cdev_del(&chrdev);
76     
77     return 0;
78 }
79 
80 module_init(test_init);
81 module_exit(test_exit);
82 
83 
84 MODULE_AUTHOR("FENG");
85 MODULE_DESCRIPTION("the first module of char drivers");
86 MODULE_LICENSE("GPL");
87 MODULE_VERSION("V1.0");

7.字符设备的调试
7。1安装驱动
[@GEC2103 /]# insmod chrdev.ko
7.2查看分配的设备号
[@GEC2103 /]# cat /proc/devices

250 chrdev_test

7.3手动创建设备文件
mknod /dev/chrdev c 250 0

[@GEC2103 /]# ls /dev/chrdev -l
crw-r--r-- 1 0 0 250, 0 Jan 1 00:45 /dev/chrdev
7.4应用程序来/dev/chrdev设备文件,等效操作设备文件对应那个设备

int main()
{
fd=open("/dev/chrdev",O_RDWR);
write(fd,usrbuf,sizeof(usrbuf))
close(fd);

}

--------------------------------------------------------------------------------

test.c

 1 #include<stdio.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <errno.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 
 8 int main()
 9 {
10 char buf[20];
11 char buf1[20];
12 int fd = open("/dev/chrdev_test",O_RDWR);
13 if(fd<0)
14 perror("open() error!
");
15 bzero(buf1,20);
16 strcpy(buf1,"hello kernel!");
17 write(fd,buf1,strlen(buf1)+1);
18 sleep(1);
19 read(fd,buf,20);
20 printf("%s
",buf);
21 }

9。字符设备设计老的方法
优点:比新简洁,比较好理解.


1.使用函数
1)注册字符设备
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
参数:
major--->主设备号,如果major>0,静态注册
=0 动态分配

name-->设备名称 /proc/devices


fops-->文件操作集


返回值:
major >0 成功:0
失败:负数
major =0 成功:分配后主设备号
失败:负数


2)注销字符设备
static inline void unregister_chrdev(unsigned int major, const char *name)
major->主设备号
name-->设备名称

2.使用旧方法设计字符设备驱动的流程
2.1unsigned int Testmajor =0;//251

2.2定义文件操作集
int testopen(struct inode *inode, struct file *file)
{



}
int testclose (struct inode *inode, struct file *file);
{


}
ssize_t testwrtie(struct file *, char __user *, size_t, loff_t *)
{


}

struct file_operations fops= --->结构体初始化 .---???????????????
{ .owner=THIS_MODULE,
.open=testopen,
.write=testwrite,
.release=testclose,
}

2.3注册一个字符设备

ret=register_chrdev(Testmajor,"chrdev_test",&fops);//注册字符设备到内核
if(ret<0)
{
printk("register error")
}

if(Testmajor==0)
{
Testmajor =ret;

}

 1 #include<linux/module.h>
 2 #include<linux/kernel.h>
 3 #include<linux/cdev.h>
 4 #include<linux/fs.h>
 5 #include<linux/kdev_t.h>
 6 #include<linux/types.h>
 7 #include<linux/uaccess.h>
 8 #include<linux/string.h>
 9 
10 dev_t dev_no = 0;
11 int ret;
12 
13 int testopen(struct inode *inode,struct file *file)
14 {
15     printk("cdev init
");
16     return 0;
17     
18 }
19 ssize_t testwrite(struct file *file, const char __user *usr, size_t len, loff_t *off)
20 {
21     char buf[12];
22     
23     copy_from_user(buf,usr,len);
24     printk("%s
",buf);
25 
26 }
27 ssize_t testread(struct file *file, char __user *usr, size_t len, loff_t *off)
28 {
29     char buf[20] = "hello,user!";
30     copy_to_user(usr,buf,len);
31 
32 
33 }
34 int testrelease(struct inode *inode, struct file *file)
35 {
36     printk("close
");
37     return 0;
38 
39 }
40 
41 struct file_operations fops=
42 {
43     .owner=THIS_MODULE,
44     .open = testopen,
45     .write = testwrite,
46     .read = testread,
47     .release = testrelease,
48 };
49 static int __init test_init(void)
50 {
51     ret = register_chrdev(dev_no,"chrdev_test",&fops);
52     if(ret < 0)
53     {
54         printk("register error!
");
55     }
56     if(dev_no==0)
57     {
58         dev_no = ret;
59     }
60 
61     return 0;
62 }
63 
64 static int __exit test_exit(void)
65 {
66     unregister_chrdev(dev_no,"chrdev_test");
67     
68     return 0;
69 }
70 
71 module_init(test_init);
72 module_exit(test_exit);
73 
74 
75 MODULE_AUTHOR("FENG");
76 MODULE_DESCRIPTION("the first module of char drivers");
77 MODULE_LICENSE("GPL");
78 MODULE_VERSION("V1.0");
原文地址:https://www.cnblogs.com/defen/p/5462247.html