aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/dvb/firesat/firesat_1394.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2008-03-06 21:30:23 -0800
committerStefan Richter <stefanr@s5r6.in-berlin.de>2009-02-24 14:51:25 +0100
commitc81c8b68b46752721b0c1addfabb828da27e1489 (patch)
tree59d2a336150ce59780b73ba8dc59a829e217b2d0 /drivers/media/dvb/firesat/firesat_1394.c
parentf7e603ad8f78cd3b59e33fa72707da0cbabdf699 (diff)
DVB: add firesat driver
Original code written by Christian Dolzer <c.dolzer@digital-everywhere.com> Cleaned up by Greg. Major cleanup and reorg by Manu Abraham <manu@linuxtv.org> Additions also by Ben Backx <ben@bbackx.com> Cc: Christian Dolzer <c.dolzer@digital-everywhere.com> Cc: Andreas Monitzer <andy@monitzer.com> Cc: Manu Abraham <manu@linuxtv.org> Cc: Fabio De Lorenzo <delorenzo.fabio@gmail.com> Cc: Robert Berger <robert.berger@reliableembeddedsystems.com> Signed-off-by: Ben Backx <ben@bbackx.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Added missing dependency to dvb/firesat/Kconfig, Reported-by: Randy Dunlap <randy.dunlap@oracle.com> Tweaked dvb/Makefile. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/media/dvb/firesat/firesat_1394.c')
-rw-r--r--drivers/media/dvb/firesat/firesat_1394.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c
new file mode 100644
index 00000000000..c7ccf633c24
--- /dev/null
+++ b/drivers/media/dvb/firesat/firesat_1394.c
@@ -0,0 +1,468 @@
+/*
+ * FireSAT DVB driver
+ *
+ * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com>
+ * Copyright (c) 2007-2008 Ben Backx <ben@bbackx.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#include <ieee1394_hotplug.h>
+#include <nodemgr.h>
+#include <highlevel.h>
+#include <ohci1394.h>
+#include <hosts.h>
+#include <dvbdev.h>
+
+#include "firesat.h"
+#include "avc_api.h"
+#include "cmp.h"
+#include "firesat-rc.h"
+#include "firesat-ci.h"
+
+#define FIRESAT_Vendor_ID 0x001287
+
+static struct ieee1394_device_id firesat_id_table[] = {
+
+ {
+ /* FloppyDTV S/CI and FloppyDTV S2 */
+ .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
+ .model_id = 0x000024,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ },{
+ /* FloppyDTV T/CI */
+ .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
+ .model_id = 0x000025,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ },{
+ /* FloppyDTV C/CI */
+ .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
+ .model_id = 0x000026,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ },{
+ /* FireDTV S/CI and FloppyDTV S2 */
+ .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
+ .model_id = 0x000034,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ },{
+ /* FireDTV T/CI */
+ .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
+ .model_id = 0x000035,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ },{
+ /* FireDTV C/CI */
+ .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
+ .model_id = 0x000036,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ }, { }
+};
+
+MODULE_DEVICE_TABLE(ieee1394, firesat_id_table);
+
+/* list of all firesat devices */
+LIST_HEAD(firesat_list);
+spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED;
+
+static void firesat_add_host(struct hpsb_host *host);
+static void firesat_remove_host(struct hpsb_host *host);
+static void firesat_host_reset(struct hpsb_host *host);
+
+/*
+static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data,
+ size_t length);
+*/
+
+static void fcp_request(struct hpsb_host *host,
+ int nodeid,
+ int direction,
+ int cts,
+ u8 *data,
+ size_t length);
+
+static struct hpsb_highlevel firesat_highlevel = {
+ .name = "FireSAT",
+ .add_host = firesat_add_host,
+ .remove_host = firesat_remove_host,
+ .host_reset = firesat_host_reset,
+// FIXME .iso_receive = iso_receive,
+ .fcp_request = fcp_request,
+};
+
+static void firesat_add_host (struct hpsb_host *host)
+{
+ struct ti_ohci *ohci = (struct ti_ohci *)host->hostdata;
+
+ /* We only work with the OHCI-1394 driver */
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+ return;
+
+ if (!hpsb_create_hostinfo(&firesat_highlevel, host, 0)) {
+ printk(KERN_ERR "Cannot allocate hostinfo\n");
+ return;
+ }
+
+ hpsb_set_hostinfo(&firesat_highlevel, host, ohci);
+ hpsb_set_hostinfo_key(&firesat_highlevel, host, ohci->host->id);
+}
+
+static void firesat_remove_host (struct hpsb_host *host)
+{
+
+}
+
+static void firesat_host_reset(struct hpsb_host *host)
+{
+ printk(KERN_INFO "FireSAT host_reset (nodeid = 0x%x, hosts active = %d)\n",host->node_id,host->nodes_active);
+}
+
+struct firewireheader {
+ union {
+ struct {
+ unsigned char tcode:4;
+ unsigned char sy:4;
+ unsigned char tag:2;
+ unsigned char channel:6;
+
+ unsigned char length_l;
+ unsigned char length_h;
+ } hdr;
+ unsigned long val;
+ };
+};
+
+struct CIPHeader {
+ union {
+ struct {
+ unsigned char syncbits:2;
+ unsigned char sid:6;
+ unsigned char dbs;
+ unsigned char fn:2;
+ unsigned char qpc:3;
+ unsigned char sph:1;
+ unsigned char rsv:2;
+ unsigned char dbc;
+ unsigned char syncbits2:2;
+ unsigned char fmt:6;
+ unsigned long fdf:24;
+ } cip;
+ unsigned long long val;
+ };
+};
+
+struct MPEG2Header {
+ union {
+ struct {
+ unsigned char sync; // must be 0x47
+ unsigned char transport_error_indicator:1;
+ unsigned char payload_unit_start_indicator:1;
+ unsigned char transport_priority:1;
+ unsigned short pid:13;
+ unsigned char transport_scrambling_control:2;
+ unsigned char adaption_field_control:2;
+ unsigned char continuity_counter:4;
+ } hdr;
+ unsigned long val;
+ };
+};
+
+#if 0
+static void iso_receive(struct hpsb_host *host,
+ int channel,
+ quadlet_t *data,
+ size_t length)
+{
+ struct firesat *firesat = NULL;
+ struct firesat *firesat_entry;
+ unsigned long flags;
+
+// printk(KERN_INFO "FireSAT iso_receive: channel %d, length = %d\n", channel, length);
+
+ if (length <= 12)
+ return; // ignore empty packets
+ else {
+
+ spin_lock_irqsave(&firesat_list_lock, flags);
+ list_for_each_entry(firesat_entry,&firesat_list,list) {
+ if(firesat_entry->host == host && firesat_entry->isochannel == channel) {
+ firesat=firesat_entry;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&firesat_list_lock, flags);
+
+ if (firesat) {
+ char *buf= ((char*)data) + sizeof(struct firewireheader)+sizeof(struct CIPHeader);
+ int count = (length-sizeof(struct CIPHeader)) / 192;
+
+// printk(KERN_INFO "%s: length = %u\n data[0] = %08x\n data[1] = %08x\n data[2] = %08x\n data[3] = %08x\n data[4] = %08x\n",__func__, length, data[0],data[1],data[2],data[3],data[4]);
+
+ while (count--) {
+
+ if (buf[sizeof(quadlet_t) /*timestamp*/] == 0x47)
+ dvb_dmx_swfilter_packets(&firesat->demux, &buf[sizeof(quadlet_t)], 1);
+ else
+ printk("%s: invalid packet, skipping\n", __func__);
+ buf += 188 + sizeof (quadlet_t) /* timestamp */;
+ }
+ }
+ }
+}
+#endif
+
+static void fcp_request(struct hpsb_host *host,
+ int nodeid,
+ int direction,
+ int cts,
+ u8 *data,
+ size_t length)
+{
+ struct firesat *firesat = NULL;
+ struct firesat *firesat_entry;
+ unsigned long flags;
+
+ if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) {
+
+ spin_lock_irqsave(&firesat_list_lock, flags);
+ list_for_each_entry(firesat_entry,&firesat_list,list) {
+ if (firesat_entry->host == host &&
+ firesat_entry->nodeentry->nodeid == nodeid &&
+ (firesat_entry->subunit == (data[1]&0x7) ||
+ (firesat_entry->subunit == 0 &&
+ (data[1]&0x7) == 0x7))) {
+ firesat=firesat_entry;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&firesat_list_lock, flags);
+
+ if (firesat)
+ AVCRecv(firesat,data,length);
+ else
+ printk("%s: received fcp request from unknown source, ignored\n", __func__);
+ } // else ignore
+}
+
+static int firesat_probe(struct device *dev)
+{
+ struct unit_directory *ud = container_of(dev, struct unit_directory, device);
+ struct firesat *firesat;
+ struct dvb_frontend *fe;
+ unsigned long flags;
+ int result;
+ unsigned char subunitcount = 0xff, subunit;
+ struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL);
+
+ if (!firesats) {
+ printk("%s: couldn't allocate memory.\n", __func__);
+ return -ENOMEM;
+ }
+
+// printk(KERN_INFO "FireSAT: Detected device with GUID %08lx%04lx%04lx\n",(unsigned long)((ud->ne->guid)>>32),(unsigned long)(ud->ne->guid & 0xFFFF),(unsigned long)ud->ne->guid_vendor_id);
+ printk(KERN_INFO "%s: loading device\n", __func__);
+
+ firesats[0] = NULL;
+ firesats[1] = NULL;
+
+ ud->device.driver_data = firesats;
+
+ for (subunit = 0; subunit < subunitcount; subunit++) {
+
+ if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) ||
+ !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) {
+
+ printk("%s: couldn't allocate memory.\n", __func__);
+ kfree(firesats);
+ return -ENOMEM;
+ }
+
+ memset(firesat, 0, sizeof (struct firesat));
+
+ firesat->host = ud->ne->host;
+ firesat->guid = ud->ne->guid;
+ firesat->guid_vendor_id = ud->ne->guid_vendor_id;
+ firesat->nodeentry = ud->ne;
+ firesat->isochannel = -1;
+ firesat->tone = 0xff;
+ firesat->voltage = 0xff;
+
+ if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) {
+ printk("%s: couldn't allocate memory.\n", __func__);
+ kfree(firesat);
+ return -ENOMEM;
+ }
+
+ sema_init(&firesat->avc_sem, 1);
+ atomic_set(&firesat->avc_reply_received, 1);
+ sema_init(&firesat->demux_sem, 1);
+ atomic_set(&firesat->reschedule_remotecontrol, 0);
+
+ spin_lock_irqsave(&firesat_list_lock, flags);
+ INIT_LIST_HEAD(&firesat->list);
+ list_add_tail(&firesat->list, &firesat_list);
+ spin_unlock_irqrestore(&firesat_list_lock, flags);
+
+ if (subunit == 0) {
+ firesat->subunit = 0x7; // 0x7 = don't care
+ if (AVCSubUnitInfo(firesat, &subunitcount)) {
+ printk("%s: AVC subunit info command failed.\n",__func__);
+ spin_lock_irqsave(&firesat_list_lock, flags);
+ list_del(&firesat->list);
+ spin_unlock_irqrestore(&firesat_list_lock, flags);
+ kfree(firesat);
+ return -EIO;
+ }
+ }
+
+ printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount);
+
+ firesat->subunit = subunit;
+
+ if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type, &firesat->has_ci)) {
+ printk("%s: cannot identify subunit %d\n", __func__, subunit);
+ spin_lock_irqsave(&firesat_list_lock, flags);
+ list_del(&firesat->list);
+ spin_unlock_irqrestore(&firesat_list_lock, flags);
+ kfree(firesat);
+ continue;
+ }
+
+// ----
+ firesat_dvbdev_init(firesat, dev, fe);
+// ----
+ firesats[subunit] = firesat;
+ } // loop for all tuners
+
+ //beta ;-) Disable remote control stuff to avoid crashing
+ //if(firesats[0])
+ // AVCRegisterRemoteControl(firesats[0]);
+
+ return 0;
+}
+
+static int firesat_remove(struct device *dev)
+{
+ struct unit_directory *ud = container_of(dev, struct unit_directory, device);
+ struct dvb_frontend* fe;
+ struct firesat **firesats = ud->device.driver_data;
+ int k;
+ unsigned long flags;
+
+ if (firesats) {
+ for (k = 0; k < 2; k++)
+ if (firesats[k]) {
+ if (firesats[k]->has_ci)
+ firesat_ca_release(firesats[k]);
+
+#if 0
+ if (!(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) {
+ fe->ops = firesat_ops;
+ fe->dvb = firesats[k]->adapter;
+
+ dvb_unregister_frontend(fe);
+ kfree(fe);
+ }
+#endif
+ dvb_net_release(&firesats[k]->dvbnet);
+ firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx);
+ firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend);
+ dvb_dmxdev_release(&firesats[k]->dmxdev);
+ dvb_dmx_release(&firesats[k]->demux);
+ dvb_unregister_adapter(firesats[k]->adapter);
+
+ spin_lock_irqsave(&firesat_list_lock, flags);
+ list_del(&firesats[k]->list);
+ spin_unlock_irqrestore(&firesat_list_lock, flags);
+
+ kfree(firesats[k]->adapter);
+ kfree(firesats[k]->respfrm);
+ kfree(firesats[k]);
+ }
+ kfree(firesats);
+ } else
+ printk("%s: can't get firesat handle\n", __func__);
+
+ printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id);
+
+ return 0;
+}
+
+static int firesat_update(struct unit_directory *ud)
+{
+ struct firesat **firesats = ud->device.driver_data;
+ int k;
+ // loop over subunits
+
+ for (k = 0; k < 2; k++)
+ if (firesats[k]) {
+ firesats[k]->nodeentry = ud->ne;
+
+ if (firesats[k]->isochannel >= 0)
+ try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel);
+ }
+
+ return 0;
+}
+
+static struct hpsb_protocol_driver firesat_driver = {
+
+ .name = "FireSAT",
+ .id_table = firesat_id_table,
+ .update = firesat_update,
+
+ .driver = {
+ //.name and .bus are filled in for us in more recent linux versions
+ //.name = "FireSAT",
+ //.bus = &ieee1394_bus_type,
+ .probe = firesat_probe,
+ .remove = firesat_remove,
+ },
+};
+
+static int __init firesat_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "FireSAT loaded\n");
+ hpsb_register_highlevel(&firesat_highlevel);
+ ret = hpsb_register_protocol(&firesat_driver);
+ if (ret) {
+ printk(KERN_ERR "FireSAT: failed to register protocol\n");
+ hpsb_unregister_highlevel(&firesat_highlevel);
+ return ret;
+ }
+
+ //Crash in this function, just disable RC for the time being...
+ //Don't forget to uncomment in firesat_exit and firesat_probe when you enable this.
+ /*if((ret=firesat_register_rc()))
+ printk("%s: firesat_register_rc return error code %d (ignored)\n", __func__, ret);*/
+
+ return 0;
+}
+
+static void __exit firesat_exit(void)
+{
+ hpsb_unregister_protocol(&firesat_driver);
+ hpsb_unregister_highlevel(&firesat_highlevel);
+ printk(KERN_INFO "FireSAT quit\n");
+}
+
+module_init(firesat_init);
+module_exit(firesat_exit);
+
+MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
+MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
+MODULE_DESCRIPTION("FireSAT DVB Driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("FireSAT DVB");