精华内容
下载资源
问答
  • Linux usb键盘驱动分析

    2021-05-11 22:48:37
    /*linux初始化模块函数定义*/#include /*USB设备相关函数定义*/2. 定义键盘码表数组:/*使用第一套键盘扫描码表:A-1E;B-30;C-2E…*/static unsigned char usb_kbd_keycode[256] = {0, 0, 0, 0, 30, 48, 46, 32, 18, ...

    /*linux初始化模块函数定义*/

    #include /*USB设备相关函数定义*/

    2. 定义键盘码表数组:

    /*使用第一套键盘扫描码表:A-1E;B-30;C-2E…*/

    static unsigned char usb_kbd_keycode[256] = {

    0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,

    50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,

    4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,

    27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,

    65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,

    105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,

    72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,

    191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,

    115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,

    122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

    29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,

    150,158,159,128,136,177,178,176,142,152,173,140

    };

    3. 编写设备ID表: static struct usb_device_id usb_kbd_id_table [] = {

    { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/

    { } /* Terminating entry */

    };//必须与0结束

    MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/*指定设备ID表*/

    4. 定义USB键盘结构体:

    struct usb_kbd {

    struct input_dev *dev; /*定义一个输入设备*/

    struct usb_device *usbdev;/*定义一个usb设备*/

    unsigned char old[8]; /*按键离开时所用之数据缓冲区*/

    struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/;

    unsigned char newleds;/*目标指定灯状态*/

    char name[128];/*存放厂商名字及产品名字*/

    char phys[64];/*设备之节点*/

    unsigned char *new;/*

    按键按下时所用之数据缓冲区*/

    struct usb_ctrlrequest *cr;/*控制请求结构*/

    unsigned char *leds;/*当前指示灯状态*/

    dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/

    dma_addr_t new_dma; /*中断urb会使用该DMA缓冲区*/

    dma_addr_t leds_dma; /*指示灯DAM缓冲地址*/

    };

    5. 编写USB键盘驱动结构(任何一个LINUX下的驱动都有个类似的驱动结构):

    /*USB键盘驱动结构体*/

    static struct usb_driver usb_kbd_driver = {

    .name = "usbkbd",/*驱动名字*/

    .probe = usb_kbd_probe,/*驱动探测函数,加载时用到*/

    .disconnect = usb_kbd_disconnect,/*驱动断开函数,在卸载时用到,可选*/

    .id_table = usb_kbd_id_table,/*驱动设备ID表,用来指定设备或接口*/

    };

    6. 编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用):

    /*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/

    static int __init usb_kbd_init(void)

    {

    int result = usb_register(&usb_kbd_driver);/*注册USB键盘驱动*/

    if (result == 0) /*注册失败*/

    info(DRIVER_VERSION ":" DRIVER_DESC);

    return result;

    }

    7. 编写模块卸载函数(每个驱动都会有一个卸载函数,由module_exit调用):

    /* 驱动程序生命周期的结束点,向 USB core 注销这个键盘驱动程序。 */

    static void __exit usb_kbd_exit(void)

    {

    printk("SUNWILL-USBKBD:usb_kbd_exit begin...\n");

    usb_deregister(&usb_kbd_driver);/*注销USB键盘驱动*/

    }

    8. 指定模块初始化函数(被指定的函数在insmod驱动时调用):

    module_init(usb_kbd_init);

    9. 指定模块退出函数(被指定的函数在rmmod驱动时调用):

    module_exit(usb_kbd_exit);

    10. 编写中断请求处理函数:

    在介绍中断处理程序之前我们先来看看usb请求结结构体urb是什么样子的

    struct urb

    {

    spinlock_t lock; // lock for URB,自旋锁,用于并发访问临界资源

    void *hcpriv;// private data for host controler与主机控制器相关数据,对USB内核层是透明的

    struct list_head urb_list;//list pointer to all active urbs

    双向指针,用于将此USB联结到处于活动的URB双向链表中,在内核结构模型中很多地方都有到这个结构体,这个结构体有两个指针,一个指向下一个

    list_head结构体,一个指向前一个list_head结构体,在进程列表,工作队列中都用到这个结构体。

    struct urb *next;// pointer to associated USB device 接受此URB的USB设备指针;

    unsigned int pipe;//pipe information 表示设备的某个端点和客户设备驱动程序之间的通道

    int status;// return status 返回状态

    unsigned int transfer_flags;//USB_DISABLE_SPD 拒绝短数据包,即比最大传输包长度小的数据包

    这个变量可以被设置为许多不同的位值,取决与usb驱动程序对urb的具体操作,可以用的值包括

    URB_SHORT_NOT_OK 该值说明任何可能发生的对于IN端点的间断的读取应该被usb核心当作是一个错误处理,该值只对用usb设备

    读取的urb起作用。,对于写入urb没有意义

    URB_ISO_ASAP 如果urb是等时的,当驱动程序想要该urb被调度时可以设置这个位,只要带宽允许它这么做,而且想要在此时设置urb

    中的start_frame变量,如果一个等时的urb没有设置该位驱动程序必须制定start_frame的值,如果在传输的当时不能启动的话必须能够

    正确的恢复。

    URB_NO_TRANSFER_DMA_MAP 顾名思意这个时设置DMA缓冲区的,当urb使用DMA缓冲区的时候设置该位。usb核心使用

    transfer_dma的变量所指向的缓冲区

    URB_NO_SETUP_DMA_MAP 和URB_NO_TRANDFER_DMA_MAP位类似,该位用于控制带有已经设置好的DMA缓冲区的urb,如果它被设置了,USB

    核心使用setup_dma变量所指向的缓冲区,而不是setup_packet变量

    URB_ASYNC_UNLINK 如果该位被设置,对urb的usb_unlink_urb的调用几乎是立即返回,该urb的链接在后台被解开,否则此函数一直等

    待urb被完全解开链接和结束才返回,使用该位时要小心,因为它可能会造成伴以调试的同步问题

    URB_NO_FSBR 仅由UHCI USB主控制器驱动程序使用,指示它不要企图使用前端的总线回收逻辑。该位通常不应该被设置,因为带有

    UBCI主控制器的及其会导致大量的CPU负荷,而PCI总线忙于等待一个设置了该位的URB

    URB_ZERO_PACKET 如果该位被设置,一个批量输出urb以发送一个不包含的小数据包来结束,这是数据对齐到一个端点数据包边

    界,一些断线的USB涉黑需要该位才能正确的工作。

    URB_NO_INTERRUPT 如果该位被设置,当urb结束的时候,硬件肯那个不会产生一个中断,对该位的使用应当小心谨慎,只有把多个

    urb排队到同一个端点时才使用。usb核心的函数使用该位来进行DMA缓冲区的传输。

    void *transfer_buffer;// associated data buffer 传输数据缓冲区,接收或发送设备的数据,它必须是物理连续的,不可换页的内存块,用kmalloc(,GFP_KERNEL)分配内核内存空间

    int transfer_buffer_length;//data buffer length transfer_buffer或者transfer_dma变量所指向的缓冲区长度,在任何时候一个urb只能用一个

    int actual_length;// actual data buffer length 当urb结束后,该变量被设置为urb所大宋的数据或者urb接收的实际数据长度

    int bandwidth;//bandwidth for this transfer request(INT or ISO),此请求每次占用一帧的带宽,只适合于用实时/中断传输

    unsigned char *setup_packet;//setup packet(control only)用于指向传输中控制命令的指针.只适用控制传输.

    int start_frame;// start frame(iso/irq

    only),此请求开始传输的帧号.中断传输的时候,表示返回启动此请求的第一次中断传输的帧号.实时传输的时候,指明处理第一个实时请求数据包的帧号,

    如果设置了USB_ISO_ASAP,此变量表示返回启动第一次实时传输的帧号

    int number_of_packets;//number of packets in this request (iso)此请求包含的数据包数,只适合实时传输,在urb被发送到usb核心之前必须被USB驱动程序设置

    int interval;// polling interval(irq only)urb被轮询的时间间隔,仅对中断或者等时传输有效,在urb被发送到usb核心之前必须由usb驱动程序设置;

    int error_count;//number of errors in this transfer(iso only)由usb核心设置,仅用于等时urb结束后,他表示报告了任何一种类型的错误的等时传输的数量。

    int timeout;

    void *context;// context for completion routine指向一个可以被usb驱动程序设置的数据块,它可以在结束处理程序历程中当urb被返回到驱动程序的时候使用。

    usb_complete_t complete;//pointer to completion routine,指向回调函数的指针,当数据完成的时候或者出现出错的时候usb核心调用此函

    数,在该函数内,usb驱动程序可以检查urb,释放它,或者把它重新提交到两个一个传输中去

    usb_complete_t 定义如下

    typedef voud (*usb_complete_t)(struct urb * , struct pt_regs *);

    iso_packet_descriptor_t iso_frame_desc[0];//要进行实时传输的数据结构,每个结构表示一次数据传输

    int status

    当urb结束之后,或者正在被usb核心处理时,该变量被设置为urb的当前状态,usb驱动程序可以安全第访问该变量的唯一适

    合就是在urb结束处理的历程中。该限制是为了防止当urb正在被usb核心处理时的竞态的发生。对于等是urb,该变量的一个成功之0表示urb室友已

    经被解开链接,如果在获取等时urb的详细状态。应该检查iso_frame_desc变量,该变量的有效值包含值:

    0 urb传输成功

    -ENOENT urb被usb_kill_urb调用终止

    -ECONNRESET urb被usb_unlink_urn调用解开连接,urb的transfer_flags变量被设置为URB_ASYNC_URBLINK;

    -EINPROGRESS urb仍然咋被usb主控制器处理,如果驱动程序中检查到该值,说明存在代码缺陷

    -EPROTO urb发生了以下错误

    在传输中发生了bitstuff错误

    硬件没有及时接收到响应数据包

    -EILSEQ urb在传输发生了CRC校验不匹配

    -EPIPE 端点被中止,如果涉及的端点不是控制端点,可以调用usb_clear_halt函数来清楚该错误

    -ECOMM 传输时数据的接收速度比它写到系统内存的速度快,该错误值仅发生在IN urb上

    -ENOSR 传输时从系统内存获取的数据速度不够快,跟不上所要求的USB数据速率,该错误仅发生在OUT utb上

    -EOVERFLOW urb发生“串扰(babble)”错误。“串扰”错误发生在端点接收了超过端点指定的数据包尺寸的数据时

    -EREMOTEIO 仅发生在urb的transfer_flags变量设置为URB_SHORT_NOT_OK标识时。表示urb没有接收到所要求的全部数据量

    -ENODEV USB设备已经从系统移出

    -EXDEV 仅发生在等时urb上,表示传输仅部分完成,为了确定所传输的内容,驱动程序必须单看单个帧的状态

    -EINVAL urb发生了很糟糕的事情,URB内核文档描述的该值,如果发生这种情况,那就退出系统,然后回家去

    -ESHUTDOWN usb主控制器驱动程序发生了严重的错误,设备已经被禁止,或者已经从系统脱离,而urb在设备被移出之后提交,如果当urb被提交到设备的配置被改变,也可能发生这个错误。

    一般来说,错误值-EPROTO ,-EILSSEQ 和 -EOVERFLOW表示设备,设备的固件或者吧设备连接到了计算机的电缆发生了硬件故障。

    /*中断请求处理函数,有中断请求到达时调用该函数*/

    static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)

    {

    struct usb_kbd *kbd = urb->context;

    int i;

    switch (urb->status) {

    case 0: /* success */

    break;

    case -ECONNRESET: /* unlink */

    case -ENOENT:

    case -ESHUTDOWN:

    return;

    /* -EPIPE: should clear the halt */

    default: /* error */

    goto resubmit;

    }

    //input_regs(kbd->dev, regs);

    /*

    不知道其用意, 注释掉该部分仍可正常工作*/

    for (i = 0; i < 8; i++)/*8次的值依次是:29-42-56-125-97-54-100-126*/

    {

    input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);

    }

    /*若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下*/

    for (i = 2; i < 8; i++) {

    /*获取键盘离开的中断*/

    if (kbd->old > 3 && memscan(kbd->new + 2, kbd->old, 6) == kbd->new + 8) {/*同时没有该KEY的按下状态*/

    if (usb_kbd_keycode[kbd->old])

    {

    input_report_key(kbd->dev, usb_kbd_keycode[kbd->old], 0);

    }

    else

    info("Unknown key (scancode %#x) released.", kbd->old);

    }

    /*

    获取键盘按下的中断*/

    if (kbd->new > 3 && memscan(kbd->old + 2, kbd->new, 6) == kbd->old + 8) {/*同时没有该KEY的离开状态*/

    if (usb_kbd_keycode[kbd->new])

    {

    input_report_key(kbd->dev, usb_kbd_keycode[kbd->new], 1);

    }

    else

    info("Unknown key (scancode %#x) pressed.", kbd->new);

    }

    }

    /*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/

    input_sync(kbd->dev);

    memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/

    resubmit:

    i = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/

    if (i)

    err ("can't resubmit intr, %s-%s/input0, status %d",

    kbd->usbdev->bus->bus_name,

    kbd->usbdev->devpath, i);

    }

    11. 编写事件处理函数:

    /*事件处理函数*/

    static int usb_kbd_event(struct input_dev *dev, unsigned int type,

    unsigned int code, int value)

    {

    struct usb_kbd *kbd = dev->private;

    if (type != EV_LED) /*不支持LED事件 */

    return -1;

    /*获取指示灯的目标状态*/

    kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |

    (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |

    (!!test_bit(LED_NUML, dev->led));

    if (kbd->led->status == -EINPROGRESS)

    return 0;

    /*

    指示灯状态已经是目标状态则不需要再做任何操作*/

    if (*(kbd->leds) == kbd->newleds)

    return 0;

    *(kbd->leds) = kbd->newleds;

    kbd->led->dev = kbd->usbdev;

    /*发送usb请求块*/

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

    err("usb_submit_urb(leds) failed");

    return 0;

    }

    12. 编写LED事件处理函数:

    /*接在event之后操作,该功能其实usb_kbd_event中已经有了,该函数的作用可能是防止event的操作失败,一般注释掉该函数中的所有行都可以正常工作*/

    static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)

    {

    struct usb_kbd *kbd = urb->context;

    if (urb->status)

    warn("led urb status %d received", urb->status);

    if (*(kbd->leds) == kbd->newleds)/*

    指示灯状态已经是目标状态则不需要再做任何操作*/

    return;

    *(kbd->leds) = kbd->newleds;

    kbd->led->dev = kbd->usbdev;

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

    err("usb_submit_urb(leds) failed");

    }

    13. 编写USB设备打开函数:

    /*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */

    static int usb_kbd_open(struct input_dev *dev)

    {

    struct usb_kbd *kbd = dev->private;

    kbd->irq->dev = kbd->usbdev;

    if (usb_submit_urb(kbd->irq, GFP_KERNEL))

    return -EIO;

    return 0;

    }

    14. 编写USB设备关闭函数

    /*关闭键盘设备时,结束 urb 生命周期。 */

    static void usb_kbd_close(struct input_dev *dev)

    {

    struct usb_kbd *kbd = dev->private;

    usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/

    }

    15. 创建URB

    /*分配URB内存空间即创建URB*/

    static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)

    {

    if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))

    return -1;

    if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))

    return -1;

    if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))

    return -1;

    if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))

    return -1;

    if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))

    return -1;

    return 0;

    }

    16. 销毁URB

    /*释放URB内存空间即销毁URB*/

    static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)

    {

    if (kbd->irq)

    usb_free_urb(kbd->irq);

    if (kbd->led)

    usb_free_urb(kbd->led);

    if (kbd->new)

    usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);

    if (kbd->cr)

    usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);

    if (kbd->leds)

    usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);

    }

    17. USB键盘驱动探测函数:

    /*USB键盘驱动探测函数,初始化设备并指定一些处理函数的地址*/

    static int usb_kbd_probe(struct usb_interface *iface,

    const struct usb_device_id *id)

    {

    struct usb_device *dev = interface_to_usbdev(iface);

    struct usb_host_interface *interface;

    struct usb_endpoint_descriptor *endpoint;

    struct usb_kbd *kbd;

    struct input_dev *input_dev;

    int i, pipe, maxp;

    /*当前选择的interface*/

    interface = iface->cur_altsetting;

    /*键盘只有一个中断IN端点*/

    if (interface->desc.bNumEndpoints != 1)

    return -ENODEV;

    /*获取端点描述符*/

    endpoint = &interface->endpoint[0].desc;

    if (!(endpoint->bEndpointAddress & USB_DIR_IN))

    return -ENODEV;

    if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)

    return -ENODEV;

    /*将endpoint设置为中断IN端点*/

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    /*获取包的最大值*/

    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);

    input_dev = input_allocate_device();

    if (!kbd || !input_dev)

    goto fail1;

    if (usb_kbd_alloc_mem(dev, kbd))

    goto fail2;

    /* 填充 usb 设备结构体和输入设备结构体 */

    kbd->usbdev = dev;

    kbd->dev = input_dev;

    /*

    以"厂商名字 产品名字"的格式将其写入kbd->name*/

    if (dev->manufacturer)

    strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));

    if (dev->product) {

    if (dev->manufacturer)

    strlcat(kbd->name, " ", sizeof(kbd->name));

    strlcat(kbd->name, dev->product, sizeof(kbd->name));

    }

    /*

    检测不到厂商名字*/

    if (!strlen(kbd->name))

    snprintf(kbd->name, sizeof(kbd->name),

    "USB HIDBP Keyboard %04x:%04x",

    le16_to_cpu(dev->descriptor.idVendor),

    le16_to_cpu(dev->descriptor.idProduct));

    /*设备链接地址*/

    usb_make_path(dev, kbd->phys, sizeof(kbd->phys));

    strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));

    input_dev->name = kbd->name;

    input_dev->phys = kbd->phys;

    /*

    * input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符

    * 中的编号赋给内嵌的输入子系统结构体

    */

    usb_to_input_id(dev, &input_dev->id);

    /* cdev 是设备所属类别(class device) */

    input_dev->cdev.dev = &iface->dev;

    /* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将键盘结构体对象赋给它 */

    input_dev->private = kbd;

    input_dev->evbit[0] = BIT(EV_KEY)/*键码事件*/ | BIT(EV_LED)/*LED事件*/ | BIT(EV_REP)/*自动重覆数值*/;

    input_dev->ledbit[0] = BIT(LED_NUML)/*数字灯*/ |

    BIT(LED_CAPSL)/*大小写灯*/ | BIT(LED_SCROLLL)/*滚动灯*/ | BIT(LED_COMPOSE) |

    BIT(LED_KANA);

    for (i = 0; i < 255; i++)

    set_bit(usb_kbd_keycode, input_dev->keybit);

    clear_bit(0, input_dev->keybit);

    input_dev->event = usb_kbd_event;/*

    注册事件处理函数入口*/

    input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/

    input_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/

    /*

    初始化中断URB*/

    usb_fill_int_urb(kbd->irq/*初始化kbd->irq这个urb*/, dev/*这个urb要发送到dev这个设备*/, pipe/*这个urb要发送到pipe这个端点*/,

    kbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,

    usb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);

    kbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/

    kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/

    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/

    kbd->cr->bRequest = 0x09; /*中断请求编号*/

    kbd->cr->wValue = cpu_to_le16(0x200);

    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/

    kbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/

    /*

    初始化控制URB*/

    usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),

    (void *) kbd->cr, kbd->leds, 1,

    usb_kbd_led, kbd);

    kbd->led->setup_dma = kbd->cr_dma;

    kbd->led->transfer_dma = kbd->leds_dma;

    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP |

    URB_NO_SETUP_DMA_MAP/*如果使用DMA传输则urb中setup_dma指针所指向的缓冲区是DMA缓冲区而不是

    setup_packet所指向的缓冲区*/);

    /*

    注册输入设备*/

    input_register_device(kbd->dev);

    usb_set_intfdata(iface, kbd);/*设置接口私有数据*/

    return 0;

    fail2: usb_kbd_free_mem(dev, kbd);

    fail1: input_free_device(input_dev);

    kfree(kbd);

    return -ENOMEM;

    }

    18. 编写断开连接的函数:

    /*断开连接(如键盘设备拔出)的处理函数*/

    static void usb_kbd_disconnect(struct usb_interface *intf)

    {

    struct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/

    usb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/

    if (kbd) {

    usb_kill_urb(kbd->irq);/*取消中断请求*/

    input_unregister_device(kbd->dev);/*注销设备*/

    usb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/

    kfree(kbd);

    }

    }

    19. 编写Makefile:

    ##############################

    #usbkdb Makefile for linux

    ##############################

    obj-m:=usbkbd.o

    KERNELDIR ?= /lib/modules/$(shell uname -r)/build

    PWD:=$(shell pwd)

    default:

    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

    展开全文
  • kernel_init():此函数主要完成设备驱动程序的初始化,并且调用 init_post() 启动用户空间的init进程。 init_post():初始化的尾声,第一个用户空间的init 横空出世!其PID始终为1。 init: 内核会在过去曾使用过init...

    我们运行程序只需要点击应用程序的图标就可以了,但在这之前,我们必须启动我们的系统。在一切之前,我们必须有某些程序去引导我们系统的内核,这些程序就是内核引导程序了,例如LILO、GRUB、U-Boot、RedBoot。而这些引导程序同样需要被其他程序加载和运行,这样说下去,茫茫人生何处才是尽头啊?想必大家可以想到的----硬件!这么长的过程复杂、崎岖!正所谓万事开头难,但不怕,我们来一起走过去吧!

    X86的引导过程如图:

    e1cc540ee3a0a376c930b6bfee183178.png

    cpu自身的初始化:这是引导的第一步,如果在多处理器系统上,那么每个cpu都要自身初始化。cpu初始化后,cpu从某个固定的位置(应该是0Xfffffff0)取指,这条指令是跳转指令,目的地是BIOS的首部代码,但是cpu并不在乎BIOS是否存在,它仅仅只是执行这个地址的指令而已!

    BIOS:BIOS是只读存储器(ROM),被固化于主板上。其工作主要有两个,就是上图的加电自检即是POST(post on self  test)与加载内核引导程序。

    那么他们是具体完成什么工作的呢?

    1) 加电自检:完成系统的硬件检测,其中包括内存检测、系统总线检测等工作。

    2) 加载内核引导程序:在POST完成后,就要加载内核引导程序了,那它保存在哪里呢?磁盘里!哈哈,BIOS会读取0磁头,0磁道,一扇区的512个字节,这个扇区有叫做MBR(主引导记录),MBR中保存了内核引导程序的开始部分,BIOS将其装入内存执行。512个字节的MBR有些什么呢?这里有必要说说MBR!MBR分区表以80为起始,以55AA为结束,共64个字节。具体的MBR知识自己百度!

    MBR:1) 446个字节的引导程序代码

    2) 64个字节的分区表,有多少个分区呢。。?这还真不知道!分为4个分区表,一个可启动分区和三个不可启动分区。

    3) 2个字节的0XAA55,用于检查MBR是否有效。

    需要注意的是,内核引导程序被加载完后,POST部分的代码会被从内存中清理,只留部分在内存中留给目标操作系统使用。

    内核引导程序:内核引导程序分两部分:主、次引导程序。主引导程序的主要工作就是收索,寻找活动的分区,将活动的分区引导记录中的次引导程序加载到内存中并且执行。而这个次引导程序就是负责加载内核的并且将控制权交给内核。上面提过内核引导程序有LILO、GRUB、U-Boot、RedBoot。其中前面两个为pc中的,而后面两个是嵌入式的。

    内核:内核以压缩的形式存在,不是一个可执行的内核!所以内核阶段首先要做的是自解压内核映像。这里说说编译内核后形成的内核压缩的映像vmlinuz。编译生成vmlinux后,一般会对其进行压缩为vmlinuz,使其成为zImage--小于512KB的小内核,或者成为bzImage--大于512KB的大内核。

    vmlinuz结构如图:

    d22eaf525499c091c11d4150a326e3bc.png

    做了这么多工作终于把linux的内核给引导出来了。!!下面我们来初始化这个千呼万唤始出来的linux内核!

    内核初始化:内核会调用一系列的初始化函数去对所有的内核组件进行初始化,由start_kernel()---.....---> rest_init() ----..----> kernel_init() ----....--> init_post() ------到---> 第一个用户init进程结束。

    start_kernel():其完成大部分内核初始化的工作。相关的代码去查阅内核的源代码吧!www.kernel.org

    rest_init():start_kernel() 调用rest_init() 进行后面的初始化工作。

    kernel_init():此函数主要完成设备驱动程序的初始化,并且调用 init_post() 启动用户空间的init进程。

    init_post():初始化的尾声,第一个用户空间的init 横空出世!其PID始终为1。

    init: 内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。找到/sbin/init 后init会根据/etc/inittab (网上的资料都这样说的,我在Ubuntu3.8的内核里找不到,但在Fedora中可以找到!是发行版本不同吧?(求指教)这里附上图片!)文件完成其他一些工作,例如:getty进程接受用户的登录,设置网络等。这里详细说说吧。

    fedora19的etc有inittab文件:

    58c1c2aa67b0c91ea600170a7675f398.png

    系统中所有的进程形成树型结构,而这棵树的根就是在内核态形成的,系统自动构造的0号进程,它是所有的进程的祖先。大致是在vmlinux的入口 startup_32(head.S)中为pid号为0的原始进程设置了执行环境,然后原是进程开始执行start_kernel()完成Linux内核的初始化工作。包括初始化页表,初始化中断向量表,初始化系统时间等。继而调用 fork(),创建第内核init进程:

    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  //    参数CLONE_FS  | CLONE_SIGHAND表示0号线程和1号线程分别共享文件系统(CLONE_FS)、打开的文件(CLONE_FILES)和信号处理程序 (CLONE_SIGHAND)。

    这个进程就是著名的pid为1的init进程(内核态的),它会继续完成剩下的初始化工作比且创建若干个用于高速缓存和虚拟主存管理的内核线程,如kswapd和bdflush等,然后execve(/sbin/init)(生成用户态的init进程pid=1,因为没有调用fork(),所以pid还是1!), 成为系统中的其他所有进程的祖先。回过头来看pid=0的进程,在创建了init进程后(内核态的),pid=0的进程调用 cpu_idle()在主cpu中演变成了idle进程。而内核态pid=1的init进程同样会在各个从cpu上生成idel进程。 init在演变成/sbin/init之前,会执行一部分初始化工作,其中一个就是smp_prepare_cpus(),初始化SMP处理器,在这过程中会在处理每个从处理器时调用

    task =copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL, NULL, 0);  init_idle(task, cpu);

    即从init中复制出一个进程,并把它初始化为idle进程(pid仍然为0)。从处理器上的idle进程会进行一Activate工作,然后执行cpu_idle()。

    执行/sbin/init,这样从内核太过度到用户态,按照配置文件/etc/inittab 要求完成启动的工作,并且创建若干个不编号为1,2,3...号的终端注册进程getty,其作用就是设置其进程组的标识号,监视配置到系统终端的接口电路,当有信号来到的时候,getty会执行execve()生成注册进程login,用户可以注册登录了,如果登录成功,则login会演化为shell进程,若login不成功则关闭打开的终端线路,用户1号进程会创建新的getty。到这里init的流程基本完成了。奉上大图一张!

    init的过程:

    e1a2f20eb865e7f09ec41e2e2112c98e.png

    init 的流程讲完了,这里粗略说说init还做了什么事。先来认识下运行级别。

    运行级别:    linux可以在不同的场合启动不同的开机启动程序,这就叫做运行级别。根据不同的运行级别启动不同的程序。例如在用作服务器的时候要开启Apache,而桌面就不需要。

    linux预先设置了7种运行级别(0--6)。ubuntu有8种(0--6、S),这里主要以ubuntu来说。0:关闭系统,1:系统进入单用户模式,S:单用户恢复模式,文本登录界面,只运行少数的系统服务。2:多用户模式(系统默认的级别),图形登录界面,运行所有预定的系统服务。3--5:多用户模式,图形登录界面,运行所有预定的系统服务(对于系统定制而言,运行级别2-5的作用等同),6:重启系统。

    对于每个运行级别,在/etc/都有对应的子录目---/etc/rcN.d 用来指定要加载的程序。

    f5ca0de7efcab7be002b27daa1577e6a.png

    细心看的话可以发现,除README外其他的文件都是“S开头+两位数字+程序名”的形式。这代表什么呢?S代表Start启动。如果是K的话则代表关闭kill,如果从其他的运行级别切换过来的话则要关闭程序。之后的数字为处理的顺序,越小则越早执行,如果数字相同,按字母的顺序启动。

    上图可以看出这里的文件都是链接文件,为什么呢?上面说过各种运行级别有各自的一个录目用来存放各自的开机程序,如果有多个运行级别要启动同一个程序,那么这个程序的脚本会被拷贝到每一个录目里,这样做的话,如果要修改启动脚本就要修改每一个录目,这样不科学啊!!!!所以这些文件都是链接文件指向/etc/init.d。启动时就是运行这些脚本的。

    子系统的初始化:

    内核选项:linux 允许用户传递内核配置选项给内核,内核在初始化的过程中调用parse_args()函数对这些选项进行解析,之后调用相应的函数进行处理。对于parse_args()函数,其能够解析形如“变量名=值”的字符串,在模块加载的时候也会被调用来解析模块的参数。

    子系统的初始化:在完成内核选项的解析后,就进入初始化的函数调用。在kernel_init()函数中调用do_basic_setup()函数再去调用do_initcalls()函数来完成。各个函数具体实现请查阅源代码!

    登录:登录有三种方式。

    1) 命令行登录

    init创建getty,等用户输入用户名和密码,输入完成后调用login程序进行核对密码,如果正确就读取/etc/passwd文件,读取这个用户的指定的shell并启动它。

    2) ssh登录

    系统调用sshd程序,取代getty和login,之后启动shell。

    3) 图形界面登录

    init进程调用显示管理器,Gnome图形界面对应为gdm,然后输入用户名、密码,如果密码正确就启动用户会话。

    到这里系统就启动起来了!说了这么多,现在我们来玩点好玩的!!!!!!简单熟悉一下linux的启动!!!!!!!!!!!首先要准备一个ubuntu系统最好13.04或者12.04都得,可以是虚拟机(最好是在虚拟机上操作!因为我因为这个小实验而重装了一遍真机系统!当时不懂啊!惨。。。。。。。。。)。

    1:进入系统,在主文件夹(方便)新建一个c语言文件并且命名为init.c,输入---->最简单的c语言程序helloworld,不这是最伟大的c语言程序!

    main(){

    printf("helloworld!\n");

    }

    之后不用说就是编译啦。打开终端(ctrl + alt + t)执行这条指令:gcc --static -o init init.c    这样init文件就准备好了!猜到我想做什���了吗。。?哈哈我们继续!!

    2:将上文提到过的/sbin/init 文件备份执行这条指令:sudo cp /sbin/init /sbin/init.bak  备份成init.bak 文件

    将原来的init 文件删除,执行指令:sudo rm /sbin/init

    好了,下一步就将我们的helloworld  init复制到/sbin/录目下!执行指令: sudo mv init /sbin/  这条指令之前要注意你终端当前的路径与init的路径是不是相同,要不不能成功的!!

    3:这样就做好了!!!我们果断重启!这样在启动中我们看到了我们的 helloworld!唉。。它停哪里了!!那是肯定的,上文介绍了init的作用,你换了,不能启动是正常的啦。。思考下,到这里内核处于什么状态。?这里其实内核基本已经初始化完成了!就剩下进程的生成与子系统的初始化了。在init_post()函数的最尾会查找init的路径,如果找不到就崩溃。而且会试图建立一个交互式的shell(/bin/sh)来代替找不到的init,让用户可以修复这种错误、重新启动。

    4:现在我们来恢复我们的系统!刚才在 2 中的步骤不会让你白做的!重新启动系统,并在一开始就按着左Shift (我以前的系统是不用的,不知道以前对grub.cfg做了什么坏事!!),系统会自动检测到这个信号的,这样会出现下面图中的界面。(建议去看看grub2的特性!):

    72448e893bbc329bb3b90707a1cf322f.png

    按下e,进行编辑,按键盘右下角  向下的按键,在linux 这一行的最尾(quiet)前面加入---->  init=/sbin/init.bak  ,按下 ctrl + x 启动系统。如图:

    7fad89be436baa4a22a5bf8ce93320ba.png

    如果一切没有错,那么你肯定能成功地重新启动系统!如果看到登录界面,那么恭喜你,你已经在漫漫人生路上走了一趟了!

    好了,到这里本文要讲的知识已经讲完了。下面我们一起来思考一下我在这其中遇到的没有解决的问题,请大神们指教!

    问题:  首先我的实验是在虚拟机上做的,原本我以为init是跟内核的压缩文件有关,所以用3.8.0-19 内核进去系统将init删了,这样3.8.0-19内核肯定启动不了内核。我用3.8.0-27内核进去系统,同样不能进去。。还是出现伟大的helloworld!这样看 init跟vmlinux等 在boot里的文件无关了!那么init关机后会被保存在哪里!?还有一个最重要的!我重新装了一遍系统(虚拟机的),这是没有3.8.0-27内核的,我将我真机的3.8.0-27的内核文件(/boot 里的vmlinuz文件等)放到虚拟机的/boot/文件夹里,重新启动系统,发现分辨率变了,光标不灵活(可能是分辨率的关系!),屏幕大小感觉是原来的两倍。这是为什么呢?

    ps:本文挺长!如果你有疑问请提出来,交流交流,学习学习!如果你知道上面的问题的原因,那么请大神留几句话共小弟学习学习!

    0b1331709591d260c1c78e86d0c51c18.png

    展开全文
  • 属性里显示‘Windows 无法初始化这个硬件的设备驱动程序。 (代码 37)’ 首先判断是驱动损坏。重新安装了驱动,重启电脑,无效。这个问题还真是不好弄。不会要重装电脑吧?现在终于找到了解决方法分享给大家: 运行...

        有一天开机,发现笔记本的键盘不能用了。开始以为键盘坏了,重启启动,一看能进bios,各键正常。然后再重启,进系统,看设备管理器,发现键盘为黄色惊叹号。属性里显示‘Windows 无法初始化这个硬件的设备驱动程序。 (代码 37)’
    首先判断是驱动损坏。重新安装了驱动,重启电脑,无效。这个问题还真是不好弄。不会要重装电脑吧?
    现在终于找到了解决方法分享给大家:

    运行注册表regedit,
    1.定位到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class \{4D36E96B-E325-11CE-BFC1-08002BE10318}
    2.删除UpperFilters项
    3.卸载键盘设备,“扫描检查硬件改动”。
    4.然后设备管理器里变成:代码 10:该设备无法启动 或者还是原来的惊叹号。
    5.定位到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class \{4D36E96B-E325-11CE-BFC1-08002BE10318}
    6.新建添加字符串UpperFilters项,修改内容为kbdclass
    7.再次卸载键盘设备,重新启动,系统提示发现并成功安装了PS/2键盘驱动,这表明已经OK了.

    展开全文
  • 键盘过滤驱动

    2021-04-06 18:30:51
    在笔者接触驱动到现在以来一以后大半个月的时间,从中让我深深的体会到了万事开头难,以及学习持之以恒的重要性。笔者也是个驱动新人,开始接触驱动的时候看着张帆的《Windows驱动开发技术详解》讲的挺细,对新手来...

    在笔者接触驱动到现在以来一以后大半个月的时间,从中让我深深的体会到了万事开头难,以及学习持之以恒的重要性。笔者也是个驱动新人,开始接触驱动的时候看着张帆的《Windows驱动开发技术详解》讲的挺细,对新手来说是个不错的学习资料,但是更重要的还是自己要多动手练习,笔者在学习到同步操作的相关知识的时候,实在是看天书。最后还是放弃了学习本书。再找了本楚狂人的资料学习,感觉本书对新手来说还是比较吃力的,其中笔者就是这样,很多知识点不是很明白,只能凭借自己的感觉去做,不过造成的后果就是无情的蓝屏^_^。最终要的是笔者坚持下来了。


      今天来分享下学习过程中,编写键盘过滤的心得。关于工作原理因为笔者也是一知半解,就不在阐述。

     


      我们的目的就是将自己的驱动设备挂接/driver/kbdclass驱动下的所有设备,如图所示:

     

     

     

     

     

       然后通过处理来达到过滤我们想要的按键信息。挂接后的驱动中的第一个设备就是我们的过滤设备,当有按键触发,按键信息首先会被我们自己写的设备所拦截,但是这时候拦截到的是没有处理的按键信息,那改怎么处理呢?我们去问键盘驱动,当我们拦截到按键IRP的时候先不做处理,给IRP设置完成回调函数并传递给键盘驱动的设备。这样一来,当按键IRP被键盘驱动处理完毕之后就会执行我们的回调函数,这时我们在处理按键信息。当卸载我们的过滤设备的时候会有个麻烦就是会有个IRP已经设备了回调例程,并且在等待按键触发。如果这个IRP在没有处理之前就卸载掉我们的过滤驱动,就会引发按键蓝盘。为什么会蓝屏呢?因为这个IRP是已经被设置了回调函数,当IRP被处理完成之后去找我们设置的回调函数,因为我们在IRP没有处理之前已经卸载了,所以这时IRP已经找不到回调函数了,所以导致蓝屏。大部分都的解决方案是在处理IRP的时候放置个计数器,当计数器不为0的时候说明还有IRP未完成,这是卸载的时候就用while来一直等待这个IRP完成,如果我们要是不按键盘的话,它会无休止的等待下去,并且也影响系统性能。
      笔者通过相关资料的查阅,另个解决方案就是做个代理IRP,然后保存原来的IRP,因为我们可以取消自己的IRP。在卸载的时候先卸载我们的代理IRP,然后在发送原来保存的IRP,这样就很好的解决了无限的等待的BUG...但是笔者也没有找到相关代码,只好自己动手试。经过一下午的测试,笔者发现我们只需要做一个代理IRP即可,并不需要保存原来的IRP,卸载的时候直接取消我们的IRP,并不需要重新发送个IRP。下面我们来通过具体代码学习一下键盘过滤驱动。

     

    首先:

     

     

    [c-sharp] view plaincopy

    1. //驱动入口  

    2. extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)  

    3. {  

    4.     NTSTATUS status;  

    5.     DbgPrint("驱动加载开始.../n");  

    6.     pDriverObject->DriverUnload=FilterUnload;  

    7.   

    8.         //设置读取派遣函数  

    9.     pDriverObject->MajorFunction[IRP_MJ_READ]=FilterDispatchRoutin;  

    10.   

    11.     BindDevice(pDriverObject);  

    12.   

    13.   

    14.     DbgPrint("驱动加载结束.../n");  

    15.   

    16.     return STATUS_SUCCESS;  

    17. }  

    [c-sharp] view plain copy

    1. //驱动入口  

    2. extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)  

    3. {  

    4.     NTSTATUS status;  

    5.     DbgPrint("驱动加载开始.../n");  

    6.     pDriverObject->DriverUnload=FilterUnload;  

    7.   

    8.         //设置读取派遣函数  

    9.     pDriverObject->MajorFunction[IRP_MJ_READ]=FilterDispatchRoutin;  

    10.   

    11.     BindDevice(pDriverObject);  

    12.   

    13.   

    14.     DbgPrint("驱动加载结束.../n");  

    15.   

    16.     return STATUS_SUCCESS;  

    17. }  

       

     

     

      在主函数中,调用BindDevice来实现过滤驱动的创建与绑定,代码如下:

     

    [cpp] view plaincopy

    1. //设备类型  

    2. extern "C" POBJECT_TYPE IoDriverObjectType;  

    3.   

    4. NTSTATUS BindDevice(PDRIVER_OBJECT pDriverObject)  

    5. {  

    6.     NTSTATUS status;  

    7.     UNICODE_STRING uniNtNameString;  

    8.   

    9.     //要打开的驱动对象  

    10.     PDRIVER_OBJECT KbdDriverObject = NULL;  

    11.     //驱动对象的设备  

    12.     PDEVICE_OBJECT kbdDeviceOjbect;  

    13.   

    14.     //初始化一个字符串,就是kbdclass驱动的名子  

    15.     RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);  

    16.   

    17.     //根据名字打开驱动对象  

    18.     status=ObReferenceObjectByName(  

    19.         &uniNtNameString,  

    20.         OBJ_CASE_INSENSITIVE,  

    21.         NULL,  

    22.         0,  

    23.         IoDriverObjectType,  

    24.         KernelMode,  

    25.         NULL,  

    26.         (PVOID*)&KbdDriverObject);  

    27.   

    28.     //如果失败了就直接返回  

    29.     if(!NT_SUCCESS(status))  

    30.     {  

    31.         DbgPrint("打开设备失败.../n");  

    32.         return status;  

    33.     }  

    34.   

    35.     //调用ObReferenceObjectByName会导致对驱动对象的引用计数增加  

    36.     //必须响应的调用解引用ObDereferenceObject  

    37.     ObDereferenceObject(pDriverObject);  

    38.     DbgPrint("打开成功,解除引用.../n");  

    39.   

    40.       

    41.     //键盘驱动的第一个设备  

    42.     kbdDeviceOjbect=KbdDriverObject->DeviceObject;  

    43.     while(kbdDeviceOjbect!=NULL)  

    44.     {  

    45.         //创建并绑定过滤设备  

    46.         CreateDevice(pDriverObject,kbdDeviceOjbect);  

    47.         //下一个设备  

    48.         kbdDeviceOjbect=kbdDeviceOjbect->NextDevice;  

    49.     }  

    50.   

    51.     return status;  

    52. }  

    [cpp] view plain copy

    1. //设备类型  

    2. extern "C" POBJECT_TYPE IoDriverObjectType;  

    3.   

    4. NTSTATUS BindDevice(PDRIVER_OBJECT pDriverObject)  

    5. {  

    6.     NTSTATUS status;  

    7.     UNICODE_STRING uniNtNameString;  

    8.   

    9.     //要打开的驱动对象  

    10.     PDRIVER_OBJECT KbdDriverObject = NULL;  

    11.     //驱动对象的设备  

    12.     PDEVICE_OBJECT kbdDeviceOjbect;  

    13.   

    14.     //初始化一个字符串,就是kbdclass驱动的名子  

    15.     RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);  

    16.   

    17.     //根据名字打开驱动对象  

    18.     status=ObReferenceObjectByName(  

    19.         &uniNtNameString,  

    20.         OBJ_CASE_INSENSITIVE,  

    21.         NULL,  

    22.         0,  

    23.         IoDriverObjectType,  

    24.         KernelMode,  

    25.         NULL,  

    26.         (PVOID*)&KbdDriverObject);  

    27.   

    28.     //如果失败了就直接返回  

    29.     if(!NT_SUCCESS(status))  

    30.     {  

    31.         DbgPrint("打开设备失败.../n");  

    32.         return status;  

    33.     }  

    34.   

    35.     //调用ObReferenceObjectByName会导致对驱动对象的引用计数增加  

    36.     //必须响应的调用解引用ObDereferenceObject  

    37.     ObDereferenceObject(pDriverObject);  

    38.     DbgPrint("打开成功,解除引用.../n");  

    39.   

    40.       

    41.     //键盘驱动的第一个设备  

    42.     kbdDeviceOjbect=KbdDriverObject->DeviceObject;  

    43.     while(kbdDeviceOjbect!=NULL)  

    44.     {  

    45.         //创建并绑定过滤设备  

    46.         CreateDevice(pDriverObject,kbdDeviceOjbect);  

    47.         //下一个设备  

    48.         kbdDeviceOjbect=kbdDeviceOjbect->NextDevice;  

    49.     }  

    50.   

    51.     return status;  

    52. }  

      

     

    在这里说一下ObReferenceObjectByName函数,该方法没有被导出,知我我们在头文件中声明一下即可使用,声明如下:

     

    [cpp] view plaincopy

    1. //根据名字获取设备对象,此函数没有公开,声明一下就可以直接使用了  

    2.   

    3. extern "C" NTSTATUS ObReferenceObjectByName(  

    4.                                  PUNICODE_STRING objectName,  

    5.                                  ULONG Attributes,  

    6.                                  PACCESS_STATE AccessState,  

    7.                                  ACCESS_MASK DesiredAccess,  

    8.                                  POBJECT_TYPE objectType,  

    9.                                  KPROCESSOR_MODE accessMode,  

    10.                                  PVOID ParseContext,  

    11.                                  PVOID *Object);  

    [cpp] view plain copy

    1. //根据名字获取设备对象,此函数没有公开,声明一下就可以直接使用了  

    2.   

    3. extern "C" NTSTATUS ObReferenceObjectByName(  

    4.                                  PUNICODE_STRING objectName,  

    5.                                  ULONG Attributes,  

    6.                                  PACCESS_STATE AccessState,  

    7.                                  ACCESS_MASK DesiredAccess,  

    8.                                  POBJECT_TYPE objectType,  

    9.                                  KPROCESSOR_MODE accessMode,  

    10.                                  PVOID ParseContext,  

    11.                                  PVOID *Object);  

      

     

     

    在BindDevice方法中,调用了一个CreateDevice方法,该方法负责创建过滤设备,并且附加在目标设备上,具体代码如下:

     

    [cpp] view plaincopy

    1. //创建设备  

    2. NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject,IN PDEVICE_OBJECT oldDevObj)  

    3. {  

    4.     NTSTATUS status;  

    5.     PDEVICE_OBJECT pDevObj;  

    6.     //谁被扩展  

    7.     PDEVICE_EXTENSION pDevExt;  

    8.   

    9.     status=IoCreateDevice(pDriverObject,  

    10.         sizeof(PDEVICE_EXTENSION),  

    11.         NULL,  

    12.         oldDevObj->DeviceType,//设备类型需要和被附加的设备类型相等  

    13.         0,  

    14.         FALSE,//如果指定设备是独占的,大部分驱动程序设置这个值为FALSE,如果不是独占的话设置为TRUE.  

    15.         &pDevObj);  

    16.   

    17.     if(!NT_SUCCESS(status))  

    18.     {  

    19.         DbgPrint("创建设备失败..../n");  

    20.         return NULL;  

    21.     }  

    22.     pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;  

    23.     //存储设备对象  

    24.     pDevExt->pDevice=pDevObj;  

    25.     //绑定前原设备  

    26.     pDevExt->poldDevice=oldDevObj;  

    27.       

    28.     //标志位  

    29.     pDevObj->Flags |=oldDevObj->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);  

    30.   

    31.     //该标识指示I/O管理器对所有发送到控制设备对象的Open请求进行安全检测  

    32.     pDevObj->Characteristics=oldDevObj->Characteristics;  

    33.   

    34.   

    35.     //绑定设备  

    36.     PDEVICE_OBJECT topDev = IoAttachDeviceToDeviceStack(pDevObj,oldDevObj);  

    37.     if(topDev==NULL)  

    38.     {  

    39.         //如果绑定失败,销毁设备  

    40.         IoDeleteDevice(pDevObj);  

    41.         status=STATUS_UNSUCCESSFUL;  

    42.         return status;  

    43.     }  

    44.       

    45.     //将绑定的设备和原始设备放入设备扩展中  

    46.     pDevExt->poldDevice=oldDevObj;  

    47.     pDevExt->pbindDevice=topDev;  

    48.   

    49.     pDevObj->Flags=pDevObj->Flags & ~DO_DEVICE_INITIALIZING;  

    50.     KdPrint(("绑定成功../n"));  

    51.     return STATUS_SUCCESS;  

    52. }  

    [cpp] view plain copy

    1. //创建设备  

    2. NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject,IN PDEVICE_OBJECT oldDevObj)  

    3. {  

    4.     NTSTATUS status;  

    5.     PDEVICE_OBJECT pDevObj;  

    6.     //谁被扩展  

    7.     PDEVICE_EXTENSION pDevExt;  

    8.   

    9.     status=IoCreateDevice(pDriverObject,  

    10.         sizeof(PDEVICE_EXTENSION),  

    11.         NULL,  

    12.         oldDevObj->DeviceType,//设备类型需要和被附加的设备类型相等  

    13.         0,  

    14.         FALSE,//如果指定设备是独占的,大部分驱动程序设置这个值为FALSE,如果不是独占的话设置为TRUE.  

    15.         &pDevObj);  

    16.   

    17.     if(!NT_SUCCESS(status))  

    18.     {  

    19.         DbgPrint("创建设备失败..../n");  

    20.         return NULL;  

    21.     }  

    22.     pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;  

    23.     //存储设备对象  

    24.     pDevExt->pDevice=pDevObj;  

    25.     //绑定前原设备  

    26.     pDevExt->poldDevice=oldDevObj;  

    27.       

    28.     //标志位  

    29.     pDevObj->Flags |=oldDevObj->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);  

    30.   

    31.     //该标识指示I/O管理器对所有发送到控制设备对象的Open请求进行安全检测  

    32.     pDevObj->Characteristics=oldDevObj->Characteristics;  

    33.   

    34.   

    35.     //绑定设备  

    36.     PDEVICE_OBJECT topDev = IoAttachDeviceToDeviceStack(pDevObj,oldDevObj);  

    37.     if(topDev==NULL)  

    38.     {  

    39.         //如果绑定失败,销毁设备  

    40.         IoDeleteDevice(pDevObj);  

    41.         status=STATUS_UNSUCCESSFUL;  

    42.         return status;  

    43.     }  

    44.       

    45.     //将绑定的设备和原始设备放入设备扩展中  

    46.     pDevExt->poldDevice=oldDevObj;  

    47.     pDevExt->pbindDevice=topDev;  

    48.   

    49.     pDevObj->Flags=pDevObj->Flags & ~DO_DEVICE_INITIALIZING;  

    50.     KdPrint(("绑定成功../n"));  

    51.     return STATUS_SUCCESS;  

    52. }  

      

     

     

     

     通过以上代码可以实现过滤设备的绑定,绑定了之后还是主要处理派遣函数,功能如下:

     

    [cpp] view plaincopy

    1. //派遣函数  

    2. NTSTATUS FilterDispatchRoutin(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)  

    3. {  

    4.     PIO_STACK_LOCATION currentIrpStack;  

    5.     PDEVICE_EXTENSION pDevExt;  

    6.       

    7.     //得到设备扩展  

    8.     pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;  

    9.       

    10.       

    11.   

    12.     //得到当前irp包  

    13.     currentIrpStack=IoGetCurrentIrpStackLocation(pIrp);  

    14.     //将当前irp复制到下层设备irp堆栈  

    15.     IoCopyCurrentIrpStackLocationToNext(pIrp);  

    16.   

    17.   

    18.     //保存原来的irp  

    19.     //pDevExt->tagIrp=pIrp;  

    20.   

    21.     //代理irp  

    22.     pDevExt->proxyIrp=pIrp;  

    23.   

    24.     //设置当irp完成时的回调例程  

    25.     IoSetCompletionRoutine(pDevExt->proxyIrp,CallBackKbdFilter,pDevObj,TRUE,TRUE,TRUE);  

    26.     DbgPrint("irp回调例程设置完毕.../n");  

    27.     return IoCallDriver(pDevExt->poldDevice,pDevExt->proxyIrp);  

    28. }  

    [cpp] view plain copy

    1. //派遣函数  

    2. NTSTATUS FilterDispatchRoutin(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)  

    3. {  

    4.     PIO_STACK_LOCATION currentIrpStack;  

    5.     PDEVICE_EXTENSION pDevExt;  

    6.       

    7.     //得到设备扩展  

    8.     pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;  

    9.       

    10.       

    11.   

    12.     //得到当前irp包  

    13.     currentIrpStack=IoGetCurrentIrpStackLocation(pIrp);  

    14.     //将当前irp复制到下层设备irp堆栈  

    15.     IoCopyCurrentIrpStackLocationToNext(pIrp);  

    16.   

    17.   

    18.     //保存原来的irp  

    19.     //pDevExt->tagIrp=pIrp;  

    20.   

    21.     //代理irp  

    22.     pDevExt->proxyIrp=pIrp;  

    23.   

    24.     //设置当irp完成时的回调例程  

    25.     IoSetCompletionRoutine(pDevExt->proxyIrp,CallBackKbdFilter,pDevObj,TRUE,TRUE,TRUE);  

    26.     DbgPrint("irp回调例程设置完毕.../n");  

    27.     return IoCallDriver(pDevExt->poldDevice,pDevExt->proxyIrp);  

    28. }  

      

     

     

      注意的是在处理派遣函数的时候我们将IRP换成我们自己的IRP,这样就能达到取消IRP的目的,我们给IRP设置了回调函数,当IRP处理完成的时候就去执行回调函数,回调函数如下:

     

    [cpp] view plaincopy

    1. // flags for keyboard status  

    2. #define S_SHIFT             1  

    3. #define S_CAPS              2  

    4. #define S_NUM               4  

    5. static int kb_status = S_NUM;  

    6. void __stdcall print_keystroke(UCHAR sch)  

    7. {  

    8.     UCHAR   ch = 0;  

    9.     int     off = 0;  

    10.   

    11.     if ((sch & 0x80) == 0)  //make  

    12.     {  

    13.         if ((sch < 0x47) ||   

    14.             ((sch >= 0x47 && sch < 0x54) && (kb_status & S_NUM))) // Num Lock  

    15.         {  

    16.             ch = asciiTbl[off+sch];  

    17.         }  

    18.   

    19.         switch (sch)  

    20.         {  

    21.         case 0x3A:  

    22.             kb_status ^= S_CAPS;  

    23.             break;  

    24.   

    25.         case 0x2A:  

    26.         case 0x36:  

    27.             kb_status |= S_SHIFT;  

    28.             break;  

    29.   

    30.         case 0x45:  

    31.             kb_status ^= S_NUM;  

    32.         }  

    33.     }  

    34.     else        //break  

    35.     {  

    36.         if (sch == 0xAA || sch == 0xB6)  

    37.             kb_status &= ~S_SHIFT;  

    38.     }  

    39.   

    40.     if (ch >= 0x20 && ch < 0x7F)  

    41.     {  

    42.         DbgPrint("%C /n",ch);  

    43.     }  

    44.   

    45. }  

    46.   

    47.   

    48.   

    49.   

    50.   

    51. NTSTATUS CallBackKbdFilter( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )  

    52. {  

    53.     PIO_STACK_LOCATION currentIrp;  

    54.     PKEYBOARD_INPUT_DATA keyData;  

    55.   

    56.     currentIrp=IoGetCurrentIrpStackLocation(Irp);  

    57.   

    58.     if(NT_SUCCESS(Irp->IoStatus.Status))  

    59.     {  

    60.         keyData=(PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;  

    61.   

    62.         //DbgPrint("扫描码:%x",keyData->MakeCode);  

    63.         DbgPrint("键盘 :%s",keyData->Flags?"弹起":"按下");  

    64.         print_keystroke((UCHAR)keyData->MakeCode);  

    65.     }  

    66.     if( Irp->PendingReturned )  

    67.     {   

    68.         IoMarkIrpPending( Irp );   

    69.     }   

    70.     return Irp->IoStatus.Status;  

    71. }  

    [cpp] view plain copy

    1. // flags for keyboard status  

    2. #define S_SHIFT             1  

    3. #define S_CAPS              2  

    4. #define S_NUM               4  

    5. static int kb_status = S_NUM;  

    6. void __stdcall print_keystroke(UCHAR sch)  

    7. {  

    8.     UCHAR   ch = 0;  

    9.     int     off = 0;  

    10.   

    11.     if ((sch & 0x80) == 0)  //make  

    12.     {  

    13.         if ((sch < 0x47) ||   

    14.             ((sch >= 0x47 && sch < 0x54) && (kb_status & S_NUM))) // Num Lock  

    15.         {  

    16.             ch = asciiTbl[off+sch];  

    17.         }  

    18.   

    19.         switch (sch)  

    20.         {  

    21.         case 0x3A:  

    22.             kb_status ^= S_CAPS;  

    23.             break;  

    24.   

    25.         case 0x2A:  

    26.         case 0x36:  

    27.             kb_status |= S_SHIFT;  

    28.             break;  

    29.   

    30.         case 0x45:  

    31.             kb_status ^= S_NUM;  

    32.         }  

    33.     }  

    34.     else        //break  

    35.     {  

    36.         if (sch == 0xAA || sch == 0xB6)  

    37.             kb_status &= ~S_SHIFT;  

    38.     }  

    39.   

    40.     if (ch >= 0x20 && ch < 0x7F)  

    41.     {  

    42.         DbgPrint("%C /n",ch);  

    43.     }  

    44.   

    45. }  

    46.   

    47.   

    48.   

    49.   

    50.   

    51. NTSTATUS CallBackKbdFilter( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )  

    52. {  

    53.     PIO_STACK_LOCATION currentIrp;  

    54.     PKEYBOARD_INPUT_DATA keyData;  

    55.   

    56.     currentIrp=IoGetCurrentIrpStackLocation(Irp);  

    57.   

    58.     if(NT_SUCCESS(Irp->IoStatus.Status))  

    59.     {  

    60.         keyData=(PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;  

    61.   

    62.         //DbgPrint("扫描码:%x",keyData->MakeCode);  

    63.         DbgPrint("键盘 :%s",keyData->Flags?"弹起":"按下");  

    64.         print_keystroke((UCHAR)keyData->MakeCode);  

    65.     }  

    66.     if( Irp->PendingReturned )  

    67.     {   

    68.         IoMarkIrpPending( Irp );   

    69.     }   

    70.     return Irp->IoStatus.Status;  

    71. }  

      

     

      函数就不说明了,主要就是对makecode的处理,不过在回调函数中引用了对照表,如下:

     

     

    [c-sharp] view plaincopy

    1. unsigned char asciiTbl[]={  

    2.     0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09, //normal  

    3.         0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x0D, 0x00, 0x61, 0x73,  

    4.         0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,  

    5.         0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    6.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    7.         0x32, 0x33, 0x30, 0x2E,  

    8.         0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09, //caps  

    9.         0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x5B, 0x5D, 0x0D, 0x00, 0x41, 0x53,  

    10.         0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x5A, 0x58, 0x43, 0x56,  

    11.         0x42, 0x4E, 0x4D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    12.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    13.         0x32, 0x33, 0x30, 0x2E,  

    14.         0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09, //shift  

    15.         0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x0D, 0x00, 0x41, 0x53,  

    16.         0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,  

    17.         0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    18.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    19.         0x32, 0x33, 0x30, 0x2E,  

    20.         0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09, //caps + shift  

    21.         0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x0D, 0x00, 0x61, 0x73,  

    22.         0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x7A, 0x78, 0x63, 0x76,  

    23.         0x62, 0x6E, 0x6D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    24.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    25.         0x32, 0x33, 0x30, 0x2E  

    26. };  

    [c-sharp] view plain copy

    1. unsigned char asciiTbl[]={  

    2.     0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09, //normal  

    3.         0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x0D, 0x00, 0x61, 0x73,  

    4.         0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,  

    5.         0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    6.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    7.         0x32, 0x33, 0x30, 0x2E,  

    8.         0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09, //caps  

    9.         0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x5B, 0x5D, 0x0D, 0x00, 0x41, 0x53,  

    10.         0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x5A, 0x58, 0x43, 0x56,  

    11.         0x42, 0x4E, 0x4D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    12.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    13.         0x32, 0x33, 0x30, 0x2E,  

    14.         0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09, //shift  

    15.         0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x0D, 0x00, 0x41, 0x53,  

    16.         0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,  

    17.         0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    18.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    19.         0x32, 0x33, 0x30, 0x2E,  

    20.         0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09, //caps + shift  

    21.         0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x0D, 0x00, 0x61, 0x73,  

    22.         0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x7A, 0x78, 0x63, 0x76,  

    23.         0x62, 0x6E, 0x6D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  

    24.         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,  

    25.         0x32, 0x33, 0x30, 0x2E  

    26. };  

     

      就是卸载函数,在卸载的时候我们要删除设备和附加的设备,然后取消最后一个IRP,代码如下:

     

    [cpp] view plaincopy

    1. //卸载例程  

    2. void FilterUnload(IN PDRIVER_OBJECT pDriverObject)  

    3. {  

    4.       

    5.     //得到设备  

    6.     PDEVICE_OBJECT pDevObj=pDriverObject->DeviceObject;  

    7.     while(pDevObj!=NULL)  

    8.     {  

    9.         //设备扩展  

    10.         PDEVICE_EXTENSION pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;  

    11.   

    12.         PDEVICE_OBJECT pTagObj=pDevExt->pbindDevice;  

    13.   

    14.   

    15.   

    16.         //解除绑定  

    17.         if(pDevExt->pbindDevice!=NULL)  

    18.         {  

    19.             IoDetachDevice(pDevExt->pbindDevice);  

    20.         }  

    21.   

    22.   

    23.         //删除设备  

    24.         if(pDevExt->pDevice!=NULL)  

    25.         {  

    26.             IoDeleteDevice(pDevExt->pDevice);  

    27.         }  

    28.   

    29.         if(pDevExt->proxyIrp!=NULL)  

    30.         {  

    31.             if(CancelIrp(pDevExt->proxyIrp))  

    32.             {  

    33.                 DbgPrint("取消成功。。。/n");  

    34.             }  

    35.             else  

    36.             {  

    37.                 DbgPrint("取消失败。。。/n");  

    38.             }  

    39.         }  

    40.         //下一个设备  

    41.         pDevObj=pDevObj->NextDevice;  

    42.           

    43.     }  

    44. }  

    [cpp] view plain copy

    1. //卸载例程  

    2. void FilterUnload(IN PDRIVER_OBJECT pDriverObject)  

    3. {  

    4.       

    5.     //得到设备  

    6.     PDEVICE_OBJECT pDevObj=pDriverObject->DeviceObject;  

    7.     while(pDevObj!=NULL)  

    8.     {  

    9.         //设备扩展  

    10.         PDEVICE_EXTENSION pDevExt=(PDEVICE_EXTENSION)pDevObj->DeviceExtension;  

    11.   

    12.         PDEVICE_OBJECT pTagObj=pDevExt->pbindDevice;  

    13.   

    14.   

    15.   

    16.         //解除绑定  

    17.         if(pDevExt->pbindDevice!=NULL)  

    18.         {  

    19.             IoDetachDevice(pDevExt->pbindDevice);  

    20.         }  

    21.   

    22.   

    23.         //删除设备  

    24.         if(pDevExt->pDevice!=NULL)  

    25.         {  

    26.             IoDeleteDevice(pDevExt->pDevice);  

    27.         }  

    28.   

    29.         if(pDevExt->proxyIrp!=NULL)  

    30.         {  

    31.             if(CancelIrp(pDevExt->proxyIrp))  

    32.             {  

    33.                 DbgPrint("取消成功。。。/n");  

    34.             }  

    35.             else  

    36.             {  

    37.                 DbgPrint("取消失败。。。/n");  

    38.             }  

    39.         }  

    40.         //下一个设备  

    41.         pDevObj=pDevObj->NextDevice;  

    42.           

    43.     }  

    44. }  

      

     

     

      载函数中调用了个取消IRP的方法,代码如下:

     

    [cpp] view plaincopy

    1. BOOLEAN CancelIrp(PIRP pIrp)  

    2. {  

    3.     if(pIrp==NULL)  

    4.     {  

    5.         DbgPrint("取消irp错误.../n");  

    6.         return FALSE;  

    7.     }  

    8.     if(pIrp->Cancel || pIrp->CancelRoutine==NULL)  

    9.     {  

    10.         DbgPrint("取消irp错误.../n");  

    11.         return FALSE;  

    12.     }  

    13.     if(FALSE==IoCancelIrp(pIrp))  

    14.     {  

    15.         DbgPrint("IoCancelIrp to irp错误.../n");  

    16.         return FALSE;  

    17.     }  

    18.       

    19.     //取消后重设此例为空  

    20.     IoSetCancelRoutine(pIrp,NULL);  

    21.     return TRUE;  

    22. }  

    [cpp] view plain copy

    1. BOOLEAN CancelIrp(PIRP pIrp)  

    2. {  

    3.     if(pIrp==NULL)  

    4.     {  

    5.         DbgPrint("取消irp错误.../n");  

    6.         return FALSE;  

    7.     }  

    8.     if(pIrp->Cancel || pIrp->CancelRoutine==NULL)  

    9.     {  

    10.         DbgPrint("取消irp错误.../n");  

    11.         return FALSE;  

    12.     }  

    13.     if(FALSE==IoCancelIrp(pIrp))  

    14.     {  

    15.         DbgPrint("IoCancelIrp to irp错误.../n");  

    16.         return FALSE;  

    17.     }  

    18.       

    19.     //取消后重设此例为空  

    20.     IoSetCancelRoutine(pIrp,NULL);  

    21.     return TRUE;  

    22. }  

      

     

     

    整个键盘过滤驱动就完成了,以后还得多多学习,多多总结。

    展开全文
  • 数据库安装并初始化1 部署规划1.1 路径规划1.2 实例规划1.3 管理规划2 安装数据库前准备2.1 获取系统位数2.2 查询操作系统 release 信息2.3 查询系统信息2.4 查询系统名称3 安装数据库过程3.1 创建组和用户3.2 输入...
  • N1盒子解决刷机失败的问题我的N1自从入手后就刷了灯大的小...刷机开始后进度条到1%或者2%就会提示:Romcode/初始化DDR/读取初始化结果/USB...如下图:-34ec4b9261b51b41.jpg (197.06 KB, 下载次数: 0)2019-2-18 1...
  • 你好!...编写USB键盘驱动,可以参考Kernel里的usbkbd.c这个文件. 我越发觉得驱动都是按套路来的.......流程都差不多一样. 在这个文件里,最主要就是看usb_kbd_probe函数和usb_kbd_irq函数了。
  • 双击此设备,在设备状态中为此描述: Windows 无法初始化这个硬件的设备驱动程序。 (代码 37) 解决办法:参考 在开始菜单中运行regedit 1.定位到HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\...
  • LINUX系统初始化

    万次阅读 2018-04-07 20:35:44
    kernel_init():此函数主要完成设备驱动程序的初始化,并且调用 init_post() 启动用户空间的init进程。 init_post():初始化的尾声,第一个用户空间的init 横空出世!其PID始终为1。 init: 内核会在过去曾使用过init...
  • 近来在看 《寒江独钓 windows内核编程》,看到键盘过滤部分,记下笔记,仅供参考,有理解不对之处,还望大家指正。 现在来说一下传统型键盘过滤,就是把自己的设备对象...KbdClass被称为键盘驱动,在windows中,类
  • WinIo64驱动级别的键盘模拟(java)

    千次阅读 热门讨论 2018-05-14 19:21:38
    最近因为爬虫登录的网站加了密码控件,尝试了很多方法都... 最开始从这里获取到了winIo32实现驱动键盘事件,但使用的是JNative(目前只支持32位),同时winIO32在64位的OS下运行有问题(本人测试过,可能过程中有...
  • NFC源码分析之初始化流程

    千次阅读 2017-12-21 14:46:25
    在NfcApplication的onCreate当中会去实例化NfcService,开启Nfc的初始化. 相关代码: 1 AndroidManifest.xml 2 < application android:name = ".NfcApplication" 3 android:icon = "@...
  • 矩阵键盘是一种常用的键盘形式,它将按键设计成M行N列,这样共需M+N根信号线,却可驱动M&TImes;N个按键,大大节约了I/O资源。本文介绍了一种利用TQ2440开发板的GPIO口扩展5&TImes;4矩阵键盘的方法,并将所有...
  • 硬件设计本设计扩展5行4列的矩阵键盘,如图1所示。其中行线ROW1-ROW5连接S3C2440的中断引脚EINT8,EINT9,EINT11,EINT13,EINT14[1]。这些中断引脚本身连有10kΩ的上拉电阻,把中断引脚电平拉高,确保按键空闲时...
  • 如何编写键盘驱动程序

    千次阅读 2011-09-30 15:39:41
    1. 指定USB键盘驱动所需的头文件: #include/*内核头文件,含有内核一些常用函数的原型定义*/   #include/*定义内存分配的一些函数*/   #include/*模块编译必须的头文件*/   #include/*输入设备相关函数的...
  • Winio64在64位系统中初始化失败问题

    千次阅读 2015-04-14 14:52:01
    Winio64在64位系统中初始化失败问题 系统是win7 64位。 语言是C# 从http://www.internals.com/下载的winio3.0,下载后的Binaries文件夹中包含WinIo64.dll,WinIo64.sys,将这两个文件放在debug文
  • 如何初始化计算机网络设置如何初始化计算机网络设置?以下就是如何初始化计算机网络设置等等的介绍,希望对您有所帮助。1、打开电脑,对着桌面上的“网上邻居”点右键----属性。2、对着“本地连接”点右键----属性。3...
  • 80H~0FFH:硬盘,80H第一个硬盘,81H第二个硬盘出口参数:CF=1——操作失败,AH=状态代码,参见功能号01H中的说明, 否则,AH=00H —未安装驱动器=01H —无改变线支持的软盘驱动器=02H —带有改变线支持的软盘...
  • Linux驱动开发: USB驱动开发

    千次阅读 多人点赞 2021-08-24 22:03:05
    自微软在Windows 98中加入对USB接口的支持后,USB接口才推广开来,USB设备也日渐增多,如数码相机、摄像头、扫描仪、游戏杆、打印机、键盘、鼠标等等,其中应用最广的就是摄像头和U盘了。 USB包括老旧的USB 1.1...
  • 在GUI编程中,事件是非常常见的。...因此, 所谓事件驱动,简单地说就是你点什么按钮(即产生什么...事件收集器专门负责收集所有事件,包括来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件的(如
  • 编写Linux下的USB键盘驱动(附源码)

    千次阅读 2012-11-19 12:46:44
      1. 指定USB键盘驱动所需的头文件: 1 t! q5 f( V4 ~9 V 3 [/ M0 P8 J/ `. d" r6 o) Y #include /*内核头文件,含有内核一些常用函数的原型定义*/ 0 H( C2 p, Z; q7 D( b% m #inc
  • C#实现驱动级模拟按键

    千次阅读 2018-05-14 21:00:43
    由于网上已经有使用Winio.dll实现驱动级按键,我这就直接拿来在C#中使用了(标题有点骗人,其实都是C++的功劳),全部代码如下: WinIo.cs类 [csharp]   view plain   copy using  System;  using  System....
  • WinIo驱动键盘模拟编程

    万次阅读 2015-02-02 14:38:21
    当时是为了提高我魔兽三的按键速度,用了个叫移花接木的软件,把键盘的键位改了。的确是有所帮助,但这是共享软件,用40次就不能再用了除非注册。于是乎就有了做一个类似的软件出来,在网上搜索了一把发现WinI
  • usb hid gadget驱动

    千次阅读 2016-10-31 10:13:37
    2 hidg驱动初始化详解 1 至上而下遍历搜索绑定驱动和设备 2 至下而上遍历完成初始化 三 hid gadget应用参考文献因为usb gadget驱动在实际应用中比较少见,通常usb口主要就两个功能,一是供电;二是接外部设备。而且...
  • PC键盘驱动程序源码分析

    万次阅读 2004-11-29 12:34:00
     编写目的:描述uclinux内核中pc机键盘驱动的体系结构和工作原理,用于指导针对具体的嵌入式键盘的驱动程序的编写。二. 参考资料:1.《Linux内核源代码情景分析(下册)》第8.7和8.8章节,page330~4122.内核源...
  • [转载]键盘驱动原理

    千次阅读 2009-08-03 19:29:00
    主要讨论的内容有,ps/2 键盘的硬件,使用键盘驱动的应用层,键盘驱动初始化键盘驱动如何完成自己的工作,以及一些涉及到的相关内容。需要注意的是,以后我们提到的键盘,如果没有特殊说明,都是指 ps/2 键盘。...
  • 如何编写Linux 下的 USB 键盘驱动

    千次阅读 2012-04-17 14:23:40
    如何编写Linux 下的 USB 键盘驱动 1. 指定 USB 键盘驱动所需的头文件: #include /*内核头文件,含有内核一些常用函数的原型定义*/ #include /*定义内存分配的一些函数*/ #include /*模块编译必须的头文件*/...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,560
精华内容 6,624
热门标签
关键字:

初始化失败键盘驱动