Linux下的USB总线驱动(一) USB驱动框架usb-skeleton.c分析

来源:互联网 发布:人工蜂群算法ppt 编辑:程序博客网 时间:2024/04/30 15:40

版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127

一.USB理论

1.USB概念概述

USB1.0版本速度1.5Mbps(低速USB USB1.1版本速度12Mbps(全速USB USB2.0版本速度480Mbps(高速USB

USB驱动由USB主机控制器驱动和USB设备驱动组成。USB主机控制器是用来控制USB设备和CPU之间通信的,USB主机控制器驱动主要用来驱动芯片上的主机控制器硬件。USB设备驱动主要是指具体的例如USB鼠标,USB键盘灯设备的驱动。

一般的通用的Linux设备,如U盘、USB鼠标、USB键盘,都不需要工程师再编写驱动,需要编写的是特定厂商、特定芯片的驱动,而且往往也可以参考内核中已经提供的驱动模板。USB只是一个总线,真正的USB设备驱动的主体工作仍然是USB设备本身所属类型的驱动,如字符设备、tty设备、块设备、输入设备等。

2.USB主机控制器

USB主机控制器属于南桥芯片的一部分,通过PCI总线和处理器通信。USB主机控制器分为UHCI(英特尔提出)、OHCI(康柏和微软提出)、 EHCI。其中OHCI驱动程序用来为非PC系统上以及带有SiSALi芯片组的PC主办上的USB芯片提供支持。UHCI驱动程序多用来为大多数其他PC主板(包括IntelVia)上的USB芯片提供支持。ENCI兼容OHCIUHCIUHCI的硬件线路比OHCI简单,所以成本较低,但需要较复杂的驱动程序,CPU负荷稍重。主机控制器驱动程序完成的功能主要包括:解析和维护URB,根据不同的端点进行分类缓存URB;负责不同USB传输类型的调度工作;负责USB数据的实际传输工作;实现虚拟跟HUB的功能。

3.USB设备与USB驱动的匹配

USB设备与USB驱动怎么匹配的呢?实际上USB设备中有一个模块叫固件,是固件信息和USB驱动进行的匹配。固件是固化在集成电路内部的程序代码,USB固件中包含了USB设备的出厂信息,标识该设备的厂商ID、产品ID、主版本号和次版本号等。另外固件中还包含一组程序,这组程序主要完成USB协议的处理和设备的读写操作。USB设备固件和USB驱动之间通信的规范是通过USB协议来完成的。

4.USB设备的逻辑结构和端点的传输方式

USB设备的逻辑结构包括设备、配置、接口和端点,分别用usb_deviceusb_host_config usb_interfaceusb_host_endpoint表示。

端点的传输方式包括控制传输、中断传输、批量传输、等时传输。

控制传输主要用于向设备发送配置信息、获取设备信息、发送命令道设备,或者获取设备的状态报告。控制传输一般发送的数据量较小,当USB设备插入时,USB核心使用端点0对设备进行配置,另外,端口0与其他端点不一样,端点0可以双向传输。

中断传输就是中断端点以一个固定的速度来传输较少的数据,USB键盘和鼠标就是使用这个传输方式。这里说的中断和硬件上下文中的中断不一样,它不是设备主动发送一个中断请求,而是主机控制器在保证不大于某个时间间隔内安排一次传输。中断传输对时间要求比较严格,所以可以用中断传输来不断地检测某个设备,当条件满足后再使用批量传输传输大量的数据。

批量传输通常用在数据量大、对数据实时性要求不高的场合,例如USB打印机、扫描仪、大容量存储设备、U盘等。

等时传输同样可以传输大批量数据,但是对数据是否到达没有保证,它对实时性的要求很高,例如音频、视频等设备。

5.USBURB请求块

USB请求块(USB request blockurb)是USB主机控制器和设备通信的主要数据结构,主机和设备之间通过urb进行数据传输。当主机控制器需要与设备交互时,只需要填充一个urb结构,然后将其提交给USB核心,由USB核心负责对其进行处理。

URB处理流程:

Step1:创建一个URB结构体 usb_alloc_urb()

Step2:初始化,被安排一个特定的USB设备的特定端点。fill_int/bulk/control_urb()

Step3:被USB设备驱动提交给USB核心usb_submit_urb(),注意GPF_ATOMIC,GPF_NOIO,GPF_KERNEL的使用区别。

Step4:提交由USB核心指定的USB主机控制器驱动,被主机控制器驱动处理,进行一次到USB设备的传输,该过程由USB核心和主机控制器完成,不受USB设备驱动控制

Step5:当urb完成,USB主机控制器驱动通知USB设备驱动。

简单的批量与控制URB

有时候USB驱动程序只是从USB设备上接收或发送一些简单的数据,这时候可以使用usb_bulk/control_msg()完成,这两个函数是同步的,因此不能在中断上下文和持有自旋锁的情况下使用。

6.USB的枚举过程

内核辅助线程khubd用来监视与该集线器连接的所有端口,通常情况下,该线程处于休眠状态,当集线器驱动程序检测到USB端口状态变化后,该内核线程立马唤醒。

USB的枚举过程:USB的枚举过程是热插拔USB设备的起始步骤,该过程中,主机控制器获取设备的相关信息并配置好设备,集线器驱动程序负责该枚举过程。枚举过程主要分如下几步:

Step1:根集线器报告插入设备导致的端口电流变化,集线器驱动程序检测到这一状态变化后,唤醒khubd线程。

Step2khubd识别出电流变化的那个端口

Step3khubd通过给控制端点0发送控制URB来实现从1-127中选出一个数作为插入设备的批量端点

Step4khubd利用端口0使用的控制URB从插入的设备那里获得设备描述符,然后获得配置描述符,并选择一个合适的。

Step5:khubd请求USB核心把对应的客户驱动程序和该USB设备挂钩。

二.USB驱动分析

内核代码分析包括USB驱动框架、鼠标驱动、键盘驱动、U盘驱动。

USB驱动编写的主要框架usb-skeleton.c

USB鼠标驱动 usbmouse.c

USB键盘驱动usbkbd.c

USB Mass Storage是一类USB存储设备, U盘便是其中之一,主要分析的驱动文件是usb.c

1.USB驱动框架usb-skeleton.c

USB骨架程序可以被看做一个最简单的USB设备驱动的实例。

首先看看USB骨架程序的usb_driver的定义

static struct usb_driver skel_driver = {

.name = "skeleton",

.probe = skel_probe, //设备探测

.disconnect = skel_disconnect,

.suspend = skel_suspend,

.resume = skel_resume,

.pre_reset = skel_pre_reset,

.post_reset = skel_post_reset,

.id_table = skel_table, //设备支持项

.supports_autosuspend = 1,

};

#define USB_SKEL_VENDOR_ID 0xfff0

#define USB_SKEL_PRODUCT_ID 0xfff0

static struct usb_device_id skel_table[] = {

{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },

{ }

};

MODULE_DEVICE_TABLE(usb, skel_table);

由上面代码可见,通过USB_DEVICE宏定义了设备支持项。

对上面usb_driver的注册和注销发送在USB骨架程序的模块加载和卸载函数中。

static int __init usb_skel_init(void)

{

int result;

result = usb_register(&skel_driver); //将该驱动挂在USB总线上

if (result)

err("usb_register failed. Error number %d", result);

return result;

}

一个设备被安装或者有设备插入后,当USB总线上经过match匹配成功,就会调用设备驱动程序中的probe探测函数,向探测函数传递设备的信息,以便确定驱动程序是否支持该设备。

static int skel_probe(struct usb_interface *interface,

const struct usb_device_id *id)

{

struct usb_skel *dev; //特定设备结构体

struct usb_host_interface *iface_desc; //设置结构体

struct usb_endpoint_descriptor *endpoint; //端点描述符

size_t buffer_size;

int i;

int retval = -ENOMEM;

dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配内存

if (!dev) {

err("Out of memory");

goto error;

}

kref_init(&dev->kref);

sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); //初始化信号量

mutex_init(&dev->io_mutex); //初始化互斥锁

spin_lock_init(&dev->err_lock); //初始化信号量

init_usb_anchor(&dev->submitted);

init_completion(&dev->bulk_in_completion); //初始化完成量

dev->udev = usb_get_dev(interface_to_usbdev(interface)); //获取usb_device结构体

dev->interface = interface; //获取usb_interface结构体

iface_desc = interface->cur_altsetting; //由接口获取当前设置

for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { //根据端点个数逐一扫描端点

endpoint = &iface_desc->endpoint[i].desc; //由设置获取端点描述符

if (!dev->bulk_in_endpointAddr &&

usb_endpoint_is_bulk_in(endpoint)) { //如果该端点为批量输入端点

buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); //缓冲大小

dev->bulk_in_size = buffer_size; //缓冲大小

dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; //端点地址

dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); //缓冲区

if (!dev->bulk_in_buffer) {

err("Could not allocate bulk_in_buffer");

goto error;

}

dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); //分配urb空间

if (!dev->bulk_in_urb) {

err("Could not allocate bulk_in_urb");

goto error;

}

}

if (!dev->bulk_out_endpointAddr &&

usb_endpoint_is_bulk_out(endpoint)) { //如果该端点为批量输出端点

dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; //端点地址

}

}

