linux/drivers/media/dvb/firewire/firedtv-1394.c
<<
>>
Prefs
   1/*
   2 * FireDTV driver (formerly known as FireSAT)
   3 *
   4 * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
   5 * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com>
   6 * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
   7 *
   8 *      This program is free software; you can redistribute it and/or
   9 *      modify it under the terms of the GNU General Public License as
  10 *      published by the Free Software Foundation; either version 2 of
  11 *      the License, or (at your option) any later version.
  12 */
  13
  14#include <linux/device.h>
  15#include <linux/errno.h>
  16#include <linux/kernel.h>
  17#include <linux/list.h>
  18#include <linux/spinlock.h>
  19#include <linux/types.h>
  20
  21#include <dma.h>
  22#include <csr1212.h>
  23#include <highlevel.h>
  24#include <hosts.h>
  25#include <ieee1394.h>
  26#include <iso.h>
  27#include <nodemgr.h>
  28
  29#include "firedtv.h"
  30
  31static LIST_HEAD(node_list);
  32static DEFINE_SPINLOCK(node_list_lock);
  33
  34#define FIREWIRE_HEADER_SIZE    4
  35#define CIP_HEADER_SIZE         8
  36
  37static void rawiso_activity_cb(struct hpsb_iso *iso)
  38{
  39        struct firedtv *f, *fdtv = NULL;
  40        unsigned int i, num, packet;
  41        unsigned char *buf;
  42        unsigned long flags;
  43        int count;
  44
  45        spin_lock_irqsave(&node_list_lock, flags);
  46        list_for_each_entry(f, &node_list, list)
  47                if (f->backend_data == iso) {
  48                        fdtv = f;
  49                        break;
  50                }
  51        spin_unlock_irqrestore(&node_list_lock, flags);
  52
  53        packet = iso->first_packet;
  54        num = hpsb_iso_n_ready(iso);
  55
  56        if (!fdtv) {
  57                dev_err(fdtv->device, "received at unknown iso channel\n");
  58                goto out;
  59        }
  60
  61        for (i = 0; i < num; i++, packet = (packet + 1) % iso->buf_packets) {
  62                buf = dma_region_i(&iso->data_buf, unsigned char,
  63                        iso->infos[packet].offset + CIP_HEADER_SIZE);
  64                count = (iso->infos[packet].len - CIP_HEADER_SIZE) /
  65                        (188 + FIREWIRE_HEADER_SIZE);
  66
  67                /* ignore empty packet */
  68                if (iso->infos[packet].len <= CIP_HEADER_SIZE)
  69                        continue;
  70
  71                while (count--) {
  72                        if (buf[FIREWIRE_HEADER_SIZE] == 0x47)
  73                                dvb_dmx_swfilter_packets(&fdtv->demux,
  74                                                &buf[FIREWIRE_HEADER_SIZE], 1);
  75                        else
  76                                dev_err(fdtv->device,
  77                                        "skipping invalid packet\n");
  78                        buf += 188 + FIREWIRE_HEADER_SIZE;
  79                }
  80        }
  81out:
  82        hpsb_iso_recv_release_packets(iso, num);
  83}
  84
  85static inline struct node_entry *node_of(struct firedtv *fdtv)
  86{
  87        return container_of(fdtv->device, struct unit_directory, device)->ne;
  88}
  89
  90static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg)
  91{
  92        return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data,
  93                              (__force quadlet_t)arg);
  94}
  95
  96static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len)
  97{
  98        return hpsb_node_read(node_of(fdtv), addr, data, len);
  99}
 100
 101static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
 102{
 103        return hpsb_node_write(node_of(fdtv), addr, data, len);
 104}
 105
 106#define FDTV_ISO_BUFFER_PACKETS 256
 107#define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200)
 108
 109static int start_iso(struct firedtv *fdtv)
 110{
 111        struct hpsb_iso *iso_handle;
 112        int ret;
 113
 114        iso_handle = hpsb_iso_recv_init(node_of(fdtv)->host,
 115                                FDTV_ISO_BUFFER_SIZE, FDTV_ISO_BUFFER_PACKETS,
 116                                fdtv->isochannel, HPSB_ISO_DMA_DEFAULT,
 117                                -1, /* stat.config.irq_interval */
 118                                rawiso_activity_cb);
 119        if (iso_handle == NULL) {
 120                dev_err(fdtv->device, "cannot initialize iso receive\n");
 121                return -ENOMEM;
 122        }
 123        fdtv->backend_data = iso_handle;
 124
 125        ret = hpsb_iso_recv_start(iso_handle, -1, -1, 0);
 126        if (ret != 0) {
 127                dev_err(fdtv->device, "cannot start iso receive\n");
 128                hpsb_iso_shutdown(iso_handle);
 129                fdtv->backend_data = NULL;
 130        }
 131        return ret;
 132}
 133
 134static void stop_iso(struct firedtv *fdtv)
 135{
 136        struct hpsb_iso *iso_handle = fdtv->backend_data;
 137
 138        if (iso_handle != NULL) {
 139                hpsb_iso_stop(iso_handle);
 140                hpsb_iso_shutdown(iso_handle);
 141        }
 142        fdtv->backend_data = NULL;
 143}
 144
 145static const struct firedtv_backend fdtv_1394_backend = {
 146        .lock           = node_lock,
 147        .read           = node_read,
 148        .write          = node_write,
 149        .start_iso      = start_iso,
 150        .stop_iso       = stop_iso,
 151};
 152
 153static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
 154                        int cts, u8 *data, size_t length)
 155{
 156        struct firedtv *f, *fdtv = NULL;
 157        unsigned long flags;
 158        int su;
 159
 160        if (length == 0 || (data[0] & 0xf0) != 0)
 161                return;
 162
 163        su = data[1] & 0x7;
 164
 165        spin_lock_irqsave(&node_list_lock, flags);
 166        list_for_each_entry(f, &node_list, list)
 167                if (node_of(f)->host == host &&
 168                    node_of(f)->nodeid == nodeid &&
 169                    (f->subunit == su || (f->subunit == 0 && su == 0x7))) {
 170                        fdtv = f;
 171                        break;
 172                }
 173        spin_unlock_irqrestore(&node_list_lock, flags);
 174
 175        if (fdtv)
 176                avc_recv(fdtv, data, length);
 177}
 178
 179static int node_probe(struct device *dev)
 180{
 181        struct unit_directory *ud =
 182                        container_of(dev, struct unit_directory, device);
 183        struct firedtv *fdtv;
 184        int kv_len, err;
 185        void *kv_str;
 186
 187        kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t);
 188        kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv);
 189
 190        fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len);
 191        if (!fdtv)
 192                return -ENOMEM;
 193
 194        /*
 195         * Work around a bug in udev's path_id script:  Use the fw-host's dev
 196         * instead of the unit directory's dev as parent of the input device.
 197         */
 198        err = fdtv_register_rc(fdtv, dev->parent->parent);
 199        if (err)
 200                goto fail_free;
 201
 202        spin_lock_irq(&node_list_lock);
 203        list_add_tail(&fdtv->list, &node_list);
 204        spin_unlock_irq(&node_list_lock);
 205
 206        err = avc_identify_subunit(fdtv);
 207        if (err)
 208                goto fail;
 209
 210        err = fdtv_dvb_register(fdtv);
 211        if (err)
 212                goto fail;
 213
 214        avc_register_remote_control(fdtv);
 215        return 0;
 216fail:
 217        spin_lock_irq(&node_list_lock);
 218        list_del(&fdtv->list);
 219        spin_unlock_irq(&node_list_lock);
 220        fdtv_unregister_rc(fdtv);
 221fail_free:
 222        kfree(fdtv);
 223        return err;
 224}
 225
 226static int node_remove(struct device *dev)
 227{
 228        struct firedtv *fdtv = dev_get_drvdata(dev);
 229
 230        fdtv_dvb_unregister(fdtv);
 231
 232        spin_lock_irq(&node_list_lock);
 233        list_del(&fdtv->list);
 234        spin_unlock_irq(&node_list_lock);
 235
 236        cancel_work_sync(&fdtv->remote_ctrl_work);
 237        fdtv_unregister_rc(fdtv);
 238
 239        kfree(fdtv);
 240        return 0;
 241}
 242
 243static int node_update(struct unit_directory *ud)
 244{
 245        struct firedtv *fdtv = dev_get_drvdata(&ud->device);
 246
 247        if (fdtv->isochannel >= 0)
 248                cmp_establish_pp_connection(fdtv, fdtv->subunit,
 249                                            fdtv->isochannel);
 250        return 0;
 251}
 252
 253static struct hpsb_protocol_driver fdtv_driver = {
 254        .name           = "firedtv",
 255        .update         = node_update,
 256        .driver         = {
 257                .probe  = node_probe,
 258                .remove = node_remove,
 259        },
 260};
 261
 262static struct hpsb_highlevel fdtv_highlevel = {
 263        .name           = "firedtv",
 264        .fcp_request    = fcp_request,
 265};
 266
 267int __init fdtv_1394_init(struct ieee1394_device_id id_table[])
 268{
 269        int ret;
 270
 271        hpsb_register_highlevel(&fdtv_highlevel);
 272        fdtv_driver.id_table = id_table;
 273        ret = hpsb_register_protocol(&fdtv_driver);
 274        if (ret) {
 275                printk(KERN_ERR "firedtv: failed to register protocol\n");
 276                hpsb_unregister_highlevel(&fdtv_highlevel);
 277        }
 278        return ret;
 279}
 280
 281void __exit fdtv_1394_exit(void)
 282{
 283        hpsb_unregister_protocol(&fdtv_driver);
 284        hpsb_unregister_highlevel(&fdtv_highlevel);
 285}
 286