Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      


Xenomai: Hard Real Time Driver Example Tutorial with MMAP using the RTDM (Real Time Driver Model)



Driver source code: realtimedriver.c - version 1.1

Real Time Driver Example Tutorial Home


Releases: 07-MAR-2006: V1.1 released
26-FEB-2006: V1.0 released

Bugfixes:

07-MAR-2006: The common return value from an real time interrupt routine is to return RTDM_IRQ_HANDLED. Forwarding interrupts to the non-realtime domain is not a common use-case of realtime device drivers, so usually DON'T DO THIS. But in our case, where we grab the timer interrupt, we need to propagate the INT with the XN_ISR_PROPAGATE return value, so that Linux sees the timer.
04-MAR-2006: The RTDM API changed, so we need to return XN_ISR_PROPAGATE from our ISR in order to let Linux also see the timer/parallel port interrupt. Otherwise, if the interrupt should not be passed to Linux, return RTDM_IRQ_HANDLED.
26-FEB-2006: In the interrupt handler:
Change ret = RTDM_IRQ_ENABLE; to ret = RTDM_IRQ_ENABLE | RTDM_IRQ_PROPAGATE; so that Linux sees the timer / parallel port interrupt too.

NO REPRODUCTION OR PUBLISHING ON ANY OTHER WEBSITE THAN ON CAPTAIN.AT IS PERMITTED.

/**********************************************************/
/*    HARD REAL TIME RTDM Driver Skeleton V1.1            */
/*                                                        */
/*    Based on code of Jan Kiszka - thanks Jan!           */
/*    (C) 2006 Jan Kiszka, www.captain.at                 */
/*    License: GPL                                        */
/**********************************************************/

#include <linux/mman.h>
#include <rtdm/rtdm_driver.h>
#include "inc.h"

char dummy_buffer[BUFSIZE];
int  events;
int  ioctlvalue;

// 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;
    rtdm_user_info_t        *mapped_user_info;
    void                    *mapped_user_addr;
};

#ifdef USEMMAP
static void demo_vm_open(struct vm_area_struct *vma)
{
    printk("opening %p, private_data = %p\n", vma, vma->vm_private_data);
}
static void demo_vm_close(struct vm_area_struct *vma)
{
    printk("releasing %p, private_data = %p\n", vma, vma->vm_private_data);
}
static struct vm_operations_struct mmap_ops = {
    .open = demo_vm_open,
    .close = demo_vm_close,
};
#endif

/**********************************************************/
/*            INTERRUPT HANDLING                          */
/**********************************************************/
static int demo_interrupt(rtdm_irq_t *irq_context)
{
    struct demodrv_context *ctx;
    int           dev_id;
//    timestamp, if needed, can be obtained list this:
//    u64           timestamp = rtdm_clock_read();
    int           ret = RTDM_IRQ_HANDLED; // usual return value

    ctx = rtdm_irq_get_arg(irq_context, struct demodrv_context);
    dev_id    = ctx->dev_id;

    rtdm_lock_get(&ctx->lock);

    // do stuff
#ifdef TIMERINT
    if (events > 100) {
        rtdm_event_signal(&ctx->irq_event);
        events = 0;
    }
#else
    rtdm_event_signal(&ctx->irq_event);
#endif
    events++;
        
    rtdm_lock_put(&ctx->lock);
    // those return values were dropped from the RTDM
    // ret = RTDM_IRQ_ENABLE | RTDM_IRQ_PROPAGATE;

#ifdef TIMERINT
    // Only propagate the timer interrupt, so that linux sees it.
    // Forwarding interrupts to the non-realtime domain is not a common
    //     use-case of realtime device drivers, so usually DON'T DO THIS.
    // But here we grab the important timer interrupt, so we need to propagate it.
    return XN_ISR_PROPAGATE;
#else
    // signal interrupt is handled and don't propagate the interrupt to linux
    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;
#ifdef USEMMAP
    unsigned long vaddr;
#endif
    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;

#ifdef USEMMAP
    // allocate and prepare memory for our buffer
    my_context->buf = kmalloc(BUFFER_SIZE, 0);
    /* mark pages reserved so that remap_pfn_range works */
    for (vaddr = (unsigned long)my_context->buf;
         vaddr < (unsigned long)my_context->buf + BUFFER_SIZE;
         vaddr += PAGE_SIZE)
        SetPageReserved(virt_to_page(vaddr));
    // write some test value to the start of our buffer
    *(int *)my_context->buf = 1234;

    my_context->mapped_user_addr = NULL;
#endif

    // we also have an interrupt handler:
#ifdef TIMERINT
    ret = rtdm_irq_request(&my_context->irq_handle, TIMER_INT, demo_interrupt,
                 0, context->device->proc_name, my_context);
#else
    ret = rtdm_irq_request(&my_context->irq_handle, PAR_INT, demo_interrupt,
                 0, context->device->proc_name, my_context);
#endif
    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->timeout = 0; // wait INFINITE

#ifndef TIMERINT
    //set port to interrupt mode; pins are output
    outb_p(0x10, BASEPORT + 2);
#endif
    // 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;
#ifdef USEMMAP
    unsigned long vaddr;
#endif
    // get the context
    my_context = (struct demodrv_context *)context->dev_private;

#ifdef USEMMAP
    // printk some test value
    printk("%d\n", *((int *)my_context->buf + 10));

    // munmap our buffer
    if (my_context->mapped_user_addr) {
        int ret = rtdm_munmap(my_context->mapped_user_info,
                              my_context->mapped_user_addr, BUFFER_SIZE);
        printk("rtdm_munmap = %p, %d\n", my_context->mapped_user_info, ret);
    }

    /* clear pages reserved */
    for (vaddr = (unsigned long)my_context->buf;
         vaddr < (unsigned long)my_context->buf + BUFFER_SIZE;
         vaddr += PAGE_SIZE)
        ClearPageReserved(virt_to_page(vaddr));

    kfree(my_context->buf);
#endif

    // if we need to do some stuff with preemption disabled:
    rtdm_lock_get_irqsave(&my_context->lock, lock_ctx);
    // other stuff here
    rtdm_lock_put_irqrestore(&my_context->lock, lock_ctx);

    // free irq in RTDM
    rtdm_irq_free(&my_context->irq_handle);
    // destroy our interrupt signal/event
    rtdm_event_destroy(&my_context->irq_event);
    return 0;
}

