i.MX283开发板第一个Linux驱动-LED驱动改进

上一个博客i.MX283开发板第一个Linux驱动讲的是最简单的LED驱动的编写,但是其中还有一些不足。

首先就是在利用insmod加载驱动时,需要用指令mknod /dev/imx283_led c 200 0手动创建设备节点,否则在/dev下是不会有我们的设备的,应用程序中open("/dev/imx283_led",O_RDWR)必然会失败。

下面就针对上一个LED驱动作下改进,让其insmod之后自动在/dev下创建设备节点。

  • udev、mdev机制

Linux有udev、mdev的机制,而我们的ARM开发板上移植的busybox有mdev机制,然后mdev机制会通过class类来找到相应类的驱动设备来自动创建设备节点 (前提需要有mdev)

udev 是一个用户程序,在 Linux 下通过 udev 来实现设备文件的创建与删除,udev 可以检测系统中硬件设备状态,可以根据系统中硬件设备状态来创建或者删除设备文件。比如使用modprobe 命令成功加载驱动模块以后就自动在/dev 目录下创建对应的设备节点文件,使用rmmod 命令卸载驱动模块以后就删除掉/dev 目录下的设备节点文件。

1.首先要创建一个 class 类和device设备。

static struct class *led_class;//创建一个LED类
static struct device *led_device;//创建一个LED设备 该设备是需要挂在LED类下面的
static int major;//主设备号

注意:major是为接下来动态申请主设备号定义的,如果是手动分配,此处的major需要给定值

2.改进注册与注销字符设备函数

  • 注册设备函数:
static int __init led_init(void)
{  
   //1.申请或者指定主设备号 此处由内核动态分配主设备号
   major = register_chrdev(0,DEVICE_NAME,&led_fops);
   if(major < 0)
   {
     printk("register_chrdev error %d\n",major); 
     return major;
   }
   //2.创建一个LED类
   led_class = class_create(THIS_MODULE,"led_class");
   //3.在LED类下面创建一个LED设备 然后mdev通过这个自动创建/dev/"DEVICE_NAME"
   led_device = device_create(led_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);
   printk("module init ok\n");
   return 0; 
}

class_create是类创建函数,需要引用头文件#include <linux/device.h>

/* This is a #define to keep the compiler from merging different
 * instances of the __key variable */
#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})

owner:THIS_MODULE

name:类名字,可以随意

device_create用于在指定类下面创建一个设备

struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)
{
	va_list vargs;
	struct device *dev;

	va_start(vargs, fmt);
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
	va_end(vargs);
	return dev;
}

class:指定类,这里指定上一步创建的类

parent:父设备,一般为NULL

devt:设备号,可以通过MKDEV构建出来

drvdata:是设备可能会使用的一些数据,一般为 NULL

fmt:设备名字,如果设置 fmt=xxx 的话,就会生成/dev/xxx这个设备文件。返回值就是创建好的设备。

MKDEV是个宏定义,作用是根据已有的主设备号和次设备号构建一个dev_t类型的设备号。

  • 注销设备函数:
static void __exit led_exit(void)
{
   
  //1.注销设备 cat /proc/devices下不可见
  unregister_chrdev(major, DEVICE_NAME);
  //2.删除LED设备
  device_destroy(led_class, MKDEV(major,0));
  //3.删除LED类
  class_destroy(led_class);
  printk("module exit ok\n");
}

 device_destroy函数是从指定类下面删除一个设备

/**
 * device_destroy - removes a device that was created with device_create()
 * @class: pointer to the struct class that this device was registered with
 * @devt: the dev_t of the device that was previously registered
 *
 * This call unregisters and cleans up a device that was created with a
 * call to device_create().
 */
void device_destroy(struct class *class, dev_t devt)
{
	struct device *dev;

	dev = class_find_device(class, NULL, &devt, __match_devt);
	if (dev) {
		put_device(dev);
		device_unregister(dev);
	}
}

class:指定类            devt:设备号

