注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

男儿当自强的博客

每天进步一点

 
 
 

日志

 
 
 
 

WINCE6.0+S3C2443下的usb function(功能)驱动  

2011-03-16 15:38:26|  分类: windows CE 驱动 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

********************************LoongEmbedded************************

作者:LoongEmbedded(kandi)

时间:2011.03.16

类别:WINCE驱动开发

********************************LoongEmbedded************************

 

注:这里提到的MDD层和PDD层是对于usb function controller driver来说的

主要基于activesync功能的实现来学习,也即Serial:用于支持USB Device作为串口设备。

 

1.       usb function驱动架构

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图1

Architecturally, a USB function client driver is above a USB function controller driver. A USB function client driver interfaces with a USB function controller driver through the USB function controller driver's hardware independent MDD. The USB function controller driver's MDD and all USB function client drivers are hardware platform independent. Multiple USB function client drivers interface with the same USB function controller driver. Each client interfaces with the USB function controller driver through the USB function controller driver's MDD. Along with the MDD, a USB function controller driver is also comprised of a PDD, which is architecturally below the MDD. The PDD interfaces directly with the USB function controller hardware.

 

从架构上来说,USB function client驱动位于USB function controller驱动的之上,USB function controller驱动的MDD层是USB function client驱动和USB function controller驱动之间的接口,多个USB function client驱动共用可以同一个USB function controller驱动,USB function controller驱动的PDD层直接和USB function controller hardware打交道。

 

2.       usb function驱动组件

下图是WINCE6.0中usb function

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图2

3.       usb function硬件设计

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

 

图3

 

 

4.       usb function驱动的实现

4.1   USB设备端点的概念

每个端点都是一个简单的连接点,或者支持数据流进设备,或者支持其流出设备,两者不可兼得。基于PnP机制,设备被枚举时,它必须向主机报告各个端点的特性,包括端点号,通信方向,端点支持的最大包大小,带宽要求等(其中端点支持的最大包大小叫做数据有效负载)。每个设备必须有端点0,它用于设备枚举和对设备进行一些基本的控制功能。除了端点0,其余的端点在设备配置之前不能与主机通信,只有向主机报告这些端点的特性并被确认后才能被激活。

 

特别地,缺省控制通道也是一个消息通道。当客户程序通过USB管道发送或接收数据时,它首先调用Win32 APl,调用最终将使功能驱动程序收到一个IRP。而驱动程序的工作就是把客户的请求引导到有正确端点的管道上。它把请求提交到总线驱动程序,总线驱动程序再把请求分解成多个事务,然后这些事务被送往总线。总线上的信息流以每毫秒一帧数据的形式流动。总线驱动程序必须安排好多个事务以使它们能被装入同一帧中。在主机和设备的端口之间,可视为一个通道。USB中有一个特殊的通道一缺省控制通道,它属于消息通道,设备一启动即存在,从而为设备的设置、状态查询和输入控制信息提供一个入口。

 

端点有三个状态:空闲,数据流入和数据流出。

 

 

 

4.2   USB function client驱动总线接口

USB function controller驱动的MDD层实现并导出了总线接口,这样就可以利用这个接口来加载USB function client驱动。IOCTL的初始化之后,USB function controller驱动的MDD层根据注册表项HKEY_LOCAL_MACHINE\Drivers\USB\FunctionDrivers下面的键值来加载默认的USB function client驱动,比如加了Device Drivers->USB Function->USB Function Clients->serial组件之后在common.reg中有此client驱动对应的注册表项

[HKEY_LOCAL_MACHINE\Drivers\USB\FunctionDrivers]

   "DefaultClientDriver"=- ; erase previous default

[HKEY_LOCAL_MACHINE\Drivers\USB\FunctionDrivers]

   "DefaultClientDriver"="Serial_Class"

如果此键值不存在,那么在IOCTL的初始化也会成功,但不能使能function controller。当然应用程序可以稍后增加这个注册表项,并且调用MDD层的IOCTL_BUS_ACTIVATE_CHILD来加载client驱动。

 

为了增加我们应用的灵活性,可以在platform.reg中增加注册表项HKEY_LOCAL_MACHINE\Drivers\USB\FunctionDrivers来替代common.reg中相应的内容,下面是我们的platform.reg中的相关注册表信息

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图4

