register_chrdev,register_chrdev_region,alloc_chrdev_region---udev class bus driver device

我要啦免费统计
3.4.2. The Older Way

If you dig through much driver code in the 2.6 kernel, you may notice that quite a few char drivers donot use the cdev interface that we have just described. What you are seeing is older code that has not yet been upgraded to the 2.6 interface. Since that code works as it is, this upgrade may not happen for a long time. For completeness, we describe theolder char device registration interface, but new code should not use it; this mechanism will likely go away in a future kernel.

The classic way to register a char device driver is with:

int register_chrdev(unsigned int major, const char *name,

                    struct file_operations *fops);

Here, major is the major number of interest, name is the name of the driver (it appears in/proc/devices), and fops is the default file_operations structure. A call toregister_chrdev registers minor numbers 0-255 for the given major, and sets up a default cdev structure for each. Drivers using this interface must be prepared to handleopen calls on all 256 minor numbers (whether they correspond to real devices or not), and they cannot use major or minor numbers greater than 255.

If you use register_chrdev, the proper function to remove your device(s) from the system is:

int unregister_chrdev(unsigned int major, const char *name);

major and name must be thesame as those passed toregister_chrdev, or the call will fail.

***************************************************************************************************************************************************
3.2.2. Allocating and Freeing Device Numbers

One of the first things your driverwill need to dowhen setting up a char device is to obtain one or more device numbers to work with. The necessary function for this task isregister_chrdev_region, which is declared in<linux/fs.h>:

int register_chrdev_region(dev_t first, unsigned int count, 

                           char *name);

Here, first is the beginning device number of the range you would like to allocate. The minor number portion of first is often 0, but there is no requirement to that effect. count is the total number of contiguous device numbers you are requesting. Note that, if count is large, the range you request could spill over to the next major number; but everything will still work properly as long as the number range you request is available. Finally, name is the name of the device that should be associated with this number range; it will appear in /proc/devices and sysfs.

As with most kernel functions, the return value fromregister_chrdev_region will be 0 if the allocation was successfully performed. In case of error, a negative error code will be returned, and you will not have access to the requested region.

register_chrdev_region works well if you know ahead of time exactly which device numbers you want. Often, however, you will not know which major numbers your device will use; there is a constant effort within the Linux kernel development community to move over to the use of dynamicly-allocated device numbers. The kernel will happily allocate a major number for you on the fly, but you must request this allocation by using a different function:

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, 

                        unsigned int count, char *name);

With this function, dev is an output-only parameter that will, on successful completion, hold the first number in your allocated range. firstminor should be the requested first minor number to use; it is usually 0. The count and name parameters work like those given to request_chrdev_region.

Regardless of how you allocate your device numbers, you should free them when they are no longer in use. Device numbers are freed with:

void unregister_chrdev_region(dev_t first, unsigned int count);

The usual place to call unregister_chrdev_region would be in your module's cleanup function.

The above functions allocate device numbers for your driver's use, but they do not tell the kernel anything about what you will actually do with those numbers. Before a user-space program can access one of those device numbers, your driver needs to connect them to its internal functions that implement the device's operations. We will describe how this connection is accomplished shortly, but there are a couple of necessary digressions to take care of first.


register_chrdev参数包含file_operation,
register_chrdev_region(alloc_chrdev_region)的参数却包括cdev,由cdev去处理file_operation

看来新版的字符设备注册貌似比旧版麻烦,增加了一层字符设备描述结构cdev
新版分两步
register_chrdev_region(alloc_chrdev_region)申请设备号
cdev注册设备
旧版一步到位
register_chrdev 申请设备号并注册设备

使用上述方法并不能在/dev目录下自动建立设备文件,需要手动 mknod Name { b | c } Major Minor 
或者借助class_create和device_create创建之,如下.
*******************************************************************************************************************

内核的好多驱动的实现都会使用class_create和device_create借助udev(mdev)
自动创建设备节点,下面是一个例子 for linux2.6.32---2011-12-8


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

int hello_major = 66;
int hello_minor = 0;
int number_of_devices = 1;

struct cdev cdev;
dev_t dev = 0;

struct file_operations hello_fops = {
	.owner = THIS_MODULE
};
struct class *my_class;

static int __init test_init (void)
{
	int result,error,devno;
	dev = MKDEV (hello_major, hello_minor);
	result = register_chrdev_region (dev, number_of_devices, "hello");
	if (result<0) {
	   printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
	   return result;
	}

	devno = MKDEV (hello_major, hello_minor);
	cdev_init (&cdev, &hello_fops);
	cdev.owner = THIS_MODULE;
	cdev.ops = &hello_fops;
	error = cdev_add (&cdev, devno , 1);
	if (error)
	   printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);

	/* create your own class under /sysfs */
	my_class = class_create(THIS_MODULE, "my_class");
	if(IS_ERR(my_class))
	{
	printk("Err: failed in creating class.\n");
	return -1;
	}

	/* register your own device in sysfs, and this will cause udev to create corresponding device node */
	device_create( my_class, NULL, MKDEV(hello_major,hello_minor), NULL,"hello%d",0);
	printk (KERN_INFO "Registered character driver\n");
	return 0;
}

static void __exit test_exit (void)
{
	dev_t devno = MKDEV (hello_major, hello_minor);
	cdev_del (&cdev);
	device_destroy(my_class, MKDEV(hello_major,  hello_minor));         //delete device node under /dev
	class_destroy(my_class);                               //delete class created by us

	unregister_chrdev_region (devno, number_of_devices);
	printk (KERN_INFO "char driver cleaned up\n");
}

