PDF全文下载地址:http://download.csdn.net/source/2320280
http://bbs.driverdevelop.com/read.php?tid-120461.html

《USB 软件结构 》

 

软件结构比硬件来的复杂很多。因为它包含了许多从表面上看不到的层次。比如总线驱动、功能驱动、过滤驱动等。套用社会学的话,这体现了功能应用中的分工和统筹。下面我们逐层来看它们。

  总线驱动
总线驱动位于驱动栈的最低层,处理复杂的任务,必须资源分配,子设备管理。作为下层驱动,负责处理上层驱动发下来的请求。 USB 设备中的总线驱动主要有两类:控制器驱动、 HUB 驱动;另外还有一个端口驱动。

1 ) 控制器驱动: Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 。

首先解释一下 HCI ,它是主机控制接口( Host Control Interface )的缩写。前后一共有三种 HCI 协议出现: USB 1.1 时代,有 OHCI (开发 HCI )协议和 UHCI (通用 HCI )协议; USB2.0 时代,有 EHCI (扩展 HCI )协议。三个协议分别对应了上面的三个驱动程序。因为 USB 是向后兼容的,所以 UsbEHC.sys 中也包含了 UsbOhci 和 UsbUhci 的功能。请看下图。

 


图 1 总线设备 

上图中设备 1 、 3 是控制器设,从名称上就可以区别它们的不同: Universal Host Controller 和 Enhanced Host Controller 。所以设备 1 的驱动程序是 USBUhci.sys ,设备 3 的驱动程序是 USBEhci.sys 。

这说明同一台主机特别是笔记本电脑中, 1.1 和 2.0 的控制器并存,像笔记本电脑中的键盘通过内置 USB 接口与系统连接,其数据吞吐量小,只需要 1.1 的控制器就能满足;而暴露在外供用户使用的接口则需要 2.0 。

2) Hub 驱动: UsbHub.sys 。 Hub 驱动是所有 USB 设备的父驱动。下图描绘了这一景况:


图2


上图中看到, Hub 驱动的子设备要不是独立设备,如左侧图;要么是一个含有多个子设备的父设备,如右侧图。 Hub 驱动只为直系子设备创建唯一的物理设备对象。上图中, Hub 驱动为设备 1 和设备 2 创建物理设备对象,但并不为设备 2 的子设备创建物理设备对象。

3 ) Port 驱动: UsbPort.sys 。这是个框架驱动,比较复杂,很少人会用到。而上面的 Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 其实都是他的微端口驱动。对于这么偏门的框架,不提也罢。

  系统类驱动
所以出现类驱动,体现了 USB 总线在应用上的繁荣景象。只有用得多了,才有被归类的可能。就像人类社会中有几百个国家,几千个民族,正是体现了人类这个团体的多样性与繁荣。只有在“多”的基础上,分类才是有必要的;少数人的小群体,再怎么独立特行,也都不足以被分类,甚至定义为“ XX 民族”。

USB 设备包含很多的通用的功能类,比如: USB 集线器设备, USB HID 设备, USB 音频设备, USB MIDI 设备, USB 存储设备。为了让开发工作变得更加简单, Windows 操作系统为他们提供了系统驱动程序。

大部分时候,系统类驱动就是功能驱动。下表是系统提供的 USB 类驱动。

 

USB 类名称
 类代码
 驱动名称
 系统支持
 
蓝牙设备
 0xE0
 Bthusb.sys
 Vista 、 XP
 
USB 芯片智能卡接口设备
(CCID)
 0x0B
 Usbccid.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
Hub 设备
 0x09
 Usbhub.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
HID 设备
 0x03
 Hidusb.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
USB 大容量存储设备
 0x08
 Usbstor.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
打印设备
 0x07
 Usbprint.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
扫描设备
 0x06
 WpdUsb.sys Usbscan.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
媒体传输设备 (MTP)
 0x06
 WpdUsb.sys
 XP 、 2K3 、 Vista 、 2K8
 
音频设备
 0x01
 Usbaudio.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
Modem 设备 (CDC)
 0x02
 Usbser.sys
 2K 、 XP 、 2K3 、 Vista 、 2K8
 
视频设备 (UVC)
 0x0E
 Usbvideo.sys
 XP 、 Vista
 

 

表 1   系统提供的 USB 类驱动

 

功能驱动
 

不是所有的 USB 设备都有类驱动,但功能驱动却是它们唯一的身份证。没有功能驱动,设备就不足以在系统中存在。它的作用是为设备创造一个独一无二的内核设备对象( DEVICE_OBJCET ),并因此而在需要的时候,系统能够通过此内核设备对象找到它。

如果要让用户层也能够知道并使用 USB 设备,功能驱动更加不可少。它为设备在用户程序可见的名字空间中,为它起一个别名,这个别名可以是一个符号链接,也可以是一个由 GUID 定义的设备 Interface 。通过对这个别名进行操作,也就是对设备本身进行操作。

OK ,上面的话说得太满了,有例外的!唯一的例外是以 RAW 模式驱动的设备。这种设备直接由总线驱动来驱动其工作,不需要功能驱动。这种例子真的不多见,也许只有很很底层的控制器设备、 Hub 设备之类,才会这样做。对于 RAW 模式驱动的设备,当收到 IRP_MN_QUERY_CAPABILITIES 查询请求的时候,在返回的 DEVICE_CAPABILITIES 结构体中,必须将 RawDeviceOK 位设置为 TRUE 。

建议读者在需要的时候使用 WinOBJ.exe 工具查看系统空间中的设备与别名。

 

父驱动与混合设备
 

通过一定的设置, USB 设备中的每个接口可以拥有不同的 Class 和 Protocol 定义,从而实现:一个设备,多个功能。这种设备被称为混合设备。混合设备的前提是拥有多个接口,对单接口设备谈 “ 混合 ” 是没有意义的。

满足了如下两个条件的多接口 USB 设备,被系统认为是混合设备:

1.         设备描述符中,设备类的值为 0 : (bDeviceClass , bDeviceSubClass, bDeviceProtocol ) = (0, 0, 0);

2.         只有唯一的配置描述符,即设备描述符中: (bNumConfigurations ) = (1)

 

从 WinXP SP2 以后,还支持另外一种混合设备的判别方式,称作: Interface Association Descriptor ( IAD )。其实 IAD 描述符是用来组织 “ 接口组( Interfaces Group ) ” 的。配置描述符中可以有多个 IAD 存在,如果将某两个接口将组成接口组,那么首先这两个接口必须是紧挨着的,其次,必须有一个 IAD 描述符位于这两个接口描述符的前面,也必须是紧挨着的, IAD 描述符中的 bFirstInterface 用来描述接口组中的第一个接口 ID , bInterfaceCount 用来描述接口组中包含多少个接口。这样接口集合 : [bFirstInterface, bFirstInterface+bInterfaceCount) 为一个接口组。

当然,能够用上 IAD 的设备,一定是有多接口存在了,否则就是多此一举了。 IAD 描述符的识别和实现是通用父驱动完成的,所以有 IAD 支持的设备,都被认作混合设备。而识别设备是否有 IAD 支持,是通过设备描述符中的如下值判断的:

(bDeviceClass, bDeviceSubClass, bDeviceProtocol) = (0xEF, 0x02, 0x01)

IAD 普及率不是很广,一个原因就是 XP sp2 以后的操作系统版本才对它支持,这样如果把设备插入到 Win 2000 甚至 XP SP1 上,都不能被正确识别。这样,厂商可能必须为不同的系统写两套驱动程序。