享有特权的应用程序可以通过DeviceIoControl函数获取MDD层总线接口的句柄,并且通过IOCTL_BUS_N_ENUMERATE_AVAILABLE_CLIENTS可以枚举不同的客户端程序,当然也可以获取当前的客户程序的名字,可以下载当前的客户程序并且加载新的客户程序等等,关于USB function controller驱动的IO控制码,见下面的描述

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图5

对上面IOCTLs的支持和实现是在MDD层的nbus.cpp下面的CnBus::IOControl()来实现的,

在此需要注意的是MDD层支持的总线访问IOCTLs不包含IOCTL_BUS_GET_CONFIGURE_DATA 和IOCTL_BUS_SET_CONFIGURE_DATA。

 

4.3   USB function controller驱动MDD层及DDI接口

MDD层和client的接口函数如下,在p lic\common\oak\inc\usbfntypes.h下定义

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图6

既然是MDD层提供给Client调用的接口函数,就必须在MDD层来实现这些函数并且填充些个函数指针,先看MDD层的N_Init()函数中下面部分:

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图7

而CnBus类主要是用于加载client驱动并且处理USB function controller驱动的IOCTLs,见图5,创建CnBus后会调用其成员函数CnBus::PostInit()

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图8

Client驱动被加载后,此后此client就是usb bus下的一个child,接着client会调用nclient.cpp下面的函数nInitializeInterface()向usb bus发出IOCTL_N_GET_CLIENT_DATA_EX请求

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图9

下面就来看CnBus::IOControl()函数对这个case的处理

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图10

下面看看GetClientFunctions()函数

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图11

到这里client就获取到MDD层为其提供的函数接口了,后面就可以调用这些函数了。

 

MDD层的主要工作如下:

4.3.1根据usb描述符来注册usb设备(在此指WINCE的usb function接口)

这里先来了解一下usb描述符,USB采用USB标准描述符说明一个USB设备,这些描述符包括设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符。

(1)设备描述符 (Device Descriptor)用于指出USB设备的总体信息,其内容对该设备中同一传输模式下的所有配置都有效。一个设备只能有一个设备描述符,但是一个设备允许多个配置描述符。设备描述符的结构体如下:

typedef str t _USB_DEVICE_DESCRIPTOR {

    HAR bLength;

    HAR bDescriptorType;

    USHORT bcdUSB;

    HAR bDeviceClass;

    HAR bDeviceS Class;

    HAR bDeviceProtocol;

    HAR bMaxPacketSize0;

 

    USHORT idVendor;

    USHORT idProd t;

    USHORT bcdDevice;

    HAR iManacturer;

    HAR iProd t;

    HAR iSerialNumber;

    HAR bNumConfigurations;

} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;

 

(2)配置描述符  (Configuration Descriptor)为USB设备的配置指出其配置信息。USB设备的一个配置可以包含一个或者多个接口,且每个接口都可以相互独立工作,所有的USB设备都至少支持一个配置描述符,每个配置都必须有自己的配置描述符。当主机请求配置描述符时,其所有相关的接口描述符和端点描述符都将被返回。

typedef str t _USB_CONFIGURATION_DESCRIPTOR {

    HAR bLength;

    HAR bDescriptorType;

    USHORT wTotalLength;

    HAR bNumInterfaces;

    HAR bConfigurationVal;

    HAR iConfiguration;

    HAR bmAttributes;

    HAR MaxPower;

} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR;

 

(3)接口描述符(interface DescriPtor)用于指定usB设备中各个接口的特性,设备的每个接口都必须有一个描述符。USB设备的接口是一个端点的集合,负责完成设备的特定功能,接口可以包含一个或者多个可替换配置,它们能够在USB设备处于配置状态时,改变当前接口所含端点的个数和特性。USB设备同一配置的各个接口间不能使用相同的端点,但是同一接口的各个可替换配置间可以使用相同的端点。

