PCI device driver exporting information to /proc file system - c

I was asked this question in an interview. You are writing a PCI driver and you want to export the hardware-related information to the /proc filesystem. The interesting thing is that I searched the driver code and I couldn't find any call related to /proc filesystem though actually the information is exported. Is it done automatically? What is the mechanism? Can anyone please explain?

Creating entries in the /proc pseudo-filesystem is explained in Linux Device Drivers [3rd ed], chapter 4.
Nowadays you probably want to consider using sysfs instead; it's covered in LDD3 chapter 14.

One way to do it is for your driver to
implement a function that will get called whenever a process reads the corresponding /proc entry with the following signature:
int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);
register your function by passing its pointer to create_proc_read_entry(), which accepts the name of the /proc entry as a string among other things:
create_proc_read_entry("foobar", 0, NULL, your_read_func_ptr, NULL);
When your driver unloads, it should remove the entry with remove_proc_entry()

Related

How does fread in C actually work?

I understand that fread() has the following function definition:
size_t fread(void *buffer, size_t size, size_t qty, FILE *inptr);
I also understand that inptr is a file pointer that is returned when a FILE pointer is opened using the fopen() function. My question is does inptr store the memory address of every single character/letter of the file in its memory? If that is the case, do the memory addresses from the inptr get copied to *buffer (pointer to buffer array)?
There is one more thing that I am confused about. For each time fread() is called, size * qty bytes of memory is being copied/transferred. Is it the content of the file pointed to by inptr itself or is the memory address of the content of the file that is being copied/transferred?
Would appreciate if someone can help me clear the confusion. Thank you :)
FILE is implemented by your operating system. The functions operating on FILE are implemented by your system. You don't know. To know, you need to browse sources of your operating system.
inptr may be a pointer to memory allocated by your operating system. Or it may be a number, that your operating system uses to find it's data. Anyway, it's a handle, that your system uses to find FILE specific data. And your system decides what is in that data. For caching purposes, maybe all letters are cached in some buffer. Maybe not.
fread call. Fread reads data from an underlying entity behind inptr handle. inptr is interpreted by your system, to access the underlying memory or structure or device or hard drive or printer or keyboard or mouse or anything. It reads qty*size bytes of data. Those data are placed in the buffer. No pointers are placed there. The bytes that are read from the device are placed in the memory pointed to by buffer.
Your questions are a bit confusing (which is probably why you're asking them) so I'll do my best to answer.
FILE *inptr is a handle to the open file. You do not directly read it, it is just used to tell related functions what to operate on. You can kinda think of it like a human reading a file name in a folder, where the file name is used to identify the file, but the contents are accessed in another way.
As for the data, it is read from the file which is opened with fopen() and subsequently provided a file handle. The data does not directly correlate to the FILE pointer, and typically you should not be messing with the FILE pointer directly (don't try to read/write from it directly).
I tried to not get too technical as to the operation, as it seems you are new to C, but just kind of think of the FILE * as the computer's way of "naming" the file internally for its own usage, and the data buffer is merely the content.
You can think of fread as being implemented something like this:
size_t fread(char *ptr, size_t size, size_t nitems, FILE *fp)
{
size_t i;
for(i = 0; i < size * nitems; i++) {
int c = getc(fp);
if(c == EOF) break;
*ptr++ = c;
}
(I've left out the return value because in my simplified illustration there isn't a good way to show it.)
In other words, fread reads a bunch of characters as if by repeatedly calling getc(). So obviously this begs the question of how getc works.
What you have to know is that FILE * points to a structure which, one way or another, contains a buffer of some (not necessarily all) of the file's characters read into memory. So, in pseudocode, getc() looks like this:
int getc(FILE *fp)
{
if(fp->buffer is empty) {
fill fp->buffer by reading more characters from underlying file;
if(that resulted in end-of-file)
return EOF;
}
return(next character from fp->buffer);
}
The answer to the question,
"how does fread() work?"
is basically
"it asks your operating system to read the file for you."
More or less the sole purpose of an operating system kernel is to perform actions like this on your behalf. The kernel hosts the device drivers for the disks and file systems, and is able to fetch data for your program no matter what the file is stored on (e.g. a FAT32 formatted HDD, a network share, etc).
The way in which fread() asks your operating system to fetch data from a file varies slightly between OS and CPU. Back in the good old days of MS-DOS, the fread() function would load up various parameters (calculated from the parameters your program gave to fread()) into CPU registers, and then raise an interrupt. The interrupt handler, which was actually part of MS-DOS, would then go and fetch the requested data, and place it in a given place in memory. The registers to be loaded and the interrupt to raise were all specified by the MS-DOS manuals. The parameters you pass to fread() are abstractions of those needed by the system call.
This is what's known as making a system call. Every operating system has a system calling interface. Libraries like glibc on Linux provide handy functions like fread() (which is part of the standard C library), and make the system call for you (which is not standardised between operating systems).
Note that this means that glibc is not a fundamental part of the operating system. It's just a library of routines that implements the C standard library around the system calls that Linux provides. This means you can use an alternative C library. For example, Android does not use glibc, even though it has a Linux kernel.
Similarly on Windows. All software in Windows (C, C++, the .NET runtime, etc) is written to use the WIN32 API library (win32.dll). The difference on Windows is that the NT kernel system calling interface is not published; we don't know what it is.
This leads to some interesting things.
WINE on Linux recreates WIN32.dll, not the NT kernel system call interface.
Windows Subsystem for Linux on Windows 10 does recreate the Linux system calling interface (which is possible because it is public knowledge).
Solaris, QNX and FreeBSD pull the same trick.
Even more oddly it's looking like MS have done a NT kernel system interface shim for Linux (i.e, the thing that WINE hasn't done) to allow MS-SQLServer to run on Linux. This in effect is a Linux Subsystem for Windows. They've not given this away.

reading seq_file from kernel

Could you post some examples how to read list of meanings from /proc files?
list_head* get_from_proc_file()
{
struct file* file = fopen("example","r");
seq_open(file, &seq_ops);
struct seq_file *p = file->private_data;
READ LIST OF DATA?????
}
You can't use fopen as this is a libc function. The example bellow shows how to read a file from the kernel.
http://www.wasm.ru/forum/viewtopic.php?pid=467952#p467952
Probably you don't need to read a /proc file within kernel, because a /proc interface is used by kernel to export some information to user-space, the information definitely already exists in kernel, either in some list of struct's or other global containers. So the proper way is probably just getting the global list/container by calling some kernel API or using them directly, if they are exported.

connecting application with device driver

This is an interview question.
I had written device driver for a char device so I know that code structure looks like this
struct file_operations something {
.owner=my_device_open;
.read=my_device_read;
.close=my_device_close;
.write=my_device_write;
}
When the device driver is active then in
/dev/mydevice
you can actually read and write into it.
But what I was not clear is how an application will read or write to this device.
I know insmod will insert the module to kernel,and register_chrdev(); will register the driver in kernel but how will application program communicate with this driver.
Let me know what will be correct answer for it.
Well Martin Beckett summed it up. It is not really more complex,
although you could say the same with slightly more detail. Here is my
try at it:
The program performs an open("/dev/mydevice", flags) syscall, then the
kernel reads /dev/mydevice from disk. It is just an inode, with no
associated data blocks, but it holds two important pieces of
information: the major number and the minor number. From these numbers,
the kernel finds the struct file_operations that you provided through
register_chrdev(), and it calls it's .open field. It returns to the
program a file descriptor that it associated with this particular
struct file_operations. Next, when the kernel receives a syscall like
write(fd, buf, count), it will call the .write field and so on.
In unix it simply opens the device node as a file and sends/receives data and commands from it.
The beauty of Unix is that from an app's point of view there is nothing special about devices - they are just files (except for ioctls to set some modes). There is work to do in the kernel to accomodate this but that's the kernel modules problem.
Or were you asking something more complex?

struct file in linux driver

I am currently learning how to write Linux device drivers and I have trouble understanding "struct file". I am using the book Linux Device Drivers 3rd edition to help me out.
This is what I understood.
a. struct file represents an open file thus, when open is called in the device driver module, the kernel will create a struct file that includes everything related to the device driver.
b. If you want to pass around this instance of the device driver then one has to pass a pointer to the particular struct file that was created by the kernel after open()
c. file->private_data will always return a pointer to the device.
Another question related to this is the field "f_pos". The book says that the driver can read this value if it wants to know the current position in the file. This is what I understand from it.
d. If struct foo_dev and if the total amount of memory used by this driver to store data is X then f_pos points to the current position in that block of memory reserved by the driver.
How much of what I understood is right and please correct me where I am wrong.
Thanks,
Mir
The struct file is created by the kernel and represents the kernels view of your device it allows the kernel to map from a file handle to the device.
The struct file only contains the data the kernels upper layers needs, this is unlikely to be everything you need for your driver, if you need extra storage to track your devices status (and generally you will) you need to allocate the memory for your structure yourself either in the open function or more normally when you detect your hardware.
If you do allocate storage then you can use the file->private_data to allow you to get from the struct file thats passed to your driver by read / write / etc to your structure.
How the file->private_data is used is up to the driver, the kernel doesn't touch it. Its just there for the drivers use.
The f_pos field is a legacy from the kernel using the same struct file for devices and files. It is an index into a file were the next operation will happen, it depends on your device if this makes sense, if your device supports some form of random access (say a ram device) then using f_pos and implementing lseek might make sense, if you hardware is sequential then f_pos is normally irrelevant.
This is in addition to what andrew has said ...
a) struct FILE is provided by kernel, but it is meant as an interface between kernel and one application.
b) In other words, you cannot pass around FILE structure between multiple applications for sharing a device. The only exception where it is possible to share is between parent & child processes.
To access a device or device drivers simultaneously from multiple applications, each app. shall have to call open on the device & create a FILE struct of its own. It is up to the driver whether to allow simultaneous accesses or not. Kernel has no say here.
c) private_data is exactly what it says. Data that's private to device driver. Application or library can use this field to communicate data that is very specific for the device driver.

