18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 1999-2001 Vojtech Pavlik 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on the work of: 68c2ecf20Sopenharmony_ci * James Banks Matthew Dillon 78c2ecf20Sopenharmony_ci * David Giller Nathan Laredo 88c2ecf20Sopenharmony_ci * Linus Torvalds Johan Myreen 98c2ecf20Sopenharmony_ci * Cliff Matthews Philip Blundell 108c2ecf20Sopenharmony_ci * Russell King 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * Logitech Bus Mouse Driver for Linux 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/ioport.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/input.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/io.h> 288c2ecf20Sopenharmony_ci#include <asm/irq.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Logitech busmouse driver"); 328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define LOGIBM_BASE 0x23c 358c2ecf20Sopenharmony_ci#define LOGIBM_EXTENT 4 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define LOGIBM_DATA_PORT LOGIBM_BASE + 0 388c2ecf20Sopenharmony_ci#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1 398c2ecf20Sopenharmony_ci#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2 408c2ecf20Sopenharmony_ci#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define LOGIBM_ENABLE_IRQ 0x00 438c2ecf20Sopenharmony_ci#define LOGIBM_DISABLE_IRQ 0x10 448c2ecf20Sopenharmony_ci#define LOGIBM_READ_X_LOW 0x80 458c2ecf20Sopenharmony_ci#define LOGIBM_READ_X_HIGH 0xa0 468c2ecf20Sopenharmony_ci#define LOGIBM_READ_Y_LOW 0xc0 478c2ecf20Sopenharmony_ci#define LOGIBM_READ_Y_HIGH 0xe0 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define LOGIBM_DEFAULT_MODE 0x90 508c2ecf20Sopenharmony_ci#define LOGIBM_CONFIG_BYTE 0x91 518c2ecf20Sopenharmony_ci#define LOGIBM_SIGNATURE_BYTE 0xa5 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define LOGIBM_IRQ 5 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int logibm_irq = LOGIBM_IRQ; 568c2ecf20Sopenharmony_cimodule_param_hw_named(irq, logibm_irq, uint, irq, 0); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ number (5=default)"); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct input_dev *logibm_dev; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic irqreturn_t logibm_interrupt(int irq, void *dev_id) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci char dx, dy; 648c2ecf20Sopenharmony_ci unsigned char buttons; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT); 678c2ecf20Sopenharmony_ci dx = (inb(LOGIBM_DATA_PORT) & 0xf); 688c2ecf20Sopenharmony_ci outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT); 698c2ecf20Sopenharmony_ci dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4; 708c2ecf20Sopenharmony_ci outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT); 718c2ecf20Sopenharmony_ci dy = (inb(LOGIBM_DATA_PORT) & 0xf); 728c2ecf20Sopenharmony_ci outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT); 738c2ecf20Sopenharmony_ci buttons = inb(LOGIBM_DATA_PORT); 748c2ecf20Sopenharmony_ci dy |= (buttons & 0xf) << 4; 758c2ecf20Sopenharmony_ci buttons = ~buttons >> 5; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci input_report_rel(logibm_dev, REL_X, dx); 788c2ecf20Sopenharmony_ci input_report_rel(logibm_dev, REL_Y, dy); 798c2ecf20Sopenharmony_ci input_report_key(logibm_dev, BTN_RIGHT, buttons & 1); 808c2ecf20Sopenharmony_ci input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2); 818c2ecf20Sopenharmony_ci input_report_key(logibm_dev, BTN_LEFT, buttons & 4); 828c2ecf20Sopenharmony_ci input_sync(logibm_dev); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); 858c2ecf20Sopenharmony_ci return IRQ_HANDLED; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int logibm_open(struct input_dev *dev) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { 918c2ecf20Sopenharmony_ci printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); 928c2ecf20Sopenharmony_ci return -EBUSY; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void logibm_close(struct input_dev *dev) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); 1018c2ecf20Sopenharmony_ci free_irq(logibm_irq, NULL); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int __init logibm_init(void) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int err; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) { 1098c2ecf20Sopenharmony_ci printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE); 1108c2ecf20Sopenharmony_ci return -EBUSY; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT); 1148c2ecf20Sopenharmony_ci outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT); 1158c2ecf20Sopenharmony_ci udelay(100); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) { 1188c2ecf20Sopenharmony_ci printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE); 1198c2ecf20Sopenharmony_ci err = -ENODEV; 1208c2ecf20Sopenharmony_ci goto err_release_region; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT); 1248c2ecf20Sopenharmony_ci outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci logibm_dev = input_allocate_device(); 1278c2ecf20Sopenharmony_ci if (!logibm_dev) { 1288c2ecf20Sopenharmony_ci printk(KERN_ERR "logibm.c: Not enough memory for input device\n"); 1298c2ecf20Sopenharmony_ci err = -ENOMEM; 1308c2ecf20Sopenharmony_ci goto err_release_region; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci logibm_dev->name = "Logitech bus mouse"; 1348c2ecf20Sopenharmony_ci logibm_dev->phys = "isa023c/input0"; 1358c2ecf20Sopenharmony_ci logibm_dev->id.bustype = BUS_ISA; 1368c2ecf20Sopenharmony_ci logibm_dev->id.vendor = 0x0003; 1378c2ecf20Sopenharmony_ci logibm_dev->id.product = 0x0001; 1388c2ecf20Sopenharmony_ci logibm_dev->id.version = 0x0100; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 1418c2ecf20Sopenharmony_ci logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | 1428c2ecf20Sopenharmony_ci BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); 1438c2ecf20Sopenharmony_ci logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci logibm_dev->open = logibm_open; 1468c2ecf20Sopenharmony_ci logibm_dev->close = logibm_close; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci err = input_register_device(logibm_dev); 1498c2ecf20Sopenharmony_ci if (err) 1508c2ecf20Sopenharmony_ci goto err_free_dev; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci err_free_dev: 1558c2ecf20Sopenharmony_ci input_free_device(logibm_dev); 1568c2ecf20Sopenharmony_ci err_release_region: 1578c2ecf20Sopenharmony_ci release_region(LOGIBM_BASE, LOGIBM_EXTENT); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return err; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void __exit logibm_exit(void) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci input_unregister_device(logibm_dev); 1658c2ecf20Sopenharmony_ci release_region(LOGIBM_BASE, LOGIBM_EXTENT); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cimodule_init(logibm_init); 1698c2ecf20Sopenharmony_cimodule_exit(logibm_exit); 170