I.MX6U嵌入式Linux驱动开发(3)新驱动框架

使用register_chrdev函数注册字符设备,浪费了很多设备号,而且需要我们手动指定主设备号,还有这个函数还得需要自己去查哪个设备号没有被使用。因此,我们可以使用新的设备驱动函数来实现。

1、原理

是要使用设备号的时候向 Linux 内核申请,需要几个就申请几个,由 Linux 内核分配设备可以使用的设备号。

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)//没有指定设备号
int register_chrdev_region(dev_t from, unsigned count, const char *name)//给定了设备的主设备号和次设备号
void unregister_chrdev_region(dev_t from, unsigned count)//给定了设备的主设备号和次设备号

2、准备工作

在Ubuntu中新建文件夹,进入Linux_Driver

ls
mkdir 3_newchrled
cp 2_led/ * 3_newchrled / -rf
cd 3_newchrled/
ls -a
cp ../2_led/.vscode/ ./ -rf
ls -a
rm led.code-workspace
ls
mv led.c newchrled.c
ls         //驱动程序变了,应用程序不变

用VSCode打开工程,打开newchrled.c文件,只保留寄存器地址、地址映射后的虚拟地址指针、定义的开关灯,其余的全都删了。

3、编写驱动

3.1、编写驱动框架

#define NEWCHRLED_NAME "newchrled"
#define NEWCHRLED_COUNT 1
/* LED设备结构体 */
struct newchrled_dev{
	dev_t  devid; 	/* 设备号 */
	int major;		/* 主设备号 */
	int minor;		/* 次设备号 */
};
struct newchrled_dev newchrled;/* led设备 */
/* 入口 */
static int __init newchrled_init(void)
{
	int ret = 0;
	printk("newchrled_init!\r\n");
	/* 1、初始化LED */
	/* 2、申请字符设备 参照Linux内核来写*/
	if(newchrled.major){/* 给定主设备号 */
		newchrled.devid = MKDEV(newchrled.major, 0);
		ret = register_chrdev_region(newchrled.devid, NEWCHRLED_COUNT , NEWCHRLED_NAME);
	}else{/* 没有给定主设备号 */
		ret = alloc_chrdev_region(&newchrled.devid,0,NEWCHRLED_COUNT ,NEWCHRLED_NAME);
		newchrled.major = MAJOR(newchrled.devid);/* 提取出来设备号 */
		newchrled.minor = MINOR(newchrled.devid);
	}
	if(ret < 0){
		printk("newchrled chrdev_region err!\r\n");
		return -1;
	}
	printk("newchrled major=%d, minor=%d\r\n",newchrled.major, newchrled.minor);
	/* 3、注册字符设备 */
	return 0;
}
/* 出口 */
static void __exit newchrled_exit(void)
{
	printk("newchrled_exit!\r\n");
	/* 注销设备号 */
	unregister_chrdev_region(newchrled.devid, NEWCHRLED_COUNT);
}
//注册和卸载驱动
module_init(newchrled_init);
module_exit(newchrled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yang");

修改Makefile,将文件名字修改为:newchrled.o
打开终端。编译。

3.2、字符设备注册方法

在Linux中使用用 cdev 结构体表示一个字符设备,cdev 结构体在 include/linux/cdev.h 文件中。

在 cdev 中有两个重要的成员变量:ops 和 dev,这两个就是字符设备文件操作函数集合file_operations 以及设备号 dev_t。编写字符设备驱动之前需要定义一个 cdev 结构体变量,这个变量就表示一个字符设备,如下所示:

struct cdev test_cdev;

因此在前面定义的结构体newchrled_dev中添加下面这句代码:

struct newchrled_dev{
	struct cdev cdev;
	...
};

添加结构体cdev所在的一个头文件。
使用cdev_init()函数初始化结构体cdev的成员变量。参数 cdev 就是要初始化的 cdev 结构体变量,参数 fops 就是字符设备文件操作函数集合。
初始化完了之后,使用函数cdev_add()向内核添加设备。

void cdev_init(struct cdev *cdev, const struct file_operations *fops)//第一个参数是指针类型,第二个参数是结构体,需要定义一个结构体。
cdev_add(struct cdev *p, dev_t dev, unsigned count);//第一个参数是指哪个设备,第二个参数是设备号,第三个是设备数

在.c文件中定义结构体:

static const struct file_operations newchrled_fops = {
	.owner = THIS_MODULE,
};

在newchrled_init(void)中添加如下代码:

/* 3、注册字符设备 */
newchrled.cdev.owner = THIS_MODULE;
cdev_init(&newchrled.cdev, &newchrled_fops);//初始化
ret = cdev_add(&newchrdev.cdev, newchrled.devid, NEWCHRLED_COUNT);

在newchrled_exit(void)函数中,先删除字符设备,再注销设备号

/* 1、删除字符设备 */
cdev_del(&newchrled.cdev);
/* 2、注销设备号 */

完善字符设备结构体

static int newchrled_open(struct inode *inode, struct file *filp)
{
	return 0;
}
static int newchrled_release(struct inode *inode, struct file *filp)
{
	return 0;
}
static ssize_t newchrled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
	
}
static const struct file_operations newchrled_fops={
	.owner = THIS_MODULE,
	.write = newchrled_write,
	.open = newchrled_open,
	.release = newchrled_release,
}