我们下面来说说当一个多接口混合设备插入电脑后,系统是如何识别它,并为它加载驱动的。这里大家要注意到一点,就是系统是如何把一个设备,通过多接口,识别为多个物理设备的。

一开始,系统 PNP 管理器安装常规,读取并分析 USB 设备描述符,然后为它分配如下设备 ID :

 

USB\VID_vvvv&PID_pppp

USB\VID_vvvv&PID_pppp&REV_rrrr

(vvvv, pppp, rrrr: 4 位 16 进制数,分别代表了厂商 ID ,产品 ID , USB 版本。对应于设备描述符中的这些值: idVendor/ idProduct/ bcdDevice)

 

和兼容 ID :

 

USB\COMPOSITE

 

PNP 管理器首先按照常规,根据上述的设备 ID 为设备寻找合适的驱动程序:根据设备 ID 到注册表的设备安装信息库(对应于 Enum 和 Class 两个键)中进行搜索,如果找到了安装记录,就根据记录中的信息加载驱动程序。

问题是如果找不到合法的记录怎么办?这时候就用的着兼容 ID 了,兼容 ID “ USB\COMPOSITE ”是在系统中有注册记录的,并且就对应着通用父设备驱动( USBCCGP.sys) 。于是,系统为混合设备加载通用父设备驱动。读者可到目录 Windows\Inf 下查看 usb.inf 文件,此文件中包含了通用父设备驱动的安装信息。

通用父设备驱动通过分析配置描述符,完成两个动作:首先为每个 USB 接口分配一个的设备 ID 和兼容 ID ;然后为每个 USB 接口创建一个物理设备对象( Physical Device Object )。设备 ID 形式如下:

 

USB\VID_vvvv&PID_pppp&MI_mm

USB\VID_vvvv&PID_pppp&REV_rrrr&MI_mm

(mm: 两个 16 位数字表示的接口号 )

 

根据接口描述符中的 clsss 类型,分配兼容 ID ,其形式如下:

 

USB\CLASS_cc

USB\CLASS_cc&SUBCLASS_ss

USB\CLASS_cc&SUBCLASS_ss&PROT_pp

(cc/ ss / pp: 两位 16 进制数。分别对应于接口描述符中的: bInterfaceClass/ bInterfaceSubClass/ bInterfaceProtocol)

 

通用父设备驱动为每个接口创建了物理设备对象后,将接口的设备 ID 、兼容 ID 信息提交给 PNP 管理器, PNP 管理器就有责任为这些“虚假的”物理设备安装驱动。仍然重复上面的过程:根据每个接口的设备 ID 和兼容 ID 在注册表的设备安装信息库中搜索,试图找到相关的安装记录,如果找到了,就为接口加载相应的驱动程序。如果找不到,系统就会弹出“发现新设备”的对话框,启动驱动安装向导。此时用户需为这些接口手动安装驱动。

所以,对于多接口的混合设备( composite device ),其设备驱动的安装分为两个过程,先尝试安装混合驱动,如果找不到,就默认安装通用父设备驱动;接下来通用父设备驱动为每个接口分配设备 ID ,并要求系统为每个接口启动 PNP 过程。

最后,那么来说,在 USB 混合设备中,一定是 X 个接口对应于 X 个功能设备(有一个物理设备对象)吗?一般情况下是这样的,但也有例外,这就是接口组:在符合一定条件的情况下,多个接口中的某些接口可以被组合为一个接口组,一个接口组代表一个功能,父设备驱动只为之创建一个物理设备对象。

接口组的详情,大家看后面的章节内容。

 

过滤驱动
 

过滤驱动无处不在。在很多时候,它被称作 Hook ,是一种 Hack 手段;很多时候它又是必不可少的,这种技术甚至被操作系统自己使用。没有哪一个杀防毒软件不使用过滤驱动,我们在使用网上银行的时候,会用到一些安全控件,基本上都借助了过滤驱动的技术。

过滤驱动可以位于任何一层驱动的上面,或下面。过滤的对象也包括已经存在于系统中的其他的过滤驱动。当它位于某层驱动( D 驱动)上面的时候,所有目标发往 D 驱动的请求,都首先被它截取;当它位于某层驱动下面的时候,所有和 D 驱动相关的从更底层驱动反馈回来的的“完成消息”都预先被过滤驱动截取。这正是它威力强大的原因所在。对于被过滤的驱动来说,过滤驱动简直就是它的先知了。

但使用过滤驱动,要很慎重。很容易把系统搞得很不稳定。读者在写过滤驱动的时候,要明白这样一件事:你想过滤谁,得先了解谁;好像你追求一个人,要先认识这个人。否则死机蓝屏都会与你不期而遇。

 

USB 驱动栈、设备栈
 

请大家不要把这里的“栈”理解成“程序堆栈”的那个栈,朋友们要回到这个字最简单的本意来理解它,而不是想象成一个数据结构。就看成草垛柴堆一样。

驱动栈、设备栈本质上是并行概念。驱动之间的联系是通过设备对象进行的,所以驱动之间是间接联系的,驱动栈多少也就只是概念上的,他用来表示一个设备能够在系统中识别、运行,从上到下中共需要哪些驱动程序支持。

设备栈则是由据可查的。系统中每个 DevNode 就表现一个设备栈。可以这样理解,多个设备栈,串联成了驱动栈。

使用 WinDBG 的 !devnode 命令,可以列举系统中的设备树。下图截取了 CY001 相关片段。

 


图 3 CY001 DevNode 片段 

上图中红色框标出的三个 DevNode ,正好对应于三个内核驱动,建立了 CY001 的驱动栈。从上到下分别是控制器驱动 (usbEHCI) ,集线器驱动 (usbHUB) ,和功能驱动 (CY001) 。下图以 CY001 为例,更加清晰详细地描绘了驱动栈、设备栈的面貌。


图 4 CY001 的驱动栈和设备栈 

上图是单接口 CY001 设备的驱动栈、设备栈全图。也适用于所有其他的单接口 USB 设备。如果是多接口混合设备,就要多一个通用父驱动,稍微复杂一点。

最上面是可能存在的过滤驱动,因为只是“可能存在”,所以都用虚框表示。其实过滤驱动可以存在于设备栈的任何一个位置,而不仅仅是最上层。笔者不可能尽皆画全,以上层过滤为例,能说明问题也就可以了。

过滤驱动生成的过滤设备对象,挂载到 CY001 驱动生成的功能设备对象上;这样所有发送给 CY001 功能设备对象的请求,过滤设备对象总是先得到。根集线器生成了 CY001 驱动的物理设备对象。三个设备连在一起,就是 CY001 驱动程序的设备栈。

但还没有完,根集线器驱动并不是最底层驱动,他必须得到控制器驱动的支持,于是加上可能存在的过滤驱动,由此形成中间的那条设备栈。

仍旧未结束,控制器驱动也不是直接和系统交互的,而是通过更底层的系统总线如 PCI 、 ACPI 进行的。这样,右侧的第三条设备栈也形成了。

由此也可以看出设备栈、驱动栈之间的关系了。驱动栈是形而上的,设备栈是形而下的。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/changpei/archive/2010/05/06/5562542.aspx

posted @ 2010-11-30 10:36 wrh 阅读(1702) | 评论 (0)编辑 收藏

在MFC类库提供了CWnd::OnCtlColor函数,在工作框架的子窗口被重画时将调用该成员函数.因此可以重载WM_CTLCOLOR消息的响应函数.此函数的原型:
  
