Character Devices register_chrdev_region (), Alloc_chrdev_region () and Register_chrdev () (reproduced)

Source: Internet
Author: User
Tags ranges

1. Character Device structure body

All assigned character device numbers in the kernel are recorded in a hash list named Chrdevs. Each element in the hash list is a char_device_struct structure, which is defined as follows:

static struct Char_device_struct {
struct Char_device_struct *next; Pointer to the next element in the hash conflict list
unsigned int major; Main device number
unsigned int baseminor; Starting device number
int minorct; Range size of device number
Char name[64]; Processing device-driven names within the device number range
struct File_operations *fops; Not used
struct Cdev *cdev; Pointer to character device driver descriptor
}*chrdevs[chrdev_major_hash_size];

Note that the kernel does not define a char_device_struct structure for each character device, Instead, a CHAR_DEVICE_STRUCT structure is defined for a group (the same device with the main device number) that corresponds to the same character device-driven device number range. The size of the Chrdevs hash table is 255, and the hashing algorithm inserts the main device number of each set of character device number ranges into the corresponding hash bucket with 255 modulo. The character device number range in the same hash bucket is ordered incrementally by the starting device number.

2. Registration of a character device
The kernel provides three functions to register a set of character device numbers, three of which are Register_chrdev_region (), Alloc_chrdev_region (), and Register_chrdev (). These three functions call a common __register_chrdev_region () function to register a set of device numbering ranges (that is, a char_device_struct structure).

Register_chrdev_region (dev_t first,unsigned int Count,char *name)
First: The initial value of the range of device numbers to be assigned (the secondary device number is standing at 0);
Count: Continuous numbering range.
Name: The device name associated with the number. (/proc/devices);
Dynamic allocation:
int alloc_chrdev_region (dev_t *dev,unsigned int firstminor,unsigned int Count,char *name);
Firstminor: usually 0;
*dev: Store the returned device number;
Release:
Void unregist_chrdev_region (dev_t first,unsigned int count);
The assigned device number can be found in the call Documentation/devices.txt.

So let's take a look at the implementation code of the __register_chrdev_region () function.


__register_chrdev_region ()
static struct char_device_struct * __register_chrdev_region (unsigned int major, unsigned int baseminor, int minorct, const Char *name) {
struct char_device_struct *cd, **CP;
int ret = 0;
int i;
cd = kzalloc (sizeof (struct char_device_struct), gfp_kernel);
if (cd = = NULL)
Return Err_ptr (-ENOMEM);
Mutex_lock (&chrdevs_lock);
if (major = = 0) {
for (i = array_size (Chrdevs)-1; i > 0; i--)
if (chrdevs[i] = = NULL)
Break
if (i = = 0) {
ret =-ebusy;
Goto out;
}
major = i;
ret = major;
}
Cd->major = major;
Cd->baseminor = Baseminor;
CD->MINORCT = MINORCT;
strncpy (Cd->name,name, 64);
i = Major_to_index (major);
for (cp = &chrdevs[i]; *CP; cp = & (*CP)->next)
if ((*CP)->major > Major | |
((*CP)->major = = Major && (((*CP)->baseminor >= baseminor) | | ((*CP)->baseminor + (*CP)->minorct > Baseminor)) ))
Break
/* Check for overlapping minor ranges. */
if (*CP && (*CP)->major = = Major) {
int old_min = (*CP)->baseminor;
int Old_max = (*CP)->baseminor + (*CP)->minorct-1;
int new_min = Baseminor;
int New_max = Baseminor + minorct-1;

/* New driver overlaps from the left. */
if (New_max >= old_min && new_max <= old_max) {
ret =-ebusy;
Goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret =-ebusy;
Goto out;
}
}
Cd->next = *CP;
*CP = CD;
Mutex_unlock (&chrdevs_lock);
return CD;
Out
Mutex_unlock (&chrdevs_lock);
Kfree (CD);
Return err_ptr (ret);
}
The function __register_chrdev_region () primarily performs the following steps:
1. Assign a new char_device_struct structure and fill it with 0.
2if the requested device number range has a main device number of 0, then the device driver request dynamically assigns a master device number. The principle of dynamically allocating a master device number is to look forward from the last bucket in the hash list, which is empty, and the main device number is the ordinal of the corresponding hash bucket. So the dynamic allocation of the main device number is always less than 256, if each bucket has a character device number, then the dynamic allocation will fail.
3. Set the initial device number, the range size and the device driver name in the CHAR_DEVICE_STRUCT structure according to the parameters.
4. Calculate the hash bucket corresponding to the main device number and find the correct position for the new char_device_struct structure. Also, if the device number range is duplicated, an error is returned.
5. Insert the new char_device_struct structure into the hash table and return the address of the CHAR_DEVICE_STRUCT structure.

