手機 - Nokia N900 - Maemo - 解決/dev/ttyS2無法使用問題



參考資訊:
https://lore.kernel.org/patchwork/patch/171865/

雖然在Maemo系統下有USB Host功能可以使用,不過缺點則是UART(/dev/ttyS2)無法使用,而Native Debian系統下,則是UART(/dev/ttyO2)可以使用,但是USB Host無法使用,原因是驅動程式的問題,雖然司徒更喜愛Native Debian系統,畢竟可以直接使用Debian的套件,不過在Native Debian系統下,還是有很多不完善的地方,如:過於耗電(150mA, Idle)、USB Host無法使用、Camera無法使用、Display架構過於耗CPU資源,而最讓司徒頭痛就是耗電問題,因為150mA的耗電速度,大約是一小時10%(原廠電池),因此,這讓司徒又重新安裝回Maemo系統,加上司徒目前已經可以解決Root 256MB的容量限制,因此,司徒只要把Maemo的UART(/dev/ttyS2)問題解決後,基於Maemo系統的N900就可以成為一台真正好用的開發機器,而經過多次努力移植以及測試後,司徒發現其實只要修改一個小判斷就可以讓Maemo系統支援/dev/ttyS2,不需要重新移植OMAP UART驅動

司徒一開始使用Patch方式添加,發現只有兩個地方需要修改,分別是arch/arm/mach-omap2/serial.c、drivers/omap-serial.c,雖然有一些Redefine問題,不過手動修復後,TTY依然沒有被添加,於是司徒往回翻舊版Kernel,發現omap-serial.c是在Kernel 2.6.37新增,於是直接使用2.6.37 omap-serial.c,結果還是無法使用,後來發現omap-serial.c的probe()沒有被呼叫,於是查看arch/arm/mach-omap2/serial.c的添加是否有問題

void __init omap_serial_init(void)
{
  int i;
  const struct omap_uart_config *info;
  char name[16];

  /*
   * Make sure the serial ports are muxed on at this point.
   * You have to mux them off in device drivers later on
   * if not needed.
   */

  info = omap_get_config(OMAP_TAG_UART, struct omap_uart_config);

  if (info == NULL)
    return;

  for (i = 0; i < OMAP_MAX_NR_PORTS; i++) {
    struct plat_serial8250_port *p = serial_platform_data + i;
    struct omap_uart_state *uart = &omap_uart[i];

    if (!(info->enabled_uarts & (1 << i))) {
      p->membase = NULL;
      p->mapbase = 0;
      continue;
    }

info->enabled_uarts拿到的uart flag都是disabled,於是往回找omap_get_config()

const void *__omap_get_config(u16 tag, size_t len, int nr)
{
  return get_config(tag, len, nr, NULL);
}
EXPORT_SYMBOL(__omap_get_config);

P.S. 位於arch/arm/plat-omap/common.c

往回找到get_config()

static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out)
{
  struct omap_board_config_kernel *kinfo = NULL;
  int i;

#ifdef CONFIG_OMAP_BOOT_TAG
  struct omap_board_config_entry *info = NULL;

  if (omap_bootloader_tag_len > 4)
    info = (struct omap_board_config_entry *) omap_bootloader_tag;
  while (info != NULL) {
    u8 *next;

    if (info->tag == tag) {
      if (skip == 0)
        break;
      skip--;
    }

    if ((info->len & 0x03) != 0) {
      /* We bail out to avoid an alignment fault */
      printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n",
             info->len, info->tag);
      return NULL;
    }
    next = (u8 *) info + sizeof(*info) + info->len;
    if (next >= omap_bootloader_tag + omap_bootloader_tag_len)
      info = NULL;
    else
      info = (struct omap_board_config_entry *) next;
  }
  if (info != NULL) {
    /* Check the length as a lame attempt to check for
     * binary inconsistency. */
    if (len != NO_LENGTH_CHECK) {
      /* Word-align len */
      if (len & 0x03)
        len = (len + 3) & ~0x03;
      if (info->len != len) {
        printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n",
               tag, len, info->len);
        return NULL;
      }
    }
    if (len_out != NULL)
      *len_out = info->len;
    return info->data;
  }
#endif
  /* Try to find the config from the board-specific structures
   * in the kernel. */
  for (i = 0; i < omap_board_config_size; i++) {
    if (omap_board_config[i].tag == tag) {
      if (skip == 0) {
        kinfo = &omap_board_config[i];
        break;
      } else {
        skip--;
      }
    }
  }
  if (kinfo == NULL)
    return NULL;
  return kinfo->data;
}

接著看一下omap_board_config

static struct omap_uart_config rx51_uart_config = { 
  .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
};

P.S. 位於arch/arm/mach-omap2/board-rx51.c

從這個結構來看UART1、UART2、UART3應該都是被Enabled才是,怎會到omap_serial_init()就變成Disabled呢?於是埋了一些資訊Debug,這才發現CONFIG_OMAP_BOOT_TAG是被定義的,由UBoot傳入參數,這時司徒突然想起ITEM_OMAPTAG這個東西,原來是從那裡傳入,而列印後,發現UART確實被Disabled,於是司徒直接修改get_config(),只要是UART Tag(OMAP_TAG_UART)就直接讀取Kernel定義的Flag

static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out)
{
  struct omap_board_config_kernel *kinfo = NULL;
  int i;

#ifdef CONFIG_OMAP_BOOT_TAG
  if(tag != OMAP_TAG_UART){
    struct omap_board_config_entry *info = NULL;

    if (omap_bootloader_tag_len > 4)
      info = (struct omap_board_config_entry *) omap_bootloader_tag;
    while (info != NULL) {
      u8 *next;

      if (info->tag == tag) {
        if (skip == 0)
          break;
        skip--;
      }

      if ((info->len & 0x03) != 0) {
        /* We bail out to avoid an alignment fault */
        printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n",
               info->len, info->tag);
        return NULL;
      }
      next = (u8 *) info + sizeof(*info) + info->len;
      if (next >= omap_bootloader_tag + omap_bootloader_tag_len)
        info = NULL;
      else
        info = (struct omap_board_config_entry *) next;
    }
    if (info != NULL) {
      /* Check the length as a lame attempt to check for
       * binary inconsistency. */
      if (len != NO_LENGTH_CHECK) {
        /* Word-align len */
        if (len & 0x03)
          len = (len + 3) & ~0x03;
        if (info->len != len) {
          printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n",
                 tag, len, info->len);
          return NULL;
        }
      }
      if (len_out != NULL)
        *len_out = info->len;
      return info->data;
    }
  }
#endif
  /* Try to find the config from the board-specific structures
   * in the kernel. */
  for (i = 0; i < omap_board_config_size; i++) {
    if (omap_board_config[i].tag == tag) {
      if (skip == 0) {
        kinfo = &omap_board_config[i];
        break;
      } else {
        skip--;
      }
    }
  }
  if (kinfo == NULL)
    return NULL;
  return kinfo->data;
}

重新編譯zImage後,就可以使用/dev/ttyS2

$ dmesg | grep serial
    [65435.071594] serial8250.0: ttyS0 at MMIO 0x4806a000 (irq = 72) is a ST16654
    [65435.092071] serial8250.0: ttyS1 at MMIO 0x4806c000 (irq = 73) is a ST16654
    [65435.112548] serial8250.0: ttyS2 at MMIO 0x49020000 (irq = 74) is a ST16654 


感動