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 25 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 26 27 static 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 } 74 error: 75 kfree(usb_buf); 76 return ret; 77 } 78 79 static 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 */ 96 static 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 126 static u32 au6610_i2c_func(struct i2c_adapter *adapter) 127 { 128 return I2C_FUNC_I2C; 129 } 130 131 static struct i2c_algorithm au6610_i2c_algo = { 132 .master_xfer = au6610_i2c_xfer, 133 .functionality = au6610_i2c_func, 134 }; 135 136 /* Callbacks for DVB USB */ 137 static struct zl10353_config au6610_zl10353_config = { 138 .demod_address = 0x0f, 139 .no_tuner = 1, 140 .parallel_ts = 1, 141 }; 142 143 static 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 153 static struct qt1010_config au6610_qt1010_config = { 154 .i2c_address = 0x62 155 }; 156 157 static 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 164 static 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 171 static 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 189 static 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 }; 194 MODULE_DEVICE_TABLE(usb, au6610_id_table); 195 196 static 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 208 module_usb_driver(au6610_driver); 209 210 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 211 MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0"); 212 MODULE_VERSION("0.1"); 213 MODULE_LICENSE("GPL"); 214