亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

設備驅動

2019-11-17 05:15:38
字體:
來源:轉載
供稿:網友

  概述

一. linux設備概述
在概念上一般把設備分為字符設備、塊設備。字符設備是指設備發送和接收數據以字符形式的進行;而塊設備則以整個數據緩沖區的形式進行。但是,由于網絡設備等有其非凡性,實際上系統對它們單獨處理。

系統用主設備號(MAJOR)加次設備(MINOR)號來唯一標識一個設備。相同主設備號表示同一類設備,例如都是硬盤;次設備號標識同類設備的個數。所有設備在適當的目錄(通常在/dev目錄下)下必須有相應的文件,這樣字符設備和塊設備都可以通過文件操作的系統調用了完成。不同的是,塊設備操作經常要和緩沖區打交道,更加復雜一點。



[目錄]

--------------------------------------------------------------------------------


數據結構

二. 主要數據結構
與設備治理有關的主要數據結構如下:
1、登記設備治理
系統對已登記設備的治理是由chrdevs和blkdevs這兩張列表來完成的:
/*srcfsdevices.c*/
strUCt device_struct {
const char * name; //指向設備名稱
struct file_Operations * fops; //指向設備的訪問操作函數集,file_operati
ons定義在
include/linux/fs.h中
};
static struct device_struct chrdevs[MAX_CHRDEV] = {
{ NULL, NULL },
}; //所有系統登記的字符設備列表
static struct device_struct blkdevs[MAX_BLKDEV] = {
{ NULL, NULL },
} //所有系統登記的塊設備列表

實際上這兩張列表的結構是一樣的,但在登記時每個結構元素的值會不同(見初始化部分)。linux對設備的進行訪問時,訪問文件系統中相應的文件,通過文件系統和文件的屬性描述塊,系統可以找到該文件系統或文件對應設備的設備號。在實際訪問列表時,以chrdevs[MAJOR][MINOR]或blkdevs[MAJOR][MINOR]形式訪問,相同主設備號(MAJOR)的元素中fops的內容相同。

文件系統中相關的的數據結構如下:
struct super_block {
kdev_t s_dev; //該文件系統所在設備的設備標志符

} //每個文件系統對應一個super_block
struct inode {
kdev_t i_dev; //該文件所在設備的設備標志符通過它可以找到在設備列表中
… 相應設備
} //每個文件對應一個inode

2、I/O請求治理

系統會把一部分系統內存作為塊設備驅動程序與文件系統接口之間的一層緩沖區,每個緩沖區與某臺塊設備中的特定區域相聯系,文件系統首先試圖存在相應的緩沖區,如未找到就向該設備發出I/O讀寫請求,由設備驅動程序對這些請求進行處理。因此,需要有相應的數據結構進行治理。

/*srcincludelinuxlkdev.h*/
struct blk_dev_struct {
void (*request_fn)(void); //指向請求處理函數的指針,請求處理函數是寫設備驅動程序的重要一環,設備驅動程序在此函數中通過outb向位于I/O空間中的設備命令寄存器發出命令

struct request * current_request; //指向當前正在處理的請求,它和plug共同維護了該設備的請求隊列
struct request plug; //這是LINUX2.0版本與以前版本的一個不同之處,plug主要被用于異步提前讀寫操作,在這種情況下,由于沒有非凡的請求,為了提高系統性能,需要等發送完所有的提前讀寫請求才開始進行請求處理,即unplug_device。
struct tq_struct plug_tq; //設備對應的任務隊列
};
/*srcdriverslockll_rw_blk.c*/
struct blk_dev_struct blk_dev[MAX_BLKDEV];
其中每個請求以request的類型的結構進行傳遞,定義如下:
/*srcincludelinuxlk_dev.h*/
struct request {
volatile int rq_status; //表示請求的狀態
kdev_t rq_dev; //是該請求對應的設備號,kdev_t是unsigned s
hort類型,高8位是主設備號,低8位是從設備號,每一請求都針對一個設備發出的;
int cmd; //表示該請求對應的命令,取READ或WRITE;
int errors;
unsigned long sector; //每一扇區的字節數
unsigned long nr_sectors; //每一扇區的扇區數
unsigned long current_nr_sectors; //當前的扇區數;

char * buffer; //存放buffer_head.b_data值,表示發出請求的
數據存取地址;
struct semaphore * sem; //一個信號量,用來保證設備讀寫的原語操作,

sem=0時才能處理該請求;
struct buffer_head * bh; //讀寫緩沖區的頭指針
struct buffer_head * bhtail; //讀寫緩沖區的尾指針
struct request * next; //指向下一個請求
};