if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {//都不是批量端点

err("Could not find both bulk-in and bulk-out endpoints");

goto error;

}

usb_set_intfdata(interface, dev); //将特定设备结构体设置为接口的私有数据

retval = usb_register_dev(interface, &skel_class); //注册USB设备

if (retval) {

err("Not able to get a minor for this device.");

usb_set_intfdata(interface, NULL);

goto error;

}

dev_info(&interface->dev,

"USB Skeleton device now attached to USBSkel-%d",

interface->minor);

return 0;

error:

if (dev)

kref_put(&dev->kref, skel_delete);

return retval;

}

通过上面分析,我们知道,usb_driverprobe函数中根据usb_interface的成员寻找第一个批量输入和输出的端点,将端点地址、缓冲区等信息存入USB骨架程序定义的usb_skel结构体中,并将usb_skel通过usb_set_intfdata传为USB接口的私有数据,最后注册USB设备。

我们来看看这个USB骨架程序定义的usb_skel结构体

struct usb_skel {

struct usb_device *udev; //该设备的usb_device指针

struct usb_interface *interface; //该设备的usb_interface指针

struct semaphore limit_sem; //限制进程写的数量

struct usb_anchor submitted;

struct urb *bulk_in_urb;

unsigned char *bulk_in_buffer; //接收数据缓冲区

size_t bulk_in_size; //接收数据大小

size_t bulk_in_filled;

size_t bulk_in_copied;

__u8 bulk_in_endpointAddr; //批量输入端点地址

__u8 bulk_out_endpointAddr; //批量输出端点地址

int errors;

int open_count;

bool ongoing_read;

bool processed_urb;

spinlock_t err_lock;

struct kref kref;

struct mutex io_mutex;

struct completion bulk_in_completion; //完成量

};

