rk3399添加EC25E/EC20...模組

搞來搞去搞了很久,差點放棄走人的時候,發現竟然是這點事。。。辛酸史如下:

android6.01-----kernel4.4.36-----編譯環境ubuntu16.04

一、kernel driver

內核有4種驅動方式,usb serial、CDC ACM、Gobinet、QMI WWAN,本文選用的是usb serial和QMI WWAN兩種驅動。

 

1.usb serial

(1)添加模組的PID/VID。

/kernel/driver/usb/serial/option.c

如果,你添加的是EC20。那麼,註釋掉以下代碼,避免型號衝突。

/kernel/driver/usb/serial/qcserial.c

{USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */

/kernel/driver/net/usb/qmi_wwan.c

{QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */

此時,在/dev/下可以找到ttyUSB0/ttyUSB1/ttyUSB2/ttyUSB3設備節點

(2)添加零包機制

根據 USB 協議的要求, 需要添加在批量輸出傳輸過程中處理零數據包的機制。

/kernel/drivers/usb/serial/usb_wwan.c

(3)添加重置回調

當 MCU 進入掛起狀態時, 某些 USB 主機控制器/集線器將斷電或重置模式, 並且在 MCU 退出掛起/休眠模式後, 它們無法恢復 USB 設備。添加以下語句, 以啓用重新安置過程。

/kernel/drivers/usb/serial/option.c

(4)如果使用Gobinet、QMI WWAN驅動

/kernel/drivers/usb/serial/option.c

(5)添加內核模塊

[*] Device Driver

   [*]USB Support

      [*]USB Serial Converter support

         [*]USB Driver for GSM and CDMA modems

 

2.QMI WWAN

(1)添加PID/VID

/kernel/drivers/net/usb/qmi_wwan.c

如果添加的是EC20型號,請註釋以下代碼。

/kernel/drivers/usb/serial/qcserial.c

{USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */

/kernel/drivers/net/usb/qmi_wwan.c

{QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */

此時,在/dev/下可以找到qcqmi0設備節點

(2)添加對原始 IP 模式的支持

       Ec25僅支持原始 IP 模式 (IP 數據包未封裝在以太網幀)。因此, 當數據包發送到模塊時, 必須剝離以太網標頭, 並且從模塊接收數據包時添加。

/kernel/drivers/net/usb/qmi_wwan.c

#if 1 //Added by Quectel 
#include <linux/etherdevice.h> 
struct sk_buff *qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 
{ 
    if (dev->udev->descriptor.idVendor != cpu_to_le16(0x2C7C)) 
    return skb; 
    // Skip Ethernet header from message 
    if (skb_pull(skb, ETH_HLEN))
        return skb; 
    else 
        dev_err(&dev->intf->dev, "Packet Dropped "); 
    // Filter the packet out, release it 
    dev_kfree_skb_any(skb); 
    return NULL; 
} 
#include <linux/version.h> 
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 )) 
static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 
{ 
    __be16 proto; 
    if (dev->udev->descriptor.idVendor != cpu_to_le16(0x2C7C)) 
    return 1; 
    /* This check is no longer done by usbnet */ 
    if (skb->len < dev->net->hard_header_len)
        return 0; 
    switch (skb->data[0] & 0xf0) { 
    case 0x40: 
        proto = htons(ETH_P_IP); 
        break; 
    case 0x60: 
        proto = htons(ETH_P_IPV6); 
        break; 
    case 0x00: 
        if (is_multicast_ether_addr(skb->data)) 
            return 1; 
        /* possibly bogus destination - rewrite just in case */ 
        skb_reset_mac_header(skb); 
        goto fix_dest; 
    default: 
        /* pass along other packets without modifications */ 
        return 1; 
} 
    if (skb_headroom(skb) < ETH_HLEN) 
    return 0; 
    skb_push(skb, ETH_HLEN); 
    skb_reset_mac_header(skb); 
    eth_hdr(skb)->h_proto = proto; 
    memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); 
fix_dest: 
    memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); 
    return 1; 
} 
/* very simplistic detection of IPv4 or IPv6 headers */ 
static bool possibly_iphdr(const char *data) 
{ 
    return (data[0] & 0xd0) == 0x40; 
} 
#endif 
#endif 
……
/* if follow function exist, modify it as below */
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) 
{ 
……
#if 1 //Added by Quectel
    if (dev->udev->descriptor.idVendor == cpu_to_le16(0x2C7C)) { 
    dev_info(&intf->dev, "Quectel EC25&EC21&EG91&EG95&EG06&EP06&EM06&BG96&AG35 work on RawIP mode\n"); 
    dev->net->flags |= IFF_NOARP; 
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,9,1 )) 
    /* make MAC addr easily distinguishable from an IP header */ 
    if (possibly_iphdr(dev->net->dev_addr)) { 
    dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ 
    dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ 
 } 
#endif 
    usb_control_msg( 
    interface_to_usbdev(intf), 
    usb_sndctrlpipe(interface_to_usbdev(intf), 0), 
    0x22, //USB_CDC_REQ_SET_CONTROL_LINE_STATE 
    0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE 
    1, //active CDC DTR 
    intf->cur_altsetting->desc.bInterfaceNumber, 
    NULL, 0, 100); 
    } 
#endif 
err: 
    return status; 
}

/* if follow struct exist, modify it as below */ 
static const struct driver_info qmi_wwan_info = 
{ 
……
#if 1 //Added by Quectel 
    .tx_fixup = qmi_wwan_tx_fixup, 
    .rx_fixup = qmi_wwan_rx_fixup, 
#endif 
}

