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目錄,如:

[plain] 

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$   

切換成超級用戶權限:

[plain] 

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 驅動目錄:

[plain]  

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或者其他編輯器)

[plain]  

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 的內容:

[cpp] 

/* 

 * 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 文件:

[plain] 

root@brantyou-ubuntu:~/workspace/samsung_android_kernel_3.0/drivers/ttt# gedit ttt.c  

添加 ttt.c 文件的處理:

[cpp] 

/* 

 * 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時會用到)

[plain]  

config TTT  

tristate "ttt Android test Driver"  

default n  

help  

It is a Android test driver.  

添加 ttt 的 Makefile 文件:

[plain] 

obj-$(CONFIG_TTT) += ttt.o  

 

修改 drivers/Kconfig 文件,在menu "Device Drivers"和endmenu之間添加驅動模塊配置選項:

[plain]  

source "drivers/ttt/Kconfig"  

在 drivers/Makefile 文件末尾添加:

[plain]  

obj-$(CONFIG_TTT)       += ttt/  

 

這樣是為瞭方便定制內核,在make menuconfig的時候可以找到我們新加入的模塊。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *