以ADC设备驱动了解设计流程:

举报
内核笔记 发表于 2021/06/08 22:40:15 2021/06/08
【摘要】 驱动设计流程: module -> bus -> char -> mknod -> resource -> hardware -> noblock/signal -> lock(sem) ->module #include <linux/init.h> #include <linux/module...

驱动设计流程:

module -> bus -> char -> mknod -> resource -> hardware -> noblock/signal -> lock(sem)

->module

#include <linux/init.h>
#include <linux/module.h>

int __init adc_init(void)
{ printk("%s\n", __func__); return 0;
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
void __exit adc_exit(void)
{ printk("%s\n", __func__);
}
  
 
  • 1
  • 2
  • 3
  • 4
module_init(adc_init);
module_exit(adc_exit);
  
 
  • 1
  • 2

–>bus

#include <linux/platform_device.h>

int __init adc_init(void)
{ printk("%s\n", __func__); platform_driver_register(&adc_driver); return 0;
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
struct platform_driver adc_driver = { .probe = adc_probe, .remove = adc_remove,

// 和BSP代码中的设备匹配 .id_table = adc_ids, .driver = { .name = "adc", .owner = THIS_MODULE,
// 设备列表(和设备树中的设备列表进行匹配)
// .of_match_table = ... },
};
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

设备列表(驱动多类相近设备时)

struct platform_device_id adc_ids[] = { [0] = { .name = "s3c-adc", }, {/* end */}
};
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

MODULE_DEVICE_TABLE(platform, adc_ids);

adc_probe中暂时不做任何操作。

—>char

struct adc_cdev{ struct cdev cdev; dev_t devno; // 增加设备属性 //...
};
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
int adc_probe(struct platform_device *pdev)
{ int ret = 0; struct adc_cdev *adc; adc = kmalloc(sizeof(struct adc_cdev), GFP_KERNEL); platform_set_drvdata(pdev, adc); cdev_init(&adc->cdev, &fops); adc->cdev.owner = THIS_MODULE;

/*
 * @brief 动态分配设备编号
 * @param[out] dev 设备编号(第一个)
 * @param[in] firstminor 分配第一个次编号
 * @param[in] count 分配编号数量
 * @param[in] name 设备名称(在/proc/devices文件中可见) 
 * @return =0 分配成功
 * <0 错误码
 * int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
 */ ret = alloc_chrdev_region(&adc->devno, 0, 1, "adc"); ret = cdev_add(&adc->cdev, adc->devno, 1); //mknod //此部分见下一节: return ret;
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

cdev_init中添加的fops:

struct file_operations fops = {
.open = adc_open,
.release = adc_release,
.read = adc_read,
.unlocked_ioctl = adc_unlocked_ioctl,
};
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

重点看下:adc_open

int adc_open(struct inode *inode, struct file *filp)
{ struct adc_cdev *adc = container_of(inode->i_cdev, struct adc_cdev, cdev); filp->private_data = adc; return 0;
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

—->mknod

需要添加的头文件:
copy_to_user / copy_from_user

#include <linux/uaccess.h>

#include <linux/ioctl.h>
  
 
  • 1
  • 2
  • 3

kmalloc

#include <linux/slab.h>
  
 
  • 1

mknod

#include <linux/device.h>
  
 
  • 1

struct class * adc_class;

adc_probe中添加如下:

device = device_create(adc_class, NULL, adc->devno, NULL, "adc");
if (IS_ERR(device)){ ret = PTR_ERR(device); goto err_device_create;
}

err_alloc_chrdev_region: kfree(adc);
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

adc_remove

int adc_remove(struct platform_device *pdev)
{ struct adc_cdev *adc = platform_get_drvdata(pdev); printk("%s\n", __func__); device_destroy(adc_class, adc->devno); cdev_del(&adc->cdev); unregister_chrdev_region(adc->devno, 1); kfree(adc); return 0;
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

int __init adc_init(void)中添加

class_create(THIS_MODULE, "adc");
  
 
  • 1

void __exit adc_exit(void)中添加:

class_destroy(adc_class);
  
 
  • 1

现在框架好了 :

需要添加硬件相关的操作:

—–>hardware

实现步骤:

  1. clk设置
    1.1 获取时钟
    1.2 使能时钟
    1.3 设置时钟频率(本驱动不做)

  2. IOMEM配置
    2.1 获取(从设备中获取)
    2.2 申请/注销
    2.3 映射/取消映射
    2.4 使用

  3. IRQ配置
    3.1 获取号
    3.2 实现ISR
    3.3 注册中断

  4. 硬件驱动
    adc_init
    adc_read
    adc_set_resolution

  5. 读(阻塞)
    5.1 创建等待队列
    5.1.1 分配内存
    设备的结构体

    5.1.2 初始化
    probe

    5.2 在需要等待的地方等待
    file_operations : read

    5.3 在需要唤醒的地方唤醒
    adc_isr

—–>noblock

  1. 非阻塞(read/write)
    1.1 read
  2. IO多路复用
    2.1 创建等待队列
    已经完成
    2.2 poll(file_operations)
    2.3 唤醒
    已经完成

—–>signal

  1. 创建异步信号队列指针
    1.1 分配内存
    1.2 初始化
  2. 创建异步信号队列
    销毁异步信号队列
  3. 设备就绪时,发送信号给应用程序

—–>lock(sem)

  1. 确定竞态代码范围
    1.1 确定并发代码
    file_operations和中断处理函数

    1.2 确定同时访问共享资源的代码
    file_operations: read、ioctl、poll和adc_isr
    file_operations和adc_isr之间没用共享资源

    1.3 确定访问独占共享资源的代码

  2. 解决竞态(自旋锁、信号量)
    2.1 创建信号量
    2.1.1 分配内存
    设备结构体

    2.1.2 初始化
    probe

    2.2 获取信号量/释放信号量

文章来源: xuesong.blog.csdn.net,作者:内核笔记,版权归原作者所有,如需转载,请联系作者。

原文链接:xuesong.blog.csdn.net/article/details/81750759

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。