Repository contains core sys functionality of PurePhone system (service abstraction and communication between them)
module-sys is made of four main parts which are thoroughly described below:
System manager is responsible for:
In order to crate new custom service you have to inherit from base Service class. Then you have to implement several virtual methods which are listed below:
ReturnCodes InitHandler()This handler is invoked upon creation of the service. It is very important to return proper return code specified in:
enum class ReturnCodes{
Success,
Failure,
Timeout,
ServiceDoesntExist,
Unresolved
};
ReturnCodes DeinitHandler()This handler is invoked upon destruction of the service. The same rules about return codes apply here.
ReturnCodes SwitchPowerModeHandler(const ServicePowerMode mode)This handler is invoked when there is request for switching specified service's power mode. Available power modes which service should support are specified below:
enum class ServicePowerMode{
Active,
SuspendToRAM,
SuspendToNVM
};
Important: Currently there is no distinction between SuspendToRAM and SuspendsToNVM. These two cases should be handled the same. Additionally only SuspendToNVM can be received by service.
MessagePointer DataReceivedHandler(DataMessage* msg,ResponseMessage* resp)This is the main handler which should handle incoming messages.
If DataMessage* msg is not nullptr then it contains valid message which was sent by using blocking Bus API.
If ResponseMessage* resp is not nullptr then it contains valid message which was sent by using non-blocking Bus API.
This handler MUST return valid MessagePointer. In practice each service will return custom ResponseMessage which is created
by inheriting from base sys::ResponseMessage class.
Please check existing services for more examples of use
Please check "Caveats & good practices" chapter for more info
Timers are system feature and is Service/Application thread safe. Timer callback life expectancy should be as short as possible for system to work smoothly (as with any other message call)
System have basic coarse sys::Timer capability. There are two ways to handle actions on timer:
void sys::timer::SystemTimer::connect(timer::TimerCallback &&newCallback)SystemTimer is GuiTimer which is meant as a connector between System <=> GUI.GuiTimer one can connect it to gui::Item related element with Application::connect(GuiTimer &&, gui::Item*) so that timer life-cycle would be same as Item referencedBus subsystem was developed in order for services to be able to communicate with each other. Bus consists of four main methods:
bool SendUnicast(std::shared_ptr<Message> msg,const std::string& service,Service* s)Non-blocking variant of sending messages to specified service.
Service* s is pointer to API caller.
SendResult SendUnicast(std::shared_ptr<Message> msg,const std::string& service,Service* s,uint32_t timeout)Blocking variant of sending messages to specified service. timeout is in ms. SendResult is std::pair which consist return code and message hence
it is required to first check for return code before using message field. In case of timeout return code will be set to Timeout
Service* s is pointer to API caller.
void SendMulticast(std::shared_ptr<Message> msg,BusChannels channel,Service* s)Send message to specified channel. Every service which is registered to this channel will receive this message.
Service* s is pointer to API caller.
void SendBroadcast(std::shared_ptr<Message> msg,Service* s)Send message to all active service except the caller.
Service* s is pointer to API caller.
M.P: This section is incomplete mainly due to not having enough info about implementation.
As far as I know workers were created as abstraction over FreeRTOS threads and are designed to tightly cooperate with services. By design their lifecycle is controlled by services hence this relationship is a little bit similar to process(service)-thread(worker) except that workers don't share memory/resources with parent service. They are also separate units of processing as they don't know anything about each other.
From my understanding they are mostly used as a mean of unloading services from doing cpu-intensive work which could block service's DataReceivedHandler for too long.
For examples of using them please check application code as it seems that's where they are used the most.
MuditaOS power management was designed and developed based on Linux Power Management subsystem. There are three main assumptions:
Most information about design and implementation can be found in AND_0011_PowerManager.
Additionally current implementation of PowerManager(it should be considered as first iteration of development and absolutely it cannot be treated as final solution) is very simple but it proved to be working and it fulfilled current requirements. For the time being PowerManager class exposes two methods which are internally used by SystemManager:
int32_t Switch(const Mode mode)This method allows for switching CPU to different modes which are listed below:
enum class Mode{
FullSpeed,
LowPowerRun,
LowPowerIdle,
Suspend
};
In current implementation only FullSpeed and LowPowerIdle modes are used. It is worth to note that LowPowerIdle is heavy customized
and it absolutely doesn't correspond to low power idle mode from RT1051's data sheet. Main differences are:
Actual code is implemented in module-bsp/board/rt1051/bsp/lpm/RT1051LPM.cpp and module-bsp/board/rt1051/common/clock_config.cpp.
Research was done about using Suspend state and it resulted in several conclusions:
Suspend state in order to fulfill business requirements (5mA in aeroplane mode)Based on above use of Suspend mode is not necessary and was dropped.
LowPowerRun mode is only listed for convenience as it is not even implemented.
int32_t PowerOff()This method is used to turn off power supply to system. It is invoked by SystemManager after successful system close.
This is optional feature but by implementing it we will be able to limit current consumption during normal operation.
Currently whole mechanism of switching services into low power mode is very simple. There are no additional checks being made and there is no possibility to disable auto-lock feature and so on. Another thing is that most of the low-power logic is placed into ApplicationManager which unfortunately is bad design choice. It is to be considered if current solution will be sufficient or not. It is very possible that more advanced mechanism of communication between PowerManager and system logic will have to be designed and developed.
Currently core voltage during operating in low power mode is set to 0.9V. This results in 2,08mA current consumption. This can be lowered even further by lowering it to 0.8V ( resulting in current consumed at around 1.86mA). As far as I know setting core voltage to 0.8V is considered to be unstable but it is worth trying/testing.
Core voltage is set in LPM_EnterLowPowerIdle function which can be found in module-bsp/board/rt1051/common/clock_config.cpp. For more info please check RT1051 Reference Manual, Chapter 18 "DCDC Converter" and DCDC Register 3 .
It is okay to do some minor blocking tasks but blocking for longer periods will make whole system unpredictable or behave
in very uncontrolled manner. It is perfectly fine to use Bus::SendUnicast blocking API in the handler.
Some time ago second parameter was added to DataReceivedHandler
DataReceivedHandler(DataMessage* msg,ResponseMessage* resp)
This addition was dictated by need of using non-blocking variant of Bus::SendUnicast which then can trigger receiving async
response. In this case second parameter is not nullptr used and should contain valid ResponseMessage, otherwise
ResponseMessage* resp will be set to nullptr. Additionally by design there won't be the situation where both DataMessage* msg
and ResponseMessage* resp are not nullptr. Users of services should be aware of that and always check if params are valid
before using them.