设备驱动-----Android关机流程总结

 

分类: 设备驱动
2013-07-07 09:02 
358人阅读 
评论(0) 
收藏 
举报

关机动作从按键触发中断,linux kernel层给android framework层返回按键事件进入 framework层,

再从 framework层到kernel层执行kernel层关机任务。长按键对应的handler代码:

frameworks\policies\base\phone\com\android\internal\policy\impl\phonewindowmanager.java

 

[cpp] 
view plain
copy
print
?  

  1. Runnable mPowerLongPress;  
  2. private final Runnable mPowerLongPress = new Runnable() {  
  3.         public void run() {  
  4.             if (!mPowerKeyHandled) {  
  5.                 mPowerKeyHandled = true;  
  6.                 performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);  
  7.                 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);  
  8.                 showGlobalActionsDialog();  
  9.             }  
  10.         }  
  11.     };  

调用showglobalactionsdialog() 方法将会显示上面提到的显示 “飞行模式” ,“ 静音” ,“ ” 关机 ,

 

选项的对话框。

《设备驱动-----Android关机流程总结》

 

mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned())该函数是 

 

mGlobalActions.showDialog()函数定义在frameworks/base/policy/src/com/android/internal/policy/impl/GlobalActions.java文件

《设备驱动-----Android关机流程总结》

 

其中会调用 createDialog()

private AlertDialog createDialog()中

《设备驱动-----Android关机流程总结》

 

根据平台不同这里有差异,在createDialog()中

 

调用mWindowManagerFuncs.shutdown();

此函数在frameworks/base/services/java/com/android/server/pm/ShutdownThread.java实现

 

此函数中调用shutdownInner(context,confirm); 

 

[cpp] 
view plain
copy
print
?  

  1. public static void reboot(final Context context, String reason, boolean confirm) {    
  2.        mReboot = true;    
  3.        mRebootSafeMode = false;    
  4.        mRebootReason = reason;    
  5.        shutdownInner(context, confirm);    
  6.    }    

shutdownInner函數如下,其調用了beginShutdownSequence(context);

 

[cpp] 
view plain
copy
print
?  

  1. static void shutdownInner(final Context context, boolean confirm) {    
  2.       ….    
  3.  if (confirm) {    
  4.             final CloseDialogReceiver closer = new CloseDialogReceiver(context);    
  5.             final AlertDialog dialog = new AlertDialog.Builder(context)    
  6.                     .setTitle(mRebootSafeMode    
  7.                             ? com.android.internal.R.string.reboot_safemode_title    
  8.                             : com.android.internal.R.string.power_off)    
  9.                     .setMessage(resourceId)    
  10.                     .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {    
  11.                         public void onClick(DialogInterface dialog, int which) {    
  12.                             beginShutdownSequence(context);//調用beginShutdownSequence    
  13.                         }    
  14.                     })    
  15.                     .setNegativeButton(com.android.internal.R.string.no, null)    
  16.                     .create();    
  17.             closer.dialog = dialog;    
  18.             dialog.setOnDismissListener(closer);    
  19.             dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);    
  20.             dialog.show();    
  21.         } else {    
  22.             beginShutdownSequence(context);    
  23.         }    
  24.     }    

 

beginShutdownSequence如下

 

[cpp] 
view plain
copy
print
?  

  1. private static void beginShutdownSequence(Context context) {    
  2.            
  3.     ……    
  4.        sInstance.start();//從這兒就會開始調用到run()方法    
  5.    }    

在run()方法中也會做一些reboot之前的清除工作,关掉要关的服务等。

 

