drivers/input/serio/ps2-gpio.c

Source file repositories/reference/linux-study-clean/drivers/input/serio/ps2-gpio.c

File Facts

System
Linux kernel
Corpus path
drivers/input/serio/ps2-gpio.c
Extension
.c
Size
12509 bytes
Lines
505
Domain
Driver Families
Bucket
drivers/input
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct ps2_gpio_data {
	struct device *dev;
	struct serio *serio;
	unsigned char mode;
	struct gpio_desc *gpio_clk;
	struct gpio_desc *gpio_data;
	bool write_enable;
	int irq;
	ktime_t t_irq_now;
	ktime_t t_irq_last;
	struct {
		unsigned char cnt;
		unsigned char byte;
	} rx;
	struct {
		unsigned char cnt;
		unsigned char byte;
		ktime_t t_xfer_start;
		ktime_t t_xfer_end;
		struct completion complete;
		struct mutex mutex;
		struct delayed_work work;
	} tx;
};

static int ps2_gpio_open(struct serio *serio)
{
	struct ps2_gpio_data *drvdata = serio->port_data;

	drvdata->t_irq_last = 0;
	drvdata->tx.t_xfer_end = 0;

	enable_irq(drvdata->irq);
	return 0;
}

static void ps2_gpio_close(struct serio *serio)
{
	struct ps2_gpio_data *drvdata = serio->port_data;

	flush_delayed_work(&drvdata->tx.work);
	disable_irq(drvdata->irq);
}

static int __ps2_gpio_write(struct serio *serio, unsigned char val)
{
	struct ps2_gpio_data *drvdata = serio->port_data;

	disable_irq_nosync(drvdata->irq);
	gpiod_direction_output(drvdata->gpio_clk, 0);

	drvdata->mode = PS2_MODE_TX;
	drvdata->tx.byte = val;

	schedule_delayed_work(&drvdata->tx.work, usecs_to_jiffies(200));

	return 0;
}

static int ps2_gpio_write(struct serio *serio, unsigned char val)
{
	struct ps2_gpio_data *drvdata = serio->port_data;
	int ret = 0;

	if (in_task()) {
		guard(mutex)(&drvdata->tx.mutex);

		__ps2_gpio_write(serio, val);
		if (!wait_for_completion_timeout(&drvdata->tx.complete,
						 msecs_to_jiffies(10000)))
			ret = SERIO_TIMEOUT;
	} else {
		__ps2_gpio_write(serio, val);
	}

	return ret;
}

static void ps2_gpio_tx_work_fn(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct ps2_gpio_data *drvdata = container_of(dwork,
						     struct ps2_gpio_data,
						     tx.work);

	drvdata->tx.t_xfer_start = ktime_get();
	enable_irq(drvdata->irq);
	gpiod_direction_output(drvdata->gpio_data, 0);
	gpiod_direction_input(drvdata->gpio_clk);
}

Annotation

Implementation Notes