1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * |
4 | * some helper function for simple DVB cards which simply DMA the |
5 | * complete transport stream and let the computer sort everything else |
6 | * (i.e. we are using the software demux, ...). Also uses vb2 |
7 | * to manage DMA buffers. |
8 | * |
9 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/init.h> |
14 | #include <linux/device.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include <media/videobuf2-dvb.h> |
18 | |
19 | /* ------------------------------------------------------------------ */ |
20 | |
21 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]" ); |
22 | MODULE_LICENSE("GPL" ); |
23 | |
24 | /* ------------------------------------------------------------------ */ |
25 | |
26 | static int dvb_fnc(struct vb2_buffer *vb, void *priv) |
27 | { |
28 | struct vb2_dvb *dvb = priv; |
29 | |
30 | dvb_dmx_swfilter(demux: &dvb->demux, buf: vb2_plane_vaddr(vb, plane_no: 0), |
31 | count: vb2_get_plane_payload(vb, plane_no: 0)); |
32 | return 0; |
33 | } |
34 | |
35 | static int vb2_dvb_start_feed(struct dvb_demux_feed *feed) |
36 | { |
37 | struct dvb_demux *demux = feed->demux; |
38 | struct vb2_dvb *dvb = demux->priv; |
39 | int rc = 0; |
40 | |
41 | if (!demux->dmx.frontend) |
42 | return -EINVAL; |
43 | |
44 | mutex_lock(&dvb->lock); |
45 | dvb->nfeeds++; |
46 | |
47 | if (!dvb->dvbq.threadio) { |
48 | rc = vb2_thread_start(q: &dvb->dvbq, fnc: dvb_fnc, priv: dvb, thread_name: dvb->name); |
49 | if (rc) |
50 | dvb->nfeeds--; |
51 | } |
52 | if (!rc) |
53 | rc = dvb->nfeeds; |
54 | mutex_unlock(lock: &dvb->lock); |
55 | return rc; |
56 | } |
57 | |
58 | static int vb2_dvb_stop_feed(struct dvb_demux_feed *feed) |
59 | { |
60 | struct dvb_demux *demux = feed->demux; |
61 | struct vb2_dvb *dvb = demux->priv; |
62 | int err = 0; |
63 | |
64 | mutex_lock(&dvb->lock); |
65 | dvb->nfeeds--; |
66 | if (0 == dvb->nfeeds) |
67 | err = vb2_thread_stop(q: &dvb->dvbq); |
68 | mutex_unlock(lock: &dvb->lock); |
69 | return err; |
70 | } |
71 | |
72 | static int vb2_dvb_register_adapter(struct vb2_dvb_frontends *fe, |
73 | struct module *module, |
74 | void *adapter_priv, |
75 | struct device *device, |
76 | struct media_device *mdev, |
77 | char *adapter_name, |
78 | short *adapter_nr, |
79 | int mfe_shared) |
80 | { |
81 | int result; |
82 | |
83 | mutex_init(&fe->lock); |
84 | |
85 | /* register adapter */ |
86 | result = dvb_register_adapter(adap: &fe->adapter, name: adapter_name, module, |
87 | device, adapter_nums: adapter_nr); |
88 | if (result < 0) { |
89 | pr_warn("%s: dvb_register_adapter failed (errno = %d)\n" , |
90 | adapter_name, result); |
91 | } |
92 | fe->adapter.priv = adapter_priv; |
93 | fe->adapter.mfe_shared = mfe_shared; |
94 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
95 | if (mdev) |
96 | fe->adapter.mdev = mdev; |
97 | #endif |
98 | return result; |
99 | } |
100 | |
101 | static int vb2_dvb_register_frontend(struct dvb_adapter *adapter, |
102 | struct vb2_dvb *dvb) |
103 | { |
104 | int result; |
105 | |
106 | /* register frontend */ |
107 | result = dvb_register_frontend(dvb: adapter, fe: dvb->frontend); |
108 | if (result < 0) { |
109 | pr_warn("%s: dvb_register_frontend failed (errno = %d)\n" , |
110 | dvb->name, result); |
111 | goto fail_frontend; |
112 | } |
113 | |
114 | /* register demux stuff */ |
115 | dvb->demux.dmx.capabilities = |
116 | DMX_TS_FILTERING | DMX_SECTION_FILTERING | |
117 | DMX_MEMORY_BASED_FILTERING; |
118 | dvb->demux.priv = dvb; |
119 | dvb->demux.filternum = 256; |
120 | dvb->demux.feednum = 256; |
121 | dvb->demux.start_feed = vb2_dvb_start_feed; |
122 | dvb->demux.stop_feed = vb2_dvb_stop_feed; |
123 | result = dvb_dmx_init(demux: &dvb->demux); |
124 | if (result < 0) { |
125 | pr_warn("%s: dvb_dmx_init failed (errno = %d)\n" , |
126 | dvb->name, result); |
127 | goto fail_dmx; |
128 | } |
129 | |
130 | dvb->dmxdev.filternum = 256; |
131 | dvb->dmxdev.demux = &dvb->demux.dmx; |
132 | dvb->dmxdev.capabilities = 0; |
133 | result = dvb_dmxdev_init(dmxdev: &dvb->dmxdev, adap: adapter); |
134 | |
135 | if (result < 0) { |
136 | pr_warn("%s: dvb_dmxdev_init failed (errno = %d)\n" , |
137 | dvb->name, result); |
138 | goto fail_dmxdev; |
139 | } |
140 | |
141 | dvb->fe_hw.source = DMX_FRONTEND_0; |
142 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); |
143 | if (result < 0) { |
144 | pr_warn("%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n" , |
145 | dvb->name, result); |
146 | goto fail_fe_hw; |
147 | } |
148 | |
149 | dvb->fe_mem.source = DMX_MEMORY_FE; |
150 | result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); |
151 | if (result < 0) { |
152 | pr_warn("%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n" , |
153 | dvb->name, result); |
154 | goto fail_fe_mem; |
155 | } |
156 | |
157 | result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); |
158 | if (result < 0) { |
159 | pr_warn("%s: connect_frontend failed (errno = %d)\n" , |
160 | dvb->name, result); |
161 | goto fail_fe_conn; |
162 | } |
163 | |
164 | /* register network adapter */ |
165 | result = dvb_net_init(adap: adapter, dvbnet: &dvb->net, dmxdemux: &dvb->demux.dmx); |
166 | if (result < 0) { |
167 | pr_warn("%s: dvb_net_init failed (errno = %d)\n" , |
168 | dvb->name, result); |
169 | goto fail_fe_conn; |
170 | } |
171 | return 0; |
172 | |
173 | fail_fe_conn: |
174 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); |
175 | fail_fe_mem: |
176 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); |
177 | fail_fe_hw: |
178 | dvb_dmxdev_release(dmxdev: &dvb->dmxdev); |
179 | fail_dmxdev: |
180 | dvb_dmx_release(demux: &dvb->demux); |
181 | fail_dmx: |
182 | dvb_unregister_frontend(fe: dvb->frontend); |
183 | fail_frontend: |
184 | dvb_frontend_detach(fe: dvb->frontend); |
185 | dvb->frontend = NULL; |
186 | |
187 | return result; |
188 | } |
189 | |
190 | /* ------------------------------------------------------------------ */ |
191 | /* Register a single adapter and one or more frontends */ |
192 | int vb2_dvb_register_bus(struct vb2_dvb_frontends *f, |
193 | struct module *module, |
194 | void *adapter_priv, |
195 | struct device *device, |
196 | struct media_device *mdev, |
197 | short *adapter_nr, |
198 | int mfe_shared) |
199 | { |
200 | struct list_head *list, *q; |
201 | struct vb2_dvb_frontend *fe; |
202 | int res; |
203 | |
204 | fe = vb2_dvb_get_frontend(f, id: 1); |
205 | if (!fe) { |
206 | pr_warn("Unable to register the adapter which has no frontends\n" ); |
207 | return -EINVAL; |
208 | } |
209 | |
210 | /* Bring up the adapter */ |
211 | res = vb2_dvb_register_adapter(fe: f, module, adapter_priv, device, mdev, |
212 | adapter_name: fe->dvb.name, adapter_nr, mfe_shared); |
213 | if (res < 0) { |
214 | pr_warn("vb2_dvb_register_adapter failed (errno = %d)\n" , res); |
215 | return res; |
216 | } |
217 | |
218 | /* Attach all of the frontends to the adapter */ |
219 | mutex_lock(&f->lock); |
220 | list_for_each_safe(list, q, &f->felist) { |
221 | fe = list_entry(list, struct vb2_dvb_frontend, felist); |
222 | res = vb2_dvb_register_frontend(adapter: &f->adapter, dvb: &fe->dvb); |
223 | if (res < 0) { |
224 | pr_warn("%s: vb2_dvb_register_frontend failed (errno = %d)\n" , |
225 | fe->dvb.name, res); |
226 | goto err; |
227 | } |
228 | res = dvb_create_media_graph(adap: &f->adapter, create_rf_connector: false); |
229 | if (res < 0) |
230 | goto err; |
231 | } |
232 | |
233 | mutex_unlock(lock: &f->lock); |
234 | return 0; |
235 | |
236 | err: |
237 | mutex_unlock(lock: &f->lock); |
238 | vb2_dvb_unregister_bus(f); |
239 | return res; |
240 | } |
241 | EXPORT_SYMBOL(vb2_dvb_register_bus); |
242 | |
243 | void vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f) |
244 | { |
245 | vb2_dvb_dealloc_frontends(f); |
246 | |
247 | dvb_unregister_adapter(adap: &f->adapter); |
248 | } |
249 | EXPORT_SYMBOL(vb2_dvb_unregister_bus); |
250 | |
251 | struct vb2_dvb_frontend *vb2_dvb_get_frontend( |
252 | struct vb2_dvb_frontends *f, int id) |
253 | { |
254 | struct list_head *list, *q; |
255 | struct vb2_dvb_frontend *fe, *ret = NULL; |
256 | |
257 | mutex_lock(&f->lock); |
258 | |
259 | list_for_each_safe(list, q, &f->felist) { |
260 | fe = list_entry(list, struct vb2_dvb_frontend, felist); |
261 | if (fe->id == id) { |
262 | ret = fe; |
263 | break; |
264 | } |
265 | } |
266 | |
267 | mutex_unlock(lock: &f->lock); |
268 | |
269 | return ret; |
270 | } |
271 | EXPORT_SYMBOL(vb2_dvb_get_frontend); |
272 | |
273 | int vb2_dvb_find_frontend(struct vb2_dvb_frontends *f, |
274 | struct dvb_frontend *p) |
275 | { |
276 | struct list_head *list, *q; |
277 | struct vb2_dvb_frontend *fe = NULL; |
278 | int ret = 0; |
279 | |
280 | mutex_lock(&f->lock); |
281 | |
282 | list_for_each_safe(list, q, &f->felist) { |
283 | fe = list_entry(list, struct vb2_dvb_frontend, felist); |
284 | if (fe->dvb.frontend == p) { |
285 | ret = fe->id; |
286 | break; |
287 | } |
288 | } |
289 | |
290 | mutex_unlock(lock: &f->lock); |
291 | |
292 | return ret; |
293 | } |
294 | EXPORT_SYMBOL(vb2_dvb_find_frontend); |
295 | |
296 | struct vb2_dvb_frontend *vb2_dvb_alloc_frontend( |
297 | struct vb2_dvb_frontends *f, int id) |
298 | { |
299 | struct vb2_dvb_frontend *fe; |
300 | |
301 | fe = kzalloc(size: sizeof(struct vb2_dvb_frontend), GFP_KERNEL); |
302 | if (fe == NULL) |
303 | return NULL; |
304 | |
305 | fe->id = id; |
306 | mutex_init(&fe->dvb.lock); |
307 | |
308 | mutex_lock(&f->lock); |
309 | list_add_tail(new: &fe->felist, head: &f->felist); |
310 | mutex_unlock(lock: &f->lock); |
311 | return fe; |
312 | } |
313 | EXPORT_SYMBOL(vb2_dvb_alloc_frontend); |
314 | |
315 | void vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f) |
316 | { |
317 | struct list_head *list, *q; |
318 | struct vb2_dvb_frontend *fe; |
319 | |
320 | mutex_lock(&f->lock); |
321 | list_for_each_safe(list, q, &f->felist) { |
322 | fe = list_entry(list, struct vb2_dvb_frontend, felist); |
323 | if (fe->dvb.net.dvbdev) { |
324 | dvb_net_release(dvbnet: &fe->dvb.net); |
325 | fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, |
326 | &fe->dvb.fe_mem); |
327 | fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, |
328 | &fe->dvb.fe_hw); |
329 | dvb_dmxdev_release(dmxdev: &fe->dvb.dmxdev); |
330 | dvb_dmx_release(demux: &fe->dvb.demux); |
331 | dvb_unregister_frontend(fe: fe->dvb.frontend); |
332 | } |
333 | if (fe->dvb.frontend) |
334 | /* always allocated, may have been reset */ |
335 | dvb_frontend_detach(fe: fe->dvb.frontend); |
336 | list_del(entry: list); /* remove list entry */ |
337 | kfree(objp: fe); /* free frontend allocation */ |
338 | } |
339 | mutex_unlock(lock: &f->lock); |
340 | } |
341 | EXPORT_SYMBOL(vb2_dvb_dealloc_frontends); |
342 | |