1. 程式人生 > >linux驅動之分離分層的概念

linux驅動之分離分層的概念

bus_drv_dev模型:

簡單說明:

這張圖就是全部了。

device是與硬體相關的程式碼,driver是比較穩定的驅動程式碼。

當修改硬體部分的時候,只修改dev裡面的東西。

=============================================================================================================================

LED例子

下面用一個點亮LED的例子來說明這個分離的的例子:

這個驅動程式分為左右兩邊,即:dev   與  drv

在led_dev.中 分配,設定,註冊一個platform_device

在led_drv中分配,設定,註冊一個platform_driver

led_dev.c

定義這個平臺裝置的資源:

static struct resource led_resource[] = {
    [0] = {
        .start	= 0x56000010,//GPFCON的實體地址
        .end	= 0x56000010 + 8 - 1,
        .flags	= IORESOURCE_MEM,
    },
    [1] = {
        .start	= 6,// F6引腳
        .end	= 6,
        .flags	= IORESOURCE_IRQ,
    },
};

flags表示資源的型別。這裡表示引腳

定義一個平臺裝置:

struct platform_device device_led = {
    .name		= "myled",
    .id			= -1,
    .num_resources	= ARRAY_SIZE(led_resource),
    .resource		= led_resource,
	.dev={
			.release = led_release,
    },
};

在入口函式中 註冊 這個 “平臺裝置”

static int led_dev_init(void)
{
	platform_device_register(&device_led);
	return 0;
}

出口函式是解除安裝這個平臺裝置
static void led_dev_exit(void)
{
	platform_device_unregister(&device_led);
}


led_drv.c

定義一個平臺driver

static struct platform_driver led_drv = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
	}
};

這裡需要注意的是這個平臺的name和dev的平臺裝置的名字要一致。

如果平臺裝置和平臺driver匹配的上,就會呼叫這個led_driver這個函式。

實現這個probe函式:

static int  led_probe(struct platform_device *pdev)
{
	return 0;
}

在這個函式中需要完成以下工作:
  1. 註冊字元裝置
  2. 根據platfor_device的資源進行ioremap

註冊字元裝置:

major = register_chrdev(0,"myled",&led_fops);

這裡需要構造led_fops結構體:
static struct file_operations led_fops=
{
		.owner = THIS_MODULE,//這個巨集在推向編譯模組時自動建立  __this_module變數
		.open  = led_open,
		.write = led_write,
};

完成 open,write的功能函式
static int led_open(struct inode *inode,struct file *file)
{
	//配置為輸出引腳
	*gpio_con &=~(0x3<<(pin*2));
	*gpio_con |=(0x1<<(pin*2));

	return 0;
}

static ssize_t led_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
	int val;
	copy_from_user(&val,buf,count);//從使用者空間向核心空間拷貝資料
	if(val == 1)
	{
		printk("val ==1");
		*gpio_dat &=~(1<<pin);
	}
	else
	{
		printk("val ==0");
		*gpio_dat|=(1<<pin);
	}

	return 0;
}

這兩個函式在前面的博文中多次提到。但是這裡需要注意  gpio_dat和gpio_con還沒有定義,需要對映一下:
	struct resource *res;
	/*根據platform_device的資源進行ioremap*/
	res = platform_get_resource(pdev,IORESOURCE_MEM,0);
	gpio_con = ioremap(res->start,res->end - res->start + 1);
	gpio_dat = gpio_con + 1;

	res = platform_get_resource(pdev,IORESOURCE_IRQ,0);
	pin = res->start;

這部分功能就是剛才提到的 “2、根據platfor_device的資源進行ioremap ”

 platform_get_resource(pdev,IORESOURCE_IRQ,0);

是獲得pdev的第1個IORESOURCE_IRQ型別的資源。

另外還需要在probe函式中建立裝置節點:

	led_cls = class_create(THIS_MODULE,"myled");
	if(IS_ERR(led_cls))
		return PTR_ERR(led_cls);
	led_class_dev = device_create(led_cls,NULL,MKDEV(major,0),NULL,"wq_led");
	if(unlikely(IS_ERR(led_class_dev)))
		return PTR_ERR(led_class_dev);