/* if follow struct exist, modify it as below */ 
static const struct driver_info qmi_wwan_force_int4 = { 
…… 
#if 1 //Added by Quectel 
    .tx_fixup = qmi_wwan_tx_fixup, 
    .rx_fixup = qmi_wwan_rx_fixup, 
#endif 
};

(3)添加內核模塊

[*]Device Drivers

   [*]Network device support

      [*]USB Network Adapters

         [*]Multi-purpose USB Networking Framework

            <*>QMI WWAN driver for Qualcomm MSM based 3G and LTE modems

 

3.添加內核ppp

[*]Device Driver

   [*]Network device support

      [*]PPP (point-to-point protocol) support

 

4.電源管理

(1)自動掛起

/kernel/drivers/usb/serial/option.c

static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { 
 struct usb_wwan_intf_private *data; 
……
#if 1 //Added by Quectel
    //For USB Auto Suspend
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) && serial->dev->descriptor.idProduct == cpu_to_le16(0x9090)) { 
        pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000); 
        usb_enable_autosuspend(serial->dev); 
    } 
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) && serial->dev->descriptor.idProduct == cpu_to_le16(0x9003)) { 
        pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000); 
        usb_enable_autosuspend(serial->dev); 
    } 
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) && serial->dev->descriptor.idProduct == cpu_to_le16(0x9215)) { 
        pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000);
        usb_enable_autosuspend(serial->dev); 
    } 
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) { 
        pm_runtime_set_autosuspend_delay(&serial->dev->dev, 3000); 
        usb_enable_autosuspend(serial->dev); 
    } 
#endif 
    /* Store device id so we can use it during attach. */ 
    usb_set_serial_data(serial, (void *)id); 
    return 0; 
}

(2)自動喚醒

/kernel/drivers/usb/serial/option.c

static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { 
    struct usb_wwan_intf_private *data; 
    ……
#if 1 //Added by Quectel
    //For USB Remote Wakeup
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) && serial->dev->descriptor.idProduct == cpu_to_le16(0x9090)) { 
        device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup 
    } 
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) && serial->dev->descriptor.idProduct == cpu_to_le16(0x9003)) { 
        device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup 
    } 
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) && serial->dev->descriptor.idProduct == cpu_to_le16(0x9215)) { 
        device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup 
    } 
    if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) { 
        device_init_wakeup(&serial->dev->dev, 1); //usb remote wakeup 
    } 
#endif 
    /* Store device id so we can use it during attach. */ 
    usb_set_serial_data(serial, (void *)id); 
    return 0; 
}

此時,驅動層相關已經添加完了。

如果是linux系統,再添加quectel-CM的撥號工具就可以了。

 

5.quectel-CM撥號

(1)移植quctel-CM源碼

放入/hardware/ril/reference-ril/路徑下;

(2)添加default.script腳本

android環境,命令udhcpc -h檢查版本調用路徑:

將default.script腳本放至該路徑(或/usr/share/udhcpc/),並注意執行權限。

權限:可在/system/core/libcutils/fs_config.c文件或init.rc文件中的on init中指定

(3)執行quectel-CM

將sudo quectel-CM -s ctnet 命令添加到腳本,並在/etc/init.d/rc中啓動腳本。

檢查IP/DNS/網關等配置,沒問題可以正常啓動。

 

 

二.android RIL庫

 

1.添加ril源碼

(1)移植ril源碼

放入/hardware/ril/reference-ril路徑

(2)指定rild庫路徑

/device/rockchip/rkxx/rkxxxxxx/system.prop

rild.llibpath=/system/lib64/libreference-ril.so

rild.libargs=-d /dev/ttyUSB2      //ttyUSB2是AT口

 

2.添加phone進程

源碼路徑:/packages/apps/PhoneCommon

添加編譯:/device/rockchip/rk3399/device.mk

PRODUCT_PACKAGES += com.android.phone.common

在/out/target/product/rkxxsdk/system/app/下出現com.android.phone.common

 

3.android系統設置

(1)添加ril-daemon服務到init.rc

/device/rockchip/rk3399/init.rc

service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so
    class main
    socket rild stream 660 root radio 
    socket rild-debug stream 660 radio system 
    user root
    group radio cache inet misc audio sdcard_rw log

(android8及以上)

service ril-daemon /vendor/bin/hw/rild -l /vendor/lib64/libreference-ril.so
    class main
    user root
    group radio cache inet misc audio sdcard_rw log

(2)保證rild的root權限

/hardware/ril/rild/rild.c

OpenLib:
#endif
    //switchUser();

    dlHandle = dlopen(rilLibPath, RTLD_NOW);

(3)添加APN

/device/rockchip/rkxx/rkxx.mk

PRODUT_COPY_FILES:= /vendor/rockchip/common/phone/etc/apns-full-conf.xml:system/etc/qpns-conf.xml

檢查該文件中是否包含所使用的運營商APN信息,如果沒有,添加上相應的APN信息。

比如:使用MCC/MNC來確認,而MCC/MNC的值是通過模塊查詢到的IMSI碼的前五位來確定的。

(4)打開dongle功能

/device/rockchip/common/BoardConfig.mk

BOARD_HAVE_DONGLE ?= true

(5)檢查

          logcat -b radio -v time    //查看log

          getprop init.svc.ril-daemon    //檢查ril守護進程Runing

          cat init.rc | grep ril-daemon    //檢查ril-daemon服務是否生效

           getenforce                             //檢查SELinux是否開啓,<setenforce 0>命令關閉

           getprop gsm.version.ril-impl  //檢查ril版本,出現Quectel_Android_RIL_SR01A41V17,如未出現ril庫沒有添加上,或者檢查phone進程

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