Wednesday, January 15, 2014

cmus player on-screen display in python

I'm actively using the C* music player (cmus) as my main music player on my working machine. It's a powerful console music player which suits best especially I mainly work in consoles. However it would be convenient if there is on-screen display (OSD) on my Gnome desktop when I am working on another window/console.

Here is a version implemented in python using the notify-send program:

status_display.sh

#!/bin/sh ~/.cmus/status_notify.py "$@" > ~/.cmus/statusinfo&
status_notify.py

#!/usr/bin/env python """ ============================================ CMUS status update program using notify-send ============================================ Example status update as program's parameter list status playing file /data/music/Zeb-Jesterized/12-Zeb-Spy From Cairo.mp3 artist Zeb album Jesterized tracknumber 12 title Spy From Cairo date 2000 duration 322 Usage: Create a shell script at ~/.cmus/status_display_program.sh and 'chmod +x' it: #!/bin/sh program1 "$@" & (replace program1 with path to this script) In CMUS: :set status_display_program=/home/user/.cmus/status_display_program.sh Reference: http://cmus.sourceforge.net/wiki/doku.php?id=status_display_programs """ import sys, os from subprocess import call if __name__ == '__main__': # basic checking if len(sys.argv) % 2 != 1 or len(sys.argv) < 2 \ or sys.argv[1] != 'status': raise Exception('please set this program as CMUS status update program') # make it a dict s = sys.argv[1:] status = dict(zip(s[::2], s[1::2])) osd_info = "[%s] %s (%s-%s)" % \ ( status['status'], os.path.basename(status['file']), status['artist'], status['title'] ) # --hint parameter is for gnome-shell's buggy notification.. anyway call(['notify-send', '--hint=int:transient:1', osd_info])

Friday, January 10, 2014

Unbalanced IRQ enable/disable in pm.c during system suspend/resume

When doing Android system power stress test (entering low power state and exiting it repetitively), I found that sometimes RTC alarm won't wake the device up.

The wake-up mechanism is easy. It's from Android framework:

// using alarm in Android framework // creating a repeated alarm every 30 sec AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE); am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 30*1000, 30 * 1000, pendingIntent); // setRepeating(int type, long triggerAtMillis, // long intervalMillis, PendingIntent operation)

The issue is considered serious. Because whenever the alarm fails to trigger, it won't work anymore until the next power cycle (reboot). I used the scope and confirmed the hardware didn't pull up the interrupt line. Software did set the alarm but there is no interrupt happening. And it won't work since the problem first happened. It's dead until the device is rebooted.

On the device we were using, all interrupts are logically combined on a single output line 'INT'. It's important to note that when an interrupt is masked (bit 1), STATUS register is not updated, and INT is not triggered even if the masked event occurs.

A detailed register dump and trace was performed and we found out the mask is not correctly configured after some time, that's why the RTC alarm's interrupt failed to deliver. The next question became: who was modifying the MASK register so that it failed?

After more tests, we came closer. This issue occurred only if there was some suspend failure (caused by other unrelated device) and system resumed immediately. For some reason the irq_enable part of the RTC driver was not invoked in this case, which was supposed to set the correct MASK value for RTC interrupt to work.

Root cause is then found. The RTC's IRQ is configured to IRQF_EARLY_RESUME (resume IRQ early during syscore instead of at device resume time), and there is unbalanced IRQ enable/disable in system suspend/resume path.

To explain that in more detail, a rough suspend/resume trace is here:

pm_suspend() 
|- enter_state()  
|-- suspend_devices_and_enter() 
|---- dpm_suspend_start() 
|------ dpm_prepare()
|------ dpm_suspend() 
|------- device_suspend()
|---- suspend_enter()
|------ dpm_suspend_end() 
|------ syscore_suspend()  
|=========================  
|------ syscore_resume()  
|------ dpm_resume_start() 
|---- dpm_resume_end() 
|------ dpm_resume() 
|------- device_resume() 
|------ dpm_complete()

And the problem is in suspend_enter(). You have to read counterclockwise (happening in order of time) from the upper-left part of the following diagram:

      [Suspend]           time      [Resume]
