469 lines
12 KiB
C
469 lines
12 KiB
C
|
/*
|
||
|
* 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");
|