linux驱动程序开发-第六节:驱动自动创建设备节点

知识理论:

1.  创建设备节点有两种方法:(1)手动:mknod   (2)class_create()  device_create()

2.  手动创建:mknod	设备文件   设备类型	主设备号   次设备号
	获取设备号两种方法:(1)驱动printk打印  (2)/sys/class/xxx/uevent
	
3.  自动创建:

	insmod  led_drv.ko 时候,udev 自动帮我们在/dev 目录下创建设备节点
	rmmod  led_drv 时候,udev 自动帮我们在/dev 目录下删除设备节点

思路:若要编写一个能用  udev  管理的设备驱动,需要在驱动代码中调用  class_create()为设备创建一个  class  类,
再调用  device_create()为每个设备创建对应的设备。

------------------------------------
struct  class  *__class_create(struct  module  *owner,  const  char  *name,
struct  lock_class_key  *key)

参数:

owner:class 的所有者,默认写 THIS_MODULE
name:自定义 class 的名字,会显示在/sys/class 目录当中

返回值:
成功:就返回创建好的 class 指针

失败:就返回错误码指针

如何判断当前的指针为错误码,使用 IS_ERR 函数进行判断。
-------------------------------------

struct  device  *device_create(struct  class  *class,  struct  device  *parent,
dev_t  devt,  void  *drvdata,  const  char  *fmt,  ...)

参数:
class:创建 device 是属于哪个类
parent:默认为 NULL
devt:设备号,设备号必须正确,因为这个函数会在/dev 目录下帮我们自动创建设备文件
drvdata:默认为 NULL
fmt:设备的名字,如果创建成功,就可以在/dev 目录看到该设备的名字

返回值:
成功:就返回创建好的设备指针
失败:就返回错误码指针
----------------------------------------

3.  参考三星公司创建类与设备的方法

#include  <linux/device.h>

static  struct  class   *adb_dev_class;
static  struct  device  *adb_dev_device;

static  int  __init  adbdev_init(void)
{
...............
if  (register_chrdev(ADB_MAJOR,  "adb",  &adb_fops)) 
	{
		printk(KERN_ERR  "adb:  unable  to  get  major  %d\n",  ADB_MAJOR);
		return;
	}

	adb_dev_class  =  class_create(THIS_MODULE,  "adb");

	if  (IS_ERR(adb_dev_class))
	return  PTR_ERR(adb_dev_class);

		adb_dev_device=device_create(adb_dev_class,  NULL,  MKDEV(ADB_MAJOR,  0),  NULL,  "adb");

	if  (IS_ERR(adb_dev_device))
	{
		class_destroy(adb_dev_class);
		return  PTR_ERR(adb_dev_device);
	}

return  0;
}



驱动程序:led_drv.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>

static struct cdev gec6818_led_cdev;		//字符设备结构体

static dev_t	led_num=0;					//设备号

static struct class  *led_class;
static struct device *led_device;

static int  gec6818_led_open (struct inode * inode, struct file *file)
{
	printk("gec6818_led_open \n");
	return 0;
}

static int  gec6818_led_release (struct inode * inode, struct file *file)
{
	printk("gec6818_led_release \n");
	return 0;
}

static ssize_t gec6818_led_write (struct file * file, const char __user * buf, size_t len, loff_t * off)
{
	int rt;
	
	char kbuf[64]={0};
	
	//判断当前len是否合法
	if(len > sizeof kbuf)
		return -EINVAL;		//返回参数无效错误码
		
	//从用户空间拷贝数据
	
	rt = copy_from_user(kbuf,buf,len);
	
	//获取成功复制的字节数
	
	len = len - rt;
	
	
	printk("gec6818_led_write,kbuf[%s],len[%d]\n",kbuf,len);
	
	return len;
}

static ssize_t gec6818_led_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
{
	
	int rt;
	
	char kbuf[5]={'1','2','3','4','\0'};
	
	//判断当前len是否合法
	if(len > sizeof kbuf)
		return -EINVAL;		//返回参数无效错误码
		
	//从内核空间拷贝到用户空间
	
	rt = copy_to_user(buf,kbuf,len);
	
	//获取成功复制的字节数
	
	len = len - rt;
	
	
	printk("gec6818_led_read,__user buf[%s],len[%d]\n",buf,len);
	
	return len;
	
	
}

