In the hardware/RIL directory, the ril driver module of Android is divided into three parts: rild, libril. so, and librefrence_ril.so, and radiooptions are available for automatic or manual debugging. All depend on the ril. h header file in the include directory. Currently, the cupcake branch is equipped with gsm support and the cdma branch. The gsm driver is analyzed here.
The GSM module, due to the history of the Modem, the AP has been interacting with BB through the AT command based on the serial port. Some existing edge or 3G modules, or ap and bp integrated chips such as omap, have already used USB or other high-speed bus communications, however, most of them still use the simulated serial port mechanism to use the AT command. Here, the RIL (Radio Interface Layer) Layer is mainly based on AT commands, such as sending commands and response Parsing. MUX protocols used for transmission such as gprs are not included here and will not be introduced for the moment .)
The following is a detailed analysis. This article mainly covers the basic architecture and initialization content:
First, we will introduce the relationship between rild and libril. so and librefrence_ril.so:
1. rild:
Implements only one main function as the entry point of the entire ril layer and completes initialization.
2. libril. so:
It is closely integrated with rild and is its shared library. This relationship has been established during compilation. The components are ril. cpp and ril_event.cpp. Libril. so resides in the rild daemon. It mainly completes the communication at the same layer, accepts the ril request and passes it to librefrence_ril.so, and sends the feedback from librefrence_ril.so to the calling process.
3. librefrence_ril.so:
Rild is loaded in the manual dlopen mode, and the combination is slightly loose. This is also because librefrence. so is mainly responsible for communicating with Modem Hardware. This makes it easier to replace or modify to adapt to more Modem types. It converts the request from libril. so to the AT command, monitors the Modem feedback, and passes it back to libril. so. During initialization, rild obtains a set of function pointers through the symbol RIL_Init and establishes a connection with them.
4. radiooptions:
Radiooptiongs obtains the startup parameters and uses socket to communicate with rild, so that Modem parameters can be configured during debugging.
Next, analyze the initialization process. The main entry is the main function in rild. c, which mainly completes three tasks:
1. Enable the event mechanism in libril. so, which is the core message loop driven by multiple I/O in RIL_startEventLoop.
2. initialize librefrence_ril.so, that is, the part that communicates with the hardware or the analog hardware modem is called the hardware), which is completed through the RIL_Init function.
3. Use RIL_Init to obtain a set of function pointers RIL_RadioFunctions, register RIL_register, and open the socket channel that accepts upper-layer commands.
The first task is the RIL_startEventLoop function. RIL_startEventLoop in ril. cpp. The main purpose is to create a dispatch thread through pthread_create (& s_tid_dispatch, & attr, eventLoop, NULL). The entry point is eventLoop. in eventLoop, The ril_event_loop function in ril_event.cpp is called to establish the message (event) queue mechanism.
Let's take a closer look at the message queue mechanism. These codes are all in ril_event.cpp.
void ril_event_init(); void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param); void ril_event_add(struct ril_event * ev); void ril_timer_add(struct ril_event * ev, struct timeval * tv); void ril_event_del(struct ril_event * ev); void ril_event_loop(); struct ril_event { struct ril_event *next; struct ril_event *prev; int fd; int index; bool persist; struct timeval timeout; ril_event_cb func; void *param; }; |
Each ril_event structure can be a file, socket, or pipeline bound to an fd handle) and carries a func pointer to perform the specified operation.
The specific process is: after ril_event_init is complete, use ril_event_set to configure a new ril_event and add it to the queue through ril_event_add. Actually, rilEventAddWakeup is usually used.) add adds all ril_event fd in the queue, put it in a fd set readFds. In this way, ril_event_loop can use a multiplexing I/O mechanism to select) to wait for these fd. If any fd has data to write, it will enter the analysis process processTimeouts (), processreadies (& rfds, n), firePending (). The process will be analyzed in detail later.
In addition, we can see that before entering ril_event_loop, A s_wakeupfd_event has been mounted and implemented through the pipe mechanism. This event can be implemented in some situations, it can internally wake up ril_event_loop multiplexing blocking, such as when some timeout commands expire.
Now that the analysis of the first task is complete, a message loop based on the event queue is established. Later, the event object of the Upper-layer request can be accepted, in the third task ).
Next, let's look at the second task. The entry of this task is RIL_Init. RIL_Init first obtains the device file of the hardware interface or the socket of the simulation hardware interface through parameters. next we will start a new thread to continue initialization, that is, mainLoop.
The main task of mainLoop is to establish communication with the hardware, and then block waiting for the active reporting or response of the hardware through the read method.After registering some basic callback timeout, readerclose), mainLoop first opens the hardware device file and establishes communication with the hardware. s_device_path and s_port are the device path parameters obtained earlier, you can open both of them and have their own reader. It is also easy to add dual-card dual-waiting support ).
The reader wait loop on the device file is established through the at_open function, which is also completed by creating a new thread. ret = pthread_create (& s_tid_reader, & attr, readerLoop, & attr ), the entry point is readerLoop.
The AT command uses the line breaks of \ r \ n or \ n \ r as separators. Therefore, readerLoop is line-driven unless errors or timeout occurs, otherwise, a complete response or active report will be read before the response is returned. After this loop is run, our basic AT response mechanism has been established. Its specific analysis, including the ATUnsolHandler mounted in at_open, will be included in the subsequent articles for analyzing response's serialization.
With the response mechanism, of course, requests can also be sent to the hardware.) run the RIL_requestTimedCallback (initializeCallback, NULL, & TIMEVAL_0) command to initializeCallback, execute some Modem initialization commands, mainly through the AT command. The process of sending the AT command is put in the subsequent analysis of the request serialization article. Here we can see that it mainly involves some parameter configurations and network status checks. Now the second task has been analyzed and the hardware is ready for access.
The last is the third task. The third task starts with the return value of RIL_Init, which is a pointer to the RIL_RadioFunctions structure.
typedef struct { int version; /* set to RIL_VERSION */ RIL_RequestFunc onRequest; RIL_RadioStateRequest onStateRequest; RIL_Supports supports; RIL_Cancel onCancel; RIL_GetVersion getVersion; } RIL_RadioFunctions; |
The most important one is the onRequest domain. The requests from the upper layer are mapped by this function and converted to the corresponding AT command and sent to the hardware. Rild registers this pointer through RIL_register.
Another task to be completed in RIL_register is to open the socket interface s_fdListen that communicates with the upper layer as the main interface, and s_fdDebug is used for debugging ).
Then, register the two socket interfaces using the mechanism implemented in Task 1 to list only s_fdListen)
ril_event_set (&s_listen_event, s_fdListen, false, listenCallback, NULL); rilEventAddWakeup (&s_listen_event); |
In this way, the two sockets are added to the check handle set of multi-multiplexing I/O in Task 1, and the event mechanism can respond to and process the request once there is a debug from the upper layer. The process has been analyzed.