s5p4418 Android 4.4.2 驱动层 HAL层 服务层 应用层 开发流程记录(一 硬件驱动层)

来源:互联网 发布:orange 软件 编辑:程序博客网 时间:2024/04/30 22:09

欢迎转载,务必注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44303069

本文章是记录Android开发中驱动层、HAL层、应用层之间的关系,以及其开发方法,本文将会以实现LED的控制为例来进行记录。

一是可以给以后自己做开发做参考,二是希望可以帮助正在学习的朋友参考。

一般的app不需要我们去关注hal和驱动,但在设计一个硬件系统时,原生的Android并未提供合适的服务,所以我们才需要去了解这个流程。由于也是刚入门,很多还不太懂,朋友们有什么疑问可以留言。


首先需要了解,Android的app想要操作硬件,是什么样的一个流程。一般是这样的,app应用层、服务层、硬件抽象层、底层驱动。

我是从底层到上层来进行学习和测试的。也就是:底层->硬件抽象层->服务层->app。原因是,首先需要确定底层的驱动没有问题,而且底层驱动可以使用Linux的方法来进行测试,一步一步走到上层应用。


驱动代码我就直接贴上来,就不去详细解释里面的含义了,不懂的可以参考罗升阳的《Android系统源码情景分析》的第二章。

代码如下:


