kernelnewbies.kernelnewbies.org archive mirror
 help / color / mirror / Atom feed
* Timing issues with RPi GPIO misc char driver
@ 2014-09-20 12:52 mhornung.linux at gmail.com
       [not found] ` <CAHnt0GXeTWArTeXAyoxpvX3A4gpXSg3v1Pu+sVhpRc2A9UZ30Q@mail.gmail.com>
  2014-09-30 12:49 ` mhornung.linux at gmail.com
  0 siblings, 2 replies; 3+ messages in thread
From: mhornung.linux at gmail.com @ 2014-09-20 12:52 UTC (permalink / raw)
  To: kernelnewbies

Hello,

I am trying to do my first driver, a Raspberry Pi misc char driver for
controlling 433 MHz power plugs. The transmitter is a 433 MHz radio module
connected to one of the RPi's GPIOs, the receiver is a 433 MHz power plug. The
power plug expects a specific protocol which is normally send by an encoder
chip (PT2260 or PT2262) within the appropriate remote control. The char driver
shall replace those encoder chips and generate the correct bit sequence by the
GPIO.

I already have a user-space library [1] which works if I send the codeword
multiple times (no real time kernel). Now I am trying to do this in kernel space
which is where I ran into the same problem. The socket does only switch if I
send the codeword multiple times (e.g. #define REPEAT 3).

I would really appreciate any hint on how to get around this problem. Any comment
on the code in general is also highly desirable.

With best regards

Michael

[1] https://github.com/graznik/librsswitch

<code>

#include <linux/cdev.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>

#define GPIO4     4  /* The default GPIO pin */
#define REPEAT    1  /* Times to repeat the codeword */
#define HIGH      1
#define LOW       0
#define PULSE_LEN 350

/*
 * Each encoder chip used in the power socket remote controls can be described
 * with the following struct.
*/
struct Encoder {
        char **groups;    /* Group identifiers                  */
        uint ngroups;     /* Number of groups                   */
        char **sockets;   /* Socket identifiers                 */
        uint nsockets;    /* Number of sockets within one group */
        char **data;      /* On or off                          */
        uint ndata;       /* On or off => 2                     */
        uint pulse_len;   /* This might differ                  */
};

/* The 433 MHz sender must be connected to one of these pins */
static uint valid_gpios[] = {4, 17, 21, 22, 23, 24, 25};

static int send_pin = GPIO4; /* The default pin */
module_param(send_pin, int, 0644);
MODULE_PARM_DESC(send_pin, "GPIO the 433 MHz sender is connected to");

static void transmit(int nhigh, int nlow)
{
        /*
         * FIXME: PULSE_LEN is the pulse length in us. This should be a
         parameter in the future, depending on the encoder chip within
         the remote control.
        */
        gpio_set_value(send_pin, HIGH);
        udelay(PULSE_LEN * nhigh);
        gpio_set_value(send_pin, LOW);
        udelay(PULSE_LEN * nlow);
}
/**
 * Sends a Tri-State "0" Bit
 *            _     _
 * Waveform: | |___| |___
 */
static void send_0(void)
{
        transmit(1, 3);
        transmit(1, 3);
}

/**
 * Sends a Tri-State "1" Bit
 *            ___   ___
 * Waveform: |   |_|   |_
 */
static void send_1(void)
{
        transmit(3, 1);
        transmit(3, 1);
}

/**
 * Sends a Tri-State "F" Bit
 *            _     ___
 * Waveform: | |___|   |_
 */
static void send_f(void)
{
        transmit(1, 3);
        transmit(3, 1);
}

/**
 * Sends a "Sync" Bit
 *                       _
 * Waveform Protocol 1: | |_______________________________
 *                       _
 * Waveform Protocol 2: | |__________
 */
static void send_sync(void)
{
        transmit(1, 31);
}

static void send_tris(char *codeword)
{
        int i = 0;
        unsigned long flags;

        local_irq_save(flags);
        while (codeword[i] != '\0') {
                switch (codeword[i]) {
                case '0':
                        send_0();
                        break;
                case '1':
                        send_1();
                        break;
                case 'F':
                        send_f();
                        break;
                }
                i++;
        }
        send_sync();
        local_irq_restore(flags);
}

/**
 * Configure struct for the PT2260 encoder
 * @param pt2260     Pointer to a pt2260 instance
 */
static int pt2260_init(struct Encoder *pt2260)
{
        char * const groups[]  = {"1FFF", "F1FF", "FF1F", "FFF1"};
        char * const sockets[] = {"1FF0", "F1F0", "FF10"};
        char * const data[]    = {"0001", "0010"};
        int i;

        /* Four possible switch groups */
        pt2260->ngroups = 4;
        pt2260->groups = kmalloc(pt2260->ngroups * sizeof(char *), GFP_KERNEL);
        if (pt2260->groups == NULL) {
                pr_err("modrss: Cannot kmalloc\n");
                return -1;
        }
        /* Three possible switches per group */
        pt2260->nsockets = 3;
        pt2260->sockets = kmalloc(pt2260->nsockets * sizeof(char *),
                                  GFP_KERNEL);
        if (pt2260->sockets == NULL) {
                pr_err("modrss: Cannot kmalloc\n");
                return -1;
        }

        /* Data is either "On" or "Off" */
        pt2260->ndata = 2;
        pt2260->data = kmalloc(pt2260->ndata * sizeof(char *), GFP_KERNEL);
        if (pt2260->data == NULL) {
                pr_err("modrss: Cannot kmalloc\n");
                return -1;
        }

        for (i = 0; i < pt2260->ngroups; i++)
                pt2260->groups[i] = groups[i];

        for (i = 0; i < pt2260->nsockets; i++)
                pt2260->sockets[i] = sockets[i];

        for (i = 0; i < pt2260->ndata; i++)
                pt2260->data[i] = data[i];

        return 0;
}

/**
 * Configure struct for the PT2262 encoder
 * @param *pt2262     Pointer to a pt2262 instance
 */
static int pt2262_init(struct Encoder *pt2262)
{
        char * const groups[]   = {"FFFF", "0FFF", "F0FF", "00FF",
                                   "FF0F", "0F0F", "F00F", "000F",
                                   "FFF0", "0FF0", "F0F0", "00F0",
                                   "FF00", "0F00", "F000", "0000"};
        char * const sockets[]  = {"F0FF", "FF0F", "FFF0", "FFFF"};
        char * const data[]     = {"FFF0", "FF0F"};
        int i;

        /* 16 possible switch groups (A-P in Intertechno code) */
        pt2262->ngroups = 16;
        pt2262->groups = kmalloc(pt2262->ngroups * sizeof(char *), GFP_KERNEL);
        if (pt2262->groups == NULL) {
                pr_err("modrss: Cannot kmalloc\n");
                return -1;
        }

        /* Four possible switches per group */
        pt2262->nsockets = 4;
        pt2262->sockets = kmalloc(pt2262->nsockets * sizeof(char *),
                                  GFP_KERNEL);
        if (pt2262->sockets == NULL) {
                pr_err("modrss: Cannot kmalloc\n");
                return -1;
        }

        /* Data is either "On" or "Off" */
        pt2262->ndata = 2;
        pt2262->data = kmalloc(pt2262->ndata * sizeof(char *), GFP_KERNEL);
        if (pt2262->data == NULL) {
                pr_err("modrss: Cannot kmalloc\n");
                return -1;
        }

        for (i = 0; i < pt2262->ngroups; i++)
                pt2262->groups[i] = groups[i];

        for (i = 0; i < pt2262->nsockets; i++)
                pt2262->sockets[i] = sockets[i];

        for (i = 0; i < pt2262->ndata; i++)
                pt2262->data[i] = data[i];

        return 0;
}

/**
 * Emulate an encoder chip
 * @param *enc          Pointer to an encoder instance
 * @param uint group    Socket group
 * @param uint socket   Socket within group
 * @param uint data     Data to send
 */
static int socket_ctrl(struct Encoder *enc, uint group, uint socket, uint data)
{
        int i;
        size_t s;
        char *codeword;

        /* Calculate the codeword size */
        s = strlen(enc->groups[group]) +
                strlen(enc->sockets[socket]) +
                strlen(enc->data[data]);

        codeword = kmalloc(s + 1, GFP_KERNEL);

        /* Generate the codeword including '\0' */
        snprintf(codeword, s + 1, "%s%s%s",
                 enc->groups[group],
                 enc->sockets[socket],
                 enc->data[data]);

        pr_debug("codeword: %s\n", codeword);

        /* Send the codeword */
        for (i = 0; i < REPEAT; i++)
                send_tris(codeword);

        return 0;
}

static int socket_send(uint dev, uint group, uint socket, uint data)
{
        struct Encoder encoder;

        switch (dev) {
        case 0:
                pt2260_init(&encoder);
                break;
        case 1:
                pt2262_init(&encoder);
                break;
        default:
                pr_err("modrss: Unknown encoder type.\n");
                return -1;
        }

        socket_ctrl(&encoder, group, socket, data);

        return 0;
}

static ssize_t driver_write(struct file *f, const char __user *ubuf,
                            size_t len, loff_t *off)
{
        char *kbuf;
        uint encoder, group, socket, data;
        int i, data_len;

        kbuf = kmalloc(len, GFP_KERNEL);
        if (!kbuf)
                return -ENOMEM;

        if (copy_from_user(kbuf, ubuf, len)) {
                pr_err("Error: Unable to read user input\n");
                kfree(kbuf);
                return -EFAULT;
        }

        data_len = strlen(kbuf);

        /* Check for valid hex values from user space */
        for (i = 0; i < data_len; i++) {
                if ((kbuf[i] >= 'a') && (kbuf[i] <= 'f')) {
                        kbuf[i] = kbuf[i] - 'a';
                } else if ((kbuf[i] >= 'A') && (kbuf[i] <= 'F')) {
                        kbuf[i] = kbuf[i] - 'A';
                } else if ((kbuf[i] >= '0') && (kbuf[i] <= '9')) {
                        kbuf[i] = kbuf[i] - '0';
                } else {
                        pr_err("modrss: Only characters 0-9, a-f, and A-F.\n");
                        return -1;
                }
        }

        pr_debug("modrss: socket_ctrl(%d, %d, %d, %d)\n", (uint)kbuf[0],
                 (uint)kbuf[1], (uint)kbuf[2], (uint)kbuf[3]);

        socket_send((uint)kbuf[0], (uint)kbuf[1], (uint)kbuf[2], (uint)kbuf[3]);

        return len;
}

static const struct file_operations fops = {
        .owner   = THIS_MODULE,
        .write   = driver_write,
};

static struct miscdevice modrss_dev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "rsswitch",
        .fops = &fops,
};

