summaryrefslogtreecommitdiffstats
path: root/drivers/comedi/comedi_pcmcia.c
blob: c53aad0fc2ce2a5fe25ee329decb9ec7562aed2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// SPDX-License-Identifier: GPL-2.0+
/*
 * comedi_pcmcia.c
 * Comedi PCMCIA driver specific functions.
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/comedi/comedi_pcmcia.h>

/**
 * comedi_to_pcmcia_dev() - Return PCMCIA device attached to COMEDI device
 * @dev: COMEDI device.
 *
 * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a
 * a &struct device embedded in a &struct pcmcia_device.
 *
 * Return: Attached PCMCIA device if @dev->hw_dev is non-%NULL.
 * Return %NULL if @dev->hw_dev is %NULL.
 */
struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *dev)
{
	return dev->hw_dev ? to_pcmcia_dev(dev->hw_dev) : NULL;
}
EXPORT_SYMBOL_GPL(comedi_to_pcmcia_dev);

static int comedi_pcmcia_conf_check(struct pcmcia_device *link,
				    void *priv_data)
{
	if (link->config_index == 0)
		return -EINVAL;

	return pcmcia_request_io(link);
}

/**
 * comedi_pcmcia_enable() - Request the regions and enable the PCMCIA device
 * @dev: COMEDI device.
 * @conf_check: Optional callback to check each configuration option of the
 *	PCMCIA device and request I/O regions.
 *
 * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a a
 * &struct device embedded in a &struct pcmcia_device.  The comedi PCMCIA
 * driver needs to set the 'config_flags' member in the &struct pcmcia_device,
 * as appropriate for that driver, before calling this function in order to
 * allow pcmcia_loop_config() to do its internal autoconfiguration.
 *
 * If @conf_check is %NULL it is set to a default function.  If is
 * passed to pcmcia_loop_config() and should return %0 if the configuration
 * is valid and I/O regions requested successfully, otherwise it should return
 * a negative error value.  The default function returns -%EINVAL if the
 * 'config_index' member is %0, otherwise it calls pcmcia_request_io() and
 * returns the result.
 *
 * If the above configuration check passes, pcmcia_enable_device() is called
 * to set up and activate the PCMCIA device.
 *
 * If this function returns an error, comedi_pcmcia_disable() should be called
 * to release requested resources.
 *
 * Return:
 *	0 on success,
 *	-%ENODEV id @dev->hw_dev is %NULL,
 *	a negative error number from pcmcia_loop_config() if it fails,
 *	or a negative error number from pcmcia_enable_device() if it fails.
 */
int comedi_pcmcia_enable(struct comedi_device *dev,
			 int (*conf_check)(struct pcmcia_device *p_dev,
					   void *priv_data))
{
	struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
	int ret;

	if (!link)
		return -ENODEV;

	if (!conf_check)
		conf_check = comedi_pcmcia_conf_check;

	ret = pcmcia_loop_config(link, conf_check, NULL);
	if (ret)
		return ret;

	return pcmcia_enable_device(link);
}
EXPORT_SYMBOL_GPL(comedi_pcmcia_enable);

/**
 * comedi_pcmcia_disable() - Disable the PCMCIA device and release the regions
 * @dev: COMEDI device.
 *
 * Assuming @dev->hw_dev is non-%NULL, it is assumed to be pointing to a
 * a &struct device embedded in a &struct pcmcia_device.  Call
 * pcmcia_disable_device() to disable and clean up the PCMCIA device.
 */
void comedi_pcmcia_disable(struct comedi_device *dev)
{
	struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);

	if (link)
		pcmcia_disable_device(link);
}
EXPORT_SYMBOL_GPL(comedi_pcmcia_disable);

/**
 * comedi_pcmcia_auto_config() - Configure/probe a PCMCIA COMEDI device
 * @link: PCMCIA device.
 * @driver: Registered COMEDI driver.
 *
 * Typically called from the pcmcia_driver (*probe) function.  Auto-configure
 * a COMEDI device, using a pointer to the &struct device embedded in *@link
 * as the hardware device.  The @driver's "auto_attach" handler may call
 * comedi_to_pcmcia_dev() on the passed in COMEDI device to recover @link.
 *
 * Return: The result of calling comedi_auto_config() (0 on success, or a
 * negative error number on failure).
 */
int comedi_pcmcia_auto_config(struct pcmcia_device *link,
			      struct comedi_driver *driver)
{
	return comedi_auto_config(&link->dev, driver, 0);
}
EXPORT_SYMBOL_GPL(comedi_pcmcia_auto_config);

/**
 * comedi_pcmcia_auto_unconfig() - Unconfigure/remove a PCMCIA COMEDI device
 * @link: PCMCIA device.
 *
 * Typically called from the pcmcia_driver (*remove) function.
 * Auto-unconfigure a COMEDI device attached to this PCMCIA device, using a
 * pointer to the &struct device embedded in *@link as the hardware device.
 * The COMEDI driver's "detach" handler will be called during unconfiguration
 * of the COMEDI device.
 *
 * Note that the COMEDI device may have already been unconfigured using the
 * %COMEDI_DEVCONFIG ioctl, in which case this attempt to unconfigure it
 * again should be ignored.
 */
void comedi_pcmcia_auto_unconfig(struct pcmcia_device *link)
{
	comedi_auto_unconfig(&link->dev);
}
EXPORT_SYMBOL_GPL(comedi_pcmcia_auto_unconfig);

/**
 * comedi_pcmcia_driver_register() - Register a PCMCIA COMEDI driver
 * @comedi_driver: COMEDI driver to be registered.
 * @pcmcia_driver: PCMCIA driver to be registered.
 *
 * This function is used for the module_init() of PCMCIA COMEDI driver modules
 * to register the COMEDI driver and the PCMCIA driver.  Do not call it
 * directly, use the module_comedi_pcmcia_driver() helper macro instead.
 *
 * Return: 0 on success, or a negative error number on failure.
 */
int comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver,
				  struct pcmcia_driver *pcmcia_driver)
{
	int ret;

	ret = comedi_driver_register(comedi_driver);
	if (ret < 0)
		return ret;

	ret = pcmcia_register_driver(pcmcia_driver);
	if (ret < 0) {
		comedi_driver_unregister(comedi_driver);
		return ret;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_register);

/**
 * comedi_pcmcia_driver_unregister() - Unregister a PCMCIA COMEDI driver
 * @comedi_driver: COMEDI driver to be registered.
 * @pcmcia_driver: PCMCIA driver to be registered.
 *
 * This function is called from the module_exit() of PCMCIA COMEDI driver
 * modules to unregister the PCMCIA driver and the COMEDI driver.  Do not call
 * it directly, use the module_comedi_pcmcia_driver() helper macro instead.
 */
void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver,
				     struct pcmcia_driver *pcmcia_driver)
{
	pcmcia_unregister_driver(pcmcia_driver);
	comedi_driver_unregister(comedi_driver);
}
EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_unregister);

static int __init comedi_pcmcia_init(void)
{
	return 0;
}
module_init(comedi_pcmcia_init);

static void __exit comedi_pcmcia_exit(void)
{
}
module_exit(comedi_pcmcia_exit);

MODULE_AUTHOR("https://www.comedi.org");
MODULE_DESCRIPTION("Comedi PCMCIA interface module");
MODULE_LICENSE("GPL");