让Android也支持华为EM770模块

来源:互联网 发布:js的闭包 编辑:程序博客网 时间:2024/05/01 07:51

硬件:

Devkit8000 256M+256M

HuaWei EM770  WCDMA模块

Option ICON7.2 mini-pci转USB卡座

EM770ICON7.2

 

软件版本:

0xdroid beagle-eclair

0xkernel 2.6.32

 

源码下载与编译,参考0xlab官方wiki:http://code.google.com/p/0xdroid/wiki/Source

 


 

     Android中负责与Modem交互的模块是RIL,位于源码的hardware目录之下。这部份代码自发布以来基本上没怎么更新过,主要都是各个基带厂商根据自己的芯片去实现这部份代码。

 

     前不久在Google上网卡的时候,看到HUAWEI EM770支持语音电话,所以就萌生了一个想法,将它更到Android上来玩。在淘宝上入手了一块拿来研究,在windows上只要修改下Mobile Partner的configure文件就可以打接电话了,下载链接:

 

一、编译内核 

    弄到Android上,首先得让Linux内核认识EM770才行,所以第一步理所当然的就是配置kernel.

    make menuconfig:

    Device Drivers  --->

                 [*] USB support  --->

                            <*>   USB Serial Converter support  --->

 

                                                    [*]   USB Serial Console device support

                                                    [ ]   Functions for loading firmware on EZUSB chips

                                                    [*]   USB Generic Serial Driver

                                                   <*>   USB FTDI Single Port Serial Driver

                                                   <*>   USB driver for GSM and CDMA modems

     make uImage

    好像直接用官方编好的0xkernel镜像也行。

 

 

二、进入Linux,检查驱动是否正常工作。

    我调试Android的是先进入普通Linux再chroot到Android,自己怎样玩看个人喜好。

    配置好usb转串口的驱动后,插入上网卡,会在dev下生成ttyUSB0、ttyUSB1、ttyUSB2三个设备。通过先前在X86下测试这三个串口可以得知ttyUSB0是AT口,ttyUSB2是URC口,ttyUSB1谷狗告诉我是trace log用的没找到Linux下可用的软件进行验证,先不管它。

 

三、修改RIL源码

    RIL默认下访问Modem是打开的/dev/ttySx结点,而且URC与AT都是一个口子,显然EM770就不能工作了。修改的思路就是让收发AT的线程打开/dev/ttyUSB0,同时创建一个新的线程来收URC就行了。

1、在ril/reference-ril/Android.mk添加一行:

 

    LOCAL_CFLAGS += -DHUAWEI_EM770

2、在ril/reference-ril/atchannel.c中添加下面两个函数:

 

#ifdef HUAWEI_EM770

#include <termios.h>

static int urc_fd = -1;    /* fd of the URC channel */

static char s_URCBuffer[MAX_AT_RESPONSE+1];

static char *s_URCBufferCur = s_URCBuffer;

static pthread_t s_tid_reader_urc;

#endif

 

 

 

#ifdef HUAWEI_EM770

static const char *urc_readline()

