USBデバイス駆動(一):USBデバイス駆動フレームワークの簡単な分析

9619 ワード

生活、仕事の中でよくUSBデバイスに接触します.例えば、マウス、キーボード、カメラ、リムーバブルハードディスク、スキャンガンなどです.これらのデバイスがUSBインタフェースを介してパソコンに接続されると、すぐに「新しいハードウェアが検出された…」ドライバなどを取り付けます.ここで、USBデバイスはUSBバスを使用していることを強調し、windowやLinuxカーネルにはusbバス駆動が付いているので、USBデバイスを接続すると、ホストはすぐに検出することができ、デバイス駆動のインストールが必要であることを注意するのはUSBデバイス駆動のインストールです.
USBデバイスの駆動はUSBバスを使用するので、多くの操作はUSBバスの駆動によって私たちを助けて、私たちはバス、デバイス、駆動フレームワークに従ってUSBデバイスの駆動を実現するだけでいいです.USBデバイスデータの読み書き操作はバスによって駆動されています.現在、バスで読み取ったデータを直接使用して、これらのデータの意味を解析し、関連する操作を行えばいいのです(ここで注意しなければならないのは、USBバス駆動はUSBデバイスの読み書き操作関数のみを提供し、この関数は汎用的で、つまり中のデータの意味はバス駆動が知らないことです).
USBデバイスが駆動するフレーム図では、具体的なコードは、カーネル内の/drivers/hid/usbhid/usbmouseを参照することができる.c
  • 割り当て/設定usb_driver構造体、中を充填する.id_table、.probe、.disconnectメンバー.
  • 呼び出しusb_register usb_driver構造体はカーネルに登録されています.
  • static struct usb_driver usbtouch_driver = {
    	.name		= "myusb",
    	.probe		= usbtouch_probe,
    	.disconnect	= usbtouch_disconnect,
    	.id_table	= usbtouch_devices,
    };
    
    static int __init usbtouch_init(void)
    {
    	return usb_register(&usbtouch_driver);
    }
    
    static void __exit usbtouch_cleanup(void)
    {
    	usb_deregister(&usbtouch_driver);
    }

    struct usb_driverのid_tableメンバーは、usbデバイスとマッチングするオプションで、このドライバがサポートするデバイスを表します.
    .probeメンバーは関数ポインタであり,デバイスと駆動マッチングが成功したときに実行される.
    .disconnectメンバーも関数ポインタであり、USBデバイスを抜いたり、USBデバイスドライバをアンインストールしたりするなど、デバイスとドライバの関連付けに成功した後に実行を切断します.
    .Probe関数で処理する必要があることは次のとおりです(ここではマウスを例に挙げます):
  • input_を作成devデバイス
  • 設定input_devがサポートするクラスは、キークラス、相対変位クラスをサポートする必要があります.
  • 設定input_devがサポートするイベントは、左、中、右、およびサイド付加キーイベント、およびxy方向の相対変位イベントおよびローラスライドイベントを生成することができる.
  • 登録input_devはinput入力サブシステムに入力されます.
  • ハードウェアに関する動作は、異なるデバイスの動作に差がある
  • .
    USBバスはグループ従構造で、USBバス駆動はポーリングしてUSBデバイス上のデータを取得するしかなく、USBデバイスは自発的にUSBホストにデータの転送を通知することができない.USBドライバではurb(usb request block)構造体を構築し、充填して使用する必要があります.urbでは、USBデバイスデータをホストに格納する物理アドレスや仮想アドレス、USBデータの転送方向、長さ、デバイス通信のエンドポイントなどを指定します.
    USb_の使用alloc_urb()関数はurb構造体を割り当て,終了後はusb_を用いる.free_urb()はこの構造体を放出する.
    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
    /*    urb    usb    */
     /*  If the driver want to use this urb for interrupt, control, or bulk
     * endpoints, pass '0' as the number of iso packets.
     * The driver must call usb_free_urb() when it is finished with the urb.
    */

    USBデバイスのデータをホスト上のどこに格納するか、USBデバイスドライバで指定する必要があります.
    void *usb_buffer_alloc(struct usb_device *dev,size_t size,gfp_t mem_flags,dma_addr_t *dma)
    /*    usb  ,         */
    /*@dma: used to return DMA address of buffer           
    *          
    */

    対応するデバイスのエンドポイントとデータ長を見つけます
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    /*pipe    */
    len = endpoint->wMaxPacketSize;
    /*len            ,     */

    現在、データ転送の3要素(ソース、長さ、目的)が決定され、残りはurb構造体、すなわちホストデータ転送の3要素を充填することである.
    	static inline void usb_fill_int_urb (struct urb *urb,struct usb_device *dev,
                         unsigned int pipe,void *transfer_buffer,
    				     int buffer_length,usb_complete_t complete_fn,
    				     void *context,int interval)
     / * usb_fill_int_urb - macro to help initialize a interrupt urb
     * @urb: pointer to the urb to initialize.
     * @dev: pointer to the struct usb_device for this urb.
     * @pipe: the endpoint pipe
     * @transfer_buffer: pointer to the transfer buffer
     * @buffer_length: length of the transfer buffer
     * @complete_fn: pointer to the usb_complete_t function
     * @context: what to set the urb context to.
     * @interval: what to set the urb interval to, encoded like
     *	the endpoint descriptor's bInterval value.	
     */
    
    	mouse->irq->transfer_dma = mouse->data_dma;
    	mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    呼び出しusb_fill_int_urb()、complete_fnは割り込み関数に相当します.すなわち、ホストがデータを読み出すと指定したメモリdata_に格納されます.dmaで.次に、USBデバイスドライバのみがその意味を知っているデータの処理をCPUに通知する.この完了関数では、レポートなどのデータを処理します.
    最後にusrリクエストブロックをコミットします.
    usb_submit_urb(urb, GFP_ATOMIC);

    ここまででほぼ終わり、あとはdisconnect関数の操作です.この関数と.Probeはまったく逆で、申請したリソースはここで解放する必要があります.
    ここを見るとUSBドライブが簡単だと思いますか?フレームワークは確かに簡単です!簡単です!簡単です!重要なことは3回言って、あなたたちの実力を信じなければなりません.
    次のコードは私がARMボードにusbマウスのために書いたものです.
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /*USB        input                  
    *    input       ,   input                   input_event    
    * usb   ,      usb      ,        urb     。   urb        
    */
    
    static struct input_dev *input_dev;
    static struct urb *urb;
    static dma_addr_t data_dma;
    static unsigned char *data;	
    static unsigned len;
    
    static void usbtouch_irq(struct urb *urb)
    {
    	#if 1
    	int status;
    
    	switch (urb->status) {
    	case 0:			/* success */
    		break;
    	case -ECONNRESET:	/* unlink */
    	case -ENOENT:
    	case -ESHUTDOWN:
    		return;
    	/* -EPIPE:  should clear the halt */
    	default:		/* error */
    		goto resubmit;
    	}
    	/* usb       ,         。 usb                
    	*   usb           ,  usb  data[1]     
    	*data[2]    x         ,data[3]    y         
    	*data[4]           
    	*/
    	input_report_key(input_dev, BTN_LEFT,   data[1] & 0x01);
    	input_report_key(input_dev, BTN_RIGHT,  data[1] & 0x02);
    	input_report_key(input_dev, BTN_MIDDLE, data[1] & 0x04);
    	input_report_key(input_dev, BTN_SIDE,   data[0] & 0x08);
    	input_report_key(input_dev, BTN_EXTRA,  data[0] & 0x10);
    
    	input_report_rel(input_dev, REL_X,     data[2]);
    	input_report_rel(input_dev, REL_Y,     data[3]);
    	input_report_rel(input_dev, REL_WHEEL, data[4]);
    
    	input_sync(input_dev);
    resubmit:	
    	/*   ,    urb  */
    	status = usb_submit_urb (urb, GFP_ATOMIC);
    		
    	#else
    	/*  USB          ,    。       */	
    	int i =0,status;
    	for(i = 0; i < len;i++){
    		printk("%x ",data[i]);	
    	}
    	printk("
    "); status = usb_submit_urb (urb, GFP_ATOMIC); #endif } static int usbtouch_probe(struct usb_interface *intf,const struct usb_device_id *id) { int pipe; struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; interface = intf->cur_altsetting; /* 0 , 0*/ endpoint = &interface->endpoint[0].desc; /*1. input_dev */ input_dev = input_allocate_device(); if(!input_dev){return -1;} /*2. 。 */ input_dev->name = "usb-mouse"; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); input_dev->relbit[0] |= BIT(REL_WHEEL); /*3. input_dev */ input_register_device(input_dev); /*4. 。 usb */ urb=usb_alloc_urb(0, GFP_KERNEL); /* , 、 、 */ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); /* */ len = endpoint->wMaxPacketSize; /*USB */ data = usb_buffer_alloc(udev, len,GFP_ATOMIC, &data_dma); urb->dev = udev; urb->transfer_dma = data_dma; urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* ( NULL), */ usb_fill_int_urb(urb, udev,pipe,data, len,usbtouch_irq, NULL, endpoint->bInterval); /* urb */ usb_submit_urb(urb, GFP_ATOMIC); return 0; } static void usbtouch_disconnect(struct usb_interface *intf) { usb_kill_urb(urb); input_unregister_device(input_dev); usb_free_urb(urb); usb_buffer_free(interface_to_usbdev(intf), len, data,data_dma); input_free_device(input_dev); } /* */ static struct usb_device_id usbtouch_devices [] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, { } /* Terminating entry */ }; static struct usb_driver usbtouch_driver = { .name = "myusb", .probe = usbtouch_probe, .disconnect = usbtouch_disconnect, .id_table = usbtouch_devices, }; static int __init usbtouch_init(void) { return usb_register(&usbtouch_driver); } static void __exit usbtouch_cleanup(void) { usb_deregister(&usbtouch_driver); } module_init(usbtouch_init); module_exit(usbtouch_cleanup); MODULE_LICENSE("GPL");

    次の図のようにテストすると、マウスを押したり移動したりして、対応するイベントコードを生成することができます.ここで/dev/event 0のフォーマットはともかく、よくわからない場合はinput入力サブシステムを参照してください.
    usb駆動装置はinput入力サブシステムフレームワークを用いており,これは従来の入力サブシステム(ボタン,タッチスクリーン)などと少し異なる.以前は、割り込みサービス関数で直接データを取得してinputを呼び出していました.eventは、USBデバイスドライバのデータソースがUSBバスドライバであり、完了関数でinput_を呼び出すことを報告する.eventはデータを報告します.
    # hexdump /dev/event0
    0000000 4998 0000 4b9a 000c 0001 0110 0001 0000
    0000010 4998 0000 4bb6 000c 0000 0000 0000 0000
    0000020 4998 0000 cc2f 000e 0001 0110 0000 0000
    0000030 4998 0000 cc4b 000e 0000 0000 0000 0000
    0000040 4999 0000 93c0 000d 0001 0111 0001 0000
    0000050 4999 0000 93db 000d 0000 0000 0000 0000
    0000060 499a 0000 2fe0 0001 0001 0111 0000 0000
    0000070 499a 0000 2ff2 0001 0000 0000 0000 0000
    0000080 499c 0000 b23e 0006 0002 0000 00ff 0000
    0000090 499c 0000 b24e 0006 0002 0001 0001 0000
    00000a0 499c 0000 b252 0006 0000 0000 0000 0000
    00000b0 499e 0000 cf82 0007 0001 0112 0001 0000
    00000c0 499e 0000 cf9d 0007 0000 0000 0000 0000
    00000d0 499e 0000 59ad 000b 0001 0112 0000 0000
    00000e0 499e 0000 59c2 000b 0000 0000 0000 0000
    00000f0 499f 0000 df37 0007 0002 0008 00ff 0000
    0000100 499f 0000 df4e 0007 0000 0000 0000 0000
    0000110 499f 0000 cacc 000d 0002 0008 00ff 0000
    0000120 499f 0000 cadf 000d 0000 0000 0000 0000

    事はこのような事で、状況はこのような状況です.
    これがusbデバイス駆動の簡単なフレームワークであり,書き込み操作(ホストからデバイスにデータを送信する)の駆動モデルや各種伝送タイプの駆動更新の下で暇である.