精华内容
下载资源
问答
  • 使用udev监测usb摄像头状态,并在终端进行显示add和remove,add为添加设备,remove为移除设备。
  • QT5.9.8鼠标键盘热插拔默认情况是使用udev进行热插拔的,但很多情况下嵌入式系统udev配置不完整, 可能需要systemd里面的udev组件才能正常工作 鉴于此,参考网上的朋友的修改例子,不使用udev也能正常检测键鼠热插...
  • 一个很好用的跨平台的USB设备(比如USB鼠标、U盘等)热插拔 QT库。兼容LINUX(Ubuntu),WINDOWS,WINCE,MAC OS。带有终端以及GUI例子,方便运行测试。
  • MFC 实现USB热插拔

    2017-12-05 14:48:49
    在MFC开发环境下实现实时检测USB插入与出情况,并给与不同的反馈
  • Qt之USB热插拔

    2017-12-29 17:47:03
    基于Qt5.5.1的应用程序,支持监控系统设备变化,当插入可移动设备、右键弹出和移除可移动设备时可以实时的通知应用程序
  • 基于LibUsbDotNet 实现的USB设备的热插拔的上位机,是完整的工程,希望能够帮到大家,谢谢
  • netlink实现usb热插拔

    2018-05-11 10:49:05
    linux下netlink机制实现usb热插拔事件并获取诸如厂商编号、协议号等获取相关信息,在不采用udev机制的情况下,可以参考的一种实现方式
  • USB热插拔,MFC,demo程序,有消息响应以及处理逻辑,适合新手参考使用。欢迎参考。 。
  • USB热插拔

    千次阅读 2018-10-12 11:13:21
    学习USB热插拔之前,先学习一些USB的硬件知识: 一、USB基础概念 1、硬件知识(USB插座和插头)        在最初的标准里,USB接头有4条线:电源,D-,D+,地线。我们暂且把这样的...

    转自:http://blog.csdn.net/zqixiao_09/article/details/51056903  

      学习USB热插拔之前,先学习一些USB的硬件知识:

    一、USB基础概念

    1、硬件知识(USB插座和插头)

           在最初的标准里,USB接头有4条线:电源,D-,D+,地线。我们暂且把这样的叫做标准的USB接头吧。后来OTG出现了,又增加了miniUSB接头。而miniUSB接头则有5条线,多了一条ID线,用来标识身份用的。标准USB口只有A型和B型。其中每一型又分为插头和插座,例如A型插头,A型插座等。我们平常电脑上用的那种插座叫做A型USB插座,而相应的插头,叫做A型插头,例如U盘上那种。而像打印机上面那个插座,则是B型插座(比较四方的,没电脑上面那种扁),相应的插头,就是B型插头。也许你见过一头方一头扁的USB延长线,没错了,扁的那头就叫做A型插头,而方的那头,就叫做B型插头,而相应的被插的那两个插座,就分别是A型插座和B型插座了。A型插头是插不进B型插座的,反之亦然。

          miniUSB也分为A型,B型,但增加了一个AB型(不是血型呀,别搞错了,没有O型^_^)。既然它叫做miniUSB,那么当然它就是很小的了,主要是给便携式设备用的,例如MP3、手机、数码相机等。USB是一主多从结构,即一个时刻只能有一台主机。像PC机就是一个主机,其它的只能是设备,因而两个设备之间是无法直接进行通信的。而USB OTG(on the go)的出现,则解决了这个矛盾:一个设备可以在某种场合下,改变身份,以主机的形式出现。因而就出现了AB型的miniUSB插座,不管是A型miniUSB插头,还是B型miniUSB插头,都可以插进去,而靠里面多出的那条ID线来识别它的身份:是主机还是从机。这样两个USB设备就可以直接连接起来,进行数据传送了。 像我们MP3上用的那中miniUSB插座,就是B型的miniUSB插座(注意,有一类miniUSB插座,似乎不是USB规范里面的,因为miniUSB接头应该有5条线,而这种插座只有4条线)。由于USB是支持热插拔的,因此它在接头的设计上也有相应的措施。USB插头的地引脚和电源引脚比较长,而两个数据引脚则比较短,这样在插入到插座中时,首先接通电源和地,然后再接通两个数据线。这样就可以保证电源在数据线之前接通,防止闩锁发生。至于USB电缆,通常我们不怎么关心,买现成的就行了,除非你是生产USB线缆的。在全速模式下需要使用带屏蔽的双绞电缆线,而低速模式模式则可以不用屏蔽和双绞。此外,USB协议规定,USB低速电缆长度不得超过3米,而全速电缆长度不得超过5米。这是因为线缆传输有延迟,要保证能够正确响应,就不能延迟太多。USB标准规定了里面信号线的颜色,其中Vbus为红色,D-为白色,D+为绿色,GND为黑色。然而,我见过很多USB线缆并没有遵循标准,所以大家在使用时要小心,用表测量一下比较可靠。


    2、集线器把USB设备的连接报告给USB主控制器

          首先,在USB集线器的每个下游端口的D+和D-上,分别接了一个15K欧姆的下拉电阻到地。这样,在集线器的端口悬空时,就被这两个下拉电阻拉到了低电平。而在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和高速设备,上拉电阻是接在D+上;而低速设备则是上拉电阻接在D-上。这样,当设备插入到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的一条拉高了。集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上一层的集线器报告给USB主控制器),这样就检测到设备的插入了。USB高速设备先是被识别为全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到高速模式的。在高速模式下,是电流传输模式,这时将D+上的上拉电阻断开。


    二、什么是热插拔

    1、基础概念

         热插拔(hot-plugging或Hot Swap)即带电插拔,热插拔功能就是允许用户在不关闭系统,不切断电源的情况下取出和更换损坏的硬盘、电源或板卡等部件,从而提高了系统对灾难的及时恢复能力、扩展性和灵活性等,例如一些面向高端应用的磁盘镜像系统都可以提供磁盘的热插拔功能。具体用学术的说法就是:热替换(Hot replacement)、热添加(hot expansion)和热升级(hot upgrade)


    2、热插拔好处

    系统中加入热插拔的好处包括:

    在系统开机情况下将损坏的模块移除,还可以在开机情况下做更新或扩容而不影响系统操作。

    由于热插拔零件的可靠度提升,还可以将它们用做断电器,而且因为热插拔能够自动恢复,有很多热插拔芯片为系统提供线路供电情况的信号,以便系统做故障分析,因此减少了成本。


    三、Linux 下USB热插拔处理

    1、 Linux下USB HUB的驱动的实现和分析:

           在系统初始化的时候在usb_init函数中调用usb_hub_init函数,就进入了hub的初始化

           在usb_hub_init函数中完成了注册hub驱动,并且利用函数kthread_run创建一个内核线程。该线程用来管理监视hub的状态,所有的情况都通过该线程来报告。

          USB设备是热插拔,这就和PCI设备不同,PCI设备是在系统启动的时候都固定了,因此PCI设备只需要初始化进行枚举就可以了,采用递归算法即可。而USB设备需要热插拔,因此在hub_probe函数中调用hub_configure函数来配置hub,在这个函数中主要是利用函数usb_alloc_urb函数来分配一个urb,利用usb_fill_int_urb来初始化这个urb结构,包括hub的中断服务程序hub_irq的,查询的周期等。

           每当有设备连接到USB接口时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq,在该函数中利用kick_khubd将hub结构通过event_list添加到khubd的队列hub_event_list,然后唤醒khubd。进入hub_events函数,该函数用来处理khubd事件队列,从khubd的hub_event_list中的每个usb_hub数据结构。该函数中首先判断hub是否出错,然后通过一个for循环来检测每个端口的状态信息。利用usb_port_status获取端口信息,如果发生变化就调用hub_port_connect_change函数来配置端口等。


    2、软件层次分析

    这里我们先讲讲USB热插拔事件的处理工作。---Khubd守护进程。

    -Khubd守护进程它是一个守护进程,来检查usb port的事件通知HCD和usb core,然后做相应的处理。

    驱动目录drivers/usb/*
    usb/serial  usb 串行设备驱动 (例如usb 3G卡、蓝牙等)
    usb/storage  usb 大储量磁盘驱动(u盘)  
    usb/host usb host usb主机控制器驱动(嵌入式otg:dwc_otg)
    usb/core   usb 核心一些处理代码,所有的驱动相关处理都在这里,也都注册到它里面。
    usb/usb-skeleton.c 经典的usb客户驱动框架,可以参考

    当然还有其他这里不再说明。

    下面贴出USB的整体驱动框架:


    这里我们主要分析khub的工作原理: 硬件层次是hub的工作,如何和host及其设备间通信及相应事件

    [usb/core/hub.c ]

    1. int usb_hub_init(void)  
    2. {  
    3.     if (usb_register(&hub_driver) < 0) {  
    4.         printk(KERN_ERR "%s: can't register hub driver\n",  
    5.             usbcore_name);  
    6.         return -1;  
    7.     }  
    8.   
    9.     khubd_task = kthread_run(hub_thread, NULL, "khubd");  
    10.     if (!IS_ERR(khubd_task))  
    11.         return 0;  
    12.   
    13.     /* Fall through if kernel_thread failed */  
    14.     usb_deregister(&hub_driver);  
    15.     printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);  
    16.   
    17.     return -1;  
    18. }  
    
      
    1. int usb_hub_init(void)
    2. {
    3. if (usb_register(&hub_driver) < 0) {
    4. printk(KERN_ERR "%s: can't register hub driver\n",
    5. usbcore_name);
    6. return -1;
    7. }
    8. khubd_task = kthread_run(hub_thread, NULL, "khubd");
    9. if (!IS_ERR(khubd_task))
    10. return 0;
    11. /* Fall through if kernel_thread failed */
    12. usb_deregister(&hub_driver);
    13. printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
    14. return -1;
    15. }
    这里我们只关心kthread_run(hub_thread, NULL, "khubd"); 然后我们看hub_thread函数
    1. static int hub_thread(void *__unused)  
    2. {  
    3.     do {  
    4.         hub_events();  
    5.         wait_event_interruptible(khubd_wait,  
    6.                 !list_empty(&hub_event_list) ||  
    7.                 kthread_should_stop());  
    8.         try_to_freeze();  
    9.     } while (!kthread_should_stop() || !list_empty(&hub_event_list));  
    10.   
    11.     pr_debug("%s: khubd exiting\n", usbcore_name);  
    12.     return 0;  
    13. }  
    
      
    1. static int hub_thread(void *__unused)
    2. {
    3. do {
    4. hub_events();
    5. wait_event_interruptible(khubd_wait,
    6. !list_empty(&hub_event_list) ||
    7. kthread_should_stop());
    8. try_to_freeze();
    9. } while (!kthread_should_stop() || !list_empty(&hub_event_list));
    10. pr_debug( "%s: khubd exiting\n", usbcore_name);
    11. return 0;
    12. }

    这里我们看到了hub_events()函数。然后设置运行状态,如果有事件就加入hub_event_list。自此khubd运行起来了。

    这里我们同样贴出它的函数调用流程图(这里懒得自己画了,就剪切了个^^)



    通过流程图我们可以清晰的明白,当usb设备插入usb接口后,khubd运行,它检测到port状态的变化,调用hub_port_connect_change(),如果是新设备那么usb_allco_dev,然后调用usb_new_device来进行配置使usb设备可以正常工作。详细流程请看源码。


    四、USB的枚举过程

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

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

    枚举过程主要分如下几步:

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

    Step2:khubd识别出电流变化的那个端口

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

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

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



    展开全文
  • 自动检测USB热插拔

    2018-11-21 09:25:47
    利用Linux的netlink机制,自动检测U盘或SD卡的热插拔事件,完成存储器的挂载
  • 本文详细介绍了热插拔保护电路设计的全过程.
  • qt -evdev默认编译选项,鼠标和键盘热拔插支持文件 /Users/xiejianwei/Qt5.7.0/5.7/Src/qtbase/src/platformsupport/devicediscovery目录下qdevicediscovery_static.cpp和qdevicediscovery_static_p.h, 编辑这2个...
  • usb热插拔检测挂载.rar

    2020-06-03 10:25:13
    ubuntu16.04下usb设备的自动检测挂载(脚本),可实现usb设备自动检测插拔挂载,将mountusb.sh、umountusb.sh脚本拷贝至/opt/usb(如果没有目录则自行创建一个)负责挂载和卸载usb设备,将11-add-usb.rules和11-add-...
  • linux下usb热插拔处理

    2011-12-01 21:30:12
    本文介绍的是linux下处理usb热插拔的方法
  • 消息循环通过创建单独的消息响应窗口实现。
  • USB 设备热插拔的检测

    2020-09-18 18:02:37
    2. 打开软件后, USB 热插拔 。 此时用WM_DEVICECHANGE消息去获取设备信息。 当设备插入或者移除时,系统给当前主窗口发送WM_DEVICECHANGE消息 。 注意: 默认状态, 任何设备都会通知。 所以需要注册指定的通知消息...

    系统检测USB 设备往往分为两个过程:

     

    1. USB 设备已经插入, 打开软件。 此时需要枚举当前设备列表中所有的设备,并过滤指定的USB 设备。

    2. 打开软件后, USB 热插拔 。 此时用WM_DEVICECHANGE 消息去获取设备信息。 当设备插入或者移除时,系统给当前主窗口发送WM_DEVICECHANGE 消息 。 注意: 默认状态, 任何设备都会通知。 所以需要注册指定的通知消息。

     

    第一种:

    枚举系统中所有的USB 设备, 可根据 PID,VID 进一步做判断。

    void CDataSyncAccessDlg::CollectUSBInfo()
    {
    	// 获取当前系统所有使用的设备
    	DWORD dwFlag = (DIGCF_ALLCLASSES | DIGCF_PRESENT);
    	HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, NULL, NULL, dwFlag);
    	if (INVALID_HANDLE_VALUE == hDevInfo)
    	{
    		AfxMessageBox(_T("获取系统设备列表失败"));
    		return;
    	}
     
    	// 准备遍历所有设备查找USB
    	SP_DEVINFO_DATA sDevInfoData;
    	sDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
     
    	TCHAR szDIS[MAX_PATH]; // Device Identification Strings, 
    	DWORD nSize = 0;
    	for (int i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &sDevInfoData); i++)
    	{
    		nSize = 0;
    		if (!SetupDiGetDeviceInstanceId(hDevInfo, &sDevInfoData, szDIS, sizeof(szDIS), &nSize))
    		{
    			AfxMessageBox(_T("获取设备识别字符串失败"));
    			break;
    		}
    		// 设备识别串的前三个字符是否是"USB", 模板: USB\VID_XXXX&PID_XXXX\00000xxxxxxx
    		CString strDIS(szDIS);
    		strDIS.MakeUpper();
    		if (strDIS.Left(3) == _T("USB"))
    		{
    			int pos = strDIS.ReverseFind('\\');
    			CString DeviceSer = strDIS.Right(strDIS.GetLength() - pos-1);
    			//匹配序列号
    			if (ValidDevcie(DeviceSer))
    			{
    				SetUSBState(TRUE);
    			}
    		}
    	}
    	// 释放设备
    	SetupDiDestroyDeviceInfoList(hDevInfo);
    }

     

    第二种:

    windows操作系统在检测到硬件变化时,会发送一个WM_DEVICECHANGE硬件change消息。因此,我们要做的就是在我们的程序中添加WM_DEVICECHANGE的消息响应。

    BEGIN_MESSAGE_MAP(CHWDetectDlg, CDialog)zai
        // ... other handlers
        ON_MESSAGE(WM_DEVICECHANGE, OnMyDeviceChange)
    END_MESSAGE_MAP()
     
    LRESULT CHWDetectDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam)
    {
        // for more information, see MSDN help of WM_DEVICECHANGE
        // this part should not be very difficult to understand
        if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) {
            PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
            switch( pHdr->dbch_devicetype ) {
                case DBT_DEVTYP_DEVICEINTERFACE:
                    PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
                     //根据pDevHnd结构体内的一些数据判断是否为指定设备,例如
                    //Str = pDevInf->dbcc_name;
    			   //Resul1 = Str.Find("0483");
    			    //resul2 = Str.Find("5750");
                     //利用上面获取比较PID与VID方式判断发生改变的设备是否为指定设备
                    break;
     
                case DBT_DEVTYP_HANDLE:
                    PDEV_BROADCAST_HANDLE pDevHnd = (PDEV_BROADCAST_HANDLE)pHdr;
                   
                    // do something...
                    break;
     
                case DBT_DEVTYP_OEM:
                    PDEV_BROADCAST_OEM pDevOem = (PDEV_BROADCAST_OEM)pHdr;
                    // do something...
                    break;
     
                case DBT_DEVTYP_PORT:
                    PDEV_BROADCAST_PORT pDevPort = (PDEV_BROADCAST_PORT)pHdr;
                    // do something...
                    break;
     
                case DBT_DEVTYP_VOLUME:
                    PDEV_BROADCAST_VOLUME pDevVolume = (PDEV_BROADCAST_VOLUME)pHdr;
                    // do something...
                    break;
            }
        }
        return 0;
    }

    然而默认情况下,Windows操作系统发送WM_DEVICECHANGE有些限制:

    1 只有顶层窗体的程序才能收到这个消息

    2 仅仅串口、磁盘发生改变,才对每个程序广播这个消息

    的确不错,至少你可以知道移动U盘、移动硬盘、光盘被安装或弹出了,通过DEV_BROADCAST_VOLUME.dbcv_unitmask你也可以获得其对应的盘符。但实际上,你不知道底层处理的是哪个物理设备实际上被安装到了系统中。

    API:RegisterDeviceNotification()

    所以,你不得不调用RegisterDeviceNotification()API来注册其他类型的设备改变,或是你的程序仅仅是一个服务程序、没有顶层窗体的程序。例如:如下的例子是用来注册一个设备类型的接口的:

    void CDataSyncAccessDlg::RegMessage()
    {
    	HDEVNOTIFY hDevNotify;
    	DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
    	ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
    	NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    	NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;  //需要注册的设备类型
       //下面是枚举了很多设备的dbcc_classguid,这里也可以指定具体某一个
    	for (int i = 0; i<sizeof(GUID_DEVINTERFACE_LIST) / sizeof(GUID); i++) {
    		NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_LIST[i]; 
    		hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
    		if (!hDevNotify) {
    			AfxMessageBox(CString("Can't register device notification: ")
    				+ _com_error(GetLastError()).ErrorMessage(), MB_ICONEXCLAMATION);
    			return;
    		}
    	}
    }
    
    //DEVICE_NOTIFY_WINDOW_HANDLE 只响应对应dbcc_classguid的设备
    //DEVICE_NOTIFY_ALL_INTERFACE_CLASSES 响应所有设备(即所有设备变动都会响应)

     

    GUID简介

    一个支持即插即用的设备,有2个不同的GUID相关,一个设备接口GUID, 一个是设备类GUID

    设备类GUID:定义了广泛意义上一类设备的GUID,如果你打开设备管理器[我的电脑右键—>设备管理器],默认的是按照“类型”排列的,每一个“类型”就是一个设备类,同时每一个设备类有一个唯一的ID就是设备类GUID。设备GUID定义了此类设备的图标、默认的安全设置、安装属性(例如用户不能手动安装这类设备,而必须通过PNP来遍历),以及其他的设置信息。设备类GUID没有定义对应的I/O接口(请参考术语表),而更像是设备的分组。我认为一个比较好的例子是端口类。串口COM和并口LPT 都是端口类的一部分,但其各有各的I/O接口,而且彼此互不兼容.一个设备仅仅属于一个设备类。我们可以通过设备驱动的INF文件的开头来查看该设备的设备类GUID。

    设备接口GUID:定义了相互关联I/O接口的GUID,每一个接口GUID的具体实例都支持基本的I/O设置。设备接口GUID也是对应的驱动程序基于PNP状态来注册、启用、禁用设备。如果需要,一个设备甚至可以注册多个同样GUID的实例(假使每个都有相同的名字)[注:在实际的程序中,多次插拔USB口,确实会驱出相同的串口,例如port12,port12,port12…],尽管在现实世界中完全不需要这样。一个简单的I/O关联接口是键盘设备,每个键盘设备的接口GUID必须相同。

    可以通过如下的注册表路径来查看当前设备类GUID, 设备接口GUID:

    • \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class
    • \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses

     常用设备的接口GUID如下:

    Device Interface NameGUID
    USB Raw Device{a5dcbf10-6530-11d2-901f-00c04fb951ed}
    Disk Device{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
    Network Card{ad498944-762f-11d0-8dcb-00c04fc3358c}
    Human Interface Device (HID){4d1e55b2-f16f-11cf-88cb-001111000030}
    Palm{784126bf-4190-11d4-b5c2-00c04f687a67}

    从MSDN中,我们知道:

    typedef struct _DEV_BROADCAST_DEVICEINTERFACE {
        DWORD dbcc_size;
        DWORD dbcc_devicetype;
        DWORD dbcc_reserved;
        GUID dbcc_classguid;
        TCHAR dbcc_name[1];
    } DEV_BROADCAST_DEVICEINTERFACE *PDEV_BROADCAST_DEVICEINTERFACE;

    我们似乎可以通过dbcc_name知道那个设备安装到了当前系统。答案是错误的,dbcc_name仅仅是操作系统内部使用来做为ID的,其实不易读的,例如下面的这个dbcc_name:

     

    \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}

    • \\?\USB: USB 意思是这是一个USB设备类
    • Vid_04e8&Pid_053b: Vid/Pid 是硬件ID,由厂商ID和产品ID组成(但这是由设备类指定的,USB设备类使用VID/PID,不同的设备类使用不同的命名约定)
    • 002F9A9828E0F06: 不清楚是怎么生成的,是唯一设备ID
    • {a5dcbf10-6530-11d2-901f-00c04fb951ed}:设备接口类GUID

    现在,我们来解出设备描述信息或是设备别名,有2种办法:

    1 直接读注册表, \\HKLM\SYSTEM\CurrentControlSet\Enum\USB\Vid_04e8&Pid_503b\0002F9A9828E0F06

    2 使用 SetupDiXxx 系列API

    API:SetupDiXxx()

    Windows定义了一组API,让用户通过编程的办法来获取对应的硬件设备信息。例如,我们可以通过dbcc_name来获得设备描述信息或是设备别名。下面是这个办法都具体步骤:

    1 首先通过SetupDiGetClassDevs()来获得设备信息集 HDEVINFO,这个操作等同于是一个获取目录句柄的过程。

    2 接着使用SetupDiEnumDeviceInfo()来遍历出这个设备信息集内的所有设备,这个操作等同于把目录列表的过程。对于每个遍历出的,我们可以获得SP_DEVINFO_DATA,这个等同于是文件句柄。

    3 在上面的枚举过程中,使用SetupDiGetDeviceInstanceId()来读取每个设备的实例ID,这个操作等同于是读文件的属性,一个设备的实例ID类似这个:”USB\Vid_04e8&Pid_503b\0002F9A9828E0F06”,和dbcc_name非常像。

    4 如果设备的实例ID等同于dbcc_name,则通过SetupDiGetDeviceRegistryProperty()来获取设备描述信息或是设备别名信息。

    void CHWDetectDlg::UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam)
    {
        // dbcc_name:
        // \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
        // convert to
        // USB\Vid_04e8&Pid_503b\0002F9A9828E0F06
        ASSERT(lstrlen(pDevInf->dbcc_name) > 4);
        CString szDevId = pDevInf->dbcc_name+4;
        int idx = szDevId.ReverseFind(_T('#'));
        ASSERT( -1 != idx );
        szDevId.Truncate(idx);
        szDevId.Replace(_T('#'), _T('\\'));
        szDevId.MakeUpper();
     
        CString szClass;
        idx = szDevId.Find(_T('\\'));
        ASSERT(-1 != idx );
        szClass = szDevId.Left(idx);
     
        // if we are adding device, we only need present devices
        // otherwise, we need all devices
        DWORD dwFlag = DBT_DEVICEARRIVAL != wParam
            ? DIGCF_ALLCLASSES : (DIGCF_ALLCLASSES | DIGCF_PRESENT);
        HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, szClass, NULL, dwFlag);
        if( INVALID_HANDLE_VALUE == hDevInfo )
        {
            AfxMessageBox(CString("SetupDiGetClassDevs(): ")
                + _com_error(GetLastError()).ErrorMessage(), MB_ICONEXCLAMATION);
            return;
        }
     
        SP_DEVINFO_DATA* pspDevInfoData =
            (SP_DEVINFO_DATA*)HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA));
        pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
        for(int i=0; SetupDiEnumDeviceInfo(hDevInfo,i,pspDevInfoData); i++)
        {
            DWORD DataT ;
            DWORD nSize=0 ;
            TCHAR buf[MAX_PATH];
     
            if ( !SetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize) )
            {
                AfxMessageBox(CString("SetupDiGetDeviceInstanceId(): ")
                    + _com_error(GetLastError()).ErrorMessage(), MB_ICONEXCLAMATION);
                break;
            }
     
            if ( szDevId == buf )
            {
                // device found
                if ( SetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData,
                    SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, sizeof(buf), &nSize) ) {
                    // do nothing
                } else if ( SetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData,
                    SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, sizeof(buf), &nSize) ) {
                    // do nothing
                } else {
                    lstrcpy(buf, _T("Unknown"));
                }
                // update UI
                // .....
                // .....
                break;
            }
        }
     
        if ( pspDevInfoData ) HeapFree(GetProcessHeap(), 0, pspDevInfoData);
        SetupDiDestroyDeviceInfoList(hDevInfo);
    }
     

    禁用设备

    假使你有一个正确的HDEVINFO和SP_DEVINFO_DATA(实际上,我们保持dbcc_name座位树节点的tag,当右键单击某一个节点的时候,可以通过调用SetupDiGetClassDevs和SetupDiEnumDeviceInfo来获得所需东西),按照如下的步骤即可禁用一个设备:

    1 给SP_PROPCHANGE_PARAMS结构体赋上正确的值

    2 把上面赋完值的SP_PROPCHANGE_PARAMS作为参数传入到SetupDiSetClassInstallParams()

    3 调用SetupDiCallClassInstaller(),传递参数DIF_PROPEFRTYCHANGE

    实际上,DIF也是按位做与运算后兼容的,你也可以去传递不同的DIF参数来调用SetupDiSetClassInstallParams()。 更多信息,请参考MSDN”Handling DIF Codes”

    SP_PROPCHANGE_PARAMS spPropChangeParams ;
    spPropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
    spPropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE ;
    spPropChangeParams.Scope = DICS_FLAG_GLOBAL ;
    spPropChangeParams.HwProfile = 0; // current hardware profile
    spPropChangeParams.StateChange = DICS_DISABLE
     
    if( !SetupDiSetClassInstallParams(hDevInfo, &spDevInfoData,
        // note we pass spPropChangeParams as SP_CLASSINSTALL_HEADER
        // but set the size as sizeof(SP_PROPCHANGE_PARAMS)
        (SP_CLASSINSTALL_HEADER*)&spPropChangeParams, sizeof(SP_PROPCHANGE_PARAMS)) )
    {
        // handle error
    }
    else if(!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, &spDevInfoData))
    {
        // handle error
    }
    else
    {
        // ok, show disable success dialog
        // note, after that, the OS will post DBT_DEVICEREMOVECOMPLETE for the disabled device
    }

    附录:

    我使用这个程序,已经多次测试了USB的无线网卡的,插入、拔出测试。

    局限性:

    1 明显的,必须先运行该程序,才能检测硬件设备。例如:设备在操作系统启动前就已经连接,或者在这个程序运行前的连接都不会被检测。但这个问题,可以通过保存当前系统配置到远程计算机上,等启动完这个程序后再坚持不同的配置来解决

    2 我们可以禁用设备,换而言之这也是我们所有能做到。我们不能访问设备底层控制。 我认为可以通过重新用基于内核的过滤驱动来实现,则可以解决这个问题。

    非常好的文章

    原文:http://www.codeproject.com/Articles/14500/Detecting-Hardware-Insertion-and-or-Removal

    一个封装的库http://www.codeproject.com/Articles/119168/Hardware-Change-Detection

    展开全文
  • openwrt USB外挂U盘的时候很需要热插拔功能 openwrt官网传送门 当某些事件发生时,Procd(init系统和进程管理守护进程)执行位于/etc/hotplug.d/中的脚本,例如当接口启动或关闭时,检测到新的存储驱动器时,或者...

    目录

    前言

    一、hotplug相关子目录

    block 文件夹脚本中的变量

    二、相关内核勾选

    三、脚本示例

    总结



    前言

    openwrt USB外挂U盘的时候很需要热插拔功能(内核版本21.02内测版本)

    openwrt官网传送门

    当某些事件发生时,Procd(init系统和进程管理守护进程)执行位于/etc/hotplug.d/中的脚本,例如当接口启动或关闭时,检测到新的存储驱动器时,或者按下按钮时.

    当使用PPPoE连接或者在不稳定的网络中,或使用硬件按钮时非常有用。

    该功能模块模拟/扩展了长期未用的Hotplug2程序包所执行的操作。


    提示:以下是本篇文章正文内容,下面案例可供参考

    一、hotplug相关子目录

    在 /etc/hotplug.d 文件夹你可以发现 block ifacenet 和 ntp 等文件夹.

    触发事件触发后,Procd将按字母顺序执行该触发器子文件夹中的所有脚本。 这就是为什么那里的大多数脚本都使用数字前缀。

    • block 文件夹用于块设备事件(块设备已连接/已断开连接)

    • iface 文件夹用于接口事件(当LAN或WAN等接口连接/断开时)

    • net 文件夹用于:(可能与网络相关)

    • ntp 文件夹用于时间同步事件(Time step,时间服务器层变化)

    • button 文件夹用于按钮事件 (缺省不创建, 由 /etc/rc.button 代替)

    • usb 文件夹用于类似3g-modem和tty*的USB设备

    对于其他类型的触发器,可能(应该)是其他的。通过查看源码 in git 他们可以是按钮, 声音设备, 串口和USB串口加密狗。

    block 文件夹脚本中的变量

    对于 block文件夹中的脚本,他们是这些(相关的)环境变量

    变量名描述
    ACTION“add” 或 “remove”
    DEVICENAME与下面的DEVNAME相同
    DEVNAME设备或分区名称(如果连接驱动器,你会得到一个使用“sda”的热插拔调用,另一个使用“sda1”进行热插拔调用)
    DEVPATH完整的设备路径(如 “/devices/pci0000:00/0000:00:0b.0/usb1/1-1/1-1:1.0/host7/target7:0:0/7:0:0:0/block/sdc/sdc1 “)
    DEVTYPEDEVNAME和DEVICENAME的名称是什么类型,当插入具有可读分区的设备时,该值为“partition”,当移除该设备时,该值为“disk”。
    MAJOR主设备号
    MINOR次要设备号
    SEQNUM序号(一个数字)
    SUBSYSTEM固定值 “block”

    二、相关内核勾选

    添加USB相关支持

    Kernel modules —> USB Support —> <*> kmod-usb-core. ##默认已经选了
    Kernel modules —> USB Support —> <*> kmod-usb-ohci. ##默认已选 old usb1.0
    Kernel modules —> USB Support —> <*> kmod-usb-uhci. ## usb2.0
    Kernel modules —> USB Support —> <*> kmod-usb-storage.

     OHCI、UHCI都是USB1.1的接口标准,而EHCI是对应USB2.0的接口标准,最新的xHCI是USB3.0的接口标准。

    1.OHCI(Open Host Controller Interface)是支持USB1.1的标准,但它不仅仅是针对USB,还支持其他的一些接口,比如它还支持Apple的火线(Firewire,IEEE 1394)接口。与UHCI相比,OHCI的硬件复杂,硬件做的事情更多,所以实现对应的软件驱动的任务,就相对较简单。主要用于非x86的USB,如扩展卡、嵌入式开发板的USB主控。
    2.UHCI(Universal Host Controller Interface),是Intel主导的对USB1.0、1.1的接口标准,与OHCI不兼容。UHCI的软件驱动的任务重,需要做得比较复杂,但可以使用较便宜、较简单的硬件的USB控制器。Intel和VIA使用UHCI,而其余的硬件提供商使用OHCI。
    3.EHCI(Enhanced Host Controller Interface),是Intel主导的USB2.0的接口标准。EHCI仅提供USB2.0的高速功能,而依靠UHCI或OHCI来提供对全速(full-speed)或低速(low-speed)设备的支持。
    4.xHCI(eXtensible Host Controller Interface),是最新最火的USB3.0的接口标准,它在速度、节能、虚拟化等方面都比前面3中有了较大的提高。xHCI支持所有种类速度的USB设备(USB 3.0 SuperSpeed, USB 2.0 Low-, Full-, and High-speed, USB 1.1 Low- and Full-speed)。xHCI的目的是为了替换前面3中(UHCI/OHCI/EHCI)。

    添加文件系统支持

    这里我只勾选了VFAT格式

    Kernel modules —> Filesystems —> < > kmod-fs-ext4 (移动硬盘EXT4格式选择)
    Kernel modules —> Filesystems —> <*> kmod-fs-vfat(FAT16 / FAT32 格式 选择)
    Kernel modules —> Filesystems —> < > kmod-fs-ntfs (NTFS 格式 选择)
    

    添加USB挂载

    注意:如果不勾选,etc/hotplug.d/block 会不存在

    Base system —> <*>block-mount

    三、脚本示例

    源码添加位置:penwrt/target/linux/ramips/base-files/etc/hotplug.d/block (package/base-files/files/etc/hotplug.d/block/)

    如果没有hotplug.d/block 自己新建就可以,脚本放到block/下

    脚本的名称为:20-udisk-mount

    关于linux下 dev/sda    dev/sda1  ..........   dev/sdb  说明

    意义如下:

    • 第一个软驱 /dev/fd0.

    • 第二个软驱 /dev/fd1.

    • 第一块硬盘 /dev/sda.

    • 第二块硬盘 /dev/sdb, 以此类推.

    • 第一个SCSI CD-ROM /dev/scd0, 可以用/dev/sr0.

    sda1, sda2是sda这块硬盘上的两个分区。

    如果没有分区比如dev/sda1 直接挂载 dev/sda 即可

    脚本如下(示例):

    #!/bin/ash
    udisk_folder=/mnt/udisk
    
    case "$ACTION" in
        add)
    	    for i in $(ls /dev/ | grep 'sd[a-z][1-9]')
    	    do
    	        mkdir  $udisk_folder
    	        chmod 777 $udisk_folder
    	        mount -t vfat /dev/$i $udisk_folder
    	        done
    	    ;;
    	remove)
    		     umount $udisk_folder
    	      ;;
    esac

    总结

    总结了openwrt官网热插拔的相关资料和自动挂载脚本

    展开全文
  • USB热拔插事件

    万次阅读 2018-08-26 11:35:41
    前言 USB(Universal Serial ...USB接口常用在诸如USB串行设备驱动(3G/4G上网卡、蓝牙设备、串口设备)、USB大容量磁盘驱动(U盘、移动硬盘)、USB主机控制器驱动(嵌入式otg,dwc_otg)、USB键盘鼠标等,这一些的...

    前言

    USBUniversal Serial Bus通用串行总线的缩写,是一个外部总线标准,用于规范主机与外围设备的连接以及通讯,目前使用较多的版本有USB1.1USB2.0USB3.0等。USB接口常用在诸如USB串行设备驱动(3G/4G上网卡、蓝牙设备、串口设备)USB大容量磁盘驱动(U盘、移动硬盘)、USB主机控制器驱动(嵌入式otgdwc_otg)、USB键盘鼠标等,这一些的USB功能支持可以通过内核配置来实现,内核会管理这些USB设备的信息(lsusb 命令可以查看USB设备的情况)。上层应用开发则可以通过监听内核socket来获取设备的热拔插信息,进而利用此信息来确定相关的挂载操作或者其他的一些上层应用的业务逻辑。

    USB驱动框图

    为了加深对USB的理解,我们且看看USB2.0 Host逻辑框图,以达到对USB设备与主机对接接口及协议有一个比较全面的理解。可以看出最右边的PHY0PHY1是用来实现与外部设备进行物理连接的,然后通过serial interfaceUSB控制单元相连接,USB控制单元里面又分为EHCI Host ControllerOHCI Host Controller以实现USB2.0USB1.1的兼容,EHCI Host ControllerOHCI Host Controller则通过AHB BUS(在PC机则是PCI总线)总线与MemoryCPU相连。整个过程看起来并不是十分复杂,但是实际应用中有大量的控制寄存器需要配置来实现与不同的外围设备进行相连,但是这一切的繁杂工作内核已经帮我们实现了。

    USB热拔插

    热插拔(hot-pluggingHot Swap)即带电插拔,它的诞生提高了系统与外围设备的交互能力。那么我们如何获取USB的热拔插事件呢,这里可以通过与内核建立socket连接,然后对socket进行监听来获取USB拔插信息,接着对监听的信息进行处理,对于大容量存储设备则决定设备的挂载目录(mount /dev/sdb /usb)或者设备卸载(umount -l /USB)、对于诸如3G/4G等上网卡设备,则可以通过监听拔插信息来决定拨号上网或者接听电话等。下面通过例子说明如何通过内核socket获取设备热拔插信息的。

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    #include <sys/un.h>
    #include <sys/ioctl.h>
    #include <sys/socket.h>
    #include <linux/netlink.h>
    #include <errno.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <dirent.h>
    #include <stddef.h>
    #include <sys/mount.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <mntent.h>
    
    
    static int CreateHotPlugSock(void)
    {
        struct sockaddr_nl snl;
        const int buffersize = 16*1024;
        int retval = 0;
    
        memset(&snl,0x00,sizeof(struct sockaddr_nl));
        snl.nl_family = AF_NETLINK;
        snl.nl_pid = getpid();
        snl.nl_groups = 1;
    
        int hotplug_sock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT);
        if(hotplug_sock == 1)
        {
            printf("error get socket:%s",strerror(errno));
            return -1;
        }
    
        setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
        retval = bind(hotplug_sock,(struct sockaddr *)&snl,sizeof(struct sockaddr_nl));
        if(0 > retval)
        {
            printf("bind failed:%s",strerror(errno));
            close(hotplug_sock);
            hotplug_sock = -1;
            return -1;
        }
        return hotplug_sock;
    }
    
    int main(int argc, char* argv[])
    {
    	int sockfd;
    	int sum,size;
    	char buf[1024] = {0};
    	char *str;
    	sockfd = CreateHotPlugSock();
    	while(1)
    	{
    	    sum = 0;
            memset(buf, 0, sizeof(buf));
            size = recv(sockfd, buf, sizeof(buf), 0);
            	//	fprintf(stderr,"size=%d, buf=[%s], strlen(buf)=%d\n", size, buf, strlen(buf));
            while(sum < size)
            {
                str = buf + sum;
                sum += strlen(str);
                buf[sum] = '\n';
            }
            buf[sum] = 0;
    		//输出热拔插socket监听的信息
            fprintf(stderr,"buf=[%s]\n\n", buf);
    		usleep(100*1000);
    	}	
    }

    下面是样例代码socket监听鼠标设备拔插的部分打印信息

    总结

    正如linux下一切都是文件的哲学理念一样,现在的外围设备则一切以USB为标准看齐,在应用程序开发过程中,经常需要对这些USB外围设备的热拔插事件进行监听处理来实现上层软件的业务逻辑,内核socket很好解决了这个问题,通过它可以很方便地与这些USB热拔插设备交互。原创不易,转载请说明出处。

    文章参考:

    https://www.cnblogs.com/oracleloyal/p/5333276.html

    https://blog.csdn.net/gladyoucame/article/details/8768731

    展开全文
  • usb热插拔原理

    千次阅读 2019-06-14 12:11:59
    USB主机是如何检测到设备的插入的呢?首先,在USB集线器的每个下游端口的D+和D-上, 分别接了一个15K欧姆的下拉电阻到地。这样,在集线器的端口悬空时,就被这两个下拉电阻 拉到了低电平。而在USB设备端,在D+...
  • 通常这称为“热插拔”或“热插拔”,但也可称为“带电插拔”。在将电子设备插入带电系统并完全通电并运行之前,需要对热插拔有基本了解。没有中断。根据应用程序的不同,这些要求可能更具挑战性。例如,在热交换过程...
  • VS2010 MFC控件 MSCOMM做的串口工具,可自动识别串口热插拔,发送接收均为16进制,没有做正常字符型发送和16进制双选择,适合新手
  • MFC检测USB热插拔

    2017-11-22 16:23:49
    在网上找了很久,发现这个方法还是可以实现的,但是必须要知道USB设备的GUID,我准备写下来留作以便以后查询 BOOL CXXdlg::OnDeviceChange(UINT nEventType,DWORD dwData) { DEV_BROADCAST_DEVICEINTERFACE* dbd ...
  • win32 hidapi读取usb设备,包括热拔插;项目是vs2013项目; hidapi是一个开源项目可从这里下载https://github.com/signal11/hidapi
  • Android usb 麦克风热插拔

    千次阅读 2019-05-11 15:21:27
    /* USB管理 */ ...在 Android 系统中是用 广播的方式来通知系统, USB 麦克风的热插拔。 Linux 内核的设备热插拔: Linux 采用一种特殊类的的文件描述符(套结字)专门用于Linux内核跟用户空间...
  • Linux USB U盘热插拔挂载和卸载

    万次阅读 2016-03-31 16:57:35
    MT7620的U盘热插拔监听,热插拔挂载与卸载。 当插入U盘,则挂载; 当拔出U盘,则卸载。 监听热插拔采用socket监听,由用户层程序监听,并实现自动挂载与卸载。
  • 基于WPF的串口助手 自动检测串口 支持热插拔 C#语言 VS2015工程
  • 实现android usb光驱 光盘支持 光驱热插拔 光盘热插拔 1.增加vold对Iso9660光盘自动挂载的支持,基于RK3188_RK3066_R-BOX_ANDROID4.4.2-SDK_V1.0.0_140318,也可用于mid 2.实现Iso9660.cpp为vold实现的domount接口 3...
  • 本应用笔记讨论热插拔集成电路。典型的热交换电路,并给出不同的热插拔电路的优点是解释。  热插拔电路的基础知识  热插拔电路保护设备,人员,或两者兼而有之。有些电源供应器,例如,有内置和可调电流限制(在...
  • 一、usb设备的热插拔 之前就做过Qt之支持usb...来针对usb热插拔的进行了解决,根据网络大佬的解释,我在直接替换了Qt5.7的源码中的: qtbase/src/platformsupport/devicediscovery/qdevicediscovery_static.cp...
  • MFC检测USB设备热插拔

    千次阅读 2018-06-15 20:51:59
     GUID GUID_CLASS_USB_DEVICE = {0xa503e2d3, 0xa031, 0x49dc, 0xb6, 0x84, 0xc9, 0x90, 0x85, 0xdb,0xfe, 0x92};  HDEVNOTIFY m_hDeviceNotify; DEV_BROADCAST_DEVICEINTERFACE Filter; ZeroMemory(&Filter,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,479
精华内容 4,191
关键字:

usb可以热插拔