Bluetooth詳説
Bluetoothプロトコルの解説とライブラリ開発
ホーム  :  検索  :  記事一覧  :  RSSフィード  :  リンク  :  ダウンロード  
 2010年09月 5日(日曜日) 20:29 JST

キャラクタ型USBドライバの実装(openとcloseだけ)

   
開発

1週間に10時間の予定はいったいどこへやら・・・。
別件で仕事が入った上に最近疲れが溜ってきたので、今月はお休みにします。

とりあえず2週間程前にキャラクタ型USBドライバの形だけ作ってみたので、それだけアップします。
openとcloseだけを実装したドライバです。writeとreadは中身空っぽ。
説明も9割がた出来てたんですけど、完成は今日になってしまいました。

>> 続く



はじめに

このページの最後に全ソースコードを掲載します。
先に各部を順に解説していきます。

追加したソースコードはカーネルソース中のusb-skeletonドライバの内容を参考にしています。
usb-skeletonには複数のプロセスから利用される場合を想定して、リファレンスカウントを使ったりセマフォによる排他制御を利用していますが、それらは削除しました。

USBデバイスの登録に利用するデータ

/* デバイス管理に必要な情報を集めた構造体 */
struct bt_usb {
    struct usb_device *udev;

    unsigned char* bulk_in_buffer;     /* 受信データバッファ */
    size_t bulk_in_size;               /* 受信バッファのサイズ */
    __u8 bulk_in_endpointAddr;          /* バルクINエンドポイントのアドレス */
    __u8 bulk_out_endpointAddr;         /* バルクOURエンドポイントのアドレス */
};

/* キャラクタ型ドライバの操作関数登録に使う構造体 */
static struct file_operations bt_usb_fops = {
    .owner   = THIS_MODULE,
    .read    = bt_usb_read,
    .write   = bt_usb_write,
    .open    = bt_usb_open,
    .release = bt_usb_release,
};

/* usb coreからマイナー番号を取得してデバイスを登録するためのUSBクラスドライバ情報 */
static struct usb_class_driver bt_usb_class = {
    .name       = "bt_usb%d",
    .fops       = &bt_usb_fops,
    .minor_base = BT_USB_MINOR_BASE,
};

file_operations構造体は、open/close/write/readの関数を指定するのに利用します。
usb_class_driver構造体には上記file_operations構造体へのポインタと、デバイスファイル名、マイナー番号の開始値を指定します。
このusb_class_driverはusb_register_dev関数にてUSBコアシステムに登録します。

bt_usb構造体はデバイス管理に利用するデータを集めたユーザー定義の構造体です。
usb_set_intfdata関数でインターフェースに関連づけると、後でusb_get_intfdata関数で取得することができます。
一応、後で通信に必要になるだろう情報を内部に格納していますが、まだwriteもreadも実装していないので、これらの情報は利用していません。
HCIコマンド送信に利用するControl転送や、HCIイベント受信に利用するInterrupt(IN)転送のこともまだ考えていないので、今後内部情報は変更することになるでしょう。

probe/disconnect

init/exitは前回と同様です。
probeで行っている処理は、

  1. デバイスの管理に必要なデータ(bt_usb構造体)を保存するメモリを確保する
  2. HCIイベント用のInterrupt(IN)エンドポイントとACLデータ用のBulk(IN/OUT)エンドポイントを探して、後ほど利用するための情報を取得する
  3. USBインターフェースにデバイス管理データを関連づける
  4. USBサブシステムにデバイスを登録する

disconnectでは、probeで確保したメモリを開放します。

open/close

キャラクタ型ドライバのopen/close/write/read関数は、openとcloseだけ実装し、write/readはまだ空っぽになっています。

openではファイルのプライベートデータにデバイス管理オブジェクトを保存しています。
また受信バッファの確保を行っています。(readを実装していないので使っていませんが)

closeでは受信バッファの開放を行っています。

動作確認

#include <stdio.h>
#include <fcntl.h>

int main()
{
    int fd = open("/dev/bt_usb0", O_RDWR);
    if (fd < 0) {
        fprintf(stderr, "Could not open device.n");
        return -1;
    }

    close(fd);

    return 0;
}

こんなプログラムを作ってopen/closeを呼び出してみました。

/home/keigo/work/bluetooth/blacktooth/drivers/bt_usb/bt_usb.c: Bluetooth USB driver ver 0.1
usbcore: registered new driver bt_usb
usb 4-2: new full speed USB device using uhci_hcd and address 3
usb 4-2: configuration #1 chosen from 1 choice
/home/keigo/work/bluetooth/blacktooth/drivers/bt_usb/bt_usb.c: USB Bluetooth device now attached to BT_USB-192
/home/keigo/work/bluetooth/blacktooth/drivers/bt_usb/bt_usb.c: Could not find necessary endpoints
bt_usb: probe of 4-2:1.1 failed with error -12
/home/keigo/work/bluetooth/blacktooth/drivers/bt_usb/bt_usb.c: bt_usb_open
/home/keigo/work/bluetooth/blacktooth/drivers/bt_usb/bt_usb.c: bt_usb_release
usb 4-2: USB disconnect, address 3
/home/keigo/work/bluetooth/blacktooth/drivers/bt_usb/bt_usb.c: Bluetooth USB driver now disconnected