好了看完了probe,我们再看看disconnect函数

static void skel_disconnect(struct usb_interface *interface)

{

struct usb_skel *dev;

int minor = interface->minor; //获得接口的次设备号

dev = usb_get_intfdata(interface); //获取接口的私有数据

usb_set_intfdata(interface, NULL); //设置接口的私有数据为空

usb_deregister_dev(interface, &skel_class); //注销USB设备

mutex_lock(&dev->io_mutex);

dev->interface = NULL;

mutex_unlock(&dev->io_mutex);

usb_kill_anchored_urbs(&dev->submitted);

kref_put(&dev->kref, skel_delete);

dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);

}

我们在skel_probe中最后执行了usb_register_dev(interface, &skel_class)来注册了一个USB设备,我们看看skel_class的定义

static struct usb_class_driver skel_class = {

.name = "skel%d",

.fops = &skel_fops,

.minor_base = USB_SKEL_MINOR_BASE,

};

static const struct file_operations skel_fops = {

.owner = THIS_MODULE,

.read = skel_read,

.write = skel_write,

.open = skel_open,

.release = skel_release,

.flush = skel_flush,

};

根据上面代码我们知道,其实我们在probe中注册USB设备的时候使用的skel_class是一个包含file_operations的结构体,而这个结构体正是字符设备文件操作结构体。

