usbドライバ


Rev.1を表示中。最新版はこちら

カーネルソースにusb-skeleton.cと言うのがあります。これはUSBドライバの雛形で、これをコンパイルし、insmodすると、skeletonと言うなusbドライバがインストールされ、ベンダーIDが0xfff0,プロダクトIDも0xfff0の仮想と思われるUSBデバイスを挿入すると、/sys/bus/usb/devices/と、マイナー番号が192の/dev/skel1のデバイスファイルが作成され、/dev/skel1をread/writeする事で、usbデバイスとバルク読み書きする事ができます。

単にバルク読み書きするUSBデバイスなら、usb-skeleton.cのベンダーIDとプロダクトIDを書き換えることだけで、usbドライバを作ることができます。

USBドライバは、ドライバから見ると2つのフェーズで構成されています。最初はUSBコア(下位の層で、ややっこしいUSBハードに掛かる処理を行ってくれます。)に、ベンダーIDとプロダクトIDそして掛かるコールバックを有するUSBドライバを登録することです。USBデバイスが挿入されると、USBコアはそのベンダーIDとプロダクトIDを一致するusbドライバのコールバック関数probeを呼び出します。

usbインターフェースを引数にprove()がコールされます。ここでこのusbインターフェースから、バッファーとかリスト、エンドポイントを取得し、デバイスファイルを作成します。ことデバイスファイルの file_operationsには、デバイスのread/writeのコールバックが設定されています。

usbデバイスとのやり取りは、エンドポイント間でやり取りします。ソケットで言うなら、デバイスがIPアドレスで、エンドポイントがポートと例えれば理解しやすいかと思います。なお、エンドポイントはin/outおよびコマンド用とか機能によって有しています。


全ソースはカーネル下のそれを見てもらえばと思います。ここではエッセンスだけと言う事で。

insmodすると、skel_driverを引数にしてusb_register()がコールされ、usbコアーにusbドライバとして登録されます。usbコアのこのid_tableを参照することで、挿入されたusbデバイスにマッチするドライバを検索でき、マッチしたドライバのprobeをコールします。従ってオリジナルなドライバを作成する場合、USB_SKEL_VENDOR_IDとUSB_SKEL_PRODUCT_IDを、必要ならnameをデバイスの適切なIDに書き直します。
#define USB_SKEL_VENDOR_ID      0xfff0
#define USB_SKEL_PRODUCT_ID     0xfff0

