P/S2键盘驱动分析
初始化
在本驱动中,我们只需要关注有差异的地方,拿出来讲一下就行了。
static iostatus_t keyboard_enter(driver_object_t *driver)
{
iostatus_t status;
device_object_t *devobj;
device_extension_t *devext;
/* 初始化一些其它内容 */
status = io_create_device(driver, sizeof(device_extension_t), DEV_NAME, DEVICE_TYPE_KEYBOARD, &devobj);
if (status != IO_SUCCESS) {
keprint(PRINT_ERR "keyboard_enter: create device failed!\n");
return status;
}
/* neither io mode */
devobj->flags = 0;
devext = (device_extension_t *)devobj->device_extension;
devext->device_object = devobj;
devext->irq = IRQ1;
devext->flags = 0;
devext->opened = 0;
/* 初始化私有数据 */
devext->code_with_e0 = 0;
devext->shift_left = devext->shift_right = 0;
devext->alt_left = devext->alt_right = 0;
devext->ctl_left = devext->ctl_right = 0;
devext->caps_lock = 0;
devext->num_lock = 1;
devext->scroll_lock = 0;
unsigned char *buf = mem_alloc(DEV_FIFO_BUF_LEN);
if (buf == NULL) {
status = IO_FAILED;
keprint(PRINT_DEBUG "keyboard_enter: alloc buf failed!\n");
return status;
}
fifo_io_init(&devext->fifoio, buf, DEV_FIFO_BUF_LEN);
input_even_init(&devext->evbuf);
/* 初始化键盘控制器 */
/* 发送写配置命令 */
WAIT_KBC_WRITE();
out8(KBC_CMD, KBC_CMD_WRITE_CONFIG);
/* 往数据端口写入配置值 */
WAIT_KBC_WRITE();
out8(KBC_WRITE_DATA, KBC_CONFIG);
/* 注册时钟中断并打开中断,因为设定硬件过程中可能产生中断,所以要提前打开 */
irq_register(devext->irq, keyboard_handler, IRQF_DISABLED, "IRQ1", DRV_NAME, (void *) devext);
/* 启动一个内核线程来处理数据 */
task_create("kbd", TASK_PRIO_LEVEL_REALTIME, kbd_thread, devext);
return status;
}
键盘驱动进入的时候就进行了初始化,在io_create_device
创建设备的时候指定了扩展结构体大小,那么就会为这个扩展结构分配内存,然后记录到devobj->device_extension
中。在后面就可以直接使用这个地址了。于是devext = (device_extension_t *)devobj->device_extension;
这行就把devext变量执行了扩展区域。
再来看一下他的扩展结构:
typedef struct _device_extension {
device_object_t *device_object; /* 设备对象 */
char irq; /* irq号 */
int code_with_e0; /* 携带E0的值 */
int shift_left; /* l shift state */
int shift_right; /* r shift state */
int alt_left; /* l alt state */
int alt_right; /* r left state */
int ctl_left; /* l ctrl state */
int ctl_right; /* l ctrl state */
int caps_lock; /* Caps Lock */
int num_lock; /* Num Lock */
int scroll_lock; /* Scroll Lock */
int column; /* 数据位于哪一列 */
fifo_io_t fifoio;
input_even_buf_t evbuf; /* 事件缓冲区 */
uint32_t flags;
uint32_t opened;
} device_extension_t;
每一个设备都可以有一个扩展结构来存放设备自己的数据,尽量保持每个驱动的扩展结构都一致为device_extension_t
,当然你也可以使用其它结构,这是自由的,因为是属于这个驱动自己的内容。在这个驱动中,devobj->flags = 0;
意味着执行读写操作时使用ioreq->user_buffer
。
读操作
iostatus_t keyboard_read(device_object_t *device, io_request_t *ioreq)
{
device_extension_t *ext = device->device_extension;
iostatus_t status = IO_SUCCESS;
/* 直接返回读取的数据 */
ioreq->io_status.infomation = ioreq->parame.read.length;
input_event_t *even = (input_event_t *) ioreq->user_buffer;
/* 参数正确 */
if (even && ioreq->parame.read.length == sizeof(input_event_t)) {
if (ext->flags & DEV_NOWAIT) {
// 解析
if (input_even_get(&ext->evbuf, even) < 0) {
status = IO_FAILED;
} else {
#ifdef DEBUG_DRV
keprint(PRINT_DEBUG "key even get: type=%d code=%x value=%d\n", even->type, even->code, even->value);
keprint(PRINT_DEBUG "key even buf: head=%d tail=%d\n", ext->evbuf.head, ext->evbuf.tail);
#endif
}
} else {
while (1) {
// 解析
if (!input_even_get(&ext->evbuf, even))
break;
task_yield();
}
}
} else {
status = IO_FAILED;
}
#ifdef DEBUG_DRV
keprint(PRINT_DEBUG "key even get: type=%d code=%x value=%d\n", even->type, even->code, even->value);
keprint(PRINT_DEBUG "key even buf: head=%d tail=%d\n", ext->evbuf.head, ext->evbuf.tail);
#endif
ioreq->io_status.status = status;
/* 调用完成请求 */
io_complete_request(ioreq);
return status;
}
我们可以通过ioreq->parame.read.length
来获取要读取数据的长度。因为device flags是0,所以通过ioreq->user_buffer
来访问用户缓冲区。读取完成后,调用io_complete_request(ioreq);
来完成读取。
设备控制
iostatus_t keyboard_devctl(device_object_t *device, io_request_t *ioreq)
{
device_extension_t *extension = device->device_extension;
unsigned int ctlcode = ioreq->parame.devctl.code;
unsigned int leds;
iostatus_t status;
switch (ctlcode) {
case EVENIO_GETLED:
leds = extension->num_lock |
(extension->caps_lock << 1) |
(extension->scroll_lock << 2);
*(unsigned int *) ioreq->parame.devctl.arg = leds;
break;
case EVENIO_SETFLG:
extension->flags = *(unsigned int *) ioreq->parame.devctl.arg;
break;
case EVENIO_GETFLG:
*(unsigned int *) ioreq->parame.devctl.arg = extension->flags;
break;
default:
status = IO_FAILED;
break;
}
ioreq->io_status.status = status;
ioreq->io_status.infomation = 0;
io_complete_request(ioreq);
return status;
}
设备控制是对设备进行一个IO控制,可以从设备获取或者写入配置。在这里,我们通过ioreq->parame.devctl.code
获取控制码,在用switch根据不同的控制码执行不同的操作。除此之外,还可以传一个参数地址ioreq->parame.devctl.arg
,然后可以从参数地址上读取或者写入数据。
中断处理
static int keyboard_handler(irqno_t irq, void *data)
{
device_extension_t *ext = (device_extension_t *) data;
...xxx...
return 0;
}
irq_register(devext->irq, keyboard_handler, IRQF_DISABLED, "IRQ1", DRV_NAME, (void *) devext);
在这个键盘驱动例子中,flags
是IRQF_DISABLED
,表示在键盘中断函数处理期间需要关闭中断,data
是devext
,那么就可以在keyboard_handler
中通过data
传入,从而可以解析出对应的接口体,来满足我们的使用。