I'm having an issue reading data over the pc104 bus from a kernel
module. I am able to read just fine from a userspace app using mmap.
Please tell me what the difference is between the access I am doing in
userspace and kernel space. The externel hardware generates the
interrupt and I see it trap to the interrupt handler. At that point it
tries to do a read over the bus but it is not latching the data right.
I get random results as if I'm just reading random memory although I
do see the mem_r line strobe low. I know you can't use mmap in a
kernel module but from what I've read it sounded like the ISA memory
region could be accessed using __ioremap(). Any suggestions would be
great.
-Elliot
TS7300 - 2.4.26-ts11 #35
Here is my userspace app:
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include "stdtypes.h"
#include "peekpoke.h"
/*
Other possible bases
0x21810000
0x21A00000
0x21A10000
*/
#define PC104_BASE 0x21A00000L
int passed=0, failed=0, reads=0;
uint16 expected=0x0;
int t_delay = 0;
clock_t begin, end;
delay( volatile int x )
{
volatile int k=0;
while( k < x ) k++;
}
int main(int argc, char **argv){
uint16 reading;
volatile uint16 *start;
long seconds;
uint32 count = 0;
int fd = open("/dev/mem", O_RDWR|O_SYNC);
start = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED,
reading = PEEK16((long)start);
expected = reading + 1;
while(count < 10000000)
{
reading = PEEK16((long)start);
if(reading == expected)
{
passed++;
expected++;
}
else
{
failed++;
expected=reading+1;
}
count++;
}
printf("%d READS\n---------\nPASSED: %d FAILED: %d\n",
count, passed, failed);
close(fd);
return 0;
}
/***********************************/
Here is my kernel module:
// Linux Device Driver Template/Skeleton
// Kernel Module
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "peekpoke.h"
#define PC104_MAJOR 63
#define PC104_NAME "PC104"
#define PC104_INTERRUPT 22 // Corresponds to IRQ5
#define PC104_BASE 0x21A00000L
#define PC104_MEM_SIZE 4
MODULE_AUTHOR("Elliot Buller: Colorado State 2007");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PC104 Data Acquisition Card Driver");
extern void disable_irq (unsigned int irq);
static unsigned int counter = 0;
static int interruptcount = 0;
static volatile uint16_t *base;
static char string[150];
DECLARE_WAIT_QUEUE_HEAD(pc104_wait);
static int data_not_ready = 1;
static int pc104_open (struct inode *inode, struct file *file) {
printk("pc104_open\n");
return 0;
}
static int pc104_release (struct inode *inode, struct file *file) {
printk("pc104_release\n");
return 0;
}
static ssize_t pc104_read (struct file *file, char *buf,
size_t count, loff_t *ppos) {
int len, err;
// check if we have data - if not, sleep
// wake up in interrupt_handler
while (data_not_ready) {
interruptible_sleep_on(&pc104_wait);
}
data_not_ready = 1;
/* Add read code here */
return len;
}
// write function called when to /dev/skeleton is written
static ssize_t pc104_write (struct file *file, const char *buf,
size_t count, loff_t *ppos) {
int err;
err = copy_from_user(string,buf,count);
if (err != 0)
return -EFAULT;
counter += count;
return count;
}
// interrupt handler
static int interrupt_handler(void)
{
interruptcount++;
printk(">>> PC104 BUS INT: interruptcount=%d\n", interruptcount);
printk("PC104 Samples: 0x%04X\n", PEEK16(base));
/* Data ready. Wake up userspace process */
data_not_ready = 0;
wake_up_interruptible(&pc104_wait);
return IRQ_HANDLED;
}
// define which file operations are supported
struct file_operations pc104_fops = {
.owner = THIS_MODULE,
.llseek = NULL,
.read = pc104_read,
.write = pc104_write,
.readdir = NULL,
.poll = NULL,
.ioctl = NULL,
.mmap = NULL,
.open = pc104_open,
.flush = NULL,
.release = pc104_release,
.fsync = NULL,
.fasync = NULL,
.lock = NULL,
.readv = NULL,
.writev = NULL,
};
// initialize module (and interrupt)
static int pc104_init_module (void) {
int i;
int ret;
printk("<1>Initializing PC104 module\n");
i = register_chrdev (PC104_MAJOR, PC104_NAME, &pc104_fops);
if (i != 0) return - EIO;
// INIT INTERRUPT
ret = request_irq(PC104_INTERRUPT, &interrupt_handler,
SA_INTERRUPT, "pc104_driver", NULL);
/* Check to see if ISA memory in use */
if (check_mem_region(PC104_BASE, PC104_MEM_SIZE)) {
printk("pc104: memory already in use\n");
return -EBUSY;
}
/* Request memory space */
request_mem_region(PC104_BASE, PC104_MEM_SIZE, PC104_NAME);
/* Warning: Cannot use ioremap as it adds offset */
base = (volatile uint16_t*)__ioremap(PC104_BASE, PC104_MEM_SIZE, 0);
if(base == NULL)
{
printk("Error mapping memory!\n");
return -EBUSY;
}
printk("Memory mapped: 0x%08X\n", base);
return 0;
}
// close and cleanup module
static void pc104_cleanup_module (void) {
printk("<1>Cleaning up PC104 module\n");
iounmap((void*)base);
release_mem_region(base, PC104_MEM_SIZE);
unregister_chrdev (PC104_MAJOR, PC104_NAME);
disable_irq(PC104_INTERRUPT);
free_irq(PC104_INTERRUPT, NULL);
}
module_init(pc104_init_module);
module_exit(pc104_cleanup_module);
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/ts-7000/
<*> Your email settings:
Individual Email | Traditional
<*> To change settings online go to:
http://groups.yahoo.com/group/ts-7000/join
(Yahoo! ID required)
<*> To change settings via email:
<*> To unsubscribe from this group, send an email to:
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/
|