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