這樣基本就完成了。

完整的led _dev.c

#include <linux/init.h>
#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/io.h>

/*分配/設定/註冊一個platform_device*/
/*這個平臺裝置的資源*/
static struct resource led_resource[] = {
    [0] = {
        .start	= 0x56000010,
        .end	= 0x56000010 + 8 - 1,
        .flags	= IORESOURCE_MEM,
    },
    [1] = {
        .start	= 6,
        .end	= 6,
        .flags	= IORESOURCE_IRQ,
    },


};

static void led_release(struct device *dev)
{

}

struct platform_device device_led = {
    .name		= "myled",
    .id			= -1,
    .num_resources	= ARRAY_SIZE(led_resource),
    .resource		= led_resource,
	.dev={
			.release = led_release,
    },
};

static int led_dev_init(void)
{
	platform_device_register(&device_led);
	return 0;
}

static void led_dev_exit(void)
{
	platform_device_unregister(&device_led);
}
module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");

完整的led_drv.c

/*分配/設定/註冊一個platform_driver*/

#include <linux/module.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/cdev.h>

#include <asm/uaccess.h>
#include <asm/io.h>

static int major;

static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;

static struct class *led_cls;//裝置類
static struct class_devices *led_class_dev;//裝置

static int pin;

static int led_open(struct inode *inode,struct file *file)
{
	//配置為輸出引腳
	*gpio_con &=~(0x3<<(pin*2));
	*gpio_con |=(0x1<<(pin*2));

	return 0;
}

static ssize_t led_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
	int val;
	copy_from_user(&val,buf,count);//從使用者空間向核心空間拷貝資料
	if(val == 1)
	{
		printk("val ==1");
		*gpio_dat &=~(1<<pin);
	}
	else
	{
		printk("val ==0");
		*gpio_dat|=(1<<pin);
	}

	return 0;
}
static struct file_operations led_fops=
{
		.owner = THIS_MODULE,//這個巨集在推向編譯模組時自動建立  __this_module變數
		.open  = led_open,
		.write = led_write,
};



static int  led_probe(struct platform_device *pdev)
{
	struct resource *res;
	/*根據platform_device的資源進行ioremap*/
	res = platform_get_resource(pdev,IORESOURCE_MEM,0);
	gpio_con = ioremap(res->start,res->end - res->start + 1);
	gpio_dat = gpio_con + 1;

	res = platform_get_resource(pdev,IORESOURCE_IRQ,0);
	pin = res->start;

	/*註冊字元裝置驅動*/
	printk("led_probe   found  led \n");
	major = register_chrdev(0,"myled",&led_fops);

	led_cls = class_create(THIS_MODULE,"myled");
	if(IS_ERR(led_cls))
		return PTR_ERR(led_cls);
	led_class_dev = device_create(led_cls,NULL,MKDEV(major,0),NULL,"wq_led");
	if(unlikely(IS_ERR(led_class_dev)))
		return PTR_ERR(led_class_dev);

	return 0;
}

static int  led_remove(struct platform_device *pdev)
{
	/*解除安裝字元裝置驅動*/
	device_unregister(led_class_dev);
	class_destroy(led_cls);
	unregister_chrdev(major,"myled");
	iounmap(gpio_con);
	/*根據platform_device的資源進行iounmap*/
	return 0;
}

static struct platform_driver led_drv = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
	}
};

static int led_drv_init(void)
{
	platform_driver_register(&led_drv);
	return 0;
}

static void led_drv_exit(void)
{
	platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");

測試程式:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/*wq_led on  開啟
 *wq_led off 關閉
 */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/wq_led",O_RDWR);
	if(fd < 0)
	{
		printf("can't open \n");
	}
	if(argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n",argv[0]);
		return 0;
	}
	if(strcmp(argv[1],"on") == 0)
	{
		val =1;
	}
	else
	{
		val =0;
	}
	write(fd,&val,4);
	return 0;
}