Commit | Line | Data |
---|---|---|
f0c3a2ca CL |
1 | /* DVB USB compliant linux driver for GL861 USB2.0 devices. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify it | |
fb49558c AP |
4 | * under the terms of the GNU General Public License as published by the |
5 | * Free Software Foundation, version 2. | |
f0c3a2ca CL |
6 | * |
7 | * see Documentation/dvb/README.dvb-usb for more information | |
8 | */ | |
9 | #include "gl861.h" | |
10 | ||
11 | #include "zl10353.h" | |
12 | #include "qt1010.h" | |
13 | ||
14 | /* debug */ | |
3721929e | 15 | static int dvb_usb_gl861_debug; |
fb49558c AP |
16 | module_param_named(debug, dvb_usb_gl861_debug, int, 0644); |
17 | MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." | |
18 | DVB_USB_DEBUG_STATUS); | |
78e92006 JG |
19 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
20 | ||
f0c3a2ca | 21 | static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, |
1f78867b | 22 | u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) |
f0c3a2ca CL |
23 | { |
24 | u16 index; | |
26a154c3 | 25 | u16 value = addr << (8 + 1); |
f0c3a2ca CL |
26 | int wo = (rbuf == NULL || rlen == 0); /* write-only */ |
27 | u8 req, type; | |
28 | ||
29 | if (wo) { | |
30 | req = GL861_REQ_I2C_WRITE; | |
31 | type = GL861_WRITE; | |
32 | } else { /* rw */ | |
33 | req = GL861_REQ_I2C_READ; | |
34 | type = GL861_READ; | |
35 | } | |
36 | ||
37 | switch (wlen) { | |
1f78867b MK |
38 | case 1: |
39 | index = wbuf[0]; | |
40 | break; | |
41 | case 2: | |
42 | index = wbuf[0]; | |
43 | value = value + wbuf[1]; | |
44 | break; | |
45 | default: | |
46 | warn("wlen = %x, aborting.", wlen); | |
47 | return -EINVAL; | |
f0c3a2ca CL |
48 | } |
49 | ||
1a78db82 | 50 | msleep(1); /* avoid I2C errors */ |
f56ebe16 | 51 | |
f0c3a2ca | 52 | return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, |
1f78867b | 53 | value, index, rbuf, rlen, 2000); |
f0c3a2ca CL |
54 | } |
55 | ||
56 | /* I2C */ | |
57 | static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], | |
1f78867b | 58 | int num) |
f0c3a2ca CL |
59 | { |
60 | struct dvb_usb_device *d = i2c_get_adapdata(adap); | |
61 | int i; | |
62 | ||
f0c3a2ca CL |
63 | if (num > 2) |
64 | return -EINVAL; | |
65 | ||
c80296d7 RK |
66 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) |
67 | return -EAGAIN; | |
68 | ||
f0c3a2ca CL |
69 | for (i = 0; i < num; i++) { |
70 | /* write/read request */ | |
71 | if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { | |
72 | if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, | |
fb49558c | 73 | msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) |
f0c3a2ca CL |
74 | break; |
75 | i++; | |
76 | } else | |
77 | if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, | |
1f78867b | 78 | msg[i].len, NULL, 0) < 0) |
f0c3a2ca CL |
79 | break; |
80 | } | |
81 | ||
82 | mutex_unlock(&d->i2c_mutex); | |
83 | return i; | |
84 | } | |
85 | ||
86 | static u32 gl861_i2c_func(struct i2c_adapter *adapter) | |
87 | { | |
88 | return I2C_FUNC_I2C; | |
89 | } | |
90 | ||
91 | static struct i2c_algorithm gl861_i2c_algo = { | |
92 | .master_xfer = gl861_i2c_xfer, | |
93 | .functionality = gl861_i2c_func, | |
94 | }; | |
95 | ||
96 | /* Callbacks for DVB USB */ | |
f0c3a2ca | 97 | static struct zl10353_config gl861_zl10353_config = { |
26a154c3 | 98 | .demod_address = 0x0f, |
f0c3a2ca | 99 | .no_tuner = 1, |
4131fd4f | 100 | .parallel_ts = 1, |
f0c3a2ca CL |
101 | }; |
102 | ||
103 | static int gl861_frontend_attach(struct dvb_usb_adapter *adap) | |
104 | { | |
f0c3a2ca | 105 | |
fb49558c AP |
106 | adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config, |
107 | &adap->dev->i2c_adap); | |
108 | if (adap->fe == NULL) | |
109 | return -EIO; | |
110 | ||
111 | return 0; | |
f0c3a2ca CL |
112 | } |
113 | ||
4c7e3ea9 | 114 | static struct qt1010_config gl861_qt1010_config = { |
26a154c3 | 115 | .i2c_address = 0x62 |
4c7e3ea9 AP |
116 | }; |
117 | ||
118 | static int gl861_tuner_attach(struct dvb_usb_adapter *adap) | |
119 | { | |
4c7e3ea9 AP |
120 | return dvb_attach(qt1010_attach, |
121 | adap->fe, &adap->dev->i2c_adap, | |
122 | &gl861_qt1010_config) == NULL ? -ENODEV : 0; | |
123 | } | |
124 | ||
f0c3a2ca CL |
125 | /* DVB USB Driver stuff */ |
126 | static struct dvb_usb_device_properties gl861_properties; | |
127 | ||
128 | static int gl861_probe(struct usb_interface *intf, | |
1f78867b | 129 | const struct usb_device_id *id) |
f0c3a2ca CL |
130 | { |
131 | struct dvb_usb_device *d; | |
132 | struct usb_host_interface *alt; | |
133 | int ret; | |
134 | ||
135 | if (intf->num_altsetting < 2) | |
136 | return -ENODEV; | |
137 | ||
78e92006 JG |
138 | ret = dvb_usb_device_init(intf, &gl861_properties, THIS_MODULE, &d, |
139 | adapter_nr); | |
140 | if (ret == 0) { | |
f0c3a2ca CL |
141 | alt = usb_altnum_to_altsetting(intf, 0); |
142 | ||
143 | if (alt == NULL) { | |
144 | deb_rc("not alt found!\n"); | |
145 | return -ENODEV; | |
146 | } | |
147 | ||
148 | ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, | |
1f78867b | 149 | alt->desc.bAlternateSetting); |
f0c3a2ca CL |
150 | } |
151 | ||
152 | return ret; | |
153 | } | |
154 | ||
155 | static struct usb_device_id gl861_table [] = { | |
6f7880f0 | 156 | { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801) }, |
4919c492 | 157 | { USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU) }, |
1f78867b | 158 | { } /* Terminating entry */ |
f0c3a2ca | 159 | }; |
fb49558c | 160 | MODULE_DEVICE_TABLE(usb, gl861_table); |
f0c3a2ca CL |
161 | |
162 | static struct dvb_usb_device_properties gl861_properties = { | |
b3b2b8b5 | 163 | .caps = DVB_USB_IS_AN_I2C_ADAPTER, |
f0c3a2ca CL |
164 | .usb_ctrl = DEVICE_SPECIFIC, |
165 | ||
166 | .size_of_priv = 0, | |
167 | ||
f0c3a2ca CL |
168 | .num_adapters = 1, |
169 | .adapter = {{ | |
f0c3a2ca CL |
170 | |
171 | .frontend_attach = gl861_frontend_attach, | |
4c7e3ea9 | 172 | .tuner_attach = gl861_tuner_attach, |
f0c3a2ca CL |
173 | |
174 | .stream = { | |
175 | .type = USB_BULK, | |
176 | .count = 7, | |
177 | .endpoint = 0x81, | |
178 | .u = { | |
179 | .bulk = { | |
180 | .buffersize = 512, | |
181 | } | |
182 | } | |
183 | }, | |
fb49558c | 184 | } }, |
f0c3a2ca CL |
185 | .i2c_algo = &gl861_i2c_algo, |
186 | ||
4919c492 | 187 | .num_device_descs = 2, |
f0c3a2ca | 188 | .devices = { |
ea3a13b7 AP |
189 | { |
190 | .name = "MSI Mega Sky 55801 DVB-T USB2.0", | |
191 | .cold_ids = { NULL }, | |
192 | .warm_ids = { &gl861_table[0], NULL }, | |
f0c3a2ca | 193 | }, |
ea3a13b7 AP |
194 | { |
195 | .name = "A-LINK DTU DVB-T USB2.0", | |
196 | .cold_ids = { NULL }, | |
197 | .warm_ids = { &gl861_table[1], NULL }, | |
4919c492 | 198 | }, |
f0c3a2ca CL |
199 | } |
200 | }; | |
201 | ||
202 | static struct usb_driver gl861_driver = { | |
05ec6cc8 | 203 | .name = "dvb_usb_gl861", |
f0c3a2ca CL |
204 | .probe = gl861_probe, |
205 | .disconnect = dvb_usb_device_exit, | |
206 | .id_table = gl861_table, | |
207 | }; | |
208 | ||
209 | /* module stuff */ | |
210 | static int __init gl861_module_init(void) | |
211 | { | |
212 | int ret; | |
213 | ||
fb49558c AP |
214 | ret = usb_register(&gl861_driver); |
215 | if (ret) | |
f0c3a2ca | 216 | err("usb_register failed. Error number %d", ret); |
f0c3a2ca | 217 | |
fb49558c | 218 | return ret; |
f0c3a2ca CL |
219 | } |
220 | ||
221 | static void __exit gl861_module_exit(void) | |
222 | { | |
223 | /* deregister this driver from the USB subsystem */ | |
224 | usb_deregister(&gl861_driver); | |
225 | } | |
226 | ||
fb49558c AP |
227 | module_init(gl861_module_init); |
228 | module_exit(gl861_module_exit); | |
f0c3a2ca | 229 | |
947af8fd | 230 | MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>"); |
f0c3a2ca | 231 | MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861"); |
4131fd4f | 232 | MODULE_VERSION("0.1"); |
f0c3a2ca | 233 | MODULE_LICENSE("GPL"); |