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

Commit 03910cc3 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab
Browse files

V4L/DVB (6536): Add a hint for boards without unique USB ID



This patch adds a function to allow trying to detect boards that shares
the generic IDs.

The current detection method is based at eeprom checksum.

Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 3dbd85ba
Loading
Loading
Loading
Loading
+158 −40
Original line number Diff line number Diff line
@@ -37,6 +37,16 @@
#include "em28xx.h"
#include "tuner-xc2028.h"

static int tuner = -1;
module_param(tuner, int, 0444);
MODULE_PARM_DESC(tuner, "tuner type");

struct em28xx_hash_table {
	unsigned long hash;
	unsigned int  model;
	unsigned int  tuner;
};

struct em28xx_board em28xx_boards[] = {
	[EM2800_BOARD_UNKNOWN] = {
		.name         = "Unknown EM2800 video grabber",
@@ -342,7 +352,15 @@ struct usb_device_id em28xx_id_table [] = {
	{ USB_DEVICE(0x0ccd, 0x0047), .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS },
	{ },
};
MODULE_DEVICE_TABLE (usb, em28xx_id_table);

static struct em28xx_hash_table em28xx_hash [] = {
	{ 0, 0, 0 },
};

/* Since em28xx_pre_card_setup() requires a proper dev->model,
 * this won't work for boards with generic PCI IDs
 */
void em28xx_pre_card_setup(struct em28xx *dev)
{
	/* request some modules */
@@ -350,18 +368,50 @@ void em28xx_pre_card_setup(struct em28xx *dev)
	case EM2880_BOARD_TERRATEC_PRODIGY_XS:
	case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
	case EM2880_BOARD_TERRATEC_HYBRID_XS:
			{
				em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1); // reset through GPIO?
		/* reset through GPIO? */
		em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1);
		break;
	}
}

static int em28xx_tuner_callback(void *ptr, int command, int arg)
{
	int rc = 0;
	struct em28xx *dev = ptr;

	if (dev->tuner_type != TUNER_XC2028)
		return 0;

	switch (command) {
	case XC2028_TUNER_RESET:
		/* FIXME: This is device-dependent */
		dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
		dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);

		msleep(140);
		break;
	}
	return rc;
}

static void em28xx_config_tuner (struct em28xx *dev)
{
	struct v4l2_priv_tun_config  xc2028_cfg;
	struct xc2028_ctrl           ctl;
	struct tuner_setup           tun_setup;
	struct v4l2_frequency        f;

	if (!dev->has_tuner)
		return;

	tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
	tun_setup.type = dev->tuner_type;
	tun_setup.addr = dev->tuner_addr;
	tun_setup.tuner_callback = em28xx_tuner_callback;

	em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);

	if (dev->tuner_type == TUNER_XC2028) {
		memset (&ctl, 0, sizeof(ctl));

		ctl.fname   = XC2028_DEFAULT_FIRMWARE;
@@ -373,6 +423,54 @@ static void em28xx_config_tuner (struct em28xx *dev)
		em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
	}

	/* configure tuner */
	f.tuner = 0;
	f.type = V4L2_TUNER_ANALOG_TV;
	f.frequency = 9076;     /* just a magic number */
	dev->ctl_freq = f.frequency;
	em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
}

static int em28xx_hint_board(struct em28xx *dev)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(em28xx_hash); i++) {
		if (dev->hash == em28xx_hash[i].hash) {
			dev->model = em28xx_hash[i].model;
			dev->tuner_type = em28xx_hash[i].tuner;

			em28xx_errdev("Your board has no unique USB ID.\n");
			em28xx_errdev("A hint were successfully done, "
				      "based on eeprom hash.\n");
			em28xx_errdev("This method is not 100%% failproof.\n");
			em28xx_errdev("If the board were missdetected, "
				      "please email this log to:\n");
			em28xx_errdev("\tV4L Mailing List "
				      " <video4linux-list@redhat.com>\n");
			em28xx_errdev("Board detected as %s\n",
				      em28xx_boards[dev->model].name);

			return 0;
		}
	}
	em28xx_errdev("Your board has no unique USB ID and thus need a "
		      "hint to be detected.\n");
	em28xx_errdev("You may try to use card=<n> insmod option to "
		      "workaround that.\n");
	em28xx_errdev("Please send an email with this log to:\n");
	em28xx_errdev("\tV4L Mailing List <video4linux-list@redhat.com>\n");
	em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);

	em28xx_errdev("Here is a list of valid choices for the card=<n>"
		      " insmod option:\n");
	for (i = 0; i < em28xx_bcount; i++) {
		em28xx_errdev("    card=%d -> %s\n",
				i, em28xx_boards[i].name);
	}
	return -1;
}

