summaryrefslogtreecommitdiff
path: root/drivers/comedi/comedi_pci.c
blob: cc25819021953fcd2880d5f3598bf829fdaa4030 (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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// SPDX-License-Identifier: GPL-2.0+
/*
 * comedi_pci.c
 * Comedi PCI 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/interrupt.h>
#include <linux/comedi/comedi_pci.h>

/**
 * comedi_to_pci_dev() - Return PCI 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 pci_dev.
 *
 * Return: Attached PCI device if @dev->hw_dev is non-%NULL.
 * Return %NULL if @dev->hw_dev is %NULL.
 */
struct pci_dev *comedi_to_pci_dev(struct comedi_device *dev)
{
	return dev->hw_dev ? to_pci_dev(dev->hw_dev) : NULL;
}
EXPORT_SYMBOL_GPL(comedi_to_pci_dev);

/**
 * comedi_pci_enable() - Enable the PCI device and request 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 pci_dev.  Enable the PCI device
 * and request its regions.  Set @dev->ioenabled to %true if successful,
 * otherwise undo what was done.
 *
 * Calls to comedi_pci_enable() and comedi_pci_disable() cannot be nested.
 *
 * Return:
 *	0 on success,
 *	-%ENODEV if @dev->hw_dev is %NULL,
 *	-%EBUSY if regions busy,
 *	or some negative error number if failed to enable PCI device.
 *
 */
int comedi_pci_enable(struct comedi_device *dev)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
	int rc;

	if (!pcidev)
		return -ENODEV;

	rc = pci_enable_device(pcidev);
	if (rc < 0)
		return rc;

	rc = pci_request_regions(pcidev, dev->board_name);
	if (rc < 0)
		pci_disable_device(pcidev);
	else
		dev->ioenabled = true;

	return rc;
}
EXPORT_SYMBOL_GPL(comedi_pci_enable);

/**
 * comedi_pci_disable() - Release the regions and disable the PCI 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 pci_dev.  If the earlier call
 * to comedi_pci_enable() was successful, release the PCI device's regions
 * and disable it.  Reset @dev->ioenabled back to %false.
 */
void comedi_pci_disable(struct comedi_device *dev)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);

	if (pcidev && dev->ioenabled) {
		pci_release_regions(pcidev);
		pci_disable_device(pcidev);
	}
	dev->ioenabled = false;
}
EXPORT_SYMBOL_GPL(comedi_pci_disable);

/**
 * comedi_pci_detach() - A generic "detach" handler for PCI COMEDI drivers
 * @dev: COMEDI device.
 *
 * COMEDI drivers for PCI devices that need no special clean-up of private data
 * and have no ioremapped regions other than that pointed to by @dev->mmio may
 * use this function as its "detach" handler called by the COMEDI core when a
 * COMEDI device is being detached from the low-level driver.  It may be also
 * called from a more specific "detach" handler that does additional clean-up.
 *
 * Free the IRQ if @dev->irq is non-zero, iounmap @dev->mmio if it is
 * non-%NULL, and call comedi_pci_disable() to release the PCI device's regions
 * and disable it.
 */
void comedi_pci_detach(struct comedi_device *dev)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);

	if (!pcidev || !dev->ioenabled)
		return;

	if (dev->irq) {
		free_irq(dev->irq, dev);
		dev->irq = 0;
	}
	if (dev->mmio) {
		iounmap(dev->mmio);
		dev->mmio = NULL;
	}
	comedi_pci_disable(dev);
}
EXPORT_SYMBOL_GPL(comedi_pci_detach);

/**
 * comedi_pci_auto_config() - Configure/probe a PCI COMEDI device
 * @pcidev: PCI device.
 * @driver: Registered COMEDI driver.
 * @context: Driver specific data, passed to comedi_auto_config().
 *
 * Typically called from the pci_driver (*probe) function.  Auto-configure
 * a COMEDI device, using the &struct device embedded in *@pcidev as the
 * hardware device.  The @context value gets passed through to @driver's
 * "auto_attach" handler.  The "auto_attach" handler may call
 * comedi_to_pci_dev() on the passed in COMEDI device to recover @pcidev.
 *
 * Return: The result of calling comedi_auto_config() (0 on success, or
 * a negative error number on failure).
 */
int comedi_pci_auto_config(struct pci_dev *pcidev,
			   struct comedi_driver *driver,
			   unsigned long context)
{
	return comedi_auto_config(&pcidev->dev, driver, context);
}
EXPORT_SYMBOL_GPL(comedi_pci_auto_config);

/**
 * comedi_pci_auto_unconfig() - Unconfigure/remove a PCI COMEDI device
 * @pcidev: PCI device.
 *
 * Typically called from the pci_driver (*remove) function.  Auto-unconfigure
 * a COMEDI device attached to this PCI device, using a pointer to the
 * &struct device embedded in *@pcidev 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_pci_auto_unconfig(struct pci_dev *pcidev)
{
	comedi_auto_unconfig(&pcidev->dev);
}
EXPORT_SYMBOL_GPL(comedi_pci_auto_unconfig);

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

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

	ret = pci_register_driver(pci_driver);
	if (ret < 0) {
		comedi_driver_unregister(comedi_driver);
		return ret;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(comedi_pci_driver_register);

/**
 * comedi_pci_driver_unregister() - Unregister a PCI COMEDI driver
 * @comedi_driver: COMEDI driver to be unregistered.
 * @pci_driver: PCI driver to be unregistered.
 *
 * This function is called from the module_exit() of PCI COMEDI driver modules
 * to unregister the PCI driver and the COMEDI driver.  Do not call it
 * directly, use the module_comedi_pci_driver() helper macro instead.
 */
void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver,
				  struct pci_driver *pci_driver)
{
	pci_unregister_driver(pci_driver);
	comedi_driver_unregister(comedi_driver);
}
EXPORT_SYMBOL_GPL(comedi_pci_driver_unregister);

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

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

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