03 - 总线之平台总线

//=================================================================================================================================
涉及函数与结构体
struct platform_device
struct platform_driver
platform_device_register()
platform_device_unregister()
struct file_operations
kmalloc()
register_chrdev()
class_create()
device_create()
MKDEV()
platform_get_resource()
ioremap()
resource_size()
device_destroy()
class_destroy()
unregister_chrdev()
kfree()
platform_driver_register()
platform_driver_unregister()
readl()
writel()
//=================================================================================================================================
[1] >> 平台总线
-->>平台总线结构如下
-----------------------------------------------------------------------------------------------------------------------------------	
		← ← ← ← ← ← ← ← ← ← ← ← ← ←  ← ← ← ← ← ← ← ← ←  ← ← ← ← ← ←← ← ← ← ← ← ← ← ← ← ← ←			逻辑注册链表
		↓ 																				 ↑			↗		 
	[platform_driver1.ko] --> [platform_driver2.ko] --> [platform_driver3.ko] --> [platform_driver4.ko]	   
-----------------------------------------------------------------------------------------------------------------------------------
														platform_bus总线					 
-----------------------------------------------------------------------------------------------------------------------------------
	[platform_device1.ko] --> [platform_device2.ko] --> [platform_device3.ko] --> [platform_device4.ko]	 硬件注册链表      resourse
		↑																			    ↓				 
		← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ←



-----------------------------------------------------------------------------------------------------------------------------------
//platform_bus系统自动创建
//platform bus是bus总线的扩展 #bus总线使用用户自定义结构体保存数据,平台总线提供resourse结构体存储设备私有数据
//device对象:
struct platform_device{
	const char*name;     //用于做匹配int id:
	int id;  			 //一般取-1
	struct device dev;  //继承了 device父类
	u32 num_resources; //资源个数
	struct resource *resource; //资源
} 
//driver对象
struct platform_driver {
	int* probe)( struct platform device*;  //probe(探针)
	int* remove)( struct platform device*;
	struct device_driver driver: 			   //继承了 driver父类
	const struct platform_device id* id_table://如果 driver支持多个平台,在列表中写出来
}
//=================================================================================================================================

平台总线实验(利用平台总线驱动字符设备LED)

platform_device.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#define FUNC_IN(FILE,FUNCTION)  (printk("\n>>kerner:=======%s:%s IN========\n",(FILE),(FUNCTION)))
#define FUNC_OUT(FILE,FUNCTION)  (printk("\n>>kerner:=======%s:%s OUT========\n",(FILE),(FUNCTION)))
MODULE_LICENSE("GPL");

void pdev_release(struct device *);
static void  platform_device_exit(void);
static int platform_device_init(void);
module_init(platform_device_init);
module_exit(platform_device_exit);


//平台总线的resource结构体资源用于存放平台的所有设备的resource资源
struct resource	 pdev_resource[] = 
{
	{		
		//起始物理地址与结束地址,32位ARM芯片,两个寄存器(contorl+data)=32*2位=8字节
		.start =  0x11000100,	
		.end = 0x11000108,  

		//名字起:设备资源1
		.name = "pdev_resource1",
		//设备资源的类型:寄存器地址 --> IORESOURCE_MEM ; 中断地址 --> IORESOURCE_IRQ
		.flags = IORESOURCE_MEM
	},
	{
		
		//起始物理地址与结束地址,32位ARM芯片,两个寄存器(contorl+data)=32*2位=8字节
		.start = 0x11000060,
		.end = 0x11000068,
		//名字:设备资源2
		.name = "pdev_resource2",
		//设备资源的类型:寄存器地址 --> IORESOURCE_MEM ; 中断地址 --> IORESOURCE_IRQ
		.flags = IORESOURCE_MEM
	}
};

//平台设备对象:设备名+设备资源+
struct platform_device  pdev =
{
	//名字:platform总线下的设备文件名; --> platform/devices/ARM_dev
	.name = "ARM_dev",
	.id = -1,

	//总线设备资源的空间大小
	.num_resources = ARRAY_SIZE(pdev_resource),

	//指向总线设备资源
	.resource = &pdev_resource,

	//platform总线是传统bus总线的拓展与标准化(传统bus总线用户自定义私有资源结构体,现在统一存储在resource结构体)
	.dev = 
	{
		.release = pdev_release,
	}
};

