stm32 ECM 驅動ME909成功了,先慶祝一下,後面補移植過程。
TFQ 先說一下參考資料:
1. 網上廣爲流傳的RNDIS,最早是國外了lrndis-master.zip,我下載下來看了下,感覺很複雜,關鍵是不知道幹什麼用的,只是弄明白了這個使用的是USB DEVICE, 好像模擬了一個網卡, 以這個爲祖師,在這個基礎上做出RNDIS的就很多了,比如RTTHREAD也把RNDIS也在RTT基礎上發佈了一版本, 安富萊的硬漢也搞起來了, 最初的lrndis-master.zip是裸機運行的。說明這個RNDIS還是很有吸引人的。
2. USB協議相關 USB2.0協議英文原版(全).pdf
基於USB+CDC的虛擬以太網接口研究與設計.pdf
USB CDC相關的資料
3,linux 原碼中有關ECM CDC的c 文件
只看協議會感覺很空洞,如果只看代碼的會使人眩暈,只有協議與代碼結合着看,纔會使人清晰,
調試過程中遇到空難無數,最終一個一個解決了,
理論指導,所有的工作都是在理念的指導下完成的,下面這個圖給我提供了最直接的理論指導:
上面詳細說明了NDIS的過程,首先PC(STM32) 給4G模塊發送撥號命令 echo "AT^NDISDUP=1,1">/dev/ttyUSB0, 也就是發起數據業務請求,4G模塊收到請求後,會向無線網絡請求IP地址與DNS,
然後PC(STM32)通過DHCP 客戶端請求IP地址,這個過程,4G模塊並沒有與無線網絡交互,完全是在PC與4G模塊之間進行的,PC請求到的IP地址也是“發起業務”階段4G模塊向無線網絡申請的IP地址。
下面的ARP其實就是PING程序吧,我的理解,但實際上我PING不通DHCP獲得的IP地址。有時候卻可以PING通網關。
數據傳輸階段,其實4G模塊大部分時候工作在這個階段,這個階段,4G模塊完全就是起到一個以太網數據幀中繼的作用,PC(STM32)把以太網數據發送給4G模塊,4G模塊再把以太網數據原封不動發到無線網絡上; 同樣無線網絡上的數據4G模塊也會原封不動 的轉發給PC(STM32),
最後就是釋放業務,這個我沒有試。echo "AT^NDISDUP=1,0">/dev/ttyUSB0, 應該是這個命令吧, 由PC發起請求中止數據業務。
理論上是這樣的,我調試的過程中,實際也是這樣的,尤其是DHCP階段,因爲在LINUX上調試4G模塊時,根本就不會去調用DHCP(可能linux後臺會自動的調用DHCP來獲得IP),但在STM32上時,必須人爲的調用DHCP來獲得IP地址,其實獲得的這個IP地址就是第一階段4G模塊向無線網絡申請的IP地址。
移植過程中遇到的幾個難點。
1,USB協議棧 匹配
2. MAC地址的獲得。
MAC地址的獲得真是一點一點的啃出來的,因爲無從參考,在RNDDIS中,MAC地址是設置死的,而4G模塊無論是ME909 還是EC20這些正規廠家生產的4G模塊,它的MAC地址應該是全球唯一的吧,不應該隨便設置,即使可以隨便設置,但ME909內部卻存在真實的MAC地址,如果2都不一樣,數據會發出去嗎?我表示懷疑。因此必須把MAC地址給弄出來。
於是我參考linux內核中mac地址的獲得辦法。在cdc_ether.c中是有獲得MAC地址的代碼的。
int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status;
struct cdc_state *info = (void *) &dev->data;
BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
< sizeof(struct cdc_state)));
status = usbnet_generic_cdc_bind(dev, intf);
if (status < 0)
return status;
status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress);
if (status < 0) {
usb_set_intfdata(info->data, NULL);
usb_driver_release_interface(driver_of(intf), info->data);
return status;
}
return 0;
}
這名程序usbnet_get_ethernet_addr(dev, info->ether->iMACAddress);獲得MAC地址。
int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress)
{
int tmp = -1, ret;
unsigned char buf [13];
ret = usb_string(dev->udev, iMACAddress, buf, sizeof buf);
if (ret == 12)
tmp = hex2bin(dev->net->dev_addr, buf, 6);
if (tmp < 0) {
dev_dbg(&dev->udev->dev,
"bad MAC string %d fetch, %d\n", iMACAddress, tmp);
if (ret >= 0)
ret = -EINVAL;
return ret;
}
return 0;
}
程序很清楚的的說明了,MAC地址是通過string descriptor 獲得的, 但是MAC地址的 string descriptor index是多少呢,還得把程序再往前翻,
string descriptor index在這個結構體中,
/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
struct usb_cdc_ether_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__u8 iMACAddress;
__le32 bmEthernetStatistics;
__le16 wMaxSegmentSize;
__le16 wNumberMCFilters;
__u8 bNumberPowerFilters;
} __attribute__ ((packed));
這個描述符之前沒有聽說過,應該是不常用, 常用的是的設備描述符, 配置描述符,接口描述符,端點描述符, Functional Descriptor這是什麼鬼, 功能描述符? 暫時就稱作功能描述符吧,但是它存在哪呢?
/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */這句話說明了這個結構體在CDC的協議中是有定義的。
CS_INTERFACE 協議中有定義爲0X24,
Ethernet Networking functional descriptor也有定義爲0X0F
在CDC120.PDF 有定義
雖然找到了MAC地址的 string descriptor index的位置,但是還不確定在哪能獲得到index,這個地方走了很多彎路,其實也是因爲自己對USB協議認識不徹底的原因,我想當然的就認爲這個struct usb_cdc_ether_desc是通過 GET INTERFACE DESCRIPTOR來獲得的,並在程序中付諸實施了,但一個偶然的發現,我單步調試的過程中發現在接口描述符的後面,端點描述符的前面,還有幾個未知描述符,突然發現這幾個描述符的類型剛好就是0X24,靈感來了, MAC地址的 string descriptor index 就這麼找到了。 MAC 字符串描述符也就獲得了。