|
Hello, for teaching purposes, I have a very simple driver built with RTDM to catch interrupts generated on the parallel port via a pulse generator. On receipt of a command passed by ioctl, the driver measures the time interval between two interrupts and stores the result (in fact, the sum of n time intervals, n being passed as parameter of ioctl). The result is returned to the user by a read command. If I call ioctl immediatly after the open command, the machine freezes and only the reset button is effective. If I insert a print command between open and ioctl, everything works fine. I use Xenomai 2.4.9.1 and Linux 2.6.29.5. The processor is a Pentium 4. I attach the sources of the driver and the user application. Any hint ? TIA Francois --
--
|
/**************************************************************************************** * pulseDrv.c V6 * oct 2009 (c) François Touchard ****************************************************************************************/
#include <linux/module.h>
#include <rtdm/rtdm_driver.h>
#include <semaphore.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#define DEBUG
#define VERSION "V6.0.2"
#include "pulse.h"
/**
* global buffer
*/
rtStat_t RtStat;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");
rtdm_irq_t irq;
rtdm_task_t meas_thr;
int IRQ_ON=0;
rtdm_sem_t sem;
/*************************************************************************************/
/* measurement thread */
/*************************************************************************************/
void measurement_thread(void *cookie)
{
struct timespec tc;
unsigned int deltatmus;
nanosecs_abs_t tcns, tpns, deltatns;
int count=0;
#ifdef DEBUG
rtdm_printk("thread launched. Maxcount : %d\n", RtStat.maxcount);
#endif
while (1) {
rtdm_sem_down(&sem);
tcns = rtdm_clock_read();
if (count > 0) {
deltatns = tcns - tpns;
deltatmus = ((long int) deltatns)/1000;
RtStat.sum = RtStat.sum + deltatmus;
if (count == 1) {
RtStat.min = deltatmus;
RtStat.max = deltatmus;
} else {
if (deltatmus < RtStat.min) RtStat.min = deltatmus;
if (deltatmus > RtStat.max) RtStat.max = deltatmus;
}
} else {
RtStat.sum = 0;
deltatmus = 0;
tpns=0;
}
#ifdef DEBUG
rtdm_printk("%s : got IRQ %d : %d mus (sum : %d, min : %d, max %d)\n",
DEVICE_NAME, count, deltatmus, RtStat.sum,
RtStat.min, RtStat.max);
#endif
tpns = tcns;
count ++;
if (count > RtStat.maxcount) {
#ifdef DEBUG
rtdm_printk("thread exit\n");
#endif
rtdm_task_destroy(&meas_thr);
}
}
}
/*************************************************************************************/
/* Interrupt routine */
/*************************************************************************************/
static int pulse_isr(rtdm_irq_t *irq)
{
rtdm_sem_up(&sem);
return 0;
}
/**
* open the device
* statistical data is reset to 0
*/
/*************************************************************************************/
/* Driver open */
/*************************************************************************************/
static int PulseOpen_nrt( struct rtdm_dev_context *context,
rtdm_user_info_t *user_info,
int oflags)
{
RtStat.sum = 0;
#ifdef DEBUG
rtdm_printk("%s : device opened in nrt context\n", DEVICE_NAME);
#endif
return 0;
}
static int PulseOpen_rt( struct rtdm_dev_context *context,
rtdm_user_info_t *user_info,
int oflags)
{
RtStat.sum = 0;
#ifdef DEBUG
rtdm_printk("%s : device opened in rt context\n", DEVICE_NAME);
#endif
return 0;
}
/*************************************************************************************/
/* Driver close */
/*************************************************************************************/
static int PulseClose_nrt( struct rtdm_dev_context *context,
rtdm_user_info_t *user_info)
{
if (IRQ_ON) {
rtdm_irq_free(&irq);
}
#ifdef DEBUG
rtdm_printk("%s : device closed in nrt context\n", DEVICE_NAME);
#endif
return 0;
}
static int PulseClose_rt( struct rtdm_dev_context *context,
rtdm_user_info_t *user_info)
{
if (IRQ_ON) {
rtdm_irq_free(&irq);
}
#ifdef DEBUG
rtdm_printk("%s : device closed in rt context\n", DEVICE_NAME);
#endif
return 0;
}
/************************************************************************************/
/* Driver ioctl */
/************************************************************************************/
static int PulseIoctl_nrt( struct rtdm_dev_context *context,
rtdm_user_info_t *user_info,
int cmd,
void *arg)
{
int ret;
switch (cmd) {
case RPP_ISR:
IRQ_ON=1;
#ifdef DEBUG
rtdm_printk("ISR measurement\n");
#endif
rtdm_irq_free(&irq);
// Stores maximum number of captured interrupts (passed in ioctl argument) in kernel space
ret = rtdm_safe_copy_from_user(user_info,
&RtStat.maxcount, arg, sizeof(int));
if (ret) {
rtdm_printk("%s : error on rtdm_safe_copy_from_user %d\n",
DEVICE_NAME, ret);
rtdm_irq_free(&irq);
return ret;
}
#ifdef DEBUG
rtdm_printk("%s : maxcount %d copied from user to kernel\n",
DEVICE_NAME, RtStat.maxcount);
#endif
// Semaphore initialisation
rtdm_sem_init(&sem, 0);
RtStat.sum = 0;
// Measurement task initialisation. No argument passed, priority : 30, non cyclic task
ret = rtdm_task_init(&meas_thr, "AcqPulse", measurement_thread,
NULL, 30, 0);
if (ret) {
rtdm_printk("%s : error on rtdm_task_init %d\n"
,DEVICE_NAME, ret);
}
// irq handler initialisation
ret = rtdm_irq_request(&irq, PulseInt_Irq,
pulse_isr, 0, "pulse", 0);
if (ret) {
rtdm_printk("%s : error on rtdm_irq_request %d\n"
,DEVICE_NAME, ret);
return ret;
}
#ifdef DEBUG
rtdm_printk("%s : IRQ enabled\n", DEVICE_NAME);
#endif
// enable interrupts on parallel port interface
outb(PulseCtr_Enable, PulseInt_Ctrl);
// wait for the end of measurement. Polling every 100 ms
rtdm_task_join_nrt(&meas_thr, 100);
rtdm_sem_destroy(&sem);
rtdm_irq_free(&irq);
// Disable interrupts on parallel port interface
outb(PulseCtr_Disable, PulseInt_Ctrl);
return 0;
case RPP_POLL:
#ifdef DEBUG
rtdm_printk("POLL measurement, arg %d\n", (int) arg);
#endif
return -1;
default:
rtdm_printk("invalid ioctl\n");
return -1;
}
}
/************************************************************************************/
/* Driver read */
/************************************************************************************/
static ssize_t PulseRead_rt( struct rtdm_dev_context *context,
rtdm_user_info_t *user_info,
void *buf,
size_t nbyte)
{
int ret;
char *out_pos = (char *) buf;
#ifdef DEBUG
rtdm_printk("%s : read device\n", DEVICE_NAME);
#endif
ret = rtdm_safe_copy_to_user(user_info, out_pos, &RtStat, sizeof(rtStat_t));
if (ret) {
rtdm_printk("%s : error on rtdm_copy_to_user %d\n", DEVICE_NAME, ret);
return ret;
}
#ifdef DEBUG
rtdm_printk("%s : copied from kernel to user\n", DEVICE_NAME);
#endif
return sizeof(rtStat_t);;
}
/*********************************************************************************/
/* Structure describing the device */
/*********************************************************************************/
static struct rtdm_device device = {
struct_version : RTDM_DEVICE_STRUCT_VER,
device_flags : RTDM_NAMED_DEVICE,
context_size : sizeof(rtStat_t),
device_name : DEVICE_NAME,
open_nrt : PulseOpen_nrt,
open_rt : PulseOpen_rt,
ops : {
close_nrt : PulseClose_nrt,
close_rt : PulseClose_rt,
ioctl_nrt : PulseIoctl_nrt,
ioctl_rt : PulseIoctl_nrt,
read_nrt : PulseRead_rt,
read_rt : PulseRead_rt,
write_nrt : NULL,
write_rt : NULL,
recvmsg_rt: NULL,
recvmsg_nrt: NULL,
sendmsg_rt: NULL,
sendmsg_nrt: NULL,
},
device_class : RTDM_CLASS_EXPERIMENTAL,
device_sub_class : 4711,
profile_version : 1,
driver_name : DRIVER_NAME,
driver_version : RTDM_DRIVER_VER(0,1,2),
peripheral_name : "Pulse",
provider_name : "esil_info",
proc_name : device.device_name,
};
int __init pulse_init(void) {
rtdm_dev_register (&device);
rtdm_printk("PulseDrv %s loaded. ", VERSION);
#ifdef DEBUG
rtdm_printk("DEBUG mode\n");
#else
rtdm_printk("NODEBUG mode\n");
#endif
return 0;
}
void __exit pulse_exit(void) {
rtdm_dev_unregister(&device, 1000);
rtdm_printk("PulseDrv %s unloaded\n", VERSION);
}
module_init(pulse_init);
module_exit(pulse_exit);
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
#include "pulse.h"
#define USER_NAME "pulse user"
#define DEBUG
int device;
int nmeas;
void *thread_code(void *cookie) {
int ret;
rtStat_t Stat;
float freq, period;
// device = rt_dev_open(DEVICE_NAME, 0);
device = open(DEVICE_NAME, 0);
if (device < 0) {
printf("%s : Error open %s %s\n", USER_NAME, DEVICE_NAME, strerror(device));
exit(1);
}
#ifdef DEBUG
printf("thread : opened\n");
#endif
// ret = rt_dev_ioctl(device, RPP_ISR, &nmeas);
ret = ioctl(device, RPP_ISR, &nmeas);
if (ret < 0) {
printf("%s : Error while ioctl : %s \n", USER_NAME, strerror(-ret));
exit(1);
}
#ifdef DEBUG
printf("thread : ioctl\n");
#endif
// ret = rt_dev_read(device, &Stat, sizeof(rtStat_t));
ret = read(device, &Stat, sizeof(rtStat_t));
if (ret < 0) {
printf("%s : Error while read : %s \n", USER_NAME, strerror(-ret));
exit(1);
}
period = ((float) Stat.sum/ (float)Stat.maxcount)/1.E6;
freq = 1./period;
printf ("period %f s frequency %f Hz \n", period, freq);
printf ("min : %f max : %f delta t : %f\n",
(float)Stat.min/1.E6, (float)Stat.max/1.E6,
(float)(Stat.max-Stat.min)/1.E6);
printf("thread : read\n");
// ret = rt_dev_close(device);
ret = close(device);
if (ret < 0) {
printf("Error while closing %s\n", USER_NAME);
exit(1);
}
printf("thread : closed\n");
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
int device, ret;
pthread_t tid;
pthread_attr_t attr;
struct sched_param sp;
if (argc == 2) {
nmeas = atoi(argv[1]);
} else {
nmeas = 5;
}
ret = mlockall(MCL_CURRENT | MCL_FUTURE);
if (ret) {
printf("error mlockall\n");
exit(1);
}
ret = pthread_attr_init(&attr);
if (ret) {
printf("error pthread_attr_init\n");
exit(1);
}
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
printf("error pthread_attr_setinheritsched\n");
exit(1);
}
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret) {
printf("error pthread_attr_setschedpolicy\n");
exit(1);
}
sp.sched_priority = 1;
ret = pthread_attr_setschedparam(&attr, &sp);
if (ret) {
printf("error pthread_attr_setschedparam\n");
exit(1);
}
ret = pthread_create(&tid, &attr, thread_code, NULL);
if (ret) {
printf("error pthread_create\n");
exit(1);
}
ret = pthread_join(tid, NULL);
if (ret) {
printf("error pthread_join\n");
exit(1);
}
printf("main : joined\n");
// scanf("%c", &ret);
return 0;
}
/*************************************************
* Fichier pulse.h
*************************************************/
#define DEVICE_NAME "Pulse"
#define DRIVER_NAME "PulseDrv"
typedef enum {
RPP_ISR,
RPP_POLL,
} Ioctl_ops;
typedef enum {
PulseInt_Port = 0x378, /*------------------------Data access */
PulseInt_Sts = PulseInt_Port+1, /*Status register */
PulseInt_Ctrl = PulseInt_Port+2, /*Control register */
PulseInt_Irq = 7, /*Interrupt vector */
PulseCtr_Enable = 0x10, /* Control register command */
PulseCtr_Disable = 0x00,
PulseSts_Ack = 0x40,
} PulseInt_t;
typedef struct RtStat_s {
long int sum;
int maxcount;
long int min;
long int max;
} rtStat_t;
_______________________________________________ Xenomai-help mailing list [email protected] https://mail.gna.org/listinfo/xenomai-help
