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;
}