一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

字符設(shè)備驅(qū)動程序及數(shù)據(jù)結(jié)構(gòu)簡介

 *扶搖* 2011-04-04
1.設(shè)備號
  分為主次設(shè)備號,看上去像是兩個(gè)號碼,但在內(nèi)核中用dev_t(<linux/types.h>)一種結(jié)構(gòu)表示,同時(shí)不應(yīng)該自己去假設(shè)賦值設(shè)備號,而是使用宏(<linux/kdev_t.h>)來取得.
MAJOR(dev_t dev);
MINOR(dev_t dev);
即使你有確定的主,次設(shè)備號也要用
dev=MKDEV(int major, int minor);
1.1分配設(shè)備號
<linux/fs.h>
靜態(tài)分配                //first就是上面的dev
int register_chrdev_region(dev_t first, unsigned int count, char *name);
  first 是你要分配的起始設(shè)備編號. first 的次編號部分常常是 0, 但是沒有要求是那個(gè)效果.
  count 是你請求的連續(xù)設(shè)備編號的總數(shù). (一般為1)注意, 如果 count 太大, 你要求的范圍可能溢出到下一個(gè)次編號; 但是只要你要求的編號范圍可用, 一切都仍然會正確工作.
  name 是應(yīng)當(dāng)連接到這個(gè)編號范圍的設(shè)備的名子(DEVICE_NAME); 它會出現(xiàn)在 /proc/devices 和 sysfs 中
動態(tài)分配   
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
  dev 是一個(gè)只輸出的參數(shù), 它在函數(shù)成功完成時(shí)持有你的分配范圍的第一個(gè)數(shù).
  fisetminor 應(yīng)當(dāng)是請求的第一個(gè)要用的次編號; 它常常是 0.
  count 和 name 參數(shù)如同給 request_chrdev_region 的一樣
>>>應(yīng)該始終使用動態(tài)分配,但最好為定制設(shè)備號留有接口,以參數(shù)形式,以name_major=0做為默認(rèn)值,可能的操作如下:
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}

