写完第一个hello world 的驱动以后,想编写一个比较完整的字符设备驱动,就把《linux 设备驱动开发详解》上的globalmem驱动写了一下,写完以后,发现了几个问题,没有自动生成设备节点,在多设备的支持中,两个设备的关系不明确。搜集了一下资料,编写了一个能够自动生成设备节点的字符驱动。
在字符驱动中,内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。这三个函数都会调用一个共用的 __register_chrdev_region() 函数来注册一组设备编号范围(即一个char_device_struct 结构)。
register_chrdev_region(dev_t first,unsigned int count,char *name)
First :要分配的设备编号范围的初始值(次设备号常设为0);
Count:连续编号范围.
Name:编号相关联的设备名称. (/proc/devices);
动态分配:
Int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
Firstminor : 通常为0;
*dev:存放返回的设备号;
char_device_struct 结构体:
static struct char_device_struct {
struct char_device_struct *next; // 指向散列冲突链表中的下一个元素的指针
unsigned int major; // 主设备号
unsigned int baseminor; // 起始次设备号
int minorct; // 设备编号的范围大小
char name[64]; // 处理该设备编号范围内的设备驱动的名称
struct file_operations *fops; // 没有使用
struct cdev *cdev; // 指向字符设备驱动程序描述符的指针
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
使用udev自动生成设备节点的函数:
class_create(…)在/drivers/base/class.c中实现:
/**
* class_create - create a struct class structure
* @owner: pointer to the module that is to "own" this struct class
* @nAME: pointer to a string for the name of this class.
*
* This is used to create a struct class pointer that can then be used
* in calls to device_create().
*
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
struct class *class_create(struct module *owner, cONst char *name)
第一个参数指定类的所有者是哪个模块,第二个参数指定类名。
device_create(…)函数在/drivers/base/core.c中实现:
/**
* 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
* @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.
*
* 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, const char *fmt, ...)
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。
下面的代码就是支持两个设备节点,但是两个设备节点支持同一个设备。
static struct class *cdev_class;
static int __init gm_cdev_init(void)
{
int result;
dev_t devno = MKDEV(MAJOR_NR, 0);
if(0 != MAJOR_NR){
result = register_chrdev_region(devno, 2, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
} else {
result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
MAJOR_NR = MAJOR(devno);
}
printk(KERN_CRIT"hello\n");
if(result < 0)
return result;
gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
if(NULL == gm_cdevp){
unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
result = -ENOMEM;
}
memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
gm_setup_cdev(&gm_cdevp[0], 0);
gm_setup_cdev(&gm_cdevp[0], 1);
//gm_setup_cdev(&gm_cdevp[1], 1); //使用这一行的话支持两个设备
cdev_class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(cdev_class))
return PTR_ERR(cdev_class);
device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0), NULL, DEV_NAME"%d", 0); //生成 /dev/gmem0 节点
device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1), NULL, DEV_NAME"%d", 1); //生成 /dev/gmem1 节点 return 0;
}
void gm_cdev_exit(void)
{
cdev_del(&gm_cdevp[0].cdev);
cdev_del(&gm_cdevp[0].cdev);
device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
class_destroy(cdev_class); //delete class created by us
kfree(gm_cdevp);
unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
}
root@wang:/work/wanghuan/drives# ls
cdev.c cdev.ko hello.c Makefile modules.order
root@wang:/work/wanghuan/drives# insmod cdev.ko
root@wang:/work/wanghuan/drives# lsmod | grep cdev
cdev 1853 0
root@wang:/work/wanghuan/drives# ls /dev/gmem* -l
crw------- 1 root root 250, 0 2011-06-12 14:00 /dev/gmem0
crw------- 1 root root 250, 1 2011-06-12 14:00 /dev/gmem1
root@wang:/work/wanghuan/drives# ls > /dev/gmem0
root@wang:/work/wanghuan/drives# cat /dev/gmem1
cdev.c
cdev.ko
hello.c
Makefile
modules.order
下面的代码就是支持两个设备节点,但是两个设备节点支持不同的设备。
static struct class *cdev_class; static int __init gm_cdev_init(void){
int result;
dev_t devno = MKDEV(MAJOR_NR, 0);
if(0 != MAJOR_NR){
result = register_chrdev_region(devno, 2, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
} else {
result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
MAJOR_NR = MAJOR(devno);
}
printk(KERN_CRIT"hello\n");
if(result < 0)
return result;
gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
if(NULL == gm_cdevp){
unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
result = -ENOMEM;
}
memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
gm_setup_cdev(&gm_cdevp[0], 0);
//gm_setup_cdev(&gm_cdevp[0], 1);
gm_setup_cdev(&gm_cdevp[1], 1); //使用这一行的话支持两个设备
cdev_class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(cdev_class))
return PTR_ERR(cdev_class);
device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0), NULL, DEV_NAME"%d", 0); //生成 /dev/gmem0 节点
device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1), NULL, DEV_NAME"%d", 1); //生成 /dev/gmem1 节点 return 0;
}
void gm_cdev_exit(void)
{
cdev_del(&gm_cdevp[0].cdev);
cdev_del(&gm_cdevp[1].cdev);
device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
class_destroy(cdev_class); //delete class created by us
kfree(gm_cdevp);
unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
}root@wang:/work/wanghuan/drives# lscdev.c cdev.ko hello.c Makefile modules.orderroot@wang:/work/wanghuan/drives# insmod cdev.ko root@wang:/work/wanghuan/drives# lsmod | grep cdevcdev 1853 0 root@wang:/work/wanghuan/drives# ls /dev/gmem* -lcrw------- 1 root root 250, 0 2011-06-12 14:08 /dev/gmem0crw------- 1 root root 250, 1 2011-06-12 14:08 /dev/gmem1root@wang:/work/wanghuan/drives# ls > /dev/gmem0root@wang:/work/wanghuan/drives# cat /dev/gmem1root@wang:/work/wanghuan/drives# cat /dev/gmem0cdev.ccdev.kohello.cMakefilemodules.order
- /**
-
* =====================================================================================
-
* Filename: cdev.c
-
* Description: 字符设备驱动模型
-
* Version: 1.0
-
* Created: 2011年06月02日 19时27分50秒
-
* Revision: none
-
* Compiler: gcc
-
*
-
* Author: wanghuan,
-
* Company:
-
* =====================================================================================
-
*/
-
-
#include <linux/module.h>
-
#include <linux/types.h>
-
#include <linux/fs.h> /* file_operation */
-
#include <linux/errno.h> /* Error number */
-
#include <linux/mm.h>
-
#include <linux/sched.h>
-
#include <linux/slab.h>
-
#include <linux/init.h> /* __init __exit */
-
#include <linux/device.h>
-
#include <linux/cdev.h>
-
#include <asm/io.h>
-
#include <asm/system.h>
-
#include <asm/uaccess.h> /* copy_to_user, copy_from_user */
-
#include <linux/kernel.h> /* printk() */
-
-
#define GLOBALMEM_SIZE 0x1000
-
#define MEM_CLEAN 0x1
-
#define DEV_NAME "gmem"
-
#define MEM_CLEAR _IO(GLOBALMEM_SIZE, 0)
-
-
static int MAJOR_NR = 250; /* Driver Major Number */
-
//static int MINOR_NR = 0; /* Driver Major Number */
-
-
struct gm_cdev_t{
-
struct cdev cdev;
-
unsigned char mem[GLOBALMEM_SIZE];
-
};
-
static struct gm_cdev_t *gm_cdevp;
-
-
static int gm_open(struct inode *inode, struct file *filp)
-
{
-
struct gm_cdev_t *dev;
-
dev = container_of(inode->i_cdev, struct gm_cdev_t, cdev);
-
filp->private_data = dev;
-
return 0;
-
}
-
static int gm_release(struct inode *inode, struct file *filp)
-
{
-
filp->private_data = NULL;
-
return 0;
-
}
-
static ssize_t gm_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
-
{
-
unsigned long p = *fpos;
-
int ret = 0;
-
-
struct gm_cdev_t *dev = filp->private_data;
-
if(p >= GLOBALMEM_SIZE)
-
return 0;
-
-
if(count > GLOBALMEM_SIZE - p)
-
count = GLOBALMEM_SIZE -p;
-
-
if(copy_to_user(buf, (void*)(dev->mem) + p, count))
-
ret = -EFAULT;
-
else{
-
*fpos +=count;
-
ret =count;
-
-
printk(KERN_INFO "read %d butes from %ld \n", count, p);
-
}
-
-
return ret;
-
}
-
-
static ssize_t gm_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
-
{
-
unsigned long p = *fpos;
-
int ret = 0;
-
-
struct gm_cdev_t *dev = filp->private_data;
-
if(p >= GLOBALMEM_SIZE)
-
return 0;
-
-
if(p >= GLOBALMEM_SIZE - p)
-
count = GLOBALMEM_SIZE - p;
-
-
if(copy_from_user(dev->mem + p, buf, count))
-
ret = - EFAULT;
-
else{
-
*fpos +=count;
-
ret = count;
-
-
printk(KERN_INFO "write %d butes from %ld \n", count, p);
-
}
-
-
return ret;
-
}
-
-
static loff_t gm_llseek(struct file *filp, loff_t offset, int orig)
-
{
-
loff_t ret;
-
switch(orig){
-
case 0:
-
if(offset < 0){
-
ret = -EFAULT;
-
break;
-
}
-
if((unsigned int)offset > GLOBALMEM_SIZE){
-
ret = -EFAULT;
-
break;
-
}
-
-
filp->f_pos = (unsigned int)offset;
-
ret = filp->f_pos;
-
break;
-
case 1:
-
if(filp->f_pos + offset > GLOBALMEM_SIZE){
-
ret = -EFAULT;
-
break;
-
}
-
-
if(filp->f_pos + offset < 0){
-
ret = -EFAULT;
-
break;
-
}
-
-
filp->f_pos += offset;
-
ret = filp->f_pos;
-
break;
-
default:
-
ret = -EFAULT;
-
}
-
return ret;
-
}
-
-
static int gm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
-
{
-
struct gm_cdev_t *dev = filp->private_data;
-
switch(cmd){
-
case MEM_CLEAR:
-
memset(dev->mem, 0, GLOBALMEM_SIZE);
-
printk(KERN_INFO "globalmem is set to zero \n");
-
break;
-
default:
-
return -EINVAL;
-
}
-
-
return 0;
-
}
-
-
static const struct file_operations gm_fops = {
-
.owner = THIS_MODULE,
-
.open = gm_open,
-
.release = gm_release,
-
.read = gm_read,
-
.write = gm_write,
-
.ioctl = gm_ioctl,
-
.llseek = gm_llseek,
-
};
-
-
static void gm_setup_cdev(struct gm_cdev_t *gm_cdev, int minor)
-
{
-
int err;
-
dev_t devno = MKDEV(MAJOR_NR, minor);
-
-
cdev_init(&gm_cdev->cdev, &gm_fops);
-
gm_cdevp->cdev.owner = THIS_MODULE;
-
err = cdev_add(&gm_cdev->cdev, devno, 1);
-
if(err != 0)
-
printk(KERN_NOTICE "Error %d adding gmen", err);
-
}
-
-
static struct class *cdev_class;
-
static int __init gm_cdev_init(void)
-
{
-
int result;
-
dev_t devno = MKDEV(MAJOR_NR, 0);
-
-
if(0 != MAJOR_NR){
-
result = register_chrdev_region(devno, 2, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
-
} else {
-
result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
-
MAJOR_NR = MAJOR(devno);
-
}
-
-
printk(KERN_CRIT"hello\n");
-
if(result < 0)
-
return result;
-
-
gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
-
if(NULL == gm_cdevp){
-
unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
-
result = -ENOMEM;
-
}
-
-
memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
-
gm_setup_cdev(&gm_cdevp[0], 0);
-
//gm_setup_cdev(&gm_cdevp[0], 1);
-
gm_setup_cdev(&gm_cdevp[1], 1);
-
-
cdev_class = class_create(THIS_MODULE, DEV_NAME);
-
if(IS_ERR(cdev_class))
-
return PTR_ERR(cdev_class);
-
-
device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0), NULL, DEV_NAME"%d", 0);
-
device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1), NULL, DEV_NAME"%d", 1);
-
return 0;
-
}
-
-
void gm_cdev_exit(void)
-
{
-
cdev_del(&gm_cdevp[0].cdev);
-
cdev_del(&gm_cdevp[1].cdev);
-
-
device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
-
device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
-
class_destroy(cdev_class); //delete class created by us
-
-
kfree(gm_cdevp);
-
unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2); //由于注册了两个设备,最后一个参数为 2
-
}
-
-
module_init(gm_cdev_init);
-
module_exit(gm_cdev_exit);
-
MODULE_LICENSE("GPL");
-
MODULE_AUTHOR("wanghuan");
阅读(958) | 评论(0) | 转发(1) |