Android 学习-Driver in kernel-2-添加GPIO控制

来源:互联网 发布:程序员必读书单 搞笑 编辑:程序博客网 时间:2024/05/16 07:42

在上一篇的基础上添加了Device Tree 的节点,用于控制一个GPIO,实现5V的输出控制。 

上篇地址:http://blog.csdn.net/daichchch/article/details/50725497

平台是mt8163 , Android M0 ,kernel-3.18

1,增加Device Tree 的节点

kernel-3.18/arch/arm64/boot/dts/MT8163.dtsi中,按顺序在SOC{....}内添加节点:

hello: hello@1401d000 {    compatible = "mediatek,mt8163-hello";};

kernel-3.18/arch/arm64/boot/dts/Tb8163p1_64.dts中,按顺序添加gpio pin:

&hello {    hello_power_control = <&pio 27 0>;};

2,增加kernel 内的platform 注册和ioctl

目录kernel-3.18\drivers\misc\mediatek\hello\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 <linux/platform_device.h>#include <linux/slab.h>#include <asm/uaccess.h>#include <linux/of_platform.h>#include <linux/of_address.h>#include <linux/of_gpio.h>#include "hello.h"#define HELLO_DEVNAME "hello"int hello_power_control_pin;/*主设备和从设备号变量*/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 long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg);static long hello_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg);static int hello_probe(struct platform_device *pdev);static int hello_remove(struct platform_device *pdev);static int  __hello_setup_dev(struct hello_android_dev* dev);/*设备文件操作方法表*/static struct file_operations hello_fops = {.owner = THIS_MODULE,.open = hello_open,.release = hello_release,.read = hello_read,.write = hello_write,.unlocked_ioctl = hello_ioctl,#ifdef CONFIG_COMPAT.compat_ioctl = hello_ioctl_compat,#endif};#ifdef CONFIG_PMint hello_pm_suspend(struct device *device){pr_debug("hello_pm_suspend()\n");return 0;}int hello_pm_resume(struct device *device){pr_debug("hello_Npm_resume()\n");return 0;}const struct dev_pm_ops hello_pm_ops = {.suspend = hello_pm_suspend,.resume = hello_pm_resume,};#endifstatic const struct of_device_id hello_of_ids[] = {{.compatible = "mediatek,mt8163-hello",},{},};/*platform file ops*/static struct platform_driver hello_driver = {.probe = hello_probe,.remove = hello_remove,.driver = {   .name = HELLO_DEVNAME,   .owner = THIS_MODULE,#ifdef CONFIG_PM   .pm = &hello_pm_ops,#endif   .of_match_table = hello_of_ids,   }};/*访问设置属性方法*/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;}/*读取设备的寄存器val的值*/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;}        /*将寄存器val的值拷贝到用户提供的缓冲区*/if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {err = -EFAULT;goto out;}err = sizeof(dev->val);out:up(&(dev->sem));return err;}/*写设备的寄存器值val*/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 long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg){void __user *argp = (void __user *)arg;HELLO_ARG hello_arg;static int val = 0;int ret = 0;pr_err("[hello]ioctl=%d, arg = %lu\n", cmd, arg);switch (cmd) {case HELLO_READ_WRITE: {if (copy_from_user(&hello_arg, argp, sizeof(HELLO_ARG))) {pr_err("[hello]ioctl copy_from_user failed! line:%d\n", __LINE__);ret = -EFAULT;} else {hello_arg.times = hello_arg.times + 1;if(hello_arg.write_data != 0) {val += hello_arg.write_data;}hello_arg.read_data = val;if (copy_to_user(argp, &hello_arg, sizeof(HELLO_ARG))) {pr_err("[hello]ioctl copy_to_user failed! line:%d\n", __LINE__);ret = -EFAULT;}}pr_err("[hello]ioctl hello_arg.read_data=%d,hello_arg.write_data=%d,hello_arg.times=%d\n", hello_arg.read_data, hello_arg.write_data, hello_arg.times);break;}case HELLO_POWER: {if (copy_from_user(&hello_arg, argp, sizeof(HELLO_ARG))) {pr_err("[hello]ioctl copy_from_user failed! line:%d\n", __LINE__);ret = -EFAULT;} else {hello_arg.write_data = 0xff;if(hello_arg.read_data == 1) {if (hello_power_control_pin > 0) {pr_err("[hello]hello 1 control pin number is %d\n", hello_power_control_pin);gpio_direction_output(hello_power_control_pin, 1);gpio_set_value(hello_power_control_pin, 1);hello_arg.write_data = hello_arg.read_data;} else {pr_err("[hello]ioctl 1 hello_power_control_pin <= 0 ERROR\n");hello_arg.write_data = 0xf0;}} else if(hello_arg.read_data == 0) {if (hello_power_control_pin > 0) {pr_err("[hello]hello 0 control pin number is %d\n", hello_power_control_pin);gpio_direction_output(hello_power_control_pin, 1);gpio_set_value(hello_power_control_pin, 0);hello_arg.write_data = hello_arg.read_data;} else {pr_err("[hello]ioctl 0 hello_power_control_pin <= 0 ERROR\n");hello_arg.write_data = 0xf1;}}if (copy_to_user(argp, &hello_arg, sizeof(HELLO_ARG))) {pr_err("[hello]ioctl copy_to_user failed! line:%d\n", __LINE__);ret = -EFAULT;}}pr_err("[hello]ioctl hello_arg.read_data=%d,hello_arg.write_data=%d,hello_arg.times=%d\n", hello_arg.read_data, hello_arg.write_data, hello_arg.times);break;}default: {pr_err("[hello]ioctl(%d) arguments is not support\n", cmd);ret = -EFAULT;break;}}return ret;}static long hello_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) {return 0;}/*读取寄存器val的值到缓冲区buf中,内部使用*/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);}/*把缓冲区buf的值写到设备寄存器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;}/*读取设备属性val*/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);}/*写设备属性val*/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);}#if 0/*读取设备寄存器val的值,保存在page缓冲区中*/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);}/*把缓冲区的值buff保存到设备寄存器val中去*/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;}/*创建/proc/hello文件*/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;}}/*删除/proc/hello文件*/static void hello_remove_proc(void) {remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL);}#endifstatic int hello_device_tree_probe(struct platform_device *pdev){int ret = 0;pr_err("[hello_device_tree_probe] probe start\n");if (pdev->dev.of_node == NULL) {pr_err("[hdmi_internal_probe] Device Node Error\n");return -1;}/* Get Power Control Pin */hello_power_control_pin = of_get_named_gpio(pdev->dev.of_node, "hello_power_control", 0);ret = gpio_request(hello_power_control_pin, "hello power control pin");if (ret)pr_err("hello power control pin, failure of setting\n");return 0;}static int hello_probe(struct platform_device *pdev){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);        /*分配helo设备结构体变量*/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;}        /*在/sys/class/目录下创建设备类别目录hello*/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;}        /*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/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;}        /*在/sys/class/hello/hello目录下创建属性文件val*/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);        /*创建/proc/hello文件*///hello_create_proc();hello_device_tree_probe(pdev);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 int hello_remove(struct platform_device *pdev){return 0;}/*初始化设备*/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;}        /*初始化信号量和寄存器val的值*/sema_init(&(dev->sem), 1);dev->val = 0;return 0;}/*模块加载方法*/static int __init hello_init(void){ if (platform_driver_register(&hello_driver)) {pr_err("[hello_init]failed to register hello driver\n");return -1;}return 0;}/*模块卸载方法*/static void __exit hello_exit(void) {dev_t devno = MKDEV(hello_major, hello_minor);printk(KERN_ALERT"Destroy hello device.\n");        /*删除/proc/hello文件*///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);

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;};typedef struct _HELLO_ARG {int times;int read_data;int write_data;}HELLO_ARG;#define HELLO_IOW(num, dtype)     _IOW('h', num, dtype)#define HELLO_IOR(num, dtype)     _IOR('h', num, dtype)#define HELLO_IOWR(num, dtype)    _IOWR('h', num, dtype)#define HELLO_IO(num)             _IO('h', num)#define HELLO_READ_WRITE                HELLO_IOWR(1, HELLO_ARG)#define HELLO_POWER                 HELLO_IOWR(2, HELLO_ARG)#endif

