首页 > 解决方案 > hrtimer 状态发生变化,并在通过用户定义的软 uart 驱动程序访问时崩溃

问题描述

我从 [这里] ( https://github.com/adrianomarto/soft_uart/ ) 获取了基于软件的串行端口模块代码,并为我的 iMX-6 处理器应用程序实现了它。

虽然我的驱动程序可以通过此驱动程序模块在 MCU(STM32L476)和 MPU(iMX-6)之间发送/接收数据。运行此模块数小时后,我因“hrtimer 状态”而崩溃。故障转储附在下面。

以下是设置的详细信息:

我怀疑软uart模块“hrtimer_start”中的某个地方被频繁调用,或者处理不正确,以至于一旦软件期望它处于回调状态,hrtimer就会从“__run_hrtimer”排队是崩溃的原因。任何人都可以有类似的问题吗?或有关于此行为的信息,请指导我。什么可能导致这种行为?

下面是基于软件的UART模块的代码

#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);
}

崩溃细节:

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

标签: clinuxlinux-kernellinux-device-driverusart

解决方案


推荐阅读