#include <linux/kernel.h>#include <linux/module.h>#include <linux/cdev.h> #include <linux/fs.h>#include <linux/types.h>#include <linux/moduleparam.h>#include <linux/slab.h>#include <linux/ioctl.h>#include <linux/cdev.h>#include <linux/delay.h>#include <linux/io.h>#include <linux/gpio.h>#include <mach/platform.h>#include <mach/devices.h>#include <mach/soc.h>#include <mach/gpio.h>#include <linux/uaccess.h>#include <linux/pci.h>#include <linux/proc_fs.h>#define DEVICE_NAME "real_led"#define LED_DEVICE_NODE_NAMEDEVICE_NAME#define LED_DEVICE_FILE_NAMEDEVICE_NAME#define LED_DEVICE_PROC_NAMEDEVICE_NAME#define LED_DEVICE_CLASS_NAMEDEVICE_NAMEstatic int led_gpios[] = {(PAD_GPIO_C + 1),};#define LED_NUMARRAY_SIZE(led_gpios)/*主设备和从设备号变量*/  static int led_major = 0;  static int led_minor = 0;  static struct class* led_class = NULL; /*访问设置属性方法*/  static ssize_t led_val_show(struct device* dev, struct device_attribute* attr,  char* buf);  static ssize_t led_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);    /*定义设备属性*/  static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, led_val_show, led_val_store);  struct leds_dev{struct cdev dev;int led_status;};struct leds_dev *led_dev=NULL;/*打开设备方法*/  static int led_open(struct inode* inode, struct file* filp) {      struct leds_dev* dev;                    /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/      dev = container_of(inode->i_cdev, struct leds_dev, dev);      filp->private_data = dev;          return 0;  }    /*设备文件释放时调用,空实现*/  static int led_release(struct inode* inode, struct file* filp) {      return 0;  }    /*读取设备的寄存器val的值*/  static ssize_t led_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {      ssize_t err = 0;      struct leds_dev* dev = filp->private_data;                    if(count < sizeof(dev->led_status)) {          goto out;      }                /*将寄存器val的值拷贝到用户提供的缓冲区*/      if(copy_to_user(buf, &(dev->led_status), sizeof(dev->led_status))) {          err = -EFAULT;          goto out;      }        err = sizeof(dev->led_status);    out:      return err;  }    /*写设备的寄存器值val*/  static ssize_t led_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {      struct leds_dev* dev = filp->private_data;      ssize_t err = 0;            int i;          if(count != sizeof(dev->led_status)) {          goto out;              }                /*将用户提供的缓冲区的值写到设备寄存器去*/      if(copy_from_user(&(dev->led_status), buf, count)) {          err = -EFAULT;          goto out;      }    for(i=0;i<LED_NUM;i++){if((0x01&(dev->led_status>>i))==1)nxp_soc_gpio_set_out_value(led_gpios[i], 0);elsenxp_soc_gpio_set_out_value(led_gpios[i], 1);}    err = sizeof(dev->led_status);    out:       return err;  } static int led_ioctl(struct file *file, unsigned int cmd, unsigned long num) {//由于开发板只有一个LED,所以这里做个判断,传入的num不等于0,返回错误  if(num != 0)  {  printk("RealARM S5P4418 board only have one led lights.Please check app input parameters.\n");return -EINVAL;  }nxp_soc_gpio_set_out_value(led_gpios[num], cmd);}/**************************************************************************************//*设备文件操作方法表*/  static struct file_operations led_fops = {      .owner = THIS_MODULE,      .open = led_open,      .release = led_release,      .read = led_read,      .write = led_write,     .unlocked_ioctl= led_ioctl,  };  /**************************************************************************************/static ssize_t __led_get_val(struct leds_dev* dev, char* buf) {      int val = 0;                         val = dev->led_status;                        return snprintf(buf, PAGE_SIZE, "%d\n", val);  }    /*把缓冲区buf的值写到设备寄存器val中去,内部使用*/  static ssize_t __led_set_val(struct leds_dev* dev, const char* buf, size_t count) {      int val = 0;              int i;       /*将字符串转换成数字*/              val = simple_strtol(buf, NULL, 10);                  for(i=0;i<LED_NUM;i++){    nxp_soc_gpio_set_out_value(led_gpios[i], val);    }        dev->led_status = val;                return count;  }    /*读取设备属性val*/  static ssize_t led_val_show(struct device* dev, struct device_attribute* attr, char* buf) {      struct leds_dev* hdev = (struct leds_dev*)dev_get_drvdata(dev);                return __led_get_val(hdev, buf);  }    /*写设备属性val*/  static ssize_t led_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {       struct leds_dev* hdev = (struct leds_dev*)dev_get_drvdata(dev);              return __led_set_val(hdev, buf, count);  }  /**************************************************************************************//*读取设备寄存器val的值,保存在page缓冲区中*/  static ssize_t led_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {      if(off > 0) {          *eof = 1;          return 0;      }        return __led_get_val(led_dev, page);  }    /*把缓冲区的值buff保存到设备寄存器val中去*/  static ssize_t led_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {      int err = 0;      char* page = NULL;        if(len > PAGE_SIZE) {          printk(KERN_ALERT"The buff is too large: %lu./n", len);          return -EFAULT;      }        page = (char*)__get_free_page(GFP_KERNEL);      if(!page) {                          printk(KERN_ALERT"Failed to alloc page./n");          return -ENOMEM;      }                /*先把用户提供的缓冲区值拷贝到内核缓冲区中去*/      if(copy_from_user(page, buff, len)) {          printk(KERN_ALERT"Failed to copy buff from user./n");                          err = -EFAULT;          goto out;      }        err = __led_set_val(led_dev, page, len);    out:      free_page((unsigned long)page);      return err;  }    /*创建/proc/led文件*/  static void led_create_proc(void) {      struct proc_dir_entry* entry;            entry = create_proc_entry(DEVICE_NAME, 0, NULL);      if(entry) {      //    entry->owner = THIS_MODULE;          entry->read_proc = led_proc_read;          entry->write_proc = led_proc_write;      }  }    /*删除/proc/led文件*/  static void led_remove_proc(void) {      remove_proc_entry(DEVICE_NAME, NULL);  }  /**************************************************************************************/static int  __led_setup_dev(struct leds_dev* dev) {      int err;      dev_t devno = MKDEV(led_major, led_minor);        memset(dev, 0, sizeof(struct leds_dev));        cdev_init(&(led_dev->dev), &led_fops);      dev->dev.owner = THIS_MODULE;      dev->dev.ops = &led_fops;                /*注册字符设备*/      err = cdev_add(&(dev->dev),devno, 1);      if(err) {          return err;      }                return 0;  }  static int __init real4418_led_dev_init(void) {int ret;int i;unsigned int val;int err = -1;      dev_t dev = 0;  struct device* temp = NULL;     for (i = 0; i < LED_NUM; i++) {ret = gpio_request(led_gpios[i], "LED");if (ret) {printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,led_gpios[i], ret);goto fail;}nxp_soc_gpio_set_io_func(led_gpios[i], 1);     nxp_soc_gpio_set_io_dir(led_gpios[i], 1);nxp_soc_gpio_set_out_value(led_gpios[i], 0);}      /*动态分配主设备和从设备号*/      err = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);      if(err < 0) {          printk(KERN_ALERT"Failed to alloc char dev region./n");          goto fail;      }        led_major = MAJOR(dev);      led_minor = MINOR(dev);                /*分配led设备结构体变量*/      led_dev = kmalloc(sizeof(struct leds_dev), GFP_KERNEL);      if(!led_dev) {          err = -ENOMEM;          printk(KERN_ALERT"Failed to alloc led_dev./n");          goto unregister;      }                /*初始化设备*/      err = __led_setup_dev(led_dev);      if(err) {          printk(KERN_ALERT"Failed to setup dev: %d./n", err);          goto cleanup;      }              /*在/sys/class/目录下创建设备类别目录led*/      led_class = class_create(THIS_MODULE, LED_DEVICE_CLASS_NAME);      if(IS_ERR(led_class)) {          err = PTR_ERR(led_class);          printk(KERN_ALERT"Failed to create led class./n");          goto destroy_cdev;      }                /*在/dev/目录和/sys/class/led目录下分别创建设备文件led*/      temp = device_create(led_class, NULL, dev, "%s", LED_DEVICE_FILE_NAME);      if(IS_ERR(temp)) {          err = PTR_ERR(temp);          printk(KERN_ALERT"Failed to create led device.");          goto destroy_class;      }                /*在/sys/class/led/led目录下创建属性文件val*/      err = device_create_file(temp, &dev_attr_val);      if(err < 0) {          printk(KERN_ALERT"Failed to create attribute val.");                          goto destroy_device;      }        dev_set_drvdata(temp, led_dev);  led_create_proc();      printk(DEVICE_NAME"\tinitialized\n");    return ret;destroy_device:      device_destroy(led_class, dev);    destroy_class:      class_destroy(led_class);    destroy_cdev:      cdev_del(&(led_dev->dev));    cleanup:      kfree(led_dev);    unregister:      unregister_chrdev_region(MKDEV(led_major, led_minor), 1);    fail:  for (; i >=0; i--)gpio_free(led_gpios[i]);return err; }static void __exit real4418_led_dev_exit(void) {int i;dev_t devno = MKDEV(led_major, led_minor);  for (i = 0; i < LED_NUM; i++) {gpio_free(led_gpios[i]);}                    /*删除/proc/led文件*/     led_remove_proc();                /*销毁设备类别和设备*/      if(led_class) {          device_destroy(led_class, MKDEV(led_major, led_minor));          class_destroy(led_class);      }                /*删除字符设备和释放设备内存*/      if(led_dev) {          cdev_del(&(led_dev->dev));          kfree(led_dev);      }                /*释放设备号*/      unregister_chrdev_region(devno, 1);  printk(KERN_ALERT"Destroy led device./n");  }module_init(real4418_led_dev_init);module_exit(real4418_led_dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("sean <wsh_sean@qq.com>");



