18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * driver.c - device id matching, driver model, etc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/string.h>
98c2ecf20Sopenharmony_ci#include <linux/list.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/ctype.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/pnp.h>
148c2ecf20Sopenharmony_ci#include "base.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic int compare_func(const char *ida, const char *idb)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	int i;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	/* we only need to compare the last 4 chars */
218c2ecf20Sopenharmony_ci	for (i = 3; i < 7; i++) {
228c2ecf20Sopenharmony_ci		if (ida[i] != 'X' &&
238c2ecf20Sopenharmony_ci		    idb[i] != 'X' && toupper(ida[i]) != toupper(idb[i]))
248c2ecf20Sopenharmony_ci			return 0;
258c2ecf20Sopenharmony_ci	}
268c2ecf20Sopenharmony_ci	return 1;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciint compare_pnp_id(struct pnp_id *pos, const char *id)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	if (!pos || !id || (strlen(id) != 7))
328c2ecf20Sopenharmony_ci		return 0;
338c2ecf20Sopenharmony_ci	if (memcmp(id, "ANYDEVS", 7) == 0)
348c2ecf20Sopenharmony_ci		return 1;
358c2ecf20Sopenharmony_ci	while (pos) {
368c2ecf20Sopenharmony_ci		if (memcmp(pos->id, id, 3) == 0)
378c2ecf20Sopenharmony_ci			if (compare_func(pos->id, id) == 1)
388c2ecf20Sopenharmony_ci				return 1;
398c2ecf20Sopenharmony_ci		pos = pos->next;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic const struct pnp_device_id *match_device(struct pnp_driver *drv,
458c2ecf20Sopenharmony_ci						struct pnp_dev *dev)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	const struct pnp_device_id *drv_id = drv->id_table;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (!drv_id)
508c2ecf20Sopenharmony_ci		return NULL;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	while (*drv_id->id) {
538c2ecf20Sopenharmony_ci		if (compare_pnp_id(dev->id, drv_id->id))
548c2ecf20Sopenharmony_ci			return drv_id;
558c2ecf20Sopenharmony_ci		drv_id++;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	return NULL;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint pnp_device_attach(struct pnp_dev *pnp_dev)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
638c2ecf20Sopenharmony_ci	if (pnp_dev->status != PNP_READY) {
648c2ecf20Sopenharmony_ci		mutex_unlock(&pnp_lock);
658c2ecf20Sopenharmony_ci		return -EBUSY;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci	pnp_dev->status = PNP_ATTACHED;
688c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_civoid pnp_device_detach(struct pnp_dev *pnp_dev)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	mutex_lock(&pnp_lock);
758c2ecf20Sopenharmony_ci	if (pnp_dev->status == PNP_ATTACHED)
768c2ecf20Sopenharmony_ci		pnp_dev->status = PNP_READY;
778c2ecf20Sopenharmony_ci	mutex_unlock(&pnp_lock);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int pnp_device_probe(struct device *dev)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	int error;
838c2ecf20Sopenharmony_ci	struct pnp_driver *pnp_drv;
848c2ecf20Sopenharmony_ci	struct pnp_dev *pnp_dev;
858c2ecf20Sopenharmony_ci	const struct pnp_device_id *dev_id = NULL;
868c2ecf20Sopenharmony_ci	pnp_dev = to_pnp_dev(dev);
878c2ecf20Sopenharmony_ci	pnp_drv = to_pnp_driver(dev->driver);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	error = pnp_device_attach(pnp_dev);
908c2ecf20Sopenharmony_ci	if (error < 0)
918c2ecf20Sopenharmony_ci		return error;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (pnp_dev->active == 0) {
948c2ecf20Sopenharmony_ci		if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) {
958c2ecf20Sopenharmony_ci			error = pnp_activate_dev(pnp_dev);
968c2ecf20Sopenharmony_ci			if (error < 0)
978c2ecf20Sopenharmony_ci				return error;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci	} else if ((pnp_drv->flags & PNP_DRIVER_RES_DISABLE)
1008c2ecf20Sopenharmony_ci		   == PNP_DRIVER_RES_DISABLE) {
1018c2ecf20Sopenharmony_ci		error = pnp_disable_dev(pnp_dev);
1028c2ecf20Sopenharmony_ci		if (error < 0)
1038c2ecf20Sopenharmony_ci			return error;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	error = 0;
1068c2ecf20Sopenharmony_ci	if (pnp_drv->probe) {
1078c2ecf20Sopenharmony_ci		dev_id = match_device(pnp_drv, pnp_dev);
1088c2ecf20Sopenharmony_ci		if (dev_id != NULL)
1098c2ecf20Sopenharmony_ci			error = pnp_drv->probe(pnp_dev, dev_id);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	if (error >= 0) {
1128c2ecf20Sopenharmony_ci		pnp_dev->driver = pnp_drv;
1138c2ecf20Sopenharmony_ci		error = 0;
1148c2ecf20Sopenharmony_ci	} else
1158c2ecf20Sopenharmony_ci		goto fail;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return error;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cifail:
1208c2ecf20Sopenharmony_ci	pnp_device_detach(pnp_dev);
1218c2ecf20Sopenharmony_ci	return error;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int pnp_device_remove(struct device *dev)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
1278c2ecf20Sopenharmony_ci	struct pnp_driver *drv = pnp_dev->driver;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (drv) {
1308c2ecf20Sopenharmony_ci		if (drv->remove)
1318c2ecf20Sopenharmony_ci			drv->remove(pnp_dev);
1328c2ecf20Sopenharmony_ci		pnp_dev->driver = NULL;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (pnp_dev->active &&
1368c2ecf20Sopenharmony_ci	    (!drv || !(drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)))
1378c2ecf20Sopenharmony_ci		pnp_disable_dev(pnp_dev);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	pnp_device_detach(pnp_dev);
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void pnp_device_shutdown(struct device *dev)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
1468c2ecf20Sopenharmony_ci	struct pnp_driver *drv = pnp_dev->driver;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (drv && drv->shutdown)
1498c2ecf20Sopenharmony_ci		drv->shutdown(pnp_dev);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int pnp_bus_match(struct device *dev, struct device_driver *drv)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
1558c2ecf20Sopenharmony_ci	struct pnp_driver *pnp_drv = to_pnp_driver(drv);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (match_device(pnp_drv, pnp_dev) == NULL)
1588c2ecf20Sopenharmony_ci		return 0;
1598c2ecf20Sopenharmony_ci	return 1;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int __pnp_bus_suspend(struct device *dev, pm_message_t state)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
1658c2ecf20Sopenharmony_ci	struct pnp_driver *pnp_drv = pnp_dev->driver;
1668c2ecf20Sopenharmony_ci	int error;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (!pnp_drv)
1698c2ecf20Sopenharmony_ci		return 0;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (pnp_drv->driver.pm && pnp_drv->driver.pm->suspend) {
1728c2ecf20Sopenharmony_ci		error = pnp_drv->driver.pm->suspend(dev);
1738c2ecf20Sopenharmony_ci		suspend_report_result(pnp_drv->driver.pm->suspend, error);
1748c2ecf20Sopenharmony_ci		if (error)
1758c2ecf20Sopenharmony_ci			return error;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (pnp_drv->suspend) {
1798c2ecf20Sopenharmony_ci		error = pnp_drv->suspend(pnp_dev, state);
1808c2ecf20Sopenharmony_ci		if (error)
1818c2ecf20Sopenharmony_ci			return error;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (pnp_can_disable(pnp_dev)) {
1858c2ecf20Sopenharmony_ci		error = pnp_stop_dev(pnp_dev);
1868c2ecf20Sopenharmony_ci		if (error)
1878c2ecf20Sopenharmony_ci			return error;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (pnp_can_suspend(pnp_dev))
1918c2ecf20Sopenharmony_ci		pnp_dev->protocol->suspend(pnp_dev, state);
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int pnp_bus_suspend(struct device *dev)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	return __pnp_bus_suspend(dev, PMSG_SUSPEND);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int pnp_bus_freeze(struct device *dev)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	return __pnp_bus_suspend(dev, PMSG_FREEZE);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int pnp_bus_poweroff(struct device *dev)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	return __pnp_bus_suspend(dev, PMSG_HIBERNATE);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int pnp_bus_resume(struct device *dev)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
2138c2ecf20Sopenharmony_ci	struct pnp_driver *pnp_drv = pnp_dev->driver;
2148c2ecf20Sopenharmony_ci	int error;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (!pnp_drv)
2178c2ecf20Sopenharmony_ci		return 0;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (pnp_dev->protocol->resume) {
2208c2ecf20Sopenharmony_ci		error = pnp_dev->protocol->resume(pnp_dev);
2218c2ecf20Sopenharmony_ci		if (error)
2228c2ecf20Sopenharmony_ci			return error;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (pnp_can_write(pnp_dev)) {
2268c2ecf20Sopenharmony_ci		error = pnp_start_dev(pnp_dev);
2278c2ecf20Sopenharmony_ci		if (error)
2288c2ecf20Sopenharmony_ci			return error;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (pnp_drv->driver.pm && pnp_drv->driver.pm->resume) {
2328c2ecf20Sopenharmony_ci		error = pnp_drv->driver.pm->resume(dev);
2338c2ecf20Sopenharmony_ci		if (error)
2348c2ecf20Sopenharmony_ci			return error;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (pnp_drv->resume) {
2388c2ecf20Sopenharmony_ci		error = pnp_drv->resume(pnp_dev);
2398c2ecf20Sopenharmony_ci		if (error)
2408c2ecf20Sopenharmony_ci			return error;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pnp_bus_dev_pm_ops = {
2478c2ecf20Sopenharmony_ci	/* Suspend callbacks */
2488c2ecf20Sopenharmony_ci	.suspend = pnp_bus_suspend,
2498c2ecf20Sopenharmony_ci	.resume = pnp_bus_resume,
2508c2ecf20Sopenharmony_ci	/* Hibernate callbacks */
2518c2ecf20Sopenharmony_ci	.freeze = pnp_bus_freeze,
2528c2ecf20Sopenharmony_ci	.thaw = pnp_bus_resume,
2538c2ecf20Sopenharmony_ci	.poweroff = pnp_bus_poweroff,
2548c2ecf20Sopenharmony_ci	.restore = pnp_bus_resume,
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistruct bus_type pnp_bus_type = {
2588c2ecf20Sopenharmony_ci	.name    = "pnp",
2598c2ecf20Sopenharmony_ci	.match   = pnp_bus_match,
2608c2ecf20Sopenharmony_ci	.probe   = pnp_device_probe,
2618c2ecf20Sopenharmony_ci	.remove  = pnp_device_remove,
2628c2ecf20Sopenharmony_ci	.shutdown = pnp_device_shutdown,
2638c2ecf20Sopenharmony_ci	.pm	 = &pnp_bus_dev_pm_ops,
2648c2ecf20Sopenharmony_ci	.dev_groups = pnp_dev_groups,
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciint pnp_register_driver(struct pnp_driver *drv)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	drv->driver.name = drv->name;
2708c2ecf20Sopenharmony_ci	drv->driver.bus = &pnp_bus_type;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return driver_register(&drv->driver);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_civoid pnp_unregister_driver(struct pnp_driver *drv)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	driver_unregister(&drv->driver);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/**
2818c2ecf20Sopenharmony_ci * pnp_add_id - adds an EISA id to the specified device
2828c2ecf20Sopenharmony_ci * @dev: pointer to the desired device
2838c2ecf20Sopenharmony_ci * @id: pointer to an EISA id string
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_cistruct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct pnp_id *dev_id, *ptr;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
2908c2ecf20Sopenharmony_ci	if (!dev_id)
2918c2ecf20Sopenharmony_ci		return NULL;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	dev_id->id[0] = id[0];
2948c2ecf20Sopenharmony_ci	dev_id->id[1] = id[1];
2958c2ecf20Sopenharmony_ci	dev_id->id[2] = id[2];
2968c2ecf20Sopenharmony_ci	dev_id->id[3] = tolower(id[3]);
2978c2ecf20Sopenharmony_ci	dev_id->id[4] = tolower(id[4]);
2988c2ecf20Sopenharmony_ci	dev_id->id[5] = tolower(id[5]);
2998c2ecf20Sopenharmony_ci	dev_id->id[6] = tolower(id[6]);
3008c2ecf20Sopenharmony_ci	dev_id->id[7] = '\0';
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	dev_id->next = NULL;
3038c2ecf20Sopenharmony_ci	ptr = dev->id;
3048c2ecf20Sopenharmony_ci	while (ptr && ptr->next)
3058c2ecf20Sopenharmony_ci		ptr = ptr->next;
3068c2ecf20Sopenharmony_ci	if (ptr)
3078c2ecf20Sopenharmony_ci		ptr->next = dev_id;
3088c2ecf20Sopenharmony_ci	else
3098c2ecf20Sopenharmony_ci		dev->id = dev_id;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return dev_id;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_register_driver);
3158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_unregister_driver);
3168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_device_attach);
3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_device_detach);
318