Commit | Line | Data |
---|---|---|
5decdd27 AP |
1 | /* DVB USB compliant linux driver for Sigmatek DVB-110 DVB-T USB2.0 receiver |
2 | * | |
4c7e3ea9 | 3 | * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> |
5decdd27 AP |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the Free | |
7 | * Software Foundation, version 2. | |
8 | * | |
9 | * see Documentation/dvb/README.dvb-usb for more information | |
10 | */ | |
11 | ||
12 | #include "au6610.h" | |
13 | ||
14 | #include "zl10353.h" | |
15 | #include "qt1010.h" | |
16 | ||
17 | /* debug */ | |
18 | static int dvb_usb_au6610_debug; | |
19 | module_param_named(debug, dvb_usb_au6610_debug, int, 0644); | |
20 | MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); | |
21 | ||
5decdd27 AP |
22 | static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, |
23 | u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) | |
24 | { | |
25 | int ret; | |
26 | u16 index; | |
38d0629f MK |
27 | u8 usb_buf[6]; /* enough for all known requests, |
28 | read returns 5 and write 6 bytes */ | |
5decdd27 AP |
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 | warn("wlen = %x, aborting.", wlen); | |
39 | return -EINVAL; | |
40 | } | |
41 | ||
42 | ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, | |
26a154c3 | 43 | USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf, |
5decdd27 AP |
44 | sizeof(usb_buf), AU6610_USB_TIMEOUT); |
45 | ||
46 | if (ret < 0) | |
47 | return ret; | |
48 | ||
49 | switch (operation) { | |
50 | case AU6610_REQ_I2C_READ: | |
51 | case AU6610_REQ_USB_READ: | |
52 | /* requested value is always 5th byte in buffer */ | |
53 | rbuf[0] = usb_buf[4]; | |
54 | } | |
55 | ||
56 | return ret; | |
57 | } | |
58 | ||
38d0629f MK |
59 | static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr, |
60 | u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) | |
5decdd27 AP |
61 | { |
62 | u8 request; | |
63 | u8 wo = (rbuf == NULL || rlen == 0); /* write-only */ | |
64 | ||
65 | if (wo) { | |
66 | request = AU6610_REQ_I2C_WRITE; | |
67 | } else { /* rw */ | |
68 | request = AU6610_REQ_I2C_READ; | |
69 | } | |
70 | ||
71 | return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen); | |
72 | } | |
73 | ||
74 | ||
75 | /* I2C */ | |
38d0629f MK |
76 | static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], |
77 | int num) | |
5decdd27 AP |
78 | { |
79 | struct dvb_usb_device *d = i2c_get_adapdata(adap); | |
80 | int i; | |
81 | ||
5decdd27 AP |
82 | if (num > 2) |
83 | return -EINVAL; | |
84 | ||
c80296d7 RK |
85 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) |
86 | return -EAGAIN; | |
87 | ||
5decdd27 AP |
88 | for (i = 0; i < num; i++) { |
89 | /* write/read request */ | |
90 | if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { | |
91 | if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, | |
38d0629f MK |
92 | msg[i].len, msg[i+1].buf, |
93 | msg[i+1].len) < 0) | |
5decdd27 AP |
94 | break; |
95 | i++; | |
96 | } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, | |
97 | msg[i].len, NULL, 0) < 0) | |
98 | break; | |
99 | } | |
100 | ||
101 | mutex_unlock(&d->i2c_mutex); | |
102 | return i; | |
103 | } | |
104 | ||
105 | ||
106 | static u32 au6610_i2c_func(struct i2c_adapter *adapter) | |
107 | { | |
108 | return I2C_FUNC_I2C; | |
109 | } | |
110 | ||
111 | static struct i2c_algorithm au6610_i2c_algo = { | |
112 | .master_xfer = au6610_i2c_xfer, | |
113 | .functionality = au6610_i2c_func, | |
114 | }; | |
115 | ||
116 | /* Callbacks for DVB USB */ | |
117 | static int au6610_identify_state(struct usb_device *udev, | |
118 | struct dvb_usb_device_properties *props, | |
119 | struct dvb_usb_device_description **desc, | |
120 | int *cold) | |
121 | { | |
122 | *cold = 0; | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static struct zl10353_config au6610_zl10353_config = { | |
26a154c3 | 127 | .demod_address = 0x0f, |
5decdd27 AP |
128 | .no_tuner = 1, |
129 | .parallel_ts = 1, | |
130 | }; | |
131 | ||
5decdd27 AP |
132 | static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap) |
133 | { | |
134 | if ((adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config, | |
135 | &adap->dev->i2c_adap)) != NULL) { | |
136 | return 0; | |
137 | } | |
138 | ||
139 | return -EIO; | |
140 | } | |
141 | ||
4c7e3ea9 | 142 | static struct qt1010_config au6610_qt1010_config = { |
26a154c3 | 143 | .i2c_address = 0x62 |
4c7e3ea9 AP |
144 | }; |
145 | ||
146 | static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap) | |
147 | { | |
4c7e3ea9 AP |
148 | return dvb_attach(qt1010_attach, |
149 | adap->fe, &adap->dev->i2c_adap, | |
150 | &au6610_qt1010_config) == NULL ? -ENODEV : 0; | |
151 | } | |
152 | ||
5decdd27 AP |
153 | /* DVB USB Driver stuff */ |
154 | static struct dvb_usb_device_properties au6610_properties; | |
155 | ||
38d0629f MK |
156 | static int au6610_probe(struct usb_interface *intf, |
157 | const struct usb_device_id *id) | |
5decdd27 AP |
158 | { |
159 | struct dvb_usb_device *d; | |
160 | struct usb_host_interface *alt; | |
161 | int ret; | |
162 | ||
163 | if (intf->num_altsetting < AU6610_ALTSETTING_COUNT) | |
164 | return -ENODEV; | |
165 | ||
166 | if ((ret = dvb_usb_device_init(intf, &au6610_properties, THIS_MODULE, &d)) == 0) { | |
167 | alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING); | |
168 | ||
169 | if (alt == NULL) { | |
170 | deb_rc("no alt found!\n"); | |
171 | return -ENODEV; | |
172 | } | |
173 | ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, | |
174 | alt->desc.bAlternateSetting); | |
175 | } | |
176 | ||
177 | return ret; | |
178 | } | |
179 | ||
180 | ||
181 | static struct usb_device_id au6610_table [] = { | |
182 | { USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) }, | |
183 | { } /* Terminating entry */ | |
184 | }; | |
185 | MODULE_DEVICE_TABLE (usb, au6610_table); | |
186 | ||
187 | static struct dvb_usb_device_properties au6610_properties = { | |
188 | .caps = DVB_USB_IS_AN_I2C_ADAPTER, | |
189 | .usb_ctrl = DEVICE_SPECIFIC, | |
190 | .size_of_priv = 0, | |
191 | .identify_state = au6610_identify_state, | |
192 | .num_adapters = 1, | |
193 | .adapter = { | |
194 | { | |
195 | .frontend_attach = au6610_zl10353_frontend_attach, | |
4c7e3ea9 | 196 | .tuner_attach = au6610_qt1010_tuner_attach, |
5decdd27 AP |
197 | |
198 | .stream = { | |
199 | .type = USB_ISOC, | |
200 | .count = 5, | |
201 | .endpoint = 0x82, | |
202 | .u = { | |
203 | .isoc = { | |
204 | .framesperurb = 40, | |
205 | .framesize = 942, /* maximum packet size */ | |
206 | .interval = 1.25, /* 125 us */ | |
207 | } | |
208 | } | |
209 | }, | |
210 | } | |
211 | }, | |
212 | .i2c_algo = &au6610_i2c_algo, | |
213 | .num_device_descs = 1, | |
214 | .devices = { | |
215 | { | |
216 | "Sigmatek DVB-110 DVB-T USB2.0", | |
217 | { &au6610_table[0], NULL }, | |
218 | { NULL }, | |
219 | }, | |
220 | } | |
221 | }; | |
222 | ||
223 | static struct usb_driver au6610_driver = { | |
224 | .name = "dvb_usb_au6610", | |
225 | .probe = au6610_probe, | |
226 | .disconnect = dvb_usb_device_exit, | |
227 | .id_table = au6610_table, | |
228 | }; | |
229 | ||
230 | /* module stuff */ | |
231 | static int __init au6610_module_init(void) | |
232 | { | |
233 | int ret; | |
234 | ||
235 | if ((ret = usb_register(&au6610_driver))) { | |
236 | err("usb_register failed. Error number %d", ret); | |
237 | return ret; | |
238 | } | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
243 | static void __exit au6610_module_exit(void) | |
244 | { | |
245 | /* deregister this driver from the USB subsystem */ | |
246 | usb_deregister(&au6610_driver); | |
247 | } | |
248 | ||
249 | module_init (au6610_module_init); | |
250 | module_exit (au6610_module_exit); | |
251 | ||
252 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | |
253 | MODULE_DESCRIPTION("Driver Sigmatek DVB-110 DVB-T USB2.0 / AU6610"); | |
254 | MODULE_VERSION("0.1"); | |
255 | MODULE_LICENSE("GPL"); |