Professional Documents
Culture Documents
Automatically Allocated Major Number and Multiple Minor Numbers
Automatically Allocated Major Number and Multiple Minor Numbers
Mestrado em Engenharia Eletrotécnica e de Computadores Automatically allocated major number and multiple minor numbers
Arquitetura de Computadores
The module presented below creates NDEVICES character devices, all sharing the same major
number. Each character device is associated to a different minor number. The major and
Loadable Kernel Module – Blocking operations and waitqueues minor numbers are automatically assigned by the system (alloc_chrdev_region is used
instead of register_chrdev_region), i.e., they are not “hard coded”.
Deliverables: final implementation of chrdev.c to jes@isep.ipp.pt, using the following text
as subject: ARCOM-LAB7-FINAL Since the major and minor numbers are automatically assigned, how does the user space
Deliverables: handwritten implementation of 7.2 (arcom_chrdev_read) processes get to know this information and create the respective device files? In this case, the
device files are created by the module itself (see the call to the device_create function),
using a denomination that is defined in module code and, therefore, is known a priori.
Additionally, device_create passes the major and minor number to sysfs1, and this
The minimal skeleton information can be queried under the directory corresponding to the device class (in this case,
/sys/class/arcom-examples/).
Recall the minimal structure of a Linux kernel module:
1
sysfs is a non-persistent virtual filesystem, usually mounted in /sys, that provides a global view of the system
and exposes the kernel object's attributes and overall topology.
int arcom_chrdev_open(struct inode * inode, struct file * filp) ls -l /dev | grep arcom-chrdev
{
pr_info("Device open\n"); 6) Perform a read operation on the device file:
return 0;
} cat /dev/arcom-chrdev0
2
See “man kernel_lockdown” and https://fedoraproject.org/wiki/Secureboot
ssize_t arcom_chrdev_write(struct file * filp, const char __user * buf, The condition for wait_event_interruptible may simply be the test of a flag variable,
size_t count, loff_t *offset)
for instance:
{
int i, n;
int deviceOpen = 0;
int last = *offset + count;
if(last > MAX_FILE_SIZE) This variable should be set to 1 when the access to the device is granted to a process, and to 0
last = MAX_FILE_SIZE; when the process closes the device. Then, upon each call to the open function, the module
should check the condition like this:
i = last - *offset;
n = copy_from_user(data + *offset, buf, i); wait_event_interruptible(myWaitQueue, !deviceOpen);
if(n!=0) {
pr_warning("Could not copy %d bytes\n", n); Moreover, the module should call wake_up_interruptible before returning from the
return -EFAULT;
}
release function.
*offset += i;
8.2) Test the new feature using the following program (open_dev.c, provided at the website):
if(size < *offset) //keep data from previous accesses
size = *offset; void handler(int s) {
printf("Received signal %d\n", s);
return i; };
}
int main(int argc, char *argv[]) {
int fd;
7.2) Implement the arcom_chrdev_read function.
signal(SIGINT, handler); //handler for SIGINT (signal generated by CTRL+C)
printf("Process %d\n", getpid());
Module locking/unlocking if((argc > 1) && strcmp(argv[1], "-NONBLOCK")==0)
fd = open("/dev/arcom-chrdev0", O_RDWR | O_NONBLOCK);
7.3) The functions try_module_get and module_put are used to update an internal counter else
of processes using the module. This is useful to ensure the module is not unloaded while a fd = open("/dev/arcom-chrdev0", O_RDWR);
device is in use. To achieve that goal, call try_module_get(THIS_MODULE) at the if(fd<0) {
perror("open");
beginning of the open function, and module_put(THIS_MODULE) before returning from the return 1;
release function. }
printf("File descriptor: %d\n", fd);
pause(); //wait until a signal is received
return 0;
}
9.2) Change NDEVICES to 2 and test the module as described next. In the first terminal:
./open_dev
10) Execute the following command as a standard user: int alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char
* name)
cat /dev/arcom-chrdev0
register a range of char device numbers
10.1) The command will fail due to lack of permissions. It is possible to change the permissions
manually using chmod, however, if the module must be accessed frequently by non-privileged
Parameters
users, that would quickly become a tedious task and would eventually end up requiring adding
a new command to the system startup scripts. dev_t * dev
For distributions based on udev, it is possible to automatically change the permissions of a output parameter for first assigned number
(automatically created) device file by creating a file with the corresponding rule in unsigned baseminor
/etc/udev/rules.d: first of the requested range of minor numbers
unsigned count
# nano /etc/udev/rules.d/70-arcom.rules the number of minor numbers required
KERNEL=="arcom-chrdev*", MODE="0666" const char * name
the name of the associated device or driver
In this case, the rule gives read and write permissions for all users for device files whose name
has the arcom-chrdev prefix.
Description
After creating the file listed above, run udevadm control --reload-rules (to make sure
Allocates a range of char device numbers. The major number will be chosen dynamically,
udev loads the new rule) and reload the module: and returned (along with the first minor number) in dev. Returns zero or a negative error
udevadm control --reload-rules #required only after changing the rules code.
rmmod chrdev
insmod chrdev.ko
struct device * device_create(struct class * class, struct device * parent, dev_t devt, void
The device file permissions should now be crw-rw-rw-. In the standard user console: * drvdata, const char * fmt, ...)
ls -l /dev | grep arcom-chrdev
cat /dev/arcom-chrdev0
creates a device and registers it with sysfs
10.2) In general, file permissions should be handled by udev. However, as an alternative, the Parameters
default permissions can be set by the module itself (note, however, that these settings are
struct class * class
overridden by udev in case any rule, as the one defined above, is applicable):
pointer to the struct class that this device should be registered to
struct device * parent
static char *arcom_devnode(struct device *dev, umode_t *mode)
{ pointer to the parent struct device of this new device, if any
if (!mode) dev_t devt
return NULL; the dev_t for the char device to be added
void * drvdata
if (MAJOR(dev->devt) == MAJOR(dev_num)) the data to be added to the device for callbacks
*mode = 0666; const char * fmt
string for the device’s name
return NULL; ...
} variable arguments
static int __init arcom_chrdev_init(void)
{ Description
(…) This function can be used by char device classes. A struct device will be created in sysfs,
registered to the specified class.
arcom_class = class_create(THIS_MODULE, CLASS);
arcom_class->devnode = arcom_devnode;
A “dev” file will be created, showing the dev_t for the device, if the dev_t is not 0,0. If a
pointer to a parent struct device is passed in, the newly created struct device will be a child
Loadable Kernel Module 9/12 Loadable Kernel Module 10/12
ARCOM – MEEC – ISEP – 2020/2021 ARCOM – MEEC – ISEP – 2020/2021
of that device in sysfs. The pointer to the struct device will be returned from the call. Any
further sysfs files that might be required can be created using this pointer. Bibliography
Returns struct device pointer on success, or ERR_PTR() on error. • Linux Device Drivers Development, published by Packt, 2017,
https://github.com/PacktPublishing/Linux-Device-Drivers-
Note Development/blob/master/Chapter04/dummy-char.c
• Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman, Linux Device
the struct class passed to this function must have previously been created with a call to Drivers, Third Edition, 2005, https://lwn.net/Kernel/LDD3/
class_create(). • The Linux Documentation, https://www.kernel.org/doc/html/latest/index.html
• Peter Jay Salzman, Michael Burian, Ori Pomerantz, The Linux Kernel Module
Programming Guide, 2001
container_of(ptr, type, member)