I am trying to write a simple device driver. The driver has only read/write operations. Ideally, I would like the read() function to work in such a way that when the device file is read from, a message is printed to terminal along with a count of how many times the device file was read from. The driver contains the following libraries and global variables:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include "my_driver.h"
#define DEVICE_NAME "Edev"
#define SUCCESS 0
#define FAILURE -1
#define BUF_LEN 1024
static struct class * cls;
static int major_num;
static int counter=0;
static char message_buffer[BUF_LEN];
static char * message_pointer;
static char * write_pointer;
static struct file_operations elliott_fops = {
.read=device_read,
.write=device_write,
.owner=THIS_MODULE
};
The read function is written like so:
static ssize_t device_read(struct file * filp2, char *buffer2, size_t length2, loff_t *offset2){
ssize_t bytes_in_message=0;
sprintf(message_buffer,"Yes,you read from the driver this many times: %d",counter++);
message_pointer=message_buffer;
while (length2 && *message_pointer++){
put_user(*(message_pointer++),buffer2++);
bytes_in_message++;
length2--;
}
pr_info("Read %lu bytes with %lu bytes remaining in the buffer",bytes_in_message,length2);
return bytes_in_message;
}
What I would like the read() function to do is print the message out to the terminal using the sprint() call, and then the while loop will obtain the message's byte length. The message byte length information as well as the remaining buffer size will then be logged within the kernel
The write function is written like this:
static ssize_t device_write(struct file * filp, const char *buffer, size_t length, loff_t *offset){
ssize_t bytes_read_in=0;
while (length && *buffer++){
get_user(*(write_pointer++),buffer++);
bytes_read_in++;
}
pr_info("The device had %lu bytes written to it",bytes_read_in);
pr_info("Got message from user: %s",write_pointer);
return bytes_read_in;
}
Ideally, if someone performed a write to the device with a "echo "hello linux kernel" > /dev/Edev" command, the message that was written to the device file would be printed in the kernel, as well as how long the message was. However, the read() and write() functions are not behaving like this at all. If, for instance, I load the module and write to the device file, no message have been logged in the kernel. If I then performed a cat /dev/Edev command, what is printed to terminal output is not the message with the count, but rather what was just written to the device with echo.
I am assuming this may have something to do with buffers overwriting each other, but I am confused as the buffer in write is userspace, while the buffer in read is in kernel space. I also am unsure why no messages are being logged to the kernel.
to copy a block of data into userspace for ref (https://www.cs.bham.ac.uk/~exr/teaching/lectures/opsys/13_14/docs/kernelAPI/r4037.html)
use copy_to_user function.
There is too much use of the post-increment operator in the loop in device_read():
while (length2 && *message_pointer++){
put_user(*(message_pointer++),buffer2++);
bytes_in_message++;
length2--;
}
message_pointer has been incremented twice per iteration. Similarly, in device_write():
while (length && *buffer++){
get_user(*(write_pointer++),buffer++);
bytes_read_in++;
}
buffer has been incremented twice per iteration. Also the length variable is not being decremented and the write_pointer variable has not been set.
The modified loops can be as follows. In device_read():
while (length2 && *message_pointer){
put_user(*message_pointer, buffer2);
message_pointer++;
buffer2++;
bytes_in_message++;
length2--;
}
And in device_write():
while (length && *buffer){
get_user(*write_pointer,buffer);
write_pointer++;
buffer++;
bytes_read_in++;
length--;
}
The write_pointer variable still needs to be set to point to a valid buffer in device_write().
[EDIT: Removed paragraph about put_user() and get_user() evaluating their arguments more than once, as I do not think that is the case.]
Related
I 'm trying to get the hostname of my school mac os. I can't use gethostname() as it's in section 3 of the man pages of my school macs, instead of section 2. Is there another way of getting the hostname, without using gethostname()? I'm only allowed to use libc functions in man 2 section.
gethostname is just a sysctl, and sysctl is just a syscall.
And syscalls are (per definition) in section 2 of the manual.
So grab your favourite disassembler (or otool -tV if you have none), nm the libraries in /usr/lib/system to find out which ones export _gethostname and _sysctl, and get to work (or look up the source :P).
Below I re-implemented gethostname using sysctl, and sysctl using syscall:
#include <sys/syscall.h> // SYS_sysctl
#include <sys/sysctl.h> // CTL_KERN, KERN_HOSTNAME
#include <unistd.h> // syscall
int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
return syscall(SYS_sysctl, name, namelen, oldp, oldlenp, newp, newlen);
}
int gethostname(char *buf, size_t buflen)
{
int name[] = { CTL_KERN, KERN_HOSTNAME };
size_t namelen = 2;
return sysctl(name, namelen, buf, &buflen, NULL, 0);
}
int puts(const char *s)
{
// left as an exercise to the reader ;)
}
int main(void)
{
#define BUFSIZE 256
char buf[BUFSIZE];
size_t buflen = BUFSIZE;
if(gethostname(buf, buflen) == 0)
{
puts(buf);
}
return 0;
}
The implementation of sysctl isn't too complicated; you really just slap SYS_sysctl (from sys/syscall.h) in front of the other arguments and pass them all on to syscall.
To understand the implementation of gethostname, you have to know how sysctl works:
oldp is where the queried value will be stored.
newp is where the new value will be read from. Since we're not setting any new value, this is NULL here.
name is more or less the actual list of arguments to sysctl, and its contents depend on the actual sysctl being queried.
CTL_KERN denotes that we want something from the kernel.
KERN_HOSTNAME denotes that we'd like to retrieve the hostname.
And since KERN_HOSTNAME doesn't take any arguments, that's all there is to it.
Just for demonstration, had you called KERN_PROCARGS, name would require an additional argument, namely the process ID of which the arguments should be retrieved.
In that case, name would look like this:
int name[] = { CTL_KERN, KERN_PROCARGS, pid };
and namelen would have to be set to 3 accordingly.
Now in the above implementation I've made use of puts, which you're obviously not allowed to do, but I trust you can figure out how to re-implement strlen and use the write syscall with that. ;)
I'm supposed to change a configuration parameter of the kernel by using a kernel module. The kernel module should create a proc file and then I should be able to change the parameter by using the cat command, e.g. cat "foobar" > /proc/prompt is supposed to set the parameter to "foobar", where prompt is the name of the proc file that was created in the module.
Furthermore I should be able to initialize the parameter by passing it as an argument when calling the module.
These two articles were basically the only relevant sources that I have found:
http://www.tldp.org/LDP/lkmpg/2.6/html/x769.html for writing to a proc file and http://www.tldp.org/LDP/lkmpg/2.6/html/x323.html for initializing the parameter from the command line.
Now I have a couple of questions, first of all this is the module thus far:
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include "sar_main.h"
#define PROCFS_NAME "sarlkm"
char procfs_buffer[PROCFS_MAX_SIZE];
static unsigned long procfs_buffer_size = 0
struct proc_dir_entry *proc_file_entry;
int procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data){
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) aufgerufen \n", PROCFS_NAME);
if (offset > 0){
ret = 0;
}
else{
memcpy(buffer, procfs_buffer, procfs_buffer_size);
ret = procfs_buffer_size;
}
return ret;
}
int procfile_write(struct file *file, const char *buffer, unsigned long count, void *data){
procfs_buffer_size = count;
if (procfs_buffer_size > PROCFS_MAX_SIZE){
procfs_buffer_size = PROCFS_MAX_SIZE;
}
if ( copy_from_user(procfs_buffer, buffer, procfs_buffer)){
return -EFAULT;
}
return procfs_buffer_size;
}
static int __init sar_init(void)
{
prompt_proc = create_proc_entry(PROCFS_NAME, 0644, NULL);
if (prompt_proc = NULL){
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_ALERT "Error: Konnte proc file nicht kreieren")
return -ENOMEM;
}
prompt_proc->read_proc = procfile_read;
prompt_proc->write_proc = procfile_write;
printk(KERN_INFO "proc/%s wurde erfolgreich kreiert", PROCFS_NAME);
return 0;
}
static void __exit sar_cleanup(void)
{
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_INFO "proc/%s gelöscht", PROCFS_NAME);
}
module_init(sar_init);
module_exit(sar_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
I think I should say that I don't really understand why the read and write functions are supposed to work when using the cat command.
My main question is where exactly is the configuration parameter stored in the proc file? If I would write "foobar" to the proc file using cat and then use cat proc/prompt to read the parameter, how does the read function actually get the new value of the parameter, i.e. where is "foobar" stored in the procfile?
If I would try to initialize the parameter using a command line argument I would have to use a global variable in which to store the value of the parameter, but then how could I use that global variable in the read function, so that cat proc/prompt actually gives out the value that was given to the module from the command line?
The cat command internally calls the read() system call to read data from a file (see man strace).
read() passes the arguments to the VFS and the VFS finally calls your custom procfile_read() routine with the passed arguments (and some additional ones passed by the VFS code). If you want to know more about this, look at the fs directory in kernel sources, especially file read_write.c.
Your particular reading function copies, if some conditions are met, the parameter value (which is stored in procfs_buffer to answer one of your questions) into the user-supplied buffer allocated by cat, which is called buffer in your particular code. It is the same one as passed by the read() system call like in:
read(proc_fd, userspace_buf, 10); /* userspace_buf is buffer! */
Error checking omitted for clearness's sake.
To pass the value to the proc file you have two options:
Use module_param() and write it to your buffer; can only be done once because the module is only loadable once (or unload/reload it every time you want to change the parameter but that sounds inconvenient)
Invoke write() from userspace (like in cat) and modify the buffer as often as you want to (this is currently used by your code)
BTW, I really think your reading function should check the pointer to the user data, i.e. use copy_to_user(), not memcpy().
For further information, read Linux Device Drivers. There's only an old edition available at the moment but an updated one is being written.
you can treat xxx_write or xxx_read in driver just as a interface implement,
when you call write or read in user space,
the kernel will invoke xxx_write or xxx_read in kernel space.
so you need to store it yourself when write call,
and fetch them back when read call,
in xxx_write xxx_read
I have usb char device which I managed to bind to /dev/device0 with usb skeleton 2.2 driver (only with few comments to understand it).
Now I have to write user application, which will send and recieve commands as ascii chars.
I am able to send commands with write without problems, but I don't know how to read properly from device.
As I don't know how long the message will be, I tried something like this
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
char *c, *ret;
int fd,err;
ret = malloc(1);
char *dev = "/dev/device0";
fd = open(dev, O_RDWR);
printf("fd: %d\n", fd);
if (fd == -1) {
printf("fopen() failed");
exit(1);
}
command = "command1";
write(fd, command, strlen(command));
while (read(fd, ret,1)!=EOF)
{
fprintf(stderr,"%c\n",ret);
}
close(fd);
return 0;
}
but it doesn't work, it seems to deadlock somehow or get into state very similar to that. I was able to find, that the number of reads is random, usually 3-6 and then the program waits (maybe waiting for data from device, but I'm not sure with this), during last read function wait_event_interruptible() in driver's read() function returns -512 and meanwhile the callback function of penultimate read isn't called.
Why is this happening and how do I find out whether there are any data the device sent?
char *ret;
Memory is not allocated to your pointer ret and you are writing to that location which is UB.Hence you might see a crash. Allocate memory to your pointer.
Edits:
Else
If you just want to reach character by character have
char ret;
read():
read returns the number of characters it read. When it reaches the end
of the file, it won't be able to read any more (at all) and it'll
return 0, not EOF.
So make the below changes:
while (read(fd, ret,1)!= 0)
{
fprintf(stderr,"%c\n",ret);
}
There are probably several problems with the code below. Found it online after searching for a way to get keyboard input in linux. I've verified the correct event for keyboard input. The reason it seems fishy to me is regardless of what i put in the filepath, it always seems to pass the error check (the open call returns something greater than 0). Something is obviously wrong, so suggestions are welcome.
This won't run correctly unless you run the exe as su.
When i want to read in my keystroke, do i just use something like fgets on the file descriptor in an infinite while loop(would that even work)? I want it to be constantly polling for keyboard inputs. Any tips on decoding the inputs from the keyboard event?
Thanks again! This project of mine may be overly ambitious, as it's been a really long time since i've done any coding.
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
// Edit this line to reflect your filepath
#define FILE_PATH "/dev/input/event4"
int main()
{
printf("Starting KeyEvent Module\n");
size_t file; //will change this to int file; to make it possible to be negative
const char *str = FILE_PATH;
printf("File Path: %s\n", str);
error check here
if((file = open(str, O_RDONLY)) < 0)
{
printf("ERROR:File can not open\n");
exit(0);
}
struct input_event event[64];
size_t reader;
reader = read(file, event, sizeof(struct input_event) * 64);
printf("DO NOT COME HERE...\n");
close(file);
return 0;
}
the problem is here:
size_t file;
size_t is unsigned, so it will always be >=0
it should have been:
int file;
the open call returns something greater than 0
open returns int, but you put in in an unsigned variable (size_t is usually unsigned), so you fail to detect when it is <0
For my OS class I'm supposed to implement Linux's cat using only system calls (no printf)
Reading this reference I found it being used to print to a file. I guess I should manipulate ofstream.
In the example appears: ofstream outfile ("new.txt",ofstream::binary);
How can I make it write to the screen?
EDIT: I realized this write() is part of iostream library, is this the same as the int write (int fd, char *buf , int size) system call?
A system call is a service provided by Linux kernel. In C programming, functions are defined in libc which provide a wrapper for many system calls. The function call write() is one of these system calls.
The first argument passed to write() is the file descriptor to write to. The symbolic constants STDERR_FILENO, STDIN_FILENO, and STDOUT_FILENO are respectively defined to 2, 0, and 1 in unidtd.h. You want to write to either STDOUT_FILENO or STDERR_FILENO.
const char msg[] = "Hello World!";
write(STDOUT_FILENO, msg, sizeof(msg)-1);
You can alternatively use the syscall() function to perform an indirrect system call by specifying the function number defined in syscall.h or unistd.h. Using this method, you can guarantee that you are only using system calls. You may find The Linux System Call Quick Refernence (PDF Link) to be helpful.
/* 4 is the system call number for write() */
const char msg[] = "Hello World!";
syscall(4, STDOUT_FILENO, msg, sizeof(msg)-1);
No, std::ostream::write is not the same as the write system call. It does (almost certainly) use the write system call, at least on a system like Linux that has such a thing, and it normally does pretty similar things, but it's still a separate thing of its own.
Linux will, however, pre-open standard input, standard output and standard error streams for your process. To write to the screen, you'd normally use write (i.e., the one that is a system call) to write to stream number 1 or stream number 2 (which are standard output and standard error respectively).
If you need to write to the screen even if those are re-directed, you'd normally open a stream to /dev/tty and (again) use write to write to it:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
char msg[] = "hello\n";
int fd = open("/dev/tty", O_WRONLY);
write(fd, msg, sizeof(msg));
return 0;
}
#include <unistd.h>
/* ... */
const char msg[] = "Hello world";
write( STDOUT_FILENO, msg, sizeof( msg ) - 1 );
First argument is the file descriptor for STDOUT (usually 1), the second is the buffer to write from, third is the size of the text in the buffer (-1 is to not print zero terminator).
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h> // For open, close, read, write, fsync
#include <sys/syscall.h> //For SYSCALL id __NR_xxx
//Method 1 : API
write(1,"Writing via API\n",\
strlen("Writing via API\n") );
fsync(1);
//Method 2 : Via syscall id
const char msg[] = "Hello World! via Syscall\n";
syscall(__NR_write, STDOUT_FILENO, msg, sizeof(msg)-1);
syscall(__NR_fsync, STDOUT_FILENO ); // fsync(STDOUT_FILENO);
Your reference is incorrect. It's part of C++ and has nothing to do with your assignment. The correct reference is http://www.opengroup.org/onlinepubs/9699919799/functions/write.html