1 // SPDX-License-Identifier: GPL-2.0-only 2 /* DVB USB compliant Linux driver for the 3 * - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver 4 * - DigitalNow TinyUSB2 DVB-t receiver 5 * 6 * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) 7 * 8 * Thanks to Twinhan who kindly provided hardware and information. 9 * 10 * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information 11 */ 12 #include "vp7045.h" 13 14 /* debug */ 15 static int dvb_usb_vp7045_debug; 16 module_param_named(debug,dvb_usb_vp7045_debug, int, 0644); 17 MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); 18 19 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 20 21 #define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args) 22 #define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args) 23 #define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args) 24 25 int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec) 26 { 27 int ret = 0; 28 u8 *buf = d->priv; 29 30 buf[0] = cmd; 31 32 if (outlen > 19) 33 outlen = 19; 34 35 if (inlen > 11) 36 inlen = 11; 37 38 ret = mutex_lock_interruptible(&d->usb_mutex); 39 if (ret) 40 return ret; 41 42 if (out != NULL && outlen > 0) 43 memcpy(&buf[1], out, outlen); 44 45 deb_xfer("out buffer: "); 46 debug_dump(buf, outlen+1, deb_xfer); 47 48 49 if (usb_control_msg(d->udev, 50 usb_sndctrlpipe(d->udev,0), 51 TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, 52 buf, 20, 2000) != 20) { 53 err("USB control message 'out' went wrong."); 54 ret = -EIO; 55 goto unlock; 56 } 57 58 msleep(msec); 59 60 if (usb_control_msg(d->udev, 61 usb_rcvctrlpipe(d->udev,0), 62 TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, 63 buf, 12, 2000) != 12) { 64 err("USB control message 'in' went wrong."); 65 ret = -EIO; 66 goto unlock; 67 } 68 69 deb_xfer("in buffer: "); 70 debug_dump(buf, 12, deb_xfer); 71 72 if (in != NULL && inlen > 0) 73 memcpy(in, &buf[1], inlen); 74 75 unlock: 76 mutex_unlock(&d->usb_mutex); 77 78 return ret; 79 } 80 81 u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg) 82 { 83 u8 obuf[2] = { 0 },v; 84 obuf[1] = reg; 85 86 vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30); 87 88 return v; 89 } 90 91 static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) 92 { 93 u8 v = onoff; 94 return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150); 95 } 96 97 static int vp7045_rc_query(struct dvb_usb_device *d) 98 { 99 int ret; 100 u8 key; 101 102 ret = vp7045_usb_op(d, RC_VAL_READ, NULL, 0, &key, 1, 20); 103 if (ret) 104 return ret; 105 106 deb_rc("remote query key: %x\n", key); 107 108 if (key != 0x44) { 109 /* 110 * The 8 bit address isn't available, but since the remote uses 111 * address 0 we'll use that. nec repeats are ignored too, even 112 * though the remote sends them. 113 */ 114 rc_keydown(d->rc_dev, RC_PROTO_NEC, RC_SCANCODE_NEC(0, key), 0); 115 } 116 117 return 0; 118 } 119 120 static int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset) 121 { 122 int i, ret; 123 u8 v, br[2]; 124 for (i=0; i < len; i++) { 125 v = offset + i; 126 ret = vp7045_usb_op(d, GET_EE_VALUE, &v, 1, br, 2, 5); 127 if (ret) 128 return ret; 129 130 buf[i] = br[1]; 131 } 132 deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ", offset, i); 133 debug_dump(buf, i, deb_info); 134 return 0; 135 } 136 137 static int vp7045_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) 138 { 139 return vp7045_read_eeprom(d,mac, 6, MAC_0_ADDR); 140 } 141 142 static int vp7045_frontend_attach(struct dvb_usb_adapter *adap) 143 { 144 u8 buf[255] = { 0 }; 145 146 vp7045_usb_op(adap->dev,VENDOR_STRING_READ,NULL,0,buf,20,0); 147 buf[10] = '\0'; 148 deb_info("firmware says: %s ",buf); 149 150 vp7045_usb_op(adap->dev,PRODUCT_STRING_READ,NULL,0,buf,20,0); 151 buf[10] = '\0'; 152 deb_info("%s ",buf); 153 154 vp7045_usb_op(adap->dev,FW_VERSION_READ,NULL,0,buf,20,0); 155 buf[10] = '\0'; 156 deb_info("v%s\n",buf); 157 158 /* Dump the EEPROM */ 159 /* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */ 160 161 adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev); 162 163 return 0; 164 } 165 166 static struct dvb_usb_device_properties vp7045_properties; 167 168 static int vp7045_usb_probe(struct usb_interface *intf, 169 const struct usb_device_id *id) 170 { 171 return dvb_usb_device_init(intf, &vp7045_properties, 172 THIS_MODULE, NULL, adapter_nr); 173 } 174 175 enum { 176 VISIONPLUS_VP7045_COLD, 177 VISIONPLUS_VP7045_WARM, 178 VISIONPLUS_TINYUSB2_COLD, 179 VISIONPLUS_TINYUSB2_WARM, 180 }; 181 182 static struct usb_device_id vp7045_usb_table[] = { 183 DVB_USB_DEV(VISIONPLUS, VISIONPLUS_VP7045_COLD), 184 DVB_USB_DEV(VISIONPLUS, VISIONPLUS_VP7045_WARM), 185 DVB_USB_DEV(VISIONPLUS, VISIONPLUS_TINYUSB2_COLD), 186 DVB_USB_DEV(VISIONPLUS, VISIONPLUS_TINYUSB2_WARM), 187 { } 188 }; 189 190 MODULE_DEVICE_TABLE(usb, vp7045_usb_table); 191 192 static struct dvb_usb_device_properties vp7045_properties = { 193 .usb_ctrl = CYPRESS_FX2, 194 .firmware = "dvb-usb-vp7045-01.fw", 195 .size_of_priv = 20, 196 197 .num_adapters = 1, 198 .adapter = { 199 { 200 .num_frontends = 1, 201 .fe = {{ 202 .frontend_attach = vp7045_frontend_attach, 203 /* parameter for the MPEG2-data transfer */ 204 .stream = { 205 .type = USB_BULK, 206 .count = 7, 207 .endpoint = 0x02, 208 .u = { 209 .bulk = { 210 .buffersize = 4096, 211 } 212 } 213 }, 214 }}, 215 } 216 }, 217 .power_ctrl = vp7045_power_ctrl, 218 .read_mac_address = vp7045_read_mac_addr, 219 220 .rc.core = { 221 .rc_interval = 400, 222 .rc_codes = RC_MAP_TWINHAN_VP1027_DVBS, 223 .module_name = KBUILD_MODNAME, 224 .rc_query = vp7045_rc_query, 225 .allowed_protos = RC_PROTO_BIT_NEC, 226 .scancode_mask = 0xff, 227 }, 228 229 .num_device_descs = 2, 230 .devices = { 231 { .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)", 232 .cold_ids = { &vp7045_usb_table[VISIONPLUS_VP7045_COLD], NULL }, 233 .warm_ids = { &vp7045_usb_table[VISIONPLUS_VP7045_WARM], NULL }, 234 }, 235 { .name = "DigitalNow TinyUSB 2 DVB-t Receiver", 236 .cold_ids = { &vp7045_usb_table[VISIONPLUS_TINYUSB2_COLD], NULL }, 237 .warm_ids = { &vp7045_usb_table[VISIONPLUS_TINYUSB2_WARM], NULL }, 238 }, 239 { NULL }, 240 } 241 }; 242 243 /* usb specific object needed to register this driver with the usb subsystem */ 244 static struct usb_driver vp7045_usb_driver = { 245 .name = "dvb_usb_vp7045", 246 .probe = vp7045_usb_probe, 247 .disconnect = dvb_usb_device_exit, 248 .id_table = vp7045_usb_table, 249 }; 250 251 module_usb_driver(vp7045_usb_driver); 252 253 MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); 254 MODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0"); 255 MODULE_VERSION("1.0"); 256 MODULE_LICENSE("GPL"); 257