/*
* Afatech AF9035 DVB USB driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
* Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "af9035.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static DEFINE_MUTEX(af9035_usb_mutex);
static struct dvb_usb_device_properties af9035_properties[2];
static int af9035_properties_count = ARRAY_SIZE(af9035_properties);
static u16 af9035_checksum(const u8 *buf, size_t len)
{
size_t i;
u16 checksum = 0;
for (i = 1; i < len; i++) {
if (i % 2)
checksum += buf[i] << 8;
else
checksum += buf[i];
}
checksum = ~checksum;
return checksum;
}
static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req)
{
#define BUF_LEN 64
#define REQ_HDR_LEN 4 /* send header size */
#define ACK_HDR_LEN 3 /* rece header size */
#define CHECKSUM_LEN 2
#define USB_TIMEOUT 2000
int ret, msg_len, act_len;
u8 buf[BUF_LEN];
static u8 seq; /* packet sequence number */
u16 checksum, tmp_checksum;
/* buffer overflow check */
if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) ||
req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) {
pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__,
req->wlen, req->rlen);
return -EINVAL;
}
if (mutex_lock_interruptible(&af9035_usb_mutex) < 0)
return -EAGAIN;
buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1;
buf[1] = req->mbox;
buf[2] = req->cmd;
buf[3] = seq++;
if (req->wlen)
memcpy(&buf[4], req->wbuf, req->wlen);
/* calc and add checksum */
checksum = af9035_checksum(buf, buf[0] - 1);
buf[buf[0] - 1] = (checksum >> 8);
buf[buf[0] - 0] = (checksum & 0xff);
msg_len = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN ;
/* send req */
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
&act_len, USB_TIMEOUT);
if (ret < 0)
err("bulk message failed=%d (%d/%d)", ret, msg_len, act_len);
else
if (act_len != msg_len)
ret = -EIO; /* all data is not send */
if (ret < 0)
goto err_mutex_unlock;
/* no ack for those packets */
if (req->cmd == CMD_FW_DL)
goto exit_mutex_unlock;
/* receive ack and data if read req */
msg_len = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN;
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
&act_len, USB_TIMEOUT);
if (ret < 0) {
err("recv bulk message failed=%d", ret);
ret = -EIO;
goto err_mutex_unlock;
}
if (act_len != msg_len) {
err("recv bulk message truncated (%d != %d)", act_len, msg_len);
ret = -EIO;
goto err_mutex_unlock;
}
/* verify checksum */
checksum = af9035_checksum(buf, act_len - 2);
tmp_checksum = (buf[act_len - 2] << 8) | buf[act_len - 1];
if (tmp_checksum != checksum) {
err("%s: command=%02x checksum mismatch (%04x != %04x)",
__func__, req->cmd, tmp_checksum, checksum);
ret = -EIO;
goto err_mutex_unlock;
}
/* check status */
if (buf[2]) {
pr_debug("%s: command=%02x failed fw error=%d\n", __func__,
req->cmd, buf[2]);
ret = -EIO;
goto err_mutex_unlock;
}
/* read request, copy returned data to return buf */
if (req->rlen)
memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen);
err_mutex_unlock:
exit_mutex_unlock:
mutex_unlock(&af9035_usb_mutex);
return ret;
}
/* write multiple registers */
static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
{
u8 wbuf[6 + len];
u8 mbox = (reg >> 16) & 0xff;
struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL };
wbuf[0] = len;
wbuf[1] = 2;
wbuf[2] = 0;
wbuf[3] = 0;
wbuf[
|