aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/dvb/dibusb/dvb-dibusb-core.c
blob: 26235f9247e4588e25e20ea6b8c999ac14317713 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
/*
 * Driver for mobile USB Budget DVB-T devices based on reference 
 * design made by DiBcom (http://www.dibcom.fr/)
 * 
 * dvb-dibusb-core.c
 * 
 * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
 * 
 * based on GPL code from DiBcom, which has
 * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
 *
 * Remote control code added by David Matthews (dm@prolingua.co.uk)
 * 
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License as
 *	published by the Free Software Foundation, version 2.
 *
 * Acknowledgements
 * 
 *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
 *  sources, on which this driver (and the dib3000mb/mc/p frontends) are based.
 * 
 * see Documentation/dvb/README.dibusb for more information
 */
#include "dvb-dibusb.h"

#include <linux/moduleparam.h>

/* debug */
int dvb_dibusb_debug;
module_param_named(debug, dvb_dibusb_debug,  int, 0644);

#ifdef CONFIG_DVB_DIBCOM_DEBUG
#define DBSTATUS ""
#else
#define DBSTATUS " (debugging is not enabled)"
#endif
MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err,32=rc (|-able))." DBSTATUS);
#undef DBSTATUS

static int pid_parse;
module_param(pid_parse, int, 0644);
MODULE_PARM_DESC(pid_parse, "enable pid parsing (filtering) when running at USB2.0");

static int rc_query_interval = 100;
module_param(rc_query_interval, int, 0644);
MODULE_PARM_DESC(rc_query_interval, "interval in msecs for remote control query (default: 100; min: 40)");

static int rc_key_repeat_count = 2;
module_param(rc_key_repeat_count, int, 0644);
MODULE_PARM_DESC(rc_key_repeat_count, "how many key repeats will be dropped before passing the key event again (default: 2)");

/* Vendor IDs */
#define USB_VID_ADSTECH						0x06e1
#define USB_VID_ANCHOR						0x0547
#define USB_VID_AVERMEDIA					0x14aa
#define USB_VID_COMPRO						0x185b
#define USB_VID_COMPRO_UNK					0x145f
#define USB_VID_CYPRESS						0x04b4
#define USB_VID_DIBCOM						0x10b8
#define USB_VID_EMPIA						0xeb1a
#define USB_VID_GRANDTEC					0x5032
#define USB_VID_HANFTEK						0x15f4
#define USB_VID_HAUPPAUGE					0x2040
#define USB_VID_HYPER_PALTEK				0x1025
#define USB_VID_IMC_NETWORKS				0x13d3
#define USB_VID_TWINHAN						0x1822
#define USB_VID_ULTIMA_ELECTRONIC			0x05d8

/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD			0xa333
#define USB_PID_ADSTECH_USB2_WARM			0xa334
#define USB_PID_AVERMEDIA_DVBT_USB_COLD		0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM		0x0002
#define USB_PID_COMPRO_DVBU2000_COLD		0xd000
#define USB_PID_COMPRO_DVBU2000_WARM		0xd001
#define USB_PID_COMPRO_DVBU2000_UNK_COLD	0x010c
#define USB_PID_COMPRO_DVBU2000_UNK_WARM	0x010d
#define USB_PID_DIBCOM_MOD3000_COLD			0x0bb8
#define USB_PID_DIBCOM_MOD3000_WARM			0x0bb9
#define USB_PID_DIBCOM_MOD3001_COLD			0x0bc6
#define USB_PID_DIBCOM_MOD3001_WARM			0x0bc7
#define USB_PID_DIBCOM_ANCHOR_2135_COLD		0x2131
#define USB_PID_GRANDTEC_DVBT_USB_COLD		0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM		0x0fa1
#define USB_PID_KWORLD_VSTREAM_COLD			0x17de
#define USB_PID_KWORLD_VSTREAM_WARM			0x17df
#define USB_PID_TWINHAN_VP7041_COLD			0x3201
#define USB_PID_TWINHAN_VP7041_WARM			0x3202
#define USB_PID_ULTIMA_TVBOX_COLD			0x8105
#define USB_PID_ULTIMA_TVBOX_WARM			0x8106
#define USB_PID_ULTIMA_TVBOX_AN2235_COLD	0x8107
#define USB_PID_ULTIMA_TVBOX_AN2235_WARM	0x8108
#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD	0x2235
#define USB_PID_ULTIMA_TVBOX_USB2_COLD		0x8109
#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD	0x8613
#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM	0x1002
#define USB_PID_UNK_HYPER_PALTEK_COLD		0x005e
#define USB_PID_UNK_HYPER_PALTEK_WARM		0x005f
#define USB_PID_HANFTEK_UMT_010_COLD		0x0001
#define USB_PID_HANFTEK_UMT_010_WARM		0x0015
#define USB_PID_YAKUMO_DTT200U_COLD			0x0201
#define USB_PID_YAKUMO_DTT200U_WARM			0x0301
#define USB_PID_WINTV_NOVA_T_USB2_COLD		0x9300
#define USB_PID_WINTV_NOVA_T_USB2_WARM		0x9301

