Linux USB U盤熱插拔掛載和卸載

一、硬件平臺

        1、  控制器:MT7620(A9內核)

        2、  RTC芯片:MCP7940

二、軟件平臺

       1、開發環境:Ubuntu12.04 

       2、SDK內核包:MT7620 SDK軟件開發包(MediaTek_ApSoC_SDK_4320_20150414.tar.bz2)

       3、內核版本:linux-2.6.36.x

三、參考資料

      《MTK_APSoC_SDK_User_Manual.pdf》。

      下載鏈接:http://download.csdn.net/detail/xhoufei2010/9478004

四、USB U盤驅動簡介

        USB Mass Storage是一類USB存儲設備,這些設備包括USB磁盤、USB硬盤、USB磁帶機、USB光驅、U盤、記憶棒、智能卡和一些USB攝像頭等,這類設備由USB協議支持。

       對於USB的U盤驅動,已經非常完善,大家只需要簡單地配置一下內核,開啓U盤的功能即可。

五、U盤配置

       5.1 取消內核自動掛載功能

        由於Linux 內核包默認會自動掛載,且內核初始化的過程中,掛載出現在創建USB節點之前,經常出現自動掛載導致內核崩潰,故取消內核掛載,自己監聽USB的熱插拔,然後掛載。

        1.開啓設置busybox

        進入到內核開發包目錄 cd /home/kernel/source

        輸入命令 make menuconfig

        Kernel/Library/Defaults Selection  --->Customize Busybox Settings ,選中該選項,如圖5-1所示,設置完成之後,保存退出。

        

                                                          圖5-1 設置Busybox

       2. 取消USB自動掛載/卸載功能

       在圖5-1保存設置之後,會自動跳轉到busybox的設置界面,在界面中,進入Linux System Utilities,取消掉Support command execution at device addition/removal 選項,如圖5-2所示。


                                                       圖5-2 取消USB的自動掛載/卸載

       5.2 開啓U盤功能

        在linux-2.6.36.x中,輸入命令make menuconfig,進入配置

        Linux Kernel Configuration ---> Device Drivers  ---> USB support ,在配置中,選中USB Mass Storage support,如圖5-3所示。


                                                         圖5-3 開啓USB U盤支持

六、監聽USB熱插拔程序

       6.1 說明

        對於USB的熱插拔,實際就是建立一個socket,採用socket監聽USB的插拔信息。

        當監聽到USB插入信息,且發現在 /dev目錄下,存在 sda1或者sdb1分區(有時候分區節點爲其他名稱,根據實際分區修改分區的名稱判斷條件)的時候,就掛載USB分區到 /tmp/usb目錄下。

        當監聽到USB拔出信息,則卸載 /tmp/usb目錄。

        6.2 usb控制器頭文件,UsbController.h。

/**
 * @addtogroup module_genericGateway
 * @{
 */

/**
 * @file
 * @brief USB控制器,管理USB插拔及掛載。
 * @details 
 * @version 1.0.0
 * @author sky.houfei
 * @date 2016-3-18
 */


#ifndef _USB_USBCONTROLLER_H_
#define _USB_USBCONTROLLER_H_


#ifdef __cplusplus
extern "C" {
#endif

//******************************************************************************
#include <stdbool.h>


//******************************************************************************
/**
 * @brief USB控制器初始化,準備USB的監聽服務。
 * @return ret, int,如果初始化成功,則返回0,否則爲-1.
 */
int UsbController_Init(void);


/**
 * @brief USB設備掛載監聽。
 * @details 如果USB之前沒有掛載且當前可以掛載,則掛載。
 * \n 如果USB之前掛載成功,此時設備已經被拔出,則卸載。
 */
void UsbController_MountMonitor(void);


/**
* @brief 是否已經掛載成功。
* @return bool s_isMounted, USB設備掛載成功,則返回 true, 否則返回false。
*/
bool UsbController_IsMounted(void);


#ifdef __cplusplus
}
#endif


#endif  // _USB_USBCONTROLLER_H_

/** @} */

       6.3 usb控制器監聽熱插拔c文件, UsbController.c

/**
 * @addtogroup module_genericGateway
 * @{
 */

/**
 * @file
 * @brief USB控制器,管理USB插拔及掛載。
 * @details 
 * @version 1.0.0
 * @author sky.houfei
 * @date 2016-3-18
 */

//******************************************************
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 
#include <sys/un.h> 
#include <sys/ioctl.h> 
#include <sys/socket.h> 
#include <linux/types.h> 
#include <linux/netlink.h> 
#include <errno.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include "UsbController.h"
#include "GenericGateway.h"

