diff options
Diffstat (limited to 'drivers/media/dvb/firewire/firedtv-1394.c')
-rw-r--r-- | drivers/media/dvb/firewire/firedtv-1394.c | 332 |
1 files changed, 163 insertions, 169 deletions
diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 9536182..4e20765 100644 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -15,162 +15,181 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/list.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/string.h> #include <linux/types.h> -#include <dmxdev.h> -#include <dvb_demux.h> -#include <dvb_frontend.h> -#include <dvbdev.h> - +#include <dma.h> #include <csr1212.h> #include <highlevel.h> #include <hosts.h> -#include <ieee1394_hotplug.h> +#include <ieee1394.h> +#include <iso.h> #include <nodemgr.h> -#include "avc.h" -#include "cmp.h" #include "firedtv.h" -#include "firedtv-ci.h" -#include "firedtv-rc.h" - -#define MATCH_FLAGS IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ - IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION -#define DIGITAL_EVERYWHERE_OUI 0x001287 - -static struct ieee1394_device_id fdtv_id_table[] = { - - { - /* FloppyDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000024, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FloppyDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000025, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FloppyDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000026, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV S/CI and FloppyDTV S2 */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000034, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV T/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000035, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - },{ - /* FireDTV C/CI */ - .match_flags = MATCH_FLAGS, - .vendor_id = DIGITAL_EVERYWHERE_OUI, - .model_id = 0x000036, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, - .version = AVC_SW_VERSION_ENTRY, - }, { } -}; -MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); +static LIST_HEAD(node_list); +static DEFINE_SPINLOCK(node_list_lock); -/* list of all firedtv devices */ -LIST_HEAD(fdtv_list); -DEFINE_SPINLOCK(fdtv_list_lock); +#define FIREWIRE_HEADER_SIZE 4 +#define CIP_HEADER_SIZE 8 -static void fcp_request(struct hpsb_host *host, - int nodeid, - int direction, - int cts, - u8 *data, - size_t length) +static void rawiso_activity_cb(struct hpsb_iso *iso) { - struct firedtv *fdtv = NULL; - struct firedtv *fdtv_entry; + struct firedtv *f, *fdtv = NULL; + unsigned int i, num, packet; + unsigned char *buf; unsigned long flags; + int count; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (f->backend_data == iso) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); + + packet = iso->first_packet; + num = hpsb_iso_n_ready(iso); + + if (!fdtv) { + dev_err(fdtv->device, "received at unknown iso channel\n"); + goto out; + } - if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { - - spin_lock_irqsave(&fdtv_list_lock, flags); - list_for_each_entry(fdtv_entry,&fdtv_list,list) { - if (fdtv_entry->ud->ne->host == host && - fdtv_entry->ud->ne->nodeid == nodeid && - (fdtv_entry->subunit == (data[1]&0x7) || - (fdtv_entry->subunit == 0 && - (data[1]&0x7) == 0x7))) { - fdtv=fdtv_entry; - break; - } + for (i = 0; i < num; i++, packet = (packet + 1) % iso->buf_packets) { + buf = dma_region_i(&iso->data_buf, unsigned char, + iso->infos[packet].offset + CIP_HEADER_SIZE); + count = (iso->infos[packet].len - CIP_HEADER_SIZE) / + (188 + FIREWIRE_HEADER_SIZE); + + /* ignore empty packet */ + if (iso->infos[packet].len <= CIP_HEADER_SIZE) + continue; + + while (count--) { + if (buf[FIREWIRE_HEADER_SIZE] == 0x47) + dvb_dmx_swfilter_packets(&fdtv->demux, + &buf[FIREWIRE_HEADER_SIZE], 1); + else + dev_err(fdtv->device, + "skipping invalid packet\n"); + buf += 188 + FIREWIRE_HEADER_SIZE; } - spin_unlock_irqrestore(&fdtv_list_lock, flags); + } +out: + hpsb_iso_recv_release_packets(iso, num); +} + +static inline struct node_entry *node_of(struct firedtv *fdtv) +{ + return container_of(fdtv->device, struct unit_directory, device)->ne; +} + +static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg) +{ + return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data, + (__force quadlet_t)arg); +} + +static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_read(node_of(fdtv), addr, data, len); +} + +static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_write(node_of(fdtv), addr, data, len); +} + +#define FDTV_ISO_BUFFER_PACKETS 256 +#define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200) + +static int start_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle; + int ret; + + iso_handle = hpsb_iso_recv_init(node_of(fdtv)->host, + FDTV_ISO_BUFFER_SIZE, FDTV_ISO_BUFFER_PACKETS, + fdtv->isochannel, HPSB_ISO_DMA_DEFAULT, + -1, /* stat.config.irq_interval */ + rawiso_activity_cb); + if (iso_handle == NULL) { + dev_err(fdtv->device, "cannot initialize iso receive\n"); + return -ENOMEM; + } + fdtv->backend_data = iso_handle; + + ret = hpsb_iso_recv_start(iso_handle, -1, -1, 0); + if (ret != 0) { + dev_err(fdtv->device, "cannot start iso receive\n"); + hpsb_iso_shutdown(iso_handle); + fdtv->backend_data = NULL; + } + return ret; +} + +static void stop_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle = fdtv->backend_data; - if (fdtv) - avc_recv(fdtv, data, length); + if (iso_handle != NULL) { + hpsb_iso_stop(iso_handle); + hpsb_iso_shutdown(iso_handle); } + fdtv->backend_data = NULL; } -const char *fdtv_model_names[] = { - [FIREDTV_UNKNOWN] = "unknown type", - [FIREDTV_DVB_S] = "FireDTV S/CI", - [FIREDTV_DVB_C] = "FireDTV C/CI", - [FIREDTV_DVB_T] = "FireDTV T/CI", - [FIREDTV_DVB_S2] = "FireDTV S2 ", +static const struct firedtv_backend fdtv_1394_backend = { + .lock = node_lock, + .read = node_read, + .write = node_write, + .start_iso = start_iso, + .stop_iso = stop_iso, }; -static int fdtv_probe(struct device *dev) +static void fcp_request(struct hpsb_host *host, int nodeid, int direction, + int cts, u8 *data, size_t length) { - struct unit_directory *ud = - container_of(dev, struct unit_directory, device); - struct firedtv *fdtv; + struct firedtv *f, *fdtv = NULL; unsigned long flags; - int kv_len; - void *kv_str; - int i; - int err = -ENOMEM; + int su; - fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); - if (!fdtv) - return -ENOMEM; + if (length == 0 || (data[0] & 0xf0) != 0) + return; - dev->driver_data = fdtv; - fdtv->ud = ud; - fdtv->subunit = 0; - fdtv->isochannel = -1; - fdtv->tone = 0xff; - fdtv->voltage = 0xff; + su = data[1] & 0x7; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (node_of(f)->host == host && + node_of(f)->nodeid == nodeid && + (f->subunit == su || (f->subunit == 0 && su == 0x7))) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); - mutex_init(&fdtv->avc_mutex); - init_waitqueue_head(&fdtv->avc_wait); - fdtv->avc_reply_received = true; - mutex_init(&fdtv->demux_mutex); - INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); + if (fdtv) + avc_recv(fdtv, data, length); +} + +static int node_probe(struct device *dev) +{ + struct unit_directory *ud = + container_of(dev, struct unit_directory, device); + struct firedtv *fdtv; + int kv_len, err; + void *kv_str; - /* Reading device model from ROM */ kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); - for (i = ARRAY_SIZE(fdtv_model_names); --i;) - if (strlen(fdtv_model_names[i]) <= kv_len && - strncmp(kv_str, fdtv_model_names[i], kv_len) == 0) - break; - fdtv->type = i; + + fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len); + if (!fdtv) + return -ENOMEM; /* * Work around a bug in udev's path_id script: Use the fw-host's dev @@ -180,50 +199,39 @@ static int fdtv_probe(struct device *dev) if (err) goto fail_free; - INIT_LIST_HEAD(&fdtv->list); - spin_lock_irqsave(&fdtv_list_lock, flags); - list_add_tail(&fdtv->list, &fdtv_list); - spin_unlock_irqrestore(&fdtv_list_lock, flags); + spin_lock_irq(&node_list_lock); + list_add_tail(&fdtv->list, &node_list); + spin_unlock_irq(&node_list_lock); err = avc_identify_subunit(fdtv); if (err) goto fail; - err = fdtv_dvbdev_init(fdtv, dev); + err = fdtv_dvb_register(fdtv); if (err) goto fail; avc_register_remote_control(fdtv); return 0; - fail: - spin_lock_irqsave(&fdtv_list_lock, flags); + spin_lock_irq(&node_list_lock); list_del(&fdtv->list); - spin_unlock_irqrestore(&fdtv_list_lock, flags); + spin_unlock_irq(&node_list_lock); fdtv_unregister_rc(fdtv); fail_free: kfree(fdtv); return err; } -static int fdtv_remove(struct device *dev) +static int node_remove(struct device *dev) { struct firedtv *fdtv = dev->driver_data; - unsigned long flags; - fdtv_ca_release(fdtv); - dvb_unregister_frontend(&fdtv->fe); - dvb_net_release(&fdtv->dvbnet); - fdtv->demux.dmx.close(&fdtv->demux.dmx); - fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, - &fdtv->frontend); - dvb_dmxdev_release(&fdtv->dmxdev); - dvb_dmx_release(&fdtv->demux); - dvb_unregister_adapter(&fdtv->adapter); - - spin_lock_irqsave(&fdtv_list_lock, flags); + fdtv_dvb_unregister(fdtv); + + spin_lock_irq(&node_list_lock); list_del(&fdtv->list); - spin_unlock_irqrestore(&fdtv_list_lock, flags); + spin_unlock_irq(&node_list_lock); cancel_work_sync(&fdtv->remote_ctrl_work); fdtv_unregister_rc(fdtv); @@ -232,7 +240,7 @@ static int fdtv_remove(struct device *dev) return 0; } -static int fdtv_update(struct unit_directory *ud) +static int node_update(struct unit_directory *ud) { struct firedtv *fdtv = ud->device.driver_data; @@ -243,17 +251,11 @@ static int fdtv_update(struct unit_directory *ud) } static struct hpsb_protocol_driver fdtv_driver = { - .name = "firedtv", - .id_table = fdtv_id_table, - .update = fdtv_update, - + .update = node_update, .driver = { - //.name and .bus are filled in for us in more recent linux versions - //.name = "FireDTV", - //.bus = &ieee1394_bus_type, - .probe = fdtv_probe, - .remove = fdtv_remove, + .probe = node_probe, + .remove = node_remove, }, }; @@ -262,11 +264,12 @@ static struct hpsb_highlevel fdtv_highlevel = { .fcp_request = fcp_request, }; -static int __init fdtv_init(void) +int __init fdtv_1394_init(struct ieee1394_device_id id_table[]) { int ret; hpsb_register_highlevel(&fdtv_highlevel); + fdtv_driver.id_table = id_table; ret = hpsb_register_protocol(&fdtv_driver); if (ret) { printk(KERN_ERR "firedtv: failed to register protocol\n"); @@ -275,17 +278,8 @@ static int __init fdtv_init(void) return ret; } -static void __exit fdtv_exit(void) +void __exit fdtv_1394_exit(void) { hpsb_unregister_protocol(&fdtv_driver); hpsb_unregister_highlevel(&fdtv_highlevel); } - -module_init(fdtv_init); -module_exit(fdtv_exit); - -MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); -MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); -MODULE_DESCRIPTION("FireDTV DVB Driver"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("FireDTV DVB"); |