1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Apple Motion Sensor driver (joystick emulation) 4 * 5 * Copyright (C) 2005 Stelian Pop (stelian@popies.net) 6 * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) 7 */ 8 9 #include <linux/module.h> 10 11 #include <linux/types.h> 12 #include <linux/errno.h> 13 #include <linux/init.h> 14 #include <linux/delay.h> 15 16 #include "ams.h" 17 18 static bool joystick; 19 module_param(joystick, bool, S_IRUGO); 20 MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); 21 22 static bool invert; 23 module_param(invert, bool, S_IWUSR | S_IRUGO); 24 MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); 25 26 static DEFINE_MUTEX(ams_input_mutex); 27 28 static void ams_idev_poll(struct input_dev *idev) 29 { 30 s8 x, y, z; 31 32 mutex_lock(&ams_info.lock); 33 34 ams_sensors(&x, &y, &z); 35 36 x -= ams_info.xcalib; 37 y -= ams_info.ycalib; 38 z -= ams_info.zcalib; 39 40 input_report_abs(idev, ABS_X, invert ? -x : x); 41 input_report_abs(idev, ABS_Y, invert ? -y : y); 42 input_report_abs(idev, ABS_Z, z); 43 44 input_sync(idev); 45 46 mutex_unlock(&ams_info.lock); 47 } 48 49 /* Call with ams_info.lock held! */ 50 static int ams_input_enable(void) 51 { 52 struct input_dev *input; 53 s8 x, y, z; 54 int error; 55 56 ams_sensors(&x, &y, &z); 57 ams_info.xcalib = x; 58 ams_info.ycalib = y; 59 ams_info.zcalib = z; 60 61 input = input_allocate_device(); 62 if (!input) 63 return -ENOMEM; 64 65 input->name = "Apple Motion Sensor"; 66 input->id.bustype = ams_info.bustype; 67 input->id.vendor = 0; 68 input->dev.parent = &ams_info.of_dev->dev; 69 70 input_set_abs_params(input, ABS_X, -50, 50, 3, 0); 71 input_set_abs_params(input, ABS_Y, -50, 50, 3, 0); 72 input_set_abs_params(input, ABS_Z, -50, 50, 3, 0); 73 input_set_capability(input, EV_KEY, BTN_TOUCH); 74 75 error = input_setup_polling(input, ams_idev_poll); 76 if (error) 77 goto err_free_input; 78 79 input_set_poll_interval(input, 25); 80 81 error = input_register_device(input); 82 if (error) 83 goto err_free_input; 84 85 ams_info.idev = input; 86 joystick = true; 87 88 return 0; 89 90 err_free_input: 91 input_free_device(input); 92 return error; 93 } 94 95 static void ams_input_disable(void) 96 { 97 if (ams_info.idev) { 98 input_unregister_device(ams_info.idev); 99 ams_info.idev = NULL; 100 } 101 102 joystick = false; 103 } 104 105 static ssize_t ams_input_show_joystick(struct device *dev, 106 struct device_attribute *attr, char *buf) 107 { 108 return sprintf(buf, "%d\n", joystick); 109 } 110 111 static ssize_t ams_input_store_joystick(struct device *dev, 112 struct device_attribute *attr, const char *buf, size_t count) 113 { 114 unsigned long enable; 115 int error = 0; 116 int ret; 117 118 ret = kstrtoul(buf, 0, &enable); 119 if (ret) 120 return ret; 121 if (enable > 1) 122 return -EINVAL; 123 124 mutex_lock(&ams_input_mutex); 125 126 if (enable != joystick) { 127 if (enable) 128 error = ams_input_enable(); 129 else 130 ams_input_disable(); 131 } 132 133 mutex_unlock(&ams_input_mutex); 134 135 return error ? error : count; 136 } 137 138 static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, 139 ams_input_show_joystick, ams_input_store_joystick); 140 141 int ams_input_init(void) 142 { 143 if (joystick) 144 ams_input_enable(); 145 146 return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); 147 } 148 149 void ams_input_exit(void) 150 { 151 device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); 152 153 mutex_lock(&ams_input_mutex); 154 ams_input_disable(); 155 mutex_unlock(&ams_input_mutex); 156 } 157