[Android Software/Hacking General [Developers Only]] llrraa2010:
implementation of one kernel driver for android

1. mkdir hello

USER-NAME@MACHINE-NAME:~/Android$ cd kernel/common/drivers

USER-NAME@MACHINE-NAME:~/Android/kernel/common/drivers$ mkdir hello

2. hello.h

#ifndef _HELLO_ANDROID_H_

#define _HELLO_ANDROID_H_

#include <linux/cdev.h>

#include <linux/semaphore.h>

#define HELLO_DEVICE_NODE_NAME "hello"

#define HELLO_DEVICE_FILE_NAME "hello"

#define HELLO_DEVICE_PROC_NAME "hello"

#define HELLO_DEVICE_CLASS_NAME "hello"

struct hello_android_dev {

int val;

struct semaphore sem;

struct cdev dev;

};

#endif

3. hello.c

#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 "hello.h"

static int hello_major = 0;

static int hello_minor = 0;

static struct class* hello_class = NULL;

static struct hello_android_dev* hello_dev = NULL;

static int hello_open(struct inode* inode, struct file* filp);

static int hello_release(struct inode* inode, struct file* filp);

static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);

static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);

static struct file_operations hello_fops = {

.owner = THIS_MODULE,

.open = hello_open,

.release = hello_release,

.read = hello_read,

.write = hello_write,

};

static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf);

static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);

static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store);

static int hello_open(struct inode* inode, struct file* filp) {

struct hello_android_dev* dev;

dev = container_of(inode->i_cdev, struct hello_android_dev, dev);

filp->private_data = dev;

return 0;

}

static int hello_release(struct inode* inode, struct file* filp) {

return 0;

}

static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {

ssize_t err = 0;

struct hello_android_dev* dev = filp->private_data;

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;

}

static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {

struct hello_android_dev* dev = filp->private_data;

ssize_t err = 0;

if(down_interruptible(&(dev->sem))) {

return -ERESTARTSYS;

}

if(count != sizeof(dev->val)) {

goto out;

}

if(copy_from_user(&(dev->val), buf, count)) {

err = -EFAULT;

goto out;

}

err = sizeof(dev->val);

out:

up(&(dev->sem));

return err;

}

static ssize_t __hello_get_val(struct hello_android_dev* dev, char* buf) {

int val = 0;

if(down_interruptible(&(dev->sem))) {

return -ERESTARTSYS;

}

val = dev->val;

up(&(dev->sem));

return snprintf(buf, PAGE_SIZE, "%d\n", val);

}

static ssize_t __hello_set_val(struct hello_android_dev* dev, const char* buf, size_t count) {

int val = 0;

val = simple_strtol(buf, NULL, 10);

if(down_interruptible(&(dev->sem))) {

return -ERESTARTSYS;

}

dev->val = val;

up(&(dev->sem));

return count;

}

static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) {

struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);

return __hello_get_val(hdev, buf);

}

static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {

struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);

return __hello_set_val(hdev, buf, count);

}

static ssize_t hello_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {

if(off > 0) {

*eof = 1;

return 0;

}

return __hello_get_val(hello_dev, page);

}

static ssize_t hello_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 = __hello_set_val(hello_dev, page, len);

out:

free_page((unsigned long)page);

return err;

}

static void hello_create_proc(void) {

struct proc_dir_entry* entry;

entry = create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL);

if(entry) {

entry->owner = THIS_MODULE;

entry->read_proc = hello_proc_read;

entry->write_proc = hello_proc_write;

}

}

static void hello_remove_proc(void) {

remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL);

}

static int __hello_setup_dev(struct hello_android_dev* dev) {

int err;

dev_t devno = MKDEV(hello_major, hello_minor);

memset(dev, 0, sizeof(struct hello_android_dev));

cdev_init(&(dev->dev), &hello_fops);

dev->dev.owner = THIS_MODULE;

dev->dev.ops = &hello_fops;

err = cdev_add(&(dev->dev),devno, 1);

if(err) {

return err;

}

init_MUTEX(&(dev->sem));

dev->val = 0;

return 0;

}

