Timing issues with RPi GPIO misc char driver
mhornung.linux at gmail.com
mhornung.linux at gmail.com
Sun Sep 21 12:30:33 EDT 2014
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 at 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
More information about the Kernelnewbies
mailing list