1.2釋放設(shè)備號                  //設(shè)備號
void unregister_chrdev_region(dev_t first, unsigned int count);
2.重要的數(shù)據(jù)結(jié)構(gòu)
2.1文件操作
<linux/fs.h>中的file_operation結(jié)構(gòu),其成員
struct module *owner 第一個(gè) file_operations 成員根本不是一個(gè)操作; 幾乎所有時(shí)間中, 它被簡單初始化為 THIS_MODULE, 一個(gè)在 <linux/module.h> 中定義的宏.
loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改變文件中的當(dāng)前讀/寫位置, 并且新位置作為(正的)返回值. loff_t 參數(shù)是一個(gè)"long offset", 并且就算在 32位平臺上也至少 64 位寬. 錯(cuò)誤由一個(gè)負(fù)返回值指示. 如果這個(gè)函數(shù)指針是 NULL, seek 調(diào)用會以潛在地?zé)o法預(yù)知的方式修改 file 結(jié)構(gòu)中的位置計(jì)數(shù)器
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用來從設(shè)備中獲取數(shù)據(jù). 在這個(gè)位置的一個(gè)空指針導(dǎo)致 read 系統(tǒng)調(diào)用以 -EINVAL("Invalid argument") 失敗. 一個(gè)非負(fù)返回值代表了成功讀取的字節(jié)數(shù)( 返回值是一個(gè) "signed size" 類型, 常常是目標(biāo)平臺本地的整數(shù)類型).
ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t); 初始化一個(gè)異步讀 -- 可能在函數(shù)返回前不結(jié)束的讀操作. 如果這個(gè)方法是 NULL, 所有的操作會由 read 代替進(jìn)行(同步地).
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 發(fā)送數(shù)據(jù)給設(shè)備. 如果 NULL, -EINVAL 返回給調(diào)用 write 系統(tǒng)調(diào)用的程序. 如果非負(fù), 返回值代表成功寫的字節(jié)數(shù).
ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *); 初始化設(shè)備上的一個(gè)異步寫.
int (*readdir) (struct file *, void *, filldir_t); 對于設(shè)備文件這個(gè)成員應(yīng)當(dāng)為 NULL; 它用來讀取目錄, 并且僅對文件系統(tǒng)有用.
unsigned int (*poll) (struct file *, struct poll_table_struct *); poll 方法是 3 個(gè)系統(tǒng)調(diào)用的后端: poll, epoll, 和 select, 都用作查詢對一個(gè)或多個(gè)文件描述符的讀或?qū)懯欠駮枞? poll 方法應(yīng)當(dāng)返回一個(gè)位掩碼指示是否非阻塞的讀或?qū)懯强赡艿? 并且, 可能地, 提供給內(nèi)核信息用來使調(diào)用進(jìn)程睡眠直到 I/O 變?yōu)榭赡? 如果一個(gè)驅(qū)動的 poll 方法為 NULL, 設(shè)備假定為不阻塞地可讀可寫.
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); ioctl 系統(tǒng)調(diào)用提供了發(fā)出設(shè)備特定命令的方法(例如格式化軟盤的一個(gè)磁道, 這不是讀也不是寫). 另外, 幾個(gè) ioctl 命令被內(nèi)核識別而不必引用 fops 表. 如果設(shè)備不提供 ioctl 方法, 對于任何未事先定義的請求(-ENOTTY, "設(shè)備無這樣的 ioctl"), 系統(tǒng)調(diào)用返回一個(gè)錯(cuò)誤.
int (*mmap) (struct file *, struct vm_area_struct *); mmap 用來請求將設(shè)備內(nèi)存映射到進(jìn)程的地址空間. 如果這個(gè)方法是 NULL, mmap 系統(tǒng)調(diào)用返回 -ENODEV.
int (*open) (struct inode *, struct file *); 盡管這常常是對設(shè)備文件進(jìn)行的第一個(gè)操作, 不要求驅(qū)動聲明一個(gè)對應(yīng)的方法. 如果這個(gè)項(xiàng)是 NULL, 設(shè)備打開一直成功, 但是你的驅(qū)動不會得到通知.
int (*flush) (struct file *); flush 操作在進(jìn)程關(guān)閉它的設(shè)備文件描述符的拷貝時(shí)調(diào)用; 它應(yīng)當(dāng)執(zhí)行(并且等待)設(shè)備的任何未完成的操作. 這個(gè)必須不要和用戶查詢請求的 fsync 操作混淆了. 當(dāng)前, flush 在很少驅(qū)動中使用; SCSI 磁帶驅(qū)動使用它, 例如, 為確保所有寫的數(shù)據(jù)在設(shè)備關(guān)閉前寫到磁帶上. 如果 flush 為 NULL, 內(nèi)核簡單地忽略用戶應(yīng)用程序的請求.
int (*release) (struct inode *, struct file *); 在文件結(jié)構(gòu)被釋放時(shí)引用這個(gè)操作. 如同 open, release 可以為 NULL.
int (*fsync) (struct file *, struct dentry *, int); 這個(gè)方法是 fsync 系統(tǒng)調(diào)用的后端, 用戶調(diào)用來刷新任何掛著的數(shù)據(jù). 如果這個(gè)指針是 NULL, 系統(tǒng)調(diào)用返回 -EINVAL.
int (*aio_fsync)(struct kiocb *, int); 這是 fsync 方法的異步版本.
int (*fasync) (int, struct file *, int); 這個(gè)操作用來通知設(shè)備它的 FASYNC 標(biāo)志的改變. 異步通知是一個(gè)高級的主題, 在第 6 章中描述. 這個(gè)成員可以是NULL 如果驅(qū)動不支持異步通知.
int (*lock) (struct file *, int, struct file_lock *); lock 方法用來實(shí)現(xiàn)文件加鎖; 加鎖對常規(guī)文件是必不可少的特性, 但是設(shè)備驅(qū)動幾乎從不實(shí)現(xiàn)它.
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); 這些方法實(shí)現(xiàn)發(fā)散/匯聚讀和寫操作. 應(yīng)用程序偶爾需要做一個(gè)包含多個(gè)內(nèi)存區(qū)的單個(gè)讀或?qū)懖僮? 這些系統(tǒng)調(diào)用允許它們這樣做而不必對數(shù)據(jù)進(jìn)行額外拷貝. 如果這些函數(shù)指針為 NULL, read 和 write 方法被調(diào)用( 可能多于一次 ).
ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *); 這個(gè)方法實(shí)現(xiàn) sendfile 系統(tǒng)調(diào)用的讀, 使用最少的拷貝從一個(gè)文件描述符搬移數(shù)據(jù)到另一個(gè). 例如, 它被一個(gè)需要發(fā)送文件內(nèi)容到一個(gè)網(wǎng)絡(luò)連接的 web 服務(wù)器使用. 設(shè)備驅(qū)動常常使 sendfile 為 NULL.
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); sendpage 是 sendfile 的另一半; 它由內(nèi)核調(diào)用來發(fā)送數(shù)據(jù), 一次一頁, 到對應(yīng)的文件. 設(shè)備驅(qū)動實(shí)際上不實(shí)現(xiàn) sendpage.
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); 這個(gè)方法的目的是在進(jìn)程的地址空間找一個(gè)合適的位置來映射在底層設(shè)備上的內(nèi)存段中. 這個(gè)任務(wù)通常由內(nèi)存管理代碼進(jìn)行; 這個(gè)方法存在為了使驅(qū)動能強(qiáng)制特殊設(shè)備可能有的任何的對齊請求. 大部分驅(qū)動可以置這個(gè)方法為 NULL.[10]
int (*check_flags)(int) 這個(gè)方法允許模塊檢查傳遞給 fnctl(F_SETFL...) 調(diào)用的標(biāo)志.
int (*dir_notify)(struct file *, unsigned long); 這個(gè)方法在應(yīng)用程序使用 fcntl 來請求目錄改變通知時(shí)調(diào)用. 只對文件系統(tǒng)有用; 驅(qū)動不需要實(shí)現(xiàn) dir_notify.
>>>下面是一個(gè)實(shí)現(xiàn)的可能例子,重要的函數(shù)被實(shí)現(xiàn)
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
這個(gè)聲明使用標(biāo)準(zhǔn)的 C 標(biāo)記式結(jié)構(gòu)初始化語法.
2.2文件結(jié)構(gòu)
struct file, 定義于 <linux/fs.h>
其成員:
mode_t f_mode; 文件模式確定文件是可讀的或者是可寫的(或者都是), 通過位 FMODE_READ 和 FMODE_WRITE. 你可能想在你的 open 或者 ioctl 函數(shù)中檢查這個(gè)成員的讀寫許可, 但是你不需要檢查讀寫許可, 因?yàn)閮?nèi)核在調(diào)用你的方法之前檢查. 當(dāng)文件還沒有為那種存取而打開時(shí)讀或?qū)懙钠髨D被拒絕, 驅(qū)動甚至不知道這個(gè)情況.
loff_t f_pos; 當(dāng)前讀寫位置. loff_t 在所有平臺都是 64 位( 在 gcc 術(shù)語里是 long long ). 驅(qū)動可以讀這個(gè)值, 如果它需要知道文件中的當(dāng)前位置, 但是正常地不應(yīng)該改變它; 讀和寫應(yīng)當(dāng)使用它們作為最后參數(shù)而收到的指針來更新一個(gè)位置, 代替直接作用于 filp->f_pos. 這個(gè)規(guī)則的一個(gè)例外是在 llseek 方法中, 它的目的就是改變文件位置.
unsigned int f_flags; 這些是文件標(biāo)志, 例如 O_RDONLY, O_NONBLOCK, 和 O_SYNC. 驅(qū)動應(yīng)當(dāng)檢查 O_NONBLOCK 標(biāo)志來看是否是請求非阻塞操作( 我們在第一章的"阻塞和非阻塞操作"一節(jié)中討論非阻塞 I/O ); 其他標(biāo)志很少使用. 特別地, 應(yīng)當(dāng)檢查讀/寫許可, 使用 f_mode 而不是 f_flags. 所有的標(biāo)志在頭文件 <linux/fcntl.h> 中定義.
struct file_operations *f_op; 和文件關(guān)聯(lián)的操作. 內(nèi)核安排指針作為它的 open 實(shí)現(xiàn)的一部分, 接著讀取它當(dāng)它需要分派任何的操作時(shí). filp->f_op 中的值從不由內(nèi)核保存為后面的引用; 這意味著你可改變你的文件關(guān)聯(lián)的文件操作, 在你返回調(diào)用者之后新方法會起作用. 例如, 關(guān)聯(lián)到主編號 1 (/dev/null, /dev/zero, 等等)的 open 代碼根據(jù)打開的次編號來替代 filp->f_op 中的操作. 這個(gè)做法允許實(shí)現(xiàn)幾種行為, 在同一個(gè)主編號下而不必在每個(gè)系統(tǒng)調(diào)用中引入開銷. 替換文件操作的能力是面向?qū)ο缶幊痰?方法重載"的內(nèi)核對等體.
void *private_data; open 系統(tǒng)調(diào)用設(shè)置這個(gè)指針為 NULL, 在為驅(qū)動調(diào)用 open 方法之前. 你可自由使用這個(gè)成員或者忽略它; 你可以使用這個(gè)成員來指向分配的數(shù)據(jù), 但是接著你必須記住在內(nèi)核銷毀文件結(jié)構(gòu)之前, 在 release 方法中釋放那個(gè)內(nèi)存. private_data 是一個(gè)有用的資源, 在系統(tǒng)調(diào)用間保留狀態(tài)信息, 我們大部分例子模塊都使用它.
struct dentry *f_dentry; 關(guān)聯(lián)到文件的目錄入口( dentry )結(jié)構(gòu). 設(shè)備驅(qū)動編寫者正常地不需要關(guān)心 dentry 結(jié)構(gòu), 除了作為 filp->f_dentry->d_inode 存取 inode 結(jié)構(gòu).
2.3inode結(jié)構(gòu)
其成員:
dev_t i_rdev; 對于代表設(shè)備文件的節(jié)點(diǎn), 這個(gè)成員包含實(shí)際的設(shè)備編號.
struct cdev *i_cdev; struct cdev 是內(nèi)核的內(nèi)部結(jié)構(gòu), 代表字符設(shè)備; 這個(gè)成員包含一個(gè)指針, 指向這個(gè)結(jié)構(gòu), 當(dāng)節(jié)點(diǎn)指的是一個(gè)字符設(shè)備文件時(shí).
i_rdev 類型在 2.5 開發(fā)系列中改變了, 破壞了大量的驅(qū)動. 作為一個(gè)鼓勵更可移植編程的方法, 內(nèi)核開發(fā)者已經(jīng)增加了 2 個(gè)宏, 可用來從一個(gè) inode 中獲取主次編號:
unsigned int iminor(struct inode *inode);unsigned int imajor(struct inode *inode);//不太了解
為了不要被下一次改動抓住, 應(yīng)當(dāng)使用這些宏代替直接操作 i_rdev
3.字符設(shè)備的注冊
3.1添加
struct cdev *my_cdev = cdev_alloc();my_cdev->ops = &my_fops;//有了cdev_init函數(shù) 這個(gè)多余了
但是, 偶爾你會想將 cdev 結(jié)構(gòu)嵌入一個(gè)你自己的設(shè)備特定的結(jié)構(gòu); scull 這樣做了. 在這種情況下, 你應(yīng)當(dāng)初始化你已經(jīng)分配的結(jié)構(gòu), 使用:
void cdev_init(struct cdev *cdev, struct file_operations *fops);
任一方法, 有一個(gè)其他的 struct cdev 成員你需要初始化. 象 file_operations 結(jié)構(gòu), struct cdev 有一個(gè)擁有者成員, 應(yīng)當(dāng)設(shè)置為 THIS_MODULE. 一旦 cdev 結(jié)構(gòu)建立, 最后的步驟是把它告訴內(nèi)核, 調(diào)用:
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
這里, dev 是 cdev 結(jié)構(gòu), num 是這個(gè)設(shè)備響應(yīng)的第一個(gè)設(shè)備號, count 是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號的數(shù)目. 常常 count 是 1, 但是有多個(gè)設(shè)備號對應(yīng)于一個(gè)特定的設(shè)備的情形.
>>>在使用 cdev_add 是有幾個(gè)重要事情要記住. 第一個(gè)是這個(gè)調(diào)用可能失敗. 如果它返回一個(gè)負(fù)的錯(cuò)誤碼, 你的設(shè)備沒有增加到系統(tǒng)中. 它幾乎會一直成功, 但是, 并且?guī)鹆似渌狞c(diǎn): cdev_add 一返回, 你的設(shè)備就是"活的"并且內(nèi)核可以調(diào)用它的操作. 除非你的驅(qū)動完全準(zhǔn)備好處理設(shè)備上的操作, 你不應(yīng)當(dāng)調(diào)用 cdev_add
3.2去除一個(gè)字符設(shè)備, 調(diào)用:
void cdev_del(struct cdev *dev);
>>>實(shí)例,將cdev放入一個(gè)自定義的結(jié)構(gòu)中:
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
以下代碼是對其初始化:
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err, devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)//it is importent!
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
五、open和release
open方法提供給驅(qū)動程序以初始化的能力,為以后的操作作準(zhǔn)備。應(yīng)完成的工作如下:
(1)檢查設(shè)備特定的錯(cuò)誤(如設(shè)備未就緒或硬件問題);
(2)如果設(shè)備是首次打開,則對其進(jìn)行初始化;
(3)如有必要,更新f_op指針;
(4)分配并填寫置于filp->private_data里的數(shù)據(jù)結(jié)構(gòu).
exampe:dev=container_of(.....);//dev is a device struct pointer
         filp->private_data=dev;//filp->private_data aim at a device struct pointer is ok!!!!