afx_msg HBRUSH OnCtlColor(CDC *pDC,CWnd *pWnd,UINT nCtlColor);
           参数nCtlColor用于指定控件的类型,可以是:
           .CTLCOLOR_BTN                按钮控件
           .CTLCOLOR_DLG                对话框
           .CTLCOLOR_EDIT               编辑框
           .CTLCOLOR_LISTBOX            列表控件
           .CTLCOLOR_MSGBOX             消息控件
           .CTLCOLOR_SCROLLBAR 滚动条控件
           .CTLCOLOR_STATIC             静态控件
[程序实现]
           假设你已有了名为My的对话框工程.你有了一个STATIC的控件,ID为IDC_STATIC1.
  
HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
           {
        HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
  
        // TODO: Change any attributes of the DC here
           if (nCtlColor==CTLCOLOR_STATIC)

              {
                    pDC->SetTextColor(RGB(255,0,0));
  
//字体颜色
                    pDC->SetBkColor(RGB(0, 0, 255));   //字体背景色  

                }
       
// TODO: Return a different brush if the default is not desired
        return hbr;
           }


如果要指定某个特定控件可以这样写:ID为IDC_STATIC1

if (pWnd->GetDlgCtrlID()==IDC_STATIC1)
{
       pDC->SetTextColor(
RGB(255,0,0));  //设置字体颜色
       pDC->SetBkMode(TRANSPARENT); //设置字体背景为透明
// TODO: Return a different brush if the default is not desired
  return (HBRUSH)::GetStockObject(BLACK_BRUSH);  // 设置背景色
}
else
return hbr;

【注】

BLACK_BRUSH:黑色

WHITE_BRUSH:白色

GRAY_BRUSH:灰色

NULL_BRUSH:透明

HOLLOW_BRUSH :透明











1.为对话框类添加WM_CTLCOLOR的响应函数afx_msg HBRUSH OnCtlColor(CDC*pDC,CWnd*pWnd,UINT nCtlColor){...}

2.定义一个m_brush(CBrush类型)的成员变量和一个m_font(CFont类型)成员变量,在构造函数中初始化,例如:m_brush.CreateSolidBrush(RGB(0,0,255));m_font.CreatePointFont(200,"华文行楷");

3.改变背景颜色和文本颜色和字体:在OnCtlColor()添加代码:

if(pWnd->GetDlgCtrlID()==IDC_LINE_STYLE/*控件ID*/)

{

pDC->SetTextColor(RGB(255,0,0));

pDC->SetBkMode(TRANSPARENT);//设置文本背景色为透明

pDC->SelectObject(&m_font);//设置字体

return m_brush;//设置控件背景颜色

}

//对于按钮来说上面的方法无效


posted @ 2010-11-29 11:24 wrh 阅读(2428) | 评论 (0)编辑 收藏

VC通用控件自适应屏幕类

此为我程序中的一个类,本用于WinCE,但在桌面系统上也同样适用!

使用方法(在WM_INITDIALOG或WM_CREATE消息中加入):

CWindowAnchor::BeginControlBound(hwnd)

 

手动调整控件位置:

CWindowAnchor::AddControl(hwnd,IDC_STATIC1,&WindowAnchorInfo(WAT_LEFT|WAT_TOP,2,8,4,10));
CWindowAnchor::AddControl(hwnd,IDC_STATIC1,
&WindowAnchorInfo(WAT_LEFT|WAT_TOP|WAT_RIGHT,2,20,4,10));
CWindowAnchor::AddControl(hwnd,IDC_STATIC1,
&WindowAnchorInfo(WAT_LEFT|WAT_TOP,2,8,40,10));


自动调整控件位置(跟据设计时资源文件中控件的大小及位置):

CWindowAnchor::AddControl(hwnd,IDC_STATIC1,&WindowAnchorInfo(WAT_LEFT|WAT_TOP));
CWindowAnchor::AddControl(hwnd,IDC_STATIC1,
&WindowAnchorInfo(WAT_LEFT|WAT_TOP|WAT_RIGHT));

 

响应WM_SIZE消息:

case WM_SIZE:
    
return HANDLE_WM_SIZE(hwndDlg,wParam,lParam,CWindowAnchor::OnSize);

 

响应WM_DESTROY消息:

CWindowAnchor::EndControlBound(hwnd);

 

 

代码:

#pragma once
#include 
<map>

#if defined (_MSC_VER)
    
#pragma warning(disable: 4786)
#endif

/*用于WindowAnchorInfo结构的停靠类型*/
typedef 
enum WindowAnchorType
{
    WAT_TOP
=0x0001,
    WAT_LEFT
=0x0002,
    WAT_RIGHT
=0x0004,
    WAT_BOTTOM
=0x0008
};

/*控件定位描述信息*/
typedef 
struct WindowAnchorInfo{
    DWORD dwAnchor; 
//WAT_*
    RECT rcOriginalRect; //控件的原始边距,如果为空则自动获取(仅适用于WM_INIT中)
    
    WindowAnchorInfo(DWORD pAnchor
=WAT_TOP|WAT_LEFT,LONG pLeft=0,LONG pTop=0,LONG pRight=0,LONG pBottom=0)
    {
        dwAnchor
=pAnchor;
        rcOriginalRect.left
=pLeft;
        rcOriginalRect.top
=pTop;
        rcOriginalRect.right
=pRight;
        rcOriginalRect.bottom
=pBottom;
    };
};

typedef std::map
<HWND,WindowAnchorInfo> ControlHashtable;

typedef 
struct{
    INT nWidth; 
//对话框宽度
    INT nHeight; //对话框高度
    INT nMinHeight; //对话框最小高度
    ControlHashtable mapControls; //对话框所有子控件
}WindowAnchorDialog;

/*
 * 对话框子控件定位
 * 2009.03.29 By Frank
*/
static class CWindowAnchor
{
private:
    
static BOOL _ReSize(HWND hwndDlg, const WindowAnchorDialog *wad, HWND hwndCtrl, const WindowAnchorInfo *wai);

public:
    
/*
     * 开始调整(此调用中会获取当前对话框的大小,如果在设计后要调整对话框大小,请先调用此方法)
     * hwndDlg:对话框句柄
    
*/
    
static BOOL BeginControlBound(HWND hwndDlg);

    
/*
     * 结束调整
     * hwndDlg:对话框句柄
    
*/
    
static BOOL EndControlBound(HWND hwndDlg);

    
/*
     * 添加一个控件到调整列表
     * hWndInsertAfter:HWND_BOTTOM |HWND_NOTOPMOST | HWND_TOP | HWND_TOPMOST |-2不改变 | Is Hwnd
    
*/
    
static BOOL AddControl(HWND hwndDlg, INT nCtrlID, WindowAnchorInfo *wai, HWND hWndInsertAfter=(HWND)-2);

    
/*
     * 调整一个指定控件的大小
    
*/
    
static BOOL ReSize(HWND hwndDlg, HWND hwndCtrl);

    
/*
     * 响应WM_SIZE消息
    
*/
    
static BOOL OnSize(HWND hwndDlg, UINT state, int cx, int cy);

    
/*相应WM_VSCROLL消息*/
    
static BOOL OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
};

 

下载地址:单击下载

posted @ 2010-11-29 11:14 wrh 阅读(785) | 评论 (0)编辑 收藏

