Linux has a global array of structs, a total of 255 elements, recording device nodes in the system.
The primary device number is the same, and the secondary device number differs from the list of devices.
Reference: http://edsionte.com/techblog/archives/1393
Registering a character device call function
Register_chrdev (Major, Dev_name, &fops)StaticInlineintRegister_chrdev (unsignedintMajorConst Char*name,Const structFile_operations *fops) { return__register_chrdev (Major,0, the, name, fops);} Application secondary device number minimum starting from 0, apply for 256 secondary devices. int__register_chrdev (unsignedintMajor, unsignedintBaseminor, unsignedintCountConst Char*name,Const structFile_operations *fops) { structChar_device_struct *cd; structCdev *Cdev; intErr =-Enomem; //gets the structure body that can be used from the global array of character device structures Char_device_structcd =__register_chrdev_region (Major, Baseminor, count, name); if(Is_err (CD))returnPtr_err (CD); Cdev=Cdev_alloc (); if(!Cdev)GotoOut2; Cdev->owner = fops->owner; Cdev->ops =FoPs; Kobject_set_name (&cdev->kobj,"%s", name); //adding a character device to the systemErr = Cdev_add (Cdev, MKDEV (cd->Major, Baseminor), count); if(ERR)Goto out; CD->cdev =Cdev; returnMajor?0: cd->Major; out: Kobject_put (&cdev->kobj); Out2:kfree (__unregister_chrdev_region (CD-Major, Baseminor, count)); returnerr;}Static structChar_device_struct *__register_chrdev_region (unsignedintMajor, unsignedintBaseminor,intMINORCT,Const Char*name) { structChar_device_struct *CD, * *CP; intRET =0; inti; CD= Kzalloc (sizeof(structchar_device_struct), Gfp_kernel); if(cd = =NULL)returnErr_ptr (-Enomem); Mutex_lock (&Chrdevs_lock); /*Temporary*/ if(Major = =0) { //If the device number is 0, iterate through the array of character device nodes, find the idle device number, assign to the device 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; strlcpy (CD->name, Name,sizeof(cd->name)); I=Major_to_index (Major); //traverse the device list to add a new device to the linked list based on the main device number and secondary device number//Offset Master device number 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 that the scope of the secondary device number is overlapping, but this function has already specified the device 0-255 (256) at the beginning.
So it's bound to overlap, which requires that the main device number cannot overlap the existing character device, or the device must fail.
Other functions may be smaller and need to be judged below.
/*Check for overlapping minor ranges. */ if(*CP && (*CP)->major = =Major) { intOld_min = (*CP)Baseminor; intOld_max = (*CP)->baseminor + (*CP)->MINORCT-1; intNew_min =Baseminor; intNew_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; } } //add to Linked listCd->next = *CP; *CP =cd; Mutex_unlock (&Chrdevs_lock); returncd; out: Mutex_unlock (&Chrdevs_lock); Kfree (CD); returnErr_ptr (ret);}
Another type of registered character device function, similar to the one above.
struct Cdev Cdev;
dev_t Devno;
int __init test_init (void)
{
int ret;
Static assignment
Specify the main device number and the secondary device number, and the device range is 1.
dev_t Devno = MKDEV (globalmem_major, 0);
//ret = register_chrdev_region (&devno, 1, dev_name);
Dynamic allocation
ret = alloc_chrdev_region (&devno, 0, 1, dev_name);
if (ret)
{
PRINTK ("Alloc_chrdev_region failed!\n");
Return-eagain;
}
Else
PRINTK ("major =%d\n", Major (Devno));
Cdev_init (&cdev, &fops);
Cdev.owner = This_module;
ret = Cdev_add (&cdev, Devno, 1);
if (ret)
{
Release the previously assigned device number
Unregister_chrdev_region (Devno, 1);
PRINTK ("Cdev_add failed!\n");
}
return ret;
}
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;
Determine whether the scope of the requested device number will overflow to the next master device.
Overflow, it is divided into basic points, part of the current main device node allocation
Another part to the next main device node assignment
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);
}
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 structure relationship is as follows
Linux character device