Linux字符驱动


 

                        驱动开发笔记

注册模块加载和卸载函数:

  module_init(xxx_init);

module_exit(xxx_exit);



模块加载命令

insmod xx.ko

modprobe  xx.ko



模块卸载:

rmmode  xx.ko

modprobe -r xx.ko

字符设备注册和注销:

static inline int register_chrdev(unsigned int major,const char *name,cosnt struct file_operations *fps);

static inline void unregister_chrdev(unsigned int major,const char *name);



获取设备号:

int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned cout,const char *name);

释放设备号:

viod unregister_chrdev_region(dev *from,unsigned count);



Ubuntu18.4的内核头文件路径;

/usr/src/linux-headers-5.0.0-37-generic



字符设备驱动编写:

虚拟地址和物理地址:

ioremap() 获取物理地址对应到的物理地址;

Iounmap()释放获取到的虚拟地址



外部寄存器和内存 映射到虚拟内存空间时叫做 IO内存;



IO内存操纵函数

u8 readb(const volatile void __iomem *addr);

u16 readw(const volatile void __iomem *addr);

u32 readl(const volatile void __iomem *addr);



void writeb(u8 value,volatile void __iomem *addr);

void writew(u16 value,volatile void __iomem *addr);



void writel(u32 value,volatile void __iomem *addr);





添加作者、版权描述、版本信息;



MODULE_LICENSE("GPL");添加模块 LICENSE 信息

MODULE_DESCRIPTION("VirtualDisk character device driver"); //描述信息

MODULE_AUTHOR("Jack");//作者信息

MODULE_VERSION("V1.0");//版本信息



老的和struct file_operations 绑定在register_chrdev()中;

新的和struct file_operations 绑定在cdev结构初始化cdev_init()中





函数介绍:

驱动注册和注销:mudoles_init();modules_exit();
字符设备的注册和注销;register_chrdev();unregister_chrdev();
添加作者、版权描述、版本信息;


MODULE_LICENSE("GPL");添加模块 LICENSE 信息
MODULE_DESCRIPTION("VirtualDisk character device driver"); //描述信息

MODULE_AUTHOR("Jack");//作者信息

MODULE_VERSION("V1.0");//版本信息



动态设备号的申请和释放:alloc_chardev_region();unregister_chardev_region()
新的字符设备结构:struct cdev{
struct kobject kobj;
 struct module *owner;
 const struct file_operations *ops;
 struct list_head list;
dev_t dev;
unsigned int count; 

}

字符设备初始化:cedv_init(struct cdev *cdev, const struct file_operations *fops) ;
添加字符设备:cdev_add(struct cdev *p, dev_t dev, unsigned count) ;
删除字符设备:cdev_del(struct cdev *p);
创建class类;struct class* class_creat( struct module *owner, const char *name)
参数 owner 一般为 THIS_MODULE,

参数 name 是类名字。
返回值是个指向结构体 class 的指针,也就是创建的类。 

11.删除类:class_destroy(struct class *class);

12.创建设备:struct device *device_create(struct class *class,struct device *parent,dev_t devt,void *drvdata,const char *fmt, ...)

device_create 是个可变参数函数,

参数 parent 是父设备,一般为 NULL,也就是没有父设备;

参数 devt 是设备号;

参数 drvdata 是设备可能会使用的一些数据,一般为 NULL;

参数 fmt 是设备名字,如果设置 fmt=xxx 的话就会生成/dev/xxx这个设备文件。

13.删除设备:device_destroy(struct device *d);

测试代码:

1、驱动代码:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <asm/io.h>

#define VIRTUALDISK_SIZE 0x2000
#define MEM_CLEAR 0x1
#define PORT1_SET 0x2
#define PORT2_SET 0x3
//#define VIRTUALDISK_MAJOR 200
#define NEWCHRLED_NAME "newchrled" /* 名字*/
#define NEWCHRLED_CNT 1			   //设备数量

//static int VirtualDisk_major = VIRTUALDISK_MAJOR;
static void *VirtualDisk_devp = NULL;

/* newchrled 设备结构体 */
struct VirtualDisk
{
	dev_t devid;		 /* 设备号 */
	struct cdev cdev;	/* cdev */
	struct class *class; /* 类 */
	struct device *device; /* 设备 */
	int major;														/* 主设备号 */
	int minor;														/* 次设备号 */
};

struct VirtualDisk newchrled; /* led 设备 */
static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minor);

static int VirtualDisk_open(struct inode *inode, struct file *filp);

static int VirtualDisk_release(struct inode *inode, struct file *filp);

static ssize_t VirtualDisk_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos);

static ssize_t VirtualDisk_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos);

static loff_t VirtualDisk_llseek(struct file *filp, loff_t offset, int orig);

static const struct file_operations VirtualDisk_fops =
	{
		.owner = THIS_MODULE,
		.llseek = VirtualDisk_llseek,
		.read = VirtualDisk_read,
		.write = VirtualDisk_write,
		.open = VirtualDisk_open,
		.release = VirtualDisk_release,
};

static int VirtualDisk_open(struct inode *inode, struct file *filp)
{
	//struct VirtualDisk *devp = NULL;

	filp->private_data = VirtualDisk_devp;
	//devp = filp->private_data;
	//	devp->count++;
	printk(KERN_INFO "open virtual_disk..\n");
	return 0;
}

static int VirtualDisk_release(struct inode *inode, struct file *filp)
{
	//struct VirtualDisk *devp = filp->private_data;
	//devp->count--;
	return 0;
}

