drivers/i2c/i2c-slave-testunit.c

Source file repositories/reference/linux-study-clean/drivers/i2c/i2c-slave-testunit.c

File Facts

System
Linux kernel
Corpus path
drivers/i2c/i2c-slave-testunit.c
Extension
.c
Size
7021 bytes
Lines
291
Domain
Driver Families
Bucket
drivers/i2c
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 testunit_data {
	unsigned long flags;
	u8 regs[TU_NUM_REGS];
	u8 reg_idx;
	u8 read_idx;
	struct i2c_client *client;
	struct delayed_work worker;
	struct gpio_desc *gpio;
	struct completion alert_done;
};

static char tu_version_info[] = "v" UTS_RELEASE "\n\0";

static int i2c_slave_testunit_smbalert_cb(struct i2c_client *client,
					  enum i2c_slave_event event, u8 *val)
{
	struct testunit_data *tu = i2c_get_clientdata(client);

	switch (event) {
	case I2C_SLAVE_READ_PROCESSED:
		gpiod_set_value(tu->gpio, 0);
		fallthrough;
	case I2C_SLAVE_READ_REQUESTED:
		*val = tu->regs[TU_REG_DATAL];
		break;

	case I2C_SLAVE_STOP:
		complete(&tu->alert_done);
		break;

	case I2C_SLAVE_WRITE_REQUESTED:
	case I2C_SLAVE_WRITE_RECEIVED:
		return -EOPNOTSUPP;
	}

	return 0;
}

static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
				     enum i2c_slave_event event, u8 *val)
{
	struct testunit_data *tu = i2c_get_clientdata(client);
	bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 &&
			    tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL;
	bool is_get_version = tu->reg_idx == 3 &&
			      tu->regs[TU_REG_CMD] == TU_CMD_GET_VERSION_WITH_REP_START;
	int ret = 0;

	switch (event) {
	case I2C_SLAVE_WRITE_REQUESTED:
		if (test_bit(TU_FLAG_IN_PROCESS | TU_FLAG_NACK, &tu->flags)) {
			ret = -EBUSY;
			break;
		}

		memset(tu->regs, 0, TU_NUM_REGS);
		tu->reg_idx = 0;
		tu->read_idx = 0;
		break;

	case I2C_SLAVE_WRITE_RECEIVED:
		if (test_bit(TU_FLAG_IN_PROCESS | TU_FLAG_NACK, &tu->flags)) {
			ret = -EBUSY;
			break;
		}

		if (tu->reg_idx < TU_NUM_REGS)
			tu->regs[tu->reg_idx] = *val;
		else
			ret = -EMSGSIZE;

		if (tu->reg_idx <= TU_NUM_REGS)
			tu->reg_idx++;

		/* TU_REG_CMD always written at this point */
		if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS)
			ret = -EINVAL;

		break;

	case I2C_SLAVE_STOP:
		if (tu->reg_idx == TU_NUM_REGS) {
			set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
			queue_delayed_work(system_dfl_long_wq, &tu->worker,
					   msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
		}

		/*
		 * Reset reg_idx to avoid that work gets queued again in case of
		 * STOP after a following read message. But do not clear TU regs

Annotation

Implementation Notes