cdev简单解析

1. cdev是linux用来管理字符设备的结构体,其在内核中采用数组结构设计,这样系统中有多少个主设备号就约定了数组大小,此设备号采用链表管理,同一主设备号下可以有多个子设备。设备即文件,上层应用要访问设备,必须通过文件,cdev中包含file_operations结构体,该结构体就是驱动的文件操作集合。
(根据于说得)
2. cdev定义在include/linux/cdev.h中,如下。具体实现在fs/char_dev.c中。

#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>

struct file_operations;
struct inode;
struct module;

struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};

void cdev_init(struct cdev *, const struct file_operations *); 
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

void cd_forget(struct inode *);

extern struct backing_dev_info directly_mappable_cdev_bdi;
//fs/char_dev.c
/**
* 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. */
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

3. cdev函数

>>cdev定义有两种方式,struct cdev cdev;另外一种struct cdev *cdev; cdev=cdev_alloc();
一种静态声明定义,另一种动态分配。
>>cdev通过函数cdev_init()初始化,主要工作就是将file_operations和cdev关联起来。file_operations是字符驱动需要实现的主要内容。
>>cdev通过cdev_add()实现cdev的注册,所谓注册就是将cdev根据设备号(dev_t)添加到cdev数组(cdev_map)中供系统管理。
>>cdev通过cdev_del()将cdev从cdev_map中移除。
4. 设备号
申请设备号用如下两个函数:
alloc_chrdev_region() --自动分配设备号
register_chrdev_region() --分配已设定的设备号

/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);

/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name);

5. 示例

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/reboot.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
//#include <plat/gpio.h>
/*gpios*/
#define GPIO_MAJOR 212

//#define KEY_S1 AT91_PIN_PC2 //S1按键
#define DI0 AT91_PIN_PB17 //DI0
#define DI1 AT91_PIN_PB16 //DI1
#define DI2 AT91_PIN_PB15 //DI2

#define RELAY AT91_PIN_PA14 //继电器
#define LED_YELLOW AT91_PIN_PB23

#define RELAY1 AT91_PIN_PB18 //继电器 do1
#define RELAY2 AT91_PIN_PB19 //继电器 do2
#define RELAY3 AT91_PIN_PB20 //继电器 do3
/*cmds*/
#define LED_YELLOW_SET 1001
#define LED_YELLOW_CLR 1002

#define GPRS_RELAY1_SET 1020 //继电器
#define GPRS_RELAY1_CLR 1021

#define GPRS_RELAY2_SET 1022 //继电器
#define GPRS_RELAY2_CLR 1023

#define GPRS_RELAY3_SET 1024 //继电器
#define GPRS_RELAY3_CLR 1025

#define GET_DI0   1026 
#define GET_DI1   1027
#define GET_DI2   1028

struct gpio_dev
{
    struct cdev    gpio_cdev; //设备驱动的对象
    unsigned int     major;
    dev_t        dev_no; //设备号 = 主 + 次
    struct mutex    mutex;
};

struct gpio_dev *dev;

static int gpio_open(struct inode *inode, struct file *filp)
{
    filp->private_data = dev;
    //printk("%s,  %d\n", __FUNCTION__, __LINE__);
    return 0;
}

static int gpio_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t gpio_read(struct file *filp, char __user *val, size_t size, loff_t *offset)
{
    struct gpio_dev * gpio_device = filp->private_data;
    //printk("%s, %d\n", __FUNCTION__, __LINE__);
    return 0;
}

static ssize_t gpio_write(struct file *filp, const char __user *val, size_t size, loff_t *offset)
{
    return 0;    
}

static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct gpio_dev *gpio_device = filp->private_data;
    mutex_lock(&gpio_device->mutex);
    switch (cmd)
    {
        case LED_YELLOW_SET: 
            at91_set_gpio_value(LED_YELLOW,1);
            break;
        case LED_YELLOW_CLR: 
            at91_set_gpio_value(LED_YELLOW,0);
            break;
        case GET_DI0:
            {
                int key_val=0;
                int ret = 0;

                key_val = at91_get_gpio_value(DI0);
                ret = copy_to_user((int *)arg,&key_val,sizeof(int));
                printk("GET_DI0:%d\n",key_val);
                if(ret)    
                    return -1;
            }
            break;
        case GET_DI1:
            {
                int key_val=0;
                int ret = 0;

                key_val = at91_get_gpio_value(DI1);
                ret = copy_to_user((int *)arg,&key_val,sizeof(int));
                printk("GET_DI1:%d\n",key_val);
                if(ret)    
                    return -1;
            }
            break;
        case GET_DI2:
            {
                int key_val=0;
                int ret = 0;

                key_val = at91_get_gpio_value(DI2);
                ret = copy_to_user((int *)arg,&key_val,sizeof(int));
                printk("GET_DI2:%d\n",key_val);
                if(ret)    
                    return -1;
            }
            break;
        default:
            printk("kernal Unkown command.\n");
            mutex_unlock(&gpio_device->mutex);
            return -1;
    }
    mutex_unlock(&gpio_device->mutex);
    return 0;
}

static struct file_operations gpio_fops = {
    .owner = THIS_MODULE,
    .open    = gpio_open,
    .read    = gpio_read,
    .write    = gpio_write,
    .unlocked_ioctl    = gpio_ioctl,
    .release= gpio_release,
};

static int __init gpio_init(void)
{
    dev = (struct gpio_dev *)kmalloc(sizeof(struct gpio_dev), GFP_KERNEL);
    if (!dev)
    {
        printk("kmalloc error\n");
        return -1;
    }
    memset(dev, 0, sizeof(struct gpio_dev));
    
    dev->major = GPIO_MAJOR;
    dev->dev_no = MKDEV(dev->major, 0); //生成设备号

    if (register_chrdev_region(dev->dev_no, 1, "board_gpio") < 0)
    {
        printk("register_chrdev_region error\n");
        goto out2;
    }

    cdev_init(&dev->gpio_cdev, &gpio_fops);
    dev->gpio_cdev.owner = THIS_MODULE;

    if (0 != cdev_add(&dev->gpio_cdev, dev->dev_no, 1))
    {
        printk("cdev_add error.\n");
        goto out1;
    }
    mutex_init(&dev->mutex);
/* GPIO init */
    at91_set_gpio_output(LED_YELLOW, 1);
    at91_set_gpio_output(RELAY, 1); 

    at91_set_gpio_output(RELAY1, 1); 
    at91_set_gpio_output(RELAY2, 1); 
    at91_set_gpio_output(RELAY3, 1); 

    at91_set_gpio_input(DI0, 1); 
    at91_set_gpio_input(DI1, 1); 
    at91_set_gpio_input(DI2, 1); 

    return 0;

out1:
    unregister_chrdev_region(dev->dev_no, 1);
out2:
    kfree(dev);
    return -1;
}

static void gpio_exit(void)
{
    cdev_del(&dev->gpio_cdev);
    unregister_chrdev_region(dev->dev_no, 1);
    kfree(dev);
}

module_init(gpio_init);
module_exit(gpio_exit);
MODULE_DESCRIPTION("This is dido driver for BoardA project");
MODULE_AUTHOR("Zhu");
MODULE_LICENSE("Dual BSD/GPL");

 

参考:
1. http://www.embedu.org/Column/Column552.htm

2. http://blog.chinaunix.net/uid-24517893-id-161446.html

3. 浅析字符设备驱动程序__register_chrdev_region

posted @ 2016-10-25 20:38  yuxi_o  阅读(5743)  评论(0编辑  收藏  举报