首頁>技術>

什麼是匯流排:

通俗來說就是:一個匯流排是處理器和一個或多個裝置之間的通道。所有的裝置都透過一個匯流排連線,比如STM32的APB2匯流排,在其上面掛載著相應的外設(GPIOx等外設)。

驅動程式分離/分層的概念:

回顧之前講解的控制LED亮滅的Linux驅動程式,我們瞭解到,想要點亮LED主要需要兩個部分:1,硬體部分:設定相應的硬體,比如:重對映某個暫存器的地址,然後設定為輸出模式。2,軟體部分:編寫相應的驅動程式碼,比如註冊驅動裝置等。

當我們修改硬體連線,即換成其它引腳控制LED時,那麼我們按照之前的方法就是重新編寫整個程式,這樣顯得比較麻煩。此時就引入一個分離/分層的概念,通俗來講就是,將上述所說的兩部分(硬體部分和軟體部分)分離出來,以後修改硬體連線時,就直接修改硬體部分,而那些通用的軟體部分(所謂通用:就是不管我們的燈連線在哪個引腳,但都需要註冊相應的裝置,這些註冊程式碼就通用的)就不用修改,這樣就可以減少開發時間。

問題:怎麼將上述分離/分層的兩個部分連線起來?

方法:引入我們上述講的“匯流排”,即使用一個虛擬匯流排掛接兩個部分(注:這裡的虛擬匯流排不是物理上真實存在的匯流排,是用軟體程式碼模擬出來的匯流排)

簡單檢視核心提供的匯流排相關程式碼:

linux-2.6.22.6\Documentation\driver-model這裡面有相應的描述檔案

匯流排型別:

struct bus_type {	char			* name;	struct subsystem	subsys;	struct kset		drivers;	struct kset		devices;	struct bus_attribute	* bus_attrs;	struct device_attribute	* dev_attrs;	struct driver_attribute	* drv_attrs;	int		(*match)(struct device * dev, struct device_driver * drv);	int		(*hotplug) (struct device *dev, char **envp, 				    int num_envp, char *buffer, int buffer_size);	int		(*suspend)(struct device * dev, pm_message_t state);	int		(*resume)(struct device * dev);};

平臺裝置

struct platform_device {	const char	* name;	u32		id;	struct device	dev;	u32		num_resources;	struct resource	* resource;};

備註:struct device dev;

device

