Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9bbe076f authored by Alan Nisota's avatar Alan Nisota Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (4029): [PATCH] Genpix 8PSK->USB driver (Take 2)



This is a patch which includes support for the GENPIX 8PSK->USB module.  The
board supports QPSK, BPSK and 8PSK decoding (though I don't think it will be
DVB-S2 compliant) With the following patch, the boad is equivalent to a budget
card (no CA Module)
The patch which adds 8psk suppot will follow, but is seperate, as it requires
DVB-S2 support
More info on the board can be found at www.genpix-electronics.com (and they
host the requisite firmwares there as well)
Signed off by: Alan Nisota <alannisota@gmail.com>

Signed-off-by: default avatarPatrick Boettcher <pb@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 6daa4f86
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -130,6 +130,15 @@ config DVB_USB_VP702X

	  DVB-S USB2.0 receivers.

config DVB_USB_GP8PSK
	tristate "GENPIX 8PSK->USB module support"
	depends on DVB_USB
	help
	  Say Y here to support the
	    GENPIX 8psk module

	  DVB-S USB2.0 receivers.

config DVB_USB_NOVA_T_USB2
	tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
	depends on DVB_USB
+3 −0
Original line number Diff line number Diff line
@@ -7,6 +7,9 @@ obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o
dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o
obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o

dvb-usb-gp8psk-objs = gp8psk.o gp8psk-fe.o
obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o

dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o
obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o

+3 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#define USB_VID_VISIONPLUS					0x13d3
#define USB_VID_TWINHAN						0x1822
#define USB_VID_ULTIMA_ELECTRONIC			0x05d8
#define USB_VID_GENPIX					0x09c0

/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD			0xa333
@@ -104,5 +105,6 @@
#define USB_PID_KYE_DVB_T_WARM				0x701f
#define USB_PID_PCTV_200E					0x020e
#define USB_PID_PCTV_400E					0x020f

#define USB_PID_GENPIX_8PSK_COLD				0x0200
#define USB_PID_GENPIX_8PSK_WARM				0x0201
#endif
+272 −0
Original line number Diff line number Diff line
/* DVB USB compliant Linux driver for the
 *  - GENPIX 8pks/qpsk USB2.0 DVB-S module
 *
 * Copyright (C) 2006 Alan Nisota (alannisota@gmail.com)
 *
 * Thanks to GENPIX for the sample code used to implement this module.
 *
 * This module is based off the vp7045 and vp702x modules
 *
 *	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.
 *
 * see Documentation/dvb/README.dvb-usb for more information
 */
#include "gp8psk.h"

struct gp8psk_fe_state {
	struct dvb_frontend fe;

	struct dvb_usb_device *d;

	u16 snr;

	unsigned long next_snr_check;
};

static int gp8psk_fe_read_status(struct dvb_frontend* fe, fe_status_t *status)
{
	struct gp8psk_fe_state *st = fe->demodulator_priv;
	u8 lock;

	if (gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0, 0, &lock,1))
		return -EINVAL;

	if (lock)
		*status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER;
	else
		*status = 0;

	return 0;
}

/* not supported by this Frontend */
static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
{
	(void) fe;
	*ber = 0;
	return 0;
}

/* not supported by this Frontend */
static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
{
	(void) fe;
	*unc = 0;
	return 0;
}

static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
{
	struct gp8psk_fe_state *st = fe->demodulator_priv;
	u8 buf[2];

	if (time_after(jiffies,st->next_snr_check)) {
		gp8psk_usb_in_op(st->d,GET_SIGNAL_STRENGTH,0,0,buf,2);
		*snr = (int)(buf[1]) << 8 | buf[0];
		/* snr is reported in dBu*256 */
		/* snr / 38.4 ~= 100% strength */
		/* snr * 17 returns 100% strength as 65535 */
		if (*snr <= 3855)
			*snr = (*snr<<4) + *snr; // snr * 17
		else
			*snr = 65535;
		st->next_snr_check = jiffies + (10*HZ)/1000;
	} else {
		*snr = st->snr;
	}
	return 0;
}

