162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2 362306a36Sopenharmony_ci * DVB-T receiver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include "dibusb.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic int debug; 1262306a36Sopenharmony_cimodule_param(debug, int, 0644); 1362306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=rc,2=eeprom (|-able))." DVB_USB_DEBUG_STATUS); 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define deb_rc(args...) dprintk(debug,0x01,args) 1862306a36Sopenharmony_ci#define deb_ee(args...) dprintk(debug,0x02,args) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Hauppauge NOVA-T USB2 keys */ 2162306a36Sopenharmony_cistatic struct rc_map_table rc_map_haupp_table[] = { 2262306a36Sopenharmony_ci { 0x1e00, KEY_0 }, 2362306a36Sopenharmony_ci { 0x1e01, KEY_1 }, 2462306a36Sopenharmony_ci { 0x1e02, KEY_2 }, 2562306a36Sopenharmony_ci { 0x1e03, KEY_3 }, 2662306a36Sopenharmony_ci { 0x1e04, KEY_4 }, 2762306a36Sopenharmony_ci { 0x1e05, KEY_5 }, 2862306a36Sopenharmony_ci { 0x1e06, KEY_6 }, 2962306a36Sopenharmony_ci { 0x1e07, KEY_7 }, 3062306a36Sopenharmony_ci { 0x1e08, KEY_8 }, 3162306a36Sopenharmony_ci { 0x1e09, KEY_9 }, 3262306a36Sopenharmony_ci { 0x1e0a, KEY_KPASTERISK }, 3362306a36Sopenharmony_ci { 0x1e0b, KEY_RED }, 3462306a36Sopenharmony_ci { 0x1e0c, KEY_RADIO }, 3562306a36Sopenharmony_ci { 0x1e0d, KEY_MENU }, 3662306a36Sopenharmony_ci { 0x1e0e, KEY_GRAVE }, /* # */ 3762306a36Sopenharmony_ci { 0x1e0f, KEY_MUTE }, 3862306a36Sopenharmony_ci { 0x1e10, KEY_VOLUMEUP }, 3962306a36Sopenharmony_ci { 0x1e11, KEY_VOLUMEDOWN }, 4062306a36Sopenharmony_ci { 0x1e12, KEY_CHANNEL }, 4162306a36Sopenharmony_ci { 0x1e14, KEY_UP }, 4262306a36Sopenharmony_ci { 0x1e15, KEY_DOWN }, 4362306a36Sopenharmony_ci { 0x1e16, KEY_LEFT }, 4462306a36Sopenharmony_ci { 0x1e17, KEY_RIGHT }, 4562306a36Sopenharmony_ci { 0x1e18, KEY_VIDEO }, 4662306a36Sopenharmony_ci { 0x1e19, KEY_AUDIO }, 4762306a36Sopenharmony_ci { 0x1e1a, KEY_IMAGES }, 4862306a36Sopenharmony_ci { 0x1e1b, KEY_EPG }, 4962306a36Sopenharmony_ci { 0x1e1c, KEY_TV }, 5062306a36Sopenharmony_ci { 0x1e1e, KEY_NEXT }, 5162306a36Sopenharmony_ci { 0x1e1f, KEY_BACK }, 5262306a36Sopenharmony_ci { 0x1e20, KEY_CHANNELUP }, 5362306a36Sopenharmony_ci { 0x1e21, KEY_CHANNELDOWN }, 5462306a36Sopenharmony_ci { 0x1e24, KEY_LAST }, /* Skip backwards */ 5562306a36Sopenharmony_ci { 0x1e25, KEY_OK }, 5662306a36Sopenharmony_ci { 0x1e29, KEY_BLUE}, 5762306a36Sopenharmony_ci { 0x1e2e, KEY_GREEN }, 5862306a36Sopenharmony_ci { 0x1e30, KEY_PAUSE }, 5962306a36Sopenharmony_ci { 0x1e32, KEY_REWIND }, 6062306a36Sopenharmony_ci { 0x1e34, KEY_FASTFORWARD }, 6162306a36Sopenharmony_ci { 0x1e35, KEY_PLAY }, 6262306a36Sopenharmony_ci { 0x1e36, KEY_STOP }, 6362306a36Sopenharmony_ci { 0x1e37, KEY_RECORD }, 6462306a36Sopenharmony_ci { 0x1e38, KEY_YELLOW }, 6562306a36Sopenharmony_ci { 0x1e3b, KEY_GOTO }, 6662306a36Sopenharmony_ci { 0x1e3d, KEY_POWER }, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Firmware bug? sometimes, when a new key is pressed, the previous pressed key 7062306a36Sopenharmony_ci * is delivered. No workaround yet, maybe a new firmware. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci u8 *buf, data, toggle, custom; 7562306a36Sopenharmony_ci u16 raw; 7662306a36Sopenharmony_ci int i, ret; 7762306a36Sopenharmony_ci struct dibusb_device_state *st = d->priv; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci buf = kmalloc(5, GFP_KERNEL); 8062306a36Sopenharmony_ci if (!buf) 8162306a36Sopenharmony_ci return -ENOMEM; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci buf[0] = DIBUSB_REQ_POLL_REMOTE; 8462306a36Sopenharmony_ci buf[1] = 0x35; 8562306a36Sopenharmony_ci ret = dvb_usb_generic_rw(d, buf, 2, buf, 5, 0); 8662306a36Sopenharmony_ci if (ret < 0) 8762306a36Sopenharmony_ci goto ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci *state = REMOTE_NO_KEY_PRESSED; 9062306a36Sopenharmony_ci switch (buf[0]) { 9162306a36Sopenharmony_ci case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED: 9262306a36Sopenharmony_ci raw = ((buf[1] << 8) | buf[2]) >> 3; 9362306a36Sopenharmony_ci toggle = !!(raw & 0x800); 9462306a36Sopenharmony_ci data = raw & 0x3f; 9562306a36Sopenharmony_ci custom = (raw >> 6) & 0x1f; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n", 9862306a36Sopenharmony_ci buf[1], buf[2], buf[3], custom, data, toggle); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rc_map_haupp_table); i++) { 10162306a36Sopenharmony_ci if (rc5_data(&rc_map_haupp_table[i]) == data && 10262306a36Sopenharmony_ci rc5_custom(&rc_map_haupp_table[i]) == custom) { 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci deb_rc("c: %x, d: %x\n", rc5_data(&rc_map_haupp_table[i]), 10562306a36Sopenharmony_ci rc5_custom(&rc_map_haupp_table[i])); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci *event = rc_map_haupp_table[i].keycode; 10862306a36Sopenharmony_ci *state = REMOTE_KEY_PRESSED; 10962306a36Sopenharmony_ci if (st->old_toggle == toggle) { 11062306a36Sopenharmony_ci if (st->last_repeat_count++ < 2) 11162306a36Sopenharmony_ci *state = REMOTE_NO_KEY_PRESSED; 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci st->last_repeat_count = 0; 11462306a36Sopenharmony_ci st->old_toggle = toggle; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY: 12262306a36Sopenharmony_ci default: 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciret: 12762306a36Sopenharmony_ci kfree(buf); 12862306a36Sopenharmony_ci return ret; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6]) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int i, ret; 13462306a36Sopenharmony_ci u8 b; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci mac[0] = 0x00; 13762306a36Sopenharmony_ci mac[1] = 0x0d; 13862306a36Sopenharmony_ci mac[2] = 0xfe; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* this is a complete guess, but works for my box */ 14162306a36Sopenharmony_ci for (i = 136; i < 139; i++) { 14262306a36Sopenharmony_ci ret = dibusb_read_eeprom_byte(d, i, &b); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci mac[5 - (i - 136)] = b; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* USB Driver stuff */ 15362306a36Sopenharmony_cistatic struct dvb_usb_device_properties nova_t_properties; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int nova_t_probe(struct usb_interface *intf, 15662306a36Sopenharmony_ci const struct usb_device_id *id) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return dvb_usb_device_init(intf, &nova_t_properties, 15962306a36Sopenharmony_ci THIS_MODULE, NULL, adapter_nr); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* do not change the order of the ID table */ 16362306a36Sopenharmony_cienum { 16462306a36Sopenharmony_ci HAUPPAUGE_WINTV_NOVA_T_USB2_COLD, 16562306a36Sopenharmony_ci HAUPPAUGE_WINTV_NOVA_T_USB2_WARM, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic struct usb_device_id nova_t_table[] = { 16962306a36Sopenharmony_ci DVB_USB_DEV(HAUPPAUGE, HAUPPAUGE_WINTV_NOVA_T_USB2_COLD), 17062306a36Sopenharmony_ci DVB_USB_DEV(HAUPPAUGE, HAUPPAUGE_WINTV_NOVA_T_USB2_WARM), 17162306a36Sopenharmony_ci { } 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, nova_t_table); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct dvb_usb_device_properties nova_t_properties = { 17762306a36Sopenharmony_ci .caps = DVB_USB_IS_AN_I2C_ADAPTER, 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci .usb_ctrl = CYPRESS_FX2, 18062306a36Sopenharmony_ci .firmware = "dvb-usb-nova-t-usb2-02.fw", 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci .num_adapters = 1, 18362306a36Sopenharmony_ci .adapter = { 18462306a36Sopenharmony_ci { 18562306a36Sopenharmony_ci .num_frontends = 1, 18662306a36Sopenharmony_ci .fe = {{ 18762306a36Sopenharmony_ci .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, 18862306a36Sopenharmony_ci .pid_filter_count = 32, 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci .streaming_ctrl = dibusb2_0_streaming_ctrl, 19162306a36Sopenharmony_ci .pid_filter = dibusb_pid_filter, 19262306a36Sopenharmony_ci .pid_filter_ctrl = dibusb_pid_filter_ctrl, 19362306a36Sopenharmony_ci .frontend_attach = dibusb_dib3000mc_frontend_attach, 19462306a36Sopenharmony_ci .tuner_attach = dibusb_dib3000mc_tuner_attach, 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* parameter for the MPEG2-data transfer */ 19762306a36Sopenharmony_ci .stream = { 19862306a36Sopenharmony_ci .type = USB_BULK, 19962306a36Sopenharmony_ci .count = 7, 20062306a36Sopenharmony_ci .endpoint = 0x06, 20162306a36Sopenharmony_ci .u = { 20262306a36Sopenharmony_ci .bulk = { 20362306a36Sopenharmony_ci .buffersize = 4096, 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci }, 20762306a36Sopenharmony_ci }}, 20862306a36Sopenharmony_ci .size_of_priv = sizeof(struct dibusb_state), 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci }, 21162306a36Sopenharmony_ci .size_of_priv = sizeof(struct dibusb_device_state), 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci .power_ctrl = dibusb2_0_power_ctrl, 21462306a36Sopenharmony_ci .read_mac_address = nova_t_read_mac_address, 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci .rc.legacy = { 21762306a36Sopenharmony_ci .rc_interval = 100, 21862306a36Sopenharmony_ci .rc_map_table = rc_map_haupp_table, 21962306a36Sopenharmony_ci .rc_map_size = ARRAY_SIZE(rc_map_haupp_table), 22062306a36Sopenharmony_ci .rc_query = nova_t_rc_query, 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci .i2c_algo = &dibusb_i2c_algo, 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci .generic_bulk_ctrl_endpoint = 0x01, 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci .num_device_descs = 1, 22862306a36Sopenharmony_ci .devices = { 22962306a36Sopenharmony_ci { "Hauppauge WinTV-NOVA-T usb2", 23062306a36Sopenharmony_ci { &nova_t_table[HAUPPAUGE_WINTV_NOVA_T_USB2_COLD], NULL }, 23162306a36Sopenharmony_ci { &nova_t_table[HAUPPAUGE_WINTV_NOVA_T_USB2_WARM], NULL }, 23262306a36Sopenharmony_ci }, 23362306a36Sopenharmony_ci { NULL }, 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic struct usb_driver nova_t_driver = { 23862306a36Sopenharmony_ci .name = "dvb_usb_nova_t_usb2", 23962306a36Sopenharmony_ci .probe = nova_t_probe, 24062306a36Sopenharmony_ci .disconnect = dvb_usb_device_exit, 24162306a36Sopenharmony_ci .id_table = nova_t_table, 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cimodule_usb_driver(nova_t_driver); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciMODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); 24762306a36Sopenharmony_ciMODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2"); 24862306a36Sopenharmony_ciMODULE_VERSION("1.0"); 24962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 250