Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Stephen Chandler Paul | 931 | 63.46% | 1 | 8.33% |
Dmitry Torokhov | 160 | 10.91% | 3 | 25.00% |
Andrew Duggan | 159 | 10.84% | 2 | 16.67% |
Benjamin Tissoires | 138 | 9.41% | 2 | 16.67% |
Dennis Wassenberg | 59 | 4.02% | 1 | 8.33% |
Nick Dyer | 17 | 1.16% | 1 | 8.33% |
Thomas Gleixner | 2 | 0.14% | 1 | 8.33% |
Wolfram Sang | 1 | 0.07% | 1 | 8.33% |
Total | 1467 | 12 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015-2016 Red Hat * Copyright (C) 2015 Lyude Paul <thatslyude@gmail.com> */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/serio.h> #include <linux/notifier.h> #include "rmi_driver.h" #define RMI_F03_RX_DATA_OFB 0x01 #define RMI_F03_OB_SIZE 2 #define RMI_F03_OB_OFFSET 2 #define RMI_F03_OB_DATA_OFFSET 1 #define RMI_F03_OB_FLAG_TIMEOUT BIT(6) #define RMI_F03_OB_FLAG_PARITY BIT(7) #define RMI_F03_DEVICE_COUNT 0x07 #define RMI_F03_BYTES_PER_DEVICE 0x07 #define RMI_F03_BYTES_PER_DEVICE_SHIFT 4 #define RMI_F03_QUEUE_LENGTH 0x0F #define PSMOUSE_OOB_EXTRA_BTNS 0x01 struct f03_data { struct rmi_function *fn; struct serio *serio; bool serio_registered; unsigned int overwrite_buttons; u8 device_count; u8 rx_queue_length; }; int rmi_f03_overwrite_button(struct rmi_function *fn, unsigned int button, int value) { struct f03_data *f03 = dev_get_drvdata(&fn->dev); unsigned int bit; if (button < BTN_LEFT || button > BTN_MIDDLE) return -EINVAL; bit = BIT(button - BTN_LEFT); if (value) f03->overwrite_buttons |= bit; else f03->overwrite_buttons &= ~bit; return 0; } void rmi_f03_commit_buttons(struct rmi_function *fn) { struct f03_data *f03 = dev_get_drvdata(&fn->dev); struct serio *serio = f03->serio; serio_pause_rx(serio); if (serio->drv) { serio->drv->interrupt(serio, PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA); serio->drv->interrupt(serio, f03->overwrite_buttons, SERIO_OOB_DATA); } serio_continue_rx(serio); } static int rmi_f03_pt_write(struct serio *id, unsigned char val) { struct f03_data *f03 = id->port_data; int error; rmi_dbg(RMI_DEBUG_FN, &f03->fn->dev, "%s: Wrote %.2hhx to PS/2 passthrough address", __func__, val); error = rmi_write(f03->fn->rmi_dev, f03->fn->fd.data_base_addr, val); if (error) { dev_err(&f03->fn->dev, "%s: Failed to write to F03 TX register (%d).\n", __func__, error); return error; } return 0; } static int rmi_f03_initialize(struct f03_data *f03) { struct rmi_function *fn = f03->fn; struct device *dev = &fn->dev; int error; u8 bytes_per_device; u8 query1; u8 query2[RMI_F03_DEVICE_COUNT * RMI_F03_BYTES_PER_DEVICE]; size_t query2_len; error = rmi_read(fn->rmi_dev, fn->fd.query_base_addr, &query1); if (error) { dev_err(dev, "Failed to read query register (%d).\n", error); return error; } f03->device_count = query1 & RMI_F03_DEVICE_COUNT; bytes_per_device = (query1 >> RMI_F03_BYTES_PER_DEVICE_SHIFT) & RMI_F03_BYTES_PER_DEVICE; query2_len = f03->device_count * bytes_per_device; /* * The first generation of image sensors don't have a second part to * their f03 query, as such we have to set some of these values manually */ if (query2_len < 1) { f03->device_count = 1; f03->rx_queue_length = 7; } else { error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 1, query2, query2_len); if (error) { dev_err(dev, "Failed to read second set of query registers (%d).\n", error); return error; } f03->rx_queue_length = query2[0] & RMI_F03_QUEUE_LENGTH; } return 0; } static int rmi_f03_pt_open(struct serio *serio) { struct f03_data *f03 = serio->port_data; struct rmi_function *fn = f03->fn; const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; const u16 data_addr = fn->fd.data_base_addr + RMI_F03_OB_OFFSET; u8 obs[RMI_F03_QUEUE_LENGTH * RMI_F03_OB_SIZE]; int error; /* * Consume any pending data. Some devices like to spam with * 0xaa 0x00 announcements which may confuse us as we try to * probe the device. */ error = rmi_read_block(fn->rmi_dev, data_addr, &obs, ob_len); if (!error) rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: Consumed %*ph (%d) from PS2 guest\n", __func__, ob_len, obs, ob_len); return fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask); } static void rmi_f03_pt_close(struct serio *serio) { struct f03_data *f03 = serio->port_data; struct rmi_function *fn = f03->fn; fn->rmi_dev->driver->clear_irq_bits(fn->rmi_dev, fn->irq_mask); } static int rmi_f03_register_pt(struct f03_data *f03) { struct serio *serio; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!serio) return -ENOMEM; serio->id.type = SERIO_PS_PSTHRU; serio->write = rmi_f03_pt_write; serio->open = rmi_f03_pt_open; serio->close = rmi_f03_pt_close; serio->port_data = f03; strscpy(serio->name, "RMI4 PS/2 pass-through", sizeof(serio->name)); snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", dev_name(&f03->fn->dev)); serio->dev.parent = &f03->fn->dev; f03->serio = serio; printk(KERN_INFO "serio: %s port at %s\n", serio->name, dev_name(&f03->fn->dev)); serio_register_port(serio); return 0; } static int rmi_f03_probe(struct rmi_function *fn) { struct device *dev = &fn->dev; struct f03_data *f03; int error; f03 = devm_kzalloc(dev, sizeof(struct f03_data), GFP_KERNEL); if (!f03) return -ENOMEM; f03->fn = fn; error = rmi_f03_initialize(f03); if (error < 0) return error; if (f03->device_count != 1) dev_warn(dev, "found %d devices on PS/2 passthrough", f03->device_count); dev_set_drvdata(dev, f03); return 0; } static int rmi_f03_config(struct rmi_function *fn) { struct f03_data *f03 = dev_get_drvdata(&fn->dev); int error; if (!f03->serio_registered) { error = rmi_f03_register_pt(f03); if (error) return error; f03->serio_registered = true; } else { /* * We must be re-configuring the sensor, just enable * interrupts for this function. */ fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask); } return 0; } static irqreturn_t rmi_f03_attention(int irq, void *ctx) { struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f03_data *f03 = dev_get_drvdata(&fn->dev); const u16 data_addr = fn->fd.data_base_addr + RMI_F03_OB_OFFSET; const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; u8 obs[RMI_F03_QUEUE_LENGTH * RMI_F03_OB_SIZE]; u8 ob_status; u8 ob_data; unsigned int serio_flags; int i; int error; if (drvdata->attn_data.data) { /* First grab the data passed by the transport device */ if (drvdata->attn_data.size < ob_len) { dev_warn(&fn->dev, "F03 interrupted, but data is missing!\n"); return IRQ_HANDLED; } memcpy(obs, drvdata->attn_data.data, ob_len); drvdata->attn_data.data += ob_len; drvdata->attn_data.size -= ob_len; } else { /* Grab all of the data registers, and check them for data */ error = rmi_read_block(fn->rmi_dev, data_addr, &obs, ob_len); if (error) { dev_err(&fn->dev, "%s: Failed to read F03 output buffers: %d\n", __func__, error); serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); return IRQ_RETVAL(error); } } for (i = 0; i < ob_len; i += RMI_F03_OB_SIZE) { ob_status = obs[i]; ob_data = obs[i + RMI_F03_OB_DATA_OFFSET]; serio_flags = 0; if (!(ob_status & RMI_F03_RX_DATA_OFB)) continue; if (ob_status & RMI_F03_OB_FLAG_TIMEOUT) serio_flags |= SERIO_TIMEOUT; if (ob_status & RMI_F03_OB_FLAG_PARITY) serio_flags |= SERIO_PARITY; rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: Received %.2hhx from PS2 guest T: %c P: %c\n", __func__, ob_data, serio_flags & SERIO_TIMEOUT ? 'Y' : 'N', serio_flags & SERIO_PARITY ? 'Y' : 'N'); serio_interrupt(f03->serio, ob_data, serio_flags); } return IRQ_HANDLED; } static void rmi_f03_remove(struct rmi_function *fn) { struct f03_data *f03 = dev_get_drvdata(&fn->dev); if (f03->serio_registered) serio_unregister_port(f03->serio); } struct rmi_function_handler rmi_f03_handler = { .driver = { .name = "rmi4_f03", }, .func = 0x03, .probe = rmi_f03_probe, .config = rmi_f03_config, .attention = rmi_f03_attention, .remove = rmi_f03_remove, }; MODULE_AUTHOR("Lyude Paul <thatslyude@gmail.com>"); MODULE_DESCRIPTION("RMI F03 module"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1