[cpp] 
view plain
copy
print
?  

  1. public void run() {    
  2.        BroadcastReceiver br = new BroadcastReceiver() {    
  3.            @Override public void onReceive(Context context, Intent intent) {    
  4.                // We don’t allow apps to cancel this, so ignore the result.    
  5.                actionDone();    
  6.            }    
  7.        };    
  8.     
  9.              ……省略    
  10.        // Set initial variables and time out time.    
  11.        mActionDone = false;    
  12.        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;    
  13.        synchronized (mActionDoneSync) {    
  14.            try {    
  15.                final IMountService mount = IMountService.Stub.asInterface(    
  16.                       ServiceManager.checkService(“mount”));    
  17.                if (mount != null) {    
  18.                    mount.shutdown(observer);    
  19.               } else {    
  20.                    Log.w(TAG, “MountService unavailable for shutdown”);    
  21.                }    
  22.            } catch (Exception e) {    
  23.                Log.e(TAG, “Exception during MountService shutdown”, e);    
  24.            }    
  25.            while (!mActionDone) {    
  26.                long delay = endShutTime – SystemClock.elapsedRealtime();    
  27.                if (delay <= 0) {    
  28.                    Log.w(TAG, “Shutdown wait timed out”);    
  29.                    break;    
  30.                }    
  31.                try {    
  32.                    mActionDoneSync.wait(delay);    
  33.                } catch (InterruptedException e) {    
  34.                }    
  35.            }    
  36.       }    
  37.     
  38.        rebootOrShutdown(mReboot, mRebootReason);//又繞到rebootOrShutdown函數    

此线程中的动作为:

(1)  广播全局事件,ACTION_SHUTDOWN Intent

 

(2)  shutdown  ActivityManager 服务

 

(3)  停止电话服务 (radio phone service)

 

(4)  停止mount 服务

 

(5)  最后调用 rebootOrShutdown(mReboot, mRebootReason);

 

在rebootOrShutdown(mReboot,mRebootReason);中

[cpp]
view plain
copy
print
?  

  1. public static void rebootOrShutdown(boolean reboot, String reason) {  
  2.     if (reboot) {  
  3.         Log.i(TAG, “Rebooting, reason: “ + reason);  
  4.         try {  
  5.            PowerManagerService.lowLevelReboot(reason);//我們偉進來的reboot參數為true,所以走這一支  
  6.         } catch (Exception e) {  
  7.             Log.e(TAG, “Reboot failed, will attempt shutdown instead”, e);  
  8.        }  
  9.     } else if (SHUTDOWN_VIBRATE_MS > 0) {  
  10.         // vibrate before shutting down  
  11.        Vibrator vibrator = new SystemVibrator();  
  12.         try {  
  13.             vibrator.vibrate(SHUTDOWN_VIBRATE_MS);  
  14.         } catch (Exception e) {  
  15.             // Failure to vibrate shouldn’t interrupt shutdown.  Just log it.  
  16.            Log.w(TAG, “Failed to vibrate during shutdown.”, e);  
  17.         }  
  18.   
  19.         // vibrator is asynchronous so we need to wait to avoid shutting down too soon.  
  20.        try {  
  21.             Thread.sleep(SHUTDOWN_VIBRATE_MS);  
  22.         } catch (InterruptedException unused) {  
  23.         }  
  24.    }  
  25.   
  26.     // Shutdown power  
  27.     Log.i(TAG, “Performing low-level shutdown…”);  
  28.     PowerManagerService.lowLevelShutdown();//如果我們偉進來的reboot為false那就是關機  
  29. }  
调用PowerManagerService.lowLevelShutdown();
此函数在frameworks/base/services/java/com/android/server/PowerManagerService.java中
又回到了PowerManagerService. 其實就是要開一個線程來在reboot之前把要做的一些工作先做完。 好吧,我們來看一下lowLevelReboot. 

 

 

[cpp] 
view plain
copy
print
?  

  1. public static void lowLevelReboot(String reason) throws IOException {    
  2.     nativeReboot(reason);//哈哈,還是要進入到時JNI    
  3. }  
lowLevelReboot调用了 JNI 方法 nativeReboot

 

lowLevelReboot定义在

 

/frameworks/base/services/jni/com_android_server_PowerManagerService.cpp中

 

[cpp] 
view plain
copy
print
?  

  1. static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {    
  2.     if (reason == NULL) {    
  3.         android_reboot(ANDROID_RB_RESTART, 0, 0);//這兒是直接重啟,因為沒有偉reason,    
  4.     } else {    
  5.         const char *chars = env->GetStringUTFChars(reason, NULL);    
  6.         android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);//這兒一般是上面偉入了recovery而重啟進入recovery,主要是oat或手機重置    
  7.         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.    
  8.     }    
  9.     jniThrowIOException(env, errno);    
  10. }    

 

上面的android_reboot
在/system/core/libcutils/android_reboot.c中。
接下來我們就看一下android_reboot()又做些什麽。

