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

驱动设计流程:

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;
}
void __exit adc_exit(void)
{
    printk("%s\n", __func__);
}
module_init(adc_init);
module_exit(adc_exit);

–>bus

#include <linux/platform_device.h>

int __init adc_init(void)
{
    printk("%s\n", __func__);
    platform_driver_register(&adc_driver);
    return 0;
}
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 = ...
    },
};

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

struct platform_device_id adc_ids[] = {
    [0] = {
        .name = "s3c-adc",
    },
    {/* end */}
};

MODULE_DEVICE_TABLE(platform, adc_ids);

adc_probe中暂时不做任何操作。

—>char

struct adc_cdev{
    struct cdev cdev;
    dev_t devno;
    // 增加设备属性
    //...
};
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;
}

cdev_init中添加的fops:

struct file_operations fops = {
.open = adc_open,
.release = adc_release,
.read = adc_read,
.unlocked_ioctl = adc_unlocked_ioctl,
};

重点看下: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;
}

—->mknod

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

#include <linux/uaccess.h>

#include <linux/ioctl.h>

kmalloc

#include <linux/slab.h>

mknod

扫描二维码关注公众号,回复: 2823417 查看本文章
#include <linux/device.h>

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

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

int __init adc_init(void)中添加

class_create(THIS_MODULE, "adc");

void __exit adc_exit(void)中添加:

class_destroy(adc_class);

现在框架好了 :

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

—–>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 获取信号量/释放信号量

猜你喜欢

转载自blog.csdn.net/qq_33487044/article/details/81750759
今日推荐