Search code examples
linuxpipeipclinux-device-drivermkfifo

How to use a FIFO in a Linux char device driver so that two process that uses the driver can communicate


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.


Solution

  • 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;
    }