[cpp] 
view plain
copy
print
?  

  1. int android_reboot(int cmd, int flags, char *arg)    
  2. {    
  3.     int ret;    
  4.     
  5.     if (!(flags & ANDROID_RB_FLAG_NO_SYNC))    
  6.         sync();    
  7.     
  8.     if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))    
  9.         remount_ro();    
  10.     
  11.     switch (cmd) {    
  12.         case ANDROID_RB_RESTART://我們一般的重啟就是偉一這個,從上面的代碼有體現    
  13.             ret = reboot(RB_AUTOBOOT);    
  14.             break;    
  15.     
  16.         case ANDROID_RB_POWEROFF://shutdown走的是這兒,其實reboot和shutdown就很相似,從powermanagerservice中才都是call了nativeReboot    
  17.             ret = reboot(RB_POWER_OFF);    
  18.             break;    
  19.     
  20.         case ANDROID_RB_RESTART2://當一開始偉入了recovery時會走這兒    
  21.            ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,    
  22.                            LINUX_REBOOT_CMD_RESTART2, arg);    
  23.             break;    
  24.     
  25.         default:    
  26.             ret = -1;    
  27.     }    
  28.     
  29.     return ret;    
  30. }    
上面的 reboot(RB_AUTOBOOT)又会call到/bionic/libc/unistd/reboot.c

[cpp] 
view plain
copy
print
?  

  1. int reboot (int  mode)    
  2. {    
  3.     return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );    
  4.     //這兒的__reboot和上面的case ANDROID_RB_RESTART2的__reboot就是一樣的    
  5. }    
__reboot就到了/bionic/libc/arch-arm/syscalls/__reboot.S
这是一個内联汇编的函数。
通過swi軟中斷來重啟設備。
到此android部份的代碼重啟流程就大致完成。
其實我們剛才也注意到shutdown和reboot差不多走一樣的路線下來。

[cpp] 
view plain
copy
print
?  

  1. ENTRY(__reboot)    
  2.     .save   {r4, r7}    
  3.     stmfd   sp!, {r4, r7}    
  4.     ldr     r7, =__NR_reboot    
  5.     swi     #0    
  6.     ldmfd   sp!, {r4, r7}    
  7.     movs    r0, r0    
  8.     bxpl    lr    
  9.     b       __set_syscall_errno    
  10. END(__reboot)    

可以看出来,这里将__reboot的实现映射到了__NR_reboot, 

而在bionic/libc/sys/linux-syscalls.h能够找到:

 #define __NR_reboot (__NR_SYSCALL_BASE + 88)

其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,

由此可见,内核中一定有着相同的定义,否则将不能成功调用。

内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,

相关信息完全一致。

已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,

在include/asm-generic/unistd.h中可以找到内核对__NR_reboot的syscall函数映射,即

 

 

[cpp] 
view plain
copy
print
?  

  1. /* kernel/sys.c */   
  2. #define __NR_setpriority 140 __SYSCALL(__NR_setpriority, sys_setpriority)  
  3. #define __NR_getpriority 141 __SYSCALL(__NR_getpriority, sys_getpriority)   
  4. #define __NR_reboot 142 __SYSCALL(__NR_reboot, sys_reboot)  

kernel/sys.c

 

在进入这个文件前,我们先去include/linux/syscalls.h中查看一下sys_reboot的定义:

 

[cpp] 
view plain
copy
print
?  

  1. asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user *arg);  

与__reboot的调用参数一致。 

 

进入sys.c文件后,并没有找到名为sys_reboot的函数,而通过仔细查找,

发现一个很有趣的函数,其定义为

 

[cpp] 
view plain
copy
print
?  

  1. SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),  

 

对比__reboot的参数,能够符合。究竟是不是这个函数?

同样在include/linux/syscalls.h文件中,能够找到这样几个定义:

 

 

[cpp] 
view plain
copy
print
?  

  1. #define SYSCALL_DEFINE1(name, …) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)   
  2. #define SYSCALL_DEFINE2(name, …) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)   
  3. #define SYSCALL_DEFINE3(name, …) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)   
  4. #define SYSCALL_DEFINE4(name, …) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)   
  5. #define SYSCALL_DEFINE5(name, …) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)  
  6.  #define SYSCALL_DEFINE6(name, …) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) …   
  7. #define SYSCALL_DEFINEx(x, sname, …) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) …   
  8. #define __SYSCALL_DEFINEx(x, name, …) \ asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))  

整合后等价于:

 

 

 

[cpp] 
view plain
copy
print
?  

  1. #define SYSCALL_DEFINE4(name, …) \ asmlinkage long sys##_name(__SC_DECL##4(__VA_ARGS__))  

