UART data sliced in two piece ....

Asked by 4 months ago
Hi, I would like to know if someone has experienced data sliced in two pieces ? My case is simple, the UART in the beagleBone Black send the data in two piece ..... The problem is that the device answer with an error crc .... so why the uart is doing that ? Micka,

Your Answer

Name:
Reply:

All Answers

Answer by 4 months ago
You're missing a bunch of information needed to help troubleshoot/ For starters how fast ? Which distro ? Which kernel driver are you using ? etc etc etc.
Answer by 4 months ago
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 57d6b29..aa71c13 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -37,6 +37,8 @@ #include <linux/clk.h> #include <linux/serial_core.h> #include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/uaccess.h> #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/gpio.h> @@ -160,6 +162,7 @@ struct uart_omap_port { u32 calc_latency; struct work_struct qos_work; struct pinctrl *pins; + struct serial_rs485 rs485; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -268,11 +271,11 @@ static void serial_omap_enable_ms(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); +static inline void wait_for_xmitr(struct uart_omap_port *up); static void serial_omap_stop_tx(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); + int val; pm_runtime_get_sync(up->dev); if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; @@ -283,6 +286,15 @@ static void serial_omap_stop_tx(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); + wait_for_xmitr(up); + if (up->rs485.flags & SER_RS485_ENABLED) { + if(up->rs485.delay_rts_after_send>0){ + udelay(up->rs485.delay_rts_after_send); + } + /* Disable RS485 TX EN */ + val = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + gpio_set_value(up->rs485.gpio_pin, val); + } static void serial_omap_stop_rx(struct uart_port *port) @@ -345,12 +357,23 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); + int val; + if (up->rs485.flags & SER_RS485_ENABLED) { + /* Enable RS485 TX EN */ + val = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 0 : 1; + gpio_set_value(up->rs485.gpio_pin, val); + if(up->rs485.delay_rts_before_send>0){ + udelay(up->rs485.delay_rts_before_send); + } + } pm_runtime_get_sync(up->dev); serial_omap_enable_ier_thri(up); serial_omap_set_noidle(up); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); static void serial_omap_throttle(struct uart_port *port) @@ -702,6 +725,7 @@ static void serial_omap_shutdown(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags = 0; + int val; dev_dbg(up->port.dev, "serial_omap_shutdown+%d ", up->port.line); @@ -722,6 +746,12 @@ static void serial_omap_shutdown(struct uart_port *port) */ serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); serial_omap_clear_fifos(up); + /* if in RS485 mode, make sure we disable the driver */ + if (up->rs485.flags & SER_RS485_ENABLED) { + val = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + gpio_set_value(up->rs485.gpio_pin, val); + } /* * Read data port to reset things, and then free the irq @@ -1250,6 +1280,93 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) #endif +static int +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + int r = 0; + int val; + struct uart_omap_port *p = (struct uart_omap_port *)port; + spin_lock(&port->lock); + /* TODO - disable transmitter ? */ + if (rs485conf->flags & SER_RS485_ENABLED) { + val = (p->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + /* if using GPIO, request the resource and set it up */ + if (rs485conf->flags & SER_RS485_USE_GPIO) { + /* get gpio resources if not already set */ + if (!(p->rs485.flags & SER_RS485_USE_GPIO) || + (p->rs485.gpio_pin != rs485conf->gpio_pin)) { + r = gpio_request(rs485conf->gpio_pin, + "RS485 TXE"); + if (r) { + dev_warn(port->dev, + "Could not request GPIO %d : %d ", + rs485conf->gpio_pin, r); + r = -EFAULT; + goto exit_bail; + } + r = gpio_direction_output(rs485conf->gpio_pin, val); + if (r) { + dev_warn(port->dev, + "Could not drive GPIO %d : %d ", + rs485conf->gpio_pin, r); + r = -EFAULT; + goto exit_bail; + } + /* free up old pin */ +//TODO: What if old pin is same as current?!!?!? + //if (p->rs485.flags & SER_RS485_USE_GPIO) + //gpio_free(p->rs485.gpio_pin); + } + } else { /* RTS pin requested */ + dev_warn(port->dev, "Must use GPIO for RS485 Support "); + goto exit_bail; + } + } + p->rs485 = *rs485conf; +exit_bail: + spin_unlock(&port->lock); + return r; +} +static int +serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + switch (cmd) { + case TIOCSRS485: + printk("rs485 "); + if (copy_from_user(&rs485conf, (struct serial_rs485 *)arg, + sizeof(rs485conf))) + return -EFAULT; + serial_omap_config_rs485(port, &rs485conf); + break; + case TIOCGRS485: + printk("rs485 "); + if (copy_to_user((struct serial_rs485 *)arg, + &((struct uart_omap_port *)port)->rs485, + sizeof(rs485conf))) + return -EFAULT; + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} static struct uart_ops serial_omap_pops = { .tx_empty = serial_omap_tx_empty, .set_mctrl = serial_omap_set_mctrl, @@ -1271,6 +1388,7 @@ static struct uart_ops serial_omap_pops = { .request_port = serial_omap_request_port, .config_port = serial_omap_config_port, .verify_port = serial_omap_verify_port, + .ioctl = serial_omap_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_put_char = serial_omap_poll_put_char, .poll_get_char = serial_omap_poll_get_char, diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index 5e0d0ed..1830ceb 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -109,18 +109,16 @@ struct serial_icounter_struct { */ struct serial_rs485 { - __u32 flags; /* RS485 feature flags */ -#define SER_RS485_ENABLED (1 << 0) /* If enabled */ -#define SER_RS485_RTS_ON_SEND (1 << 1) /* Logical level for - RTS pin when - sending */ -#define SER_RS485_RTS_AFTER_SEND (1 << 2) /* Logical level for - RTS pin after sent*/ -#define SER_RS485_RX_DURING_TX (1 << 4) - __u32 delay_rts_before_send; /* Delay before send (milliseconds) */ - __u32 delay_rts_after_send; /* Delay after send (milliseconds) */ - __u32 padding[5]; /* Memory is cheap, new structs - are a royal PITA .. */ -}; + __u32 flags; /* RS485 feature flags */ + #define SER_RS485_ENABLED (1 << 0) + #define SER_RS485_RTS_ON_SEND (1 << 1) + #define SER_RS485_RTS_AFTER_SEND (1 << 2) + #define SER_RS485_RTS_BEFORE_SEND (1 << 3) + #define SER_RS485_USE_GPIO (1 << 5) + __u32 delay_rts_before_send; /* Microseconds */ + __u32 delay_rts_after_send; /* Microseconds */ + __u32 gpio_pin; /* GPIO Pin Index */ + __u32 padding[4]; /* Memory is cheap, new structs + are a royal PITA .. */ + }; #endif /* _UAPI_LINUX_SERIAL_H */
Answer by 4 months ago
Quoted message by William Hermans 4 months ago
You're missing a bunch of information needed to help troubleshoot/ For starters how fast ? Which distro ? Which kernel driver are you using ? etc etc etc.
Hi, Robert C Nelson ( 3.8.13 bone 30 ), the driver is the omap . I've modified a little bit the driver to add RS485 support ( see the patch attached ) , the only major modification that I've done is to add : 0; behavior ..... How much data are you writing? Are you doing it with a single call to write? If you're doing multipe calls to write then its quite possible that a context switch is allowing another task to run and delaying your second write.
Answer by 4 months ago
Quoted message by Micka 4 months ago
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 57d6b29..aa71c13 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -37,6 +37,8 @@ #include <linux/clk.h> #include <linux/serial_core.h> #include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/uaccess.h> #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/gpio.h> @@ -160,6 +162,7 @@ struct uart_omap_port { u32 calc_latency; struct work_struct qos_work; struct pinctrl *pins; + struct serial_rs485 rs485; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -268,11 +271,11 @@ static void serial_omap_enable_ms(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); +static inline void wait_for_xmitr(struct uart_omap_port *up); static void serial_omap_stop_tx(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); + int val; pm_runtime_get_sync(up->dev); if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; @@ -283,6 +286,15 @@ static void serial_omap_stop_tx(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); + wait_for_xmitr(up); + if (up->rs485.flags & SER_RS485_ENABLED) { + if(up->rs485.delay_rts_after_send>0){ + udelay(up->rs485.delay_rts_after_send); + } + /* Disable RS485 TX EN */ + val = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + gpio_set_value(up->rs485.gpio_pin, val); + } static void serial_omap_stop_rx(struct uart_port *port) @@ -345,12 +357,23 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); + int val; + if (up->rs485.flags & SER_RS485_ENABLED) { + /* Enable RS485 TX EN */ + val = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 0 : 1; + gpio_set_value(up->rs485.gpio_pin, val); + if(up->rs485.delay_rts_before_send>0){ + udelay(up->rs485.delay_rts_before_send); + } + } pm_runtime_get_sync(up->dev); serial_omap_enable_ier_thri(up); serial_omap_set_noidle(up); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); static void serial_omap_throttle(struct uart_port *port) @@ -702,6 +725,7 @@ static void serial_omap_shutdown(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags = 0; + int val; dev_dbg(up->port.dev, "serial_omap_shutdown+%d ", up->port.line); @@ -722,6 +746,12 @@ static void serial_omap_shutdown(struct uart_port *port) */ serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); serial_omap_clear_fifos(up); + /* if in RS485 mode, make sure we disable the driver */ + if (up->rs485.flags & SER_RS485_ENABLED) { + val = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + gpio_set_value(up->rs485.gpio_pin, val); + } /* * Read data port to reset things, and then free the irq @@ -1250,6 +1280,93 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) #endif +static int +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + int r = 0; + int val; + struct uart_omap_port *p = (struct uart_omap_port *)port; + spin_lock(&port->lock); + /* TODO - disable transmitter ? */ + if (rs485conf->flags & SER_RS485_ENABLED) { + val = (p->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + /* if using GPIO, request the resource and set it up */ + if (rs485conf->flags & SER_RS485_USE_GPIO) { + /* get gpio resources if not already set */ + if (!(p->rs485.flags & SER_RS485_USE_GPIO) || + (p->rs485.gpio_pin != rs485conf->gpio_pin)) { + r = gpio_request(rs485conf->gpio_pin, + "RS485 TXE"); + if (r) { + dev_warn(port->dev, + "Could not request GPIO %d : %d ", + rs485conf->gpio_pin, r); + r = -EFAULT; + goto exit_bail; + } + r = gpio_direction_output(rs485conf->gpio_pin, val); + if (r) { + dev_warn(port->dev, + "Could not drive GPIO %d : %d ", + rs485conf->gpio_pin, r); + r = -EFAULT; + goto exit_bail; + } + /* free up old pin */ +//TODO: What if old pin is same as current?!!?!? + //if (p->rs485.flags & SER_RS485_USE_GPIO) + //gpio_free(p->rs485.gpio_pin); + } + } else { /* RTS pin requested */ + dev_warn(port->dev, "Must use GPIO for RS485 Support "); + goto exit_bail; + } + } + p->rs485 = *rs485conf; +exit_bail: + spin_unlock(&port->lock); + return r; +} +static int +serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + switch (cmd) { + case TIOCSRS485: + printk("rs485 "); + if (copy_from_user(&rs485conf, (struct serial_rs485 *)arg, + sizeof(rs485conf))) + return -EFAULT; + serial_omap_config_rs485(port, &rs485conf); + break; + case TIOCGRS485: + printk("rs485 "); + if (copy_to_user((struct serial_rs485 *)arg, + &((struct uart_omap_port *)port)->rs485, + sizeof(rs485conf))) + return -EFAULT; + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} static struct uart_ops serial_omap_pops = { .tx_empty = serial_omap_tx_empty, .set_mctrl = serial_omap_set_mctrl, @@ -1271,6 +1388,7 @@ static struct uart_ops serial_omap_pops = { .request_port = serial_omap_request_port, .config_port = serial_omap_config_port, .verify_port = serial_omap_verify_port, + .ioctl = serial_omap_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_put_char = serial_omap_poll_put_char, .poll_get_char = serial_omap_poll_get_char, diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index 5e0d0ed..1830ceb 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -109,18 +109,16 @@ struct serial_icounter_struct { */ struct serial_rs485 { - __u32 flags; /* RS485 feature flags */ -#define SER_RS485_ENABLED (1 << 0) /* If enabled */ -#define SER_RS485_RTS_ON_SEND (1 << 1) /* Logical level for - RTS pin when - sending */ -#define SER_RS485_RTS_AFTER_SEND (1 << 2) /* Logical level for - RTS pin after sent*/ -#define SER_RS485_RX_DURING_TX (1 << 4) - __u32 delay_rts_before_send; /* Delay before send (milliseconds) */ - __u32 delay_rts_after_send; /* Delay after send (milliseconds) */ - __u32 padding[5]; /* Memory is cheap, new structs - are a royal PITA .. */ -}; + __u32 flags; /* RS485 feature flags */ + #define SER_RS485_ENABLED (1 << 0) + #define SER_RS485_RTS_ON_SEND (1 << 1) + #define SER_RS485_RTS_AFTER_SEND (1 << 2) + #define SER_RS485_RTS_BEFORE_SEND (1 << 3) + #define SER_RS485_USE_GPIO (1 << 5) + __u32 delay_rts_before_send; /* Microseconds */ + __u32 delay_rts_after_send; /* Microseconds */ + __u32 gpio_pin; /* GPIO Pin Index */ + __u32 padding[4]; /* Memory is cheap, new structs + are a royal PITA .. */ + }; #endif /* _UAPI_LINUX_SERIAL_H */
Hi, Thanks for your answer, Well, it's just 16 octets or less .... most of the time it works, but somehow the driver decide to interrupt the transfer ...... yes it's a single call to write .... Do you know if there is a configuration to solve this bug ? I'm also wondering if it's because my linux ( 3.8 ) is not a real time kernel .... maybe i should apply the kernel PREEMPT_RT ? I'm really lost .... Micka,
Answer by 4 months ago
Quoted message by Dave Hylands 4 months ago
Hi, Robert C Nelson ( 3.8.13 bone 30 ), the driver is the omap . I've modified a little bit the driver to add RS485 support ( see the patch attached ) , the only major modification that I've done is to add : 0; behavior ..... How much data are you writing? Are you doing it with a single call to write? If you're doing multipe calls to write then its quite possible that a context switch is allowing another task to run and delaying your second write.
Problem Solved ! By disabling the preempt mode in the kernel config .... I have some fopen call in my program which is not a good idea in real time application .... Thx everyone for the help !
Answer by 4 months ago
Quoted message by Micka 4 months ago
Hi, Thanks for your answer, Well, it's just 16 octets or less .... most of the time it works, but somehow the driver decide to interrupt the transfer ...... yes it's a single call to write .... Do you know if there is a configuration to solve this bug ? I'm also wondering if it's because my linux ( 3.8 ) is not a real time kernel .... maybe i should apply the kernel PREEMPT_RT ? I'm really lost .... Micka,
Good to her Micka, I had an idea that this was the problem ( thread was getting interrupted, and state not save properly etc ), but I'm a complete Linux programming newb so . . . Glad to hear you got it resolved.