android驱动学习---led实验
========================
驱动:
内核:android-kernel 2.6.36 (必须对应你的板子上内核,不然会出现insmod错误)
目的:通过android应用层用户空间访问内核空间驱动程序。
实验:Button控件,点亮和熄灭LED。
注明:android应用层访问驱动,一般有2种方法来访问到硬件驱动程序。
这里讲解个人看法,各位别介意。
1: 应用层 ---> framwork层JNI ---> 驱动c
2: 应用层 ---> framwork层JNI ---> 硬件抽象层HAL ----> 驱动c
2种方法,各有各的好处,第1种,方便与驱动调试实验,只要编译好ko文件和libxxx.so文件,放入小机就可以立即调试了。
第2种JNI方法有些改变和增加了HAl,对驱动有了封装,简单来讲,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的硬件抽象层中。不要误会android把驱动分成两半了,其实android只是在驱动的调用接口上在增加一层对应接口功能来让framwork层JNI来调用。比如,驱动有read接口,HAl层就是封装read接口,做一个 hal_read接口,里面调用read接口,然后把hal_read接口给JNI调用。
明白了吗?这方面有人讲得非常好,请点击此:
http://blog.csdn.net/luoshengyang/article/details/6575988
好处就是对对厂家来说能把商业秘密隐藏起来,我们做驱动实验的话,操作会极其复杂,不过对理解android整个系统都是极其用用的,因为它从下到上涉及到了android系统的硬件驱动层,硬件抽象层,运行时库和应用程序框架层等等。
这里目前只将第1种方法的实现:在此之前,请大家了解下JNI的编程方法,JNI是一种为JAVA和C、C++之间能互相访问所提供的编程接口。推荐此教材(见附件):
===============================================================
驱动:
#include <linux/module.h> /* For module specific items */#include <linux/moduleparam.h> /* For new moduleparam's */#include <linux/types.h> /* For standard types (like size_t) */#include <linux/errno.h> /* For the -ENODEV/... values */#include <linux/kernel.h> /* For printk/panic/... */#include <linux/fs.h> /* For file operations */^M#include <linux/ioport.h> /* For io-port access */#include <linux/platform_device.h> /* For platform_driver framework */#include <linux/init.h> /* For __init/__exit/... */#include <linux/uaccess.h> /* For copy_to_user/put_user/... */#include <linux/io.h> /* For inb/outb/... */#include <linux/gpio.h>#include <linux/device.h>#include <linux/cdev.h>#include <linux/slab.h> /*kamlloc *///#include <asm-generic/ioctl.h> //ioctl 命令码建议规范化。#define CMD_FLAG 'i'#define LED_ON_IOR(CMD_FLAG,0x00000000,__u32) #define LED_OFF_IOR(CMD_FLAG,0x00000001,__u32)static int major =0;static struct class *led_class;struct cdev_led {struct cdev cdev;}; //建议用2.6的注册方法,2.4的已经离我们越来越远了。struct cdev_led *led_dev;static int led_ioctl(struct file* filp,unsigned int cmd,unsigned long argv){printk(KERN_INFO "entry kernel.... \n");switch(cmd){case LED_ON:{gpio_set_value(S3C64XX_GPK(4),0); //LED1 亮 gpkdat[4]printk(KERN_INFO "led on \n");break;}case LED_OFF:{gpio_set_value(S3C64XX_GPK(4),1); //LED1 灭 gpkdat[4]printk(KERN_INFO "led off \n");break;}default:return -EINVAL;}return 0;}//openstatic int led_open(struct inode* i_node,struct file* filp){printk(KERN_INFO "open init.... \n");int err;err = gpio_request(S3C64XX_GPK(4),"led1"); //请求gpkcon0[4]控制权。if(err<0){printk(KERN_INFO "gpio request faile \n");return err;}gpio_direction_output(S3C64XX_GPK(4),1); // 设置gpkcon0[4] 为输出模式return 0;}//closestatic void led_close(struct inode* i_node,struct file* filp){printk(KERN_INFO "close init \n");gpio_free(S3C64XX_GPK(4)); //释放return ;}/* file operations */struct file_operations fops={ .owner = THIS_MODULE,.open= led_open,.unlocked_ioctl= led_ioctl, // 特别注意从2.6.36以后ioctl已经移除,内核里面用unlocked_ioctl和compat_ioctl. 应用层不变,仍是ioctl调用。.release= led_close,};static int __init led_init(void){printk(KERN_INFO "init .... \n");dev_t dev_no;int result,err;err = alloc_chrdev_region(&dev_no,0,1,"my_led"); //动态申请设备号if(err<0){printk(KERN_INFO "ERROR\n");return err;}major = MAJOR(dev_no);led_dev = kmalloc(sizeof(struct cdev_led),GFP_KERNEL);if(!led_dev){result = -ENOMEM;goto fail_malloc;}memset(led_dev,0,sizeof(led_dev));cdev_init(&led_dev->cdev,&fops); // 初始化cdevled_dev->cdev.owner = THIS_MODULE;result = cdev_add(&led_dev->cdev,dev_no,1); //加载设备if(result <0){printk(KERN_INFO "error\n");goto fail_add;}led_class = class_create(THIS_MODULE,"myled"); //在sys/class下创建sysfs文件device_create(led_class,NULL,MKDEV(major,0),NULL,"myled"); //动态创建设备文件 /dev/myled, 以后不用手动创建了return 0;fail_add:kfree(led_dev);fail_malloc:unregister_chrdev_region(dev_no,1);return result;}static void __exit led_exit(void){dev_t dev_no=MKDEV(major,0);unregister_chrdev_region(dev_no,1);cdev_del(&led_dev->cdev);kfree(led_dev);device_destroy(led_class,dev_no);class_destroy(led_class); printk(KERN_INFO "exit........ \n");}module_init(led_init);module_exit(led_exit);MODULE_AUTHOR("koliy <xxxx@163.com>");MODULE_DESCRIPTION("ARM test led");MODULE_LICENSE("GPL");KERN_DIR = /android/linux-2.6.36-androidall:make -C $(KERN_DIR) M=`pwd` modulesclean:make -C $(KERN_DIR) M=`pwd` cleanobj-m+= android_led.o
public class LedControl extends Activity implements OnClickListener {private Button led1_on;private Button led1_off;public int led_on = 1;public int led_off = 2; public int fd =0;static {try{Log.i("JNI","try to load libled.so");System.loadLibrary("led"); //加载本地库,也就是JNI生成的libxxx.so文件,下面再说。}catch (UnsatisfiedLinkError ule){Log.e("JNI","WARNING: Could not load libled.so");}}//JNI 接口,为JAVA能跟C 交互。Java 怎么使用这些接口,请了解下资料。这点很重要,这里只是简单的方法。public static native int openled(); public static native void closeled();public static native int ioctl(int led_num,int on_off); /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); fd =this.openled(); // 调用JNI 接口来打开/dev/myled 设备 ,注意前面要加个对象。 if(fd<0) { setTitle("open device faile!"); finish(); }else{ setTitle("open device success!"); } findById(); setOnClickListener(); } private void findById(){ led1_on = (Button)findViewById(R.id.bt_led1); led1_off=(Button)findViewById(R.id.bt_led2); } private void setOnClickListener(){ led1_on.setOnClickListener(this); led1_off.setOnClickListener(this); }@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch(v.getId()){case R.id.bt_led1: {this.ioctl(1,led_on); // led1 onbreak;}case R.id.bt_led2:{this.ioctl(1,led_off); // led1 offbreak;}}} protected void onDestroy(){super.onDestroy();this.closeled();} }布局: <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" ><TextView android:id="@+id/position"android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="25sp" android:text=" led " /><Button android:id="@+id/bt_led1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp"android:text="LED1 on"android:layout_toLeftOf="@+id/position"android:layout_centerHorizontal="true"android:layout_alignTop="@+id/position"/><Button android:id="@+id/bt_led2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp"android:text="LED1 off"android:layout_toRightOf="@+id/position"android:layout_alignTop="@+id/position"/></RelativeLayout>#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<errno.h>#include<unistd.h>#include<sys/ioctl.h>#include<jni.h> // 一定要包含此文件#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include "android/log.h"//驱动里的命令码.#define CMD_FLAG 'i'#define LED_ON_IOR(CMD_FLAG,0x00000000,__u32) #define LED_OFF_IOR(CMD_FLAG,0x00000001,__u32)#define DEVICE_NAME "/dev/myled"int fd ;static const char *TAG="led";#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)/* * Class: Linuxc * Method: openled* Signature: ()I */ JNIEXPORT Jint JNICALL Java_tw_com_mini6410_led_LedControl_openled(JNIEnv* env, jclass mc){fd = open(DEVICE_NAME,O_RDWR);if(fd<0){LOGI("don't open dev");return -1;}elseLOGI("open success");return fd;}/* * Class: Linuxc * Method: clsoeled* Signature: ()V*/JNIEXPORT void JNICALL Java_tw_com_mini6410_led_LedControl_closeled(JNIEnv* env, jclass mc){LOGI("dev close");close(fd);}JNIEXPORT jint JNICALL Java_tw_com_mini6410_led_LedControl_ioctl(JNIEnv* env,jclass mc, jint a, jint b){ int tmp = b;if(tmp==1){ //int err= write(fd,&tmp,sizeof(tmp));LOGI("led on");ioctl(fd,LED_ON,NULL);}else{//int err= write(fd,&tmp,sizeof(tmp));LOGI("led1 off");ioctl(fd,LED_OFF,NULL);}return 0;}LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)TARGET_PLATFORM := android-3LOCAL_MODULE := led //模块名 libled.soLOCAL_SRC_FILES := led.c //要编译的文件。LOCAL_LDLIBS := -lloginclude $(BUILD_SHARED_LIBRARY)