iMx键盘驱动分析

1. kernel_imx/arch/arm/mach-mx5/mx51_babbage.c文件mxc_board_init()函数中

mxc_register_device(&mxc_keypad_device, &keypad_plat_data);     //注册按键设备

mxc_register_device定义为:

int __init mxc_register_device(struct platform_device *pdev, void *data)
{
 int ret;

 pdev->dev.platform_data = data;

 ret = platform_device_register(pdev);
 if (ret)
  pr_debug("Unable to register platform device '%s': %d\n",
    pdev->name, ret);

 return ret;
}

keypad_plat_data的定义是

static struct keypad_data keypad_plat_data = {
 .rowmax = 6,
 .colmax = 8,
 .learning = 0,
 .delay = 2,
 .matrix = keymapping,   // 48个数据的矩阵键盘表
};

48个数据的矩阵键盘表定义为:

static u16 keymapping[48] = {
 KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H,
 KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P,
 KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X,
 KEY_Y, KEY_Z, KEY_ENTER, KEY_F24, KEY_F24, KEY_F24, KEY_F24, KEY_F24,
 KEY_TAB, KEY_BACKSPACE, KEY_CAPSLOCK, KEY_COMMA, KEY_F1, KEY_SPACE, KEY_DOT, KEY_F20,
 KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_F21, KEY_F10, KEY_F11, KEY_F9,
};

 

2. kernel_imx/drivers/input/keyboard/mxc_keyb.c文件中

初始探测函数