{

    ssize_t count;

 

    char *p_read = NULL;

    char *p_eol = NULL;

    char *ret;

 

    /* this is a little odd. I use *s_URCBufferCur == 0 to

     * mean "buffer consumed completely". If it points to a character, than

     * the buffer continues until a /0

     */

    if (*s_URCBufferCur == '/0') {

        /* empty buffer */

        s_URCBufferCur = s_URCBuffer;

        *s_URCBufferCur = '/0';

        p_read = s_URCBuffer;

    } else {   /* *s_URCBufferCur != '/0' */

        /* there's data in the buffer from the last read */

 

        // skip over leading newlines

        while (*s_URCBufferCur == '/r' || *s_URCBufferCur == '/n')

            s_URCBufferCur++;

 

        p_eol = findNextEOL(s_URCBufferCur);

 

        if (p_eol == NULL) {

            /* a partial line. move it up and prepare to read more */

            size_t len;

 

            len = strlen(s_URCBufferCur);

 

            memmove(s_URCBuffer, s_URCBufferCur, len + 1);

            p_read = s_URCBuffer + len;

            s_URCBufferCur = s_URCBuffer;

        }

        /* Otherwise, (p_eol !- NULL) there is a complete line  */

        /* that will be returned the while () loop below        */

    }

 

    while (p_eol == NULL) {

        if (0 == MAX_AT_RESPONSE - (p_read - s_URCBuffer)) {

            LOGE("ERROR: Input line exceeded buffer/n");

            /* ditch buffer and start over again */

            s_URCBufferCur = s_URCBuffer;

            *s_URCBufferCur = '/0';

            p_read = s_URCBuffer;

        }

 

        do {

            count = read(urc_fd, p_read,

                            MAX_AT_RESPONSE - (p_read - s_URCBuffer));

        } while (count < 0 && errno == EINTR);

 

        if (count > 0) {

            AT_DUMP( "<< ", p_read, count );

            s_readCount += count;

 

            p_read[count] = '/0';

 

            // skip over leading newlines

            while (*s_URCBufferCur == '/r' || *s_URCBufferCur == '/n')

                s_URCBufferCur++;

 

            p_eol = findNextEOL(s_URCBufferCur);

            p_read += count;

        } else if (count <= 0) {

            /* read error encountered or EOF reached */

            if(count == 0) {

                LOGD("atchannel: EOF reached");

            } else {

                LOGD("atchannel: read error %s", strerror(errno));

            }

            return NULL;

        }

    }

 

    /* a full line in the buffer. Place a /0 over the /r and return */

 

    ret = s_URCBufferCur;

    *p_eol = '/0';

    s_URCBufferCur = p_eol + 1; /* this will always be <= p_read,    */

                              /* and there will be a /0 at *p_read */

 

    LOGD("AT< %s/n", ret);

    return ret;

}

 

 

#endif

 

 

 

#ifdef HUAWEI_EM770

 

 

static void *urc_readerLoop(void *arg)

{

    for (;;) {

        const char * line;

 

        line = urc_readline();

 

        if (line == NULL) {

            break;

        }

 

        if(isSMSUnsolicited(line)) {

            char *line1;

            const char *line2;

 

            // The scope of string returned by 'readline()' is valid only

            // till next call to 'readline()' hence making a copy of line

            // before calling readline again.

            line1 = strdup(line);

            line2 = readline();

 

            if (line2 == NULL) {

                break;

            }

 

            if (s_unsolHandler != NULL) {

                s_unsolHandler (line1, line2);

            }

            free(line1);

        } else {

            processLine(line);

        }

    }

 

    onReaderClosed();

 

    return NULL;

}

#endif

 

3、修改ril/reference-ril/atchannel.c中的at_open函数,如下:

 

/**

 * Starts AT handler on stream "fd'

 * returns 0 on success, -1 on error

 */

int at_open(int fd, ATUnsolHandler h)

{

    int ret;

    pthread_t tid;

    pthread_attr_t attr;

 

    s_fd = fd;

    s_unsolHandler = h;

    s_readerClosed = 0;

 

    s_responsePrefix = NULL;

    s_smsPDU = NULL;

    sp_response = NULL;

 

    /* Android power control ioctl */

#ifdef HAVE_ANDROID_OS

#ifdef OMAP_CSMI_POWER_CONTROL

    ret = ioctl(fd, OMAP_CSMI_TTY_ENABLE_ACK);

    if(ret == 0) {

        int ack_count;

int read_count;

        int old_flags;

        char sync_buf[256];

        old_flags = fcntl(fd, F_GETFL, 0);

        fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);

        do {

            ioctl(fd, OMAP_CSMI_TTY_READ_UNACKED, &ack_count);

read_count = 0;

            do {

                ret = read(fd, sync_buf, sizeof(sync_buf));

if(ret > 0)

read_count += ret;

            } while(ret > 0 || (ret < 0 && errno == EINTR));

            ioctl(fd, OMAP_CSMI_TTY_ACK, &ack_count);

         } while(ack_count > 0 || read_count > 0);

        fcntl(fd, F_SETFL, old_flags);

        s_readCount = 0;

        s_ackPowerIoctl = 1;

    }

    else

        s_ackPowerIoctl = 0;

 