static ssize_t VirtualDisk_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	//struct VirtualDisk *devp = filp->private_data;
	/*
	if (p >= VIRTUALDISK_SIZE)
	{
		return count ? -ENXIO : 0;
	}
	if (count > VIRTUALDISK_SIZE - p)
	{buf
		count = VIRTUALDISK_SIZE - p;
	}
*/
	if (copy_to_user(buf, (void *)filp->private_data, size))
	{
		ret = -EFAULT;
	}
	else
	{
		*ppos += count;
		ret = count;
		printk(KERN_INFO "read %d bytes from %lx\n", count, p);
	}
	return ret;
}

static ssize_t VirtualDisk_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	/*
//	struct VirtualDisk *devp = filp->private_data;

	if (p >= VIRTUALDISK_SIZE)
	{
		return count ? -ENXIO : 0;
	}
	if (count > VIRTUALDISK_SIZE - p)
	{
		count = VIRTUALDISK_SIZE - p;
	}
*/
	if (copy_from_user(filp->private_data, buf, count))
	{
		ret = -EFAULT;
	}
	else
	{
		*ppos += count;
		ret = count;
		printk(KERN_INFO "written %d bytes to %lx\n", count, p);
	}
	return ret;
}

static loff_t VirtualDisk_llseek(struct file *filp, loff_t offset, int orig)
{
	/*
	loff_t ret = 0;

	switch (orig)
	{
	case SEEK_SET:
		if (offset < 0)
		{
			ret = -EINVAL;
			break;
		}
		if ((unsigned int)offset > VIRTUALDISK_SIZE)
		{
			ret = -EINVAL;
			break;
		}
		filp->f_pos = offset;
		ret = filp->f_pos;
		break;
	case SEEK_CUR:
		if ((filp->f_pos + offset) > VIRTUALDISK_SIZE)
		{
			ret = -EINVAL;
			break;
		}
		if ((filp->f_pos + offset) < 0)
		{
			ret = -EINVAL;
			break;
		}
		filp->f_pos += offset;
		ret = filp->f_pos;
		break;
	default:
		ret = -EINVAL;
		break;
	}*/
	return 1;
}
static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minor)
{
	int err;
	//	dev_t devno = MKDEV(VirtualDisk_major, 0);
}
/* Device driver module init function */

static int __init VirtualDisk_init(void)
{
	VirtualDisk_devp = kmalloc(VIRTUALDISK_SIZE, GFP_KERNEL);
	memset(VirtualDisk_devp, 0, VIRTUALDISK_SIZE);
	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	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); /* 获取次设备号 */
	}
	printk("newcheled major=%d,minor=%d\r\n", newchrled.major,
		   newchrled.minor);

	/* 2、初始化 cdev */
	newchrled.cdev.owner = THIS_MODULE;
	cdev_init(&newchrled.cdev, &VirtualDisk_fops);

	/* 3、添加一个 cdev */
	cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);

	/* 4、创建类 */
	newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.class))
	{
		return PTR_ERR(newchrled.class);
	}
	/* 5、创建设备 */
	newchrled.device = device_create(newchrled.class, NULL,
									 newchrled.devid, NULL, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.device))
	{
		return PTR_ERR(newchrled.device);
	}

	return 0;
}

/* Driver module exit function */
static void __exit VirtualDisk_exit(void)
{
	/* 注销字符设备 */
	kfree(VirtualDisk_devp);
	cdev_del(&newchrled.cdev); /* 删除 cdev */
	unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);

	device_destroy(newchrled.class, newchrled.devid);
	class_destroy(newchrled.class);
}

module_init(VirtualDisk_init);

module_exit(VirtualDisk_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VirtualDisk character device driver");
MODULE_AUTHOR("Jack");
MODULE_VERSION("V1.0");

 

2、测试代码:

 

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
 
#define MAX_LEN (100)
 
int main()
{
    int fd;
	int str_len = 0;
    char write_buf[255] = { 0 };
    char read_buf[255]  = { 0 };
    
    printf("please input a string:\n");
    scanf("%s", write_buf);
    str_len = strlen(write_buf);
	if (str_len > MAX_LEN)
	{
		printf("Error, the input string length is beyond the maximum str length.\n");
		return -1;
	}
 
    fd = open("/dev/newchrled", O_RDWR);
    if(fd < 0)
	{
        printf("virtual_disk device open fail\n");
        return -1;
    }
	//lseek(fd, 0, SEEK_SET);1
	
    printf("Write %d bytes data to /dev/virtual_disk \n", str_len);
    printf("%s\n", write_buf);
 
    write(fd, write_buf, str_len);
 
	lseek(fd, 0, SEEK_SET);
    printf("Read %d bytes data from /dev/virtual_disk \n", str_len);
    read(fd, read_buf, str_len);
    printf("%s\n", read_buf);
    
    close(fd);
    return 0;
 
}

 

3、makefile文件

 

ifneq ($(KERNELRELEASE),)
	obj-m := virtual_disk.o
 
else
	CC := gcc
	#KERNEL_DIR = /usr/src/linux-headers-5.0.0-37-generic
	KERNEL_DIR = /usr/src/linux-headers-$(shell uname -r)
	PWD := $(shell pwd)
endif

modules:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
modules_install:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
clean:
	rm -f *.o
	rm -f *.o.*
	rm -f *.symvers
	rm -f *.order
	rm -f *.ko
	rm -f *.ko.*
	rm -f *.mod.c
	rm -f *.mod.*
	rm -f .counter.*
	rm -rf .tmp_versions

 

4、编译驱动为模块方式 make

5、加载驱动带内核并创建文件: modprobe vitual_dist.ko

6、测试测序编译测试

   gcc virtual_disk_test.c   

   ./a.out

7、测试结果

 

 

 

 

 

发布了136 篇原创文章 · 获赞 22 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/u010261063/article/details/104431135