/* USB Driver stuff
 * table of devices that this driver is working with
 *
 * ATTENTION: Never ever change the order of this table, the particular 
 * devices depend on this order 
 *
 * Each entry is used as a reference in the device_struct. Currently this is 
 * the only non-redundant way of assigning USB ids to actual devices I'm aware 
 * of, because there is only one place in the code where the assignment of 
 * vendor and product id is done, here.
 */
static struct usb_device_id dib_table [] = {
/* 00 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_AVERMEDIA_DVBT_USB_COLD)},
/* 01 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_AVERMEDIA_DVBT_USB_WARM)},
/* 02 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_YAKUMO_DTT200U_COLD) },
/* 03 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_YAKUMO_DTT200U_WARM) },

/* 04 */	{ USB_DEVICE(USB_VID_COMPRO,		USB_PID_COMPRO_DVBU2000_COLD) },
/* 05 */	{ USB_DEVICE(USB_VID_COMPRO,		USB_PID_COMPRO_DVBU2000_WARM) },
/* 06 */	{ USB_DEVICE(USB_VID_COMPRO_UNK,	USB_PID_COMPRO_DVBU2000_UNK_COLD) },
/* 07 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3000_COLD) },
/* 08 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3000_WARM) },
/* 09 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3001_COLD) },
/* 10 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3001_WARM) },
/* 11 */	{ USB_DEVICE(USB_VID_EMPIA,			USB_PID_KWORLD_VSTREAM_COLD) },
/* 12 */	{ USB_DEVICE(USB_VID_EMPIA,			USB_PID_KWORLD_VSTREAM_WARM) },
/* 13 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_GRANDTEC_DVBT_USB_COLD) },
/* 14 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_GRANDTEC_DVBT_USB_WARM) },
/* 15 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_DIBCOM_MOD3000_COLD) },
/* 16 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_DIBCOM_MOD3000_WARM) },
/* 17 */	{ USB_DEVICE(USB_VID_HYPER_PALTEK,	USB_PID_UNK_HYPER_PALTEK_COLD) },
/* 18 */	{ USB_DEVICE(USB_VID_HYPER_PALTEK,	USB_PID_UNK_HYPER_PALTEK_WARM) },
/* 19 */	{ USB_DEVICE(USB_VID_IMC_NETWORKS,	USB_PID_TWINHAN_VP7041_COLD) },
/* 20 */	{ USB_DEVICE(USB_VID_IMC_NETWORKS,	USB_PID_TWINHAN_VP7041_WARM) },
/* 21 */	{ USB_DEVICE(USB_VID_TWINHAN, 		USB_PID_TWINHAN_VP7041_COLD) },
/* 22 */	{ USB_DEVICE(USB_VID_TWINHAN, 		USB_PID_TWINHAN_VP7041_WARM) },
/* 23 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) },
/* 24 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) },
/* 25 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) },
/* 26 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) },
/* 27 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC,	USB_PID_ULTIMA_TVBOX_USB2_COLD) },
	
/* 28 */	{ USB_DEVICE(USB_VID_HANFTEK,		USB_PID_HANFTEK_UMT_010_COLD) },
/* 29 */	{ USB_DEVICE(USB_VID_HANFTEK,		USB_PID_HANFTEK_UMT_010_WARM) },

/* 30 */	{ USB_DEVICE(USB_VID_HAUPPAUGE,		USB_PID_WINTV_NOVA_T_USB2_COLD) },
/* 31 */	{ USB_DEVICE(USB_VID_HAUPPAUGE,		USB_PID_WINTV_NOVA_T_USB2_WARM) },
/* 32 */	{ USB_DEVICE(USB_VID_ADSTECH,		USB_PID_ADSTECH_USB2_COLD) },
/* 33 */	{ USB_DEVICE(USB_VID_ADSTECH,		USB_PID_ADSTECH_USB2_WARM) },
/* 
 * activate the following define when you have one of the devices and want to 
 * build it from build-2.6 in dvb-kernel
 */
// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
/* 34 */	{ USB_DEVICE(USB_VID_ANCHOR,		USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) },
/* 35 */	{ USB_DEVICE(USB_VID_CYPRESS,		USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) },
/* 36 */	{ USB_DEVICE(USB_VID_ANCHOR,		USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) },
/* 37 */	{ USB_DEVICE(USB_VID_ANCHOR,		USB_PID_DIBCOM_ANCHOR_2135_COLD) },
#endif
			{ }		/* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, dib_table);

static struct dibusb_usb_controller dibusb_usb_ctrl[] = {
	{ .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 },
	{ .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 },
	{ .name = "Cypress FX2",    .cpu_cs_register = 0xe600 },
};

struct dibusb_tuner dibusb_tuner[] = {
	{ DIBUSB_TUNER_CABLE_THOMSON, 
	  0x61 
	},
	{ DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5,
	  0x60 
	},
	{ DIBUSB_TUNER_CABLE_LG_TDTP_E102P,
	  0x61
	},
	{ DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5,
	  0x60
	},
};

static struct dibusb_demod dibusb_demod[] = {
	{ DIBUSB_DIB3000MB,
	  16,
	  { 0x8, 0 },
	},
	{ DIBUSB_DIB3000MC,
	  32,
	  { 0x9, 0xa, 0xb, 0xc }, 
	},
	{ DIBUSB_MT352,
	  254,
	  { 0xf, 0 }, 
	},
	{ DTT200U_FE,
	  8,
	  { 0xff,0 }, /* there is no i2c bus in this device */
	}
};

