summaryrefslogtreecommitdiff
path: root/linux/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c')
-rw-r--r--linux/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c b/linux/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
new file mode 100644
index 000000000..3d35ad322
--- /dev/null
+++ b/linux/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
@@ -0,0 +1,205 @@
+/*
+ * dvb-dibusb-dvb.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the
+ * linux-dvb API.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/usb.h>
+#include <linux/version.h>
+
+/*
+ * MPEG2 TS DVB stuff
+ */
+void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+ struct usb_dibusb *dib = urb->context;
+ int ret;
+
+ deb_ts("urb complete feedcount: %d, status: %d\n",dib->feedcount,urb->status);
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ warn("urb completition error %d.", urb->status);
+ }
+
+ if (dib->feedcount > 0) {
+ deb_ts("URB return len: %d\n",urb->actual_length);
+ if (urb->actual_length % 188)
+ deb_ts("TS Packets: %d, %d\n", urb->actual_length/188,urb->actual_length % 188);
+
+ /* Francois recommends to drop not full-filled packets, even if they may
+ * contain valid TS packets, at least for USB1.1
+ */
+ if (urb->actual_length == dib->dibdev->parm->default_size && dib->dvb_is_ready)
+ dvb_dmx_swfilter_packets(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length/188);
+ else
+ deb_ts("URB dropped because of the "
+ "actual_length or !dvb_is_ready (%d).\n",dib->dvb_is_ready);
+ } else
+ deb_ts("URB dropped because of feedcount.\n");
+
+ ret = usb_submit_urb(urb,GFP_ATOMIC);
+ deb_ts("urb resubmitted, (%d)\n",ret);
+}
+
+static int dibusb_ctrl_feed(struct usb_dibusb *dib, int pid, int onoff)
+{
+ int newfeedcount = dib->feedcount + (onoff ? 1 : -1);
+ /*
+ * stop feed before setting a new pid and firmware_bug
+ * or if there will be no pid anymore
+ */
+ if ((dib->dibdev->parm->firmware_bug && dib->feedcount) || newfeedcount == 0) {
+ deb_ts("stop feeding\n");
+ if (dib->xfer_ops.fifo_ctrl != NULL) {
+ if (dib->xfer_ops.fifo_ctrl(dib->fe,0)) {
+ err("error while inhibiting fifo.");
+ return -ENODEV;
+ }
+ } else {
+ err("fifo_ctrl is not set.");
+ return -ENODEV;
+ }
+ }
+
+ dib->feedcount = newfeedcount;
+
+ if (dib->pid_parse) {
+ if (dib->xfer_ops.pid_ctrl != NULL) {
+ if (dib->xfer_ops.pid_ctrl(dib->fe,pid,onoff) < 0) {
+ err("no free pid in list.");
+ return -ENODEV;
+ }
+ } else {
+ err("no pid ctrl callback.");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * start the feed, either if there is the firmware bug or
+ * if this was the first pid to set and there is still a pid for
+ * reception.
+ */
+ if ((dib->dibdev->parm->firmware_bug || dib->feedcount == onoff) &&
+ dib->feedcount > 0) {
+
+ deb_ts("controlling pid parser\n");
+ if (dib->xfer_ops.pid_parse != NULL) {
+ if (dib->xfer_ops.pid_parse(dib->fe,dib->pid_parse) < 0) {
+ err("could not handle pid_parser");
+ }
+ }
+
+ deb_ts("start feeding\n");
+ if (dib->xfer_ops.fifo_ctrl != NULL) {
+ if (dib->xfer_ops.fifo_ctrl(dib->fe,1)) {
+ err("error while enabling fifo.");
+ return -ENODEV;
+ }
+ } else {
+ err("fifo_ctrl is not set.");
+ return -ENODEV;
+ }
+ dibusb_streaming(dib,1);
+ }
+ return 0;
+}
+
+static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct usb_dibusb *dib = dvbdmxfeed->demux->priv;
+ deb_ts("pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
+ dvbdmxfeed->priv = dib;
+ return dibusb_ctrl_feed(dib,dvbdmxfeed->pid,1);
+}
+
+static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct usb_dibusb *dib = (struct usb_dibusb *) dvbdmxfeed->priv;
+ if (dib == NULL) {
+ err("dib in dmxfeed->priv was NULL");
+ return -EINVAL;
+ }
+ deb_ts("dvbdmxfeed pid: 0x%04x, feedtype: %d\n",
+ dvbdmxfeed->pid, dvbdmxfeed->type);
+ return dibusb_ctrl_feed(dib,dvbdmxfeed->pid,0);
+}
+
+int dibusb_dvb_init(struct usb_dibusb *dib)
+{
+ int ret;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,4)
+ if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC)) < 0) {
+#else
+ if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC ,
+ THIS_MODULE)) < 0) {
+#endif
+ deb_info("dvb_register_adapter failed: error %d", ret);
+ goto err;
+ }
+ dib->adapter->priv = dib;
+
+/* i2c is done in dibusb_i2c_init */
+
+ dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+
+ dib->demux.priv = (void *)dib;
+ /* get pidcount from demod */
+ dib->demux.feednum = dib->demux.filternum = 24;
+ dib->demux.start_feed = dibusb_start_feed;
+ dib->demux.stop_feed = dibusb_stop_feed;
+ dib->demux.write_to_decoder = NULL;
+ if ((ret = dvb_dmx_init(&dib->demux)) < 0) {
+ err("dvb_dmx_init failed: error %d",ret);
+ goto err_dmx;
+ }
+
+ dib->dmxdev.filternum = dib->demux.filternum;
+ dib->dmxdev.demux = &dib->demux.dmx;
+ dib->dmxdev.capabilities = 0;
+ if ((ret = dvb_dmxdev_init(&dib->dmxdev, dib->adapter)) < 0) {
+ err("dvb_dmxdev_init failed: error %d",ret);
+ goto err_dmx_dev;
+ }
+
+ dvb_net_init(dib->adapter, &dib->dvb_net, &dib->demux.dmx);
+
+ goto success;
+err_dmx_dev:
+ dvb_dmx_release(&dib->demux);
+err_dmx:
+ dvb_unregister_adapter(dib->adapter);
+err:
+ return ret;
+success:
+ dib->dvb_is_ready = 1;
+ return 0;
+}
+
+int dibusb_dvb_exit(struct usb_dibusb *dib)
+{
+ dib->dvb_is_ready = 0;
+ deb_info("unregistering DVB part\n");
+ dvb_net_release(&dib->dvb_net);
+ dib->demux.dmx.close(&dib->demux.dmx);
+ dvb_dmxdev_release(&dib->dmxdev);
+ dvb_dmx_release(&dib->demux);
+ dvb_unregister_adapter(dib->adapter);
+ return 0;
+}