//******************************************************
#define UEVENT_BUFFER_SIZE 2048 

//******************************************************
static bool isUsbConnected = false;
static int s_hotplugSock = 0;
static bool s_isMounted = false;

 //******************************************************
static void* UsbController_HotPlugMonitor(void);  // USB監聽,監聽USB的插拔事件,並進行掛載和卸載USB設備。


//******************************************************
static int UsbController_HotplugSockInit(void) 
{ 
    const int buffersize = 1024; 
    int ret; 

    struct sockaddr_nl snl; 
    bzero(&snl, sizeof(struct sockaddr_nl)); 
    snl.nl_family = AF_NETLINK; 
    snl.nl_pid = getpid(); 
    snl.nl_groups = 1; 

    int s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
    if (s == -1)  
    { 
        perror("socket"); 
        return -1; 
    } 
    setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)); 

    ret = bind(s, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)); 
    if (ret < 0)  
    { 
        perror("bind"); 
        close(s); 
        return -1; 
    } 

    return s; 
} 


/**
 * @brief USB控制器初始化,準備USB的監聽服務。
 * @return ret, int,如果初始化成功,則返回0,否則爲-1.
 */
int UsbController_Init(void)
{
	const int buffersize = 1024; 
    int ret; 
	pthread_t id;

    struct sockaddr_nl snl; 
    bzero(&snl, sizeof(struct sockaddr_nl)); 
    snl.nl_family = AF_NETLINK; 
    snl.nl_pid = getpid(); 
    snl.nl_groups = 1; 

	if (access("/dev/sda1", 0) == 0)
	{
		// USB已經連接成功
		isUsbConnected = true;
	}


	UsbController_MountMonitor();	// 首次檢查USB是否掛載
    s_hotplugSock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
    if (s_hotplugSock == -1)  
    { 
        perror("socket error"); 
        return -1; 
    } 
    setsockopt(s_hotplugSock, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)); 

    ret = bind(s_hotplugSock, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)); 
    if (ret < 0)  
    { 
        perror("bind error"); 
        close(s_hotplugSock); 
        return -1; 
    } 

	ret = pthread_create(&id, NULL, UsbController_HotPlugMonitor, NULL);  
    if (ret != 0)   
    {  
        printf("pthread_create error = %d\n", ret);  
    }  

	return 0;
}


/**
 * @brief USB監聽熱插拔,監聽USB的插拔事件,並進行掛載和卸載USB設備。
 */
static void* UsbController_HotPlugMonitor(void)
{ 
	pthread_detach(pthread_self());
	char *result = NULL;
    char buf[UEVENT_BUFFER_SIZE * 2] = {0};     //  Netlink message buffer  
	
	while (1)
	{
	    recv(s_hotplugSock, &buf, sizeof(buf), 0); // 獲取 USB 設備的插拔會出現字符信息,
		result = strtok(buf, "@");                // 查看 USB的插入還是拔出信息
		if (result != NULL)
		{
			if ((strcmp(result, "add") == 0))
			{
				if (isUsbConnected == false)
				{
					isUsbConnected = true;
				}

			}
			else if ((strcmp(result, "remove") == 0))
			{
				if (isUsbConnected == true)
				{
					isUsbConnected = false;
				}
			}
		}
		memset(buf, 0, UEVENT_BUFFER_SIZE * 2);
	}
}


/**
* @brief 是否連接成功。
* @return bool isConnnected, USB設備連接成功,則返回 true, 否則返回false。
*/
static bool UsbController_IsConnected(void)
{
	return isUsbConnected;
}


/**
 * @brief 掛載文件系統。
 * @details 創建文件夾 /tmp/usb,將USB設備掛在在該目錄下。嘗試掛在 sda1和sdb1,如果都掛在失敗,則認爲掛載失敗。
 * @return 如果掛載成功,則返回0,否則爲-1。
 */
static int UsbController_MountFileSystem(void)
{
	const char directory[] = "/tmp/usb";
	int ret = 0;

	printf("Try to mount the usb device\n");
	// 檢測是否存在文件夾
	if (access(directory, 0) == -1)
	{
		// 文件夾不存在
		if (mkdir(directory, 0777)) // 創建文件夾
        {
            printf("creat directory(%s) failed!!!", directory);
			return -1;
        }
	}

	if (system("mount -t vfat /dev/sda1  /tmp/usb") < 0)  // 掛載USB的文件系統
	{
		if (system("mount -t vfat /dev/sdb1  /tmp/usb") < 0)
		{
			return -1;
		}
	}

	return 0;
}


