blob: c186736ea8f76a350135c3cf13deb63fa1c93688 [file] [log] [blame]
Antti Palosaari831e0b72011-07-08 23:36:07 -03001/*
2 * Realtek RTL28xxU DVB USB driver
3 *
4 * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
5 * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "rtl28xxu.h"
23
24#include "rtl2830.h"
25
26#include "qt1010.h"
27#include "mt2060.h"
28#include "mxl5005s.h"
29
30/* debug */
31static int dvb_usb_rtl28xxu_debug;
32module_param_named(debug, dvb_usb_rtl28xxu_debug, int, 0644);
33MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
34DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
35
36static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
37{
38 int ret;
39 unsigned int pipe;
40 u8 requesttype;
41 u8 *buf;
42
43 buf = kmalloc(req->size, GFP_KERNEL);
44 if (!buf) {
45 ret = -ENOMEM;
46 goto err;
47 }
48
49 if (req->index & CMD_WR_FLAG) {
50 /* write */
51 memcpy(buf, req->data, req->size);
52 requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
53 pipe = usb_sndctrlpipe(d->udev, 0);
54 } else {
55 /* read */
56 requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
57 pipe = usb_rcvctrlpipe(d->udev, 0);
58 }
59
60 ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
61 req->index, buf, req->size, 1000);
62
63 deb_dump(0, requesttype, req->value, req->index, buf, req->size,
64 deb_xfer);
65
66 if (ret < 0)
67 goto err_dealloc;
68 else
69 ret = 0;
70
71 /* read request, copy returned data to return buf */
72 if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
73 memcpy(req->data, buf, req->size);
74
75 kfree(buf);
76 return ret;
77
78err_dealloc:
79 kfree(buf);
80err:
81 deb_info("%s: failed=%d\n", __func__, ret);
82 return ret;
83}
84
85static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
86{
87 struct rtl28xxu_req req;
88
89 if (reg < 0x3000)
90 req.index = CMD_USB_WR;
91 else
92 req.index = CMD_SYS_WR;
93
94 req.value = reg;
95 req.size = len;
96 req.data = val;
97
98 return rtl28xxu_ctrl_msg(d, &req);
99}
100
101static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
102{
103 struct rtl28xxu_req req;
104
105 if (reg < 0x3000)
106 req.index = CMD_USB_RD;
107 else
108 req.index = CMD_SYS_RD;
109
110 req.value = reg;
111 req.size = len;
112 req.data = val;
113
114 return rtl28xxu_ctrl_msg(d, &req);
115}
116
117static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
118{
119 return rtl2831_wr_regs(d, reg, &val, 1);
120}
121
122static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
123{
124 return rtl2831_rd_regs(d, reg, val, 1);
125}
126
127/* I2C */
128static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
129 int num)
130{
131 int ret;
132 struct dvb_usb_device *d = i2c_get_adapdata(adap);
133 struct rtl28xxu_req req;
134
135 /*
136 * It is not known which are real I2C bus xfer limits, but testing
137 * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes.
138 */
139
140 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
141 return -EAGAIN;
142
143 if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
144 (msg[1].flags & I2C_M_RD)) {
145 if (msg[0].len > 2 || msg[1].len > 24) {
146 ret = -EOPNOTSUPP;
147 goto err_unlock;
148 }
149 if (msg[0].addr == 0x10) {
150 /* integrated demod */
151 req.value = (msg[0].buf[1] << 8) | (msg[0].addr << 1);
152 req.index = CMD_DEMOD_RD | msg[0].buf[0];
153 req.size = msg[1].len;
154 req.data = &msg[1].buf[0];
155 ret = rtl28xxu_ctrl_msg(d, &req);
156 } else {
157 /* real I2C */
158 req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
159 req.index = CMD_I2C_RD;
160 req.size = msg[1].len;
161 req.data = &msg[1].buf[0];
162 ret = rtl28xxu_ctrl_msg(d, &req);
163 }
164 } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
165 if (msg[0].len > 22) {
166 ret = -EOPNOTSUPP;
167 goto err_unlock;
168 }
169 if (msg[0].addr == 0x10) {
170 /* integrated demod */
171 req.value = (msg[0].buf[1] << 8) | (msg[0].addr << 1);
172 req.index = CMD_DEMOD_WR | msg[0].buf[0];
173 req.size = msg[0].len-2;
174 req.data = &msg[0].buf[2];
175 ret = rtl28xxu_ctrl_msg(d, &req);
176 } else {
177 /* real I2C */
178 req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
179 req.index = CMD_I2C_WR;
180 req.size = msg[0].len-1;
181 req.data = &msg[0].buf[1];
182 ret = rtl28xxu_ctrl_msg(d, &req);
183 }
184 } else {
185 ret = -EINVAL;
186 }
187
188err_unlock:
189 mutex_unlock(&d->i2c_mutex);
190
191 return ret ? ret : num;
192}
193
194static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter)
195{
196 return I2C_FUNC_I2C;
197}
198
199static struct i2c_algorithm rtl28xxu_i2c_algo = {
200 .master_xfer = rtl28xxu_i2c_xfer,
201 .functionality = rtl28xxu_i2c_func,
202};
203
204static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
205 .i2c_addr = 0x10, /* 0x20 */
206 .xtal = 28800000,
207 .ts_mode = 0,
208 .spec_inv = 1,
209 .if_dvbt = 36150000,
210 .vtop = 0x20,
211 .krf = 0x04,
212 .agc_targ_val = 0x2d,
213
214};
215
216static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
217 .i2c_addr = 0x10, /* 0x20 */
218 .xtal = 28800000,
219 .ts_mode = 0,
220 .spec_inv = 1,
221 .if_dvbt = 36125000,
222 .vtop = 0x20,
223 .krf = 0x04,
224 .agc_targ_val = 0x2d,
225};
226
227static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
228 .i2c_addr = 0x10, /* 0x20 */
229 .xtal = 28800000,
230 .ts_mode = 0,
231 .spec_inv = 0,
232 .if_dvbt = 4570000,
233 .vtop = 0x3f,
234 .krf = 0x04,
235 .agc_targ_val = 0x3e,
236};
237
238static int rtl28xxu_frontend_attach(struct dvb_usb_adapter *adap)
239{
240 int ret;
241 struct rtl28xxu_priv *priv = adap->dev->priv;
242 u8 buf[1];
243 struct rtl2830_config *rtl2830_config;
244 /* for MT2060 tuner probe */
245 struct rtl28xxu_req req_mt2060 = {0x00c0, CMD_I2C_RD, 1, buf};
246 /* for QT1010 tuner probe */
247 struct rtl28xxu_req req_qt1010 = {0x0fc4, CMD_I2C_RD, 1, buf};
248 /* open RTL2831U/RTL2830 I2C gate */
249 struct rtl28xxu_req req_gate = {0x0120, 0x0011, 0x0001, "\x08"};
250
251 deb_info("%s:\n", __func__);
252
253 /*
254 * RTL2831U GPIOs
255 * =========================================================
256 * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?)
257 * GPIO2 | LED | 0 off | 1 on |
258 * GPIO4 | tuner#1 | 0 on | 1 off | MT2060
259 */
260
261 /* GPIO direction */
262 ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
263 if (ret)
264 goto err;
265
266 /* enable as output GPIO0, GPIO2, GPIO4 */
267 ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
268 if (ret)
269 goto err;
270
271 /*
272 * Probe used tuner. We need to know used tuner before demod attach
273 * since there is some demod params needed to set according to tuner.
274 */
275
276 /* open demod I2C gate */
277 ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
278 if (ret)
279 goto err;
280
281 /* check QT1010 ID(?) register; reg=0f val=2c */
282 ret = rtl28xxu_ctrl_msg(adap->dev, &req_qt1010);
283 if (ret == 0 && buf[0] == 0x2c) {
284 priv->tuner = TUNER_RTL2830_QT1010;
285 rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
286 deb_info("%s: QT1010\n", __func__);
287 goto found;
288 } else {
289 deb_info("%s: QT1010 probe failed=%d - %02x\n",
290 __func__, ret, buf[0]);
291 }
292
293 /* open demod I2C gate */
294 ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
295 if (ret)
296 goto err;
297
298 /* check MT2060 ID register; reg=00 val=63 */
299 ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2060);
300 if (ret == 0 && buf[0] == 0x63) {
301 priv->tuner = TUNER_RTL2830_MT2060;
302 rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
303 deb_info("%s: MT2060\n", __func__);
304 goto found;
305 } else {
306 deb_info("%s: MT2060 probe failed=%d - %02x\n",
307 __func__, ret, buf[0]);
308 }
309
310 /* assume MXL5005S */
Antti Palosaarie9320ec2011-08-03 04:35:30 -0300311 ret = 0;
Antti Palosaari831e0b72011-07-08 23:36:07 -0300312 priv->tuner = TUNER_RTL2830_MXL5005S;
313 rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
314 deb_info("%s: MXL5005S\n", __func__);
315 goto found;
316
317found:
318 /* attach demodulator */
319 adap->fe[0] = dvb_attach(rtl2830_attach, rtl2830_config,
320 &adap->dev->i2c_adap);
321 if (adap->fe[0] == NULL) {
322 ret = -ENODEV;
323 goto err;
324 }
325
326 return ret;
327err:
328 deb_info("%s: failed=%d\n", __func__, ret);
329 return ret;
330}
331
332static struct qt1010_config rtl28xxu_qt1010_config = {
333 .i2c_address = 0x62, /* 0xc4 */
334};
335
336static struct mt2060_config rtl28xxu_mt2060_config = {
337 .i2c_address = 0x60, /* 0xc0 */
338 .clock_out = 0,
339};
340
341static struct mxl5005s_config rtl28xxu_mxl5005s_config = {
342 .i2c_address = 0x63, /* 0xc6 */
343 .if_freq = IF_FREQ_4570000HZ,
344 .xtal_freq = CRYSTAL_FREQ_16000000HZ,
345 .agc_mode = MXL_SINGLE_AGC,
346 .tracking_filter = MXL_TF_C_H,
347 .rssi_enable = MXL_RSSI_ENABLE,
348 .cap_select = MXL_CAP_SEL_ENABLE,
349 .div_out = MXL_DIV_OUT_4,
350 .clock_out = MXL_CLOCK_OUT_DISABLE,
351 .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
352 .top = MXL5005S_TOP_25P2,
353 .mod_mode = MXL_DIGITAL_MODE,
354 .if_mode = MXL_ZERO_IF,
355 .AgcMasterByte = 0x00,
356};
357
358static int rtl28xxu_tuner_attach(struct dvb_usb_adapter *adap)
359{
360 int ret;
361 struct rtl28xxu_priv *priv = adap->dev->priv;
362 struct i2c_adapter *rtl2830_tuner_i2c;
363 struct dvb_frontend *fe = NULL;
364
365 deb_info("%s:\n", __func__);
366
367 /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
368 rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe[0]);
369
370 switch (priv->tuner) {
371 case TUNER_RTL2830_QT1010:
372 fe = dvb_attach(qt1010_attach, adap->fe[0], rtl2830_tuner_i2c,
373 &rtl28xxu_qt1010_config);
374 break;
375 case TUNER_RTL2830_MT2060:
376 fe = dvb_attach(mt2060_attach, adap->fe[0], rtl2830_tuner_i2c,
377 &rtl28xxu_mt2060_config, 1220);
378 break;
379 case TUNER_RTL2830_MXL5005S:
380 fe = dvb_attach(mxl5005s_attach, adap->fe[0], rtl2830_tuner_i2c,
381 &rtl28xxu_mxl5005s_config);
382 break;
383 default:
384 err("unknown tuner=%d", priv->tuner);
385 }
386
387 if (fe == NULL) {
388 ret = -ENODEV;
389 goto err;
390 }
391
392 return 0;
393err:
394 deb_info("%s: failed=%d\n", __func__, ret);
395 return ret;
396}
397
398static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
399{
400 int ret;
401 u8 buf[2], gpio;
402
403 deb_info("%s: onoff=%d\n", __func__, onoff);
404
405 ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
406 if (ret)
407 goto err;
408
409 if (onoff) {
410 buf[0] = 0x00;
411 buf[1] = 0x00;
412 gpio |= 0x04; /* LED on */
413 } else {
414 buf[0] = 0x10; /* stall EPA */
415 buf[1] = 0x02; /* reset EPA */
416 gpio &= (~0x04); /* LED off */
417 }
418
419 ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
420 if (ret)
421 goto err;
422
423 ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
424 if (ret)
425 goto err;
426
427 return ret;
428err:
429 deb_info("%s: failed=%d\n", __func__, ret);
430 return ret;
431}
432
433static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
434{
435 int ret;
436 u8 gpio, sys0;
437
438 deb_info("%s: onoff=%d\n", __func__, onoff);
439
440 /* demod adc */
441 ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0);
442 if (ret)
443 goto err;
444
445 /* tuner power, read GPIOs */
446 ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
447 if (ret)
448 goto err;
449
450 deb_info("%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
451
452 if (onoff) {
453 gpio |= 0x01; /* GPIO0 = 1 */
454 gpio &= (~0x10); /* GPIO4 = 0 */
455 sys0 = sys0 & 0x0f;
456 sys0 |= 0xe0;
457 } else {
458
459#if 0 /* keep */
460 /*
461 * FIXME: Use .fe_ioctl_override() to prevent demod
462 * IOCTLs in case of device is powered off.
463 *
464 * For now we cannot power off device because most FE IOCTLs
465 * can be performed only when device is powered.
466 * Using IOCTLs when device is powered off will result errors
467 * because register access to demod fails.
468 */
469 gpio &= (~0x01); /* GPIO0 = 0 */
470 gpio |= 0x10; /* GPIO4 = 1 */
471 sys0 = sys0 & (~0xc0);
472#endif
473 }
474
475 deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
476
477 /* demod adc */
478 ret = rtl2831_wr_reg(d, SYS_SYS0, sys0);
479 if (ret)
480 goto err;
481
482 /* tuner power, write GPIOs */
483 ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
484 if (ret)
485 goto err;
486
487 return ret;
488err:
489 deb_info("%s: failed=%d\n", __func__, ret);
490 return ret;
491}
492
493static int rtl28xxu_rc_query(struct dvb_usb_device *d)
494{
495 int ret;
496 u8 buf[5];
497 u32 rc_code;
498
499 ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
500 if (ret)
501 goto err;
502
503 if (buf[4] & 0x01) {
504 if (buf[2] == (u8) ~buf[3]) {
505 if (buf[0] == (u8) ~buf[1]) {
506 /* NEC standard (16 bit) */
507 rc_code = buf[0] << 8 | buf[2];
508 } else {
509 /* NEC extended (24 bit) */
510 rc_code = buf[0] << 16 |
511 buf[1] << 8 | buf[2];
512 }
513 } else {
514 /* NEC full (32 bit) */
515 rc_code = buf[0] << 24 | buf[1] << 16 |
516 buf[2] << 8 | buf[3];
517 }
518
519 rc_keydown(d->rc_dev, rc_code, 0);
520
521 ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
522 if (ret)
523 goto err;
524
525 /* repeated intentionally to avoid extra keypress */
526 ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
527 if (ret)
528 goto err;
529 }
530
531 return ret;
532err:
533 deb_info("%s: failed=%d\n", __func__, ret);
534 return ret;
535}
536
537/* DVB USB Driver stuff */
538#define USB_VID_REALTEK 0x0bda
539#define USB_PID_RTL2831U 0x2831
540#define USB_PID_FREECOM 0x0160
541
542#define RTL2831U_0BDA_2831 0
543#define RTL2831U_14AA_0160 1
544
545static struct usb_device_id rtl28xxu_table[] = {
546 [RTL2831U_0BDA_2831] = {
547 USB_DEVICE(USB_VID_REALTEK, USB_PID_RTL2831U)},
548 [RTL2831U_14AA_0160] = {
549 USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM)},
550 {} /* terminating entry */
551};
552
553MODULE_DEVICE_TABLE(usb, rtl28xxu_table);
554
555static struct dvb_usb_device_properties rtl28xxu_properties[] = {
556 {
557 .caps = DVB_USB_IS_AN_I2C_ADAPTER,
558
559 .usb_ctrl = DEVICE_SPECIFIC,
560 .no_reconnect = 1,
561
562 .size_of_priv = sizeof(struct rtl28xxu_priv),
563
564 .num_adapters = 1,
565 .adapter = {
566 {
567 .frontend_attach = rtl28xxu_frontend_attach,
568 .tuner_attach = rtl28xxu_tuner_attach,
569 .streaming_ctrl = rtl28xxu_streaming_ctrl,
570 .stream = {
571 .type = USB_BULK,
572 .count = 6,
573 .endpoint = 0x81,
574 .u = {
575 .bulk = {
576 .buffersize = 4096,
577 }
578 }
579 },
580 }
581 },
582
583 .power_ctrl = rtl28xxu_power_ctrl,
584
585 .rc.core = {
586 .protocol = RC_TYPE_NEC,
587 .module_name = "rtl28xxu",
588 .rc_query = rtl28xxu_rc_query,
589 .rc_interval = 400,
590 .allowed_protos = RC_TYPE_NEC,
591 .rc_codes = RC_MAP_EMPTY,
592 },
593
594 .i2c_algo = &rtl28xxu_i2c_algo,
595
596 .num_device_descs = 2,
597 .devices = {
598 {
599 .name = "Realtek RTL2831U reference design",
600 .cold_ids = {NULL},
601 .warm_ids = {
602 &rtl28xxu_table[RTL2831U_0BDA_2831], NULL},
603 },
604 {
605 .name = "Freecom USB2.0 DVB-T",
606 .cold_ids = {NULL},
607 .warm_ids = {
608 &rtl28xxu_table[RTL2831U_14AA_0160], NULL},
609 },
610 }
611 },
612};
613
614static int rtl28xxu_probe(struct usb_interface *intf,
615 const struct usb_device_id *id)
616{
617 int ret, i;
618 int properties_count = ARRAY_SIZE(rtl28xxu_properties);
619 struct dvb_usb_device *d = NULL;
620 struct rtl28xxu_reg_val rc_nec_tab[] = {
621 { 0x3033, 0x80 },
622 { 0x3020, 0x43 },
623 { 0x3021, 0x16 },
624 { 0x3022, 0x16 },
625 { 0x3023, 0x5a },
626 { 0x3024, 0x2d },
627 { 0x3025, 0x16 },
628 { 0x3026, 0x01 },
629 { 0x3028, 0xb0 },
630 { 0x3029, 0x04 },
631 { 0x302c, 0x88 },
632 { 0x302e, 0x13 },
633 { 0x3030, 0xdf },
634 { 0x3031, 0x05 },
635 };
636
637 deb_info("%s: interface=%d\n", __func__,
638 intf->cur_altsetting->desc.bInterfaceNumber);
639
640 if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
641 return 0;
642
643 for (i = 0; i < properties_count; i++) {
644 ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i],
645 THIS_MODULE, &d, adapter_nr);
646 if (ret == 0 || ret != -ENODEV)
647 break;
648 }
649
650 if (ret)
651 goto err;
652
653 /* init USB endpoints */
654 ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09);
655 if (ret)
656 goto err;
657
658 ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
659 if (ret)
660 goto err;
661
662 ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
663 if (ret)
664 goto err;
665
666 /* init remote controller */
667 for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
668 ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg, rc_nec_tab[i].val);
669 if (ret)
670 goto err;
671 }
672
673 return ret;
674err:
675 deb_info("%s: failed=%d\n", __func__, ret);
676 return ret;
677}
678
679static struct usb_driver rtl28xxu_driver = {
680 .name = "dvb_usb_rtl28xxu",
681 .probe = rtl28xxu_probe,
682 .disconnect = dvb_usb_device_exit,
683 .id_table = rtl28xxu_table,
684};
685
686/* module stuff */
687static int __init rtl28xxu_module_init(void)
688{
689 int ret;
690 deb_info("%s:\n", __func__);
691 ret = usb_register(&rtl28xxu_driver);
692 if (ret)
693 err("usb_register failed=%d", ret);
694
695 return ret;
696}
697
698static void __exit rtl28xxu_module_exit(void)
699{
700 deb_info("%s:\n", __func__);
701 /* deregister this driver from the USB subsystem */
702 usb_deregister(&rtl28xxu_driver);
703}
704
705module_init(rtl28xxu_module_init);
706module_exit(rtl28xxu_module_exit);
707
708MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
709MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
710MODULE_LICENSE("GPL");