162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sample dynamic sized record fifo 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 variable sized record fifo. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* fifo size in elements (bytes) */ 1962306a36Sopenharmony_ci#define FIFO_SIZE 128 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* name of the proc entry */ 2262306a36Sopenharmony_ci#define PROC_FIFO "record-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/* 4062306a36Sopenharmony_ci * struct kfifo_rec_ptr_1 and STRUCT_KFIFO_REC_1 can handle records of a 4162306a36Sopenharmony_ci * length between 0 and 255 bytes. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * struct kfifo_rec_ptr_2 and STRUCT_KFIFO_REC_2 can handle records of a 4462306a36Sopenharmony_ci * length between 0 and 65535 bytes. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#ifdef DYNAMIC 4862306a36Sopenharmony_cistruct kfifo_rec_ptr_1 test; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#else 5162306a36Sopenharmony_citypedef STRUCT_KFIFO_REC_1(FIFO_SIZE) mytest; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic mytest test; 5462306a36Sopenharmony_ci#endif 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const char *expected_result[] = { 5762306a36Sopenharmony_ci "a", 5862306a36Sopenharmony_ci "bb", 5962306a36Sopenharmony_ci "ccc", 6062306a36Sopenharmony_ci "dddd", 6162306a36Sopenharmony_ci "eeeee", 6262306a36Sopenharmony_ci "ffffff", 6362306a36Sopenharmony_ci "ggggggg", 6462306a36Sopenharmony_ci "hhhhhhhh", 6562306a36Sopenharmony_ci "iiiiiiiii", 6662306a36Sopenharmony_ci "jjjjjjjjjj", 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int __init testfunc(void) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci char buf[100]; 7262306a36Sopenharmony_ci unsigned int i; 7362306a36Sopenharmony_ci unsigned int ret; 7462306a36Sopenharmony_ci struct { unsigned char buf[6]; } hello = { "hello" }; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci printk(KERN_INFO "record fifo test start\n"); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci kfifo_in(&test, &hello, sizeof(hello)); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* show the size of the next record in the fifo */ 8162306a36Sopenharmony_ci printk(KERN_INFO "fifo peek len: %u\n", kfifo_peek_len(&test)); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* put in variable length data */ 8462306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 8562306a36Sopenharmony_ci memset(buf, 'a' + i, i + 1); 8662306a36Sopenharmony_ci kfifo_in(&test, buf, i + 1); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* skip first element of the fifo */ 9062306a36Sopenharmony_ci printk(KERN_INFO "skip 1st element\n"); 9162306a36Sopenharmony_ci kfifo_skip(&test); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test)); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* show the first record without removing from the fifo */ 9662306a36Sopenharmony_ci ret = kfifo_out_peek(&test, buf, sizeof(buf)); 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci printk(KERN_INFO "%.*s\n", ret, buf); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* check the correctness of all values in the fifo */ 10162306a36Sopenharmony_ci i = 0; 10262306a36Sopenharmony_ci while (!kfifo_is_empty(&test)) { 10362306a36Sopenharmony_ci ret = kfifo_out(&test, buf, sizeof(buf)); 10462306a36Sopenharmony_ci buf[ret] = '\0'; 10562306a36Sopenharmony_ci printk(KERN_INFO "item = %.*s\n", ret, buf); 10662306a36Sopenharmony_ci if (strcmp(buf, expected_result[i++])) { 10762306a36Sopenharmony_ci printk(KERN_WARNING "value mismatch: test failed\n"); 10862306a36Sopenharmony_ci return -EIO; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci if (i != ARRAY_SIZE(expected_result)) { 11262306a36Sopenharmony_ci printk(KERN_WARNING "size mismatch: test failed\n"); 11362306a36Sopenharmony_ci return -EIO; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci printk(KERN_INFO "test passed\n"); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic ssize_t fifo_write(struct file *file, const char __user *buf, 12162306a36Sopenharmony_ci size_t count, loff_t *ppos) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int ret; 12462306a36Sopenharmony_ci unsigned int copied; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (mutex_lock_interruptible(&write_access)) 12762306a36Sopenharmony_ci return -ERESTARTSYS; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = kfifo_from_user(&test, buf, count, &copied); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci mutex_unlock(&write_access); 13262306a36Sopenharmony_ci if (ret) 13362306a36Sopenharmony_ci return ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return copied; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic ssize_t fifo_read(struct file *file, char __user *buf, 13962306a36Sopenharmony_ci size_t count, loff_t *ppos) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci int ret; 14262306a36Sopenharmony_ci unsigned int copied; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (mutex_lock_interruptible(&read_access)) 14562306a36Sopenharmony_ci return -ERESTARTSYS; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = kfifo_to_user(&test, buf, count, &copied); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci mutex_unlock(&read_access); 15062306a36Sopenharmony_ci if (ret) 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return copied; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const struct proc_ops fifo_proc_ops = { 15762306a36Sopenharmony_ci .proc_read = fifo_read, 15862306a36Sopenharmony_ci .proc_write = fifo_write, 15962306a36Sopenharmony_ci .proc_lseek = noop_llseek, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int __init example_init(void) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci#ifdef DYNAMIC 16562306a36Sopenharmony_ci int ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL); 16862306a36Sopenharmony_ci if (ret) { 16962306a36Sopenharmony_ci printk(KERN_ERR "error kfifo_alloc\n"); 17062306a36Sopenharmony_ci return ret; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci#else 17362306a36Sopenharmony_ci INIT_KFIFO(test); 17462306a36Sopenharmony_ci#endif 17562306a36Sopenharmony_ci if (testfunc() < 0) { 17662306a36Sopenharmony_ci#ifdef DYNAMIC 17762306a36Sopenharmony_ci kfifo_free(&test); 17862306a36Sopenharmony_ci#endif 17962306a36Sopenharmony_ci return -EIO; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (proc_create(PROC_FIFO, 0, NULL, &fifo_proc_ops) == NULL) { 18362306a36Sopenharmony_ci#ifdef DYNAMIC 18462306a36Sopenharmony_ci kfifo_free(&test); 18562306a36Sopenharmony_ci#endif 18662306a36Sopenharmony_ci return -ENOMEM; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void __exit example_exit(void) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci remove_proc_entry(PROC_FIFO, NULL); 19462306a36Sopenharmony_ci#ifdef DYNAMIC 19562306a36Sopenharmony_ci kfifo_free(&test); 19662306a36Sopenharmony_ci#endif 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cimodule_init(example_init); 20062306a36Sopenharmony_cimodule_exit(example_exit); 20162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 20262306a36Sopenharmony_ciMODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>"); 203