18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SEGA Dreamcast controller driver 48c2ecf20Sopenharmony_ci * Based on drivers/usb/iforce.c 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright Yaegashi Takeshi, 2001 78c2ecf20Sopenharmony_ci * Adrian McMenamin, 2008 - 2009 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/input.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/timer.h> 168c2ecf20Sopenharmony_ci#include <linux/maple.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>"); 198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SEGA Dreamcast controller driver"); 208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct dc_pad { 238c2ecf20Sopenharmony_ci struct input_dev *dev; 248c2ecf20Sopenharmony_ci struct maple_device *mdev; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void dc_pad_callback(struct mapleq *mq) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned short buttons; 308c2ecf20Sopenharmony_ci struct maple_device *mapledev = mq->dev; 318c2ecf20Sopenharmony_ci struct dc_pad *pad = maple_get_drvdata(mapledev); 328c2ecf20Sopenharmony_ci struct input_dev *dev = pad->dev; 338c2ecf20Sopenharmony_ci unsigned char *res = mq->recvbuf->buf; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci buttons = ~le16_to_cpup((__le16 *)(res + 8)); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_HAT0Y, 388c2ecf20Sopenharmony_ci (buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0)); 398c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_HAT0X, 408c2ecf20Sopenharmony_ci (buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0)); 418c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_HAT1Y, 428c2ecf20Sopenharmony_ci (buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0)); 438c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_HAT1X, 448c2ecf20Sopenharmony_ci (buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0)); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci input_report_key(dev, BTN_C, buttons & 0x0001); 478c2ecf20Sopenharmony_ci input_report_key(dev, BTN_B, buttons & 0x0002); 488c2ecf20Sopenharmony_ci input_report_key(dev, BTN_A, buttons & 0x0004); 498c2ecf20Sopenharmony_ci input_report_key(dev, BTN_START, buttons & 0x0008); 508c2ecf20Sopenharmony_ci input_report_key(dev, BTN_Z, buttons & 0x0100); 518c2ecf20Sopenharmony_ci input_report_key(dev, BTN_Y, buttons & 0x0200); 528c2ecf20Sopenharmony_ci input_report_key(dev, BTN_X, buttons & 0x0400); 538c2ecf20Sopenharmony_ci input_report_key(dev, BTN_SELECT, buttons & 0x0800); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_GAS, res[10]); 568c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_BRAKE, res[11]); 578c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, res[12]); 588c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, res[13]); 598c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_RX, res[14]); 608c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_RY, res[15]); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int dc_pad_open(struct input_dev *dev) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct dc_pad *pad = dev_get_platdata(&dev->dev); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20, 688c2ecf20Sopenharmony_ci MAPLE_FUNC_CONTROLLER); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void dc_pad_close(struct input_dev *dev) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct dc_pad *pad = dev_get_platdata(&dev->dev); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci maple_getcond_callback(pad->mdev, dc_pad_callback, 0, 788c2ecf20Sopenharmony_ci MAPLE_FUNC_CONTROLLER); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* allow the controller to be used */ 828c2ecf20Sopenharmony_cistatic int probe_maple_controller(struct device *dev) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci static const short btn_bit[32] = { 858c2ecf20Sopenharmony_ci BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1, 868c2ecf20Sopenharmony_ci BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1, 878c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, 888c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, 898c2ecf20Sopenharmony_ci }; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci static const short abs_bit[32] = { 928c2ecf20Sopenharmony_ci -1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X, 938c2ecf20Sopenharmony_ci -1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X, 948c2ecf20Sopenharmony_ci ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1, 958c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, 968c2ecf20Sopenharmony_ci }; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci struct maple_device *mdev = to_maple_dev(dev); 998c2ecf20Sopenharmony_ci struct maple_driver *mdrv = to_maple_driver(dev->driver); 1008c2ecf20Sopenharmony_ci int i, error; 1018c2ecf20Sopenharmony_ci struct dc_pad *pad; 1028c2ecf20Sopenharmony_ci struct input_dev *idev; 1038c2ecf20Sopenharmony_ci unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL); 1068c2ecf20Sopenharmony_ci idev = input_allocate_device(); 1078c2ecf20Sopenharmony_ci if (!pad || !idev) { 1088c2ecf20Sopenharmony_ci error = -ENOMEM; 1098c2ecf20Sopenharmony_ci goto fail; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci pad->dev = idev; 1138c2ecf20Sopenharmony_ci pad->mdev = mdev; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci idev->open = dc_pad_open; 1168c2ecf20Sopenharmony_ci idev->close = dc_pad_close; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 1198c2ecf20Sopenharmony_ci if (data & (1 << i)) { 1208c2ecf20Sopenharmony_ci if (btn_bit[i] >= 0) 1218c2ecf20Sopenharmony_ci __set_bit(btn_bit[i], idev->keybit); 1228c2ecf20Sopenharmony_ci else if (abs_bit[i] >= 0) 1238c2ecf20Sopenharmony_ci __set_bit(abs_bit[i], idev->absbit); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (idev->keybit[BIT_WORD(BTN_JOYSTICK)]) 1288c2ecf20Sopenharmony_ci idev->evbit[0] |= BIT_MASK(EV_KEY); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (idev->absbit[0]) 1318c2ecf20Sopenharmony_ci idev->evbit[0] |= BIT_MASK(EV_ABS); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci for (i = ABS_X; i <= ABS_BRAKE; i++) 1348c2ecf20Sopenharmony_ci input_set_abs_params(idev, i, 0, 255, 0, 0); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++) 1378c2ecf20Sopenharmony_ci input_set_abs_params(idev, i, 1, -1, 0, 0); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci idev->dev.platform_data = pad; 1408c2ecf20Sopenharmony_ci idev->dev.parent = &mdev->dev; 1418c2ecf20Sopenharmony_ci idev->name = mdev->product_name; 1428c2ecf20Sopenharmony_ci idev->id.bustype = BUS_HOST; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci error = input_register_device(idev); 1458c2ecf20Sopenharmony_ci if (error) 1468c2ecf20Sopenharmony_ci goto fail; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci mdev->driver = mdrv; 1498c2ecf20Sopenharmony_ci maple_set_drvdata(mdev, pad); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cifail: 1548c2ecf20Sopenharmony_ci input_free_device(idev); 1558c2ecf20Sopenharmony_ci kfree(pad); 1568c2ecf20Sopenharmony_ci maple_set_drvdata(mdev, NULL); 1578c2ecf20Sopenharmony_ci return error; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int remove_maple_controller(struct device *dev) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct maple_device *mdev = to_maple_dev(dev); 1638c2ecf20Sopenharmony_ci struct dc_pad *pad = maple_get_drvdata(mdev); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mdev->callback = NULL; 1668c2ecf20Sopenharmony_ci input_unregister_device(pad->dev); 1678c2ecf20Sopenharmony_ci maple_set_drvdata(mdev, NULL); 1688c2ecf20Sopenharmony_ci kfree(pad); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic struct maple_driver dc_pad_driver = { 1748c2ecf20Sopenharmony_ci .function = MAPLE_FUNC_CONTROLLER, 1758c2ecf20Sopenharmony_ci .drv = { 1768c2ecf20Sopenharmony_ci .name = "Dreamcast_controller", 1778c2ecf20Sopenharmony_ci .probe = probe_maple_controller, 1788c2ecf20Sopenharmony_ci .remove = remove_maple_controller, 1798c2ecf20Sopenharmony_ci }, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int __init dc_pad_init(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci return maple_driver_register(&dc_pad_driver); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void __exit dc_pad_exit(void) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci maple_driver_unregister(&dc_pad_driver); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cimodule_init(dc_pad_init); 1938c2ecf20Sopenharmony_cimodule_exit(dc_pad_exit); 194