Detailed analysis of the android GSM Driver Module (rild) (2) request process
Please note that pandatv posted on it168 and opendroid
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. Let's 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;
Here, PRI is a pointer to the requestinfo structure. 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 },
Run 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. It has its own Parameter Parsing Method, and so on.
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, at_send_command_singleline is required for at + CSQ and text messages are sent. Because there are prompt prompts ">", upload bare data, Terminator, and other operations, at_send_command_sms must be used to implement them.
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.