Linux驱动-字符设备驱动hello

写在前面:文章中的代码及截图,基本来自韦东山老师的课程。

<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>的两个函数;
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值