18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Sample dynamic sized record fifo implementation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * This module shows how to create a variable sized record fifo. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* fifo size in elements (bytes) */ 198c2ecf20Sopenharmony_ci#define FIFO_SIZE 128 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* name of the proc entry */ 228c2ecf20Sopenharmony_ci#define PROC_FIFO "record-fifo" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* lock for procfs read access */ 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(read_lock); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* lock for procfs write access */ 288c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(write_lock); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * define DYNAMIC in this example for a dynamically allocated fifo. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Otherwise the fifo storage will be a part of the fifo structure. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci#if 0 368c2ecf20Sopenharmony_ci#define DYNAMIC 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * struct kfifo_rec_ptr_1 and STRUCT_KFIFO_REC_1 can handle records of a 418c2ecf20Sopenharmony_ci * length between 0 and 255 bytes. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * struct kfifo_rec_ptr_2 and STRUCT_KFIFO_REC_2 can handle records of a 448c2ecf20Sopenharmony_ci * length between 0 and 65535 bytes. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#ifdef DYNAMIC 488c2ecf20Sopenharmony_cistruct kfifo_rec_ptr_1 test; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#else 518c2ecf20Sopenharmony_citypedef STRUCT_KFIFO_REC_1(FIFO_SIZE) mytest; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic mytest test; 548c2ecf20Sopenharmony_ci#endif 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const char *expected_result[] = { 578c2ecf20Sopenharmony_ci "a", 588c2ecf20Sopenharmony_ci "bb", 598c2ecf20Sopenharmony_ci "ccc", 608c2ecf20Sopenharmony_ci "dddd", 618c2ecf20Sopenharmony_ci "eeeee", 628c2ecf20Sopenharmony_ci "ffffff", 638c2ecf20Sopenharmony_ci "ggggggg", 648c2ecf20Sopenharmony_ci "hhhhhhhh", 658c2ecf20Sopenharmony_ci "iiiiiiiii", 668c2ecf20Sopenharmony_ci "jjjjjjjjjj", 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int __init testfunc(void) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci char buf[100]; 728c2ecf20Sopenharmony_ci unsigned int i; 738c2ecf20Sopenharmony_ci unsigned int ret; 748c2ecf20Sopenharmony_ci struct { unsigned char buf[6]; } hello = { "hello" }; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci printk(KERN_INFO "record fifo test start\n"); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci kfifo_in(&test, &hello, sizeof(hello)); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* show the size of the next record in the fifo */ 818c2ecf20Sopenharmony_ci printk(KERN_INFO "fifo peek len: %u\n", kfifo_peek_len(&test)); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* put in variable length data */ 848c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 858c2ecf20Sopenharmony_ci memset(buf, 'a' + i, i + 1); 868c2ecf20Sopenharmony_ci kfifo_in(&test, buf, i + 1); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* skip first element of the fifo */ 908c2ecf20Sopenharmony_ci printk(KERN_INFO "skip 1st element\n"); 918c2ecf20Sopenharmony_ci kfifo_skip(&test); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test)); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* show the first record without removing from the fifo */ 968c2ecf20Sopenharmony_ci ret = kfifo_out_peek(&test, buf, sizeof(buf)); 978c2ecf20Sopenharmony_ci if (ret) 988c2ecf20Sopenharmony_ci printk(KERN_INFO "%.*s\n", ret, buf); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* check the correctness of all values in the fifo */ 1018c2ecf20Sopenharmony_ci i = 0; 1028c2ecf20Sopenharmony_ci while (!kfifo_is_empty(&test)) { 1038c2ecf20Sopenharmony_ci ret = kfifo_out(&test, buf, sizeof(buf)); 1048c2ecf20Sopenharmony_ci buf[ret] = '\0'; 1058c2ecf20Sopenharmony_ci printk(KERN_INFO "item = %.*s\n", ret, buf); 1068c2ecf20Sopenharmony_ci if (strcmp(buf, expected_result[i++])) { 1078c2ecf20Sopenharmony_ci printk(KERN_WARNING "value mismatch: test failed\n"); 1088c2ecf20Sopenharmony_ci return -EIO; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci if (i != ARRAY_SIZE(expected_result)) { 1128c2ecf20Sopenharmony_ci printk(KERN_WARNING "size mismatch: test failed\n"); 1138c2ecf20Sopenharmony_ci return -EIO; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci printk(KERN_INFO "test passed\n"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic ssize_t fifo_write(struct file *file, const char __user *buf, 1218c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci unsigned int copied; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&write_lock)) 1278c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = kfifo_from_user(&test, buf, count, &copied); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci mutex_unlock(&write_lock); 1328c2ecf20Sopenharmony_ci if (ret) 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return copied; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic ssize_t fifo_read(struct file *file, char __user *buf, 1398c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci unsigned int copied; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&read_lock)) 1458c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = kfifo_to_user(&test, buf, count, &copied); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci mutex_unlock(&read_lock); 1508c2ecf20Sopenharmony_ci if (ret) 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return copied; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct proc_ops fifo_proc_ops = { 1578c2ecf20Sopenharmony_ci .proc_read = fifo_read, 1588c2ecf20Sopenharmony_ci .proc_write = fifo_write, 1598c2ecf20Sopenharmony_ci .proc_lseek = noop_llseek, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int __init example_init(void) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL); 1688c2ecf20Sopenharmony_ci if (ret) { 1698c2ecf20Sopenharmony_ci printk(KERN_ERR "error kfifo_alloc\n"); 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci#else 1738c2ecf20Sopenharmony_ci INIT_KFIFO(test); 1748c2ecf20Sopenharmony_ci#endif 1758c2ecf20Sopenharmony_ci if (testfunc() < 0) { 1768c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1778c2ecf20Sopenharmony_ci kfifo_free(&test); 1788c2ecf20Sopenharmony_ci#endif 1798c2ecf20Sopenharmony_ci return -EIO; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (proc_create(PROC_FIFO, 0, NULL, &fifo_proc_ops) == NULL) { 1838c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1848c2ecf20Sopenharmony_ci kfifo_free(&test); 1858c2ecf20Sopenharmony_ci#endif 1868c2ecf20Sopenharmony_ci return -ENOMEM; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void __exit example_exit(void) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci remove_proc_entry(PROC_FIFO, NULL); 1948c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1958c2ecf20Sopenharmony_ci kfifo_free(&test); 1968c2ecf20Sopenharmony_ci#endif 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cimodule_init(example_init); 2008c2ecf20Sopenharmony_cimodule_exit(example_exit); 2018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>"); 203