添加调试节点 sysfs_create_group

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");



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值