1

I have taken the Software-based serial port module code from [here] (https://github.com/adrianomarto/soft_uart/) and implemented it for my application for the iMX-6 processor.

Although my driver is working fine to send/receive data between MCU (STM32L476) and MPU (iMX-6) via this driver module. I am getting crash for "hrtimer state" after running this module for hours. crash dump is attached below.

Below is the details for the setup:

  • This module is used to receive incoming usart data from STM32 MCU usart set @ 4800 baud, 8N1.
  • MPU two gpio pins are configured to mimic usart through bit-bang method.
  • MPU software usart module has baud set to 4800, 8N1 with no flow control.

I am suspecting that somewhere in the soft-uart module "hrtimer_start" is called so much frequently or it is being handled incorrectly such that hrtimer is getting enqueued from "__run_hrtimer" as soon as the software is expecting it to be in callback state and that is the cause of the crash. Can anyone have a similar issue? or have information about this behavior please guide me. what could cause this kind of behavior?

Below is the code for software-based UART module

#include <linux/gpio.h> 
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/ktime.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/tty_driver.h>

#define SOFT_UART_MAJOR            0
#define N_PORTS                    1
#define NONE                       0
#define TX_BUFFER_FLUSH_TIMEOUT 4000  // milliseconds
#define DEV_BAUDRATE            4800

MODULE_LICENSE("GPL");
MODULE_AUTHOR("General");
MODULE_DESCRIPTION("Software-UART");
MODULE_VERSION("0.2");

//***********************************************************************************************//
int imx_soft_uart_init(const int gpio_tx, const int gpio_rx);
int imx_soft_uart_finalize(void);
int imx_soft_uart_open(struct tty_struct* tty);
int imx_soft_uart_close(void);
int imx_soft_uart_set_rx_callback(void (*callback)(unsigned char));
int imx_soft_uart_set_baudrate(const int baudrate);
int imx_soft_uart_send_string(const unsigned char* string, int string_size);
int imx_soft_uart_get_tx_queue_room(void);
int imx_soft_uart_get_tx_queue_size(void);
//***********************************************************************************************//

static irq_handler_t handle_rx_start(unsigned int irq, void* device, struct pt_regs* registers);
static irq_handler_t handle_rx_bytes(unsigned int irq, void* device, struct pt_regs* registers);

static enum hrtimer_restart handle_tx(struct hrtimer* timer);
static enum hrtimer_restart handle_rx(struct hrtimer* timer);
static void receive_character(unsigned char character);

static struct queue queue_tx;
static struct tty_struct* current_tty = NULL;
static DEFINE_MUTEX(current_tty_mutex);
static struct hrtimer timer_tx;
static struct hrtimer timer_rx;
static ktime_t period;
static ktime_t half_period;
unsigned long long t_half_period;
static int rx_bit_index = -1;
static void (*rx_callback)(unsigned char) = NULL;

//***********************************************************************************************//
#define QUEUE_MAX_SIZE  256

struct queue
{
  int front;
  int rear;
  int size;
  unsigned char data[QUEUE_MAX_SIZE];
};


void initialize_queue(struct queue* queue);
int  enqueue_character(struct queue* queue, const unsigned char character);
int  dequeue_character(struct queue* queue, unsigned char* character);
int  enqueue_string(struct queue* queue, const unsigned char* string, int string_size);
int  get_queue_room(struct queue* queue);
int  get_queue_size(struct queue* queue);
//***********************************************************************************************//

/**
 * Initializes a given queue.
 * @param queue given queue
 */
void initialize_queue(struct queue* queue)
{
  queue->size  = 0;
  queue->front = 0;
  queue->rear  = 0;
}

/**
 * Adds a given character into a given queue.
 * @param queue given queue
 * @param character given character
 * @return 1 if the character is added to the queue. 0 if the queue is full.
 */
int enqueue_character(struct queue* queue, const unsigned char character)
{
  int success = 0;
  if (queue->size < QUEUE_MAX_SIZE)
  {
    if (queue->size != 0)
    {
      queue->rear++;
      if (queue->rear >= QUEUE_MAX_SIZE)
      {
        queue->rear = 0;
      }
    }
    else
    {
      queue->rear = 0;
      queue->front = 0;
    }
    queue->data[queue->rear] = character;
    queue->size++;
    success = 1;
  }
  return success;
}

/**
 * Gets a character from a fiven queue.
 * @param queue given queue
 * @param character a character
 * @return 1 if a character is fetched from the queue. 0 if the queue is empy.
 */
int dequeue_character(struct queue* queue, unsigned char* character)
{
  int success = 0;
  if (queue->size > 0)
  {
    *character = queue->data[queue->front];
    queue->front++;
    if (queue->front >= QUEUE_MAX_SIZE)
    {
      queue->front = 0;
    }
    queue->size--;
    success = 1;
  }
  return success;
}

/**
 * Adds a given string to a given queue.
 * @param queue given queue
 * @param string given string
 * @param string_size size of the given string
 * @return The amount of characters successfully added to the queue.
 */
int enqueue_string(struct queue* queue, const unsigned char* string, int string_size)
{
  int n = 0;
  while (n < string_size && enqueue_character(queue, string[n]))
  {
    n++;
  }
  return n;
}

/**
 * Gets the number of characters that can be added to a given queue.
 * @return number of characters.
 */
int get_queue_room(struct queue* queue)
{
  return QUEUE_MAX_SIZE - queue->size;
}

/**
 * Gets the number of characters contained in a given queue.
 * @return number of characters.
 */
int get_queue_size(struct queue* queue)
{
  return queue->size;
}

//****************************************************************************//

static int gpio_tx = 147;
module_param(gpio_tx, int, 0);

static int gpio_rx = 146;
module_param(gpio_rx, int, 0);

// Module prototypes.
static int  soft_uart_open(struct tty_struct*, struct file*);
static void soft_uart_close(struct tty_struct*, struct file*);
static int  soft_uart_write(struct tty_struct*, const unsigned char*, int);
static int  soft_uart_write_room(struct tty_struct*);
static void soft_uart_flush_buffer(struct tty_struct*);
static int  soft_uart_chars_in_buffer(struct tty_struct*);
static void soft_uart_set_termios(struct tty_struct*, struct ktermios*);
static void soft_uart_stop(struct tty_struct*);
static void soft_uart_start(struct tty_struct*);
static void soft_uart_hangup(struct tty_struct*);
static int  soft_uart_tiocmget(struct tty_struct*);
static int  soft_uart_tiocmset(struct tty_struct*, unsigned int, unsigned int);
static int  soft_uart_ioctl(struct tty_struct*, unsigned int, unsigned int long);
static void soft_uart_throttle(struct tty_struct*);
static void soft_uart_unthrottle(struct tty_struct*);

// Module operations.
static const struct tty_operations soft_uart_operations = {
  .open            = soft_uart_open,
  .close           = soft_uart_close,
  .write           = soft_uart_write,
  .write_room      = soft_uart_write_room,
  .flush_buffer    = soft_uart_flush_buffer,
  .chars_in_buffer = soft_uart_chars_in_buffer,
  .ioctl           = soft_uart_ioctl,
  .set_termios     = soft_uart_set_termios,
  .stop            = soft_uart_stop,
  .start           = soft_uart_start,
  .hangup          = soft_uart_hangup,
  .tiocmget        = soft_uart_tiocmget,
  .tiocmset        = soft_uart_tiocmset,
  .throttle        = soft_uart_throttle,
  .unthrottle      = soft_uart_unthrottle
};

// Driver instance.
static struct tty_driver* soft_uart_driver = NULL;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
static struct tty_port port;
#endif

/**
 * Module initialization.
 */
static int __init soft_uart_init(void)
{
  imx_soft_uart_set_baudrate(DEV_BAUDRATE);
  printk(KERN_INFO "soft_uart: Initializing module...#2\n");
  if (!imx_soft_uart_init(gpio_tx, gpio_rx))
  {
    printk(KERN_ALERT "soft_uart: Failed initialize GPIO.\n");
    return -ENOMEM;
  }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
  printk(KERN_INFO "soft_uart: LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0).\n");

  // Initializes the port.
  tty_port_init(&port);
  port.low_latency = 0;

  // Allocates the driver.
  soft_uart_driver = tty_alloc_driver(N_PORTS, TTY_DRIVER_REAL_RAW);

  // Returns if the allocation fails.
  if (IS_ERR(soft_uart_driver))
  {
    printk(KERN_ALERT "soft_uart: Failed to allocate the driver.\n");
    return -ENOMEM;
  }
#else
  printk(KERN_INFO "soft_uart: LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0).\n");

  // Allocates the driver.
  soft_uart_driver = alloc_tty_driver(N_PORTS);

  // Returns if the allocation fails.
  if (!soft_uart_driver)
  {
    printk(KERN_ALERT "soft_uart: Failed to allocate the driver.\n");
    return -ENOMEM;
  }
#endif

  // Initializes the driver.
  soft_uart_driver->owner                 = THIS_MODULE;
  soft_uart_driver->driver_name           = "soft_uart";
  soft_uart_driver->name                  = "ttySOFT";
  soft_uart_driver->major                 = SOFT_UART_MAJOR;
  soft_uart_driver->minor_start           = 0;
  soft_uart_driver->flags                 = TTY_DRIVER_REAL_RAW;
  soft_uart_driver->type                  = TTY_DRIVER_TYPE_SERIAL;
  soft_uart_driver->subtype               = SERIAL_TYPE_NORMAL;
  soft_uart_driver->init_termios          = tty_std_termios;
  soft_uart_driver->init_termios.c_ispeed = 4800;
  soft_uart_driver->init_termios.c_ospeed = 4800;
  soft_uart_driver->init_termios.c_cflag  = B4800 | CREAD | CS8 | CLOCAL;

  // Sets the callbacks for the driver.
  tty_set_operations(soft_uart_driver, &soft_uart_operations);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
  // Link the port with the driver.
  tty_port_link_device(&port, soft_uart_driver, 0);
#endif

  // Registers the TTY driver.
  if (tty_register_driver(soft_uart_driver))
  {
    printk(KERN_ALERT "soft_uart: Failed to register the driver.\n");
    put_tty_driver(soft_uart_driver);
    return -1; // return if registration fails
  }
  printk(KERN_INFO "soft_uart: baudrate : %d\n", DEV_BAUDRATE);
  printk(KERN_INFO "soft_uart: Module initialized.\n");
  return 0;
}

/**
 * Cleanup function that gets called when the module is unloaded.
 */
static void __exit soft_uart_exit(void)
{
  printk(KERN_INFO "soft_uart: Finalizing the module...\n");

  // Finalizes the soft UART.
  if (!imx_soft_uart_finalize())
  {
    printk(KERN_ALERT "soft_uart: Something went wrong whilst finalizing the soft UART.\n");
  }

  // Unregisters the driver.
  if (tty_unregister_driver(soft_uart_driver))
  {
    printk(KERN_ALERT "soft_uart: Failed to unregister the driver.\n");
  }

  put_tty_driver(soft_uart_driver);
  printk(KERN_INFO "soft_uart: Module finalized.\n");
}

/**
 * Opens a given TTY device.
 * @param tty given TTY device
 * @param file
 * @return error code.
 */
static int soft_uart_open(struct tty_struct* tty, struct file* file)
{
  int error = NONE;

  if (imx_soft_uart_open(tty))
  {
    printk(KERN_INFO "soft_uart: Device opened.\n");
  }
  else
  {
    printk(KERN_ALERT "soft_uart: Device busy.\n");
    error = -ENODEV;
  }

  return error;
}

/**
 * Closes a given TTY device.
 * @param tty
 * @param file
 */
static void soft_uart_close(struct tty_struct* tty, struct file* file)
{
  // Waits for the TX buffer to be empty before closing the UART.
  int wait_time = 0;
  while ((imx_soft_uart_get_tx_queue_size() > 0)
    && (wait_time < TX_BUFFER_FLUSH_TIMEOUT))
  {
    msleep(100);
    wait_time += 100;
  }

  if (imx_soft_uart_close())
  {
    printk(KERN_INFO "soft_uart: Device closed.\n");
  }
  else
  {
    printk(KERN_ALERT "soft_uart: Could not close the device.\n");
  }
}

/**
 * Writes the contents of a given buffer into a given TTY device.
 * @param tty given TTY device
 * @param buffer given buffer
 * @param buffer_size number of bytes contained in the given buffer
 * @return number of bytes successfuly written into the TTY device
 */
static int soft_uart_write(struct tty_struct* tty, const unsigned char* buffer, int buffer_size)
{
  return imx_soft_uart_send_string(buffer, buffer_size);
}

/**
 * Tells the kernel the number of bytes that can be written to a given TTY.
 * @param tty given TTY
 * @return number of bytes
 */
static int soft_uart_write_room(struct tty_struct* tty)
{
  return imx_soft_uart_get_tx_queue_room();
}

/**
 * Does nothing.
 * @param tty
 */
static void soft_uart_flush_buffer(struct tty_struct* tty)
{
}

/**
 * Tells the kernel the number of bytes contained in the buffer of a given TTY.
 * @param tty given TTY
 * @return number of bytes
 */
static int soft_uart_chars_in_buffer(struct tty_struct* tty)
{
  return imx_soft_uart_get_tx_queue_size();
}

/**
 * Sets the UART parameters for a given TTY (only the baudrate is taken into account).
 * @param tty given TTY
 * @param termios parameters
 */
static void soft_uart_set_termios(struct tty_struct* tty, struct ktermios* termios)
{
  int cflag = 0;
  speed_t baudrate = tty_get_baud_rate(tty);
  printk(KERN_INFO "soft_uart: soft_uart_set_termios: baudrate = %d.\n", baudrate);

  // Gets the cflag.
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
  cflag = tty->termios.c_cflag;
#else
  cflag = tty->termios->c_cflag;
#endif

  // Verifies the number of data bits (it must be 8).
  if ((cflag & CSIZE) != CS8)
  {
    printk(KERN_ALERT "soft_uart: Invalid number of data bits.\n");
  }

  // Verifies the number of stop bits (it must be 1).
  if (cflag & CSTOPB)
  {
    printk(KERN_ALERT "soft_uart: Invalid number of stop bits.\n");
  }

  // Verifies the parity (it must be none).
  if (cflag & PARENB)
  {
    printk(KERN_ALERT "soft_uart: Invalid parity.\n");
  }

  // Configure the baudrate.
  if (!imx_soft_uart_set_baudrate(baudrate))
  {
    printk(KERN_ALERT "soft_uart: Invalid baudrate.\n");
  }
}

/**
 * Does nothing.
 * @param tty
 */
static void soft_uart_stop(struct tty_struct* tty)
{
  printk(KERN_DEBUG "soft_uart: soft_uart_stop.\n");
}

/**
 * Does nothing.
 * @param tty
 */
static void soft_uart_start(struct tty_struct* tty)
{
  printk(KERN_DEBUG "soft_uart: soft_uart_start.\n");
}

/**
 * Does nothing.
 * @param tty
 */
static void soft_uart_hangup(struct tty_struct* tty)
{
  printk(KERN_DEBUG "soft_uart: soft_uart_hangup.\n");
}

/**
 * Does nothing.
 * @param tty
 */
static int soft_uart_tiocmget(struct tty_struct* tty)
{
  return 0;
}

/**
 * Does nothing.
 * @param tty
 * @param set
 * @param clear
 */
static int soft_uart_tiocmset(struct tty_struct* tty, unsigned int set, unsigned int clear)
{
  return 0;
}

/**
 * Does nothing.
 * @param tty
 * @param command
 * @param parameter
 */
static int soft_uart_ioctl(struct tty_struct* tty, unsigned int command, unsigned int long parameter)
{
  int error = NONE;

  switch (command)
  {
    case TIOCMSET:
      error = NONE;
      break;

    case TIOCMGET:
      error = NONE;
      break;

      default:
        error = -ENOIOCTLCMD;
        break;
  }

  return error;
}

/**
 * Does nothing.
 * @param tty
 */
static void soft_uart_throttle(struct tty_struct* tty)
{
  printk(KERN_DEBUG "soft_uart: soft_uart_throttle.\n");
}

/**
 * Does nothing.
 * @param tty
 */
static void soft_uart_unthrottle(struct tty_struct* tty)
{
  printk(KERN_DEBUG "soft_uart: soft_uart_unthrottle.\n");
}

// Module entry points.
module_init(soft_uart_init);
module_exit(soft_uart_exit);

//**************************************************************************************************//

/**
 * Initializes the Soft UART infrastructure.
 * This must be called during the module initialization.
 * The GPIO pin used as TX is configured as output.
 * The GPIO pin used as RX is configured as input.
 * @param gpio_tx GPIO pin used as TX
 * @param gpio_rx GPIO pin used as RX
 * @return 1 if the initialization is successful. 0 otherwise.
 */
int imx_soft_uart_init(const int _gpio_tx, const int _gpio_rx)
{
  bool success = true;

  mutex_init(&current_tty_mutex);

  // Initializes the TX timer.
  hrtimer_init(&timer_tx, CLOCK_MONOTONIC_RAW, HRTIMER_MODE_REL);
  timer_tx.function = &handle_tx;

  // Initializes the RX timer.
  hrtimer_init(&timer_rx, CLOCK_MONOTONIC_RAW, HRTIMER_MODE_REL);
  timer_rx.function = &handle_rx;

  // Initializes the GPIO pins.
  gpio_tx = _gpio_tx;
  gpio_rx = _gpio_rx;

  success &= gpio_request(gpio_tx, "soft_uart_tx") == 0;
  success &= gpio_direction_output(gpio_tx, 1) == 0;

  success &= gpio_request(gpio_rx, "soft_uart_rx") == 0;
  success &= gpio_direction_input(gpio_rx) == 0;

     // Initializes the interruption.
  // success &= request_threaded_irq(
  //   gpio_to_irq(gpio_rx), (irq_handler_t) handle_rx_bytes,
  //   (irq_handler_t) handle_rx_start,
  //   IRQF_TRIGGER_FALLING,
  //   "soft_uart_irq_handler",
  //   NULL) == 0;

  // Initializes the interruption.
  success &= request_irq(
    gpio_to_irq(gpio_rx),
    (irq_handler_t) handle_rx_start,
    IRQF_TRIGGER_FALLING,
    "soft_uart_irq_handler",
    NULL) == 0;
  disable_irq(gpio_to_irq(gpio_rx));

  return success;
}

/**
 * Finalizes the Soft UART infrastructure.
 */
int imx_soft_uart_finalize(void)
{
  free_irq(gpio_to_irq(gpio_rx), NULL);
  gpio_set_value(gpio_tx, 0);
  gpio_free(gpio_tx);
  gpio_free(gpio_rx);
  return 1;
}

/**
 * Opens the Soft UART.
 * @param tty
 * @return 1 if the operation is successful. 0 otherwise.
 */
int imx_soft_uart_open(struct tty_struct* tty)
{
  int success = 0;
  mutex_lock(&current_tty_mutex);
  rx_bit_index = -1;
  if (current_tty == NULL)
  {
    current_tty = tty;
    initialize_queue(&queue_tx);
    success = 1;
    enable_irq(gpio_to_irq(gpio_rx));
  }
  mutex_unlock(&current_tty_mutex);
  return success;
}

/**
 * Closes the Soft UART.
 */
int imx_soft_uart_close(void)
{
  mutex_lock(&current_tty_mutex);
  disable_irq(gpio_to_irq(gpio_rx));
  hrtimer_cancel(&timer_tx);
  hrtimer_cancel(&timer_rx);
  current_tty = NULL;
  mutex_unlock(&current_tty_mutex);
  return 1;
}

/**
 * Sets the Soft UART baudrate.
 * @param baudrate desired baudrate
 * @return 1 if the operation is successful. 0 otherwise.
 */
int imx_soft_uart_set_baudrate(const int baudrate) 
{
  period = ktime_set(0, 1000000000/baudrate);
  half_period = ktime_set(0, 1000000000/baudrate/2);
  t_half_period = half_period.tv64;
  gpio_set_debounce(gpio_rx, 1000/baudrate/2);
  return 1;
}

/**
 * Adds a given string to the TX queue.
 * @param string given string
 * @param string_size size of the given string
 * @return The amount of characters successfully added to the queue.
 */
int imx_soft_uart_send_string(const unsigned char* string, int string_size)
{
  int result = enqueue_string(&queue_tx, string, string_size);

  // Starts the TX timer if it is not already running.
  if (!hrtimer_active(&timer_tx))
  {
    hrtimer_start(&timer_tx, period, HRTIMER_MODE_REL);
  }

  return result;
}

/*
 * Gets the number of characters that can be added to the TX queue.
 * @return number of characters.
 */
int imx_soft_uart_get_tx_queue_room(void)
{
  return get_queue_room(&queue_tx);
}

/*
 * Gets the number of characters in the TX queue.
 * @return number of characters.
 */
int imx_soft_uart_get_tx_queue_size(void)
{
  return get_queue_size(&queue_tx);
}

/**
 * Sets the callback function to be called on received character.
 * @param callback the callback function
 */
int imx_soft_uart_set_rx_callback(void (*callback)(unsigned char))
{
    rx_callback = callback;
    return 1;
}

//-----------------------------------------------------------------------------
// Internals
//-----------------------------------------------------------------------------

/**
 * If we are waiting for the RX start bit, then starts the RX timer. Otherwise,
 * does nothing.
 */
static irq_handler_t handle_rx_start(unsigned int irq, void* device, struct pt_regs* registers)
{
  if (rx_bit_index == -1)
  {
    hrtimer_start(&timer_rx, ktime_set( 0, t_half_period), HRTIMER_MODE_REL);
  }
  return (irq_handler_t) IRQ_HANDLED;
}

/**
 * If we are waiting for the RX start bit, then starts the RX timer. Otherwise,
 * does nothing.
 */
static irq_handler_t handle_rx_bytes(unsigned int irq, void* device, struct pt_regs* registers)
{
  return (irq_handler_t) IRQ_WAKE_THREAD;
}

/**
 * Dequeues a character from the TX queue and sends it.
 */
static enum hrtimer_restart handle_tx(struct hrtimer* timer)
{
  ktime_t current_time = ktime_get();
  static unsigned char character = 0;
  static int bit_index = -1;
  enum hrtimer_restart result = HRTIMER_NORESTART;
  bool must_restart_timer = false;

  // Start bit.
  if (bit_index == -1)
  {
    if (dequeue_character(&queue_tx, &character))
    {
      gpio_set_value(gpio_tx, 0);
      bit_index++;
      must_restart_timer = true;
    }
  }

  // Data bits.
  else if (0 <= bit_index && bit_index < 8)
  {
    gpio_set_value(gpio_tx, 1 & (character >> bit_index));
    bit_index++;
    must_restart_timer = true;
  }

  // Stop bit.
  else if (bit_index == 8)
  {
    gpio_set_value(gpio_tx, 1);
    character = 0;
    bit_index = -1;
    must_restart_timer = get_queue_size(&queue_tx) > 0;
  }

  // Restarts the TX timer.
  if (must_restart_timer)
  {
    hrtimer_forward(&timer_tx, current_time, period);
    result = HRTIMER_RESTART;
  }

  return result;
}

/*
 * Receives a character and sends it to the kernel.
 */
static enum hrtimer_restart handle_rx(struct hrtimer* timer)
{
  ktime_t current_time = ktime_get();
  static unsigned int character = 0;
  int bit_value = gpio_get_value(gpio_rx);
  enum hrtimer_restart result = HRTIMER_NORESTART;
  bool must_restart_timer = false;

  // Start bit.
  if (rx_bit_index == -1)
  {
    rx_bit_index++;
    character = 0;
    must_restart_timer = true;
  }

  // Data bits.
  else if (0 <= rx_bit_index && rx_bit_index < 8)
  {
    if (bit_value == 0)
    {
      character &= 0xfeff;
    }
    else
    {
      character |= 0x0100;
    }

    rx_bit_index++;
    character >>= 1;
    must_restart_timer = true;
  }

  // Stop bit.
  else if (rx_bit_index == 8)
  {
    receive_character(character);
    rx_bit_index = -1;
  }

  // Restarts the RX timer.
  if (must_restart_timer)
  {
    hrtimer_forward(&timer_rx, current_time, period);
    result = HRTIMER_RESTART;
  }

  return result;
}

/**
 * Adds a given (received) character to the RX buffer, which is managed by the kernel,
 * and then flushes (flip) it.
 * @param character given character
 */
void receive_character(unsigned char character)
{
  mutex_lock(&current_tty_mutex);
  if (rx_callback != NULL) {
      (*rx_callback)(character);
  } else {
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
      if (current_tty != NULL && current_tty->port != NULL)
      {
        tty_insert_flip_char(current_tty->port, character, TTY_NORMAL);
        tty_flip_buffer_push(current_tty->port);
      }
    #else
      if (tty != NULL)
      {
        tty_insert_flip_char(current_tty, character, TTY_NORMAL);
        tty_flip_buffer_push(tty);
      }
    #endif
  }
  mutex_unlock(&current_tty_mutex);
}

Crash details:

[2019-11-23 21:58:12] ------------[ cut here ]------------
[2019-11-23 21:58:12] WARNING: CPU: 1 PID: 707 at /home/shalin/Documents/mainboard_src/yocto/core-image-base/linux_src/kernel/time/h)
[2019-11-23 21:58:12] Modules linked in: brcmfmac brcmutil usb_f_ecm g_ether usb_f_rndis u_ether libcomposite soft_uart bt8xxx(O) sd8xxx(O) mlan(PO) cfg80211
[2019-11-23 21:58:12] CPU: 1 PID: 707 Comm: hw_watchdog Tainted: P           O    4.1.15-2.0.0-ga+yocto+g83728b9 #4
[2019-11-23 21:58:12] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[2019-11-23 21:58:12] Backtrace: 
[2019-11-23 21:58:12] [<8010c358>] (dump_backtrace) from [<8010c5d4>] (show_stack+0x20/0x24)
[2019-11-23 21:58:12]  r7:80190514 r6:80e45ca0 r5:00000000 r4:80e45ca0
[2019-11-23 21:58:12] [<8010c5b4>] (show_stack) from [<80923a58>] (dump_stack+0x7c/0xbc)
[2019-11-23 21:58:12] [<809239dc>] (dump_stack) from [<80133844>] (warn_slowpath_common+0x94/0xc4)
[2019-11-23 21:58:12]  r7:80190514 r6:000004ce r5:00000009 r4:00000000
[2019-11-23 21:58:12] [<801337b0>] (warn_slowpath_common) from [<80133978>] (warn_slowpath_null+0x2c/0x34)
[2019-11-23 21:58:12]  r8:00000001 r7:ab72e3c0 r6:ab72e3f8 r5:00005e38 r4:7f19e2a8
[2019-11-23 21:58:12] [<8013394c>] (warn_slowpath_null) from [<80190514>] (__run_hrtimer+0x248/0x294)
[2019-11-23 21:58:12] [<801902cc>] (__run_hrtimer) from [<8019096c>] (hrtimer_interrupt+0x138/0x344)
[2019-11-23 21:58:12]  r9:00000001 r8:ab72e3c0 r7:00000000 r6:ab72e3f8 r5:00005e38 r4:be670b5f
[2019-11-23 21:58:12] [<80190834>] (hrtimer_interrupt) from [<80110d24>] (twd_handler+0x40/0x50)
[2019-11-23 21:58:12]  r10:80e46000 r9:a8006d80 r8:00000001 r7:00000010 r6:a8035340 r5:ab7344c0
[2019-11-23 21:58:12]  r4:00000001
[2019-11-23 21:58:12] [<80110ce4>] (twd_handler) from [<80180574>] (handle_percpu_devid_irq+0xac/0x1d0)
[2019-11-23 21:58:12]  r5:ab7344c0 r4:00000010
[2019-11-23 21:58:12] [<801804c8>] (handle_percpu_devid_irq) from [<8017bbdc>] (generic_handle_irq+0x3c/0x4c)
[2019-11-23 21:58:12]  r10:a872bcc0 r9:a8020000 r8:00000001 r7:00000000 r6:00000010 r5:00000000
[2019-11-23 21:58:12]  r4:00000010 r3:801804c8
[2019-11-23 21:58:12] [<8017bba0>] (generic_handle_irq) from [<8017bf08>] (__handle_domain_irq+0x8c/0xfc)
[2019-11-23 21:58:12]  r5:00000000 r4:80d9ac34
[2019-11-23 21:58:12] [<8017be7c>] (__handle_domain_irq) from [<80101560>] (gic_handle_irq+0x34/0x6c)
[2019-11-23 21:58:12]  r10:000033d4 r9:00000000 r8:00000006 r7:f4a00100 r6:a872bcc0 r5:80e02f7c
[2019-11-23 21:58:12]  r4:f4a0010c r3:a872bcc0
[2019-11-23 21:58:12] [<8010152c>] (gic_handle_irq) from [<8010d240>] (__irq_svc+0x40/0x74)
[2019-11-23 21:58:12] Exception stack(0xa872bcc0 to 0xa872bd08)
[2019-11-23 21:58:12] bcc0: 00000001 80000093 00000001 20000013 80ea5eb0 00000002 0000002b 80ed14a8
[2019-11-23 21:58:12] bce0: 00000006 00000000 000033d4 a872bd5c a872bc58 a872bd08 809296d8 8017a7a0
[2019-11-23 21:58:12] bd00: 60000013 ffffffff
[2019-11-23 21:58:12]  r7:a872bcf4 r6:ffffffff r5:60000013 r4:8017a7a0
[2019-11-23 21:58:12] [<8017a468>] (console_unlock) from [<8017abdc>] (vprintk_emit+0x2ac/0x50c)
[2019-11-23 21:58:12]  r10:00000000 r9:00000000 r8:00000000 r7:80e915a8 r6:80ed1efc r5:0000002a
[2019-11-23 21:58:12]  r4:00000001
[2019-11-23 21:58:12] [<8017a930>] (vprintk_emit) from [<8056a758>] (dev_vprintk_emit+0xc0/0x1f8)
[2019-11-23 21:58:12]  r10:a872bebc r9:80c0543c r8:80c36ab0 r7:a872bde4 r6:a8512200 r5:80e02508
[2019-11-23 21:58:12]  r4:00000013
[2019-11-23 21:58:12] [<8056a698>] (dev_vprintk_emit) from [<8056a8d0>] (dev_printk_emit+0x40/0x5c)
[2019-11-23 21:58:12]  r10:a86dc788 r9:00000008 r8:00000000 r7:a602bc38 r6:a849649c r5:a84964b4
[2019-11-23 21:58:12]  r4:80e02508
[2019-11-23 21:58:12] [<8056a894>] (dev_printk_emit) from [<8056ac40>] (__dev_printk+0x58/0x98)
[2019-11-23 21:58:12]  r3:80c36ab0 r2:80c0543c
[2019-11-23 21:58:12]  r4:80e02508
[2019-11-23 21:58:12] [<8056abe8>] (__dev_printk) from [<8056ae28>] (dev_crit+0x58/0x74)
[2019-11-23 21:58:12] [<8056add4>] (dev_crit) from [<806b9f0c>] (watchdog_release+0xd4/0xd8)
[2019-11-23 21:58:12]  r3:00000003 r2:00000000 r1:80c36b18
[2019-11-23 21:58:12]  r4:a8496434
[2019-11-23 21:58:12] [<806b9e38>] (watchdog_release) from [<8024ce80>] (__fput+0x90/0x1e0)
[2019-11-23 21:58:12]  r7:a602bc38 r6:a8534850 r5:a84e8da8 r4:a86dc780
[2019-11-23 21:58:12] [<8024cdf0>] (__fput) from [<8024d040>] (____fput+0x18/0x1c)
[2019-11-23 21:58:12]  r10:00000000 r9:a872a000 r8:80108124 r7:a8b27700 r6:80e90f20 r5:00000000
[2019-11-23 21:58:12]  r4:a8b27b18
[2019-11-23 21:58:12] [<8024d028>] (____fput) from [<8014f834>] (task_work_run+0xc0/0xf8)
[2019-11-23 21:58:12] [<8014f774>] (task_work_run) from [<8010bbd8>] (do_work_pending+0x8c/0xb4)
[2019-11-23 21:58:12]  r7:00000006 r6:a872bfb0 r5:80108124 r4:a872a000
[2019-11-23 21:58:12] [<8010bb4c>] (do_work_pending) from [<80107fcc>] (work_pending+0xc/0x20)
[2019-11-23 21:58:12]  r7:00000006 r6:00010a78 r5:00000004 r4:00000001
[2019-11-23 21:58:12] ---[ end trace 4de5a0751e851227 ]---
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • I don't think a software UART is the best idea for continuous operation. Even at slow baud rates, the base workload imposed by bit-banging non-stop can be too much. If you don't have a hardware UART and there is no other option I would suggest you monitor the processes that are running at the time of the crash and make sure you need those processes running. You might also want to play with the priority of the UART task and/or take a look at the preempt RT patch. If fyou can accept even slower baud rates, that might help too – Marcos G. Nov 25 '19 at 09:47
  • with slower baud there is a chance of data overlapping, so that is not an option. But How preempt RT patch will help to resolve this issue? Is there a way to just make hrtimere kernel source to be compatible for soft-real-time systems rather changing the whole linux kernel by applying prempt RT patch. – Shalin Doshi Nov 26 '19 at 07:01
  • The idea is to give maximum priority to the software UART task. Anything in that direction should help. The preempt RT might be worth a try, see [here](https://lists.gt.net/linux/kernel/3390804). If that won't fix your issue I'm afraid you need to be prepared to get down the rabbit hole of the kernel mailing list... Unless for educational purposes I don't see the point of a software UART. It's like running your car at night with a hand torch instead of its headlights. Can't your product afford the cost/weight or any other drawbacks of installing a hardware UART? – Marcos G. Nov 26 '19 at 07:21
  • Not sure [this](https://www.ctrlinux.com/blog/?p=154) would be applicable, take a look a the last part... Be aware that increased power consumption might result, not sure if that would be an issue – Marcos G. Nov 26 '19 at 07:38
  • In your *"crash details"*, the `WARNING: ...` line appears to be prematurely truncated. Normally there's a kernel source module name, a line number, and a procedure name, e.g. `__run_hrtimer()`. This *"crash"* should not be fatal, it's just a warning, indicating that there's a non-fatal issue that needs attention, e.g. probably there's an attempt to re-enter the hrtimer callback function while it's still executing. – sawdust Nov 27 '19 at 02:25
  • You can raise execution priorities without resorting to the RT patch. The purpose of the RT patch is primarily to provide *consistent* (or reliable) interrupt latency. Considering that bit-banging requires reliable sampling of the input, it should be considered. Maybe you need to measure the timing variation of your hrtimer callback function by toggling a GPIO output? – sawdust Nov 27 '19 at 02:38
  • @MarcosG. I had applied the Preempt RT patch but with this patch, my system is rebooting here n there without any software crash or backtrace. Don't know if there is an issue with the RT patch or it isn't compatible with my application. I have to debug this further. But could you point out some direction from where to start debugging? – Shalin Doshi Nov 27 '19 at 09:37
  • I can try to give you a hand but I think it would be better if you open a new question explaining what is exactly going on, otherwise, I don't have any silver bullets at hand. Did you look at increasing the priority of the task? I guess the challenge is to reproduce the crash if it only happens every so many hours ruunning... – Marcos G. Nov 27 '19 at 09:50
  • @MarcosG. Things are working correctly after increasing the priority of the task. will observe the system for days for further analysis till than thank u for the help :) – Shalin Doshi Nov 28 '19 at 07:19
  • great, you are welcome, and good luck. – Marcos G. Nov 28 '19 at 07:46

0 Answers0