Linux裝置驅動入門及demo事例
在 Unix like 系統中,正是有了驅動程式才使得使用者可以完全透明的使用計算機系統。裝置驅動隱藏了硬體裝置的具體的細節和功能,對於不同的硬體裝置都提供了一致的介面。比如在 Linux 系統中,為了便於使用者的使用,系統把計算機系統的各種裝置對映成一系列的特殊的裝置檔案,叫裝置檔案節點。使用者可以任意的使用它來協助自己完成任何工作。在 Linux 系統中,所有的裝置都可歸為三類之列,一是字元裝置,如鍵盤等;二是塊裝置,如磁碟等;第三種就是用於網路的網路裝置,這類裝置主要是用於網路通訊中,在其他的地方沒有什麼特別使用。
一、 裝置驅動程式的三個主要組成部分
1、 自動配置和初始化子程式;
2、 為 I/O 提供服務的子程式;
3、 中斷子程式;
二、 裝置驅動入口點分析
此處以字元裝置為例列舉裝置驅動程式的入口點
1、 open 入口點:裝置 I/O 操作之前進行的操作,打開了裝置後準備 I/O 操作;
2、 close 入口點:對裝置操作完成之後對裝置的關閉操作;
3、 read 入口點:完成對裝置的 open 操作之後就可以以此入口點在裝置上讀取資料;
4、 write 入口點:完成對裝置的 open 操作之後就可以以此入口點在裝置上寫入資料;
5、 ioctl 入口點:在裝置上執行 I/O 控制操作;
6、 select 入口點:在資料寫入或讀取之前對裝置的檢測操作。
三、 Linux 系統下的裝置驅動程式
以下借用 Linux 系統核心的程式碼來分析字元裝置驅動程式
1 、以下是入口點的結構體定義:
#/usr/include/linux/fs.h
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, 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);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};
2 、入口點 file_operations 分析:
1> lseek ,移動檔案指標的位置,用於可隨機存取的裝置;
2> read ,裝置讀操作,引數 buf 指向的是存放讀取結果的緩衝池, count 為將要讀取的資料的長度。若函式返回值為負,表示讀取操作出錯;若操作成功,則返回實際的讀取的位元組;
3> write ,裝置寫操作;
4> ioctl ,進行除裝置的讀寫之外的控制操作;
5> open ,開啟一個裝置,並對其 I/O 操作準備。函式返回值為 0 表示裝置開啟成功,若返回負,表示操作失敗;
6> release ,裝置的 close 操作;
4、 Linux 系統下,字元裝置驅動的註冊:
在 Linux 裡通過呼叫 register_chrdev 向作業系統註冊字元型的裝置驅動,其定義為:
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops) ;
引數說明:
major 是裝置驅動程式註冊的主裝置號,若為 0 則系統為此驅動程式動態地分配一個主裝置號。
name 是裝置檔案節點名。此函式返回 0 表示成功。返回 -EINVAL 表示申請的主裝置號不合法,一般來說是因為主裝置號大於系統所允許的最大裝置號。返回 -EBUSY 表示所申請的主裝置號正在被其它裝置驅動程式使用。如果是動態分配主裝置號成功,此函式將返回所分配的主裝置號。如果 register_chrdev 操作成功,裝置名就可以在 /proc/devices 檔案裡看到。
|
四、 字元裝置驅動程式示例及其分析
//#define CONFIG_DEVFS_FS /* 系統是否支援 devfs*/
#ifndef __KERNEL__ /* 編譯進核心 */
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE /* 以模組方式編譯 */
#endif
#include <linux/config.h> /* 配置標頭檔案 */
#include <linux/module.h> /* 驅動模型標頭檔案 */
#include <linux/devfs_fs_kernel.h> /* 裝置檔案系統標頭檔案 */
#include <linux/init.h> /* 初始化相關標頭檔案 */
#include <linux/kernel.h> /* 與 printk() 等函式有關的標頭檔案 */
#include <linux/slab.h> /* 與 kmalloc() 等函式有關的標頭檔案 */
#include <linux/fs.h> /* 與檔案系統有關的標頭檔案 everything... */
#include <linux/errno.h> /* 錯誤程式碼處理標頭檔案 error codes */
#include <linux/types.h> /* 資料型別標頭檔案 size_t */
#include <linux/proc_fs.h> /* 與程序排程相關的標頭檔案 */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/poll.h> /* COPY_TO_USER */
#include <asm/system.h> /* cli(), *_flags */
#define DEVICE_NAME " demo" /* 該驅動的裝置名 */
#define demo_MAJOR 254 /* 驅動主裝置號 */
#define demo_MINOR 0 /* 驅動次裝置號 */
static int MAX_BUF_LEN=1024; /* 定義一緩衝區最大長度 */
static char drv_buf[1024]; /* 定義一緩衝區 */
static int WRI_LENGTH=0;
/***********************************************************************
* 名稱: demo_write ()
* 功能:對應使用者空間的 write 系統呼叫,從使用者空間拷貝給定長度緩衝區資料到核心空間
* 入口引數: *filp 操作裝置檔案的 ID , *buffer 對應使用者空間的緩衝區的起始地址, count 使用者空間資料緩衝區長度
* 出口引數:返回使用者空間資料緩衝區長度
**********************************************************************/
static ssize_t demo_write(struct file *filp,const char *buffer, size_t count)
{
if(count > MAX_BUF_LEN)count = MAX_BUF_LEN;
copy_from_user(drv_buf , buffer, count); /* 從使用者空間拷貝緩衝區資料到核心空間的關鍵函式,把使用者空間的 buffer 資料傳遞給 drv_buf 陣列 */
WRI_LENGTH = count;
printk("user write data to driver/n");
do_write();
return count;
}
/***********************************************************************
* 名稱: static void do_write()
* 功能:
* 入口引數:無
* 出口引數:無
**********************************************************************/
static void do_write()
{
int i;
int len = WRI_LENGTH;
char tmp;
for(i = 0; i < (len>>1); i++,len--){
tmp = drv_buf[len-1];
drv_buf[len-1] = drv_buf[i];
drv_buf[i] = tmp;
}
}
/*************************************************************
* 名稱: demo_ read ()
* 功能:對應使用者空間的 read 系統呼叫,從核心空間拷貝給定長度緩衝區資料到使用者空間
* 入口引數: *filp 操作裝置檔案的 ID , *buffer 對應使用者空間的緩衝區的起始地址, count 使用者空間資料緩衝區長度, *ppos 使用者在檔案中進行儲存操作的位置
* 出口引數:返回使用者空間資料緩衝區長度
**********************************************************************/
static ssize_t demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
if(count > MAX_BUF_LEN)
count=MAX_BUF_LEN;
copy_to_user(buffer, drv_buf,count); /* 從核心空間拷貝緩衝區資料到使用者空間的關鍵函式,把核心空間經過逆序排列後的 drv_buf 陣列傳遞給使用者空間的 buffer 陣列 */
printk("user read data from driver/n");
return count;
}
/***********************************************************************
* 名稱: demo_ioctl ()
* 功能:對應使用者空間的 ioctl 系統呼叫,對使用者空間傳遞過來的命令進行 swith 判斷,並進行相應處理,本函式只對使用者空間傳遞過來的 1 , 2 做簡單的處理,列印一條資訊
* 入口引數: *filp 操作裝置檔案的 ID , cmd 對應使用者空間的 cmd , arg 對應使用者空間傳遞過來的引數
列表
* 出口引數:正確返回 0 ,錯誤命令返回 default 的提示內容
**********************************************************************/
static int demo_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
switch(cmd){
case 1:printk("runing command 1 /n");break;
case 2:printk("runing command 2 /n");break;
default:
printk("error cmd number/n");break;
}
return 0;
}
/***********************************************************************
* 名稱: static void demo_open ()
* 功能:裝置檔案開啟函式,對應使用者空間 open 系統呼叫,
* 入口引數:裝置檔案節點
* 出口引數:無
**********************************************************************/
static int demo_open(struct inode *inode, struct file *file)
{
MOD_INC_USE_COUNT;
printk("device open sucess!/n");
return 0;
}
* 名稱: static int demo_release
* 功能:裝置檔案釋放函式,對應使用者空間 close 系統呼叫,
* 入口引數:裝置檔案節點
* 出口引數:無
**********************************************************************/
static int demo_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
printk("device release/n");
return 0;
}
原文出處:http://blog.csdn.net/pinglinux/article/details/6526934