linux设备驱动之控制台驱动(2)
时间:2024-02-18 05:13:14 点击:229

  对于规范模式,要读满一行才会返回用户空间.例如我们在shell上输入指令的时候,要按下enter键指令才会进行处理.在 tty->read_flags数组中定义了一些满行的标志,如果read_buf中对应的数据在tty->read_flags中被置位. 就会认为这次读入已经到结尾了.在这里还要注意的是,不要将__DISABLED_CHAR即’/0’拷贝到用户空间.

  对于原始模式,只需要将read_buf中的数据读入到用户空间就可以返回了.在这里需要注意read_buf是一个环形缓存,需要copy两次.例如tail在head之前的情况.

  /* If there is enough space in the read buffer now, let the

  * low-level driver know. We use n_tty_chars_in_buffer() to

  * check the buffer, as it now knows about canonical mode.

  * Otherwise, if the driver is throttled and the line is

  * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,

  * we won't get any more characters.

  */

  if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {

  n_tty_set_room(tty);

  check_unthrottle(tty);

  }

  OK.到这里,read_buf中或多或少已经有数据被取出了.如果当前的数据量少于TTY_THRESHOLD_UNTHROTTLE.就可以调用check_unthrottle()将其它的写进程唤醒了

  if (b - buf >= minimum)

  break;

  if (time)

  timeout = time;

  }

  mutex_unlock(&tty->atomic_read_lock);

  remove_wait_queue(&tty->read_wait, &wait);

  if (!waitqueue_active(&tty->read_wait))

  tty->minimum_to_wake = minimum;

  __set_current_state(TASK_RUNNING);

  已经读完了数据,是该到清理的时候了.将进程移出等待队列,并当进程状态设为TASK_RUNNING

  size = b - buf;

  if (size) {

  retval = size;

  if (nr)

  clear_bit(TTY_PUSH, &tty->flags);

  } else if (test_and_clear_bit(TTY_PUSH, &tty->flags))

  goto do_it_again;

  //更新剩余空间数

  n_tty_set_room(tty);

  return retval;

  }

  TTY_PUSH:是由底层驱动程序在读到一个EOF字符并将其放入缓存区造成的,表示用户要尽快将缓存区数据取走.

  如果本次操作没有读取任何数据,且被设置了TTY_PUSH,则跳转到do_it_again,继续执行.如果本次操作读取了数据,可以等到下一次read的时候再来取.

  最后,更新read_buf的剩余空间数.

  五:控制终端数据的来源

  从这个函数里面我们可以看到,数据是从read_buf中取出来的,但是谁将数据放入到read_buf中的呢?为了探究出它的根源.我们还得要从vty_init()说起.

  在之前分析过. vty_init()会调用一个表面字义看起来与键盘相关的一个子函数: kbd_init().跟踪这个函数:

  int __init kbd_init(void)

  {

  int i;

  int error;

  for (i = 0; i < MAX_NR_CONSOLES; i++) {

  kbd_table[i].ledflagstate = KBD_DEFLEDS;

  kbd_table[i].default_ledflagstate = KBD_DEFLEDS;

  kbd_table[i].ledmode = LED_SHOW_FLAGS;

  kbd_table[i].lockstate = KBD_DEFLOCK;

  kbd_table[i].slockstate = 0;

  kbd_table[i].modeflags = KBD_DEFMODE;

  kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;

  }

  error = input_register_handler(&kbd_handler);

  if (error)

  return error;

  tasklet_enable(&keyboard_tasklet);

  tasklet_schedule(&keyboard_tasklet);

  return 0;

  }

  暂时用不到的部份我们先不与分析。 在这里注册了一个input handler。结合前面我们分析的input子系统,在handler里会处理input device上报的事件。跟进这个handler看一下:

  kbd_handler定义如下:

  static struct input_handler kbd_handler = {

  .event = kbd_event,

  .connect = kbd_connect,

  .disconnect = kbd_disconnect,

  .start = kbd_start,

  .name = “kbd“,

  .id_table = kbd_ids,

  };

  Id_table是用来匹配input device的。跟进去看一下,看哪些device的事件,才会交给它处理:

  static const struct input_device_id kbd_ids[] = {

  {

  .flags = INPUT_DEVICE_ID_MATCH_EvbIT,

  .evbit = { BIT_MASK(EV_KEY) },

  },

  {

  .flags = INPUT_DEVICE_ID_MATCH_EVBIT,

  .evbit = { BIT_MASK(EV_SND) },

  },

  { }, /* Terminating entry */

  };

  从这个id_table中看来,只要是能支持EV_KEY或者是EV_SND的设备都会被这个hnadler匹配到。相应的。也就能够处理input device上报的事件了.

  根据之前的input子系统分析,在input device和handler 进行匹配的时候会调用handler->connect.即kbd_connect().代码如下:

  static int kbd_connect(struct input_handler *handler, struct input_dev *dev,

  const struct input_device_id *id)

  {

  struct input_handle *handle;

  int error;

  int i;

  for (i = KEY_RESERVED; i < BTN_MISC; i++)

  if (test_bit(i, dev->keybit))

  break;

  if (i == BTN_MISC && !test_bit(EV_SND, dev->evbit))

  return -ENODEV;

  handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);

  if (!handle)

  return -ENOMEM;

  handle->dev = dev;

  handle->handler = handler;

  handle->name = “kbd“;

  error = input_register_handle(handle);

  if (error)

  goto err_free_handle;

  error = input_open_device(handle);

  if (error)

  goto err_unregister_handle;

  return 0;

  err_unregister_handle:

  input_unregister_handle(handle);

  err_free_handle:

  kfree(handle);

  return error;

  }

  在这段代码里,它申请分初始化了一个hande结构,并将其注册。Open。这些都是我们之前分析过的东东。在注册handle的时候。又会调用到hande->start.函数如下:

  static void kbd_start(struct input_handle *handle)

  {

  unsigned char leds = ledstate;

  tasklet_disable(&keyboard_tasklet);

  if (leds != 0xff) {

  input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));

  input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));

  input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));

  input_inject_event(handle, EV_SYN, SYN_REPORT, 0);

  }

  tasklet_enable(&keyboard_tasklet);

  }

  这里就是对键盘上的LED进行操作。启用了tasklent。这些都不是我们所关心的重点。

  来看下它的事件处理过程:

  static void kbd_event(struct input_handle *handle, unsigned int event_type,

  unsigned int event_code, int value)

  {

  if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))

  kbd_rawcode(value);

  if (event_type == EV_KEY)

  kbd_keycode(event_code, value, HW_RAW(handle->dev));

  tasklet_schedule(&keyboard_tasklet);

  do_poke_blanked_console = 1;

  schedule_console_callback();

  }

  不管对应键盘的那一种模式。后面的数据流程都会转入到input_queue()进等处理。

  实际上。控制终端由vc_cons[ ]数组表示。数组中的每一个项都表示一个控制终端。由全局变量fg_console来指示当前所用的cosole/另外。对于键盘等输出设备也对应一个数组。即kbd_table[ ].用来表示当前终端的控制信息.

  其余的都不是我们想关心的。来跟踪一下这个函数的实现:

  static void put_queue(struct vc_data *vc, int ch)

展开 ↓

最新游戏更多

最新软件更多

  • 玩家推荐
  • 游戏攻略

峰溢下载站 Copyright(C) 2008- ytdonghua.net All Rights Reserved!

闽ICP备2023006282号-2| 免责声明