首页 > 代码库 > 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【原创 】