Address: http://lisaguo.iteye.com/blog/920065
Android phone analysis (1)
Android's radio interface layer (RIL) provides an abstraction layer between the telephone service and the radio hardware.
Radio interface layer RIL (radio interface layer) is responsible for reliable data transmission, at command transmission, and Response Parsing. The application processor communicates with the wireless communication module with the GPRS function through the AT command set.
Invented by Hayes, at Command is a modem command language used by a modem manufacturer. Each command starts with the letter ".
Java framework
The code path is:
Frameworks/base/telephony/Java/Android/Telephony
Android. telephony and Android. telephony. GSM
Core native:
In the hardware/RIL directory, the local code supported by RIL is provided, including four folders:
Hardware/RIL/include
Hardware/RIL/libril
Hardware/RIL/reference-RIL
Hardware/RIL/rild
Kernel Driver
The Linux Kernel Driver provides support for related drivers, which can be built on UART, sdio, USB, and other high-speed serial buses.
The RIL. h file in the hardware/RIL/include/telephony/directory is the basic header file of the RIL part.
The struct ril_radiofunctions is defined as follows:
Typedef struct {
Int version;
Ril_requestfunc onrequest;
Ril_radiostaterequest onstaterequest;
Ril_supports supports;
Ril_cancel oncancel;
Ril_getversion getversion;
} Ril_radiofunctions;
Ril_radiofunctions contains the struct of several function pointers. This is actually an interface at the porting layer. After implementation of the underlying library, the rild daemon obtains these function pointers and executes the corresponding functions.
The prototype of several function pointers is:
Typedef void (* ril_requestfunc) (INT request, void * data,
Size_t datalen, ril_token t );
Typedef ril_radiostate (* ril_radiostaterequest )();
Typedef int (* ril_supports) (INT requestcode );
Typedef void (* ril_cancel) (ril_token t );
Typedef const char * (* ril_getversion) (void );
The most important function is onrequest (), which is a function executed by the request.
Android phone Analysis (2)
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 time being .)
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. At the beginning, 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.
Android phone analysis (III)
Analyze the initialization process. The main function in rild. C is used to complete 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 communication with hardware or analog hardware modem (hereinafter referred to as 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 is bound to an FD handle (which can be a file, socket, or pipe) 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 (usually rileventaddwakeup). Add adds all ril_event FD in the queue, put it in a FD set readfds. In this way, ril_event_loop can wait for these FD through a multiplexing I/O mechanism (select). If any FD has data to write, it will enter the analysis process processtimeouts (), processreadreadies (& 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 first task has been analyzed, a message loop based on the event queue is established, and later the requests from the upper layer can be accepted (the event object of the upper layer request is created, 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 callbacks (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, open it (the two can be opened at the same time 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 Rn or NR as the separator. Therefore, readerloop is line-driven. Unless an error occurs or times out, it will read a complete response or report it on its own initiative,. 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 be sent when communication with the hardware), run to initializecallback through ril_requesttimedcallback (initializecallback, null, & timeval_0, 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 for upper-layer communication (s_fdlisten is the main interface, s_fddebug is used for debugging ).
Then register the two socket interfaces using the mechanism implemented in Task 1 (only list 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. Once there are upper-layer (debugging) requests, the event mechanism can respond to and process the requests. The process has been analyzed.
Android phone analysis (4)
Request Process
1. Operation of multiplexing I/O Mechanism
As mentioned above, requests are received through multiplexing I/O in ril_event_loop, and Initialization is also analyzed. Now let's take a closer look at how this mechanism works.
Ril_event_set is responsible for configuring an event. There are two main types of events:
Ril_event_add adds an event that uses multi-path I/O. It is responsible for attaching the event to the queue, adding the event channel handle FD to watch_table, and then waiting through select.
Ril_timer_add adds a timer event, which is attached to the queue and recalculates the shortest timeout time.
Whatever the add method, triggerevloop will be called to refresh the queue, update the timeout value or wait for the object.
After the refresh, ril_event_loop returns the result from the blocking position and select. There are only two possibilities: timeout and I/O.
Timeout processing in processtimeouts, remove the timeout event and add pending_list.
Check the processing of channels with I/O operations in processreadreadies and add the time-out event to pending_list.
Finally, in firepending, retrieve the pending_list event and execute the event-> func.
After these operations are completed, calculate the new timeout time and re-select to block Multiple I/O.
The previous initialization process has been analyzed. After Initialization is complete, three event objects are attached to the queue:
S_listen_event: Socket named rild, main requeset & response channel
S_debug_event: Socket named rild-Debug. requeset & response channel is used for debugging (the process is basically the same as s_listen_event, and only s_listen_event is analyzed later)
S_wakeupfd_event: Unknown MPs queue, used to actively wake up the queue. (You can use it to refresh the queue. For details, refer to the relevant sections on using it)
2. Request input and dispatch
After understanding the basic operation process of the event queue, let's take a look at how the request is passed in and dispatch.
The core code is in frameworks/base/telephony/Java/COM/Android/Internal/telephony/GSM/RIL. java, which is the core component for Android Java framework to process Radio (GSM. this article focuses on rild, which is the driver part.
Let's look at a specific example. The dial function in RIL. Java:
Public void
Dial (string address, int clirmode, message result)
{
Rilrequest RR = rilrequest. Obtain (ril_request_dial, result );
Rr. MP. writestring (Address );
Rr. MP. writeint (clirmode );
If (rilj_logd) riljlog (RR. serialstring () + ">" + requesttostring (RR. mrequest ));
Send (RR );
}
RR is an rilrequest object applied for with ril_request_dial as the request number. This request number is shared in the Java framework and the rild Library (refer to the origin of these values in rilconstants. Java :))
During rilrequest initialization, the socket named rild will be connected (that is, the socket bound to s_listen_event in rild) to initialize the data transmission channel.
Rr. MP is a parcel object. parcel is a simple serialization protocol used to serialize an object (or a member of an object) into word throttling for passing parameters. here we can see that both string address and INT clirmode are serialized members in sequence. before the RR initialization, the request and request serial numbers (auto-generated incremental numbers) have become the first two serialized members. this laid the foundation for later Request Parsing.
Next is the process of sending to handlemessage. Send directly transmits the RR to the handlemessage of another thread, and the handlemessage executes data = RR. MP. marshall () executes the serialization operation and writes the Data byte stream to rild socket.
Return to our rild. Select finds that the rild socket has a request link signal, causing s_listen_event to be mounted to pending_list. Execute event-> func, that is
Static void listencallback (int fd, short flags, void * PARAM );
Next, s_fdcommand = accept (s_fdlisten, (sockaddr *) & peeraddr, & socklen) gets the passed-in socket descriptor, that is, the connection passed in by the upper-layer Java RIL.
Then, create a record_stream through record_stream_new and bind it to s_fdcommand. Here we do not care about the specific process of record_stream. We will focus on the command Event Callback and processcommandscallback function, from the previous event mechanism analysis, this callback function will be called once s_fdcommand has data. (skip onnewcommandconnect Analysis)
Processcommandscallback uses record_stream_get_next to block reading data from s_fdcommand until a complete request is received (the integrity of the request packet is guaranteed by the record_stream mechanism), and then delivers it to processcommandbuffer.
After entering processcommandbuffer, we will officially enter the Command Parsing section. Each Command will exist in the form of requestinfo.
Typedef struct requestinfo {
Int32_t token; // This Is Not ril_token
Commandinfo * PCI;
Struct requestinfo * p_next;
Char canceled;
Char local; // responses to local commands do not go back to command process
} Requestinfo;
In this example, PRI is a requestinfo structure pointer. The data streams from the socket are serialized byte streams processed by parcel, which will be extracted through deserialization. the first part is the request number and the token domain (request incremental serial number ). we pay more attention to this request number. As mentioned above, the number between the upper layer and the rild is uniform. it is defined as an enumeration that contains ril_commands.h. CPP
Static commandinfo s_commands [] = {
# Include "ril_commands.h"
};
PRI directly accesses this array to obtain its own PCI.
This is a commandinfo structure:
Typedef struct {
Int requestnumber;
Void (* dispatchfunction) (parcel & P, struct requestinfo * Pri );
INT (* responsefunction) (parcel & P, void * response, size_t responselen );
} Commandinfo;
The basic resolution is completed here. Next, PRI is mounted to the pending request queue, and the specific PCI-> dispatchfunction is executed for detailed resolution.
3. Detailed Request Parsing
For dial, The commandinfo structure is initialized as follows:
{Ril_request_dial, dispatchdial, responsevoid },
Execute the dispatchfunction, that is, the dispatchdial function. we can see that there are many types of dispatch functions, such as dispatchvoid, dispatchstrings, and dispatchsim_io. The difference between these functions lies in the parameter format passed in by parcel. void does not contain parameters, strings uses string [] as a parameter, such as dial, and has its own Parameter Parsing Method.
The request number and parameters are now available, so you can call the specific request function.
S_callbacks.onrequest (PRI-> PCI-> requestnumber, XXX, Len, PRI) to complete this operation.
S_callbacks is the ril_radiofunctions structure pointer from libreference-RIL mentioned in the previous article. The request is transferred to the underlying libreference-RIL processing here, and handler is the onrequest in the reference-ril.c.
Onrequest for a simple switch distribution, we still look at ril_request_dial
The process is onrequest --> requestdial --> at_send_command --> at_send_command_full --> at_send_command_full_nolock --> writeline
In requestdial, convert the command and parameter to the corresponding at command and call the Public send command interface at_send_command.
In addition to this interface, there are at_send_command_singleline, at_send_command_sms, at_send_command_multiline, and so on, which are different based on the at return value and the type of the command process. for example, for at + CSQ, at_send_command_singleline is required to send a short message. Because a prompt ">" is provided to transmit raw data, Terminator, and other operations, at_send_command_sms must be used to implement this function.
Then execute at_send_command_full. The first several interfaces will eventually be called here, and then a mutex at_send_command_full_nolock will be used to complete the final write operation. In writeline, write it to the device opened during initialization.
After the writeline is returned, there are still some operations, such as saving the type and other information, for use when the response returns, and some timeout processing.
Here, the detailed request process is analyzed.
Android phone analysis (5)
Response Process
In the previous analysis of the request, the writeline operation in at_send_command_full_nolock was terminated, because the operation to write the command to the hardware device was completed here, and the next step was to wait for the hardware response, that is, the response process. Our analysis also starts from here.
Response information is obtained in the readerloop mentioned in the First initialization analysis. Received by the Readline function in the unit of 'row.
There are two types of at response: one is actively reported, such as the network status, SMS, incoming calls, etc. do not need to go through the request, there is a dedicated description of the unsolicited words. The other is the real response, that is, the command response.
In this case, we can see that all rows are filtered by the automatic reporting of SMS, because at processing of SMS messages is usually troublesome and is listed separately regardless of sending and receiving. This is because the text message (two lines, logo + PDU) needs to be processed in real time and cannot be split for processing. The processing function is onunsolicited (pointed by s_unsolhandler). Let's take a look.
Except for the exception of SMS, all lines must go through processline. Let's take a look at this process:
Processline
| ---- No cmd ---> handleunsolicited // actively report
| ---- Isfinalresponsesuccess ---> handlefinalresponse // success, standard response
| ---- Isfinalresponseerror ---> handlefinalresponse // failed, standard response
| ---- Get '>' ---> send sms pdu // receive> symbol, send SMS data and wait for response
| ---- Switch s_type ---> the specific response // The specific response information of the Command needs to be analyzed accordingly.
Here, we mainly focus on handleunsolicited automatic reporting (the onunsolicite will be called to the previous smsunsolicite also called), and the specific response information of switch s_type. In addition, the specific response must be a standard response such as handlefinalresponse to complete the process.
1. onunsolicite (actively report the response)
Static void onunsolicited (const char * s, const char * sms_pdu );
The text message at design is really troublesome, so that the second parameter of this function is completely prepared for it.
The main parsing process of response is completed by the function in at_tok.c. In fact, the string is parsed by block. The specific parsing method is determined by each command or report information. Here we will not go into details. onunsolicited only resolves the first part (usually in the form of + XXXX), and then decides the next operation based on the type. The operation is ril_onunsolicitedresponse and ril_requesttimedcallback.
A) ril_onunsolicitedresponse:
Return unsolicited information directly to the upper layer. Use parcel to write response_unsolicited and unsolresponse (request number) to parcel first, then use the s_unsolresponses array to find the corresponding responsefunction for further parsing and store it in parcel. Finally, it is passed back to the original process through sendresponse. Process:
Sendresponse --> sendresponseraw --> blockingwrite --> write to s_fdcommand (the socket connection established earlier with the upper-layer framework)
After these steps, there are some other operations such as wake-up system. Not detailed.
B) ril_requesttimedcallback:
Call back the corresponding internal processing functions through the timer mechanism implemented by the event mechanism (see Article 2. Add the callback to the event loop through internalrequesttimedcallback, and finally complete the callback of the function mounted on the callback. For example, pollsimstate, onpdpcontextlistchanged, and so on, you do not need to return the upper layer, internal processing is OK.
2. Switch s_type (specific command response) and handlefinalresponse (standard response)
The type of the command (s_type) is set when sending the command (see Article 2). There are several types of commands: no_result, numeric, singleline, and Multiline for different at times. For example, if at + CSQ is singleline, at + CSQ = XX, XX is returned, and a line of OK is added. For example, some setting commands are no_result and only one row of OK or error is returned.
These types of parsing are very similar. Through some judgment (comparing at header tags, etc.), if the corresponding response is, it is mounted to a temporary result sp_response-> p_intermediates queue through addintermediate. If it is not the corresponding response, it should actually be the automatic reporting in the interspersed content, which should be handled by onunsolicite.
A specific response can only be used to obtain the response information to a temporary result and wait for the specific analysis. Whether or not there is a specific response, the standard response handlefinalresponse is finally completed, that is, the standard response such as OK and error is accepted to end. This is the specification of most AT commands.
Handlefinalresponse sets the s_commandcond object, which is the waiting object of at_send_command_full_nolock. At this point, the complete response information has been fully obtained, and the send command can further process the returned information (temporary results, and standard return success or failure, all in sp_response ).
The pp_outresponse parameter returns sp_response to the function that calls at_send_command_full_nolock.
In our analysis in Article 2, this function is actually requestdial, but requestdial ignores the response. So let's look at another example, such as requestsignalstrength, the command is actually the at + CSQ mentioned above:
We can see that the operation is indeed performed through at_send_command_singleline, and the response is in p_response.
P_response if the returned result fails (that is, the error of the standard response, etc.), send the returned data to the upper layer through ril_onrequestcomplete and end the command.
If the execution is successful, p_response-> p_intermediates will be further analyzed, which is also analyzed through the functions in at_tok.c. The result is also returned through ril_onrequestcomplete.
Ril_onrequestcomplete:
Ril_onrequestcomplete is similar to ril_onunsolicitedresponse and has the same functions.
It is passed back to the upper layer through parcel. It is also written to response_solicited (different from response_unsolicited), PRI-> token (request number passed by the upper layer), error code (send command error, is not an AT response ). If an AT response exists, access pri-> PCI-> responsefunction to parse the specific response and write it to parcel.
Then, in the same way:
Sendresponse --> sendresponseraw --> blockingwrite --> write to s_fdcommand
Complete the final response delivery.
At this point, we have analyzed the automatic reporting and command response. In fact, the response part has come to an end.