2019-06-04 11:10:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-08-11 05:03:39 +04:00
/*
* Sample kfifo byte stream implementation
*
* Copyright ( C ) 2010 Stefani Seibold < stefani @ seibold . net >
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/proc_fs.h>
# include <linux/mutex.h>
# include <linux/kfifo.h>
/*
* This module shows how to create a byte stream fifo .
*/
/* fifo size in elements (bytes) */
# define FIFO_SIZE 32
/* name of the proc entry */
# define PROC_FIFO "bytestream-fifo"
/* lock for procfs read access */
static DEFINE_MUTEX ( read_lock ) ;
/* lock for procfs write access */
static DEFINE_MUTEX ( write_lock ) ;
/*
* define DYNAMIC in this example for a dynamically allocated fifo .
*
* Otherwise the fifo storage will be a part of the fifo structure .
*/
#if 0
# define DYNAMIC
# endif
# ifdef DYNAMIC
static struct kfifo test ;
# else
static DECLARE_KFIFO ( test , unsigned char , FIFO_SIZE ) ;
# endif
2010-08-20 01:13:30 +04:00
static const unsigned char expected_result [ FIFO_SIZE ] = {
2010-08-20 01:13:29 +04:00
3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 ,
1 , 20 , 21 , 22 , 23 , 24 , 25 , 26 ,
27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 ,
35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 ,
} ;
2010-08-11 05:03:39 +04:00
static int __init testfunc ( void )
{
unsigned char buf [ 6 ] ;
2010-08-20 01:13:29 +04:00
unsigned char i , j ;
2010-08-11 05:03:39 +04:00
unsigned int ret ;
printk ( KERN_INFO " byte stream fifo test start \n " ) ;
/* put string into the fifo */
kfifo_in ( & test , " hello " , 5 ) ;
/* put values into the fifo */
for ( i = 0 ; i ! = 10 ; i + + )
2013-11-15 02:32:17 +04:00
kfifo_put ( & test , i ) ;
2010-08-11 05:03:39 +04:00
/* show the number of used elements */
printk ( KERN_INFO " fifo len: %u \n " , kfifo_len ( & test ) ) ;
/* get max of 5 bytes from the fifo */
i = kfifo_out ( & test , buf , 5 ) ;
printk ( KERN_INFO " buf: %.*s \n " , i , buf ) ;
/* get max of 2 elements from the fifo */
ret = kfifo_out ( & test , buf , 2 ) ;
printk ( KERN_INFO " ret: %d \n " , ret ) ;
/* and put it back to the end of the fifo */
ret = kfifo_in ( & test , buf , ret ) ;
printk ( KERN_INFO " ret: %d \n " , ret ) ;
2010-08-20 01:13:28 +04:00
/* skip first element of the fifo */
printk ( KERN_INFO " skip 1st element \n " ) ;
kfifo_skip ( & test ) ;
2010-08-11 05:03:39 +04:00
/* put values into the fifo until is full */
2013-11-15 02:32:17 +04:00
for ( i = 20 ; kfifo_put ( & test , i ) ; i + + )
2010-08-11 05:03:39 +04:00
;
printk ( KERN_INFO " queue len: %u \n " , kfifo_len ( & test ) ) ;
2010-08-20 01:13:30 +04:00
/* show the first value without removing from the fifo */
if ( kfifo_peek ( & test , & i ) )
printk ( KERN_INFO " %d \n " , i ) ;
2010-08-20 01:13:29 +04:00
/* check the correctness of all values in the fifo */
j = 0 ;
while ( kfifo_get ( & test , & i ) ) {
2010-08-20 01:13:30 +04:00
printk ( KERN_INFO " item = %d \n " , i ) ;
2010-08-20 01:13:29 +04:00
if ( i ! = expected_result [ j + + ] ) {
printk ( KERN_WARNING " value mismatch: test failed \n " ) ;
return - EIO ;
}
}
if ( j ! = ARRAY_SIZE ( expected_result ) ) {
printk ( KERN_WARNING " size mismatch: test failed \n " ) ;
return - EIO ;
}
printk ( KERN_INFO " test passed \n " ) ;
2010-08-11 05:03:39 +04:00
return 0 ;
}
static ssize_t fifo_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
int ret ;
unsigned int copied ;
if ( mutex_lock_interruptible ( & write_lock ) )
return - ERESTARTSYS ;
ret = kfifo_from_user ( & test , buf , count , & copied ) ;
mutex_unlock ( & write_lock ) ;
return ret ? ret : copied ;
}
static ssize_t fifo_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
int ret ;
unsigned int copied ;
if ( mutex_lock_interruptible ( & read_lock ) )
return - ERESTARTSYS ;
ret = kfifo_to_user ( & test , buf , count , & copied ) ;
mutex_unlock ( & read_lock ) ;
return ret ? ret : copied ;
}
2020-02-04 04:37:17 +03:00
static const struct proc_ops fifo_proc_ops = {
. proc_read = fifo_read ,
. proc_write = fifo_write ,
. proc_lseek = noop_llseek ,
2010-08-11 05:03:39 +04:00
} ;
static int __init example_init ( void )
{
# ifdef DYNAMIC
int ret ;
ret = kfifo_alloc ( & test , FIFO_SIZE , GFP_KERNEL ) ;
if ( ret ) {
printk ( KERN_ERR " error kfifo_alloc \n " ) ;
return ret ;
}
# else
INIT_KFIFO ( test ) ;
# endif
2010-08-20 01:13:29 +04:00
if ( testfunc ( ) < 0 ) {
# ifdef DYNAMIC
kfifo_free ( & test ) ;
# endif
return - EIO ;
}
2010-08-11 05:03:39 +04:00
2020-02-04 04:37:17 +03:00
if ( proc_create ( PROC_FIFO , 0 , NULL , & fifo_proc_ops ) = = NULL ) {
2010-08-11 05:03:39 +04:00
# ifdef DYNAMIC
kfifo_free ( & test ) ;
# endif
return - ENOMEM ;
}
return 0 ;
}
static void __exit example_exit ( void )
{
remove_proc_entry ( PROC_FIFO , NULL ) ;
# ifdef DYNAMIC
kfifo_free ( & test ) ;
# endif
}
module_init ( example_init ) ;
module_exit ( example_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Stefani Seibold <stefani@seibold.net> " ) ;