最近开始接触Android底层的开发,这里面将记录开发一个驱动以及到测试的全过程。
首先,需要搭建Android的开发环境。我这里采用的是Ubuntu 11.04 64bit的系统,里面使用gcc-4.4,Android 4.0.4源码,硬件是S5PV210。
搭建环境这一步就不说了。
其次,编译好u-boot、kernel和ICS。
最后,开始确定要编写什么驱动。
由于我这个210板子上没有可控制的GPIO设备,所以这里硬件部分就不涉及了,直接编写一个驱动,实现读写接口。
在开始之前,我们先来看看由驱动到接口、应用是怎么一个流程。
从这个图中可以看出我们需要 实现HAL层,实现Framework层,以及编写对应的程序。
在这里,有必要提一下:在Android下有以下两种访问HAL的方式:
1、Android 的 app 直接通过service 调用.so格式的JNI :此方法比较简单高效,但是不正规。
2、经过Manager 调用 Service :此方法实现起来比较复杂,但更符合目前的 Android框架。在此方法中 Manager 进程和 Service(JAVA) 进程需要通过进程通信的方式实现通信。
下面正式开始:
一、HAL层驱动的实现
1、添加 ttt 驱动
首先,打开终端,进入到kernel源码目录下的drivers目录,如:
- brantyou@brantyou-ubuntu:~/workspace$ cd samsung_android_kernel_3.0/drivers/
- brantyou@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers$ ls
- accessibility clk gpio Kconfig misc pcmcia sfi usb
- acpi clocksource gpu Kconfig~ mmc platform sh uwb
- amba connector hello leds modules.builtin pnp sn vhost
- ata cpufreq hid lguest modules.order power spi video
- atm cpuidle hwmon macintosh mtd pps ssb virtio
- auxdisplay crypto hwspinlock Makefile net ps3 staging vlynq
- base dca i2c Makefile~ nfc ptp switch w1
- bcma dio ide mca nubus rapidio target watchdog
- block dma idle md of regulator tc xen
- bluetooth edac ieee802154 media oprofile rtc telephony zorro
- built-in.o eisa infiniband memstick parisc s390 thermal
- cdrom firewire input message parport sbus tty
- char firmware isdn mfd pci scsi uio
- brantyou@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers$
切换成超级用户权限:
- brantyou@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers$ sudo -s
- [sudo] password for brantyou:
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers#
创建 ttt 驱动目录:
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers# mkdir ttt
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers# ls
- accessibility clk gpio Kconfig misc pcmcia sfi uio
- acpi clocksource gpu Kconfig~ mmc platform sh usb
- amba connector hello leds modules.builtin pnp sn uwb
- ata cpufreq hid lguest modules.order power spi vhost
- atm cpuidle hwmon macintosh mtd pps ssb video
- auxdisplay crypto hwspinlock Makefile net ps3 staging virtio
- base dca i2c Makefile~ nfc ptp switch vlynq
- bcma dio ide mca nubus rapidio target w1
- block dma idle md of regulator tc watchdog
- bluetooth edac ieee802154 media oprofile rtc telephony xen
- built-in.o eisa infiniband memstick parisc s390 thermal zorro
- cdrom firewire input message parport sbus ttt
- char firmware isdn mfd pci scsi tty
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers#
进入到ttt目录,并创建 ttt.h 头文件:(这里使用gedit编辑器,大家也可以使用vi或者其他编辑器)
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers# cd ttt
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers/ttt# gedit ttt.h
然后输入 ttt.h 的内容:
- /*
- * ttt device head file
- *
- * Copyright (C) 2013 brantyou Open Source Project
- * Copyright (C) 2013,2013 brantyou Inc.
- *
- * Author: brantyou <brantyou@qq.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifndef _TTT_ANDROID_H_
- #define _TTT_ANDROID_H_
- #include <linux/cdev.h>
- #include <linux/semaphore.h>
- #define TTT_DEVICE_NODE_NAME "ttt"
- #define TTT_DEVICE_FILE_NAME "ttt"
- #define TTT_DEVICE_PROC_NAME "ttt"
- #define TTT_DEVICE_CLASS_NAME "ttt"
- struct ttt_android_dev{
- int val;
- struct semaphore sem;
- struct cdev dev;
- };
- #endif
添加 ttt.c 文件:
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers/ttt# gedit ttt.c
添加 ttt.c 文件的处理:
- /*
- * ttt device c file
- *
- * Copyright (C) 2013 brantyou Open Source Project
- * Copyright (C) 2013,2013 brantyou Inc.
- *
- * Author: brantyou <brantyou@qq.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #include <linux/proc_fs.h>
- #include <linux/device.h>
- #include <asm/uaccess.h>
- #include "ttt.h"
- // * Master and slave devices number variables
- static int ttt_major = 0;
- static int ttt_minor = 0;
- // * device types and device variables
- static struct class* ttt_class = NULL;
- static struct ttt_android_dev* ttt_dev = NULL;
- // * traditional method of operation of the device file
- static int ttt_open(struct inode* inode, struct file* flip);
- static int ttt_release(struct inode* inode, struct file* flip);
- static ssize_t ttt_read(struct file* flip, char __user* buf, size_t count, loff_t* f_pos);
- static ssize_t ttt_write(struct file* flip, const char __user* buf, size_t count, loff_t* f_pos);
- // * the method of operation of the device file table
- static struct file_operations ttt_fops = {
- .owner = THIS_MODULE,
- .open = ttt_open,
- .release = ttt_release,
- .read = ttt_read,
- .write = ttt_write,
- };
- // * access to set property methods
- static ssize_t ttt_val_show(struct device* dev, struct device_attribute* attr, char* buf);
- static ssize_t ttt_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);
- // * define the device properties
- static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, ttt_val_show, ttt_val_store);
- // * open the device methods
- static int ttt_open(struct inode* inode, struct file* flip)
- {
- struct ttt_android_dev* dev;
- printk(KERN_ALERT"[ttt]: ttt_open().\n");
- // save the device struct to the private area
- dev = container_of(inode->i_cdev, struct ttt_android_dev, dev);
- flip->private_data = dev;
- return 0;
- }
- // * release
- static int ttt_release(struct inode* inode, struct file* filp)
- {
- printk(KERN_ALERT"[ttt]: ttt_release().\n");
- return 0;
- }
- // * read
- static ssize_t ttt_read(struct file* filp, char __user* buf, size_t count, loff_t* f_pos)
- {
- ssize_t err = 0;
- struct ttt_android_dev* dev = filp->private_data;
- printk(KERN_ALERT"[ttt]: ttt_read().\n");
- // async access
- if(down_interruptible( &(dev->sem) )){
- return -ERESTARTSYS;
- }
- if(count < sizeof(dev->val) ){
- goto out;
- }
- //
- if(copy_to_user(buf, &(dev->val), sizeof(dev->val) )){
- err = -EFAULT;
- goto out;
- }
- err = sizeof(dev->val);
- out:
- up(&(dev->sem));
- return err;
- }
- // * write
- static ssize_t ttt_write(struct file* filp, const char __user* buf, size_t count, loff_t* f_pos)
- {
- struct ttt_android_dev* dev = filp->private_data;
- ssize_t err = 0;
- printk(KERN_ALERT"[ttt]: ttt_write().\n");
- // async access
- if(down_interruptible( &(dev->sem) )){
- return -ERESTARTSYS;
- }
- if(count != sizeof(dev->val) ){
- goto out;
- }
- // save the buffer value to device registers
- if( copy_from_user( &(dev->val), buf, count) ){
- err = -EFAULT;
- goto out;
- }
- err = sizeof(dev->val);
- out:
- up(&(dev->sem));
- return err;
- }
- // * read the registers value val to the buffer buf, inner
- static ssize_t __ttt_get_val(struct ttt_android_dev* dev, char* buf)
- {
- int val = 0;
- // async access
- if(down_interruptible( &(dev->sem) )){
- return -ERESTARTSYS;
- }
- val = dev->val;
- up( &(dev->sem) );
- return snprintf(buf, PAGE_SIZE, "%d\n", val);
- }
- // * write the buffer value buf to the device registers val, inner
- static ssize_t __ttt_set_val(struct ttt_android_dev* dev, const char* buf, size_t count)
- {
- int val = 0;
- // translate the string to number
- val = simple_strtol(buf, NULL, 10);
- // async access
- if(down_interruptible( &(dev->sem) )){
- return -ERESTARTSYS;
- }
- dev->val = val;
- up( &(dev->sem));
- return count;
- }
- // * read the device properties val
- static ssize_t ttt_val_show(struct device* dev, struct device_attribute* attr, char* buf)
- {
- struct ttt_android_dev* hdev = (struct ttt_android_dev*)dev_get_drvdata(dev);
- return __ttt_get_val(hdev, buf);
- }
- // * write the device properties val
- static ssize_t ttt_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
- {
- struct ttt_android_dev* hdev = (struct ttt_android_dev*)dev_get_drvdata(dev);
- return __ttt_set_val(hdev, buf, count);
- }
- // * read the device registers val, and save to the page buffer
- static ssize_t ttt_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data)
- {
- if(off > 0){
- *eof = 1;
- return 0;
- }
- return __ttt_get_val(ttt_dev, page);
- }
- // * save the buffer value buff to the device registers val
- static ssize_t ttt_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"[ttt]: The buff is too large:%lu.\n", len);
- return -EFAULT;
- }
- page = (char*)__get_free_page(GFP_KERNEL);
- if(!page){
- printk(KERN_ALERT"[ttt]: Failed to alloc page.\n");
- return -ENOMEM;
- }
- // copy the user buffer value to kernel buffer
- if(copy_from_user(page, buff, len) ){
- printk(KERN_ALERT"[ttt]: Failed to copy buff from user.\n");
- err = -EFAULT;
- goto out;
- }
- err = __ttt_set_val(ttt_dev, page, len);
- out:
- free_page( (unsigned long)page);
- return err;
- }
- // * create /proc/ttt file
- static void ttt_create_proc(void)
- {
- struct proc_dir_entry* entry;
- entry = create_proc_entry(TTT_DEVICE_PROC_NAME, 0, NULL);
- if(entry){
- entry->owner = THIS_MODULE;
- entry->read_proc = ttt_proc_read;
- entry->write_proc = ttt_proc_write;
- }
- }
- // * delete /proc/ttt file
- static void ttt_remove_proc(void)
- {
- remove_proc_entry(TTT_DEVICE_PROC_NAME, NULL);
- }
- // * init device
- static int __ttt_setup_dev(struct ttt_android_dev* dev)
- {
- int err;
- dev_t devno = MKDEV(ttt_major, ttt_minor);
- memset(dev, 0, sizeof(struct ttt_android_dev) );
- cdev_init( &(dev->dev), &ttt_fops);
- dev->dev.owner = THIS_MODULE;
- dev->dev.ops = &ttt_fops;
- // registe charater device
- err = cdev_add( &(dev->dev), devno, 1);
- if(err){
- return err;
- }
- // init single and registers value val
- init_MUTEX(&(dev->sem));
- dev->val = 0;
- return 0;
- }
- // * load module
- static int __init ttt_init(void)
- {
- int err = -1;
- dev_t dev = 0;
- struct device* temp = NULL;
- printk(KERN_ALERT"[ttt]: Initializing ttt device.\n");
- // malloc master and slave device number
- err = alloc_chrdev_region( &dev, 0, 1, TTT_DEVICE_NODE_NAME);
- if(err < 0){
- printk(KERN_ALERT"[ttt]: Failed to alloc char dev region.\n");
- goto fail;
- }
- ttt_major = MAJOR(dev);
- ttt_minor = MINOR(dev);
- // alloc ttt device struct valiriable
- ttt_dev = kmalloc( sizeof(struct ttt_android_dev), GFP_KERNEL);
- if(!ttt_dev){
- err = -ENOMEM;
- printk(KERN_ALERT"[ttt]: Failed to alloc ttt_dev.\n");
- goto unregister;
- }
- // init device
- err = __ttt_setup_dev(ttt_dev);
- if(err){
- printk(KERN_ALERT"[ttt]: Failed to setup dev:%d.\n", err);
- goto cleanup;
- }
- // create device type directory ttt on /sys/class/
- ttt_class = class_create(THIS_MODULE, TTT_DEVICE_CLASS_NAME);
- if(IS_ERR(ttt_class)){
- err = PTR_ERR(ttt_class);
- printk(KERN_ALERT"[ttt]: Failed to create ttt class.\n");
- goto destroy_cdev;
- }
- // create device file ttt on /dev/ and /sys/class/ttt
- temp = device_create(ttt_class, NULL, dev, "%s", TTT_DEVICE_FILE_NAME);
- if(IS_ERR(temp)){
- err = PTR_ERR(temp);
- printk(KERN_ALERT"Failed to create ttt device.\n");
- goto destroy_class;
- }
- // create property file val on /sys/class/ttt/ttt
- err = device_create_file(temp, &dev_attr_val);
- if(err < 0){
- printk(KERN_ALERT"[ttt]: Failed to create attribute val.\n");
- goto destroy_device;
- }
- dev_set_drvdata(temp, ttt_dev);
- // create /proc/ttt file
- ttt_create_proc();
- printk(KERN_ALERT"[ttt]: Successed to initialize ttt device.\n");
- return 0;
- destroy_device:
- device_destroy(ttt_class, dev);
- destroy_class:
- class_destroy(ttt_class);
- destroy_cdev:
- cdev_del(&ttt_dev->dev);
- cleanup:
- kfree(ttt_dev);
- unregister:
- unregister_chrdev_region(MKDEV(ttt_major, ttt_minor), 1);
- fail:
- return err;
- }
- // * unload module
- static void __exit ttt_exit(void)
- {
- dev_t devno = MKDEV(ttt_major, ttt_minor);
- printk(KERN_ALERT"[ttt]: Destroy ttt device.\n");
- // delete /proc/ttt file
- ttt_remove_proc();
- // destroy device type and device
- if(ttt_class){
- device_destroy(ttt_class, MKDEV(ttt_major, ttt_minor) );
- class_destroy(ttt_class);
- }
- // delete character device and release device memory
- if(ttt_dev){
- cdev_del(&(ttt_dev->dev) );
- kfree(ttt_dev);
- }
- // destroy device number
- unregister_chrdev_region(devno, 1);
- }
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Android Test Device");
- module_init(ttt_init);
- module_exit(ttt_exit);
接下来则添加 Kconfig 配置文件:(make menuconfig时会用到)
- config TTT
- tristate "ttt Android test Driver"
- default n
- help
- It is a Android test driver.
添加 ttt 的 Makefile 文件:
- obj-$(CONFIG_TTT) += ttt.o
修改 drivers/Kconfig 文件,在menu "Device Drivers"和endmenu之间添加驱动模块配置选项:
- source "drivers/ttt/Kconfig"
在 drivers/Makefile 文件末尾添加:
- obj-$(CONFIG_TTT) += ttt/
这样是为了方便定制内核,在make menuconfig的时候可以找到我们新加入的模块。
好了,驱动到此基本编写完了,下面开始选中新家的驱动编译一下内核:
回到内核源码目录下执行 make menuconfig:
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0# ls
- arch Documentation ipc Makefile README System.map
- block drivers Kbuild mm REPORTING-BUGS tools
- cesv210_android_defconfig firmware Kconfig modules.builtin samples usr
- COPYING fs kernel modules.order scripts virt
- CREDITS include lib Module.symvers security vmlinux
- crypto init MAINTAINERS net sound vmlinux.o
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0# make menuconfig
将会出现这样一个定制内核模块的界面:
按上下键选择 Device Drivers 按 enter进入下面的界面:
最后面这个驱动模块就是我们刚才加进去的,按 y 键选择,然后选中Exit,按enter键回到下面的界面:
选中最好一个Save an Alternate Configuration File按enter,保存后,退出这个配置。
然后执行make编译命令:
- root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0# make
编译成功后,驱动会包含在zImage里面。
然后在arch/arm/boot/目录下看到生成的zImage Kernel文件,把这个文件烧写到板子。
- root@android:/ # ll /dev/ttt
- crw------- root root 249, 0 2013-03-21 13:59 ttt
这样就说明ttt驱动已经成功加到内核了。
驱动编写完了,这章就先到这里了