/*
* FireDTV driver (formerly known as FireSAT)
*
* Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
* Copyright (C) 2008 Ben Backx <ben@bbackx.com>
* Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
*
* 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/bug.h>
#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <ieee1394_transactions.h>
#include <nodemgr.h>
#include "avc_api.h"
#include "firesat.h"
#include "firesat-rc.h"
#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL
static int __avc_write(struct firesat *firesat,
const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm)
{
int err, retry;
if (RspFrm)
firesat->avc_reply_received = false;
for (retry = 0; retry < 6; retry++) {
err = hpsb_node_write(firesat->ud->ne, FCP_COMMAND_REGISTER,
(quadlet_t *)CmdFrm, CmdFrm->length);
if (err) {
firesat->avc_reply_received = true;
dev_err(&firesat->ud->device,
"FCP command write failed\n");
return err;
}
if (!RspFrm)
return 0;
/*
* AV/C specs say that answers should be sent within 150 ms.
* Time out after 200 ms.
*/
if (wait_event_timeout(firesat->avc_wait,
firesat->avc_reply_received,
HZ / 5) != 0) {
memcpy(RspFrm, firesat->respfrm, firesat->resp_length);
RspFrm->length = firesat->resp_length;
return 0;
}
}
dev_err(&firesat->ud->device, "FCP response timed out\n");
return -ETIMEDOUT;
}
static int avc_write(struct firesat *firesat,
const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm)
{
int ret;
if (mutex_lock_interruptible(&firesat->avc_mutex))
return -EINTR;
ret = __avc_write(firesat, CmdFrm, RspFrm);
mutex_unlock(&firesat->avc_mutex);
return ret;
}
int avc_recv(struct firesat *firesat, u8 *data, size_t length)
{
AVCRspFrm *RspFrm = (AVCRspFrm *)data;
if (length >= 8 &&
RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 &&
RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 &&
RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 &&
RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) {
if (RspFrm->resp == CHANGED) {
firesat_handle_rc(firesat,
RspFrm->operand[4] << 8 | RspFrm->operand[5]);
schedule_work(&firesat->remote_ctrl_work);
} else if (RspFrm->resp != INTERIM) {
dev_info(&firesat->ud->device,
"remote control result = %d\n", RspFrm->resp);
}
return 0;
}
if (firesat->avc_reply_received) {
dev_err(&firesat->ud->device,
"received out-of-order AVC response, ignored\n");
return -EIO;
}
memcpy(firesat->respfrm, data, length);
firesat->resp_length = length;
firesat->avc_reply_received = true;
wake_up(&firesat->avc_wait);
return 0;
}
/*
* tuning command for setting the relative LNB frequency
* (not supported by the AVC standard)
*/
static void avc_tuner_tuneqpsk(struct firesat *firesat,
struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm)
{
CmdFrm->opcode = VENDOR;
CmdFrm->operand[0] = SFE_VENDOR_DE_COMPANYID_0;
CmdFrm->operand[1] = SFE_VENDOR_DE_COMPANYID_1;
CmdFrm->operand[2] = SFE_VENDOR_DE_COMPANYID_2;
CmdFrm->operand[3<