linux/drivers/media/usb/dvb-usb-v2/au6610.c
<<
>>
Prefs
   1/*
   2 * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
   3 *
   4 * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
   5 *
   6 *    This program is free software; you can redistribute it and/or modify
   7 *    it under the terms of the GNU General Public License as published by
   8 *    the Free Software Foundation; either version 2 of the License, or
   9 *    (at your option) any later version.
  10 *
  11 *    This program is distributed in the hope that it will be useful,
  12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *    GNU General Public License for more details.
  15 *
  16 *    You should have received a copy of the GNU General Public License
  17 *    along with this program; if not, write to the Free Software
  18 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19 */
  20
  21#include "au6610.h"
  22#include "zl10353.h"
  23#include "qt1010.h"
  24
  25DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
  26
  27static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
  28                          u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
  29{
  30        int ret;
  31        u16 index;
  32        u8 *usb_buf;
  33
  34        /*
  35         * allocate enough for all known requests,
  36         * read returns 5 and write 6 bytes
  37         */
  38        usb_buf = kmalloc(6, GFP_KERNEL);
  39        if (!usb_buf)
  40                return -ENOMEM;
  41
  42        switch (wlen) {
  43        case 1:
  44                index = wbuf[0] << 8;
  45                break;
  46        case 2:
  47                index = wbuf[0] << 8;
  48                index += wbuf[1];
  49                break;
  50        default:
  51                dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n",
  52                                KBUILD_MODNAME, wlen);
  53                ret = -EINVAL;
  54                goto error;
  55        }
  56
  57        ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
  58                              USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index,
  59                              usb_buf, 6, AU6610_USB_TIMEOUT);
  60
  61        dvb_usb_dbg_usb_control_msg(d->udev, operation,
  62                        (USB_TYPE_VENDOR|USB_DIR_IN), addr << 1, index,
  63                        usb_buf, 6);
  64
  65        if (ret < 0)
  66                goto error;
  67
  68        switch (operation) {
  69        case AU6610_REQ_I2C_READ:
  70        case AU6610_REQ_USB_READ:
  71                /* requested value is always 5th byte in buffer */
  72                rbuf[0] = usb_buf[4];
  73        }
  74error:
  75        kfree(usb_buf);
  76        return ret;
  77}
  78
  79static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr,
  80                          u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
  81{
  82        u8 request;
  83        u8 wo = (rbuf == NULL || rlen == 0); /* write-only */
  84
  85        if (wo) {
  86                request = AU6610_REQ_I2C_WRITE;
  87        } else { /* rw */
  88                request = AU6610_REQ_I2C_READ;
  89        }
  90
  91        return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen);
  92}
  93
  94
  95/* I2C */
  96static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
  97                           int num)
  98{
  99        struct dvb_usb_device *d = i2c_get_adapdata(adap);
 100        int i;
 101
 102        if (num > 2)
 103                return -EINVAL;
 104
 105        if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
 106                return -EAGAIN;
 107
 108        for (i = 0; i < num; i++) {
 109                /* write/read request */
 110                if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
 111                        if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
 112                                           msg[i].len, msg[i+1].buf,
 113                                           msg[i+1].len) < 0)
 114                                break;
 115                        i++;
 116                } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
 117                                               msg[i].len, NULL, 0) < 0)
 118                                break;
 119        }
 120
 121        mutex_unlock(&d->i2c_mutex);
 122        return i;
 123}
 124
 125
 126static u32 au6610_i2c_func(struct i2c_adapter *adapter)
 127{
 128        return I2C_FUNC_I2C;
 129}
 130
 131static struct i2c_algorithm au6610_i2c_algo = {
 132        .master_xfer   = au6610_i2c_xfer,
 133        .functionality = au6610_i2c_func,
 134};
 135
 136/* Callbacks for DVB USB */
 137static struct zl10353_config au6610_zl10353_config = {
 138        .demod_address = 0x0f,
 139        .no_tuner = 1,
 140        .parallel_ts = 1,
 141};
 142
 143static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
 144{
 145        adap->fe[0] = dvb_attach(zl10353_attach, &au6610_zl10353_config,
 146                        &adap_to_d(adap)->i2c_adap);
 147        if (adap->fe[0] == NULL)
 148                return -ENODEV;
 149
 150        return 0;
 151}
 152
 153static struct qt1010_config au6610_qt1010_config = {
 154        .i2c_address = 0x62
 155};
 156
 157static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
 158{
 159        return dvb_attach(qt1010_attach, adap->fe[0],
 160                        &adap_to_d(adap)->i2c_adap,
 161                        &au6610_qt1010_config) == NULL ? -ENODEV : 0;
 162}
 163
 164static int au6610_init(struct dvb_usb_device *d)
 165{
 166        /* TODO: this functionality belongs likely to the streaming control */
 167        /* bInterfaceNumber 0, bAlternateSetting 5 */
 168        return usb_set_interface(d->udev, 0, 5);
 169}
 170
 171static struct dvb_usb_device_properties au6610_props = {
 172        .driver_name = KBUILD_MODNAME,
 173        .owner = THIS_MODULE,
 174        .adapter_nr = adapter_nr,
 175
 176        .i2c_algo = &au6610_i2c_algo,
 177        .frontend_attach = au6610_zl10353_frontend_attach,
 178        .tuner_attach = au6610_qt1010_tuner_attach,
 179        .init = au6610_init,
 180
 181        .num_adapters = 1,
 182        .adapter = {
 183                {
 184                        .stream = DVB_USB_STREAM_ISOC(0x82, 5, 40, 942, 1),
 185                },
 186        },
 187};
 188
 189static const struct usb_device_id au6610_id_table[] = {
 190        { DVB_USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110,
 191                &au6610_props, "Sigmatek DVB-110", NULL) },
 192        { }
 193};
 194MODULE_DEVICE_TABLE(usb, au6610_id_table);
 195
 196static struct usb_driver au6610_driver = {
 197        .name = KBUILD_MODNAME,
 198        .id_table = au6610_id_table,
 199        .probe = dvb_usbv2_probe,
 200        .disconnect = dvb_usbv2_disconnect,
 201        .suspend = dvb_usbv2_suspend,
 202        .resume = dvb_usbv2_resume,
 203        .reset_resume = dvb_usbv2_reset_resume,
 204        .no_dynamic_id = 1,
 205        .soft_unbind = 1,
 206};
 207
 208module_usb_driver(au6610_driver);
 209
 210MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 211MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0");
 212MODULE_VERSION("0.1");
 213MODULE_LICENSE("GPL");
 214