ちゃんとopen/releaseが呼ばれています。

また、"Could not find necessary endpoints"というエラー表示が見えます。
やはり2つ目のインターフェース(SCO通信用)でもprobeが呼ばれているようです。

今後の予定

ACLデータの送受信はL2CAPの実装まで行わないので、先にHCIの実装に必要なHCIコマンド/イベント通信を実装します。
つまりControl転送とInterrupt転送処理ですね。

L2CAPの実装前までwrite/read関数はこのまま空っぽにしておきます。

全ソースコード

/*
 * HCI USB driver for Bluetooth protocol stack (Blacktooth Project)
 *
 * Copyright (C) 2007 Lily <bt@k5-n.com>
 * This driver was written referring to "drivers/usb/usb-skeleton.c"
 * in Linux kernel soruce.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY 
 * CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
 * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
 * SOFTWARE IS DISCLAIMED.
 */

#include <linux/usb.h>

/* ドライバのバージョン */
#define VERSION "0.1"

/* このドライバがサポートするデバイスのベンダーIDとプロダクトID */
#define VENDOR_ID   0x0a12
#define PRODUCT_ID  0x0001

/* デバイスに割り当てるマイナー番号の範囲 */
#define BT_USB_MINOR_BASE   192

/*
 * 関数宣言
 */

/* USBドライバ関係の関数 */
static int bt_usb_probe(struct usb_interface *iface, const struct usb_device_id *id);
static void bt_usb_disconnect(struct usb_interface *iface);
static int __init bt_usb_init(void);
static void __exit bt_usb_exit(void);

/* キャラクタ型ドライバ関係の関数 */
static int bt_usb_open(struct inode *inode, struct file *file);
static int bt_usb_release(struct inode *inode, struct file *file);
static ssize_t bt_usb_read(struct file *file, char *buffer, size_t count, loff_t *ppos);
static ssize_t bt_usb_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos);

/*
 * データ定義
 */

/* このドライバを利用する機器のリスト */
static struct usb_device_id bt_id_table[] = {
        { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
        { },
};
MODULE_DEVICE_TABLE (usb, bt_id_table);

/* USBドライバ構造体 */
static struct usb_driver bt_usb_driver = {
    .name       = "bt_usb",
    .probe      = bt_usb_probe,
    .disconnect = bt_usb_disconnect,
    .id_table   = bt_id_table,
};

/* デバイス管理に必要な情報を集めた構造体 */
struct bt_usb {
    struct usb_device *udev;

    unsigned char* bulk_in_buffer;     /* 受信データバッファ */
    size_t bulk_in_size;               /* 受信バッファのサイズ */
    __u8 bulk_in_endpointAddr;          /* バルクINエンドポイントのアドレス */
    __u8 bulk_out_endpointAddr;         /* バルクOURエンドポイントのアドレス */
};

/* キャラクタ型ドライバの操作関数登録に使う構造体 */
static struct file_operations bt_usb_fops = {
    .owner   = THIS_MODULE,
    .read    = bt_usb_read,
    .write   = bt_usb_write,
    .open    = bt_usb_open,
    .release = bt_usb_release,
};

/* usb coreからマイナー番号を取得してデバイスを登録するためのUSBクラスドライバ情報 */
static struct usb_class_driver bt_usb_class = {
    .name       = "bt_usb%d",
    .fops       = &bt_usb_fops,
    .minor_base = BT_USB_MINOR_BASE,
};

/*
 * 関数定義
 */

/* デバイスをオープンすると呼ばれる */
static int bt_usb_open(struct inode *inode, struct file *file)
{
    struct bt_usb *dev;
    struct usb_interface *iface;
    int subminor;
    int retval = 0;

    info("bt_usb_open");

    subminor = iminor(inode);

    /* USBインターフェースを取得する */
    iface = usb_find_interface(&bt_usb_driver, subminor);
    if (!iface) {
        err ("%s - error, can't find device for minor %d",
             __FUNCTION__, subminor);
        retval = -ENODEV;
        goto exit;
    }

    /* USBインターフェースに関連づけられているデバイス管理オブジェクトを取得する */
    dev = usb_get_intfdata(iface);
    if (!dev) {
        err("Could not get bt_usb data.");
        retval = -ENODEV;
        goto exit;
    }

    /* ファイルのプライベートデータにデバイス管理オブジェクトを保存しておく */
    file->private_data = dev;

    /* 受信バッファを確保する */
    dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
    if (!dev->bulk_in_buffer) {
        err("Could not allocate bulk_in_buffer");
        retval = -ENOMEM;
        goto exit;
    }

exit:
    return retval;
}

/* デバイスがクローズされると呼ばれる */
static int bt_usb_release(struct inode *inode, struct file *file)
{
    struct bt_usb *dev;

    info("bt_usb_release");

    /* ファイルのプライベートデータからデバイス管理オブジェクトを取り出す */
    dev = (struct bt_usb*)file->private_data;
    if (dev == NULL) {
        return -ENODEV;
    }

    /* 受信バッファを開放する */
    kfree(dev->bulk_in_buffer);

    return 0;
}

/* 読み出されたときに呼ばれる */
static ssize_t bt_usb_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
    info("The bt_usb_read function has not been implemented yet.");

