歡迎轉載,務必注明出處: 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_NAME DEVICE_NAME
#define LED_DEVICE_FILE_NAME DEVICE_NAME
#define LED_DEVICE_PROC_NAME DEVICE_NAME
#define LED_DEVICE_CLASS_NAME DEVICE_NAMEstatic int led_gpios[] = {(PAD_GPIO_C + 1),
};#define LED_NUM ARRAY_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"helpThis option enables support for on-chip LED drivers found on RealARMS5P4418.
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
0
root@realarm:/sys/class/real_led/real_led # echo '1' > val
root@realarm:/sys/class/real_led/real_led # cat val
1
root@realarm:/sys/class/real_led/real_led # echo '0' > val
root@realarm:/sys/class/real_led/real_led # cat val
0
root@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_led
root@realarm:/proc # echo '1' > real_led
root@realarm:/proc # echo '0' > real_led
root@realarm:/proc #我這里測試也是沒有問題。
這三種方式測試都沒問題了,那么驅動程序是沒有什么問題了,下一部分是硬件抽象層的說明。
總結
以上是生活随笔 為你收集整理的s5p4418 Android 4.4.2 驱动层 HAL层 服务层 应用层 开发流程记录(一 硬件驱动层) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。