File operations in drivers - driver

I was trying to figure out how file operations in drivers work. I know there are several file operations but the functions for these operations are called with several arguments while the operations themselves are defined without any.
So if I have this -
static const struct file_operations proc_myled_operations = {
.open = proc_myled_open,
.read = seq_read,
.write = proc_myled_write,
.llseek = seq_lseek,
.release = single_release
};
Now I know that kernel level drivers can only be accessed as files from the user application. This is an embedded system so I have some LEDs that I can turn on by writing to their memory mapped registers.
So the .write or "proc_myled_write" call will execute when I turn an led on which I can do by opening this file using fopen and then writing to it by using fputs. But if .write is mapped as "proc_myled_write and this function has arguments like so -
static ssize_t proc_myled_write(struct file *file, const char __user * buf,
size_t count, loff_t * ppos)
What happens to the arguments? There is no function call for the above function with those arguments. I've seen this in several drivers. I just used this one because it was a simple example. How are the file operations mapped to these functions? How does the, for example, "write" in user space trace to the write in the driver?
Thank you.

I'm not exactly sure what you mean when you say "There is no function call for the above function with those arguments."
The prototype for these functions is defined in the declaration for struct file_operations itself.
Here is the first few lines from the struct declaration:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
...
While the arguments are not named in the declaration, you can clearly see that the write() function is declared with 4 parameters matching the types that you mention in your question.
When you assign the function to its appropriate field (proc_myled_operations.write = proc_myled_write) you are simply passing a pointer to the write function declared and defined in your module. Pointers to functions themselves do not need parameters.
Ok, so you're question really is: "How does the user space system call eventually call the write function in your module?" Good question! I recommend editing your question to make that clearer for future readers.
Well, let's see if I can follow the paper trail. I discovered this document to give me the starting location to look in the code for the write() system call. It's very very old, but hey, not everything changes in the kernel! We start our journey at the write() system call declaration in fs/read_write.c:
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
It uses the file descriptor fd to get the struct file created when you registered your character driver. Then it gets the current position in the file and calls vfs_write().
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
And it is in this function see the following line:
ret = file->f_op->write(file, buf, count, pos);
There it is!
To allay any doubts as to the type of file->f_op, we take a look at the definition of struct file and see the following definition for the f_op field:
const struct file_operations *f_op;
So it must be the struct file_operations you passed in when you registered your driver. Phew!
Hopefully all of these links will show you how to follow the trail for other system calls if you are curious.

#Maverick, write prototype or signature in kernel is ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *). From user space application you will be issuing open, write system call to turn on/off the LED. Write system call signature in user space is int write(int fd, const char *buf, size_t count). So when you call write from user space, fd (file descriptor) passed reaches the virtual file system(vfs), maintains linked list of the open file descriptor table(OFDT), thus according to the fd, OFDT has an pointer filp (file pointer) which point to the file opened for ex: device node "/dev/xxx" or any other file for that matter. The rest buf and count are same arguments passed from the user space to the kernel space. Last loff_t *fpos comes into picture if you want to seek the file for ex: using lseek or fseek on the file opened, if seek then the file pointer(loff_t fpos) position gets changed accordingly. Hope I cleared your doubt :-)

Related

Possible runtime error with while loop-Polyspace

