162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * comedi_pcmcia.c
462306a36Sopenharmony_ci * Comedi PCMCIA driver specific functions.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * COMEDI - Linux Control and Measurement Device Interface
762306a36Sopenharmony_ci * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/comedi/comedi_pcmcia.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/**
1562306a36Sopenharmony_ci * comedi_to_pcmcia_dev() - Return PCMCIA device attached to COMEDI device
1662306a36Sopenharmony_ci * @dev: COMEDI device.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a
1962306a36Sopenharmony_ci * a &struct device embedded in a &struct pcmcia_device.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Return: Attached PCMCIA device if @dev->hw_dev is non-%NULL.
2262306a36Sopenharmony_ci * Return %NULL if @dev->hw_dev is %NULL.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_cistruct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *dev)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return dev->hw_dev ? to_pcmcia_dev(dev->hw_dev) : NULL;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_to_pcmcia_dev);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int comedi_pcmcia_conf_check(struct pcmcia_device *link,
3162306a36Sopenharmony_ci				    void *priv_data)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	if (link->config_index == 0)
3462306a36Sopenharmony_ci		return -EINVAL;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return pcmcia_request_io(link);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/**
4062306a36Sopenharmony_ci * comedi_pcmcia_enable() - Request the regions and enable the PCMCIA device
4162306a36Sopenharmony_ci * @dev: COMEDI device.
4262306a36Sopenharmony_ci * @conf_check: Optional callback to check each configuration option of the
4362306a36Sopenharmony_ci *	PCMCIA device and request I/O regions.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a a
4662306a36Sopenharmony_ci * &struct device embedded in a &struct pcmcia_device.  The comedi PCMCIA
4762306a36Sopenharmony_ci * driver needs to set the 'config_flags' member in the &struct pcmcia_device,
4862306a36Sopenharmony_ci * as appropriate for that driver, before calling this function in order to
4962306a36Sopenharmony_ci * allow pcmcia_loop_config() to do its internal autoconfiguration.
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * If @conf_check is %NULL it is set to a default function.  If is
5262306a36Sopenharmony_ci * passed to pcmcia_loop_config() and should return %0 if the configuration
5362306a36Sopenharmony_ci * is valid and I/O regions requested successfully, otherwise it should return
5462306a36Sopenharmony_ci * a negative error value.  The default function returns -%EINVAL if the
5562306a36Sopenharmony_ci * 'config_index' member is %0, otherwise it calls pcmcia_request_io() and
5662306a36Sopenharmony_ci * returns the result.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * If the above configuration check passes, pcmcia_enable_device() is called
5962306a36Sopenharmony_ci * to set up and activate the PCMCIA device.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * If this function returns an error, comedi_pcmcia_disable() should be called
6262306a36Sopenharmony_ci * to release requested resources.
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * Return:
6562306a36Sopenharmony_ci *	0 on success,
6662306a36Sopenharmony_ci *	-%ENODEV id @dev->hw_dev is %NULL,
6762306a36Sopenharmony_ci *	a negative error number from pcmcia_loop_config() if it fails,
6862306a36Sopenharmony_ci *	or a negative error number from pcmcia_enable_device() if it fails.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ciint comedi_pcmcia_enable(struct comedi_device *dev,
7162306a36Sopenharmony_ci			 int (*conf_check)(struct pcmcia_device *p_dev,
7262306a36Sopenharmony_ci					   void *priv_data))
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
7562306a36Sopenharmony_ci	int ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!link)
7862306a36Sopenharmony_ci		return -ENODEV;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!conf_check)
8162306a36Sopenharmony_ci		conf_check = comedi_pcmcia_conf_check;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	ret = pcmcia_loop_config(link, conf_check, NULL);
8462306a36Sopenharmony_ci	if (ret)
8562306a36Sopenharmony_ci		return ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return pcmcia_enable_device(link);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_pcmcia_enable);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/**
9262306a36Sopenharmony_ci * comedi_pcmcia_disable() - Disable the PCMCIA device and release the regions
9362306a36Sopenharmony_ci * @dev: COMEDI device.
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a
9662306a36Sopenharmony_ci * a &struct device embedded in a &struct pcmcia_device.  Call
9762306a36Sopenharmony_ci * pcmcia_disable_device() to disable and clean up the PCMCIA device.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_civoid comedi_pcmcia_disable(struct comedi_device *dev)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (link)
10462306a36Sopenharmony_ci		pcmcia_disable_device(link);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_pcmcia_disable);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/**
10962306a36Sopenharmony_ci * comedi_pcmcia_auto_config() - Configure/probe a PCMCIA COMEDI device
11062306a36Sopenharmony_ci * @link: PCMCIA device.
11162306a36Sopenharmony_ci * @driver: Registered COMEDI driver.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * Typically called from the pcmcia_driver (*probe) function.  Auto-configure
11462306a36Sopenharmony_ci * a COMEDI device, using a pointer to the &struct device embedded in *@link
11562306a36Sopenharmony_ci * as the hardware device.  The @driver's "auto_attach" handler may call
11662306a36Sopenharmony_ci * comedi_to_pcmcia_dev() on the passed in COMEDI device to recover @link.
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * Return: The result of calling comedi_auto_config() (0 on success, or a
11962306a36Sopenharmony_ci * negative error number on failure).
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ciint comedi_pcmcia_auto_config(struct pcmcia_device *link,
12262306a36Sopenharmony_ci			      struct comedi_driver *driver)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	return comedi_auto_config(&link->dev, driver, 0);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_pcmcia_auto_config);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/**
12962306a36Sopenharmony_ci * comedi_pcmcia_auto_unconfig() - Unconfigure/remove a PCMCIA COMEDI device
13062306a36Sopenharmony_ci * @link: PCMCIA device.
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * Typically called from the pcmcia_driver (*remove) function.
13362306a36Sopenharmony_ci * Auto-unconfigure a COMEDI device attached to this PCMCIA device, using a
13462306a36Sopenharmony_ci * pointer to the &struct device embedded in *@link as the hardware device.
13562306a36Sopenharmony_ci * The COMEDI driver's "detach" handler will be called during unconfiguration
13662306a36Sopenharmony_ci * of the COMEDI device.
13762306a36Sopenharmony_ci *
13862306a36Sopenharmony_ci * Note that the COMEDI device may have already been unconfigured using the
13962306a36Sopenharmony_ci * %COMEDI_DEVCONFIG ioctl, in which case this attempt to unconfigure it
14062306a36Sopenharmony_ci * again should be ignored.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_civoid comedi_pcmcia_auto_unconfig(struct pcmcia_device *link)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	comedi_auto_unconfig(&link->dev);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_pcmcia_auto_unconfig);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/**
14962306a36Sopenharmony_ci * comedi_pcmcia_driver_register() - Register a PCMCIA COMEDI driver
15062306a36Sopenharmony_ci * @comedi_driver: COMEDI driver to be registered.
15162306a36Sopenharmony_ci * @pcmcia_driver: PCMCIA driver to be registered.
15262306a36Sopenharmony_ci *
15362306a36Sopenharmony_ci * This function is used for the module_init() of PCMCIA COMEDI driver modules
15462306a36Sopenharmony_ci * to register the COMEDI driver and the PCMCIA driver.  Do not call it
15562306a36Sopenharmony_ci * directly, use the module_comedi_pcmcia_driver() helper macro instead.
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * Return: 0 on success, or a negative error number on failure.
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_ciint comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver,
16062306a36Sopenharmony_ci				  struct pcmcia_driver *pcmcia_driver)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int ret;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	ret = comedi_driver_register(comedi_driver);
16562306a36Sopenharmony_ci	if (ret < 0)
16662306a36Sopenharmony_ci		return ret;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ret = pcmcia_register_driver(pcmcia_driver);
16962306a36Sopenharmony_ci	if (ret < 0) {
17062306a36Sopenharmony_ci		comedi_driver_unregister(comedi_driver);
17162306a36Sopenharmony_ci		return ret;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_pcmcia_driver_register);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * comedi_pcmcia_driver_unregister() - Unregister a PCMCIA COMEDI driver
18062306a36Sopenharmony_ci * @comedi_driver: COMEDI driver to be registered.
18162306a36Sopenharmony_ci * @pcmcia_driver: PCMCIA driver to be registered.
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * This function is called from the module_exit() of PCMCIA COMEDI driver
18462306a36Sopenharmony_ci * modules to unregister the PCMCIA driver and the COMEDI driver.  Do not call
18562306a36Sopenharmony_ci * it directly, use the module_comedi_pcmcia_driver() helper macro instead.
18662306a36Sopenharmony_ci */
18762306a36Sopenharmony_civoid comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver,
18862306a36Sopenharmony_ci				     struct pcmcia_driver *pcmcia_driver)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	pcmcia_unregister_driver(pcmcia_driver);
19162306a36Sopenharmony_ci	comedi_driver_unregister(comedi_driver);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_pcmcia_driver_unregister);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int __init comedi_pcmcia_init(void)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_cimodule_init(comedi_pcmcia_init);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void __exit comedi_pcmcia_exit(void)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_cimodule_exit(comedi_pcmcia_exit);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciMODULE_AUTHOR("https://www.comedi.org");
20762306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi PCMCIA interface module");
20862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
209