static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
	return gp8psk_fe_read_snr(fe, strength);
}

static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
{
	tune->min_delay_ms = 800;
	return 0;
}

static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
				  struct dvb_frontend_parameters *fep)
{
	struct gp8psk_fe_state *state = fe->demodulator_priv;
	u8 cmd[10];
	u32 freq = fep->frequency * 1000;

	cmd[4] = freq         & 0xff;
	cmd[5] = (freq >> 8)  & 0xff;
	cmd[6] = (freq >> 16) & 0xff;
	cmd[7] = (freq >> 24) & 0xff;

	switch(fe->ops.info.type) {
	case FE_QPSK:
		cmd[0] =  fep->u.qpsk.symbol_rate        & 0xff;
		cmd[1] = (fep->u.qpsk.symbol_rate >>  8) & 0xff;
		cmd[2] = (fep->u.qpsk.symbol_rate >> 16) & 0xff;
		cmd[3] = (fep->u.qpsk.symbol_rate >> 24) & 0xff;
		cmd[8] = ADV_MOD_DVB_QPSK;
		cmd[9] = 0x03; /*ADV_MOD_FEC_XXX*/
		break;
	default:
		// other modes are unsuported right now
		cmd[0] = 0;
		cmd[1] = 0;
		cmd[2] = 0;
		cmd[3] = 0;
		cmd[8] = 0;
		cmd[9] = 0;
		break;
	}

	gp8psk_usb_out_op(state->d,TUNE_8PSK,0,0,cmd,10);

	state->next_snr_check = jiffies;

	return 0;
}

static int gp8psk_fe_get_frontend(struct dvb_frontend* fe,
				  struct dvb_frontend_parameters *fep)
{
	return 0;
}


static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe,
				    struct dvb_diseqc_master_cmd *m)
{
	struct gp8psk_fe_state *st = fe->demodulator_priv;

	deb_fe("%s\n",__FUNCTION__);

	if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0,
			m->msg, m->msg_len)) {
		return -EINVAL;
	}
	return 0;
}

static int gp8psk_fe_send_diseqc_burst (struct dvb_frontend* fe,
				    fe_sec_mini_cmd_t burst)
{
	struct gp8psk_fe_state *st = fe->demodulator_priv;
	u8 cmd;

	deb_fe("%s\n",__FUNCTION__);

	/* These commands are certainly wrong */
	cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01;

	if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0,
			&cmd, 0)) {
		return -EINVAL;
	}
	return 0;
}

static int gp8psk_fe_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
{
	struct gp8psk_fe_state* state = fe->demodulator_priv;

	if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE,
		 (tone == SEC_TONE_ON), 0, NULL, 0)) {
		return -EINVAL;
	}
	return 0;
}

static int gp8psk_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
{
	struct gp8psk_fe_state* state = fe->demodulator_priv;

	if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE,
			 voltage == SEC_VOLTAGE_18, 0, NULL, 0)) {
		return -EINVAL;
	}
	return 0;
}

static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd)
{
	struct gp8psk_fe_state* state = fe->demodulator_priv;
	u8 cmd = sw_cmd & 0x7f;

	if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0,
			NULL, 0)) {
		return -EINVAL;
	}
	if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80),
			0, NULL, 0)) {
		return -EINVAL;
	}

	return 0;
}

static void gp8psk_fe_release(struct dvb_frontend* fe)
{
	struct gp8psk_fe_state *state = fe->demodulator_priv;
	kfree(state);
}

static struct dvb_frontend_ops gp8psk_fe_ops;

struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d)
{
	struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL);
	if (s == NULL)
		goto error;

	s->d = d;
	memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops));
	s->fe.demodulator_priv = s;

	goto success;
error:
	return NULL;
success:
	return &s->fe;
}


static struct dvb_frontend_ops gp8psk_fe_ops = {
	.info = {
		.name			= "Genpix 8psk-USB DVB-S",
		.type			= FE_QPSK,
		.frequency_min		= 950000,
		.frequency_max		= 2150000,
		.frequency_stepsize	= 100,
		.symbol_rate_min        = 1000000,
		.symbol_rate_max        = 45000000,
		.symbol_rate_tolerance  = 500,  /* ppm */
		.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
	},

