1  // SPDX-License-Identifier: GPL-2.0-or-later
2  /*
3   *  Copyright (c) 1999-2001 Vojtech Pavlik
4   */
5  
6  /*
7   * Creative Labs Blaster GamePad Cobra driver for Linux
8   */
9  
10  /*
11   */
12  
13  #include <linux/kernel.h>
14  #include <linux/module.h>
15  #include <linux/slab.h>
16  #include <linux/gameport.h>
17  #include <linux/input.h>
18  #include <linux/jiffies.h>
19  
20  #define DRIVER_DESC	"Creative Labs Blaster GamePad Cobra driver"
21  
22  MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
23  MODULE_DESCRIPTION(DRIVER_DESC);
24  MODULE_LICENSE("GPL");
25  
26  #define COBRA_MAX_STROBE	45	/* 45 us max wait for first strobe */
27  #define COBRA_LENGTH		36
28  
29  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 };
30  
31  struct cobra {
32  	struct gameport *gameport;
33  	struct input_dev *dev[2];
34  	int reads;
35  	int bads;
36  	unsigned char exists;
37  	char phys[2][32];
38  };
39  
cobra_read_packet(struct gameport * gameport,unsigned int * data)40  static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data)
41  {
42  	unsigned long flags;
43  	unsigned char u, v, w;
44  	__u64 buf[2];
45  	int r[2], t[2];
46  	int i, j, ret;
47  
48  	int strobe = gameport_time(gameport, COBRA_MAX_STROBE);
49  
50  	for (i = 0; i < 2; i++) {
51  		r[i] = buf[i] = 0;
52  		t[i] = COBRA_MAX_STROBE;
53  	}
54  
55  	local_irq_save(flags);
56  
57  	u = gameport_read(gameport);
58  
59  	do {
60  		t[0]--; t[1]--;
61  		v = gameport_read(gameport);
62  		for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2)
63  			if (w & 0x30) {
64  				if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) {
65  					buf[i] |= (__u64)((w >> 5) & 1) << r[i]++;
66  					t[i] = strobe;
67  					u = v;
68  				} else t[i] = 0;
69  			}
70  	} while (t[0] > 0 || t[1] > 0);
71  
72  	local_irq_restore(flags);
73  
74  	ret = 0;
75  
76  	for (i = 0; i < 2; i++) {
77  
78  		if (r[i] != COBRA_LENGTH) continue;
79  
80  		for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++)
81  			buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1));
82  
83  		if (j < COBRA_LENGTH) ret |= (1 << i);
84  
85  		data[i] = ((buf[i] >>  7) & 0x000001f) | ((buf[i] >>  8) & 0x00003e0)
86  			| ((buf[i] >>  9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000)
87  			| ((buf[i] >> 11) & 0x1f00000);
88  
89  	}
90  
91  	return ret;
92  }
93  
cobra_poll(struct gameport * gameport)94  static void cobra_poll(struct gameport *gameport)
95  {
96  	struct cobra *cobra = gameport_get_drvdata(gameport);
97  	struct input_dev *dev;
98  	unsigned int data[2];
99  	int i, j, r;
100  
101  	cobra->reads++;
102  
103  	if ((r = cobra_read_packet(gameport, data)) != cobra->exists) {
104  		cobra->bads++;
105  		return;
106  	}
107  
108  	for (i = 0; i < 2; i++)
109  		if (cobra->exists & r & (1 << i)) {
110  
111  			dev = cobra->dev[i];
112  
113  			input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1));
114  			input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1));
115  
116  			for (j = 0; cobra_btn[j]; j++)
117  				input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j));
118  
119  			input_sync(dev);
120  
121  		}
122  }
123  
cobra_open(struct input_dev * dev)124  static int cobra_open(struct input_dev *dev)
125  {
126  	struct cobra *cobra = input_get_drvdata(dev);
127  
128  	gameport_start_polling(cobra->gameport);
129  	return 0;
130  }
131  
cobra_close(struct input_dev * dev)132  static void cobra_close(struct input_dev *dev)
133  {
134  	struct cobra *cobra = input_get_drvdata(dev);
135  
136  	gameport_stop_polling(cobra->gameport);
137  }
138  
cobra_connect(struct gameport * gameport,struct gameport_driver * drv)139  static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
140  {
141  	struct cobra *cobra;
142  	struct input_dev *input_dev;
143  	unsigned int data[2];
144  	int i, j;
145  	int err;
146  
147  	cobra = kzalloc(sizeof(struct cobra), GFP_KERNEL);
148  	if (!cobra)
149  		return -ENOMEM;
150  
151  	cobra->gameport = gameport;
152  
153  	gameport_set_drvdata(gameport, cobra);
154  
155  	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
156  	if (err)
157  		goto fail1;
158  
159  	cobra->exists = cobra_read_packet(gameport, data);
160  
161  	for (i = 0; i < 2; i++)
162  		if ((cobra->exists >> i) & data[i] & 1) {
163  			printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d"
164  				" Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7);
165  			cobra->exists &= ~(1 << i);
166  		}
167  
168  	if (!cobra->exists) {
169  		err = -ENODEV;
170  		goto fail2;
171  	}
172  
173  	gameport_set_poll_handler(gameport, cobra_poll);
174  	gameport_set_poll_interval(gameport, 20);
175  
176  	for (i = 0; i < 2; i++) {
177  		if (~(cobra->exists >> i) & 1)
178  			continue;
179  
180  		cobra->dev[i] = input_dev = input_allocate_device();
181  		if (!input_dev) {
182  			err = -ENOMEM;
183  			goto fail3;
184  		}
185  
186  		snprintf(cobra->phys[i], sizeof(cobra->phys[i]),
187  			 "%s/input%d", gameport->phys, i);
188  
189  		input_dev->name = "Creative Labs Blaster GamePad Cobra";
190  		input_dev->phys = cobra->phys[i];
191  		input_dev->id.bustype = BUS_GAMEPORT;
192  		input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
193  		input_dev->id.product = 0x0008;
194  		input_dev->id.version = 0x0100;
195  		input_dev->dev.parent = &gameport->dev;
196  
197  		input_set_drvdata(input_dev, cobra);
198  
199  		input_dev->open = cobra_open;
200  		input_dev->close = cobra_close;
201  
202  		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
203  		input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0);
204  		input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0);
205  		for (j = 0; cobra_btn[j]; j++)
206  			set_bit(cobra_btn[j], input_dev->keybit);
207  
208  		err = input_register_device(cobra->dev[i]);
209  		if (err)
210  			goto fail4;
211  	}
212  
213  	return 0;
214  
215   fail4:	input_free_device(cobra->dev[i]);
216   fail3:	while (--i >= 0)
217  		if (cobra->dev[i])
218  			input_unregister_device(cobra->dev[i]);
219   fail2:	gameport_close(gameport);
220   fail1:	gameport_set_drvdata(gameport, NULL);
221  	kfree(cobra);
222  	return err;
223  }
224  
cobra_disconnect(struct gameport * gameport)225  static void cobra_disconnect(struct gameport *gameport)
226  {
227  	struct cobra *cobra = gameport_get_drvdata(gameport);
228  	int i;
229  
230  	for (i = 0; i < 2; i++)
231  		if ((cobra->exists >> i) & 1)
232  			input_unregister_device(cobra->dev[i]);
233  	gameport_close(gameport);
234  	gameport_set_drvdata(gameport, NULL);
235  	kfree(cobra);
236  }
237  
238  static struct gameport_driver cobra_drv = {
239  	.driver		= {
240  		.name	= "cobra",
241  	},
242  	.description	= DRIVER_DESC,
243  	.connect	= cobra_connect,
244  	.disconnect	= cobra_disconnect,
245  };
246  
247  module_gameport_driver(cobra_drv);
248