Linux驱动编程常用代码

一:GPIO

//dtsi
device {
    rst-gpio = <&tlmm 67 0x00>;
};
// sample code
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>

void device_probe(struct platform_device *pdev)
{
    struct device *dev;
    struct device_node *np;
    int rst_gpio;
    int rc;
    
probe:
    dev = &pdev->dev;
    np = &pdev->dev.of_node;
    //dev = &client->dev;
    //np = client->dev.of_node; // i2c

    rst_gpio = of_get_named_gpio(np, "rst-gpio", 0);
    //rst_gpio = of_get_named_gpio_flags(np, "rst-gpio", 0, &flags);

init:
    if (gpio_is_valid(rst_gpio)) {
        rc = devm_gpio_request_one(dev, pdata->enable_gpio,
                                GPIOF_OUT_INIT_LOW, "xxx_reset");
        if (rc < 0) {
            pr_err("rst gpio request failed\n");
        }
    }
use:
    if (gpio_is_valid(rst_gpio)) {
        gpio_set_value(rst_gpio, 0);
        usleep_range(1000, 2000); /* Keep enable down at least 1ms */

        gpio_set_value(rst_gpio, 1);
        usleep_range(500, 1000); /* #include <linux/delay.h> */
    }

deinit:
    if (gpio_is_valid(rst_gpio)) {
        gpio_set_value(rst_gpio, 0);
    }
}

//API
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label);
int gpio_get_value(unsigned int gpio);
void gpio_set_value(unsigned int gpio, int value);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);
bool gpio_is_valid(int number);
int gpio_to_irq(unsigned int gpio);

# 使用需要显示free资源
int gpio_request(unsigned gpio, const char *label);
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int gpio_free(unsigned gpio);
int gpio_request_array(const struct gpio *array, size_t num);
void gpio_free_array(const struct gpio *array, size_t num);

二:IRQ

//dtsi
device {
    qcom,platform-te-gpio = <&tlmm 23 0>;
}

// sample code
#include <linux/interrupt.h>

static irqreturn_t dsi_display_panel_te_irq_handler(int irq, void *data)
{
    struct dsi_display *display = (struct dsi_display *)data;
    ...
    ...
    return IRQ_HANDLED;
}

void device_irq_init(struct platform_device *pdev)
{
    struct device *dev;
    struct device_node *np; 
    int disp_te_gpio;
    unsigned int te_irq;
    int flags;
    int rc;

    dev = &pdev->dev;
    np = = pdev->dev.of_node;
    disp_te_gpio = of_get_named_gpio(np,
                    "qcom,platform-te-gpio", 0);
    //disp_te_gpio = of_get_named_gpio_flags(np, "rst-gpio", 0, &flags);
    if (!gpio_is_valid(disp_te_gpio)) {
        rc = -EINVAL;
        goto error;
    }

    rc = devm_gpio_request_one(dev, pdata->enable_gpio, GPIOF_IN, "xxx_irq");
    if (rc < 0) {
        pr_err("rst gpio request failed\n");
    }
 
    te_irq = gpio_to_irq(disp_te_gpio);

    rc = devm_request_irq(dev, te_irq, dsi_display_panel_te_irq_handler,
                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                  "TE_GPIO", display);
error:
    return;
}

//API
disable_irq(te_irq);
enable_irq(te_irq);
# handler:上半部处理函数,thread_fn:下半部处理函数
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
                  irq_handler_t handler, irq_handler_t thread_fn,
                  unsigned long irqflags, const char *devname,
                  void *dev_id);

三:regulator

// dtsi
device {
   vdd-supply = <&pm8350_l6>;
}

// sample code
#include <linux/regulator/consumer.h>

void device_regulator_sample(struct platform_device *pdev)
{
    struct device *dev;
    struct regulator *vdd_reg;
    int ret;

    dev = &pdev->dev;
    vdd_reg = devm_regulator_get(dev, "vdd");
    if (IS_ERR(vdd_reg)) {
        ret = PTR_ERR(vdd_reg);
        pr_err("Failed to get vdd regulator: %d\n", ret);
        //return ret;
    }

    // enable
    if (vdd_reg && !regulator_is_enabled(vdd_reg)) {
        ret = regulator_enable(axera_sdio->dvdd3_3);
        if (ret < 0) {
            pr_err("Couldn't enable vdd_reg regulator ret=%d\n",ret);
        }
    }
    // disbale 
    if (vdd_reg && regulator_is_enabled(vdd_reg)) {
        ret = regulator_disable(vdd_reg);
        if (ret < 0) {
            pr_err("Couldn't disable vdd_reg regulator ret=%d\n",ret);
        }
    }
}

