summaryrefslogtreecommitdiffstats
path: root/kernel/samples/kfifo/dma-example.c
blob: aa243db93f01fb88e32ad6fdd990f11034a4b18f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * Sample fifo dma implementation
 *
 * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
 *
 * Released under the GPL version 2 only.
 *
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kfifo.h>

/*
 * This module shows how to handle fifo dma operations.
 */

/* fifo size in elements (bytes) */
#define FIFO_SIZE	32

static struct kfifo fifo;

static int __init example_init(void)
{
	int			i;
	unsigned int		ret;
	unsigned int		nents;
	struct scatterlist	sg[10];

	printk(KERN_INFO "DMA fifo test start\n");

	if (kfifo_alloc(&fifo, FIFO_SIZE, GFP_KERNEL)) {
		printk(KERN_WARNING "error kfifo_alloc\n");
		return -ENOMEM;
	}

	printk(KERN_INFO "queue size: %u\n", kfifo_size(&fifo));

	kfifo_in(&fifo, "test", 4);

	for (i = 0; i != 9; i++)
		kfifo_put(&fifo, i);

	/* kick away first byte */
	kfifo_skip(&fifo);

	printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));

	/*
	 * Configure the kfifo buffer to receive data from DMA input.
	 *
	 *  .--------------------------------------.
	 *  | 0 | 1 | 2 | ... | 12 | 13 | ... | 31 |
	 *  |---|------------------|---------------|
	 *   \_/ \________________/ \_____________/
	 *    \          \                  \
	 *     \          \_allocated data   \
	 *      \_*free space*                \_*free space*
	 *
	 * We need two different SG entries: one for the free space area at the
	 * end of the kfifo buffer (19 bytes) and another for the first free
	 * byte at the beginning, after the kfifo_skip().
	 */
	sg_init_table(sg, ARRAY_SIZE(sg));
	nents = kfifo_dma_in_prepare(&fifo, sg, ARRAY_SIZE(sg), FIFO_SIZE);
	printk(KERN_INFO "DMA sgl entries: %d\n", nents);
	if (!nents) {
		/* fifo is full and no sgl was created */
		printk(KERN_WARNING "error kfifo_dma_in_prepare\n");
		return -EIO;
	}

	/* receive data */
	printk(KERN_INFO "scatterlist for receive:\n");
	for (i = 0; i < nents; i++) {
		printk(KERN_INFO
		"sg[%d] -> "
		"page_link 0x%.8lx offset 0x%.8x length 0x%.8x\n",
			i, sg[i].page_link, sg[i].offset, sg[i].length);

		if (sg_is_last(&sg[i]))
			break;
	}

	/* put here your code to setup and exectute the dma operation */
	/* ... */

	/* example: zero bytes received */
	ret = 0;

	/* finish the dma operation and update the received data */
	kfifo_dma_in_finish(&fifo, ret);

	/* Prepare to transmit data, example: 8 bytes */
	nents = kfifo_dma_out_prepare(&fifo, sg, ARRAY_SIZE(sg), 8);
	printk(KERN_INFO "DMA sgl entries: %d\n", nents);
	if (!nents) {
		/* no data was available and no sgl was created */
		printk(KERN_WARNING "error kfifo_dma_out_prepare\n");
		return -EIO;
	}

	printk(KERN_INFO "scatterlist for transmit:\n");
	for (i = 0; i < nents; i++) {
		printk(KERN_INFO
		"sg[%d] -> "
		"page_link 0x%.8lx offset 0x%.8x length 0x%.8x\n",
			i, sg[i].page_link, sg[i].offset, sg[i].length);

		if (sg_is_last(&sg[i]))
			break;
	}

	/* put here your code to setup and exectute the dma operation */
	/* ... */

	/* example: 5 bytes transmitted */
	ret = 5;

	/* finish the dma operation and update the transmitted data */
	kfifo_dma_out_finish(&fifo, ret);

	ret = kfifo_len(&fifo);
	printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));

	if (ret != 7) {
		printk(KERN_WARNING "size mismatch: test failed");
		return -EIO;
	}
	printk(KERN_INFO "test passed\n");

	return 0;
}

static void __exit example_exit(void)
{
	kfifo_free(&fifo);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");