问题
I want to create the number of minor character drivers for my module. I want to see it at the /dev/ . However, I see only one driver. What is the problem in my code? What should be the right code?
#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h> /** This is for KERN_ALERT **/
#include <linux/fs.h> /** for file operations **/
#include <linux/cdev.h> /** character device **/
#include <linux/device.h> /** for sys device registration in /dev/ and /sys/class **/
/** for copy_to_user **/
#include <asm/uaccess.h>
/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");
static struct cdev basicCdev;
static struct class *basicDriverClass;
static int basicMajorNumber = 0;
#define NUMBER_OF_MINOR_DEVICE (5)
/** Prototype for read, this will be invoked when the read function is done on to the driver **/
/** The declaration type is file operations based function pointer - read **/
static ssize_t basicRead(struct file *filp, char *buffer, size_t length,loff_t *offset);
static int basicOspen(struct inode *inode, struct file *file);
/** File Operations function pointer table **/
/** There are plenty of file operations **/
static struct file_operations fops = {
.read = basicRead,
.write = NULL,
.open = basicOspen,
.release = NULL
};
static ssize_t basicRead(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
char msg[1024] = "Hello SJ_read\0";
printk(KERN_ALERT "The Read operation called\r\n");
copy_to_user( buffer, msg, sizeof(msg) );
return sizeof(msg);
}
static int basicOspen(struct inode *inode, struct file *file)
{
printk("Kernel.Basic Driver Opened now!!\r\n");
return 0;
}
static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
int err = -1;
/** MKDEV call creates a device number i.e. combination of major and minor number **/
int devno = MKDEV(basicMajorNumber, minor);
/** Initiliaze character dev with fops **/
cdev_init(dev, fops);
/**owner and operations initialized **/
dev->owner = THIS_MODULE;
dev->ops = fops;
/** add the character device to the system**/
/** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
err = cdev_add (dev, devno, 1);
if (err)
{
printk (KERN_NOTICE "Couldn't add cdev");
}
}
static int chrDriverInit(void)
{
int result;
dev_t dev;
printk("Welcome!! Device Init now..");
/** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); **/
/** dev -> The dev_t variable type,which will get the major number that the kernel allocates. **/
/**The same name will appear in /proc/devices. **/
/** it is registering the character device **/
/** a major number will be dynamically allocated here **/
/** alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");
if( result < 0 )
{
printk("Error in allocating device");
return -1;
}
/** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
/** creating class, and then device created removes the dependency of calling mknod **/
/** A good method - the mknod way is depreciated **/
/** mknod way is - mknod /dev/<driver_name> c <majorNumber> <minorNumber>
/** add the driver to /sys/class/chardrv **/
if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL) //$ls /sys/class
{
unregister_chrdev_region(dev, 1);
return -1;
}
/** add the driver to /dev/pSeudoDrv -- here **/
if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
/** let's see what major number was assigned by the Kernel **/
basicMajorNumber = MAJOR(dev);
printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );
/** Now setup the cdev **/
setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);
return 0;
}
static void chrDriverExit(void)
{
/** A reverse - destroy mechansim -- the way it was created **/
printk("Releasing Simple Devs -- %s\r\n", __FUNCTION__);
/** delete the character driver added **/
cdev_del(&basicCdev);
/** destroy the device created **/
device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
/** destroy the class created **/
class_destroy(basicDriverClass);
/** unregister the chr dev **/
unregister_chrdev(basicMajorNumber, NUMBER_OF_MINOR_DEVICE);
}
module_init(chrDriverInit);
module_exit(chrDriverExit);
I get errors, if I modify the init function to the following.
static int chrDriverInit(void)
{
int result;
dev_t dev;
dev_t dev2;
printk("Welcome!! Device Init now..");
/** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); **/
/** dev -> The dev_t variable type,which will get the major number that the kernel allocates. **/
/**The same name will appear in /proc/devices. **/
/** it is registering the character device **/
/** a major number will be dynamically allocated here **/
/** alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");
if( result < 0 )
{
printk("Error in allocating device");
return -1;
}
/** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
/** creating class, and then device created removes the dependency of calling mknod **/
/** A good method - the mknod way is depreciated **/
/** mknod way is - mknod /dev/<driver_name> c <majorNumber> <minorNumber>
/** add the driver to /sys/class/chardrv **/
if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL) //$ls /sys/class
{
unregister_chrdev_region(dev, 1);
return -1;
}
/** add the driver to /dev/pSeudoDrv -- here **/
if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
/** let's see what major number was assigned by the Kernel **/
basicMajorNumber = MAJOR(dev);
printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );
dev2 = MKDEV(basicMajorNumber,2);
#if 1
if (device_create(basicDriverClass, NULL, dev2, NULL, "pSeudoDrv2") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
#endif
/** Now setup the cdev **/
setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);
return 0;
}
Here is the crash -
[19554.180535] CPU: 1 PID: 16636 Comm: insmod Tainted: GF W O 3.13.5-101.fc19.x86_64 #1
[19554.180537] Hardware name: Dell Inc. Studio 1558/0G939P, BIOS A07 06/24/2010
[19554.180539] 0000000000000009 ffff8800667d7ad8 ffffffff81680664 ffff8800667d7b20
[19554.180543] ffff8800667d7b10 ffffffff8106d35d ffff880130f9e810 00000000ffffffef
[19554.180547] 0000000000000000 ffff880133432378 0000000000000000 ffff8800667d7b70
[19554.180551] Call Trace:
[19554.180555] [<ffffffff81680664>] dump_stack+0x45/0x56
[19554.180558] [<ffffffff8106d35d>] warn_slowpath_common+0x7d/0xa0
[19554.180562] [<ffffffff8106d3cc>] warn_slowpath_fmt+0x4c/0x50
[19554.180566] [<ffffffff81315be4>] kobject_add_internal+0x214/0x320
[19554.180570] [<ffffffff81316115>] kobject_add+0x65/0xb0
[19554.180574] [<ffffffff81315550>] ? kobject_put+0x30/0x60
[19554.180578] [<ffffffff814138b5>] device_add+0x125/0x640
[19554.180582] [<ffffffff81413fe0>] device_create_groups_vargs+0xe0/0x110
[19554.180586] [<ffffffffa078d0e0>] ? basicRead+0xc0/0xc0 [character_driver]
[19554.180590] [<ffffffff81414071>] device_create+0x41/0x50
[19554.180595] [<ffffffffa078d1d3>] chrDriverInit+0xf3/0x160 [character_driver]
[19554.180598] [<ffffffff8100214a>] do_one_initcall+0xfa/0x1b0
[19554.180602] [<ffffffff81054803>] ? set_memory_nx+0x43/0x50
[19554.180609] [<ffffffff810e060d>] load_module+0x1e1d/0x25b0
[19554.180613] [<ffffffff810dc320>] ? store_uevent+0x40/0x40
[19554.180617] [<ffffffff810e0f16>] SyS_finit_module+0x86/0xb0
[19554.180622] [<ffffffff8168f629>] system_call_fastpath+0x16/0x1b
[19554.180624] ---[ end trace dd8ae4ccda100ad8 ]---
回答1:
Your problem is that you only call device_create() for the first device. alloc_chrdev_region() only allocate major/minor numbers but it does not actually create the device structures in the kernel. The dev_t returned by alloc_chrdev_region() represents the first of your five major/minor numbers. You need to use a for loop, increment the minor number and then call device_create() on all of them.
Don't forget to destroy all of them in the exit function.
回答2:
The kernel does not create device nodes automatically.
Something in userspace must do it, using mknod(2) or the mknod utility.
This can be done manually or when a file system image is created, by a script on boot, or by a daemon such as udev.
来源:https://stackoverflow.com/questions/23085691/why-the-number-of-minor-devices-are-not-listed-here-in-this-program-in-dev