我们先来看看这个file_operationsopen函数的实现

static int skel_open(struct inode *inode, struct file *file)

{

struct usb_skel *dev;

struct usb_interface *interface;

int subminor;

int retval = 0;

subminor = iminor(inode); //获得次设备号

//根据usb_driver和次设备号获取设备的接口

interface = usb_find_interface(&skel_driver, subminor);

if (!interface) {

err("%s - error, can't find device for minor %d",

__func__, subminor);

retval = -ENODEV;

goto exit;

}

dev = usb_get_intfdata(interface); //获取接口的私有数据usb_skel

if (!dev) {

retval = -ENODEV;

goto exit;

}

kref_get(&dev->kref);

mutex_lock(&dev->io_mutex);

if (!dev->open_count++) {

retval = usb_autopm_get_interface(interface);

if (retval) {

dev->open_count--;

mutex_unlock(&dev->io_mutex);

kref_put(&dev->kref, skel_delete);

goto exit;

}

}

file->private_data = dev; //usb_skel设置为文件的私有数据

mutex_unlock(&dev->io_mutex);

exit:

return retval;

}

这个open函数实现非常简单,它根据usb_driver和次设备号通过usb_find_interface获取USB接口,然后通过usb_get_intfdata获得接口的私有数据并赋值给文件。

好了,我们看看write函数,在write函数中,我们进行了urb的分配、初始化和提交的操作

static ssize_t skel_write(struct file *file, const char *user_buffer,

size_t count, loff_t *ppos)

{

struct usb_skel *dev;

int retval = 0;

struct urb *urb = NULL;

char *buf = NULL;

size_t writesize = min(count, (size_t)MAX_TRANSFER); //待写数据大小

dev = (struct usb_skel *)file->private_data; //获取文件的私有数据

if (count == 0)

goto exit;

if (!file->f_flags & O_NONBLOCK) { //如果文件采用非阻塞方式

if (down_interruptible(&dev->limit_sem)) { //获取限制读的次数的信号量

retval = -ERESTARTSYS;

goto exit;

}

} else {

if (down_trylock(&dev->limit_sem)) {

retval = -EAGAIN;

goto exit;

}

}

spin_lock_irq(&dev->err_lock); //关中断

retval = dev->errors;

if (retval < 0) {

dev->errors = 0;

retval = (retval == -EPIPE) ? retval : -EIO;

}

spin_unlock_irq(&dev->err_lock); //开中断

if (retval < 0)

goto error;

urb = usb_alloc_urb(0, GFP_KERNEL); //分配urb

if (!urb) {

retval = -ENOMEM;

goto error;

}

buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL,

&urb->transfer_dma); //分配写缓冲区

if (!buf) {

retval = -ENOMEM;

goto error;

}

if (copy_from_user(buf, user_buffer, writesize)) { //将用户空间数据拷贝到缓冲区

retval = -EFAULT;

goto error;

}

mutex_lock(&dev->io_mutex);

if (!dev->interface) { /* disconnect() was called */

mutex_unlock(&dev->io_mutex);

retval = -ENODEV;

goto error;

}

usb_fill_bulk_urb(urb, dev->udev,

usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),

buf, writesize, skel_write_bulk_callback, dev); //填充urb

urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //urb->transfer_dma有效 usb_anchor_urb(urb, &dev->submitted);

retval = usb_submit_urb(urb, GFP_KERNEL); //提交urb

mutex_unlock(&dev->io_mutex);

if (retval) {

err("%s - failed submitting write urb, error %d", __func__,

retval);

goto error_unanchor;

}

usb_free_urb(urb);

return writesize;

error_unanchor:

usb_unanchor_urb(urb);

error:

if (urb) {

usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);

usb_free_urb(urb);

}

up(&dev->limit_sem);

exit:

return retval;

}

