From: Michael Hunold - [DVB] new driver for mobile USB Budget DVB-T devices, thanks to Patrick Boettcher Signed-off-by: Michael Hunold Signed-off-by: Andrew Morton --- 25-akpm/drivers/media/dvb/Makefile | 3 25-akpm/drivers/media/dvb/dibusb/Kconfig | 31 + 25-akpm/drivers/media/dvb/dibusb/Makefile | 3 25-akpm/drivers/media/dvb/dibusb/dvb-dibusb.c | 719 ++++++++++++++++++++++++++ 25-akpm/drivers/media/dvb/dibusb/dvb-dibusb.h | 175 ++++++ 25-akpm/drivers/media/dvb/frontends/Kconfig | 46 + 6 files changed, 965 insertions(+), 12 deletions(-) diff -puN /dev/null drivers/media/dvb/dibusb/dvb-dibusb.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/dibusb/dvb-dibusb.c 2004-09-20 11:22:37.730914992 -0700 @@ -0,0 +1,719 @@ +/* + * Driver for mobile USB Budget DVB-T devices based on reference + * design made by DiBcom (http://www.dibcom.fr/) + * + * dvb-dibusb.c + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DiBcom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * Acknowledgements + * + * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver + * sources, on which this driver (and the dib3000mb frontend) are based. + * + * TODO + * - probing for i2c addresses, it is possible, that they have been changed + * by the vendor + * + * see Documentation/dvb/README.dibusb for more information + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_filter.h" +#include "dvb_net.h" +#include "dvb_frontend.h" + +#include "dvb-dibusb.h" + +/* debug */ + +#ifdef CONFIG_DVB_DIBCOM_DEBUG +#define dprintk_new(level,args...) \ + do { if ((debug & level)) { printk(args); } } while (0) + +#define debug_dump(b,l) if (debug) {\ + int i; deb_xfer("%s: %d > ",__FUNCTION__,l); \ + for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \ + deb_xfer("\n");\ +} + +static int debug; +module_param(debug, int, 0x644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore (|-able))."); +#else +#define dprintk_new(args...) +#define debug_dump(b,l) +#endif + +#define deb_info(args...) dprintk_new(0x01,args) +#define deb_xfer(args...) dprintk_new(0x02,args) +#define deb_alot(args...) dprintk_new(0x04,args) + +/* Version information */ +#define DRIVER_VERSION "0.0" +#define DRIVER_DESC "DiBcom based USB Budget DVB-T device" +#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" + +/* USB Driver stuff */ + +/* table of devices that work with this driver */ +static struct usb_device_id dibusb_table [] = { + { USB_DEVICE(USB_TWINHAN_VENDOR_ID, USB_VP7041_PRODUCT_PREFW_ID) }, + { USB_DEVICE(USB_TWINHAN_VENDOR_ID, USB_VP7041_PRODUCT_ID) }, + { USB_DEVICE(USB_IMC_NETWORKS_VENDOR_ID, USB_VP7041_PRODUCT_PREFW_ID) }, + { USB_DEVICE(USB_IMC_NETWORKS_VENDOR_ID, USB_VP7041_PRODUCT_ID) }, + { USB_DEVICE(USB_KWORLD_VENDOR_ID, USB_VSTREAM_PRODUCT_PREFW_ID) }, + { USB_DEVICE(USB_KWORLD_VENDOR_ID, USB_VSTREAM_PRODUCT_ID) }, + { USB_DEVICE(USB_DIBCOM_VENDOR_ID, USB_DIBCOM_PRODUCT_PREFW_ID) }, + { USB_DEVICE(USB_DIBCOM_VENDOR_ID, USB_DIBCOM_PRODUCT_ID) }, + { USB_DEVICE(USB_ULTIMA_ELECTRONIC_ID, USB_ULTIMA_ELEC_PROD_PREFW_ID) }, + { USB_DEVICE(USB_ULTIMA_ELECTRONIC_ID, USB_ULTIMA_ELEC_PROD_ID) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, dibusb_table); + +static int dibusb_readwrite_usb(struct usb_dibusb *dib, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + int actlen,ret = -ENOMEM; + + if (wbuf == NULL || wlen == 0) + return -EINVAL; + +/* if (dib->disconnecting) + return -EINVAL;*/ + + if ((ret = down_interruptible(&dib->usb_sem))) + return ret; + + debug_dump(wbuf,wlen); + + ret = usb_bulk_msg(dib->udev,COMMAND_PIPE, + wbuf,wlen,&actlen,DIBUSB_I2C_TIMEOUT); + + if (ret) + err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); + else + ret = actlen != wlen ? -1 : 0; + + /* an answer is expected */ + if (!ret && rbuf && rlen) { + ret = usb_bulk_msg(dib->udev,RESULT_PIPE,rbuf,rlen, + &actlen,DIBUSB_I2C_TIMEOUT); + + if (ret) + err("recv bulk message failed: %d",ret); + else { + deb_alot("rlen: %d\n",rlen); + debug_dump(rbuf,actlen); + } + } + + up(&dib->usb_sem); + return ret; +} + +static int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len) +{ + return dibusb_readwrite_usb(dib,buf,len,NULL,0); +} + +static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */ + /* write only ? */ + int wo = (rbuf == NULL || rlen == 0), + len = 2 + wlen + (wo ? 0 : 2); + + deb_alot("wo: %d, wlen: %d, len: %d\n",wo,wlen,len); + + sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ; + sndbuf[1] = (addr & 0xfe) | (wo ? 0 : 1); + + memcpy(&sndbuf[2],wbuf,wlen); + + if (!wo) { + sndbuf[wlen+2] = (rlen >> 8) & 0xff; + sndbuf[wlen+3] = rlen & 0xff; + } + + return dibusb_readwrite_usb(dib,sndbuf,len,rbuf,rlen); +} + +/* + * DVB stuff + */ + +static struct dibusb_pid * dibusb_get_free_pid(struct usb_dibusb *dib) +{ + int i; + unsigned long flags; + struct dibusb_pid *dpid = NULL; + + spin_lock_irqsave(&dib->pid_list_lock,flags); + for (i=0; i < DIBUSB_MAX_PIDS; i++) + if (!dib->pid_list[i].active) { + dpid = dib->pid_list + i; + dpid->active = 1; + break; + } + spin_unlock_irqrestore(&dib->pid_list_lock,flags); + return dpid; +} + +static int dibusb_start_xfer(struct usb_dibusb *dib) +{ + u8 b[4] = { + (DIB3000MB_REG_FIFO >> 8) & 0xff, + (DIB3000MB_REG_FIFO) & 0xff, + (DIB3000MB_FIFO_ACTIVATE >> 8) & 0xff, + (DIB3000MB_FIFO_ACTIVATE) & 0xff + }; + return dibusb_i2c_msg(dib,DIBUSB_DEMOD_I2C_ADDR_DEFAULT,b,4,NULL,0); +} + +static int dibusb_stop_xfer(struct usb_dibusb *dib) +{ + u8 b[4] = { + (DIB3000MB_REG_FIFO >> 8) & 0xff, + (DIB3000MB_REG_FIFO) & 0xff, + (DIB3000MB_FIFO_INHIBIT >> 8) & 0xff, + (DIB3000MB_FIFO_INHIBIT) & 0xff + }; + return dibusb_i2c_msg(dib,DIBUSB_DEMOD_I2C_ADDR_DEFAULT,b,4,NULL,0); +} + +static int dibusb_set_pid(struct dibusb_pid *dpid) +{ + u16 pid = dpid->pid | (dpid->active ? DIB3000MB_ACTIVATE_FILTERING : 0); + u8 b[4] = { + (dpid->reg >> 8) & 0xff, + (dpid->reg) & 0xff, + (pid >> 8) & 0xff, + (pid) & 0xff + }; + + return dibusb_i2c_msg(dpid->dib,DIBUSB_DEMOD_I2C_ADDR_DEFAULT,b,4,NULL,0); +} + +static void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs) +{ + struct usb_dibusb *dib = urb->context; + + if (!dib->streaming) + return; + + if (urb->status == 0) { + deb_info("URB return len: %d\n",urb->actual_length); + if (urb->actual_length % 188) + deb_info("TS Packets: %d, %d\n", urb->actual_length/188,urb->actual_length % 188); + dvb_dmx_swfilter_packets(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length/188); + } + + if (dib->streaming) + usb_submit_urb(urb,GFP_KERNEL); +} + + +static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ +// struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct usb_dibusb *dib = dvbdmxfeed->demux->priv; + struct dibusb_pid *dpid; + int ret = 0; + + deb_info("pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); + + if ((dpid = dibusb_get_free_pid(dib)) == NULL) { + err("no free pid in list."); + return -ENODEV; + } + dvbdmxfeed->priv = dpid; + dpid->pid = dvbdmxfeed->pid; + + dibusb_set_pid(dpid); + + if (0 == dib->feed_count++) { + usb_fill_bulk_urb( dib->buf_urb, dib->udev, DATA_PIPE, + dib->buffer, 8192, dibusb_urb_complete, dib); + dib->buf_urb->transfer_flags = 0; + + + if ((ret = usb_submit_urb(dib->buf_urb,GFP_KERNEL))) { + dibusb_stop_xfer(dib); + err("could not submit buffer urb."); + return ret; + } + + if ((ret = dibusb_start_xfer(dib))) + return ret; + + dib->streaming = 1; + } + return 0; +} + +static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct usb_dibusb *dib = dvbdmxfeed->demux->priv; + struct dibusb_pid *dpid = (struct dibusb_pid *) dvbdmxfeed->priv; + + deb_info("stopfeed pid: 0x%04x, feedtype: %d",dvbdmxfeed->pid, dvbdmxfeed->type); + + if (dpid == NULL) + err("channel in dmxfeed->priv was NULL"); + else { + dpid->active = 0; + dpid->pid = 0; + dibusb_set_pid(dpid); + } + + if (--dib->feed_count == 0) { + dib->streaming = 0; + usb_unlink_urb(dib->buf_urb); + dibusb_stop_xfer(dib); + } + return 0; +} + +/* + * firmware transfers + */ + +/* + * do not use this, just a workaround for a bug, + * which will never occur :). + */ +static int dibusb_interrupt_read_loop(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_REQ_INTR_READ }; + return dibusb_write_usb(dib,b,1); +} + +/* + * TODO: a tasklet should run with a delay of 1/10 second + * and fill an appropriate event device ? + */ +static int dibusb_read_remote_control(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5]; + int ret; + if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5))) + return ret; + + return 0; +} + +/* + * ioctl for the firmware + */ +static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen) +{ + u8 b[34]; + int size = plen > 32 ? 32 : plen; + b[0] = DIBUSB_REQ_SET_IOCTL; + b[1] = cmd; + memcpy(&b[2],param,size); + + return dibusb_write_usb(dib,b,2+size); +} + +/* + * ioctl for power control + */ +static int dibusb_hw_sleep(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_IOCTL_POWER_SLEEP }; + return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); +} + +static int dibusb_hw_wakeup(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP }; + return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); +} + +/* + * I2C + */ +static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct usb_dibusb *dib = i2c_get_adapdata(adap); + int i; + + if (down_interruptible(&dib->i2c_sem) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len, + msg[i+1].buf,msg[i+1].len) < 0) + break; + i++; + } else + if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0) + break; + } + + up(&dib->i2c_sem); + return i; +} + +static u32 dibusb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static int dibusb_i2c_client_register (struct i2c_client *i2c) +{ + struct usb_dibusb *dib = i2c_get_adapdata(i2c->adapter); + if (i2c->driver->command) + return i2c->driver->command(i2c,FE_REGISTER,dib->adapter); + return 0; +} + +static int dibusb_i2c_client_unregister (struct i2c_client *i2c) +{ + struct usb_dibusb *dib = i2c_get_adapdata(i2c->adapter); + if (i2c->driver->command) + return i2c->driver->command(i2c,FE_UNREGISTER,dib->adapter); + return 0; +} + +static struct i2c_algorithm dibusb_algo = { + .name = "DiBcom USB i2c algorithm", + .id = I2C_ALGO_BIT, + .master_xfer = dibusb_i2c_xfer, + .functionality = dibusb_i2c_func, +}; + +static int dibusb_dvb_init(struct usb_dibusb *dib) +{ + int ret; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,4) + if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC)) < 0) { +#else + if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC , + THIS_MODULE)) < 0) { +#endif + deb_info("dvb_register_adapter failed: error %d", ret); + goto err; + } + + strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + dib->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, +#else + dib->i2c_adap.class = I2C_CLASS_TV_DIGITAL, +#endif + dib->i2c_adap.algo = &dibusb_algo; + dib->i2c_adap.algo_data = NULL; + dib->i2c_adap.id = I2C_ALGO_BIT; + dib->i2c_adap.client_register = dibusb_i2c_client_register, + dib->i2c_adap.client_unregister = dibusb_i2c_client_unregister, + + i2c_set_adapdata(&dib->i2c_adap, dib); + + if ((i2c_add_adapter(&dib->i2c_adap) < 0)) { + err("could not add i2c adapter"); + goto err_i2c; + } + + dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + + dib->demux.priv = (void *)dib; + dib->demux.filternum = DIBUSB_MAX_PIDS; + dib->demux.feednum = DIBUSB_MAX_PIDS; + dib->demux.start_feed = dibusb_start_feed; + dib->demux.stop_feed = dibusb_stop_feed; + dib->demux.write_to_decoder = NULL; + if ((ret = dvb_dmx_init(&dib->demux)) < 0) { + err("dvb_dmx_init failed: error %d",ret); + goto err_dmx; + } + + dib->dmxdev.filternum = dib->demux.filternum; + dib->dmxdev.demux = &dib->demux.dmx; + dib->dmxdev.capabilities = 0; + if ((ret = dvb_dmxdev_init(&dib->dmxdev, dib->adapter)) < 0) { + err("dvb_dmxdev_init failed: error %d",ret); + goto err_dmx_dev; + } + + dvb_net_init(dib->adapter, &dib->dvb_net, &dib->demux.dmx); + + goto success; +err_dmx_dev: + dvb_dmx_release(&dib->demux); +err_dmx: + i2c_del_adapter(&dib->i2c_adap); +err_i2c: + dvb_unregister_adapter(dib->adapter); +err: + return ret; +success: + return 0; +} + +static int dibusb_dvb_exit(struct usb_dibusb *dib) +{ + deb_info("unregistering DVB part\n"); + dvb_net_release(&dib->dvb_net); + dib->demux.dmx.close(&dib->demux.dmx); + dvb_dmxdev_release(&dib->dmxdev); + dvb_dmx_release(&dib->demux); + i2c_del_adapter(&dib->i2c_adap); + dvb_unregister_adapter(dib->adapter); + + return 0; +} + +static int dibusb_exit(struct usb_dibusb *dib) +{ + usb_free_urb(dib->buf_urb); + pci_free_consistent(NULL,8192,dib->buffer,dib->dma_handle); + return 0; +} + +static int dibusb_init(struct usb_dibusb *dib) +{ + int ret,i; + sema_init(&dib->usb_sem, 1); + sema_init(&dib->i2c_sem, 1); + + /* + * when reloading the driver w/o replugging the device + * a timeout occures, this helps + */ + usb_clear_halt(dib->udev,COMMAND_PIPE); + usb_clear_halt(dib->udev,RESULT_PIPE); + usb_clear_halt(dib->udev,DATA_PIPE); + + /* dibusb_reset_cpu(dib); */ + + dib->buffer = pci_alloc_consistent(NULL,8192, &dib->dma_handle); + memset(dib->buffer,0,8192); + if (!(dib->buf_urb = usb_alloc_urb(0,GFP_KERNEL))) { + dibusb_exit(dib); + return -ENOMEM; + } + + for (i=0; i < DIBUSB_MAX_PIDS; i++) { + dib->pid_list[i].reg = i+DIB3000MB_REG_FIRST_PID; + dib->pid_list[i].pid = 0; + dib->pid_list[i].active = 0; + dib->pid_list[i].dib = dib; + } + + dib->streaming = 0; + dib->feed_count = 0; + + if ((ret = dibusb_dvb_init(dib))) { + dibusb_exit(dib); + return ret; + } + return 0; +} + +/* + * load a firmware packet to the device + */ +static int dibusb_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev,0), + 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5*HZ); +} + +static int dibusb_loadfirmware(struct usb_device *udev, + struct dibusb_device *dibdev) +{ + const struct firmware *fw = NULL; + u16 addr; + u8 *b,*p; + int ret = 0,i; + + for (i = 0; i < sizeof(valid_firmware_filenames)/sizeof(const char*); i++) { + if ((ret = request_firmware(&fw, valid_firmware_filenames[i], &udev->dev)) == 0) { + info("using firmware file (%s).",valid_firmware_filenames[i]); + break; + } + deb_info("tried to find '%s' firmware - unsuccessful. (%d)\n", + valid_firmware_filenames[i],ret); + } + + if (fw == NULL) { + err("did not find a valid firmware file. " + "Please see linux/Documentation/dvb/ for more details on firmware-problems."); + return -EINVAL; + } + p = kmalloc(fw->size,GFP_KERNEL); + if (p != NULL) { + u8 reset; + /* + * you cannot use the fw->data as buffer for + * usb_control_msg, a new buffer has to be + * created + */ + memcpy(p,fw->data,fw->size); + + /* stop the CPU */ + reset = 1; + if ((ret = dibusb_writemem(udev,DIBUSB_CPU_CSREG,&reset,1)) != 1) + err("could not stop the USB controller CPU."); + for(i = 0; p[i+3] == 0 && i < fw->size; ) { + b = (u8 *) &p[i]; + addr = *((u16 *) &b[1]); + + ret = dibusb_writemem(udev,addr,&b[4],b[0]); + + if (ret != b[0]) { + err("error while transferring firmware " + "(transferred size: %d, block size: %d)", + ret,b[1]); + ret = -EINVAL; + break; + } + i += 5 + b[0]; + } + /* restart the CPU */ + reset = 0; + if ((ret = dibusb_writemem(udev,DIBUSB_CPU_CSREG,&reset,1)) != 1) + err("could not restart the USB controller CPU."); + + kfree(p); + ret = 0; + } else { + ret = -ENOMEM; + } + release_firmware(fw); + + return ret; +} + +/* + * USB + */ +static int dibusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_dibusb *dib = NULL; + struct dibusb_device *dibdev = NULL; + + int ret = -ENOMEM,i,cold=0; + + for (i = 0; i < DIBUSB_SUPPORTED_DEVICES; i++) + if (dibusb_devices[i].cold_product_id == udev->descriptor.idProduct || + dibusb_devices[i].warm_product_id == udev->descriptor.idProduct) { + dibdev = &dibusb_devices[i]; + + cold = dibdev->cold_product_id == udev->descriptor.idProduct; + + if (cold) + info("found a '%s' in cold state, will try to load a firmware",dibdev->name); + else + info("found a '%s' in warm state.",dibdev->name); + } + + if (dibdev == NULL) { + err("something went very wrong, " + "unknown product ID: %.4x",udev->descriptor.idProduct); + return -ENODEV; + } + + if (cold) + ret = dibusb_loadfirmware(udev,dibdev); + else { + dib = kmalloc(sizeof(struct usb_dibusb),GFP_KERNEL); + if (dib == NULL) { + err("no memory"); + return ret; + } + memset(dib,0,sizeof(struct usb_dibusb)); + + dib->udev = udev; + dib->dibdev = dibdev; + + usb_set_intfdata(intf, dib); + + ret = dibusb_init(dib); + } + + if (ret == 0) + info("%s successfully initialized and connected.",dibdev->name); + else + info("%s error while loading driver (%d)",dibdev->name,ret); + return ret; +} + +static void dibusb_disconnect(struct usb_interface *intf) +{ + struct usb_dibusb *dib = usb_get_intfdata(intf); + const char *name = DRIVER_DESC; + + usb_set_intfdata(intf,NULL); + if (dib != NULL) { + name = dib->dibdev->name; + dibusb_dvb_exit(dib); + dibusb_exit(dib); + kfree(dib); + } + info("%s successfully deinitialized and disconnected.",name); + +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver dibusb_driver = { + .owner = THIS_MODULE, + .name = "dvb_dibusb", + .probe = dibusb_probe, + .disconnect = dibusb_disconnect, + .id_table = dibusb_table, +}; + +/* module stuff */ +static int __init usb_dibusb_init(void) +{ + int result; + if ((result = usb_register(&dibusb_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit usb_dibusb_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&dibusb_driver); +} + +module_init (usb_dibusb_init); +module_exit (usb_dibusb_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -puN /dev/null drivers/media/dvb/dibusb/dvb-dibusb.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/dibusb/dvb-dibusb.h 2004-09-20 11:22:37.731914840 -0700 @@ -0,0 +1,175 @@ +/* + * dvb-dibusb.h + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * + * for more information see dvb-dibusb.c . + */ + +#ifndef __DVB_DIBUSB_H__ +#define __DVB_DIBUSB_H__ + +/* Vendor IDs */ +#define USB_TWINHAN_VENDOR_ID 0x1822 +#define USB_IMC_NETWORKS_VENDOR_ID 0x13d3 +#define USB_KWORLD_VENDOR_ID 0xeb1a +#define USB_DIBCOM_VENDOR_ID 0x10b8 +#define USB_ULTIMA_ELECTRONIC_ID 0x05d8 + +/* Product IDs before loading the firmware */ +#define USB_VP7041_PRODUCT_PREFW_ID 0x3201 +#define USB_VSTREAM_PRODUCT_PREFW_ID 0x17de +#define USB_DIBCOM_PRODUCT_PREFW_ID 0x0bb8 +#define USB_ULTIMA_ELEC_PROD_PREFW_ID 0x8105 + +/* product ID afterwards */ +#define USB_VP7041_PRODUCT_ID 0x3202 +#define USB_VSTREAM_PRODUCT_ID 0x17df +#define USB_DIBCOM_PRODUCT_ID 0x0bb9 +#define USB_ULTIMA_ELEC_PROD_ID 0x8106 + +/* CS register start/stop the usb controller cpu */ +#define DIBUSB_CPU_CSREG 0x7F92 + +// 0x10 is the I2C address of the first demodulator on the board +#define DIBUSB_DEMOD_I2C_ADDR_DEFAULT 0x10 +#define DIBUSB_I2C_TIMEOUT HZ*5 + +#define DIBUSB_MAX_PIDS 16 + +#define DIB3000MB_REG_FIRST_PID ( 153) + +struct usb_dibusb; + +struct dibusb_pid { + u16 reg; + u16 pid; + int active; + struct usb_dibusb *dib; +}; + +struct usb_dibusb { + /* usb */ + struct usb_device * udev; + + struct dibusb_device * dibdev; + + int streaming; + int feed_count; + struct urb *buf_urb; + u8 *buffer; + dma_addr_t dma_handle; + + spinlock_t pid_list_lock; + struct dibusb_pid pid_list[DIBUSB_MAX_PIDS]; + + /* I2C */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + /* locking */ + struct semaphore usb_sem; + struct semaphore i2c_sem; + + /* dvb */ + struct dvb_adapter *adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; +}; + + +struct dibusb_device { + u16 cold_product_id; + u16 warm_product_id; + u8 demod_addr; + const char *name; +}; + +/* static array of valid firmware names, the best one first */ +static const char * valid_firmware_filenames[] = { + "dvb-dibusb-5.0.0.11.fw", +}; + +#define DIBUSB_SUPPORTED_DEVICES 4 + +/* USB Driver stuff */ +static struct dibusb_device dibusb_devices[DIBUSB_SUPPORTED_DEVICES] = { + { .cold_product_id = USB_VP7041_PRODUCT_PREFW_ID, + .warm_product_id = USB_VP7041_PRODUCT_ID, + .name = "Twinhan VisionDTV USB-Ter/HAMA USB DVB-T device", + .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, + }, + { .cold_product_id = USB_VSTREAM_PRODUCT_PREFW_ID, + .warm_product_id = USB_VSTREAM_PRODUCT_ID, + .name = "KWorld V-Stream XPERT DTV - DVB-T USB", + .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, + }, + { .cold_product_id = USB_DIBCOM_PRODUCT_PREFW_ID, + .warm_product_id = USB_DIBCOM_PRODUCT_ID, + .name = "DiBcom USB reference design", + .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, + }, + { + .cold_product_id = USB_ULTIMA_ELEC_PROD_PREFW_ID, + .warm_product_id = USB_ULTIMA_ELEC_PROD_ID, + .name = "Ultima Electronic/Artec T1 USB TVBOX", + .demod_addr = DIBUSB_DEMOD_I2C_ADDR_DEFAULT, + }, +}; + +#define COMMAND_PIPE usb_sndbulkpipe(dib->udev, 0x01) +#define RESULT_PIPE usb_rcvbulkpipe(dib->udev, 0x81) +#define DATA_PIPE usb_rcvbulkpipe(dib->udev, 0x82) +/* + * last endpoint 0x83 only used for chaining the buffers + * of the endpoints in the cypress + */ +#define CHAIN_PIPE_DO_NOT_USE usb_rcvbulkpipe(dib->udev, 0x83) + +/* types of first byte of each buffer */ + +#define DIBUSB_REQ_START_READ 0x00 +#define DIBUSB_REQ_START_DEMOD 0x01 +#define DIBUSB_REQ_I2C_READ 0x02 +#define DIBUSB_REQ_I2C_WRITE 0x03 + +/* prefix for reading the current RC key */ +#define DIBUSB_REQ_POLL_REMOTE 0x04 + +/* 0x05 0xXX */ +#define DIBUSB_REQ_SET_STREAMING_MODE 0x05 + +/* interrupt the internal read loop, when blocking */ +#define DIBUSB_REQ_INTR_READ 0x06 + +/* IO control + * 0x07 + */ +#define DIBUSB_REQ_SET_IOCTL 0x07 + +/* IOCTL commands */ + +/* change the power mode in firmware */ +#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00 +#define DIBUSB_IOCTL_POWER_SLEEP 0x00 +#define DIBUSB_IOCTL_POWER_WAKEUP 0x01 + + +/* + * values from the demodulator which are needed in + * the usb driver as well + */ + +#define DIB3000MB_REG_FIFO ( 145) +#define DIB3000MB_FIFO_INHIBIT ( 1) +#define DIB3000MB_FIFO_ACTIVATE ( 0) + +#define DIB3000MB_ACTIVATE_FILTERING (0x2000) + +#endif diff -puN /dev/null drivers/media/dvb/dibusb/Kconfig --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/dibusb/Kconfig 2004-09-20 11:22:37.731914840 -0700 @@ -0,0 +1,31 @@ +config DVB_DIBUSB + tristate "Twinhan/KWorld/Hama/Artec USB DVB-T devices" + depends on DVB_CORE && USB + select FW_LOADER + help + Support for USB 1.1 DVB-T devices based on a reference design made by + DiBcom (http://www.dibcom.fr). + + Devices supported by this driver: + + Twinhan VisionPlus VisionDTV USB-Ter (VP7041) + KWorld V-Stream XPERT DTV - DVB-T USB + Hama DVB-T USB-Box + DiBcom reference device (non-public) + Ultima Electronic/Artec T1 USB TVBOX + + The VP7041 seems to be identical to "CTS Portable" (Chinese + Television System). + + These devices can be understood as budget ones, they "only" deliver + the MPEG data. + + Currently all known copies of the DiBcom reference design have the DiBcom 3000MB + frontend onboard. Please enable and load this one manually in order to use this + device. + + A firmware is needed to use the device. See Documentation/dvb/README.dibusb + details. + + Say Y if you own such a device and want to use it. You should build it as + a module. diff -puN /dev/null drivers/media/dvb/dibusb/Makefile --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/dibusb/Makefile 2004-09-20 11:22:37.731914840 -0700 @@ -0,0 +1,3 @@ +obj-$(CONFIG_DVB_DIBUSB) += dvb-dibusb.o + +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ diff -puN drivers/media/dvb/frontends/Kconfig~DVB-new-driver-dibusb drivers/media/dvb/frontends/Kconfig --- 25/drivers/media/dvb/frontends/Kconfig~DVB-new-driver-dibusb 2004-09-20 11:22:37.712917728 -0700 +++ 25-akpm/drivers/media/dvb/frontends/Kconfig 2004-09-20 11:22:37.733914536 -0700 @@ -63,13 +63,9 @@ config DVB_SP887X help A DVB-T tuner module. Say Y when you want to support this frontend. - This driver needs a copy of the Avermedia firmware. The version tested - is part of the Avermedia DVB-T 1.3.26.3 Application. If the software is - installed in Windoze the file will be in the /Program Files/AVerTV DVB-T/ - directory and is called sc_main.mc. Alternatively it can "extracted" from - the install cab files. - - Copy this file to '/usr/lib/hotplug/firmware/dvb-fe-sp887x.fw'. + This driver needs external firmware. Please use the command + "/Documentation/dvb/get_dvb_firmware sp887x" to + download/extract it, and then copy it to /usr/lib/hotplug/firmware. If you don't know what tuner module is soldered on your DVB adapter simply enable all supported frontends, the @@ -81,9 +77,9 @@ config DVB_ALPS_TDLB7 help A DVB-T tuner module. Say Y when you want to support this frontend. - This driver needs a copy of the firmware file from the Haupauge - Windoze driver. Copy 'Sc_main.mc' to - '/usr/lib/hotplug/firmware/dvb-fe-tdlb7.fw'. + This driver needs external firmware. Please use the command + "/Documentation/dvb/get_dvb_firmware alps_tdlb7" to + download/extract it, and then copy it to /usr/lib/hotplug/firmware. If you don't know what tuner module is soldered on your DVB adapter simply enable all supported frontends, the @@ -99,6 +95,16 @@ config DVB_ALPS_TDMB7 DVB adapter simply enable all supported frontends, the right one will get autodetected. +config DVB_CX22702 + tristate "Conexant cx22702 demodulator (OFDM)" + depends on DVB_CORE + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + If you don't know what tuner module is soldered on your + DVB adapter simply enable all supported frontends, the + right one will get autodetected. + config DVB_GRUNDIG_29504_401 tristate "Grundig 29504-401 based" depends on DVB_CORE @@ -115,6 +121,11 @@ config DVB_TDA1004X help A DVB-T tuner module. Say Y when you want to support this frontend. + This driver needs external firmware. Please use the commands + "/Documentation/dvb/get_dvb_firmware tda10045", + "/Documentation/dvb/get_dvb_firmware tda10046" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware. + If you don't know what tuner module is soldered on your DVB adapter simply enable all supported frontends, the right one will get autodetected. @@ -139,6 +150,21 @@ config DVB_MT352 DVB adapter simply enable all supported frontends, the right one will get autodetected. +config DVB_DIB3000MB + tristate "DiBcom 3000-MB" + depends on DVB_CORE + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + + Used on USB-powered devices. You should also say Y to DVB_DIBUSB + (DiBcom USB DVB-T Adapter) to support the actual device, + this is "only" the frontend/tuner. + + If you don't know what tuner module is soldered on your + DVB adapter simply enable all supported frontends, the + right one will get autodetected. + comment "DVB-C (cable) frontends" depends on DVB_CORE diff -puN drivers/media/dvb/Makefile~DVB-new-driver-dibusb drivers/media/dvb/Makefile --- 25/drivers/media/dvb/Makefile~DVB-new-driver-dibusb 2004-09-20 11:22:37.724915904 -0700 +++ 25-akpm/drivers/media/dvb/Makefile 2004-09-20 11:22:37.732914688 -0700 @@ -2,5 +2,4 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ - +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dibusb/ _