platform driver

image.png

device

需要实现的结构体是:platform_device 。

1)初始化 resource 结构变量

2)初始化 platform_device 结构变量

3)向系统注册设备:platform_device_register。

以上三步,必须在设备驱动加载前完成,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。

platform_driver_register()中添加device到内核最终还是调用的device_add函数。

Platform_device_add和device_add最主要的区别是多了一步insert_resource(p, r),即将platform资源(resource)添加进内核,由内核统一管理。

driver

驱动注册中,需要实现的结构体是:platform_driver 。

在驱动程序的初始化函数中,调用了platform_driver_register()注册 platform_driver 。

需要注意的是:platform_driver 和 platform_device 中的 name 变量的值必须是相同的【在不考虑设备树情况下,关于设备树,后面会写新的文章详细讲述】 。

这样在 platform_driver_register() 注册时,会将当前注册的 platform_driver 中的 name 变量的值和已注册的所有 platform_device 中的 name 变量的值进行比较,只有找到具有相同名称的 platform_device 才能注册成功。

当注册成功时,会调用 platform_driver 结构元素 probe 函数指针。

image.png

for example:
device:

device: 
struct resource res[]={ [0] ={
  .start = 0x139d0000,
  .end  = 0x139d0000 + 0x3,
  .flags = IORESOURCE_MEM,
 },

 [1] ={
  .start = 199,
  .end  = 199,
  .flags = IORESOURCE_IRQ,
 }, 
};static struct platform_device hello_device = 
{ .name = "duang",
 .id = -1,
 .dev.release = hello_release, 
 .num_resources = ARRAY_SIZE(res),
 .resource = res,
};

driver:

static int hello_probe(struct platform_device *pdev)
{
 printk("match ok \n");

 printk("mem = %x \n",pdev->resource[0].start);
 printk("irq = 红豆博客%d \n",pdev->resource[1].start);

 //注册中断、申请内存 return 0;
}

再来个example:

module_exit(myled_exit);
static void myled_exit(void)
{
    platform_driver_unregister(&led_drv);    
}

module_init(myled_init);

static int myled_init(void)
{
    platform_driver_register(&led_drv);     
    return 0;
}

struct platform_driver led_drv = {
    .probe      = led_probe,
    .remove     = led_remove,
    .driver     = {
        .name   = "myled",
        .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
    }
};
static const struct of_device_id of_match_leds[] = {
    { .compatible = "jz2440_led", .data = NULL },
    { /* sentinel */ }
};

static int led_probe(struct platform_device *pdev)
{
    struct resource     *res;

    /* 根据platform_device的资源进行ioremap */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res) {
        led_pin = res->start;
    }
    else {
        /* 获得pin属性 */
        of_property_read_s32(pdev->dev.of_node, "pin", &led_pin);
    }

    if (!led_pin) 
    {
        printk("can not get pin for led\n");
        return -EINVAL;
    }
        

    major = register_chrdev(0, "myled", &myled_oprs);
        //创建类,在类下面创建设备。
    led_class = class_create(THIS_MODULE, "myled");
    device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
    
    return 0;
}

myled_oprs

open release 一般是硬件资源的一些初始化在这里做
这里就是做了一些IO资源的分配处理
因为我们这里是用了MMU的,所以操作的都是虚拟地址,所以有一个物理到虚拟地址的映射,gpio寄存器的配置

static struct file_operations myled_oprs = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .write = led_write,
    .release = led_release,
};

static int led_open (struct inode *node, struct file *filp)
{
    /* 把LED引脚配置为输出引脚 */
    /* GPF5 - 0x56000050 */
    int bank = led_pin >> 16;
    int base = gpio_base[bank];

    int pin = led_pin & 0xffff;
    gpio_con = ioremap(base, 8);
    if (gpio_con) {
        printk("ioremap(0x%x) = 0x%x\n", base, gpio_con);
    }
    else {
        return -EINVAL;
    }
    
    gpio_dat = gpio_con + 1;

    *gpio_con &= ~(3<<(pin * 2));
    *gpio_con |= (1<<(pin * 2));  

    return 0;
}

static int led_release (struct inode *node, struct file *filp)
{
    printk("iounmap(0x%x)\n", gpio_con);
    iounmap(gpio_con);
    return 0;
}

static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
    /* 根据APP传入的值来设置LED引脚 */
    unsigned char val;
    int pin = led_pin & 0xffff;
    
    copy_from_user(&val, buf, 1);

    if (val)
    {
        /* 点灯 */
        *gpio_dat &= ~(1<<pin);
    }
    else
    {
        /* 灭灯 */
        *gpio_dat |= (1<<pin);
    }

    return 1; /* 已写入1个数据 */
}

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

推荐阅读更多精彩内容