Linuxのキャラクタデバイス - ハードウェア動作(C)

文字デバイスドライバ - 点灯ドライブ

I.はじめに

次のフレームを書き込むために、単純な文字デバイスドライバの前に(A) (B) 次光簡単なハードウェアで動作--led

第二に、回路図

(GPF4開発ボードにLEDL、LED2 GPF5開発ボードに、LED4 GPF6開発ボードへ)

第三に、ドライバー

以前書かれたドライバ(フレームA)のみ操作レジスタの増加にプログラムを、(ここでは記載していない、S3C2440データシートを読み、具体的な説明を登録します)

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h> 
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/uaccess.h>

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

static int myled_open (struct inode * inode, struct file * file){
    /* 配置GPF4,5,6为输出 */
    *gpfcon &= ~((0x3<<(4*2))|(0x3<<(5*2))|(0x3<<(6*2)));
    *gpfcon |= ((0x1<<(4*2))|(0x1<<(5*2))|(0x1<<(6*2)));
    
    /* 如果不想操作寄存器,还可以直接使用linux 提供的接口,使用个函数,就不用寄存器映射了 */

    /*
        s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
        s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
        s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
     */
    return 0;
}


ssize_t myled_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){
    int val;
    /* 内核空间和用户空间传递数据的方法 */
    if(copy_from_user(&val, buf, size)){
        printk("copy failed\n");
        return -EFAULT;
    }
    
    if(val == 1){
        //点灯,将GPF4,5,6拉低
        *gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
        /*也可以使用下面的函数设置gpio口*/
        //s3c2410_gpio_setpin(S3C2410_GPF4, 1);
    }else{
        //灭灯,将GPF4,5,6拉高
        *gpfdat |= ((1<<4)|(1<<5)|(1<<6));
    }
    
    return size;
}

/* file_operations 用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。
 * 该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址。
 */
static struct file_operations myled_fop = {
        .owner      = THIS_MODULE,
    .open        = myled_open,
    .write        = myled_write,
};
static struct cdev *myled_cdev;
static struct class *myled_class;

static dev_t device;


/* 驱动模块的入口函数 */
static int myled_test_init(void){
    /* 映射到0x56000050物理地址 */
    gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
    gpfdat =  gpfcon+1;
    if (device){
        /* 静态申请设备号 */
        register_myleddev_region(device,1,"myled_dev");
    }else{
        /* 动态申请设备号 */
        alloc_myleddev_region(&device,0, 1,"myled_dev");
    }
    
    /* 申请空间 */
    myled_cdev = cdev_alloc();
    /* 注册字符设备驱动 */
    cdev_init(myled_cdev,&myled_fop);
    cdev_add(myled_cdev, device, 1);
    
    /* 创建设备节点 */
    myled_class = class_create(THIS_MODULE, "mydev");
    class_device_create(myled_class, NULL, device, NULL,"mydev");
    return 0;
}

/* 驱动模块的退出函数 */
static void myled_test_exit(void){
    /* 注销映射 */
    iounmap(gpfcon);
    /* 删除设备节点 */
    class_device_destroy(myled_class, device);
    class_destroy(myled_class);
    /* 注销字符设备驱动 */
    cdev_del(myled_cdev);
    /* 释放空间 */
    cdev_put(myled_cdev);
    /* 释放设备号和相应的设备名 */
    unregister_myleddev_region(device, 1);
}

/* 这个宏将 myled_test_init 函数修饰为模块的入口函数 */
module_init(myled_test_init);
/* 这个宏将 myled_test_exit 函数修饰为模块的退出函数 */
module_exit(myled_test_exit);
/* 遵循GPL协议 */
MODULE_LICENSE("GPL");

第四に、Makefileを書きます

#内核源码树路径
KERN_DIR = /work/system/kernel/source/linux-2.6.22.6
#目标文件
obj-m += myled.o

all:
    make -C $(KERN_DIR) M=`pwd` modules
clean:
    make -C $(KERN_DIR) M=`pwd` modules clean

第五に、アプリケーションをテスト

端子に、コマンドラインパラメータをプログラムを点灯するためのテストプログラムを実行する必要があります。たとえば、次のように./led on./led off

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char **argv){
    
    int fd;
    int val = 1;
    fd = open("/dev/mydev",O_RDWR);
    if(fd < 0){
        printf("can't open\n");
        return -1;
    }
    if(argc != 2){
        printf("Usage :\n");
        printf("%s <on|off>\n",argv[0]);
        return 0;
    }
    if(strcmp(argv[1],"on") == 0){
        val = 1;
    }else{
        val = 0;
    }
    write(fd,&val,4);
    return 0;

}

第六に、テストをコンパイル

試験方法及び(フレーム)は、試験方法と同様です。
実験的な現象:
コマンドライン端末で実行./led on3灯の(私の開発ボード用)、開発ボードは、上にある
コマンドライン端末から実行された場合には、実行./led off3つのライトが開発ボードに消滅しているとき。

おすすめ

転載: www.cnblogs.com/gulan-zmc/p/11600207.html