1. gzyueqian
      18078865874
      首頁 > 新聞中心 > > 正文

      linux驅動相關

      更新時間: 2007-06-29 11:14:38來源: 粵嵌教育瀏覽量:848

      對linux的devfs類型的驅動程序的編寫可以從以下幾大內容理解和入手:
      通過分析驅動程序源代碼可以發(fā)現(xiàn)驅動程序一般可分三部分:
      核心數(shù)據(jù)結構;核心數(shù)據(jù)和資源的初始化,注冊以及注消,釋放;底層設備操作函數(shù);

      A.核心數(shù)據(jù)結構
      struct file_operations fops 設備驅動程序接口
      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);
      };


      block_device_operations 塊設備驅動程序接口
      { int (*open) (struct inode *, struct file *);
      int (*release) (struct inode *, struct file *);
      int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
      int (*check_media_change) (kdev_t);
      int (*revalidate) (kdev_t);
      struct module *owner;
      };塊設備的READ().WRITE()不在這里注冊,而是在設備的讀寫請求隊列里注冊,內核在這里將調用通用的blk_read(),blk_write().向讀寫隊列
      發(fā)出讀寫請求.

      Linux 利用這些數(shù)據(jù)結構向內核注冊open(),release(),ioctl(),check_media_change(),rvalidate()等函數(shù)的入口句柄.
      我們將要編寫的open(),release(),ioctl(),check_media_change(),revalidate()等函數(shù),將在驅動初始化的時候,
      通過一個此結構類型的變量向內核提供函數(shù)的 入口.

      struct request_queue_t 設備請求隊列的數(shù)據(jù)結構
      struct request_list {
      unsigned int count;
      unsigned int pending[2];
      struct list_head free;
      };
      struct request {
      struct list_head queue;
      int elevator_sequence;
      kdev_t rq_dev;
      int cmd; /* READ or WRITE */
      int errors;
      unsigned long start_time;
      unsigned long sector;
      unsigned long nr_sectors;
      unsigned long hard_sector, hard_nr_sectors;
      unsigned int nr_segments;
      unsigned int nr_hw_segments;
      unsigned long current_nr_sectors, hard_cur_sectors;
      void * special;
      char * buffer;
      struct completion * waiting;
      struct buffer_head * bh;
      struct buffer_head * bhtail;
      request_queue_t *q;
      };

      struct request_queue
      {
      /*
      * the queue request freelist, one for reads and one for writes
      */
      struct request_list rq;

      /*
      * The total number of requests on each queue
      */
      int nr_requests;

      /*
      * Batching threshold for sleep/wakeup decisions
      */
      int batch_requests;

      /*
      * The total number of 512byte blocks on each queue
      */
      atomic_t nr_sectors;

      /*
      * Batching threshold for sleep/wakeup decisions
      */
      int batch_sectors;

      /*
      * The max number of 512byte blocks on each queue
      */
      int max_queue_sectors;

      /*
      * Together with queue_head for cacheline sharing
      */
      struct list_head queue_head;
      elevator_t elevator;

      request_fn_proc * request_fn;
      merge_request_fn * back_merge_fn;
      merge_request_fn * front_merge_fn;
      merge_requests_fn * merge_requests_fn;
      make_request_fn * make_request_fn;
      plug_device_fn * plug_device_fn;
      /*
      * The queue owner gets to use this for whatever they like.
      * ll_rw_blk doesn't touch it.
      */
      void * queuedata;

      /*
      * This is used to remove the plug when tq_disk runs.
      */
      struct tq_struct plug_tq;

      /*
      * Boolean that indicates whether this queue is plugged or not.
      */
      int plugged:1;

      /*
      * Boolean that indicates whether current_request is active or
      * not.
      */
      int head_active:1;

      /*
      * Boolean that indicates you will use blk_started_sectors
      * and blk_finished_sectors in addition to blk_started_io
      * and blk_finished_io. It enables the throttling code to
      * help keep the sectors in flight to a reasonable value
      */
      int can_throttle:1;

      unsigned long bounce_pfn;

      /*
      * Is meant to protect the queue in the future instead of
      * io_request_lock
      */
      spinlock_t queue_lock;

      /*
      * Tasks wait here for free read and write requests
      */
      wait_queue_head_t wait_for_requests;

      struct request *last_request;
      };

      緩沖區(qū)和對緩沖區(qū)相應的I/O操作在此任務隊列中相關聯(lián),等待內核的調度.如果是字符設備就不需要此數(shù)據(jù)結構.而
      塊設備的read(),write()函數(shù)則在buffer_queue的initize和設備請求隊列進行處理請求時候傳遞給request_fn().
      struct request_queue_t{}設備請求隊列的變量類型,驅動程序在初始化的時候需要填寫request_fn().

      其他的數(shù)據(jù)結構還有 I/O port,Irq,DMA 資源分配,符合POSIX標準的ioctl的cmd的構造和定義,以及描述設備自身的
      相關數(shù)據(jù)結構定義-如設備的控制寄存器的相關數(shù)據(jù)結構定義,BIOS里的參數(shù)定義,設備類型定義等.

      B.初始化和注冊和注消,模塊方式驅動程序的加載和卸載.
      設備驅動程序在定義了數(shù)據(jù)結構后 ,首先開始初始化:
      如I/O 端口的檢查和登記,內核對 I/O PORT的檢查和登記提供了兩個 函數(shù)check_region(int io_port, int off_set)
      和request_region(int io_port, int off_set,char *devname).I/O Port登記后,就可以用inb()和outb()進行操作了 .

      還有DMA和Irq的初始化檢查和 登記,
      int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *),unsigned int long flags,

      const char *device);

      irq: 是要申請的中斷。

      handle:中斷處理函數(shù)指針。

      flags:SA_INTERRUPT 請求一個快速中斷,0 正常中斷。

      device:設備名。

      如果登記成功,返回0,這時在/proc/interrupts文件中可以看你請求的中斷。

      DMA主要是在內存中分配交換內存空間.還有緩沖區(qū),設備請求隊列的初始化.

      還有設備控制寄存器的檢查和初始化,還有對設備自身相關的數(shù)據(jù)結構的初始化,填寫一些設備特定的數(shù)據(jù)等.

      然后,開始注冊
      devfs_register()向VFS注冊統(tǒng)一的設備操作函數(shù).
      static struct file_operations XXX_fops = {
      owner: THIS_MODULE, XXX_fops所屬的設備模塊
      read: XXX_read, 讀設備操作
      write: XXX_write, 寫設備操作
      ioctl: XXX_ioctl, 控制設備操作
      mmap: XXX_mmap, 內存重映射操作
      open: XXX_open, 打開設備操作
      release: XXX_release 釋放設備操作
      /* ... */
      };


      blk_init_queue()隊列初始化函數(shù).
      request_irq()中斷注冊函數(shù)
      相應的注消函數(shù):
      devfs_unregister (devfs_handle_t de){};
      free_irq()釋放中斷,I/O資源,釋放緩沖區(qū),釋放設備,請求隊列,VFS節(jié)點等.

      模塊方式驅動程序的加載和卸載.

      static int __init _init_module (void)
      {
      /* ... */
      }

      static void __exit _cleanup_module (void)
      {

      }

      /* 加載驅動程序模塊入口 */
      module_init(_init_module);

      /* 卸載驅動程序模塊入口 */
      module_exit(_cleanup_module);


      _intrrupt()
      設備發(fā)生中斷時的處理程序.
      {
      1.對共享中斷的處理;
      2.對spinlock以及其他的事務的處理;
      }

      C. 底層設備操作函數(shù)的編寫
      read().write(),open(),release(),check_media_change(),revalidate()等.


      open()和release()

      打開設備是通過調用file_operations結構中的函數(shù)open( )來完成的,它是驅動程序用來為今后的操作完成初始化準備工作的。在大部分驅動程序中,open( )通常需要完成下列工作:

      1. 檢查設備相關錯誤,如設備尚未準備好等。
      2. 如果是次打開,則初始化硬件設備。
      3. 識別次設備號,如果有必要則更新讀寫操作的當前位置指針f_ops。
      4. 分配和填寫要放在file->private_data里的數(shù)據(jù)結構。
      5. 使用計數(shù)增1。

      釋放設備是通過調用file_operations結構中的函數(shù)release( )來完成的,這個設備方法有時也被稱為close( ),它的作用正好與open( )相反,通常要完成下列工作:

      1. 使用計數(shù)減1。
      2. 釋放在file->private_data中分配的內存。
      3. 如果使用計算為0,則關閉設備。

      read()和 write()

      字符設備的讀寫操作相對比較簡單,直接使用函數(shù)read( )和write( )就可以了。但如果是塊設備的話,則需要調用函數(shù)block_read( )和block_write( )來進行數(shù)據(jù)讀寫,這兩個函數(shù)將向設備請求表中增加讀寫請求,以便Linux內核可以對請求順序進行優(yōu)化。由于是對內存緩沖區(qū)而不是直接對設備進行操作的,因此能很大程度上加快讀寫速度。如果內存緩沖區(qū)中沒有所要讀入的數(shù)據(jù),或者需要執(zhí)行寫操作將數(shù)據(jù)寫入設備,那么就要執(zhí)行真正的數(shù)據(jù)傳輸,這是通過調用數(shù)據(jù)結構blk_dev_struct中的函數(shù)request_fn( )來完成的。

      ioctl()--將cmd進行解釋,并送到設備的控制寄存器.事實上,read()和write()也要通過ioctl()來完成操作的 .
      ioctl(){
      CASE CMD{
      SWITCH CASE1:{...};
      SWITCH CASE2:{...};
      SWITCH CASE N:{...};
      .
      .
      DEFAULT : {...};
      }
      END CASE

      總結:
      我們可以看出一個linux的驅動程序通常包含如下:
      初始化設備模塊、
      {I/O port ,DMA.Irq,內存 buffer,初始化并且填寫具體設備數(shù)據(jù)結構,注冊 fops的具體函數(shù)等等 }
      中斷處理模塊、設備釋放模塊、設備卸載模塊
      設備打開模塊、數(shù)據(jù)讀寫和控制模塊、
      驅動裝載模塊、驅動釋放模塊.

      免費預約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 亚洲产在线观看亚洲第一站 | 偷自拍亚洲综合在线 | 色妞综合一区二区三区 | 五月天在线播放综合网 | 婷婷精品视频在线观看的 | 亚洲欧美日韩另类中文字幕组 |