首先说明一个问题,填充urb后,设置了transfer_flags标志,当transfer_flags中的URB_NO_TRANSFER_DMA_MAP被设置,USB核心使用transfer_dma指向的缓冲区而不是使用transfer_buffer指向的缓冲区,这表明即将传输DMA缓冲区。当transfer_flags中的URB_NO_SETUP_DMA_MAP被设置,如果控制urbDMA缓冲区,USB核心将使用setup_dma指向的缓冲区而不是使用setup_packet指向的缓冲区。

另外,通过上面这个write函数我们知道,当写函数发起的urb结束后,其完成函数skel_write_bulk_callback会被调用,我们继续跟踪

static void skel_write_bulk_callback(struct urb *urb)

{

struct usb_skel *dev;

dev = urb->context;

if (urb->status) {

if (!(urb->status == -ENOENT ||

urb->status == -ECONNRESET ||

urb->status == -ESHUTDOWN))

err("%s - nonzero write bulk status received: %d",

__func__, urb->status); //出错显示

spin_lock(&dev->err_lock);

dev->errors = urb->status;

spin_unlock(&dev->err_lock);

}

usb_buffer_free(urb->dev, urb->transfer_buffer_length,

urb->transfer_buffer, urb->transfer_dma); //释放urb空间

up(&dev->limit_sem);

}

很明显,skel_write_bulk_callback主要对urb->status进行判断,根据错误提示显示错误信息,然后释放urb空间。

接着,我们看看USB骨架程序的字符设备的read函数

static ssize_t skel_read(struct file *file, char *buffer, size_t count,

loff_t *ppos)

{

struct usb_skel *dev;

int rv;

bool ongoing_io;

dev = (struct usb_skel *)file->private_data; //获得文件私有数据

if (!dev->bulk_in_urb || !count) //正在写的时候禁止读操作

return 0;

rv = mutex_lock_interruptible(&dev->io_mutex); //获得锁

if (rv < 0)

return rv;

if (!dev->interface) {

rv = -ENODEV;

goto exit;

}

retry:

spin_lock_irq(&dev->err_lock);

ongoing_io = dev->ongoing_read;

spin_unlock_irq(&dev->err_lock);

if (ongoing_io) { //USB核正在读取数据中,数据没准备好

if (file->f_flags & O_NONBLOCK) { //如果为非阻塞,则结束

rv = -EAGAIN;

goto exit;

}

rv = wait_for_completion_interruptible(&dev->bulk_in_completion); //等待

if (rv < 0)

goto exit;

dev->bulk_in_copied = 0; //拷贝到用户空间操作已成功

dev->processed_urb = 1; //目前已处理好urb

}

if (!dev->processed_urb) { //目前还没已处理好urb

wait_for_completion(&dev->bulk_in_completion); //等待完成

dev->bulk_in_copied = 0; //拷贝到用户空间操作已成功

dev->processed_urb = 1; //目前已处理好urb

}

rv = dev->errors;

if (rv < 0) {

dev->errors = 0;

rv = (rv == -EPIPE) ? rv : -EIO;

dev->bulk_in_filled = 0;

goto exit;

}

if (dev->bulk_in_filled) { //缓冲区有内容

//可读数据大小为缓冲区内容减去已经拷贝到用户空间的数据大小

size_t available = dev->bulk_in_filled - dev->bulk_in_copied;

size_t chunk = min(available, count); //真正读取数据大小

if (!available) {

rv = skel_do_read_io(dev, count); //没可读数据则调用IO操作

if (rv < 0)

goto exit;

else

goto retry;

}

//拷贝缓冲区数据到用户空间

if (copy_to_user(buffer, dev->bulk_in_buffer + dev->bulk_in_copied,chunk)) rv = -EFAULT;

else

rv = chunk;

dev->bulk_in_copied += chunk; //目前拷贝完成的数据大小

if (available < count) //剩下可用数据小于用户需要的数据

skel_do_read_io(dev, count - chunk); //调用IO操作

} else {

rv = skel_do_read_io(dev, count); //缓冲区没数据则调用IO操作

if (rv < 0)

goto exit;

else if (!file->f_flags & O_NONBLOCK)

goto retry;

rv = -EAGAIN;

}

exit:

mutex_unlock(&dev->io_mutex);

return rv;

}

