首页 未命名正文

linux编程_关于字符装备驱动

云返利网 未命名 2020-05-26 09:07:39 14 0

Linux Device Drivers 条记

内核模块框架

最简朴的内核模块

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

 static int __init init_testko(void)
 {
         printk("test ko init\n");
         return 0;
 }

 static void __exit exit_testko(void)
 {
         printk("test ko exit\n");
 }

 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("KEVIN");
 module_init(init_testko);
 module_exit(exit_testko);

Makefile写法

obj-m = test.o #模块源代码

KER_DIR = /root/linux-3.0  #内核源码路径
CUR_DIR = $(shell pwd)

all:
        make -C $(KER_DIR) M=$(CUR_DIR) modules

clean:
        rm -rf *.o *.ko *.mod.o *.mod.c *.symvers *.order

cp:
        cp *.ko /root/rootfs/

分配装备号

装备号

在内核中装备号用一个32位数dev_t(linux/types.h)示意,高十二位示意主装备号,低二十位示意次装备号。

#include<linux/kdev_t.h>
MAJOR(dev_t dev);
MINOR(dev_t dev);
以上来两个宏分别从dev_t中提取主装备号和次装备号。下边的宏用来合成一个装备号。
MKDEV(int major, int minor);

分配装备号的方式

静态分配
    int register_chrdev_region(dev_t first, unsigned int count, char * name);
动态分配
    int alloc_chrdev_region(dev_t *dev, unsigned int dirstminor, unsigned int count, char * name);
这两个函数在分配乐成的时刻返回0,分配失败的时刻返回一个错误码。
释放占用的装备号
    void unregister_chrdev_region(dev_t first, unsigned int count);
参数:
  first 要分配的装备编号局限的启始值
  count 所请求的延续装备编号的个数
  name  是和该编号局限关联的装备名,将出现在/proc/devices和sysfs中
  dev   是用来保留分配的装备号的内存地址

关于错误码,内核界说了一组宏来示意,这些宏保留在./include/asm-generic/errno-base.h中
可以在写驱动代码的时刻使用,例如
return -EPERM; 示意操作没有权限,使用的时刻需要加上‘-’符号。

关于装备号的分配

内核中有块装备表和字符装备表,凭据装备的类型和主装备号可通过这两个表之一找到对应的驱动程序函数跳转结构,而次装备号则一样平常只用做同类型装备中详细装备项的编号。
linux2.6之前的额版本接纳8位的数据类型存储主装备号,因此将块装备和字符装备的种类都限制到了256种。

内核源代码中关于装备号分配最主要的一个函数是__register_chrdev_region,此函数主要维护了一个指针数组chrdevs,以散列表的形式治理装备。而register_chrdev_region和alloc_chrdev_region都间接的挪用了这个函数,其中alloc_chrdev_region更是直接以第一个参数为0的方式挪用,详细的解说见http://blog.csdn.net/virlhs/article/details/51711112

此处另有一个疑问:在以动态分配形式分配装备号时__register_chrdev_region从255向前搜索chrdevs数组,找到一个指向NULL的指针时就返回此指针的索引(数组下标)作为动态分配的主装备号,然则若是数组chrdevs中的指针全不为NULL,是不是会分配失败,也就是说动态分配只能分配0-255的主装备号?(先Mark一下,以后探讨)

注册装备

ldd3的方式–关于cdev结构

#include <linux/cdev.h>
struct cdev * cdev_alloc(void);
void cdev_init(struct cdev * dev, struct file_operations * fops);
int cdev_add(struct cdev * dev, dev_t num, unsigned int count);
void cdev_del(struct cdev * dev);

每一个字符装备都对应一个cdev结构,注册装备的历程就是分配和初始化cdev结构的历程,因此常使用如下代码完成
动态建立

struct cdev * my_cdev = cdev_alloc();
my_cdev->ops = &my_cdev;
my_cdev->owner = THIS_MODULE;
result = cdev_add(my_cdev, devno, count);

在装备注销时挪用
cdev_del(my_cdev);

静态建立

struct cdev mcdev;
struct file_operations fops;
cdev_init(&mcdev, &fops);
mcdev.owner = THIS_MODULE;
result = cdev_add(&mycdev, devno, count);

在装备注销的时刻挪用
cdev_del(&mcdev);

老方式

int register_chrdev(unsigned int major, const char * name, struct file_operations * fops);
  major : 主装备号,为0时示意自动分配
  name  : 驱动程序的名字,将在/proc/devices中显示
  fops  : 默认的file_operations结构
int unregister_chrdev(unsigned int major, const char * name);

这种注册装备的方式是最经常看到的,据说是以前老版本的内核推荐的方式,个人感觉虽然这种方式使用起来利便,然则
却屏障了许多细节,对于学系内核来说并不是很直观。

使用此种方式注册的驱动程序,主次装备号都不能大于255

下面看一下几个函数的挪用关系

register_chrdev  
 |---- __register_chrdev  
             |---- __register_chrdev_region  
             |---- cdev_alloc  
             |---- cdev_add

