diff options
author | Igor M. Liplianin <liplianin@me.by> | 2009-11-27 12:37:35 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-12-16 06:27:46 -0500 |
commit | 141cc35e2d2941fcf4cfc78a75c75f7fc083d25f (patch) | |
tree | 9ba16cc8247eb5dea0c52d92d9834a0e5b9c8063 /drivers/media | |
parent | 21508b9ad4bb1db788b5307ab2070f1c2602caf2 (diff) |
V4L/DVB (13678): Add support for yet another DvbWorld, TeVii and Prof USB devices
Patch to support DvbWorld DW2104 device modifications
with STV0903 and DS3000 demods as well as TeVii S660 and Prof 1100
Also replace some magic numbers with meaningfull variables.
Signed-off-by: Igor M. Liplianin <liplianin@me.by>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/dvb/dvb-usb/Kconfig | 8 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/dw2102.c | 456 |
2 files changed, 315 insertions, 149 deletions
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 2dee1bf73577..1b249897c9fb 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig | |||
@@ -265,9 +265,13 @@ config DVB_USB_DW2102 | |||
265 | select DVB_TDA10021 if !DVB_FE_CUSTOMISE | 265 | select DVB_TDA10021 if !DVB_FE_CUSTOMISE |
266 | select DVB_MT312 if !DVB_FE_CUSTOMISE | 266 | select DVB_MT312 if !DVB_FE_CUSTOMISE |
267 | select DVB_ZL10039 if !DVB_FE_CUSTOMISE | 267 | select DVB_ZL10039 if !DVB_FE_CUSTOMISE |
268 | select DVB_DS3000 if !DVB_FE_CUSTOMISE | ||
269 | select DVB_STB6100 if !DVB_FE_CUSTOMISE | ||
270 | select DVB_STV6110 if !DVB_FE_CUSTOMISE | ||
271 | select DVB_STV0900 if !DVB_FE_CUSTOMISE | ||
268 | help | 272 | help |
269 | Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers | 273 | Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 |
270 | and the TeVii S650, S630. | 274 | receivers. |
271 | 275 | ||
272 | config DVB_USB_CINERGY_T2 | 276 | config DVB_USB_CINERGY_T2 |
273 | tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" | 277 | tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" |
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 5bb9479d154e..64132c0cf80d 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c | |||
@@ -20,6 +20,11 @@ | |||
20 | #include "tda1002x.h" | 20 | #include "tda1002x.h" |
21 | #include "mt312.h" | 21 | #include "mt312.h" |
22 | #include "zl10039.h" | 22 | #include "zl10039.h" |
23 | #include "ds3000.h" | ||
24 | #include "stv0900.h" | ||
25 | #include "stv6110.h" | ||
26 | #include "stb6100.h" | ||
27 | #include "stb6100_proc.h" | ||
23 | 28 | ||
24 | #ifndef USB_PID_DW2102 | 29 | #ifndef USB_PID_DW2102 |
25 | #define USB_PID_DW2102 0x2102 | 30 | #define USB_PID_DW2102 0x2102 |
@@ -37,12 +42,20 @@ | |||
37 | #define USB_PID_CINERGY_S 0x0064 | 42 | #define USB_PID_CINERGY_S 0x0064 |
38 | #endif | 43 | #endif |
39 | 44 | ||
45 | #ifndef USB_PID_TEVII_S630 | ||
46 | #define USB_PID_TEVII_S630 0xd630 | ||
47 | #endif | ||
48 | |||
40 | #ifndef USB_PID_TEVII_S650 | 49 | #ifndef USB_PID_TEVII_S650 |
41 | #define USB_PID_TEVII_S650 0xd650 | 50 | #define USB_PID_TEVII_S650 0xd650 |
42 | #endif | 51 | #endif |
43 | 52 | ||
44 | #ifndef USB_PID_TEVII_S630 | 53 | #ifndef USB_PID_TEVII_S660 |
45 | #define USB_PID_TEVII_S630 0xd630 | 54 | #define USB_PID_TEVII_S660 0xd660 |
55 | #endif | ||
56 | |||
57 | #ifndef USB_PID_PROF_1100 | ||
58 | #define USB_PID_PROF_1100 0xb012 | ||
46 | #endif | 59 | #endif |
47 | 60 | ||
48 | #define DW210X_READ_MSG 0 | 61 | #define DW210X_READ_MSG 0 |
@@ -55,6 +68,10 @@ | |||
55 | #define DW2102_VOLTAGE_CTRL (0x1800) | 68 | #define DW2102_VOLTAGE_CTRL (0x1800) |
56 | #define DW2102_RC_QUERY (0x1a00) | 69 | #define DW2102_RC_QUERY (0x1a00) |
57 | 70 | ||
71 | #define err_str "did not find the firmware file. (%s) " \ | ||
72 | "Please see linux/Documentation/dvb/ for more details " \ | ||
73 | "on firmware-problems." | ||
74 | |||
58 | struct dvb_usb_rc_keys_table { | 75 | struct dvb_usb_rc_keys_table { |
59 | struct dvb_usb_rc_key *rc_keys; | 76 | struct dvb_usb_rc_key *rc_keys; |
60 | int rc_keys_size; | 77 | int rc_keys_size; |
@@ -71,6 +88,12 @@ static int ir_keymap; | |||
71 | module_param_named(keymap, ir_keymap, int, 0644); | 88 | module_param_named(keymap, ir_keymap, int, 0644); |
72 | MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..."); | 89 | MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..."); |
73 | 90 | ||
91 | /* demod probe */ | ||
92 | static int demod_probe = 1; | ||
93 | module_param_named(demod, demod_probe, int, 0644); | ||
94 | MODULE_PARM_DESC(demod, "demod to probe (1=cx24116 2=stv0903+stv6110 " | ||
95 | "4=stv0903+stb6100(or-able))."); | ||
96 | |||
74 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | 97 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
75 | 98 | ||
76 | static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value, | 99 | static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value, |
@@ -183,7 +206,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, | |||
183 | switch (num) { | 206 | switch (num) { |
184 | case 2: | 207 | case 2: |
185 | /* read si2109 register by number */ | 208 | /* read si2109 register by number */ |
186 | buf6[0] = 0xd0; | 209 | buf6[0] = msg[0].addr << 1; |
187 | buf6[1] = msg[0].len; | 210 | buf6[1] = msg[0].len; |
188 | buf6[2] = msg[0].buf[0]; | 211 | buf6[2] = msg[0].buf[0]; |
189 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | 212 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, |
@@ -198,7 +221,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, | |||
198 | switch (msg[0].addr) { | 221 | switch (msg[0].addr) { |
199 | case 0x68: | 222 | case 0x68: |
200 | /* write to si2109 register */ | 223 | /* write to si2109 register */ |
201 | buf6[0] = 0xd0; | 224 | buf6[0] = msg[0].addr << 1; |
202 | buf6[1] = msg[0].len; | 225 | buf6[1] = msg[0].len; |
203 | memcpy(buf6 + 2, msg[0].buf, msg[0].len); | 226 | memcpy(buf6 + 2, msg[0].buf, msg[0].len); |
204 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, | 227 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, |
@@ -239,7 +262,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms | |||
239 | /* read */ | 262 | /* read */ |
240 | /* first write first register number */ | 263 | /* first write first register number */ |
241 | u8 ibuf[msg[1].len + 2], obuf[3]; | 264 | u8 ibuf[msg[1].len + 2], obuf[3]; |
242 | obuf[0] = 0xd0; | 265 | obuf[0] = msg[0].addr << 1; |
243 | obuf[1] = msg[0].len; | 266 | obuf[1] = msg[0].len; |
244 | obuf[2] = msg[0].buf[0]; | 267 | obuf[2] = msg[0].buf[0]; |
245 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | 268 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, |
@@ -256,7 +279,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms | |||
256 | case 0x68: { | 279 | case 0x68: { |
257 | /* write to register */ | 280 | /* write to register */ |
258 | u8 obuf[msg[0].len + 2]; | 281 | u8 obuf[msg[0].len + 2]; |
259 | obuf[0] = 0xd0; | 282 | obuf[0] = msg[0].addr << 1; |
260 | obuf[1] = msg[0].len; | 283 | obuf[1] = msg[0].len; |
261 | memcpy(obuf + 2, msg[0].buf, msg[0].len); | 284 | memcpy(obuf + 2, msg[0].buf, msg[0].len); |
262 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | 285 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, |
@@ -266,7 +289,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms | |||
266 | case 0x61: { | 289 | case 0x61: { |
267 | /* write to tuner */ | 290 | /* write to tuner */ |
268 | u8 obuf[msg[0].len + 2]; | 291 | u8 obuf[msg[0].len + 2]; |
269 | obuf[0] = 0xc2; | 292 | obuf[0] = msg[0].addr << 1; |
270 | obuf[1] = msg[0].len; | 293 | obuf[1] = msg[0].len; |
271 | memcpy(obuf + 2, msg[0].buf, msg[0].len); | 294 | memcpy(obuf + 2, msg[0].buf, msg[0].len); |
272 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | 295 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, |
@@ -301,78 +324,78 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i | |||
301 | { | 324 | { |
302 | struct dvb_usb_device *d = i2c_get_adapdata(adap); | 325 | struct dvb_usb_device *d = i2c_get_adapdata(adap); |
303 | int ret = 0; | 326 | int ret = 0; |
304 | int len, i; | 327 | int len, i, j; |
305 | 328 | ||
306 | if (!d) | 329 | if (!d) |
307 | return -ENODEV; | 330 | return -ENODEV; |
308 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) | 331 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) |
309 | return -EAGAIN; | 332 | return -EAGAIN; |
310 | 333 | ||
311 | switch (num) { | 334 | for (j = 0; j < num; j++) { |
312 | case 2: { | 335 | switch (msg[j].addr) { |
313 | /* read */ | ||
314 | /* first write first register number */ | ||
315 | u8 ibuf[msg[1].len + 2], obuf[3]; | ||
316 | obuf[0] = 0xaa; | ||
317 | obuf[1] = msg[0].len; | ||
318 | obuf[2] = msg[0].buf[0]; | ||
319 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | ||
320 | obuf, msg[0].len + 2, DW210X_WRITE_MSG); | ||
321 | /* second read registers */ | ||
322 | ret = dw210x_op_rw(d->udev, 0xc3, 0xab , 0, | ||
323 | ibuf, msg[1].len + 2, DW210X_READ_MSG); | ||
324 | memcpy(msg[1].buf, ibuf + 2, msg[1].len); | ||
325 | |||
326 | break; | ||
327 | } | ||
328 | case 1: | ||
329 | switch (msg[0].addr) { | ||
330 | case 0x55: { | ||
331 | if (msg[0].buf[0] == 0xf7) { | ||
332 | /* firmware */ | ||
333 | /* Write in small blocks */ | ||
334 | u8 obuf[19]; | ||
335 | obuf[0] = 0xaa; | ||
336 | obuf[1] = 0x11; | ||
337 | obuf[2] = 0xf7; | ||
338 | len = msg[0].len - 1; | ||
339 | i = 1; | ||
340 | do { | ||
341 | memcpy(obuf + 3, msg[0].buf + i, (len > 16 ? 16 : len)); | ||
342 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | ||
343 | obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG); | ||
344 | i += 16; | ||
345 | len -= 16; | ||
346 | } while (len > 0); | ||
347 | } else { | ||
348 | /* write to register */ | ||
349 | u8 obuf[msg[0].len + 2]; | ||
350 | obuf[0] = 0xaa; | ||
351 | obuf[1] = msg[0].len; | ||
352 | memcpy(obuf + 2, msg[0].buf, msg[0].len); | ||
353 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | ||
354 | obuf, msg[0].len + 2, DW210X_WRITE_MSG); | ||
355 | } | ||
356 | break; | ||
357 | } | ||
358 | case(DW2102_RC_QUERY): { | 336 | case(DW2102_RC_QUERY): { |
359 | u8 ibuf[2]; | 337 | u8 ibuf[2]; |
360 | ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, | 338 | ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, |
361 | ibuf, 2, DW210X_READ_MSG); | 339 | ibuf, 2, DW210X_READ_MSG); |
362 | memcpy(msg[0].buf, ibuf , 2); | 340 | memcpy(msg[j].buf, ibuf , 2); |
363 | break; | 341 | break; |
364 | } | 342 | } |
365 | case(DW2102_VOLTAGE_CTRL): { | 343 | case(DW2102_VOLTAGE_CTRL): { |
366 | u8 obuf[2]; | 344 | u8 obuf[2]; |
367 | obuf[0] = 0x30; | 345 | obuf[0] = 0x30; |
368 | obuf[1] = msg[0].buf[0]; | 346 | obuf[1] = msg[j].buf[0]; |
369 | ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, | 347 | ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, |
370 | obuf, 2, DW210X_WRITE_MSG); | 348 | obuf, 2, DW210X_WRITE_MSG); |
371 | break; | 349 | break; |
372 | } | 350 | } |
351 | /*case 0x55: cx24116 | ||
352 | case 0x6a: stv0903 | ||
353 | case 0x68: ds3000, stv0903 | ||
354 | case 0x60: ts2020, stv6110, stb6100 */ | ||
355 | default: { | ||
356 | if (msg[j].flags == I2C_M_RD) { | ||
357 | /* read registers */ | ||
358 | u8 ibuf[msg[j].len + 2]; | ||
359 | ret = dw210x_op_rw(d->udev, 0xc3, | ||
360 | (msg[j].addr << 1) + 1, 0, | ||
361 | ibuf, msg[j].len + 2, | ||
362 | DW210X_READ_MSG); | ||
363 | memcpy(msg[j].buf, ibuf + 2, msg[j].len); | ||
364 | mdelay(10); | ||
365 | } else if (((msg[j].buf[0] == 0xb0) && | ||
366 | (msg[j].addr == 0x68)) || | ||
367 | ((msg[j].buf[0] == 0xf7) && | ||
368 | (msg[j].addr == 0x55))) { | ||
369 | /* write firmware */ | ||
370 | u8 obuf[19]; | ||
371 | obuf[0] = msg[j].addr << 1; | ||
372 | obuf[1] = (msg[j].len > 15 ? 17 : msg[j].len); | ||
373 | obuf[2] = msg[j].buf[0]; | ||
374 | len = msg[j].len - 1; | ||
375 | i = 1; | ||
376 | do { | ||
377 | memcpy(obuf + 3, msg[j].buf + i, | ||
378 | (len > 16 ? 16 : len)); | ||
379 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | ||
380 | obuf, (len > 16 ? 16 : len) + 3, | ||
381 | DW210X_WRITE_MSG); | ||
382 | i += 16; | ||
383 | len -= 16; | ||
384 | } while (len > 0); | ||
385 | } else { | ||
386 | /* write registers */ | ||
387 | u8 obuf[msg[j].len + 2]; | ||
388 | obuf[0] = msg[j].addr << 1; | ||
389 | obuf[1] = msg[j].len; | ||
390 | memcpy(obuf + 2, msg[j].buf, msg[j].len); | ||
391 | ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, | ||
392 | obuf, msg[j].len + 2, | ||
393 | DW210X_WRITE_MSG); | ||
394 | } | ||
395 | break; | ||
396 | } | ||
373 | } | 397 | } |
374 | 398 | ||
375 | break; | ||
376 | } | 399 | } |
377 | 400 | ||
378 | mutex_unlock(&d->i2c_mutex); | 401 | mutex_unlock(&d->i2c_mutex); |
@@ -442,63 +465,85 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], | |||
442 | return num; | 465 | return num; |
443 | } | 466 | } |
444 | 467 | ||
445 | static int s630_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], | 468 | static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], |
446 | int num) | 469 | int num) |
447 | { | 470 | { |
448 | struct dvb_usb_device *d = i2c_get_adapdata(adap); | 471 | struct dvb_usb_device *d = i2c_get_adapdata(adap); |
449 | int ret = 0; | 472 | int ret = 0; |
473 | int len, i, j; | ||
450 | 474 | ||
451 | if (!d) | 475 | if (!d) |
452 | return -ENODEV; | 476 | return -ENODEV; |
453 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) | 477 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) |
454 | return -EAGAIN; | 478 | return -EAGAIN; |
455 | 479 | ||
456 | switch (num) { | 480 | for (j = 0; j < num; j++) { |
457 | case 2: { /* read */ | 481 | switch (msg[j].addr) { |
458 | u8 ibuf[msg[1].len], obuf[3]; | ||
459 | obuf[0] = msg[1].len; | ||
460 | obuf[1] = (msg[0].addr << 1); | ||
461 | obuf[2] = msg[0].buf[0]; | ||
462 | |||
463 | ret = dw210x_op_rw(d->udev, 0x90, 0, 0, | ||
464 | obuf, 3, DW210X_WRITE_MSG); | ||
465 | msleep(5); | ||
466 | ret = dw210x_op_rw(d->udev, 0x91, 0, 0, | ||
467 | ibuf, msg[1].len, DW210X_READ_MSG); | ||
468 | memcpy(msg[1].buf, ibuf, msg[1].len); | ||
469 | break; | ||
470 | } | ||
471 | case 1: | ||
472 | switch (msg[0].addr) { | ||
473 | case 0x60: | ||
474 | case 0x0e: { | ||
475 | /* write to zl10313, zl10039 register, */ | ||
476 | u8 obuf[msg[0].len + 2]; | ||
477 | obuf[0] = msg[0].len + 1; | ||
478 | obuf[1] = (msg[0].addr << 1); | ||
479 | memcpy(obuf + 2, msg[0].buf, msg[0].len); | ||
480 | ret = dw210x_op_rw(d->udev, 0x80, 0, 0, | ||
481 | obuf, msg[0].len + 2, DW210X_WRITE_MSG); | ||
482 | break; | ||
483 | } | ||
484 | case (DW2102_RC_QUERY): { | 482 | case (DW2102_RC_QUERY): { |
485 | u8 ibuf[4]; | 483 | u8 ibuf[4]; |
486 | ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, | 484 | ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, |
487 | ibuf, 4, DW210X_READ_MSG); | 485 | ibuf, 4, DW210X_READ_MSG); |
488 | msg[0].buf[0] = ibuf[3]; | 486 | memcpy(msg[j].buf, ibuf + 1, 2); |
489 | break; | 487 | break; |
490 | } | 488 | } |
491 | case (DW2102_VOLTAGE_CTRL): { | 489 | case (DW2102_VOLTAGE_CTRL): { |
492 | u8 obuf[2]; | 490 | u8 obuf[2]; |
493 | obuf[0] = 0x03; | 491 | obuf[0] = 3; |
494 | obuf[1] = msg[0].buf[0]; | 492 | obuf[1] = msg[j].buf[0]; |
495 | ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, | 493 | ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, |
496 | obuf, 2, DW210X_WRITE_MSG); | 494 | obuf, 2, DW210X_WRITE_MSG); |
497 | break; | 495 | break; |
498 | } | 496 | } |
497 | /*case 0x55: cx24116 | ||
498 | case 0x6a: stv0903 | ||
499 | case 0x68: ds3000, stv0903 | ||
500 | case 0x60: ts2020, stv6110, stb6100 | ||
501 | case 0xa0: eeprom */ | ||
502 | default: { | ||
503 | if (msg[j].flags == I2C_M_RD) { | ||
504 | /* read registers */ | ||
505 | u8 ibuf[msg[j].len]; | ||
506 | ret = dw210x_op_rw(d->udev, 0x91, 0, 0, | ||
507 | ibuf, msg[j].len, | ||
508 | DW210X_READ_MSG); | ||
509 | memcpy(msg[j].buf, ibuf, msg[j].len); | ||
510 | break; | ||
511 | } else if ((msg[j].buf[0] == 0xb0) && | ||
512 | (msg[j].addr == 0x68)) { | ||
513 | /* write firmware */ | ||
514 | u8 obuf[19]; | ||
515 | obuf[0] = (msg[j].len > 16 ? | ||
516 | 18 : msg[j].len + 1); | ||
517 | obuf[1] = msg[j].addr << 1; | ||
518 | obuf[2] = msg[j].buf[0]; | ||
519 | len = msg[j].len - 1; | ||
520 | i = 1; | ||
521 | do { | ||
522 | memcpy(obuf + 3, msg[j].buf + i, | ||
523 | (len > 16 ? 16 : len)); | ||
524 | ret = dw210x_op_rw(d->udev, 0x80, 0, 0, | ||
525 | obuf, (len > 16 ? 16 : len) + 3, | ||
526 | DW210X_WRITE_MSG); | ||
527 | i += 16; | ||
528 | len -= 16; | ||
529 | } while (len > 0); | ||
530 | } else { | ||
531 | /* write registers */ | ||
532 | u8 obuf[msg[j].len + 2]; | ||
533 | obuf[0] = msg[j].len + 1; | ||
534 | obuf[1] = (msg[j].addr << 1); | ||
535 | memcpy(obuf + 2, msg[j].buf, msg[j].len); | ||
536 | ret = dw210x_op_rw(d->udev, | ||
537 | (num > 1 ? 0x90 : 0x80), 0, 0, | ||
538 | obuf, msg[j].len + 2, | ||
539 | DW210X_WRITE_MSG); | ||
540 | break; | ||
541 | } | ||
542 | break; | ||
543 | } | ||
499 | } | 544 | } |
500 | 545 | ||
501 | break; | 546 | msleep(3); |
502 | } | 547 | } |
503 | 548 | ||
504 | mutex_unlock(&d->i2c_mutex); | 549 | mutex_unlock(&d->i2c_mutex); |
@@ -535,8 +580,8 @@ static struct i2c_algorithm dw3101_i2c_algo = { | |||
535 | .functionality = dw210x_i2c_func, | 580 | .functionality = dw210x_i2c_func, |
536 | }; | 581 | }; |
537 | 582 | ||
538 | static struct i2c_algorithm s630_i2c_algo = { | 583 | static struct i2c_algorithm s6x0_i2c_algo = { |
539 | .master_xfer = s630_i2c_transfer, | 584 | .master_xfer = s6x0_i2c_transfer, |
540 | .functionality = dw210x_i2c_func, | 585 | .functionality = dw210x_i2c_func, |
541 | }; | 586 | }; |
542 | 587 | ||
@@ -564,25 +609,34 @@ static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) | |||
564 | return 0; | 609 | return 0; |
565 | }; | 610 | }; |
566 | 611 | ||
567 | static int s630_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) | 612 | static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) |
568 | { | 613 | { |
569 | int i, ret; | 614 | int i, ret; |
570 | u8 buf[3], eeprom[256], eepromline[16]; | 615 | u8 ibuf[] = { 0 }, obuf[] = { 0 }; |
616 | u8 eeprom[256], eepromline[16]; | ||
617 | struct i2c_msg msg[] = { | ||
618 | { | ||
619 | .addr = 0xa0 >> 1, | ||
620 | .flags = 0, | ||
621 | .buf = obuf, | ||
622 | .len = 1, | ||
623 | }, { | ||
624 | .addr = 0xa0 >> 1, | ||
625 | .flags = I2C_M_RD, | ||
626 | .buf = ibuf, | ||
627 | .len = 1, | ||
628 | } | ||
629 | }; | ||
571 | 630 | ||
572 | for (i = 0; i < 256; i++) { | 631 | for (i = 0; i < 256; i++) { |
573 | buf[0] = 1; | 632 | obuf[0] = i; |
574 | buf[1] = 0xa0; | 633 | ret = s6x0_i2c_transfer(&d->i2c_adap, msg, 2); |
575 | buf[2] = i; | 634 | if (ret != 2) { |
576 | ret = dw210x_op_rw(d->udev, 0x90, 0, 0, | ||
577 | buf, 3, DW210X_WRITE_MSG); | ||
578 | ret = dw210x_op_rw(d->udev, 0x91, 0, 0, | ||
579 | buf, 1, DW210X_READ_MSG); | ||
580 | if (ret < 0) { | ||
581 | err("read eeprom failed."); | 635 | err("read eeprom failed."); |
582 | return -1; | 636 | return -1; |
583 | } else { | 637 | } else { |
584 | eepromline[i % 16] = buf[0]; | 638 | eepromline[i % 16] = ibuf[0]; |
585 | eeprom[i] = buf[0]; | 639 | eeprom[i] = ibuf[0]; |
586 | } | 640 | } |
587 | 641 | ||
588 | if ((i % 16) == 15) { | 642 | if ((i % 16) == 15) { |
@@ -644,19 +698,104 @@ static struct mt312_config zl313_config = { | |||
644 | .demod_address = 0x0e, | 698 | .demod_address = 0x0e, |
645 | }; | 699 | }; |
646 | 700 | ||
701 | static struct ds3000_config dw2104_ds3000_config = { | ||
702 | .demod_address = 0x68, | ||
703 | }; | ||
704 | |||
705 | static struct stv0900_config dw2104a_stv0900_config = { | ||
706 | .demod_address = 0x6a, | ||
707 | .demod_mode = 0, | ||
708 | .xtal = 27000000, | ||
709 | .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ | ||
710 | .diseqc_mode = 2,/* 2/3 PWM */ | ||
711 | .tun1_maddress = 0,/* 0x60 */ | ||
712 | .tun1_adc = 0,/* 2 Vpp */ | ||
713 | .path1_mode = 3, | ||
714 | }; | ||
715 | |||
716 | static struct stb6100_config dw2104a_stb6100_config = { | ||
717 | .tuner_address = 0x60, | ||
718 | .refclock = 27000000, | ||
719 | }; | ||
720 | |||
721 | static struct stv0900_config dw2104_stv0900_config = { | ||
722 | .demod_address = 0x68, | ||
723 | .demod_mode = 0, | ||
724 | .xtal = 8000000, | ||
725 | .clkmode = 3, | ||
726 | .diseqc_mode = 2, | ||
727 | .tun1_maddress = 0, | ||
728 | .tun1_adc = 1,/* 1 Vpp */ | ||
729 | .path1_mode = 3, | ||
730 | }; | ||
731 | |||
732 | static struct stv6110_config dw2104_stv6110_config = { | ||
733 | .i2c_address = 0x60, | ||
734 | .mclk = 16000000, | ||
735 | .clk_div = 1, | ||
736 | }; | ||
737 | |||
647 | static int dw2104_frontend_attach(struct dvb_usb_adapter *d) | 738 | static int dw2104_frontend_attach(struct dvb_usb_adapter *d) |
648 | { | 739 | { |
649 | if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config, | 740 | struct dvb_tuner_ops *tuner_ops = NULL; |
650 | &d->dev->i2c_adap)) != NULL) { | 741 | |
742 | if (demod_probe & 4) { | ||
743 | d->fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config, | ||
744 | &d->dev->i2c_adap, 0); | ||
745 | if (d->fe != NULL) { | ||
746 | if (dvb_attach(stb6100_attach, d->fe, | ||
747 | &dw2104a_stb6100_config, | ||
748 | &d->dev->i2c_adap)) { | ||
749 | tuner_ops = &d->fe->ops.tuner_ops; | ||
750 | tuner_ops->set_frequency = stb6100_set_freq; | ||
751 | tuner_ops->get_frequency = stb6100_get_freq; | ||
752 | tuner_ops->set_bandwidth = stb6100_set_bandw; | ||
753 | tuner_ops->get_bandwidth = stb6100_get_bandw; | ||
754 | d->fe->ops.set_voltage = dw210x_set_voltage; | ||
755 | info("Attached STV0900+STB6100!\n"); | ||
756 | return 0; | ||
757 | } | ||
758 | } | ||
759 | } | ||
760 | |||
761 | if (demod_probe & 2) { | ||
762 | d->fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config, | ||
763 | &d->dev->i2c_adap, 0); | ||
764 | if (d->fe != NULL) { | ||
765 | if (dvb_attach(stv6110_attach, d->fe, | ||
766 | &dw2104_stv6110_config, | ||
767 | &d->dev->i2c_adap)) { | ||
768 | d->fe->ops.set_voltage = dw210x_set_voltage; | ||
769 | info("Attached STV0900+STV6110A!\n"); | ||
770 | return 0; | ||
771 | } | ||
772 | } | ||
773 | } | ||
774 | |||
775 | if (demod_probe & 1) { | ||
776 | d->fe = dvb_attach(cx24116_attach, &dw2104_config, | ||
777 | &d->dev->i2c_adap); | ||
778 | if (d->fe != NULL) { | ||
779 | d->fe->ops.set_voltage = dw210x_set_voltage; | ||
780 | info("Attached cx24116!\n"); | ||
781 | return 0; | ||
782 | } | ||
783 | } | ||
784 | |||
785 | d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, | ||
786 | &d->dev->i2c_adap); | ||
787 | if (d->fe != NULL) { | ||
651 | d->fe->ops.set_voltage = dw210x_set_voltage; | 788 | d->fe->ops.set_voltage = dw210x_set_voltage; |
652 | info("Attached cx24116!\n"); | 789 | info("Attached DS3000!\n"); |
653 | return 0; | 790 | return 0; |
654 | } | 791 | } |
792 | |||
655 | return -EIO; | 793 | return -EIO; |
656 | } | 794 | } |
657 | 795 | ||
658 | static struct dvb_usb_device_properties dw2102_properties; | 796 | static struct dvb_usb_device_properties dw2102_properties; |
659 | static struct dvb_usb_device_properties dw2104_properties; | 797 | static struct dvb_usb_device_properties dw2104_properties; |
798 | static struct dvb_usb_device_properties s6x0_properties; | ||
660 | 799 | ||
661 | static int dw2102_frontend_attach(struct dvb_usb_adapter *d) | 800 | static int dw2102_frontend_attach(struct dvb_usb_adapter *d) |
662 | { | 801 | { |
@@ -670,14 +809,17 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d) | |||
670 | return 0; | 809 | return 0; |
671 | } | 810 | } |
672 | } | 811 | } |
812 | |||
673 | if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) { | 813 | if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) { |
674 | /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/ | ||
675 | d->fe = dvb_attach(stv0288_attach, &earda_config, | 814 | d->fe = dvb_attach(stv0288_attach, &earda_config, |
676 | &d->dev->i2c_adap); | 815 | &d->dev->i2c_adap); |
677 | if (d->fe != NULL) { | 816 | if (d->fe != NULL) { |
678 | d->fe->ops.set_voltage = dw210x_set_voltage; | 817 | if (dvb_attach(stb6000_attach, d->fe, 0x61, |
679 | info("Attached stv0288!\n"); | 818 | &d->dev->i2c_adap)) { |
680 | return 0; | 819 | d->fe->ops.set_voltage = dw210x_set_voltage; |
820 | info("Attached stv0288!\n"); | ||
821 | return 0; | ||
822 | } | ||
681 | } | 823 | } |
682 | } | 824 | } |
683 | 825 | ||
@@ -705,15 +847,38 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d) | |||
705 | return -EIO; | 847 | return -EIO; |
706 | } | 848 | } |
707 | 849 | ||
708 | static int s630_frontend_attach(struct dvb_usb_adapter *d) | 850 | static int s6x0_frontend_attach(struct dvb_usb_adapter *d) |
709 | { | 851 | { |
710 | d->fe = dvb_attach(mt312_attach, &zl313_config, | 852 | d->fe = dvb_attach(mt312_attach, &zl313_config, |
711 | &d->dev->i2c_adap); | 853 | &d->dev->i2c_adap); |
854 | if (d->fe != NULL) { | ||
855 | if (dvb_attach(zl10039_attach, d->fe, 0x60, | ||
856 | &d->dev->i2c_adap)) { | ||
857 | d->fe->ops.set_voltage = dw210x_set_voltage; | ||
858 | info("Attached zl100313+zl10039!\n"); | ||
859 | return 0; | ||
860 | } | ||
861 | } | ||
862 | |||
863 | d->fe = dvb_attach(stv0288_attach, &earda_config, | ||
864 | &d->dev->i2c_adap); | ||
865 | if (d->fe != NULL) { | ||
866 | if (dvb_attach(stb6000_attach, d->fe, 0x61, | ||
867 | &d->dev->i2c_adap)) { | ||
868 | d->fe->ops.set_voltage = dw210x_set_voltage; | ||
869 | info("Attached stv0288+stb6000!\n"); | ||
870 | return 0; | ||
871 | } | ||
872 | } | ||
873 | |||
874 | d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, | ||
875 | &d->dev->i2c_adap); | ||
712 | if (d->fe != NULL) { | 876 | if (d->fe != NULL) { |
713 | d->fe->ops.set_voltage = dw210x_set_voltage; | 877 | d->fe->ops.set_voltage = dw210x_set_voltage; |
714 | info("Attached zl10313!\n"); | 878 | info("Attached ds3000+ds2020!\n"); |
715 | return 0; | 879 | return 0; |
716 | } | 880 | } |
881 | |||
717 | return -EIO; | 882 | return -EIO; |
718 | } | 883 | } |
719 | 884 | ||
@@ -724,14 +889,6 @@ static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) | |||
724 | return 0; | 889 | return 0; |
725 | } | 890 | } |
726 | 891 | ||
727 | static int dw2102_earda_tuner_attach(struct dvb_usb_adapter *adap) | ||
728 | { | ||
729 | dvb_attach(stb6000_attach, adap->fe, 0x61, | ||
730 | &adap->dev->i2c_adap); | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) | 892 | static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) |
736 | { | 893 | { |
737 | dvb_attach(dvb_pll_attach, adap->fe, 0x60, | 894 | dvb_attach(dvb_pll_attach, adap->fe, 0x60, |
@@ -740,14 +897,6 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) | |||
740 | return 0; | 897 | return 0; |
741 | } | 898 | } |
742 | 899 | ||
743 | static int s630_zl10039_tuner_attach(struct dvb_usb_adapter *adap) | ||
744 | { | ||
745 | dvb_attach(zl10039_attach, adap->fe, 0x60, | ||
746 | &adap->dev->i2c_adap); | ||
747 | |||
748 | return 0; | ||
749 | } | ||
750 | |||
751 | static struct dvb_usb_rc_key dw210x_rc_keys[] = { | 900 | static struct dvb_usb_rc_key dw210x_rc_keys[] = { |
752 | { 0xf80a, KEY_Q }, /*power*/ | 901 | { 0xf80a, KEY_Q }, /*power*/ |
753 | { 0xf80c, KEY_M }, /*mute*/ | 902 | { 0xf80c, KEY_M }, /*mute*/ |
@@ -922,6 +1071,8 @@ static struct usb_device_id dw2102_table[] = { | |||
922 | {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, | 1071 | {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, |
923 | {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)}, | 1072 | {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)}, |
924 | {USB_DEVICE(0x9022, USB_PID_TEVII_S630)}, | 1073 | {USB_DEVICE(0x9022, USB_PID_TEVII_S630)}, |
1074 | {USB_DEVICE(0x3011, USB_PID_PROF_1100)}, | ||
1075 | {USB_DEVICE(0x9022, USB_PID_TEVII_S660)}, | ||
925 | { } | 1076 | { } |
926 | }; | 1077 | }; |
927 | 1078 | ||
@@ -935,15 +1086,13 @@ static int dw2102_load_firmware(struct usb_device *dev, | |||
935 | u8 reset; | 1086 | u8 reset; |
936 | u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; | 1087 | u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; |
937 | const struct firmware *fw; | 1088 | const struct firmware *fw; |
938 | const char *filename = "dvb-usb-dw2101.fw"; | 1089 | const char *fw_2101 = "dvb-usb-dw2101.fw"; |
939 | 1090 | ||
940 | switch (dev->descriptor.idProduct) { | 1091 | switch (dev->descriptor.idProduct) { |
941 | case 0x2101: | 1092 | case 0x2101: |
942 | ret = request_firmware(&fw, filename, &dev->dev); | 1093 | ret = request_firmware(&fw, fw_2101, &dev->dev); |
943 | if (ret != 0) { | 1094 | if (ret != 0) { |
944 | err("did not find the firmware file. (%s) " | 1095 | err(err_str, fw_2101); |
945 | "Please see linux/Documentation/dvb/ for more details " | ||
946 | "on firmware-problems.", filename); | ||
947 | return ret; | 1096 | return ret; |
948 | } | 1097 | } |
949 | break; | 1098 | break; |
@@ -983,6 +1132,11 @@ static int dw2102_load_firmware(struct usb_device *dev, | |||
983 | } | 1132 | } |
984 | /* init registers */ | 1133 | /* init registers */ |
985 | switch (dev->descriptor.idProduct) { | 1134 | switch (dev->descriptor.idProduct) { |
1135 | case USB_PID_PROF_1100: | ||
1136 | s6x0_properties.rc_key_map = tbs_rc_keys; | ||
1137 | s6x0_properties.rc_key_map_size = | ||
1138 | ARRAY_SIZE(tbs_rc_keys); | ||
1139 | break; | ||
986 | case USB_PID_TEVII_S650: | 1140 | case USB_PID_TEVII_S650: |
987 | dw2104_properties.rc_key_map = tevii_rc_keys; | 1141 | dw2104_properties.rc_key_map = tevii_rc_keys; |
988 | dw2104_properties.rc_key_map_size = | 1142 | dw2104_properties.rc_key_map_size = |
@@ -1021,7 +1175,6 @@ static int dw2102_load_firmware(struct usb_device *dev, | |||
1021 | DW210X_READ_MSG); | 1175 | DW210X_READ_MSG); |
1022 | if (reset16[2] == 0x11) { | 1176 | if (reset16[2] == 0x11) { |
1023 | dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo; | 1177 | dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo; |
1024 | dw2102_properties.adapter->tuner_attach = &dw2102_earda_tuner_attach; | ||
1025 | break; | 1178 | break; |
1026 | } | 1179 | } |
1027 | } | 1180 | } |
@@ -1184,13 +1337,13 @@ static struct dvb_usb_device_properties dw3101_properties = { | |||
1184 | } | 1337 | } |
1185 | }; | 1338 | }; |
1186 | 1339 | ||
1187 | static struct dvb_usb_device_properties s630_properties = { | 1340 | static struct dvb_usb_device_properties s6x0_properties = { |
1188 | .caps = DVB_USB_IS_AN_I2C_ADAPTER, | 1341 | .caps = DVB_USB_IS_AN_I2C_ADAPTER, |
1189 | .usb_ctrl = DEVICE_SPECIFIC, | 1342 | .usb_ctrl = DEVICE_SPECIFIC, |
1190 | .firmware = "dvb-usb-s630.fw", | 1343 | .firmware = "dvb-usb-s630.fw", |
1191 | .no_reconnect = 1, | 1344 | .no_reconnect = 1, |
1192 | 1345 | ||
1193 | .i2c_algo = &s630_i2c_algo, | 1346 | .i2c_algo = &s6x0_i2c_algo, |
1194 | .rc_key_map = tevii_rc_keys, | 1347 | .rc_key_map = tevii_rc_keys, |
1195 | .rc_key_map_size = ARRAY_SIZE(tevii_rc_keys), | 1348 | .rc_key_map_size = ARRAY_SIZE(tevii_rc_keys), |
1196 | .rc_interval = 150, | 1349 | .rc_interval = 150, |
@@ -1199,12 +1352,12 @@ static struct dvb_usb_device_properties s630_properties = { | |||
1199 | .generic_bulk_ctrl_endpoint = 0x81, | 1352 | .generic_bulk_ctrl_endpoint = 0x81, |
1200 | .num_adapters = 1, | 1353 | .num_adapters = 1, |
1201 | .download_firmware = dw2102_load_firmware, | 1354 | .download_firmware = dw2102_load_firmware, |
1202 | .read_mac_address = s630_read_mac_address, | 1355 | .read_mac_address = s6x0_read_mac_address, |
1203 | .adapter = { | 1356 | .adapter = { |
1204 | { | 1357 | { |
1205 | .frontend_attach = s630_frontend_attach, | 1358 | .frontend_attach = s6x0_frontend_attach, |
1206 | .streaming_ctrl = NULL, | 1359 | .streaming_ctrl = NULL, |
1207 | .tuner_attach = s630_zl10039_tuner_attach, | 1360 | .tuner_attach = NULL, |
1208 | .stream = { | 1361 | .stream = { |
1209 | .type = USB_BULK, | 1362 | .type = USB_BULK, |
1210 | .count = 8, | 1363 | .count = 8, |
@@ -1217,12 +1370,20 @@ static struct dvb_usb_device_properties s630_properties = { | |||
1217 | }, | 1370 | }, |
1218 | } | 1371 | } |
1219 | }, | 1372 | }, |
1220 | .num_device_descs = 1, | 1373 | .num_device_descs = 3, |
1221 | .devices = { | 1374 | .devices = { |
1222 | {"TeVii S630 USB", | 1375 | {"TeVii S630 USB", |
1223 | {&dw2102_table[6], NULL}, | 1376 | {&dw2102_table[6], NULL}, |
1224 | {NULL}, | 1377 | {NULL}, |
1225 | }, | 1378 | }, |
1379 | {"Prof 1100 USB ", | ||
1380 | {&dw2102_table[7], NULL}, | ||
1381 | {NULL}, | ||
1382 | }, | ||
1383 | {"TeVii S660 USB", | ||
1384 | {&dw2102_table[8], NULL}, | ||
1385 | {NULL}, | ||
1386 | }, | ||
1226 | } | 1387 | } |
1227 | }; | 1388 | }; |
1228 | 1389 | ||
@@ -1235,10 +1396,10 @@ static int dw2102_probe(struct usb_interface *intf, | |||
1235 | THIS_MODULE, NULL, adapter_nr) || | 1396 | THIS_MODULE, NULL, adapter_nr) || |
1236 | 0 == dvb_usb_device_init(intf, &dw3101_properties, | 1397 | 0 == dvb_usb_device_init(intf, &dw3101_properties, |
1237 | THIS_MODULE, NULL, adapter_nr) || | 1398 | THIS_MODULE, NULL, adapter_nr) || |
1238 | 0 == dvb_usb_device_init(intf, &s630_properties, | 1399 | 0 == dvb_usb_device_init(intf, &s6x0_properties, |
1239 | THIS_MODULE, NULL, adapter_nr)) { | 1400 | THIS_MODULE, NULL, adapter_nr)) |
1240 | return 0; | 1401 | return 0; |
1241 | } | 1402 | |
1242 | return -ENODEV; | 1403 | return -ENODEV; |
1243 | } | 1404 | } |
1244 | 1405 | ||
@@ -1269,6 +1430,7 @@ module_exit(dw2102_module_exit); | |||
1269 | MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); | 1430 | MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); |
1270 | MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," | 1431 | MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," |
1271 | " DVB-C 3101 USB2.0," | 1432 | " DVB-C 3101 USB2.0," |
1272 | " TeVii S600, S630, S650 USB2.0 devices"); | 1433 | " TeVii S600, S630, S650, S660 USB2.0," |
1434 | " Prof 1100 USB2.0 devices"); | ||
1273 | MODULE_VERSION("0.1"); | 1435 | MODULE_VERSION("0.1"); |
1274 | MODULE_LICENSE("GPL"); | 1436 | MODULE_LICENSE("GPL"); |