static int __init modrsswitch_init(void)
{
        int ret, i, valid;

        pr_debug("modrss: Module registered");

        misc_register(&modrss_dev);

        valid = 0;
        /* Check for valid GPIO */
        for (i = 0; i < ARRAY_SIZE(valid_gpios); i++) {
                if (send_pin == valid_gpios[i]) {
                        valid = 1;
                        break;
                }
        }

        if (valid) {
                send_pin = GPIO4;
                pr_err("modrss: using default GPIO %d\n", GPIO4);
        }

        /* Register GPIO and set to LOW */
        ret = gpio_request_one(send_pin, GPIOF_OUT_INIT_LOW, "send_pin");
        if (ret) {
                pr_err("modrss: Unable to request GPIO: %d\n", ret);
                return ret;
        }

        pr_debug("modrss: Using GPIO %d\n", send_pin);

        return 0;
}

static void __exit modrsswitch_exit(void)
{
        gpio_set_value(send_pin, 0);
        gpio_free(send_pin);

        misc_deregister(&modrss_dev);

        pr_debug("modrss: Module unregistered");
}

module_init(modrsswitch_init);
module_exit(modrsswitch_exit);

MODULE_AUTHOR("Michael Hornung");
MODULE_DESCRIPTION("Remote socket switch character driver");
MODULE_LICENSE("GPL");

