Timing issues with RPi GPIO misc char driver

mhornung.linux at gmail.com mhornung.linux at gmail.com
Tue Sep 30 08:49:38 EDT 2014


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>
> 



More information about the Kernelnewbies mailing list