/**
 * @brief 卸載文件系統。
 * @return 如果掛載成功,則返回0,否則爲-1。
 */
static int UsbController_UnmountFileSystem(void)
{
	int ret = 0;

	if (system("umount /tmp/usb") < 0)  // 掛載USB的文件系統
	{
		printf("Umount the usb device failed\n");
		ret =  -1;
	}

	printf("Umount the usb device success\n");
	return ret;
}


/**
 * @brief USB設備是否可以掛載。
 * @details 設備處於連接狀態,且在/dev/目錄下創建了sda1或者sdb1節點,則視爲可以掛載。
 * @return 如果可以掛在,則返回true,否則爲false。
 */
static bool UsbController_IsMountable(void)
{
	bool isMountable = false;
	bool isPartitionExist = false;

	if (access("/dev/sda1", 0) == 0 || access("/dev/sdb1", 0) == 0)
	{
		// 存在分區 /dev/sda1 或者 /dev/sdb1
		isPartitionExist = true;
	}

	if (isUsbConnected == true && isPartitionExist == true)
	{
		isMountable = true;
	}

	return isMountable;
}


/**
 * @brief USB設備掛載監聽。
 * @details 如果USB之前沒有掛載且當前可以掛載,則掛載。
 * \n 如果USB之前掛載成功,此時設備已經被拔出,則卸載。
 */
void UsbController_MountMonitor(void)
{
	if (s_isMounted == false && UsbController_IsMountable() == true)
	{
		// 之前沒有掛載且當前可以掛載,掛載文件系統
		if (0 == UsbController_MountFileSystem())
		{
			printf("Mount success\n");
			s_isMounted = true;
			GenericGateway_SetUsbMounted(s_isMounted);
		}
	}
	else if (s_isMounted == true && UsbController_IsConnected() == false)
	{
		// 之前掛載成功,此時設備已經被拔出,卸載設備
		if (0 == UsbController_UnmountFileSystem())
		{
			s_isMounted = false;
			GenericGateway_SetUsbMounted(s_isMounted);
		}
	}
}


/**
* @brief 是否已經掛載成功。
* @return bool s_isMounted, USB設備掛載成功,則返回 true, 否則返回false。
*/
bool UsbController_IsMounted(void)
{
	return s_isMounted;
}


/** @} */

       6.4 主函數 main.c

#include <stdio.h>
#include "usb/UsbController.h"

int main(int argc, char** argv)
{
	int secondCount = 0;
	UsbController_Init();
	
	
	while (1)
	{
		sleep(1);
		secondCount++;
		if (secondCount >= 5)
		{
			secondCount = 0;
			UsbController_MountMonitor();
		}
	}
}

    6.5 測試結果

    6.5.1 USB 插入

     1. 查看USB設備

     # ls /dev/sd*
     /dev/sda1  /dev/sda

     2. 插入設備打印信息

# [  226.340000] usb 1-1: new high speed USB device using rt3xxx-ehci and address 6
[  226.490000] scsi4 : usb-storage 1-1:1.0
[  227.520000] scsi 4:0:0:0: Direct-Access     Kingston DataTraveler 2.0 1.00 PQ: 0 ANSI: 4
[  227.540000] sd 4:0:0:0: [sda] 30310400 512-byte logical blocks: (15.5 GB/14.4 GiB)
[  227.550000] sd 4:0:0:0: [sda] Write Protect is off
[  227.560000] sd 4:0:0:0: [sda] Assuming drive cache: write through
[  227.570000] sd 4:0:0:0: [sda] Assuming drive cache: write through
[  227.580000]  sda: sda1
[  227.590000] sd 4:0:0:0: [sda] Assuming drive cache: write through
[  227.590000] sd 4:0:0:0: [sda] Attached SCSI removable disk
Try to mount the usb device
[  232.110000] FAT: utf8 is not a recommended IO charset for FAT filesystems, filesystem will be case sensitive!
Mount success

     3. 查看USB掛載後的文件系統

     查看之後,可以正常獲取到U盤的內容。

     # ls /tmp/usb/
test       software   computime

         6.5.2 USB 拔出

      1. USB拔出之後,打印信息:

      # [  789.230000] usb 1-1: USB disconnect, address 6
      Umount the usb device success

      2. 查看文件系統

      查看,發現/tmp/usb目錄的內容已經被卸載,正常卸載。

      # ls /tmp/usb/
      # 
      # 

發佈了44 篇原創文章 · 獲贊 20 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章