    return 0;
}

/* 書き込まれたときに呼ばれる */
static ssize_t bt_usb_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)
{
    info("The bt_usb_write function has not been implemented yet.");

    return 0;
}

/* デバイスが接続されるたびに呼び出される */
static int bt_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
    struct bt_usb *dev = NULL;
    struct usb_host_interface *iface_desc;
    int i;
    int retval = -ENOMEM;

    /* デバイス管理オブジェクト(bt_usb構造体)にメモリ割当て */
    dev = kzalloc(sizeof(struct bt_usb), GFP_KERNEL);
    if (dev == NULL) {
        err("Could not allocate memory for the bt_usb structure");
        goto error;
    }
    dev->bulk_in_buffer = NULL;

    /* USBデバイス構造体へのポインタを取得 */
    dev->udev = interface_to_usbdev(iface);

    /* ACLデータ通信のために、必要なエンドポイントを探す */
    iface_desc = iface->cur_altsetting; /* 現在選択されている代替設定 */
    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        struct usb_endpoint_descriptor *ep_desc = &iface_desc->endpoint[i].desc;

        /* ディスクリプタの属性を調べる */
        switch (ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
        case USB_ENDPOINT_XFER_INT:
            /* Interrupt転送 */
            if (ep_desc->bEndpointAddress & USB_DIR_IN) {
                // TODO 必要な情報をdevに保存する
            }
            break;
        case USB_ENDPOINT_XFER_BULK:
            /* Bulk転送 */
            if (ep_desc->bEndpointAddress & USB_DIR_IN) { /* Bulk Input */
                /* エンドポイントアドレスとバッファサイズを保存 */
                dev->bulk_in_size = le16_to_cpu(ep_desc->wMaxPacketSize);
                dev->bulk_in_endpointAddr = ep_desc->bEndpointAddress;
            } else { /* Bulk Output */
                /* エンドポイントアドレスの保存 */
                dev->bulk_out_endpointAddr = ep_desc->bEndpointAddress;
            }
            break;
        default:
            /* DO NOTHING */
            break;
        }
    }

    /* 必要なエンドポイントが見つかったかどうかチェック */
    if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
        err("Could not find necessary endpoints");
        goto error;
    }

    /* このインターフェースにデバイス管理オブジェクトへのポインタを保存する */
    usb_set_intfdata(iface, dev);

    /* USBサブシステムにデバイスを登録する */
    retval = usb_register_dev(iface, &bt_usb_class);
    if (retval) {
        /* 何かがこのドライバを妨害している */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(iface, NULL);
        goto error;
    }

    /* ユーザーにデバイスが接続されたノード番号を通知する */
    info("USB Bluetooth device now attached to BT_USB-%d", iface->minor);

    return 0;

error:
    /* エラー前に確保したメモリを開放する */
    if (dev != NULL) {
        kfree(dev);
    }

    return retval;
}

/* デバイスが取り外されるたびに呼び出される */
static void bt_usb_disconnect(struct usb_interface *iface)
{
    struct bt_usb *dev = usb_get_intfdata(iface);

    /* インターフェースに関連付けらたオブジェクトを解除 */
    usb_set_intfdata(iface, NULL);
    /* USBサブシステムから削除してマイナー番号を戻す */
    usb_deregister_dev(iface, &bt_usb_class);
    /* デバイス管理オブジェクトを開放 */
    kfree(dev);

    info("Bluetooth USB driver now disconnected");
}

/* ロードされた時にモジュールを初期化する */
static int __init bt_usb_init(void)
{
    int ret;

    info("Bluetooth USB driver ver %s", VERSION);

    ret = usb_register(&bt_usb_driver);
    if (ret < 0) {
        err("usb_register failed. Error Number = %d", ret);
    }

    return ret;
}

/* モジュールを削除する直前に呼ばれる */
static void __exit bt_usb_exit(void)
{
    usb_deregister(&bt_usb_driver);
    info("Bluetooth USB Driver now deregistered");
}

/*
 * ドライバ情報の登録
 */

/* 初期化関数、終了関数の登録 */
module_init(bt_usb_init);
module_exit(bt_usb_exit);

MODULE_AUTHOR("Lily <bt@k5-n.com>");
MODULE_DESCRIPTION("Blacktooth Project Bluetooth USB driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
 

関連情報

記事のオプション

トラックバック

このエントリのトラックバックURL: http://bluetooth.k5-n.com/trackback.php?id=20070206231205997

キャラクタ型USBドライバの実装(openとcloseだけ) | 0 件のコメント | アカウントの作成
コメントは投稿者の責任においてなされるものであり、サイト管理者は責任を負いません。
 Copyright © 2010 Bluetooth詳説
 本ページのすべての商標と著作権はそれぞれの所有者に帰属します。
Powered By Geeklog & Geeklog Japanese
ページ作成時間: 0.24 秒