这样就不难看出,

 

 

[cpp] 
view plain
copy
print
?  

  1. SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)  

 

就是sys_reboot,也就是上层调用的__reboot的最终实现。函数实现如下:

4 。内核部分。 kernel/sys.c

其中,

[cpp] 
view plain
copy
print
?  

  1. if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)  
  2. cmd = LINUX_REBOOT_CMD_HALT;  
  3.   
  4. lock_kernel();  
  5. switch (cmd) {  
  6. case LINUX_REBOOT_CMD_RESTART:  
  7. kernel_restart(NULL);  
  8. break;  
  9.   
  10. case LINUX_REBOOT_CMD_CAD_ON:  
  11. C_A_D = 1;  
  12. break;  
  13.   
  14. case LINUX_REBOOT_CMD_CAD_OFF:  
  15. C_A_D = 0;  
  16. break;  
  17.   
  18. case LINUX_REBOOT_CMD_HALT:  
  19. kernel_halt();  
  20. unlock_kernel();  
  21. do_exit(0);  
  22. break;  
  23.   
  24. case LINUX_REBOOT_CMD_POWER_OFF:  
  25. printk(“test powe down in %s(%d)\n”, __FILE__, __LINE__);  
  26. kernel_power_off();  
  27. unlock_kernel();  
  28. do_exit(0);  
  29. break;  

当pm_power_off为空时, 由上层传入的命令LINUX_REBOOT_CMD_POWER_OFF会变为

 

LINUX_REBOOT_CMD_HALT,从而执行kernel_halt()函数。

在该函数中,会执行函数machine_halt()。错误原因就在这儿(pm_power_off为空)。


函数指针pm_power_off是与平台相关的指针, 只是一个函数指针,而且做了全局操作,整个kernel都可以调用它。

以高通msm7x30为例,在arch/arm/mach-msm/pm2.c中对这个函数指针进行了赋值.

添加函数

[cpp] 
view plain
copy
print
?  

  1. static void lpc32xx_power_off(void)  
  2. {  
  3. __raw_writel((0x1<<9),GPIO_P2_OUTP_CLR(GPIO_IOBASE));  
  4. }  

具体平台都不一样,但一般都是拉低power halt的gpio管脚即可。

用到的函数都在<driver/gpio/gpiolib.c>

 

[cpp] 
view plain
copy
print
?  

  1. static int gpiod_request(struct gpio_desc *desc, const char *label);  
  2. static void gpiod_free(struct gpio_desc *desc);  
  3. static int gpiod_direction_input(struct gpio_desc *desc);  
  4. static int gpiod_direction_output(struct gpio_desc *desc, int value);  
  5. static int gpiod_get_direction(const struct gpio_desc *desc);  
  6. static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);  
  7. static int gpiod_get_value_cansleep(const struct gpio_desc *desc);  
  8. static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);  
  9. static int gpiod_get_value(const struct gpio_desc *desc);  
  10. static void gpiod_set_value(struct gpio_desc *desc, int value);  
  11. static int gpiod_cansleep(const struct gpio_desc *desc);  
  12. static int gpiod_to_irq(const struct gpio_desc *desc);  
  13. static int gpiod_export(struct gpio_desc *desc, bool direction_may_change);  
  14. static int gpiod_export_link(struct device *dev, const char *name,  
  15.                               struct gpio_desc *desc);  
  16. static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value);  
  17. static void gpiod_unexport(struct gpio_desc *desc);  

 


gpiod_request   // 一个GPIO引脚
int gpio_direction_output //设置为输出,并初始化值为value.

gpio_set_value //拉低GPIO,下电即可

GPIO操作见参考文章2

 

在ea3250_board_init中添加

[cpp] 
view plain
copy
print
?  

  1. pm_power_off = lpc32xx_power_off;  

编译内核 ,OK

android层可以正常关闭电源了

 

 

***************************************************************************************************************************

参考文章1:

 

Andriod关机&重启分析

 

1jni

frameworks/base/core/jni/android_os_Power.cpp

 

[cpp] 
view plain
copy
print
?  

  1. static void android_os_Power_shutdown(JNIEnv *env, jobject clazz)  
  2. {  
  3.         sync();  
  4. #ifdef HAVE_ANDROID_OS  
  5.         reboot(RB_POWER_OFF);  
  6. #endif  
  7. }  