struct device {	struct klist		klist_children;	struct klist_node	knode_parent;		/* node in sibling list */	struct klist_node	knode_driver;	struct klist_node	knode_bus;	struct device		*parent;	struct kobject kobj;	char	bus_id[BUS_ID_SIZE];	/* position on parent bus */	struct device_type	*type;	unsigned		is_registered:1;	unsigned		uevent_suppress:1;	struct device_attribute uevent_attr;	struct device_attribute *devt_attr;	struct semaphore	sem;	/* semaphore to synchronize calls to					 * its driver.					 */	struct bus_type	* bus;		/* type of bus device is on */	struct device_driver *driver;	/* which driver has allocated this					   device */	void		*driver_data;	/* data private to the driver */	void		*platform_data;	/* Platform specific data, device					   core doesn't touch it */	struct dev_pm_info	power;#ifdef CONFIG_NUMA	int		numa_node;	/* NUMA node this device is close to */#endif	u64		*dma_mask;	/* dma mask (if dma'able device) */	u64		coherent_dma_mask;/* Like dma_mask, but for					     alloc_coherent mappings as					     not all hardware supports					     64 bit addresses for consistent					     allocations such descriptors. */	struct list_head	dma_pools;	/* dma pools (if dma'ble) */	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem					     override */	/* arch specific additions */	struct dev_archdata	archdata;	spinlock_t		devres_lock;	struct list_head	devres_head;	/* class_device migration path */	struct list_head	node;	struct class		*class;	dev_t			devt;		/* dev_t, creates the sysfs "dev" */	struct attribute_group	**groups;	/* optional groups */	void	(*release)(struct device * dev);};

平臺驅動

struct platform_driver {	int (*probe)(struct platform_device *);	int (*remove)(struct platform_device *);	void (*shutdown)(struct platform_device *);	int (*suspend)(struct platform_device *, pm_message_t state);	int (*suspend_late)(struct platform_device *, pm_message_t state);	int (*resume_early)(struct platform_device *);	int (*resume)(struct platform_device *);	struct device_driver driver;};

備註:struct device_driver driver;

device_driver

struct device_driver {	const char		* name;	struct bus_type		* bus;	struct kobject		kobj;	struct klist		klist_devices;	struct klist_node	knode_bus;	struct module		* owner;	const char 		* mod_name;	/* used for built-in modules */	struct module_kobject	* mkobj;	int	(*probe)	(struct device * dev);	int	(*remove)	(struct device * dev);	void	(*shutdown)	(struct device * dev);	int	(*suspend)	(struct device * dev, pm_message_t state);	int	(*resume)	(struct device * dev);};
平臺驅動與平臺裝置的關係平臺設備註冊函式執行主要流程:

我們從上圖可知,int platform_device_register(struct platform_device * pdev) 不僅向上將設備註冊到匯流排bus上,而且最終還會檢視是否有合適的驅動,如果有就呼叫 int device_bind_driver(struct device *dev)函式進行裝置與驅動的繫結。

平臺驅動註冊函式執行主要流程:

我們從上圖可知,int platform_driver_register(struct platform_driver *drv)不僅向上將驅動註冊到匯流排bus上,而且最終還會檢視是否有合適的裝置。

關聯:

上述講解了相應的結構體和相關函式的主要執行過程,但引入一個問題:核心中這麼多的匯流排,平臺裝置和平臺驅動是怎麼知道它們是屬於同一個虛擬匯流排的?

方法:

我們可以從下圖中標記的數字1就可知道,它們在執行時會繫結到同一個總線上。

備註:數字1是:

pdev->dev.bus = &platform_bus_type;

drv->driver.bus = &platform_bus_type;

具體的平臺匯流排定義如下:

struct bus_type platform_bus_type = {	.name		= "platform",	.dev_attrs	= platform_dev_attrs,	.match		= platform_match,	.uevent		= platform_uevent,	.suspend	= platform_suspend,	.suspend_late	= platform_suspend_late,	.resume_early	= platform_resume_early,	.resume		= platform_resume,};
總結:通俗來講就是:平臺設備註冊函式在名為platform的總線上進行裝置的註冊,同時檢視是否有合適的平臺驅動,如有就進行繫結;平臺驅動註冊函式在名為platform的總線上進行驅動的註冊,同時檢視是否有合適的裝置,如有就進行繫結。上述過程需透過match函式(.match = platform_match)。
static int platform_match(struct device * dev, struct device_driver * drv){	struct platform_device *pdev = container_of(dev, struct platform_device, dev);	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);}
補充:(匯流排初始化相關呼叫過程)
1, start_kernel2, rest_init3, kernel_thread4, kernel_init5, do_basic_setup6, driver_init7, platform_bus_init8, device_register(&platform_bus);struct device platform_bus = {	.bus_id		= "platform",};9, bus_register(&platform_bus_type);struct bus_type platform_bus_type = {	.name		= "platform",	.dev_attrs	= platform_dev_attrs,	.match		= platform_match,	.uevent		= platform_uevent,	.suspend	= platform_suspend,	.suspend_late	= platform_suspend_late,	.resume_early	= platform_resume_early,	.resume		= platform_resume,};
編寫程式驗證

編寫一個簡單的控制LED的程式,分別是兩個C檔案:一個編寫平臺裝置,一個編寫平臺驅動。

1,編寫平臺裝置