而根據(jù)scull的實(shí)際情況,他的open函數(shù)只要完成第四步(將初始化過的struct scull_dev dev的指針傳遞到filp->private_data里,以備后用)就好了,所以open函數(shù)很簡單。但是其中用到了定義在<linux/kernel.h>中的container_of宏,源碼如下:
#define container_of(ptr, type, member) ({            \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
其實(shí)從源碼可以看出,其作用就是:通過指針ptr,獲得包含ptr所指向數(shù)據(jù)(是member結(jié)構(gòu)體)的type結(jié)構(gòu)體的指針。即是用指針得到另外一個(gè)指針。
release方法提供釋放內(nèi)存,關(guān)閉設(shè)備的功能。應(yīng)完成的工作如下:
(1)釋放由open分配的、保存在file->private_data中的所有內(nèi)容;
(2)在最后一次關(guān)閉操作時(shí)關(guān)閉設(shè)備。
由于前面定義了scull是一個(gè)全局且持久的內(nèi)存區(qū),所以他的release什么都不做。
六、read和write
read和write方法的主要作用就是實(shí)現(xiàn)內(nèi)核與用戶空間之間的數(shù)據(jù)拷貝。因?yàn)長inux的內(nèi)核空間和用戶空間隔離的,所以要實(shí)現(xiàn)數(shù)據(jù)拷貝就必須使用在<asm/uaccess.h>中定義的:
unsigned long copy_to_user(void __user *to,
                           const void *from,
                           unsigned long count);