/**********************************************************/
/*            DRIVER IOCTL                                */
/**********************************************************/
int demo_ioctl_rt(struct rtdm_dev_context   *context,
                  rtdm_user_info_t          *user_info,
                  int                       request,
                  void                      *arg)
{
    struct demodrv_context  *my_context;
#ifdef USEMMAP
    int err;
#endif
    int ret = 0;
    my_context = (struct demodrv_context *)context->dev_private;

    switch (request) {
        case MMAP: // set mmap pointer
#ifdef USEMMAP
          printk("buf = %p:%x\n", my_context->buf, *(int *)my_context->buf);
      
          err = rtdm_mmap_to_user(user_info, my_context->buf, BUFFER_SIZE,
                              PROT_READ|PROT_WRITE, (void **)arg, &mmap_ops,
                              (void *)0x12345678);
          if (!err) {
              my_context->mapped_user_info = user_info;
              my_context->mapped_user_addr = *(void **)arg;
          }
          printk("rtdm_mmap = %p %d\n", my_context->mapped_user_info, err);
#else
          return -EPERM;
#endif
          break;
        case GETVALUE: // write "ioctlvalue" to user
            if (user_info) {
                if (!rtdm_rw_user_ok(user_info, arg, sizeof(int)) ||
                    rtdm_copy_to_user(user_info, arg, &ioctlvalue,
                                      sizeof(int)))
                    return -EFAULT;
            } else
                memcpy(arg, &ioctlvalue, sizeof(int));
            break;
        case SETVALUE: // read "ioctlvalue" from user
            if (user_info) {
                if (!rtdm_read_user_ok(user_info, arg, sizeof(int)) ||
                    rtdm_copy_from_user(user_info, &ioctlvalue, arg,
                                        sizeof(int)))
                    return -EFAULT;
            }
            break;
        default:
            ret = -ENOTTY;
    }
    return ret;
}

/**********************************************************/
/*            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;
//    rtdm_lockctx_t          lock_ctx;
    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;

    // in case we need to check if reading is allowed (locking)
/*    if (test_and_set_bit(0, &ctx->in_lock))
        return -EBUSY;
*/
/*  // if we need to do some stuff with preemption disabled:
    rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
    // stuff here
    rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
*/

    // 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 WRITE                                */
/**********************************************************/
int demo_write_rt(struct rtdm_dev_context *context,
                   rtdm_user_info_t *user_info,
                   const void *buf, size_t nbyte)
{
    struct demodrv_context *ctx;
    int                     dev_id;
    char                    *in_pos = (char *)buf;
    int                     ret;

    if (nbyte == 0)
        return 0;
    if (user_info && !rtdm_read_user_ok(user_info, buf, nbyte))
        return -EFAULT;

    ctx    = (struct demodrv_context *)context->dev_private;
    dev_id = ctx->dev_id;

    // make write operation atomic
/*  ret = rtdm_mutex_timedlock(&ctx->out_lock,
              ctx->config.rx_timeout, &timeout_seq);
    if (ret)
        return ret; */

/*  // if we need to do some stuff with preemption disabled:
    rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
    // stuff here
    rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
*/

    // now copy the stuff from user-space to our kernel dummy_buffer
    if (rtdm_copy_from_user(user_info, dummy_buffer,
                             in_pos, BUFSIZE) != 0) {
        ret = -EFAULT;
    } else {
       ret = BUFSIZE;
    }

    // used when it is atomic
//   rtdm_mutex_unlock(&ctx->out_lock);
    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:      demo_ioctl_rt, // rtdm_mmap_to_user is not RT safe

        read_rt:        demo_read_rt,
        read_nrt:       NULL,

        write_rt:       demo_write_rt,
        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)
{
    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");


Last-Modified: Tue, 07 Mar 2006 16:51:17 GMT

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top