void em28xx_card_setup(struct em28xx *dev)
{
	/* request some modules */
@@ -383,7 +481,6 @@ void em28xx_card_setup(struct em28xx *dev)
#ifdef CONFIG_MODULES
		request_module("tveeprom");
		request_module("ir-kbd-i2c");
				request_module("msp3400");
#endif
		/* Call first TVeeprom */

@@ -394,18 +491,39 @@ void em28xx_card_setup(struct em28xx *dev)
		if (tv.audio_processor == AUDIO_CHIP_MSP34XX) {
			dev->i2s_speed = 2048000;
			dev->has_msp34xx = 1;
				} else
					dev->has_msp34xx=0;
		}
		break;
	}
	case EM2820_BOARD_KWORLD_PVRTV2800RF:
			{
				em28xx_write_regs_req(dev,0x00,0x08, "\xf9", 1); // GPIO enables sound on KWORLD PVR TV 2800RF
		/* GPIO enables sound on KWORLD PVR TV 2800RF */
		em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1);
		break;
	case EM2820_BOARD_UNKNOWN:
	case EM2800_BOARD_UNKNOWN:
		em28xx_hint_board(dev);
	}

	}
	dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
	dev->has_tuner = em28xx_boards[dev->model].has_tuner;
	dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx;
	dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
	dev->decoder = em28xx_boards[dev->model].decoder;
	dev->video_inputs = em28xx_boards[dev->model].vchannels;

	if (tuner >= 0)
		dev->tuner_type = tuner;

#ifdef CONFIG_MODULES
	/* request some modules */
	if (dev->has_msp34xx)
		request_module("msp3400");
	if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
		request_module("saa7115");
	if (dev->decoder == EM28XX_TVP5150)
		request_module("tvp5150");
	if (dev->has_tuner)
		request_module("tuner");
#endif

	em28xx_config_tuner (dev);
}

MODULE_DEVICE_TABLE (usb, em28xx_id_table);
+34 −40
Original line number Diff line number Diff line
@@ -292,6 +292,31 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
	return rc;
}

/* based on linux/sunrpc/svcauth.h and linux/hash.h
 * The original hash function returns a different value, if arch is x86_64
 *  or i386.
 */
static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
{
	unsigned long hash = 0;
	unsigned long l = 0;
	int len = 0;
	unsigned char c;
	do {
		if (len == length) {
			c = (char)len;
			len = -1;
		} else
			c = *buf++;
		l = (l << 8) | c;
		len++;
		if ((len & (32 / 8 - 1)) == 0)
			hash = ((hash^l) * 0x9e370001UL);
	} while (len);

	return (hash >> (32 - bits)) & 0xffffffffUL;
}

static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
{
	unsigned char buf, *p = eedata;
@@ -335,7 +360,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
			printk("\n");
	}

	printk(KERN_INFO "EEPROM ID= 0x%08x\n", em_eeprom->id);
	if (em_eeprom->id == 0x9567eb1a)
		dev->hash = em28xx_hash_mem(eedata, len, 32);

	printk(KERN_INFO "EEPROM ID= 0x%08x, hash = 0x%08lx\n",
	       em_eeprom->id, dev->hash);
	printk(KERN_INFO "Vendor/Product ID= %04x:%04x\n", em_eeprom->vendor_ID,
	       em_eeprom->product_ID);

@@ -392,43 +421,6 @@ static u32 functionality(struct i2c_adapter *adap)
}


static int em28xx_tuner_callback(void *ptr, int command, int arg)
{
	int rc = 0;
	struct em28xx *dev = ptr;

	if (dev->tuner_type != TUNER_XC2028)
		return 0;

	switch (command) {
	case XC2028_TUNER_RESET:
		/* FIXME: This is device-dependent */
		dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
		dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);

		msleep(140);
		break;
	}
	return rc;
}

static int em28xx_set_tuner(int check_eeprom, struct i2c_client *client)
{
	struct em28xx *dev = client->adapter->algo_data;
	struct tuner_setup tun_setup;

	if (dev->has_tuner) {
		tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
		tun_setup.type = dev->tuner_type;
		tun_setup.addr = dev->tuner_addr;
		tun_setup.tuner_callback = em28xx_tuner_callback;

		em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
	}

	return (0);
}

