ok6410 android driver(8)

  In the past, we know how to create and run a simple character device driver on pc, goldfish and ok6410.

  These two essays I will talk about a led device real exists on ok6410.

  

  In this essay, we will compile a led device driver and test it.

  At first, I wanna write some short summary of the different between a character device and a real device.

  (1) Character device :

  We always control character device in bytes, just like we control a normal file.
  Open file with a file handle, then read, write, llseek and put others control to the handle.

  (2) Real device :

  Acturely, we can control a real device like a normal character driver with file streams.
  But we should know more about the ioctl.
  When a real device connects to pc, it register a address for itself automaticly.
  Then the pc malloc some I/O memory for it, and they will communicate via the I/O memory instead of immediately.
  The I/O memory will protect both pc and device from speech mismach.

  1、 leds.c

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-m.h>

#define DEVICE_NAME "s3c6410_leds"
#define DEVICE_COUNT 1
#define S3C6410_LEDS_MAJOR 0
#define S3C6410_LEDS_MINOR 234
#define PARAM_SIZE 3

static unsigned char mem[4];
static int major = S3C6410_LEDS_MAJOR;
static int minor = S3C6410_LEDS_MINOR;
static dev_t leds_number;
static int leds_state = 1;
static char *param[] = {"string1", "string2", "string3"};
static int param_size = PARAM_SIZE;
static struct class *leds_class = NULL;

static long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd,
    unsigned long arg)
{
    switch (cmd) {
        unsigned int tmp;
        case 0:
        case 1:
        if (arg > 4) {
            return -EINVAL;
        }
        tmp = ioread32(S3C64XX_GPMDAT);
        if (cmd == 1) {
            tmp &= (~(1 << arg));
        } else {
            tmp |= (1 << arg);
        }
        iowrite32(tmp, S3C64XX_GPMDAT);
        return 0;
        default :
        return -EINVAL;
    }    
}

static ssize_t s3c6410_leds_write(struct file *filp,
    const char __user *buf,    size_t count, loff_t *ppos)
{
    unsigned tmp = count;
    unsigned long i = 0;
    memset(mem, 0, 4);

    if (count > 4) {
        tmp = 4;    
    }
    if (copy_from_user(mem, buf, tmp)) {
        return -EINVAL;
    } else {
        for (i = 0; i < 4; i++) {
            tmp = ioread32(S3C64XX_GPMDAT);
            if (mem[i] == '1') {
                tmp &= (~(1 << i));
            } else {
                tmp |= (1 << i);
            }
            iowrite32(tmp, S3C64XX_GPMDAT);
        }
        return count;
    }
}

static struct file_operations leds_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = s3c6410_leds_ioctl,
    .write = s3c6410_leds_write,
};
static struct cdev leds_cdev;

static int leds_create_device(void)
{
    int ret = 0;
    int err = 0;

    cdev_init(&leds_cdev, &leds_fops);
    leds_cdev.owner = THIS_MODULE;
    if (major > 0) {
        leds_number = MKDEV(major, minor);
        err = register_chrdev_region(leds_number, DEVICE_COUNT, DEVICE_NAME);
        if (err < 0) {
            printk(KERN_WARNING "register_chardev_region() failed.
");
            return err;
        }
    } else {
        err = alloc_chrdev_region(&leds_cdev.dev, 10,
                DEVICE_COUNT, DEVICE_NAME);
        if (err < 0) {
            printk(KERN_WARNING "register_chardev_region failed.
");
            return err;
        }
        major = MAJOR(leds_cdev.dev);
        minor = MINOR(leds_cdev.dev);
        leds_number = leds_cdev.dev;
    }

    ret = cdev_add(&leds_cdev, leds_number, DEVICE_COUNT);
    leds_class = class_create(THIS_MODULE, DEVICE_NAME)    ;
    device_create(leds_class, NULL, leds_number, NULL, DEVICE_NAME);
    return ret;

}
static void leds_init_gpm(int leds_default)
{
    int tmp = 0;
    tmp = ioread32(S3C64XX_GPMCON);
    tmp &= (~0xFFFF);
    tmp |= 0x1111;
    iowrite32(tmp, S3C64XX_GPMCON);

    tmp = ioread32(S3C64XX_GPMPUD);
    tmp &= (~0xFF);
    tmp |= 0xAA;
    iowrite32(tmp, S3C64XX_GPMPUD);

    tmp = ioread32(S3C64XX_GPMDAT);
    tmp &= (~0xF);
    tmp |= leds_default;
    iowrite32(tmp, S3C64XX_GPMDAT);
}

static int leds_init(void)
{
    int ret;
    ret = leds_create_device();
    leds_init_gpm(~leds_state);
    printk(DEVICE_NAME "	initialized.
");

    printk("param0	%s
", param[0]);
    printk("param1	%s
", param[1]);
    printk("param2	%s
", param[2]);
    
    return ret;
}

static void leds_destroy_device(void)
{
    device_destroy(leds_class, leds_number);
    if (leds_class) {
        unregister_chrdev_region(leds_number, DEVICE_COUNT);
        return;
    }
}
static void leds_exit(void)
{
    leds_destroy_device();
    printk(DEVICE_NAME"	exit.
");
}

module_init(leds_init);
module_exit(leds_exit);

module_param(leds_state, int, S_IRUGO | S_IWUSR);
module_param_array(param, charp, &param_size, S_IRUGO | S_IWUSR);

MODULE_LICENSE("GPL");

  Then follow the steps we analysis in wordcount device (character device).

  2、init and exit entrance functions

  (1) init function :

// init entrance function
module_init(leds_init);
// 
static int leds_init(void)
{
    ...
        // create device like normal character device
    ret = leds_create_device();
        // init the device by its I/O memory
    leds_init_gpm(~leds_state);
    ...
}

  (2) exit function :

// exit entrance function
module_exit(leds_exit);
//
static void leds_exit(void)
{
        // destroy and unregister the device
    leds_destroy_device();
    printk(DEVICE_NAME"	exit.
");
}

  3、the device mechanism supported by the device driver

// file control mechanism
static struct file_operations leds_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = s3c6410_leds_ioctl,
    .write = s3c6410_leds_write,
};
// device defination
static struct cdev leds_cdev;

  4、the callback operation functions

static struct file_operations leds_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = s3c6410_leds_ioctl,
    .write = s3c6410_leds_write,
};
//
static long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd,
    unsigned long arg)
...
//
static ssize_t s3c6410_leds_write(struct file *filp,
    const char __user *buf,    size_t count, loff_t *ppos)
...

  I don't want to talk much about file_operations, you can check it in /kernel_dir/include/linux/fs.h and 《LDD》.

  TIPS-1 :

If you want to test the device, you could use echo.
But don't try to use a shell scripts to control it. because the shell in android and pc is working not like the original shell.
Ok, if you turely want to got a test in shell scripts, try this in you pc :
// choose no
$ sudo dpkg-reconfigure dash

  TIPS-2 :

Our device is /dev/s3c6410_leds instead of /dev/leds

  

原文地址:https://www.cnblogs.com/plinx/p/3220639.html