Entry: main  << >>
linuxタイマーデバイスドライバ
JUGEMテーマ:Linux
【概要】 ioctl()で要求することにより、タイマーを起動し、タイムアウトごとにioctl要求元のアプリにsignalで通知するプログラムです。
タイマーは、init_timer()、add_timer()、del_timer()を使用して実装します。

※)2.6.35位の版は、「/devディレクトリ下にデバイスを作成する」も参照して下さい。


#####################################################
/**
 * @file usr_drv.h
 * @brief template of control driver for kernel 2.6
*/
#ifndef USR_DRV_H_
#define USR_DRV_H_

#include <linux/ioctl.h>

/* Version */
#define USR_DRV_VERSION     "01.01.00"

/* status定数 */
#define USR_DRV_NORMAL      0       /* 正常状態 */
#define USR_DRV_ERROR       -1      /* 異常状態 */
#define USR_DRV_NOT_INIT    -2      /* 未初期化状態 */

/* 戻り値用定数 */
#define RET_OK              0       /* 正常終了 */
#define RET_ERR             -1      /* 異常終了 */

/* 通知要求パラメータ */
typedef struct {
    int     kind;                   /* 通知登録(1)/削除(0) */
    int     signo;                  /* シグナル番号     */
    pid_t   pid;                    /* 通知先プロセスID       */
} NOTICE_REQ;
#define NOTICE_SET          1   /* 通知登録 */
#define NOTICE_CLEAR            0   /* 通知クリア */


/*====================================================================*
** IOCTLコマンド定義
**/
/** IOCTLマジックナンバー */
#define USR_DRV_IOC_MAGIC   0xE0

/** Get Status command */
#define USR_DRV_IOCTL_GET_STATUS        _IOWR( USR_DRV_IOC_MAGIC, 0,  int )
#define USR_DRV_IOCTL_SET_NOTICE        _IOWR( USR_DRV_IOC_MAGIC, 1,  int )

/** IOCTLコマンド最大番号 */
#define USR_DRV_IOC_MAXNR   1

#endif /*USR_DRV_H_*/

#####################################################
/**
 * @file usr_drv.c
 * @brief template of control driver for kernel 2.6
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
#include <linux/sysfs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/sched.h>        // kill_proc

#include "usr_drv.h"

/*====================================================================*
** for debug print macro
**/
#define DEBUG_ON    /* debug */
#ifdef DEBUG_ON
#define DBGPRINT(fmt, args...) printk( KERN_DEBUG DEV_NAME" :" fmt, ## args)
#else
#define DBGPRINT(fmt, args...)
#endif


/*====================================================================*
** device macro
**/
#define DEV_MAJOR_DEFAULT   0           /** major number(auto numbering) */
#define DEV_MINOR_NO        0           /** minor number */
#define DEV_NAME            "usr_drv"   /** device name */
#define DEV_NUM             2           /** number of devices */


/*====================================================================*
** date structure
**/
/** driver infomation */
typedef struct drv_info {
    int     status;             /** driver status */
    int     majorNo;            /** major number */
    struct class    *pClass;    /** driver class */
    NOTICE_REQ      notice;     /** notice request info */
} DRV_INFO;

/** device descriptor */
typedef struct dev_desc {
    // 制御に必要な情報はディスクリプタに持たせる
    int     value;
    // 排他制御子、cdevは基本情報
    struct semaphore    semDev; /** access lock */
    struct cdev cdev;           /** character device's structure */
} DEV_DESC;

static DRV_INFO     g_DrvInfo;
static DEV_DESC     g_DevDesc[DEV_NUM];

/** 周期タイマー */
static struct timer_list    g_timer;
#define TIMER_CNT           100     /** 周期タイマー値(×tick単位時間) */

/* version */
static char g_usr_drv_version[] = USR_DRV_VERSION;


/*====================================================================*
** prototype
**/
static int usr_drvModuleOpen( struct inode *inode, struct file *filp );
static int usr_drvModuleRelease( struct inode *inode, struct file *filp );
static int usr_drvModuleIoctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
static int usr_drvFuncGetStatus( DEV_DESC *p_desc, unsigned long arg );
static int usr_drvFuncSetNotice( DEV_DESC *p_desc, unsigned long arg );
static void usr_drvFuncInterrupt( unsigned long data );

static int usr_drvModuleResume( struct device *dev );
static int usr_drvModuleSuspend( struct device *dev, pm_message_t state );


