稍简单的字符驱动程序-操作led


本文是基于韦东山视频的学习笔记

汇总点这

自动创建设备节点mdev

最简单的驱动程序 这篇文章里,设备号是自己确定的,如果要用驱动程序,还需要自己创建一个设备节点/dev/xxx,才能open("/dev/xxx", O_RDWR)。如果我们让系统自己去创建,岂不美哉。

首先把111改成0,系统将自动分配设备号。

/* 初始化(注册)驱动 */
static int __init first_drv_init(void)
{	
	major = register_chrdev(0, "first_drv", &first_drv);
	return 0;

}

/* 卸载驱动 */
static void __exit first_drv_exit(void)
{
	unregister_chrdev(major, "first_drv");
}

使用mdev的话,需要创建一个类,在这个类下面创建一个设备。

static struct class *first_class;
static struct class_device *first_drv_class;

...
static int __init first_drv_init(void)	
{
	first_class = class_create(THIS_MODULE, "first_drv");
	
	first_drv_class = class_device_create(first_class, NULL, MKDEV(major, 0), NULL, "first_drv"); /* /dev/hello */
...
}


...
static void __exit first_drv_exit(void)
{
	...
	class_device_unregister(first_drv_class);
	class_destroy(first_class);
	...
}

测试程序也得把/dev/xxx 改成 /dev/first_drv,和刚刚创建的设备名对应。

...
fd = open("/dev/xxx", O_RDWR);
...

led驱动程序

前文提到用户怎么控制开发板上的led灯的呢?

用户控制应用程序,应用程序调用应用程序里的open、read、write 函数,通过库函数发出swi异常指令,再调用驱动程序里面的open、read、write等函数去操作硬件设备led。

那么我们就在驱动程序里的open函数做硬件初始化

int first_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	*pGPFCON &= ~((3<<8)| (3<<10) | (3<<12)); /* 把GPFCON 8~13位清零 */
	*pGPFCON |=  ((1<<8)| (1<<10) | (1<<12)); /* 把GPFCON 9、11、13位置1,即设置为输出模式*/ 

	return 0;
}

驱动程序里的write函数做相应的操作,用copy_from_user函数读取应用程序传过来的参数。

ssize_t first_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int val;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	copy_from_user(&val, buf, size);	//把用户空间的数据保存到kernel_buf	
	if (val == 1)
	{
		/* leds on */
		*pGPFDAT = ~((1<<4)| (1<<5) | (1<<6));
	
}
	else if (val == 0)
	{
	
		/* leds off */
		*pGPFDAT = (1<<4)| (1<<5) | (1<<6);
	}

	return 0;
}

应用程序

int main(int argc, char **argv)
{
	int val = 1;
	int fd = 0;
	

	fd = open("/dev/xxx", O_RDWR);
	if (fd<0) printf("can't open file first_drv\n");

	write(fd, &val, 4);

	if (argc!=2)
	{
		printf("Usage: first_drv <on/off>\n");

	}
	else 
	{
		if (strcmp(argv[1], "on") == 0)
		{
			val  = 1;
		}
		else
		{
			val = 0;
		}
		write(fd, &val, 4);

	}
	return 0;
}

总结

附上完整代码

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/backing-dev.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>

unsigned int *pGPFCON = NULL; //指针变量pGPFCON设置为地址0x56000050
unsigned int *pGPFDAT = NULL; //指针变量pGPFDAT设置为地址0x56000054


static int major = 0;

static struct class *first_class;
static struct class_device *first_drv_class;


ssize_t first_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int val;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	copy_from_user(&val, buf, size);	//把用户空间的数据保存到kernel_buf	
	if (val == 1)
	{
		/* leds on */
		*pGPFDAT = ~((1<<4)| (1<<5) | (1<<6));
	
}
	else if (val == 0)
	{
	
		/* leds off */
		*pGPFDAT = (1<<4)| (1<<5) | (1<<6);
	}

	return 0;
}
int first_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	*pGPFCON &= ~((3<<8)| (3<<10) | (3<<12)); /* 把GPFCON 8~13位清零 */
	*pGPFCON |=  ((1<<8)| (1<<10) | (1<<12)); /* 把GPFCON 9、11、13位置1,即设置为输出模式*/ 

	return 0;
}

/* 定义结构体,传参给内核 */
static const struct file_operations first_drv = {
	.owner		= THIS_MODULE,
	.open		= first_drv_open,
	.write		= first_drv_write,
};

/* 初始化(注册)驱动 */
static int __init first_drv_init(void)
{
//	int err;
	
	major = register_chrdev(0, "first_drv", &first_drv);

	first_class = class_create(THIS_MODULE, "first_drv");
	
	first_drv_class = class_device_create(first_class, NULL, MKDEV(major, 0), NULL, "first_drv"); /* /dev/hello */

	pGPFCON = (unsigned int *)ioremap(0x56000050, 16);
	pGPFDAT = pGPFCON + 1;
		
	return 0;

}

/* 卸载驱动 */
static void __exit first_drv_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	class_device_unregister(first_drv_class);
	class_destroy(first_class);
	unregister_chrdev(major, "first_drv");
	iounmap(pGPFCON);
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(first_drv_init);
module_exit(first_drv_exit);

MODULE_LICENSE("GPL");



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值