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