typedef str t _USB_INTERFACE_DESCRIPTOR {

    HAR bLength;

    HAR bDescriptorType;

    HAR bInterfaceNumber;

    HAR bAlternateSetting;

    HAR bNumEndpoints;

    HAR bInterfaceClass;

    HAR bInterfaceS Class;

    HAR bInterfaceProtocol;

    HAR iInterface;

} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;

 

(4)端点描述符(EndPointDescriptor)用于指出usB设备端点的特性,如其所支持的传输类型、传输方向等信息。除端点O外,USB设备的每个端点都必须有一个端点描述符。

         typedef str t _USB_ENDPOINT_DESCRIPTOR {

    HAR bLength;

    HAR bDescriptorType;

    HAR bEndpointAddress;

    HAR bmAttributes;

    USHORT wMaxPacketSize;

    HAR bInterval;

} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;

      (5)字符串描述符(stringDescriptor)用于保存一些文本信息,它是可选的。在USB设备的其他描述符中,可以含有指向字符串描述符的索引值。

         typedef str t _USB_STRING_DESCRIPTOR {

    HAR bLength;

    HAR bDescriptorType;

    WCHAR bString[1];

} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR;

 

USB function clients调用lpRegisterDevice函数(最终会调用MDD层的nMdd_RegisterDevice())把预先填充的描述符传递给MDD层,这些描述符包含端点数据包(endpoint packet)期望的最大包大小(在此为字节数)。MDD和PDD层在client指定的最大限制条件下协调使用的端点数据大小,MDD和PDD层可以使用小于端点本身供给的包大小,但不能大于。Client通过使用调整后的包长度来打开管道到端点的通道。

 

为了处理注册过程,client提供总线速度最大可能的端点大小,比如,对于USB1.1来说,支持的大块端点数据包是64个字节,而USB2.0支持的大块端点数据包是512个字节,这样允许MDD和PDD层可以灵活指定其大小。

 

4.3.2 处理client的枚举请求

对于USB1.1和2.0,client驱动传递usb描述符给MDD和PDD,除非PDD先处理标准的枚举的请求,否从由MDD来处理这些请求。当枚举完成后,MDD或PDD传递一个枚举完成事件给client,然后,client就适当地初始化端点。MDD需要处理下面的设置请求(setup reqst)

⑴Get device descriptor

⑵Get device q lifier

⑶Get high speed configuration descriptor, if the MDD is on a high speed bus

⑷Get Full Speed Configuration Descriptor, if the MDD is on a full speed bus

⑸Get string descriptor

⑹Set address

 

下面是help文档中对MDD层的描述,为避免误导大家,故贴出来稍加谈谈自己的理解。

Before interacting with the host(PC的usb host端), a function driver(usb function controller driver) must configure the underlying(下面的) USB function controller to support the function that it implements. The model device driver (MDD) must provide a mounted function driver(在此为总线驱动,见\P LIC\COMMON\OAK\DRIVERS\USBFN\CONTROLLER\MDD\nbus.cpp) with a way to configure the underlying USB function controller through a USB descriptor set. In addition, a mounted function driver must address a logical endpoint or pipe, through the endpoint address specified in a USB endpoint descriptor(通过端点描述符的bEndpointAddress为端点或通道编址). The MDD must provide a mapping between a pipe and a physical endpoint.

 

MDD functions wrap most of the similarly named PDD functions(这些函数见下图). This indirection provides the function driver with the ability to access logical endpoints or pipes. The indirection allows the MDD to map the specified pipe to a physical endpoint.

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图12

The MDD provides functions to activate a particular configuration or interface. These functions handle configuration and pipe access.

 

The following steps show how the USB function driver is configured and how pipe access is set up:

⑴The function driver initializes the MDD, which initializes the PDD.

调用nPdd_Init()来初始化PDD,见图7

⑵The function driver registers a configuration with the MDD.

这个在nMdd_RegisterDevice函数中实现。

By qrying the capabilities of the USB function controller, through the PDD, the MDD determines whether the supplied configuration can be supported.

在nPdd_Init函数中实现

 

⑶The function driver starts the USB function controller if the configuration can be supported.

主要是通过调用nPdd_Start函数来实现,在这里会创建线程ISTMain来检测usb function中断。

 

⑷The function driver specifies notification functions for device events and default pipe events.

 

