在上一篇的基础上添加了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_PM
int 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,
};
#endif
static 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);
}
#endif
static 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");
else
printf("[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 ok
root@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