[cpp] 
view plain
copy
print
?  

  1. static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason)  
  2. {  
  3.     sync();  
  4. #ifdef HAVE_ANDROID_OS  
  5.     if (reason == NULL) {  
  6.         reboot(RB_AUTOBOOT);  
  7.     } else {  
  8.         const char *chars = env->GetStringUTFChars(reason, NULL);  
  9.         __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,  
  10.                  LINUX_REBOOT_CMD_RESTART2, (char*) chars);  
  11.         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.                                                        
  12.     }  
  13.     jniThrowIOException(env, errno);  
  14. #endif  
  15. }  

 

 

 

[cpp] 
view plain
copy
print
?  

  1. static JNINativeMethod method_table[] = {  
  2. ……  
  3.     { “shutdown”“()V”, (void*)android_os_Power_shutdown },  
  4.     { “rebootNative”“(Ljava/lang/String;)V”, (void*)android_os_Power_reboot },  
  5. };  

 

 

其中,RB_POWER_OFF RB_AUTOBOOT 定义bionic/libc/include/sys/reboot.h

中。

 

[cpp] 
view plain
copy
print
?  

  1. #define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART  
  2. #define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT  
  3. #define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF  
  4. ……  

2
、通过
jni
层,最后会执行系统调用
sys_reboot
,即
reboot
。以
imx51
平台为例,
reboot
定义在
kernel_imx/kernel/sys.c

 


if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;

lock_kernel();
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
……

case LINUX_REBOOT_CMD_HALT:
kernel_halt();
unlock_kernel();
do_exit(0);
break;

case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
unlock_kernel();
do_exit(0);
break;

 

其中,函数指针pm_power_off是与平台相关的指针,以imx51平台为例,其定义于kernel_imx/arch/arm/mach-mx5/mx51_baggage.c中。

static void mxc_power_off(void)

{

     ……

/* Set the power gate bits to power down */

         pmic_write_reg(REG_POWER_MISC, (PWGT1SPIEN|PWGT2SPIEN),

                 (PWGT1SPIEN|PWGT2SPIEN));

}

 

另外,在 static void __init mxc_board_init(void) 函数中,有

pm_power_off = mxc_power_off;

另外,在pmic中,也定义了pm_power_off = pmic_power_off;

这是因为imx51支持2中关机方式,具体要看硬件支持。

 

 

3sys_reboot分析

 

 当pm_power_off为空时, 由上层传入的命令LINUX_REBOOT_CMD_POWER_OFF会变为LINUX_REBOOT_CMD_HALT,从而执行kernel_halt()函数。在该函数中,会执行函数machine_halt(),其定义在kernel_imx/arch/arm/kernel/process.c中。

void machine_halt(void)

{

}      

由于该函数为空,因此,当pm_power_off指针为空时,执行关机操作会失败。

pm_power_off不为空时, 上层命令LINUX_REBOOT_CMD_POWER_OFF,执行的函数是kernel_power_off()。在该函数中,会执行函数 machine_power_off,其于machine_halt定义于同一文件中。

void machine_power_off(void)

{

if (pm_power_off)

pm_power_off();

}

因此,只有当pm_power_off指针不为空,且实现代码符合该cpu体系时,andriod才能实现正常的关机。

 

 

4reboot

 

reboot较简单,通过命令LINUX_REBOOT_CMD_RESTART执行kernel_restart(NULL)即可。

 

*****************************************************************************************************************

参考文章2:

 

gpio框架及处理流程分析

 

———————————————————————————————-
gpio作为一种通用的IO接口,使用方法主要如下:
———————————————————————————————-
Gpio:每个 GPIO 都代表一个连接到特定引脚或球栅阵列(BGA)封装中“球珠”的一个位
标准头文件 <linux/gpio.h> [对外接口]
其中根据是否定义CONFIG_GENERIC_GPIO判断系统是否支持gpio
头文件为 <asm/gpio.h>;实现文件为 <driver/gpio/gpiolib.c>