/*
 * attach_inform()
 * gets called when a device attaches to the i2c bus
@@ -487,9 +479,11 @@ static int attach_inform(struct i2c_client *client)
			break;

		default:
			dprintk1(1,"attach inform: detected I2C address %x\n", client->addr << 1);
			if (!dev->tuner_addr)
				dev->tuner_addr = client->addr;
			em28xx_set_tuner(-1, client);

			dprintk1(1,"attach inform: detected I2C address %x\n", client->addr << 1);

	}

	return 0;
+35 −85
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@
#include <linux/mutex.h>

#include "em28xx.h"
#include <media/tuner.h>
#include <media/v4l2-common.h>
#include <media/msp3400.h>

@@ -71,10 +70,6 @@ MODULE_PARM_DESC(card,"card type");
MODULE_PARM_DESC(video_nr,"video device numbers");
MODULE_PARM_DESC(vbi_nr,"vbi device numbers");

static int tuner = -1;
module_param(tuner, int, 0444);
MODULE_PARM_DESC(tuner, "tuner type");

static unsigned int video_debug = 0;
module_param(video_debug,int,0644);
MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
@@ -171,7 +166,6 @@ static int em28xx_config(struct em28xx *dev)
 */
static void em28xx_config_i2c(struct em28xx *dev)
{
	struct v4l2_frequency f;
	struct v4l2_routing route;

	route.input = INPUT(dev->ctl_input)->vmux;
@@ -179,13 +173,6 @@ static void em28xx_config_i2c(struct em28xx *dev)
	em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);
	em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
	em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);

	/* configure tuner */
	f.tuner = 0;
	f.type = V4L2_TUNER_ANALOG_TV;
	f.frequency = 9076;	/* FIXME:remove magic number */
	dev->ctl_freq = f.frequency;
	em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
}

