blob: cd5dd4cecd0bad83e68bbff68bbc27471c59917b [file] [log] [blame]
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -03001/* DVB USB compliant Linux driver for the AzureWave 6017 USB2.0 DVB-S
2 * receiver.
3 * see Documentation/dvb/README.dvb-usb for more information
4 */
5
6#include "az6007.h"
7#include "drxk.h"
8#include "mt2063.h"
9#include "dvb_ca_en50221.h"
10
11/* debug */
12int dvb_usb_az6007_debug;
13module_param_named(debug,dvb_usb_az6007_debug, int, 0644);
14MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
15
16
17static int az6007_type =0;
18module_param(az6007_type, int, 0644);
19MODULE_PARM_DESC(az6007_type, "select delivery mode (0=DVB-T, 1=DVB-T");
20
21//module_param_named(type, 6007_type, int, 0644);
22//MODULE_PARM_DESC(type, "select delivery mode (0=DVB-T, 1=DVB-C)");
23
24DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
25
26
27struct az6007_device_state {
28 struct dvb_ca_en50221 ca;
29 struct mutex ca_mutex;
30 u8 power_state;
31};
32
33struct drxk3913_config az6007_drxk3913_config_DVBT = {
34 .demod_address = 0x52,
35 .min_delay_ms = 100,
36 .standard = MTTUNEA_DVBT,
37 .set_tuner = mt2063_setTune,
38 .tuner_getlocked = mt2063_lockStatus,
39 .tuner_MT2063_Open = tuner_MT2063_Open,
40 .tuner_MT2063_SoftwareShutdown = tuner_MT2063_SoftwareShutdown,
41 .tuner_MT2063_ClearPowerMaskBits = tuner_MT2063_ClearPowerMaskBits,
42};
43
44struct drxk3913_config az6007_drxk3913_config_DVBC = {
45 .demod_address = 0x52,
46 .min_delay_ms = 100,
47 .standard = MTTUNEA_DVBC,
48 .set_tuner = mt2063_setTune,
49 .tuner_getlocked = mt2063_lockStatus,
50 .tuner_MT2063_Open = tuner_MT2063_Open,
51 .tuner_MT2063_SoftwareShutdown = tuner_MT2063_SoftwareShutdown,
52 .tuner_MT2063_ClearPowerMaskBits = tuner_MT2063_ClearPowerMaskBits,
53};
54
55struct mt2063_config az6007_mt2063_config = {
56 .tuner_address = 0xc0,
57 .refclock = 36125000,
58};
59
60/* check for mutex FIXME */
61int az6007_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
62{
63 int ret = -1;
64
65 ret = usb_control_msg(d->udev,
66 usb_rcvctrlpipe(d->udev,0),
67 req,
68 USB_TYPE_VENDOR | USB_DIR_IN,
69 value,index,b,blen,
70 5000);
71
72 if (ret < 0) {
73 warn("usb in operation failed. (%d)", ret);
74 ret = -EIO;
75 } else
76 ret = 0;
77
78
79 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
80 debug_dump(b,blen,deb_xfer);
81
82 return ret;
83}
84
85static int az6007_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
86 u16 index, u8 *b, int blen)
87{
88 int ret;
89
90#if 0
91 int i=0, cyc=0, rem=0;
92 cyc = blen/64;
93 rem = blen%64;
94#endif
95
96 deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
97 debug_dump(b,blen,deb_xfer);
98
99
100#if 0
101 if (blen>64)
102 {
103 for (i=0; i<cyc; i++)
104 {
105 if ((ret = usb_control_msg(d->udev,
106 usb_sndctrlpipe(d->udev,0),
107 req,
108 USB_TYPE_VENDOR | USB_DIR_OUT,
109 value,index+i*64,b+i*64,64,
110 5000)) != 64) {
111 warn("usb out operation failed. (%d)",ret);
112 return -EIO;
113 }
114 }
115
116 if (rem>0)
117 {
118 if ((ret = usb_control_msg(d->udev,
119 usb_sndctrlpipe(d->udev,0),
120 req,
121 USB_TYPE_VENDOR | USB_DIR_OUT,
122 value,index+cyc*64,b+cyc*64,rem,
123 5000)) != rem) {
124 warn("usb out operation failed. (%d)",ret);
125 return -EIO;
126 }
127 }
128 }
129 else
130#endif
131 {
132 if ((ret = usb_control_msg(d->udev,
133 usb_sndctrlpipe(d->udev,0),
134 req,
135 USB_TYPE_VENDOR | USB_DIR_OUT,
136 value,index,b,blen,
137 5000)) != blen) {
138 warn("usb out operation failed. (%d)",ret);
139 return -EIO;
140 }
141 }
142
143 return 0;
144}
145
146static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
147{
148 return 0;
149}
150
151/* keys for the enclosed remote control */
152static struct dvb_usb_rc_key az6007_rc_keys[] = {
153 { 0x00, 0x01, KEY_1 },
154 { 0x00, 0x02, KEY_2 },
155};
156
157/* remote control stuff (does not work with my box) */
158static int az6007_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
159{
160 return 0;
161#if 0
162 u8 key[10];
163 int i;
164
165/* remove the following return to enabled remote querying */
166
167
168 az6007_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10);
169
170 deb_rc("remote query key: %x %d\n",key[1],key[1]);
171
172 if (key[1] == 0x44) {
173 *state = REMOTE_NO_KEY_PRESSED;
174 return 0;
175 }
176
177 for (i = 0; i < ARRAY_SIZE(az6007_rc_keys); i++)
178 if (az6007_rc_keys[i].custom == key[1]) {
179 *state = REMOTE_KEY_PRESSED;
180 *event = az6007_rc_keys[i].event;
181 break;
182 }
183 return 0;
184#endif
185}
186
187/*
188int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
189{
190 u8 v = onoff;
191 return az6007_usb_out_op(d,0xBC,v,3,NULL,1);
192}
193*/
194
195static int az6007_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
196{
197 az6007_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6);
198 return 0;
199}
200
201static int az6007_frontend_poweron(struct dvb_usb_adapter *adap)
202{
203 int ret;
204 u8 req;
205 u16 value;
206 u16 index;
207 int blen;
208
209 info("az6007_frontend_poweron adap=%p adap->dev=%p\n", adap, adap->dev);
210
211 req = 0xBC;
212 value = 1;//power on
213 index = 3;
214 blen =0;
215
216 if((ret = az6007_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0)
217 {
218 err("az6007_frontend_poweron failed!!!");
219 return -EIO;
220 }
221
222 msleep_interruptible(200);
223
224 req = 0xBC;
225 value = 0;//power on
226 index = 3;
227 blen =0;
228
229 if((ret = az6007_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0)
230 {
231 err("az6007_frontend_poweron failed!!!");
232 return -EIO;
233 }
234
235 msleep_interruptible(200);
236
237 req = 0xBC;
238 value = 1;//power on
239 index = 3;
240 blen =0;
241
242 if((ret = az6007_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0)
243 {
244 err("az6007_frontend_poweron failed!!!");
245 return -EIO;
246 }
247 info("az6007_frontend_poweron\n");
248 return 0;
249}
250
251static int az6007_frontend_reset(struct dvb_usb_adapter *adap)
252{
253 int ret;
254 u8 req;
255 u16 value;
256 u16 index;
257 int blen;
258
259 info("az6007_frontend_reset adap=%p adap->dev=%p\n", adap, adap->dev);
260
261 //reset demodulator
262 req = 0xC0;
263 value = 1;//high
264 index = 3;
265 blen =0;
266 if((ret = az6007_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0)
267 {
268 err("az6007_frontend_reset failed 1 !!!");
269 return -EIO;
270 }
271
272 req = 0xC0;
273 value = 0;//low
274 index = 3;
275 blen =0;
276 msleep_interruptible(200);
277 if((ret = az6007_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0)
278 {
279 err("az6007_frontend_reset failed 2 !!!");
280 return -EIO;
281 }
282 msleep_interruptible(200);
283 req = 0xC0;
284 value = 1;//high
285 index = 3;
286 blen =0;
287
288 if((ret = az6007_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0)
289 {
290 err("az6007_frontend_reset failed 3 !!!");
291 return -EIO;
292 }
293
294 msleep_interruptible(200);
295
296 info("reset az6007 frontend\n");
297
298 return 0;
299}
300
301static int az6007_led_on_off(struct usb_interface *intf, int onoff)
302{
303 int ret = -1;
304 u8 req;
305 u16 value;
306 u16 index;
307 int blen;
308 //TS through
309 req = 0xBC;
310 value = onoff;
311 index = 0;
312 blen =0;
313
314 ret = usb_control_msg(interface_to_usbdev(intf),
315 usb_rcvctrlpipe(interface_to_usbdev(intf),0),
316 req,
317 USB_TYPE_VENDOR | USB_DIR_OUT,
318 value,index,NULL,blen,
319 2000);
320
321 if (ret < 0) {
322 warn("usb in operation failed. (%d)", ret);
323 ret = -EIO;
324 } else
325 ret = 0;
326
327
328 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
329
330 return ret;
331}
332
333static int az6007_frontend_tsbypass(struct dvb_usb_adapter *adap,int onoff)
334{
335 int ret;
336 u8 req;
337 u16 value;
338 u16 index;
339 int blen;
340 //TS through
341 req = 0xC7;
342 value = onoff;
343 index = 0;
344 blen =0;
345
346 if((ret = az6007_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0)
347 return -EIO;
348 return 0;
349}
350
351static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
352{
353 az6007_frontend_poweron(adap);
354 az6007_frontend_reset(adap);
355
356 info("az6007_frontend_attach\n");
357
358 if (az6007_type == 0)
359 {
360 info("az6007_drxk3913_config_DVBT\n");
361 adap->fe = drxk3913_attach(&az6007_drxk3913_config_DVBT, &adap->dev->i2c_adap);
362 }
363 else
364 {
365 info("az6007_drxk3913_config_DVBC\n");
366 adap->fe = drxk3913_attach(&az6007_drxk3913_config_DVBC, &adap->dev->i2c_adap);
367 }
368 if (adap->fe) {
369 if (mt2063_attach(adap->fe, &az6007_mt2063_config, &adap->dev->i2c_adap)) {
370 info("found STB6100 DVB-C/DVB-T frontend @0x%02x\n",az6007_mt2063_config.tuner_address);
371
372 //vp6027_ci_init(adap);
373 } else {
374 adap->fe = NULL;
375 }
376 }
377 else
378 {
379 adap->fe = NULL;
380 err("no front-end attached\n");
381 }
382 //az6007_frontend_tsbypass(adap,0);
383
384 return 0;
385}
386
387static struct dvb_usb_device_properties az6007_properties;
388
389static void
390az6007_usb_disconnect(struct usb_interface *intf)
391{
392 dvb_usb_device_exit (intf);
393}
394
395/* I2C */
396static int az6007_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
397{
398 struct dvb_usb_device *d = i2c_get_adapdata(adap);
399 int j=0,len=0;
400 int ret=0;
401 u16 index;
402 u16 value;
403 int length;
404 u8 req;
405 u8 data[512];
406
407 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
408 return -EAGAIN;
409 if (num > 2)
410 warn("more than 2 i2c messages at a time is not handled yet. TODO.");
411
412
413 if (msg[0].addr == 0xc0) //MT2063
414 {
415 if (msg[0].flags != I2C_M_RD) //write
416 {
417 //printk("Tuner Tuner Write DevAddr=%02x RegAddr=%d\n", msg[0].addr, msg[0].buf[0]);
418 req = 0xBD;
419 index = msg[0].buf[0];
420 value = msg[0].addr | (1<<8);
421 length = msg[0].len - 1;
422 len = msg[0].len - 1;
423 //printk("Tuner Tuner WriteDATA len=%d ", len);
424 for(j=0;j<len;j++)
425 {
426 data[j] = msg[0].buf[j+1];
427 //printk("data[%d]=%02x ", j, data[j]);
428 }
429 //printk("\n");
430 ret = az6007_usb_out_op(d,req,value,index,data,length);
431 //ret = az6007_usb_out_op(d,req,value,index,&(msg[0].buf[1]),length);
432 }
433 else //read
434 {
435 //printk("Tuner Tuner Read DevAddr=%02x RegAddr=%02x\n", msg[0].addr, msg[0].buf[0]);
436 req = 0xB9;
437 index = msg[0].buf[0];
438 value = msg[0].addr + (1 << 8);
439 length = msg[1].len + 6;
440 ret = az6007_usb_in_op(d,req,value,index,data,length);
441 len = msg[1].len;
442 //printk("Tuner Tuner ReadDATA len=%d ", len);
443 for (j=0; j<len; j++)
444 {
445 msg[1].buf[j] = data[j+5];
446 //printk("data[%d]=%02x ", j, data[j+5]);
447 }
448 //printk("\n");
449 }
450 }
451 else
452 { //Demodulator
453 if (msg[0].flags != I2C_M_RD) //write
454 {
455 //printk("Demodulator Write DevAddr=%02x RegAddr=%d\n", msg[0].addr, msg[0].buf[0]);
456 req = 0xBD;
457 index = msg[0].buf[0];
458 value = msg[0].addr | (1<<8);
459 length = msg[0].len - 1;
460 len = msg[0].len - 1;
461 //printk("Demodulator WriteDATA len=%d ", len);
462 for(j=0;j<len;j++)
463 {
464 data[j] = msg[0].buf[j+1];
465 //printk("data[%d]=%02x ", j, data[j]);
466 }
467 //printk("\n");
468 ret = az6007_usb_out_op(d,req,value,index,data,length);
469 }
470 else //read
471 {
472 //printk("Demodulator Read DevAddr=%02x RegAddr=%02x\n", msg[0].addr, msg[0].buf[0]);
473 req = 0xB9;
474 index = 0;
475 value = msg[0].addr + (0 << 8);
476 length = msg[0].len + 6;
477 ret = az6007_usb_in_op(d,req,value,index,data,length);
478 len = msg[0].len;
479 //printk("Demodulator ReadDATA len=%d ", len);
480 for (j=0; j<len; j++)
481 {
482 msg[0].buf[j] = data[j+5];
483 //printk("data[%d]=%02x ", j, data[j+5]);
484 }
485 //printk("\n");
486 }
487 }
488 mutex_unlock(&d->i2c_mutex);
489 return ret;
490}
491
492
493static u32 az6007_i2c_func(struct i2c_adapter *adapter)
494{
495 return I2C_FUNC_I2C;
496}
497
498static struct i2c_algorithm az6007_i2c_algo = {
499 .master_xfer = az6007_i2c_xfer,
500 .functionality = az6007_i2c_func,
501#ifdef NEED_ALGO_CONTROL
502 .algo_control = dummy_algo_control,
503#endif
504};
505
506int az6007_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
507 struct dvb_usb_device_description **desc, int *cold)
508{
509 u8 b[16];
510 s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev,0),
511 0xb7, USB_TYPE_VENDOR | USB_DIR_IN, 6, 0, b, 6, USB_CTRL_GET_TIMEOUT);
512
513 info("FW GET_VERSION length: %d\n",ret);
514
515 *cold = ret <= 0;
516
517 info("cold: %d\n", *cold);
518 return 0;
519}
520
521static int az6007_usb_probe(struct usb_interface *intf,
522 const struct usb_device_id *id)
523{
524 az6007_led_on_off(intf, 0);
525
526 return dvb_usb_device_init(intf, &az6007_properties,
527 THIS_MODULE, NULL, adapter_nr);
528}
529
530static struct usb_device_id az6007_usb_table [] = {
531 { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007) },
532 { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7) },
533 { 0 },
534};
535
536MODULE_DEVICE_TABLE(usb, az6007_usb_table);
537
538static struct dvb_usb_device_properties az6007_properties = {
539 .caps = DVB_USB_IS_AN_I2C_ADAPTER,
540 .usb_ctrl = CYPRESS_FX2,
541 //.download_firmware = az6007_download_firmware,
542 .firmware = "dvb-usb-az6007-03.fw",
543 .no_reconnect = 1,
544
545 .size_of_priv = sizeof(struct az6007_device_state),
546 .identify_state = az6007_identify_state,
547 .num_adapters = 1,
548 .adapter = {
549 {
550 //.caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
551
552 .streaming_ctrl = az6007_streaming_ctrl,
553 .frontend_attach = az6007_frontend_attach,
554
555 /* parameter for the MPEG2-data transfer */
556 .stream = {
557 .type = USB_BULK,
558 .count = 10,
559 .endpoint = 0x02,
560 .u = {
561 .bulk = {
562 .buffersize = 4096,
563 }
564 }
565 },
566 .size_of_priv = 0,//sizeof(struct az6007_state),
567 }
568 },
569 //.power_ctrl = az6007_power_ctrl,
570 .read_mac_address = az6007_read_mac_addr,
571
572 .rc_key_map = az6007_rc_keys,
573 .rc_key_map_size = ARRAY_SIZE(az6007_rc_keys),
574 .rc_interval = 400,
575 .rc_query = az6007_rc_query,
576 .i2c_algo = &az6007_i2c_algo,
577
578 .num_device_descs = 2,
579 .devices = {
580 { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
581 .cold_ids = { &az6007_usb_table[0], NULL },
582 .warm_ids = { NULL },
583 },
584 { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
585 .cold_ids = { &az6007_usb_table[1], NULL },
586 .warm_ids = { NULL },
587 },
588 { NULL },
589 }
590};
591
592/* usb specific object needed to register this driver with the usb subsystem */
593static struct usb_driver az6007_usb_driver = {
594 .name = "dvb_usb_az6007",
595 .probe = az6007_usb_probe,
596 .disconnect = dvb_usb_device_exit,
597 //.disconnect = az6007_usb_disconnect,
598 .id_table = az6007_usb_table,
599};
600
601/* module stuff */
602static int __init az6007_usb_module_init(void)
603{
604 int result;
605 info("henry :: az6007 usb module init");
606 if ((result = usb_register(&az6007_usb_driver))) {
607 err("usb_register failed. (%d)",result);
608 return result;
609 }
610
611 return 0;
612}
613
614static void __exit az6007_usb_module_exit(void)
615{
616 /* deregister this driver from the USB subsystem */
617 info("henry :: az6007 usb module exit");
618 usb_deregister(&az6007_usb_driver);
619}
620
621module_init(az6007_usb_module_init);
622module_exit(az6007_usb_module_exit);
623
624MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
625MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
626MODULE_VERSION("1.0");
627MODULE_LICENSE("GPL");