1.首先在初始化函数中,FormView在OnInitialUpdate(),Dialog在OnInitDialog()中初始化控件的大小。

view plaincopy to clipboardprint?
01.//开始初始化控件大小  
02. m_IsInitialed = false;  
03. 
04. CRect m_ClientRect;  
05. this->GetClientRect(&m_ClientRect);  
06. CSize m_Forsize;  
07. m_Forsize = GetTotalSize();//在资源编辑器中定好大小后,程序运行时大小(不管最大化和最小化,该大小均为同一个值),客户区大于或等于显示的大小  
08. double m_x = (double)m_ClientRect.Width() / m_Forsize.cx;//宽度方向发大倍数  
09. double m_y = (double)m_ClientRect.Height() / m_Forsize.cy;//高度方向发大倍数  
10. 
11. //调整控件的大小  
12. CWnd *pWnd = NULL;   
13. pWnd = GetWindow(GW_CHILD);  
14. while(pWnd)//判断是否为空,因为对话框创建时会调用此函数,而当时控件还未创建  
15. {  
16.  CRect rect;   //获取控件变化前大小  
17.  pWnd->GetWindowRect(&rect);  
18.  ScreenToClient(&rect);//将控件大小转换为在对话框中的区域坐标  
19.  m_ControlRect.insert(pair<int, CRect>(pWnd->GetDlgCtrlID(), rect));//保存控件的初始大小,以便在OnSize函数中继续使用  
20.  int width = rect.Width();  
21.  int height = rect.Height();  
22. 
23.  WCHAR szBuf[256];  
24.  GetClassName(pWnd->m_hWnd,szBuf,256);           
25.  if( _tcsicmp(szBuf,_T("Edit")) == 0)     
26.  {   
27.   //Edit只是位置变化,大小没有变  
28.   rect.top = m_y * rect.top;  
29.   rect.left = m_x * rect.left;  
30.   rect.bottom = rect.top + height;  
31.   rect.right = rect.left + width;  
32.  }  
33.  else 
34.  {  
35.   //其它控件位置和大小均变化  
36.   rect.top = m_y * rect.top;  
37.   rect.left = m_x * rect.left;  
38.   rect.bottom = m_y * rect.bottom;  
39.   rect.right = m_x * rect.right;  
40.  }  
41. 
42.  pWnd->MoveWindow(&rect);//设置控件大小  
43.  pWnd = pWnd->GetWindow(GW_HWNDNEXT);  
44. }  
45.   
46. //控件初始化结束  
47. m_IsInitialed = true; 
//开始初始化控件大小
 m_IsInitialed = false;

 CRect m_ClientRect;
 this->GetClientRect(&m_ClientRect);
 CSize m_Forsize;
 m_Forsize = GetTotalSize();//在资源编辑器中定好大小后,程序运行时大小(不管最大化和最小化,该大小均为同一个值),客户区大于或等于显示的大小
 double m_x = (double)m_ClientRect.Width() / m_Forsize.cx;//宽度方向发大倍数
 double m_y = (double)m_ClientRect.Height() / m_Forsize.cy;//高度方向发大倍数

 //调整控件的大小
 CWnd *pWnd = NULL;
 pWnd = GetWindow(GW_CHILD);
 while(pWnd)//判断是否为空,因为对话框创建时会调用此函数,而当时控件还未创建
 {
  CRect rect;   //获取控件变化前大小
  pWnd->GetWindowRect(&rect);
  ScreenToClient(&rect);//将控件大小转换为在对话框中的区域坐标
  m_ControlRect.insert(pair<int, CRect>(pWnd->GetDlgCtrlID(), rect));//保存控件的初始大小,以便在OnSize函数中继续使用
  int width = rect.Width();
  int height = rect.Height();

  WCHAR szBuf[256];
  GetClassName(pWnd->m_hWnd,szBuf,256);        
  if( _tcsicmp(szBuf,_T("Edit")) == 0)  
  {
   //Edit只是位置变化,大小没有变
   rect.top = m_y * rect.top;
   rect.left = m_x * rect.left;
   rect.bottom = rect.top + height;
   rect.right = rect.left + width;
  }
  else
  {
   //其它控件位置和大小均变化
   rect.top = m_y * rect.top;
   rect.left = m_x * rect.left;
   rect.bottom = m_y * rect.bottom;
   rect.right = m_x * rect.right;
  }

  pWnd->MoveWindow(&rect);//设置控件大小
  pWnd = pWnd->GetWindow(GW_HWNDNEXT);
 }
 
 //控件初始化结束
 m_IsInitialed = true;
 

2.如果界面在运行时大小可以改变,则在OnSize函数中加入如下代码

view plaincopy to clipboardprint?
01.// TODO: 在此处添加消息处理程序代码  
02.    CFormView::ShowScrollBar(SB_BOTH, false);//设置没有滚动条,视情况而定。  
03.         //在界面不是最小化并且已经初始化完毕  
04.    if (!IsIconic() && m_IsInitialed)  
05.    {  
06.        CSize m_Forsize;  
07.        m_Forsize = GetTotalSize();  
08.        double m_x = (double)cx / m_Forsize.cx;  
09.        double m_y = (double)cy / m_Forsize.cy;  
10. 
11.                //读取控件的初始大小  
12.        map<int, CRect>::iterator pos = m_ControlRect.begin();  
13.        for (; pos != m_ControlRect.end(); ++pos)  
14.        {  
15.            CRect rect = pos->second;  
16.            int width = rect.Width();  
17.            int height = rect.Height();  
18. 
19.            WCHAR szBuf[256];  
20.            GetClassName(GetDlgItem(pos->first)->m_hWnd,szBuf,256);                     
21.            if( _tcsicmp(szBuf,_T("Edit")) == 0)     
22.            {   
23.                rect.top = m_y * rect.top;  
24.                rect.left = m_x * rect.left;  
25.                rect.bottom = rect.top + height;  
26.                rect.right = rect.left + width;  
27.            }  
28.            else 
29.            {  
30.                rect.top = m_y * rect.top;  
31.                rect.left = m_x * rect.left;  
32.                rect.bottom = m_y * rect.bottom;  
33.                rect.right = m_x * rect.right;  
34.            }  
35.            GetDlgItem(pos->first)->MoveWindow(rect);  
36.        }  
37.    } 
// TODO: 在此处添加消息处理程序代码
 CFormView::ShowScrollBar(SB_BOTH, false);//设置没有滚动条,视情况而定。
         //在界面不是最小化并且已经初始化完毕
 if (!IsIconic() && m_IsInitialed)
 {
  CSize m_Forsize;
  m_Forsize = GetTotalSize();
  double m_x = (double)cx / m_Forsize.cx;
  double m_y = (double)cy / m_Forsize.cy;

                //读取控件的初始大小
  map<int, CRect>::iterator pos = m_ControlRect.begin();
  for (; pos != m_ControlRect.end(); ++pos)
  {
   CRect rect = pos->second;
   int width = rect.Width();
   int height = rect.Height();

   WCHAR szBuf[256];
   GetClassName(GetDlgItem(pos->first)->m_hWnd,szBuf,256);         
   if( _tcsicmp(szBuf,_T("Edit")) == 0)  
   {
    rect.top = m_y * rect.top;
    rect.left = m_x * rect.left;
    rect.bottom = rect.top + height;
    rect.right = rect.left + width;
   }
   else
   {
    rect.top = m_y * rect.top;
    rect.left = m_x * rect.left;
    rect.bottom = m_y * rect.bottom;
    rect.right = m_x * rect.right;
   }
   GetDlgItem(pos->first)->MoveWindow(rect);
  }
 }

