写在前面:文章中的代码及截图,基本来自韦东山老师的课程。
<1>最简单的字符设备驱动:
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
//头文件的来源:抄其他调用这个函数的文件
static int major;
static int hello_open (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return size;
}
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return size;
}
static int hello_release (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 1. create file_operations */
static const struct file_operations hello_drv = {
.owner = THIS_MODULE,
.read = hello_read,
.write = hello_write,
.open = hello_open,
.release = hello_release,
};
/* 2. register_chrdev */
/* 3. entry function */
static int hello_init(void)
{
major = register_chrdev(0, "100ask_hello", &hello_drv);//0:动态分配主设备号
return 0;
}
/* 4. exit function */
static void hello_exit(void)
{
unregister_chrdev(major, "100ask_hello");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
<2>APP和driver如何交互数据:
<3>自动创建设备节点
如果不自动创建设备节点,需要使用手动创建;
自动创建设备节点的步骤:
(1)创建类:
(2)创建类之后,在类下面创建设备
<4> 字符设备的另一种创建方法
#include "asm-generic/errno-base.h"
#include "asm/cacheflush.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
static struct class *hello_class;
static struct cdev hello_cdev;
static dev_t dev;
static unsigned char hello_buf[100];
static int hello_open (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
unsigned long len = size > 100 ? 100 : size;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_to_user(buf, hello_buf, len);
return len;
}
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
unsigned long len = size > 100 ? 100 : size;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_from_user(hello_buf, buf, len);
return len;
}
static int hello_release (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 1. create file_operations */
static const struct file_operations hello_drv = {
.owner = THIS_MODULE,
.read = hello_read,
.write = hello_write,
.open = hello_open,
.release = hello_release,
};
/* 2. register_chrdev */
/* 3. entry function */
static int hello_init(void)
{
int ret;
// register_chrdev
ret = alloc_chrdev_region(&dev, 0, 2, "hello");
if (ret < 0) {
printk(KERN_ERR "alloc_chrdev_region() failed for hello\n");
return -EINVAL;
}
cdev_init(&hello_cdev, &hello_drv);
ret = cdev_add(&hello_cdev, dev, 2);
if (ret)
{
printk(KERN_ERR "cdev_add() failed for hello\n");
return -EINVAL;
}
hello_class = class_create(THIS_MODULE, "hello_class");
if (IS_ERR(hello_class)) {
printk("failed to allocate class\n");
return PTR_ERR(hello_class);
}
device_create(hello_class, NULL, dev, NULL, "hello"); /* /dev/hello */
return 0;
}
/* 4. exit function */
static void hello_exit(void)
{
device_destroy(hello_class, dev);
class_destroy(hello_class);
//unregister_chrdev(major, "100ask_hello");
cdev_del(&hello_cdev);
unregister_chrdev_region(dev, 2);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
注意到:这两种方法都有创建class和device。
<5>总线设备模型platform-分离字符设备驱动
分为:hello_drv.c 和 hello_dev.c
虚拟总线:platform_bus:两边分别是xxx_drv.c(驱动相关)和xxx_dev.c(设备相关)
从匹配的platform_device里得到版本信息,设置版本信息。
xxx_drv.c(驱动相关):
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/platform_device.h>
/* 参考: drivers\char\ppdev.c
* drivers\char\ipmi\ipmi_powernv.c
*/
static char hello_buf[100] = "ver2";
static int hello_open(struct inode *inode, struct file *file)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* APP : read(fd, buffer, len) */
static ssize_t hello_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int err;
if (count > 100)
count = 100;
err = copy_to_user(buf, hello_buf, count);
return count;
}
static int hello_release(struct inode *inode, struct file *file)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 分配/设置/注册 file_operations结构体 */
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.read = hello_read,
.open = hello_open,
.release = hello_release,
};
static int hello_probe(struct platform_device *pdev)
{
int err;
int len;
/* 从匹配的platform_device里得到版本信息
* 用来设置: hello_buf
*/
len = strlen(pdev->resource[0].name) + 1;
if (len > 100)
len = 100;
strncpy(hello_buf, pdev->resource[0].name, len);
hello_buf[len - 1] = '\0';
err = register_chrdev(88, "100ask_hello_drv", &hello_fops);
printk("register_chrdev ret = %d\n", err);
return err;
}
static int hello_remove(struct platform_device *pdev)
{
unregister_chrdev(88, "100ask_hello_drv");
return 0;
}
/* 分配/设置/注册一个 platform_driver */
static struct platform_driver hello_driver = {
.driver = {
.name = "100ask_hello",
},
.probe = hello_probe,
.remove = hello_remove,
};
int hello_init(void)
{
int err;
err = platform_driver_register(&hello_driver);
return err;
}
//int init_module(void) __attribute__((alias("hello_init")));
void hello_exit(void)
{
platform_driver_unregister(&hello_driver);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
xxx_dev.c(设备相关):
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/platform_device.h>
/* 参考 :drivers/char/ipmi/ipmi_msghandler.c */
static struct resource hello_resources[] = {
{
.name = "ver123",
.start = 0,
.end = 3,
.flags = IORESOURCE_MEM,
},
};
static void hello_dev_release(struct device *dev)
{
}
/* 分配/设置/注册 platform_device */
static struct platform_device hello_device = {
.name = "100ask_hello",
.id = -1,
.dev = {
.release = hello_dev_release,
},
.num_resources = ARRAY_SIZE(hello_resources),
.resource = hello_resources,
};
int hello_dev_init(void)
{
int err;
err = platform_device_register(&hello_device);
return err;
}
//int init_module(void) __attribute__((alias("hello_init")));
void hello_dev_exit(void)
{
platform_device_unregister(&hello_device);
}
module_init(hello_dev_init);
module_exit(hello_dev_exit);
MODULE_LICENSE("GPL");
<6> 设备树——字符设备驱动(需要手动创建设备结点)
(1)修改设备树
(2)xxx_driver.c中的
从设备树读取版本信息:
将设备树转化为platform_dev;
xxx_drv.c(驱动相关):
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/platform_device.h>
#include <linux/of.h>
/* 参考: drivers\char\ppdev.c
* drivers\char\ipmi\ipmi_powernv.c
*/
static char hello_buf[100] = "ver2";
static int hello_open(struct inode *inode, struct file *file)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* APP : read(fd, buffer, len) */
static ssize_t hello_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int err;
if (count > 100)
count = 100;
err = copy_to_user(buf, hello_buf, count);
return count;
}
static int hello_release(struct inode *inode, struct file *file)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 分配/设置/注册 file_operations结构体 */
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.read = hello_read,
.open = hello_open,
.release = hello_release,
};
static int hello_probe(struct platform_device *pdev)
{
int err;
int len;
const char *vers; /* vers is a pointer, point to "const char" */
/* 从匹配的platform_device里得到版本信息
* 用来设置: hello_buf
*/
err = of_property_read_string_index(pdev->dev.of_node, "version",
0, &vers);
if (err)
{
vers = "no version";
}
len = strlen(vers) + 1;
if (len > 100)
len = 100;
strncpy(hello_buf, vers, len);
hello_buf[len - 1] = '\0';
err = register_chrdev(88, "100ask_hello_drv", &hello_fops);
printk("register_chrdev ret = %d\n", err);
return err;
}
static int hello_remove(struct platform_device *pdev)
{
unregister_chrdev(88, "100ask_hello_drv");
return 0;
}
static const struct of_device_id hello_dt_ids[] = {
{ .compatible = "100ask,hello", },
{ /* sentinel */ }
};
/* 分配/设置/注册一个 platform_driver */
static struct platform_driver hello_driver = {
.driver = {
.name = "100ask_hello",
.of_match_table = hello_dt_ids,
},
.probe = hello_probe,
.remove = hello_remove,
};
int hello_init(void)
{
int err;
err = platform_driver_register(&hello_driver);
return err;
}
//int init_module(void) __attribute__((alias("hello_init")));
void hello_exit(void)
{
platform_driver_unregister(&hello_driver);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
右边:是设备树,本质上将设备树转化为dev
/* arch/arm/boot/dts/100ask_imx6ull-14x14.dts */
/ {
model = "Freescale i.MX6 ULL 14x14 EVK Board";
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
hello {
compatible = "100ask,hello";
version = "ver123456";
};
<7>设备树-自动创建设备结点
这一节本质上是在<6>的基础上加入<3>的两个函数;