1. 程式人生 > >Linux裝置驅動入門及demo事例

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 檔案裡看到。

這裡看到的就是一段裝置驅動經過註冊之後在 /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