static int mxc_kpp_probe(struct platform_device *pdev)
{
  int i, irq;
  int retval;
  unsigned int reg_val;
  struct resource *res;

  keypad = (struct keypad_data *)pdev->dev.platform_data;  // 实际为keypad_plat_data数据结构

  kpp_dev.kpp_cols = keypad->colmax;       // 引入矩阵大小
  kpp_dev.kpp_rows = keypad->rowmax;
  key_pad_enabled = 0;

  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  if (!res)
    return -ENODEV;

  kpp_dev.base = ioremap(res->start, res->end - res->start + 1);
  if (!kpp_dev.base)
    return -ENOMEM;

  irq = platform_get_irq(pdev, 0);
  keypad->irq = irq;

  /* Enable keypad clock */
  kpp_clk = clk_get(&pdev->dev, "kpp_clk");
  clk_enable(kpp_clk);

  /* IOMUX configuration for keypad */
  gpio_keypad_active();

  /* Configure keypad */

  /* Enable number of rows in keypad (KPCR[7:0])
   * Configure keypad columns as open-drain (KPCR[15:8])
   *
   * Configure the rows/cols in KPP
   * LSB nibble in KPP is for 8 rows
   * MSB nibble in KPP is for 8 cols
   */
  reg_val = __raw_readw(kpp_dev.base + KPCR);
  reg_val |= (1 << keypad->rowmax) - 1; /* LSB */
  reg_val |= ((1 << keypad->colmax) - 1) << 8;  /* MSB */
  __raw_writew(reg_val, kpp_dev.base + KPCR);

  /* Write 0's to KPDR[15:8] */
  reg_val = __raw_readw(kpp_dev.base + KPDR);
  reg_val &= 0x00ff;
  __raw_writew(reg_val, kpp_dev.base + KPDR);

  /* Configure columns as output, rows as input (KDDR[15:0]) */
  reg_val = __raw_readw(kpp_dev.base + KDDR);
  reg_val |= 0xff00;
  reg_val &= 0xff00;
  __raw_writew(reg_val, kpp_dev.base + KDDR);

  reg_val = __raw_readw(kpp_dev.base + KPSR);
  reg_val &= ~(KBD_STAT_KPKR | KBD_STAT_KPKD);
  reg_val |= KBD_STAT_KPKD;
  reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
  __raw_writew(reg_val, kpp_dev.base + KPSR);
  reg_val |= KBD_STAT_KDIE;
  reg_val &= ~KBD_STAT_KRIE;
  __raw_writew(reg_val, kpp_dev.base + KPSR);

  has_leaning_key = keypad->learning;
  mxckpd_keycodes = keypad->matrix;        // 引入矩阵数据
  mxckpd_keycodes_size = keypad->rowmax * keypad->colmax;

  if ((keypad->matrix == (void *)0)
      || (mxckpd_keycodes_size == 0)) {
    retval = -ENODEV;
    goto err1;
  }

  mxckbd_dev = input_allocate_device();
  if (!mxckbd_dev) {
    printk(KERN_ERR
           "mxckbd_dev: not enough memory for input device\n");
    retval = -ENOMEM;
    goto err1;
  }

  mxckbd_dev->keycode = (void *)mxckpd_keycodes;
  mxckbd_dev->keycodesize = sizeof(mxckpd_keycodes[0]);
  mxckbd_dev->keycodemax = mxckpd_keycodes_size;
  mxckbd_dev->name = "mxckpd";
  mxckbd_dev->id.bustype = BUS_HOST;
  mxckbd_dev->open = mxc_kpp_open;
  mxckbd_dev->close = mxc_kpp_close;

  retval = input_register_device(mxckbd_dev);
  if (retval < 0) {
    printk(KERN_ERR
           "mxckbd_dev: failed to register input device\n");
    goto err2;
  }

  /* allocate required memory */
  press_scancode = kmalloc(kpp_dev.kpp_rows * sizeof(press_scancode[0]),
         GFP_KERNEL);
  release_scancode =
      kmalloc(kpp_dev.kpp_rows * sizeof(release_scancode[0]), GFP_KERNEL);

  if (!press_scancode || !release_scancode) {
    retval = -ENOMEM;
    goto err3;
  }

  for (i = 0; i < kpp_dev.kpp_rows; i++) {
    press_scancode[i] = kmalloc(kpp_dev.kpp_cols
              * sizeof(press_scancode[0][0]),
              GFP_KERNEL);
    release_scancode[i] =
        kmalloc(kpp_dev.kpp_cols * sizeof(release_scancode[0][0]),
          GFP_KERNEL);

    if (!press_scancode[i] || !release_scancode[i]) {
      retval = -ENOMEM;
      goto err3;
    }
  }

  cur_rcmap =
      kmalloc(kpp_dev.kpp_rows * sizeof(cur_rcmap[0]), GFP_KERNEL);
  prev_rcmap =
      kmalloc(kpp_dev.kpp_rows * sizeof(prev_rcmap[0]), GFP_KERNEL);

  if (!cur_rcmap || !prev_rcmap) {
    retval = -ENOMEM;
    goto err3;
  }

  __set_bit(EV_KEY, mxckbd_dev->evbit);

  for (i = 0; i < mxckpd_keycodes_size; i++)
    __set_bit(mxckpd_keycodes[i], mxckbd_dev->keybit);

  for (i = 0; i < kpp_dev.kpp_rows; i++) {
    memset(press_scancode[i], -1,
           sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
    memset(release_scancode[i], -1,
           sizeof(release_scancode[0][0]) * kpp_dev.kpp_cols);
  }
  memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
  memset(prev_rcmap, 0, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));

  key_pad_enabled = 1;
  /* Initialize the polling timer */
  init_timer(&kpp_dev.poll_timer);

  /*
   * Request for IRQ number for keypad port. The Interrupt handler
   * function (mxc_kpp_interrupt) is called when ever interrupt occurs on
   * keypad port.
   */
  retval = request_irq(irq, mxc_kpp_interrupt, 0, MOD_NAME, MOD_NAME);
  if (retval) {
    pr_debug("KPP: request_irq(%d) returned error %d\n",
       irq, retval);
    goto err3;
  }

  /* By default, devices should wakeup if they can */
  /* So keypad is set as "should wakeup" as it can */
  device_init_wakeup(&pdev->dev, 1);
  key_dev = &pdev->dev;

  if (cpu_is_mx50())
    register_early_suspend(&mxc_keypad_earlysuspend);
  return 0;

      err3:
  mxc_kpp_free_allocated();
      err2:
  input_free_device(mxckbd_dev);
      err1:
  free_irq(irq, MOD_NAME);
  clk_disable(kpp_clk);
  clk_put(kpp_clk);
  return retval;
}

定时扫描函数