module_init (test_init);
module_exit (test_exit);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Song");
demo refer to http://www.embedu.org/Column/Column120.htm

相关的只有3行代码
1.struct class *my_class;
2.my_class = class_create(THIS_MODULE, "my_class");
3.device_create( my_class, NULL, MKDEV(hello_major,hello_minor), NULL,"hello%d",0);
在各个子系统的驱动实现中,device_create一般会写在probe函数
,以便在探测到设备时自动执行 device_create
*******************************************************************************************************************

关于bus driver device的测试----2011-12-8
the codes as follows refer to
http://blog.chinaunix.net/space.php?uid=25677340&do=blog&id=2183894
http://blog.chinaunix.net/space.php?uid=25622207&do=blog&id=2792948
2.6.32

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

#include<linux/string.h>
#include<linux/device.h>

MODULE_AUTHOR("AW");
MODULE_LICENSE("GPL");

static char *Version="imlink 2.0";
static int my_match(struct device *dev,struct device_driver *driver)
{
	int result=0;
	printk("<1>Now prepare to compare!!!!");
	printk("<1>the device init_name is %s\n",dev->init_name);
	printk("<1>the new ini_name is %s\n",(dev_name(dev)));
	printk("<1>the driver name is %s\n",driver->name);
	result=strncmp(dev_name(dev),driver->name,strlen(driver->name));

	if(result==0)
	    return 1;
	else
	    return 0;
}

static void my_bus_release(struct device *dev)
{
	printk("<1>my bus released!");
}


struct device my_bus=
{
	.init_name="my_bus0",
	.release=my_bus_release,
};


struct bus_type my_bus_type=
{
	.name="my_bus2",
	.match=my_match,
};

EXPORT_SYMBOL(my_bus);
EXPORT_SYMBOL(my_bus_type);

static ssize_t show_bus_version(struct bus_type *bus,char *buf)
{
	return snprintf(buf,PAGE_SIZE,"%s\n",Version);
}

static BUS_ATTR(version,S_IRUGO,show_bus_version,NULL);

static int my_bus_init(void)
{
	int ret=bus_register(&my_bus_type);
	if(ret)
	{
	    return ret;
	}

	if(bus_create_file(&my_bus_type,&bus_attr_version))
	{
	    printk("<1>Fail to create version attribute!\n");
	}

	ret=device_register(&my_bus);

	if(ret)
	{
	    printk("<1>Fail to register device :my_bus\n");
	}
	return ret;
}

static void my_bus_exit(void)
{
	device_unregister(&my_bus);
	bus_unregister(&my_bus_type);
}

module_init(my_bus_init);
module_exit(my_bus_exit); 

//driver.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

MODULE_AUTHOR("AW");
MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

static int my_probe(struct device *dev)
{
	printk("Driver found device which my driver can handle!\n");
	return 0;
}

static int my_remove(struct device *dev)
{
	printk("Driver found device unpluged!\n");
	return 0;
}

struct device_driver my_driver = {
	.name = "my_dev1",
	.bus = &my_bus_type,
	.probe = my_probe,
	    .remove    = my_remove,
};

static ssize_t mydriver_show(struct device_driver *driver, char *buf)
{
	return sprintf(buf, "%s\n", "This is my driver!");
}

static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);

static int my_driver_init(void)
{
	int ret=0;
	driver_register(&my_driver);
	driver_create_file(&my_driver, &driver_attr_drv);
	return ret;    
}

static void my_driver_exit(void)
{
	driver_unregister(&my_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit); 

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

#include<linux/string.h>
#include<linux/device.h>

MODULE_AUTHOR("AW");
MODULE_LICENSE("GPL");

extern struct device my_bus;
extern struct bus_type my_bus_type;

static void my_dev_release(struct device *dev)
{

}

struct device my_dev=
{
	.init_name="my_dev1",
	.bus=&my_bus_type,
	.parent=&my_bus,
	.release=my_dev_release,
};

static ssize_t my_dev_show(struct device *dev,struct device_attribute *attr,char *buf)
{
	return sprintf(buf,"%s\n","this is my device");
}

static DEVICE_ATTR(dev,S_IRUGO,my_dev_show,NULL);

static int my_device_init(void)
{
	int ret=0;
	device_register(&my_dev);
	device_create_file(&my_dev,&dev_attr_dev);
	return ret;
}

static void my_device_exit(void)
{
	device_unregister(&my_dev);
}

module_init(my_device_init);
module_exit(my_device_exit); 
执行结果:
[root@FriendlyARM plg]# insmod bus.ko 
[root@FriendlyARM plg]# insmod driver.ko 
[root@FriendlyARM plg]# insmod device.ko 
Now prepare to compare!!!!
the device init_name is <NULL>
the new ini_name is my_dev1
the driver name is my_dev1
Driver found device which my driver can handle!

[root@FriendlyARM plg]# ls  /sys/bus/
/sys/bus/hid/         /sys/bus/platform/    /sys/bus/usb-serial/
/sys/bus/i2c/         /sys/bus/scsi/        /sys/bus/usb/
/sys/bus/mmc/         /sys/bus/sdio/
/sys/bus/my_bus2/     /sys/bus/spi/
[root@FriendlyARM plg]# ls  /sys/bus/my_bus2/
devices            drivers_autoprobe  uevent
drivers            drivers_probe      version
[root@FriendlyARM plg]# ls  /sys/bus/my_bus2/devices/
my_dev1
[root@FriendlyARM plg]# ls  /sys/bus/my_bus2/drivers
my_dev1



sys目录下的结构
/sys/devices是最重要的目录,/sys/class和/sys/bus/中的很多文件都是链接到那里的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值