Commit 61927565 authored by Ian Abbott's avatar Ian Abbott Committed by Greg Kroah-Hartman

staging: comedi: skel: do auto-attachment of PCI devices

This driver is an example of how to write a Comedi driver and includes
some code for handling PCI devices, but not very much.  It calls
`comedi_pci_auto_config()` at PCI probe time.  That normally expects to
call the comedi driver's `auto_attach()` hook (or the deprecated
`attach_pci()` hook), but will fall back to using the `attach()` hook
that should only be used for attaching devices "manually" via the
`COMEDI_DEVCONFIG` ioctl.

Add an `auto_attach()` hook (`skel_auto_attach()`) to handle
auto-attachment of supported PCI devices.  Add comments to the
`attach()` hook (`skel_attach()`) to indicate that it shouldn't
generally allow PCI devices to be attached that way (and probably isn't
needed at all for PCI-only Comedi drivers).  Add code to the `detach()`
hook (`skel_detach()`) to disable PCI devices enabled by the
`auto_attach()` hook.

`skel_auto_attach()` calls new function `skel_find_pci_board()` to find
a matching element in `skel_boards[]`.  PCI device ID information has
been added to `skel_boards[]` to give the function something to look
for.

Remove the `pci_dev` member of `struct skel_private` as drivers now use
the `hw_dev` member of `struct comedi_device` to get at the PCI device.
Signed-off-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d8d7a823
...@@ -92,6 +92,7 @@ Configuration Options: ...@@ -92,6 +92,7 @@ Configuration Options:
*/ */
struct skel_board { struct skel_board {
const char *name; const char *name;
unsigned int devid;
int ai_chans; int ai_chans;
int ai_bits; int ai_bits;
int have_dio; int have_dio;
...@@ -100,12 +101,14 @@ struct skel_board { ...@@ -100,12 +101,14 @@ struct skel_board {
static const struct skel_board skel_boards[] = { static const struct skel_board skel_boards[] = {
{ {
.name = "skel-100", .name = "skel-100",
.devid = 0x100,
.ai_chans = 16, .ai_chans = 16,
.ai_bits = 12, .ai_bits = 12,
.have_dio = 1, .have_dio = 1,
}, },
{ {
.name = "skel-200", .name = "skel-200",
.devid = 0x200,
.ai_chans = 8, .ai_chans = 8,
.ai_bits = 16, .ai_bits = 16,
.have_dio = 0, .have_dio = 0,
...@@ -120,9 +123,6 @@ struct skel_private { ...@@ -120,9 +123,6 @@ struct skel_private {
int data; int data;
/* would be useful for a PCI device */
struct pci_dev *pci_dev;
/* Used for AO readback */ /* Used for AO readback */
unsigned int ao_readback[2]; unsigned int ao_readback[2];
}; };
...@@ -421,37 +421,30 @@ static int skel_dio_insn_config(struct comedi_device *dev, ...@@ -421,37 +421,30 @@ static int skel_dio_insn_config(struct comedi_device *dev,
return insn->n; return insn->n;
} }
/* static const struct skel_board *skel_find_pci_board(struct pci_dev *pcidev)
* Attach is called by the Comedi core to configure the driver
* for a particular board. If you specified a board_name array
* in the driver structure, dev->board_ptr contains that
* address.
*/
static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{ {
const struct skel_board *thisboard; unsigned int i;
struct skel_private *devpriv;
struct comedi_subdevice *s;
int ret;
/* /*
* If you can probe the device to determine what device in a series * This example code assumes all the entries in skel_boards[] are PCI boards
* it is, this is the place to do it. Otherwise, dev->board_ptr * and all use the same PCI vendor ID. If skel_boards[] contains a mixture
* should already be initialized. * of PCI and non-PCI boards, this loop should skip over the non-PCI boards.
*/ */
/* dev->board_ptr = skel_probe(dev, it); */ for (i = 0; i < ARRAY_SIZE(skel_boards); i++)
if (/* skel_boards[i].bustype == pci_bustype && */
pcidev->device == skel_boards[i].devid)
return &skel_boards[i];
return NULL;
}
thisboard = comedi_board(dev);
/* /*
* Initialize dev->board_name. * Handle common part of skel_attach() and skel_auto_attach().
*/ */
dev->board_name = thisboard->name; static int skel_common_attach(struct comedi_device *dev)
{
/* Allocate the private data */ const struct skel_board *thisboard = comedi_board(dev);
devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); struct comedi_subdevice *s;
if (!devpriv) int ret;
return -ENOMEM;
dev->private = devpriv;
ret = comedi_alloc_subdevices(dev, 3); ret = comedi_alloc_subdevices(dev, 3);
if (ret) if (ret)
...@@ -504,6 +497,146 @@ static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it) ...@@ -504,6 +497,146 @@ static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return 0; return 0;
} }
/*
* _attach is called by the Comedi core to configure the driver
* for a particular board in response to the COMEDI_DEVCONFIG ioctl for
* a matching board or driver name. If you specified a board_name array
* in the driver structure, dev->board_ptr contains that address.
*
* Drivers that handle only PCI or USB devices do not usually support
* manual attachment of those devices via the COMEDI_DEVCONFIG ioctl, so
* those drivers do not have an _attach function; they just have an
* _auto_attach function instead. (See skel_auto_attach() for an example
* of such a function.)
*/
static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
const struct skel_board *thisboard;
struct skel_private *devpriv;
/*
* If you can probe the device to determine what device in a series
* it is, this is the place to do it. Otherwise, dev->board_ptr
* should already be initialized.
*/
/* dev->board_ptr = skel_probe(dev, it); */
thisboard = comedi_board(dev);
/*
* Initialize dev->board_name.
*/
dev->board_name = thisboard->name;
/* Allocate the private data */
devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
if (!devpriv)
return -ENOMEM;
dev->private = devpriv;
/*
* Supported boards are usually either auto-attached via the
* Comedi driver's _auto_attach routine, or manually attached via the
* Comedi driver's _attach routine. In most cases, attempts to
* manual attach boards that are usually auto-attached should be
* rejected by this function.
*/
/*
* if (thisboard->bustype == pci_bustype) {
* dev_err(dev->class_dev,
* "Manual attachment of PCI board '%s' not supported\n",
* thisboard->name);
* }
*/
/*
* For ISA boards, get the i/o base address from it->options[],
* request the i/o region and set dev->iobase * from it->options[].
* If using interrupts, get the IRQ number from it->options[].
*/
/*
* Call a common function to handle the remaining things to do for
* attaching ISA or PCI boards. (Extra parameters could be added
* to pass additional information such as IRQ number.)
*/
return skel_common_attach(dev);
}
/*
* _auto_attach is called via comedi_pci_auto_config() (or
* comedi_usb_auto_config(), etc.) to handle devices that can be attached
* to the Comedi core automatically without the COMEDI_DEVCONFIG ioctl.
*
* For PCI devices, comedi_pci_auto_config() is usually called directly from
* the struct pci_driver probe() function, so this _auto_attach() function
* can be tagged __devinit.
*
* The context parameter is usually unused, but if the driver called
* comedi_auto_config() directly instead of the comedi_pci_auto_config()
* wrapper function, this will be a copy of the context passed to
* comedi_auto_config().
*/
static int __devinit skel_auto_attach(struct comedi_device *dev,
unsigned long context)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
const struct skel_board *thisboard;
struct skel_private *devpriv;
int ret;
/* Hack to allow unused code to be optimized out. */
if (!IS_ENABLED(CONFIG_COMEDI_PCI_DRIVERS))
return -EINVAL;
/* Find a matching board in skel_boards[]. */
thisboard = skel_find_pci_board(pcidev);
if (!thisboard) {
dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
return -EINVAL;
}
/*
* Point the struct comedi_device to the matching board info
* and set the board name.
*/
dev->board_ptr = thisboard;
dev->board_name = thisboard->name;
/* Allocate the private data */
devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
if (!devpriv)
return -ENOMEM;
dev->private = devpriv;
/* Enable the PCI device. */
ret = comedi_pci_enable(pcidev, dev->board_name);
if (ret)
return ret;
/*
* Record the fact that the PCI device is enabled so that it can
* be disabled during _detach().
*
* For this example driver, we assume PCI BAR 0 is the main I/O
* region for the board registers and use dev->iobase to hold the
* I/O base address and to indicate that the PCI device has been
* enabled.
*
* (For boards with memory-mapped registers, dev->iobase is not
* usually needed for register access, so can just be set to 1
* to indicate that the PCI device has been enabled.)
*/
dev->iobase = pci_resource_start(pcidev, 0);
/*
* Call a common function to handle the remaining things to do for
* attaching ISA or PCI boards. (Extra parameters could be added
* to pass additional information such as IRQ number.)
*/
return skel_common_attach(dev);
}
/* /*
* _detach is called to deconfigure a device. It should deallocate * _detach is called to deconfigure a device. It should deallocate
* resources. * resources.
...@@ -514,6 +647,40 @@ static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it) ...@@ -514,6 +647,40 @@ static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
*/ */
static void skel_detach(struct comedi_device *dev) static void skel_detach(struct comedi_device *dev)
{ {
const struct skel_board *thisboard = comedi_board(dev);
struct skel_private *devpriv = dev->private;
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
if (!thisboard || !devpriv)
return;
/*
* Do common stuff such as freeing IRQ, unmapping remapped memory
* regions, etc., being careful to check that the stuff is valid given
* that _detach() is called even when _attach() or _auto_attach() return
* an error.
*/
if (IS_ENABLED(CONFIG_COMEDI_PCI_DRIVERS) /* &&
thisboard->bustype == pci_bustype */) {
/*
* PCI board
*
* If PCI device enabled by _auto_attach() (or _attach()),
* disable it here.
*/
if (pcidev && dev->iobase)
comedi_pci_disable(pcidev);
} else {
/*
* ISA board
*
* If I/O regions successfully requested by _attach(),
* release them here.
*/
if (dev->iobase)
release_region(dev->iobase, SKEL_SIZE);
}
} }
/* /*
...@@ -526,6 +693,7 @@ static struct comedi_driver skel_driver = { ...@@ -526,6 +693,7 @@ static struct comedi_driver skel_driver = {
.driver_name = "dummy", .driver_name = "dummy",
.module = THIS_MODULE, .module = THIS_MODULE,
.attach = skel_attach, .attach = skel_attach,
.auto_attach = skel_auto_attach,
.detach = skel_detach, .detach = skel_detach,
/* It is not necessary to implement the following members if you are /* It is not necessary to implement the following members if you are
* writing a driver for a ISA PnP or PCI card */ * writing a driver for a ISA PnP or PCI card */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment