data/method/一些思考/系统移植+驱动/driver/driver.html

477 lines
15 KiB
HTML
Raw Normal View History

2024-01-29 10:44:43 +08:00
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>&#x9a71;&#x52a8;</title>
<style>
/* From extension vscode.github */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.vscode-dark img[src$=\#gh-light-mode-only],
.vscode-light img[src$=\#gh-dark-mode-only] {
display: none;
}
</style>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css">
<link href="https://cdn.jsdelivr.net/npm/katex-copytex@latest/dist/katex-copytex.min.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/markdown.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/highlight.css">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif;
font-size: 14px;
line-height: 1.6;
}
</style>
<style>
.task-list-item {
list-style-type: none;
}
.task-list-item-checkbox {
margin-left: -20px;
vertical-align: middle;
pointer-events: none;
}
</style>
</head>
<body class="vscode-body vscode-light">
<ul>
<li><a href="#%E9%A9%B1%E5%8A%A8">驱动</a>
<ul>
<li><a href="#%E9%A9%B1%E5%8A%A8%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86">驱动基础知识</a>
<ul>
<li><a href="#%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B">内核源码编译过程</a>
<ul>
<li><a href="#%E7%AC%AC%E4%B8%80%E6%AD%A5%E9%85%8D%E7%BD%AEkconfig">第一步配置Kconfig</a></li>
<li><a href="#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E9%85%8D%E7%BD%AEmakefile">第二步配置Makefile</a></li>
<li><a href="#%E7%AC%AC%E4%B8%89%E6%AD%A5-%E9%85%8D%E7%BD%AE%E4%B8%8A%E5%B1%82%E7%9B%AE%E5%BD%95%E7%9A%84makefile%E4%B8%8Ekconfig">第三步 配置上层目录的Makefile与Kconfig</a></li>
</ul>
</li>
<li><a href="#%E5%85%A5%E5%8F%A3%E5%87%BD%E6%95%B0%E5%92%8C%E5%87%BA%E5%8F%A3%E5%87%BD%E6%95%B0">入口函数和出口函数</a></li>
</ul>
</li>
<li><a href="#%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87">字符设备</a>
<ul>
<li><a href="#%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E6%A1%86%E6%9E%B6">字符设备框架</a>
<ul>
<li><a href="#%E9%9D%99%E6%80%81%E6%B3%A8%E5%86%8C%E8%AE%BE%E5%A4%87">静态注册设备</a></li>
<li><a href="#%E6%96%B0%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E5%BC%95%E7%94%A8%E4%BA%86%E5%AD%97%E7%AC%A6%E8%AE%BE%E5%A4%87%E7%BB%93%E6%9E%84%E4%BD%93">新字符设备,引用了字符设备结构体</a></li>
<li><a href="#platform%E9%A9%B1%E5%8A%A8">platform驱动</a></li>
<li><a href="#dts%E7%9A%84platform%E9%A9%B1%E5%8A%A8">dts的platform驱动</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="驱动">驱动</h1>
<h2 id="驱动基础知识">驱动基础知识</h2>
<h3 id="内核源码编译过程">内核源码编译过程</h3>
<pre><code>1.遍历每个源码目录或配置指定的源码目录Makefile
2.每个目录的Makefile 会根据Kconfig来定制要编译对象
3.回到顶层目录的Makeifle执行编译
Kconfig ---&gt; (每个源码目录下)提供选项 (决定哪些需要编译那些不需要)
.config ---&gt; (源码顶层目录下)保存选择结果 (默认配置)
Makefile---&gt; (每个源码目录下)根据.config中的内容来告知编译系统如何编译
</code></pre>
<p><strong>举例</strong></p>
<h4 id="第一步配置kconfig">第一步配置Kconfig</h4>
<pre><code>在driver目录下新建一个目录
mkdir driver/test
进入test目录创建Kconfig文件
</code></pre>
<p><img src="file:pic/0.png" alt=""></p>
<p>这里定义了一个TEST的句柄Kconfig可以通过这个句柄来控制Makefile中是否编译”Test driver”是显示在终端的名称</p>
<h4 id="第二步配置makefile">第二步配置Makefile</h4>
<p><img src="file:pic/1.png" alt=""></p>
<p>Obj-$(CONFIG_选项名) += xxx.o
/<em>当CONFIG_选项名=y时表示对应目录下的xxx.c将被编译进内核 当CONFIG_选项名=m时对应目录下的xxx.c将被编译成模块</em>/</p>
<h4 id="第三步-配置上层目录的makefile与kconfig">第三步 配置上层目录的Makefile与Kconfig</h4>
<p>在上一层目录的Kconfig中</p>
<p><img src="file:pic/2.png" alt=""></p>
<p>在上一层目录的Makefile中</p>
<p><img src="file:pic/3.png" alt=""></p>
<p>结果,运行根目录的.config查看结果</p>
<p><img src="file:pic/4.png" alt=""></p>
<h3 id="入口函数和出口函数">入口函数和出口函数</h3>
<pre><code>subsys_initcall(); //用于核心子系统的初始化,初始化时间早于module_init
module_init(); //适用于以模块形式编译的代码insmod加载驱动时会调用这个函数
module_exit();
</code></pre>
<h2 id="字符设备">字符设备</h2>
<p><img src="file:pic/5.png" alt=""></p>
<h3 id="字符设备框架">字符设备框架</h3>
<h4 id="静态注册设备">静态注册设备</h4>
<pre><code>#include &lt;linux/types.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/delay.h&gt;
#include &lt;linux/ide.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/errno.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;asm/mach/map.h&gt;
#include &lt;asm/uaccess.h&gt;
#include &lt;asm/io.h&gt;
#define LED_MAJOR 200 /* 主设备号 */
#define LED_NAME &quot;led&quot; /* 设备名字 */
static int led_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
static int __init led_init(void)
{
register_chrdev(LED_MAJOR, LED_NAME, &amp;led_fops);
return 0;
}
static void __exit led_exit(void)
{
unregister_chrdev(LED_MAJOR, LED_NAME);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;sakura&quot;);
</code></pre>
<p>static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)</p>
<blockquote>
<p>major主设备号Linux 下每个设备都有一个设备号,设备号分为主设备号和次设备号两部分<br>
name设备名字指向一串字符串。<br>
fops结构体 file_operations 类型指针,指向设备的操作函数集合变量。</p>
</blockquote>
<h4 id="新字符设备引用了字符设备结构体">新字符设备,引用了字符设备结构体</h4>
<pre><code>#include &lt;linux/types.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/delay.h&gt;
#include &lt;linux/ide.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/errno.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;linux/cdev.h&gt;
#include &lt;linux/device.h&gt;
#include &lt;asm/mach/map.h&gt;
#include &lt;asm/uaccess.h&gt;
#include &lt;asm/io.h&gt;
/* newchrled设备结构体 */
struct newchrled_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchrled_dev newchrled; /* led设备 */
static int led_open(struct inode *inode, struct file *filp)
{
filp-&gt;private_data = &amp;newchrled; /* 设置私有数据 */
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* 设备操作函数 */
static struct file_operations newchrled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
static int __init led_init(void)
{
if (newchrled.major) { /* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
} else { /* 没有定义设备号 */
alloc_chrdev_region(&amp;newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取分配号的主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取分配号的次设备号 */
}
newchrled.cdev.owner = THIS_MODULE;
cdev_init(&amp;newchrled.cdev, &amp;newchrled_fops);
cdev_add(&amp;newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&amp;newchrled.cdev);/* 删除cdev */
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */
device_destroy(newchrled.class, newchrled.devid);
class_destroy(newchrled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;zuozhongkai&quot;);
</code></pre>
<h4 id="platform驱动">platform驱动</h4>
<pre><code>#include &lt;linux/types.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/delay.h&gt;
#include &lt;linux/ide.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/errno.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;linux/cdev.h&gt;
#include &lt;linux/device.h&gt;
#include &lt;linux/of_gpio.h&gt;
#include &lt;linux/semaphore.h&gt;
#include &lt;linux/timer.h&gt;
#include &lt;linux/irq.h&gt;
#include &lt;linux/wait.h&gt;
#include &lt;linux/poll.h&gt;
#include &lt;linux/fs.h&gt;
#include &lt;linux/fcntl.h&gt;
#include &lt;linux/platform_device.h&gt;
#include &lt;asm/mach/map.h&gt;
#include &lt;asm/uaccess.h&gt;
#include &lt;asm/io.h&gt;
struct leddev_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
};
struct leddev_dev leddev; /* led设备 */
static int led_open(struct inode *inode, struct file *filp)
{
filp-&gt;private_data = &amp;leddev; /* 设置私有数据 */
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
static int led_probe(struct platform_device *dev)
{
if (leddev.major) { /* 定义了设备号 */
leddev.devid = MKDEV(leddev.major, 0);
register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
} else { /* 没有定义设备号 */
alloc_chrdev_region(&amp;leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME); /* 申请设备号 */
leddev.major = MAJOR(leddev.devid); /* 获取分配号的主设备号 */
}
leddev.cdev.owner = THIS_MODULE;
cdev_init(&amp;leddev.cdev, &amp;led_fops);
cdev_add(&amp;leddev.cdev, leddev.devid, LEDDEV_CNT);
leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
return 0;
}
static int led_remove(struct platform_device *dev)
{
cdev_del(&amp;leddev.cdev);/* 删除cdev */
unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */
device_destroy(leddev.class, leddev.devid);
class_destroy(leddev.class);
return 0;
}
static struct platform_driver led_driver = {
.driver = {
.name = &quot;imx6ul-led&quot;, /* 驱动名字,用于和设备匹配 */
},
.probe = led_probe,
.remove = led_remove,
};
static int __init leddriver_init(void)
{
return platform_driver_register(&amp;led_driver);
}
static void __exit leddriver_exit(void)
{
platform_driver_unregister(&amp;led_driver);
}
module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;zuozhongkai&quot;);
</code></pre>
<h4 id="dts的platform驱动">dts的platform驱动</h4>
<pre><code>#include &lt;linux/types.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/delay.h&gt;
#include &lt;linux/ide.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/errno.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;linux/cdev.h&gt;
#include &lt;linux/device.h&gt;
#include &lt;linux/of_gpio.h&gt;
#include &lt;linux/semaphore.h&gt;
#include &lt;linux/timer.h&gt;
#include &lt;linux/irq.h&gt;
#include &lt;linux/wait.h&gt;
#include &lt;linux/poll.h&gt;
#include &lt;linux/fs.h&gt;
#include &lt;linux/fcntl.h&gt;
#include &lt;linux/platform_device.h&gt;
#include &lt;asm/mach/map.h&gt;
#include &lt;asm/uaccess.h&gt;
#include &lt;asm/io.h&gt;
struct leddev_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
struct device_node *node; /* LED设备节点 */
int led0; /* LED灯GPIO标号 */
};
struct leddev_dev leddev; /* led设备 */
static int led_open(struct inode *inode, struct file *filp)
{
filp-&gt;private_data = &amp;leddev; /* 设置私有数据 */
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
static int led_probe(struct platform_device *dev)
{
if (leddev.major) {
leddev.devid = MKDEV(leddev.major, 0);
register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
} else {
alloc_chrdev_region(&amp;leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
leddev.major = MAJOR(leddev.devid);
}
cdev_init(&amp;leddev.cdev, &amp;led_fops);
cdev_add(&amp;leddev.cdev, leddev.devid, LEDDEV_CNT);
leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
leddev.node = of_find_node_by_path(&quot;/gpioled&quot;);
of_get_named_gpio(leddev.node, &quot;led-gpio&quot;, 0);
return 0;
}
static int led_remove(struct platform_device *dev)
{
cdev_del(&amp;leddev.cdev); /* 删除cdev */
unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */
device_destroy(leddev.class, leddev.devid);
class_destroy(leddev.class);
return 0;
}
static const struct of_device_id led_of_match[] = {
{ .compatible = &quot;atkalpha-gpioled&quot; },
{ /* Sentinel */ }
};
static struct platform_driver led_driver = {
.driver = {
.name = &quot;imx6ul-led&quot;, /* 驱动名字,用于和设备匹配 */
.of_match_table = led_of_match, /* 设备树匹配表 */
},
.probe = led_probe,
.remove = led_remove,
};
static int __init leddriver_init(void)
{
return platform_driver_register(&amp;led_driver);
}
static void __exit leddriver_exit(void)
{
platform_driver_unregister(&amp;led_driver);
}
module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;zuozhongkai&quot;);
</code></pre>
<script async src="https://cdn.jsdelivr.net/npm/katex-copytex@latest/dist/katex-copytex.min.js"></script>
</body>
</html>