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