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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容