驱动开发:实现字符设备

本文通过操作寄存器实现led灯的亮灭,使用开发板iTop4412


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define DEVICE_NAME "led_ctrl"
int led_major = 0;
int led_minor = 0;

#define	GPX2_CONFIG	(0x11000000+0x0060)
#define	GPX2_DATA	(0x11000000+0x0064)


module_param(led_major, int, S_IRUGO);

static struct class *led_class;

void led_ctrl(int sw)
{
	int val = 0;
	void *virt_config = NULL;
	void *virt_data = NULL;
	virt_config = ioremap(GPX2_CONFIG,4);
	virt_data = ioremap(GPX2_DATA,4);
	val = readl(virt_config)&(~(0xF<<4))|(1<<4);
	writel(val,virt_config);
	if(sw)
		val = readl(virt_data)|(1<<1);
	else
		val = readl(virt_data)&(~(0x1<<1));
	writel(val,virt_data);
	iounmap(virt_config);
	iounmap(virt_data);
}

int led_open(struct inode *inode, struct file *file)
{
	printk(KERN_EMERG "led_open!!\n");
	return 0;
}

ssize_t led_write(struct file *file, const char __user *a, size_t s, loff_t* l)//向设备发送数据
{
	printk(KERN_EMERG "led_write!!\n");
	return 0;
}

ssize_t led_read(struct file *file, char __user *a, size_t s, loff_t *l)//从设备中同步读取数据
{
	printk(KERN_EMERG "led_read!!\n");
	return 0;
}

int	led_release(struct inode *inode, struct file *file)
{
	printk(KERN_EMERG "led_release!!\n");
	return 0;
}

long led_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	if(cmd > 1)
		printk(KERN_EMERG "cmd is 0 or 1\n");
	if(arg > 1)
		printk(KERN_EMERG "arg is only 1\n");
	led_ctrl(cmd);
	return 0;
}


struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.write = led_write,
	.read = led_read,
	.release = led_release,
	.unlocked_ioctl = led_unlocked_ioctl,
};


struct led_dev_t {
	struct cdev cdev;
};

struct led_dev_t *led_device;

void led_setup_cdev(struct led_dev_t *dev,int index)
{
	int err,devno = MKDEV(led_major,led_minor+index);
	cdev_init(&dev->cdev,&fops);

	err = cdev_add(&dev->cdev, devno, 1);
	if(err)
		printk(KERN_NOTICE "error %d adding led%d\n",err,led_minor);
	else
		printk(KERN_NOTICE "OK %d adding led%d\n",err,led_minor);
}

//设备驱动模块加载函数
static int __init led_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(led_major,led_minor);
	//获取字符设备号
	if(led_major)
	{
		//静态获取设备号
		ret = register_chrdev_region(devno,1,DEVICE_NAME);
	}
	if((ret < 0)||(!led_major))
	{
		//动态申请设备号
		ret = alloc_chrdev_region(&devno,0, 1,DEVICE_NAME);
		/*
		int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
			dev:		内核会将分配给你的设备号放里面,设备号类型 dev_t
			baseminor:	指定次设备号从数字几开始申请,代码中是0
			count:		申请设备号的个数,设备号是连续的
			name:		名字,自己随便写,char* 类型
			返回值:		小于等于0 失败,大于0为主设备号
		*/
	}
	if(ret <0)
	{
		printk(KERN_NOTICE "led_init is error\n");
		return ret;
	}

	led_major = MAJOR(devno);
	led_minor = MINOR(devno);

	led_class = class_create(THIS_MODULE,DEVICE_NAME);//注册一个类,可以在"/dev/"目录下面建立设备节点
	led_device = kzalloc(sizeof(struct led_dev_t), GFP_KERNEL);
	if(!led_device)
	{
		printk(KERN_NOTICE "led_device error!\n");
		ret = -ENOMEM;
		goto fail;
	}
	led_setup_cdev(led_device,0);//内含注册字符设备
	/*
		实现一个字符设备对象
		1.创建一个struct cdev cdev对象,名为cdev
		2.初始化cdev
			使用函数:void cdev_init(struct cdev * cdev,const struct file_operations * fops);
			*cdev: 为创建的cdev对象,一定要传入地址进来
			*fops: file_operations结构体对象,该结构体包含open read等函数
		3.将cdev对象添加到内核中
			int cdev_add(struct cdev  *cdev,dev_t dev,unsigned count);
			*cdev:	cdev对象
			dev:	设备号,前面申请的
			count:	一次添加几个设备
			返回值 <0 错误
	*/
	device_create(led_class,NULL,devno,NULL,DEVICE_NAME);//创建设备节点
	/*
		创建设备节点
		1.定义static struct class *led_class;
		2.注册一个类,可以在"/dev/"目录下面建立设备节点
			led_class = class_create(owner,name);
			owner:	恒等于THIS_MODULE,指向当前模块的指针
			name:	设备节点名称,随便取
			返回值NULL 错误
		3.创建设备节点
			device_create(led_class,NULL,devno,NULL,DEVICE_NAME);
			led_class:	已经注册好的类名
			parent:		NULL
			devno:		设备号,该文件的设备号,和字符设备对象一致
			drvdata:	null
			DEVICE_NAME:名称,随便写
	*/
	return 0;
fail:
	unregister_chrdev_region(devno, 1);
	return ret;
}

//设备驱动模块卸载函数
static void __exit led_exit(void)
{
	/*释放资源,逆序*/
	device_destroy(led_class,MKDEV(led_major,led_minor));
	class_destroy(led_class);//销毁设备节点
	cdev_del(&led_device->cdev);//删除字符设备
	kfree(led_device);//释放内存
	unregister_chrdev_region(MKDEV(led_major,led_minor), 1);//释放设备号
	printk(KERN_NOTICE "led_exit ok!\n");
}

module_init(led_init);
module_exit(led_exit);

MODULE_AUTHOR("CHEN");
MODULE_LICENSE("GPL");

 对应的测试APP代码如下:

#include 
#include "stdio.h"
#include 
#include 
#include 
#include 

main()
{
	int fd;
	char *hello_node = "/dev/led_ctrl";

	if((fd = open(hello_node,O_RDWR|O_NDELAY)) < 0)
	{
		printf("APP open %s failed\n",hello_node);
	}
	else
	{
		printf("APP open %s success\n",hello_node);
		while(1)
		{
			ioctl(fd,1,1);
			sleep(1);
			ioctl(fd,0,1);
			sleep(1);
		}
	}
	close(fd);
}

 

你可能感兴趣的:(linux嵌入式)