static int __init hello_init(void){

int err = -1;

dev_t dev = 0;

struct device* temp = NULL;

printk(KERN_ALERT"Initializing hello device.\n");

err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);

if(err < 0) {

printk(KERN_ALERT"Failed to alloc char dev region.\n");

goto fail;

}

hello_major = MAJOR(dev);

hello_minor = MINOR(dev);

hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL);

if(!hello_dev) {

err = -ENOMEM;

printk(KERN_ALERT"Failed to alloc hello_dev.\n");

goto unregister;

}

err = __hello_setup_dev(hello_dev);

if(err) {

printk(KERN_ALERT"Failed to setup dev: %d.\n", err);

goto cleanup;

}

hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);

if(IS_ERR(hello_class)) {

err = PTR_ERR(hello_class);

printk(KERN_ALERT"Failed to create hello class.\n");

goto destroy_cdev;

}

temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);

if(IS_ERR(temp)) {

err = PTR_ERR(temp);

printk(KERN_ALERT"Failed to create hello device.");

goto destroy_class;

}

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, hello_dev);

hello_create_proc();

printk(KERN_ALERT"Succedded to initialize hello device.\n");

return 0;

destroy_device:

device_destroy(hello_class, dev);

destroy_class:

class_destroy(hello_class);

destroy_cdev:

cdev_del(&(hello_dev->dev));

cleanup:

kfree(hello_dev);

unregister:

unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);

fail:

return err;

}

static void __exit hello_exit(void) {

dev_t devno = MKDEV(hello_major, hello_minor);

printk(KERN_ALERT"Destroy hello device.\n");

hello_remove_proc();

if(hello_class) {

device_destroy(hello_class, MKDEV(hello_major, hello_minor));

class_destroy(hello_class);

}

if(hello_dev) {

cdev_del(&(hello_dev->dev));

kfree(hello_dev);

}

unregister_chrdev_region(devno, 1);

}

MODULE_LICENSE("GPL");

MODULE_DESCRIPTION("First Android Driver");

module_init(hello_init);

module_exit(hello_exit);

4. things added to Kconfig Makefile

Kconfig:

config HELLO

tristate "First Android Driver"

default n

help

This is the first android driver.

Makefile:

obj-$(CONFIG_HELLO) += hello.o

5. menu menuconfig

In arch/arm/Kconfig and drivers/kconfig, add one line between menu "Device Drivers" and endmenu:

source "drivers/hello/Kconfig"

6. change drivers/Makefile, add one line:

obj-$(CONFIG_HELLO) += hello/

7. menuconfig

USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make menuconfig

set option "Device Drivers" => "First Android Drivers" to "y"

8. make

USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make

9. rebuild kernel and test the driver

USER-NAME@MACHINE-NAME:~/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &

USER-NAME@MACHINE-NAME:~/Android$ adb shell

File hello should be available in /dev:

root@android:/ # cd dev

root@android:/dev # ls

File hello should be available in /proc:

root@android:/ # cd proc

root@android:/proc # ls

Get the value:

root@android:/proc # cat hello

0

root@android:/proc # echo '5' > hello

root@android:/proc # cat hello

5

Dir hello should be available in /sys/class:

root@android:/ # cd sys/class

root@android:/sys/class # ls

Dir hello should be available in /sys/class/hello:

root@android:/sys/class # cd hello

root@android:/sys/class/hello # ls

File val should be available in /sys/class/hello/hello:

root@android:/sys/class/hello # cd hello

root@android:/sys/class/hello/hello # ls

access the value of val:

root@android:/sys/class/hello/hello # cat val

5

root@android:/sys/class/hello/hello # echo '0' > val

root@android:/sys/class/hello/hello # cat val

0