或在OnShowWindow()函数中加入也可以(特别是在对话框作为tabpage时)

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ybw20041910/archive/2010/06/19/5679730.aspx

posted @ 2010-11-29 11:06 wrh 阅读(1594) | 评论 (0)编辑 收藏

1.

重载OnCtlColor    (CDC*    pDC,    CWnd*    pWnd,    UINT    nCtlColor),即WM_CTLCOLOR消息。  
   ----    ①在CExampleDlgDlg的头文件中,添加一CBrush的成员变量:    
   class    CExampleDlgDlg    :    public    CDialog  
   {...  
   protected:  
   CBrush    m_brush;    
   ...  
   };  
   ----    ②在OnInitDialog()函数中添加如下代码:    
   BOOL    CExampleDlgDlg::OnInitDialog()    
   {  
   ...  
   //    TODO:    Add    extra    initialization    here  
   m_brush.CreateSolidBrush(RGB(0,    255,    0));    //    生成一绿色刷子    
   ...  
   }    
   ----    ③利用ClassWizard重载OnCtlColor(…),即WM_CTLCOLOR消息:    
   HBRUSH    CExampleDlgDlg::OnCtlColor  
   (CDC*    pDC,    CWnd*    pWnd,    UINT    nCtlColor)    
   {  
   /*  
   **    这里不必编写任何代码!  
   **下行代码要注释掉  
   **    HBRUSH    hbr    =    CDialog::OnCtlColor(pDC,    pWnd,    nCtlColor);  
   */  
   return    m_brush;        //返加绿色刷子  
   }

2.

   修改对话框的OnPaint,在else中添加如下代码  
           CPaintDC    dc(this);  
           CRect    rect;    
           GetClientRect(rect);    
           dc.FillSolidRect(rect,    RGB(0,0,0));    
           CDialog::OnPaint();

3.

在对话框的应用类(App)的.cpp的Initinstance()中加入代码:  
                   //加在int    nResponse=dlg.DoModal();  
                   前一个RGB设置背景色,第二个设置字体颜色  
   SetDialogBkColor(RGB(0,0,255),RGB(0,255,0));

4.

1.在对话框类中添加成员变量:  
   public:  
           CBrush          m_brushBlue;  
   
   2.在对话框类的OnInitDialog()中添加代码:  
   m_brushBlue.CreateSolidBrush(RGB(0,0,255));  
   
   3.用ClassWizard在对话框类中添加成员函数OnCtlCollor(),并在其中添加代码:  
   if(nCtlColor==CTLCOLOR_DLG)  
   return    m_brushBlue;


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mfreesky/archive/2007/08/27/1760222.aspx

posted @ 2010-11-29 10:55 wrh 阅读(1505) | 评论 (0)编辑 收藏
=============获取设备描述符
bRequestType:80
bRequest    :06
wValue      :0100
wIndex      :0000
wLength     :0040 期望长度64字节

usb bus Reset总线复位我的usb设备
=============发出为我的usb设备设置地址指令,我的usb地址被设置为0x04
bRequestType:00
bRequest    :05
wValue      :0004
wIndex      :0000
wLength     :0000

=============获取配置描述符
bRequestType:80
bRequest    :06
wValue      :0200
wIndex      :0000
wLength     :0009 期望长度9字节

=============尝试读取配置描述符0xff长度
bRequestType:80
bRequest    :06
wValue      :0200
wIndex      :0000
wLength     :00ff

=============尝试读取配置描述符0x12长度,不会超时
bRequestType:80
bRequest    :06
wValue      :0200
wIndex      :0000
wLength     :0012

=============尝试读取配置描述符0x09长度,正好
bRequestType:80
bRequest    :06
wValue      :0200
wIndex      :0000
wLength     :0009

=============读取配置描述符总长度0x22
bRequestType:80
bRequest    :06
wValue      :0200
wIndex      :0000
wLength     :0022

=============设置配置,将配置生效,使能cpu上的endpoint端点
bRequestType:00
bRequest    :09
wValue      :0001 将配置数值设置为1
wIndex      :0000
wLength     :0000

=============
bRequestType:00
bRequest    :09
wValue      :0001
wIndex      :0000
wLength     :0000

=============
bRequestType:81 读取接口
bRequest    :06 读取接口描述符
wValue      :2200 读取报告描述符
wIndex      :0000
wLength     :0072

=============
bRequestType:81
bRequest    :06
wValue      :2200
wIndex      :0000
wLength     :0072

============= 读取配置描述符
bRequestType:80
bRequest    :06
wValue      :0200
wIndex      :0000
wLength     :0022

posted @ 2010-11-26 13:29 wrh 阅读(232) | 评论 (0)编辑 收藏
     摘要: 一、USB命令   在USB规范里,对命令一词提供的单词为“Request”,但这里为了更好的理解主机与设备之间的主从关系,将它定义成“命令”。   所有的USB设备都要求对主机发给自己的控制命令作出响应,USB规范定义了11个标准命令,它们分别是:C...  阅读全文
posted @ 2010-11-24 14:39 wrh 阅读(921) | 评论 (0)编辑 收藏

在《USB系列之三》中,我们实现了一系列的SCSI命令,在这个系列中,我们要实现向U盘上写扇区的命令,所以,本文相对比较容易,更多地是给出一个实现的源程序。

    在《USB系列之三》中,我们实现的SCSI命令有:INQUIRY、READ CAPACITY(10)、TEST UNIT READY、REQUEST SENSE、READ(10);都是一些读出的命令,所以不会破坏U盘的内容,在文档SBC-2的第29页有一个SCSI命令的表,在这个表中列出了所有的命令,其TYPE为“M”的都是SCSI设备必须实现的命令,这些命令有:

Num

Command Name

Operation Code

 Type

 Reference

 1

 FORMAT UNIT

 04h

 M

 SBC-2

 2

 INQUIRY

 12h

 M

 SPC-3

 3

 READ(6)

 08h

 M

 SBC-2

 4

 READ(10)

 28h

 M

 SBC-2

 5

 READ(16)

 88h

 M

 SBC-2

 6

 READ CAPACITY(10)

 25h

 M

 SBC-2

 7

 READ CAPACITY(16)

 9Eh/10h

 M

 SBC-2

 8

 REQUEST SENSE

 03h

 M

 SPC-3

 9

 SEND DIAGNOSTIC

 1Dh

 M

 SPC-3

 10

 TEST UNIT READY

 00h

 M

 SPC-3

 11

 WRITE(10)

 2Ah

 O

 SBC-2

    这里面最后的一个命令并不是SBC-2中要求强制实现的,而是可选的,但如果我们不去实现,U盘的操作将失色很多;我们不打算去实现序号为1、3、5、7和9的命令,READ(6)、READ(16)和READ(10)十分相似,只是LBA的长度不同而已,如果需要实现,参考READ(10)就可以了,FORMAT和SEND DIAGNOSTIC两个命令对使用芯片的U盘来说没有什么意义,当然对硬盘是有意义的,所以在本文中,我们只需要实现一个很重要的WRTE(10),向U盘上写数据,我们需要准备一张没有有用数据的U盘,因为我们要改变其中的内容。

    WRITE(10)源代码下载地址:

    http://blog.hengch.com/source/usb-write.zip

    程序中,我们向《USB系列三》中的程序一样,先reset,然后得到最大的LUN,这个步骤不是必须的,然后我们向device发出WRITE(10)命令,注意,这是一个OUT事务,所以,CBW_FLAGS=0X00而不是像以前一样是0X80,发出WRITE(10)命令后,我们还要向device发送要写入的数据,每次64个字节,一个扇区512字节需要启动8个OUT事务,这个工作又函数putData完成,每次发送的64个字节我们分别写入了0--63,程序中,我们把这些数据写入到了LBA=100的扇区中,写入后,我们在使用在《USB系列之三》中介绍过的READ(10)命令把相同的扇区读出来,我们会看到我们所希望的结果,由于在读之前,我们已经把buffer全部清为0了,所以我们有把握相信,我们读到的数据是真实的。

    到这里,我们已经把控制U盘的主要命令都介绍完了,利用DOSUSB,我们已经有可能为U盘编写一个简单的驱动程序,但可能我们还不知道DOS下的驱动程序该如何写,从下一篇文章开始,我们将暂时放下USB系列文章,介绍一下DOS下驱动程序的写法。

posted @ 2010-11-24 14:10 wrh 阅读(1146) | 评论 (0)编辑 收藏

U盘是我们最常使用的一种USB设备,本文继续使用DOSUSB做驱动,试图以读取扇区的方式读取你的U盘。
    本文可能涉及的协议可能会比较多。
一、了解你的U盘
    首先我们用上一篇文章介绍的程序usbview.exe去看一下你的U盘,我在本文中用于测试的U盘情况如下:
    Device Descriptor: (设备描述符)
    USB Address:             1
    Length:                  18
    Descriptor Type:         1
    USB Specification nr.:   0x0110
    Calss Code:              Class code specified by interface
    Subclass Code:           0x00
    Protocol Code:           0x00
    MAX Packet Size:         0x08
    Vendor ID:               0x058f
    Product ID:              0x9321
    Device Code:             0x0100
    Manufacture Index:       1
    Product Index:           2
    Serial Number Index:     0
    Number of Configuration: 1

    String Descriptor: (字符串描述符)
    Manufacturer: Alcor Micro
    Product: Mass Storage Device

    Configuration Descriptor: (配置描述符)
    Length:               9
    Descriptor Type:      2
    Total Length:         32
    Number of Interfaces: 1
    Configuration Value:  1
    Configuration Index:  0
    Attributes:           Bus Powered
    Max Power:            50mA

    Interface Descriptor: (接口描述符)
    Length:               9
    Descriptor Type:      4
    Interface Number:     0
    Alternate Setting:    0
    Number of Endpoints:  2
    Interface Class:      Mass Storage Device
    Interface Sub Class:  6
    Interface Protocol:   80
    Interface Index:      0

    Endpoint Descriptor: (端点描述符)
    Length:               7
    Descriptor Type:      5
    Endpoint Address:     
1 OUT endpoint
    Attributes:           Bulk
    Max Packet Size:      64
    Interval:             0

    Endpoint Descriptor: (端点描述符)
    Length:               7
    Descriptor Type:      5
    Endpoint Address:     
2 IN endpoint
    Attributes:           Bulk
    Max Packet Size:      64
    Interval:             0

    各种描述符的含义在以前的文章中介绍过了,或者去翻阅USB的specification,这里就不多说了,我们从接口描述符开始就一些关键点进行一下说明。
    首先看接口描述符,Interface Class = 8,表明是Mass Storage Device;Sub Class = 6,表明执行SCSI命令;Interface Protocol = 0x80,表明支持Bulk传输;另外,Number of Endpoints = 2,表明有两个端点。
    两个端点描述符要注意的是,Endpoint Address = 1的是OUT端点,Endpoint Address = 2的是IN端点,有些可能会不一样;有些U盘可能还会有第三个端点,比如支持中断传输的U盘还会有一个Interrupt端点,不过这都没有关系。
    我大概看了我手头有的5个U盘,都支持批量传输,且支持SCSI命令,所以,这可能是一个比较典型的例子,我们就以它为例。

二、CBW(Command Block Wrapper)和CSW(Command Status Wrapper)

    在《USB系列之一》中,我们安装了一个DOSUSB,在《USB系列之二》中,我们利用USBDOS读取了所有的描述表,掌握这些内容需要了解USB协议1.1(USB Specification Revision 1.1)即可,当然还要了解USBDOS,不过这个比较简单。

    在系列一和系列二中,我们已经对DOSUSB的一个数据结构URB有所了解,本文中还要大量用到,我们还接触了一个结构叫device_request,这个结构是在USB协议中定义的,用于向设备发送命令(Request),本文也会用到。

    与前面不同的是,前面的两个系列可以针对任何USB设备,比如U盘、摄像头、打印机等,而本文将只针对我们经常使用的USB设备----U盘,如果你打算尝试本文所介绍的内容,请准备好一个U盘,什么样子的都行,或者是一个USB读卡器,不过要记得插一张卡进去,实际上本文所载范例就是使用一个USB的CF卡读卡器完成的,不用担心损害你的U盘中的数据,本文不会对U盘进行任何写操作,仅仅做一些读操作。

    这个系列中我们需要针对U盘读更多的规范,如下:

     不用为规范发愁,实际上,前两个规范都很短,其中第一个对实际编程没有什么作用,但最好看一下;第二个规范连目录一共22页,其中13页以前的内容可以跳过(很多和USB Specification中相同),第三个规范主要看第6章,第四个规范主要看第5章,后两个规范在编程时需要经常翻阅,以便了解你正在实现的SCSI命令的具体格式和参数。

    本节我们主要介绍两个新的数据结构,这两个结构都是在第二个规范中定义的。

    第一个数据结构叫CBW(Command Block Wrapper)


    这个结构将承载具体的与设备有关的命令发送到设备上去,这个结构分成两部分,第一部分从byte[0]--byte[14]共15个字节,第而部分从byte[15]--byte[30]共16个字节,整个数据结构为31个字节。规范中并没有定义第二部分的内容,这是因为第二部分承载的具体的命令,既与命令集(SCSI命令集)有关,也与具体的命令有关,我们使用SCSI命令集,所以后16个字节的内容在前面提到的后面两个规范中有定义。

    比如我们要向设备发出一个SCSI命令INQUIRY(我们姑且先不要管命令的含义),那么这个命令的结构在SPC-3的第142页有定义,如下:


    对于SCSI INQUIRY这条命令而言,CBW的第二部分的定义就是上面的这六个字节,不同的命令,定义也会不同。

    好,我们回到CSW的结构上来,根据规范,dCBWSignature的值必须是0X43425355,其实就是USBC这几个字母倒过来,这是因为CBW的字符顺序是little endian(这个东东在以前有关网络编程的文章中介绍过),而我们PC机中的字符顺序是big endian,所以要颠倒一下,总之写dCBWSignature = 0X43425355就OK了;dCBWTag仅仅是一个标志,你可以填任何值,这里要先说一下CSW(Command Status Wrapper),我们每发出一个命令,设备都会返回一个CSW(这个东东下面很快就要介绍了),以说明命令的执行状态,这个结构中也有Signature和Tag这两个字段,其中Tag字段和发出命令时CBW中的Tag字段相同,这样就可以区分这个CSW是和那个CBW对应的了,至于Signature,下面再说。

    下一个字段是dCBWDataTransferLength,表示的是当这个命令发出后,我们希望设备返回数据的字符数或者我们要向设备传输的字符数,本文仅涉及从设备返回数据,不涉及向设备传输数据;举例来说:我们发送INQIURY命令到设备,按照SPC-3第144页的说明,该命令返回的数据至少为36个字节,所以,此时这个字节应该填36;再如:我们读取U盘的一个扇区,如果扇区的长度是512个字节,那么这个字段就要填512。

    再下来是bmCBWFlags字段,这个字段只有bit 7有意义,为0表示要向设备传输数据,为1表示要从设备获得数据。

    bCBWLUN字段总是填0,因为绝大多数的U盘都不支持多LUN(Logical Unit Number),只有一个逻辑单元自然好吗就是0了。

    bCBWCBLength字段是只CBW第二部分的长度,像前面举例的INQUIRY命令,长度为6个字节,则这个字段就应该填6,再如:READ(10)命令的长度是10个字节(SBC-2第42页有定义),这个字段当然要填10了。

    第二个要说的数据结构是CSW,当host向device发送一个CBW后,接着就可以从device收到数据(或者发数据到device),当接受完所需的的数据后,就可以从device获得一个CSW(Command Status Wrapper),CSW的结构如下:

     前面说过,在CBW中的dCBWSignature的值恒为:0x43425355,得到的CSW中的dCSWSignature的值为:0x53425355,dCSWTag与dCBWTag中的一致。

    在得到的CSW中,恒定有13个字节,bCSWStatus的定义如下:

 