/** file operation */
struct file_operations  g_FileOps = {
    .owner      = THIS_MODULE,
    .ioctl      = usr_drvModuleIoctl,
    .open       = usr_drvModuleOpen,
    .release    = usr_drvModuleRelease,
};

/** バス構造体定義 */
struct bus_type g_BusType = {
    .name       = DEV_NAME,
    .suspend    = usr_drvModuleSuspend,
    .resume = usr_drvModuleResume,
};

/** デバイス構造体定義 */
struct device g_Device = {
    .parent     = NULL,
    .bus_id = DEV_NAME,
    .bus        = &g_BusType,
    .release    = NULL,
};

/**
 * @brief Initialize of driver
*/
static void usr_drv_init( void )
{
    g_DevDesc[0].value = 0;
    g_DevDesc[1].value = 0;

    /* 周期タイマー初期化 */
    init_timer( &g_timer );
    g_timer.data    = 0;
    g_timer.expires = 0;
    g_timer.function = usr_drvFuncInterrupt;
}

/**
 * @brief ドライバ初期化処理
 * @retval 0 正常終了
 * @note insmodによって実行される。
*/
int usr_drvModuleInit(void)
{
    int     result;
    dev_t  dev_t = 0;
    DEV_DESC    *p_dev_desc;
    int     devno;
    int     num;

    DBGPRINT( "%s entry¥n", __FUNCTION__ );

    g_DrvInfo.status = USR_DRV_NOT_INIT;        // sets initial state

    /** デバイス番号及びメジャー番号を動的に割当てる */
    result = alloc_chrdev_region( &dev_t, DEV_MINOR_NO, DEV_NUM, DEV_NAME );

    g_DrvInfo.majorNo = MAJOR(dev_t);

    if ( result < 0 ) {
        printk( KERN_ERR "%s: can't get major %d, result %d¥n", DEV_NAME,
                g_DrvInfo.majorNo, result );
        g_DrvInfo.status = USR_DRV_ERROR;   // sets error state
        return result;
    }
    DBGPRINT( "Register %s into major %d¥n", DEV_NAME, g_DrvInfo.majorNo );

    for ( num = 0; num < DEV_NUM; num++ ) {
        p_dev_desc = &g_DevDesc[num];
        p_dev_desc->value = 0;

        init_MUTEX( &p_dev_desc->semDev );

        devno = MKDEV(g_DrvInfo.majorNo, DEV_MINOR_NO + num);
        cdev_init( &p_dev_desc->cdev, &g_FileOps );

        p_dev_desc->cdev.owner = THIS_MODULE;
        p_dev_desc->cdev.ops   = &g_FileOps;
        result = cdev_add( &p_dev_desc->cdev, devno, 1 );
        if( result < 0 ) {
            printk( KERN_ERR "%s%d: can't append device, result %d¥n", DEV_NAME, num,
                    result );
            g_DrvInfo.status = USR_DRV_ERROR;   // sets error state
            return result;
        }
        DBGPRINT( "Registered %s%d successfully¥n", DEV_NAME, num );
    }

    /* ドライバ内部データの初期化処理 */
    usr_drv_init();

    /** /devディレクトリにデバイスを生成する */
    g_DrvInfo.pClass = class_create( THIS_MODULE, DEV_NAME );
    for ( num = 0; num < DEV_NUM; num++ ) {
        devno = MKDEV(g_DrvInfo.majorNo, DEV_MINOR_NO + num);
        class_device_create( g_DrvInfo.pClass, NULL, devno, NULL, DEV_NAME"%d", num );
    }

    /* suspend,resume登録 */
    if( bus_register(&g_BusType) ) {
        printk( KERN_ERR "%s: failed to bus_register¥n", DEV_NAME);
        return -ENODEV;
    }
    if( device_register(&g_Device) ) {
        printk( KERN_ERR "%s: failed to device_register¥n", DEV_NAME);
        bus_unregister(&g_BusType);
        return -ENODEV;
    }

    g_DrvInfo.status = USR_DRV_NORMAL;  // sets ok state

    DBGPRINT( "%s exit¥n", __FUNCTION__ );
    return 0;
}

/**
 * @brief End process of driver
*/
static void usr_drv_cleanup( void )
{
    /* 周期タイマー削除 */
    del_timer( &g_timer );
}

