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_polled_dev *dev) 29 { 30 struct input_dev *idev = dev->input; 31 s8 x, y, z; 32 33 mutex_lock(&ams_info.lock); 34 35 ams_sensors(&x, &y, &z); 36 37 x -= ams_info.xcalib; 38 y -= ams_info.ycalib; 39 z -= ams_info.zcalib; 40 41 input_report_abs(idev, ABS_X, invert ? -x : x); 42 input_report_abs(idev, ABS_Y, invert ? -y : y); 43 input_report_abs(idev, ABS_Z, z); 44 45 input_sync(idev); 46 47 mutex_unlock(&ams_info.lock); 48 } 49 50 /* Call with ams_info.lock held! */ 51 static int ams_input_enable(void) 52 { 53 struct input_dev *input; 54 s8 x, y, z; 55 int error; 56 57 ams_sensors(&x, &y, &z); 58 ams_info.xcalib = x; 59 ams_info.ycalib = y; 60 ams_info.zcalib = z; 61 62 ams_info.idev = input_allocate_polled_device(); 63 if (!ams_info.idev) 64 return -ENOMEM; 65 66 ams_info.idev->poll = ams_idev_poll; 67 ams_info.idev->poll_interval = 25; 68 69 input = ams_info.idev->input; 70 input->name = "Apple Motion Sensor"; 71 input->id.bustype = ams_info.bustype; 72 input->id.vendor = 0; 73 input->dev.parent = &ams_info.of_dev->dev; 74 75 input_set_abs_params(input, ABS_X, -50, 50, 3, 0); 76 input_set_abs_params(input, ABS_Y, -50, 50, 3, 0); 77 input_set_abs_params(input, ABS_Z, -50, 50, 3, 0); 78 79 set_bit(EV_ABS, input->evbit); 80 set_bit(EV_KEY, input->evbit); 81 set_bit(BTN_TOUCH, input->keybit); 82 83 error = input_register_polled_device(ams_info.idev); 84 if (error) { 85 input_free_polled_device(ams_info.idev); 86 ams_info.idev = NULL; 87 return error; 88 } 89 90 joystick = true; 91 92 return 0; 93 } 94 95 static void ams_input_disable(void) 96 { 97 if (ams_info.idev) { 98 input_unregister_polled_device(ams_info.idev); 99 input_free_polled_device(ams_info.idev); 100 ams_info.idev = NULL; 101 } 102 103 joystick = false; 104 } 105 106 static ssize_t ams_input_show_joystick(struct device *dev, 107 struct device_attribute *attr, char *buf) 108 { 109 return sprintf(buf, "%d\n", joystick); 110 } 111 112 static ssize_t ams_input_store_joystick(struct device *dev, 113 struct device_attribute *attr, const char *buf, size_t count) 114 { 115 unsigned long enable; 116 int error = 0; 117 int ret; 118 119 ret = kstrtoul(buf, 0, &enable); 120 if (ret) 121 return ret; 122 if (enable > 1) 123 return -EINVAL; 124 125 mutex_lock(&ams_input_mutex); 126 127 if (enable != joystick) { 128 if (enable) 129 error = ams_input_enable(); 130 else 131 ams_input_disable(); 132 } 133 134 mutex_unlock(&ams_input_mutex); 135 136 return error ? error : count; 137 } 138 139 static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, 140 ams_input_show_joystick, ams_input_store_joystick); 141 142 int ams_input_init(void) 143 { 144 if (joystick) 145 ams_input_enable(); 146 147 return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); 148 } 149 150 void ams_input_exit(void) 151 { 152 device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); 153 154 mutex_lock(&ams_input_mutex); 155 ams_input_disable(); 156 mutex_unlock(&ams_input_mutex); 157 } 158