Access block level storage via kernel

How to access block level storage via the kernel (w/o using scsi libraries)?
My intent is to implement a block level storage protocol over network for learning purpose, almost the same way SCSI works. Requests will be generated by initiator and sent to target (both userspace program) which makes call to kernel module and returns the data using TCP protocol to initiator.
So far, I have managed to build a simple "Hello" module and run it (I am new at kernel programming), but unable to proceed with block access.
After searching a lot, I found struct buffer_head * bread(int dev,int block) in linux/fs.h, but the compiler throws error.
error: implicit declaration of function ‘bread’
Please help, also feel free to advice on starting with kernel programming.
Thank you!
bread as used in old kernels.
Looking into struct request *blk_get_request(struct request_queue *, int, gfp_t); in linux/blkdev.h
Accessing the block device has to be accomplished via kernel.
Not a kernel developer, but a few comments:
The implicit declaration error means that the definition you've found somehow isn't in scope when you call the function. Maybe it's hidden in an #ifdef or maybe you forgot to include linux/fs.h somehow.
As far as advice on linux kernel programming, you might want to check out kernelnewbies.org.
There have been various books written on kernel programming, but be aware that the details in the kernel change very rapidly. Most of the concepts in the older books will still be valid, but at least some of the details in some areas will have changed.
Finally, you might have to brave the linux kernel mailing list. It's rather intimidating, I'm sorry to say, so try to have your questions well thought out before you post them.
A block level storage protocol is itself a fair bit of work. Perhaps you want to get the protocol in place in user space first, with the target doing direct access to, eg, /dev/sdc before diving into the kernel.
As I read your question more closely, it appears your main interest is in the storage protocol aspect of this project. If so, why do you need to modify the kernel. If you have a locally attached disk, say /dev/sdX on the target, then you can do something like this from user space:
fd = open("/dev/sdX", O_RDWR);
pwrite(fd, buf, len, offset);
pread(fd, buf, len, offset);
So, unless you're specifically interested in playing around inside the kernel, I don't think you need to do any kernel module to do a basic storage protocol between user processes.

Resources