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