可以看出register_chardev函数封装了,分配装备号的__register_chrdev_region函数和注册装备的cdev_alloc函数和cdev_add函数,就是将上边的历程封装了起来,

使用装备类自动注册装备文件

自动注册装备节点需要mdev的支持,(PC平台叫做udev)mdev会以守护历程的形式运行,在后台监听sysfs的uevent事宜,自动在/dev,目录下建立响应的装备节点。

为了知足这样的需求,根文件系统首先需要响应的支持。
1、必须存在sysfs文件系统 mount -t sysfs sysfs /sys
2、/dev目录需要挂载为tmpfs mount -t tmpfs mdev /dev
3、在装备开机时启动mdev, 在/etc/init.d/rcS中添加echo /bin/mdev > /proc/sys/kernel/hotplug && mdev -s

其次代码中也要添加建立装备节点的响应代码。

/* This is a #define to keep the compiler from merging different
 * instances of the __key variable */
#define class_create(owner, name)

/**
 * 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);
}

class_create 是一个宏,参数有两个,owner指所属模块,一样平常为THIS_MODULE,name为装备类名,
此宏挪用了 __class_create(owner, name, &__key); 函数,挪用关系如下

  __class_create
    | ---- __class_register
              | ---- kset_init
              | ---- kobject_set_name
              | ---- kset_register
                      | ---- kobject_add_internal
                      | ---- kobject_uevent v
              | ---- add_class_attrs
                      | ---- class_create_file
/**
* device_create - creates a device and registers it with sysfs
* @class: pointer to the struct class that this device should be registered to
* @parent: pointer to the parent struct device of this new device, if any
* @devt: the dev_t for the char device to be added
* @drvdata: the data to be added to the device for callbacks
* @fmt: string for the device's name
*
* This function can be used by char device classes.  A struct device
* will be created in sysfs, registered to the specified class.
*
* A "dev" file will be created, showing the dev_t for the device, if
* the dev_t is not 0,0.
* If a pointer to a parent struct device is passed in, the newly created
* struct device will be a child of that device in sysfs.
* The pointer to the struct device will be returned from the call.
* Any further sysfs files that might be required can be created using this
* pointer.
*
* Returns &struct device pointer on success, or ERR_PTR() on error.
*
* Note: the struct class passed to this function must have previously
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
                              dev_t devt, void *drvdata, const char *fmt, ...)


/**
* device_destroy - removes a device that was created with device_create()
* @cla: 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)

device_createde 函数的挪用关系如下:

device_createde
    | ---- device_create_vargs
            | ---- device_register
                      | ---- device_initialize
                      | ---- device_add
                              | ---- kobject_add
                              | ---- device_create_file
                              | ---- device_add_class_symlinks
                              | ---- kobject_uevent

这四个函数的的头文件都是 #include <linux/device.h>

下面是一个例子

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

#include <linux/cdev.h> // for cdev_add
#include <linux/device.h> // for class_create
#include <linux/err.h> //for IS_ERR
#include <linux/fs.h> //for alloc_chrdev_region

static dev_t devno;
static struct cdev * dev;
static struct class * class;
static struct device * device;

static struct file_operations ops = {
    .owner  =   THIS_MODULE,
};

static int __init init_testko(void)
{
    printk("test ko init\n");
    if (alloc_chrdev_region(&devno, 0, 1, "Hello")) {
        printk("device number register failed!\n");
        return  -1;
    }
    printk("device number register success, major : %u\n", MAJOR(devno));

    dev = cdev_alloc();
    dev->owner = THIS_MODULE;
    dev->ops = &ops;
    if (cdev_add(dev, devno, 1)) {
        printk("cdev_add failed!\n");
        unregister_chrdev_region(devno, 1);
        return -1;
    }

    class = class_create(THIS_MODULE, "HELLO");
    if (IS_ERR(class)) {
        cdev_del(dev);
        unregister_chrdev_region(devno, 1);
        return PTR_ERR(class);
    }

    device = device_create(class, NULL,devno, NULL, "hello");
    if (IS_ERR(device)) {
        class_destroy(class);
        cdev_del(dev);
        unregister_chrdev_region(devno, 1);
        return PTR_ERR(device);
    }

    return 0;
}

static void __exit exit_testko(void)
{
    device_destroy(class, devno);
    class_destroy(class);
    cdev_del(dev);
    unregister_chrdev_region(devno, 1);
    printk("test ko exit\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("KEVIN");
module_init(init_testko);
module_exit(exit_testko);

【关于云返利网】

云返利网是阿里云、腾讯云、华为云产品推广返利平台,在各个品牌云产品官网优惠活动之外,云返利网还提供返利。您可以无门槛获得阿里云、华为云、腾讯云所有产品返利,在官网下单后就可以领取,无论是自己用、公司用还是帮客户采购,您个人都可以获得返利。云返利网的目标是让返利更多、更快、更简单!详情咨询13121395187(微信同号)