18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2 38c2ecf20Sopenharmony_ci * DVB-T receiver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include "dibusb.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int debug; 128c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 138c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=rc,2=eeprom (|-able))." DVB_USB_DEBUG_STATUS); 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define deb_rc(args...) dprintk(debug,0x01,args) 188c2ecf20Sopenharmony_ci#define deb_ee(args...) dprintk(debug,0x02,args) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Hauppauge NOVA-T USB2 keys */ 218c2ecf20Sopenharmony_cistatic struct rc_map_table rc_map_haupp_table[] = { 228c2ecf20Sopenharmony_ci { 0x1e00, KEY_0 }, 238c2ecf20Sopenharmony_ci { 0x1e01, KEY_1 }, 248c2ecf20Sopenharmony_ci { 0x1e02, KEY_2 }, 258c2ecf20Sopenharmony_ci { 0x1e03, KEY_3 }, 268c2ecf20Sopenharmony_ci { 0x1e04, KEY_4 }, 278c2ecf20Sopenharmony_ci { 0x1e05, KEY_5 }, 288c2ecf20Sopenharmony_ci { 0x1e06, KEY_6 }, 298c2ecf20Sopenharmony_ci { 0x1e07, KEY_7 }, 308c2ecf20Sopenharmony_ci { 0x1e08, KEY_8 }, 318c2ecf20Sopenharmony_ci { 0x1e09, KEY_9 }, 328c2ecf20Sopenharmony_ci { 0x1e0a, KEY_KPASTERISK }, 338c2ecf20Sopenharmony_ci { 0x1e0b, KEY_RED }, 348c2ecf20Sopenharmony_ci { 0x1e0c, KEY_RADIO }, 358c2ecf20Sopenharmony_ci { 0x1e0d, KEY_MENU }, 368c2ecf20Sopenharmony_ci { 0x1e0e, KEY_GRAVE }, /* # */ 378c2ecf20Sopenharmony_ci { 0x1e0f, KEY_MUTE }, 388c2ecf20Sopenharmony_ci { 0x1e10, KEY_VOLUMEUP }, 398c2ecf20Sopenharmony_ci { 0x1e11, KEY_VOLUMEDOWN }, 408c2ecf20Sopenharmony_ci { 0x1e12, KEY_CHANNEL }, 418c2ecf20Sopenharmony_ci { 0x1e14, KEY_UP }, 428c2ecf20Sopenharmony_ci { 0x1e15, KEY_DOWN }, 438c2ecf20Sopenharmony_ci { 0x1e16, KEY_LEFT }, 448c2ecf20Sopenharmony_ci { 0x1e17, KEY_RIGHT }, 458c2ecf20Sopenharmony_ci { 0x1e18, KEY_VIDEO }, 468c2ecf20Sopenharmony_ci { 0x1e19, KEY_AUDIO }, 478c2ecf20Sopenharmony_ci { 0x1e1a, KEY_IMAGES }, 488c2ecf20Sopenharmony_ci { 0x1e1b, KEY_EPG }, 498c2ecf20Sopenharmony_ci { 0x1e1c, KEY_TV }, 508c2ecf20Sopenharmony_ci { 0x1e1e, KEY_NEXT }, 518c2ecf20Sopenharmony_ci { 0x1e1f, KEY_BACK }, 528c2ecf20Sopenharmony_ci { 0x1e20, KEY_CHANNELUP }, 538c2ecf20Sopenharmony_ci { 0x1e21, KEY_CHANNELDOWN }, 548c2ecf20Sopenharmony_ci { 0x1e24, KEY_LAST }, /* Skip backwards */ 558c2ecf20Sopenharmony_ci { 0x1e25, KEY_OK }, 568c2ecf20Sopenharmony_ci { 0x1e29, KEY_BLUE}, 578c2ecf20Sopenharmony_ci { 0x1e2e, KEY_GREEN }, 588c2ecf20Sopenharmony_ci { 0x1e30, KEY_PAUSE }, 598c2ecf20Sopenharmony_ci { 0x1e32, KEY_REWIND }, 608c2ecf20Sopenharmony_ci { 0x1e34, KEY_FASTFORWARD }, 618c2ecf20Sopenharmony_ci { 0x1e35, KEY_PLAY }, 628c2ecf20Sopenharmony_ci { 0x1e36, KEY_STOP }, 638c2ecf20Sopenharmony_ci { 0x1e37, KEY_RECORD }, 648c2ecf20Sopenharmony_ci { 0x1e38, KEY_YELLOW }, 658c2ecf20Sopenharmony_ci { 0x1e3b, KEY_GOTO }, 668c2ecf20Sopenharmony_ci { 0x1e3d, KEY_POWER }, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Firmware bug? sometimes, when a new key is pressed, the previous pressed key 708c2ecf20Sopenharmony_ci * is delivered. No workaround yet, maybe a new firmware. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci u8 *buf, data, toggle, custom; 758c2ecf20Sopenharmony_ci u16 raw; 768c2ecf20Sopenharmony_ci int i, ret; 778c2ecf20Sopenharmony_ci struct dibusb_device_state *st = d->priv; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci buf = kmalloc(5, GFP_KERNEL); 808c2ecf20Sopenharmony_ci if (!buf) 818c2ecf20Sopenharmony_ci return -ENOMEM; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci buf[0] = DIBUSB_REQ_POLL_REMOTE; 848c2ecf20Sopenharmony_ci buf[1] = 0x35; 858c2ecf20Sopenharmony_ci ret = dvb_usb_generic_rw(d, buf, 2, buf, 5, 0); 868c2ecf20Sopenharmony_ci if (ret < 0) 878c2ecf20Sopenharmony_ci goto ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci *state = REMOTE_NO_KEY_PRESSED; 908c2ecf20Sopenharmony_ci switch (buf[0]) { 918c2ecf20Sopenharmony_ci case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED: 928c2ecf20Sopenharmony_ci raw = ((buf[1] << 8) | buf[2]) >> 3; 938c2ecf20Sopenharmony_ci toggle = !!(raw & 0x800); 948c2ecf20Sopenharmony_ci data = raw & 0x3f; 958c2ecf20Sopenharmony_ci custom = (raw >> 6) & 0x1f; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n", 988c2ecf20Sopenharmony_ci buf[1], buf[2], buf[3], custom, data, toggle); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rc_map_haupp_table); i++) { 1018c2ecf20Sopenharmony_ci if (rc5_data(&rc_map_haupp_table[i]) == data && 1028c2ecf20Sopenharmony_ci rc5_custom(&rc_map_haupp_table[i]) == custom) { 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci deb_rc("c: %x, d: %x\n", rc5_data(&rc_map_haupp_table[i]), 1058c2ecf20Sopenharmony_ci rc5_custom(&rc_map_haupp_table[i])); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci *event = rc_map_haupp_table[i].keycode; 1088c2ecf20Sopenharmony_ci *state = REMOTE_KEY_PRESSED; 1098c2ecf20Sopenharmony_ci if (st->old_toggle == toggle) { 1108c2ecf20Sopenharmony_ci if (st->last_repeat_count++ < 2) 1118c2ecf20Sopenharmony_ci *state = REMOTE_NO_KEY_PRESSED; 1128c2ecf20Sopenharmony_ci } else { 1138c2ecf20Sopenharmony_ci st->last_repeat_count = 0; 1148c2ecf20Sopenharmony_ci st->old_toggle = toggle; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY: 1228c2ecf20Sopenharmony_ci default: 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciret: 1278c2ecf20Sopenharmony_ci kfree(buf); 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6]) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci int i, ret; 1348c2ecf20Sopenharmony_ci u8 b; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci mac[0] = 0x00; 1378c2ecf20Sopenharmony_ci mac[1] = 0x0d; 1388c2ecf20Sopenharmony_ci mac[2] = 0xfe; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* this is a complete guess, but works for my box */ 1418c2ecf20Sopenharmony_ci for (i = 136; i < 139; i++) { 1428c2ecf20Sopenharmony_ci ret = dibusb_read_eeprom_byte(d, i, &b); 1438c2ecf20Sopenharmony_ci if (ret) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mac[5 - (i - 136)] = b; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* USB Driver stuff */ 1538c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties nova_t_properties; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int nova_t_probe(struct usb_interface *intf, 1568c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci return dvb_usb_device_init(intf, &nova_t_properties, 1598c2ecf20Sopenharmony_ci THIS_MODULE, NULL, adapter_nr); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* do not change the order of the ID table */ 1638c2ecf20Sopenharmony_cistatic struct usb_device_id nova_t_table [] = { 1648c2ecf20Sopenharmony_ci/* 00 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) }, 1658c2ecf20Sopenharmony_ci/* 01 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) }, 1668c2ecf20Sopenharmony_ci { } /* Terminating entry */ 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, nova_t_table); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties nova_t_properties = { 1718c2ecf20Sopenharmony_ci .caps = DVB_USB_IS_AN_I2C_ADAPTER, 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci .usb_ctrl = CYPRESS_FX2, 1748c2ecf20Sopenharmony_ci .firmware = "dvb-usb-nova-t-usb2-02.fw", 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci .num_adapters = 1, 1778c2ecf20Sopenharmony_ci .adapter = { 1788c2ecf20Sopenharmony_ci { 1798c2ecf20Sopenharmony_ci .num_frontends = 1, 1808c2ecf20Sopenharmony_ci .fe = {{ 1818c2ecf20Sopenharmony_ci .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, 1828c2ecf20Sopenharmony_ci .pid_filter_count = 32, 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci .streaming_ctrl = dibusb2_0_streaming_ctrl, 1858c2ecf20Sopenharmony_ci .pid_filter = dibusb_pid_filter, 1868c2ecf20Sopenharmony_ci .pid_filter_ctrl = dibusb_pid_filter_ctrl, 1878c2ecf20Sopenharmony_ci .frontend_attach = dibusb_dib3000mc_frontend_attach, 1888c2ecf20Sopenharmony_ci .tuner_attach = dibusb_dib3000mc_tuner_attach, 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* parameter for the MPEG2-data transfer */ 1918c2ecf20Sopenharmony_ci .stream = { 1928c2ecf20Sopenharmony_ci .type = USB_BULK, 1938c2ecf20Sopenharmony_ci .count = 7, 1948c2ecf20Sopenharmony_ci .endpoint = 0x06, 1958c2ecf20Sopenharmony_ci .u = { 1968c2ecf20Sopenharmony_ci .bulk = { 1978c2ecf20Sopenharmony_ci .buffersize = 4096, 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci }, 2018c2ecf20Sopenharmony_ci }}, 2028c2ecf20Sopenharmony_ci .size_of_priv = sizeof(struct dibusb_state), 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci }, 2058c2ecf20Sopenharmony_ci .size_of_priv = sizeof(struct dibusb_device_state), 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci .power_ctrl = dibusb2_0_power_ctrl, 2088c2ecf20Sopenharmony_ci .read_mac_address = nova_t_read_mac_address, 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci .rc.legacy = { 2118c2ecf20Sopenharmony_ci .rc_interval = 100, 2128c2ecf20Sopenharmony_ci .rc_map_table = rc_map_haupp_table, 2138c2ecf20Sopenharmony_ci .rc_map_size = ARRAY_SIZE(rc_map_haupp_table), 2148c2ecf20Sopenharmony_ci .rc_query = nova_t_rc_query, 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci .i2c_algo = &dibusb_i2c_algo, 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci .generic_bulk_ctrl_endpoint = 0x01, 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci .num_device_descs = 1, 2228c2ecf20Sopenharmony_ci .devices = { 2238c2ecf20Sopenharmony_ci { "Hauppauge WinTV-NOVA-T usb2", 2248c2ecf20Sopenharmony_ci { &nova_t_table[0], NULL }, 2258c2ecf20Sopenharmony_ci { &nova_t_table[1], NULL }, 2268c2ecf20Sopenharmony_ci }, 2278c2ecf20Sopenharmony_ci { NULL }, 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic struct usb_driver nova_t_driver = { 2328c2ecf20Sopenharmony_ci .name = "dvb_usb_nova_t_usb2", 2338c2ecf20Sopenharmony_ci .probe = nova_t_probe, 2348c2ecf20Sopenharmony_ci .disconnect = dvb_usb_device_exit, 2358c2ecf20Sopenharmony_ci .id_table = nova_t_table, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cimodule_usb_driver(nova_t_driver); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); 2418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2"); 2428c2ecf20Sopenharmony_ciMODULE_VERSION("1.0"); 2438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 244