sysfs_create_group
使用sysfs_create_group创建调试节点,节点位置在于他的第一个参数,一般在/sys/device/ 目录下,剩下的路径取决于是什么设备,我的是platform设备,所以路径就是/sys/device/platform/beep/beep,我创建的是beep节点
函数原型:
`int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)`
其中kobj是device结构体中kobj,grp参数就是我们需要填写的,原型为
struct attribute_group {
const char *name;
umode_t (*is_visible)(struct kobject *,
struct attribute *, int);
struct attribute **attrs; //这个是重点
};
attr的原型为
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
}
好了,以上就是理论知识,接下来以实际例子进行介绍
首先在probe函数或者其他地方中建立调试节点
//建立调试节点
ret = sysfs_create_group(&dev->dev.kobj,&beep_attr_group);
if(ret < 0) {
dev_err(&dev->dev,"创建节点失败");
} else {
dev_info(&dev->dev,"创建节点成功");
}
之后填写具体beep_attr_group
static DEVICE_ATTR(beep,S_IWUSR | S_IRUGO,attr_beep_read,attr_beep_write); //第二个参数是权限
static struct attribute *beep_attrs[] = { //beep的名字会将beep_attrs和上面连起来。
&dev_attr_beep.attr,
NULL
};
static const struct attribute_group beep_attr_group = {
.attrs = beep_attrs,
};
其中attr_beep_read和attr_beep_write就是具体的读写函数,在这两个函数中可以实现具体的读写
static ssize_t attr_beep_read(struct device *d, struct device_attribute*attr, char *buf)
{
ssize_t len = 0;
len += snprintf(buf+len,PAGE_SIZE-len,"beep = %d\n",dtsbeep.beep_gpio);
pr_info("attr_beep_read\n");
pr_info("gpio_beep_value = %d\n",gpio_get_value(dtsbeep.beep_gpio));
// pr_info("%d\n",(int)*buf);
// *buf = dtsbeep.beep_gpio;
return len;
}
static ssize_t attr_beep_write(struct device *d, struct device_attribute *attr,const char *buf,size_t count)
{
int temp = 0;
pr_info("attr_beep_write\n");
temp = (int)(*buf - '0');
printk("temp = %d\n",temp);
printk("buf = %s\n",buf);
gpio_set_value(dtsbeep.beep_gpio,temp);
return 0;
}
这两个函数需要注意buf,buf就是传递的媒介
全部的代码如下
/*添加调试接口sysfs_create_group*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/sysfs.h>
#define BEEPOFF 0 关beep
#define BEEPON 1 开beep
#define DTSBEEP_CNT 1 //设备数量
#define DTSBEEP_NAME "dtsbeep"
struct dtsbeep_dev
{
dev_t devid; /*设备号*/
/*cdev这是一个向系统描述设备的结构体,
*linux系统为每个设备均分配一个cdev结构体,通过设备号来唯一区分设备
*cdev结构体主要描述了file_operations和设备号*/
struct cdev cdev;
/*class和device用于自动创建设备节点*/
struct class *class;/*类*/
struct device *device;/*设备*/
int major;//主设备号
int minjor; //次设备号
struct device_node *nd;//设备节点
int beep_gpio; //gpio编号
};
struct dtsbeep_dev dtsbeep;/**dtsbeep设备*/
static int beep_open(struct inode *inode,struct file *filp)
{
filp->private_data = &dtsbeep;
return 0;
}
static int beep_release(struct inode *inode,struct file *filp)
{
return 0;
}
static ssize_t beep_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
int ret;
unsigned char databuf[1];
struct dtsbeep_dev *dev = filp->private_data;
ret = copy_from_user(databuf,buf,count);
if(ret < 0 )
{
printk("writre faibeep");
return -EINVAL;
}
if(databuf[0] == BEEPON)
{
gpio_set_value(dev->beep_gpio,0);//开beep
}
else if(databuf[0] == BEEPOFF)
{
gpio_set_value(dev->beep_gpio,1);//关beep
}
return 0;
}
// /**设备操作函数*
// * 该file_operations结构体在系统中定义好了,此时是对该结构体的成员(操作函数)进行初始化
// */
static struct file_operations dtsbeep_fops = {
.owner = THIS_MODULE,
.open = beep_open,
.write = beep_write,
.release = beep_release,
};
static ssize_t attr_beep_read(struct device *d, struct device_attribute*attr, char *buf)
{
ssize_t len = 0;
len += snprintf(buf+len,PAGE_SIZE-len,"beep = %d\n",gpio_get_value(dtsbeep.beep_gpio));
pr_info("attr_beep_read\n");
pr_info("gpio_beep_value = %d\n",gpio_get_value(dtsbeep.beep_gpio));
// pr_info("%d\n",(int)*buf);
// *buf = dtsbeep.beep_gpio;
return len;
}
static ssize_t attr_beep_write(struct device *d, struct device_attribute *attr,const char *buf,size_t count)
{
int temp = 0;
pr_info("attr_beep_write\n");
temp = (int)(*buf - '0');
printk("temp = %d\n",temp);
printk("buf = %s\n",buf);
gpio_set_value(dtsbeep.beep_gpio,temp);
return 0;
}
static DEVICE_ATTR(beep,S_IWUSR | S_IRUGO,attr_beep_read,attr_beep_write);
static struct attribute *beep_attrs[] = {
&dev_attr_beep.attr,
NULL
};
static const struct attribute_group beep_attr_group = {
.attrs = beep_attrs,
};
static int beep_probe(struct platform_device *dev)
{
int ret = 0;
//int num;
printk("was matched");
dtsbeep.major=0;
if(dtsbeep.major)/*定义了主设备号*/
{
dtsbeep.devid = MKDEV(dtsbeep.major,0);/*根据主设备号构建设备号*/
ret = register_chrdev_region(dtsbeep.devid,DTSBEEP_CNT,DTSBEEP_NAME); ///*根据设备号,设备数量,设备名字向系统注册字符设备*/
}
else{/*没有定义主设备号*/
/*由系统自动分配设备号,其中参数2为次设备号范围中的一个取0即可*/
ret = alloc_chrdev_region(&dtsbeep.devid,0,DTSBEEP_CNT,DTSBEEP_NAME);
dtsbeep.major = MAJOR(dtsbeep.devid);/*根据设备号获取主设备号*/
dtsbeep.minjor = MINOR(dtsbeep.devid);/*根据设备号获取次设备号*/
}
if(ret < 0) {
goto fail_devid;
}
printk("major = %d\n",dtsbeep.major);
printk("minjor = %d\n",dtsbeep.minjor);
/*初始化cdev结构体*/
dtsbeep.cdev.owner = THIS_MODULE;
cdev_init(&dtsbeep.cdev,&dtsbeep_fops);
/*添加cdev*/
ret = cdev_add(&dtsbeep.cdev,dtsbeep.devid,DTSBEEP_CNT);
if(ret < 0)
goto fail_cdev;
/*自动添加设备节点*/
dtsbeep.class = class_create(THIS_MODULE,DTSBEEP_NAME);
if(IS_ERR(dtsbeep.class))
{
return PTR_ERR(dtsbeep.class);
}
dtsbeep.device = device_create(dtsbeep.class,NULL,dtsbeep.devid,NULL,DTSBEEP_NAME);
if (IS_ERR(dtsbeep.device)) {
return PTR_ERR(dtsbeep.device);
}
/*通过gpio子系统获取gpio相关信息*/
//1、获取设备节点
dtsbeep.nd = of_find_node_by_path("/beep");
//2,根据设备节点获取对应的gpio
dtsbeep.beep_gpio = of_get_named_gpio(dtsbeep.nd,"beep-gpio",0);
if(dtsbeep.beep_gpio < 0)
{
printk("can't find gpio\n");
ret = -EINVAL;
goto fail_findnode;
}
//3申请IO
#if 0
ret = gpio_request(dtsbeep.beep_gpio,"beep");
if (ret) {
printk("Faibeep to request the beep gpio\r\n");
ret = -EINVAL;
goto fail_findnode;
}
#endif
/*
* GPIOF_OUT_INIT_HIGH 输出高电平
* GPIOF_OUT_INIT_LOW 输出低电平
* GPIOF_IN 输入模式
*/
if(gpio_is_valid(dtsbeep.beep_gpio)) {
ret = devm_gpio_request_one(&dev->dev,dtsbeep.beep_gpio,GPIOF_OUT_INIT_HIGH,"beep");
if( ret ) {
dev_err(&dev->dev,"request gpio error");
return -ENAVAIL;
} else {
dev_info(&dev->dev,"request gpio success");
}
}
#if 0
//4设置IO为输出方式
//gpio_direction_output返回值0表示成功
ret = gpio_direction_output(dtsbeep.beep_gpio,1);
if(ret)
{
goto fail_setoutput;
}
#endif
//建立调试节点
ret = sysfs_create_group(&dev->dev.kobj,&beep_attr_group);
if(ret < 0) {
dev_err(&dev->dev,"创建节点失败");
} else {
dev_info(&dev->dev,"创建节点成功");
}
return 0;
fail_findnode:
fail_cdev:
unregister_chrdev_region(dtsbeep.devid, DTSBEEP_CNT);
fail_devid:
return ret;
}
static int beep_remove(struct platform_device *dev)
{
// gpio_set_value(dtsbeep.beep_gpio,0);
sysfs_remove_group(&dev->dev.kobj,&beep_attr_group);
gpio_set_value(dtsbeep.beep_gpio,1);//关beep
// gpio_free(dtsbeep.beep_gpio);
device_destroy(dtsbeep.class, dtsbeep.devid);
class_destroy(dtsbeep.class);
/*删除cdev*/
cdev_del(&dtsbeep.cdev);
/*注销设备号*/
unregister_chrdev_region(dtsbeep.devid,DTSBEEP_CNT);
return 0;
}
static const struct of_device_id beep_of_match[] = {
{.compatible = "atkalpha-beep"},
{ }
};
static struct platform_driver beep_driver = {
.driver = {
.name = "imx6ul-beep",
.of_match_table = beep_of_match,
},
.probe = beep_probe,
.remove = beep_remove,
};
/*驱动入口函数,整个驱动从此函数开始*/
static int __init beep_init(void)
{
return platform_driver_register(&beep_driver);
}
/*驱动出口函数,驱动退出时要执行这个函数,一般用做注销
*注销的原则就是后申请的先注销*/
static void __exit beep_exit(void)
{
platform_driver_unregister(&beep_driver);
}
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WWD");