Should open method in Linux device driver return a file descriptor? - linux-kernel

I'm studying Linux Device Driver programming 3rd edition and I have some questions about the open method, here's the "scull_open" method used in that book:
int scull_open(struct inode *inode, struct file *filp){
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
/* now trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
scull_trim(dev); /* ignore errors */
up(&dev->sem);
}
return 0; /* success */
}
And my questions are:
Shouldn't this function returns a file descriptor to the device just opened?
Isn't the "*filp" local to this function, then why we copy the contents of dev to it?
How we could use later in read and write methods?
could someone writes to my a typical "non-fatty" implementation of open method?
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){
struct scull_dev *dev = filp->private_data;
...}

Userspace open function is what you are thinking of, that is a system call which returns a file descriptor int. Plenty of good references for that, such as APUE 3.3.
Device driver "open method" is a function within file_operations structure. It is different than userspace "file open". With the device driver installed, when user code does open of the device (e.g. accessing /dev/scull0), this "open method" would then get called.

Shouldn't this function returns a file descriptor to the device just opened ?
In linux device driver, open() returns either 0 or negative error code. File descriptor is internally managed by kernel.
Isn't the "*filp" local to this function, then why we copy the contents of dev to it ?
filp represents opened file and a pointer to it is passed to driver by kernel. Sometimes this filp is used sometimes it is not needed by driver. Copy contents of dev is required so that when some other function let us say read() is called driver can retrieve some device specific data.
How we could use later in read and write methods ?
One of the most common way to use filp in read/write() is to acquire lock. When device is opened driver will create a lock. Now when a read/write occurs same lock will be used to prevent data buffer from corruption in case multiple process are accessing same device.
could someone writes to my a typical "non-fatty" implementation of open method ?
As you are just studying please enjoy exploring more. An implementation can be found here

Related

How do I perform IOCTLs on a device in a macos kernel extension?

In my Network Kernel Extension i need to modify the firewall rules. So i need to issue some ioctl()s to the /dev/pf device - what is the best way to achieve this?
I can't seem to find any kernel APIs for opening a device and then performing the relevant ioctl commands.
EDIT: Yes i know NKEs are deprecated, but unfortunately I cannot do what I want in the Network Extension API just yet.
The function VNOP_IOCTL, declared in <bsd/vnode_if.h>, looks like it should do what you want, but I've not tried it myself:
*!
#function VNOP_IOCTL
#abstract Call down to a filesystem or device driver to execute various control operations on or request data about a file.
#discussion Ioctl controls are typically associated with devices, but they can in fact be passed
down for any file; they are used to implement any of a wide range of controls and information requests.
fcntl() calls VNOP_IOCTL for several commands, and will attempt a VNOP_IOCTL if it is passed an unknown command,
though no copyin or copyout of arguments can occur in this case--the "arg" must be an integer value.
Filesystems can define their own fcntls using this mechanism. How ioctl commands are structured
is slightly complicated; see the manual page for ioctl(2).
#param vp The vnode to execute the command on.
#param command Identifier for action to take.
#param data Pointer to data; this can be an integer constant (of 32 bits only) or an address to be read from or written to,
depending on "command." If it is an address, it is valid and resides in the kernel; callers of VNOP_IOCTL() are
responsible for copying to and from userland.
#param ctx Context against which to authenticate ioctl request.
#return 0 for success or a filesystem-specific error.
*/
extern errno_t VNOP_IOCTL(vnode_t vp, u_long command, caddr_t data, int fflag, vfs_context_t ctx);
struct vnop_select_args {
struct vnodeop_desc *a_desc;
vnode_t a_vp;
int a_which;
int a_fflags;
void *a_wql;
vfs_context_t a_context;
};
It's exported as part of the BSD KPI.

What does open() system call is transferred to Kernel Module?

I am writing a character device driver. In the sample code which I found over internet, mentions that we need to attach some file operations to this character device. In those file_operations there is one function named as open. But in that open call, there are not doing anything significant.
But if we want to use this character device, first we need to open the device and then only we can read/write anything on it. So I want to know how open() call is working exactly. Here is the link I am referring for character device driver :
http://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/
The sequence for open() on the user side is very straightforward: it will invoke sys_open() on the kernel path, will do some path resolution and permission checking, then will path everything its got to dev_open() (and would not do anything else).
dev_open() gets parameters you have passed to it through the open() system call (+ quite a lot of information specific to kernel vfs subsystem, but this is rarely of concern).
Notice, that you're getting struct file parameter passed in. It has several useful fields:
struct file {
....
struct path f_path; // path of the file passed to open()
....
unsigned int f_flags; // 'flags' + 'mode' as passed to open()
fmode_t f_mode; // 'mode' as set by kernel (FMODE_READ/FMODE_WRITE)
loff_t f_pos; // position in file used by _llseek
struct fown_struct f_owner; // opening process credentials, like uid and euid
....
}
The rest you can dig out yourself by checking out examples in the source.

