forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
5.5 KiB
247 lines
5.5 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Copyright (c) 1999-2001 Vojtech Pavlik |
|
*/ |
|
|
|
/* |
|
* Creative Labs Blaster GamePad Cobra driver for Linux |
|
*/ |
|
|
|
/* |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
#include <linux/slab.h> |
|
#include <linux/gameport.h> |
|
#include <linux/input.h> |
|
#include <linux/jiffies.h> |
|
|
|
#define DRIVER_DESC "Creative Labs Blaster GamePad Cobra driver" |
|
|
|
MODULE_AUTHOR("Vojtech Pavlik <[email protected]>"); |
|
MODULE_DESCRIPTION(DRIVER_DESC); |
|
MODULE_LICENSE("GPL"); |
|
|
|
#define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */ |
|
#define COBRA_LENGTH 36 |
|
|
|
static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 }; |
|
|
|
struct cobra { |
|
struct gameport *gameport; |
|
struct input_dev *dev[2]; |
|
int reads; |
|
int bads; |
|
unsigned char exists; |
|
char phys[2][32]; |
|
}; |
|
|
|
static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data) |
|
{ |
|
unsigned long flags; |
|
unsigned char u, v, w; |
|
__u64 buf[2]; |
|
int r[2], t[2]; |
|
int i, j, ret; |
|
|
|
int strobe = gameport_time(gameport, COBRA_MAX_STROBE); |
|
|
|
for (i = 0; i < 2; i++) { |
|
r[i] = buf[i] = 0; |
|
t[i] = COBRA_MAX_STROBE; |
|
} |
|
|
|
local_irq_save(flags); |
|
|
|
u = gameport_read(gameport); |
|
|
|
do { |
|
t[0]--; t[1]--; |
|
v = gameport_read(gameport); |
|
for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) |
|
if (w & 0x30) { |
|
if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) { |
|
buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; |
|
t[i] = strobe; |
|
u = v; |
|
} else t[i] = 0; |
|
} |
|
} while (t[0] > 0 || t[1] > 0); |
|
|
|
local_irq_restore(flags); |
|
|
|
ret = 0; |
|
|
|
for (i = 0; i < 2; i++) { |
|
|
|
if (r[i] != COBRA_LENGTH) continue; |
|
|
|
for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) |
|
buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1)); |
|
|
|
if (j < COBRA_LENGTH) ret |= (1 << i); |
|
|
|
data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) |
|
| ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) |
|
| ((buf[i] >> 11) & 0x1f00000); |
|
|
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static void cobra_poll(struct gameport *gameport) |
|
{ |
|
struct cobra *cobra = gameport_get_drvdata(gameport); |
|
struct input_dev *dev; |
|
unsigned int data[2]; |
|
int i, j, r; |
|
|
|
cobra->reads++; |
|
|
|
if ((r = cobra_read_packet(gameport, data)) != cobra->exists) { |
|
cobra->bads++; |
|
return; |
|
} |
|
|
|
for (i = 0; i < 2; i++) |
|
if (cobra->exists & r & (1 << i)) { |
|
|
|
dev = cobra->dev[i]; |
|
|
|
input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1)); |
|
input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1)); |
|
|
|
for (j = 0; cobra_btn[j]; j++) |
|
input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j)); |
|
|
|
input_sync(dev); |
|
|
|
} |
|
} |
|
|
|
static int cobra_open(struct input_dev *dev) |
|
{ |
|
struct cobra *cobra = input_get_drvdata(dev); |
|
|
|
gameport_start_polling(cobra->gameport); |
|
return 0; |
|
} |
|
|
|
static void cobra_close(struct input_dev *dev) |
|
{ |
|
struct cobra *cobra = input_get_drvdata(dev); |
|
|
|
gameport_stop_polling(cobra->gameport); |
|
} |
|
|
|
static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv) |
|
{ |
|
struct cobra *cobra; |
|
struct input_dev *input_dev; |
|
unsigned int data[2]; |
|
int i, j; |
|
int err; |
|
|
|
cobra = kzalloc(sizeof(struct cobra), GFP_KERNEL); |
|
if (!cobra) |
|
return -ENOMEM; |
|
|
|
cobra->gameport = gameport; |
|
|
|
gameport_set_drvdata(gameport, cobra); |
|
|
|
err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); |
|
if (err) |
|
goto fail1; |
|
|
|
cobra->exists = cobra_read_packet(gameport, data); |
|
|
|
for (i = 0; i < 2; i++) |
|
if ((cobra->exists >> i) & data[i] & 1) { |
|
printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d" |
|
" Contact [email protected]\n", i, gameport->phys, (data[i] >> 2) & 7); |
|
cobra->exists &= ~(1 << i); |
|
} |
|
|
|
if (!cobra->exists) { |
|
err = -ENODEV; |
|
goto fail2; |
|
} |
|
|
|
gameport_set_poll_handler(gameport, cobra_poll); |
|
gameport_set_poll_interval(gameport, 20); |
|
|
|
for (i = 0; i < 2; i++) { |
|
if (~(cobra->exists >> i) & 1) |
|
continue; |
|
|
|
cobra->dev[i] = input_dev = input_allocate_device(); |
|
if (!input_dev) { |
|
err = -ENOMEM; |
|
goto fail3; |
|
} |
|
|
|
snprintf(cobra->phys[i], sizeof(cobra->phys[i]), |
|
"%s/input%d", gameport->phys, i); |
|
|
|
input_dev->name = "Creative Labs Blaster GamePad Cobra"; |
|
input_dev->phys = cobra->phys[i]; |
|
input_dev->id.bustype = BUS_GAMEPORT; |
|
input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE; |
|
input_dev->id.product = 0x0008; |
|
input_dev->id.version = 0x0100; |
|
input_dev->dev.parent = &gameport->dev; |
|
|
|
input_set_drvdata(input_dev, cobra); |
|
|
|
input_dev->open = cobra_open; |
|
input_dev->close = cobra_close; |
|
|
|
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
|
input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0); |
|
input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0); |
|
for (j = 0; cobra_btn[j]; j++) |
|
set_bit(cobra_btn[j], input_dev->keybit); |
|
|
|
err = input_register_device(cobra->dev[i]); |
|
if (err) |
|
goto fail4; |
|
} |
|
|
|
return 0; |
|
|
|
fail4: input_free_device(cobra->dev[i]); |
|
fail3: while (--i >= 0) |
|
if (cobra->dev[i]) |
|
input_unregister_device(cobra->dev[i]); |
|
fail2: gameport_close(gameport); |
|
fail1: gameport_set_drvdata(gameport, NULL); |
|
kfree(cobra); |
|
return err; |
|
} |
|
|
|
static void cobra_disconnect(struct gameport *gameport) |
|
{ |
|
struct cobra *cobra = gameport_get_drvdata(gameport); |
|
int i; |
|
|
|
for (i = 0; i < 2; i++) |
|
if ((cobra->exists >> i) & 1) |
|
input_unregister_device(cobra->dev[i]); |
|
gameport_close(gameport); |
|
gameport_set_drvdata(gameport, NULL); |
|
kfree(cobra); |
|
} |
|
|
|
static struct gameport_driver cobra_drv = { |
|
.driver = { |
|
.name = "cobra", |
|
}, |
|
.description = DRIVER_DESC, |
|
.connect = cobra_connect, |
|
.disconnect = cobra_disconnect, |
|
}; |
|
|
|
module_gameport_driver(cobra_drv);
|
|
|