1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2
3*4882a593Smuzhiyun * DVB-T receiver.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun #include "dibusb.h"
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun static int debug;
12*4882a593Smuzhiyun module_param(debug, int, 0644);
13*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "set debugging level (1=rc,2=eeprom (|-able))." DVB_USB_DEBUG_STATUS);
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #define deb_rc(args...) dprintk(debug,0x01,args)
18*4882a593Smuzhiyun #define deb_ee(args...) dprintk(debug,0x02,args)
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun /* Hauppauge NOVA-T USB2 keys */
21*4882a593Smuzhiyun static struct rc_map_table rc_map_haupp_table[] = {
22*4882a593Smuzhiyun { 0x1e00, KEY_0 },
23*4882a593Smuzhiyun { 0x1e01, KEY_1 },
24*4882a593Smuzhiyun { 0x1e02, KEY_2 },
25*4882a593Smuzhiyun { 0x1e03, KEY_3 },
26*4882a593Smuzhiyun { 0x1e04, KEY_4 },
27*4882a593Smuzhiyun { 0x1e05, KEY_5 },
28*4882a593Smuzhiyun { 0x1e06, KEY_6 },
29*4882a593Smuzhiyun { 0x1e07, KEY_7 },
30*4882a593Smuzhiyun { 0x1e08, KEY_8 },
31*4882a593Smuzhiyun { 0x1e09, KEY_9 },
32*4882a593Smuzhiyun { 0x1e0a, KEY_KPASTERISK },
33*4882a593Smuzhiyun { 0x1e0b, KEY_RED },
34*4882a593Smuzhiyun { 0x1e0c, KEY_RADIO },
35*4882a593Smuzhiyun { 0x1e0d, KEY_MENU },
36*4882a593Smuzhiyun { 0x1e0e, KEY_GRAVE }, /* # */
37*4882a593Smuzhiyun { 0x1e0f, KEY_MUTE },
38*4882a593Smuzhiyun { 0x1e10, KEY_VOLUMEUP },
39*4882a593Smuzhiyun { 0x1e11, KEY_VOLUMEDOWN },
40*4882a593Smuzhiyun { 0x1e12, KEY_CHANNEL },
41*4882a593Smuzhiyun { 0x1e14, KEY_UP },
42*4882a593Smuzhiyun { 0x1e15, KEY_DOWN },
43*4882a593Smuzhiyun { 0x1e16, KEY_LEFT },
44*4882a593Smuzhiyun { 0x1e17, KEY_RIGHT },
45*4882a593Smuzhiyun { 0x1e18, KEY_VIDEO },
46*4882a593Smuzhiyun { 0x1e19, KEY_AUDIO },
47*4882a593Smuzhiyun { 0x1e1a, KEY_IMAGES },
48*4882a593Smuzhiyun { 0x1e1b, KEY_EPG },
49*4882a593Smuzhiyun { 0x1e1c, KEY_TV },
50*4882a593Smuzhiyun { 0x1e1e, KEY_NEXT },
51*4882a593Smuzhiyun { 0x1e1f, KEY_BACK },
52*4882a593Smuzhiyun { 0x1e20, KEY_CHANNELUP },
53*4882a593Smuzhiyun { 0x1e21, KEY_CHANNELDOWN },
54*4882a593Smuzhiyun { 0x1e24, KEY_LAST }, /* Skip backwards */
55*4882a593Smuzhiyun { 0x1e25, KEY_OK },
56*4882a593Smuzhiyun { 0x1e29, KEY_BLUE},
57*4882a593Smuzhiyun { 0x1e2e, KEY_GREEN },
58*4882a593Smuzhiyun { 0x1e30, KEY_PAUSE },
59*4882a593Smuzhiyun { 0x1e32, KEY_REWIND },
60*4882a593Smuzhiyun { 0x1e34, KEY_FASTFORWARD },
61*4882a593Smuzhiyun { 0x1e35, KEY_PLAY },
62*4882a593Smuzhiyun { 0x1e36, KEY_STOP },
63*4882a593Smuzhiyun { 0x1e37, KEY_RECORD },
64*4882a593Smuzhiyun { 0x1e38, KEY_YELLOW },
65*4882a593Smuzhiyun { 0x1e3b, KEY_GOTO },
66*4882a593Smuzhiyun { 0x1e3d, KEY_POWER },
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /* Firmware bug? sometimes, when a new key is pressed, the previous pressed key
70*4882a593Smuzhiyun * is delivered. No workaround yet, maybe a new firmware.
71*4882a593Smuzhiyun */
nova_t_rc_query(struct dvb_usb_device * d,u32 * event,int * state)72*4882a593Smuzhiyun static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun u8 *buf, data, toggle, custom;
75*4882a593Smuzhiyun u16 raw;
76*4882a593Smuzhiyun int i, ret;
77*4882a593Smuzhiyun struct dibusb_device_state *st = d->priv;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun buf = kmalloc(5, GFP_KERNEL);
80*4882a593Smuzhiyun if (!buf)
81*4882a593Smuzhiyun return -ENOMEM;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun buf[0] = DIBUSB_REQ_POLL_REMOTE;
84*4882a593Smuzhiyun buf[1] = 0x35;
85*4882a593Smuzhiyun ret = dvb_usb_generic_rw(d, buf, 2, buf, 5, 0);
86*4882a593Smuzhiyun if (ret < 0)
87*4882a593Smuzhiyun goto ret;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun *state = REMOTE_NO_KEY_PRESSED;
90*4882a593Smuzhiyun switch (buf[0]) {
91*4882a593Smuzhiyun case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED:
92*4882a593Smuzhiyun raw = ((buf[1] << 8) | buf[2]) >> 3;
93*4882a593Smuzhiyun toggle = !!(raw & 0x800);
94*4882a593Smuzhiyun data = raw & 0x3f;
95*4882a593Smuzhiyun custom = (raw >> 6) & 0x1f;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",
98*4882a593Smuzhiyun buf[1], buf[2], buf[3], custom, data, toggle);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(rc_map_haupp_table); i++) {
101*4882a593Smuzhiyun if (rc5_data(&rc_map_haupp_table[i]) == data &&
102*4882a593Smuzhiyun rc5_custom(&rc_map_haupp_table[i]) == custom) {
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun deb_rc("c: %x, d: %x\n", rc5_data(&rc_map_haupp_table[i]),
105*4882a593Smuzhiyun rc5_custom(&rc_map_haupp_table[i]));
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun *event = rc_map_haupp_table[i].keycode;
108*4882a593Smuzhiyun *state = REMOTE_KEY_PRESSED;
109*4882a593Smuzhiyun if (st->old_toggle == toggle) {
110*4882a593Smuzhiyun if (st->last_repeat_count++ < 2)
111*4882a593Smuzhiyun *state = REMOTE_NO_KEY_PRESSED;
112*4882a593Smuzhiyun } else {
113*4882a593Smuzhiyun st->last_repeat_count = 0;
114*4882a593Smuzhiyun st->old_toggle = toggle;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun break;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun break;
121*4882a593Smuzhiyun case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY:
122*4882a593Smuzhiyun default:
123*4882a593Smuzhiyun break;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun ret:
127*4882a593Smuzhiyun kfree(buf);
128*4882a593Smuzhiyun return ret;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
nova_t_read_mac_address(struct dvb_usb_device * d,u8 mac[6])131*4882a593Smuzhiyun static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6])
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun int i, ret;
134*4882a593Smuzhiyun u8 b;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun mac[0] = 0x00;
137*4882a593Smuzhiyun mac[1] = 0x0d;
138*4882a593Smuzhiyun mac[2] = 0xfe;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun /* this is a complete guess, but works for my box */
141*4882a593Smuzhiyun for (i = 136; i < 139; i++) {
142*4882a593Smuzhiyun ret = dibusb_read_eeprom_byte(d, i, &b);
143*4882a593Smuzhiyun if (ret)
144*4882a593Smuzhiyun return ret;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun mac[5 - (i - 136)] = b;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun /* USB Driver stuff */
153*4882a593Smuzhiyun static struct dvb_usb_device_properties nova_t_properties;
154*4882a593Smuzhiyun
nova_t_probe(struct usb_interface * intf,const struct usb_device_id * id)155*4882a593Smuzhiyun static int nova_t_probe(struct usb_interface *intf,
156*4882a593Smuzhiyun const struct usb_device_id *id)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun return dvb_usb_device_init(intf, &nova_t_properties,
159*4882a593Smuzhiyun THIS_MODULE, NULL, adapter_nr);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun /* do not change the order of the ID table */
163*4882a593Smuzhiyun static struct usb_device_id nova_t_table [] = {
164*4882a593Smuzhiyun /* 00 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) },
165*4882a593Smuzhiyun /* 01 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) },
166*4882a593Smuzhiyun { } /* Terminating entry */
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, nova_t_table);
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun static struct dvb_usb_device_properties nova_t_properties = {
171*4882a593Smuzhiyun .caps = DVB_USB_IS_AN_I2C_ADAPTER,
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun .usb_ctrl = CYPRESS_FX2,
174*4882a593Smuzhiyun .firmware = "dvb-usb-nova-t-usb2-02.fw",
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun .num_adapters = 1,
177*4882a593Smuzhiyun .adapter = {
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun .num_frontends = 1,
180*4882a593Smuzhiyun .fe = {{
181*4882a593Smuzhiyun .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
182*4882a593Smuzhiyun .pid_filter_count = 32,
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun .streaming_ctrl = dibusb2_0_streaming_ctrl,
185*4882a593Smuzhiyun .pid_filter = dibusb_pid_filter,
186*4882a593Smuzhiyun .pid_filter_ctrl = dibusb_pid_filter_ctrl,
187*4882a593Smuzhiyun .frontend_attach = dibusb_dib3000mc_frontend_attach,
188*4882a593Smuzhiyun .tuner_attach = dibusb_dib3000mc_tuner_attach,
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun /* parameter for the MPEG2-data transfer */
191*4882a593Smuzhiyun .stream = {
192*4882a593Smuzhiyun .type = USB_BULK,
193*4882a593Smuzhiyun .count = 7,
194*4882a593Smuzhiyun .endpoint = 0x06,
195*4882a593Smuzhiyun .u = {
196*4882a593Smuzhiyun .bulk = {
197*4882a593Smuzhiyun .buffersize = 4096,
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun },
201*4882a593Smuzhiyun }},
202*4882a593Smuzhiyun .size_of_priv = sizeof(struct dibusb_state),
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun },
205*4882a593Smuzhiyun .size_of_priv = sizeof(struct dibusb_device_state),
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun .power_ctrl = dibusb2_0_power_ctrl,
208*4882a593Smuzhiyun .read_mac_address = nova_t_read_mac_address,
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun .rc.legacy = {
211*4882a593Smuzhiyun .rc_interval = 100,
212*4882a593Smuzhiyun .rc_map_table = rc_map_haupp_table,
213*4882a593Smuzhiyun .rc_map_size = ARRAY_SIZE(rc_map_haupp_table),
214*4882a593Smuzhiyun .rc_query = nova_t_rc_query,
215*4882a593Smuzhiyun },
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun .i2c_algo = &dibusb_i2c_algo,
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun .generic_bulk_ctrl_endpoint = 0x01,
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun .num_device_descs = 1,
222*4882a593Smuzhiyun .devices = {
223*4882a593Smuzhiyun { "Hauppauge WinTV-NOVA-T usb2",
224*4882a593Smuzhiyun { &nova_t_table[0], NULL },
225*4882a593Smuzhiyun { &nova_t_table[1], NULL },
226*4882a593Smuzhiyun },
227*4882a593Smuzhiyun { NULL },
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun };
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun static struct usb_driver nova_t_driver = {
232*4882a593Smuzhiyun .name = "dvb_usb_nova_t_usb2",
233*4882a593Smuzhiyun .probe = nova_t_probe,
234*4882a593Smuzhiyun .disconnect = dvb_usb_device_exit,
235*4882a593Smuzhiyun .id_table = nova_t_table,
236*4882a593Smuzhiyun };
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun module_usb_driver(nova_t_driver);
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
241*4882a593Smuzhiyun MODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2");
242*4882a593Smuzhiyun MODULE_VERSION("1.0");
243*4882a593Smuzhiyun MODULE_LICENSE("GPL");
244