步骤
1. gpio_request(gpio_num, “xx gpio”); 申请GPIO, 返回0为申请成功,否则失败。
2. 设置gpio方向:
int gpio_direction_input(unsigned gpio); //设置为输入
int gpio_direction_output(unsigned gpio, int value); //设置为输出,并初始化值为value.
3. 获取/设置gpio值: int gpio_cansleep(unsigned gpio); 
a.不可睡眠: 
gpio_get_value(unsigned gpio); //返回value
gpio_set_value(unsigned gpio, int value); //设置值
b.可睡眠:(对于有些挂载在I2C,SPI总线上的扩展GPIO,读写操作可能会导致睡眠,因此不能在中断函数中使用。使用下面的函数以区别于正常的GPIO)
int gpio_get_value_cansleep(unsigned gpio); //输入端口:返回零或非零,可能睡眠
void gpio_set_value_cansleep(unsigned gpio, int value); //输出端口:可能睡眠
4. void gpio_free(unsigned gpio); //释放GPIO
5. int gpio_is_valid(int number); //检测此gpio口是否有效
批量初始化方法:
申请:
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
释放:
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));

导出gpio到用户空间:int gpio_export(unsigned gpio, bool direction_may_change);
创建一个sysfs连接到已导出的GPIO节点:
int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
取消导出:void gpio_unexport();

Gpio设置中断:
gpio —> irq int gpio_to_irq(unsigned gpio);
首先应该设置此gpio为输入状态,然后获取对应的中断号(或错误吗)。返回编号调用:
request_irq()和free_irq()。
Irq —> gpio int irq_to_gpio(unsigned irq);
返回gpio编号,再调用gpio_get_value()获取相应的值。(避免使用反向映射,不支持)

—————————————————————————————–
gpiolib.c (gpio框架) drivers/gpio/gpiolib.c + include/asm-generic/gpio.h [对gpio chip接口]
—————————————————————————————–
gpio_chip作为一个接口负责框架层与控制器层的通讯,主要关注点有:
其申请/释放/方向/获取输入/设置输出/转irq/base+ngpio[见第三部分控制器驱动]

在框架层的主要关注点在:
1. 如何分配不同chip的gpio域
2. 如何管理隶属与不同chip的gpio,并反向追溯到chip以调用控制器的具体寄存器操作
3. 统一提前管理了哪些gpio状态以及是否有必要

在gpiochip_add()中是对gpio chip的注册,并插入到框架gpio的管理中,

全局变量 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
gpio_desc作为整个系统的gpio的管理者,主要包含两个成员:chip 与 flags.
flags为框架层对gpio的整体管理标识,起MASK的作用。[有:是否已申请/是否是输出/是否保留等]

插入的规则实现在:gpiochip_find_base(int ngpio)
从后往前遍历全局gpio desc, 只要是非保留gpio且无宿主chip的连续gpio的空间起址作为base, ngpio则依次向下扩展。
简单的初始化:挂上对应的chip,检查是否有设置输入函数,没有则设置 IS_OUT位到flags.
[其中关于gpio的设备树管理暂时不予关注]

request流程: 检查对应gpio的flags是否FLAG_REQUESTED,如果未被request则调用chip的request接口实现芯片级别的调用。
free流程: 确认已经被REQUESTED, 后启用芯片级的free.

从系统gpio接口传递下来的gpio均是以base为基址,而传递到芯片上都是回归到原始gpio号
——————————————————————————————
SC8810 gpio控制器驱动
——————————————————————————————-
这里是整个gpio系统的核心,初步总结需要关注以下几点:
chip支持的gpio section如何划分
gpio如何配置使能即芯片如何管理众多gpio口的多个标识位的功能选项
申请/释放/方向/获取输入/设置输出/转irq/base+ngpio的处理流程及原理
gpio对应irq号的分配及映射规则
配置一个系统gpio需要的必要步骤

section:(GPIO_BASE:0xE0031000/SPRD_MISC_BASE:0xE0037000)
{ (GPIO_BASE + 0*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 1*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 2*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 3*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 4*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 5*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 6*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 7*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 8*0x80), 0x10, GPIO_SECTION_GPIO },

{ (SPRD_MISC_BASE + 0x480), 0x10, GPIO_SECTION_GPIO },
{ (SPRD_MISC_BASE + 0x4c0), 0xe, GPIO_SECTION_GPIO },

当获取一个gpio号后,需要获取的基本信息为:在哪个section/偏移量是多大/是何种芯片gpio
获取方法: 
a.((gpio_id>>4) -1) * 0x80 + (u32) GPIO_BASE;
b.gpio_id & 0xF
c.gpio是在数字芯片上还是模拟芯片上:
#define NR_D_DIE_GPIOS 147
即:芯片上的gpio号小于147即位于数字芯片,否则位于模拟芯片

