162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sample kfifo byte stream implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/proc_fs.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/kfifo.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * This module shows how to create a byte stream fifo. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* fifo size in elements (bytes) */ 1962306a36Sopenharmony_ci#define FIFO_SIZE 32 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* name of the proc entry */ 2262306a36Sopenharmony_ci#define PROC_FIFO "bytestream-fifo" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* lock for procfs read access */ 2562306a36Sopenharmony_cistatic DEFINE_MUTEX(read_access); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* lock for procfs write access */ 2862306a36Sopenharmony_cistatic DEFINE_MUTEX(write_access); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * define DYNAMIC in this example for a dynamically allocated fifo. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Otherwise the fifo storage will be a part of the fifo structure. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci#if 0 3662306a36Sopenharmony_ci#define DYNAMIC 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#ifdef DYNAMIC 4062306a36Sopenharmony_cistatic struct kfifo test; 4162306a36Sopenharmony_ci#else 4262306a36Sopenharmony_cistatic DECLARE_KFIFO(test, unsigned char, FIFO_SIZE); 4362306a36Sopenharmony_ci#endif 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const unsigned char expected_result[FIFO_SIZE] = { 4662306a36Sopenharmony_ci 3, 4, 5, 6, 7, 8, 9, 0, 4762306a36Sopenharmony_ci 1, 20, 21, 22, 23, 24, 25, 26, 4862306a36Sopenharmony_ci 27, 28, 29, 30, 31, 32, 33, 34, 4962306a36Sopenharmony_ci 35, 36, 37, 38, 39, 40, 41, 42, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int __init testfunc(void) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned char buf[6]; 5562306a36Sopenharmony_ci unsigned char i, j; 5662306a36Sopenharmony_ci unsigned int ret; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci printk(KERN_INFO "byte stream fifo test start\n"); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* put string into the fifo */ 6162306a36Sopenharmony_ci kfifo_in(&test, "hello", 5); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* put values into the fifo */ 6462306a36Sopenharmony_ci for (i = 0; i != 10; i++) 6562306a36Sopenharmony_ci kfifo_put(&test, i); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* show the number of used elements */ 6862306a36Sopenharmony_ci printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test)); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* get max of 5 bytes from the fifo */ 7162306a36Sopenharmony_ci i = kfifo_out(&test, buf, 5); 7262306a36Sopenharmony_ci printk(KERN_INFO "buf: %.*s\n", i, buf); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* get max of 2 elements from the fifo */ 7562306a36Sopenharmony_ci ret = kfifo_out(&test, buf, 2); 7662306a36Sopenharmony_ci printk(KERN_INFO "ret: %d\n", ret); 7762306a36Sopenharmony_ci /* and put it back to the end of the fifo */ 7862306a36Sopenharmony_ci ret = kfifo_in(&test, buf, ret); 7962306a36Sopenharmony_ci printk(KERN_INFO "ret: %d\n", ret); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* skip first element of the fifo */ 8262306a36Sopenharmony_ci printk(KERN_INFO "skip 1st element\n"); 8362306a36Sopenharmony_ci kfifo_skip(&test); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* put values into the fifo until is full */ 8662306a36Sopenharmony_ci for (i = 20; kfifo_put(&test, i); i++) 8762306a36Sopenharmony_ci ; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci printk(KERN_INFO "queue len: %u\n", kfifo_len(&test)); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* show the first value without removing from the fifo */ 9262306a36Sopenharmony_ci if (kfifo_peek(&test, &i)) 9362306a36Sopenharmony_ci printk(KERN_INFO "%d\n", i); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* check the correctness of all values in the fifo */ 9662306a36Sopenharmony_ci j = 0; 9762306a36Sopenharmony_ci while (kfifo_get(&test, &i)) { 9862306a36Sopenharmony_ci printk(KERN_INFO "item = %d\n", i); 9962306a36Sopenharmony_ci if (i != expected_result[j++]) { 10062306a36Sopenharmony_ci printk(KERN_WARNING "value mismatch: test failed\n"); 10162306a36Sopenharmony_ci return -EIO; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci if (j != ARRAY_SIZE(expected_result)) { 10562306a36Sopenharmony_ci printk(KERN_WARNING "size mismatch: test failed\n"); 10662306a36Sopenharmony_ci return -EIO; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci printk(KERN_INFO "test passed\n"); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic ssize_t fifo_write(struct file *file, const char __user *buf, 11462306a36Sopenharmony_ci size_t count, loff_t *ppos) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int ret; 11762306a36Sopenharmony_ci unsigned int copied; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (mutex_lock_interruptible(&write_access)) 12062306a36Sopenharmony_ci return -ERESTARTSYS; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret = kfifo_from_user(&test, buf, count, &copied); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mutex_unlock(&write_access); 12562306a36Sopenharmony_ci if (ret) 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return copied; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic ssize_t fifo_read(struct file *file, char __user *buf, 13262306a36Sopenharmony_ci size_t count, loff_t *ppos) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int ret; 13562306a36Sopenharmony_ci unsigned int copied; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (mutex_lock_interruptible(&read_access)) 13862306a36Sopenharmony_ci return -ERESTARTSYS; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = kfifo_to_user(&test, buf, count, &copied); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mutex_unlock(&read_access); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return copied; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic const struct proc_ops fifo_proc_ops = { 15062306a36Sopenharmony_ci .proc_read = fifo_read, 15162306a36Sopenharmony_ci .proc_write = fifo_write, 15262306a36Sopenharmony_ci .proc_lseek = noop_llseek, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int __init example_init(void) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci#ifdef DYNAMIC 15862306a36Sopenharmony_ci int ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL); 16162306a36Sopenharmony_ci if (ret) { 16262306a36Sopenharmony_ci printk(KERN_ERR "error kfifo_alloc\n"); 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci#else 16662306a36Sopenharmony_ci INIT_KFIFO(test); 16762306a36Sopenharmony_ci#endif 16862306a36Sopenharmony_ci if (testfunc() < 0) { 16962306a36Sopenharmony_ci#ifdef DYNAMIC 17062306a36Sopenharmony_ci kfifo_free(&test); 17162306a36Sopenharmony_ci#endif 17262306a36Sopenharmony_ci return -EIO; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (proc_create(PROC_FIFO, 0, NULL, &fifo_proc_ops) == NULL) { 17662306a36Sopenharmony_ci#ifdef DYNAMIC 17762306a36Sopenharmony_ci kfifo_free(&test); 17862306a36Sopenharmony_ci#endif 17962306a36Sopenharmony_ci return -ENOMEM; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void __exit example_exit(void) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci remove_proc_entry(PROC_FIFO, NULL); 18762306a36Sopenharmony_ci#ifdef DYNAMIC 18862306a36Sopenharmony_ci kfifo_free(&test); 18962306a36Sopenharmony_ci#endif 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cimodule_init(example_init); 19362306a36Sopenharmony_cimodule_exit(example_exit); 19462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 19562306a36Sopenharmony_ciMODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>"); 196