3,修改可执行bin

external\hello\hello_program.c改成如下:

#include <stdint.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <getopt.h>#include <fcntl.h>#include <errno.h>#include <sys/ioctl.h>#include <string.h>typedef unsigned char bool;/*#include "hello.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"typedef struct _HELLO_ARG {int times;int read_data;int write_data;}HELLO_ARG;#define HELLO_IOW(num, dtype)     _IOW('h', num, dtype)#define HELLO_IOR(num, dtype)     _IOR('h', num, dtype)#define HELLO_IOWR(num, dtype)    _IOWR('h', num, dtype)#define HELLO_IO(num)             _IO('h', num)#define HELLO_READ_WRITE              HELLO_IOWR(1, HELLO_ARG)#define HELLO_POWER                 HELLO_IOWR(2, HELLO_ARG)static int hello_read_write(HELLO_ARG *hello_arg);static int hello_power(HELLO_ARG *hello_arg);static const char *pHelloDev = "/dev/hello";static char *pFilePath;static unsigned int u4Addr;static unsigned int u4Data;typedef  enum{   write_register_cmd=0,   read_write_cmd,   power_cmd,   cmd_nubmer}   hdmitx_debug_mode;typedef struct _TEXT2ENUM_T{    char* szText;    unsigned int i4Cmd;char* szHelp;} TEXT2ENUM_T;static TEXT2ENUM_T _arDebugModeEnumTbl[] ={{ "rw",     read_write_cmd,     "(hello_read_write)res (resolution/help)1/0xff"},{ "power",     power_cmd,     "(hello_power)res (resolution/help)1/0xff"},    { NULL, -1, NULL }};static unsigned int comparestr(const char *szText, const TEXT2ENUM_T* prText2Enum){    if ((NULL == szText) ||        (NULL == prText2Enum))    {        return 0;    }    while (prText2Enum->szText)    {        if((strlen(prText2Enum->szText)==strlen(szText))&&(strncmp(prText2Enum->szText, szText,  strlen(prText2Enum->szText)) == 0))        {            break;        }        else        {            prText2Enum++;        }    }    return prText2Enum->i4Cmd;}int main(int argc, char *argv[]){    int ret = 0;    unsigned int read, write, times, i4cmd, i;    char *pEnd;    TEXT2ENUM_T* prTextEnum;    HELLO_ARG hello_arg;prTextEnum = _arDebugModeEnumTbl;    if (argc > 2) {i4cmd = comparestr(argv[1], prTextEnum);        switch(i4cmd) {case read_write_cmd:read = strtoul(argv[2],&pEnd,16);write = strtoul(argv[3],&pEnd,16);times = strtoul(argv[4],&pEnd,16);printf("[hello_read_write] read:0x%04x,write:0x%04x,times:%d\n",read,write,times);hello_arg.read_data = read;hello_arg.write_data = write;hello_arg.times = 0;for(i = 0; i < times; i++) {hello_read_write(&hello_arg);printf("[hello_read_write] done i=%d,hello_arg.read_data:0x%04x,hello_arg.write_data:0x%04x,times=%d\n",i, hello_arg.read_data,hello_arg.write_data,hello_arg.times);}break;case power_cmd:read = strtoul(argv[2],&pEnd,16);write = strtoul(argv[3],&pEnd,16);times = strtoul(argv[4],&pEnd,16);printf("[hello_power] read:0x%04x,write:0x%04x,times:%d\n",read,write,times);hello_arg.read_data = read;hello_arg.write_data = write;hello_arg.times = 0;hello_power(&hello_arg);printf("[hello_power] done ,hello_arg.read_data:0x%04x,hello_arg.write_data:0x%04x,times=%d\n", hello_arg.read_data,hello_arg.write_data,hello_arg.times);if(hello_arg.write_data == hello_arg.read_data && hello_arg.read_data == 1)printf("[hello_power] ------->>>power on ok\n");else if (hello_arg.write_data == hello_arg.read_data && hello_arg.read_data == 0)printf("[hello_power] ------->>>power off ok\n");elseprintf("[hello_power] ------->>>power error\n");break;    default:    printf("please input correct mode\n");    ret = -1;    break;        }    } else {        printf("==============================================================\n");        ret = -1;    } return ret;}static int hello_read_write(HELLO_ARG *hello_arg){int fpHello;int ret;fpHello = open(pHelloDev, O_RDWR, 0);if (fpHello >= 0) {    ret = ioctl(fpHello, HELLO_READ_WRITE, hello_arg);    if (ret == -1) {        return 0;    }    close(fpHello);} else {    return 0;}return ret;}static int hello_power(HELLO_ARG *hello_arg){int fpHello;int ret;fpHello = open(pHelloDev, O_RDWR, 0);if (fpHello >= 0) {    ret = ioctl(fpHello, HELLO_POWER, hello_arg);    if (ret == -1) {        return 0;    }    close(fpHello);} else {    return 0;}return ret;}

4,使用可执行bin测试

root@tb8163p1_64:/data # ./hello power 1 1 2./hello power 1 1 2[hello_power] read:0x0001,write:0x0001,times:2[hello_power] done ,hello_arg.read_data:0x0001,hello_arg.write_data:0x0001,times=0[hello_power] ------->>>power on okroot@tb8163p1_64:/data # ./hello power 0 1 2./hello power 0 1 2[hello_power] read:0x0000,write:0x0001,times:2[hello_power] done ,hello_arg.read_data:0x0000,hello_arg.write_data:0x0000,times=0[hello_power] ------->>>power off ok


0 0
原创粉丝点击