xref: /linux/samples/kfifo/dma-example.c (revision 3eb3c33c1d87029a3832e205eebd59cfb56ba3a4)
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * Sample fifo dma implementation
4   *
5   * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
6   */
7  
8  #include <linux/init.h>
9  #include <linux/kfifo.h>
10  #include <linux/module.h>
11  #include <linux/scatterlist.h>
12  
13  /*
14   * This module shows how to handle fifo dma operations.
15   */
16  
17  /* fifo size in elements (bytes) */
18  #define FIFO_SIZE	32
19  
20  static struct kfifo fifo;
21  
22  static int __init example_init(void)
23  {
24  	int			i;
25  	unsigned int		ret;
26  	unsigned int		nents;
27  	struct scatterlist	sg[10];
28  
29  	printk(KERN_INFO "DMA fifo test start\n");
30  
31  	if (kfifo_alloc(&fifo, FIFO_SIZE, GFP_KERNEL)) {
32  		printk(KERN_WARNING "error kfifo_alloc\n");
33  		return -ENOMEM;
34  	}
35  
36  	printk(KERN_INFO "queue size: %u\n", kfifo_size(&fifo));
37  
38  	kfifo_in(&fifo, "test", 4);
39  
40  	for (i = 0; i != 9; i++)
41  		kfifo_put(&fifo, i);
42  
43  	/* kick away first byte */
44  	kfifo_skip(&fifo);
45  
46  	printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
47  
48  	/*
49  	 * Configure the kfifo buffer to receive data from DMA input.
50  	 *
51  	 *  .--------------------------------------.
52  	 *  | 0 | 1 | 2 | ... | 12 | 13 | ... | 31 |
53  	 *  |---|------------------|---------------|
54  	 *   \_/ \________________/ \_____________/
55  	 *    \          \                  \
56  	 *     \          \_allocated data   \
57  	 *      \_*free space*                \_*free space*
58  	 *
59  	 * We need two different SG entries: one for the free space area at the
60  	 * end of the kfifo buffer (19 bytes) and another for the first free
61  	 * byte at the beginning, after the kfifo_skip().
62  	 */
63  	sg_init_table(sg, ARRAY_SIZE(sg));
64  	nents = kfifo_dma_in_prepare(&fifo, sg, ARRAY_SIZE(sg), FIFO_SIZE);
65  	printk(KERN_INFO "DMA sgl entries: %d\n", nents);
66  	if (!nents) {
67  		/* fifo is full and no sgl was created */
68  		printk(KERN_WARNING "error kfifo_dma_in_prepare\n");
69  		return -EIO;
70  	}
71  
72  	/* receive data */
73  	printk(KERN_INFO "scatterlist for receive:\n");
74  	for (i = 0; i < nents; i++) {
75  		printk(KERN_INFO
76  		"sg[%d] -> "
77  		"page %p offset 0x%.8x length 0x%.8x\n",
78  			i, sg_page(&sg[i]), sg[i].offset, sg[i].length);
79  
80  		if (sg_is_last(&sg[i]))
81  			break;
82  	}
83  
84  	/* put here your code to setup and exectute the dma operation */
85  	/* ... */
86  
87  	/* example: zero bytes received */
88  	ret = 0;
89  
90  	/* finish the dma operation and update the received data */
91  	kfifo_dma_in_finish(&fifo, ret);
92  
93  	/* Prepare to transmit data, example: 8 bytes */
94  	nents = kfifo_dma_out_prepare(&fifo, sg, ARRAY_SIZE(sg), 8);
95  	printk(KERN_INFO "DMA sgl entries: %d\n", nents);
96  	if (!nents) {
97  		/* no data was available and no sgl was created */
98  		printk(KERN_WARNING "error kfifo_dma_out_prepare\n");
99  		return -EIO;
100  	}
101  
102  	printk(KERN_INFO "scatterlist for transmit:\n");
103  	for (i = 0; i < nents; i++) {
104  		printk(KERN_INFO
105  		"sg[%d] -> "
106  		"page %p offset 0x%.8x length 0x%.8x\n",
107  			i, sg_page(&sg[i]), sg[i].offset, sg[i].length);
108  
109  		if (sg_is_last(&sg[i]))
110  			break;
111  	}
112  
113  	/* put here your code to setup and exectute the dma operation */
114  	/* ... */
115  
116  	/* example: 5 bytes transmitted */
117  	ret = 5;
118  
119  	/* finish the dma operation and update the transmitted data */
120  	kfifo_dma_out_finish(&fifo, ret);
121  
122  	ret = kfifo_len(&fifo);
123  	printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
124  
125  	if (ret != 7) {
126  		printk(KERN_WARNING "size mismatch: test failed");
127  		return -EIO;
128  	}
129  	printk(KERN_INFO "test passed\n");
130  
131  	return 0;
132  }
133  
134  static void __exit example_exit(void)
135  {
136  	kfifo_free(&fifo);
137  }
138  
139  module_init(example_init);
140  module_exit(example_exit);
141  MODULE_LICENSE("GPL");
142  MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
143