</code>

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Timing issues with RPi GPIO misc char driver
       [not found] ` <CAHnt0GXeTWArTeXAyoxpvX3A4gpXSg3v1Pu+sVhpRc2A9UZ30Q@mail.gmail.com>
@ 2014-09-21 16:30   ` mhornung.linux at gmail.com
  0 siblings, 0 replies; 3+ messages in thread
From: mhornung.linux at gmail.com @ 2014-09-21 16:30 UTC (permalink / raw)
  To: kernelnewbies

Hi Peter,

On Sun, 21. Sep 13:25, Peter Teoh wrote:
> A few possible problem (sorry, I am not sure):
> 

Thank you very much for your reply. As agreed upon, I am adding the mailing
list to my reply.

> GPIO 4 is the GPCLK0, for output only?

I don't think so. According to the BCM2835 ARM Peripherals datasheet [1],
GPCLK0 is ALT0 of GPIO4. The pin is a General-purpose input/output an can be
either an input, an output or one of the alternate functions.

> 
> http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=11127
> 
> and if switch its behavior, is that possible?   (are all GPIO can have
> ....then synchronous input is needed....which is why multiple transition is
> needed?   try other pins?
>

I am not shure if I understand you correctly here. I configure GPIO4, or one of
the pins {4, 17, 21, 22, 23, 24, 25} (given as module parameter), as output
inside the modrsswitch_init function:

         /* Register GPIO and set to LOW */
         ret = gpio_request_one(send_pin, GPIOF_OUT_INIT_LOW, "send_pin");

It is unlikely that the pin changes its behaviour. To remove doubts, I tried
the other pins, too, without any improvements.
 
> I suspect another source of latency coming from copy_from_user.   Why not
> avoiding that completely by transferring to kernel completely before
> starting the gpio access mechanism?   whenever u switch to userspace....the
> kernel will block and wait for input from the userspace...
> 

I think that I am doing this already. The copy_from_user call takes place inside
function driver_write. The bit banging of the GPIO follows some function calls
later and should not overlap with the copy_from_user call. The data is completely
copied from user- to kernel-space before the sending of the data takes place.

With best regards

Michael

[1] http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

> 
> 
> On Sat, Sep 20, 2014 at 8:52 PM, <mhornung.linux@gmail.com> wrote:
> 
> > Hello,
> >
> > I am trying to do my first driver, a Raspberry Pi misc char driver for
> > controlling 433 MHz power plugs. The transmitter is a 433 MHz radio module
> > connected to one of the RPi's GPIOs, the receiver is a 433 MHz power plug.
> > The
> > power plug expects a specific protocol which is normally send by an encoder
> > chip (PT2260 or PT2262) within the appropriate remote control. The char
> > driver
> > shall replace those encoder chips and generate the correct bit sequence by
> > the
> > GPIO.
> >
> > I already have a user-space library [1] which works if I send the codeword
> > multiple times (no real time kernel). Now I am trying to do this in kernel
> > space
> > which is where I ran into the same problem. The socket does only switch if
> > I
> > send the codeword multiple times (e.g. #define REPEAT 3).
> >
> > I would really appreciate any hint on how to get around this problem. Any
> > comment
> > on the code in general is also highly desirable.
> >
> > With best regards
> >
> > Michael
> >
> > [1] https://github.com/graznik/librsswitch
> >
> > <code>
> >
> > #include <linux/cdev.h>
> > #include <linux/ctype.h>
> > #include <linux/delay.h>
> > #include <linux/device.h>
> > #include <linux/fs.h>
> > #include <linux/gpio.h>
> > #include <linux/init.h>
> > #include <linux/kdev_t.h>
> > #include <linux/kernel.h>
> > #include <linux/module.h>
> > #include <linux/slab.h>
> > #include <linux/time.h>
> > #include <linux/types.h>
> > #include <linux/uaccess.h>
> > #include <linux/moduleparam.h>
> > #include <linux/miscdevice.h>
> >
> > #define GPIO4     4  /* The default GPIO pin */
> > #define REPEAT    1  /* Times to repeat the codeword */
> > #define HIGH      1
> > #define LOW       0
> > #define PULSE_LEN 350
> >
> > /*
> >  * Each encoder chip used in the power socket remote controls can be
> > described
> >  * with the following struct.
> > */
> > struct Encoder {
> >         char **groups;    /* Group identifiers                  */
> >         uint ngroups;     /* Number of groups                   */
> >         char **sockets;   /* Socket identifiers                 */
> >         uint nsockets;    /* Number of sockets within one group */
> >         char **data;      /* On or off                          */
> >         uint ndata;       /* On or off => 2                     */
> >         uint pulse_len;   /* This might differ                  */
> > };
> >
> > /* The 433 MHz sender must be connected to one of these pins */
> > static uint valid_gpios[] = {4, 17, 21, 22, 23, 24, 25};
> >
> > static int send_pin = GPIO4; /* The default pin */
> > module_param(send_pin, int, 0644);
> > MODULE_PARM_DESC(send_pin, "GPIO the 433 MHz sender is connected to");
> >
> > static void transmit(int nhigh, int nlow)
> > {
> >         /*
> >          * FIXME: PULSE_LEN is the pulse length in us. This should be a
> >          parameter in the future, depending on the encoder chip within
> >          the remote control.
> >         */
> >         gpio_set_value(send_pin, HIGH);
> >         udelay(PULSE_LEN * nhigh);
> >         gpio_set_value(send_pin, LOW);
> >         udelay(PULSE_LEN * nlow);
> > }
> > /**
> >  * Sends a Tri-State "0" Bit
> >  *            _     _
> >  * Waveform: | |___| |___
> >  */
> > static void send_0(void)
> > {
> >         transmit(1, 3);
> >         transmit(1, 3);
> > }
> >
> > /**
> >  * Sends a Tri-State "1" Bit
> >  *            ___   ___
> >  * Waveform: |   |_|   |_
> >  */
> > static void send_1(void)
> > {
> >         transmit(3, 1);
> >         transmit(3, 1);
> > }
> >
> > /**
> >  * Sends a Tri-State "F" Bit
> >  *            _     ___
> >  * Waveform: | |___|   |_
> >  */
> > static void send_f(void)
> > {
> >         transmit(1, 3);
> >         transmit(3, 1);
> > }
> >
> > /**
> >  * Sends a "Sync" Bit
> >  *                       _
> >  * Waveform Protocol 1: | |_______________________________
> >  *                       _
> >  * Waveform Protocol 2: | |__________
> >  */
> > static void send_sync(void)
> > {
> >         transmit(1, 31);
> > }
> >
> > static void send_tris(char *codeword)
> > {
> >         int i = 0;
> >         unsigned long flags;
> >
> >         local_irq_save(flags);
> >         while (codeword[i] != '\0') {
> >                 switch (codeword[i]) {
> >                 case '0':
> >                         send_0();
> >                         break;
> >                 case '1':
> >                         send_1();
> >                         break;
> >                 case 'F':
> >                         send_f();
> >                         break;
> >                 }
> >                 i++;
> >         }
> >         send_sync();
> >         local_irq_restore(flags);
> > }
> >
> > /**
> >  * Configure struct for the PT2260 encoder
> >  * @param pt2260     Pointer to a pt2260 instance
> >  */
> > static int pt2260_init(struct Encoder *pt2260)
> > {
> >         char * const groups[]  = {"1FFF", "F1FF", "FF1F", "FFF1"};
> >         char * const sockets[] = {"1FF0", "F1F0", "FF10"};
> >         char * const data[]    = {"0001", "0010"};
> >         int i;
> >
> >         /* Four possible switch groups */
> >         pt2260->ngroups = 4;
> >         pt2260->groups = kmalloc(pt2260->ngroups * sizeof(char *),
> > GFP_KERNEL);
> >         if (pt2260->groups == NULL) {
> >                 pr_err("modrss: Cannot kmalloc\n");
> >                 return -1;
> >         }
> >         /* Three possible switches per group */
> >         pt2260->nsockets = 3;
> >         pt2260->sockets = kmalloc(pt2260->nsockets * sizeof(char *),
> >                                   GFP_KERNEL);
> >         if (pt2260->sockets == NULL) {
> >                 pr_err("modrss: Cannot kmalloc\n");
> >                 return -1;
> >         }
> >
> >         /* Data is either "On" or "Off" */
> >         pt2260->ndata = 2;
> >         pt2260->data = kmalloc(pt2260->ndata * sizeof(char *), GFP_KERNEL);
> >         if (pt2260->data == NULL) {
> >                 pr_err("modrss: Cannot kmalloc\n");
> >                 return -1;
> >         }
> >
> >         for (i = 0; i < pt2260->ngroups; i++)
> >                 pt2260->groups[i] = groups[i];
> >
> >         for (i = 0; i < pt2260->nsockets; i++)
> >                 pt2260->sockets[i] = sockets[i];
> >
> >         for (i = 0; i < pt2260->ndata; i++)
> >                 pt2260->data[i] = data[i];
> >
> >         return 0;
> > }
> >
> > /**
> >  * Configure struct for the PT2262 encoder
> >  * @param *pt2262     Pointer to a pt2262 instance
> >  */
> > static int pt2262_init(struct Encoder *pt2262)
> > {
> >         char * const groups[]   = {"FFFF", "0FFF", "F0FF", "00FF",
> >                                    "FF0F", "0F0F", "F00F", "000F",
> >                                    "FFF0", "0FF0", "F0F0", "00F0",
> >                                    "FF00", "0F00", "F000", "0000"};
> >         char * const sockets[]  = {"F0FF", "FF0F", "FFF0", "FFFF"};
> >         char * const data[]     = {"FFF0", "FF0F"};
> >         int i;
> >
> >         /* 16 possible switch groups (A-P in Intertechno code) */
> >         pt2262->ngroups = 16;
> >         pt2262->groups = kmalloc(pt2262->ngroups * sizeof(char *),
> > GFP_KERNEL);
> >         if (pt2262->groups == NULL) {
> >                 pr_err("modrss: Cannot kmalloc\n");
> >                 return -1;
> >         }
> >
> >         /* Four possible switches per group */
> >         pt2262->nsockets = 4;
> >         pt2262->sockets = kmalloc(pt2262->nsockets * sizeof(char *),
> >                                   GFP_KERNEL);
> >         if (pt2262->sockets == NULL) {
> >                 pr_err("modrss: Cannot kmalloc\n");
> >                 return -1;
> >         }
> >
> >         /* Data is either "On" or "Off" */
> >         pt2262->ndata = 2;
> >         pt2262->data = kmalloc(pt2262->ndata * sizeof(char *), GFP_KERNEL);
> >         if (pt2262->data == NULL) {
> >                 pr_err("modrss: Cannot kmalloc\n");
> >                 return -1;
> >         }
> >
> >         for (i = 0; i < pt2262->ngroups; i++)
> >                 pt2262->groups[i] = groups[i];
> >
> >         for (i = 0; i < pt2262->nsockets; i++)
> >                 pt2262->sockets[i] = sockets[i];
> >
> >         for (i = 0; i < pt2262->ndata; i++)
> >                 pt2262->data[i] = data[i];
> >
> >         return 0;
> > }
> >
> > /**
> >  * Emulate an encoder chip
> >  * @param *enc          Pointer to an encoder instance
> >  * @param uint group    Socket group
> >  * @param uint socket   Socket within group
> >  * @param uint data     Data to send
> >  */
> > static int socket_ctrl(struct Encoder *enc, uint group, uint socket, uint
> > data)
> > {
> >         int i;
> >         size_t s;
> >         char *codeword;
> >
> >         /* Calculate the codeword size */
> >         s = strlen(enc->groups[group]) +
> >                 strlen(enc->sockets[socket]) +
> >                 strlen(enc->data[data]);
> >
> >         codeword = kmalloc(s + 1, GFP_KERNEL);
> >
> >         /* Generate the codeword including '\0' */
> >         snprintf(codeword, s + 1, "%s%s%s",
> >                  enc->groups[group],
> >                  enc->sockets[socket],
> >                  enc->data[data]);
> >
> >         pr_debug("codeword: %s\n", codeword);
> >
> >         /* Send the codeword */
> >         for (i = 0; i < REPEAT; i++)
> >                 send_tris(codeword);
> >
> >         return 0;
> > }
> >
> > static int socket_send(uint dev, uint group, uint socket, uint data)
> > {
> >         struct Encoder encoder;
> >
> >         switch (dev) {
> >         case 0:
> >                 pt2260_init(&encoder);
> >                 break;
> >         case 1:
> >                 pt2262_init(&encoder);
> >                 break;
> >         default:
> >                 pr_err("modrss: Unknown encoder type.\n");
> >                 return -1;
> >         }
> >
> >         socket_ctrl(&encoder, group, socket, data);
> >
> >         return 0;
> > }
> >
> > static ssize_t driver_write(struct file *f, const char __user *ubuf,
> >                             size_t len, loff_t *off)
> > {
> >         char *kbuf;
> >         uint encoder, group, socket, data;
> >         int i, data_len;
> >
> >         kbuf = kmalloc(len, GFP_KERNEL);
> >         if (!kbuf)
> >                 return -ENOMEM;
> >
> >         if (copy_from_user(kbuf, ubuf, len)) {
> >                 pr_err("Error: Unable to read user input\n");
> >                 kfree(kbuf);
> >                 return -EFAULT;
> >         }
> >
> >         data_len = strlen(kbuf);
> >
> >         /* Check for valid hex values from user space */
> >         for (i = 0; i < data_len; i++) {
> >                 if ((kbuf[i] >= 'a') && (kbuf[i] <= 'f')) {
> >                         kbuf[i] = kbuf[i] - 'a';
> >                 } else if ((kbuf[i] >= 'A') && (kbuf[i] <= 'F')) {
> >                         kbuf[i] = kbuf[i] - 'A';
> >                 } else if ((kbuf[i] >= '0') && (kbuf[i] <= '9')) {
> >                         kbuf[i] = kbuf[i] - '0';
> >                 } else {
> >                         pr_err("modrss: Only characters 0-9, a-f, and
> > A-F.\n");
> >                         return -1;
> >                 }
> >         }
> >
> >         pr_debug("modrss: socket_ctrl(%d, %d, %d, %d)\n", (uint)kbuf[0],
> >                  (uint)kbuf[1], (uint)kbuf[2], (uint)kbuf[3]);
> >
> >         socket_send((uint)kbuf[0], (uint)kbuf[1], (uint)kbuf[2],
> > (uint)kbuf[3]);
> >
> >         return len;
> > }
> >
> > static const struct file_operations fops = {
> >         .owner   = THIS_MODULE,
> >         .write   = driver_write,
> > };
> >
> > static struct miscdevice modrss_dev = {
> >         .minor = MISC_DYNAMIC_MINOR,
> >         .name = "rsswitch",
> >         .fops = &fops,
> > };
> >
> > static int __init modrsswitch_init(void)
> > {
> >         int ret, i, valid;
> >
> >         pr_debug("modrss: Module registered");
> >
> >         misc_register(&modrss_dev);
> >
> >         valid = 0;
> >         /* Check for valid GPIO */
> >         for (i = 0; i < ARRAY_SIZE(valid_gpios); i++) {
> >                 if (send_pin == valid_gpios[i]) {
> >                         valid = 1;
> >                         break;
> >                 }
> >         }
> >
> >         if (valid) {
> >                 send_pin = GPIO4;
> >                 pr_err("modrss: using default GPIO %d\n", GPIO4);
> >         }
> >
> >         /* Register GPIO and set to LOW */
> >         ret = gpio_request_one(send_pin, GPIOF_OUT_INIT_LOW, "send_pin");
> >         if (ret) {
> >                 pr_err("modrss: Unable to request GPIO: %d\n", ret);
> >                 return ret;
> >         }
> >
> >         pr_debug("modrss: Using GPIO %d\n", send_pin);
> >
> >         return 0;
> > }
> >
> > static void __exit modrsswitch_exit(void)
> > {
> >         gpio_set_value(send_pin, 0);
> >         gpio_free(send_pin);
> >
> >         misc_deregister(&modrss_dev);
> >
> >         pr_debug("modrss: Module unregistered");
> > }
> >
> > module_init(modrsswitch_init);
> > module_exit(modrsswitch_exit);
> >
> > MODULE_AUTHOR("Michael Hornung");
> > MODULE_DESCRIPTION("Remote socket switch character driver");
> > MODULE_LICENSE("GPL");
> >
> > </code>
> >
> >
> > _______________________________________________
> > Kernelnewbies mailing list
> > Kernelnewbies at kernelnewbies.org
> > http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies
> >
> 
> 
> 
> -- 
> Regards,
> Peter Teoh

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Timing issues with RPi GPIO misc char driver
  2014-09-20 12:52 Timing issues with RPi GPIO misc char driver mhornung.linux at gmail.com
       [not found] ` <CAHnt0GXeTWArTeXAyoxpvX3A4gpXSg3v1Pu+sVhpRc2A9UZ30Q@mail.gmail.com>
@ 2014-09-30 12:49 ` mhornung.linux at gmail.com
  1 sibling, 0 replies; 3+ messages in thread
From: mhornung.linux at gmail.com @ 2014-09-30 12:49 UTC (permalink / raw)
  To: kernelnewbies

On Sat, 20. Sep 14:52, mhornung.linux at gmail.com wrote:
Hello again,

> Hello,
> 
> I am trying to do my first driver, a Raspberry Pi misc char driver for
> controlling 433 MHz power plugs. The transmitter is a 433 MHz radio module
> connected to one of the RPi's GPIOs, the receiver is a 433 MHz power plug. The
> power plug expects a specific protocol which is normally send by an encoder
> chip (PT2260 or PT2262) within the appropriate remote control. The char driver
> shall replace those encoder chips and generate the correct bit sequence by the
> GPIO.
> 
> I already have a user-space library [1] which works if I send the codeword
> multiple times (no real time kernel). Now I am trying to do this in kernel space
> which is where I ran into the same problem. The socket does only switch if I
> send the codeword multiple times (e.g. #define REPEAT 3).

One of the possible counterparts of the remote control encoder is the PT2270
decoder chip, sitting inside the power plug. The PT2270 datasheet tells the
following:

"If both addresses match *for 2 consecutive code words*, PT2270 drives the data
output pins."

If I set

#define REPEAT    2

the socket always switches as expected.

> 
> I would really appreciate any hint on how to get around this problem. Any comment
> on the code in general is also highly desirable.
> 

I would really appreciate if someone could say some words about the code itself.

> With best regards
> 
> Michael
> 
> [1] https://github.com/graznik/librsswitch
> 

Thank you very much

Michael


> <code>
> 
> #include <linux/cdev.h>
> #include <linux/ctype.h>
> #include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/fs.h>
> #include <linux/gpio.h>
> #include <linux/init.h>
> #include <linux/kdev_t.h>
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/slab.h>
> #include <linux/time.h>
> #include <linux/types.h>
> #include <linux/uaccess.h>
> #include <linux/moduleparam.h>
> #include <linux/miscdevice.h>
> 
> #define GPIO4     4  /* The default GPIO pin */
> #define REPEAT    1  /* Times to repeat the codeword */
> #define HIGH      1
> #define LOW       0
> #define PULSE_LEN 350
> 
> /*
>  * Each encoder chip used in the power socket remote controls can be described
>  * with the following struct.
> */
> struct Encoder {
>         char **groups;    /* Group identifiers                  */
>         uint ngroups;     /* Number of groups                   */
>         char **sockets;   /* Socket identifiers                 */
>         uint nsockets;    /* Number of sockets within one group */
>         char **data;      /* On or off                          */
>         uint ndata;       /* On or off => 2                     */
>         uint pulse_len;   /* This might differ                  */
> };
> 
> /* The 433 MHz sender must be connected to one of these pins */
> static uint valid_gpios[] = {4, 17, 21, 22, 23, 24, 25};
> 
> static int send_pin = GPIO4; /* The default pin */
> module_param(send_pin, int, 0644);
> MODULE_PARM_DESC(send_pin, "GPIO the 433 MHz sender is connected to");
> 
> static void transmit(int nhigh, int nlow)
> {
>         /*
>          * FIXME: PULSE_LEN is the pulse length in us. This should be a
>          parameter in the future, depending on the encoder chip within
>          the remote control.
>         */
>         gpio_set_value(send_pin, HIGH);
>         udelay(PULSE_LEN * nhigh);
>         gpio_set_value(send_pin, LOW);
>         udelay(PULSE_LEN * nlow);
> }
> /**
>  * Sends a Tri-State "0" Bit
>  *            _     _
>  * Waveform: | |___| |___
>  */
> static void send_0(void)
> {
>         transmit(1, 3);
>         transmit(1, 3);
> }
> 
> /**
>  * Sends a Tri-State "1" Bit
>  *            ___   ___
>  * Waveform: |   |_|   |_
>  */
> static void send_1(void)
> {
>         transmit(3, 1);
>         transmit(3, 1);
> }
> 
> /**
>  * Sends a Tri-State "F" Bit
>  *            _     ___
>  * Waveform: | |___|   |_
>  */
> static void send_f(void)
> {
>         transmit(1, 3);
>         transmit(3, 1);
> }
> 
> /**
>  * Sends a "Sync" Bit
>  *                       _
>  * Waveform Protocol 1: | |_______________________________
>  *                       _
>  * Waveform Protocol 2: | |__________
>  */
> static void send_sync(void)
> {
>         transmit(1, 31);
> }
> 
> static void send_tris(char *codeword)
> {
>         int i = 0;
>         unsigned long flags;
> 
>         local_irq_save(flags);
>         while (codeword[i] != '\0') {
>                 switch (codeword[i]) {
>                 case '0':
>                         send_0();
>                         break;
>                 case '1':
>                         send_1();
>                         break;
>                 case 'F':
>                         send_f();
>                         break;
>                 }
>                 i++;
>         }
>         send_sync();
>         local_irq_restore(flags);
> }
> 
> /**
>  * Configure struct for the PT2260 encoder
>  * @param pt2260     Pointer to a pt2260 instance
>  */
> static int pt2260_init(struct Encoder *pt2260)
> {
>         char * const groups[]  = {"1FFF", "F1FF", "FF1F", "FFF1"};
>         char * const sockets[] = {"1FF0", "F1F0", "FF10"};
>         char * const data[]    = {"0001", "0010"};
>         int i;
> 
>         /* Four possible switch groups */
>         pt2260->ngroups = 4;
>         pt2260->groups = kmalloc(pt2260->ngroups * sizeof(char *), GFP_KERNEL);
>         if (pt2260->groups == NULL) {
>                 pr_err("modrss: Cannot kmalloc\n");
>                 return -1;
>         }
>         /* Three possible switches per group */
>         pt2260->nsockets = 3;
>         pt2260->sockets = kmalloc(pt2260->nsockets * sizeof(char *),
>                                   GFP_KERNEL);
>         if (pt2260->sockets == NULL) {
>                 pr_err("modrss: Cannot kmalloc\n");
>                 return -1;
>         }
> 
>         /* Data is either "On" or "Off" */
>         pt2260->ndata = 2;
>         pt2260->data = kmalloc(pt2260->ndata * sizeof(char *), GFP_KERNEL);
>         if (pt2260->data == NULL) {
>                 pr_err("modrss: Cannot kmalloc\n");
>                 return -1;
>         }
> 
>         for (i = 0; i < pt2260->ngroups; i++)
>                 pt2260->groups[i] = groups[i];
> 
>         for (i = 0; i < pt2260->nsockets; i++)
>                 pt2260->sockets[i] = sockets[i];
> 
>         for (i = 0; i < pt2260->ndata; i++)
>                 pt2260->data[i] = data[i];
> 
>         return 0;
> }
> 
> /**
>  * Configure struct for the PT2262 encoder
>  * @param *pt2262     Pointer to a pt2262 instance
>  */
> static int pt2262_init(struct Encoder *pt2262)
> {
>         char * const groups[]   = {"FFFF", "0FFF", "F0FF", "00FF",
>                                    "FF0F", "0F0F", "F00F", "000F",
>                                    "FFF0", "0FF0", "F0F0", "00F0",
>                                    "FF00", "0F00", "F000", "0000"};
>         char * const sockets[]  = {"F0FF", "FF0F", "FFF0", "FFFF"};
>         char * const data[]     = {"FFF0", "FF0F"};
>         int i;
> 
>         /* 16 possible switch groups (A-P in Intertechno code) */
>         pt2262->ngroups = 16;
>         pt2262->groups = kmalloc(pt2262->ngroups * sizeof(char *), GFP_KERNEL);
>         if (pt2262->groups == NULL) {
>                 pr_err("modrss: Cannot kmalloc\n");
>                 return -1;
>         }
> 
>         /* Four possible switches per group */
>         pt2262->nsockets = 4;
>         pt2262->sockets = kmalloc(pt2262->nsockets * sizeof(char *),
>                                   GFP_KERNEL);
>         if (pt2262->sockets == NULL) {
>                 pr_err("modrss: Cannot kmalloc\n");
>                 return -1;
>         }
> 
>         /* Data is either "On" or "Off" */
>         pt2262->ndata = 2;
>         pt2262->data = kmalloc(pt2262->ndata * sizeof(char *), GFP_KERNEL);
>         if (pt2262->data == NULL) {
>                 pr_err("modrss: Cannot kmalloc\n");
>                 return -1;
>         }
> 
>         for (i = 0; i < pt2262->ngroups; i++)
>                 pt2262->groups[i] = groups[i];
> 
>         for (i = 0; i < pt2262->nsockets; i++)
>                 pt2262->sockets[i] = sockets[i];
> 
>         for (i = 0; i < pt2262->ndata; i++)
>                 pt2262->data[i] = data[i];
> 
>         return 0;
> }
> 
> /**
>  * Emulate an encoder chip
>  * @param *enc          Pointer to an encoder instance
>  * @param uint group    Socket group
>  * @param uint socket   Socket within group
>  * @param uint data     Data to send
>  */
> static int socket_ctrl(struct Encoder *enc, uint group, uint socket, uint data)
> {
>         int i;
>         size_t s;
>         char *codeword;
> 
>         /* Calculate the codeword size */
>         s = strlen(enc->groups[group]) +
>                 strlen(enc->sockets[socket]) +
>                 strlen(enc->data[data]);
> 
>         codeword = kmalloc(s + 1, GFP_KERNEL);
> 
>         /* Generate the codeword including '\0' */
>         snprintf(codeword, s + 1, "%s%s%s",
>                  enc->groups[group],
>                  enc->sockets[socket],
>                  enc->data[data]);
> 
>         pr_debug("codeword: %s\n", codeword);
> 
>         /* Send the codeword */
>         for (i = 0; i < REPEAT; i++)
>                 send_tris(codeword);
> 
>         return 0;
> }
> 
> static int socket_send(uint dev, uint group, uint socket, uint data)
> {
>         struct Encoder encoder;
> 
>         switch (dev) {
>         case 0:
>                 pt2260_init(&encoder);
>                 break;
>         case 1:
>                 pt2262_init(&encoder);
>                 break;
>         default:
>                 pr_err("modrss: Unknown encoder type.\n");
>                 return -1;
>         }
> 
>         socket_ctrl(&encoder, group, socket, data);
> 
>         return 0;
> }
> 
> static ssize_t driver_write(struct file *f, const char __user *ubuf,
>                             size_t len, loff_t *off)
> {
>         char *kbuf;
>         uint encoder, group, socket, data;
>         int i, data_len;
> 
>         kbuf = kmalloc(len, GFP_KERNEL);
>         if (!kbuf)
>                 return -ENOMEM;
> 
>         if (copy_from_user(kbuf, ubuf, len)) {
>                 pr_err("Error: Unable to read user input\n");
>                 kfree(kbuf);
>                 return -EFAULT;
>         }
> 
>         data_len = strlen(kbuf);
> 
>         /* Check for valid hex values from user space */
>         for (i = 0; i < data_len; i++) {
>                 if ((kbuf[i] >= 'a') && (kbuf[i] <= 'f')) {
>                         kbuf[i] = kbuf[i] - 'a';
>                 } else if ((kbuf[i] >= 'A') && (kbuf[i] <= 'F')) {
>                         kbuf[i] = kbuf[i] - 'A';
>                 } else if ((kbuf[i] >= '0') && (kbuf[i] <= '9')) {
>                         kbuf[i] = kbuf[i] - '0';
>                 } else {
>                         pr_err("modrss: Only characters 0-9, a-f, and A-F.\n");
>                         return -1;
>                 }
>         }
> 
>         pr_debug("modrss: socket_ctrl(%d, %d, %d, %d)\n", (uint)kbuf[0],
>                  (uint)kbuf[1], (uint)kbuf[2], (uint)kbuf[3]);
> 
>         socket_send((uint)kbuf[0], (uint)kbuf[1], (uint)kbuf[2], (uint)kbuf[3]);
> 
>         return len;
> }
> 
> static const struct file_operations fops = {
>         .owner   = THIS_MODULE,
>         .write   = driver_write,
> };
> 
> static struct miscdevice modrss_dev = {
>         .minor = MISC_DYNAMIC_MINOR,
>         .name = "rsswitch",
>         .fops = &fops,
> };
> 
> static int __init modrsswitch_init(void)
> {
>         int ret, i, valid;
> 
>         pr_debug("modrss: Module registered");
> 
>         misc_register(&modrss_dev);
> 
>         valid = 0;
>         /* Check for valid GPIO */
>         for (i = 0; i < ARRAY_SIZE(valid_gpios); i++) {
>                 if (send_pin == valid_gpios[i]) {
>                         valid = 1;
>                         break;
>                 }
>         }
> 
>         if (valid) {
>                 send_pin = GPIO4;
>                 pr_err("modrss: using default GPIO %d\n", GPIO4);
>         }
> 
>         /* Register GPIO and set to LOW */
>         ret = gpio_request_one(send_pin, GPIOF_OUT_INIT_LOW, "send_pin");
>         if (ret) {
>                 pr_err("modrss: Unable to request GPIO: %d\n", ret);
>                 return ret;
>         }
> 
>         pr_debug("modrss: Using GPIO %d\n", send_pin);
> 
>         return 0;
> }
> 
> static void __exit modrsswitch_exit(void)
> {
>         gpio_set_value(send_pin, 0);
>         gpio_free(send_pin);
> 
>         misc_deregister(&modrss_dev);
> 
>         pr_debug("modrss: Module unregistered");
> }
> 
> module_init(modrsswitch_init);
> module_exit(modrsswitch_exit);
> 
> MODULE_AUTHOR("Michael Hornung");
> MODULE_DESCRIPTION("Remote socket switch character driver");
> MODULE_LICENSE("GPL");
> 
> </code>
> 

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2014-09-30 12:49 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-09-20 12:52 Timing issues with RPi GPIO misc char driver mhornung.linux at gmail.com
     [not found] ` <CAHnt0GXeTWArTeXAyoxpvX3A4gpXSg3v1Pu+sVhpRc2A9UZ30Q@mail.gmail.com>
2014-09-21 16:30   ` mhornung.linux at gmail.com
2014-09-30 12:49 ` mhornung.linux at gmail.com

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).