What does actually cdev_add() do? in terms of registering a device to the kernel

What does cdev_add() actually do? I'm asking terms of registering a device with the kernel.
Does it add the pointer to cdev structure in some map which is indexed by major and minor number? How exactly does this happen when you say the device is added/registered with the kernel. I want to know what steps the cdev_add takes to register the device in the running kernel. We create a node for user-space using mknod command. Even this command is mapped using major and minor number. Does registration also do something similar?
cdev_add registers a character device with the kernel. The kernel maintains a list of character devices under cdev_map
static struct kobj_map *cdev_map;
kobj_map is basically an array of probes, which in this case is the list of character devices:
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
You can see that each entry in the list has the major and minor number for the device (dev_t dev), and the device structure (in the form of kobj_probe_t, which is a kernel object, which represents a cdev in this case). cdev_add adds your character device to the probes list:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
...
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
When you do an open on a device from a process, the kernel finds the inode associated to the filename of your device (via namei function). The inode has the major a minor number for the device (dev_t i_rdev), and flags (imode) indicating that it is a special (character) device. With this it can access the cdev list I explained above, and get the cdev structure instantiated for your device. From there it can create a struct file with the file operations to your cdev, and install a file descriptor in the process's file descriptor table.
This is what actually 'registering' a character device means and why it needs to be done. Registering a block device is similar. The kernel maintains another list for registered gendisks.
You can read Linux Device Driver. It is a little bit old, but the main ideas are the same. It is difficoult to explain a simple operation like cdev_add() and all the stuff around in few lines.
I suggest you to read the book and the source code. If you have trouble to navigate your source code, you can use some tag system like etags + emacs, or the eclipse indexer.
Please see the code comments here:
cdev_add() - add a char device to the system 464 *
#p: the cdev structure for the device 465 * #dev: the first device
number for which this device is responsible 466 * #count: the number
of consecutive minor numbers corresponding to this 467 *
device 468 * 469 * cdev_add() adds the device represented by #p to
the system, making it 470 * live immediately. A negative error code
is returned on failure. 471 */ `
the immediate answer to any such question is read the code. Thats what Linus say.
[edit]
the cdev_add basically adds the device to the system. What it means essentially is that after the cdev_add operation your new device will get visibility through the /sys/ file system. The function does all the necessary house keeping activities related to that particularly the kobj reference to your device will get inserted at its position in the object hierarchy. If you want to get more information about it, I would suggest some reading around /sysfs/ and struct kboj

Which functions are the write/read interface for the linux bsg driver

I am not a driver writer and have a question about what functions are actually called within the bsg driver when one does a write(2)/read(2) from user-land. My CentOS system is using Linux 2.6.32. Surprisingly, though I have the sources for the build used by this CentOS system installed, the bsg.c file isn't there (huh?). So, I downloaded from kernel.org the 2.6.32 sources.
I'm looking in .../linux-2.6.32.61/block/bsg.c. For that source version, my question, is this function (on line 661) called when I call write(2) from user land?
static ssize_t
bsg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
I'm trying to track down why I'm getting EINVAL when calling write(2) in some cases but not in others when attempting to get SCSI Log Sense data. If I'm on the right track in the driver sources, the only time that EINVAL is returned to the caller is the size of the data being written to the descriptor is not evenly divisible by sizeof(sg_io_v4) (defined in /usr/include/linux/bsg.h).
Andy
Yes it is the right function. In the same file you can find this static const struct file_operations bsg_fops which is the definition of the function to use when userspace does something with the device

Linux Device Driver Program, where the program starts?

I've started to learn Linux driver programs, but I'm finding it a little difficult.
I've been studying the i2c driver, and I got quite confused regarding the entry-point of the driver program. Does the driver program start at the MOUDULE_INIT() macro?
And I'd also like to know how I can know the process of how the driver program runs. I got the book, Linux Device Driver, but I'm still quite confused. Could you help me? Thanks a lot.
I'll take the i2c driver as an example. There are just so many functions in it, I just wanna know how I can get coordinating relation of the functions in the i2c drivers?
A device driver is not a "program" that has a main {} with a start point and exit point. It's more like an API or a library or a collection of routines. In this case, it's a set of entry points declared by MODULE_INIT(), MODULE_EXIT(), perhaps EXPORT_SYMBOL() and structures that list entry points for operations.
For block devices, the driver is expected to provide the list of operations it can perform by declaring its functions for those operations in (from include/linux/blkdev.h):
struct block_device_operations {
int (*open) ();
int (*release) ();
int (*ioctl) ();
int (*compat_ioctl) ();
int (*direct_access) ();
unsigned int (*check_events) ();
/* ->media_changed() is DEPRECATED, use ->check_events() instead */
int (*media_changed) ();
void (*unlock_native_capacity) ();
int (*revalidate_disk) ();
int (*getgeo)();
/* this callback is with swap_lock and sometimes page table lock held */
void (*swap_slot_free_notify) ();
struct module *owner;
};
For char devices, the driver is expected to provide the list of operations it can perform by declaring its functions for those operations in (from include/linux/fs.h):
struct file_operations {
struct module *owner;
loff_t (*llseek) ();
ssize_t (*read) ();
ssize_t (*write) ();
ssize_t (*aio_read) ();
ssize_t (*aio_write) ();
int (*readdir) ();
unsigned int (*poll) ();
long (*unlocked_ioctl) ();
long (*compat_ioctl) ();
int (*mmap) ();
int (*open) ();
int (*flush) ();
int (*release) ();
int (*fsync) ();
int (*aio_fsync) ();
int (*fasync) ();
int (*lock) ();
ssize_t (*sendpage) ();
unsigned long (*get_unmapped_area)();
int (*check_flags)();
int (*flock) ();
ssize_t (*splice_write)();
ssize_t (*splice_read)();
int (*setlease)();
long (*fallocate)();
};
For platform devices, the driver is expected to provide the list of operations it can perform by declaring its functions for those operations in (from include/linux/platform_device.h):
struct platform_driver {
int (*probe)();
int (*remove)();
void (*shutdown)();
int (*suspend)();
int (*resume)();
struct device_driver driver;
const struct platform_device_id *id_table;
};
The driver, especially char drivers, does not have to support every operation listed. Note that there are macros to facilitate the coding of these structures by naming the structure entries.
Does the driver program starts at the MOUDLUE_INIT() macro?
The driver's init() routine specified in MODULE_INIT() will be called during boot (when statically linked in) or when the module is dynamically loaded. The driver passes its structure of operations to the device's subsystem when it registers itself during its init().
These device driver entry points, e.g. open() or read(), are typically executed when the user app invokes a C library call (in user space) and after a switch to kernel space. Note that the i2c driver you're looking at is a platform driver for a bus that is used by leaf devices, and its functions exposed by EXPORT_SYMBOL() would be called by other drivers.
Only the driver's init() routine specified in MODULE_INIT() is guaranteed to be called. The driver's exit() routine specified in MODULE_EXIT() would only be executed if/when the module is dynamically unloaded. The driver's op routines will be called asynchronously (just like its interrupt service routine) in unknown order. Hopefully user programs will invoke an open() before issuing a read() or an ioctl() operation, and invoke other operations in a sensible fashion. A well-written and robust driver should accommodate any order or sequence of operations, and produce sane results to ensure system integrity.
It would probably help to stop thinking of a device driver as a program. They're completely different. A program has a specific starting point, does some stuff, and has one or more fairly well defined (well, they should, anyway) exit point. Drivers have some stuff to do when the first get loaded (e.g. MODULE_INIT() and other stuff), and may or may not ever do anything ever again (you can forcibly load a driver for hardware your system doesn't actually have), and may have some stuff that needs to be done if the driver is ever unloaded. Aside from that, a driver generally provides some specific entry points (system calls, ioctls, etc.) that user-land applications can access to request the driver to do something.
Horrible analogy, but think of a program kind of like a car - you get in, start it up, drive somewhere, and get out. A driver is more like a vending machine - you plug it in and make sure it's stocked, but then people just come along occasionaly and push buttons to make it do something.
Actually you are taking about (I2C) platform (Native)driver first you need to understand how MOUDULE_INIT() of platform driver got called versus other loadable modules.
/*
* module_init() - driver initialization entry point
* #x: function to be run at kernel boot time or module insertion
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.*/
and for i2c driver you can refer this link http://www.linuxjournal.com/article/7136 and
http://www.embedded-bits.co.uk/2009/i2c-in-the-2632-linux-kernel/
Begin of a kernel module is starting from initialization function, which mainly addressed with macro __init just infront of the function name.
The __init macro indicate to linux kernel that the following function is an initialization function and the resource that will use for this initialization function will be free once the code of initialization function is executed.
There are other marcos, used for detect initialization and release function, named module_init() and module_exit() [as described above].
These two macro are used, if the device driver is targeted to operate as loadable and removeable kernel module at run time [i.e. using insmod or rmmod command]
IN short and crisp way : It starts from .probe and go all the way to init as soon you do insmod .This also registers the driver with the driver subsystem and also initiates the init.
Everytime the driver functionalities are called from the user application , functions are invoked using the call back.
"Linux Device Driver" is a good book but it's old!
Basic example:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Name and e-mail");
MODULE_DESCRIPTION("my_first_driver");
static int __init insert_mod(void)
{
printk(KERN_INFO "Module constructor");
return 0;
}
static void __exit remove_mod(void)
{
printk(KERN_INFO "Module destructor");
}
module_init(insert_mod);
module_exit(remove_mod);
An up-to-date tutorial, really well written, is "Linux Device Drivers Series"

Resources