2007年11月27日 星期二

USB韌體解析-2

HID裝置

有時我們去郵局辦事會遇到一些保險業務拿些資料讓我們填,有的人還會誤認他們是郵局的人,還真的就填了咧。



這種偷偷搭上現成官僚系統便車的做法就與今天所要探討的主題非常相似。從USB韌體解析-1我們學到:整個USB系統就是一個官僚系統,一切流程都已經被制定得非常嚴謹。既然它是經由非常週詳的考慮而制定的,那當我們想要自己搞些創新的應用時(如開場白的直銷之於郵務)就可以偷偷撘上這輛官僚便車而不用自己再重頭制定一套規則。



我們可以從USB 2.0規格書中usb_20.pdf 位於9.3的table 9-2發現一個Request Type叫做Vendor ,這就是原本USB在制定規範時保留給我們做一些還沒有變成標準的創新應用來用的。譬如說要做一種給小孩玩的音樂滑鼠,那可以利用這種Vendor Request來下載音樂到滑鼠裡。



這個部落格要是放上那麼完美的範例大家看完就不會燃起要改寫的衝動,所以今天只是稍微把之前用EasyLogger改的搖桿範例加上一個PC的小程式來啟動自動按鈕功能而已。



因為今天要寫PC平台的程式了,在開始之前我們要先下載PC的開發工具和程式庫。在這裡選用的開發工具是Dev-C++。而程式庫是libusb所以是可跨平台的,相關細節就麻煩大家自己用Google找一下吧。我今天只提供在Windows上實際執行過的壓縮檔。



另外,雖然Dev-c++跟WinAVR的核心編譯器都是GCC,但目前我還沒時間去研究如何一魚二吃。如果要研究可能要等到助手來我們公司以後了,所以這邊再打個誠徵助手的廣告。



現在就進入正題,這次的搖桿程式大致只加了下面三行而已.從新加的第一行大致就可看出Vendor Request是一片完全屬於我們自己的天地,我們自己想要如何定義就如何定義。像我就定了Request=38這個命令當做是開始自動按按鈕的命令。


else if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_VENDOR){
if(rq->bRequest == 38)
Start=1;
}




再來就是在PC上寫一個會送這種Request的程式就完工了。首先參考一下libusb的usb_control_msg函式用法。看完後我們知道只要在PC端寫下下面片段這樣的程式就可以讓我們的搖桿自己亂按按鈕了。




nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 38, 0, 0, (char *)buffer, sizeof(buffer), 5000);




請再注意官僚這個詞。PC端和裝置端的命令千萬要統一,38就二邊都要38,不然只會看到二部機器在空轉而已。





今天的完整程式在這裡



usb using attiny45

展開卷軸...

2007年11月1日 星期四

USB 韌體解析-1

回想一下我去公所辦事的狀況:當一踏進大門,義工媽媽就馬上問"帥哥,請問要來辦什麼事情?"(純屬事實,絕無虛構)並且引導我去抽號碼牌。然後坐著等待叫號之後再去櫃台填表辦事。這就是一個典型的官僚系統。



原始的USB界面(也就是非OTG的USB)就像現實世界的官僚系統一樣,全部都是PC(host 端)主導一切的動作,甚至連上傳資料都不能裝置端自己要傳就傳,一定要host叫你傳才能傳。所以實作上就非常簡單,不會發生其他網路界面那種搶頻寬的問題。



接下來的解說需要去usb.org下載USB2.0規格書配合來看。



裝置插入

我們可以看看剛才下載的USB_20.pdf 的7.1.5.1。根據USB 2.0的規範,USB的裝置端需要如下圖所示依裝置的速度並聯一個電阻在D+或D-與3.3V間。









看到這裡,我們就可以知道在自製USB滑鼠裡畫的電路圖是一個低速的USB裝置,也知道PC怎樣知道有個有個"帥哥"進來而該派"義工媽媽"去幫忙抽號碼牌了。可是電阻的接法好像跟上面的圖不太一樣也跟USB 2.0規範不太一樣耶?這個部份先保留,等到USB高級心法再詳細解說。



Device Request



再來也跟現實狀況一樣,在抽號碼牌前都是由義工媽媽來服務,等到抽完號碼牌後就要等到叫到號碼後才去櫃台由小姐服務。USB裝置在剛被RESET或是接到PC時都是固定為0號,等到PC下了一個Set Address的Request後變成由HOST指定的號碼了。



在USB的世界裡一切就是很官僚,一切都只有填充題。所有要填的表格和該填什麼都在USB_20.pdf的9.3和9.4裡。



韌體解析-1

經過前面的解說和參考資料的研讀後,我們可以知道USB韌體最重要的部份就是在處理request表格。以我們一直當範例的EasyLogger來說,在usbdrv.c的347行附近像下面這樣就是在處理request表格。




}else if(rq->bRequest == USBRQ_SET_ADDRESS){ /* 5 */
usbNewDeviceAddr = rq->wValue.bytes[0];
}else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */
flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW;
if(rq->wValue.bytes[1] == USBDESCR_DEVICE){ /* 1 */

//
//
//




不過這個程式不太好閱讀,有興趣者可以參考Silicon LabsUSB Firmware Programmer's Guide 第34頁開始的程式碼,這份文件寫得很工整很容易閱讀。



今天就暫時在這裡告一段落。

展開卷軸...