1. 程式人生 > >USB熱拔插事件

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