Linux Platform驱动模型(三) _platform+cdev

时间:2022-05-13 15:18:26

平台总线是一种实现设备信息与驱动方法相分离的方法,利用这种方法,我们可以写出一个更像样一点的字符设备驱动,即使用cdev作为接口,平台总线作为分离方式:

xjkeydrv_init():模块加载函数

└──platform_driver_register()将驱动对象模块注册到平台总线

        └──platform_driver.probe()探测函数,提取相应的信息

                └──xjkey_init():初始化cdev对象,创建设备文件等关于cdev接口创建的工作

                        └──cdev_init():将cdev结构与fops绑定到一起,在fops实现操作接口与控制硬件的逻辑

static struct class *cls = NULL;

static int major = 0;
static int minor = 0;
const int count = 1; #define DEVNAME "xjkey" static struct cdev *xjkeyp = NULL; static unsigned long irqflags;
static int irq; static atomic_t tv; #if 0
/{
key@26{
compatible = "xj4412,key";
interrupt-parent = <&gpx1>;
interrupts = <2 2>;
};
};
#endif static irqreturn_t handler_t(int irq, void *dev_id)
{
printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__);
return IRQ_HANDLED;
} //打开设备
static int xjkey_open(struct inode *inode, struct file *filp)
{
//get major and minor from inode
printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
imajor(inode), iminor(inode), __FILE__, __func__, __LINE__); if(!atomic_dec_and_test(&tv)){
atomic_inc(&tv);
return -EBUSY;
} return request_irq(irq, handler_t, irqflags, DEVNAME, NULL);
} //关闭设备
static int xjkey_release(struct inode *inode, struct file *filp)
{
//get major and minor from irqflagsinode
printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
imajor(inode), iminor(inode), __FILE__, __func__, __LINE__); free_irq(irq, NULL); atomic_inc(&tv);
return 0;
} static struct file_operations fops = {
.owner = THIS_MODULE,
.open = xjkey_open,
.release= xjkey_release,
}; static int xjkey_init(void)
{
dev_t devnum;
int ret, i; struct device *devp = NULL; //get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
current->comm, current->pid, __FILE__, __func__, __LINE__); //1. alloc cdev objxjkey_init
xjkeyp = cdev_alloc();
if(NULL == xjkeyp){
return -ENOMEM;
} //2. init cdev obj
cdev_init(xjkeyp, &fops); ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME);
if(ret){
goto ERR_STEP;
}
major = MAJOR(devnum); //3. register cdev obj
ret = cdev_add(xjkeyp, devnum, count);
if(ret){
goto ERR_STEP1;
} cls = class_create(THIS_MODULE, DEVNAME);
if(IS_ERR(cls)){
ret = PTR_ERR(cls);
goto ERR_STEP1;
} for(i = minor; i < (count+minor); i++){
devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i);
if(IS_ERR(devp)){
ret = PTR_ERR(devp);
goto ERR_STEP2;
}
} // init atomic_t
atomic_set(&tv, 1); //get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok.\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
return 0; ERR_STEP2:
for(--i; i >= minor; i--){
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls); ERR_STEP1:
unregister_chrdev_region(devnum, count); ERR_STEP:
cdev_del(xjkeyp); //get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - fail.\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
return ret;
} static void xjkey_exit(void)
{
int i;
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave.\n",
current->comm, current->pid, __FILE__, __func__, __LINE__); for(i=minor; i < (count+minor); i++){
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls); unregister_chrdev_region(MKDEV(major, minor), count);
cdev_del(xjkeyp);
} static int xjkey_probe(struct platform_device *pdev)
{
struct resource *irq_res;
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(irq_res){
irq = irq_res->start;
irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
}else{
printk(KERN_INFO "No 0 irq\n");
return -EINVAL;
} return xjkey_init();
} static int xjkey_remove(struct platform_device *pdev)
{
printk(KERN_INFO "%s : %s : %d - leave.\n", __FILE__, __func__, __LINE__);
xjkey_exit();
return 0;
} struct of_device_id of_tbl[] = {
{.compatible = "xj4412,key",},
{},
};
MODULE_DEVICE_TABLE(of, of_tbl); //1. alloc obj
static struct platform_driver xjkeydrv = {
.probe = xjkey_probe,
.remove = xjkey_remove, .driver = {
.name = DEVNAME,
.of_match_table = of_tbl,
},
}; //3. register obj
module_platform_driver(xjkeydrv); MODULE_LICENSE("GPL");