static const struct usb_device_id skel_table[] = {
       { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
       { }                                     /* Terminating entry */
};

static struct usb_driver skel_driver = {
       .name =         "skeleton",
       .probe =        skel_probe,
       .disconnect =   skel_disconnect,
       .suspend =      skel_suspend,
       .resume =       skel_resume,
       .pre_reset =    skel_pre_reset,
       .post_reset =   skel_post_reset,
       .id_table =     skel_table,
       .supports_autosuspend = 1,
};

module_usb_driver(skel_driver);
module_usb_driverマクロは以下の様に定義されていて、module_initに__usb_driverを引数にしてusb_register()をコールし、module_exitには__usb_driverを引数にしてusb_deregister()をコールするように展開されます。
#define module_usb_driver(__usb_driver) \
       module_driver(__usb_driver, usb_register, \
                      usb_deregister)
skel_probe()の主たる処理は、ドライバがやり取りする構造体(usb_skelで、ドライバ単位で独自のものとなります。)に、usb_interfaceから必要な情報をセットし、それとinterface->usb_devにセットし、USB_SKEL_MINOR_BASEをマイナー番号のデバイスファイル/sysファイルを作成します。この情報はデバイスファイルのオープン時、inodeからのマイナー番号で、usbデバイスのusb_interfaceを取得することができます。
struct usb_skel {
       struct usb_device       *udev;                  /* the usb device for this device */
       struct usb_interface    *interface;             /* the interface for this device */
       struct semaphore        limit_sem;              /* limiting the number of writes in progress */
       struct usb_anchor       submitted;              /* in case we need to retract our submissions */
       struct urb              *bulk_in_urb;           /* the urb to read data with */
       unsigned char           *bulk_in_buffer;        /* the buffer to receive data */
       size_t                  bulk_in_size;           /* the size of the receive buffer */
       size_t                  bulk_in_filled;         /* number of bytes in the buffer */
       size_t                  bulk_in_copied;         /* already copied to user space */
       __u8                    bulk_in_endpointAddr;   /* the address of the bulk in endpoint */
       __u8                    bulk_out_endpointAddr;  /* the address of the bulk out endpoint */
       int                     errors;                 /* the last request tanked */
       bool                    ongoing_read;           /* a read is going on */
       bool                    processed_urb;          /* indicates we haven't processed the urb */
       spinlock_t              err_lock;               /* lock for errors */
       struct kref             kref;
       struct mutex            io_mutex;               /* synchronize I/O with disconnect */
       struct completion       bulk_in_completion;     /* to wait for an ongoing read */
};

#define USB_SKEL_MINOR_BASE     192
static const struct file_operations skel_fops = {
       .owner =        THIS_MODULE,
       .read =         skel_read,
       .write =        skel_write,
       .open =         skel_open,
       .release =      skel_release,
       .flush =        skel_flush,
       .llseek =       noop_llseek,
};

static struct usb_class_driver skel_class = {
       .name =         "skel%d",
       .fops =         &skel_fops,
       .minor_base =   USB_SKEL_MINOR_BASE,
};

static int skel_probe(struct usb_interface *interface,
                     const struct usb_device_id *id)
{
       struct usb_skel *dev;
       struct usb_host_interface *iface_desc;
       struct usb_endpoint_descriptor *endpoint;
       size_t buffer_size;
       int i;
       int retval = -ENOMEM;

       /* allocate memory for our device state and initialize it */
       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
       if (!dev) {
               err("Out of memory");
               goto error;
       }
デバイスの参照カウンタとかリストヘッド初期化します。
       kref_init(&dev->kref);
       sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
       mutex_init(&dev->io_mutex);
       spin_lock_init(&dev->err_lock);
       init_usb_anchor(&dev->submitted);
       init_completion(&dev->bulk_in_completion);
devにinterface->usb_devとinterfaceを設定します。
       dev->udev = usb_get_dev(interface_to_usbdev(interface));
       dev->interface = interface;
ここまでは、各ドライバに依存する内容で、ドライバ本質的な物でありません。
そして以降がエンドポイントに関する本質的な項目となります。

デバイスインターフェースのエンドポイント数で、ドライバのエンドポイントにバンドルします。
この時バッファーサイズ必要ならバッファを確保したりしています。
       iface_desc = interface->cur_altsetting;
       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
               endpoint = &iface_desc->endpoint[i].desc;

               if (!dev->bulk_in_endpointAddr &&
                   usb_endpoint_is_bulk_in(endpoint)) {
読み込み処理でやり取りするチャンネル 
                      /* we found a bulk in endpoint */
                       buffer_size = usb_endpoint_maxp(endpoint);
                       dev->bulk_in_size = buffer_size;
                       dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
                       dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
                       if (!dev->bulk_in_buffer) {
                               err("Could not allocate bulk_in_buffer");
                               goto error;
                       }
                       dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
                       if (!dev->bulk_in_urb) {
                               err("Could not allocate bulk_in_urb");
                               goto error;
                       }
               }

               if (!dev->bulk_out_endpointAddr &&
                   usb_endpoint_is_bulk_out(endpoint)) {
                       /* we found a bulk out endpoint */
書き込み処理でやり取りするチャンネル
                       dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
               }
       }
       if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
               err("Could not find both bulk-in and bulk-out endpoints");
               goto error;
       }

       /* save our data pointer in this interface device */
interface->devにdevをセットします。
       usb_set_intfdata(interface, dev);

       /* we can register the device now, as it is ready */
デバイスファイル等を作成します。ファイルはskel_class.nameで%dとすると、1から順次インクリメントされます。
マイナー番号はminor_baseから空きを検索し、それを割り当てます。
       retval = usb_register_dev(interface, &skel_class);
       if (retval) {
               /* something prevented us from registering this driver */
               err("Not able to get a minor for this device.");
               usb_set_intfdata(interface, NULL);
               goto error;
       }

       /* let the user know what node this device is now attached to */
       dev_info(&interface->dev,
                "USB Skeleton device now attached to USBSkel-%d",
                interface->minor);
       return 0;

error:
       if (dev)
               /* this frees allocated memory */
               kref_put(&dev->kref, skel_delete);
       return retval;
}
skel_disconnect()はデバイスがデタッチされた時にコールされ、ドライバの削除および掛かるバッファー等の解放し、デバイスファイルの削除を行っているだけです。usb_kill_anchored_urbs()は書き込みで待っているとanchorリストを削除する処理です。
static void skel_disconnect(struct usb_interface *interface)
{
       struct usb_skel *dev;
       int minor = interface->minor;

       dev = usb_get_intfdata(interface);
       usb_set_intfdata(interface, NULL);

       /* give back our minor */
       usb_deregister_dev(interface, &skel_class);

       /* prevent more I/O from starting */
       mutex_lock(&dev->io_mutex);
       dev->interface = NULL;
       mutex_unlock(&dev->io_mutex);

       usb_kill_anchored_urbs(&dev->submitted);

       /* decrement our usage count */
       kref_put(&dev->kref, skel_delete);

       dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}
なお、/skel_suspend/skel_resume/skel_pre_reset/skel_post_resetは、ドライバデバイスの参照カウンタの処理とか、ロックを掛けたりするだけの処理です。

デバイスファイルのファイルオペレーションについては次回と言う事で。


最終更新 2013/07/10 17:49:53 - north
(2013/07/10 17:49:53 作成)


検索

アクセス数
3586312
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。