unsigned long copy_from_user(void *to,
                             const void __user *from,
                             unsigned long count);
而值得一提的是以上兩個(gè)函數(shù)和
#define __copy_from_user(to,from,n)    (memcpy(to, (void __force *)from, n), 0)
#define __copy_to_user(to,from,n)    (memcpy((void __force *)to, from, n), 0)
之間的關(guān)系:通過源碼可知,前者調(diào)用后者,但前者在調(diào)用前對用戶空間指針進(jìn)行了檢查。
至于read和write 的具體函數(shù)比較簡單,就在實(shí)驗(yàn)中驗(yàn)證好了。
scull驅(qū)動程序引入了兩個(gè)Linux內(nèi)核中用于內(nèi)存管理的核心函數(shù),它們的定義都在<linux/slab.h>:
void *kmalloc(size_t size, int flags);
void kfree(void *ptr);
三、字符設(shè)備的注冊
內(nèi)核內(nèi)部使用struct cdev結(jié)構(gòu)來表示字符設(shè)備。在內(nèi)核調(diào)用設(shè)備的操作之前,必須分配并注冊一個(gè)或多個(gè)struct cdev。代碼應(yīng)包含<linux/cdev.h>,它定義了struct cdev以及與其相關(guān)的一些輔助函數(shù)。