//平臺裝置相關程式碼用於硬體相關#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/irq.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <linux/platform_device.h>static struct resource led_dev_resource[] = {      [0] = {	  	.start          =  0x56000050,	       .end            =  0x56000058,	       .name        = "doubixioaohanhan",	       .flags          = IORESOURCE_MEM,      	},	[1] = {	  	.start          =  4,	       .end            =  4,	       .flags          = IORESOURCE_IRQ,      	},};static void  led_dev_release(struct device * dev){	printk("doubixiaohanhan");	}static struct platform_device led_dev = {       .name           = "led_bus",       .id                 = 0,       .resource       = led_dev_resource,       .num_resources  = ARRAY_SIZE(led_dev_resource),       .dev ={		.release = led_dev_release,	 }};static int led_dev_init(void){	platform_device_register(&led_dev);	return 0;}static void  led_dev_exit(void){	platform_device_unregister(&led_dev );}module_init(led_dev_init);module_exit(led_dev_exit);MODULE_LICENSE("GPL");

2,編寫平臺驅動

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/platform_device.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <linux/device.h>#define CLASS_NAME   "doubixiaohanhan"#define OUTPUT_HIGH     1#define OUTPUT_LOW      0 int major = 0;static struct class                      *led_class;static struct class_device            *led_class_device;static volatile unsigned long         *gpiof_con;static volatile unsigned long         *gpiof_dat;static int  led_pin;static int led_drv_open(struct inode * inode, struct file * file){	*gpiof_con  &= ~(3<<(led_pin * 2));		*gpiof_con  |=  (1<<(led_pin * 2));		printk("led_pin configurate output mode");	return 0;}static ssize_t led_drv_write(struct file * file, const char __user * userbuf,		     size_t count, loff_t * off){	int val = 0;		if( copy_from_user(&val, userbuf, 1))		;	if(OUTPUT_HIGH == val)	{		*gpiof_dat  |= (1<<led_pin); 	}	else	{		*gpiof_dat  &=  ~(1<<led_pin); 	}	return 0;	}static struct file_operations led_drv_fops={	.owner = THIS_MODULE,	.open   = led_drv_open,	.write   = led_drv_write, };static int  led_drv_probe(struct platform_device *pdev){	struct resource *res;		printk("I am doubixiaohanhan probe");	//register device driver 	major = register_chrdev(0,"led",&led_drv_fops);	//create class and class-device	led_class = class_create(THIS_MODULE, CLASS_NAME);	led_class_device = class_device_create(led_class, NULL, MKDEV(major, 1), NULL, "LED");	//PORT REMAP	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);		gpiof_con = ioremap(res->start, res->end - res->start +1);	gpiof_dat = gpiof_con + 1;	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);		led_pin = res->start;	return 0;} static int led_drv_remove(struct platform_device *pdev){	printk("I am doubixiaohanhan remove");	iounmap(gpiof_con);	class_destroy(led_class);	//class_device_destroy(led_class,led_class_device);	class_device_destroy(led_class,MKDEV(major, 1));	unregister_chrdev(major,"led");		return 0;}static struct platform_driver led_drv = {	.probe		=  led_drv_probe,	.remove		=  led_drv_remove,	.driver		= {		.name	= "led_bus",		.owner     = THIS_MODULE,	},	};//drv->driver.probe = platform_drv_probe;//int (*probe)(struct platform_device *);//led_drv->driver.probe=platform_drv_probe//led_drv->led_drv_probestatic int led_drv_init(void){	platform_driver_register(&led_drv);//driver_register(&drv->driver);	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");

3,編寫應用測試程式

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>//輸入數字1熄滅LED//輸入其它字元開啟LEDint main(int argc,char **argv){	int fd;	char pin[2] = {1,0};	int val;		fd = open("/dev/LED",O_RDWR);	if(fd < 0)	{		printf("/dev/LED can't open\n");		return ;	}	printf("\n");	while(1)	{		printf("\ninput integer select light on/off\n");		scanf("%d",&val);		if(val == 1)			write(fd,&pin[0],1);		else			write(fd,&pin[1],1);	}}

4,編寫Makefile

#compile regularKERN_DIR = /work/system/linux-2.6.22.6all:	make -C $(KERN_DIR) M=`pwd` modules	rm -rf modules.order Module.symvers .PHONY:clean:	make -C $(KERN_DIR) M=`pwd` modules clean	rm -rf modules.order Module.symversobj-m   += led_dev.oobj-m   += led_drv.o

然後編譯測試成功。

10
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 5 React快捷設定讓你的工作效率提升五倍