三、发送命令和接收数据

    我们知道USB协议中定义了三种传输方式,控制传输、批量传输、中断传输和实时传输,在《USB系列二》中我们一直都在使用控制传输,我们应该比较熟悉了,本文中将涉及批量传输。

    我们在使用控制传输时,我们设置好URB启动传输事务,相应的结果将返回到制定得buffer中,批量传输没有那么简单,批量传输分为输出事务和输入事务,我们应该注意到,前面在看U盘的描述表时,在端点这一级有两个端点,一个叫OUT端点,一个叫IN端点,当我们启动一个输出事务时,一定要发送给OUT端点,当我们启动一个输入事务时,一定要发送到输入端点。下面我们简单描述一下如何启动批量传输事务。

    在使用控制传输时,我们应该阅读过DOSUSB的说明,并且对URB结构比较熟悉,URB中有一个字段叫transation_type,当这个值为0x2d时为控制传输;当为0x69时为批量传输的IN事务;当为0xe1时为批量传输的OUT事务;当我们启动一个传输时,一定要正确地设置这个值。

    我们以一个具体的例子来说明如何启动一个传输,我们以SCSI INQUIRY命令为了,关于这个命令的定义在SPC-3的第142页--157页有说明,篇幅很长,但绝大多数篇幅用来解释返回数据的含义,我们可以暂时不去理会。首先我们要填写CBW结构,CBW结构的第一部分的填写前面已经说的很明白了,第二部分的定义在SPC-3的第142页,共有6个字节,我们要按照定义填写好,实际上只要填两个字段,一个是OPERATION CODE = 0X12,第二个就是ALLOCATION CODE = 36,表示需要返回36个字节的内容;CBW填好后,我们开始填写URB,首先把CBW的偏移和段地址放到URB的buffer_off和buffer_seg中,把transation_type=0xe1,表示一个输出事务,注意把end_point字段一定要放OUT endpoint的地址,从前面的描述符表中看,应该是1(2是IN endpoint的地址,你的机器可能不同),其它字段的填法在《USB系列二》中已经介绍过了,填完以后调用DOSUSB,这样,一个承载着INQUIRY命令的输出事务就发送到由URB中dev_add和end_point两个字段指定的端点上去了。

    接下来我们要接收device返回的执行INQUIRY命令的结果,这要启动一个输入事务,相对容易一些,只要填写URB就可以了,把transation_type=0x69,把end_point填上OUT endpoint的地址,本例中为2,buffer_off和buffer_seg指向缓冲区buffer,把buffer_length和actual_length均填为64,因为前面端点描述符表中写明包的最大长度为64,其它字段按常规填写,调用DOSUSB,在buffer中就可以得到返回的内容,按照SPC-3中对返回内容的解释即可了解设备的一些情况。

    接收晚数据后,不要忘了接收CSW,方法也是启动一个输入事务,与接收数据完全相同,然后根据CSW的结构解释其含义。至此一个命令执行完毕。

四、范例

    在本文的范例中,我们实现了如下内容:

  • 实现了Bulk-Only Mass Storage Reset
  • 实现了Get Max LUN
  • 实现了SCSI INQUIRY Command
  • 实现了SCSI READ CAPACITY (10) Command
  • 实现了SCSI REQUEST SENSE Command
  • 实现了SCSI TEST UNIT READY Command
  • 实现了SCSI READ (10) Command

    最后的一个命令,我将从你的U盘上读出一个扇区。

    最前面的两个命令,请翻阅《Universal Serial Bus Mass Storage Class - Bulk-Only Transport》第7页;INQUIRY、REQUEST SENSE、TEST UNIT READY三个命令请翻阅SPC-3的第142、221和232页;READ CAPACITY(10)和READ(10)命令,请翻阅SBC-2的第42和44页。

    源代码请在下面网址下载:

    http://blog.hengch.com/source/reader.rar

    各种概念在前面已经介绍过了,程序无非就是实现这些概念,几乎所有的代码都是围绕着填写数据结构和显示返回结果的,所以代码本身并不难,更重要的是理解数据结构中个字段的含义,这可能不得不阅读一些规范,我想我不可能比规范说的更严谨更完整。要注意的是,你使用的U盘不可能和我的完全一致,一般情况下有可能有变化的是:设备地址devAddr、输出端点地址outEndpoint和输入端点地址inEndpoint,所以在编译程序之前一定要使用《USB系列之二》中的方法仔细查看一下你的U盘的各种描述符表,如果这些值和我的U盘不同,请在主程序开始的地方,更改这几个变量;另外,在主程序6th step中,scsiRead10(0),传递给scsiRead10的参数为0,含义是从LBA(Logical Block Address)为0的地方读取一个扇区,如果你向读取其它扇区,可以更改这个值,其最大值我们在实现 READ CAPACITY时已经读出了,可以参考;此外,注意CBW的字符顺序是little endian,所以我们在填写LBA和读取最大LBA时都做了相应的转换。

    好了,应该没有什么了!

    Enjoy it.

posted @ 2010-11-24 14:09 wrh 阅读(1328) | 评论 (1)编辑 收藏
 USB现在已经成为PC机必不可少的接口之一,几乎所有的设备都可以接在USB设备上,USB键盘、鼠标、打印机、摄像头,还有常用的U盘等等,从本篇文章开始,将集中篇幅介绍一下在DOS中使用USB设备的方法,具体会有几篇暂不好定,写到哪里算哪里吧,三、四篇总是少不了的。
    本文介绍如何使用我以前文章中介绍过的知识在你的机器中找到USB设备,并判定设备类型。
    一个USB系统一般由一个USB主机(HOST)、一个或多个USB集线器(HUB,但不是局域网里的集线器)和一个或多个USB设备节点(NODE)组成,一个系统中只有一个HOST,我们PC机里的USB实际上就是HOST和HUB两部分,你的PC机可能会有4个USB口,其实是一个HOST,一个HUB,HUB为你提供了4个端口,我们插在USB口上的器件,一般是USB设备,比如U盘,USB打印机等,当然我们也可以插一个集线器上去,使你的一个USB口扩展成多个。
    实际上我们说在DOS下使用USB,就是对USB系统中的HOST进行编程管理,根据USB的规范,HOST将对连接在上面的HUB和USB设备进行管理,不用我们操心。HOST器件目前有三个规范,OHCI(Open Host Controller Interface)、UHCI(Universal Host Controller Interface)支持USB1.1,EHCI(Enhanced Host Controller Interface)支持USB2.0,以后的文章中,我们将侧重介绍OHCI和EHCI。
    学习USB编程,读规范是少不了的,以下是一些应该阅读的规范下载:
    OHCI规范:
