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

477 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>