==========================  ^ ==============================   
suspend_enter()  (BEGIN)  | | suspend_enter()  (END)
|- dpm_suspend_end()      | | |- dpm_resume_start()
|-- dpm_suspend_noirq()   | | |-- dpm_resume_noirq()
|--- suspend_device_irqs()| | |--- resume_device_irqs()
|    --> {all IRQs}       | | |  --> {Non-IRQF_EARLY_RESUME_IRQs}
|- syscore_suspend()      | | |- syscore_resume()
 (Entering suspend state) | | |-- irq_pm_syscore_ops.resume()
                          | |     or irq_pm_syscore_resume()
                          | |    --> {IRQF_EARLY_RESUME_IRQs}
                          \_/   (Exited suspend state)

Now it is obvious that when some device fails to suspend, syscore_suspend() doesn't get run. That is, syscore_resume() won't run either. So only Non-IRQF_EARLY_RESUME_IRQs will be resumed. So those irq_enable functions defined as IRQF_EARLY_RESUME_IRQ will stay disabled, and possibly forever.

The related code which caused this was first introduced here in this commit:
commit 9bab0b7fbaceec47d32db51cd9e59c82fb071f5a                              
Author: Ian Campbell <ian.campbell@citrix.com>                                
Date:   Mon Oct 3 15:37:00 2011 +0100                                        
                                                                             
    genirq: Add IRQF_RESUME_EARLY and resume such IRQs earlier                
                                                                             
    This adds a mechanism to resume selected IRQs during syscore_resume      
    instead of dpm_resume_noirq.                                              
                                                                             
    Under Xen we need to resume IRQs associated with IPIs early enough        
    that the resched IPI is unmasked and we can therefore schedule            
    ourselves out of the stop_machine where the suspend/resume takes          
    place.                                                                    
                                                                             
    This issue was introduced by 676dc3cf5bc3 "xen: Use IRQF_FORCE_RESUME".

A simple fix will be to resume IRQF_EARLY_RESUME_IRQs in resume_device_irqs() when irq_pm_syscore_resume() doesn't get run. A fix by Laxman has been merged to mainline kernel recently by simply enable all IRQs unconditionally in irq_resume(), which is confirmed that it doesn't cause side effects.

commit ac01810c9d2814238f08a227062e66a35a0e1ea2
Author: Laxman Dewangan <ldewangan@nvidia.com>
Date:   Mon Nov 25 19:39:47 2013 +0530
    irq: Enable all irqs unconditionally in irq_resume
 
    When the system enters suspend, it disables all interrupts in
    suspend_device_irqs(), including the interrupts marked EARLY_RESUME.
 
    On the resume side things are different. The EARLY_RESUME interrupts
    are reenabled in sys_core_ops->resume and the non EARLY_RESUME
    interrupts are reenabled in the normal system resume path.
 
    When suspend_noirq() failed or suspend is aborted for any other
    reason, we might omit the resume side call to sys_core_ops->resume()
    and therefor the interrupts marked EARLY_RESUME are not reenabled and
    stay disabled forever.
 
    To solve this, enable all irqs unconditionally in irq_resume()
    regardless whether interrupts marked EARLY_RESUMEhave been already
    enabled or not.
 
    This might try to reenable already enabled interrupts in the non
    failure case, but the only affected platform is XEN and it has been
    confirmed that it does not cause any side effects.


Sending Android BOOT_COMPLETED broadcast using adb

It's easy to broadcast an intent to a specific application using adb. For example, if you want your app to run automatically after system is up, you'd want to catch BOOT_COMPLETED intent like this in your manifest:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED">
</uses-permission>

After all those related coding, you can use this to test if everything is set up correctly:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n urdomain.appname/.classname
Note that the classname is the BroadcastReceiver which receives the "android.intent.action.BOOT_COMPLETED" intent. Broadcasting is carried out successfully if you see:
Broadcasting: Intent { act=android.intent.action.BOOT_COMPLETED cmp=com.nvidia.cms/.AutoStart } Broadcast completed: result=0

.gitignore for Android application on Eclipse

It's been 4(?) years since I developed my last Android application. I found the following .gitignore file useful for adding a Eclipse Android application project folder to git:

  *.apk *.ap_ *.dex *.class bin/ gen/ # Local config local.properties # others (may need?) proguard/ *.iml *.ipr *.iws .idea/
After adding this ignore file for git, simply 'git init' and you're good to use git for your Android application. It's also possible to directly clone a git repository and use File->Import->Existing Android Code into Workspace in Eclipse.