首页 > 代码库 > Android字符设备驱动开发基于高通msm8916【原创 】
Android字符设备驱动开发基于高通msm8916【原创 】
本人才疏浅学,写一篇文档总结自己在msm8916平台上移植自己编写的简单的字符设备驱动开发的整个流程。这个小项目的主要功能是开发一个简单的APP,APP通过JNI去调用位于kernel的字符设备驱动。
APP的设计,开发平台Android Studio
主要的文件是下面的三个文件:
MainActivity.java文件的内容如下:
1 package com.example.administrator.myled; 2 3 import android.nfc.Tag; 4 import android.support.v7.app.AppCompatActivity; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.View; 8 import android.widget.Button; 9 import android.widget.Toast;10 11 12 import com.zbahuang.led.lowlevel.LedNative;13 14 public class MainActivity extends AppCompatActivity implements View.OnClickListener {15 private final static String TAG = "zbzhuang";16 Button btn_led_on;17 Button btn_led_off;18 LedNative myled;19 20 21 @Override22 protected void onCreate(Bundle savedInstanceState) {23 super.onCreate(savedInstanceState);24 setContentView(R.layout.activity_main);25 26 initUI();27 28 myled = new LedNative();29 myled.openDev();30 Log.d(TAG,"app:open Dev");31 }32 33 private void initUI() {34 btn_led_on = (Button) findViewById(R.id.btn_led_on);35 btn_led_on.setOnClickListener(this);36 37 btn_led_off = (Button) findViewById(R.id.btn_led_off);38 btn_led_off.setOnClickListener(this);39 }40 41 @Override42 public void onClick(View v) {43 switch (v.getId()){44 case R.id.btn_led_on:45 Toast.makeText(MainActivity.this,"拉灯-->",Toast.LENGTH_SHORT).show();46 Log.d(TAG,"app:LED on");47 myled.devOn();48 break;49 case R.id.btn_led_off:50 Toast.makeText(MainActivity.this,"灭灯-->",Toast.LENGTH_SHORT).show();51 Log.d(TAG,"app:LED off");52 myled.devOff();53 break;54 default:55 break;56 }57 58 }59 60 @Override61 protected void onDestroy() {62 super.onDestroy();63 myled.closeDev();64 Log.d(TAG,"app:close Dev");65 }66 }
LedNative.java文件的内容如下:
在这个文件中所声明的方法是在jni中实现的啊。使用了特殊的关键字表示该方法是在JNI当中实现的啊。
1 package com.zbahuang.led.lowlevel; 2 3 /** 4 * Created by Administrator on 2017/3/29 0029. 5 */ 6 7 public class LedNative { 8 9 static {10 System.loadLibrary("led_jni");11 }12 13 public native int openDev();14 public native int devOn();15 public native int devOff();16 public native int closeDev();17 }18 package com.zbahuang.led.lowlevel;19 20 /**21 * Created by Administrator on 2017/3/29 0029.22 */23 24 public class LedNative {25 26 static {27 System.loadLibrary("led_jni");28 }29 30 public native int openDev();31 public native int devOn();32 public native int devOff();33 public native int closeDev();34 }
activity_main.xml文件的内容如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context="com.example.administrator.myled.MainActivity"> 8 9 10 <RelativeLayout11 android:layout_width="394dp"12 android:layout_height="520dp"13 tools:layout_editor_absoluteX="-5dp"14 tools:layout_editor_absoluteY="-10dp">15 16 <Button17 android:id="@+id/btn_led_on"18 android:layout_width="wrap_content"19 android:layout_height="wrap_content"20 android:layout_alignParentStart="true"21 android:layout_alignParentTop="true"22 android:layout_marginStart="56dp"23 android:layout_marginTop="109dp"24 android:text="拉灯" />25 26 <Button27 android:id="@+id/btn_led_off"28 android:layout_width="wrap_content"29 android:layout_height="wrap_content"30 android:layout_alignBaseline="@+id/btn_led_on"31 android:layout_alignBottom="@+id/btn_led_on"32 android:layout_marginStart="81dp"33 android:layout_toEndOf="@+id/btn_led_on"34 android:text="灭灯" />35 </RelativeLayout>36 </android.support.constraint.ConstraintLayout>
JNI的文件:
led_jni.cpp
1 /*1. 头文件*/ 2 #include<linux/init.h> 3 #include<linux/module.h> 4 #include<linux/fs.h> 5 #include<linux/device.h> 6 #include<linux/slab.h> 7 #include<linux/cdev.h> 8 #include<asm/uaccess.h> 9 #include<asm/io.h> 10 11 static unsigned int led_major=0; 12 volatile unsigned long *gpc0con = NULL; 13 volatile unsigned long *gpc0dat = NULL; 14 15 struct led_device{ 16 struct class *led_class ; //表示一类设备, 存储某些信息 17 struct device *led_device ; //表示一个设备 18 struct cdev *led_cdev; 19 unsigned int val; 20 21 }; 22 23 struct led_device *s5pv_led_dev; 24 25 26 /*可用于查询LED的状态*/ 27 static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *opps) 28 { 29 int ret; 30 31 ret = copy_to_user(buf, &s5pv_led_dev->val, count); 32 if(ret>0) 33 { 34 printk(KERN_ERR "zbzhuang### copy to user failed!\n"); 35 return ret; 36 } 37 38 printk(KERN_INFO "zbzhuang### val=%d\n", s5pv_led_dev->val); 39 40 return ret?0:count; 41 } 42 static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *opps) 43 { 44 int ret; 45 46 /*拷贝成功,返回0; 拷贝失败, 返回没有被拷贝的字节数*/ 47 ret = copy_from_user(&s5pv_led_dev->val, buf, count); 48 if(ret>0) 49 { 50 printk(KERN_ERR "zbzhuang### copy from user failed!\n"); 51 return ret; 52 } 53 54 if(s5pv_led_dev->val) 55 { 56 /*点亮LED*/ 57 //*gpc0dat |= ((0x1<<3)|(0x1<<4)); 58 printk(KERN_ERR "zbzhuang### led on\n"); 59 } 60 else 61 { 62 /*熄灭LED*/ 63 // *gpc0dat &= ~((0x1<<3)|(0x1<<4)); 64 printk(KERN_ERR "zbzhuang### led off\n"); 65 } 66 67 return ret?0:count; 68 } 69 70 71 static int led_open(struct inode *inode, struct file *file) 72 { 73 #if 0 74 /*1. 将物理地址映射为虚拟地址*/ 75 gpc0con = ioremap(0xE0200060, 8); 76 gpc0dat = gpc0con +1; 77 78 /*2. 初始化GPC0_3,4引脚功能为输出*/ 79 *gpc0con &= ~((0xf<<12)|(0xf<<16)); 80 *gpc0con |= ((0x1<<12)|(0x1<<16)); 81 #endif 82 printk(KERN_ERR "zbzhuang### -----------%s-------------\n",__FUNCTION__); 83 84 return 0; 85 } 86 87 static int led_close(struct inode *inode, struct file *file) 88 { 89 #if 0 90 91 *gpc0con &= ~((0xf<<12)|(0xf<<16)); 92 iounmap(gpc0con); 93 #endif 94 printk(KERN_ERR "zbzhuang### ------------%s-----------------\n",__FUNCTION__); 95 96 return 0; 97 98 } 99 100 /*硬件操作方法*/101 struct file_operations led_fops={102 .owner = THIS_MODULE, //当前模块所用103 .open = led_open,104 .write = led_write,105 .read = led_read,106 .release = led_close,107 108 };109 110 static void setup_led_cdev(void)111 {112 /*1. 为cdev结构体分配空间*/113 s5pv_led_dev->led_cdev = cdev_alloc();114 115 /*2. 初始化cdev结构体*/116 cdev_init(s5pv_led_dev->led_cdev, &led_fops);117 118 /*3. 注册cdev,加载到内核哈希表中*/119 cdev_add(s5pv_led_dev->led_cdev, MKDEV(led_major, 0), 1);120 121 }122 123 /*2. 实现模块加载函数*/124 static int __init led_init(void)125 {126 dev_t devno;127 int ret;128 /*1. 新的申请主设备号的方法*/129 if(led_major)130 {131 /*静态申请*/132 devno = MKDEV(led_major, 0);133 register_chrdev_region(devno, 1, "led");134 }135 else136 {137 /*动态申请*/138 alloc_chrdev_region(&devno, 0, 1, "led");139 led_major = MAJOR(devno);140 }141 142 /*2. 为本地结构体分配空间*/143 144 /*145 ** param1: 大小146 ** param2: 标号: GFP_KERNEL--->表示如果分配不成功,则休眠147 */148 s5pv_led_dev = kmalloc(sizeof(struct led_device), GFP_KERNEL);149 if (!s5pv_led_dev)150 {151 printk(KERN_ERR "zbzhuang NO memory for malloc!\n");152 ret = -ENOMEM;153 goto out_err_1;154 }155 156 /*3. 构建struct cdev结构体*/157 setup_led_cdev();158 159 160 /*4. 创建设备文件*/161 /*162 ** param1: struct class163 ** param2: 父类, 一般为NULL164 ** param3: dev_t ---> 表示一个设备号, 是一个无符号32位整形165 ** 其中高12位为主设备号, 低20为次设备号166 ** 如何操作设备号: MKDEV(major, minor)根据主设备号和次设备号组成一个设备号167 */ 168 169 s5pv_led_dev->led_class = class_create(THIS_MODULE, "led_class");170 if (IS_ERR(s5pv_led_dev->led_class)) {171 printk(KERN_ERR "zbzhuang class_create() failed for led_class\n");172 ret = -EINVAL;173 goto out_err_2;174 }175 176 177 s5pv_led_dev->led_device = device_create(s5pv_led_dev->led_class, NULL, MKDEV(led_major, 0), NULL, "led1"); // 创建设备文件/dev/led1178 if (IS_ERR(s5pv_led_dev->led_device)) {179 printk(KERN_ERR "zbzhuang device_create failed for led_device\n");180 ret = -ENODEV;181 goto out_err_3;182 }183 184 return 0;185 186 out_err_3:187 class_destroy(s5pv_led_dev->led_class);188 189 out_err_2:190 cdev_del(s5pv_led_dev->led_cdev);191 kfree(s5pv_led_dev);192 193 out_err_1:194 unregister_chrdev_region(MKDEV(led_major, 0), 1);195 return ret;196 197 }198 199 /*3. 实现模块卸载函数*/200 static void __exit led_exit(void)201 {202 unregister_chrdev_region(MKDEV(led_major, 0), 1);203 device_destroy(s5pv_led_dev->led_class, MKDEV(led_major, 0));204 class_destroy(s5pv_led_dev->led_class);205 cdev_del(s5pv_led_dev->led_cdev);206 kfree(s5pv_led_dev);207 }208 209 /*4. 模块许可声明*/210 module_init(led_init);211 module_exit(led_exit);212 MODULE_LICENSE("GPL");
Android.mk
1 LOCAL_PATH:= $(call my-dir) 2 include $(CLEAR_VARS) 3 4 LOCAL_MODULE_TAGS := optional 5 6 LOCAL_MODULE:= libled_jni 7 8 LOCAL_SRC_FILES:= 9 led_jni.cpp10 11 LOCAL_SHARED_LIBRARIES := 12 libutils liblog13 14 LOCAL_C_INCLUDES += 15 $(JNI_H_INCLUDE)16 17 include $(BUILD_SHARED_LIBRARY)
执行: mmm mytest/led_jni/ 之后会生成动态库放在 out/target/product/msm8916_64/obj/lib/libled_jni.so
将这个库推送到平板电脑就可以通过这个库去调用驱动。
内核的驱动文件:kernel/drivers/input/misc/led.c
1 /*1. 头文件*/ 2 #include<linux/init.h> 3 #include<linux/module.h> 4 #include<linux/fs.h> 5 #include<linux/device.h> 6 #include<linux/slab.h> 7 #include<linux/cdev.h> 8 #include<asm/uaccess.h> 9 #include<asm/io.h> 10 11 static unsigned int led_major=0; 12 volatile unsigned long *gpc0con = NULL; 13 volatile unsigned long *gpc0dat = NULL; 14 15 struct led_device{ 16 struct class *led_class ; //表示一类设备, 存储某些信息 17 struct device *led_device ; //表示一个设备 18 struct cdev *led_cdev; 19 unsigned int val; 20 21 }; 22 23 struct led_device *s5pv_led_dev; 24 25 26 /*可用于查询LED的状态*/ 27 static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *opps) 28 { 29 int ret; 30 31 ret = copy_to_user(buf, &s5pv_led_dev->val, count); 32 if(ret>0) 33 { 34 printk(KERN_ERR "zbzhuang### copy to user failed!\n"); 35 return ret; 36 } 37 38 printk(KERN_INFO "zbzhuang### val=%d\n", s5pv_led_dev->val); 39 40 return ret?0:count; 41 } 42 static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *opps) 43 { 44 int ret; 45 46 /*拷贝成功,返回0; 拷贝失败, 返回没有被拷贝的字节数*/ 47 ret = copy_from_user(&s5pv_led_dev->val, buf, count); 48 if(ret>0) 49 { 50 printk(KERN_ERR "zbzhuang### copy from user failed!\n"); 51 return ret; 52 } 53 54 if(s5pv_led_dev->val) 55 { 56 /*点亮LED*/ 57 //*gpc0dat |= ((0x1<<3)|(0x1<<4)); 58 printk(KERN_ERR "zbzhuang### led on\n"); 59 } 60 else 61 { 62 /*熄灭LED*/ 63 // *gpc0dat &= ~((0x1<<3)|(0x1<<4)); 64 printk(KERN_ERR "zbzhuang### led off\n"); 65 } 66 67 return ret?0:count; 68 } 69 70 71 72 static int led_open(struct inode *inode, struct file *file) 73 { 74 #if 0 75 /*1. 将物理地址映射为虚拟地址*/ 76 gpc0con = ioremap(0xE0200060, 8); 77 gpc0dat = gpc0con +1; 78 79 /*2. 初始化GPC0_3,4引脚功能为输出*/ 80 *gpc0con &= ~((0xf<<12)|(0xf<<16)); 81 *gpc0con |= ((0x1<<12)|(0x1<<16)); 82 #endif 83 printk(KERN_ERR "zbzhuang### -----------%s-------------\n",__FUNCTION__); 84 85 return 0; 86 } 87 88 static int led_close(struct inode *inode, struct file *file) 89 { 90 #if 0 91 92 *gpc0con &= ~((0xf<<12)|(0xf<<16)); 93 iounmap(gpc0con); 94 #endif 95 printk(KERN_ERR "zbzhuang### ------------%s-----------------\n",__FUNCTION__); 96 97 return 0; 98 99 }100 101 /*硬件操作方法*/102 struct file_operations led_fops={103 .owner = THIS_MODULE, //当前模块所用104 .open = led_open,105 .write = led_write,106 .read = led_read,107 .release = led_close,108 109 };110 111 static void setup_led_cdev(void)112 {113 /*1. 为cdev结构体分配空间*/114 s5pv_led_dev->led_cdev = cdev_alloc();115 116 /*2. 初始化cdev结构体*/117 cdev_init(s5pv_led_dev->led_cdev, &led_fops);118 119 /*3. 注册cdev,加载到内核哈希表中*/120 cdev_add(s5pv_led_dev->led_cdev, MKDEV(led_major, 0), 1);121 122 }123 124 /*2. 实现模块加载函数*/125 static int __init led_init(void)126 {127 dev_t devno;128 int ret;129 /*1. 新的申请主设备号的方法*/130 if(led_major)131 {132 /*静态申请*/133 devno = MKDEV(led_major, 0);134 register_chrdev_region(devno, 1, "led");135 }136 else137 {138 /*动态申请*/139 alloc_chrdev_region(&devno, 0, 1, "led");140 led_major = MAJOR(devno);141 }142 143 /*2. 为本地结构体分配空间*/144 145 /*146 ** param1: 大小147 ** param2: 标号: GFP_KERNEL--->表示如果分配不成功,则休眠148 */149 s5pv_led_dev = kmalloc(sizeof(struct led_device), GFP_KERNEL);150 if (!s5pv_led_dev)151 {152 printk(KERN_ERR "zbzhuang NO memory for malloc!\n");153 ret = -ENOMEM;154 goto out_err_1;155 }156 157 /*3. 构建struct cdev结构体*/158 setup_led_cdev();159 160 161 /*4. 创建设备文件*/162 /*163 ** param1: struct class164 ** param2: 父类, 一般为NULL165 ** param3: dev_t ---> 表示一个设备号, 是一个无符号32位整形166 ** 其中高12位为主设备号, 低20为次设备号167 ** 如何操作设备号: MKDEV(major, minor)根据主设备号和次设备号组成一个设备号168 */ 169 170 s5pv_led_dev->led_class = class_create(THIS_MODULE, "led_class");171 if (IS_ERR(s5pv_led_dev->led_class)) {172 printk(KERN_ERR "zbzhuang class_create() failed for led_class\n");173 ret = -EINVAL;174 goto out_err_2;175 }176 177 178 s5pv_led_dev->led_device = device_create(s5pv_led_dev->led_class, NULL, MKDEV(led_major, 0), NULL, "led1"); // 创建设备文件/dev/led1179 if (IS_ERR(s5pv_led_dev->led_device)) {180 printk(KERN_ERR "zbzhuang device_create failed for led_device\n");181 ret = -ENODEV;182 goto out_err_3;183 }184 185 return 0;186 187 out_err_3:188 class_destroy(s5pv_led_dev->led_class);189 190 out_err_2:191 cdev_del(s5pv_led_dev->led_cdev);192 kfree(s5pv_led_dev);193 194 out_err_1:195 unregister_chrdev_region(MKDEV(led_major, 0), 1);196 return ret;197 198 }199 200 /*3. 实现模块卸载函数*/201 static void __exit led_exit(void)202 {203 unregister_chrdev_region(MKDEV(led_major, 0), 1);204 device_destroy(s5pv_led_dev->led_class, MKDEV(led_major, 0));205 class_destroy(s5pv_led_dev->led_class);206 cdev_del(s5pv_led_dev->led_cdev);207 kfree(s5pv_led_dev);208 }209 210 /*4. 模块许可声明*/211 module_init(led_init);212 module_exit(led_exit);213 MODULE_LICENSE("GPL");
修改kconfig和makefile,在内核当中添加:
makefile
kconfig:
修改这个文件将驱动编译进内核:kernel/arch/arm64/configs/msm_defconfig 与msm-perf_deconfig
之后使用adb工具将重新编译的boot.imge镜像重新烧录就可以了啊。
看看log的输出验证一下结果:
android studio查看log的结果:
adb查看log的结果:
从app到jni到kernel,整个调用的过程成功。
如果出现打开设备失败的话在system/core/rootdir/uevent.rc中添加设备节点的权限777即可。
Android字符设备驱动开发基于高通msm8916【原创 】