drivers/input/misc/pcf8574_keypad.c

Source file repositories/reference/linux-study-clean/drivers/input/misc/pcf8574_keypad.c

File Facts

System
Linux kernel
Corpus path
drivers/input/misc/pcf8574_keypad.c
Extension
.c
Size
4466 bytes
Lines
212
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 kp_data {
	unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
	struct input_dev *idev;
	struct i2c_client *client;
	char name[64];
	char phys[32];
	unsigned char laststate;
};

static short read_state(struct kp_data *lp)
{
	unsigned char x, y, a, b;

	i2c_smbus_write_byte(lp->client, 240);
	x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));

	i2c_smbus_write_byte(lp->client, 15);
	y = 0xF & (~i2c_smbus_read_byte(lp->client));

	for (a = 0; x > 0; a++)
		x = x >> 1;
	for (b = 0; y > 0; b++)
		y = y >> 1;

	return ((a - 1) * 4) + b;
}

static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
{
	struct kp_data *lp = dev_id;
	unsigned char nextstate = read_state(lp);

	if (lp->laststate != nextstate) {
		int key_down = nextstate < ARRAY_SIZE(lp->btncode);
		unsigned short keycode = key_down ?
			lp->btncode[nextstate] : lp->btncode[lp->laststate];

		input_report_key(lp->idev, keycode, key_down);
		input_sync(lp->idev);

		lp->laststate = nextstate;
	}

	return IRQ_HANDLED;
}

static int pcf8574_kp_probe(struct i2c_client *client)
{
	int i, ret;
	struct input_dev *idev;
	struct kp_data *lp;

	if (i2c_smbus_write_byte(client, 240) < 0) {
		dev_err(&client->dev, "probe: write fail\n");
		return -ENODEV;
	}

	lp = kzalloc_obj(*lp);
	if (!lp)
		return -ENOMEM;

	idev = input_allocate_device();
	if (!idev) {
		dev_err(&client->dev, "Can't allocate input device\n");
		ret = -ENOMEM;
		goto fail_allocate;
	}

	lp->idev = idev;
	lp->client = client;

	idev->evbit[0] = BIT_MASK(EV_KEY);
	idev->keycode = lp->btncode;
	idev->keycodesize = sizeof(lp->btncode[0]);
	idev->keycodemax = ARRAY_SIZE(lp->btncode);

	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
		if (lp->btncode[i] <= KEY_MAX) {
			lp->btncode[i] = pcf8574_kp_btncode[i];
			__set_bit(lp->btncode[i], idev->keybit);
		}
	}
	__clear_bit(KEY_RESERVED, idev->keybit);

	sprintf(lp->name, DRV_NAME);
	sprintf(lp->phys, "kp_data/input0");

	idev->name = lp->name;
	idev->phys = lp->phys;
	idev->id.bustype = BUS_I2C;

Annotation

Implementation Notes