• 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

      

  • 相关阅读:
    上有传参下传json的接口调用
    通过群号,获取到群成员信息,下载头像到指定文件夹
    django模型 之 Meta
    k8s 日志的收集
    systemctl 管理服务
    安装JumpServer
    1 nginx的配置详解
    十六 RBAC
    python3 与linux间的小知识
    解决问题:OSError: mysql_config not found
  • 原文地址:https://www.cnblogs.com/plinx/p/3220639.html
Copyright © 2020-2023  润新知