上一篇中介绍到设备驱动如何匹配设备以及绑定设备的,在Linux系统下进行注册,这里将继续介绍probe函数的功能。
5、probe函数
Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data等。一般来说设备是不能被热插拔的,所以可以将probe()函数放在init段里面来节省driver运行时候的内存开销。
probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工作。资源、中断调用函数以及其他相关工作。
probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resource和platform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。
在完成了上面这些工作和一些其他必须的初始化操作后,就可以向系统注册我们在/dev目录下能看在的设备文件了。
/**
* irfpa_drv_probe - Probe call for the device.
//*针对设备探测驱动
* @pdev: handle to the platform device structure.
//*参数是平台设备的结构体。分配存储区,注册设备
* It does all the memory allocation and registration for the device.//0成功,其他负数
* Returns 0 on success, negative error otherwise.
**/
static int __devinit irfpa_drv_probe(struct platform_device *pdev)
{
struct resource *irfpa_regs_res;
struct resource *vdma_regs_res;
struct resource *vbuf_mem_res;
struct resource *xinfo_mem_res;
struct irfpa_drvdata *drvdata;
dev_t devt;
int retval;
//参考设备树文件中对设备包括的几个地址区间
irfpa_regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vdma_regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
vbuf_mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
xinfo_mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
if ((!irfpa_regs_res) || (!vdma_regs_res) || (!vbuf_mem_res) || (!xinfo_mem_res)) {
dev_err(&pdev->dev, "Invalid address.\n");
return -ENODEV;
}
/*组合设备号符合dev_t类型
devt = MKDEV(IRFPA_MAJOR, IRFPA_MINOR);
retval = register_chrdev_region(devt, IRFPA_DEVICES, DRIVER_NAME);
if (retval < 0)
return retval;
*/
retval = alloc_chrdev_region(&devt, IRFPA_MINOR, IRFPA_DEVICES, DRIVER_NAME);//动态分配设备编号,该函数需要传递给它指定的第一个次设备号firstminor(一般为0)和要分配的设备数count,以及设备名,调用该函数后自动分配得到的设备号保存在dev中。
if (retval < 0) {
dev_err(&pdev->dev, "alloc_chrdev_region fail.\n");
return retval;
}
drvdata = kzalloc(sizeof(struct irfpa_drvdata), GFP_KERNEL);//用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0.GFP_KERNEL 内核内存的正常分配. 可能睡眠.
if (!drvdata) {
dev_err(&pdev->dev, "Couldn't allocate device private record.\n");
retval = -ENOMEM;
goto failed0;
}
dev_set_drvdata(&pdev->dev, (void *)drvdata);
drvdata->devt = devt;
drvdata->is_open = 0;
mutex_init(&drvdata->sem);
// == request_mem_region
if (!request_mem_region(irfpa_regs_res->start, irfpa_regs_res->end - irfpa_regs_res->start + 1, DRIVER_NAME)) {
dev_err(&pdev->dev, "Couldn't lock memory region at %Lx\n", (unsigned long long) irfpa_regs_res->start);
retval = -EBUSY;
goto failed1_1;
}
if (!request_mem_region(vdma_regs_res->start, vdma_regs_res->end - vdma_regs_res->start + 1, DRIVER_NAME)) {
dev_err(&pdev->dev, "Couldn't lock memory region at %Lx\n", (unsigned long long) vdma_regs_res->start);
retval = -EBUSY;
goto failed1_2;
}
if (!request_mem_region(vbuf_mem_res->start, vbuf_mem_res->end - vbuf_mem_res->start + 1, DRIVER_NAME)) {
dev_err(&