stm32 ECM 驅動ME909 成功

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 字符串描述符也就獲得了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章