	.release = gp8psk_fe_release,

	.init = NULL,
	.sleep = NULL,

	.set_frontend = gp8psk_fe_set_frontend,
	.get_frontend = gp8psk_fe_get_frontend,
	.get_tune_settings = gp8psk_fe_get_tune_settings,

	.read_status = gp8psk_fe_read_status,
	.read_ber = gp8psk_fe_read_ber,
	.read_signal_strength = gp8psk_fe_read_signal_strength,
	.read_snr = gp8psk_fe_read_snr,
	.read_ucblocks = gp8psk_fe_read_unc_blocks,

	.diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg,
	.diseqc_send_burst = gp8psk_fe_send_diseqc_burst,
	.set_tone = gp8psk_fe_set_tone,
	.set_voltage = gp8psk_fe_set_voltage,
	.dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd,
};
+254 −0
Original line number Diff line number Diff line
/* DVB USB compliant Linux driver for the
 *  - GENPIX 8pks/qpsk USB2.0 DVB-S module
 *
 * Copyright (C) 2006 Alan Nisota (alannisota@gmail.com)
 *
 * Thanks to GENPIX for the sample code used to implement this module.
 *
 * This module is based off the vp7045 and vp702x modules
 *
 *	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.
 *
 * see Documentation/dvb/README.dvb-usb for more information
 */
#include "gp8psk.h"

/* debug */
static char bcm4500_firmware[] = "dvb-usb-gp8psk-02.fw";
int dvb_usb_gp8psk_debug;
module_param_named(debug,dvb_usb_gp8psk_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);

int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
{
	int ret = 0,try = 0;

	if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
		return ret;

	while (ret >= 0 && ret != blen && try < 3) {
		ret = usb_control_msg(d->udev,
			usb_rcvctrlpipe(d->udev,0),
			req,
			USB_TYPE_VENDOR | USB_DIR_IN,
			value,index,b,blen,
			2000);
		deb_info("reading number %d (ret: %d)\n",try,ret);
		try++;
	}

	if (ret < 0 || ret != blen) {
		warn("usb in operation failed.");
		ret = -EIO;
	} else
		ret = 0;

	deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index);
	debug_dump(b,blen,deb_xfer);

	mutex_unlock(&d->usb_mutex);

	return ret;
}

int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
			     u16 index, u8 *b, int blen)
{
	int ret;

	deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index);
	debug_dump(b,blen,deb_xfer);

	if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
		return ret;

	if (usb_control_msg(d->udev,
			usb_sndctrlpipe(d->udev,0),
			req,
			USB_TYPE_VENDOR | USB_DIR_OUT,
			value,index,b,blen,
			2000) != blen) {
		warn("usb out operation failed.");
		ret = -EIO;
	} else
		ret = 0;
	mutex_unlock(&d->usb_mutex);

	return ret;
}

static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d)
{
	int ret;
	const struct firmware *fw = NULL;
	u8 *ptr, *buf;
	if ((ret = request_firmware(&fw, bcm4500_firmware,
					&d->udev->dev)) != 0) {
		err("did not find the bcm4500 firmware file. (%s) "
			"Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",
			bcm4500_firmware,ret);
		return ret;
	}

	if (gp8psk_usb_out_op(d, LOAD_BCM4500,1,0,NULL, 0)) {
		release_firmware(fw);
		return -EINVAL;
	}

	info("downloaidng bcm4500 firmware from file '%s'",bcm4500_firmware);

	ptr = fw->data;
	buf = (u8 *) kmalloc(512, GFP_KERNEL | GFP_DMA);

	while (ptr[0] != 0xff) {
		u16 buflen = ptr[0] + 4;
		if (ptr + buflen >= fw->data + fw->size) {
			err("failed to load bcm4500 firmware.");
			release_firmware(fw);
			kfree(buf);
			return -EINVAL;
		}
		memcpy(buf, ptr, buflen);
		if (dvb_usb_generic_write(d, buf, buflen)) {
			err("failed to load bcm4500 firmware.");
			release_firmware(fw);
			kfree(buf);
			return -EIO;
		}
		ptr += buflen;
	}
	release_firmware(fw);
	kfree(buf);
	return 0;
}

