18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Apple Motion Sensor driver (joystick emulation) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Stelian Pop (stelian@popies.net) 68c2ecf20Sopenharmony_ci * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "ams.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic bool joystick; 198c2ecf20Sopenharmony_cimodule_param(joystick, bool, S_IRUGO); 208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick, "Enable the input class device on module load"); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic bool invert; 238c2ecf20Sopenharmony_cimodule_param(invert, bool, S_IWUSR | S_IRUGO); 248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ams_input_mutex); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void ams_idev_poll(struct input_dev *idev) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci s8 x, y, z; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci mutex_lock(&ams_info.lock); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci ams_sensors(&x, &y, &z); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci x -= ams_info.xcalib; 378c2ecf20Sopenharmony_ci y -= ams_info.ycalib; 388c2ecf20Sopenharmony_ci z -= ams_info.zcalib; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_X, invert ? -x : x); 418c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_Y, invert ? -y : y); 428c2ecf20Sopenharmony_ci input_report_abs(idev, ABS_Z, z); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci input_sync(idev); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci mutex_unlock(&ams_info.lock); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Call with ams_info.lock held! */ 508c2ecf20Sopenharmony_cistatic int ams_input_enable(void) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct input_dev *input; 538c2ecf20Sopenharmony_ci s8 x, y, z; 548c2ecf20Sopenharmony_ci int error; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci ams_sensors(&x, &y, &z); 578c2ecf20Sopenharmony_ci ams_info.xcalib = x; 588c2ecf20Sopenharmony_ci ams_info.ycalib = y; 598c2ecf20Sopenharmony_ci ams_info.zcalib = z; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci input = input_allocate_device(); 628c2ecf20Sopenharmony_ci if (!input) 638c2ecf20Sopenharmony_ci return -ENOMEM; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci input->name = "Apple Motion Sensor"; 668c2ecf20Sopenharmony_ci input->id.bustype = ams_info.bustype; 678c2ecf20Sopenharmony_ci input->id.vendor = 0; 688c2ecf20Sopenharmony_ci input->dev.parent = &ams_info.of_dev->dev; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_X, -50, 50, 3, 0); 718c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_Y, -50, 50, 3, 0); 728c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_Z, -50, 50, 3, 0); 738c2ecf20Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_TOUCH); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci error = input_setup_polling(input, ams_idev_poll); 768c2ecf20Sopenharmony_ci if (error) 778c2ecf20Sopenharmony_ci goto err_free_input; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci input_set_poll_interval(input, 25); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci error = input_register_device(input); 828c2ecf20Sopenharmony_ci if (error) 838c2ecf20Sopenharmony_ci goto err_free_input; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci ams_info.idev = input; 868c2ecf20Sopenharmony_ci joystick = true; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cierr_free_input: 918c2ecf20Sopenharmony_ci input_free_device(input); 928c2ecf20Sopenharmony_ci return error; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void ams_input_disable(void) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci if (ams_info.idev) { 988c2ecf20Sopenharmony_ci input_unregister_device(ams_info.idev); 998c2ecf20Sopenharmony_ci ams_info.idev = NULL; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci joystick = false; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic ssize_t ams_input_show_joystick(struct device *dev, 1068c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", joystick); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic ssize_t ams_input_store_joystick(struct device *dev, 1128c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci unsigned long enable; 1158c2ecf20Sopenharmony_ci int error = 0; 1168c2ecf20Sopenharmony_ci int ret; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &enable); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci if (enable > 1) 1228c2ecf20Sopenharmony_ci return -EINVAL; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_lock(&ams_input_mutex); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (enable != joystick) { 1278c2ecf20Sopenharmony_ci if (enable) 1288c2ecf20Sopenharmony_ci error = ams_input_enable(); 1298c2ecf20Sopenharmony_ci else 1308c2ecf20Sopenharmony_ci ams_input_disable(); 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci mutex_unlock(&ams_input_mutex); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return error ? error : count; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, 1398c2ecf20Sopenharmony_ci ams_input_show_joystick, ams_input_store_joystick); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ciint ams_input_init(void) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci if (joystick) 1448c2ecf20Sopenharmony_ci ams_input_enable(); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_civoid ams_input_exit(void) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci mutex_lock(&ams_input_mutex); 1548c2ecf20Sopenharmony_ci ams_input_disable(); 1558c2ecf20Sopenharmony_ci mutex_unlock(&ams_input_mutex); 1568c2ecf20Sopenharmony_ci} 157