magellan.c (4820B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 1999-2001 Vojtech Pavlik 4 */ 5 6/* 7 * Magellan and Space Mouse 6dof controller 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/input.h> 17#include <linux/serio.h> 18 19#define DRIVER_DESC "Magellan and SpaceMouse 6dof controller driver" 20 21MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 22MODULE_DESCRIPTION(DRIVER_DESC); 23MODULE_LICENSE("GPL"); 24 25/* 26 * Definitions & global arrays. 27 */ 28 29#define MAGELLAN_MAX_LENGTH 32 30 31static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; 32static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; 33 34/* 35 * Per-Magellan data. 36 */ 37 38struct magellan { 39 struct input_dev *dev; 40 int idx; 41 unsigned char data[MAGELLAN_MAX_LENGTH]; 42 char phys[32]; 43}; 44 45/* 46 * magellan_crunch_nibbles() verifies that the bytes sent from the Magellan 47 * have correct upper nibbles for the lower ones, if not, the packet will 48 * be thrown away. It also strips these upper halves to simplify further 49 * processing. 50 */ 51 52static int magellan_crunch_nibbles(unsigned char *data, int count) 53{ 54 static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?"; 55 56 do { 57 if (data[count] == nibbles[data[count] & 0xf]) 58 data[count] = data[count] & 0xf; 59 else 60 return -1; 61 } while (--count); 62 63 return 0; 64} 65 66static void magellan_process_packet(struct magellan* magellan) 67{ 68 struct input_dev *dev = magellan->dev; 69 unsigned char *data = magellan->data; 70 int i, t; 71 72 if (!magellan->idx) return; 73 74 switch (magellan->data[0]) { 75 76 case 'd': /* Axis data */ 77 if (magellan->idx != 25) return; 78 if (magellan_crunch_nibbles(data, 24)) return; 79 for (i = 0; i < 6; i++) 80 input_report_abs(dev, magellan_axes[i], 81 (data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 | 82 data[(i << 2) + 3] << 4 | data[(i << 2) + 4]) - 32768); 83 break; 84 85 case 'k': /* Button data */ 86 if (magellan->idx != 4) return; 87 if (magellan_crunch_nibbles(data, 3)) return; 88 t = (data[1] << 1) | (data[2] << 5) | data[3]; 89 for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1); 90 break; 91 } 92 93 input_sync(dev); 94} 95 96static irqreturn_t magellan_interrupt(struct serio *serio, 97 unsigned char data, unsigned int flags) 98{ 99 struct magellan* magellan = serio_get_drvdata(serio); 100 101 if (data == '\r') { 102 magellan_process_packet(magellan); 103 magellan->idx = 0; 104 } else { 105 if (magellan->idx < MAGELLAN_MAX_LENGTH) 106 magellan->data[magellan->idx++] = data; 107 } 108 return IRQ_HANDLED; 109} 110 111/* 112 * magellan_disconnect() is the opposite of magellan_connect() 113 */ 114 115static void magellan_disconnect(struct serio *serio) 116{ 117 struct magellan* magellan = serio_get_drvdata(serio); 118 119 serio_close(serio); 120 serio_set_drvdata(serio, NULL); 121 input_unregister_device(magellan->dev); 122 kfree(magellan); 123} 124 125/* 126 * magellan_connect() is the routine that is called when someone adds a 127 * new serio device that supports Magellan protocol and registers it as 128 * an input device. 129 */ 130 131static int magellan_connect(struct serio *serio, struct serio_driver *drv) 132{ 133 struct magellan *magellan; 134 struct input_dev *input_dev; 135 int err = -ENOMEM; 136 int i; 137 138 magellan = kzalloc(sizeof(struct magellan), GFP_KERNEL); 139 input_dev = input_allocate_device(); 140 if (!magellan || !input_dev) 141 goto fail1; 142 143 magellan->dev = input_dev; 144 snprintf(magellan->phys, sizeof(magellan->phys), "%s/input0", serio->phys); 145 146 input_dev->name = "LogiCad3D Magellan / SpaceMouse"; 147 input_dev->phys = magellan->phys; 148 input_dev->id.bustype = BUS_RS232; 149 input_dev->id.vendor = SERIO_MAGELLAN; 150 input_dev->id.product = 0x0001; 151 input_dev->id.version = 0x0100; 152 input_dev->dev.parent = &serio->dev; 153 154 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 155 156 for (i = 0; i < 9; i++) 157 set_bit(magellan_buttons[i], input_dev->keybit); 158 159 for (i = 0; i < 6; i++) 160 input_set_abs_params(input_dev, magellan_axes[i], -360, 360, 0, 0); 161 162 serio_set_drvdata(serio, magellan); 163 164 err = serio_open(serio, drv); 165 if (err) 166 goto fail2; 167 168 err = input_register_device(magellan->dev); 169 if (err) 170 goto fail3; 171 172 return 0; 173 174 fail3: serio_close(serio); 175 fail2: serio_set_drvdata(serio, NULL); 176 fail1: input_free_device(input_dev); 177 kfree(magellan); 178 return err; 179} 180 181/* 182 * The serio driver structure. 183 */ 184 185static const struct serio_device_id magellan_serio_ids[] = { 186 { 187 .type = SERIO_RS232, 188 .proto = SERIO_MAGELLAN, 189 .id = SERIO_ANY, 190 .extra = SERIO_ANY, 191 }, 192 { 0 } 193}; 194 195MODULE_DEVICE_TABLE(serio, magellan_serio_ids); 196 197static struct serio_driver magellan_drv = { 198 .driver = { 199 .name = "magellan", 200 }, 201 .description = DRIVER_DESC, 202 .id_table = magellan_serio_ids, 203 .interrupt = magellan_interrupt, 204 .connect = magellan_connect, 205 .disconnect = magellan_disconnect, 206}; 207 208module_serio_driver(magellan_drv);