static const struct file_operations gec6818_led_fops = {
 	.owner 		= THIS_MODULE,
	.write 		= gec6818_led_write,
	.open 		= gec6818_led_open,
	.release 	= gec6818_led_release,
	.read 		= gec6818_led_read,
};

//入口函数
static int __init gec6818_led_init(void)
{
	
	int rt=0;

	//动态申请设备号
	rt=alloc_chrdev_region(&led_num,0,1,"gec6818_leds");
	
	if(rt < 0)
	{
		printk("alloc_chrdev_region fail\n");
		
		return rt;
		
	}
	
	printk("led_major = %d\n",MAJOR(led_num));
	printk("led_minor = %d\n",MINOR(led_num));	
	
	
	//字符设备初始化
	cdev_init(&gec6818_led_cdev,&gec6818_led_fops);
	
	//字符设备添加到内核
	rt = cdev_add(&gec6818_led_cdev,led_num,1);
	
	if(rt < 0)
	{
		printk("cdev_add fail\n");
		goto fail_cdev_add;
		
	}
	

	//在sys/class目录创建文件夹gec6818_leds
	//THIS_MODULE:class 的所有者,默认写 THIS_MODULE
	//class_led:自定义 class 的名字,会显示在/sys/class 目录当中
	led_class = class_create(THIS_MODULE,"class_led1");
	
	if(IS_ERR(led_class))
	{
		rt = PTR_ERR(led_class);
		
		
		printk("class_create fail\n");
		
		goto class_create_fail;
		
	}
	
	//创建设备 
	/* 	
		struct  device  *device_create(struct  class  *class,  struct  device  *parent,
		dev_t  devt,  void  *drvdata,  const  char  *fmt,  ...)

		class:创建 device 是属于哪个类
		parent:默认为 NULL
		devt:设备号,设备号必须正确,因为这个函数会在/dev 目录下帮我们自动创建设备文件
		drvdata:默认为 NULL
		fmt:设备的名字,如果创建成功,就可以在/dev 目录看到该设备的名字 */

	led_device = device_create(led_class,NULL,led_num,NULL,"wgh_led1");
	
	if(IS_ERR(led_device))
	{
		rt = PTR_ERR(led_device);
		
		
		printk("device_create fail\n");
		
		goto device_create_fail;
		
	}	
	
	printk("gec6818 led init\n");
	return 0;
	
// 请注意goto执行的顺序,即释放资源规则:后创建先释放。
device_create_fail:
	class_destroy(led_class);
	
class_create_fail:
	cdev_del(&gec6818_led_cdev);
	
fail_cdev_add:
	unregister_chrdev_region(led_num,1);
	
	return rt;

}


//出口函数
static void __exit gec6818_led_exit(void)
{
	device_destroy(led_class,led_num);
	
	class_destroy(led_class);
	
	cdev_del(&gec6818_led_cdev);
	
	unregister_chrdev_region(led_num,1);
	
	printk("gec6818 led exit\n");
}

//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_led_init。
module_init(gec6818_led_init);

//驱动程序的出口:rmsmod led_drv调用module_exit,module_exit又会去调用gec6818_led_exit。
module_exit(gec6818_led_exit)


//模块描述
MODULE_AUTHOR("[email protected]");			//作者信息
MODULE_DESCRIPTION("gec6818 led driver");		//模块功能说明
MODULE_LICENSE("GPL");							//许可证:驱动遵循GPL协议

测试程序:

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

int main(int argc, char **argv)
{
	int fd=-1;
	
	int len;
	
	char buf[64]="hello teacher.wen";
	
	//打开gec6818_leds设备
	fd = open("/dev/gec6818_leds",O_RDWR);
	
	if(fd < 0)
	{
		perror("open /dev/gec6818_leds:");
		
		return fd;
		
	}
	
	sleep(2);
	
	write(fd,buf,strlen(buf));
	
	sleep(2);	
	
	len = read(fd,buf,5);
	
	if(len > 0)
		printf("read buf:%s\n len=%d\n",buf,len);
	
	
	sleep(2);		
	
	close(fd);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/wghkemo123/article/details/86175843