1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2*8ffdff6aSGreg Kroah-Hartman /*
3*8ffdff6aSGreg Kroah-Hartman  * comedi_pcmcia.c
4*8ffdff6aSGreg Kroah-Hartman  * Comedi PCMCIA driver specific functions.
5*8ffdff6aSGreg Kroah-Hartman  *
6*8ffdff6aSGreg Kroah-Hartman  * COMEDI - Linux Control and Measurement Device Interface
7*8ffdff6aSGreg Kroah-Hartman  * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
8*8ffdff6aSGreg Kroah-Hartman  */
9*8ffdff6aSGreg Kroah-Hartman 
10*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
11*8ffdff6aSGreg Kroah-Hartman #include <linux/kernel.h>
12*8ffdff6aSGreg Kroah-Hartman 
13*8ffdff6aSGreg Kroah-Hartman #include "comedi_pcmcia.h"
14*8ffdff6aSGreg Kroah-Hartman 
15*8ffdff6aSGreg Kroah-Hartman /**
16*8ffdff6aSGreg Kroah-Hartman  * comedi_to_pcmcia_dev() - Return PCMCIA device attached to COMEDI device
17*8ffdff6aSGreg Kroah-Hartman  * @dev: COMEDI device.
18*8ffdff6aSGreg Kroah-Hartman  *
19*8ffdff6aSGreg Kroah-Hartman  * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a
20*8ffdff6aSGreg Kroah-Hartman  * a &struct device embedded in a &struct pcmcia_device.
21*8ffdff6aSGreg Kroah-Hartman  *
22*8ffdff6aSGreg Kroah-Hartman  * Return: Attached PCMCIA device if @dev->hw_dev is non-%NULL.
23*8ffdff6aSGreg Kroah-Hartman  * Return %NULL if @dev->hw_dev is %NULL.
24*8ffdff6aSGreg Kroah-Hartman  */
25*8ffdff6aSGreg Kroah-Hartman struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *dev)
26*8ffdff6aSGreg Kroah-Hartman {
27*8ffdff6aSGreg Kroah-Hartman 	return dev->hw_dev ? to_pcmcia_dev(dev->hw_dev) : NULL;
28*8ffdff6aSGreg Kroah-Hartman }
29*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_to_pcmcia_dev);
30*8ffdff6aSGreg Kroah-Hartman 
31*8ffdff6aSGreg Kroah-Hartman static int comedi_pcmcia_conf_check(struct pcmcia_device *link,
32*8ffdff6aSGreg Kroah-Hartman 				    void *priv_data)
33*8ffdff6aSGreg Kroah-Hartman {
34*8ffdff6aSGreg Kroah-Hartman 	if (link->config_index == 0)
35*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
36*8ffdff6aSGreg Kroah-Hartman 
37*8ffdff6aSGreg Kroah-Hartman 	return pcmcia_request_io(link);
38*8ffdff6aSGreg Kroah-Hartman }
39*8ffdff6aSGreg Kroah-Hartman 
40*8ffdff6aSGreg Kroah-Hartman /**
41*8ffdff6aSGreg Kroah-Hartman  * comedi_pcmcia_enable() - Request the regions and enable the PCMCIA device
42*8ffdff6aSGreg Kroah-Hartman  * @dev: COMEDI device.
43*8ffdff6aSGreg Kroah-Hartman  * @conf_check: Optional callback to check each configuration option of the
44*8ffdff6aSGreg Kroah-Hartman  *	PCMCIA device and request I/O regions.
45*8ffdff6aSGreg Kroah-Hartman  *
46*8ffdff6aSGreg Kroah-Hartman  * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a a
47*8ffdff6aSGreg Kroah-Hartman  * &struct device embedded in a &struct pcmcia_device.  The comedi PCMCIA
48*8ffdff6aSGreg Kroah-Hartman  * driver needs to set the 'config_flags' member in the &struct pcmcia_device,
49*8ffdff6aSGreg Kroah-Hartman  * as appropriate for that driver, before calling this function in order to
50*8ffdff6aSGreg Kroah-Hartman  * allow pcmcia_loop_config() to do its internal autoconfiguration.
51*8ffdff6aSGreg Kroah-Hartman  *
52*8ffdff6aSGreg Kroah-Hartman  * If @conf_check is %NULL it is set to a default function.  If is
53*8ffdff6aSGreg Kroah-Hartman  * passed to pcmcia_loop_config() and should return %0 if the configuration
54*8ffdff6aSGreg Kroah-Hartman  * is valid and I/O regions requested successfully, otherwise it should return
55*8ffdff6aSGreg Kroah-Hartman  * a negative error value.  The default function returns -%EINVAL if the
56*8ffdff6aSGreg Kroah-Hartman  * 'config_index' member is %0, otherwise it calls pcmcia_request_io() and
57*8ffdff6aSGreg Kroah-Hartman  * returns the result.
58*8ffdff6aSGreg Kroah-Hartman  *
59*8ffdff6aSGreg Kroah-Hartman  * If the above configuration check passes, pcmcia_enable_device() is called
60*8ffdff6aSGreg Kroah-Hartman  * to set up and activate the PCMCIA device.
61*8ffdff6aSGreg Kroah-Hartman  *
62*8ffdff6aSGreg Kroah-Hartman  * If this function returns an error, comedi_pcmcia_disable() should be called
63*8ffdff6aSGreg Kroah-Hartman  * to release requested resources.
64*8ffdff6aSGreg Kroah-Hartman  *
65*8ffdff6aSGreg Kroah-Hartman  * Return:
66*8ffdff6aSGreg Kroah-Hartman  *	0 on success,
67*8ffdff6aSGreg Kroah-Hartman  *	-%ENODEV id @dev->hw_dev is %NULL,
68*8ffdff6aSGreg Kroah-Hartman  *	a negative error number from pcmcia_loop_config() if it fails,
69*8ffdff6aSGreg Kroah-Hartman  *	or a negative error number from pcmcia_enable_device() if it fails.
70*8ffdff6aSGreg Kroah-Hartman  */
71*8ffdff6aSGreg Kroah-Hartman int comedi_pcmcia_enable(struct comedi_device *dev,
72*8ffdff6aSGreg Kroah-Hartman 			 int (*conf_check)(struct pcmcia_device *p_dev,
73*8ffdff6aSGreg Kroah-Hartman 					   void *priv_data))
74*8ffdff6aSGreg Kroah-Hartman {
75*8ffdff6aSGreg Kroah-Hartman 	struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
76*8ffdff6aSGreg Kroah-Hartman 	int ret;
77*8ffdff6aSGreg Kroah-Hartman 
78*8ffdff6aSGreg Kroah-Hartman 	if (!link)
79*8ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
80*8ffdff6aSGreg Kroah-Hartman 
81*8ffdff6aSGreg Kroah-Hartman 	if (!conf_check)
82*8ffdff6aSGreg Kroah-Hartman 		conf_check = comedi_pcmcia_conf_check;
83*8ffdff6aSGreg Kroah-Hartman 
84*8ffdff6aSGreg Kroah-Hartman 	ret = pcmcia_loop_config(link, conf_check, NULL);
85*8ffdff6aSGreg Kroah-Hartman 	if (ret)
86*8ffdff6aSGreg Kroah-Hartman 		return ret;
87*8ffdff6aSGreg Kroah-Hartman 
88*8ffdff6aSGreg Kroah-Hartman 	return pcmcia_enable_device(link);
89*8ffdff6aSGreg Kroah-Hartman }
90*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_pcmcia_enable);
91*8ffdff6aSGreg Kroah-Hartman 
92*8ffdff6aSGreg Kroah-Hartman /**
93*8ffdff6aSGreg Kroah-Hartman  * comedi_pcmcia_disable() - Disable the PCMCIA device and release the regions
94*8ffdff6aSGreg Kroah-Hartman  * @dev: COMEDI device.
95*8ffdff6aSGreg Kroah-Hartman  *
96*8ffdff6aSGreg Kroah-Hartman  * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a
97*8ffdff6aSGreg Kroah-Hartman  * a &struct device embedded in a &struct pcmcia_device.  Call
98*8ffdff6aSGreg Kroah-Hartman  * pcmcia_disable_device() to disable and clean up the PCMCIA device.
99*8ffdff6aSGreg Kroah-Hartman  */
100*8ffdff6aSGreg Kroah-Hartman void comedi_pcmcia_disable(struct comedi_device *dev)
101*8ffdff6aSGreg Kroah-Hartman {
102*8ffdff6aSGreg Kroah-Hartman 	struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
103*8ffdff6aSGreg Kroah-Hartman 
104*8ffdff6aSGreg Kroah-Hartman 	if (link)
105*8ffdff6aSGreg Kroah-Hartman 		pcmcia_disable_device(link);
106*8ffdff6aSGreg Kroah-Hartman }
107*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_pcmcia_disable);
108*8ffdff6aSGreg Kroah-Hartman 
109*8ffdff6aSGreg Kroah-Hartman /**
110*8ffdff6aSGreg Kroah-Hartman  * comedi_pcmcia_auto_config() - Configure/probe a PCMCIA COMEDI device
111*8ffdff6aSGreg Kroah-Hartman  * @link: PCMCIA device.
112*8ffdff6aSGreg Kroah-Hartman  * @driver: Registered COMEDI driver.
113*8ffdff6aSGreg Kroah-Hartman  *
114*8ffdff6aSGreg Kroah-Hartman  * Typically called from the pcmcia_driver (*probe) function.  Auto-configure
115*8ffdff6aSGreg Kroah-Hartman  * a COMEDI device, using a pointer to the &struct device embedded in *@link
116*8ffdff6aSGreg Kroah-Hartman  * as the hardware device.  The @driver's "auto_attach" handler may call
117*8ffdff6aSGreg Kroah-Hartman  * comedi_to_pcmcia_dev() on the passed in COMEDI device to recover @link.
118*8ffdff6aSGreg Kroah-Hartman  *
119*8ffdff6aSGreg Kroah-Hartman  * Return: The result of calling comedi_auto_config() (0 on success, or a
120*8ffdff6aSGreg Kroah-Hartman  * negative error number on failure).
121*8ffdff6aSGreg Kroah-Hartman  */
122*8ffdff6aSGreg Kroah-Hartman int comedi_pcmcia_auto_config(struct pcmcia_device *link,
123*8ffdff6aSGreg Kroah-Hartman 			      struct comedi_driver *driver)
124*8ffdff6aSGreg Kroah-Hartman {
125*8ffdff6aSGreg Kroah-Hartman 	return comedi_auto_config(&link->dev, driver, 0);
126*8ffdff6aSGreg Kroah-Hartman }
127*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_pcmcia_auto_config);
128*8ffdff6aSGreg Kroah-Hartman 
129*8ffdff6aSGreg Kroah-Hartman /**
130*8ffdff6aSGreg Kroah-Hartman  * comedi_pcmcia_auto_unconfig() - Unconfigure/remove a PCMCIA COMEDI device
131*8ffdff6aSGreg Kroah-Hartman  * @link: PCMCIA device.
132*8ffdff6aSGreg Kroah-Hartman  *
133*8ffdff6aSGreg Kroah-Hartman  * Typically called from the pcmcia_driver (*remove) function.
134*8ffdff6aSGreg Kroah-Hartman  * Auto-unconfigure a COMEDI device attached to this PCMCIA device, using a
135*8ffdff6aSGreg Kroah-Hartman  * pointer to the &struct device embedded in *@link as the hardware device.
136*8ffdff6aSGreg Kroah-Hartman  * The COMEDI driver's "detach" handler will be called during unconfiguration
137*8ffdff6aSGreg Kroah-Hartman  * of the COMEDI device.
138*8ffdff6aSGreg Kroah-Hartman  *
139*8ffdff6aSGreg Kroah-Hartman  * Note that the COMEDI device may have already been unconfigured using the
140*8ffdff6aSGreg Kroah-Hartman  * %COMEDI_DEVCONFIG ioctl, in which case this attempt to unconfigure it
141*8ffdff6aSGreg Kroah-Hartman  * again should be ignored.
142*8ffdff6aSGreg Kroah-Hartman  */
143*8ffdff6aSGreg Kroah-Hartman void comedi_pcmcia_auto_unconfig(struct pcmcia_device *link)
144*8ffdff6aSGreg Kroah-Hartman {
145*8ffdff6aSGreg Kroah-Hartman 	comedi_auto_unconfig(&link->dev);
146*8ffdff6aSGreg Kroah-Hartman }
147*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_pcmcia_auto_unconfig);
148*8ffdff6aSGreg Kroah-Hartman 
149*8ffdff6aSGreg Kroah-Hartman /**
150*8ffdff6aSGreg Kroah-Hartman  * comedi_pcmcia_driver_register() - Register a PCMCIA COMEDI driver
151*8ffdff6aSGreg Kroah-Hartman  * @comedi_driver: COMEDI driver to be registered.
152*8ffdff6aSGreg Kroah-Hartman  * @pcmcia_driver: PCMCIA driver to be registered.
153*8ffdff6aSGreg Kroah-Hartman  *
154*8ffdff6aSGreg Kroah-Hartman  * This function is used for the module_init() of PCMCIA COMEDI driver modules
155*8ffdff6aSGreg Kroah-Hartman  * to register the COMEDI driver and the PCMCIA driver.  Do not call it
156*8ffdff6aSGreg Kroah-Hartman  * directly, use the module_comedi_pcmcia_driver() helper macro instead.
157*8ffdff6aSGreg Kroah-Hartman  *
158*8ffdff6aSGreg Kroah-Hartman  * Return: 0 on success, or a negative error number on failure.
159*8ffdff6aSGreg Kroah-Hartman  */
160*8ffdff6aSGreg Kroah-Hartman int comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver,
161*8ffdff6aSGreg Kroah-Hartman 				  struct pcmcia_driver *pcmcia_driver)
162*8ffdff6aSGreg Kroah-Hartman {
163*8ffdff6aSGreg Kroah-Hartman 	int ret;
164*8ffdff6aSGreg Kroah-Hartman 
165*8ffdff6aSGreg Kroah-Hartman 	ret = comedi_driver_register(comedi_driver);
166*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
167*8ffdff6aSGreg Kroah-Hartman 		return ret;
168*8ffdff6aSGreg Kroah-Hartman 
169*8ffdff6aSGreg Kroah-Hartman 	ret = pcmcia_register_driver(pcmcia_driver);
170*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0) {
171*8ffdff6aSGreg Kroah-Hartman 		comedi_driver_unregister(comedi_driver);
172*8ffdff6aSGreg Kroah-Hartman 		return ret;
173*8ffdff6aSGreg Kroah-Hartman 	}
174*8ffdff6aSGreg Kroah-Hartman 
175*8ffdff6aSGreg Kroah-Hartman 	return 0;
176*8ffdff6aSGreg Kroah-Hartman }
177*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_register);
178*8ffdff6aSGreg Kroah-Hartman 
179*8ffdff6aSGreg Kroah-Hartman /**
180*8ffdff6aSGreg Kroah-Hartman  * comedi_pcmcia_driver_unregister() - Unregister a PCMCIA COMEDI driver
181*8ffdff6aSGreg Kroah-Hartman  * @comedi_driver: COMEDI driver to be registered.
182*8ffdff6aSGreg Kroah-Hartman  * @pcmcia_driver: PCMCIA driver to be registered.
183*8ffdff6aSGreg Kroah-Hartman  *
184*8ffdff6aSGreg Kroah-Hartman  * This function is called from the module_exit() of PCMCIA COMEDI driver
185*8ffdff6aSGreg Kroah-Hartman  * modules to unregister the PCMCIA driver and the COMEDI driver.  Do not call
186*8ffdff6aSGreg Kroah-Hartman  * it directly, use the module_comedi_pcmcia_driver() helper macro instead.
187*8ffdff6aSGreg Kroah-Hartman  */
188*8ffdff6aSGreg Kroah-Hartman void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver,
189*8ffdff6aSGreg Kroah-Hartman 				     struct pcmcia_driver *pcmcia_driver)
190*8ffdff6aSGreg Kroah-Hartman {
191*8ffdff6aSGreg Kroah-Hartman 	pcmcia_unregister_driver(pcmcia_driver);
192*8ffdff6aSGreg Kroah-Hartman 	comedi_driver_unregister(comedi_driver);
193*8ffdff6aSGreg Kroah-Hartman }
194*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_unregister);
195*8ffdff6aSGreg Kroah-Hartman 
196*8ffdff6aSGreg Kroah-Hartman static int __init comedi_pcmcia_init(void)
197*8ffdff6aSGreg Kroah-Hartman {
198*8ffdff6aSGreg Kroah-Hartman 	return 0;
199*8ffdff6aSGreg Kroah-Hartman }
200*8ffdff6aSGreg Kroah-Hartman module_init(comedi_pcmcia_init);
201*8ffdff6aSGreg Kroah-Hartman 
202*8ffdff6aSGreg Kroah-Hartman static void __exit comedi_pcmcia_exit(void)
203*8ffdff6aSGreg Kroah-Hartman {
204*8ffdff6aSGreg Kroah-Hartman }
205*8ffdff6aSGreg Kroah-Hartman module_exit(comedi_pcmcia_exit);
206*8ffdff6aSGreg Kroah-Hartman 
207*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("https://www.comedi.org");
208*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi PCMCIA interface module");
209*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
210