//API 
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);

四:CLK

//dtsi
device {
    clock-names = "bb_clk3";
    clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
}

//sample code
#include <linux/clk.h>

void device_clk_init(struct platform_device *pdev)
{
    struct device *dev;
    struct clk *bb_clk;
    int ret;

    dev = &pdev->dev;
    bb_clk = devm_clk_get(dev, "bb_clk3");
    if (IS_ERR(bb_clk)) {
      if (PTR_ERR(bb_clk) != -EPROBE_DEFER)
          pr_err("Unable to get bb_clk3 clock\n");
          return;
    }

    ret = clk_prepare_enable(bb_clk);
    if (ret)
        pr_err("enable bb_clk failed\n");

    ret = clk_disable_unprepare(bb_clk);
    if (ret)
        pr_err("disable bb_clk failed\n");
}

//API
int clk_set_rate(struct clk *clk, unsigned long rate);
unsigned long clk_get_rate(struct clk *clk);

五:debugfs

// sample code
#include <linux/debugfs.h>

static int cam_ife_set_csid_debug(void *data, u64 val)
{
    g_ife_hw_mgr.debug_cfg.csid_debug = val;
    CAM_DBG(CAM_ISP, "Set CSID Debug value :%lld", val);
    return 0;
}

static int cam_ife_get_csid_debug(void *data, u64 *val)
{
    *val = g_ife_hw_mgr.debug_cfg.csid_debug;
    CAM_DBG(CAM_ISP, "Get CSID Debug value :%lld",
        g_ife_hw_mgr.debug_cfg.csid_debug);

    return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(cam_ife_csid_debug,
    cam_ife_get_csid_debug,
    cam_ife_set_csid_debug, "%llu\n");


static int cam_ife_hw_mgr_debug_register(void)
{
    int rc = 0;
    struct dentry *dbgfileptr = NULL;

    dbgfileptr = debugfs_create_dir("camera_ife", NULL);
    if (!dbgfileptr) {
        CAM_ERR(CAM_ISP,"DebugFS could not create directory!");
        rc = -ENOENT;
        goto end;
    }
    /* Store parent inode for cleanup in caller */
    g_ife_hw_mgr.debug_cfg.dentry = dbgfileptr;

    dbgfileptr = debugfs_create_file("ife_csid_debug", 0644,
        g_ife_hw_mgr.debug_cfg.dentry, NULL, &cam_ife_csid_debug);

    dbgfileptr = debugfs_create_u32("enable_recovery", 0644,
        g_ife_hw_mgr.debug_cfg.dentry,
        &g_ife_hw_mgr.debug_cfg.enable_recovery);

    dbgfileptr = debugfs_create_bool("enable_req_dump", 0644,
        g_ife_hw_mgr.debug_cfg.dentry,
        &g_ife_hw_mgr.debug_cfg.enable_req_dump);


    if (IS_ERR(dbgfileptr)) {
        if (PTR_ERR(dbgfileptr) == -ENODEV)
            CAM_WARN(CAM_ISP, "DebugFS not enabled in kernel!");
        else
            rc = PTR_ERR(dbgfileptr);
    }
end:
    g_ife_hw_mgr.debug_cfg.enable_recovery = 0;
    return rc;
}

static int cam_ife_hw_mgr_debug_deinit(void)
{
    debugfs_remove_recursive(g_ife_hw_mgr.debug_cfg.dentry);
    g_ife_hw_mgr.debug_cfg.dentry = NULL;
}

六:DEVICE_ATTR

/示例:
static ssize_t show_test(struct device *dev, struct device_attribute *attr,
             char *buf)
{
    return scnprintf(buf, PAGE_SIZE,
             "#Write into test to start test sequence!#\n");
}

static ssize_t store_test(struct device *dev, struct device_attribute *attr,
              const char *buf, size_t count)
{

    struct i2c_client *client;
    int ret;
    client = to_i2c_client(dev);

    /*test */
    ret = blinkm_test_run(client);
    if (ret < 0)
        return ret;

    return count;
}

static DEVICE_ATTR(test, S_IRUGO | S_IWUSR, show_test, store_test);

static struct attribute *blinkm_attrs[] = {
    //&dev_attr_red.attr,
    //&dev_attr_green.attr,
    //&dev_attr_blue.attr,
    //&dev_attr_test.attr,
    NULL,
};

probe:
    /* Register sysfs hooks */
    err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
    if (err < 0) {
        dev_err(&client->dev, "couldn't register sysfs group\n");
        goto exit;
    }

remove:
    sysfs_remove_group(&client->dev.kobj, &blinkm_group);

//sysfs.txt
结构体:
struct device_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct device *dev, struct device_attribute *attr,
            char *buf);
    ssize_t (*store)(struct device *dev, struct device_attribute *attr,
             const char *buf, size_t count);
};