注冊一個(gè)獨(dú)立的cdev設(shè)備的基本過程如下:
1、為struct cdev 分配空間(如果已經(jīng)將struct cdev 嵌入到自己的設(shè)備的特定結(jié)構(gòu)體中,并分配了空間,這步略過!)
struct cdev *my_cdev = cdev_alloc();
2、初始化struct cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
3、初始化cdev.owner
cdev.owner = THIS_MODULE;
4、cdev設(shè)置完成,通知內(nèi)核struct cdev的信息(在執(zhí)行這步之前必須確定你對struct cdev的以上設(shè)置已經(jīng)完成?。?br>int cdev_add(struct cdev *p, dev_t dev, unsigned count)
從系統(tǒng)中移除一個(gè)字符設(shè)備:void cdev_del(struct cdev *p)
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/vincent_zou/archive/2009/03/09/3974198.aspx

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产精品久久女同磨豆腐| 日韩欧美一区二区久久婷婷| av免费视屏在线观看| 国产亚洲精品一二三区| 在线日韩欧美国产自拍| 在线免费看国产精品黄片| 少妇人妻中出中文字幕| 免费人妻精品一区二区三区久久久| 激情五月天免费在线观看| 欧美又黑又粗大又硬又爽| 午夜成年人黄片免费观看| 日韩人妻精品免费一区二区三区| 亚洲欧美日产综合在线网| 国产一区二区熟女精品免费| 国产精品午夜小视频观看| 国产综合欧美日韩在线精品| 一级片黄色一区二区三区| 亚洲最新中文字幕在线视频| 日韩欧美国产三级在线观看| 国产精品午夜福利免费在线| 午夜色午夜视频之日本| 国产日产欧美精品视频| 蜜桃臀欧美日韩国产精品| 精品日韩欧美一区久久| 亚洲第一区二区三区女厕偷拍| 国产欧洲亚洲日产一区二区| 国产精品刮毛视频不卡| 成人精品一区二区三区在线| 五月婷婷亚洲综合一区| 久热青青草视频在线观看| 日韩一区二区三区18| 丝袜视频日本成人午夜视频| 国产日韩综合一区在线观看| 日韩精品中文字幕亚洲| 国产91人妻精品一区二区三区| 91欧美激情在线视频| 中文字幕一区二区久久综合| 日韩三级黄色大片免费观看| 国产成人午夜av一区二区| 麻豆看片麻豆免费视频| 日韩精品一区二区一牛|