前言
当前我们公司使用多个4g模块,为了方便android系统的使用,所以添加的多4g模块的适配,这种方式在可以根据接的4g模块的uid与pid,指定接入4g模块的ril库和at指令端口,完成4g模块的拨号工作。
1、移植多4g模块适配代码到hardware目录下
hardware/ril/runtime-ril-port/runtime_port.c
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Copyright 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#define LOG_TAG "RIL"
#include <utils/Log.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <linux/netlink.h>
#include <signal.h>
#include <unistd.h>
#include <runtime/runtime.h>
int current_modem_type = UNKNOWN_MODEM;
#define FAKE_PORT "/dev/ttyFAKEPort"
/* Rild need a fake port to pass continue init job,
return a fake port make it runable.
Or the system will enter 15s in early suspend.
*/
struct modem_3g_device {
const char *idVendor;
const char *idProduct;
const char deviceport; / sending AT command */
const char dataport; / sending 3g data */
const char *name;
const int type;
};
#define PATH_SIZE 1024
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
static const char *USB_DIR_BASE = "/sys/bus/usb/devices/";
static struct modem_3g_device modem_3g_device_table[] = {
{
.name = "Huawei-EM770",
.idVendor = "12d1",
.idProduct = "1001",
.deviceport = "/dev/ttyUSB0",
.dataport = "/dev/ttyUSB0",
.type = HUAWEI_MODEM,
},
{
.name = "Huawei-EM770W",
.idVendor = "12d1",
.idProduct = "1404",
.deviceport = "/dev/ttyUSB2",
.dataport = "/dev/ttyUSB0",
.type = HUAWEI_MODEM,
},
{
.name = "Huawei-E180",
.idVendor = "12d1",
.idProduct = "1003",
.deviceport = "/dev/ttyUSB1",
.dataport = "/dev/ttyUSB0",
.type = HUAWEI_MODEM,
},
{
.name = "Huawei-EM750",
.idVendor = "12d1",
.idProduct = "1413",
.deviceport = "/dev/ttyUSB3",
.dataport = "/dev/ttyUSB0",
.type = HUAWEI_MODEM,
},
{
.name = "InnoComm-Amazon1",
.idVendor = "1519",
.idProduct = "1001",
.deviceport = "/dev/ttyACM3",
.dataport = "/dev/ttyACM0",
.type = AMAZON_MODEM,
},
{
.name = "InnoComm-Amazon1",
.idVendor = "1519",
.idProduct = "0020",
.deviceport = "/dev/ttyACM3",
.dataport = "/dev/ttyACM0",
.type = AMAZON_MODEM,
},
{
.name = "ZTE-MF220",
.idVendor = "19d2",
.idProduct = "0145",
.deviceport = "/dev/ttyUSB2",
.dataport = "/dev/ttyUSB4",
.type = ZTE_MODEM,
},
{
.name = "ZTE-ME3630",
.idVendor = "19d2",
.idProduct = "1476",
.deviceport = "/dev/ttyUSB3",
.type = ZTE_ME3630,
},
{
.name = "SIMCOM-SIM7600",
.idVendor = "1E0E",
.idProduct = "9001",
.deviceport = "/dev/ttyUSB3",
.type = SIMCOM_SIM7600,
},
{
.name = "Huawei-ME909s",
.idVendor = "12d1",
.idProduct = "15c1",
.deviceport = "/dev/ttyUSB5",
.type = HUAWEI_ME909S,
},
};
/* -------------------------------------------------------------- */
#define DEBUG_UEVENT 0
#define UEVENT_PARAMS_MAX 32
enum uevent_action { action_add, action_remove, action_change };
struct uevent {
char *path;
enum uevent_action action;
char *subsystem;
char *param[UEVENT_PARAMS_MAX];
unsigned int seqnum;
};
static void dump_uevent(struct uevent *event);
int readfile(char *path, char *content, size_t size)
{
int ret;
FILE *f;
f = fopen(path, "r");
if (f == NULL)
return -1;
ret = fread(content, 1, size, f);
fclose(f);
return ret;
}
int is_device_equal(struct modem_3g_device *device,
const char *idv, const char *idp)
{
long pvid = 0xffff, ppid = 0xffff;
long t_vid, t_pid;
if (device == NULL)
return 0;
t_vid = strtol(device->idVendor, NULL, 16);
t_pid = strtol(device->idProduct, NULL, 16);
pvid = strtol(idv, NULL, 16);
ppid = strtol(idp, NULL, 16);
return (t_vid == pvid && t_pid == ppid);
}
struct modem_3g_device *
find_devices_in_table(const char *idvendor, const char *idproduct)
{
int i;
int size = ARRAY_SIZE(modem_3g_device_table);
struct modem_3g_device *device;
for (i = 0; i < size; i++) {
device = &modem_3g_device_table[i];
if (is_device_equal(device, idvendor, idproduct)) {
ALOGI("Runtime 3G port found matched device with "
"Name:%s idVendor:%s idProduct:%s",
device->name, device->idVendor, device->idProduct);
return device;
}
}
return NULL;
}
struct modem_3g_device *find_matched_device(void)
{
struct dirent *dent;
DIR *usbdir;
struct modem_3g_device *device = NULL;
char *path, *path2;
char idvendor[64];
char idproduct[64];
int ret, i;
path = malloc(PATH_SIZE);
if (!path)
return NULL;
path2 = malloc(PATH_SIZE);
if (!path2) {
free(path);
return NULL;
}
usbdir = opendir(USB_DIR_BASE);
if (usbdir == NULL) {
free(path);
free(path2);
return NULL;
}
memset(path, 0, PATH_SIZE);
memset(path2, 0, PATH_SIZE);
while ((dent = readdir(usbdir)) != NULL) {
if (strcmp(dent->d_name, ".") == 0
|| strcmp(dent->d_name, "..") == 0)
continue;
memset(idvendor, 0, sizeof(idvendor));
memset(idproduct, 0, sizeof(idproduct));
path = strcpy(path, USB_DIR_BASE);
path = strcat(path, dent->d_name);
strcpy(path2, path);
path = strcat(path, "/idVendor");
path2 = strcat(path2, "/idProduct");
ret = readfile(path, idvendor, 4);
if (ret <= 0)
continue;
ret = readfile(path2, idproduct, 4);
if (ret <= 0)
continue;
device = find_devices_in_table(idvendor, idproduct);
if (device != NULL)
goto out;
}
if (device == NULL)
ALOGI("Runtime 3G can't find supported modem");
out:
closedir(usbdir);
free(path);
free(path2);
return device;
}
const char *runtime_3g_port_device(void)
{
struct modem_3g_device *device;
device = find_matched_device();
if (device == NULL)
return FAKE_PORT;
/* Set gobal modem type. */
current_modem_type = device->type;
ALOGI("Current modem type = %d", current_modem_type);
return device->deviceport;
}
const char *runtime_3g_port_data(void)
{
struct modem_3g_device *device;
device = find_matched_device();
if (device == NULL)
return FAKE_PORT;
return device->dataport;
}
int runtime_3g_port_type(void)
{
struct modem_3g_device device;
int type = UNKNOWN_MODEM;
if (UNKNOWN_MODEM == current_modem_type){
if (NULL != (device = find_matched_device())){
/ Set gobal modem type. */
type = device->type;
}
}else{
type = current_modem_type;
}
ALOGI("Current modem type = %d", type);
return type;
}
static void free_uevent(struct uevent *event)
{
int i;
free(event->path);
free(event->subsystem);
for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
if (!event->param[i])
break;
free(event->param[i]);
}
free(event);
}
static int dispatch_uevent(struct uevent event)
{
/ if it's a usb tty event in our table. make the rild reboot. /
int i;
int ret;
for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
if (!event->param[i])
break;
if (strncmp(event->param[i], "PRODUCT=", 8) == 0) {
char vbuf[5], pbuf[5];
ret = sscanf(event->param[i],
"PRODUCT=%4s/%4s/", vbuf, pbuf);
if (ret < 0)
return -1;
if (find_devices_in_table(vbuf, pbuf))
alarm(1);
/ Restart in 1 second, since USB usually have
- many devices, this avoid rild restart too
- many times. */
}
}
return 0;
}
int process_uevent_message(int sock)
{
char buffer[64 * 1024];
char *s = buffer, *p;
char *end;
int count, param_idx = 0, ret;
struct uevent *event;
count = recv(sock, buffer, sizeof(buffer), 0);
if (count < 0) {
ALOGE("Error receiving uevent (%s)", strerror(errno));
return -errno;
}
event = malloc(sizeof(struct uevent));
if (!event) {
ALOGE("Error allcating memroy (%s)", strerror(errno));
return -errno;
}
memset(event, 0, sizeof(struct uevent));
end = s + count;
for (p = s; *p != '@'; p++)
;
p++;
event->path = strdup(p);
s += strlen(s) + 1;
while (s < end) {
if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
char *a = s + strlen("ACTION=");
if (!strcmp(a, "add"))
event->action = action_add;
else if (!strcmp(a, "change"))
event->action = action_change;
else if (!strcmp(a, "remove"))
event->action = action_remove;
} else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
event->seqnum = atoi(s + strlen("SEQNUM="));
else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
event->subsystem = strdup(s + strlen("SUBSYSTEM="));
else
event->param[param_idx++] = strdup(s);
s += strlen(s) + 1;
}
ret = dispatch_uevent(event);
#if DEBUG_UEVENT
dump_uevent(event);
#endif
free_uevent(event);
return ret;
}
static void dump_uevent(struct uevent *event)
{
int i;
ALOGD("[UEVENT] Sq: %u S: %s A: %d P: %s",
event->seqnum, event->subsystem, event->action, event->path);
for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
if (!event->param[i])
break;
ALOGD("%s", event->param[i]);
}
}
void restart_rild(int p)
{
ALOGI("3G Modem changed,RILD will restart...");
exit(-1);
}
void *usb_tty_monitor_thread(void *arg)
{
struct sockaddr_nl nladdr;
struct pollfd pollfds[2];
int uevent_sock;
int ret, max = 0;
int uevent_sz = 64 * 1024;
int timeout = -1;
struct sigaction timeoutsigact;
ALOGI("3G modem monitor thread is start");
timeoutsigact.sa_handler = restart_rild;
sigemptyset(&timeoutsigact.sa_mask);
sigaddset(&timeoutsigact.sa_mask, SIGALRM);
sigaction(SIGALRM, &timeoutsigact, 0);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid();
nladdr.nl_groups = 0xffffffff;
uevent_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (uevent_sock < 0) {
ALOGE(" Netlink socket faild, usb monitor exiting...");
return NULL;
}
if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz,
sizeof(uevent_sz)) < 0) {
ALOGE("Unable to set uevent socket options: %s", strerror(errno));
return NULL;
}
if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
ALOGE("Unable to bind uevent socket: %s", strerror(errno));
return NULL;
}
pollfds[0].fd = uevent_sock;
pollfds[0].events = POLLIN;
ret = fcntl(uevent_sock,F_SETFL, O_NONBLOCK);
if (ret < 0)
ALOGE("Error on fcntl:%s", strerror(errno));
while (1) {
ret = poll(pollfds, 1, timeout);
switch (ret) {
case 0:
ALOGD("poll timeout");
continue;
case -1:
ALOGD("poll error:%s", strerror(errno));
break;
default:
if (pollfds[0].revents & POLLIN)
process_uevent_message(uevent_sock);
}
}
close(uevent_sock);
}
int start_uevent_monitor(void)
{
pthread_t pth_uevent_monitor;
return pthread_create(&pth_uevent_monitor, NULL,
usb_tty_monitor_thread, NULL);
}
hardware/ril/runtime-ril-port/Android.mk
Copyright 2006 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=
runtime_port.c
LOCAL_SHARED_LIBRARIES :=
libutils
libcutils
LOCAL_CFLAGS :=
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= libruntime-ril-port
include $(BUILD_SHARED_LIBRARY)
hardware/ril/include/runtime/runtime.h
#ifndef RUNTIME_H
#define RUNTIME_H
extern int start_uevent_monitor(void);
extern const char *runtime_3g_port_device(void);
extern const char *runtime_3g_port_data(void);
extern int runtime_3g_port_type(void);
enum {
HUAWEI_MODEM = 0,
AMAZON_MODEM,
ZTE_MODEM,
ZTE_ME3630,
SIMCOM_SIM7600,
HUAWEI_ME909S,
UNKNOWN_MODEM,
};
extern int current_modem_type;
#endif
hardware/ril/rild/Android.mk
libril \
libdl
libdl
libruntime-ril-port
runtime_port.c文件主要添加了一些4g模块标志、usb设备节点信息及一些加载4g ril库时的处理函数,具体rild服务启动调用ril库将在rild.c中调用。
2、修改rild ril库及AT指令端口配置方式
修改ril库名称,每个4g模块单独对应一个ril库
hardware/ril/rild/rild.c
+#define REFERENCE_RIL_DEF_PATH "/system/lib/libreference-ril.so"
+#define REFERENCE_RIL_ZTE_PATH "/system/lib/libreference-ril-zte.so"
+#define REFERENCE_RIL_ZTE_ME3630_PATH "/system/lib/libreference-ril-me3630.so"
+#define REFERENCE_RIL_SIMCOM_SIM7600CE_PATH "/system/lib/libreference-ril-sim7600ce.so"
+#define REFERENCE_RIL_HUAWEI_ME909S_PATH "/system/lib/libreference-ril-me909s.so"
调用runtime_port.c中的函数,通过usb 设备的vid与pid,确定当前接入的4g模块,根据4g模块类型,指定系统中调用的ril库
[url=home.php?mod=space&uid=1999721]@@[/url] -153,7 +160,7 @@ int main(int argc, char **argv) {
const RIL_RadioFunctions *funcs;
char libPath[PROPERTY_VALUE_MAX];
unsigned char hasLibArgs = 0;
int modem_type = UNKNOWN_MODEM;
int i;
const char *clientId = NULL;
RLOGD("RIL Daemon Started");
@@ -186,6 +193,46 @@ int main(int argc, char **argv) {
strlcat(rild, clientId, MAX_SOCKET_NAME_LENGTH);
RIL_setRilSocketName(rild);
}
//Wait for device ready.
if (rilLibPath == NULL) {
while(UNKNOWN_MODEM == modem_type){
modem_type = runtime_3g_port_type()
ALOGD("Couldn't find proper modem, retrying...");
s_poll_device_cnt++;
if (s_poll_device_cnt > MAX_POLL_DEVICE_CNT){
/*
*Maybe no device right now, start to monitor
start_uevent_monitor();
goto done;
}
sleep(5);
}
}
start_uevent_monitor();
switch (modem_type){
case ZTE_MODEM:
case ZTE_ME3630:
rilLibPath = REFERENCE_RIL_ZTE_ME3630_PATH
break;
case SIMCOM_SIM7600:
rilLibPath = REFERENCE_RIL_SIMCOM_SIM7600CE_PATH
break;
case HUAWEI_ME909S:
rilLibPath = REFERENCE_RIL_HUAWEI_ME909S_PATH
break;
case HUAWEI_MODEM:
case AMAZON_MODEM:
default:
rilLibPath = REFERENCE_RIL_DEF_PATH
break;
}
关闭系统模块ril库路径获取方式
@@ -198,7 +245,7 @@ int main(int argc, char **argv) {
}
-#if 1
+#if 0
{
static char* arg_overrides[5];
static char arg_device[32];
@@ -317,7 +364,7 @@ int main(int argc, char **argv) {
}
OpenLib:
#endif
switchUser();
//switchUser();
总结
使用此方式,可以实现一个系统多个4g模块适配工作,在更换4g模块时,无需在次编译android系统,若有新的模块添加,可以按照上面me3630模块添加方式进行操作。
原作者:小忽悠0011