【精品博文】Linux Platform设备及其驱动(1)

题记:

虽有嘉肴,弗食,不知其旨也;虽有至道,弗学,不知其善也。是故学然后知不足,教然后知困。知不足,然后能自反也;知困,然后能自强也。故曰:教学想长也。《兑命》曰:“学学半。”其次之谓乎。

我们的文化真是博大精深,短短的几句话,精准地描述了教与学之间的关系。学学半——教人也是学习的一半。

读研的时候,带本科生的实验课,面对千奇百怪的提问,可以深刻的感受到自己的不足——不能够讲解地让别人理解,就说明自己的理解不够透彻,做不到庖丁解牛。

在写博文的时候,也有这种感觉,想要负责任地写出一篇文章,不仅仅要在技术上说服自己,还要思考用什么样的方式才能让读者更快更好地明白自己要说的东西,甚至要考虑篇幅多长才合适,文章难写啊……

闲话休提,书归正传。

Vivado中添加axi-gpio IP核,并挂载到axi-lite总线上

添加IP核后,可以利用指令自动完成axi-lite接口的连接。这里我把自动生成的时钟和复位信号发生模块删除了,保留着应该也没有影响。

在关闭Vivado之前,记得查看一下IP的物理地址,或者在SDK中查看。后面修改dts文件的时候会用到。

制作BOOT.bin文件

这里也不用多说,网上教程也很多。

编写驱动并编译生成*.ko文件

这里给出代码,编译方法参考上一篇博文

#include <linux/kernel.h>#include <linux/module.h>#include <asm/uaccess.h>  /* Needed for copy_from_user */#include <asm/io.h>  /* Needed for IO Read/Write Functions */#include <linux/proc_fs.h>  /* Needed for Proc File System Functions */#include <linux/seq_file.h> /* Needed for Sequence File Operations */ #include <linux/platform_device.h>  /* Needed for Platform Driver Functions */ /* Define Driver Name */#define DRIVER_NAME "myled" unsigned long *base_addr;  /* Vitual Base Address */struct resource *res;  /* Device Resource Structure */unsigned long remap_size;  /* Device Memory Size */ /* Write operation for /proc/myled * ----------------------------------- * When user cat a string to /proc/myled file, the string will be stored in * const char __user *buf. This function will copy the string from user * space into kernel space, and change it to an unsigned long value. * It will then write the value to the register of myled controller, * and turn on the corresponding LEDs eventually. */static ssize_t proc_myled_write(struct file *file, const char __user * buf,size_t count, loff_t * ppos){ char myled_phrase[16]; u32 myled_value; if (count < 11) { if (copy_from_user(myled_phrase, buf, count)) return -EFAULT; myled_phrase[count] = '\0'; } myled_value = simple_strtoul(myled_phrase, NULL, 0); wmb(); iowrite32(myled_value, base_addr); return count;} /* Callback function when opening file /proc/myled * ------------------------------------------------------ * Read the register value of myled controller, print the value to * the sequence file struct seq_file *p. In file open operation for /proc/myled * this callback function will be called first to fill up the seq_file, * and seq_read function will print whatever in seq_file to the terminal. */static int proc_myled_show(struct seq_file *p, void *v){ u32 myled_value; myled_value = ioread32(base_addr); seq_printf(p, "0x%x", myled_value); return 0;}  /* Open function for /proc/myled * ------------------------------------ * When user want to read /proc/myled (i.e. cat /proc/myled), the open function  * will be called first. In the open function, a seq_file will be prepared and the  * status of myled will be filled into the seq_file by proc_myled_show function. */static int proc_myled_open(struct inode *inode, struct file *file){ unsigned int size = 16; char *buf; struct seq_file *m; int res; buf = (char *)kmalloc(size * sizeof(char), GFP_KERNEL); if (!buf) return -ENOMEM;  res = single_open(file, proc_myled_show, NULL);  if (!res) { m = file->private_data; m->buf = buf; m->size = size; } else { kfree(buf); }  return res;} /* File Operations for /proc/myled */static const struct file_operations proc_myled_operations = { .open = proc_myled_open, .read = seq_read, .write = proc_myled_write, .llseek = seq_lseek, .release = single_release};  /* Shutdown function for myled  * -----------------------------------  * Before myled shutdown, turn-off all the leds  */static void myled_shutdown(struct platform_device *pdev){ iowrite32(0, base_addr);}   /* Remove function for myled  * ----------------------------------    * When myled module is removed, turn off all the leds first,  * release virtual address and the memory region requested.  */static int myled_remove(struct platform_device *pdev){ myled_shutdown(pdev);  /* Remove /proc/myled entry */ remove_proc_entry(DRIVER_NAME, NULL);  /* Release mapped virtual address */ iounmap(base_addr);  /* Release the region */ release_mem_region(res->start, remap_size);  return 0;}  /* Device Probe function for myled  * ------------------------------------  * Get the resource structure from the information in device tree.  * request the memory regioon needed for the controller, and map it into  *  kernel virtual memory space. Create an entry under /proc file system  * and register file operations for that entry.  */static int __devinit myled_probe(struct platform_device *pdev){ struct proc_dir_entry *myled_proc_entry; int ret = 0;  res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res)  { dev_err(&pdev->dev, "No memory resource\n"); return -ENODEV; }  remap_size = res->end - res->start + 1; if (!request_mem_region(res->start, remap_size, pdev->name)) { dev_err(&pdev->dev, "Cannot request IO\n"); return -ENXIO; }  base_addr = ioremap(res->start, remap_size); if (base_addr == NULL) { dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n", (unsigned long)res->start); ret = -ENOMEM; goto err_release_region; } myled_proc_entry = proc_create(DRIVER_NAME, 0, NULL,&proc_myled_operations); if (myled_proc_entry == NULL)  { dev_err(&pdev->dev, "Couldn't create proc entry\n"); ret = -ENOMEM; goto err_create_proc_entry; }  printk(KERN_INFO DRIVER_NAME " probed at VA 0x%08lx\n", (unsigned long) base_addr);  return 0; err_create_proc_entry: iounmap(base_addr); err_release_region: release_mem_region(res->start, remap_size); return ret;} /* end of myled_probe*/ /* device match table to match with device node in device tree */static const struct of_device_id myled_of_match[] __devinitconst ={ {.compatible = "dglnt,myled-1.00.a"}, {},};  MODULE_DEVICE_TABLE(of, myled_of_match); /*platform 是一个虚拟的地址总线,相比 PCI、USB,它主要用于描述 SOC 上的片上资源。比如 S3C2410 上集成的控制器( LCD、Watchdog、RTC等),platform 所描述的资源有一个共同点:在 CPU 的总线上直接取址。*/ /* platform driver structure for myled driver */static struct platform_driver myled_driver ={ .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = myled_of_match }, .probe = myled_probe, .remove = __devexit_p(myled_remove), .shutdown = __devexit_p(myled_shutdown)};/* Register myled platform driver */module_platform_driver(myled_driver);/* Module Infomations */MODULE_AUTHOR("Digilent, Inc.");MODULE_LICENSE("GPL");MODULE_DESCRIPTION(DRIVER_NAME ": MYLED driver (Simple Version)");MODULE_ALIAS(DRIVER_NAME);