static int platform_device_init(void)
{
	FUNC_IN(__FILE__,__FUNCTION__);
	//第一步:注册平台总线设备
	platform_device_register(&pdev);
	FUNC_OUT(__FILE__,__FUNCTION__);
	return 0;
}



static void  platform_device_exit(void)
{
	FUNC_IN(__FILE__,__FUNCTION__);
	//第一步:注消平台总线设备
	platform_device_unregister(&pdev);
	FUNC_OUT(__FILE__,__FUNCTION__);
}
void pdev_release(struct device *dev)
{
			
}

platform_driver.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/fs.h>
#include<linux/kdev_t.h>
#include<linux/io.h>
#include <linux/slab.h> 
#include <linux/uaccess.h> 
#define FUNC_IN(FILE,FUNCTION)  (printk("\n>>kerner:=======%s:%s IN========\n",(FILE),(FUNCTION)))
#define FUNC_OUT(FILE,FUNCTION)  (printk("\n>>kerner:=======%s:%s OUT========\n",(FILE),(FUNCTION)))

ssize_t cdev_led1_read (struct file *, char __user *, size_t , loff_t *);      //对接应用层的read函数
ssize_t cdev_led1_write (struct file *, const char __user *, size_t , loff_t *);//对接应用层的write函数
int cdev_led1_open (struct inode *, struct file *);                             //对接应用层的open函数
int cdev_led1_close (struct inode *, struct file *);  						   //对接应用层的close函数
int pdrv_probe(struct platform_device *);   //同理传统总线的probe()接口
int pdrv_remove(struct platform_device  *);//同理传统总线的remove()接口,在离开总线后调用
int pdrv_resume(struct platform_device *); 
static int platform_driver_init(void);
static void  platform_driver_exit(void);
MODULE_LICENSE("GPL");

//打包零散的变量信息
struct CLED {
	unsigned int major;
	struct class *class_cled;
	struct device *cdevice;
	struct resource * cresource;
	unsigned long *addr_start;
};
struct CLED *cled;

//注意文件操作结构体不能放在CLED结构体里面
const struct file_operations cled_fops = 
{
	.read = cdev_led1_read,        
	.write= cdev_led1_write,
	.open= cdev_led1_open,
	.release = cdev_led1_close,
};  

//设备驱动id表 ; 
const struct platform_device_id id_table[] = 
{
	//platform_bus首先对比platform_device.name与platform_device_id.name进行一一匹配
	//匹配后直接进入驱动的probe接口
	{
		.name = "ARM_dev",
		.driver_data = 1111,  //编号
	},
	{
		.name = "cortex-A8_dev",
		.driver_data = 2222,
	},
	{
		.name = "cortex-A9_dev",
		.driver_data = 333,
	},
};


//同bus的probe接口,接收平台总线传过来的平台设备信息
int pdrv_probe(struct platform_device * pdev)
{
	FUNC_IN(__FILE__,__FUNCTION__);
	//第一步:为设备申请内核栈内存,存放设备变量
	cled = kmalloc(sizeof(struct CLED),GFP_KERNEL);
	if(cled == NULL)
	{
		printk("\n >>%s:kmalloc error",__FUNCTION__);
	}

	//注册设备为LED01(register是注册,unregister是注销) --> /proc/devices/LED01
	cled->major = register_chrdev(0,"LED01",&cled_fops);
	if(cled->major == 0)
	{
		printk("\n >>%s:register_chrdev error",__FUNCTION__);
		goto error01;
	}

	//为设备划分为"class_yqj"这一类class -->/sys/class/class_yqj
	cled->class_cled = class_create(THIS_MODULE, "class_yqj");  
	if(cled->class_cled == NULL)
	{
		printk("\n >>%s:class_create error",__FUNCTION__);
		goto error02;
	}

	//创建设备节点(设备文件) ; MKDEV()返回设备号 ; 设备号 = 主设备号+次设备号 ; 
	//设备节点的名字叫做"cdev01_node"  -->  /dev/cdev01_node
	cled->cdevice = device_create(cled->class_cled,NULL,MKDEV(cled->major,01),NULL,"cdev01_node");
	if(cled->cdevice <0)
	{
		printk("\n >>%s:device_create error",__FUNCTION__);
		goto error03;
	}
	
	//从平台总线传过来的设备对象,获取其中resource结构体数组的第几个resource设备资源
	cled->cresource = platform_get_resource(pdev,IORESOURCE_MEM, 0);
	if(cled->cdevice == NULL)
	{
		printk("\n >>%s:platform_get_resource error",__FUNCTION__);
	}

	//映射物理地址 ; 
	// resource_size能返回resource设备资源的指向的物理地址开始-->物理地址结束大小
	cled->addr_start = ioremap(cled->cresource->start,resource_size(cled->cresource));
	if(cled->addr_start == NULL)
	{
		printk("\n >>%s:ioremap error",__FUNCTION__);
		goto error04;
	}
	printk("\n cled->cresource->start is %x",cled->cresource->start);
	printk("\n cled->cresource->end is %x",cled->cresource->end);
	printk("\n cled->cresource->name is %s",cled->cresource->name);
	printk("\n cled->cresource->flags is %x",cled->cresource->flags);
	FUNC_OUT(__FILE__,__FUNCTION__);
	return 0;


//错误处理

error04://销毁设备节点
	device_destroy(cled->class_cled,MKDEV(cled->major,01));

error03://类销毁
	class_destroy(cled->class_cled);

error02://注销设备
	unregister_chrdev(cled->major,"cled1");

error01://释放kmalloc内存
	kfree(cled);
	
	
}


