linux驱动注册之倒影式错误处理机制

C语言没有错误处理机制,所以一旦程序某个部分出错,需要退出的时候,就需要先把之前分配的资源先释放掉再退出。

而释放的过程是很有讲究的。

一般我们遵循先申请的资源后释放,后申请的资源先释放的原则。和栈比较像。

举个例子

struct  _c_tag
{
	int *c;
};

struct  _b_tag
{
	int b;
	struct  _c_tag *c;
};

struct  _a_tag
{
	int a;
	struct  _b_tag *b;
};


void func()
{
	struct  _a_tag* _a_value ;
	
	_a_value = malloc(sizeof(struct  _a_tag));
	...
	
	
	_a_value->b = malloc(sizeof(struct  _b_tag));
	
	...
	_a_value->b->c = malloc(sizeof(struct  _c_tag));
	...

	free(_a_value->b->c);

	free(_a_value->b);
	
	free(_a_value);
	
}

可以看到,如果先free掉_a_value就无法访问 b,c两个里面分配的资源的。


一般情况下我们是这样做的。

下面是我写的一个led驱动注册的例子,可以发现若是在程序的越后面出错,错误处理就会变得越冗余一些。

static int s5pv210_led_probe(struct platform_device *pdev)
{
    /* 得到设备传过来来的数据 */
    struct s5pv210_led_platdata *pdata = pdev->dev.platform_data;
    struct s5pv210_gpio_led *led = NULL;
    int ret = -1; 

    printk(KERN_INFO"-----------s5pv210_led_probe-------------\n");

    led = kzalloc(sizeof(struct s5pv210_gpio_led),GFP_KERNEL);
    if(NULL == led)
    {   
        dev_err(&pdev->dev, "no memoey for device \n");
        return -ENOMEM;
    }   
    /* 使用gpiolib库,申请gpio */    
    if(gpio_request(pdata->gpio,pdata->name))
    {   
        printk(KERN_ERR"gpio_request fail\n");
        kfree(led);
        return -EINVAL;
    }   
    else
    {   
        /* 初始化gpio为输出 */
        gpio_direction_output(pdata->gpio,1);
    }   

    /* 把动态申请到的driver data绑定到相应的设备的私有数据中 */ 
    platform_set_drvdata(pdev, led);

    led->cdev.name = pdata->name;
    led->cdev.brightness_set = s5pv210_led_brightness_set;
    led->cdev.brightness = 0;
//  led->cdev.flags = pdata->flags;     //这个标志必须用内核定义的,自己定义的可能会和内核的重复
    led->pdata = pdata;
    
    /* 利用led设备类模型注册该设备 */
    ret = led_classdev_register(&pdev->dev,&led->cdev );
    if(ret)
    {   
        dev_err(&pdev->dev, "led classdev_register fail\n");
        kfree(led);
        gpio_free(pdata->gpio);
        return ret;
    }

    return 0;
}

观看内核代码,发现内核的错误处理更简洁一些。

下面是我借用内核驱动错误处理机制实现的一个驱动注册函数。


  //注册字符文件
static int __init chrdev_init(void)
{
    int ret = -1;

    printk(KERN_INFO "chrdev_init\n");
    //申请分配设备号    dev_num接收新分配的设备号,0次设备号起始,1次设备号个数,""设备名称
    ret = alloc_chrdev_region(&dev_num, 0, 1, "test chrdev");
    if(ret < 0)
    {   
        printk(KERN_ERR"alloc_chrdev_region fail %s(%d)\n",__FILE__,__LINE__);
        goto err_alloc_chrdev_region;
    }   
    printk(KERN_INFO"alloc_chrdev_region success... major = %d,minor = %d \n",MAJOR(dev_num),MINOR(dev_num));
    //初始化设备fops到cdev
    cdev_init(&test_cdev, &test_fops);
    //设备操作信息注册到内核中去
    ret = cdev_add(&test_cdev, dev_num, 1); 
    if(ret)
    {   
        printk(KERN_ERR"cdev_add fail %s(%d)\n",__FILE__,__LINE__);
        goto err_cdev_add;
    }   
    //动态请求内存使用
    if(!request_mem_region(GPJ0CON_PA_ADDR, 8, "led dynamic map"))
    {   
        printk(KERN_ERR"request_mem_region fail %s(%d)\n",__FILE__,__LINE__);
        goto err_request_mem_region;
    }   
    //得到物理地址对应的虚拟地址
    pGPJ0CON = ioremap(GPJ0CON_PA_ADDR, 8); 
    if(!pGPJ0CON)
    {   
        printk(KERN_ERR"ioremap fail %s(%d)\n",__FILE__,__LINE__);
        goto err_ioremap; 
    }   
    pGPJ0DAT = pGPJ0CON + 1;

    return 0;

err_ioremap:
    release_mem_region(GPJ0CON_PA_ADDR, 8); 
err_request_mem_region:
    cdev_del(&test_cdev);
err_cdev_add:
    unregister_chrdev_region(dev_num, 1); 
err_alloc_chrdev_region:
    return -EINVAL;
}

可以发现,虽然要处理的错误更多了,但不像上面的那样,在每个return 里面处理错误,而是归结到程序最后统一处理。使得整个错误处理不会显得很冗余。

这里使用goto语句也并不会显的代码很乱。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/80868787
今日推荐