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