關(guān)于Linux的簡單字符設(shè)備驅(qū)動(dòng)程序
一、重要知識點(diǎn)
1. 主次設(shè)備號
dev_t
dev_t是內(nèi)核中用來表示設(shè)備編號的數(shù)據(jù)類型;
int MAJOR(dev_t dev)
int MINOR(dev_t dev)
這兩個(gè)宏抽取主次設(shè)備號。
dev_t MKDEV(unsigned int major, unsignedint minor)
這個(gè)宏由主/次設(shè)備號構(gòu)造一個(gè)dev_t結(jié)構(gòu)。
2. 分配和釋放設(shè)備號
int register_chardev_region(dev_t first,unsigned int count, char *name)
靜態(tài)申請?jiān)O(shè)備號。
Int alloc_chardev_region(dev_t *dev,unsigned int firstminor, unsigned int count, char *name)
動(dòng)態(tài)申請?jiān)O(shè)備號,注意第一個(gè)參數(shù)是傳地址,而靜態(tài)則是傳值。
3. 幾種重要的數(shù)據(jù)結(jié)構(gòu)
struct file
file結(jié)構(gòu)代表一個(gè)打開的文件,它由內(nèi)核在open時(shí)創(chuàng)建,并傳遞給該文件上進(jìn)行操作的所有函數(shù),直到最后的close函數(shù)。
file結(jié)構(gòu)private_data是跨系統(tǒng)調(diào)用時(shí)保存狀態(tài)信息非常有用的資源。
file結(jié)構(gòu)的f_ops 保存了文件的當(dāng)前讀寫位置。
struct inode
內(nèi)核用inode代表一個(gè)磁盤上的文件,它和file結(jié)構(gòu)不同,后者表示打開的文件描述符。對于單個(gè)文件,可能會有許多個(gè)表示打開文件的文件描述符file結(jié)構(gòu),但他們都指單個(gè)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ū)動(dòng)程序的方法。
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è)備的個(gè)數(shù),要想擁有多個(gè)次設(shè)備,就必須將該參數(shù)設(shè)為次設(shè)備的個(gè)數(shù)。
5. 并發(fā)處理
信號量和自旋鎖的區(qū)別,使用信號量時(shí)當(dāng)調(diào)用進(jìn)程試圖獲得一個(gè)鎖定了的鎖時(shí)會導(dǎo)致進(jìn)程睡眠,而自旋鎖則是一直循法的等待一直到該鎖解鎖了為止。
1)信號量
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);
聲明和初始化用在互斥模式中的信號量的兩個(gè)宏
void init_MUTEX(struct semaphore *sem)
void init_MUTEX_LOCKER(struct semaphore*sem);
這兩個(gè)函數(shù)可以在運(yùn)行時(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)用進(jìn)程置于不可中斷的休眠狀態(tài);相反,down_interruptible可被信號中斷。down_trylock不會休眠,并且會在信號量不可用時(shí)立即返回。鎖定信號量的代碼最后必須使用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ū)動(dòng)代碼
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)//注意:第二個(gè)參數(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;
//申請?jiān)O(shè)備號
dev_tdevno = MKDEV(mem_major, 0);
if(mem_major)
result= register_chrdev_region(devno, MEMDEV_NUM, "memdev");//注意靜態(tài)申請的dev_t參數(shù)和動(dòng)態(tài)dev_t參數(shù)的區(qū)別 <
關(guān)鍵詞:Linux,設(shè)備驅(qū)動(dòng)
閱讀本文后您有什么感想? 已有 人給出評價(jià)!
- 1
- 1
- 1
- 1
- 1
- 1