1/* Linux driver for devices based on the DiBcom DiB0700 USB bridge 2 * 3 * This program is free software; you can redistribute it and/or modify it 4 * under the terms of the GNU General Public License as published by the Free 5 * Software Foundation, version 2. 6 * 7 * Copyright (C) 2005-6 DiBcom, SA 8 */ 9#include "dib0700.h" 10 11/* debug */ 12int dvb_usb_dib0700_debug; 13module_param_named(debug,dvb_usb_dib0700_debug, int, 0644); 14MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS); 15 16/* expecting rx buffer: request data[0] data[1] ... data[2] */ 17static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen) 18{ 19 int status; 20 21 deb_data(">>> "); 22 debug_dump(tx,txlen,deb_data); 23 24 status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0), 25 tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen, 26 USB_CTRL_GET_TIMEOUT); 27 28 if (status != txlen) 29 deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen); 30 31 return status < 0 ? status : 0; 32} 33 34/* expecting tx buffer: request data[0] ... data[n] (n <= 4) */ 35static int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) 36{ 37 u16 index, value; 38 int status; 39 40 if (txlen < 2) { 41 err("tx buffer length is smaller than 2. Makes no sense."); 42 return -EINVAL; 43 } 44 if (txlen > 4) { 45 err("tx buffer length is larger than 4. Not supported."); 46 return -EINVAL; 47 } 48 49 deb_data(">>> "); 50 debug_dump(tx,txlen,deb_data); 51 52 value = ((txlen - 2) << 8) | tx[1]; 53 index = 0; 54 if (txlen > 2) 55 index |= (tx[2] << 8); 56 if (txlen > 3) 57 index |= tx[3]; 58 59 status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0], 60 USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen, 61 USB_CTRL_GET_TIMEOUT); 62 63 if (status < 0) 64 deb_info("ep 0 read error (status = %d)\n",status); 65 66 deb_data("<<< "); 67 debug_dump(rx,rxlen,deb_data); 68 69 return status; /* length in case of success */ 70} 71 72int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val) 73{ 74 u8 buf[3] = { REQUEST_SET_GPIO, gpio, ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6) }; 75 return dib0700_ctrl_wr(d,buf,3); 76} 77 78/* 79 * I2C master xfer function 80 */ 81static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num) 82{ 83 struct dvb_usb_device *d = i2c_get_adapdata(adap); 84 int i,len; 85 u8 buf[255]; 86 87 if (mutex_lock_interruptible(&d->i2c_mutex) < 0) 88 return -EAGAIN; 89 90 for (i = 0; i < num; i++) { 91 /* fill in the address */ 92 buf[1] = (msg[i].addr << 1); 93 /* fill the buffer */ 94 memcpy(&buf[2], msg[i].buf, msg[i].len); 95 96 /* write/read request */ 97 if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { 98 buf[0] = REQUEST_I2C_READ; 99 buf[1] |= 1; 100 101 /* special thing in the current firmware: when length is zero the read-failed */ 102 if ((len = dib0700_ctrl_rd(d, buf, msg[i].len + 2, msg[i+1].buf, msg[i+1].len)) <= 0) { 103 deb_info("I2C read failed on address %x\n", msg[i].addr); 104 break; 105 } 106 107 msg[i+1].len = len; 108 109 i++; 110 } else { 111 buf[0] = REQUEST_I2C_WRITE; 112 if (dib0700_ctrl_wr(d, buf, msg[i].len + 2) < 0) 113 break; 114 } 115 } 116 117 mutex_unlock(&d->i2c_mutex); 118 return i; 119} 120 121static u32 dib0700_i2c_func(struct i2c_adapter *adapter) 122{ 123 return I2C_FUNC_I2C; 124} 125 126struct i2c_algorithm dib0700_i2c_algo = { 127 .master_xfer = dib0700_i2c_xfer, 128 .functionality = dib0700_i2c_func, 129}; 130 131int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, 132 struct dvb_usb_device_description **desc, int *cold) 133{ 134 u8 b[16]; 135 s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev,0), 136 REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT); 137 138 deb_info("FW GET_VERSION length: %d\n",ret); 139 140 *cold = ret <= 0; 141 142 deb_info("cold: %d\n", *cold); 143 return 0; 144} 145 146static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, 147 u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv, 148 u16 pll_loopdiv, u16 free_div, u16 dsuScaler) 149{ 150 u8 b[10]; 151 b[0] = REQUEST_SET_CLOCK; 152 b[1] = (en_pll << 7) | (pll_src << 6) | (pll_range << 5) | (clock_gpio3 << 4); 153 b[2] = (pll_prediv >> 8) & 0xff; // MSB 154 b[3] = pll_prediv & 0xff; // LSB 155 b[4] = (pll_loopdiv >> 8) & 0xff; // MSB 156 b[5] = pll_loopdiv & 0xff; // LSB 157 b[6] = (free_div >> 8) & 0xff; // MSB 158 b[7] = free_div & 0xff; // LSB 159 b[8] = (dsuScaler >> 8) & 0xff; // MSB 160 b[9] = dsuScaler & 0xff; // LSB 161 162 return dib0700_ctrl_wr(d, b, 10); 163} 164 165int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3) 166{ 167 switch (clk_MHz) { 168 case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break; 169 default: return -EINVAL; 170 } 171 return 0; 172} 173 174static int dib0700_jumpram(struct usb_device *udev, u32 address) 175{ 176 int ret, actlen; 177 u8 buf[8] = { REQUEST_JUMPRAM, 0, 0, 0, 178 (address >> 24) & 0xff, 179 (address >> 16) & 0xff, 180 (address >> 8) & 0xff, 181 address & 0xff }; 182 183 if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) { 184 deb_fw("jumpram to 0x%x failed\n",address); 185 return ret; 186 } 187 if (actlen != 8) { 188 deb_fw("jumpram to 0x%x failed\n",address); 189 return -EIO; 190 } 191 return 0; 192} 193 194int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw) 195{ 196 struct hexline hx; 197 int pos = 0, ret, act_len; 198 199 u8 buf[260]; 200 201 while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) { 202 deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n",hx.addr, hx.len, hx.chk); 203 204 buf[0] = hx.len; 205 buf[1] = (hx.addr >> 8) & 0xff; 206 buf[2] = hx.addr & 0xff; 207 buf[3] = hx.type; 208 memcpy(&buf[4],hx.data,hx.len); 209 buf[4+hx.len] = hx.chk; 210 211 ret = usb_bulk_msg(udev, 212 usb_sndbulkpipe(udev, 0x01), 213 buf, 214 hx.len + 5, 215 &act_len, 216 1000); 217 218 if (ret < 0) { 219 err("firmware download failed at %d with %d",pos,ret); 220 return ret; 221 } 222 } 223 224 if (ret == 0) { 225 /* start the firmware */ 226 if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) { 227 info("firmware started successfully."); 228 msleep(500); 229 } 230 } else 231 ret = -EIO; 232 233 return ret; 234} 235 236int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) 237{ 238 struct dib0700_state *st = adap->dev->priv; 239 u8 b[4]; 240 241 b[0] = REQUEST_ENABLE_VIDEO; 242 b[1] = 0x00; 243 b[2] = (0x01 << 4); /* Master mode */ 244 b[3] = 0x00; 245 246 deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id); 247 248 if (onoff) 249 st->channel_state |= 1 << adap->id; 250 else 251 st->channel_state &= ~(1 << adap->id); 252 253 b[2] |= st->channel_state; 254 255 if (st->channel_state) /* if at least one channel is active */ 256 b[1] = (0x01 << 4) | 0x00; 257 258 deb_info("data for streaming: %x %x\n",b[1],b[2]); 259 260 return dib0700_ctrl_wr(adap->dev, b, 4); 261} 262 263static int dib0700_probe(struct usb_interface *intf, 264 const struct usb_device_id *id) 265{ 266 int i; 267 268 for (i = 0; i < dib0700_device_count; i++) 269 if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE, NULL) == 0) 270 return 0; 271 272 return -ENODEV; 273} 274 275static struct usb_driver dib0700_driver = { 276 .name = "dvb_usb_dib0700", 277 .probe = dib0700_probe, 278 .disconnect = dvb_usb_device_exit, 279 .id_table = dib0700_usb_id_table, 280}; 281 282/* module stuff */ 283static int __init dib0700_module_init(void) 284{ 285 int result; 286 info("loaded with support for %d different device-types", dib0700_device_count); 287 if ((result = usb_register(&dib0700_driver))) { 288 err("usb_register failed. Error number %d",result); 289 return result; 290 } 291 292 return 0; 293} 294 295static void __exit dib0700_module_exit(void) 296{ 297 /* deregister this driver from the USB subsystem */ 298 usb_deregister(&dib0700_driver); 299} 300 301module_init (dib0700_module_init); 302module_exit (dib0700_module_exit); 303 304MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>"); 305MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge"); 306MODULE_VERSION("1.0"); 307MODULE_LICENSE("GPL"); 308