static struct dibusb_device_class dibusb_device_classes[] = {
	{ .id = DIBUSB1_1, .usb_ctrl = &dibusb_usb_ctrl[0],
	  .firmware = "dvb-dibusb-5.0.0.11.fw",
	  .pipe_cmd = 0x01, .pipe_data = 0x02, 
	  .urb_count = 7, .urb_buffer_size = 4096,
	  DIBUSB_RC_NEC_PROTOCOL,
	  &dibusb_demod[DIBUSB_DIB3000MB],
	  &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
	},
	{ DIBUSB1_1_AN2235, &dibusb_usb_ctrl[1],
	  "dvb-dibusb-an2235-1.fw",
	  0x01, 0x02, 
	  7, 4096,
	  DIBUSB_RC_NEC_PROTOCOL,
	  &dibusb_demod[DIBUSB_DIB3000MB],
	  &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
	},
	{ DIBUSB2_0,&dibusb_usb_ctrl[2],
	  "dvb-dibusb-6.0.0.5.fw",
	  0x01, 0x06, 
	  7, 4096,
	  DIBUSB_RC_NEC_PROTOCOL,
	  &dibusb_demod[DIBUSB_DIB3000MC],
	  &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5],
	},
	{ UMT2_0, &dibusb_usb_ctrl[2],
	  "dvb-dibusb-umt-2.fw",
	  0x01, 0x06,
	  20, 512,
	  DIBUSB_RC_NO,
	  &dibusb_demod[DIBUSB_MT352],
	  &dibusb_tuner[DIBUSB_TUNER_CABLE_LG_TDTP_E102P],
	},
	{ DIBUSB2_0B,&dibusb_usb_ctrl[2],
	  "dvb-dibusb-adstech-usb2-1.fw",
	  0x01, 0x06,
	  7, 4096,
	  DIBUSB_RC_NEC_PROTOCOL,
	  &dibusb_demod[DIBUSB_DIB3000MB],
	  &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
	},
	{ NOVAT_USB2,&dibusb_usb_ctrl[2],
	  "dvb-dibusb-nova-t-1.fw",
	  0x01, 0x06,
	  7, 4096,
	  DIBUSB_RC_HAUPPAUGE_PROTO,
	  &dibusb_demod[DIBUSB_DIB3000MC],
	  &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5],
	},
	{ DTT200U,&dibusb_usb_ctrl[2],
	  "dvb-dtt200u-1.fw",
	  0x01, 0x02,
	  7, 4096,
	  DIBUSB_RC_NO,
	  &dibusb_demod[DTT200U_FE],
	  NULL, /* no explicit tuner/pll-programming necessary (it has the ENV57H1XD5) */
	},
};

