關(guān)于Linux的簡單字符設(shè)備驅(qū)動程序
一、重要知識點
1. 主次設(shè)備號
dev_t
dev_t是內(nèi)核中用來表示設(shè)備編號的數(shù)據(jù)類型;
int MAJOR(dev_t dev)
int MINOR(dev_t dev)
這兩個宏抽取主次設(shè)備號。
dev_t MKDEV(unsigned int major, unsignedint minor)
這個宏由主/次設(shè)備號構(gòu)造一個dev_t結(jié)構(gòu)。
2. 分配和釋放設(shè)備號
int register_chardev_region(dev_t first,unsigned int count, char *name)
靜態(tài)申請設(shè)備號。
Int alloc_chardev_region(dev_t *dev,unsigned int firstminor, unsigned int count, char *name)
動態(tài)申請設(shè)備號,注意第一個參數(shù)是傳地址,而靜態(tài)則是傳值。
3. 幾種重要的數(shù)據(jù)結(jié)構(gòu)
struct file
file結(jié)構(gòu)代表一個打開的文件,它由內(nèi)核在open時創(chuàng)建,并傳遞給該文件上進行操作的所有函數(shù),直到最后的close函數(shù)。
file結(jié)構(gòu)private_data是跨系統(tǒng)調(diào)用時保存狀態(tài)信息非常有用的資源。
file結(jié)構(gòu)的f_ops 保存了文件的當前讀寫位置。
struct inode
內(nèi)核用inode代表一個磁盤上的文件,它和file結(jié)構(gòu)不同,后者表示打開的文件描述符。對于單個文件,可能會有許多個表示打開文件的文件描述符file結(jié)構(gòu),但他們都指單個inode結(jié)構(gòu)。inode的dev_t i_rdev成員包含了真正的設(shè)備編號,struct cdev *i_cdev包含了指向struct cdev結(jié)構(gòu)的指針。
struct file_operations
file_operations結(jié)構(gòu)保存了字符設(shè)備驅(qū)動程序的方法。
4. 字符設(shè)備的注冊和注銷
struct cdev *cdev_alloc(void);
void cdev_init(struct cdev *dev, structfile_operations *fops);
int cdev_add(struct cdev *dev, dev_t num,unsigned int count);
void cdev_del(struct cdev *dev);
用來管理cdev結(jié)構(gòu)的函數(shù),內(nèi)核中使用該結(jié)構(gòu)表示字符設(shè)備。注意cdev_add函數(shù)的count參數(shù)為次設(shè)備的個數(shù),要想擁有多個次設(shè)備,就必須將該參數(shù)設(shè)為次設(shè)備的個數(shù)。
5. 并發(fā)處理
信號量和自旋鎖的區(qū)別,使用信號量時當調(diào)用進程試圖獲得一個鎖定了的鎖時會導(dǎo)致進程睡眠,而自旋鎖則是一直循法的等待一直到該鎖解鎖了為止。
1)信號量
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);
聲明和初始化用在互斥模式中的信號量的兩個宏
void init_MUTEX(struct semaphore *sem)
void init_MUTEX_LOCKER(struct semaphore*sem);
這兩個函數(shù)可以在運行時初始化信號量
void down(struct semaphore *sem);
int down_interruptible(struct semaphore*sem);
int down_trylock(struct semahpore *sem);
void up(struct semaphore *sem);
鎖定和解鎖信號量。如果必要,down會將調(diào)用進程置于不可中斷的休眠狀態(tài);相反,down_interruptible可被信號中斷。down_trylock不會休眠,并且會在信號量不可用時立即返回。鎖定信號量的代碼最后必須使用up解鎖該信號量。
2)自旋鎖
spionlock_t lock = SPIN_LOCK_UNLOCKED;
spin_lock_init(spinlock_t *lock);
初始化自旋鎖的兩種方式。
voidspin_lock(spinlock_t *lock);
鎖定自旋鎖
voidspin_unlock(spinlock_t *lock);
解鎖自旋鎖
二、驅(qū)動代碼
view plaincopy to clipboardprint?#include <linux/module.h> 
#include <linux/types.h> 
#include <linux/fs.h> 
#include <linux/errno.h> 
#include <linux/mm.h> 
#include <linux/sched.h> 
#include <linux/init.h> 
#include <linux/cdev.h> 
#include <asm/io.h> 
#include <asm/system.h> 
#include <asm/uaccess.h> 
#define MEMDEV_MAJOR 251 
#define MEMDEV_NUM 2 
#define MEMDEV_SIZE 1024 
struct mem_dev 
{ 
unsignedint size; 
char*data; 
structsemaphore sem; 
}; 
static int mem_major = MEMDEV_MAJOR; 
struct cdev mem_cdev; 
struct mem_dev *mem_devp; 
static int mem_open(struct inode *inode,struct file *filp) 
{ 
structmem_dev *dev; 
unsignedint num; 
printk("mem_open.\n"); 
num= MINOR(inode->i_rdev);//獲得次設(shè)備號 
if(num> (MEMDEV_NUM -1)) //檢查次設(shè)備號有效性 
return-ENODEV; 
dev= &mem_devp[num]; 
filp->private_data= dev; //將設(shè)備結(jié)構(gòu)保存為私有數(shù)據(jù) 
return0; 
} 
static int mem_release(struct inode *inode,struct file *filp) 
{ 
printk("mem_release.\n"); 
return0; 
} 
static ssize_t mem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos) 
{ 
intret = 0; 
structmem_dev *dev; 
unsignedlong p; 
unsignedlong count; 
printk("mem_read.\n"); 
dev= filp->private_data;//獲得設(shè)備結(jié)構(gòu) 
count= size; 
p= *ppos; 
//檢查偏移量和數(shù)據(jù)大小的有效性 
if(p> MEMDEV_SIZE) 
return0; 
if(count> (MEMDEV_SIZE-p)) 
count= MEMDEV_SIZE - p; 
if(down_interruptible(&dev->sem))//鎖定互斥信號量 
return -ERESTARTSYS; 
//讀取數(shù)據(jù)到用戶空間 
if(copy_to_user(buf,dev->data+p, count)){ 
ret= -EFAULT; 
printk("copyfrom user failed\n"); 
} 
else{ 
*ppos+= count; 
ret= count; 
printk("read%d bytes from dev\n", count); 
} 
up(&dev->sem);//解鎖互斥信號量 
returnret; 
} 
static ssize_t mem_write(struct file *filp,const char __user *buf, size_t size, loff_t *ppos)//注意:第二個參數(shù)和read方法不同 
{ 
intret = 0; 
structmem_dev *dev; 
unsignedlong p; 
unsignedlong count; 
printk("mem_write.\n"); 
dev= filp->private_data; 
count= size; 
p= *ppos; 
if(p> MEMDEV_SIZE) 
return0; 
if(count> (MEMDEV_SIZE-p)) 
count= MEMDEV_SIZE - p; 
if(down_interruptible(&dev->sem))//鎖定互斥信號量 
return-ERESTARTSYS; 
if(copy_from_user(dev->data+p,buf, count)){ 
ret= -EFAULT; 
printk("copyfrom user failed\n"); 
} 
else{ 
*ppos+= count; 
ret= count; 
printk("write%d bytes to dev\n", count); 
} 
up(&dev->sem);//解鎖互斥信號量 
returnret; 
} 
static loff_t mem_llseek(struct file *filp,loff_t offset, int whence) 
{ 
intnewpos; 
printk("mem_llseek.\n"); 
switch(whence) 
{ 
case0: 
newpos= offset; 
break; 
case1: 
newpos= filp->f_pos + offset; 
break; 
case2: 
newpos= MEMDEV_SIZE - 1 + offset; 
break; 
default: 
return-EINVAL; 
} 
if((newpos<0)|| (newpos>(MEMDEV_SIZE - 1))) 
return-EINVAL; 
filp->f_pos= newpos; 
returnnewpos; 
} 
static const struct file_operationsmem_fops = { 
.owner= THIS_MODULE, 
.open= mem_open, 
.write= mem_write, 
.read= mem_read, 
.release= mem_release, 
.llseek= mem_llseek, 
}; 
static int __init memdev_init(void) 
{ 
intresult; 
interr; 
inti; 
//申請設(shè)備號 
dev_tdevno = MKDEV(mem_major, 0); 
if(mem_major) 
result= register_chrdev_region(devno, MEMDEV_NUM, "memdev");//注意靜態(tài)申請的dev_t參數(shù)和動態(tài)dev_t參數(shù)的區(qū)別 <                    
關(guān)鍵詞:Linux,設(shè)備驅(qū)動
閱讀本文后您有什么感想? 已有 人給出評價!
- 1      
- 1      
- 1      
- 1      
- 1      
- 1      
