1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2017 Analog Devices Inc. |
4 | * Author: Lars-Peter Clausen <lars@metafoo.de> |
5 | */ |
6 | |
7 | #include <linux/err.h> |
8 | #include <linux/export.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/module.h> |
11 | |
12 | #include <linux/iio/iio.h> |
13 | #include <linux/iio/consumer.h> |
14 | #include <linux/iio/hw-consumer.h> |
15 | #include <linux/iio/buffer_impl.h> |
16 | |
17 | /** |
18 | * struct iio_hw_consumer - IIO hw consumer block |
19 | * @buffers: hardware buffers list head. |
20 | * @channels: IIO provider channels. |
21 | */ |
22 | struct iio_hw_consumer { |
23 | struct list_head buffers; |
24 | struct iio_channel *channels; |
25 | }; |
26 | |
27 | struct hw_consumer_buffer { |
28 | struct list_head head; |
29 | struct iio_dev *indio_dev; |
30 | struct iio_buffer buffer; |
31 | long scan_mask[]; |
32 | }; |
33 | |
34 | static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer( |
35 | struct iio_buffer *buffer) |
36 | { |
37 | return container_of(buffer, struct hw_consumer_buffer, buffer); |
38 | } |
39 | |
40 | static void iio_hw_buf_release(struct iio_buffer *buffer) |
41 | { |
42 | struct hw_consumer_buffer *hw_buf = |
43 | iio_buffer_to_hw_consumer_buffer(buffer); |
44 | kfree(objp: hw_buf); |
45 | } |
46 | |
47 | static const struct iio_buffer_access_funcs iio_hw_buf_access = { |
48 | .release = &iio_hw_buf_release, |
49 | .modes = INDIO_BUFFER_HARDWARE, |
50 | }; |
51 | |
52 | static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( |
53 | struct iio_hw_consumer *hwc, struct iio_dev *indio_dev) |
54 | { |
55 | struct hw_consumer_buffer *buf; |
56 | |
57 | list_for_each_entry(buf, &hwc->buffers, head) { |
58 | if (buf->indio_dev == indio_dev) |
59 | return buf; |
60 | } |
61 | |
62 | buf = kzalloc(struct_size(buf, scan_mask, BITS_TO_LONGS(indio_dev->masklength)), |
63 | GFP_KERNEL); |
64 | if (!buf) |
65 | return NULL; |
66 | |
67 | buf->buffer.access = &iio_hw_buf_access; |
68 | buf->indio_dev = indio_dev; |
69 | buf->buffer.scan_mask = buf->scan_mask; |
70 | |
71 | iio_buffer_init(buffer: &buf->buffer); |
72 | list_add_tail(new: &buf->head, head: &hwc->buffers); |
73 | |
74 | return buf; |
75 | } |
76 | |
77 | /** |
78 | * iio_hw_consumer_alloc() - Allocate IIO hardware consumer |
79 | * @dev: Pointer to consumer device. |
80 | * |
81 | * Returns a valid iio_hw_consumer on success or a ERR_PTR() on failure. |
82 | */ |
83 | struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) |
84 | { |
85 | struct hw_consumer_buffer *buf; |
86 | struct iio_hw_consumer *hwc; |
87 | struct iio_channel *chan; |
88 | int ret; |
89 | |
90 | hwc = kzalloc(size: sizeof(*hwc), GFP_KERNEL); |
91 | if (!hwc) |
92 | return ERR_PTR(error: -ENOMEM); |
93 | |
94 | INIT_LIST_HEAD(list: &hwc->buffers); |
95 | |
96 | hwc->channels = iio_channel_get_all(dev); |
97 | if (IS_ERR(ptr: hwc->channels)) { |
98 | ret = PTR_ERR(ptr: hwc->channels); |
99 | goto err_free_hwc; |
100 | } |
101 | |
102 | chan = &hwc->channels[0]; |
103 | while (chan->indio_dev) { |
104 | buf = iio_hw_consumer_get_buffer(hwc, indio_dev: chan->indio_dev); |
105 | if (!buf) { |
106 | ret = -ENOMEM; |
107 | goto err_put_buffers; |
108 | } |
109 | set_bit(nr: chan->channel->scan_index, addr: buf->buffer.scan_mask); |
110 | chan++; |
111 | } |
112 | |
113 | return hwc; |
114 | |
115 | err_put_buffers: |
116 | list_for_each_entry(buf, &hwc->buffers, head) |
117 | iio_buffer_put(buffer: &buf->buffer); |
118 | iio_channel_release_all(chan: hwc->channels); |
119 | err_free_hwc: |
120 | kfree(objp: hwc); |
121 | return ERR_PTR(error: ret); |
122 | } |
123 | EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc); |
124 | |
125 | /** |
126 | * iio_hw_consumer_free() - Free IIO hardware consumer |
127 | * @hwc: hw consumer to free. |
128 | */ |
129 | void iio_hw_consumer_free(struct iio_hw_consumer *hwc) |
130 | { |
131 | struct hw_consumer_buffer *buf, *n; |
132 | |
133 | iio_channel_release_all(chan: hwc->channels); |
134 | list_for_each_entry_safe(buf, n, &hwc->buffers, head) |
135 | iio_buffer_put(buffer: &buf->buffer); |
136 | kfree(objp: hwc); |
137 | } |
138 | EXPORT_SYMBOL_GPL(iio_hw_consumer_free); |
139 | |
140 | static void devm_iio_hw_consumer_release(void *iio_hwc) |
141 | { |
142 | iio_hw_consumer_free(iio_hwc); |
143 | } |
144 | |
145 | /** |
146 | * devm_iio_hw_consumer_alloc - Resource-managed iio_hw_consumer_alloc() |
147 | * @dev: Pointer to consumer device. |
148 | * |
149 | * Managed iio_hw_consumer_alloc. iio_hw_consumer allocated with this function |
150 | * is automatically freed on driver detach. |
151 | * |
152 | * returns pointer to allocated iio_hw_consumer on success, NULL on failure. |
153 | */ |
154 | struct iio_hw_consumer *devm_iio_hw_consumer_alloc(struct device *dev) |
155 | { |
156 | struct iio_hw_consumer *iio_hwc; |
157 | int ret; |
158 | |
159 | iio_hwc = iio_hw_consumer_alloc(dev); |
160 | if (IS_ERR(ptr: iio_hwc)) |
161 | return iio_hwc; |
162 | |
163 | ret = devm_add_action_or_reset(dev, devm_iio_hw_consumer_release, |
164 | iio_hwc); |
165 | if (ret) |
166 | return ERR_PTR(error: ret); |
167 | |
168 | return iio_hwc; |
169 | } |
170 | EXPORT_SYMBOL_GPL(devm_iio_hw_consumer_alloc); |
171 | |
172 | /** |
173 | * iio_hw_consumer_enable() - Enable IIO hardware consumer |
174 | * @hwc: iio_hw_consumer to enable. |
175 | * |
176 | * Returns 0 on success. |
177 | */ |
178 | int iio_hw_consumer_enable(struct iio_hw_consumer *hwc) |
179 | { |
180 | struct hw_consumer_buffer *buf; |
181 | int ret; |
182 | |
183 | list_for_each_entry(buf, &hwc->buffers, head) { |
184 | ret = iio_update_buffers(indio_dev: buf->indio_dev, insert_buffer: &buf->buffer, NULL); |
185 | if (ret) |
186 | goto err_disable_buffers; |
187 | } |
188 | |
189 | return 0; |
190 | |
191 | err_disable_buffers: |
192 | list_for_each_entry_continue_reverse(buf, &hwc->buffers, head) |
193 | iio_update_buffers(indio_dev: buf->indio_dev, NULL, remove_buffer: &buf->buffer); |
194 | return ret; |
195 | } |
196 | EXPORT_SYMBOL_GPL(iio_hw_consumer_enable); |
197 | |
198 | /** |
199 | * iio_hw_consumer_disable() - Disable IIO hardware consumer |
200 | * @hwc: iio_hw_consumer to disable. |
201 | */ |
202 | void iio_hw_consumer_disable(struct iio_hw_consumer *hwc) |
203 | { |
204 | struct hw_consumer_buffer *buf; |
205 | |
206 | list_for_each_entry(buf, &hwc->buffers, head) |
207 | iio_update_buffers(indio_dev: buf->indio_dev, NULL, remove_buffer: &buf->buffer); |
208 | } |
209 | EXPORT_SYMBOL_GPL(iio_hw_consumer_disable); |
210 | |
211 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>" ); |
212 | MODULE_DESCRIPTION("Hardware consumer buffer the IIO framework" ); |
213 | MODULE_LICENSE("GPL v2" ); |
214 | |