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