/*
@@ -1495,7 +1482,7 @@ static const struct file_operations em28xx_v4l_fops = {
 * allocates and inits the device structs, registers i2c bus and v4l device
 */
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
			   int minor, int model)
			   int minor)
{
	struct em28xx *dev = *devhandle;
	int retval = -ENOMEM;
@@ -1503,7 +1490,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
	unsigned int maxh, maxw;

	dev->udev = udev;
	dev->model = model;
	mutex_init(&dev->lock);
	init_waitqueue_head(&dev->open);

@@ -1512,43 +1498,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
	dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
	dev->em28xx_write_regs_req = em28xx_write_regs_req;
	dev->em28xx_read_reg_req = em28xx_read_reg_req;
	dev->is_em2800 = em28xx_boards[model].is_em2800;
	dev->has_tuner = em28xx_boards[model].has_tuner;
	dev->has_msp34xx = em28xx_boards[model].has_msp34xx;
	dev->tda9887_conf = em28xx_boards[model].tda9887_conf;
	dev->decoder = em28xx_boards[model].decoder;

	if (tuner >= 0)
		dev->tuner_type = tuner;
	else
		dev->tuner_type = em28xx_boards[model].tuner_type;

	dev->video_inputs = em28xx_boards[model].vchannels;

	for (i = 0; i < TVNORMS; i++)
		if (em28xx_boards[model].norm == tvnorms[i].mode)
			break;
	if (i == TVNORMS)
		i = 0;

	dev->tvnorm = &tvnorms[i];	/* set default norm */

	em28xx_videodbg("tvnorm=%s\n", dev->tvnorm->name);

	maxw = norm_maxw(dev);
	maxh = norm_maxh(dev);

	/* set default image size */
	dev->width = maxw;
	dev->height = maxh;
	dev->interlaced = EM28XX_INTERLACED_DEFAULT;
	dev->field_size = dev->width * dev->height;
	dev->frame_size =
	    dev->interlaced ? dev->field_size << 1 : dev->field_size;
	dev->bytesperline = dev->width * 2;
	dev->hscale = 0;
	dev->vscale = 0;
	dev->ctl_input = 2;

	/* setup video picture settings for saa7113h */
	memset(&dev->vpic, 0, sizeof(dev->vpic));
@@ -1561,15 +1510,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
	dev->vpic.palette = VIDEO_PALETTE_YUV422;

	em28xx_pre_card_setup(dev);
#ifdef CONFIG_MODULES
	/* request some modules */
	if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
		request_module("saa7115");
	if (dev->decoder == EM28XX_TVP5150)
		request_module("tvp5150");
	if (dev->has_tuner)
		request_module("tuner");
#endif

	errCode = em28xx_config(dev);
	if (errCode) {
		em28xx_errdev("error configuring device\n");
@@ -1579,6 +1520,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
	}

	mutex_lock(&dev->lock);

	/* register i2c bus */
	em28xx_i2c_register(dev);

@@ -1590,12 +1532,33 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,

	mutex_unlock(&dev->lock);

	for (i = 0; i < TVNORMS; i++)
		if (em28xx_boards[dev->model].norm == tvnorms[i].mode)
			break;
	if (i == TVNORMS)
		i = 0;

	dev->tvnorm = &tvnorms[i];      /* set default norm */

	em28xx_videodbg("tvnorm=%s\n", dev->tvnorm->name);

	maxw = norm_maxw(dev);
	maxh = norm_maxh(dev);

	/* set default image size */
	dev->width = maxw;
	dev->height = maxh;
	dev->interlaced = EM28XX_INTERLACED_DEFAULT;
	dev->field_size = dev->width * dev->height;
	dev->frame_size =
	    dev->interlaced ? dev->field_size << 1 : dev->field_size;
	dev->bytesperline = dev->width * 2;
	dev->hscale = 0;
	dev->vscale = 0;
	dev->ctl_input = 2;

	errCode = em28xx_config(dev);

#ifdef CONFIG_MODULES
	if (dev->has_msp34xx)
		request_module("msp3400");
#endif
	/* allocate and fill v4l2 device struct */
	dev->vdev = video_device_alloc();
	if (NULL == dev->vdev) {
@@ -1695,7 +1658,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
	struct usb_interface *uif;
	struct em28xx *dev = NULL;
	int retval = -ENODEV;
	int model,i,nr,ifnum;
	int i, nr, ifnum;

	udev = usb_get_dev(interface_to_usbdev(interface));
	ifnum = interface->altsetting[0].desc.bInterfaceNumber;
@@ -1735,8 +1698,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
		return -ENODEV;
	}

	model=id->driver_info;

	if (nr >= EM28XX_MAXBOARDS) {
		printk (DRIVER_NAME ": Supports only %i em28xx boards.\n",EM28XX_MAXBOARDS);
		em28xx_devused&=~(1<<nr);
@@ -1753,6 +1714,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,

	snprintf(dev->name, 29, "em28xx #%d", nr);
	dev->devno = nr;
	dev->model = id->driver_info;

	/* compute alternate max packet sizes */
	uif = udev->actconfig->interface[0];
@@ -1779,26 +1741,14 @@ static int em28xx_usb_probe(struct usb_interface *interface,
	}

	if ((card[nr]>=0)&&(card[nr]<em28xx_bcount))
		model=card[nr];

	if ((model==EM2800_BOARD_UNKNOWN)||(model==EM2820_BOARD_UNKNOWN)) {
		em28xx_errdev("Your board has no unique USB ID and thus can't be autodetected.\n");
		em28xx_errdev("Please pass card=<n> insmod option to workaround that.\n");
		em28xx_errdev("If there isn't any card number for you, please send an email to:\n");
		em28xx_errdev("\tV4L Mailing List <video4linux-list@redhat.com>\n");
		em28xx_errdev("Here is a list of valid choices for the card=<n> insmod option:\n");
		for (i = 0; i < em28xx_bcount; i++) {
			em28xx_errdev("    card=%d -> %s\n", i,
							em28xx_boards[i].name);
		}
	}
		dev->model = card[nr];

	/* allocate device struct */
	retval = em28xx_init_dev(&dev, udev, nr, model);
	retval = em28xx_init_dev(&dev, udev, nr);
	if (retval)
		return retval;

	em28xx_info("Found %s\n", em28xx_boards[model].name);
	em28xx_info("Found %s\n", em28xx_boards[dev->model].name);

	/* save our data pointer in this interface device */
	usb_set_intfdata(interface, dev);
+2 −0
Original line number Diff line number Diff line
@@ -257,6 +257,8 @@ struct em28xx {
	int interlaced;		/* 1=interlace fileds, 0=just top fileds */
	int type;

	unsigned long hash;	/* eeprom hash - for boards with generic ID */

	/* states */
	enum em28xx_dev_state state;
	enum em28xx_stream_state stream;