學習嵌入式Linux開發——韋東山升級版全系列嵌入式視頻之總線設備驅動模型代碼分析

概述

今天看了《韋東山升級版全系列嵌入式視頻之總線設備驅動模型》這一節的視頻,看完之後感覺有一種似懂非懂的感覺,因此我對改節視頻對應源碼進行分析,結果如下:

函數關係圖

在這裏插入圖片描述

模型分析

該模型分三層:
一、驅動層

二、設備層

三、資源層

數據流向大致爲資源層->設備層->驅動層

資源層->設備層

要用到的引腳、設備數量等定義在資源層的resource數組內:

struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};

例:

static struct resource resources[] = {
        {
                .start = GROUP_PIN(3,1),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
        {
                .start = GROUP_PIN(5,8),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
};

在設備層chip_demo_gpio.c中通過platform_get_resource()函數將resource中的數據傳入設備層,並在.probe = chip_demo_gpio_probe函數中創建device,在.remove = chip_demo_gpio_remove函數中卸載device:

static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "100ask_led",
    },
};

static int chip_demo_gpio_probe(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0;

    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
        if (!res)
            break;
        
        g_ledpins[g_ledcnt] = res->start;
        led_class_create_device(g_ledcnt);
        g_ledcnt++;
    }
    return 0;
    
}

static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0;

    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
        if (!res)
            break;
        
        led_class_destroy_device(i);
        i++;
        g_ledcnt--;
    }
    return 0;
}

設備層->驅動層

創建device的函數定義在驅動層leddrv.c:led_class_create_device();led_class_destroy_device()定義在驅動層leddrv.c:

static struct class *led_class;

void led_class_create_device(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor); /* /dev/100ask_led0,1,... */
}
void led_class_destroy_device(int minor)
{
	device_destroy(led_class, MKDEV(major, minor));
}

設備層在board_demo_led_opr結構體中對硬件進行設置,並在chip_demo_gpio_drv_init()中使用register_led_operration(&board_demo_led_opr)函數將設備層中結構體led_operations board_demo_led_opr的內容傳入驅動層。
led_operations board_demo_led_opr結構體的內容:

/* 在例程中沒有對硬件進行操作,只寫了一個框架 */
static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,	//在此函數中使能時鐘、設置引腳工作模式、設置輸入輸出模式
    .ctl  = board_demo_led_ctl,		//在此函數中設置引腳電平
};

static int board_demo_led_init (int which) /* 初始化LED, which-哪個LED */       
{   
    //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    
    printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("init pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("init pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("init pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("init pin of group 3 ...\n");
            break;
        }
    }
    
    return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪個LED, status:1-亮,0-滅 */
{
    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));

    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("set pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("set pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("set pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("set pin of group 3 ...\n");
            break;
        }
    }

    return 0;
}

chip_demo_gpio_drv_init()函數:

static int __init chip_demo_gpio_drv_init(void)
{
    int err;
    
    err = platform_driver_register(&chip_demo_gpio_driver); 
    
    /* 此函數定義在驅動層leddrv.c,在此處使用將board_demo_led_opr中的內容傳入驅動層的p_led_opr */
    register_led_operations(&board_demo_led_opr);   
    
    return 0;
}

設備層leddrv.c中register_led_operration()的定義

struct led_operations *p_led_opr;
void register_led_operations(struct led_operations *opr)
{
	p_led_opr = opr;
}

然後通過p_led_opr ->init()p_led_opr ->ctl()在open和write函數中操作硬件

/* 2. 定義自己的file_operations結構體                                              */
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};

/* 3. 實現對應的open/read/write等函數,填入file_operations結構體                   */
/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根據次設備號和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根據次設備號初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

總結

總結的比較亂,但是目前我也只理解到這一步了,如果以後有了新的思路就再改吧,謝謝觀看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章