設備驅動程序:以內核模塊的形式存在也就是*.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 \n");
}
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!\n"); //相當於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\n"); 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\n",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\n"); 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!\n"); 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\n",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\n"); 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\n",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\n"); 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!\n"); 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");