驱动设计流程:
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
#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
实现步骤:
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 获取信号量/释放信号量