在一个芯片的整个寄存器内存中,采取以section + 功能的管理方式,也就是说对于同一个gpio的不同功能配置需要通过三个步骤,第一首先
需要找到段寄存器(section的基址);第二步是功能偏移的管理寄存器(功能偏移),第三步是根据段内偏移定位到某一位(bit)来配置。

注:
根据不同类型的gpio类型:
enum gpio_section_type {
GPIO_SECTION_GPI = 0x0,
GPIO_SECTION_GPO,
GPIO_SECTION_GPIO,
GPIO_SECTION_INVALID
};
其相应的功能偏移页不同。

申请:设置其功能寄存器的GPIO_DMSK功能偏移,在对应段内偏移处置位。
释放:清除对应MASK位
输出方向:三个步骤,1.设置GPIO_DIR对应位为1;2.清除GPIO_INEN对应位;3.设置GPIO_DATA对应位为需要输出的值。
输入方向:第一二步相反,无第三步。
设置及获取值:直接设置/读取GPIO_DATA功能寄存器[需要检查一些相关的方向等信息]

to irq
全局的映射数组来管理所有的gpio与irq的映射
static struct gpio_irq_map gpio_irq_table[NR_GPIO_IRQS];
映射规则是:从0-10[仅限映射10个中断号],遍历映射表找到第一个gpio offset相同的表项,返回其对应的irq值。
方向 to gpio 一般kernel不支持使用,但实现原理与上相同。

在gpio芯片管理中,最重要的一块就是irq的管理,包括irq的所有属性管理,如:irq屏蔽使能/触发条件等等。
下一篇笔记将详细描述 kernel irq的管理框架以及gpio的irq分配规则及触发原理

,

———————————————————————————————-
gpio作为一种通用的IO接口,使用方法主要如下:
———————————————————————————————-
Gpio:每个 GPIO 都代表一个连接到特定引脚或球栅阵列(BGA)封装中“球珠”的一个位
标准头文件 <linux/gpio.h> [对外接口]
其中根据是否定义CONFIG_GENERIC_GPIO判断系统是否支持gpio
头文件为 <asm/gpio.h>;实现文件为 <driver/gpio/gpiolib.c>

步骤
1. gpio_request(gpio_num, “xx gpio”); 申请GPIO, 返回0为申请成功,否则失败。
2. 设置gpio方向:
int gpio_direction_input(unsigned gpio); //设置为输入
int gpio_direction_output(unsigned gpio, int value); //设置为输出,并初始化值为value.
3. 获取/设置gpio值: int gpio_cansleep(unsigned gpio); 
a.不可睡眠: 
gpio_get_value(unsigned gpio); //返回value
gpio_set_value(unsigned gpio, int value); //设置值
b.可睡眠:(对于有些挂载在I2C,SPI总线上的扩展GPIO,读写操作可能会导致睡眠,因此不能在中断函数中使用。使用下面的函数以区别于正常的GPIO)
int gpio_get_value_cansleep(unsigned gpio); //输入端口:返回零或非零,可能睡眠
void gpio_set_value_cansleep(unsigned gpio, int value); //输出端口:可能睡眠
4. void gpio_free(unsigned gpio); //释放GPIO
5. int gpio_is_valid(int number); //检测此gpio口是否有效
批量初始化方法:
申请:
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
释放:
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));

导出gpio到用户空间:int gpio_export(unsigned gpio, bool direction_may_change);
创建一个sysfs连接到已导出的GPIO节点:
int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
取消导出:void gpio_unexport();

Gpio设置中断:
gpio —> irq int gpio_to_irq(unsigned gpio);
首先应该设置此gpio为输入状态,然后获取对应的中断号(或错误吗)。返回编号调用:
request_irq()和free_irq()。
Irq —> gpio int irq_to_gpio(unsigned irq);
返回gpio编号,再调用gpio_get_value()获取相应的值。(避免使用反向映射,不支持)

—————————————————————————————–
gpiolib.c (gpio框架) drivers/gpio/gpiolib.c + include/asm-generic/gpio.h [对gpio chip接口]
—————————————————————————————–
gpio_chip作为一个接口负责框架层与控制器层的通讯,主要关注点有:
其申请/释放/方向/获取输入/设置输出/转irq/base+ngpio[见第三部分控制器驱动]

