李麗宏,郝志剛
(太原理工大學(xué) 信息工程學(xué)院,山西 太原 030024)
隨著計(jì)算機(jī)外圍硬件的擴(kuò)展,各種外圍設(shè)備使用不同的總線接口,導(dǎo)致計(jì)算機(jī)外部各種總線繁多,管理困難,USB總線可以解決這些問題,因此而誕生。USB總線提供統(tǒng)一的外沒的接口方式,并且支持熱插拔,方便了廠商開發(fā)設(shè)備和用戶使用設(shè)備。USB(通用串行總線)是由Microsoft,Compad,Inter和NEC等推出的外圍總線接口,目前已發(fā)展到2.0標(biāo)準(zhǔn)最高支持480 Mb/s的速率,最多可以支持127個(gè)外設(shè)。
嵌入式Linux是一款源代碼完全免費(fèi)的新興操作系統(tǒng),用戶可以用戶可以通過網(wǎng)絡(luò)等其他途徑免費(fèi)獲得,并可以任意修改其源代碼,這是其他的操作系統(tǒng)做不到的。正是由于這一點(diǎn),Linux得到了廣泛的應(yīng)用。
USB接口標(biāo)準(zhǔn)支持外部設(shè)備和主機(jī)之間進(jìn)行數(shù)據(jù)傳送。在USB結(jié)構(gòu)中主機(jī)預(yù)設(shè)各種類型外設(shè)使用的總線寬度。當(dāng)外設(shè)和主機(jī)在運(yùn)行時(shí),USB總線允許使用,設(shè)置,添加和拆除外設(shè)。
在USB體系結(jié)構(gòu)中一個(gè)USB系統(tǒng)可以分成USB設(shè)備、USB主機(jī)和USB互聯(lián)3個(gè)部分。USB互聯(lián)是USB設(shè)備和USB主機(jī)之間進(jìn)行連接通信的操作[1],主要包括:
1)總線拓?fù)浣Y(jié)構(gòu):USB主機(jī)和USB設(shè)備之間的連接方式;
2)數(shù)據(jù)流模式:描述USB通信系統(tǒng)數(shù)據(jù)如何從產(chǎn)生方傳遞到使用方;
3)USB調(diào)度:USB總線是一個(gè)共享連接,對(duì)可以使用的連按進(jìn)行調(diào)度以支持同步數(shù)據(jù)傳輸,并避兔優(yōu)先級(jí)判斷的開銷。
圖1 USB體系拓?fù)浣Y(jié)構(gòu)圖Fig.1 USB system topological structure
USB的物理連接是有層次的星型結(jié)構(gòu),如圖1所示。從圖中可以看出USB集線器在一個(gè)節(jié)點(diǎn)上連接多個(gè)設(shè)備,每條線段都是點(diǎn)點(diǎn)連接,每個(gè)USB集線器在星形的中心。從主機(jī)到設(shè)備或者USB集線器,或USB集線器到設(shè)備都是點(diǎn)點(diǎn)連接。
USB總線在技術(shù)層面上是非常簡(jiǎn)單的,它是一個(gè)單主方式實(shí)現(xiàn)的,主機(jī)輪詢各種不同的外圍設(shè)備,USB另外一個(gè)重要的特性是它只擔(dān)當(dāng)設(shè)備和主控制器之間通訊通道的角色,對(duì)所發(fā)送的數(shù)據(jù)沒有任何特殊的內(nèi)容和結(jié)構(gòu)上的要求。
Linux支持兩種類型的USB驅(qū)動(dòng),宿主系統(tǒng)[2]上的驅(qū)動(dòng)程序和設(shè)備上的驅(qū)動(dòng)程序。宿主USB驅(qū)動(dòng)程序控制插入其中的USB設(shè)備,而USB設(shè)備的驅(qū)動(dòng)程序控制設(shè)備如何作為一個(gè)USB設(shè)備和主機(jī)通訊。這里主要討論設(shè)備驅(qū)動(dòng)。
圖2 USB驅(qū)動(dòng)程序?qū)哟螆DFig.2 USB driver hierarchical graph
USB的基本通信的形式基本通過端點(diǎn)的東西。USB端點(diǎn)[3]只能往一個(gè)方向傳輸數(shù)據(jù),從主機(jī)到設(shè)備或從設(shè)備到主機(jī)。USB端點(diǎn)分別具有不同的傳輸數(shù)據(jù)的方式,他們有4種類型,分別是:1)控制端點(diǎn)用來控制對(duì)USB設(shè)備不同部分的訪問。他們用于配置設(shè)備,獲取設(shè)備信息,獲取設(shè)備的狀態(tài)報(bào)告,發(fā)送命令到設(shè)備。它是一種非周期性的可靠的傳輸。2)中斷端點(diǎn)就是設(shè)備傳輸數(shù)據(jù)時(shí)以一個(gè)固定的速率來傳輸少量的數(shù)據(jù)。這些端點(diǎn)是鼠標(biāo)和USB鍵盤所使用的主要傳輸方式。它通常用于發(fā)送數(shù)據(jù)到USB設(shè)備以控制設(shè)備,一般不用來傳輸大量數(shù)據(jù)。USB協(xié)議保證這些傳輸有足夠的保留帶寬來傳輸數(shù)據(jù)。3)等時(shí)端點(diǎn)同樣可以傳輸大批量的數(shù)據(jù),但數(shù)據(jù)是否到達(dá)沒有保障,這些端點(diǎn)用于可以應(yīng)付數(shù)據(jù)丟失的情況,這類設(shè)備更注重于保持一定的恒定的數(shù)據(jù)流,實(shí)時(shí)的數(shù)據(jù)收集都使用這類端點(diǎn)。4)批量端點(diǎn)傳輸大量的數(shù)據(jù)。這些端點(diǎn)通常比中斷端點(diǎn)大的多他們常用于需要確保沒有數(shù)據(jù)丟失的傳輸設(shè)備。USB協(xié)議不保證這些傳輸始終可以在特定的時(shí)間內(nèi)完成。如果總線上的空間不足以發(fā)送整個(gè)批量包。它將被分割為多個(gè)包進(jìn)行傳輸。
當(dāng)一個(gè)USB設(shè)備連接到主機(jī)時(shí),主機(jī)會(huì)給這個(gè)設(shè)備分配一個(gè)1~127之間的唯一的設(shè)備號(hào)同時(shí)讀取該設(shè)備的描述符,該設(shè)備描述符是描述設(shè)備信息及其屬性的數(shù)據(jù)結(jié)構(gòu),USB以一種層次化的結(jié)構(gòu)定義設(shè)備的描述符,設(shè)備描述符給出了USB設(shè)備的一般信息,包括對(duì)設(shè)備及所有設(shè)備配置起全程作用的信息,一個(gè)USB設(shè)備只能有一個(gè)設(shè)備描述符,配置描述符中的信息與設(shè)備特定的配置相關(guān),一個(gè)USB設(shè)備可以有一個(gè)或多個(gè)配置描述符,每個(gè)配置描述符又由一個(gè)或多個(gè)接口描述符組成,接口描述符的信息是與設(shè)備驅(qū)動(dòng)程序的開發(fā)密切相關(guān),可以一個(gè)接口對(duì)應(yīng)一個(gè)設(shè)備驅(qū)動(dòng)程序也可以多個(gè)接口對(duì)應(yīng)一個(gè)設(shè)備驅(qū)動(dòng)程序,接口描述符由零個(gè)或多個(gè)端點(diǎn)描述符組成,端點(diǎn)描述符定義了在一個(gè)給定的設(shè)備里實(shí)現(xiàn)的實(shí)際寄存器.這些描述符定義了每個(gè)寄存器的功能和特定的信息如端點(diǎn)要求的傳輸類型、傳輸方向、帶寬要求、查詢間隔等。另外,還有一個(gè)可選的字符串描述符,它以UNCOND碼的格式給出了一些可讀的信息,這些信息通常是有關(guān)設(shè)備生產(chǎn)廠商、設(shè)備名設(shè)備序列號(hào)等,通過這些不同層次的描述符.主機(jī)設(shè)備驅(qū)動(dòng)程序就可以知道具體設(shè)備的相關(guān)信息,從而對(duì)設(shè)備進(jìn)行相應(yīng)控制。
1.3.1 基本數(shù)據(jù)結(jié)構(gòu)
usb-skel設(shè)備使用自定義結(jié)構(gòu)usb_skel記錄設(shè)備驅(qū)動(dòng)用到的所有描述符,該結(jié)構(gòu)定義如下:struct usb_skel{
struct usb_device*udev; //USB設(shè)備描述符
struct usb_interface*interface; //USB接口描述符
struct semaphore limit_sem; //互斥信號(hào)量
unsigned char* bulk_in_buffer;//數(shù)據(jù)接收緩沖區(qū)
size_t bulk_in_size; //數(shù)據(jù)接收緩沖區(qū)大小
_u8 bulk_in_endpointAddr; //入端點(diǎn)地址
_u8 bulk_out_endpointAddr;//出端點(diǎn)地址
struct kref kref;
};
1.3.2 驅(qū)動(dòng)程序初始化和注銷
同其他所有的Linux設(shè)備驅(qū)動(dòng)程序一樣,usb-skel驅(qū)動(dòng)使用 module_init()宏初始化函數(shù),使用 module_exit()宏注銷函數(shù)。usb-skel驅(qū)動(dòng)的初始化函數(shù)usb_skel_init()函數(shù),定義如下:
static int_init usb_skel_init(void)
{
int result;
result-usb_register(&skel_driver);//注冊(cè) USB 設(shè)備驅(qū)動(dòng)if(result)
err(“usb_register failed.Error number%d”,result);return result;
}
sb_skel_init()函數(shù)調(diào)用內(nèi)核提供的 usb_register()函數(shù)注冊(cè)了一個(gè)usb_driver類型的結(jié)構(gòu)變量,該變量定義如下:static struct usb_driver skel_driver={
.name= “skeleton”, //USB 設(shè)備名稱
.probe=skel_probe, //USB設(shè)備初始化函數(shù)
.disconnect=skel_disconnect,//USB設(shè)備注銷函數(shù)
.id_table=skel_table, //USB設(shè)備ID映射表};
其中usb_skel設(shè)備的USB ID映射表定義如下:static struct usb_device_id skel_table[]={
{USB_DEVICE(USB_SKEL_VENDOR_ID,USB_SKEL_PRODUCT_ID)};
在USB驅(qū)動(dòng)中調(diào)用usb_deregister()函數(shù)注銷usb-skel設(shè)備驅(qū)動(dòng),函數(shù)定義如下:
static void_exit usb_skel_exit(void)
usb_deregister(&skel_driver); //注銷 USB設(shè)備};
1.3.3 初始化設(shè)備
從skel_driver結(jié)構(gòu)可以知道usb-skel設(shè)備的初始化函數(shù)是skel_probe()函數(shù),設(shè)備初始化主要是探測(cè)設(shè)備類型,分配USB設(shè)備用到的urb資源[5],注冊(cè)USB設(shè)備操作函數(shù)等。skel_class結(jié)構(gòu)變量記錄了usb-skel設(shè)備信息,定義如下:
static struct usb_class_driver skel_class={
.name= “skel%d”, //設(shè)備名稱
.fops= &skel_fops,//設(shè)備操作函數(shù)
.minor_base=USB_SKEL_MINOR_BASE,};
name變量使用%d通配符表示一個(gè)整型變量,當(dāng)一個(gè)usb-skel類型的設(shè)備連接到USB總先后會(huì)2按照子設(shè)備編號(hào)自動(dòng)設(shè)置設(shè)備名稱。Fops設(shè)備操作函數(shù)結(jié)構(gòu)變量,定義如下:
static struct file_operations skel_fops={
.owener=THIS_MODULE,
.read=skel_read, //讀操作
.write=skel_write, //寫操作
.open=skel_open, //打開操作
.release=skel_release, //關(guān)閉操作};
1.3.4 設(shè)備注銷
skel_disconnect()函數(shù)在注銷設(shè)備時(shí)被調(diào)用,定義如下:static void skel_disconnect(struct usb_interface*interface){
struct usb_skel*dev;
int minor=interface->minor;
lock_kernel(); //在操作之前加鎖
dev=usb_get_intfdata(interface);//獲得USB設(shè)備接口描述
usb_set_intfdata(interface,NULL); //設(shè)置USB設(shè)備接口描述無效
usb_deregister_dev(interface,&skel_class);//注銷 USB設(shè)備操作描述
unlock kernel(); //操作完畢解鎖
kref_put(&dev->kref,skel_delete); //減小引用計(jì)數(shù)
info(“USB Skeleton#%d now disconnected”,minor);};
usb_serial_init()函數(shù)是一個(gè)典型的USB設(shè)備驅(qū)動(dòng)初始化函數(shù),定義如下:
static int_int usb_setial_init(viod){
int i;
int result;
usb_tty_driver=alloc_tty_driver(SERIAL_TTY_MINORS);//申請(qǐng)tty設(shè)備驅(qū)動(dòng)描述
if(!usb_tty_driver)
return-ENOMEM;
result=bus_reqister(&usb_serial_bus_type);//注冊(cè)總線
if(result)
{
err(“Regist bus driver failed”);
qoto exit_bus;
}
usb_tty_driver=>owener=THIS_MODULE;
usb_tty_driver->driver_name= “usbserial”;//串口驅(qū)動(dòng)名稱
usb_tty_driver->devfs_name= “usb/tts”;//設(shè)備文件系統(tǒng)存放路徑
usb_tty_driver->name= “ttyUSB”; //串口設(shè)備名稱
usb_tty_driver->major=SERIAL_TTY_MAJOR;//串口設(shè)備主設(shè)備號(hào)
usb_tty_driver->minor_start=0;//串口設(shè)備從設(shè)備號(hào)起始ID
usb_tty_driver->type= TTY_DRIVER_TYPE_SERIAL;//設(shè)備類型
usb_tty_driver->subtype = SERIAL_TYPE_NORAML;//設(shè)備子類型
usb_tty_driver->flags=TTY_DREVER_REAL_RAW |TTY_DRIVER_NO_DEVFS;//設(shè)備初始化標(biāo)志
usb_tty_driver->init_terminos=tty_stb_termios;//串口設(shè)備描述
usb_tty_driver->init_termios.c_cflag =B9600 |CSB |CREAD|HUPCL|CLOCAL;//串口設(shè)備初始化參數(shù)
tty_set_operations(usb_tty_driver,&serial_ops); //串口設(shè)備操作函數(shù)
result=tty_register_driver(usb_tty_driver);//注冊(cè)串口驅(qū)動(dòng)
if(result)
{
err(“Regist tty driver failed”);
goto exit_reg_driver;
}
result=usb_register(&usb_serial_driver);//注冊(cè)USB驅(qū)動(dòng)
if(result<0)
{
err(“Register driver failed”);
goto exit_tty;
}return result;exit_generic:
usb_deregister(&usb_serial_driver);//注銷串口設(shè)備exit_tty:
tty_unregister_driver(usb_tty_driver);//注銷USB串口設(shè)備exit_reg_driver:
bus_unregister(&usb_serial_bus_type);//注銷總線exit_bus:
err(“Error Code:%d”,result);put_tty_driver(usb_tty_driver);return result;
}
函數(shù)首先調(diào)用alloc_tty_driver()函數(shù)分配一個(gè)串口驅(qū)動(dòng)描述符;然后設(shè)置串口驅(qū)動(dòng)的屬性,包括驅(qū)動(dòng)的主從設(shè)備號(hào)、設(shè)備類型、串口初始化參數(shù)等;串口驅(qū)動(dòng)描述符設(shè)置完畢后,調(diào)用usb_register()函數(shù)注冊(cè)USB串口設(shè)備。
static void__exit usb_serial_exit(void){
usb_serial_console_exit();
usb_serial_generic_deregister();
usb_deregister(&usb_serial_driver);//注銷 USB 設(shè)備驅(qū)動(dòng)
tty_unregister_driver(usb_tty_driver);//注銷串口設(shè)備
put_tty_driver(usb_tty_driver);//減少引用計(jì)數(shù)
bus_unregister(&usb_serial_bus_type);//注銷總線}
USB串口設(shè)備驅(qū)動(dòng)使用了一個(gè)tty_operations類型的結(jié)構(gòu),該結(jié)構(gòu)包含了串口的所有操作,定義如下:
static sturct tty_operations serial_ops=
{
.open=serial_open, //打開串口
.close=serial_close, //關(guān)閉串口
.write=serial_write, //串口寫操作
.write_room=serial_write_room,
.ioctl=serial_ioctl, //I/O控制操作
.set_termios=serial_set_termios, //設(shè)置串口參數(shù)
.throttle=serial_throttle,
.unthrottle=serial_unthrottle,
.break_ctl=serial_break, //break信號(hào)處理
.chars_in_buffer=serial_chars_in_buffer, //緩沖處理
.read_pros=serial_read_proc, //串口讀操作
.tiocmget=serial_tiocmget, //獲取I/O控制參數(shù)
.tiocmset=serial_tiocmset, //設(shè)置I/O控制參數(shù)};
按上述的步驟和方法通過Insmod命令成功實(shí)現(xiàn)了USB驅(qū)動(dòng)程序的加載[4],成功的通過USB串口進(jìn)行了數(shù)據(jù)的讀寫。
新出的Linux2.6[6]內(nèi)核加入了對(duì)USB2.0[7]的支持,重新定義了usb_class_driver結(jié)構(gòu)體。同時(shí)對(duì)探測(cè)函數(shù)probe和usb_submit_urb做了修改,包含了advanced linux sound Archiecture可以更安全的使用USB設(shè)備。
[1]弓雷.arm嵌入式linux系統(tǒng)開發(fā)詳解[M].北京:清華大學(xué)出版社,2010.
[2]Corbet J,Rubini A,Kroah-Hartma.G.Linux 設(shè)備驅(qū)動(dòng)程序[M].魏永明,耿岳,鐘書毅,譯.北京:中國(guó)電力出版社,2006.
[3]韋東山.嵌入式Linux應(yīng)用開發(fā)完全手冊(cè)[M].北京:人民郵電大學(xué)出版社,2008.
[4]楊水清.ARM嵌入式Linux系統(tǒng)開發(fā)技術(shù)詳解[M].北京:電子工業(yè)出版社,2008.
[5]季春志.基于Linux平臺(tái)USB視頻設(shè)備驅(qū)動(dòng)技術(shù)的研究與實(shí)現(xiàn)[D].合肥:合肥工業(yè)大學(xué),2009.
[6]博韋,西斯特.深入分析Linux內(nèi)核[M].陳莉君,張瓊聲,張宏偉,譯.北京:中國(guó)電力,2009.
[7]熊玉朋,陳興欣,龐俊銳.一種新型移動(dòng)保密存儲(chǔ)設(shè)備[J].現(xiàn)代電子技術(shù),2010(5):89-91.XIONG Yu-peng,CHEN Xing-xin,PANG Jun-rui.New Removable Privacy Storage Equipment[J].Modern Electronics Technique,2010(5):89-91.