static struct dibusb_usb_device dibusb_devices[] = {
	{	"TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[19], &dib_table[21], NULL},
		{ &dib_table[20], &dib_table[22], NULL},
	},
	{	"KWorld V-Stream XPERT DTV - DVB-T USB1.1",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[11], NULL },
		{ &dib_table[12], NULL },
	},
	{	"Grandtec USB1.1 DVB-T",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[13], &dib_table[15], NULL },
		{ &dib_table[14], &dib_table[16], NULL },
	},
	{	"DiBcom USB1.1 DVB-T reference design (MOD3000)",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[7],  NULL },
		{ &dib_table[8],  NULL },
	},
	{	"Artec T1 USB1.1 TVBOX with AN2135",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[23], NULL },
		{ &dib_table[24], NULL },
	},
	{	"Artec T1 USB1.1 TVBOX with AN2235",
		&dibusb_device_classes[DIBUSB1_1_AN2235],
		{ &dib_table[25], NULL },
		{ &dib_table[26], NULL },
	},
	{	"Avermedia AverTV DVBT USB1.1",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[0],  NULL },
		{ &dib_table[1],  NULL },
	},
	{	"Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[4], &dib_table[6], NULL},
		{ &dib_table[5], NULL },
	},
	{	"Unkown USB1.1 DVB-T device ???? please report the name to the author",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[17], NULL },
		{ &dib_table[18], NULL },
	},
	{	"DiBcom USB2.0 DVB-T reference design (MOD3000P)",
		&dibusb_device_classes[DIBUSB2_0],
		{ &dib_table[9],  NULL },
		{ &dib_table[10], NULL },
	},
	{	"Artec T1 USB2.0 TVBOX (please report the warm ID)",
		&dibusb_device_classes[DIBUSB2_0],
		{ &dib_table[27], NULL },
		{ NULL },
	},
	{	"Hauppauge WinTV NOVA-T USB2",
		&dibusb_device_classes[NOVAT_USB2],
		{ &dib_table[30], NULL },
		{ &dib_table[31], NULL },
	},
	{	"DTT200U (Yakumo/Hama/Typhoon) DVB-T USB2.0",
		&dibusb_device_classes[DTT200U],
		{ &dib_table[2], NULL },
		{ &dib_table[3], NULL },
	},	
	{	"Hanftek UMT-010 DVB-T USB2.0",
		&dibusb_device_classes[UMT2_0],
		{ &dib_table[28], NULL },
		{ &dib_table[29], NULL },
	},	
	{	"KWorld/ADSTech Instant DVB-T USB 2.0",
		&dibusb_device_classes[DIBUSB2_0B],
		{ &dib_table[32], NULL },
		{ &dib_table[33], NULL }, /* device ID with default DIBUSB2_0-firmware */
	},
#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
	{	"Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)",
		&dibusb_device_classes[DIBUSB1_1_AN2235],
		{ &dib_table[34], NULL },
		{ NULL },
	},
	{	"Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)",
		&dibusb_device_classes[DTT200U],
		{ &dib_table[35], NULL },
		{ &dib_table[36], NULL }, /* undefined, it could be that the device will get another USB ID in warm state */
	},
	{	"DiBcom USB1.1 DVB-T reference design (MOD3000) with AN2135 default IDs",
		&dibusb_device_classes[DIBUSB1_1],
		{ &dib_table[37], NULL },
		{ NULL },
	},
#endif
};

static int dibusb_exit(struct usb_dibusb *dib)
{
	deb_info("init_state before exiting everything: %x\n",dib->init_state);
	dibusb_remote_exit(dib);
	dibusb_fe_exit(dib);
	dibusb_i2c_exit(dib);
	dibusb_dvb_exit(dib);
	dibusb_urb_exit(dib);
	deb_info("init_state should be zero now: %x\n",dib->init_state);
	dib->init_state = DIBUSB_STATE_INIT;
	kfree(dib);
	return 0;
}

static int dibusb_init(struct usb_dibusb *dib)
{
	int ret = 0;
	sema_init(&dib->usb_sem, 1);
	sema_init(&dib->i2c_sem, 1);

	dib->init_state = DIBUSB_STATE_INIT;
	
	if ((ret = dibusb_urb_init(dib)) ||
		(ret = dibusb_dvb_init(dib)) || 
		(ret = dibusb_i2c_init(dib))) {
		dibusb_exit(dib);
		return ret;
	}

	if ((ret = dibusb_fe_init(dib)))
		err("could not initialize a frontend.");
	
	if ((ret = dibusb_remote_init(dib)))
		err("could not initialize remote control.");
	
	return 0;
}

static struct dibusb_usb_device * dibusb_device_class_quirk(struct usb_device *udev, struct dibusb_usb_device *dev)
{
	int i;

	/* Quirk for the Kworld/ADSTech Instant USB2.0 device. It has the same USB
	 * IDs like the USB1.1 KWorld after loading the firmware. Which is a bad
	 * idea and make this quirk necessary.
	 */
	if (dev->dev_cl->id == DIBUSB1_1 && udev->speed == USB_SPEED_HIGH) {
		info("this seems to be the Kworld/ADSTech Instant USB2.0 device or equal.");
		for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) {
			if (dibusb_devices[i].dev_cl->id == DIBUSB2_0B) {
				dev = &dibusb_devices[i];
				break;
			}
		}
	}

	return dev;
}

static struct dibusb_usb_device * dibusb_find_device (struct usb_device *udev,int *cold)
{
	int i,j;
	struct dibusb_usb_device *dev = NULL;
	*cold = -1;

	for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) {
		for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].cold_ids[j] != NULL; j++) {
			deb_info("check for cold %x %x\n",dibusb_devices[i].cold_ids[j]->idVendor, dibusb_devices[i].cold_ids[j]->idProduct);
			if (dibusb_devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
				dibusb_devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
				*cold = 1;
				dev = &dibusb_devices[i];
				break;
			}
		}

		if (dev != NULL)
			break;

		for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].warm_ids[j] != NULL; j++) {
			deb_info("check for warm %x %x\n",dibusb_devices[i].warm_ids[j]->idVendor, dibusb_devices[i].warm_ids[j]->idProduct);
			if (dibusb_devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
				dibusb_devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
				*cold = 0;
				dev = &dibusb_devices[i];
				break;
			}
		}
	}

	if (dev != NULL)
		dev = dibusb_device_class_quirk(udev,dev);

	return dev;
}

/*
 * USB 
 */
static int dibusb_probe(struct usb_interface *intf, 
		const struct usb_device_id *id)
{
	struct usb_device *udev = interface_to_usbdev(intf);
	struct usb_dibusb *dib = NULL;
	struct dibusb_usb_device *dibdev = NULL;
	
	int ret = -ENOMEM,cold=0;

	if ((dibdev = dibusb_find_device(udev,&cold)) == NULL) {
		err("something went very wrong, "
				"unknown product ID: %.4x",le16_to_cpu(udev->descriptor.idProduct));
		return -ENODEV;
	}
	
	if (cold == 1) {
		info("found a '%s' in cold state, will try to load a firmware",dibdev->name);
		ret = dibusb_loadfirmware(udev,dibdev);
	} else {
		info("found a '%s' in warm state.",dibdev->name);
		dib = kmalloc(sizeof(struct usb_dibusb),GFP_KERNEL);
		if (dib == NULL) {
			err("no memory");
			return ret;
		}
		memset(dib,0,sizeof(struct usb_dibusb));
		
		dib->udev = udev;
		dib->dibdev = dibdev;

		/* store parameters to structures */
		dib->rc_query_interval = rc_query_interval;
		dib->pid_parse = pid_parse;
		dib->rc_key_repeat_count = rc_key_repeat_count;

		usb_set_intfdata(intf, dib);
		
		ret = dibusb_init(dib);
	}
	
	if (ret == 0)
		info("%s successfully initialized and connected.",dibdev->name);
	else 
		info("%s error while loading driver (%d)",dibdev->name,ret);
	return ret;
}

static void dibusb_disconnect(struct usb_interface *intf)
{
	struct usb_dibusb *dib = usb_get_intfdata(intf);
	const char *name = DRIVER_DESC;
	
	usb_set_intfdata(intf,NULL);
	if (dib != NULL && dib->dibdev != NULL) {
		name = dib->dibdev->name;
		dibusb_exit(dib);
	}
	info("%s successfully deinitialized and disconnected.",name);
	
}

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver dibusb_driver = {
	.owner		= THIS_MODULE,
	.name		= DRIVER_DESC,
	.probe 		= dibusb_probe,
	.disconnect = dibusb_disconnect,
	.id_table 	= dib_table,
};

/* module stuff */
static int __init usb_dibusb_init(void)
{
	int result;
	if ((result = usb_register(&dibusb_driver))) {
		err("usb_register failed. Error number %d",result);
		return result;
	}
	
	return 0;
}

static void __exit usb_dibusb_exit(void)
{
	/* deregister this driver from the USB subsystem */
	usb_deregister(&dibusb_driver);
}

module_init (usb_dibusb_init);
module_exit (usb_dibusb_exit);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");