static void mxc_kpp_handle_timer(unsigned long data)
{
 unsigned short reg_val;
 int i;

 if (key_pad_enabled == 0) {
  return;
 }
 if (mxc_kpp_scan_matrix() == 0) {
  /*
   * Stop scanning and wait for interrupt.
   * Enable press interrupt and disable release interrupt.
   */
  __raw_writew(0x00FF, kpp_dev.base + KPDR);
  reg_val = __raw_readw(kpp_dev.base + KPSR);
  reg_val |= (KBD_STAT_KPKR | KBD_STAT_KPKD);
  reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
  __raw_writew(reg_val, kpp_dev.base + KPSR);
  reg_val |= KBD_STAT_KDIE;
  reg_val &= ~KBD_STAT_KRIE;
  __raw_writew(reg_val, kpp_dev.base + KPSR);

  /*
   * No more keys pressed... make sure unwanted key codes are
   * not given upstairs
   */
  for (i = 0; i < kpp_dev.kpp_rows; i++) {
   memset(press_scancode[i], -1,
          sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
   memset(release_scancode[i], -1,
          sizeof(release_scancode[0][0]) *
          kpp_dev.kpp_cols);
  }
  return;
 }

 /*
  * There are still some keys pressed, continue to scan.
  * We shall scan again in 10 ms. This has to be tuned according
  * to the requirement.
  */
 kpp_dev.poll_timer.expires = jiffies + KScanRate;
 kpp_dev.poll_timer.function = mxc_kpp_handle_timer;
 add_timer(&kpp_dev.poll_timer);
}

扫描函数

static int mxc_kpp_scan_matrix(void)
{
 unsigned short reg_val;
 int col, row;
 short scancode = 0;
 int keycnt = 0;  /* How many keys are still pressed */

 /*
  * wmb() linux kernel function which guarantees orderings in write
  * operations
  */
 wmb();

 /* save cur keypad matrix to prev */

 memcpy(prev_rcmap, cur_rcmap, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
 memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));

 for (col = 0; col < kpp_dev.kpp_cols; col++) { /* Col */
  /* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
  reg_val = __raw_readw(kpp_dev.base + KPDR);
  reg_val |= 0xff00;
  __raw_writew(reg_val, kpp_dev.base + KPDR);

  /*
   * 3. Configure columns as totem pole outputs(for quick
   * discharging of keypad capacitance)
   */
  reg_val = __raw_readw(kpp_dev.base + KPCR);
  reg_val &= 0x00ff;
  __raw_writew(reg_val, kpp_dev.base + KPCR);

  udelay(2);

  /*
   * 4. Configure columns as open-drain
   */
  reg_val = __raw_readw(kpp_dev.base + KPCR);
  reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;
  __raw_writew(reg_val, kpp_dev.base + KPCR);

  /*
   * 5. Write a single column to 0, others to 1.
   * 6. Sample row inputs and save data. Multiple key presses
   * can be detected on a single column.
   * 7. Repeat steps 2 - 6 for remaining columns.
   */

  /* Col bit starts at 8th bit in KPDR */
  reg_val = __raw_readw(kpp_dev.base + KPDR);
  reg_val &= ~(1 << (8 + col));
  __raw_writew(reg_val, kpp_dev.base + KPDR);

  /* Delay added to avoid propagating the 0 from column to row
   * when scanning. */

  udelay(5);

  /* Read row input */
  reg_val = __raw_readw(kpp_dev.base + KPDR);
  for (row = 0; row < kpp_dev.kpp_rows; row++) { /* sample row */
   if (TEST_BIT(reg_val, row) == 0) {
    cur_rcmap[row] = BITSET(cur_rcmap[row], col);
    keycnt++;
   }
  }
 }

 /*
  * 8. Return all columns to 0 in preparation for standby mode.
  * 9. Clear KPKD and KPKR status bit(s) by writing to a .1.,
  * set the KPKR synchronizer chain by writing "1" to KRSS register,
  * clear the KPKD synchronizer chain by writing "1" to KDSC register
  */
 reg_val = 0x00;
 __raw_writew(reg_val, kpp_dev.base + KPDR);
 reg_val = __raw_readw(kpp_dev.base + KPDR);
 reg_val = __raw_readw(kpp_dev.base + KPSR);
 reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS |
     KBD_STAT_KDSC;
 __raw_writew(reg_val, kpp_dev.base + KPSR);

 /* Check key press status change */

 /*
  * prev_rcmap array will contain the previous status of the keypad
  * matrix.  cur_rcmap array will contains the present status of the
  * keypad matrix. If a bit is set in the array, that (row, col) bit is
  * pressed, else it is not pressed.
  *
  * XORing these two variables will give us the change in bit for
  * particular row and column.  If a bit is set in XOR output, then that
  * (row, col) has a change of status from the previous state.  From
  * the diff variable the key press and key release of row and column
  * are found out.
  *
  * If the key press is determined then scancode for key pressed
  * can be generated using the following statement:
  *    scancode = ((row * 8) + col);
  *
  * If the key release is determined then scancode for key release
  * can be generated using the following statement:
  *    scancode = ((row * 8) + col) + MXC_KEYRELEASE;
  */
 for (row = 0; row < kpp_dev.kpp_rows; row++) {
  unsigned char diff;

  /*
   * Calculate the change in the keypad row status
   */
  diff = prev_rcmap[row] ^ cur_rcmap[row];

  for (col = 0; col < kpp_dev.kpp_cols; col++) {
   if ((diff >> col) & 0x1) {
    /* There is a status change on col */
    if ((prev_rcmap[row] & BITSET(0, col)) == 0) {
     /*
      * Previous state is 0, so now
      * a key is pressed
      */
     if (has_leaning_key) {
      scancode =
          mxc_scan_matrix_leaning_key
          (row, col, 1);
     } else {
      scancode =
          ((row * kpp_dev.kpp_cols) +
           col);
      KPress = 1;
      kpp_dev.iKeyState = KStateUp;
     }
     pr_debug("Press   (%d, %d) scan=%d "
       "Kpress=%d\n",
       row, col, scancode, KPress);
     press_scancode[row][col] =
         (short)scancode;
    } else {
     /*
      * Previous state is not 0, so
      * now a key is released
      */
     if (has_leaning_key) {
      scancode =
          mxc_scan_matrix_leaning_key
          (row, col, 0);
     } else {
      scancode =
          (row * kpp_dev.kpp_cols) +
          col + MXC_KEYRELEASE;
      KPress = 0;
      kpp_dev.iKeyState = KStateDown;
     }

     pr_debug
         ("Release (%d, %d) scan=%d Kpress=%d\n",
          row, col, scancode, KPress);
     release_scancode[row][col] =
         (short)scancode;
     keycnt++;
    }
   }
  }
 }

 /*
  * This switch case statement is the
  * implementation of state machine of debounce
  * logic for key press/release.
  * The explaination of state machine is as
  * follows:
  *
  * KStateUp State:
  * This is in intial state of the state machine
  * this state it checks for any key presses.
  * The key press can be checked using the
  * variable KPress. If KPress is set, then key
  * press is identified and switches the to
  * KStateFirstDown state for key press to
  * debounce.
  *
  * KStateFirstDown:
  * After debounce delay(10ms), if the KPress is
  * still set then pass scancode generated to
  * input device and change the state to
  * KStateDown, else key press debounce is not
  * satisfied so change the state to KStateUp.
  *
  * KStateDown:
  * In this state it checks for any key release.
  * If KPress variable is cleared, then key
  * release is indicated and so, switch the
  * state to KStateFirstUp else to state
  * KStateDown.
  *
  * KStateFirstUp:
  * After debounce delay(10ms), if the KPress is
  * still reset then pass the key release
  * scancode to input device and change
  * the state to KStateUp else key release is
  * not satisfied so change the state to
  * KStateDown.
  */
 switch (kpp_dev.iKeyState) {
 case KStateUp:
  if (KPress) {
   /* First Down (must debounce). */
   kpp_dev.iKeyState = KStateFirstDown;
  } else {
   /* Still UP.(NO Changes) */
   kpp_dev.iKeyState = KStateUp;
  }
  break;

 case KStateFirstDown:
  if (KPress) {
   for (row = 0; row < kpp_dev.kpp_rows; row++) {
    for (col = 0; col < kpp_dev.kpp_cols; col++) {
     if ((press_scancode[row][col] != -1)) {
      /* Still Down, so add scancode */
      scancode =
          press_scancode[row][col];
      input_event(mxckbd_dev, EV_KEY,
           mxckpd_keycodes
           [scancode], 1);
      if (mxckpd_keycodes[scancode] ==
          KEY_LEFTSHIFT) {
       input_event(mxckbd_dev,
            EV_KEY,
            KEY_3, 1);
      }
      kpp_dev.iKeyState = KStateDown;
      press_scancode[row][col] = -1;
     }
    }
   }
  } else {
   /* Just a bounce */
   kpp_dev.iKeyState = KStateUp;
  }
  break;

 case KStateDown:
  if (KPress) {
   /* Still down (no change) */
   kpp_dev.iKeyState = KStateDown;
  } else {
   /* First Up. Must debounce */
   kpp_dev.iKeyState = KStateFirstUp;
  }
  break;

 case KStateFirstUp:
  if (KPress) {
   /* Just a bounce */
   kpp_dev.iKeyState = KStateDown;
  } else {
   for (row = 0; row < kpp_dev.kpp_rows; row++) {
    for (col = 0; col < kpp_dev.kpp_cols; col++) {
     if ((release_scancode[row][col] != -1)) {
      scancode =
          release_scancode[row][col];
      scancode =
          scancode - MXC_KEYRELEASE;
      input_event(mxckbd_dev, EV_KEY,
           mxckpd_keycodes
           [scancode], 0);
      if (mxckpd_keycodes[scancode] ==
          KEY_LEFTSHIFT) {
       input_event(mxckbd_dev,
            EV_KEY,
            KEY_3, 0);
      }
      kpp_dev.iKeyState = KStateUp;
      release_scancode[row][col] = -1;
     }
    }
   }
  }
  break;

 default:
  return -EBADRQC;
  break;
 }

 return keycnt;
}

