本人才疏浅学,写一篇文档总结自己在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 @Override 22 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 @Override 42 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 @Override 61 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 <RelativeLayout 11 android:layout_width="394dp" 12 android:layout_height="520dp" 13 tools:layout_editor_absoluteX="-5dp" 14 tools:layout_editor_absoluteY="-10dp"> 15 16 <Button 17 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 <Button 27 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 else 136 { 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 class 163 ** param2: 父类, 一般为NULL 164 ** 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/led1 178 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.cpp 10 11 LOCAL_SHARED_LIBRARIES := 12 libutils liblog 13 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 else 137 { 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 class 164 ** param2: 父类, 一般为NULL 165 ** 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/led1 179 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即可。
时间: 2024-12-28 11:04:58