驱动开发笔记
注册模块加载和卸载函数:
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、测试结果