static int sw_connect()

in joystick/sidewinder.c [570:788]


static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
{
	struct sw *sw;
	struct input_dev *input_dev;
	int i, j, k, l;
	int err = 0;
	unsigned char *buf = NULL;	/* [SW_LENGTH] */
	unsigned char *idbuf = NULL;	/* [SW_LENGTH] */
	unsigned char m = 1;
	char comment[40];

	comment[0] = 0;

	sw = kzalloc(sizeof(struct sw), GFP_KERNEL);
	buf = kmalloc(SW_LENGTH, GFP_KERNEL);
	idbuf = kmalloc(SW_LENGTH, GFP_KERNEL);
	if (!sw || !buf || !idbuf) {
		err = -ENOMEM;
		goto fail1;
	}

	sw->gameport = gameport;

	gameport_set_drvdata(gameport, sw);

	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
	if (err)
		goto fail1;

	dbg("Init 0: Opened %s, io %#x, speed %d",
		gameport->phys, gameport->io, gameport->speed);

	i = sw_read_packet(gameport, buf, SW_LENGTH, 0);		/* Read normal packet */
	msleep(SW_TIMEOUT);
	dbg("Init 1: Mode %d. Length %d.", m , i);

	if (!i) {							/* No data. 3d Pro analog mode? */
		sw_init_digital(gameport);				/* Switch to digital */
		msleep(SW_TIMEOUT);
		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Retry reading packet */
		msleep(SW_TIMEOUT);
		dbg("Init 1b: Length %d.", i);
		if (!i) {						/* No data -> FAIL */
			err = -ENODEV;
			goto fail2;
		}
	}

	j = sw_read_packet(gameport, idbuf, SW_LENGTH, i);		/* Read ID. This initializes the stick */
	m |= sw_guess_mode(idbuf, j);					/* ID packet should carry mode info [3DP] */
	dbg("Init 2: Mode %d. ID Length %d.", m, j);

	if (j <= 0) {							/* Read ID failed. Happens in 1-bit mode on PP */
		msleep(SW_TIMEOUT);
		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Retry reading packet */
		m |= sw_guess_mode(buf, i);
		dbg("Init 2b: Mode %d. Length %d.", m, i);
		if (!i) {
			err = -ENODEV;
			goto fail2;
		}
		msleep(SW_TIMEOUT);
		j = sw_read_packet(gameport, idbuf, SW_LENGTH, i);	/* Retry reading ID */
		dbg("Init 2c: ID Length %d.", j);
	}

	sw->type = -1;
	k = SW_FAIL;							/* Try SW_FAIL times */
	l = 0;

	do {
		k--;
		msleep(SW_TIMEOUT);
		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Read data packet */
		dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k);

		if (i > l) {						/* Longer? As we can only lose bits, it makes */
									/* no sense to try detection for a packet shorter */
			l = i;						/* than the previous one */

			sw->number = 1;
			sw->gameport = gameport;
			sw->length = i;
			sw->bits = m;

			dbg("Init 3a: Case %d.\n", i * m);

			switch (i * m) {
				case 60:
					sw->number++;
					fallthrough;
				case 45:				/* Ambiguous packet length */
					if (j <= 40) {			/* ID length less or eq 40 -> FSP */
					fallthrough;
				case 43:
						sw->type = SW_ID_FSP;
						break;
					}
					sw->number++;
					fallthrough;
				case 30:
					sw->number++;
					fallthrough;
				case 15:
					sw->type = SW_ID_GP;
					break;
				case 33:
				case 31:
					sw->type = SW_ID_FFW;
					break;
				case 48:				/* Ambiguous */
					if (j == 14) {			/* ID length 14*3 -> FFP */
						sw->type = SW_ID_FFP;
						sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on");
					} else
						sw->type = SW_ID_PP;
					break;
				case 66:
					sw->bits = 3;
					fallthrough;
				case 198:
					sw->length = 22;
					fallthrough;
				case 64:
					sw->type = SW_ID_3DP;
					if (j == 160)
						sw_3dp_id(idbuf, comment, sizeof(comment));
					break;
			}
		}

	} while (k && sw->type == -1);

	if (sw->type == -1) {
		printk(KERN_WARNING "sidewinder.c: unknown joystick device detected "
			"on %s, contact <vojtech@ucw.cz>\n", gameport->phys);
		sw_print_packet("ID", j * 3, idbuf, 3);
		sw_print_packet("Data", i * m, buf, m);
		err = -ENODEV;
		goto fail2;
	}

#ifdef SW_DEBUG
	sw_print_packet("ID", j * 3, idbuf, 3);
	sw_print_packet("Data", i * m, buf, m);
#endif

	gameport_set_poll_handler(gameport, sw_poll);
	gameport_set_poll_interval(gameport, 20);

	k = i;
	l = j;

	for (i = 0; i < sw->number; i++) {
		int bits, code;

		snprintf(sw->name, sizeof(sw->name),
			 "Microsoft SideWinder %s", sw_name[sw->type]);
		snprintf(sw->phys[i], sizeof(sw->phys[i]),
			 "%s/input%d", gameport->phys, i);

		sw->dev[i] = input_dev = input_allocate_device();
		if (!input_dev) {
			err = -ENOMEM;
			goto fail3;
		}

		input_dev->name = sw->name;
		input_dev->phys = sw->phys[i];
		input_dev->id.bustype = BUS_GAMEPORT;
		input_dev->id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT;
		input_dev->id.product = sw->type;
		input_dev->id.version = 0x0100;
		input_dev->dev.parent = &gameport->dev;

		input_set_drvdata(input_dev, sw);

		input_dev->open = sw_open;
		input_dev->close = sw_close;

		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

		for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
			int min, max, fuzz, flat;

			code = sw_abs[sw->type][j];
			min = bits == 1 ? -1 : 0;
			max = (1 << bits) - 1;
			fuzz = (bits >> 1) >= 2 ? 1 << ((bits >> 1) - 2) : 0;
			flat = code == ABS_THROTTLE || bits < 5 ?
				0 : 1 << (bits - 5);

			input_set_abs_params(input_dev, code,
					     min, max, fuzz, flat);
		}

		for (j = 0; (code = sw_btn[sw->type][j]); j++)
			__set_bit(code, input_dev->keybit);

		dbg("%s%s [%d-bit id %d data %d]\n", sw->name, comment, m, l, k);

		err = input_register_device(sw->dev[i]);
		if (err)
			goto fail4;
	}

 out:	kfree(buf);
	kfree(idbuf);

	return err;

 fail4:	input_free_device(sw->dev[i]);
 fail3:	while (--i >= 0)
		input_unregister_device(sw->dev[i]);
 fail2:	gameport_close(gameport);
 fail1:	gameport_set_drvdata(gameport, NULL);
	kfree(sw);
	goto out;
}