Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
amplc_pc263.c
Go to the documentation of this file.
1 /*
2  comedi/drivers/amplc_pc263.c
3  Driver for Amplicon PC263 and PCI263 relay boards.
4 
5  Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
6 
7  COMEDI - Linux Control and Measurement Device Interface
8  Copyright (C) 2000 David A. Schleef <[email protected]>
9 
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 
24 */
25 /*
26 Driver: amplc_pc263
27 Description: Amplicon PC263, PCI263
28 Author: Ian Abbott <[email protected]>
29 Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263)
30 Updated: Wed, 22 Oct 2008 14:10:53 +0100
31 Status: works
32 
33 Configuration options - PC263:
34  [0] - I/O port base address
35 
36 Configuration options - PCI263:
37  [0] - PCI bus of device (optional)
38  [1] - PCI slot of device (optional)
39  If bus/slot is not specified, the first available PCI device will be
40  used.
41 
42 Each board appears as one subdevice, with 16 digital outputs, each
43 connected to a reed-relay. Relay contacts are closed when output is 1.
44 The state of the outputs can be read.
45 */
46 
47 #include "../comedidev.h"
48 
49 #define PC263_DRIVER_NAME "amplc_pc263"
50 
51 #define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)
52 #define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
53 
54 /* PCI263 PCI configuration register information */
55 #define PCI_VENDOR_ID_AMPLICON 0x14dc
56 #define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
57 #define PCI_DEVICE_ID_INVALID 0xffff
58 
59 /* PC263 / PCI263 registers */
60 #define PC263_IO_SIZE 2
61 
62 /*
63  * Board descriptions for Amplicon PC263 / PCI263.
64  */
65 
68 
69 struct pc263_board {
70  const char *name;
71  unsigned short devid;
74 };
75 static const struct pc263_board pc263_boards[] = {
76 #if DO_ISA
77  {
78  .name = "pc263",
79  .bustype = isa_bustype,
80  .model = pc263_model,
81  },
82 #endif
83 #if DO_PCI
84  {
85  .name = "pci263",
87  .bustype = pci_bustype,
88  .model = pci263_model,
89  },
90  {
91  .name = PC263_DRIVER_NAME,
92  .devid = PCI_DEVICE_ID_INVALID,
93  .bustype = pci_bustype,
94  .model = anypci_model, /* wildcard */
95  },
96 #endif
97 };
98 
99 /* test if ISA supported and this is an ISA board */
100 static inline bool is_isa_board(const struct pc263_board *board)
101 {
102  return DO_ISA && board->bustype == isa_bustype;
103 }
104 
105 /* test if PCI supported and this is a PCI board */
106 static inline bool is_pci_board(const struct pc263_board *board)
107 {
108  return DO_PCI && board->bustype == pci_bustype;
109 }
110 
111 /*
112  * This function looks for a board matching the supplied PCI device.
113  */
114 static const struct pc263_board *pc263_find_pci_board(struct pci_dev *pci_dev)
115 {
116  unsigned int i;
117 
118  for (i = 0; i < ARRAY_SIZE(pc263_boards); i++)
119  if (is_pci_board(&pc263_boards[i]) &&
120  pci_dev->device == pc263_boards[i].devid)
121  return &pc263_boards[i];
122  return NULL;
123 }
124 
125 
126 /*
127  * This function looks for a PCI device matching the requested board name,
128  * bus and slot.
129  */
130 static struct pci_dev *pc263_find_pci_dev(struct comedi_device *dev,
131  struct comedi_devconfig *it)
132 {
133  const struct pc263_board *thisboard = comedi_board(dev);
134  struct pci_dev *pci_dev = NULL;
135  int bus = it->options[0];
136  int slot = it->options[1];
137 
138  for_each_pci_dev(pci_dev) {
139  if (bus || slot) {
140  if (bus != pci_dev->bus->number ||
141  slot != PCI_SLOT(pci_dev->devfn))
142  continue;
143  }
144  if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
145  continue;
146 
147  if (thisboard->model == anypci_model) {
148  /* Wildcard board matches any supported PCI board. */
149  const struct pc263_board *foundboard;
150 
151  foundboard = pc263_find_pci_board(pci_dev);
152  if (foundboard == NULL)
153  continue;
154  /* Replace wildcard board_ptr. */
155  dev->board_ptr = thisboard = foundboard;
156  } else {
157  /* Match specific model name. */
158  if (pci_dev->device != thisboard->devid)
159  continue;
160  }
161  return pci_dev;
162  }
163  dev_err(dev->class_dev,
164  "No supported board found! (req. bus %d, slot %d)\n",
165  bus, slot);
166  return NULL;
167 }
168 /*
169  * This function checks and requests an I/O region, reporting an error
170  * if there is a conflict.
171  */
172 static int pc263_request_region(struct comedi_device *dev, unsigned long from,
173  unsigned long extent)
174 {
175  if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
176  dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
177  from, extent);
178  return -EIO;
179  }
180  return 0;
181 }
182 
183 static int pc263_do_insn_bits(struct comedi_device *dev,
184  struct comedi_subdevice *s,
185  struct comedi_insn *insn, unsigned int *data)
186 {
187  /* The insn data is a mask in data[0] and the new data
188  * in data[1], each channel cooresponding to a bit. */
189  if (data[0]) {
190  s->state &= ~data[0];
191  s->state |= data[0] & data[1];
192  /* Write out the new digital output lines */
193  outb(s->state & 0xFF, dev->iobase);
194  outb(s->state >> 8, dev->iobase + 1);
195  }
196  return insn->n;
197 }
198 
199 static void pc263_report_attach(struct comedi_device *dev)
200 {
201  const struct pc263_board *thisboard = comedi_board(dev);
202  struct pci_dev *pcidev = comedi_to_pci_dev(dev);
203  char tmpbuf[40];
204 
205  if (is_isa_board(thisboard))
206  snprintf(tmpbuf, sizeof(tmpbuf), "(base %#lx) ", dev->iobase);
207  else if (is_pci_board(thisboard))
208  snprintf(tmpbuf, sizeof(tmpbuf), "(pci %s) ",
209  pci_name(pcidev));
210  else
211  tmpbuf[0] = '\0';
212  dev_info(dev->class_dev, "%s %sattached\n", dev->board_name, tmpbuf);
213 }
214 
215 static int pc263_common_attach(struct comedi_device *dev, unsigned long iobase)
216 {
217  const struct pc263_board *thisboard = comedi_board(dev);
218  struct comedi_subdevice *s;
219  int ret;
220 
221  dev->board_name = thisboard->name;
222  dev->iobase = iobase;
223 
224  ret = comedi_alloc_subdevices(dev, 1);
225  if (ret)
226  return ret;
227 
228  s = &dev->subdevices[0];
229  /* digital output subdevice */
230  s->type = COMEDI_SUBD_DO;
232  s->n_chan = 16;
233  s->maxdata = 1;
235  s->insn_bits = pc263_do_insn_bits;
236  /* read initial relay state */
237  s->state = inb(dev->iobase) | (inb(dev->iobase + 1) << 8);
238 
239  pc263_report_attach(dev);
240  return 1;
241 }
242 
243 static int pc263_pci_common_attach(struct comedi_device *dev,
244  struct pci_dev *pci_dev)
245 {
246  unsigned long iobase;
247  int ret;
248 
249  comedi_set_hw_dev(dev, &pci_dev->dev);
250 
251  ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
252  if (ret < 0) {
253  dev_err(dev->class_dev,
254  "error! cannot enable PCI device and request regions!\n");
255  return ret;
256  }
257  iobase = pci_resource_start(pci_dev, 2);
258  return pc263_common_attach(dev, iobase);
259 }
260 
261 /*
262  * Attach is called by the Comedi core to configure the driver
263  * for a particular board. If you specified a board_name array
264  * in the driver structure, dev->board_ptr contains that
265  * address.
266  */
267 static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
268 {
269  const struct pc263_board *thisboard = comedi_board(dev);
270  int ret;
271 
272  dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach\n");
273 
274  /* Process options and reserve resources according to bus type. */
275  if (is_isa_board(thisboard)) {
276  unsigned long iobase = it->options[0];
277  ret = pc263_request_region(dev, iobase, PC263_IO_SIZE);
278  if (ret < 0)
279  return ret;
280  return pc263_common_attach(dev, iobase);
281  } else if (is_pci_board(thisboard)) {
282  struct pci_dev *pci_dev;
283 
284  pci_dev = pc263_find_pci_dev(dev, it);
285  if (!pci_dev)
286  return -EIO;
287  return pc263_pci_common_attach(dev, pci_dev);
288  } else {
290  ": BUG! cannot determine board type!\n");
291  return -EINVAL;
292  }
293 }
294 /*
295  * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
296  * to the "manual" attach hook. dev->board_ptr is NULL on entry. There should
297  * be a board entry matching the supplied PCI device.
298  */
299 static int __devinit pc263_attach_pci(struct comedi_device *dev,
300  struct pci_dev *pci_dev)
301 {
302  if (!DO_PCI)
303  return -EINVAL;
304 
305  dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach pci %s\n",
306  pci_name(pci_dev));
307  dev->board_ptr = pc263_find_pci_board(pci_dev);
308  if (dev->board_ptr == NULL) {
309  dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
310  return -EINVAL;
311  }
312  /*
313  * Need to 'get' the PCI device to match the 'put' in pc263_detach().
314  * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
315  * support for manual attachment of PCI devices via pc263_attach()
316  * has been removed.
317  */
318  pci_dev_get(pci_dev);
319  return pc263_pci_common_attach(dev, pci_dev);
320 }
321 
322 static void pc263_detach(struct comedi_device *dev)
323 {
324  const struct pc263_board *thisboard = comedi_board(dev);
325 
326  if (!thisboard)
327  return;
328  if (is_isa_board(thisboard)) {
329  if (dev->iobase)
331  } else if (is_pci_board(thisboard)) {
332  struct pci_dev *pcidev = comedi_to_pci_dev(dev);
333  if (pcidev) {
334  if (dev->iobase)
335  comedi_pci_disable(pcidev);
336  pci_dev_put(pcidev);
337  }
338  }
339 }
340 
341 /*
342  * The struct comedi_driver structure tells the Comedi core module
343  * which functions to call to configure/deconfigure (attach/detach)
344  * the board, and also about the kernel module that contains
345  * the device code.
346  */
347 static struct comedi_driver amplc_pc263_driver = {
348  .driver_name = PC263_DRIVER_NAME,
349  .module = THIS_MODULE,
350  .attach = pc263_attach,
351  .attach_pci = pc263_attach_pci,
352  .detach = pc263_detach,
353  .board_name = &pc263_boards[0].name,
354  .offset = sizeof(struct pc263_board),
355  .num_names = ARRAY_SIZE(pc263_boards),
356 };
357 
358 #if DO_PCI
359 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
361  {0}
362 };
363 MODULE_DEVICE_TABLE(pci, pc263_pci_table);
364 
365 static int __devinit amplc_pc263_pci_probe(struct pci_dev *dev,
366  const struct pci_device_id
367  *ent)
368 {
369  return comedi_pci_auto_config(dev, &amplc_pc263_driver);
370 }
371 
372 static void __devexit amplc_pc263_pci_remove(struct pci_dev *dev)
373 {
375 }
376 
377 static struct pci_driver amplc_pc263_pci_driver = {
379  .id_table = pc263_pci_table,
380  .probe = &amplc_pc263_pci_probe,
381  .remove = __devexit_p(&amplc_pc263_pci_remove)
382 };
383 module_comedi_pci_driver(amplc_pc263_driver, amplc_pc263_pci_driver);
384 #else
385 module_comedi_driver(amplc_pc263_driver);
386 #endif
387 
388 MODULE_AUTHOR("Comedi http://www.comedi.org");
389 MODULE_DESCRIPTION("Comedi low-level driver");
390 MODULE_LICENSE("GPL");