drivers/input/mouse/psmouse-smbus.c

Source file repositories/reference/linux-study-clean/drivers/input/mouse/psmouse-smbus.c

File Facts

System
Linux kernel
Corpus path
drivers/input/mouse/psmouse-smbus.c
Extension
.c
Size
7954 bytes
Lines
321
Domain
Driver Families
Bucket
drivers/input
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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 psmouse_smbus_dev {
	struct i2c_board_info board;
	struct psmouse *psmouse;
	struct i2c_client *client;
	struct list_head node;
	bool dead;
	bool need_deactivate;
};

static LIST_HEAD(psmouse_smbus_list);
static DEFINE_MUTEX(psmouse_smbus_mutex);

static struct workqueue_struct *psmouse_smbus_wq;

static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
{
	struct psmouse_smbus_dev *smbdev;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
		return;

	guard(mutex)(&psmouse_smbus_mutex);

	list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
		if (smbdev->dead)
			continue;

		if (smbdev->client)
			continue;

		/*
		 * Here would be a good place to check if device is actually
		 * present, but it seems that SMBus will not respond unless we
		 * fully reset PS/2 connection.  So cross our fingers, and try
		 * to switch over, hopefully our system will not have too many
		 * "host notify" I2C adapters.
		 */
		psmouse_dbg(smbdev->psmouse,
			    "SMBus candidate adapter appeared, triggering rescan\n");
		serio_rescan(smbdev->psmouse->ps2dev.serio);
	}
}

static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
{
	struct psmouse_smbus_dev *smbdev, *tmp;

	guard(mutex)(&psmouse_smbus_mutex);

	list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
		if (smbdev->client != client)
			continue;

		kfree(client->dev.platform_data);
		client->dev.platform_data = NULL;

		if (!smbdev->dead) {
			psmouse_dbg(smbdev->psmouse,
				    "Marking SMBus companion %s as gone\n",
				    dev_name(&smbdev->client->dev));
			smbdev->dead = true;
			device_link_remove(&smbdev->client->dev,
					   &smbdev->psmouse->ps2dev.serio->dev);
			serio_rescan(smbdev->psmouse->ps2dev.serio);
		} else {
			list_del(&smbdev->node);
			kfree(smbdev);
		}
	}
}

static int psmouse_smbus_notifier_call(struct notifier_block *nb,
				       unsigned long action, void *data)
{
	struct device *dev = data;

	switch (action) {
	case BUS_NOTIFY_ADD_DEVICE:
		if (dev->type == &i2c_adapter_type)
			psmouse_smbus_check_adapter(to_i2c_adapter(dev));
		break;

	case BUS_NOTIFY_REMOVED_DEVICE:
		if (dev->type == &i2c_client_type)
			psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
		break;
	}

	return 0;
}

Annotation

Implementation Notes