声明:
DEVICE_ATTR(_name, _mode, _show, _store);

增/删属性:
int device_create_file(struct device *dev,  const struct device_attribute *attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);

七:DRIVER_ATTR

// 示例1:
static ssize_t version_show(struct device_driver *driver, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s\n", ASD_DRIVER_VERSION);
}
static DRIVER_ATTR_RO(version);

static int sample_create_driver_attrs(struct device_driver *driver)
{
    return driver_create_file(driver, &driver_attr_version);
}

static void sample_remove_driver_attrs(struct device_driver *driver)
{
    driver_remove_file(driver, &driver_attr_version);
}

// 示例2:
static ssize_t debug_show(struct device_driver *dev, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s\n", debug_path);
}

static ssize_t debug_store(struct device_driver *dev, const char *buf,
        size_t count)
{
    strncpy(debug_path, buf, PATH_MAX);
    return count;
}
static DRIVER_ATTR_RW(debug);
static int sample2_create_driver_attrs(struct device_driver *driver)
{
    return driver_create_file(driver, &driver_attr_debug);
}

static void sample2_remove_driver_attrs(struct device_driver *driver)
{
    driver_remove_file(driver, &driver_attr_debug);
}

//sysfs.txt
结构体:
struct driver_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct device_driver *, char * buf);
        ssize_t (*store)(struct device_driver *, const char * buf,
                         size_t count);
};

声明:
DRIVER_ATTR(_name, _mode, _show, _store)

增/删属性:
int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);

八:i2c driver

#include <linux/i2c.h>

#define SAMPLE_DRIVER_I2C "sample_devive_name"


/* Return negative errno else zero on success */
static int sample_write_byte(struct i2c_client *client, u8 addr, u8 val)
{
    int rval;

    rval = i2c_smbus_write_byte_data(client, addr, val);
    if (rval < 0) {
        LOG_ERROR("i2c write error, err %d\n", rval);
        return rval;
    }
    LOG_DEBUG("write addr:0x%02x val:0x%02x ok\n", addr, val);

    return 0;
}

/* Return negative errno else a data byte received from the device. */
static int sample_read_byte(struct i2c_client *client, u8 addr)
{
    int rval;

    rval = i2c_smbus_read_byte_data(client, addr);
    if (rval < 0) {
        LOG_ERROR("i2c read error, err %d\n", rval);
        return rval;
    }
    LOG_DEBUG("read addr:0x%02x val:0x%02x ok\n", addr, rval);

    return rval;
}

static int32_t sample_driver_i2c_probe(struct i2c_client *client,
    const struct i2c_device_id *id)
{
    struct customer_data  *cust_data = NULL;
    struct device *dev;
    struct device_node *np;
    int rc;
    
    dev = &client->dev;
    np = client->dev.of_node; 

    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
        LOG_ERROR( "%s :: i2c_check_functionality failed",
             client->name);
        rc = -EFAULT;
        return rc;
    }

    // alloc customer struct data
    cust_data = kzalloc(sizeof(*customer_struct_data), GFP_KERNEL);
    if (!cust_data)
        return -ENOMEM;

    i2c_set_clientdata(client, cust_data);

   // TODO: parse dt

   // TODO: init power, gpio, clk, irq

   // TODO: register subsystem driver

    return rc;

free_ctrl:
    kfree(cust_data);
    return rc;
}


static int32_t sample_driver_i2c_remove(struct i2c_client *client)
{
    struct customer_data     *cust_data =
        i2c_get_clientdata(client);

    if (!cust_data) {
        LOG_ERROR("cust_data  is NULL");
        return -EINVAL;
    }

    // TODO : shutdown device
    
    // TODO : unreister subsystem driver

    // TODO : release resource

    // TODO: Free Allocated Mem

    kfree(cust_data);

    return 0;
}

