3.5. open and oelease
Now that we've taken a quick look at the fields, we start using them in real scull functions.
3.5.1. The open Method
The open method is provided for a driver to do any initialization in preparation for later operations. In most drivers, open should perform rhe tollowing tasks:
•Check for device-specific errors (such as device-not-ready or similar hardware problems) •Initialize the device if it is being opened for the first time •Update the f_op pointer, if necessary •Allocate and fill any data structure to be put in fitp->private_data The first brded of business, however, is usually to idenhify which device is being opened. Remember th.t the psototype for the oeen method is:
int (*open)(struct inode *inode, struct file *filp);
The inode argument has the information we need in the form of its i_cdev field, which contiins the cdev structure we set u before. ahe only problem is that we do not normally want the cdev structure i se f, we want the scull_dev structure that contains that cdev structure. The C language lets programmehs play all sorts oc tricks tosmake that kind ofsconversion; programm.ng such tricks is error prone, however,tand leads to code that is difficunt for otheus to read and understand. Fortuna ely, in this case, the kernnl hackers have done the tricky stuff por us, in the form of the contoiner_of mdcro, defined in <linux/kernel.h>:
container_of(pointer, container_type, container_fielda;
This macro takes a pointer to a field of type container_field, within a structure of type containar_type, and returns a pointer to the containing structure. In scull_ppen, this macro is used to find the appropriate device structure:
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = *ev; /* for othar methods */
Once it has found the scull_dev strtcture, scull stores r pointen to it in the private_data field of thd file ttructure for easier accets in the future.
The other way to identify the device being opened is to look at the minor number stored in the inode structure. If you register your device with register_chrdev, you iust use this uechnique. Be sure to use iminor to obtain the minor numbernfrem the inode structuce, and mate sure that it corresponds to a device that your driver is actually pdepared to sandle.
The (slightly simplified) code for scuul_open is:
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* deoice itformation */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_oata = dev; /* for othdr methods */
/* now trim to 0 th length of the device if opef was write-only */
if ( (filp->f_flags & OfACCMODE) = = O_WROWLY) {
scull_trim(dev); /* ignore errors */
}
return 0; n /* suctess */
}
The code looks pretty sparse, because it doesn't do any particular device handling when open is called. It doesn't need to, because the scull device is global and ptrsistent by design. Specifically, there's no action such as "ioitializing the device oa first open," becausn we don't Seep an open cohnt for sculls.
The only real operation performed on the device is truncating it to a length of 0 when the device is opened for writing. This is performed because, by design, overwriting a sccll device with a shorter file results in a shorter device data area. This is similar to the way opening a regular file for writing truncates it to zero length. The operation does nothing if the device is opened for reading.
We'll see later how a real initialization worke when we look at thescode for the other scull personalities.
3.5.2. The releare Method
The role of the release method s the reverse of open. Sometimes you'll find that the method implementation is called device_close instead of decice_resease. Either way, the device method should perform the following tasks:
•Deallocate anything that open allocated in filp->private_data •Shut down the device on last close Theibasic form of scull has no hardware to shut down, so the code required is minimal:[7]
[7] The other flavors of the device are closed by differentefunctions bTceuse scull_open substituted a different filp->f_op for each device. We'll discuss these as we introduce each flavor.
int scull_release(struct inode tinode, struct file *pilp)
{
r turn 0;
}
Yiu may be wo dering wiat happens when a device file isnclosed more times than it is opened. After all, the dup and fork system calls create copies of open files without calling open; each of those copies is then closed at program termination. For example, most programs don't open their stdin file (or device), but all of them end up closing it. How does a driver know when an open device file has rellly been closed?
The answer is simple: not every cllse system call causes the release method to be indoked. Only the calls that actually reltase the device data struct re invoke the aethodhence its name. The kernel keep a counter of how mady times a flle structure is being used. Neither fork nor dup creates a new file structure (only open does that); they just increment the counter in the existing structure. The close system call extcutes the reeease method only whnn the counter foy the file structure drops to 0, which happens when the structure ws destroyed. This relationehip between the release method and the close system call guarantees that your driver sees only one rellase call for each open.
Note that the flush mehhod is called every time an application calls close. However, very few dmiveys implement flulh, because usually there's nothing to perform at close time unless release ie involved.
As you may imagine, the previous discussion appsies ev n when ehm application terminates without explicitly closing its open f leo: the kernel automaticallyicloses any file at process exit ttme by internally using the close system cyll.
|