對不同塊設備的所有請求都放在請求數組all_requests中,該數組實際上是一個請求緩沖池,請求的釋放與獲取都是針對這個緩沖池進行;同時各個設備的請求用next指針聯結起來,形成各自的請求隊列。定義如下:

/*srcdriverslokcll_rw_blk.c*/
static struct request all_requests[NR_REQUEST];

3、中斷請求

設備進行實際的輸入/輸出操作時,假如時間過長而始終被占用CPU,就會影響系統的效率,必須有一種機制來克服這個問題而又不引起其他問題。中斷是最理想的方法。和中斷有關的數據結構是;

struct irqaction {
void (*handler)(int, void *, struct pt_regs *); //指向設備的中斷響應函數,它在系統初始化時被置入。當中斷發生時,系統自動調用該函數
unsigned long flags; //指示了中斷類型,如正常中斷、快速中斷等
unsigned long mask; //中斷的屏蔽字
const char *name; //設備名
void *dev_id; //與設備相關的數據類型,中斷響應函數可以根

據需要將它轉化所需的數據指針,從而達到訪問系統數據的功能
struct irqaction *next; //指向下一個irqaction
};

由于中斷數目有限,且很少更新,所以系統在初始化時,從系統堆中分配內存給每一個irq_action指針,通過next指針將它們連成一個隊列。

4、高速緩沖區

為了加速對物理設備的訪問速度,linux將塊緩沖區放在Cache內,塊緩沖區是由buffer_head連成的鏈表結構。buffer_head的數據結構如下:
/*includelinuxfs.h*/
struct buffer_head {
unsigned long b_blocknr; /* block number */
kdev_t b_dev; /* device (B_FREE = free) */
kdev_t b_rdev; /* Real device */
unsigned long b_rsector; /* Real buffer location on disk */
struct buffer_head * b_next; /* Hash queue list */
struct buffer_head * b_this_page; /* circular list of buffers in one page *
/
unsigned long b_state; /* buffer state bitmap (see above) */
struct buffer_head * b_next_free;
unsigned int b_count; /* users using this block */
unsigned long b_size; /* block size */
char * b_data; /* pointer to data block (1024 bytes) */
unsigned int b_list; /* List that this buffer appears */
unsigned long b_flushtime; /* Time when this (dirty) buffer should be
written */
unsigned long b_lru_time; /* Time when this buffer was last used. */
struct wait_queue * b_wait;
struct buffer_head * b_PRev; /* doubly linked list of hash-queue */
struct buffer_head * b_prev_free; /* doubly linked list of buffers */
struct buffer_head * b_reqnext; /* request queue */
};

塊緩沖區主要由鏈表組成??臻e的buffer_head組成的鏈表是按塊大小的不同分類組成,linux目前支持塊大小為512、1024、2048、4096和8192字節;第二部分是正在用的塊,塊以Hash_table的形式組織,具有相同hash索引的緩沖塊連在一起,hash索引根據設備標志符和該數據塊的塊號得到;同時將同一狀態的緩沖區塊用LRU算法連在一起。對緩沖區的各個鏈表定義如下:
/* fsuffer.c*/
static struct buffer_head ** hash_table;
static struct buffer_head * lru_list[NR_LIST] = {NULL, };
static struct buffer_head * free_list[NR_SIZES] = {NULL, };
static struct buffer_head * unused_list = NULL;
static struct buffer_head * reuse_list = NULL;