在框架层的主要关注点在:
1. 如何分配不同chip的gpio域
2. 如何管理隶属与不同chip的gpio,并反向追溯到chip以调用控制器的具体寄存器操作
3. 统一提前管理了哪些gpio状态以及是否有必要

在gpiochip_add()中是对gpio chip的注册,并插入到框架gpio的管理中,

全局变量 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
gpio_desc作为整个系统的gpio的管理者,主要包含两个成员:chip 与 flags.
flags为框架层对gpio的整体管理标识,起MASK的作用。[有:是否已申请/是否是输出/是否保留等]

插入的规则实现在:gpiochip_find_base(int ngpio)
从后往前遍历全局gpio desc, 只要是非保留gpio且无宿主chip的连续gpio的空间起址作为base, ngpio则依次向下扩展。
简单的初始化:挂上对应的chip,检查是否有设置输入函数,没有则设置 IS_OUT位到flags.
[其中关于gpio的设备树管理暂时不予关注]

request流程: 检查对应gpio的flags是否FLAG_REQUESTED,如果未被request则调用chip的request接口实现芯片级别的调用。
free流程: 确认已经被REQUESTED, 后启用芯片级的free.

从系统gpio接口传递下来的gpio均是以base为基址,而传递到芯片上都是回归到原始gpio号
——————————————————————————————
SC8810 gpio控制器驱动
——————————————————————————————-
这里是整个gpio系统的核心,初步总结需要关注以下几点:
chip支持的gpio section如何划分
gpio如何配置使能即芯片如何管理众多gpio口的多个标识位的功能选项
申请/释放/方向/获取输入/设置输出/转irq/base+ngpio的处理流程及原理
gpio对应irq号的分配及映射规则
配置一个系统gpio需要的必要步骤

section:(GPIO_BASE:0xE0031000/SPRD_MISC_BASE:0xE0037000)
{ (GPIO_BASE + 0*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 1*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 2*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 3*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 4*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 5*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 6*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 7*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 8*0x80), 0x10, GPIO_SECTION_GPIO },

{ (SPRD_MISC_BASE + 0x480), 0x10, GPIO_SECTION_GPIO },
{ (SPRD_MISC_BASE + 0x4c0), 0xe, GPIO_SECTION_GPIO },

当获取一个gpio号后,需要获取的基本信息为:在哪个section/偏移量是多大/是何种芯片gpio
获取方法: 
a.((gpio_id>>4) -1) * 0x80 + (u32) GPIO_BASE;
b.gpio_id & 0xF
c.gpio是在数字芯片上还是模拟芯片上:
#define NR_D_DIE_GPIOS 147
即:芯片上的gpio号小于147即位于数字芯片,否则位于模拟芯片

在一个芯片的整个寄存器内存中,采取以section + 功能的管理方式,也就是说对于同一个gpio的不同功能配置需要通过三个步骤,第一首先
需要找到段寄存器(section的基址);第二步是功能偏移的管理寄存器(功能偏移),第三步是根据段内偏移定位到某一位(bit)来配置。

注:
根据不同类型的gpio类型:
enum gpio_section_type {
GPIO_SECTION_GPI = 0x0,
GPIO_SECTION_GPO,
GPIO_SECTION_GPIO,
GPIO_SECTION_INVALID
};
其相应的功能偏移页不同。

申请:设置其功能寄存器的GPIO_DMSK功能偏移,在对应段内偏移处置位。
释放:清除对应MASK位
输出方向:三个步骤,1.设置GPIO_DIR对应位为1;2.清除GPIO_INEN对应位;3.设置GPIO_DATA对应位为需要输出的值。
输入方向:第一二步相反,无第三步。
设置及获取值:直接设置/读取GPIO_DATA功能寄存器[需要检查一些相关的方向等信息]

to irq
全局的映射数组来管理所有的gpio与irq的映射
static struct gpio_irq_map gpio_irq_table[NR_GPIO_IRQS];
映射规则是:从0-10[仅限映射10个中断号],遍历映射表找到第一个gpio offset相同的表项,返回其对应的irq值。
方向 to gpio 一般kernel不支持使用,但实现原理与上相同。

在gpio芯片管理中,最重要的一块就是irq的管理,包括irq的所有属性管理,如:irq屏蔽使能/触发条件等等。
下一篇笔记将详细描述 kernel irq的管理框架以及gpio的irq分配规则及触发原理

    原文作者:PowerManagerService
    原文地址: http://www.cnblogs.com/nickleback/articles/3303392.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