修改dts文件,将设备信息添加至设备树

reg = <0x41200000 0x1000>;里面的两个参数分别对应axi_gpio的物理地址和寄存器空间大小

还有一个坑:

两个compatible属性的内容,是设备和驱动注册的关键,匹配时利用该字段进行对比。具体如何进行匹配,后续再深入剖析。

生成dtb文件

启动系统,加载驱动

以上两步,可以参考之前的博文,这里不再重复。

输入指令,操作led

实际上这是Digilent提供的教程,做完之后,是一头雾水,根本不明白到底怎么回事,稀里糊涂地就可以操作led了。

当时想的问题有:

i) 在学习最简单的Linux驱动时,有module_init()和module_exit()函数,在加载和卸载驱动时会被调用,为什么这里没有?

ii) 这个好像和一般的设备驱动不太一样,什么是platform driver?

iii) 搜索了一下platform driver,更加迷糊了,不过platform driver和普通的设备驱动是有区别的,既然开始了,就先研究platform driver吧。

~明天继续……

本文由电子技术应用博主“cuter”发布。欢迎转载,但不得擅自更改博文内容,也不得用于任何盈利目的。转载时不得删除作者简介和作者单位简介。如有盗用而不说明出处引起的版权纠纷,由盗用者自负。

(0)

相关推荐

  • 【Linux笔记】总线设备驱动模型

    Linux内核中更是存在着更多的分离.分层思想的代码,platform平台设备驱动就是用了这样的思想.本篇笔记我们一起来学习一下platform驱动框架. Linux引入platform的原因 对于 ...

  • 【Linux笔记】LED驱动实验(总线设备驱动模型)

    前言 继续来点灯~学了一段时间的嵌入式Linux发现LED程序挺香的.. 从LED程序中我们可以榨取很多知识:基本的驱动框架.驱动的简单分层.驱动的分层+分离思想.总线设备驱动模型.设备树等. 这大多 ...

  • 点灯大师带你写一个Linux字符设备驱动

    [导读] 前一篇文章,介绍了如何将一个hello word模块编译进内核或者编译为动态加载内核模块,本篇来介绍一下如何利用Linux驱动模型来完成一个LED灯设备驱动.点一个灯有什么好谈呢?况且Lin ...

  • 【精品博文】PWM蜂鸣器驱动之引脚分配

    【精品博文】PWM蜂鸣器驱动之引脚分配

  • 【精品博文】linux make命令输出高亮

    最近调试代码,make 输出信息的时候 error .warning 等信息都是和普通信息一样白色打出来的,和普通信息一样很难区分.每次就要在茫茫多的输出信息中找那个小小的error 才看是哪一行出了 ...

  • 【精品博文】linux编程之 Core Dump

    一.Core Dump 定义 Core Dump 又叫核心转存.当程序在运行过程中发生异常,这时Linux系统可以把程序出错的内存内容存储在一个core文件中,这种过程叫 core Dump. Cor ...

  • 【精品博文】Linux应用程序地址布局

    一.程序构成 在学习Linux应用程序开发时,经常会遇到如下概念:代码段.数据段.BSS段等.BSS(Block Started by Symbol,又名:未初始化数据段).堆(heap).栈(sta ...

  • 【精品博文】Linux QT镜像的制作--制作SD卡启动盘

    最近买了个新的开发板,原生的是Android操作系统,需要自己少个启动盘,制作Linux+QT操作系统. 新的开发板带这个制作的源文件,要先把这个文件拷贝到虚拟机Ubunbtu的共享目录下. 打开sh ...

  • 【精品博文】详细解析基于FPGA的LCD1602驱动控制

    赢一个双肩背包 有多难? 戳一下试试看! →_→ 长摁识别 [主题]:详细解析基于FPGA的LCD1602驱动控制 [作者]:LinCoding [时间]:2016.11.23 这周末去找女票玩了,回 ...

  • 【精品博文】关于zynq linux开发的一点收获

    这两个月在专业方面落下了不少,下了班之后,基本都是玩.聊天.看视频什么,锻炼身体也不是很多.直到最近,一来是觉得自己有些不像话,有些事一拖再拖,二来是觉得玩得无聊,三是看到别人都那么努力,自己这样子实 ...