477 lines
15 KiB
HTML
477 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>驱动</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 ---> (每个源码目录下)提供选项 (决定哪些需要编译那些不需要)
|
||
.config ---> (源码顶层目录下)保存选择结果 (默认配置)
|
||
Makefile---> (每个源码目录下)根据.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 <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
#define LED_MAJOR 200 /* 主设备号 */
|
||
#define LED_NAME "led" /* 设备名字 */
|
||
|
||
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, &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("GPL");
|
||
MODULE_AUTHOR("sakura");
|
||
</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 <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <linux/cdev.h>
|
||
#include <linux/device.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
/* 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->private_data = &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(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
|
||
newchrled.major = MAJOR(newchrled.devid); /* 获取分配号的主设备号 */
|
||
newchrled.minor = MINOR(newchrled.devid); /* 获取分配号的次设备号 */
|
||
}
|
||
newchrled.cdev.owner = THIS_MODULE;
|
||
cdev_init(&newchrled.cdev, &newchrled_fops);
|
||
cdev_add(&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(&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("GPL");
|
||
MODULE_AUTHOR("zuozhongkai");
|
||
</code></pre>
|
||
<h4 id="platform驱动">platform驱动</h4>
|
||
<pre><code>#include <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <linux/cdev.h>
|
||
#include <linux/device.h>
|
||
#include <linux/of_gpio.h>
|
||
#include <linux/semaphore.h>
|
||
#include <linux/timer.h>
|
||
#include <linux/irq.h>
|
||
#include <linux/wait.h>
|
||
#include <linux/poll.h>
|
||
#include <linux/fs.h>
|
||
#include <linux/fcntl.h>
|
||
#include <linux/platform_device.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
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->private_data = &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(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME); /* 申请设备号 */
|
||
leddev.major = MAJOR(leddev.devid); /* 获取分配号的主设备号 */
|
||
}
|
||
leddev.cdev.owner = THIS_MODULE;
|
||
cdev_init(&leddev.cdev, &led_fops);
|
||
cdev_add(&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(&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 = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
|
||
},
|
||
.probe = led_probe,
|
||
.remove = led_remove,
|
||
};
|
||
|
||
static int __init leddriver_init(void)
|
||
{
|
||
return platform_driver_register(&led_driver);
|
||
}
|
||
|
||
static void __exit leddriver_exit(void)
|
||
{
|
||
platform_driver_unregister(&led_driver);
|
||
}
|
||
|
||
module_init(leddriver_init);
|
||
module_exit(leddriver_exit);
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_AUTHOR("zuozhongkai");
|
||
|
||
</code></pre>
|
||
<h4 id="dts的platform驱动">dts的platform驱动</h4>
|
||
<pre><code>#include <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <linux/cdev.h>
|
||
#include <linux/device.h>
|
||
#include <linux/of_gpio.h>
|
||
#include <linux/semaphore.h>
|
||
#include <linux/timer.h>
|
||
#include <linux/irq.h>
|
||
#include <linux/wait.h>
|
||
#include <linux/poll.h>
|
||
#include <linux/fs.h>
|
||
#include <linux/fcntl.h>
|
||
#include <linux/platform_device.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
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->private_data = &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(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
|
||
leddev.major = MAJOR(leddev.devid);
|
||
}
|
||
cdev_init(&leddev.cdev, &led_fops);
|
||
cdev_add(&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("/gpioled");
|
||
of_get_named_gpio(leddev.node, "led-gpio", 0);
|
||
return 0;
|
||
}
|
||
|
||
static int led_remove(struct platform_device *dev)
|
||
{
|
||
cdev_del(&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 = "atkalpha-gpioled" },
|
||
{ /* Sentinel */ }
|
||
};
|
||
|
||
static struct platform_driver led_driver = {
|
||
.driver = {
|
||
.name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
|
||
.of_match_table = led_of_match, /* 设备树匹配表 */
|
||
},
|
||
.probe = led_probe,
|
||
.remove = led_remove,
|
||
};
|
||
|
||
static int __init leddriver_init(void)
|
||
{
|
||
return platform_driver_register(&led_driver);
|
||
}
|
||
|
||
static void __exit leddriver_exit(void)
|
||
{
|
||
platform_driver_unregister(&led_driver);
|
||
}
|
||
|
||
module_init(leddriver_init);
|
||
module_exit(leddriver_exit);
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_AUTHOR("zuozhongkai");
|
||
</code></pre>
|
||
|
||
<script async src="https://cdn.jsdelivr.net/npm/katex-copytex@latest/dist/katex-copytex.min.js"></script>
|
||
|
||
</body>
|
||
</html> |