//在module_exit移除driver模块时,会断开与平台总线的连接,该函数释放掉在probe接口中申请的资源
int pdrv_remove(struct platform_device * pdev)
{
	FUNC_IN(__FILE__,__FUNCTION__);
	//第一步:断开映射
	iounmap(cled->addr_start);
	//第二步:销毁设备节点
	device_destroy(cled->class_cled,MKDEV(cled->major,01));
	//第三步:类销毁
	class_destroy(cled->class_cled);
	//第四步:注销设备
	unregister_chrdev(cled->major,"cled1");
	//释放kmalloc内存
	kfree(cled);
	FUNC_OUT(__FILE__,__FUNCTION__);
	return 0;
}

int pdrv_resume(struct platform_device *pdev)
{	
}


//平台设备驱动结构体
struct platform_driver  pdrv = 
{
	.probe = pdrv_probe,
	.remove = pdrv_remove,
	.driver = 
	{
		//如果没有id链表,则平台设备的匹配与该name比对,该name就是设备驱动文件的名字
		.name = "ARM_dev",
	},
	.resume = pdrv_resume,
};



static int platform_driver_init(void)
{
	FUNC_IN(__FILE__,__FUNCTION__);
	//注册设备驱动
	platform_driver_register(&pdrv);
	FUNC_OUT(__FILE__,__FUNCTION__);
	return 0;
}
static void  platform_driver_exit(void)
{
	FUNC_IN(__FILE__,__FUNCTION__);
	//释放掉module_init申请的资源;注销设备驱动
	platform_driver_unregister(&pdrv);
	FUNC_OUT(__FILE__,__FUNCTION__);
}

/*cdev_led1_read() <--> read() //读取驱动文件数据
* struct file //文件路径 
* char __user //接收用户数据的缓存
* size_t      //数据个数
* loff_t      //偏移量
*/
ssize_t cdev_led1_read (struct file *cdev1_file, char __user *cdev1_buf,size_t N, loff_t *offset)
{
	return 0;
}

/*cdev_led1_write() <--> write() //向驱动文件写数据
* struct file //文件路径 
* char __user //接收用户数据的缓存
* size_t      //数据个数
* loff_t      //偏移量
*/
ssize_t cdev_led1_write (struct file *cdev1_file, const char __user *cdev1_buf, size_t N, loff_t *offset)
{

	printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__);
	unsigned int reg_vlu = 0;  					//保存寄存器映射地址的值
	char tem = ' ';
	__copy_from_user(&tem, cdev1_buf,1);     //用户层 ---数据---> 内核
	printk("\n__kernel__ :cdev_led1_write() is %c\n",tem);         
	//打印从用户层接收的数据

	if(tem == '0')  //关灯  
	{
		//这个地方为什么不用宏来控制开关灯,宏最好是那种一句代码能实现的
		reg_vlu = readl(cled->addr_start);       //读取映射地址数据
		reg_vlu |= (0x1 << 0); 
		writel(reg_vlu,cled->addr_start);        //向映射地址写数据
	
		reg_vlu = readl((cled->addr_start)+1);  //读取映射地址数据,这里+1是加4字节 = 数据寄存器的地址
		reg_vlu  &= ~(0x1<<0);
		writel(reg_vlu,(cled->addr_start)+1);        //向映射地址写数据
		printk("\n__kernel__ :%s:close led\n",__FUNCTION__);
	}
	if(tem == '1')  //开灯
	{	
		reg_vlu = readl(cled->addr_start);       //读取映射地址数据
		reg_vlu |= (0x1 << 0); 
		writel(reg_vlu,cled->addr_start);        //向映射地址写数据
		
		reg_vlu = readl((cled->addr_start)+1);       //读取映射地址数据
		reg_vlu  |= (0x1<<0);
		writel(reg_vlu,(cled->addr_start)+1);        //向映射地址写数据	
	}
	printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__);
	return 0;
}