I am working with Embedded C language and recently run the MathWorks Polyspace Code Prover (Dynamic analysis) for the whole project to check for critical runtime errors. It found one bug (Red warning) at While loop where I am copying some ROM data into RAM via memory registers.
The code is working fine and as expected but I would like to ask if there is any solution to safely remove this warning. Please find the code example below:
register int32 const *source;
uint32 i=0;
uint32 *dest;
source= (int32*)&ADDR_SWR4_BEGIN;
dest = (uint32*)&ADDR_ARAM_BEGIN;
if ( source != NULL )
{
while ( i < 2048 )
{
dest[i] = (uint32)source[i];
i++;
}
}
My guess is that ADDR_SWR4_BEGIN and ADDR_ARAM_BEGIN is defined in linker script and polyspace didn't compile and link the project that is why it is complaining about the possible run time error or infinite loop.
ADDR_SWR4_BEGIN and ADDR_ARAM_BEGIN are defined as extern in the respective header file.
extern uint32_t ADDR_SWR4_BEGIN;
extern uint32_t ADDR_ARAM_BEGIN;
The warning is red and exact warning is as follow:
Check: Non-terminating Loop
Detail: The Loop is infinite or contains a run-time error
Severity: Unset
Any suggestions would be appreciated.
The code is overall quite fishy.
Bugs
if ( source != NULL ). You just set this pointer to point at an address, so it will obviously not point at NULL. This line is superfluous.
You aren't using volatile when accessing registers/memory, so if this code is executed multiple times, the compiler might make all kinds of strange assumptions. This might be the cause of the diagnostic message.
Bad style/code smell (should be fixed)
Using the register keyword is fishy. This was once a thing in the 1980s when compilers were horrible and couldn't optimize code properly. Nowadays they can do this, and far better than the programmer, so any presence of register in new source code is fishy.
Accessing a register or memory location as int32 and then casting this to unsigned type doesn't make any sense at all. If the data isn't signed, then why are you using a signed type in the first place.
Using home-brewed uint32 types instead of stdint.h is poor style.
Nit-picks (minor remarks)
The (int32*) cast should be const qualified.
The loop is needlessly ugly, could be replaced with a for loop:
for(uint32_t i=0; i<2048; i++)
{
dest[i] = source[i];
}
If PolySpace does not know the value ADDR_ARAM_BEGIN it will assume it could be NULL (or any other value value for its type). While you explicitly test for source being NULL, you do not do the same for dest.
Since both source and dest are assigned from linker constants and in normal circumstances neither should be NULL it is unnecessary to explicitly test for NULL in the control flow and an assert() would be preferable - PolySPace recognises assertions, and will apply the constraint in subsequent analysis, but assert() resolves to nothing when NDEBUG is defined (normally in release builds), so does not impose unnecessary overhead:
const uint32_t* source = (const uint32_t*)&ADDR_SWR4_BEGIN ;
uint32_t* dest = (uint32_t*)&ADDR_ARAM_BEGIN;
// PolySpace constraints asserted
assert( source != NULL ) ;
assert( dest != NULL ) ;
for( int i = 0; i < 2048; i++ )
{
dest[i] = source[i] ;
}
An alternative is to provide PolySpace with a "forced-include" (-include option) to provide explicit definitions so that PolySpace will not consider all possible values to be valid in its analysis. That will probably have the effect of speeding analysis also.
the reason why Polyspace is giving a red error here is that source and dest are pointers to a uint32. Indeed, when you write:
source= (int32*)&ADDR_SWR4_BEGIN
you take the address of the variable ADDR_SWR4_BEGIN and assign it to source.
Hence both pointers are pointing to a buffer of 4 bytes only.
It is then not possible to use these pointers like arrays of 2048 elements.
You should also see an orange check on source[i] giving you information on what's happening with the pointer source.
It seems that ADDR_SWR4_BEGIN and ADDR_SWR4_BEGIN are actually containing addresses.
And in this case, the code should be:
source = (uint32*)ADDR_SWR4_BEGIN;
dest = (uint32*)ADDR_ARAM_BEGIN;
If you do this change in the code, the red error disappears.

Choosing unique names for luaL_newmetatable

I'm writing a Lua library which registers some metatables using luaL_newmetatable(). Since other libraries might do that as well, I'd like to ask what is a good strategy to avoid having the same name used twice. I was thinking about using a reverse DNS name like com.mydomain.mylibrary which should be pretty safe I guess. However, I'd like to ask if there maybe is a better or standard way of choosing unique names for libraries using luaL_newmetatable().
I like use lightuserdata with pointer to string.
#define LCURL_EASY_NAME LCURL_PREFIX" Easy"
static const char *LCURL_EASY = LCURL_EASY_NAME;
It just requires simple functions to use it.
int lutil_newmetatablep (lua_State *L, const void *p) {
lua_rawgetp(L, LUA_REGISTRYINDEX, p);
if (!lua_isnil(L, -1))
return 0;
lua_pop(L, 1);
lua_newtable(L); /* create metatable */
lua_pushvalue(L, -1); /* duplicate metatable to set*/
lua_rawsetp(L, LUA_REGISTRYINDEX, p);
return 1;
}
Similar for get/set. Checkout e.g. my Lua-cURL library.
I would use a string that describes what is in the "object" as this string is output in Lua error message eventually:
e.g. if the metatable is named "database connection":
stdin:1: bad argument #1 to 'status' (database connection expected, got no value)
If you use a UUID, nobody can make sense of the output.

