//================================================================================================================================= 涉及函数与结构体 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======== 结束