/*cdev_led1_open() <--> open() 
* struct inode //文件文件描述符 
* struct file //文件路径
*/
int cdev_led1_open (struct inode *cdev1_inode, struct file *cdev1_file)
{
	printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__);
	printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__);
	return 0;
}
/*cdev_led1_close() <--> close() 
* struct inode //文件文件描述符 
* struct file //文件路径
*/
int cdev_led1_close (struct inode *cdev1_inode, struct file *cdev1_file)
{
	printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__);
	printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__);
	return 0;
}
module_init(platform_driver_init);
module_exit(platform_driver_exit);

app.c (这个应用层的测试文件与字符驱动那个是同一个)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

//用户层函数接收main传参
void main(int argc,char*argv[])
{	
	printf("%s",argv[1]);
	printf("\n=====__app__:in=====\n");
	int ret,tem,fd = 0;	
	char ledstate;     
	//第一步:接收用户开关指令,"1" = 开 ; "0" = 关
	ledstate = *(char*)argv[1];
	//第二步:打开驱动节点
	fd = open("/dev/cdev01_node",O_RDWR);	
	if(fd == -1)
	{
		perror("\nopen error\n");
	}
	//第三步:向驱动写"0" / "1"指令
	ret = write(fd,&ledstate,1);
	if(ret == -1)
    {
        	perror("\nwrite error\n");
    }
	//第三步:读取驱动数据,并打印
	close(fd);
printf("\n=====__app__:out=====\n");
}

Makefile文件

obj-m += platform_device.o
obj-m += platform_driver.o
name1 = platform_device
name2 = platform_driver
KERNEL = /home/topeet/iTop4412_Kernel_3.0  #内核源码路径

EXE = app 
PWD = $(shell pwd)  #当前路径

GCC = /home/topeet/arm-2009q3/bin/arm-none-linux-gnueabi-gcc


all:
	make -C $(KERNEL) M=$(PWD) modules #生成驱动模块
	$(GCC) app.c -o $(EXE)  

install:
	cp $(name1).ko $(name2).ko $(EXE) /home/topeet/minilinux/yqj_file/modul_pro4/

下面是运行结果

[root@iTOP-4412]# insmod platform_device.ko  //挂载平台设备
[ 1254.993846]
[ 1254.993850] >>kerner:=======/home/topeet/share/platform_text/platform_device.c:platform_device_init IN========
[ 1255.007022]
[ 1255.007025] >>kerner:=======/home/topeet/share/platform_text/platform_device.c:platform_device_init OUT=======
[root@iTOP-4412]# insmod platform_driver.ko  //挂载平台驱动
[ 1259.642814]
[ 1259.642833] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:platform_driver_init IN========
[ 1259.700550]
[ 1259.700567] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:pdrv_probe IN========
[ 1259.765283]  //匹配成功,进入probe接口,获取设备信息打印cresource数据
[ 1259.765298]  cled->cresource->start is 11000100
[ 1259.769907]  cled->cresource->end is 11000108
[ 1259.785121]  cled->cresource->name is pdev_resource1
[ 1259.788638]  cled->cresource->flags is 200
[ 1259.805098] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:pdrv_probe OUT========
[ 1259.834241]
[ 1259.834259] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:platform_driver_init OUT=======
[root@iTOP-4412]# ./app 0  //测试关灯
0
=====__app__:i[ 1270.924756]
[ 1270.924760] =====__kernel__ :cdev_led1_open in=====
[ 1270.931073]
[ 1270.931076] =====__kernel__ :cdev_led1_open out=====
[ 1270.937485]
[ 1270.937488] =====__kernel__ :cdev_led1_write in=====
[ 1270.943893]  
[ 1270.943896] __kernel__ :cdev_led1_write() is 0
[ 1270.949827]
[ 1270.949829] __kernel__ :cdev_led1_write:close led
[ 1270.955959]
[ 1270.955961] =====__kernel__ :cdev_led1_write out=====
[ 1270.962506]
[ 1270.962509] =====__kernel__ :cdev_led1_close in=====
[ 1270.968920]
[ 1270.968922] =====__kernel__ :cdev_led1_close out=====
n=====

