注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

LINUX驱动学习——内核USB驱动编写七USB键盘2  

2013-01-10 09:23:19|  分类: USB系列 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

LINUX驱动学习——内核USB驱动编写七USB键盘2

接上文:

11.  编写事件处理函数:

/*事件处理函数*/

下面这个函数是用来发送给设备处理的事件回调函数在:

static void input_handle_event(struct input_dev *dev,
                   unsigned int type, unsigned int code, int value)函数中调用

如果是发给原始设备的数据,就会调用这个,否则会调用input_handler对应的事件函数,这个大家一定要看清。

再详细的就得看input 子系统了,目前还没有意向。

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[i], 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) -I/home/usbkbd/ modules

 

这里顺便说一下:

static int input_open_file(struct inode *inode, struct file *file)中调用设备的open即:

static int usb_kbd_open(struct input_dev *dev)

static int input_open_file(struct inode *inode, struct file *file)

{

         struct input_handler *handler;

         const struct file_operations *old_fops, *new_fops = NULL;

         int err;

 

         lock_kernel();

         /* No load-on-demand here? */

         handler = input_table[iminor(inode) >> 5];

         if (!handler || !(new_fops = fops_get(handler->fops))) {

                   err = -ENODEV;

                   goto out;

         }

 

         /*

          * That's _really_ odd. Usually NULL ->open means "nothing special",

          * not "no device". Oh, well...

          */

         if (!new_fops->open) {

                   fops_put(new_fops);

                   err = -ENODEV;

                   goto out;

         }

         old_fops = file->f_op;

         file->f_op = new_fops;

 

         err = new_fops->open(inode, file);

 

         if (err) {

                   fops_put(file->f_op);

                   file->f_op = fops_get(old_fops);

         }

         fops_put(old_fops);

out:

         unlock_kernel();

         return err;

}

这也是为什么设备的打开函数与前面见到的别的驱动的打开函数有所区别,一开始可能大家会不注意。由于在open阶段已经把设备文件的操作操作方法重定位了到了具体的input_handler,所以其它接口操作(read、write、ioctl等),由各个input_handler的fops方法决定。如evdev.c中的:evdev_fops。

另外,这个是使用的标准的输入输出,如果不使用标准的,还得要自己完成读写的函数,一定不要混淆。

该认真时得认真,该大略时得大略。简繁得体,方会进步更快。

最后,再次感谢提供这个键盘驱动的同学。共同努力,一起学习,天天向上。

  评论这张
 
阅读(1064)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017