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

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