Reputation: 331
I have a char device driver that for a virtual device. I want a FIFO in the device driver so that 2 process using the device driver can transfer characters between them. I tried kfifo but I am new to this and find it difficult to use. Can any body please suggest some other way to implement the FIFO in Linux driver.
Upvotes: 2
Views: 3123
Reputation: 11428
If you are only going to allow two processes to use the driver, then you can do as this:
In your open
handler, make sure that two and only two processes can enter the driver:
If access mode = READ and not alreadyreading then
alreadyreading = 1
else
return -EBUSY
If access mode = WRITE and not alreadywritting then
alreadywritting = 1
else
return -EBUSY
In the same handler, initialize your FIFO, which could be just a single global character variable, and two wait queues: one for read, and one for write. Associated with these queues will be two variables: ready_to_read and ready_to_write. At the beginning, ready_to_read = 0 and ready_to_write = 1.
Then, in the release
handler:
If access mode = READ
alreadyreading = 0;
If access mode = WRITE
alreadywritting = 0
To allow a new process to open the device in read or write mode.
In the write
handler:
If access mode = READ then // we only support writting if the access mode is write
return -EINVAL
Else
res = wait_event_interruptible (write_queue, ready_to_write);
if (res)
return res; // if process received a signal, exit write
Take a single character from user space (copy_from_user() )
Copy it to the FIFO (the global character variable)
ready_to_write = 0; // no more writtings until a read is performed
ready_to_read = 1; // ready to read! wake up the reading process
wake_up_interruptible (&read_queue);
return 1; // 1 byte written
And finally, in the read
handler:
If access mode = READ then // we only support reading if the access mode is read
return -EINVAL
Else
res = wait_event_interruptible (read_queue, ready_to_read);
if (res)
return res; // if process received a signal, exit write
Take character from global variable (our FIFO) and send it to userspace (copy_to_user() )
ready_to_read = 0; // no more reads until a write is performed
ready_to_write = 1; // ready to write! wake up the writting process
wake_up_interruptible (&write_queue);
return 1; // 1 byte read
You can extend this example to allow a FIFO or more than one character: you would need an array of chars, and two indexes: one to know where to read from, and one to know where to write to.
To test your driver, you can open two xterms and do
cat /dev/mydriver
in one, and:
cat > /dev/mydriver
In the oher one. Then, every line you write in the second xterm will be shown in the first one.
You can even modify the driver so when the writting process closes the file, a flag is set so the next time the read process waits to read something, it detects that the write process is ended and then it returns 0 as well (to signal an EOF to the user), so when you press Ctrl-D
in the second xterm to end input, the first one ends automatically too. Something like:
(read
handler)
res = wait_event_interruptible (read_queue, ready_to_read || write_process_ended);
if (res)
return res; // -ERSTARTSYS if signal
if (write_process_ended)
{
ready_to_write = 1;
return 0; // if write process ended, send an EOF to the user
}
else
{
...
... get byte from FIFO, send to the user, etc.
...
return number_of_bytes_sent_to_user;
}
Upvotes: 2