/**
 * @brief ドライバ終了処理
 * @note rmmodによって実行される。
*/
void usr_drvModuleExit(void)
{
    int     num;
    dev_t   devno;

    DBGPRINT( "%s entry¥n", __FUNCTION__ );

    if ( USR_DRV_NORMAL == g_DrvInfo.status) {

        class_destroy( g_DrvInfo.pClass );

        devno = MKDEV( g_DrvInfo.majorNo, DEV_MINOR_NO );
        DBGPRINT( "Unregister %s devno %d¥n", DEV_NAME, devno );

        for ( num = 0; num < DEV_NUM; num++ ) {
            cdev_del( &g_DevDesc[num].cdev );
        }

        /* ドライバ内部データの後始末 */
        usr_drv_cleanup();

        /* suspen,resume登録抹消 */
        bus_unregister(&g_BusType);
        device_unregister(&g_Device);

        unregister_chrdev_region( devno, DEV_NUM );

        g_DrvInfo.status = USR_DRV_NOT_INIT;        /** 状態を未初期化にセット */
    } else {
        printk( KERN_ERR "%s: not registered!¥n", DEV_NAME );
    }
    DBGPRINT( "%s exit¥n", __FUNCTION__ );
}

/**
 * @brief ドライバOPEN処理
 * @param inode  inode構造体
 * @param filp file構造体
 * @retval 0 正常終了
*/
static int usr_drvModuleOpen( struct inode* inode, struct file* p_file )
{
    DEV_DESC* p_dev = NULL; /* デバイス情報 */

    DBGPRINT( "Open %s driver¥n", DEV_NAME );

    /** p_file->private_dataにディスクリプタを割当る */
    p_dev = container_of( inode->i_cdev, DEV_DESC, cdev );
    p_file->private_data = p_dev;

    return 0;
}

/**
 * @brief ドライバRELEASE処理
 * @param inode  inode構造体
 * @param p_file file構造体
 * @retval 0 正常終了
*/
static int usr_drvModuleRelease( struct inode* inode, struct file* p_file )
{
    DBGPRINT( "Close %s driver¥n", DEV_NAME );
    return 0;
}

/**
 * @brief ドライバIO処理
 * @param inode  inode構造体
 * @param p_file file構造体
 * @param cmd  コマンド
 * @param arg  パラメータ
 * @retval 0 正常終了
 * @retval -EINVAL
 * @retval -ENOTTY
 * @retval -ERESTARTSYS
*/
static int usr_drvModuleIoctl( struct inode* inode, struct file* p_file, unsigned int cmd,
                                    unsigned long arg )
{
    int retval = 0;
    DEV_DESC* p_dev;        /* デバイス情報 */

    DBGPRINT( "Ioctl %s driver¥n", DEV_NAME );

    p_dev = (DEV_DESC*)p_file->private_data;        /** デバイス情報を抽出 */

    /** マジックナンバーチェック */
    if ( _IOC_TYPE(cmd) != USR_DRV_IOC_MAGIC ) {
        printk( KERN_ERR "%s: MAGIC NUM error¥n", DEV_NAME );
        return -ENOTTY;
    }
    /** コマンドナンバーチェック */
    if ( _IOC_NR(cmd) > USR_DRV_IOC_MAXNR ) {
        printk( KERN_ERR "%s: COMMAND NUM error¥n", DEV_NAME );
        return -ENOTTY;
    }

    /** 同時アクセス排他開始 */
    if ( down_interruptible( &p_dev->semDev ) ) {
        printk( KERN_ERR "%s: down_interruptible error¥n", DEV_NAME );
        return -ERESTARTSYS;
    }

    /** コマンド別処理 */
    switch( cmd )
    {
      case USR_DRV_IOCTL_GET_STATUS:
        retval = usr_drvFuncGetStatus( p_dev, arg );
        break;

      case USR_DRV_IOCTL_SET_NOTICE:
        retval = usr_drvFuncSetNotice( p_dev, arg );
        break;

      default:
        break;
    }

    /** 同時アクセス排他終了 */
    up ( &p_dev->semDev );

    DBGPRINT( "%s exit¥n", __FUNCTION__ );
    return retval;
}