3.3、测试

在vscode中打开终端设备

make
sudo cp newchrled.ko /home/yang/linux/nfs/rootfs/lib/modules/4.1.15/ -f

开发板上电,进入串口端:

/lib/modules/4.1.15/ # ls //查看newchrled.ko是否存在
/lib/modules/4.1.15/ # depmod
/lib/modules/4.1.15/ # modprobe newchrled.ko
/lib/modules/4.1.15/ # 

3.4、点灯

将上一节的点灯代码拷贝过来。
在newchrled_write()里面添加灯的处理程序。
将上一节的初始化灯的函数拷贝过来到init函数中。
在exit函数中添加关灯函数。

3.5、测试

/lib/modules/4.1.15/ # lsmod
/lib/modules/4.1.15/ # modprobe newchrled.ko
/lib/modules/4.1.15/ # lsmod
/lib/modules/4.1.15/ # cat /proc/devices
/lib/modules/4.1.15/ # mknod /dev/newchrled c 249 0
/lib/modules/4.1.15/ # ls
/lib/modules/4.1.15/ # ./ledAPP /dev/newchrled 1
/lib/modules/4.1.15/ # ./ledAPP /dev/newchrled 0

4、自动创建设备节点

在init函数中自动创建设备节点。借鉴Linux内核人家的用法。拷贝过来,修改。

4.1、创建class

class_create()有一个返回值,返回值是一个class(是一个指针)。因此,把返回的值放入结构体newchrled_dev中,添加:struct class *class; 添加头文件:#include <linux/device.h>

newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
if(IS_ERR(newchrled.class))
	return PTR_ERR(newchrled.class);

在注销的时候,要删除class。在exit函数中:

class_destroy(newchrled.class);

4.2、创建设备

参考别人写的。
返回值是一个结构体类型的指针。
在结构体newchrled_dev中添加:struct device * device;//设备

newchrdev.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
if(IS_ERR(newchrled.device))
	return PTR_ERR(newchrled.device);

先摧毁设备,再摧毁类,因为这个类用到了设备。

device_destory(newchrled.class, newchrled.devid);

4.3、测试

make
sudo cp newchrled.ko /home/yang/linux/nfs/rootfs/lib/modules/4.1.15/ -f
/lib/modules/4.1.15/ # lsmod
/lib/modules/4.1.15/ # ls /dev/new+TAB
/lib/modules/4.1.15/ # modprobe newchrled.ko
/lib/modules/4.1.15/ # ls /dev/newchrled -l
/lib/modules/4.1.15/ # ./ledAPP /dev/newchrled 1
/lib/modules/4.1.15/ # ./ledAPP /dev/newchrled 0
/lib/modules/4.1.15/ # rmmod newchrled.ko

5、文件私有数据

参考cm4000.c文件的用法:
一般在open的时候设置私有数据。struct file *filp这个结构体中有一个私有数据的变量。因此我们可以在open函数中设置:

static int newchrled_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &newchrled;
	return 0;
}

在release、write中就可以不用调用newchrled了。

static int newchrled_release(struct inode *inode, struct file *filp)
{
	struct newchrled_dev *dev = (struct newchrled_dev*)filp->private_data;
	//下面就可以直接访问dev
	return 0;
}

在注册字符设备时,判断newchrled.major是否为给定的字符设备号,在这之前我们没有初始化major,为了保险起见,初始化结构体:

newchrled.major = 0;//手动清0

我们在判断是否成功的时候,返回了-1或者其它的,但是人家写的都是使用goto返回的。

goto fail_devid;
goto fail_cdevinit;

fail_devid:
	return -1;
fail_cdevinit:
	unregister_chedev_region(newchrled.devid, NEWCHRLED_COUNT);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值