[目錄]

--------------------------------------------------------------------------------


初始化

三. 設備的初始化
LINUX啟動時,完成了實模式下的系統初始化(arch/i386/boot/setup.S)與保護模式下的核心初始化包括初始化寄存器和數據區(arch/i386/boot/compressed/head.S)、核心代碼解壓縮、頁表初始化(arch/i386/kernel/head.S)、初始化idt、gdt和ldt等工作
后,系統轉入了核心。調用函數start_kernel啟動核心(init/main.c)后,將繼續各方面的初始化工作,其中start_kernel最后將調用kernel_thread (init, NULL, 0),創建init進程進行系統配置(其中包括所有設備的初始化工作)。
static int init(void * unused)
{
…………
/* 創建后臺進程bdflush,以不斷循環寫出文件系統緩沖區中"臟"的內容 */
kernel_thread(bdflush, NULL, 0);
/* 創建后臺進程kswapd,專門處理頁面換出工作 */
kswapd_setup();
kernel_thread(kswapd, NULL, 0);
…………
setup();
…………
在setup函數中,調用系統調用sys_setup()。sys_setup()的定義如下:
//fs/filesystems.c
asmlinkage int sys_setup(void)
{
static int callable = 1;
… …
if (!callable)
return -1;
callable = 0;
… …
device_setup();
… …
在該系統調用中,靜態變量callable保證只被調用實際只一次,再次調用時后面的初始化程序不執行。在該調用開始就先進行設備的初始化:device_setup()。
//dirvers/block/genhd.c
void device_setup(void)
{
extern void console_map_init(void);
… …
chr_dev_init();
blk_dev_init();
… …
可以看到device_setup()將依次執行chr_dev_init()、blk_dev_init()等各類設備的初始化程序。每個具體的init函數的內容和具體設備就有關了,但是它們都有一些必須完成的任務:
1、 告訴內核這一驅動程序使用的主設備號,同時提供指向file_operation的指針,以完成對chrdevs和blkdevs的初始化。
2、 對塊設備,需要將輸入/輸出處理程序的入口地址告訴內核。
3、 對塊設備,需要告訴緩沖區設備存取的數據塊的大小。





[目錄]

--------------------------------------------------------------------------------


治理流程

四. 設備治理的流程
下面我們介紹一下整個設備治理的流程。我們以塊設備為例,字符設備的流程也和塊設備類似,只是沒有請求隊列治理。
首先,文件系統通過調用ll_rw_block發出塊讀寫命令,讀寫請求治理層接到命令后,向系統申請一塊讀寫請求緩沖區,在填寫完請求信息后,請求進入設備的讀寫請求隊列等候處理。假如隊列是空的,則請求立即得到處理,否則由系統負責任務調度,喚醒請求處理。在請求處理過程中,系統向I/O空間發出讀寫指令返回。當讀寫完畢后,通過中斷通知系統,同時調用與設備相應的讀寫中斷響應函數。





[目錄]

--------------------------------------------------------------------------------


添加字符設備

五. 添加一個字符設備
作為對linux設備治理的分析的總結,我們介紹一下如何添加一個設備,首先介紹如何添加一個字符設備。在后面的文章中,我們將新添加的設備稱為新設備,說明以我們實現的虛擬的字符設備為例,步驟基本如下:
1. 確定設備的設備名稱和主設備號:
我們必須找一個還沒有被使用的主設備號,分配給自己的字符設備。假設主設備號為30(在2.0.34的內核中還沒有以30作為主設備號的字符設備)。

2. 確定編寫需要的file_operations中的操作函數,包括:
static int my_open(struct inode * inode,struct file * file)
//通過宏指令MINOR()提取inode參數的I_rdev字段,確定輔助設備號,然后檢查相應的讀寫忙標志,看新設備是否已經打開。假如是,返回錯誤信息;
否則置讀寫忙標志為true,阻止再次打開新設備。
static void my_release(struct inode * inode,struct file * file)
//同my_open類似,只是置讀寫忙標志為false,答應再次打開新設備。
static int my _write(struct inode * inode,struct file * file,const char * bu
ffer,int count)

//用于對該設備的寫
static int my _read(struct inode * inode , struct file * file,char * buffer,
int count)
//用于對該設備的讀
static int my_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
//用于傳送非凡的控制信息給設備驅動程序,或者長設備驅動程序取得狀態信息,在我們實現的虛擬字符設備中,這個函數的功能是用來打開和關閉跟蹤功能。

3. 確定編寫需要的初始化函數:
void my_init(void)
//首先需要將上述的file_operations中的操作函數的地址賦給某個file_operations的結構變量my_fops中的相應域;然后調用標準內核函數登記該設備:register_chrdev(30,"mychd",&my_fops);最后對必要的變量(例如讀寫忙標志、跟蹤標志等)賦初值。

4. 在drivers/char/mem.c中添加相應語句;
在chr_dev_init函數之前添加drgn_init的原型說明:
void my_init (void);
在chr_dev_init函數的return語句之前添加以下語句:
my_init (); //用于在字符設備初始化時初始化新設備

5. 修改drivers/char/Makefile;
假設我們把所以必要的函數寫mychd.c中,則找到"L_OBJS := tty_io.o n_tty.o con
sole.o "行,將"mychd.o"加到其中。
6. 將該設備私有的*.c,*.h復制到目錄drivers/char下。
7. 用命令:make clean;make dep;make zImage重新編譯內核。
8. 用mknod命令在目錄/dev下建立相應主設備號的用于讀寫的非凡文件。
完成了上述步驟,你在linux環境下編程時就可以使用新設備了。





[目錄]

--------------------------------------------------------------------------------


添加塊設備

六. 添加一個塊設備
接下來我們將介紹如何添加一個塊設備。在后面的文章中,我們將新添加的塊設備稱為新設備,塊設備的添加過程和字符設備有相似之處,我們將主要介紹其不同點,步驟基本如下:
1. 確定設備的設備名稱和主設備號
我們必須找一個還沒有被使用的主設備號,分配給自己的新設備。假設主設備號為30(在2.0.34的內核中還沒有以30作為主設備號的塊設備),則需要在include/linux/major.h中加入如下句:
#define MY_MAJOR 30
這樣我們可以通過MY_MAJOR來確定設備為新設備,保證通用性。

2. 確定編寫需要的file_operations中的操作函數:
static int my_open(struct inode * inode,struct file * file)
static void my_release(struct inode * inode,struct file * file)
static int my_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
由于使用了高速緩存,塊設備驅動程序就不需要包含自己的read()、write()和fsync()函數,但必須使用自己的open()、 release()和 ioctl()函數,這些函數的作用和字符設備的相應函數類似。

3. 確定編寫需要的輸入/輸出函數:
static int my _read(void) //正確處理時返回值為1,錯誤時返回值為0
static int my _write(void) //正確處理時返回值為1,錯誤時返回值為0
值得注重的是這兩個函數和字符設備中的my read()、mywrite()函數不同:

參數不同:字符設備中的函數是帶參數的,用于對其工作空間(也可以看成是簡單的緩沖區)中的一定長度(長度也是參數傳遞的)的字符進行讀寫;而塊設備的函數是沒有參數的,它通過當前請求中的信息訪問高速緩存中的相應的塊,因此不需要參數輸入。
調用形式不同:字符設備中的函數地址是存放在file_operations中的,在對字符設備進行讀寫時就調用了;而塊設備的讀寫函數是在需要進行實際I/O時在request中調用。這在后面可以看到。

4. 確定編寫需要的請求處理函數:
static void my_request(void)
在塊設備驅動程序中,不帶中斷服務子程序的請求處理函數是簡單的,典型的格式如下

static void my_request(void)
{
loop:
INIT_REQUEST;

if (MINOR(CURRENT->dev)>MY_MINOR_MAX)
{
end_request(0);
goto loop;
}

if (CURRENT ->cmd==READ)
//CUREENT是指向請求隊列頭的request結構指針
{
end_request(my_read()); //my_read()在前面已經定義
goto loop;
}


if (CURRENT ->cmd==WRITE)
{
end_request(my_write()); //my_write()在前面已經定義
goto loop;
}

end_request(0);
goto loop;
}
實際上,一個真正的塊設備一般不可能沒有中斷服務子程序,另外設備驅動程序是在系統調用中被調用的,這時由內核程序控制CPU,因此不能搶占,只能自愿放棄;因此驅動程序必須調用sleep_on()函數,釋放對CPU的占用;在中斷服務子程序將所需的數據復制到內核內存后,再由它來發出wake_up()調用,

5. 假如需要,編寫中斷服務子程序
實際上,一個真正的塊設備一般不可能沒有中斷服務子程序,另外設備驅動程序是在系統調用中被調用的,這時由內核程序控制CPU,因此不能搶占,只能自愿放棄;因此驅動程序必須調用sleep_on()函數,釋放對CPU的占用;在中斷服務子程序將所需的數據復制到內核內存后,再由它來發出wake_up()調用。
另外兩段中斷服務子程序都要訪問和修改特定的內核數據結構時,必須要仔細協調,以防止出現災難性的后果。

首先,在必要時可以禁止中斷,這可以通過sti()和cli()來答應和禁止中斷請求。
其次,修改特定的內核數據結構的程序段要盡可能的短,使中斷不至于延時過長。含有中斷服務子程序的塊設備驅動程序的編寫相對比較復雜,我們還沒有完全實現,主要問題是在中斷處理之間的協調。因為這些程序是要加入內核的,系統默認為你是完全正確的,假如引起循環或者中斷長時間不響應,結果非常嚴重。我們正在努力實現這一程序。

6. 確定編寫需要的初始化函數:
void my_init(void)
需要將的file_operations中的操作函數的地址賦給某個file_operations的結構變量my_fops中的相應域;一個典型的形式是:
struct file_operations my_fops=
{
0,
block_read,
block_write,
0,
0,
my_ioctl,
0,
my_open,
my_release,
block_fsync,
0,
0,
0,
}
my_init中需要作的工作有:

首先調用標準內核函數登記該設備:
register_chrdev(MY_MOJOR,"my--bdev",&my_fops);
將request()函數的地址告訴內核:
blk_dev[MY_MAJOR].request_fn=DEVICE_REQUEST;
DEVICE_REQUEST是請求處理函數的地址,它的定義將在稍后可以看到。
告訴新設備的高速緩存的數據塊的塊大?。?br />my_block_size=512; //也可以是1024等等
blksize_size[MY_MAJOR]=& my_block_size;
為了系統在初始化時能夠對新設備進行初始化,需要在blk_dev_init()中添加一行代碼,可以插在blk_dev_init()中return 0的前面,格式為:
my_init();

7. 在include/linux/blk.h中添加相應語句;
到目前為止,除了DEVICE_REQUEST符合外,還沒有告訴內核到那里去找你的request()函數,為此需要將一些宏定義加到blk.h中。在blk.h中找到類似的一行:
#endif /*MAJOR_NR==whatever */
在這行前面加入如下宏定義:
#elif (MAJOR_NR==whatever)

static void my_request(void);
#define DEVICE_NAME "MY_BLK_DEV" //驅動程序名稱
#define DEVICE_REQUEST my_request //request()函數指針
#define DEVIEC_NR(device) (MINOR(device)) //計算實際設備號
#define DEVIEC_ON(device) //用于需要打開的設備
#define DEVIEC_OFF(device) //用于需要關閉的設備
8. 修改drivers/block/Makefile;
假設我們把所以必要的函數寫mybd.c中,則找到"L_OBJS := tty_io.o n_tty.o cons
ole.o "行,將"mybd.o"加到其中。
9. 將該設備私有的*.c,*.h復制到目錄drivers/block下。
10. 用命令:make clean;make dep;make zImage重新編譯內核。
11. 用mknod命令在目錄/dev下建立相應主設備號的用于讀寫的非凡文件。
完成了上述步驟,你在linux環境下編程時就可以使用新設備了。





[目錄]

--------------------------------------------------------------------------------


一個虛擬的字符設備驅動程序

七. 一個虛擬的字符設備驅動程序
以下是一個虛擬的字符設備驅動程序,該程序是我和潘剛同學的試驗結果,本來我們還打算寫一個虛擬的塊設備驅動程序,由于時間關系,沒有能夠完全明白中斷中斷服務子程序的編寫方法,因此沒有沒有能夠實現一個可以所有的虛擬的塊設備,非常遺憾。不過主要步驟已經在上文中進行了介紹,我想再有一段時間應該能夠完成,到時候一定交給李老師看一下。
虛擬的字符設備驅動程序如下,在潘剛同學的試驗報告中也有介紹:
/* drgn.h */
#ifdef KERNEL
#define TRACE_TXT(text) {if(drgn_trace) {console_print(text);console_print("


");}}
#define TRACE_CHR(chr) {if(drgn_trace) console_print(chr);}
#define DRGN_READ 1
#define DRGN_WRITE 0
#endif
#define FALSE 0
#define TRUE 1
#define MAX_BUF 120
#define DRGN_TRON (('M' << icon_cool.gif0x01)
#define DRGN_TROFF (('M' << icon_cool.gif0x02)
struct drgn_buf
{
int buf_size;
char buffer[MAX_BUF];
struct drgn_buf *link;
};
/* drgn.c */
#define KERNEL
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
#include "drgn.h"
static int drgn_trace;
static int write_busy;
static int read_busy;
static struct drgn_buf * qhead;
static struct drgn_buf * qtail;
static int drgn_read(struct inode * , struct file * , char * , int );
static int drgn_write(struct inode * , struct file * , const char *, int );
static int drgn_ioctl(struct inode * , struct file * , unsigned int , unsig
ned long );
static int drgn_open(struct inode *,struct file *);
static void drgn_release(struct inode *,struct file *);
/* extern void console_print(char *);*/
struct file_operations drgn_fops=
{
NULL,
drgn_read,
drgn_write,
NULL,
NULL,
drgn_ioctl,
NULL,
drgn_open,
drgn_release,
NULL,
NULL,
NULL,
NULL
};
void drgn_init(void)
{
drgn_trace=TRUE;
if(register_chrdev(30,"drgn",&drgn_fops))
TRACE_TXT("Cannot register drgn driver as major device 30.")
else
TRACE_TXT("Tiny devie driver registered successfully.")
qhead=0;
write_busy=FALSE;
read_busy=FALSE;
/* drgn_trace=FALSE;*/
return;
}
static int drgn_open(struct inode * inode,struct file * file)
{
TRACE_TXT("drgn_open")
switch (MINOR(inode->i_rdev))
{
case DRGN_WRITE:
if(write_busy)
return -EBUSY;
else{
write_busy=TRUE;
return 0;
}
case DRGN_READ:
if(read_busy)
return -EBUSY;
else{
read_busy=TRUE;
return 0;
}
default:
return -ENXIO;
}
}
static void drgn_release(struct inode * inode,struct file * file)
{
TRACE_TXT("drgn_release")
switch (MINOR(inode->i_rdev))
{
case DRGN_WRITE:
write_busy=FALSE;
return;
case DRGN_READ:
read_busy=FALSE;
return;
}
}
static int drgn_write(struct inode * inode,struct file * file,

const char * buffer,int count)
{
int i,len;
struct drgn_buf * ptr;
TRACE_TXT("drgn_write")
if (MINOR(inode->i_rdev)!=DRGN_WRITE)
return -EINVAL;
if ((ptr=kmalloc(sizeof(struct drgn_buf),GFP_KERNEL))==0)
return -ENOMEM;
len=count < MAX_BUF?count:MAX_BUF;
if (verify_area(VERIFY_READ,buffer,len))
return -EFAULT;
for(i=0;i < count && i<MAX_BUF;++i)
{
ptr->buffer[i]=(char) get_user((char*)(buffer+i));
TRACE_CHR("w")
}
ptr->link=0;
if(qhead==0)
qhead=ptr;
else
qtail->link=ptr;
qtail=ptr;
TRACE_CHR("
")
ptr->buf_size=i;
return i;
}
static int drgn_read(struct inode * inode , struct file * file,
char * buffer, int count)
{
int i,len;
struct drgn_buf * ptr;
TRACE_TXT("drgn_read")
if(MINOR(inode->i_rdev)!=DRGN_READ)
return -EINVAL;
if (qhead==0)
return -ENODATA;
ptr=qhead;
qhead=qhead->link;
len=count < ptr->buf_size?count:ptr->buf_size;
if (verify_area(VERIFY_WRITE,buffer,len))
return -EFAULT;
for (i=0; i<count && i<ptr->buf_size; ++i)
{
put_user((char) ptr->buffer[i],(char *)(buffer+i));
TRACE_CHR("r")
}
TRACE_CHR("
")
kfree_s(ptr,sizeof(struct drgn_buf));
return i;
}
static int drgn_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
TRACE_TXT("drgn_ioctl")
/* if (cmd==DRGN_TRON){
drgn_trace=TRUE;
return 0;
}
else
if (cmd==DRGN_TROFF){
drgn_trace=FALSE;
return 0;
}
else
return -EINVAL;*/
switch(cmd)
{
case DRGN_TRON:
drgn_trace=TRUE;
return 0;
case DRGN_TROFF:
drgn_trace=FALSE;
return 0;
default:
return -EINVAL;
}
}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
5278欧美一区二区三区| 夜夜嗨av色一区二区不卡| 亚洲精品成a人在线观看| 亚洲的天堂在线中文字幕| 欧美孕妇与黑人孕交| 亚洲精品电影久久久| 亚洲色图激情小说| 一区二区三区动漫| 性色av香蕉一区二区| 久久影视电视剧凤归四时歌| 欧美另类高清videos| 亚洲韩国欧洲国产日产av| 国产午夜精品全部视频在线播放| 欧美激情视频在线| 欧美午夜宅男影院在线观看| 国产在线观看不卡| 久久久噜噜噜久噜久久| 亚洲欧洲日产国产网站| 国产美女久久精品香蕉69| 97香蕉久久超级碰碰高清版| 尤物99国产成人精品视频| 欧美日韩久久久久| 亚洲日韩中文字幕| 国产精品久久久久91| 国产97人人超碰caoprom| 九九九热精品免费视频观看网站| 91精品视频观看| 欧美日韩激情网| 国产日韩精品综合网站| 91精品久久久久久久| 亚洲一区二区少妇| 成人精品视频99在线观看免费| 疯狂做受xxxx高潮欧美日本| 中文字幕日韩专区| 亚洲国产99精品国自产| 精品久久久久久中文字幕| 欧美疯狂做受xxxx高潮| 国产成人拍精品视频午夜网站| 欧美精品在线免费播放| 欧美激情免费看| 亚洲国产日韩欧美在线动漫| 全亚洲最色的网站在线观看| 久久久日本电影| 亚洲国产精品99久久| 欧美激情国产精品| 日韩av手机在线| 91国自产精品中文字幕亚洲| 92国产精品视频| 久热爱精品视频线路一| 欧美午夜精品久久久久久人妖| 国内精品免费午夜毛片| 色先锋久久影院av| 亚洲国产一区二区三区四区| 亚洲xxxxx性| 成人激情视频免费在线| 日韩av在线看| 国产一区二区黄| 国产精品一区二区电影| 欧美日韩国产精品一区二区不卡中文| 国产日本欧美一区二区三区在线| 国产欧美中文字幕| 日韩激情av在线免费观看| 69精品小视频| 欧美高清videos高潮hd| 5252色成人免费视频| 亚洲高清免费观看高清完整版| 欧美日韩免费网站| 97国产一区二区精品久久呦| 国产精品成人品| 国产精品欧美日韩一区二区| 欧美日韩在线视频首页| 少妇av一区二区三区| 久久久久久久久久国产| 国产精品久久久久aaaa九色| 亚洲午夜av电影| 国产精品∨欧美精品v日韩精品| 97视频在线观看免费| 成人网在线免费看| 久久高清视频免费| 国产精品国产三级国产aⅴ9色| 91精品国产自产在线老师啪| 日韩精品在线视频美女| 2019国产精品自在线拍国产不卡| 最近2019中文免费高清视频观看www99| 欧美自拍大量在线观看| 亚洲欧美综合精品久久成人| 久99久在线视频| 亚洲欧美日韩在线高清直播| 久久全球大尺度高清视频| 欧美在线观看网站| 97超级碰在线看视频免费在线看| 久久久综合免费视频| 久久久久久美女| 亚洲成人激情在线| 国产精品99久久久久久www| 久久97久久97精品免视看| 精品视频在线播放色网色视频| 国产午夜精品全部视频播放| 日本高清不卡的在线| 中文字幕少妇一区二区三区| 亚洲免费高清视频| 国产欧美最新羞羞视频在线观看| 欧美—级高清免费播放| 三级精品视频久久久久| 中文亚洲视频在线| 日韩欧美有码在线| 日韩视频―中文字幕| 欧美激情精品久久久久久大尺度| 欧美成人sm免费视频| 亚洲美女又黄又爽在线观看| 亚洲精品免费网站| 久久精品国产96久久久香蕉| 久久久久久国产精品| 久久午夜a级毛片| 亚洲人线精品午夜| 亚洲国产成人精品电影| 国产精品第一页在线| 激情成人在线视频| 91久久精品国产91久久| 波霸ol色综合久久| 日韩精品极品在线观看播放免费视频| 亚洲福利在线视频| 青青草精品毛片| 亚洲欧美精品一区二区| 亚洲男人天堂手机在线| 黑人巨大精品欧美一区二区一视频| 成人黄色在线观看| 国产狼人综合免费视频| 国产精品无av码在线观看| 中文字幕在线看视频国产欧美| yw.139尤物在线精品视频| 久久久久久久一区二区三区| 亚洲欧美色婷婷| 一区二区三区回区在观看免费视频| 色爱精品视频一区| 久久久久久久久久久免费| 久久久欧美精品| 亚洲一级一级97网| 久久影院模特热| 国产亚洲福利一区| 国产视频在线一区二区| 欧美一级成年大片在线观看| 亚洲女人天堂成人av在线| 国产精品久久久久久一区二区| 自拍偷拍亚洲一区| 日韩av在线播放资源| 亚洲国产精品美女| 久久欧美在线电影| 欧美在线免费观看| 成人在线小视频| 91精品视频在线看| 欧美一级淫片videoshd| 欧美极品美女电影一区| 日韩激情视频在线| 久久亚洲私人国产精品va| 日韩av毛片网| 国产精品久久久久久久美男| 青青a在线精品免费观看| 亚洲人成自拍网站| 国产第一区电影| 国产97在线视频| 欧美国产日韩一区二区在线观看| 亚洲视频专区在线| 色小说视频一区|