Chapter 2 character Device Driver
Some important data structures and reference relationships:
Step 1: dynamically allocate major device numbers
Int register_chrdev_region (dev_t first, unsigned int count, char * Name );
Int alloc_chrdev_region (dev_t * Dev, unsigned int firstminor, unsigned int count, char * Name); // Dynamic Allocation
Void unregister_chrdev_region (dev_t first, unsigned int count );
Obtain the device IDCode:
If(Scull_major ){
Devno = mkdev (scull_major, scull_minor );// Dev_t devno
Result = register_chrdev_region (devno, scull_nr_devs,"Scull");
}Else{
Result = alloc_chrdev_region (& devno, scull_minor, scull_nr_devs,"Scull");
Scull_major = major (Dev );
}
If(Result <0 ){
Printk (kern_warning"Scull: Can't Get Major % d \ n", Scull_major );
ReturnResult;
}
Generally, the default major is provided, and the user-specified major is passed in through the option parameter. If the conflict exists, the device number is dynamically allocated.
Script for Loading modules and creating device files:
#! /Bin/sh
Module ="Scull"
Device ="Scull"
Mode ="664"
# Invoke insmod with all arguments we got
# And use a pathname, as newer modutils don't look in. By default
/Sbin/insmod./$ module. Ko $ * | Exit 1
# Remove stale nodes
Rm-F/dev/$ {Device} [0-3]
Major = $ (awk"\\$ 2 ==\" $ module \" {print \ $1 }"/Proc/devices)
Mknod/dev/$ {Device} 0 C $ major 0
Mknod/dev/$ {Device} 1 C $ major 1
Mknod/dev/$ {Device} 2 C $ major 2
Mknod/dev/$ {Device} 3 C $ major 3
# Give appropriate group/permissions, and change the group.
# Not all distributions have staff, some have "Wheel" instead.
Group ="Staff"
Grep-Q'^ Staff :'/Etc/group | group ="Wheel"
Chgrp $ group/dev/$ {Device} [0-3]
Chmod $ mode/dev/$ {Device} [0-3]
Step 2: create a specific device structure
Generally, the cdev structure is embedded in this device (see the leftmost box)
StructScull_dev {
StructScull_qset * data;/* Pointer to First Quantum Set */
IntQuantum;/* The current quantum size */
IntQset;/* The current array size */
UnsignedLongSize;/* Amount of data stored here */
UnsignedIntAccess_key;/* Used by sculluid and scullpriv */
StructSemaphore SEM;/* Mutual Exclusion semaphore */
StructCdev;/* Char device structure */
};
Step 3: register a character device with the kernel
Method 1: dynamically allocate cdev
StructCdev * my_cdev = cdev_alloc ();
My_cdev-> Ops = & my_fops;
Method 2: The struct has been embedded into us, that is, it already exists.
VoidCdev_init (StructCdev * cdev,StructFile_operations * FoPs); // initialize first
IntCdev_add (StructCdev * Dev, dev_t devno, unsignedIntCount );// Register with the kernel
VoidCdev_del (StructCdev * Dev );// Uninstall cdev
Because ops members have been initialized in cdev_ini, you can use Dev-> Ops = & scull_fops; again, as shown in the following code:
Static VoidScull_setup_cdev (StructScull_dev * Dev,IntIndex)
{
IntErr, devno = mkdev (scull_major, scull_minor + index );
Cdev_init (& Dev-> cdev, & scull_fops );
Dev-> cdev. Owner = this_module;
Dev-> cdev. Ops = & scull_fops;
Err = cdev_add (& Dev-> cdev, devno, 1 );
/* Fail gracefully if need be */
If(ERR)
Printk (kern_notice"Error % d adding scull % d", Err, index );
}
Step 4: Write the implementation of the operation function pointer member in the file_operation struct
Omitted
A tip worth mentioning: usually point the private_data Member of the struct file to our device struct (here it is struct scull_dev). This assignment is done in the open function. Other operation functions can directly obtain the device struct through the private member of the file, which is very convenient. In addition, the reason for doing so is that the driver is used when multiple device files exist.