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