#else // OMAP_CSMI_POWER_CONTROL

    s_ackPowerIoctl = 0;

 

#endif // OMAP_CSMI_POWER_CONTROL

#endif /*HAVE_ANDROID_OS*/

 

    pthread_attr_init (&attr);

    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

 

 

 

#ifdef HUAWEI_EM770    

    int fd2 = -1;

    while(fd2 < 0) {

fd2 = open ("/dev/ttyUSB2", O_RDWR);

     if (fd2 < 0) {

            perror ("opening URC interface. retrying...");

            sleep(10);

}

    }

    if(fd2 > 0) {

urc_fd = fd2;

     /* disable echo on serial ports */

     struct termios  ios;

     tcgetattr( fd2, &ios );

     ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */

     tcsetattr( fd2, TCSANOW, &ios );

    }

 

    ret = pthread_create(&s_tid_reader_urc, &attr, urc_readerLoop, &attr);

 

    if (ret < 0) {

        perror ("pthread_create");

        return -1;

    }

#endif

 

    ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);

 

    if (ret < 0) {

        perror ("pthread_create");

        return -1;

    }

 

 

4、修改ril/reference-ril/reference-ril.c中的RIL_Init函数,修改AT口,为了省事直接写死为/dev/ttyUSB0:

 

const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)

{

    int ret;

    int fd = -1;

    int opt;

    pthread_attr_t attr;

 

    s_rilenv = env;

 

#ifndef HUAWEI_EM770

    while ( -1 != (opt = getopt(argc, argv, "p:d:s:"))) {

        switch (opt) {

            case 'p':

                s_port = atoi(optarg);

                if (s_port == 0) {

                    usage(argv[0]);

                    return NULL;

                }

                LOGI("Opening loopback port %d/n", s_port);

            break;

 

            case 'd':

                s_device_path = optarg;

                LOGI("Opening tty device %s/n", s_device_path);

            break;

 

            case 's':

                s_device_path   = optarg;

                s_device_socket = 1;

                LOGI("Opening socket %s/n", s_device_path);

            break;

 

            default:

                usage(argv[0]);

                return NULL;

        }

    }

 

    if (s_port < 0 && s_device_path == NULL) {

        usage(argv[0]);

        return NULL;

    }

#else

    s_device_path="/dev/ttyUSB0";

#endif

    pthread_attr_init (&attr);

    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

 

    return &s_callbacks;

}

 

 

 

5、修改ril/reference-ril/reference-ril.c中mainLoop函数,将其中的:

if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) {

改为:

 

 

#ifdef HUAWEI_EM770

                if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyUSB", 11 ) ) {

#else

                if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) {

#endif

 

 

 

6、重新编译ril模块:

 

    ./build/envsetup.sh

    mmm hardware/ril/reference-ril 

    拷贝out/target/product/devkit8000/system/lib/libreference-ril.so到目标系统。

 

四、进到linux,运行Android

    chroot /android /init

    如果没有移植SGX,记得要注掉init.rc里“Start PowerVR SGX DDK”那三行。

    如果前面玩DVSDK时加了uboot参数:omapfb.vrfb=y,这里要改回去:omapfb.vrfb=n,不然android显示不正常。

 

五、后记:

    这样改下只是让EM770工作,可以打接电话了,但是没声音,不知道怎么从数据卡中取语音数据,因为在Windows上都可以打电话,所以在Linux上肯定也是行的,只是没找到方法。如果哪位朋友知道怎么玩,希望能给小弟提示一下。

 

 

 

 


原创粉丝点击