diff options
author | Patrick Boettcher <devnull@localhost> | 2005-03-30 12:40:31 +0000 |
---|---|---|
committer | Patrick Boettcher <devnull@localhost> | 2005-03-30 12:40:31 +0000 |
commit | 201fa3b5e4bf5879ef6b24ac555b4c8c1e378282 (patch) | |
tree | a720cd3376efb93bfcf13cfb5f3946c12932c782 /linux/drivers/media/dvb | |
parent | 68d4a468a95777934e2659e8f4bb4095a55969ca (diff) | |
download | mediapointer-dvb-s2-201fa3b5e4bf5879ef6b24ac555b4c8c1e378282.tar.gz mediapointer-dvb-s2-201fa3b5e4bf5879ef6b24ac555b4c8c1e378282.tar.bz2 |
Added support for the TwinhanDTV Alpha/MagicBox II USB2 DVB-T device in
conjunction with a neat dvb-usb-lib. All dibusb-drivers will be merged to it in
the near future.
Diffstat (limited to 'linux/drivers/media/dvb')
-rw-r--r-- | linux/drivers/media/dvb/Kconfig | 1 | ||||
-rw-r--r-- | linux/drivers/media/dvb/Makefile | 2 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dibusb/dvb-dibusb-usb.c | 1 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/Kconfig | 26 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/Makefile | 7 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb-common.h | 33 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c | 191 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c | 100 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c | 36 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb-init.c | 177 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c | 321 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb-urb.c | 155 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/dvb-usb.h | 182 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/vp7045-fe.c | 182 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/vp7045.c | 193 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-usb/vp7045.h | 53 |
16 files changed, 1658 insertions, 2 deletions
diff --git a/linux/drivers/media/dvb/Kconfig b/linux/drivers/media/dvb/Kconfig index 9942c5d83..6905ac12f 100644 --- a/linux/drivers/media/dvb/Kconfig +++ b/linux/drivers/media/dvb/Kconfig @@ -27,6 +27,7 @@ source "drivers/media/dvb/ttpci/Kconfig" comment "Supported USB Adapters" depends on DVB_CORE && USB +source "drivers/media/dvb/dvb-usb/Kconfig" source "drivers/media/dvb/ttusb-budget/Kconfig" source "drivers/media/dvb/ttusb-dec/Kconfig" source "drivers/media/dvb/dibusb/Kconfig" diff --git a/linux/drivers/media/dvb/Makefile b/linux/drivers/media/dvb/Makefile index 520fc3902..b6d52e31b 100644 --- a/linux/drivers/media/dvb/Makefile +++ b/linux/drivers/media/dvb/Makefile @@ -2,4 +2,4 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dibusb/ cinergyT2/ +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dibusb/ cinergyT2/ dvb-usb/ diff --git a/linux/drivers/media/dvb/dibusb/dvb-dibusb-usb.c b/linux/drivers/media/dvb/dibusb/dvb-dibusb-usb.c index 62e82b13e..41ebc430e 100644 --- a/linux/drivers/media/dvb/dibusb/dvb-dibusb-usb.c +++ b/linux/drivers/media/dvb/dibusb/dvb-dibusb-usb.c @@ -249,7 +249,6 @@ int dibusb_urb_init(struct usb_dibusb *dib) dibusb_urb_complete, dib); dib->urb_list[i]->transfer_flags = 0; - dib->init_state |= DIBUSB_STATE_URB_INIT; } diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig new file mode 100644 index 000000000..43a695d50 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -0,0 +1,26 @@ +config DVB_USB + tristate "Support for various USB DVB devices" + depends on DVB_CORE && USB + select FW_LOADER + help + By enabling this you will be able to choose the various USB 1.1 and + USB2.0 DVB devices. + + Almost every USB device needs a firmware, please look into + <file:Documentation/dvb/README.dvb-usb> + + Say Y if you own an USB DVB device. + +config DVB_USB_DEBUG + bool "Enable extended debug support for all DVB-USB devices" + depends on DVB_USB + help + Say Y if you want to enable debuging. See modinfo dvb-usb (and the + appropriate drivers) for debug levels. + +config DVB_USB_VP7045 + tristate "TwinhanDTV Alpha / MagicBox II DVB-T USB2.0 support + depends on DVB_USB + help + Say Y here to support the TwinhanDTV Alpha (stick) or the MagicBox II + DVB-T USB2.0 devices. diff --git a/linux/drivers/media/dvb/dvb-usb/Makefile b/linux/drivers/media/dvb/dvb-usb/Makefile new file mode 100644 index 000000000..116f1172e --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/Makefile @@ -0,0 +1,7 @@ +dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o +obj-$(CONFIG_DVB_USB) += dvb-usb.o + +dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o +obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o + +EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-common.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-common.h new file mode 100644 index 000000000..b18633e6b --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-common.h @@ -0,0 +1,33 @@ +#ifndef _DVB_USB_COMMON_H_ +#define _DVB_USB_COMMON_H_ + +#define DVB_USB_LOG_PREFIX "dvb-usb" +#include "dvb-usb.h" + +extern int dvb_usb_debug; + +#define deb_info(args...) dprintk(dvb_usb_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_debug,0x02,args) +#define deb_alot(args...) dprintk(dvb_usb_debug,0x04,args) +#define deb_ts(args...) dprintk(dvb_usb_debug,0x08,args) +#define deb_err(args...) dprintk(dvb_usb_debug,0x10,args) +#define deb_rc(args...) dprintk(dvb_usb_debug,0x20,args) + +/* commonly used methods */ +extern int usb_cypress_load_firmware(struct usb_device *, const char *, int); + +extern int dvb_usb_urb_submit(struct dvb_usb_device *); +extern int dvb_usb_urb_kill(struct dvb_usb_device *); +extern int dvb_usb_urb_init(struct dvb_usb_device *); +extern int dvb_usb_urb_exit(struct dvb_usb_device *); + +extern int dvb_usb_i2c_init(struct dvb_usb_device *d); +extern int dvb_usb_i2c_exit(struct dvb_usb_device *d); + +extern int dvb_usb_dvb_init(struct dvb_usb_device *d); +extern int dvb_usb_dvb_exit(struct dvb_usb_device *d); + +extern int dvb_usb_fe_init(struct dvb_usb_device *d); +extern int dvb_usb_fe_exit(struct dvb_usb_device *d); + +#endif diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c new file mode 100644 index 000000000..bec6a77d0 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -0,0 +1,191 @@ +/* This file contains functions for initializing and handling the + * linux-dvb API. + */ +#include "dvb-usb-common.h" + +static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct dvb_usb_device *d = dvbdmxfeed->demux->priv; + int newfeedcount,ret; + + if (d == NULL) + return -ENODEV; + + newfeedcount = d->feedcount + (onoff ? 1 : -1); + + /* + * stop feed before setting a new pid if there will be no pid anymore + */ + if (newfeedcount == 0) { + deb_ts("stop feeding\n"); + + if (d->props.streaming_ctrl != NULL) + if ((ret = d->props.streaming_ctrl(d,0))) + err("error while stopping stream."); + + dvb_usb_urb_kill(d); + } + + d->feedcount = newfeedcount; + + /* activate the pid on the device specific pid_filter */ + deb_ts("setting pid: %5d %04x at index %d '%s'\n",dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ? "on" : "off"); + if (d->props.caps & DVB_USB_HAS_PID_FILTER && + d->pid_filtering && + d->props.pid_filter != NULL) + d->props.pid_filter(d,dvbdmxfeed->index,dvbdmxfeed->pid,onoff); + + /* start the feed if this was the first feed and there is still a feed + * for reception. + */ + if (d->feedcount == onoff && d->feedcount > 0) { + + deb_ts("controlling pid parser\n"); + if (d->props.caps & DVB_USB_HAS_PID_FILTER && + d->props.caps & DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF && + d->props.pid_filter_ctrl != NULL) + if (d->props.pid_filter_ctrl(d,d->pid_filtering) < 0) + err("could not handle pid_parser"); + + deb_ts("start feeding\n"); + if (d->props.streaming_ctrl != NULL) + if (d->props.streaming_ctrl(d,1)) { + err("error while enabling fifo."); + return -ENODEV; + } + + dvb_usb_urb_submit(d); + } + return 0; +} + +static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); + return dvb_usb_ctrl_feed(dvbdmxfeed,1); +} + +static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type); + return dvb_usb_ctrl_feed(dvbdmxfeed,0); +} + +int dvb_usb_dvb_init(struct dvb_usb_device *d) +{ + int ret; + + if ((ret = dvb_register_adapter(&d->dvb_adap, d->desc->name, + THIS_MODULE)) < 0) { + deb_info("dvb_register_adapter failed: error %d", ret); + goto err; + } + d->dvb_adap.priv = d; + + d->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + d->demux.priv = d; + + /* get pidcount from demod */ + d->demux.feednum = d->demux.filternum = 255; + d->demux.start_feed = dvb_usb_start_feed; + d->demux.stop_feed = dvb_usb_stop_feed; + d->demux.write_to_decoder = NULL; + if ((ret = dvb_dmx_init(&d->demux)) < 0) { + err("dvb_dmx_init failed: error %d",ret); + goto err_dmx; + } + + d->dmxdev.filternum = d->demux.filternum; + d->dmxdev.demux = &d->demux.dmx; + d->dmxdev.capabilities = 0; + if ((ret = dvb_dmxdev_init(&d->dmxdev, &d->dvb_adap)) < 0) { + err("dvb_dmxdev_init failed: error %d",ret); + goto err_dmx_dev; + } + + dvb_net_init(&d->dvb_adap, &d->dvb_net, &d->demux.dmx); + + goto success; +err_dmx_dev: + dvb_dmx_release(&d->demux); +err_dmx: + dvb_unregister_adapter(&d->dvb_adap); +err: + return ret; +success: + d->init_state |= DVB_USB_STATE_DVB; + return 0; +} + +int dvb_usb_dvb_exit(struct dvb_usb_device *d) +{ + if (d->init_state & DVB_USB_STATE_DVB) { + d->init_state &= ~DVB_USB_STATE_DVB; + deb_info("unregistering DVB part\n"); + dvb_net_release(&d->dvb_net); + d->demux.dmx.close(&d->demux.dmx); + dvb_dmxdev_release(&d->dmxdev); + dvb_dmx_release(&d->demux); + dvb_unregister_adapter(&d->dvb_adap); + } + return 0; +} + +static int dvb_usb_fe_wakeup(struct dvb_frontend *fe) +{ + struct dvb_usb_device *d = fe->dvb->priv; + + if (d->props.power_ctrl) + d->props.power_ctrl(d,1); + + if (d->fe_init) + d->fe_init(fe); + + return 0; +} + +static int dvb_usb_fe_sleep(struct dvb_frontend *fe) +{ + struct dvb_usb_device *d = fe->dvb->priv; + + if (d->props.power_ctrl) + d->props.power_ctrl(d,0); + + if (d->fe_sleep) + d->fe_sleep(fe); + + return 0; +} + +int dvb_usb_fe_init(struct dvb_usb_device* d) +{ + if (d->props.frontend_attach == NULL) { + err("strange '%s' don't want to attach a frontend.",d->desc->name); + return 0; + } + + d->props.frontend_attach(d); + + /* re-assign sleep and wakeup functions */ + if (d->fe != NULL) { + d->fe_init = d->fe->ops->init; d->fe->ops->init = dvb_usb_fe_wakeup; + d->fe_sleep = d->fe->ops->sleep; d->fe->ops->sleep = dvb_usb_fe_sleep; + + if (dvb_register_frontend(&d->dvb_adap, d->fe)) { + err("Frontend registration failed."); + if (d->fe->ops->release) + d->fe->ops->release(d->fe); + d->fe = NULL; + return -ENODEV; + } + } else + err("no frontend was attached by '%s'",d->desc->name); + return 0; +} + +int dvb_usb_fe_exit(struct dvb_usb_device *d) +{ + if (d->fe != NULL) + dvb_unregister_frontend(d->fe); + return 0; +} diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c new file mode 100644 index 000000000..a2f749b0f --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c @@ -0,0 +1,100 @@ +/* + * dvb-usb-firmware.c is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This file contains functions for downloading the firmware to Cypress FX 1 and 2 based devices. + * + * FIXME: This part does actually not belong to dvb-usb, but to the usb-subsystem. + * + * See dvb-usb-core.c for copyright information. + */ +#include "dvb-usb-common.h" + +#include <linux/firmware.h> +#include <linux/usb.h> + +struct usb_cypress_controller { + int id; + const char *name; /* name of the usb controller */ + u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */ +}; + +static struct usb_cypress_controller cypress[] = { + { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 }, + { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 }, + { .id = CYPRESS_FX2, .name = "Cypress FX2", .cpu_cs_register = 0xe600 }, +}; + +/* + * load a firmware packet to the device + */ +static int usb_cypress_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); +} + +int usb_cypress_load_firmware(struct usb_device *udev, const char *filename, int type) +{ + const struct firmware *fw = NULL; + u16 addr; + u8 *b,*p; + int ret = 0,i; + + if ((ret = request_firmware(&fw, filename, &udev->dev)) != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems.", + filename); + return ret; + } + + info("downloading firmware from file '%s' to the '%s'",filename,cypress[type].name); + + 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 = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&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 = usb_cypress_writemem(udev,addr,&b[4],b[0]); + + if (ret != b[0]) { + err("error while transferring firmware " + "(transferred size: %d, block size: %d)", + ret,b[0]); + ret = -EINVAL; + break; + } + i += 5 + b[0]; + } + /* length in ret */ + if (ret > 0) + ret = 0; + /* restart the CPU */ + reset = 0; + if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + + kfree(p); + } else { + ret = -ENOMEM; + } + release_firmware(fw); + + return ret; +} diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c new file mode 100644 index 000000000..11fd89233 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -0,0 +1,36 @@ +#include "dvb-usb-common.h" + +int dvb_usb_i2c_init(struct dvb_usb_device *d) +{ + int ret = 0; + + if (!(d->props.caps & DVB_USB_IS_AN_I2C_ADAPTER)) + return 0; + + strncpy(d->i2c_adap.name,d->desc->name,I2C_NAME_SIZE); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + d->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, +#else + d->i2c_adap.class = I2C_CLASS_TV_DIGITAL, +#endif + d->i2c_adap.algo = &d->props.i2c_algo; + d->i2c_adap.algo_data = NULL; + d->i2c_adap.id = I2C_ALGO_BIT; + + i2c_set_adapdata(&d->i2c_adap, d); + + if ((ret = i2c_add_adapter(&d->i2c_adap)) < 0) + err("could not add i2c adapter"); + + d->init_state |= DVB_USB_STATE_I2C; + + return ret; +} + +int dvb_usb_i2c_exit(struct dvb_usb_device *d) +{ + if (d->init_state & DVB_USB_STATE_I2C) + i2c_del_adapter(&d->i2c_adap); + d->init_state &= ~DVB_USB_STATE_I2C; + return 0; +} diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-init.c new file mode 100644 index 000000000..c238aba8a --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-init.c @@ -0,0 +1,177 @@ +#include "dvb-usb-common.h" + +/* debug */ +int dvb_usb_debug; +module_param_named(debug,dvb_usb_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,alot=4,ts=8,err=16,rc=32 (or-able))." DVB_USB_DEBUG_STATUS); + +/* general initialization functions */ +int dvb_usb_exit(struct dvb_usb_device *d) +{ + deb_info("init_state before exiting everything: %x\n",d->init_state); +// dvb_usb_remote_exit(d); + dvb_usb_fe_exit(d); + dvb_usb_i2c_exit(d); + dvb_usb_dvb_exit(d); + dvb_usb_urb_exit(d); + deb_info("init_state should be zero now: %x\n",d->init_state); + d->init_state = DVB_USB_STATE_INIT; + kfree(d); + return 0; +} + +static int dvb_usb_init(struct dvb_usb_device *d) +{ + int ret = 0; + + sema_init(&d->usb_sem, 1); + sema_init(&d->i2c_sem, 1); + + d->init_state = DVB_USB_STATE_INIT; + +/* check the capabilites and set appropriate variables */ + +/* speed - when running at FULL speed we need a HW PID filter */ + if (d->udev->speed == USB_SPEED_FULL && !(d->props.caps & DVB_USB_HAS_PID_FILTER)) { + err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a HW PID filter)"); + return -ENODEV; + } + + + + if ((ret = dvb_usb_urb_init(d)) || + (ret = dvb_usb_dvb_init(d)) || + (ret = dvb_usb_i2c_init(d)) || + (ret = dvb_usb_fe_init(d))) { + dvb_usb_exit(d); + return ret; + } + +/* if ((ret = dvb_usb_remote_init(d))) + err("could not initialize remote control.");*/ + + return 0; +} + +/* determine the name and the state of the just found USB device */ +static struct dvb_usb_device_description * dvb_usb_find_device(struct usb_device *udev,struct dvb_usb_properties *props, int *cold) +{ + int i,j; + struct dvb_usb_device_description *desc = NULL; + *cold = -1; + + for (i = 0; i < props->num_device_descs; i++) { + + for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].cold_ids[j] != NULL; j++) { + deb_info("check for cold %x %x\n",props->devices[i].cold_ids[j]->idVendor, props->devices[i].cold_ids[j]->idProduct); + if (props->devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + props->devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 1; + desc = &props->devices[i]; + break; + } + } + + if (desc != NULL) + break; + + for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].warm_ids[j] != NULL; j++) { + deb_info("check for warm %x %x\n",props->devices[i].warm_ids[j]->idVendor, props->devices[i].warm_ids[j]->idProduct); + if (props->devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && + props->devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { + *cold = 0; + desc = &props->devices[i]; + break; + } + } + } + +/* if (desc != NULL && props->identify_desc_quirk != NULL) + desc = props->identify_desc_quirk(udev,props,desc);*/ + + return desc; +} + +/* + * USB + */ +int dvb_usb_device_init(struct usb_interface *intf, struct dvb_usb_properties *props) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct dvb_usb_device *d = NULL; + struct dvb_usb_device_description *desc = NULL; + + int ret = -ENOMEM,cold=0; + + if ((desc = dvb_usb_find_device(udev,props,&cold)) == NULL) { + err("something went very wrong, " + "unknown product ID: %.4x",le16_to_cpu(udev->descriptor.idProduct)); + return -ENODEV; + } + + if (cold) { + info("found a '%s' in cold state, will try to load a firmware",desc->name); + ret = usb_cypress_load_firmware(udev,props->firmware,props->usb_ctrl); + } else { + info("found a '%s' in warm state.",desc->name); + d = kmalloc(sizeof(struct dvb_usb_device),GFP_KERNEL); + if (d == NULL) { + err("no memory for 'struct dvb_usb_device'"); + return ret; + } + memset(d,0,sizeof(struct dvb_usb_device)); + + d->udev = udev; + memcpy(&d->props,props,sizeof(struct dvb_usb_properties)); + d->desc = desc; + + /* store parameters to structures */ +/* d->rc_query_interval = rc_query_interval; + d->pid_parse = pid_parse; + d->rc_key_repeat_count = rc_key_repeat_count;*/ + + usb_set_intfdata(intf, d); + + ret = dvb_usb_init(d); + } + + if (ret == 0) + info("%s successfully initialized and connected.",desc->name); + else + info("%s error while loading driver (%d)",desc->name,ret); + return ret; +} +EXPORT_SYMBOL(dvb_usb_device_init); + +void dvb_usb_device_exit(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + const char *name = "generic DVB-USB module"; + + usb_set_intfdata(intf,NULL); + if (d != NULL && d->desc != NULL) { + name = d->desc->name; + dvb_usb_exit(d); + } + info("%s successfully deinitialized and disconnected.",name); + +} +EXPORT_SYMBOL(dvb_usb_device_exit); + +/* module stuff */ +static int __init dvb_usb_module_init(void) +{ + return 0; +} + +static void __exit dvb_usb_module_exit(void) +{ +} + +module_init (dvb_usb_module_init); +module_exit (dvb_usb_module_exit); + +MODULE_VERSION("0.3"); +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>"); +MODULE_DESCRIPTION("A library module containing commonly used USB and DVB function USB DVB devices"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c new file mode 100644 index 000000000..d684b50c4 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -0,0 +1,321 @@ +/* + * dvb-usb-remote.c is part of the DVB USB library. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * This file contains functions for initializing the the input-device and for handling remote-control-queries. + * + * See dvb-usb-core.c for copyright information. + * + * TODO: this part is not compiling yet, some adaptions will necessary. + */ +#include "dvb-usb.h" + +/* Table to map raw key codes to key events. This should not be hard-wired + into the kernel. */ +static const struct { u8 c0, c1, c2; uint32_t key; } nec_rc_keys [] = +{ + /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ + { 0x00, 0xff, 0x16, KEY_POWER }, + { 0x00, 0xff, 0x10, KEY_MUTE }, + { 0x00, 0xff, 0x03, KEY_1 }, + { 0x00, 0xff, 0x01, KEY_2 }, + { 0x00, 0xff, 0x06, KEY_3 }, + { 0x00, 0xff, 0x09, KEY_4 }, + { 0x00, 0xff, 0x1d, KEY_5 }, + { 0x00, 0xff, 0x1f, KEY_6 }, + { 0x00, 0xff, 0x0d, KEY_7 }, + { 0x00, 0xff, 0x19, KEY_8 }, + { 0x00, 0xff, 0x1b, KEY_9 }, + { 0x00, 0xff, 0x15, KEY_0 }, + { 0x00, 0xff, 0x05, KEY_CHANNELUP }, + { 0x00, 0xff, 0x02, KEY_CHANNELDOWN }, + { 0x00, 0xff, 0x1e, KEY_VOLUMEUP }, + { 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN }, + { 0x00, 0xff, 0x11, KEY_RECORD }, + { 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */ + { 0x00, 0xff, 0x14, KEY_PLAY }, + { 0x00, 0xff, 0x1a, KEY_STOP }, + { 0x00, 0xff, 0x40, KEY_REWIND }, + { 0x00, 0xff, 0x12, KEY_FASTFORWARD }, + { 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */ + { 0x00, 0xff, 0x4c, KEY_PAUSE }, + { 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */ + { 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ + /* additional keys TwinHan VisionPlus, the Artec appearantly not has */ + { 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */ + { 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */ + { 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */ + { 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */ + { 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */ + { 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */ + /* Key codes for the KWorld/ADSTech/JetWay remote. */ + { 0x86, 0x6b, 0x12, KEY_POWER }, + { 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */ + { 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */ + { 0x86, 0x6b, 0x0b, KEY_EPG }, + { 0x86, 0x6b, 0x10, KEY_MUTE }, + { 0x86, 0x6b, 0x01, KEY_1 }, + { 0x86, 0x6b, 0x02, KEY_2 }, + { 0x86, 0x6b, 0x03, KEY_3 }, + { 0x86, 0x6b, 0x04, KEY_4 }, + { 0x86, 0x6b, 0x05, KEY_5 }, + { 0x86, 0x6b, 0x06, KEY_6 }, + { 0x86, 0x6b, 0x07, KEY_7 }, + { 0x86, 0x6b, 0x08, KEY_8 }, + { 0x86, 0x6b, 0x09, KEY_9 }, + { 0x86, 0x6b, 0x0a, KEY_0 }, + { 0x86, 0x6b, 0x18, KEY_ZOOM }, + { 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */ + { 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */ + { 0x86, 0x6b, 0x00, KEY_UNDO }, + { 0x86, 0x6b, 0x1d, KEY_RECORD }, + { 0x86, 0x6b, 0x0d, KEY_STOP }, + { 0x86, 0x6b, 0x0e, KEY_PAUSE }, + { 0x86, 0x6b, 0x16, KEY_PLAY }, + { 0x86, 0x6b, 0x11, KEY_BACK }, + { 0x86, 0x6b, 0x19, KEY_FORWARD }, + { 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */ + { 0x86, 0x6b, 0x15, KEY_ESC }, + { 0x86, 0x6b, 0x1a, KEY_UP }, + { 0x86, 0x6b, 0x1e, KEY_DOWN }, + { 0x86, 0x6b, 0x1f, KEY_LEFT }, + { 0x86, 0x6b, 0x1b, KEY_RIGHT }, +}; + +/* Hauppauge NOVA-T USB2 keys */ +static const struct { u16 raw; uint32_t key; } haupp_rc_keys [] = { + { 0xddf, KEY_GOTO }, + { 0xdef, KEY_POWER }, + { 0xce7, KEY_TV }, + { 0xcc7, KEY_VIDEO }, + { 0xccf, KEY_AUDIO }, + { 0xcd7, KEY_MEDIA }, + { 0xcdf, KEY_EPG }, + { 0xca7, KEY_UP }, + { 0xc67, KEY_RADIO }, + { 0xcb7, KEY_LEFT }, + { 0xd2f, KEY_OK }, + { 0xcbf, KEY_RIGHT }, + { 0xcff, KEY_BACK }, + { 0xcaf, KEY_DOWN }, + { 0xc6f, KEY_MENU }, + { 0xc87, KEY_VOLUMEUP }, + { 0xc8f, KEY_VOLUMEDOWN }, + { 0xc97, KEY_CHANNEL }, + { 0xc7f, KEY_MUTE }, + { 0xd07, KEY_CHANNELUP }, + { 0xd0f, KEY_CHANNELDOWN }, + { 0xdbf, KEY_RECORD }, + { 0xdb7, KEY_STOP }, + { 0xd97, KEY_REWIND }, + { 0xdaf, KEY_PLAY }, + { 0xda7, KEY_FASTFORWARD }, + { 0xd27, KEY_LAST }, /* Skip backwards */ + { 0xd87, KEY_PAUSE }, + { 0xcf7, KEY_NEXT }, + { 0xc07, KEY_0 }, + { 0xc0f, KEY_1 }, + { 0xc17, KEY_2 }, + { 0xc1f, KEY_3 }, + { 0xc27, KEY_4 }, + { 0xc2f, KEY_5 }, + { 0xc37, KEY_6 }, + { 0xc3f, KEY_7 }, + { 0xc47, KEY_8 }, + { 0xc4f, KEY_9 }, + { 0xc57, KEY_KPASTERISK }, + { 0xc77, KEY_GRAVE }, /* # */ + { 0xc5f, KEY_RED }, + { 0xd77, KEY_GREEN }, + { 0xdc7, KEY_YELLOW }, + { 0xd4f, KEY_BLUE}, +}; + +#define DIBUSB_RC_NEC_EMPTY 0x00 +#define DIBUSB_RC_NEC_KEY_PRESSED 0x01 +#define DIBUSB_RC_NEC_KEY_REPEATED 0x02 +static int dvb_usb_key2event_dibusb_nec(struct dvb_usb *d,u8 rb[5]) +{ + int i; + switch (rb[0]) { + case DIBUSB_RC_NEC_KEY_PRESSED: + /* rb[1-3] is the actual key, rb[4] is a checksum */ + deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + rb[1], rb[2], rb[3], rb[4]); + + if ((0xff - rb[3]) != rb[4]) { + deb_rc("remote control checksum failed.\n"); + break; + } + + /* See if we can match the raw key code. */ + for (i = 0; i < sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) { + if (nec_rc_keys[i].c0 == rb[1] && + nec_rc_keys[i].c1 == rb[2] && + nec_rc_keys[i].c2 == rb[3]) { + + dib->last_event = nec_rc_keys[i].key; + return 1; + } + } + break; + case DIBUSB_RC_NEC_KEY_REPEATED: + /* rb[1]..rb[4] are always zero.*/ + /* Repeats often seem to occur so for the moment just ignore this. */ + return 0; + case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */ + default: + break; + } + return -1; +} + +/* additional status values for Hauppauge Remote Control Protocol */ +#define HAUPPAUGE_RC_KEY_PRESSED 0x01 +#define HAUPPAUGE_RC_KEY_EMPTY 0x03 +static int dibusb_key2event_hauppauge(struct usb_dibusb *dib,u8 rb[3]) +{ + u16 raw; + int i,state; + switch (rb[0]) { + case HAUPPAUGE_RC_KEY_PRESSED: + raw = ((rb[1] & 0x0f) << 8) | rb[2]; + + state = !!(rb[1] & 0x40); + + deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to %04x state: %d\n",rb[1],rb[2],rb[3],raw,state); + for (i = 0; i < sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) { + if (haupp_rc_keys[i].raw == raw) { + if (dib->last_event == haupp_rc_keys[i].key && + dib->last_state == state) { + deb_rc("key repeat\n"); + return 0; + } else { + dib->last_event = haupp_rc_keys[i].key; + dib->last_state = state; + return 1; + } + } + } + + break; + case HAUPPAUGE_RC_KEY_EMPTY: + default: + break; + } + return -1; +} + +/* Read the remote control and feed the appropriate event. + * NEC protocol is used for remote controls + */ +static int dvb_usb_read_remote_control(struct usb_dibusb *dib) +{ + u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5]; + int ret,event = 0; + + if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5))) + return ret; + + switch (dib->dibdev->dev_cl->remote_type) { + case DIBUSB_RC_NEC_PROTOCOL: + event = dibusb_key2event_nec(dib,rb); + break; + case DIBUSB_RC_HAUPPAUGE_PROTO: + event = dibusb_key2event_hauppauge(dib,rb); + default: + break; + } + + /* key repeat */ + if (event == 0) + if (++dib->repeat_key_count < dib->rc_key_repeat_count) { + deb_rc("key repeat dropped. (%d)\n",dib->repeat_key_count); + event = -1; /* skip this key repeat */ + } + + if (event == 1 || event == 0) { + deb_rc("Translated key 0x%04x\n",event); + + /* Signal down and up events for this key. */ + input_report_key(&dib->rc_input_dev, dib->last_event, 1); + input_report_key(&dib->rc_input_dev, dib->last_event, 0); + input_sync(&dib->rc_input_dev); + + if (event == 1) + dib->repeat_key_count = 0; + } + return 0; +} + +/* Remote-control poll function - called every dib->rc_query_interval ms to see + whether the remote control has received anything. */ +static void dibusb_remote_query(void *data) +{ + struct usb_dibusb *dib = (struct usb_dibusb *) data; + /* TODO: need a lock here. We can simply skip checking for the remote control + if we're busy. */ + dibusb_read_remote_control(dib); + schedule_delayed_work(&dib->rc_query_work, + msecs_to_jiffies(dib->rc_query_interval)); +} + +int dibusb_remote_init(struct usb_dibusb *dib) +{ + int i; + + if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO) + return 0; + + /* Initialise the remote-control structures.*/ + init_input_dev(&dib->rc_input_dev); + + dib->rc_input_dev.evbit[0] = BIT(EV_KEY); + dib->rc_input_dev.keycodesize = sizeof(unsigned char); + dib->rc_input_dev.keycodemax = KEY_MAX; + dib->rc_input_dev.name = DRIVER_DESC " remote control"; + + switch (dib->dibdev->dev_cl->remote_type) { + case DIBUSB_RC_NEC_PROTOCOL: + for (i=0; i<sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) + set_bit(nec_rc_keys[i].key, dib->rc_input_dev.keybit); + break; + case DIBUSB_RC_HAUPPAUGE_PROTO: + for (i=0; i<sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) + set_bit(haupp_rc_keys[i].key, dib->rc_input_dev.keybit); + break; + default: + break; + } + + + input_register_device(&dib->rc_input_dev); + + INIT_WORK(&dib->rc_query_work, dibusb_remote_query, dib); + + /* Start the remote-control polling. */ + if (dib->rc_query_interval < 40) + dib->rc_query_interval = 100; /* default */ + + info("schedule remote query interval to %d msecs.",dib->rc_query_interval); + schedule_delayed_work(&dib->rc_query_work,msecs_to_jiffies(dib->rc_query_interval)); + + dib->init_state |= DIBUSB_STATE_REMOTE; + + return 0; +} + +int dibusb_remote_exit(struct usb_dibusb *dib) +{ + if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO) + return 0; + + if (dib->init_state & DIBUSB_STATE_REMOTE) { + cancel_delayed_work(&dib->rc_query_work); + flush_scheduled_work(); + input_unregister_device(&dib->rc_input_dev); + } + dib->init_state &= ~DIBUSB_STATE_REMOTE; + return 0; +} diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-urb.c new file mode 100644 index 000000000..a593b5779 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-urb.c @@ -0,0 +1,155 @@ +/* + * This file contains functions for initializing and handling the + * USB and URB stuff. + */ +#include "dvb-usb-common.h" + +#include <linux/pci.h> + +static void dvb_usb_bulk_urb_complete(struct urb *urb, struct pt_regs *ptregs) +{ + struct dvb_usb_device *d = urb->context; + + deb_ts("bulk urb completed. feedcount: %d, status: %d, length: %d\n",d->feedcount,urb->status, + urb->actual_length); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + deb_ts("urb completition error %d.", urb->status); + break; + } + + if (d->feedcount > 0 && urb->actual_length > 0) { + if (d->init_state & DVB_USB_STATE_DVB) + dvb_dmx_swfilter(&d->demux, (u8*) urb->transfer_buffer,urb->actual_length); + } else + deb_ts("URB dropped because of feedcount.\n"); + + usb_submit_urb(urb,GFP_ATOMIC); +} + +int dvb_usb_urb_kill(struct dvb_usb_device *d) +{ + int i; + for (i = 0; i < d->urbs_submitted; i++) { + deb_info("killing URB no. %d.\n",i); + + /* stop the URB */ + usb_kill_urb(d->urb_list[i]); + } + d->urbs_submitted = 0; + return 0; +} + +int dvb_usb_urb_submit(struct dvb_usb_device *d) +{ + int i,ret; + for (i = 0; i < d->urbs_initialized; i++) { + deb_info("submitting URB no. %d\n",i); + if ((ret = usb_submit_urb(d->urb_list[i],GFP_ATOMIC))) { + err("could not submit URB no. %d - get them all back\n",i); + dvb_usb_urb_kill(d); + return ret; + } + d->urbs_submitted++; + } + return 0; +} + +static int dvb_usb_bulk_urb_init(struct dvb_usb_device *d) +{ + int i,bufsize = d->props.urb.count * d->props.urb.u.bulk.buffersize; + + deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize); + /* allocate the actual buffer for the URBs */ + if ((d->buffer = pci_alloc_consistent(NULL,bufsize,&d->dma_handle)) == NULL) { + deb_info("not enough memory for urb-buffer allocation.\n"); + return -ENOMEM; + } + deb_info("allocation successful\n"); + memset(d->buffer,0,bufsize); + + d->init_state |= DVB_USB_STATE_URB_BUF; + + /* allocate the URBs */ + for (i = 0; i < d->props.urb.count; i++) { + if (!(d->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) { + return -ENOMEM; + } + + usb_fill_bulk_urb( d->urb_list[i], d->udev, + usb_rcvbulkpipe(d->udev,d->props.urb.endpoint), + &d->buffer[i*d->props.urb.u.bulk.buffersize], + d->props.urb.u.bulk.buffersize, + dvb_usb_bulk_urb_complete, d); + + d->urb_list[i]->transfer_flags = 0; + d->urbs_initialized++; + } + return 0; +} + +int dvb_usb_urb_init(struct dvb_usb_device *d) +{ + /* + * when reloading the driver w/o replugging the device + * a timeout occures, this helps + */ +// usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); +// usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); +// usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data)); + + /* allocate the array for the data transfer URBs */ + d->urb_list = kmalloc(d->props.urb.count * sizeof(struct urb *),GFP_KERNEL); + if (d->urb_list == NULL) + return -ENOMEM; + memset(d->urb_list,0,d->props.urb.count * sizeof(struct urb *)); + d->init_state |= DVB_USB_STATE_URB_LIST; + + switch (d->props.urb.type) { + case DVB_USB_BULK: + return dvb_usb_bulk_urb_init(d); + case DVB_USB_ISOC: + err("isochronous transfer not yet implemented in dvb-usb."); + return -EINVAL; + default: + err("unkown URB-type for data transfer."); + return -EINVAL; + } +} + +int dvb_usb_urb_exit(struct dvb_usb_device *d) +{ + int i; + + dvb_usb_urb_kill(d); + + if (d->init_state & DVB_USB_STATE_URB_LIST) { + for (i = 0; i < d->urbs_initialized; i++) { + if (d->urb_list[i] != NULL) { + deb_info("freeing URB no. %d.\n",i); + /* free the URBs */ + usb_free_urb(d->urb_list[i]); + } + } + d->urbs_initialized = 0; + /* free the urb array */ + kfree(d->urb_list); + d->init_state &= ~DVB_USB_STATE_URB_LIST; + } + + if (d->init_state & DVB_USB_STATE_URB_BUF) + pci_free_consistent(NULL, + d->props.urb.u.bulk.buffersize * d->props.urb.count, + d->buffer,d->dma_handle); + + d->init_state &= ~DVB_USB_STATE_URB_BUF; + return 0; +} diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h new file mode 100644 index 000000000..24aa7d077 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -0,0 +1,182 @@ +/* + * dvb-usb.h + * + * Copyright (C) 2004-5 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. + */ +#ifndef __DVB_USB_H__ +#define __DVB_USB_H__ + +#include <linux/config.h> +#include <linux/input.h> +#include <linux/usb.h> + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" + +/* debug */ +#ifdef CONFIG_DVB_USB_DEBUG +#define dprintk(var,level,args...) \ + do { if ((var & level)) { printk(args); } } while (0) + +#define debug_dump(b,l,func) {\ + int i; \ + for (i = 0; i < l; i++) func("%02x ", b[i]); \ + func("\n");\ +} +#define DVB_USB_DEBUG_STATUS +#else +#define dprintk(args...) +#define debug_dump(b,l) + +#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" + +#endif + +/* generic log methods - taken from usb.h */ +#ifndef DVB_USB_LOG_PREFIX + #define DVB_USB_LOG_PREFIX "dvb-usb (please define a log prefix)" +#endif + +#undef err +#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef info +#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +#undef warn +#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) + +struct dvb_usb_device_description { + const char *name; /* real name of the box */ + +#define DVB_USB_ID_MAX_NUM 15 + struct usb_device_id *cold_ids[DVB_USB_ID_MAX_NUM]; /* list of USB ids when this device is at pre firmware state */ + struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM]; /* list of USB ids when this device is at post firmware state */ +}; + +struct dvb_usb_device; + +/* properties of an dvb-usb-device */ +struct dvb_usb_properties { + +#define DVB_USB_HAS_PID_FILTER 0x01 +#define DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF 0x02 +#define DVB_USB_IS_AN_I2C_ADAPTER 0x04 + int caps; + +#define CYPRESS_AN2135 0 +#define CYPRESS_AN2235 1 +#define CYPRESS_FX2 2 + int usb_ctrl; /* usb controller */ + const char *firmware; /* valid firmware filenames */ + + int (*streaming_ctrl) (struct dvb_usb_device *, int); /* control the MPEG2-TS streaming of the device */ + int (*pid_filter) (struct dvb_usb_device *, int, u16, int); /* if the device has a hardware pid filter */ + int (*pid_filter_ctrl) (struct dvb_usb_device *, int); /* if the device has a hardware pid filter to en-/disable */ + + int (*power_ctrl) (struct dvb_usb_device *, int); /* power control callback of the device */ + + int (*frontend_attach) (struct dvb_usb_device *); /* each device has to know about its frontends */ + + struct dvb_usb_device_description * (*identify_desc_quirk) (void); /* the device is not distinuishable just by its USB IDs */ + +#define USB_REMOTE_PROTO_NO 1 +#define USB_REMOTE_PROTO_NEC 2 +#define USB_REMOTE_PROTO_HAUPPAUGE 3 + int remote_protocol; /* does this device have an ir-receiver */ + int rc_interval; + +#define REMOTE_KEY_NO 0x01 +#define REMOTE_KEY_REPEAT 0x02 +#define REMOTE_KEY_PRESSED 0x03 + int (*query_rc) (struct dvb_usb_device *, u8 *, int *); /* remote query callback */ + +/* i2c algorithm, if any */ + struct i2c_algorithm i2c_algo; + +/* MPEG2 TS transfer description */ + struct { +#define DVB_USB_BULK 1 +#define DVB_USB_ISOC 2 + int type; + int count; + int endpoint; + + union { + struct { + int buffersize; /* per URB */ + } bulk; + struct { + int framesperurb; + int framesize; + } isoc; + } u; + + } urb; + + int num_device_descs; + struct dvb_usb_device_description devices[]; +}; + +struct dvb_usb_device { + struct dvb_usb_properties props; + struct dvb_usb_device_description *desc; + +#define DVB_USB_STATE_INIT 0x000 +#define DVB_USB_STATE_URB_LIST 0x001 +#define DVB_USB_STATE_URB_BUF 0x002 +#define DVB_USB_STATE_DVB 0x004 +#define DVB_USB_STATE_I2C 0x008 +#define DVB_USB_STATE_REMOTE 0x010 +#define DVB_USB_STATE_URB_SUBMIT 0x020 + int init_state; + + /* usb */ + struct usb_device *udev; + struct urb **urb_list; + u8 *buffer; + dma_addr_t dma_handle; + int urbs_initialized; + int urbs_submitted; + + int feedcount; + + /* locking */ + struct semaphore usb_sem; + + /* i2c */ + struct semaphore i2c_sem; + struct i2c_adapter i2c_adap; + + /* dvb */ + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct dvb_frontend* fe; + + int (*fe_sleep) (struct dvb_frontend *); + int (*fe_init) (struct dvb_frontend *); + + /* remote control */ +// struct input_dev rc_input_dev; +// struct work_struct rc_query_work; +// int last_event; +// int last_state; /* for Hauppauge RC protocol */ +// int repeat_key_count; +// int rc_key_repeat_count; /* module parameter */ + + int pid_filtering; +// int rc_query_interval; + + void *priv; +}; + +int dvb_usb_device_init(struct usb_interface *, struct dvb_usb_properties *); +void dvb_usb_device_exit(struct usb_interface *); + +#endif diff --git a/linux/drivers/media/dvb/dvb-usb/vp7045-fe.c b/linux/drivers/media/dvb/dvb-usb/vp7045-fe.c new file mode 100644 index 000000000..84994fa09 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/vp7045-fe.c @@ -0,0 +1,182 @@ +#include "vp7045.h" + +/* It is a Zarlink MT352 within a Samsung Tuner (DNOS404ZH102A) - 040929 - AAT + * + * Programming is hidden inside the firmware, so set_frontend is very easy. + * Even though there is a Firmware command that one can use to access the demod + * via its registers. This is used for status information. + */ + +struct vp7045_fe_state { + struct dvb_frontend fe; + struct dvb_usb_device *d; +}; + + +static int vp7045_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 s0 = vp7045_read_reg(state->d,0x00), + s1 = vp7045_read_reg(state->d,0x01), + s3 = vp7045_read_reg(state->d,0x03); + + *status = 0; + if (s0 & (1 << 4)) + *status |= FE_HAS_CARRIER; + if (s0 & (1 << 1)) + *status |= FE_HAS_VITERBI; + if (s0 & (1 << 5)) + *status |= FE_HAS_LOCK; + if (s1 & (1 << 1)) + *status |= FE_HAS_SYNC; + if (s3 & (1 << 6)) + *status |= FE_HAS_SIGNAL; + + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; + + return 0; +} + +static int vp7045_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + *ber = (vp7045_read_reg(state->d, 0x0D) << 16) | + (vp7045_read_reg(state->d, 0x0E) << 8) | + vp7045_read_reg(state->d, 0x0F); + return 0; +} + +static int vp7045_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + *unc = (vp7045_read_reg(state->d, 0x10) << 8) | + vp7045_read_reg(state->d, 0x11); + return 0; +} + +static int vp7045_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u16 signal = (vp7045_read_reg(state->d, 0x14) << 8) | + vp7045_read_reg(state->d, 0x15); + + *strength = ~signal; + return 0; +} + +static int vp7045_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 _snr = vp7045_read_reg(state->d, 0x09); + *snr = (_snr << 8) | _snr; + return 0; +} + +static int vp7045_fe_init(struct dvb_frontend* fe) +{ + return 0; +} + +static int vp7045_fe_sleep(struct dvb_frontend* fe) +{ + return 0; +} + +static int vp7045_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 800; + return 0; +} + +static int vp7045_fe_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + u8 buf[5]; + u32 freq = fep->frequency / 1000; + + buf[0] = (freq >> 16) & 0xff; + buf[1] = (freq >> 8) & 0xff; + buf[2] = freq & 0xff; + buf[3] = 0; + + switch (fep->u.ofdm.bandwidth) { + case BANDWIDTH_8_MHZ: buf[4] = 8; break; + case BANDWIDTH_7_MHZ: buf[4] = 7; break; + case BANDWIDTH_6_MHZ: buf[4] = 6; break; + case BANDWIDTH_AUTO: return -EOPNOTSUPP; + default: + return -EINVAL; + } + + vp7045_usb_op(state->d,LOCK_TUNER_COMMAND,buf,5,NULL,0,200); + return 0; +} + +static int vp7045_fe_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + return 0; +} + +static void vp7045_fe_release(struct dvb_frontend* fe) +{ + struct vp7045_fe_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops vp7045_fe_ops; + +struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d) +{ + struct vp7045_fe_state *s = kmalloc(sizeof(struct vp7045_fe_state), GFP_KERNEL); + if (s == NULL) + goto error; + memset(s,0,sizeof(struct vp7045_fe_state)); + + s->d = d; + s->fe.ops = &vp7045_fe_ops; + s->fe.demodulator_priv = s; + + goto success; +error: + return NULL; +success: + return &s->fe; +} + + +static struct dvb_frontend_ops vp7045_fe_ops = { + .info = { + .name = "Twinhan VP7045/46 USB DVB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 1000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = vp7045_fe_release, + + .init = vp7045_fe_init, + .sleep = vp7045_fe_sleep, + + .set_frontend = vp7045_fe_set_frontend, + .get_frontend = vp7045_fe_get_frontend, + .get_tune_settings = vp7045_fe_get_tune_settings, + + .read_status = vp7045_fe_read_status, + .read_ber = vp7045_fe_read_ber, + .read_signal_strength = vp7045_fe_read_signal_strength, + .read_snr = vp7045_fe_read_snr, + .read_ucblocks = vp7045_fe_read_unc_blocks, +}; diff --git a/linux/drivers/media/dvb/dvb-usb/vp7045.c b/linux/drivers/media/dvb/dvb-usb/vp7045.c new file mode 100644 index 000000000..97a95d513 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/vp7045.c @@ -0,0 +1,193 @@ +#include "vp7045.h" + +/* debug */ +int dvb_usb_vp7045_debug; +module_param_named(debug,dvb_usb_vp7045_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2 (or-able))." DVB_USB_DEBUG_STATUS); + +int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec) +{ + int ret = 0; + u8 inbuf[12] = { 0 }, outbuf[20] = { 0 }; + + outbuf[0] = cmd; + + if (outlen > 19) + outlen = 19; + + if (inlen > 11) + inlen = 11; + + if (out != NULL && outlen > 0) + memcpy(&outbuf[1], out, outlen); + + deb_xfer("out buffer: "); + debug_dump(outbuf,outlen+1,deb_xfer); + + if ((ret = down_interruptible(&d->usb_sem))) + return ret; + + if (usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev,0), + TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, + outbuf, 20, 2*HZ) != 20) { + err("USB control message 'out' went wrong."); + ret = -EIO; + goto unlock; + } + + msleep(msec); + + if (usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev,0), + TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, + inbuf, 12, 2*HZ) != 12) { + err("USB control message 'in' went wrong."); + ret = -EIO; + goto unlock; + } + + deb_xfer("in buffer: "); + debug_dump(inbuf,12,deb_xfer); + + if (in != NULL && inlen > 0) + memcpy(in,&inbuf[1],inlen); + +unlock: + up(&d->usb_sem); + + return ret; +} + +u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg) +{ + u8 obuf[2] = { 0 },v; + obuf[1] = reg; + + vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30); + + return v; +} + +static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 v = onoff; + deb_info("power control: %s\n",onoff ? "on" : "off"); + + return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150); +} + +static int vp7045_rc_query(struct dvb_usb_device *d, u8 *key_buf, int *state) +{ + deb_info("remote query\n"); + *state = REMOTE_KEY_NO; + return 0; //vp7045_usb_op(d,RC_VAL_READ,&v,1,NULL,0,20); +} + +static int vp7045_frontend_attach(struct dvb_usb_device *d) +{ + u8 buf[20]; + + vp7045_usb_op(d,VENDOR_STRING_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("firmware says: %s ",buf); + + vp7045_usb_op(d,PRODUCT_STRING_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("%s ",buf); + + vp7045_usb_op(d,FW_VERSION_READ,NULL,0,buf,20,0); + buf[10] = '\0'; + deb_info("v%s\n",buf); + + d->fe = vp7045_fe_attach(d); + + return 0; +} + +static struct dvb_usb_properties vp7045_properties; + +static int vp7045_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&vp7045_properties); +} + +static struct usb_device_id vp7045_usb_table [] = { + { USB_DEVICE(0x13d3, 0x3205) }, + { USB_DEVICE(0x13d3, 0x3206) }, + { 0 }, +}; + +static struct dvb_usb_properties vp7045_properties = { + .caps = 0, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-vp7045-01.fw", + + .streaming_ctrl = NULL, + .pid_filter = NULL, + .pid_filter_ctrl = NULL, + .power_ctrl = vp7045_power_ctrl, + .frontend_attach = vp7045_frontend_attach, + + .rc_interval = 400, + .remote_protocol = USB_REMOTE_PROTO_NEC, + .query_rc = vp7045_rc_query, + + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)", + .cold_ids = { &vp7045_usb_table[0], NULL }, + .warm_ids = { &vp7045_usb_table[1], NULL }, + }, + { 0 }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver vp7045_usb_driver = { + .owner = THIS_MODULE, + .name = "Twinhan DVB-T USB2.0", + .probe = vp7045_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = vp7045_usb_table, +}; + +/* module stuff */ +static int __init vp7045_usb_module_init(void) +{ + int result; + if ((result = usb_register(&vp7045_usb_driver))) { + err("usb_register failed. (%d)",result); + return result; + } + + return 0; +} + +static void __exit vp7045_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&vp7045_usb_driver); +} + +module_init(vp7045_usb_module_init); +module_exit(vp7045_usb_module_exit); + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>"); +MODULE_DESCRIPTION("Twinhan MagicBox/Alpha DVB-T USB2.0"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/vp7045.h b/linux/drivers/media/dvb/dvb-usb/vp7045.h new file mode 100644 index 000000000..92234580e --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/vp7045.h @@ -0,0 +1,53 @@ +#ifndef _DVB_USB_VP7045_H_ +#define _DVB_USB_VP7045_H_ + +#define DVB_USB_LOG_PREFIX "vp7045" +#include "dvb-usb.h" + +extern int dvb_vp7045_debug; +#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args) + +/* vp7045 commands */ + +/* Twinhan Vendor requests */ +#define TH_COMMAND_IN 0xC0 +#define TH_COMMAND_OUT 0xC1 + +/* TH_COMMAND_OUT request type */ +#define TUNER_REG_READ 0x03 +#define TUNER_REG_WRITE 0x04 +#define RC_VAL_READ 0x05 +#define SET_TUNER_POWER 0x06 +#define GET_USB_SPEED 0x07 +#define LOCK_TUNER_COMMAND 0x09 +#define TUNER_SIGNAL_READ 0x0A +#define FW_VERSION_READ 0x0B +#define VENDOR_STRING_READ 0x0C +#define PRODUCT_STRING_READ 0x0D +#define SET_EE_VALUE 0x10 +#define GET_EE_VALUE 0x11 +#define CHECK_TUNER_POWER 0x12 +#define RESET_FX2 0x13 +#define FW_BCD_VERSION_READ 0x14 + +#define Tuner_Power_ON 1 +#define Tuner_Power_OFF 0 + +#define Tuner_Lock 1 +#define Tuner_UnLock 0 + +#define USB_SPEED_LOW 0 +#define USB_SPEED_FULL 1 +#define USB_SPEED_HIGH 2 + +#define TUNER_REG_QUALITY_2 0x0A +#define TUNER_REG_QUALITY_1 0x0B +#define TUNER_REG_QUALITY_0 0x0C +#define TUNER_REG_STRENGTH 0x09 + +extern struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d); +extern int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen,int msec); +extern u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg); + +#endif |