static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff)
{
	u8 status, buf;
	if (onoff) {
		gp8psk_usb_in_op(d, GET_8PSK_CONFIG,0,0,&status,1);
		if (! (status & 0x01))  /* started */
			if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
				return -EINVAL;

		if (! (status & 0x02)) /* BCM4500 firmware loaded */
			if(gp8psk_load_bcm4500fw(d))
				return EINVAL;

		if (! (status & 0x04)) /* LNB Power */
			if (gp8psk_usb_in_op(d, START_INTERSIL, 1, 0,
					&buf, 1))
				return EINVAL;

		/* Set DVB mode */
		if(gp8psk_usb_out_op(d, SET_DVB_MODE, 1, 0, NULL, 0))
			return -EINVAL;
		gp8psk_usb_in_op(d, GET_8PSK_CONFIG,0,0,&status,1);
	} else {
		/* Turn off LNB power */
		if (gp8psk_usb_in_op(d, START_INTERSIL, 0, 0, &buf, 1))
			return EINVAL;
		/* Turn off 8psk power */
		if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1))
			return -EINVAL;

	}
	return 0;
}


static int gp8psk_streaming_ctrl(struct dvb_usb_device *d, int onoff)
{
	return gp8psk_usb_out_op(d, ARM_TRANSFER, onoff, 0 , NULL, 0);
}

static int gp8psk_frontend_attach(struct dvb_usb_device *d)
{
	d->fe = gp8psk_fe_attach(d);

	return 0;
}

static struct dvb_usb_properties gp8psk_properties;

static int gp8psk_usb_probe(struct usb_interface *intf,
		const struct usb_device_id *id)
{
	return dvb_usb_device_init(intf,&gp8psk_properties,THIS_MODULE,NULL);
}

static struct usb_device_id gp8psk_usb_table [] = {
	    { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_COLD) },
	    { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_WARM) },
	    { 0 },
};
MODULE_DEVICE_TABLE(usb, gp8psk_usb_table);

static struct dvb_usb_properties gp8psk_properties = {
	.caps = 0,

	.usb_ctrl = CYPRESS_FX2,
	.firmware = "dvb-usb-gp8psk-01.fw",

	.streaming_ctrl   = gp8psk_streaming_ctrl,
	.power_ctrl       = gp8psk_power_ctrl,
	.frontend_attach  = gp8psk_frontend_attach,

	.generic_bulk_ctrl_endpoint = 0x01,
	/* parameter for the MPEG2-data transfer */
	.urb = {
		.type = DVB_USB_BULK,
		.count = 7,
		.endpoint = 0x82,
		.u = {
			.bulk = {
				.buffersize = 8192,
			}
		}
	},

	.num_device_descs = 1,
	.devices = {
		{ .name = "Genpix 8PSK-USB DVB-S USB2.0 receiver",
		  .cold_ids = { &gp8psk_usb_table[0], NULL },
		  .warm_ids = { &gp8psk_usb_table[1], NULL },
		},
		{ 0 },
	}
};

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gp8psk_usb_driver = {
	.name		= "dvb_usb_gp8psk",
	.probe		= gp8psk_usb_probe,
	.disconnect = dvb_usb_device_exit,
	.id_table	= gp8psk_usb_table,
};

/* module stuff */
static int __init gp8psk_usb_module_init(void)
{
	int result;
	if ((result = usb_register(&gp8psk_usb_driver))) {
		err("usb_register failed. (%d)",result);
		return result;
	}

	return 0;
}

static void __exit gp8psk_usb_module_exit(void)
{
	/* deregister this driver from the USB subsystem */
	usb_deregister(&gp8psk_usb_driver);
}

module_init(gp8psk_usb_module_init);
module_exit(gp8psk_usb_module_exit);

MODULE_AUTHOR("Alan Nisota <alannisota@gamil.com>");
MODULE_DESCRIPTION("Driver for Genpix 8psk-USB DVB-S USB2.0");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
Loading