⑸The PDD maps the interrupt to a USB event when an interrupt occurs, and then the PDD notifies the device associated with the interrupt.

 

In other words, the function driver starts the interrupt service thread (IST) of the USB function controller, and supplies the PDD with a set of functions to call on the when an interrupt occurs(中断发生的时候,MDD层调用PDD层为其提供的函数集). Handle USB events generated by the USB function controller as interrupts(在PDD层的HandleUSBEvent函数处理), and handle notification functions as interrupt service routines (ISRs).

 

 

⑺The USB function controller driver begins to service interrupts when the USB function controller is running and the default pipe is open.

默认控制管道是一个消息管道,用于在主机和USB设备的端点0之间传送控制和状态信息。

 

⑻The PDD calls the notification functions during USB enumeration.

These functions are supplied by the function driver in response to USB events. The notification function associated with the default pipe is required to read and parse setup token packets and respond to the associated USB standard reqsts associated with USB enumeration.

 

4.4   USB function controller驱动PDD层及DDI接口

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图13

PDD主要的主要工作如下:

⑴为mounted function driver(usb总线驱动)呈现抽象的USB function controller,这样可以保证mounted function driver访问不同的USB function controller时可以移植过来。

 

⑵PDD层有能力检查USB function controller的,并且允许usb function controller driver驱动(准确来说应该是其MDD层)配置和分配端点。

 

⑶USB总线事件发生的时候通知mounted function driver,并且允许function driver处理和关闭此事件。

 

4.5   USB function controller驱动PDD层的实现

4.5.1          USB function controller驱动PDD层用到的中断及对应的线程的概述

⑴检测是否插入usb device线的中断及线程

根据图3可知是用GPF2/EINT2来检测WINCE设备是否和PC端接入usb device线,检测到之后由线程PLUG_IST函数来处理,具体的处理见下面的描述。

 

⑵检测usb device中断及处理线程

线程ISTMain函数用于检测并处理usb deivce中断,具体处理见下面的描述。

 

4.5.2          PDD层的初始化函数nPdd_Init

⑴初始化MDD层和PDD层的接口

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图14

⑵读取注册表项中FunctionDrivers的内容来获取usb function client driver的信息

比如读取到下面的内容

[HKEY_LOCAL_MACHINE\Drivers\USB\FunctionDrivers]

            "DefaultClientDriver"=- ; erase previous default

            "DefaultClientDriver"="Serial_Class"

然后确定client驱动

 

⑶设置端口的信息

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图15

这里涉及到端口的结构,如下

// Transfer str ture passed to the PDD from the MDD in IssTransfer.

typedef str t _STransfer {

    DWORD               dwFlags;

    PVOID               pvBfer;

    DWORD               dwBferPhysicalAddress;

    DWORD               cbBfer;

    DWORD               cbTransferred;

    DWORD               dwUsbError; // Possible vals are in usbfntypes.h

 

    PVOID               pvPddData; // PDD can do whatever it likes with this

    PVOID               pvPddTransferInfo; // Specific to PDD from client

} STransfer, *PSTransfer;

需要传输的数量,已经传输的数量

typedef str t EP_STATUS {

    DWORD                   dwEndpointNumber;

    DWORD                   dwDirectionAssigned;

    DWORD                   dwPacketSizeAssigned;

    BOOL                    fInitialized;

    DWORD                   dwEndpointType;

    PSTransfer              pTransfer;

    CRITICAL_SECTION        cs;

} *PEP_STATUS;

这个结构体保存着一个端点传输中的基本信息:端点的Index号,传输方向,每次最大传输数量,端点的类型,此次传输的数据存放内存首地址

 

⑷调用DDKReg_GetWindowInfo()和DDKReg_GetIsrInfo()来读取注册表项[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SC2443USBFN]下保存的IRQ、SysIntr、IoBase、IoLen、等信息。

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SC2443USBFN]

   "Dll"="sc2443usbfn.dll"

   "Prefix"="N"

   "Priority256"=dword:64

   "IoBase"=dword:B0B00000

   "IoLen"=dword:1000        ; Use one page

   "Irq"=dword:19

   "BusIoctl"=dword:2a0048

   "IClass"=multi_sz:"{E2BDC372-598F-4619-BC50-54B3F7848D35}=%b","{6F40791D-300E-44E4-BC38-E0E63CA8375C}=%b"

 

