简体   繁体   English

读取设备硬件寄存器

[英]Reading Device Hardware Register

I am attempting to create a device driver, albeit a naïvely simple one, which simply reads the status of a hardware register representing a dip switch.我正在尝试创建一个设备驱动程序,尽管是一个天真简单的驱动程序,它只是读取代表拨码开关的硬件寄存器的状态。 Very new at this and I am rather unsure of where to find adequate resources on the topic.这方面很新,我不确定在哪里可以找到有关该主题的足够资源。 Would be desirable to expose the data in sysfs.最好在 sysfs 中公开数据。

DTS:数据传输系统:

switches@c1000000 {
      compatible = "test, test-switches";
      label = "security_switch";
      reg = c1000000;
      mask = 0x1000;
      status = "okay";
    };

Driver:司机:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/regmap.h>

/* Declare the probe and remove functions */
static int dt_probe(struct platform_device *pdev);
static int dt_remove(struct platform_device *pdev);
/* Declare the read switch function */
int read_switch(struct platform_device *pdev);


int read_switch(struct platform_device *pdev)
{
    static void *reg_data;
    int ret;
    unsigned int adr_reg, bit_mask;
    struct device *dev = &pdev->dev;

    ret = device_property_read_u32(dev, "reg", &adr_reg);
    if(ret) {
        printk("Error! Could not read 'reg'\n");
        return -1;
    }
    printk("Reg read as - %d\n", adr_reg);
    ret = device_property_read_u32(dev, "mask", &bit_mask);
    if(ret) {
        printk("Error! Could not read 'mask'\n");
        return -1;
    }
    printk("Mask read as - %d\n", bit_mask);
    reg_data = ioremap(adr_reg, bit_mask);
    printk("Value @%d", adr_reg);
    printk("-%d ", bit_mask);
    printk(": %d\n", ioread32(reg_data));
    iounmap(reg_data);
    return 0;
}

/**
 * @brief This structure is used to match the DTS entry
 */

static const struct of_device_id switch_driver_ids[] = {
    {
        .compatible = "test,test-switches",
    }, {}
};
MODULE_DEVICE_TABLE(of, switch_driver_ids);

/**
 * @brief Platform Driver definition including functions used for adding, removing and probing 
 * the device
 */
static struct platform_driver switch_driver = {
    .probe = dt_probe,
    .remove = dt_remove,
    .driver = {
        .name = "switch_device_driver",
        .of_match_table = switch_driver_ids,
    },
};

/**
 * @brief This function is called loading the driver 
 */
static int dt_probe(struct platform_device *pdev) {
    struct device *dev = &pdev->dev;
    int ret;
    const char *label;
    const char *status;
    unsigned int reg;

    printk("dt_probe - Probing function!\n");

    /* Check for device properties */
    if(!device_property_present(dev, "label")) {
        printk("dt_probe - Error! Device property 'label' not found!\n");
        return -1;
    }
    printk("dt_probe - Error! Device property 'label' found!\n");

    if(!device_property_present(dev, "status")) {
        printk("dt_probe - Error! Device property 'status' not found!\n");
        return -1;
    }
    printk("dt_probe - Error! Device property 'status' found!\n");

    
    if(!device_property_present(dev, "reg")) {
        printk("dt_probe - Error! Device property 'reg' not found!\n");
        return -1;
    }
    printk("dt_probe - Error! Device property 'reg' found!\n");


    /* Read device properties */
    ret = device_property_read_string(dev, "label", &label);
    if(ret) {
        printk("dt_probe - Error! Could not read 'label'\n");
        return -1;
    }
    printk("dt_probe - label: %s\n", label);
    ret = device_property_read_string(dev, "status", &status);
    if(ret) {
        printk("dt_probe - Error! Could not read 'status'\n");
        return -1;
    }
    printk("dt_probe - status: %s\n", status);
    ret = device_property_read_u32(dev, "reg", &reg);
    if(ret) {
        printk("dt_probe - Error! Could not read 'reg'\n");
        return -1;
    }
    printk("dt_probe - reg: %d\n", reg);

    read_switch(pdev);

    return 0;
}

/**
 * @brief This function is called unloading the driver 
 */
static int dt_remove(struct platform_device *pdev) {
    printk("dt_probe - Removing driver\n");
    return 0;
}

/**
 * @brief This function is called when the module is loaded into the kernel
 */
static int __init init_drv(void) {
    printk("dt_probe - Loading the driver...\n");
    if(platform_driver_register(&switch_driver)) {
        printk("dt_probe - Error! Could not load driver\n");
        return -1;
    }
    return 0;
}

/**
 * @brief This function is called when the module is removed from the kernel
 */
static void __exit exit_drv(void) {
    printk("dt_probe - Unload driver");
    platform_driver_unregister(&switch_driver);
}

module_init(init_drv);
module_exit(exit_drv);

There's basic documentation on sysfs here:这里有关于 sysfs 的基本文档:
https://docs.kernel.org/filesystems/sysfs.html https://docs.kernel.org/filesystems/sysfs.html

With more info in include/linux/sysfs.h and include/linux/device.h.在 include/linux/sysfs.h 和 include/linux/device.h 中有更多信息。

Generally speaking though: best way to learn this stuff is read kernel docs (Documentation folder under kernel source tree), Kernel source itself, mailing lists & other well-established/written mainline drivers.不过一般来说:学习这些东西的最佳方法是阅读 kernel 文档(kernel 源代码树下的文档文件夹)、Kernel 源代码本身、邮件列表和其他完善/编写的主线驱动程序。

This used to be a decent primer on device drivers, it's very dated at this point, but some concepts may still apply: https://lwn.net/Kernel/LDD3/这曾经是设备驱动程序的一本不错的入门书,现在已经过时了,但一些概念可能仍然适用: https://lwn.net/Kernel/LDD3/

Not sure I follow how a DIP switch get's "mapped" to a particular register.不确定我是否遵循 DIP 开关如何“映射”到特定寄存器。 If this is GPIO pins on your SOC, then the -easy- way to get sysfs access is to use GPIO via sysfs, though it can be tricky sorting out pin numbers depending on your platform.如果这是您的 SOC 上的 GPIO 引脚,那么获得 sysfs 访问权限的简单方法是通过 sysfs 使用 GPIO,尽管根据您的平台整理引脚号可能很棘手。

That said, if this really does need it's own driver--then usually you'll want to use DEVICE_ATTR family of macros to define your attribute and then either sysfs_create_group / sysfs_remove_group or sysfs_add_file / sysfs_remove_file to register/unregister in your probe / remove .也就是说,如果这确实需要它自己的驱动程序——那么通常您会希望使用DEVICE_ATTR宏系列来定义您的属性,然后使用sysfs_create_group / sysfs_remove_groupsysfs_add_file / sysfs_remove_file来注册/注销您的probe / remove

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM