linux驱动学习------创建杂项设备和字符设备(基于iTop4412开发板)

设备节点被创建在/dev下,是连接内核与用户层的枢纽,用户层通过open等操作实现对驱动的交互(具体通过设备号与对应驱动的设备建立联系)。
在这里插入图片描述
Linux下的设备通常分为三类,字符设备,块设备和网络设备。我们常用的主要是字符设备.

查看设备号:
cat /proc/devices
查看杂项设备:
cat /proc/misc

生成字符设备有两种方法:
1.注册杂项设备

注册函数:
int misc_register(struct miscdevice * misc);
注销函数:
int misc_deregister(struct miscdevice *misc);
杂项设备结构体:
struct miscdevice  {
	int minor;//次设备号(主设备号为10),赋MISC_DYNAMIC_MINOR自动分配
	const char *name;//设备节点名
	const struct file_operations *fops;//用于建立与应用层操作联系的方法结构体
	struct list_head list;
	struct device *parent;
	struct device *this_device;
	const char *nodename;
	mode_t mode;
};
关于file_operations结构体比较重要的成员:
struct module *owner;//THIS_MODULE
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//应用层read时对应的驱动动作
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//应用write时对应的驱动动作
int (*open) (struct inode *, struct file *);//应用open设备节点时驱动中的动作
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);//用于异步通知
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

杂项设备示例:

/***********驱动*********/
#include<linux/init.h>
#include<linux/module.h>

/*The header of driver register,include struct ,register function and unregister function about driver*/
#include<linux/platform_device.h>

/*incude header about miscdevice register*/
#include<linux/miscdevice.h>

/*include file struct about device noderegister*/
#include<linux/fs.h>

#define DRIVER_NAME "hello_ctl"
#define DEVICE_NAME "hello_ctl1"

MODULE_LICENSE("Dual BSD/GPL");
//MODULE_AUTHOR("ltx");

static int hello_open(struct inode *indoe,struct file *file)
{
	printk(KERN_EMERG"hello open\n");
	return 0;
}

static int hello_release(struct inode *inode,struct file *file)
{
	printk(KERN_EMERG"hello release\n");
	return 0;
}

static long hello_ctl(struct file *f,unsigned int cmd,unsigned long arg)
{
	printk(KERN_EMERG"cmd is %d,arg is %ld\n",cmd,arg);
	return 0;
}

static struct file_operations hello_ops = {
	.owner=THIS_MODULE,
	.open=hello_open,
	.release=hello_release,
	.unlocked_ioctl=hello_ctl,
};

static struct miscdevice misc_device = {
	.minor=MISC_DYNAMIC_MINOR,
	.name=DEVICE_NAME,
	.fops=&hello_ops,
};

static int hello_probe(struct platform_device *p)
{
	printk(KERN_EMERG "\tinitial\n");
	misc_register(&misc_device);
	return 0;
}
static int hello_remove(struct platform_device *p)
{
	printk(KERN_EMERG "\tremove\n");
	misc_deregister(&misc_device);
	return 0;
}

static void hello_shutdown(struct platform_device *p)
{
	;
}

static int hello_suspend(struct platform_device *p,pm_message_t state)
{
	return 0;
}
static int hello_resume(struct platform_device *p)
{
	return 0;
}

static struct platform_driver hello_driver={
	.probe=hello_probe,
	.remove=hello_remove,
	.shutdown=hello_shutdown,
	.suspend=hello_suspend,
	.resume=hello_resume,
	.driver={
		.name=DRIVER_NAME,
		.owner=THIS_MODULE,
		}
};

static int hello_init(void)
{
	int driver_state;

	printk(KERN_EMERG "hello world enter!\n");
	
	driver_state=platform_driver_register(&hello_driver);
	printk(KERN_EMERG "\tdriver state is %d\n",driver_state);
	return 0;
}
static void hello_exit(void)
{
	printk(KERN_EMERG "hello world exit!\n");

	platform_driver_unregister(&hello_driver);
}
//module enter
module_init(hello_init);
//module exit
module_exit(hello_exit);

/*************************************************************************************/
/*************测试应用*********/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/ioctl.h>

int main()
{
	int fd=-1;
	unsigned int cmd=1;//usually cmd is 0 or 1 
	unsigned long arg=6;
	char *file_node="/dev/hello_ctl1";
	
	fd=open(file_node,O_RDWR|O_NDELAY);
	if(fd<0)
	{
		printf("%s open failed\n",file_node);
	}
	else
	{
		printf("%s open success\n",file_node);
		if(ioctl(fd,cmd,arg)<0)
			printf("ioctl error!\n");

	}

	close(fd);
	return 0;
}

2.注册字符设备与生成设备节点

1.字符类设备号
	dev_t 高12位为主设备号,低20位为次设备号
	创建设备号:
	dev_t dev = MKDEV(major,minor)
	提取:
	MAJOR(dev)MINOR(dev)
2.设备号注册与注销
	注册连续多个不同主设备号的设备(次设备号为0):
	int register_chrdev_region(dev_t from, unsigned count, const char *name)/*
	from: the first in the desired range of device numbers; 
   count: the number of consecutive device numbers required
  name: the name of the device or driver.
  传入指定主设备号。
  如果申请的设备编号范围跨越了主设备号,它会把分配范围内的编号按主设备号分割成较小的子范围,并在每个子范围上调用 __register_chrdev_region() 。如果其中有一次分配失败的话,那会把之前成功分配的都全部退回。
*/
	注册同一主设备号不同次设备号的设备:
	int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)/*
	dev: output parameter for first assigned number
 	baseminor: first of the requested range of minor 
 	count: the number of minor numbers required
 	name: the name of the associated device or driver
 	通过传出参数获得动态分配的设备号
*/
	注销:
	void unregister_chrdev_region(dev_t from, unsigned count);
3.字符类设备注册
	字符类结构体:
	struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
};
	
	a.初始化cdev中成员(同时初始化file_operations结构体)
	void cdev_init(struct cdev *cdev, const struct file_operations *fops)(常用先声明cdev结构体指针,再由kmalloc分配内存,kmalloc用于小内存分配,最大为128k,kmalloc(size,GFP_KERNEL))
	然后手动初始化成员owner。
	b.注册设备
	int cdev_add(struct cdev *p, dev_t dev, unsigned count);
	/**
 * cdev_add() - add a char device to the system
 * @p: the cdev structure for the device
 * @dev: the first device number for which this device is responsible
 * @count: the number of consecutive minor numbers corresponding to this
 *         device
 *
 * cdev_add() adds the device represented by @p to the system, making it
 * live immediately.  A negative error code is returned on failure.
 */
 	注销:void cdev_del(struct cdev *p);
4.生成设备节点(两种方式)
	a.内核函数生成
	//先创建class结构体
	struct class *class_create(struct module *owner,const char *name);
	//class_create本身为一个宏定义实质调用__class_create。
	对应void class_destroy(struct class *cls);
	//创建设备节点注册到文件系统
	struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
	//调用device_create(class,NULL,设备号,NULL,设备名)设备名可以为“name.%d"类似printf
	对应注销void device_destroy(struct class *class, dev_t devt);
	b.mknod命令
	mknod 设备名 设备类型(字符:c,块:b) 主设备号 从设备号

字符设备示例:

#include<linux/init.h>
#include<linux/module.h>

#include<linux/moduleparam.h>
#include<linux/stat.h>

#include<linux/fs.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>

/*include kmalloc*/
#include<linux/slab.h>

/*include class and device create*/
#include<linux/device.h>

/*some header about gpio*/
#include<linux/gpio.h>
#include<plat/gpio-cfg.h>
#include<mach/gpio.h>
#include<mach/gpio-exynos4.h>

#define DEV_NAME "myled"
#define DEV_MAJOR 10
#define DEV_MINOR 0
#define DEV_MINOR_NUM 2
#define DEV_BUFF_SIZE 4096

static int led_gpios[]={
	EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),
};
#define LED_NUM ARRAY_SIZE(led_gpios)

MODULE_LICENSE("Dual BSD/GPL");
//MODULE_AUTHOR("ltx");

int devnum_major=DEV_MAJOR;
int devnum_minor=DEV_MINOR;

//afferent parameter
module_param(devnum_major,int,S_IRUSR);
module_param(devnum_minor,int,S_IRUSR);

/*device struct is used as buff and describle*/
struct dev_str
{
	char *buff;
	unsigned long size;//buff size
	struct cdev cdev;
};
struct dev_str *dev;

static int led_open(struct inode *inode,struct file *file)
{
	printk(KERN_EMERG "led_node open!\n");
	return 0;
}
static int led_release(struct inode *inode,struct file *fie)
{
	int i;
	/*release the gpio requested when close app*/
	for(i=0;i<LED_NUM;i++)
		gpio_free(led_gpios[i]);
	printk(KERN_EMERG "led_node close!\n");
	return 0;
}
static long led_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
	if((arg>LED_NUM-1)|(cmd>LED_NUM-1))
	{
		printk(KERN_EMERG "cmd is 0 or 1,and arg is 0 or 1");
		return -EINVAL;
	}
	gpio_set_value(led_gpios[arg],cmd);
	return 0;
}
static ssize_t led_read(struct file *file,char __user *buf,size_t count,loff_t *f_ops)
{
	return 0;
}
static ssize_t led_write(struct file *file,const char __user *buf,size_t count,loff_t *f_ops)
{
	return 0;
}
static loff_t led_llseek(struct file *file,loff_t offset,int ence)
{
	return 0;
}
struct file_operations my_ops={
	.owner=THIS_MODULE,
	.open=led_open,
	.release=led_release,
	.unlocked_ioctl=led_ioctl,
	.read=led_read,
	.write=led_write,
	.llseek=led_llseek,
};

