Hi!
> > I have... quite an interesting setup here.
> >
> > SMP machine, with special PCI card; that card has GPIOs and serial
> > ports. Unfortunately, there's only one interrupt, shared between
> > serials and GPIO pins, and serials are way too complex to be handled
> > by realtime layer.
> >
> > So I ended up with
> >
> > // we also have an interrupt handler:
> >
> >
> > ret = rtdm_irq_request(&my_context->irq_handle,
> > gpio_rt_config.irq, demo_interrupt,
> > RTDM_IRQTYPE_SHARED,
> > context->device->proc_name, my_context);
> >
> > and
> >
> > static int demo_interrupt(rtdm_irq_t *irq_context)
> > {
> > struct demodrv_context *ctx;
> > int dev_id;
> > int ret = RTDM_IRQ_HANDLED; // usual return value
> >
> >
> > unsigned pending, output;
> >
> > ctx = rtdm_irq_get_arg(irq_context, struct demodrv_context);
> > dev_id = ctx->dev_id;
> >
> > if (!ctx->ready) {
> > printk(KERN_CRIT "Unexpected interrupt\n");
> > return XN_ISR_PROPAGATE;
>
> Who sets ready and when? Looks racy.
Debugging aid; yes, this one is racy.
> > rtdm_lock_put(&ctx->lock);
> >
> > /* We need to propagate the interrupt, so that PMC-6L serials
> >
> >
> > work. Result is that interrupt latencies can't be
> >
> >
> > guaranteed when serials are in use. */
> >
> > return RTDM_IRQ_HANDLED;
> > }
> >
> > Unregistration is:
> > my_context->ready = 0;
> > rtdm_irq_disable(&my_context->irq_handle);
>
> Where is rtdm_irq_free? Again, this ready flag looks racy.
Aha, sorry, I quoted wrong snippet. rtdm_irq_free() follows
immediately, like this:
int demo_close_rt(struct rtdm_dev_context *context,
rtdm_user_info_t *user_info)
{
struct demodrv_context *my_context;
rtdm_lockctx_t lock_ctx;
// get the context
my_context = (struct demodrv_context *)context->dev_private;
// if we need to do some stuff with preemption disabled:
rtdm_lock_get_irqsave(&my_context->lock, lock_ctx);
my_context->ready = 0;
rtdm_irq_disable(&my_context->irq_handle);
// free irq in RTDM
rtdm_irq_free(&my_context->irq_handle);
// destroy our interrupt signal/event
rtdm_event_destroy(&my_context->irq_event);
// other stuff here
rtdm_lock_put_irqrestore(&my_context->lock, lock_ctx);
return 0;
}
Now... I'm aware that lock_get/put around irq_free should be
unneccessary, as should be irq_disable and my ->ready flag. Those were
my attempts to work around the problem. I'll attach the full source at
the end.
> > Unfortunately, when the userspace app is ran and killed repeatedly (so
> > that interrupt is registered/unregistered all the time), I get
> > oopses in __ipipe_dispatch_wired() -- it seems to call into the NULL
> > pointer.
> >
> > I decided that "wired" interrupt when the source is shared between
> > Linux and Xenomai, is wrong thing, so I disable "wired" interrupts
> > altogether, but that only moved oops to __virq_end.
>
> This is wrong. The only way to get a determistically shared IRQs across
> domains is via the wired path, either using the pattern Gilles cited or,
> in a slight variation, signaling down via a separate rtdm_nrtsig.
For now, I'm trying to get it not to oops; deterministic latencies are
the next topic :-(.
The hardware is little unusual that it generates interrupts at 1kHz,
whether realtime driver is loaded or not.
Pavel
/**********************************************************/
/* HARD REAL TIME RTDM Driver Skeleton V1.1 */
/* */
/* Based on code of Jan Kiszka - thanks Jan! */
/* (C) 2006 Jan Kiszka, www.captain.at */
/* (C) 2010 Pavel Machek, sysgo */
/* License: GPL */
/**********************************************************/
/* Note: Interrupt is shared between serial lines and GPIOs. That
means that interrupt latencies can't be guaranteed when serials are
in use.
*/
#include <linux/mman.h>
#include <rtdm/rtdm_driver.h>
#include "pmc6l-rt.h"
char dummy_buffer[BUFSIZE];
int events;
// our driver context struct: used for storing various information
struct demodrv_context {
rtdm_irq_t irq_handle;
rtdm_lock_t lock;
int dev_id;
u64 last_timestamp;
rtdm_event_t irq_event;
volatile unsigned long irq_event_lock;
volatile int irq_events;
int64_t timeout;
void *buf;
int ready;
};
#define GPIO_CONF_LOW 0xb0
#define GPIO_CONF_HIGH 0xb2
#define GPIO_STATUS_IN 0xb4
#define GPIO_STATUS_OUT 0xb6
#define GPIO_IRQ_ID 0x2a
#define iowrite(bits, val, adr) iowrite##bits(cpu_to_be##bits(val), adr)
#define ioread(bits, adr) be##bits##_to_cpu(ioread##bits(adr))
#define pgwrite(dat, adr) iowrite(16, (dat), gpio_rt_config.mmio+(adr))
#define pgread(adr) ioread(16, gpio_rt_config.mmio+(adr))
/**********************************************************/
/* INTERRUPT HANDLING */
/**********************************************************/
/*
* Demo interrupt code; when GPIO #2 is changed, GPIO #4 is toggled.
*/
static int demo_interrupt(rtdm_irq_t *irq_context)
{
struct demodrv_context *ctx;
int dev_id;
int ret = RTDM_IRQ_HANDLED; // usual return value
unsigned pending, output;
ctx = rtdm_irq_get_arg(irq_context, struct demodrv_context);
dev_id = ctx->dev_id;
if (!ctx->ready) {
printk(KERN_CRIT "Unexpected interrupt\n");
return XN_ISR_PROPAGATE;
}
#if 0
rtdm_lock_get(&ctx->lock);
pending = pgread(GPIO_IRQ_ID);
pgwrite(0, GPIO_IRQ_ID);
output = pgread(GPIO_STATUS_OUT);
output ^= (1<<3);
pgwrite(output, GPIO_STATUS_OUT);
// do stuff
if (events > 1000) {
rtdm_event_signal(&ctx->irq_event);
events = 0;
}
events++;
rtdm_lock_put(&ctx->lock);
// those return values were dropped from the RTDM
// ret = RTDM_IRQ_ENABLE | RTDM_IRQ_PROPAGATE;
/* We need to propagate the interrupt, so that PMC-6L serials
work. Result is that interrupt latencies can't be
guaranteed when serials are in use. */
// return XN_ISR_PROPAGATE;
return RTDM_IRQ_HANDLED;
#else
pending = pgread(GPIO_IRQ_ID);
pgwrite(0, GPIO_IRQ_ID);
output = pgread(GPIO_STATUS_OUT);
output = output & (~(1<<3));
if (pgread(GPIO_STATUS_IN) & (1<<1))
output |= (1<<3);
pgwrite(output, GPIO_STATUS_OUT);
// those return values were dropped from the RTDM
// ret = RTDM_IRQ_ENABLE | RTDM_IRQ_PROPAGATE;
/* We need to propagate the interrupt, so that PMC-6L serials
work. Result is that interrupt latencies can't be
guaranteed when serials are in use. */
// return XN_ISR_PROPAGATE;
return RTDM_IRQ_HANDLED;
#endif
}
/**********************************************************/
/* DRIVER OPEN */
/**********************************************************/
int demo_open_rt(struct rtdm_dev_context *context,
rtdm_user_info_t *user_info,
int oflags)
{
struct demodrv_context *my_context;
int dev_id = context->device->device_id;
int ret;
// get the context for our driver - used to store driver info
my_context = (struct demodrv_context *)context->dev_private;
// we also have an interrupt handler:
ret = rtdm_irq_request(&my_context->irq_handle, gpio_rt_config.irq,
demo_interrupt,
RTDM_IRQTYPE_SHARED, context->device->proc_name,
my_context);
if (ret < 0)
return ret;
/* IPC initialisation - cannot fail with used parameters */
rtdm_lock_init(&my_context->lock);
rtdm_event_init(&my_context->irq_event, 0);
my_context->dev_id = dev_id;
my_context->irq_events = 0;
my_context->irq_event_lock = 0;
my_context->ready = 1;
my_context->timeout = 0; // wait INFINITE
// enable interrupt in RTDM
rtdm_irq_enable(&my_context->irq_handle);
return 0;
}
/**********************************************************/
/* DRIVER CLOSE */
/**********************************************************/
int demo_close_rt(struct rtdm_dev_context *context,
rtdm_user_info_t *user_info)
{
struct demodrv_context *my_context;
rtdm_lockctx_t lock_ctx;
// get the context
my_context = (struct demodrv_context *)context->dev_private;
// if we need to do some stuff with preemption disabled:
rtdm_lock_get_irqsave(&my_context->lock, lock_ctx);
my_context->ready = 0;
rtdm_irq_disable(&my_context->irq_handle);
// free irq in RTDM
rtdm_irq_free(&my_context->irq_handle);
// destroy our interrupt signal/event
rtdm_event_destroy(&my_context->irq_event);
// other stuff here
rtdm_lock_put_irqrestore(&my_context->lock, lock_ctx);
return 0;
}
/**********************************************************/
/* DRIVER READ */
/**********************************************************/
int demo_read_rt(struct rtdm_dev_context *context,
rtdm_user_info_t *user_info, void *buf, size_t nbyte)
{
struct demodrv_context *ctx;
int dev_id;
char *out_pos = (char *)buf;
rtdm_toseq_t timeout_seq;
int ret;
// zero bytes requested ? return!
if (nbyte == 0)
return 0;
// check if R/W actions to user-space are allowed
if (user_info && !rtdm_rw_user_ok(user_info, buf, nbyte))
return -EFAULT;
ctx = (struct demodrv_context *)context->dev_private;
dev_id = ctx->dev_id;
// wait: if ctx->timeout = 0, it will block infintely until
// rtdm_event_signal(&ctx->irq_event); is called from our
// interrupt routine
ret = rtdm_event_timedwait(&ctx->irq_event, ctx->timeout, &timeout_seq);
// now write the requested stuff to user-space
if (rtdm_copy_to_user(user_info, out_pos,
dummy_buffer, BUFSIZE) != 0) {
ret = -EFAULT;
} else {
ret = BUFSIZE;
}
return ret;
}
/**********************************************************/
/* DRIVER OPERATIONS */
/**********************************************************/
static struct rtdm_device demo_device = {
struct_version: RTDM_DEVICE_STRUCT_VER,
device_flags: RTDM_NAMED_DEVICE,
context_size: sizeof(struct demodrv_context),
device_name: DEV_FILE,
/* open and close functions are not real-time safe due kmalloc
and kfree. If you do not use kmalloc and kfree, and you made
sure that there is no syscall in the open/close handler, you
can declare the open_rt and close_rt handler.
*/
open_rt: NULL,
open_nrt: demo_open_rt,
ops: {
close_rt: NULL,
close_nrt: demo_close_rt,
ioctl_rt: NULL,
ioctl_nrt: NULL,
read_rt: demo_read_rt,
read_nrt: NULL,
write_rt: NULL,
write_nrt: NULL,
recvmsg_rt: NULL,
recvmsg_nrt: NULL,
sendmsg_rt: NULL,
sendmsg_nrt: NULL,
},
device_class: RTDM_CLASS_EXPERIMENTAL,
device_sub_class: 222,
driver_name: DRV_NAME,
peripheral_name: DEV_FILE_NAME,
provider_name: "-",
proc_name: demo_device.device_name,
};
/**********************************************************/
/* INIT DRIVER */
/**********************************************************/
int init_module(void)
{
pmc_gpio_rt_config();
printk("realtime driver at irq %d mmio %x\n",
gpio_rt_config.irq, gpio_rt_config.mmio);
return rtdm_dev_register(&demo_device);
}
/**********************************************************/
/* CLEANUP DRIVER */
/**********************************************************/
void cleanup_module(void)
{
rtdm_dev_unregister(&demo_device, 1000);
}
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RTDM Real Time Driver Example");
--- user app ----
/**********************************************************/
/* HARD REAL TIME RTDM Driver User-Space Application */
/* */
/* Based on code of Jan Kiszka - thanks Jan! */
/* (C) 2006 Jan Kiszka, www.captain.at */
/* (C) 2010 Pavel Machek, sysgo */
/* License: GPL */
/**********************************************************/
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <native/task.h>
#include <native/timer.h>
#include <rtdm/rtdm.h>
#include "pmc6l-rt.h"
unsigned int my_state = 0;
int timer_started = 0;
int my_fd = -1;
int shutdownnow = 0;
RT_TASK my_task;
// --s-ms-us-ns
RTIME my_task_period_ns = 1000000000llu;
/**********************************************************/
/* CLOSE RT DRIVER */
/**********************************************************/
static int close_file( int fd, unsigned char *name) {
int ret,i=0;
do {
i++;
ret = rt_dev_close(fd);
switch(-ret){
case EBADF: printf("%s -> invalid fd or context\n",name);
break;
case EAGAIN: printf("%s -> EAGAIN (%d times)\n",name,i);
rt_task_sleep(50000); // wait 50us
break;
case 0: printf("%s -> closed\n",name);
break;
default: printf("%s -> ???\n",name);
break;
}
} while (ret == -EAGAIN && i < 10);
return ret;
}
/**********************************************************/
/* CLEANING UP */
/**********************************************************/
void cleanup_all(void) {
if (my_state & STATE_FILE_OPENED) {
close_file( my_fd, DEV_FILE " (user)");
my_state &= ~STATE_FILE_OPENED;
}
if (my_state & STATE_TASK_CREATED) {
printf("delete my_task\n");
rt_task_delete(&my_task);
my_state &= ~STATE_TASK_CREATED;
}
if (timer_started) {
printf("stop timer\n");
rt_timer_stop();
}
}
void catch_signal(int sig) {
shutdownnow = 1;
cleanup_all();
printf("exit\n");
return;
}
/**********************************************************/
/* REAL TIME TASK */
/**********************************************************/
void my_task_proc(void *arg) {
int ret;
ssize_t sz = sizeof(RTIME);
ssize_t written = 0;
ssize_t read = 0;
int counter = 0;
int readbackcounter;
unsigned char buf[17] = "CAPTAIN WAS HERE\0";
unsigned char buf2[17] = "XXXXXXXXXXXXXXXX\0";
/* no periodic task, due blocking read from the RT driver (see below
too)
ret = rt_task_set_periodic(NULL, TM_NOW,
rt_timer_ns2ticks(my_task_period_ns));
if (ret) {
printf("error while set periodic, code %d\n",ret);
goto exit_my_task;
}
*/
while (1) {
sprintf(buf, "CAPTAIN %d", counter);
counter++;
if (counter > 100) counter = 0;
/* switch to primary mode */
ret = rt_task_set_mode(0, T_PRIMARY, NULL);
if (ret) {
printf("error while rt_task_set_mode, code %d\n",ret);
goto exit_my_task;
}
sz = sizeof(buf2);
read = rt_dev_read(my_fd, &buf2, sizeof(buf2));
if (read == sz ) {
printf("READ: read=%s\n",buf2);
} else {
if (read < 0 ) {
printf("error while rt_dev_read, code
%d\n",read);
} else {
printf("only %d / %d byte received \n",read,sz);
}
}
// read blocks, so check if user hit CTRL-C meanwhile
// otherwise we segfault when mmap'ing
if (shutdownnow == 1) break;
}
exit_my_task:
if (my_state & STATE_FILE_OPENED) {
if (!close_file( my_fd, DEV_FILE " (write)")) {
my_state &= ~STATE_FILE_OPENED;
}
}
printf("exit\n");
}
/**********************************************************/
/* MAIN: mainly RT task initialization */
/**********************************************************/
int main(int argc, char* argv[]) {
int ret = 0;
signal(SIGTERM, catch_signal);
signal(SIGINT, catch_signal);
printf("PRESS CTRL-C to EXIT\n");
/* no memory-swapping for this programm */
mlockall(MCL_CURRENT | MCL_FUTURE);
/* start timer */
ret = rt_timer_start(TM_ONESHOT);
switch (ret) {
case 0: printf("timer started\n");
timer_started = 1;
break;
case -EBUSY: printf("timer is running\n");
timer_started = 0;
break;
case -ENOSYS: printf("can't start timer\n");
return ret;
}
/* open DEV_FILE */
my_fd = rt_dev_open( DEV_FILE, 0);
if (my_fd < 0) {
printf("can't open %s\n", DEV_FILE);
goto error;
}
my_state |= STATE_FILE_OPENED;
printf("%s opened\n", DEV_FILE);
/* create my_task */
ret = rt_task_create(&my_task,"my_task",0,50,0);
if (ret) {
printf("failed to create my_task, code %d\n",ret);
goto error;
}
my_state |= STATE_TASK_CREATED;
printf("my_task created\n");
/* start my_task */
printf("starting my_task\n");
ret = rt_task_start(&my_task,&my_task_proc,NULL);
if (ret) {
printf("failed to start my_task, code %d\n",ret);
goto error;
}
pause();
return 0;
error:
cleanup_all();
return ret;
}
_______________________________________________
Xenomai-help mailing list
[email protected]
https://mail.gna.org/listinfo/xenomai-help