1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card |
3 | * |
4 | * Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org) |
5 | * Copyright (C) 2006 Marco Gittler (g.marco@freenet.de) |
6 | * |
7 | * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information |
8 | */ |
9 | |
10 | #define DVB_USB_LOG_PREFIX "opera" |
11 | |
12 | #include "dvb-usb.h" |
13 | #include "stv0299.h" |
14 | |
15 | #define OPERA_READ_MSG 0 |
16 | #define OPERA_WRITE_MSG 1 |
17 | #define OPERA_I2C_TUNER 0xd1 |
18 | |
19 | #define READ_FX2_REG_REQ 0xba |
20 | #define READ_MAC_ADDR 0x08 |
21 | #define OPERA_WRITE_FX2 0xbb |
22 | #define OPERA_TUNER_REQ 0xb1 |
23 | #define REG_1F_SYMBOLRATE_BYTE0 0x1f |
24 | #define REG_20_SYMBOLRATE_BYTE1 0x20 |
25 | #define REG_21_SYMBOLRATE_BYTE2 0x21 |
26 | |
27 | #define ADDR_B600_VOLTAGE_13V (0x02) |
28 | #define ADDR_B601_VOLTAGE_18V (0x03) |
29 | #define ADDR_B1A6_STREAM_CTRL (0x04) |
30 | #define ADDR_B880_READ_REMOTE (0x05) |
31 | |
32 | struct opera1_state { |
33 | u32 last_key_pressed; |
34 | }; |
35 | struct rc_map_opera_table { |
36 | u32 keycode; |
37 | u32 event; |
38 | }; |
39 | |
40 | static int dvb_usb_opera1_debug; |
41 | module_param_named(debug, dvb_usb_opera1_debug, int, 0644); |
42 | MODULE_PARM_DESC(debug, |
43 | "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))." |
44 | DVB_USB_DEBUG_STATUS); |
45 | |
46 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
47 | |
48 | |
49 | static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value, |
50 | u8 * data, u16 len, int flags) |
51 | { |
52 | int ret; |
53 | u8 tmp; |
54 | u8 *buf; |
55 | unsigned int pipe = (flags == OPERA_READ_MSG) ? |
56 | usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0); |
57 | u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; |
58 | |
59 | buf = kmalloc(size: len, GFP_KERNEL); |
60 | if (!buf) |
61 | return -ENOMEM; |
62 | |
63 | if (flags == OPERA_WRITE_MSG) |
64 | memcpy(buf, data, len); |
65 | ret = usb_control_msg(dev, pipe, request, |
66 | requesttype: request_type | USB_TYPE_VENDOR, value, index: 0x0, |
67 | data: buf, size: len, timeout: 2000); |
68 | |
69 | if (request == OPERA_TUNER_REQ) { |
70 | tmp = buf[0]; |
71 | if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), |
72 | OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR, |
73 | value: 0x01, index: 0x0, data: buf, size: 1, timeout: 2000) < 1 || buf[0] != 0x08) { |
74 | ret = 0; |
75 | goto out; |
76 | } |
77 | buf[0] = tmp; |
78 | } |
79 | if (flags == OPERA_READ_MSG) |
80 | memcpy(data, buf, len); |
81 | out: |
82 | kfree(objp: buf); |
83 | return ret; |
84 | } |
85 | |
86 | /* I2C */ |
87 | |
88 | static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr, |
89 | u8 * buf, u16 len) |
90 | { |
91 | int ret = 0; |
92 | u8 request; |
93 | u16 value; |
94 | |
95 | if (!dev) { |
96 | info("no usb_device" ); |
97 | return -EINVAL; |
98 | } |
99 | if (mutex_lock_interruptible(&dev->usb_mutex) < 0) |
100 | return -EAGAIN; |
101 | |
102 | switch (addr>>1){ |
103 | case ADDR_B600_VOLTAGE_13V: |
104 | request=0xb6; |
105 | value=0x00; |
106 | break; |
107 | case ADDR_B601_VOLTAGE_18V: |
108 | request=0xb6; |
109 | value=0x01; |
110 | break; |
111 | case ADDR_B1A6_STREAM_CTRL: |
112 | request=0xb1; |
113 | value=0xa6; |
114 | break; |
115 | case ADDR_B880_READ_REMOTE: |
116 | request=0xb8; |
117 | value=0x80; |
118 | break; |
119 | default: |
120 | request=0xb1; |
121 | value=addr; |
122 | } |
123 | ret = opera1_xilinx_rw(dev: dev->udev, request, |
124 | value, data: buf, len, |
125 | flags: addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG); |
126 | |
127 | mutex_unlock(lock: &dev->usb_mutex); |
128 | return ret; |
129 | } |
130 | |
131 | static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], |
132 | int num) |
133 | { |
134 | struct dvb_usb_device *d = i2c_get_adapdata(adap); |
135 | int i = 0, tmp = 0; |
136 | |
137 | if (!d) |
138 | return -ENODEV; |
139 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) |
140 | return -EAGAIN; |
141 | |
142 | for (i = 0; i < num; i++) { |
143 | if ((tmp = opera1_usb_i2c_msgxfer(dev: d, |
144 | addr: (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), |
145 | buf: msg[i].buf, |
146 | len: msg[i].len |
147 | )) != msg[i].len) { |
148 | break; |
149 | } |
150 | if (dvb_usb_opera1_debug & 0x10) |
151 | info("sending i2c message %d %d" , tmp, msg[i].len); |
152 | } |
153 | mutex_unlock(lock: &d->i2c_mutex); |
154 | return num; |
155 | } |
156 | |
157 | static u32 opera1_i2c_func(struct i2c_adapter *adapter) |
158 | { |
159 | return I2C_FUNC_I2C; |
160 | } |
161 | |
162 | static struct i2c_algorithm opera1_i2c_algo = { |
163 | .master_xfer = opera1_i2c_xfer, |
164 | .functionality = opera1_i2c_func, |
165 | }; |
166 | |
167 | static int opera1_set_voltage(struct dvb_frontend *fe, |
168 | enum fe_sec_voltage voltage) |
169 | { |
170 | static u8 command_13v[1]={0x00}; |
171 | static u8 command_18v[1]={0x01}; |
172 | struct i2c_msg msg[] = { |
173 | {.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1}, |
174 | }; |
175 | struct dvb_usb_adapter *udev_adap = fe->dvb->priv; |
176 | if (voltage == SEC_VOLTAGE_18) { |
177 | msg[0].addr = ADDR_B601_VOLTAGE_18V; |
178 | msg[0].buf = command_18v; |
179 | } |
180 | i2c_transfer(adap: &udev_adap->dev->i2c_adap, msgs: msg, num: 1); |
181 | return 0; |
182 | } |
183 | |
184 | static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, |
185 | u32 ratio) |
186 | { |
187 | stv0299_writereg(fe, reg: 0x13, val: 0x98); |
188 | stv0299_writereg(fe, reg: 0x14, val: 0x95); |
189 | stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, val: (ratio >> 16) & 0xff); |
190 | stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, val: (ratio >> 8) & 0xff); |
191 | stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, val: (ratio) & 0xf0); |
192 | return 0; |
193 | |
194 | } |
195 | static u8 opera1_inittab[] = { |
196 | 0x00, 0xa1, |
197 | 0x01, 0x15, |
198 | 0x02, 0x30, |
199 | 0x03, 0x00, |
200 | 0x04, 0x7d, |
201 | 0x05, 0x05, |
202 | 0x06, 0x02, |
203 | 0x07, 0x00, |
204 | 0x0b, 0x00, |
205 | 0x0c, 0x01, |
206 | 0x0d, 0x81, |
207 | 0x0e, 0x44, |
208 | 0x0f, 0x19, |
209 | 0x10, 0x3f, |
210 | 0x11, 0x84, |
211 | 0x12, 0xda, |
212 | 0x13, 0x98, |
213 | 0x14, 0x95, |
214 | 0x15, 0xc9, |
215 | 0x16, 0xeb, |
216 | 0x17, 0x00, |
217 | 0x18, 0x19, |
218 | 0x19, 0x8b, |
219 | 0x1a, 0x00, |
220 | 0x1b, 0x82, |
221 | 0x1c, 0x7f, |
222 | 0x1d, 0x00, |
223 | 0x1e, 0x00, |
224 | REG_1F_SYMBOLRATE_BYTE0, 0x06, |
225 | REG_20_SYMBOLRATE_BYTE1, 0x50, |
226 | REG_21_SYMBOLRATE_BYTE2, 0x10, |
227 | 0x22, 0x00, |
228 | 0x23, 0x00, |
229 | 0x24, 0x37, |
230 | 0x25, 0xbc, |
231 | 0x26, 0x00, |
232 | 0x27, 0x00, |
233 | 0x28, 0x00, |
234 | 0x29, 0x1e, |
235 | 0x2a, 0x14, |
236 | 0x2b, 0x1f, |
237 | 0x2c, 0x09, |
238 | 0x2d, 0x0a, |
239 | 0x2e, 0x00, |
240 | 0x2f, 0x00, |
241 | 0x30, 0x00, |
242 | 0x31, 0x1f, |
243 | 0x32, 0x19, |
244 | 0x33, 0xfc, |
245 | 0x34, 0x13, |
246 | 0xff, 0xff, |
247 | }; |
248 | |
249 | static struct stv0299_config opera1_stv0299_config = { |
250 | .demod_address = 0xd0>>1, |
251 | .min_delay_ms = 100, |
252 | .mclk = 88000000UL, |
253 | .invert = 1, |
254 | .skip_reinit = 0, |
255 | .lock_output = STV0299_LOCKOUTPUT_0, |
256 | .volt13_op0_op1 = STV0299_VOLT13_OP0, |
257 | .inittab = opera1_inittab, |
258 | .set_symbol_rate = opera1_stv0299_set_symbol_rate, |
259 | }; |
260 | |
261 | static int opera1_frontend_attach(struct dvb_usb_adapter *d) |
262 | { |
263 | d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config, |
264 | &d->dev->i2c_adap); |
265 | if ((d->fe_adap[0].fe) != NULL) { |
266 | d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage; |
267 | return 0; |
268 | } |
269 | info("not attached stv0299" ); |
270 | return -EIO; |
271 | } |
272 | |
273 | static int opera1_tuner_attach(struct dvb_usb_adapter *adap) |
274 | { |
275 | dvb_attach( |
276 | dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1, |
277 | &adap->dev->i2c_adap, DVB_PLL_OPERA1 |
278 | ); |
279 | return 0; |
280 | } |
281 | |
282 | static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff) |
283 | { |
284 | u8 val = onoff ? 0x01 : 0x00; |
285 | |
286 | if (dvb_usb_opera1_debug) |
287 | info("power %s" , onoff ? "on" : "off" ); |
288 | return opera1_xilinx_rw(dev: d->udev, request: 0xb7, value: val, |
289 | data: &val, len: 1, OPERA_WRITE_MSG); |
290 | } |
291 | |
292 | static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) |
293 | { |
294 | static u8 buf_start[2] = { 0xff, 0x03 }; |
295 | static u8 buf_stop[2] = { 0xff, 0x00 }; |
296 | struct i2c_msg start_tuner[] = { |
297 | {.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2}, |
298 | }; |
299 | if (dvb_usb_opera1_debug) |
300 | info("streaming %s" , onoff ? "on" : "off" ); |
301 | i2c_transfer(adap: &adap->dev->i2c_adap, msgs: start_tuner, num: 1); |
302 | return 0; |
303 | } |
304 | |
305 | static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, |
306 | int onoff) |
307 | { |
308 | u8 b_pid[3]; |
309 | struct i2c_msg msg[] = { |
310 | {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, |
311 | }; |
312 | if (dvb_usb_opera1_debug) |
313 | info("pidfilter index: %d pid: %d %s" , index, pid, |
314 | onoff ? "on" : "off" ); |
315 | b_pid[0] = (2 * index) + 4; |
316 | b_pid[1] = onoff ? (pid & 0xff) : (0x00); |
317 | b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00); |
318 | i2c_transfer(adap: &adap->dev->i2c_adap, msgs: msg, num: 1); |
319 | return 0; |
320 | } |
321 | |
322 | static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) |
323 | { |
324 | int u = 0x04; |
325 | u8 b_pid[3]; |
326 | struct i2c_msg msg[] = { |
327 | {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, |
328 | }; |
329 | if (dvb_usb_opera1_debug) |
330 | info("%s hw-pidfilter" , onoff ? "enable" : "disable" ); |
331 | for (; u < 0x7e; u += 2) { |
332 | b_pid[0] = u; |
333 | b_pid[1] = 0; |
334 | b_pid[2] = 0x80; |
335 | i2c_transfer(adap: &adap->dev->i2c_adap, msgs: msg, num: 1); |
336 | } |
337 | return 0; |
338 | } |
339 | |
340 | static struct rc_map_table rc_map_opera1_table[] = { |
341 | {0x5fa0, KEY_1}, |
342 | {0x51af, KEY_2}, |
343 | {0x5da2, KEY_3}, |
344 | {0x41be, KEY_4}, |
345 | {0x0bf5, KEY_5}, |
346 | {0x43bd, KEY_6}, |
347 | {0x47b8, KEY_7}, |
348 | {0x49b6, KEY_8}, |
349 | {0x05fa, KEY_9}, |
350 | {0x45ba, KEY_0}, |
351 | {0x09f6, KEY_CHANNELUP}, /*chanup */ |
352 | {0x1be5, KEY_CHANNELDOWN}, /*chandown */ |
353 | {0x5da3, KEY_VOLUMEDOWN}, /*voldown */ |
354 | {0x5fa1, KEY_VOLUMEUP}, /*volup */ |
355 | {0x07f8, KEY_SPACE}, /*tab */ |
356 | {0x1fe1, KEY_OK}, /*play ok */ |
357 | {0x1be4, KEY_ZOOM}, /*zoom */ |
358 | {0x59a6, KEY_MUTE}, /*mute */ |
359 | {0x5ba5, KEY_RADIO}, /*tv/f */ |
360 | {0x19e7, KEY_RECORD}, /*rec */ |
361 | {0x01fe, KEY_STOP}, /*Stop */ |
362 | {0x03fd, KEY_PAUSE}, /*pause */ |
363 | {0x03fc, KEY_SCREEN}, /*<- -> */ |
364 | {0x07f9, KEY_CAMERA}, /*capture */ |
365 | {0x47b9, KEY_ESC}, /*exit */ |
366 | {0x43bc, KEY_POWER2}, /*power */ |
367 | }; |
368 | |
369 | static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) |
370 | { |
371 | struct opera1_state *opst = dev->priv; |
372 | u8 rcbuffer[32]; |
373 | const u16 startmarker1 = 0x10ed; |
374 | const u16 startmarker2 = 0x11ec; |
375 | struct i2c_msg read_remote[] = { |
376 | {.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32}, |
377 | }; |
378 | int i = 0; |
379 | u32 send_key = 0; |
380 | |
381 | if (i2c_transfer(adap: &dev->i2c_adap, msgs: read_remote, num: 1) == 1) { |
382 | for (i = 0; i < 32; i++) { |
383 | if (rcbuffer[i]) |
384 | send_key |= 1; |
385 | if (i < 31) |
386 | send_key = send_key << 1; |
387 | } |
388 | if (send_key & 0x8000) |
389 | send_key = (send_key << 1) | (send_key >> 15 & 0x01); |
390 | |
391 | if (send_key == 0xffff && opst->last_key_pressed != 0) { |
392 | *state = REMOTE_KEY_REPEAT; |
393 | *event = opst->last_key_pressed; |
394 | return 0; |
395 | } |
396 | for (; send_key != 0;) { |
397 | if (send_key >> 16 == startmarker2) { |
398 | break; |
399 | } else if (send_key >> 16 == startmarker1) { |
400 | send_key = |
401 | (send_key & 0xfffeffff) | (startmarker1 << 16); |
402 | break; |
403 | } else |
404 | send_key >>= 1; |
405 | } |
406 | |
407 | if (send_key == 0) |
408 | return 0; |
409 | |
410 | send_key = (send_key & 0xffff) | 0x0100; |
411 | |
412 | for (i = 0; i < ARRAY_SIZE(rc_map_opera1_table); i++) { |
413 | if (rc5_scan(key: &rc_map_opera1_table[i]) == (send_key & 0xffff)) { |
414 | *state = REMOTE_KEY_PRESSED; |
415 | *event = rc_map_opera1_table[i].keycode; |
416 | opst->last_key_pressed = |
417 | rc_map_opera1_table[i].keycode; |
418 | break; |
419 | } |
420 | opst->last_key_pressed = 0; |
421 | } |
422 | } else |
423 | *state = REMOTE_NO_KEY_PRESSED; |
424 | return 0; |
425 | } |
426 | |
427 | enum { |
428 | CYPRESS_OPERA1_COLD, |
429 | OPERA1_WARM, |
430 | }; |
431 | |
432 | static struct usb_device_id opera1_table[] = { |
433 | DVB_USB_DEV(CYPRESS, CYPRESS_OPERA1_COLD), |
434 | DVB_USB_DEV(OPERA1, OPERA1_WARM), |
435 | { } |
436 | }; |
437 | |
438 | MODULE_DEVICE_TABLE(usb, opera1_table); |
439 | |
440 | static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) |
441 | { |
442 | int ret; |
443 | u8 command[] = { READ_MAC_ADDR }; |
444 | ret = opera1_xilinx_rw(dev: d->udev, request: 0xb1, value: 0xa0, data: command, len: 1, OPERA_WRITE_MSG); |
445 | if (ret) |
446 | return ret; |
447 | ret = opera1_xilinx_rw(dev: d->udev, request: 0xb1, value: 0xa1, data: mac, len: 6, OPERA_READ_MSG); |
448 | if (ret) |
449 | return ret; |
450 | return 0; |
451 | } |
452 | static int opera1_xilinx_load_firmware(struct usb_device *dev, |
453 | const char *filename) |
454 | { |
455 | const struct firmware *fw = NULL; |
456 | u8 *b, *p; |
457 | int ret = 0, i,fpgasize=40; |
458 | u8 testval; |
459 | info("start downloading fpga firmware %s" ,filename); |
460 | |
461 | if ((ret = request_firmware(fw: &fw, name: filename, device: &dev->dev)) != 0) { |
462 | err("did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware" , |
463 | filename); |
464 | return ret; |
465 | } else { |
466 | p = kmalloc(size: fw->size, GFP_KERNEL); |
467 | opera1_xilinx_rw(dev, request: 0xbc, value: 0x00, data: &testval, len: 1, OPERA_READ_MSG); |
468 | if (p != NULL && testval != 0x67) { |
469 | |
470 | u8 reset = 0, fpga_command = 0; |
471 | memcpy(p, fw->data, fw->size); |
472 | /* clear fpga ? */ |
473 | opera1_xilinx_rw(dev, request: 0xbc, value: 0xaa, data: &fpga_command, len: 1, |
474 | OPERA_WRITE_MSG); |
475 | for (i = 0; i < fw->size;) { |
476 | if ( (fw->size - i) <fpgasize){ |
477 | fpgasize=fw->size-i; |
478 | } |
479 | b = (u8 *) p + i; |
480 | if (opera1_xilinx_rw |
481 | (dev, OPERA_WRITE_FX2, value: 0x0, data: b , len: fpgasize, |
482 | OPERA_WRITE_MSG) != fpgasize |
483 | ) { |
484 | err("error while transferring firmware" ); |
485 | ret = -EINVAL; |
486 | break; |
487 | } |
488 | i = i + fpgasize; |
489 | } |
490 | /* restart the CPU */ |
491 | if (ret || opera1_xilinx_rw |
492 | (dev, request: 0xa0, value: 0xe600, data: &reset, len: 1, |
493 | OPERA_WRITE_MSG) != 1) { |
494 | err("could not restart the USB controller CPU." ); |
495 | ret = -EINVAL; |
496 | } |
497 | } |
498 | } |
499 | kfree(objp: p); |
500 | release_firmware(fw); |
501 | return ret; |
502 | } |
503 | |
504 | static struct dvb_usb_device_properties opera1_properties = { |
505 | .caps = DVB_USB_IS_AN_I2C_ADAPTER, |
506 | .usb_ctrl = CYPRESS_FX2, |
507 | .firmware = "dvb-usb-opera-01.fw" , |
508 | .size_of_priv = sizeof(struct opera1_state), |
509 | |
510 | .power_ctrl = opera1_power_ctrl, |
511 | .i2c_algo = &opera1_i2c_algo, |
512 | |
513 | .rc.legacy = { |
514 | .rc_map_table = rc_map_opera1_table, |
515 | .rc_map_size = ARRAY_SIZE(rc_map_opera1_table), |
516 | .rc_interval = 200, |
517 | .rc_query = opera1_rc_query, |
518 | }, |
519 | .read_mac_address = opera1_read_mac_address, |
520 | .generic_bulk_ctrl_endpoint = 0x00, |
521 | /* parameter for the MPEG2-data transfer */ |
522 | .num_adapters = 1, |
523 | .adapter = { |
524 | { |
525 | .num_frontends = 1, |
526 | .fe = {{ |
527 | .frontend_attach = opera1_frontend_attach, |
528 | .streaming_ctrl = opera1_streaming_ctrl, |
529 | .tuner_attach = opera1_tuner_attach, |
530 | .caps = |
531 | DVB_USB_ADAP_HAS_PID_FILTER | |
532 | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, |
533 | .pid_filter = opera1_pid_filter, |
534 | .pid_filter_ctrl = opera1_pid_filter_control, |
535 | .pid_filter_count = 252, |
536 | .stream = { |
537 | .type = USB_BULK, |
538 | .count = 10, |
539 | .endpoint = 0x82, |
540 | .u = { |
541 | .bulk = { |
542 | .buffersize = 4096, |
543 | } |
544 | } |
545 | }, |
546 | }}, |
547 | } |
548 | }, |
549 | .num_device_descs = 1, |
550 | .devices = { |
551 | {"Opera1 DVB-S USB2.0" , |
552 | {&opera1_table[CYPRESS_OPERA1_COLD], NULL}, |
553 | {&opera1_table[OPERA1_WARM], NULL}, |
554 | }, |
555 | } |
556 | }; |
557 | |
558 | static int opera1_probe(struct usb_interface *intf, |
559 | const struct usb_device_id *id) |
560 | { |
561 | struct usb_device *udev = interface_to_usbdev(intf); |
562 | |
563 | if (le16_to_cpu(udev->descriptor.idProduct) == USB_PID_OPERA1_WARM && |
564 | le16_to_cpu(udev->descriptor.idVendor) == USB_VID_OPERA1 && |
565 | opera1_xilinx_load_firmware(dev: udev, filename: "dvb-usb-opera1-fpga-01.fw" ) != 0 |
566 | ) { |
567 | return -EINVAL; |
568 | } |
569 | |
570 | if (0 != dvb_usb_device_init(intf, &opera1_properties, |
571 | THIS_MODULE, NULL, adapter_nums: adapter_nr)) |
572 | return -EINVAL; |
573 | return 0; |
574 | } |
575 | |
576 | static struct usb_driver opera1_driver = { |
577 | .name = "opera1" , |
578 | .probe = opera1_probe, |
579 | .disconnect = dvb_usb_device_exit, |
580 | .id_table = opera1_table, |
581 | }; |
582 | |
583 | module_usb_driver(opera1_driver); |
584 | |
585 | MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org" ); |
586 | MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de" ); |
587 | MODULE_DESCRIPTION("Driver for Opera1 DVB-S device" ); |
588 | MODULE_VERSION("0.1" ); |
589 | MODULE_LICENSE("GPL" ); |
590 | |