以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;
}
- 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
实现步骤:
clk设置
1.1 获取时钟
1.2 使能时钟
1.3 设置时钟频率(本驱动不做)IOMEM配置
2.1 获取(从设备中获取)
2.2 申请/注销
2.3 映射/取消映射
2.4 使用IRQ配置
3.1 获取号
3.2 实现ISR
3.3 注册中断硬件驱动
adc_init
adc_read
adc_set_resolution读(阻塞)
5.1 创建等待队列
5.1.1 分配内存
设备的结构体5.1.2 初始化
probe5.2 在需要等待的地方等待
file_operations : read5.3 在需要唤醒的地方唤醒
adc_isr
—–>noblock
- 非阻塞(read/write)
1.1 read - IO多路复用
2.1 创建等待队列
已经完成
2.2 poll(file_operations)
2.3 唤醒
已经完成
—–>signal
- 创建异步信号队列指针
1.1 分配内存
1.2 初始化 - 创建异步信号队列
销毁异步信号队列 - 设备就绪时,发送信号给应用程序
—–>lock(sem)
确定竞态代码范围
1.1 确定并发代码
file_operations和中断处理函数1.2 确定同时访问共享资源的代码
file_operations: read、ioctl、poll和adc_isr
file_operations和adc_isr之间没用共享资源1.3 确定访问独占共享资源的代码
解决竞态(自旋锁、信号量)
2.1 创建信号量
2.1.1 分配内存
设备结构体2.1.2 初始化
probe2.2 获取信号量/释放信号量
文章来源: xuesong.blog.csdn.net,作者:内核笔记,版权归原作者所有,如需转载,请联系作者。
原文链接:xuesong.blog.csdn.net/article/details/81750759
- 点赞
- 收藏
- 关注作者
评论(0)