【概要】 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( ¶m, 0, sizeof(param) );
if ( copy_from_user( ¶m, (void*)arg, sizeof(param) ) <= 0 ) {
return -EINVAL;
}
if( param.kind == NOTICE_SET ) {
memcpy( &g_DrvInfo.notice, ¶m, 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);
⇒ 激安コピーブランドバッグ通販 (04/05)
⇒ momo (03/08)
⇒ てる (02/26)
⇒ momo (02/26)