static const struct of_device_id sample_driver_dt_match[] = {
    {.compatible = "vendor,device_name"},
    {}
};
MODULE_DEVICE_TABLE(of, sample_driver_dt_match);

/* not use id_table match, use  of_match_table */
//static const struct i2c_device_id i2c_id[] = {
//    {SAMPLE_DRIVER_I2C, (kernel_ulong_t)NULL},
//    { }
//};

static struct i2c_driver sample_driver_i2c = {
   // .id_table = i2c_id,
    .probe  = sample_driver_i2c_probe,
    .remove = sample_driver_i2c_remove,
    .driver = {
        .name = SAMPLE_DRIVER_I2C,
        .of_match_table = sample_driver_dt_match,
    },
};

module_i2c_driver(sample_driver_i2c);

MODULE_DESCRIPTION("sample_driver");
MODULE_LICENSE("GPL v2");

九:platform driver

#include <linux/platform_device.h>

static int32_t sample_driver_platform_probe(
    struct platform_device *pdev)
{
    struct customer_data  *cust_data = NULL;
    struct device *dev;
    struct device_node *np;
    int rc;
    
    dev = &pdev->dev;
    np = pdev->dev.of_node; 

    LOG_FUNC_ENTER();

   // TODO: parse dt

   // TODO: init power, gpio, clk, irq

   // TODO: register subsystem driver

  // TODO: create driver attr, debufs

    return rc;
}

static int32_t sample_platform_remove(
    struct platform_device *pdev)
{
    LOG_FUNC_ENTER();
    // TODO : shutdown device
    
    // TODO : unreister subsystem driver

    // TODO : release resource

    // TODO: Free Allocated Mem
    return 0;
}

static const struct of_device_id sample_driver_dt_match[] = {
    {.compatible = "vendor,device_name"},
    {}
};
MODULE_DEVICE_TABLE(of, sample_driver_dt_match);

struct platform_driver sample_platform_driver = {
    .probe = sample_driver_platform_probe,
    .driver = {
        .name = "qcom,actuator",
        .owner = THIS_MODULE,
        .of_match_table = sample_driver_dt_match,
        .suppress_bind_attrs = true,
    },
    .remove = sample_platform_remove,
};

static int __init sample_driver_init(void)
{
    int32_t rc = 0;

    rc = platform_driver_register(&sample_platform_driver);
    if (rc < 0) {
        LOG_ERROR(
            "platform_driver_register failed rc = %d", rc);
        return rc;
    }

    return rc;
}
module_init(sample_driver_init);

static void __exit sample_driver_exit(void)
{
    platform_driver_unregister(&sample_platform_driver);

}
module_exit(sample_driver_exit);

MODULE_DESCRIPTION("sample_driver");
MODULE_LICENSE("GPL v2");

十:log

#define LOG_TAG "[sample_driver]"
#define LOG_DEBUG_EN (1)

#if LOG_DEBUG_EN
#define LOG_DEBUG(fmt, args...) do { \
    printk(LOG_TAG "D %s line %d:"fmt"\n", __func__, __LINE__, ##args); \
} while (0)

#define LOG_FUNC_ENTER() do { \
    printk(LOG_TAG "D %s line %d: Enter\n", __func__, __LINE__); \
} while (0)

#define LOG_FUNC_EXIT() do { \
    printk(LOG_TAG "D %s line %d: Exit\n", __func__, __LINE__); \
} while (0)
#else /* #if LOG_DEBUG_EN*/
#define LOG_DEBUG(fmt, args...)
#define LOG_FUNC_ENTER()
#define LOG_FUNC_EXIT()
#endif

#define LOG_INFO(fmt, args...) do { \
    printk(KERN_INFO LOG_TAG "I %s line %d:"fmt"\n", __func__, __LINE__, ##args); \
} while (0)

#define LOG_ERROR(fmt, args...) do { \
    printk(KERN_ERR LOG_TAG "E %s line %d:"fmt"\n", __func__, __LINE__, ##args); \
} while (0)

十一:完成量

#include <linux/completion.h>

//struct completion preisp_wp_comp;
//init_completion(&preisp_wp_comp);
static DECLARE_COMPLETION(preisp_wp_comp);




wait_for_completion(&preisp_wp_comp);
//if (wait_for_completion_timeout(&preisp_wp_comp, msecs_to_jiffies(2000)) == 0)
//   pr_err("%s: timeout! fail wake up preisp\n", __func__);


complete(&preisp_wp_comp);

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容