⑸创建访问总线放入句柄pContext->hBusAccess,调用MapRegisterSet将USB device controller register地址映射到虚拟地址空间.,然后设置attachedState为N_DETACH,调用ResetDevice函数复位usb function device和端点。

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图16

 

⑹传递MDD层和PDD层的接口,创建usb device线插拔的事件,中断及IST。

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图17

下面来学习检测usb device线的插拔的线程PLUG_IST

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图18

 

4.5.3          处理USB device中断的ISTMain()

CUsbFn::Init()->CUsbFn::StartUSBFunction()->nMdd_Start()->nPdd_Start(),ISTMain在nPdd_Start()被创建,先来看这个IST的函数体的前面部分

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图19

下面来看后部分

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图20

下面来学习HandleUSBEvent()

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图21

接着分别介绍HandleUSBEvent函数中调用到的主要函数

⑴ HandleUSBBusIrq()

介绍这个函数之前先来认识一下USB2.0 function的系统状态寄存器(SSR),

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图22

HandleUSBBusIrq()函数主要是处理来usb总线中断,记得当系统状态的改变导致的中断发生后,要对SSR寄存器相应的位置1来清除相应的位。如果是下面几种usb总线中断时,如果处于已连接的状态,就需要告诉MDD层来做相应的处理

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图23

比如当检查到host发送过来的suspend信号,这时先要判断usb device是否处于连接的状态,如果是就要告诉MDD层来处理,

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图24

这样就可以调用nMdd_Notify函数来处理这个总线中断请求,其他总线中断请求的处理可做类似的分析

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图25

 

⑵ HandleEndpoint0Event()

先来看端点0相关寄存器

端点0中断寄存器(EIR)

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图26

 

端点0状态寄存器(ESR)

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图27

端点0控制寄存器(ECR)

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图28

下面来看HandleEndpoint0Event的函数体部分

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图29

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图30

上面部分的主要工作是准备发送配置包,读取要发送包的字节数,获得USB Device Reqst指针,读取FIFO数据(EP0 Bfer Register)到pb r中。然后对读到的数据进行解析,如果数据长度大于0,获得传输方向,如果数据长度为0,设置sendDataEnd为TR表示数据传输完成.

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图31

上面部分是根据图30中准备发送的数据包获得是发出数据还是接收数据状态,调用不同的处理流程

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图32

 

⑶HandleEndpointEvent()

WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图33

 WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图34

 WINCE6.0+S3C2443下的usb function(功能)驱动 - 男儿当自强 - 男儿当自强的博客

图35

 

 

 

CreateBusAccessHandle(LPCTSTR lpActiveRegPath)

该函数用于创建一个可以访问Bus设备驱动的句柄,一个客户端驱动(Client Driver)会在它的XXX_Init函数中调用该函数来获得Bus设备的句柄。lpActiveRegPath为Bus设备的注册表路径,返回值为句柄。pszActiveKey=Drivers\Active\51

 

 

 

 

Windows CE USB Function Driver驱动简析(1)-

http://blog.csdn.net/shevsten/archive/2010/07/15/5736889.aspx

 

2410 C driver 分析1

http://www.cevx.com/Bbs/viewthread.php?tid=13370

 

usb驱动

http://www.188928.com/sqd1/20110217/15727.htm

 

USB 软件、端点和管道

http://blog.csdn.net/Augusdi/archive/2009/05/13/4170026.aspx

 

wince USB设备驱动程序导读

http://yzcyn.blog.163.com/blog/static/38484300200831834045507/

 

WinCE系统USB Mass Storage实现

http://www.docin.com/p-55309404.html

 

WinCE USB驱动开发

http://blog.sina.com.cn/s/blog_4ad0a9940100g11a.html

 

WinCE系统USB功能定制

http://blog.csdn.net/nanjianhui/archive/2009/08/12/4438599.aspx

 

有关OHCI、UHCI、EHCI的知识

http://zh irlunjj.blog.163.com/blog/static/8005094520107203058470/

 

 

  评论这张
 
阅读(1542)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017