让Android也支持华为EM770模块
硬件:Devkit8000 256M+256MHuaWei EM770 WCDMA模块Option ICON7.2 mini-pci转USB卡座 软件版本:0xdroid beagle-eclair0xkernel 2.6.32 源码下
硬件:
Devkit8000 256M+256M
HuaWei EM770 WCDMA模块
Option ICON7.2 mini-pci转USB卡座
软件版本:
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上肯定也是行的,只是没找到方法。如果哪位朋友知道怎么玩,希望能给小弟提示一下。
更多推荐
所有评论(0)