我的文件名是real4418_leds.c,把该文件放到内核目录/drivers/leds下,并修改当前目录下的kconfig和Makefile文件,分别如下:

kconfig添加配置选项:

config LEDS_REALARMtristate "LED Support for RealARM S5P4418"help  This option enables support for on-chip LED drivers found on RealARM  S5P4418.

Makefile添加下面的代码:
obj-$(CONFIG_LEDS_REALARM)+= real4418_leds.o 
使用make ARCH=arm xconfig命令进行内核配置。如下图所示:


编译内核,下载到开发板启动。


下面是测试上面代码的方法:

需要测试三个部分,传统设备文件接口系统、devfs文件系统接口、proc文件系统接口,这三种接口均可实现对LED的硬件操作,Android部分的hal层使用的是read和write方式。

传统的接口系统需要写Linux应用程序来进行测试,我这里就不再详细写了,可以参考《Android系统源码情景分析》里面的写法,就是使用open、read、write、unlocked_ioctl这些函数。下面是使用ioctl来进行测试的,read、write按照read、write的用法就行了。

/*  ==============================================  Name           : led_test.c  Author         : sean  Date           : 16/3/2015  Description    : s5p4418 led driver test  ==============================================  */     #include <stdio.h>  #include <stdlib.h>  #include <fcntl.h>  #include<sys/ioctl.h>     int main(int argc, char**argv)  {      int turn, index, fd;         if(strcmp(argv[1], "on") == 0)      {          turn = 1;      }      else if(strcmp(argv[1], "off") ==0)      {          turn = 0;      }      else      {          printf("Usage: led_test on|off1|2|3|4\n");          exit(1);      }         //打开LED设备      fd = open("/dev/real_led", 0);         if(fd < 0)      {          printf("Open Led DeviceFaild!\n");          exit(1);      }     printf("Open Led Device success!\n");    //IO控制      ioctl(fd, turn, 1);         //关闭LED设备      close(fd);         return 0;  }  

devfs文件接口系统的测试:

启动板子,进入到控制台,进入到/sys/class/real_led/real_led/目录,如果可以进入,那么说明我们led的devfs文件系统注册成功了,反之。

使用下面所示的方法进行测试:

root@realarm:/sys/class/real_led/real_led # cat val                            0root@realarm:/sys/class/real_led/real_led # echo '1' > val                     root@realarm:/sys/class/real_led/real_led # cat val                            1root@realarm:/sys/class/real_led/real_led # echo '0' > val                     root@realarm:/sys/class/real_led/real_led # cat val                            0root@realarm:/sys/class/real_led/real_led # 
当向val写入1时,观察LED是否被点亮了,反之。我这里测试是没有问题的。


proc文件系统的测试:

进入到/proc目录,首先使用ls real_led检查是否有real_led这个文件,然后使用echo '1' > real_led和echo '0' > real_led来测试led。

如下:

root@realarm:/proc # ls real_led                                               real_ledroot@realarm:/proc # echo '1' > real_led                                       root@realarm:/proc # echo '0' > real_led                                       root@realarm:/proc #
我这里测试也是没有问题。


这三种方式测试都没问题了,那么驱动程序是没有什么问题了,下一部分是硬件抽象层的说明。


0 0