http://blog.hengch.com/specification/usb_ohci_r10a.pdf
    EHCI规范:http://blog.hengch.com/specification/usb_ehci_r10.pdf
    USB规范1.1:http://blog.hengch.com/specification/usb_spec11.pdf
    USB规范2.0:http://blog.hengch.com/specification/usb_spec20.pdf
    本文介绍的内容不需要学习规范。

    下面进入正题,列出你的USB设备,USB的HOST是挂接在PCI总线上的,所以通过PCI设备的遍历就可以找到你的机器上的所有USB设备,在以前介绍PCI的配置空间时,曾经介绍过在配置空间中有一个占三个字节的分类代码字段(如果不知道,请参阅我以前的博文
《遍历PCI设备》),在偏移为0x0B的字节叫基本分类代码,在偏移为0x0A的字节叫子分类代码,在偏移为0x09的字节叫编程接口代码,对于USB设备类说,基本分类代码为0x0C,子分类代码为0x03,对于符合不同规范的HOST器件而言,编程接口代码是不同的,UHCI的编程接口代码是0x00,OHCI的编程接口代码是0x10,EHCI的编程接口代码是0x20,我想了解这些就足够了。
    下面列出USB设备的源程序。
#include <stdio.h>
#include <stdlib.h>
#include <dpmi.h>

typedef unsigned long      UDWORD;
typedef short int          WORD;
typedef unsigned short int UWORD;
typedef unsigned char      UBYTE;

typedef union {
  struct {
    UDWORD edi;
    UDWORD esi;
    UDWORD ebp;
    UDWORD res;
    UDWORD ebx;
    UDWORD edx;
    UDWORD ecx;
    UDWORD eax;
  } d;
  struct {
    UWORD di, di_hi;
    UWORD si, si_hi;
    UWORD bp, bp_hi;
    UWORD res, res_hi;
    UWORD bx, bx_hi;
    UWORD dx, dx_hi;
    UWORD cx, cx_hi;
    UWORD ax, ax_hi;
    UWORD flags;
    UWORD es;
    UWORD ds;
    UWORD fs;
    UWORD gs;
    UWORD ip;
    UWORD cs;
    UWORD sp;
    UWORD ss;
  } x;
  struct {
    UBYTE edi[4];
    UBYTE esi[4];
    UBYTE ebp[4];
    UBYTE res[4];
    UBYTE bl, bh, ebx_b2, ebx_b3;
    UBYTE dl, dh, edx_b2, edx_b3;
    UBYTE cl, ch, ecx_b2, ecx_b3;
    UBYTE al, ah, eax_b2, eax_b3;
  } h;
} X86_REGS;
/*************************************************************
 * Excute soft interrupt in real mode
 *************************************************************/
int x86_int(int int_num, X86_REGS *x86_reg) {
  __dpmi_regs d_regs;
  int return_value;

  d_regs.d.edi = x86_reg->d.edi;
  d_regs.d.esi = x86_reg->d.esi;
  d_regs.d.ebp = x86_reg->d.ebp;
  d_regs.d.res = x86_reg->d.res;
  d_regs.d.ebx = x86_reg->d.ebx;
  d_regs.d.ecx = x86_reg->d.ecx;
  d_regs.d.edx = x86_reg->d.edx;
  d_regs.d.eax = x86_reg->d.eax;
  d_regs.x.flags = x86_reg->x.flags;
  d_regs.x.es = x86_reg->x.es;
  d_regs.x.ds = x86_reg->x.ds;
  d_regs.x.fs = x86_reg->x.fs;
  d_regs.x.gs = x86_reg->x.gs;
  d_regs.x.ip = x86_reg->x.ip;
  d_regs.x.cs = x86_reg->x.cs;
  d_regs.x.sp = x86_reg->x.sp;
  d_regs.x.ss = x86_reg->x.ss;

  return_value = __dpmi_int(int_num, &d_regs);

  x86_reg->d.edi = d_regs.d.edi;
  x86_reg->d.esi = d_regs.d.esi;
  x86_reg->d.ebp = d_regs.d.ebp;
  x86_reg->d.res = d_regs.d.res;
  x86_reg->d.ebx = d_regs.d.ebx;
  x86_reg->d.ecx = d_regs.d.ecx;
  x86_reg->d.edx = d_regs.d.edx;
  x86_reg->d.eax = d_regs.d.eax;
  x86_reg->x.flags = d_regs.x.flags;
  x86_reg->x.es = d_regs.x.es;
  x86_reg->x.ds = d_regs.x.ds;
  x86_reg->x.fs = d_regs.x.fs;
  x86_reg->x.gs = d_regs.x.gs;
  x86_reg->x.ip = d_regs.x.ip;
  x86_reg->x.cs = d_regs.x.cs;
  x86_reg->x.sp = d_regs.x.sp;
  x86_reg->x.ss = d_regs.x.ss;

  return return_value;
}
/**********************************
 * Read Configuration WORD if PCI
 **********************************/
UWORD ReadConfigWORD(WORD pciAddr, int reg) {
  X86_REGS inregs;

  inregs.x.ax = 0xB109;    // Read Configuration word
  inregs.x.bx = pciAddr;
  inregs.x.di = reg;       // Register number
  x86_int(0x1A, &inregs);

  return inregs.d.ecx;     // the value
}
// main program
int main(void) {
  UWORD pciAddr;
  UWORD subClass;
  int ehciCount = 0, ohciCount = 0, uhciCount = 0;

  for (pciAddr = 0; pciAddr < 0xffff; pciAddr++) {
    if (ReadConfigWORD(pciAddr, 0) != 0xFFFF) {
      // Read Class Code
      if (ReadConfigWORD(pciAddr, 0x000a ) == 0x0c03) {  // Usb Host Controller
        // Read SubClass Code
        subClass = ReadConfigWORD(pciAddr, 0x0008);
        if ((subClass & 0xff00) == 0x2000) {  // uhci
          ehciCount++;
        } else if ((subClass & 0xff00) == 0x1000) {  // ohci
          ohciCount++;
        } else if ((subClass & 0xff00) == 0x00) {    // uhci
          uhciCount++;
        }
      }
    }
  }
  printf("There are %d ohci device(s).\n", ohciCount);
  printf("There are %d ehci device(s).\n", ehciCount);
  printf("There are %d uhci device(s).\n", uhciCount);
}

    程序非常简单,所有概念在以前的博文中均有过介绍,其中的子程序大多是以前程序范例中使用过的,所以在这里就不做更多的解释了,程序中,我们仅仅列出了设备的数量,但很显然,用这种方法,我们可以从配置空间里读出基地址等信息,这些在以后的文章中会用到。
posted @ 2010-11-24 14:07 wrh 阅读(725) | 评论 (0)编辑 收藏
仅列出标题
共25页: First 2 3 4 5 6 7 8 9 10 Last 

导航

<2008年12月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

统计

常用链接

留言簿(19)

随笔档案

文章档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