通过上面read函数,我们知道,在读取数据时候,如果发现缓冲区没有数据,或者缓冲区的数据小于用户需要读取的数据量时,则会调用IO操作,也就是skel_do_read_io函数。

static int skel_do_read_io(struct usb_skel *dev, size_t count)

{

int rv;

usb_fill_bulk_urb(dev->bulk_in_urb,dev->udev,usb_rcvbulkpipe(dev->udev,

dev->bulk_in_endpointAddr),dev->bulk_in_buffer,

min(dev->bulk_in_size, count),skel_read_bulk_callback,dev); //填充urb

spin_lock_irq(&dev->err_lock);

dev->ongoing_read = 1; //标志正在读取数据中

spin_unlock_irq(&dev->err_lock);

rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); //提交urb

if (rv < 0) {

err("%s - failed submitting read urb, error %d",

__func__, rv);

dev->bulk_in_filled = 0;

rv = (rv == -ENOMEM) ? rv : -EIO;

spin_lock_irq(&dev->err_lock);

dev->ongoing_read = 0;

spin_unlock_irq(&dev->err_lock);

}

return rv;

}

好了,其实skel_do_read_io只是完成了urb的填充和提交,USB核心读取到了数据后,会调用填充urb时设置的回调函数skel_read_bulk_callback

static void skel_read_bulk_callback(struct urb *urb)

{

struct usb_skel *dev;

dev = urb->context;

spin_lock(&dev->err_lock);

if (urb->status) { //根据返回状态判断是否出错

if (!(urb->status == -ENOENT ||

urb->status == -ECONNRESET ||

urb->status == -ESHUTDOWN))

err("%s - nonzero write bulk status received: %d",

__func__, urb->status);

dev->errors = urb->status;

} else {

dev->bulk_in_filled = urb->actual_length; //记录缓冲区的大小

}

dev->ongoing_read = 0; //已经读取数据完毕

spin_unlock(&dev->err_lock);

complete(&dev->bulk_in_completion); //唤醒skel_read函数

}

好了,到目前为止,我们已经把USB驱动框架usb-skeleton.c分析完了,总结下,其实很简单,在模块加载里面注册usb_driver,然后在probe函数里初始化一些参数,最重要的是注册了USB设备,这个USB设备相当于一个字符设备,提供file_operations接口。然后设计openclosereadwrite函数,这个open里基本没做什么事情,在write中,通过分配urb、填充urb和提交urb。注意读的urb的分配在probe里申请空间,写的urb的分配在write里申请空间。在这个驱动程序中,我们重点掌握usb_fill_bulk_urb的设计。



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 妻子与水电工在线 丈夫出差妻子与水电工 樱萌子被水电工侵犯中文字幕 在丈夫的面前侵犯入侵中文字幕 好妻子在线中文字幕 前田香织侵犯中文字幕 佐佐木明希上司中文字幕迅雷 医生d淫孕妇 公公一晚上要8次 公公现在就想要白关婷中文字幕 丈夫上司侵犯系列在线 义父犯美媳嫁樱花叶菜在线播放 儿子的妻子在线中文字幕云播 老公去世公公每天晚上抱着我睡 被水电工掠夺的妻子在线播放b 佐佐木明希大嫂中文字幕全集 善良的小妻子字幕 公公在我洗碗时在厨房要了我 大嫂被肉翻了天佐佐木明希1 教师蕾丝短裙中文字幕 瓜棚里和大嫂乱禽 强睡大嫂中文字 免费 佐佐木明希 房东中文字幕 与父亲干柴烈火 中文字幕丈夫不在 9大嫂被禽翻中文字幕 侵犯你的贞洁 中文字幕 年轻的妻子在线观中文字幕 大嫂被翻天了佐佐木b希中文7 美丽的大嫂中文字幕影迅雷下载 邻居的妻子中文字幕下载 神马电影院电影中文 神马电影院理论中文 女儿的朋友5中文神马电影院 97手机2019电影院专用版中文 厨房里进入朋友的老婆 中文版电影院 神马电影院 中文 儿子的妻子中文字幕 下载 樱桃中文版电影院 大富豪电影院韩国中文