Reputation: 8895
I'm studying Chapter 3.5 of Linux Device Drivers, 3rd edition. This section introduces a method to retrieve a custom structure we defined ourselves from struct inode *inode
in the open function:
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev;
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
}
return 0;
}
From my understanding, while the device is opened, the struct inode *inode
representing the device is passed to scull_open
. Then, the custom structure dev
is extracted and passed to filp->private_data
so that other methods such as scull_read
can use it:
ssize_t scull_read(struct file *filp, char _ _user *buf, size_t count,
loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
/* other codes that uses *dev */
}
This seems fine to me until I realized that we already had a struct scull_dev *dev
during initialization in scull_setup_cdev
here.
I'm rather confused since I thought we can make struct scull_dev *dev
a global variable, then scull_read
and other methods will eventually have access to it without going through all the passing using inode
and file
.
My question is, why don't we just make it a global variable?
Can anyone provide some practical examples of using this method to pass data ?
Upvotes: 12
Views: 9559
Reputation: 1
The scull driver is implemented with 4 minors, each of which has a separate scull_dev, each scull_dev has "struct cdev" embedded in it. Now Let's say User has opened scull0 from /dev/scull0. In the open() function you need to point to the correct scull_dev structure. The scull_dev structures are dynamically allocated.
You can see the full implementation here https://github.com/mharsch/ldd3-samples/blob/master/scull/main.c#L450
Upvotes: 0
Reputation: 98526
The main reason is so that your driver can manage more than one device. For example, you can create (mknod
) several devices /dev/scull1
, /dev/scull2
, /dev/scull3
... and then each of these will have a different scull_dev
associated with it.
With a global variable you are limited to one. And even if your driver only supports one such device, there is little reason not to design the code future proof.
Upvotes: 10
Reputation: 41
I donot think it is a thead-safety issue. It is more like a design choice. If I am not mistaken, thread-safety is achieved by down and up the semaphore in scull_dev. If you dig into the code, you can see open, read, write all used down_interruptible().
I guess the author 1) believes accessing scull_dev directly doesnot look good 2) wants to show us how to use private_data. By putting the point to scull_dev in struct file whose pointer is sent to each operation, each operation can access it without using global variable.
Upvotes: 0
Reputation: 33283
Thread-safety! What if two threads/processes are using the driver simultaneously?
Upvotes: 8
Reputation: 1434
You can also avoid using the private data to store your actual device, which is a common choice if you need private data for something different. In that case you will need to retrieve the minor number in the scull_read routine. It will be something like that:
ssize_t scull_read( struct file *filp,
char __user* buf,
size_t count,
loff_t * f_pos ) {
int minor = MINOR(filp->f_dentry->d_inode->i_rdev);
printk( "reading on minor number %d\n", minor);
/* use dev[minor] in ur code */
return 0;
}
Upvotes: 0