After analyzing __register_chrdev_region (), let's look at the three registration functions.


Register_chrdev_region ()

int Register_chrdev_region (dev_t from, unsigned count, const char *name)
{
struct Char_device_struct *cd;
dev_t to = from + count;
dev_t N, Next;

for (n = from, n < to; n = next) {
Next = MKDEV (MAJOR (n) +1, 0);
if (Next > To)
Next = to;
cd = __register_chrdev_region (MAJOR (n), MINOR (n), next-n, name);
if (Is_err (CD))
Goto fail;
}
return 0;
Fail
to = n;
for (n = from, n < to; n = next) {
Next = MKDEV (MAJOR (n) +1, 0);
Kfree (__unregister_chrdev_region (MAJOR (n), MINOR (n), next-n));
}
Return Ptr_err (CD);
}

The Register_chrdev_region () function is used to assign a specified range of device numbers. If the requested device number range spans the main device number, it splits the number in the allocated range by the main device number into smaller sub-ranges and calls __register_chrdev_region () on each child range. If one of these allocations fails, it will return all the previously successful allocations.


Alloc_chrdev_region
int alloc_chrdev_region (dev_t *dev, unsigned baseminor, unsigned count, const char *name)
{
struct Char_device_struct *cd;
cd = __register_chrdev_region (0, Baseminor, count, name);
if (Is_err (CD))
Return Ptr_err (CD);
*dev = MKDEV (Cd->major, Cd->baseminor);
return 0;
}
The Alloc_chrdev_region () function is used to dynamically request a range of device numbers, and this function does not seem to check the scope of the case, but the dynamic allocation always find an empty hash bucket, so the problem is not very large. Returns the actual starting device number obtained by using the pointer parameter.


Register_chrdev

int Register_chrdev (unsigned int major, const char *name, const struct file_operations *fops)
{
struct Char_device_struct *cd;
struct Cdev *cdev;
Char *s;
int err =-ENOMEM;

cd = __register_chrdev_region (major, 0, N, name);
if (Is_err (CD))
Return Ptr_err (CD);

Cdev = Cdev_alloc ();
if (!cdev)
Goto Out2;

Cdev->owner = fops->owner;
Cdev->ops = FoPs;
Kobject_set_name (&cdev->kobj, "%s", name);
for (s = STRCHR (Kobject_name (&cdev->kobj), '/'); s; s = STRCHR (S, '/'))
*s = '! ';

Err = Cdev_add (Cdev, MKDEV (cd->major, 0), 256);
if (ERR)
Goto out;

Cd->cdev = Cdev;

Return major? 0:cd->major;
Out
Kobject_put (&cdev->kobj);
Out2:
Kfree (__unregister_chrdev_region (cd->major, 0, 256));
return err;
}
The last Register_chrdev () is an old-fashioned function that allocates a range of device numbers. It assigns a single master device number and a 0 to 255 secondary device number range. If the requested master device number is 0, one is dynamically assigned. The function also needs to pass in a pointer to the file_operations structure, which automatically assigns a new CDEV structure inside the function.



sign out of a character device
Similar to registering the assigned character device number range, the kernel provides a function of two logoff character device number ranges, namely Unregister_chrdev_region () and Unregister_chrdev (). They all call the __unregister_chrdev_region () function. Because it is relatively simple, it is not explained, just paste the code out.

static struct char_device_struct * __unregister_chrdev_region (unsigned major, unsigned baseminor, int minorct)
{
struct Char_device_struct *cd = NULL, **CP;
int i = Major_to_index (major);

Mutex_lock (&chrdevs_lock);
for (cp = &chrdevs[i]; *CP; cp = & (*CP)->next)
if ((*CP)->major = = Major &&
(*CP)->baseminor = = Baseminor &&
(*CP)->minorct = = MINORCT)
Break
if (*CP) {
cd = *CP;
*CP = cd->next;
}
Mutex_unlock (&chrdevs_lock);
return CD;
}

void Unregister_chrdev_region (dev_t from, unsigned count)
{
dev_t to = from + count;
dev_t N, Next;

for (n = from, n < to; n = next) {
Next = MKDEV (MAJOR (n) +1, 0);
if (Next > To)
Next = to;
Kfree (__unregister_chrdev_region (MAJOR (n), MINOR (n), next-n));
}
}

void Unregister_chrdev (unsigned int major, const char *name)
{
struct Char_device_struct *cd;
cd = __unregister_chrdev_region (major, 0, 256);
if (CD && Cd->cdev)
Cdev_del (Cd->cdev);
Kfree (CD);

}



< Span style= "FONT-SIZE:13PX;" >< Span style= "FONT-SIZE:13PX;" > using them to register character devices requires manual device node creation

Character Devices register_chrdev_region (), Alloc_chrdev_region (), and Register_chrdev () (reprinted)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.