/**
 * class_destroy - destroys a struct class structure
 * @cls: pointer to the struct class that is to be destroyed
 *
 * Note, the pointer to be destroyed must have been created with a call
 * to class_create().
 */
void class_destroy(struct class *cls)
{
	if ((cls == NULL) || (IS_ERR(cls)))
		return;

	class_unregister(cls);
}

cls:要删除的类

下面给出完整的驱动函数:

/*
   GPIO Driver driver for EasyARM-iMX283
*/
#include <linux/module.h>//模块加载卸载函数
#include <linux/kernel.h>//内核头文件
#include <linux/types.h>//数据类型定义
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/fs.h>//file_operations结构体
#include <linux/device.h>//class_create等函数
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/bcd.h>
#include <linux/capability.h>
#include <linux/rtc.h>
#include <linux/cdev.h>
#include <linux/gpio.h>//gpio_request  gpio_free函数 

#include <../arch/arm/mach-mx28/mx28_pins.h>

#define DEVICE_NAME	"imx283_led"//驱动名称
#define LED_GPIO	MXS_PIN_TO_GPIO(PINID_LCD_D23)		//for 283 287A/B

static int led_open(struct inode *inode ,struct file *flip)
{

   int ret = -1;
   gpio_free(LED_GPIO); 
   ret = gpio_request(LED_GPIO, "LED1");
   printk("gpio_request = %d\r\n",ret);
   return 0;
}


static int led_release(struct inode *inode ,struct file *flip)
{
  gpio_free(LED_GPIO);
  return 0;  
}


static int led_write(struct file *filp, const char __user *buf, size_t count,
                loff_t *f_pos)
{
   int ret = -1;
   unsigned char databuf[1];
   ret = copy_from_user(databuf,buf,1);
   if(ret < 0 ) 
   	{
   	  printk("kernel write error \n");
	  return ret;
   	}
   gpio_direction_output(LED_GPIO, databuf[0]);
   return ret; 
}


static   led_read(struct file *filp, const char __user *buf, size_t count,
                loff_t *f_pos)
{
    return 0;
}

static struct file_operations led_fops={
	.owner		= THIS_MODULE,
	.open 		= led_open,
	.write		= led_write,
	.read       = led_read,
	.release	= led_release,
};


static struct class *led_class;//创建一个LED类
static struct device *led_device;//创建一个LED设备 该设备是需要挂在LED类下面的
static int major;//主设备号

static int __init led_init(void)
{  
   //1.申请或者指定主设备号 此处由内核动态分配主设备号
   major = register_chrdev(0,DEVICE_NAME,&led_fops);
   if(major < 0)
   {
     printk("register_chrdev error %d\n",major); 
     return major;
   }
   //2.创建一个LED类
   led_class = class_create(THIS_MODULE,"led_class");
   //3.在LED类下面创建一个LED设备 然后mdev通过这个自动创建/dev/"DEVICE_NAME"
   led_device = device_create(led_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);
   printk("module init ok\n");
   return 0; 
}


static void __exit led_exit(void)
{
   
  //1.注销设备 cat /proc/devices下不可见
  unregister_chrdev(major, DEVICE_NAME);
  //2.删除LED设备
  device_destroy(led_class, MKDEV(major,0));
  //3.删除LED类
  class_destroy(led_class);
  printk("module exit ok\n");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzx2020");

3.测试

将上述驱动文件,编译生成.ko文件在开发板上测试。

首先看下/dev下有么有LED设备节点

此时没有任何LED设备节点,然后再加载驱动。

驱动加载成功,我们再到/dev下看看

已经自动生成了设备设备节点,主设备号250,次设备号0. 

最后,我们再卸载掉这个驱动,并再次查看/dev和/proc/devices

都没有“imx283_led”这个设备存在,说明已经被删除掉了。

本文参考:

1.《嵌入式Linux应用完全开发手册》 

2.《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0》

发布了45 篇原创文章 · 获赞 70 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_24835087/article/details/104100403