/**
 * @brief ステータス取得
 * @param p_desc デバイスディスクリプタ
 * @param arg  アプリが用意した領域(変数)へのアドレス
 * @retval 0 正常終了
 * @retval -EINVAL
*/
static int usr_drvFuncGetStatus( DEV_DESC *p_desc, unsigned long arg )
{
    int     *p_arg;

    DBGPRINT( "%s entry¥n", __FUNCTION__ );

    p_arg = (int*)arg;
    if ( NULL == p_arg ) {
        printk( KERN_ERR "%s: NULL parameter¥n", DEV_NAME );
        return -EINVAL;
    }

    /** アプリ側に値を返す */
    put_user( p_desc->value, p_arg );

    return 0;
}

/**
 * @brief 通知要求の設定
 * @param p_desc デバイスディスクリプタ
 * @param arg  アプリが用意した領域(変数)へのアドレス
 * @retval 0 正常終了
 * @retval -EINVAL
*/
static int usr_drvFuncSetNotice( DEV_DESC *p_desc, unsigned long arg )
{
    NOTICE_REQ      param;

    DBGPRINT( "%s entry¥n", __FUNCTION__ );

    if ( NULL == (void*)arg ) {
        printk( KERN_ERR "%s: NULL parameter¥n", DEV_NAME );
        return -EINVAL;
    }

    memset( &param, 0, sizeof(param) );
    if ( copy_from_user( &param, (void*)arg, sizeof(param) ) <= 0 ) {
        return -EINVAL;
    }

    if( param.kind == NOTICE_SET ) {
        memcpy( &g_DrvInfo.notice, &param, sizeof(g_DrvInfo.notice) );
        if( g_timer.expires == 0 ){
            /* タイマ登録 */
            g_timer.expires = jiffies + TIMER_CNT;
            add_timer( &g_timer );
        }
    } else {
        memset( &g_DrvInfo.notice, 0, sizeof(g_DrvInfo.notice) );
    }

    return 0;
}

/**
 * @brief タイマー割り込み処理
 * @param data  前回のadd_timer()時のjiffies値
*/
static void usr_drvFuncInterrupt( unsigned long data )
{
    int     notice = 0;
    int     i;

    /** 監視対象に変化があればシグナル通知する */
    for( i=0; i < 5; i++ ){
        ;
        notice = 1;
    }

    if( notice ) {
        if( g_DrvInfo.notice.signo != 0 ){
            /* シグナルが設定されていたらシグナル送信 */
            kill_proc( g_DrvInfo.notice.pid, g_DrvInfo.notice.signo, 1 );
        }
    }

    /* タイマ再登録 */
    g_timer.expires = jiffies + TIMER_CNT;
    add_timer( &g_timer );
}

/**
 * @brief ドライバRESUME処理
 * @param in  device構造体
 * @retval 0 正常終了
*/
static int usr_drvModuleResume( struct device *dev )
{
    DBGPRINT( "Resume %s driver¥n", DEV_NAME );

    usr_drv_init();

    g_DrvInfo.status = USR_DRV_NORMAL;  // sets ok state
    return 0;
}

/**
 * @brief ドライバSUSPEND処理
 * @param in  device構造体
 * @param in  PM状態
 * @retval 0 正常終了
*/
static int usr_drvModuleSuspend( struct device *dev, pm_message_t state )
{
    DBGPRINT( "Suspend %s driver¥n", DEV_NAME );

    usr_drv_cleanup();

    return 0;
}

module_init(usr_drvModuleInit);
module_exit(usr_drvModuleExit);
EXPORT_SYMBOL( g_usr_drv_version );
MODULE_DESCRIPTION("General character device driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(USR_DRV_VERSION);

| てる | 00:26 | comments(0) | trackbacks(0) | - | - |
Comment








Trackback

Calendar

   1234
567891011
12131415161718
19202122232425
2627282930  
<< April 2015 >>

Category

Profile

初音ミク
初音ミク

ドメイン取得なら

月額定額 980円 SIM

NTTコミュニケーションズ OCN モバイル エントリー d LTE 980 マイクロSIMパッケージ T0003352

¥3,150

アフリエイトするなら

ブログ・サイト公開なら

強制的な広告が殆ど無い
レンタルサーバーです
★☆★ レンタルサーバー!ロリポップ! ★☆★
☆月額105円〜容量最大30GB!Movable TypeやWordpressの簡単インストール付き!☆

Amazonから購入

STM32マイコン徹底入門 (TECH I Processor)

CPUの創りかた

Archives

Search

Entry

Comment

Link

Feed

Others

無料ブログ作成サービス JUGEM

Mobile

qrcode
出会い