drivers/comedi/drivers/pcmmio.c
Source file repositories/reference/linux-study-clean/drivers/comedi/drivers/pcmmio.c
File Facts
- System
- Linux kernel
- Corpus path
drivers/comedi/drivers/pcmmio.c- Extension
.c- Size
- 22865 bytes
- Lines
- 778
- Domain
- Driver Families
- Bucket
- drivers/comedi
- Inferred role
- Driver Families: implementation source
- Status
- source implementation candidate
Why This File Exists
Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Uses kernel synchronization; read lock ordering, sleepability, and interrupt context assumptions before translating.
- Touches IRQ or DMA behavior; this matters for the representative real-device path.
- Defines or uses C structs; map object ownership, embedded links, reference counts, and lock ownership.
Dependency Surface
linux/module.hlinux/interrupt.hlinux/slab.hlinux/comedi/comedidev.h
Detected Declarations
struct pcmmio_privatefunction pcmmio_dio_writefunction pcmmio_dio_readfunction currentfunction pcmmio_dio_insn_configfunction pcmmio_resetfunction pcmmio_stop_intrfunction pcmmio_handle_dio_intrfunction interrupt_pcmmiofunction pcmmio_start_intrfunction pcmmio_cancelfunction pcmmio_inttrig_start_intrfunction pcmmio_cmdfunction pcmmio_cmdtestfunction pcmmio_ai_eocfunction pcmmio_ai_insn_readfunction pcmmio_ao_eocfunction pcmmio_ao_insn_writefunction pcmmio_attach
Annotated Snippet
struct pcmmio_private {
spinlock_t pagelock; /* protects the page registers */
spinlock_t spinlock; /* protects the member variables */
unsigned int enabled_mask;
unsigned int active:1;
};
static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
int page, int port)
{
struct pcmmio_private *devpriv = dev->private;
unsigned long iobase = dev->iobase;
unsigned long flags;
spin_lock_irqsave(&devpriv->pagelock, flags);
if (page == 0) {
/* Port registers are valid for any page */
outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0));
outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1));
outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2));
} else {
outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0));
outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1));
outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2));
}
spin_unlock_irqrestore(&devpriv->pagelock, flags);
}
static unsigned int pcmmio_dio_read(struct comedi_device *dev,
int page, int port)
{
struct pcmmio_private *devpriv = dev->private;
unsigned long iobase = dev->iobase;
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&devpriv->pagelock, flags);
if (page == 0) {
/* Port registers are valid for any page */
val = inb(iobase + PCMMIO_PORT_REG(port + 0));
val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
} else {
outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
val = inb(iobase + PCMMIO_PAGE_REG(0));
val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
}
spin_unlock_irqrestore(&devpriv->pagelock, flags);
return val;
}
/*
* Each channel can be individually programmed for input or output.
* Writing a '0' to a channel causes the corresponding output pin
* to go to a high-z state (pulled high by an external 10K resistor).
* This allows it to be used as an input. When used in the input mode,
* a read reflects the inverted state of the I/O pin, such that a
* high on the pin will read as a '0' in the register. Writing a '1'
* to a bit position causes the pin to sink current (up to 12mA),
* effectively pulling it low.
*/
static int pcmmio_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
/* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
int port = s->index == 2 ? 0 : 3;
unsigned int chanmask = (1 << s->n_chan) - 1;
unsigned int mask;
unsigned int val;
mask = comedi_dio_update_state(s, data);
if (mask) {
/*
* Outputs are inverted, invert the state and
* update the channels.
*
* The s->io_bits mask makes sure the input channels
* are '0' so that the outputs pins stay in a high
* z-state.
*/
val = ~s->state & chanmask;
val &= s->io_bits;
pcmmio_dio_write(dev, val, 0, port);
}
Annotation
- Immediate include surface: `linux/module.h`, `linux/interrupt.h`, `linux/slab.h`, `linux/comedi/comedidev.h`.
- Detected declarations: `struct pcmmio_private`, `function pcmmio_dio_write`, `function pcmmio_dio_read`, `function current`, `function pcmmio_dio_insn_config`, `function pcmmio_reset`, `function pcmmio_stop_intr`, `function pcmmio_handle_dio_intr`, `function interrupt_pcmmio`, `function pcmmio_start_intr`.
- Atlas domain: Driver Families / drivers/comedi.
- Implementation status: source implementation candidate.
- Synchronization appears in or near this file; preserve lock ordering, sleepability, and interrupt-context constraints.
- IRQ or DMA behavior appears here, which is relevant to the selected PCIe/NVMe device path.
Implementation Notes
- This generated page is the file-by-file coverage layer; curated subsystem chapters should link here when they synthesize a multi-file control flow.
- Core OS pages should be promoted from atlas-only to deep-reviewed when they explain data structures, invariants, locking, lifecycle, and C implementation snippets.
- Driver-family pages are intentionally pattern-oriented unless they are part of the selected PCIe/NVMe representative device path.