Linux驱动入门篇:基本的字符设备模块

Posted salvare

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动入门篇:基本的字符设备模块相关的知识,希望对你有一定的参考价值。

  在熟悉了模块的基本框架后,可以开启模块编程的大门了。


  如我们所知,模块编程的目的是驱动各种各样的设备。那么设备分为哪些类型呢?对Linux内核来说,设备分为字符设备、块设备和网络设备。


  今天先谈字符设备。字符设备的特性:只能一个字节一个字节地按顺序读取,不能任意读取。常见的字符设备有键盘、LED、串口等。


预备知识

一、设备号

  每个字符设备都有一个主设备号和次设备号。


  Linux内核允许多个驱动程序共享主设备号,但大多数设备都遵循“一个主设备号对应着一个驱动程序”原则。而次设备号供内核使用,用于确定指向的设备。


  内核中用 dev_t 类型来保存设备号。定义在<linux/types.h>中。dev_t 类型定义一个32位的数,前12位表示主设备号,后20位表示次设备号。为了安全地使用设备号(比如向后兼容),应该始终使用定义在<linux/kdev_t.h>中的宏。

#define MINORBITS    20
#define MINORMASK    ((1U << MINORBITS) - 1)

#define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

  若要通过 dev_t 类型获得主设备号和次设备号:

  MAJOR(dev_t dev);

  MINOR(dev_t dev);


  若要通过主、次设备号得到 dev_t 类型的数:

  MKDEV(int major, int minor);

二、申请和释放设备号

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name);

  在<linux/fs.h>中声明了动态分配设备号的函数,成功则返回0。(静态分配使用register_chrdev_region函数,此处不叙述)


  dev:保存申请到的设备号;

  baseminor:请求的第一个次设备号;

  count:连续请求多少个设备号;

  name:设备名,它将出现在/proc/devices和sysfs中。


void unregister_chrdev_region(dev_t from, unsigned count);

  当不再使用设备号时,应该释放它们,以留给其他设备使用。释放设备号函数中,from 为设备号,count 为连续释放的个数。


三、创建和销毁设备文件

  类 UNIX 操作系统中的一条设计思想就是“一切皆文件”。自然,设备也是文件咯。访问设备即是访问设备文件。那么我们就必须为设备创建一个设备文件,以便访问它。


  首先,我们需要通过 cat /proc/devices 来查看我们的设备名对应的设备号。之后就可以创建设备文件了。


  (1)手动创建:mknod filename filetype major minor (在/dev目录下创建)


  (2)自动创建:在系统支持 udev/mdev 的时候,可以自动创建设备文件。首先,在模块初始化代码里调用 class_create宏 为设备在 /sys/class 下创建一个class,再调用 device_create函数 创建对应的设备文件。它们在<linux/device.h>中被声明和定义。


  在模块的清除函数中还需调用 device_destroy 以及 class_destroy 来进行清除不再使用的文件节点和设备文件。

初始化及清除函数

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

dev_t devno;	//设备号

static struct class *my_class;

static int __init mycdev_init(void)
{
	int ret;
	ret = alloc_chrdev_region(&devno, 0, 1, "mycdev");
	if(ret != 0){
		printk(KERN_NOTEICE "Alloc device number failed.");
		return -1;
	}
	
	mycdev_setup();	//此处实现设备初始化及注册设备到内核,暂不实现

	my_class = class_create(THIS_MODULE, "mycdev");
	device_create(my_class, NULL, devno, NULL, "mycdev");

	return 0;
}

static void mycdev_exit(void)
{
	mycdev_del();	//此处实现设备注销

	device_destroy(my_class, devno);
	class_destroy(my_class);

	unregister_chrdev_region(devno);
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE(“Dual BSD/GPL”);

















以上是关于Linux驱动入门篇:基本的字符设备模块的主要内容,如果未能解决你的问题,请参考以下文章

一张图掌握 Linux 字符设备驱动架构!建议收藏

一张图掌握 Linux 字符设备驱动架构!建议收藏

Linux驱动入门-最简单字符设备驱动(基于pc ubuntu)

Linux驱动入门-最简单字符设备驱动(基于pc ubuntu)

深入浅出Linux内核模块篇 15.2Linux 设备驱动模型的初始化

linux设备驱动第二篇:构造和运行模块