=====__app__:out=====
[root@iTOP-4412]# ./app 1  //测试开灯
1
=====__app__:in[ 1273.486600]
[ 1273.486619] =====__kernel__ :cdev_led1_open in=====
[ 1273.492929]
[ 1273.492943] =====__kernel__ :cdev_led1_open out=====
[ 1273.499320]
[ 1273.499334] =====__kernel__ :cdev_led1_write in=====
[ 1273.505699]
[ 1273.505712] __kernel__ :cdev_led1_write() is 1
[ 1273.511705]
[ 1273.511719] =====__kernel__ :cdev_led1_write out=====
[ 1273.518125]
[ 1273.518138] =====__kernel__ :cdev_led1_close in=====
[ 1273.524524]
[ 1273.524537] =====__kernel__ :cdev_led1_close out=====
=====

=====__app__:out=====

[root@iTOP-4412]# cat /proc/devices  //查看注册的字符设备
Character devices:
  1 mem
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
 10 misc
 13 input
 21 sg
 29 fb
 81 video4linux
 89 i2c
108 ppp
116 alsa
128 ptm
136 pts
153 rc522_test
166 ttyACM
180 usb
188 ttyUSB
189 usb_device
204 ttySAC
216 rfcomm
243 ump
244 mali
248 LED01      <<<<<<<-------我们注册的字符设备-----------
249 mt3326-gps
250 roccat
251 BaseRemoteCtl
252 media
253 ttyGS
254 rtc

[root@iTOP-4412]# ls /sys/class/  //查看分配的类
android_usb   gpsdrv        kovaplus      net           scsi_device   ump
arvo          graphics      lcd           power_supply  scsi_disk     usb_device
backlight     i2c-adapter   lirc          ppp           scsi_generic  video4linux
bdi           i2c-dev       mali          pyra          scsi_host
block         ieee80211     mdio_bus      rc            sound
bluetooth     input         mem           rc522         spi_master
class_yqj <<<<<------我们分配的class类-------     kone          misc          regulator     switch
firmware      koneplus      mmc_host      rtc           tty

//查看我们的设备节点,在class类里面存放,也在/dev/下存放,使用软链接进行连接
[root@iTOP-4412]# ls /sys/class/class_yqj/
cdev01_node   <<<<<<我们的设备节点

[root@iTOP-4412]# ls /dev/
AGPS                i2c-7               ptmx                tty3
HPD                 input               pts                 tty4
adc                 ion                 ram0                ttyGS0
alarm               keychord            ram1                ttyGS1
android_adb         kmem                ram10               ttyGS2
ashmem              kmsg                ram11               ttyGS3
buzzer_ctl          leds                ram12               ttyS0
cdev01_node  <<<<我们的设备节点       log                 ram13               ttyS1

//我们卸载/加载模块时如果打印了其他的什么什么地址信息,什么警告等信息,就需要该代码.没有这些乱七八糟的打印信息才是正确的
[root@iTOP-4412]# rmmod platform_driver
[ 2395.918188]
[ 2395.918192] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:platform_driver_exit IN========
[ 2395.928243]
[ 2395.928246] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:pdrv_remove IN========
[ 2395.943227]
[ 2395.943230] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:pdrv_remove OUT========
[ 2395.955736]
[ 2395.955739] >>kerner:=======/home/topeet/share/platform_text/platform_driver.c:platform_driver_exit OUT========
[root@iTOP-4412]# rmmod platform_device
[ 2404.360685]
[ 2404.360702] >>kerner:=======/home/topeet/share/platform_text/platform_device.c:platform_device_exit IN========
[ 2404.373720]
[ 2404.373738] >>kerner:=======/home/topeet/share/platform_text/platform_device.c:platform_device_exit OUT========


结束









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值