static struct class *myclass;

/*fill cdev struct and add device into system*/
static void cdev_register(struct dev_str *dev,int index)
{
	int ret=0;
	dev_t devnum=MKDEV(devnum_major,devnum_minor+index);

	cdev_init(&(dev->cdev),&my_ops);
	dev->cdev.owner=THIS_MODULE;
	//changing different ops can realize different function in different child device
	dev->cdev.ops=&my_ops;

	/*register into system*/
	ret=cdev_add(&(dev->cdev),devnum,1);
	if(ret)
	{
		printk(KERN_EMERG "cdev_add add %d failed with error %d!\n",index,ret);
	}
	else
	{
		printk(KERN_EMERG "cdev_add add %d successfully!\n",index);
	}
}

/*gpio initial*/
static int gpio_init(void)
{
	int i,ret;

	for(i=0;i<LED_NUM;i++)
	{
		ret=gpio_request(led_gpios[i],"LED");
		if(ret)
		{
			printk(KERN_EMERG "%d gpio request failed!\n",led_gpios[i]);
			return -1;
		}
		else
		{
			s3c_gpio_cfgpin(led_gpios[i],S3C_GPIO_OUTPUT);
			gpio_set_value(led_gpios[i],0);
		}
	}
	return 0;
}

/*module enter function*/	
static int chdev_init(void)
{
	int ret=0,i=0;
	dev_t devnum;
	devnum=MKDEV(devnum_major,devnum_minor);
	printk(KERN_EMERG "char device number register enter!\n");

	ret=register_chrdev_region(devnum,DEV_MINOR_NUM,DEV_NAME);
	if(ret==0)
	{
		printk(KERN_EMERG "The major device number requested is %d\n",devnum_major);
	}
	else
	{
		alloc_chrdev_region(&devnum,devnum_minor,DEV_MINOR_NUM,DEV_NAME);
		devnum_major=MAJOR(devnum);
		devnum_minor=MINOR(devnum);
		printk(KERN_EMERG"The major device number requested is %d \n",devnum_major);
	}

	myclass=class_create(THIS_MODULE,DEV_NAME);

	/*request space for struct dev_str*/
	dev=kmalloc(DEV_MINOR_NUM*sizeof(struct dev_str),GFP_KERNEL);
	if(!dev)
	{
		ret=-ENOMEM;
		goto fail;
	}
	memset(dev,0,DEV_MINOR_NUM*sizeof(struct dev_str));

	/*device initial*/
	for(i=0;i<DEV_MINOR_NUM;i++)
	{
		/*request space for the devce buffer*/
		dev[i].buff=kmalloc(DEV_BUFF_SIZE,GFP_KERNEL);
		memset(dev[i].buff,0,DEV_BUFF_SIZE);

		/*register the device into system*/
		cdev_register(&dev[i],i);

		/*create device node*/
		device_create(myclass,NULL,MKDEV(devnum_major,devnum_minor+i),NULL,DEV_NAME"%d",i);
	}

	/*init gpio*/
	ret=gpio_init();
	if(ret)
	{
		printk(KERN_EMERG "gpio init failed!\n");
	}

	return 0;
fail:
	/*unregister the device number*/
	unregister_chrdev_region(MKDEV(devnum_major,devnum_minor),DEV_MINOR_NUM);
	printk(KERN_EMERG "kmalloc failed!\n");
	return ret;	
}

/*module exit function*/
static void chdev_exit(void)
{
	int i;	
	for(i=0;i<DEV_MINOR_NUM;i++)
	{
		cdev_del(&(dev[i].cdev));
		device_destroy(myclass,MKDEV(devnum_major,devnum_minor+i));
		kfree(dev[i].buff);	
	}
	class_destroy(myclass);
	kfree(dev);
	unregister_chrdev_region(MKDEV(devnum_major,devnum_minor),DEV_MINOR_NUM);

	printk(KERN_EMERG "char deivce number register  exit!\n");
}
//module enter
module_init(chdev_init);
//module exit
module_exit(chdev_exit);

测试应用:

#include<stdio.h>

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/ioctl.h>

int main()
{
	int fd=0,count=4;
	/*myled0 is same as myled1*/
	char *led_node="/dev/myled0";

	fd=open(led_node,O_RDWR|O_NDELAY);
	if (fd<0)
	{
		printf("open %s failed!\n",led_node);
		return -1;
	}
	else
	{
		//ioctl(fd,unsigned int cmd,unsigned long arg);
		ioctl(fd,1,0);
		while(count--)
		{
			sleep(1);
			ioctl(fd,0,0);
			ioctl(fd,1,1);
			sleep(1);
			ioctl(fd,0,1);
			ioctl(fd,1,0);
		}
	}

	close(fd);
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值