18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Sample kfifo byte stream 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 byte stream fifo. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* fifo size in elements (bytes) */ 198c2ecf20Sopenharmony_ci#define FIFO_SIZE 32 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* name of the proc entry */ 228c2ecf20Sopenharmony_ci#define PROC_FIFO "bytestream-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#ifdef DYNAMIC 408c2ecf20Sopenharmony_cistatic struct kfifo test; 418c2ecf20Sopenharmony_ci#else 428c2ecf20Sopenharmony_cistatic DECLARE_KFIFO(test, unsigned char, FIFO_SIZE); 438c2ecf20Sopenharmony_ci#endif 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const unsigned char expected_result[FIFO_SIZE] = { 468c2ecf20Sopenharmony_ci 3, 4, 5, 6, 7, 8, 9, 0, 478c2ecf20Sopenharmony_ci 1, 20, 21, 22, 23, 24, 25, 26, 488c2ecf20Sopenharmony_ci 27, 28, 29, 30, 31, 32, 33, 34, 498c2ecf20Sopenharmony_ci 35, 36, 37, 38, 39, 40, 41, 42, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int __init testfunc(void) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci unsigned char buf[6]; 558c2ecf20Sopenharmony_ci unsigned char i, j; 568c2ecf20Sopenharmony_ci unsigned int ret; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci printk(KERN_INFO "byte stream fifo test start\n"); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* put string into the fifo */ 618c2ecf20Sopenharmony_ci kfifo_in(&test, "hello", 5); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* put values into the fifo */ 648c2ecf20Sopenharmony_ci for (i = 0; i != 10; i++) 658c2ecf20Sopenharmony_ci kfifo_put(&test, i); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* show the number of used elements */ 688c2ecf20Sopenharmony_ci printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test)); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* get max of 5 bytes from the fifo */ 718c2ecf20Sopenharmony_ci i = kfifo_out(&test, buf, 5); 728c2ecf20Sopenharmony_ci printk(KERN_INFO "buf: %.*s\n", i, buf); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* get max of 2 elements from the fifo */ 758c2ecf20Sopenharmony_ci ret = kfifo_out(&test, buf, 2); 768c2ecf20Sopenharmony_ci printk(KERN_INFO "ret: %d\n", ret); 778c2ecf20Sopenharmony_ci /* and put it back to the end of the fifo */ 788c2ecf20Sopenharmony_ci ret = kfifo_in(&test, buf, ret); 798c2ecf20Sopenharmony_ci printk(KERN_INFO "ret: %d\n", ret); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* skip first element of the fifo */ 828c2ecf20Sopenharmony_ci printk(KERN_INFO "skip 1st element\n"); 838c2ecf20Sopenharmony_ci kfifo_skip(&test); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* put values into the fifo until is full */ 868c2ecf20Sopenharmony_ci for (i = 20; kfifo_put(&test, i); i++) 878c2ecf20Sopenharmony_ci ; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci printk(KERN_INFO "queue len: %u\n", kfifo_len(&test)); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* show the first value without removing from the fifo */ 928c2ecf20Sopenharmony_ci if (kfifo_peek(&test, &i)) 938c2ecf20Sopenharmony_ci printk(KERN_INFO "%d\n", i); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* check the correctness of all values in the fifo */ 968c2ecf20Sopenharmony_ci j = 0; 978c2ecf20Sopenharmony_ci while (kfifo_get(&test, &i)) { 988c2ecf20Sopenharmony_ci printk(KERN_INFO "item = %d\n", i); 998c2ecf20Sopenharmony_ci if (i != expected_result[j++]) { 1008c2ecf20Sopenharmony_ci printk(KERN_WARNING "value mismatch: test failed\n"); 1018c2ecf20Sopenharmony_ci return -EIO; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci if (j != ARRAY_SIZE(expected_result)) { 1058c2ecf20Sopenharmony_ci printk(KERN_WARNING "size mismatch: test failed\n"); 1068c2ecf20Sopenharmony_ci return -EIO; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci printk(KERN_INFO "test passed\n"); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic ssize_t fifo_write(struct file *file, const char __user *buf, 1148c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int ret; 1178c2ecf20Sopenharmony_ci unsigned int copied; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&write_lock)) 1208c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = kfifo_from_user(&test, buf, count, &copied); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_unlock(&write_lock); 1258c2ecf20Sopenharmony_ci if (ret) 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return copied; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic ssize_t fifo_read(struct file *file, char __user *buf, 1328c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int ret; 1358c2ecf20Sopenharmony_ci unsigned int copied; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&read_lock)) 1388c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ret = kfifo_to_user(&test, buf, count, &copied); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mutex_unlock(&read_lock); 1438c2ecf20Sopenharmony_ci if (ret) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return copied; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct proc_ops fifo_proc_ops = { 1508c2ecf20Sopenharmony_ci .proc_read = fifo_read, 1518c2ecf20Sopenharmony_ci .proc_write = fifo_write, 1528c2ecf20Sopenharmony_ci .proc_lseek = noop_llseek, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int __init example_init(void) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1588c2ecf20Sopenharmony_ci int ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL); 1618c2ecf20Sopenharmony_ci if (ret) { 1628c2ecf20Sopenharmony_ci printk(KERN_ERR "error kfifo_alloc\n"); 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci#else 1668c2ecf20Sopenharmony_ci INIT_KFIFO(test); 1678c2ecf20Sopenharmony_ci#endif 1688c2ecf20Sopenharmony_ci if (testfunc() < 0) { 1698c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1708c2ecf20Sopenharmony_ci kfifo_free(&test); 1718c2ecf20Sopenharmony_ci#endif 1728c2ecf20Sopenharmony_ci return -EIO; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (proc_create(PROC_FIFO, 0, NULL, &fifo_proc_ops) == NULL) { 1768c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1778c2ecf20Sopenharmony_ci kfifo_free(&test); 1788c2ecf20Sopenharmony_ci#endif 1798c2ecf20Sopenharmony_ci return -ENOMEM; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void __exit example_exit(void) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci remove_proc_entry(PROC_FIFO, NULL); 1878c2ecf20Sopenharmony_ci#ifdef DYNAMIC 1888c2ecf20Sopenharmony_ci kfifo_free(&test); 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cimodule_init(example_init); 1938c2ecf20Sopenharmony_cimodule_exit(example_exit); 1948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>"); 196