键值处理函数

static signed short mxc_scan_matrix_leaning_key(int row, int col, int press)
{
 static unsigned first_row;
 static unsigned first_set, flag;
 signed short scancode = -1;

 if (press) {
  if ((3 == col) && ((3 == row) ||
       (4 == row) || (5 == row) || (6 == row))) {
   if (first_set == 0) {
    first_set = 1;
    first_row = row;
   } else {
    first_set = 0;
    if (((first_row == 6) || (first_row == 3))
        && ((row == 6) || (row == 3)))
     scancode = press_down_code;
    else if (((first_row == 3) || (first_row == 5))
      && ((row == 3) || (row == 5)))
     scancode = press_left_code;
    else if (((first_row == 6) || (first_row == 4))
      && ((row == 6) || (row == 4)))
     scancode = press_right_code;
    else if (((first_row == 4) || (first_row == 5))
      && ((row == 4) || (row == 5)))
     scancode = press_up_code;
    KPress = 1;
    kpp_dev.iKeyState = KStateUp;
    pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
      row, col, scancode, KPress);
   }
  } else {
   /*
    * check for other keys only
    * if the cursor key presses
    * are not detected may be
    * this needs better logic
    */
   if ((0 == (cur_rcmap[3] & BITSET(0, 3))) &&
       (0 == (cur_rcmap[4] & BITSET(0, 3))) &&
       (0 == (cur_rcmap[5] & BITSET(0, 3))) &&
       (0 == (cur_rcmap[6] & BITSET(0, 3)))) {
    scancode = ((col * kpp_dev.kpp_rows) + row);
    KPress = 1;
    kpp_dev.iKeyState = KStateUp;
    flag = 1;
    pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
      row, col, scancode, KPress);
   }
  }
 } else {
  if ((flag == 0) && (3 == col)
      && ((3 == row) || (4 == row) || (5 == row)
   || (6 == row))) {
   if (first_set == 0) {
    first_set = 1;
    first_row = row;
   } else {
    first_set = 0;
    if (((first_row == 6) || (first_row == 3))
        && ((row == 6) || (row == 3)))
     scancode = rel_down_code;
    else if (((first_row == 3) || (first_row == 5))
      && ((row == 3) || (row == 5)))
     scancode = rel_left_code;
    else if (((first_row == 6) || (first_row == 4))
      && ((row == 6) || (row == 4)))
     scancode = rel_right_code;
    else if (((first_row == 4) || (first_row == 5))
      && ((row == 4) || (row == 5)))
     scancode = rel_up_code;
    KPress = 0;
    kpp_dev.iKeyState = KStateDown;
    pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
      row, col, scancode, KPress);
   }
  } else {
   /*
    * check for other keys only
    * if the cursor key presses
    * are not detected may be
    * this needs better logic
    */
   if ((0 == (prev_rcmap[3] & BITSET(0, 3))) &&
       (0 == (prev_rcmap[4] & BITSET(0, 3))) &&
       (0 == (cur_rcmap[5] & BITSET(0, 3))) &&
       (0 == (cur_rcmap[6] & BITSET(0, 3)))) {
    scancode = ((col * kpp_dev.kpp_rows) + row) +
        MXC_KEYRELEASE;
    KPress = 0;
    flag = 0;
    kpp_dev.iKeyState = KStateDown;
    pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
      row, col, scancode, KPress);
   }
  }
 }
 return scancode;
}


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值