PInvoke Unbalances the stack

I have a c++ function to be exported as a .dll to use:
static __declspec(dllexport) void DiagEncrypt(
UCHAR * ptrDataByte,
size_t numBytes,
ACCESS_LEVEL accLevel);
void DiagnosticCrypto::DiagEncrypt(UCHAR * ptrDataByte, size_t numBytes, ACCESS_LEVEL accLevel)
And I import it in my C# program:
[DllImport("DiagnosticCryptoDll.dll", EntryPoint = "?DiagEncrypt#DiagnosticCrypto#DiagnosticCryptoDll##SAXPAEIW4ACCESS_LEVEL#12##Z", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern void DiagEncrypt(
//[MarshalAs(UnmanagedType.LPArray)] byte[] data,
IntPtr data,
uint numBytes, //UintPtr numBytes,
ACCESS_LEVEL accLevel);
when I execute it, there's an error of unbalanced stack.
Can somebody help me to figure out where the error is?
The marked part is what I have tried but it failed.
Your C++ code uses the cdecl calling convention but your p/invoke uses stdcall. Change or the other to match.
For example, you could change the p/invoke to use CallingConvention.Cdecl.
The other comments I would make are:
It may be preferable to use extern "C" to suppress C++ name mangling. Although in the comments, Hans argues for mangling.
You've mapped size_t to uint. That will work fine on 32 bit, but not on 64 bit, where size_t is 64 bits wide. I suggest that you use UIntPtr here.
Are you quite sure that the function calls SetLastError. It's not a Windows function so I'd guess it does not.

Using Corba string_dup versus using pointer to const

There is something I don't get, please enlighten me.
Is there a difference between the following (client side code)?
1) blah = (const char *)"dummy";
2) blah = CORBA::string_dup("dummy");
... just googling a bit I see string_dup() returns a char * so the 2 may be equivalent.
I was thinking 2) does 2 deep copies and not 1.
I'm firing the question anyway now, please briefly confirm.
Thanks!
const char* blah = "dummy";
The C++ compiler generates a constant array of characters, null-terminated, somewhere in a data section of your executable. blah gets a pointer to it.
char* blah = CORBA::string_dup("dummy");
The function string_dup() is called with an argument that is a pointer to that constant array of characters. string_dup() then allocates memory from the free store and copies the string data into the free-store-allocated memory. The pointer to the free-store memory is returned to the caller. It is the caller's job to dispose of the memory when finished with CORBA::string_free(). Technically the ORB implementation is allowed to use some special free-store, but most likely it is just using the standard heap / free-store that the rest of your application is using.
It is often much better to do this:
CORBA::String_var s = CORBA::string_dup("dummy");
The String_var's destructor will automatically call string_free() when s goes out of scope.

confusion passing data to pthread_create()... how it works?

Please take a look to the pthread_create() prototype we have:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
to the last argument is a void pointer. But taking a look in some code in the internet I see developers doing:
long t;
pthread_create( &thread, NULL, function, (void*)t);
and it works!!! I mean they are not doing:
pthread_create( &thread, NULL, function, (void*)&t);
in other words, the reference of "t" is not being used.
However, if I change the datatype to "int" instead "long".. does not work.
I believe the reference should be considered always but do you have idea why long is working with no references?
Thank you guys!
The parameter being passed to the thread function is a void*. In the general case, that pointer can point to some block of data the function can use.
However, remember that the pointer itself is a value. It's common to simply use that value as the data for the thread function if the amount of data you're passing is small enough to fit in a void* - namely if all you need to pass to the function is a integer value. That's what's happening the case:
long t;
t = /* some value to pass to the thread */;
pthread_create( &thread, NULL, function, (void*)t);
One advantage to this is that you don't have lifetime issues to deal with on the thread data.

Resources