简单字符设备驱动程序

理论知识参考Linux Device Driver, 3rd Edition。实验中使用主动分配主设备号,按照LDD3中说的最好是自动分配设备号,这里只是为了理解。

实验步骤如下:

(1)使用cat/proc/device查看字符设备主设备号,这里假设50主设备号没有使用而在本设备中使用。

     创建字符设备文件节点:mknod/dev/mycdev c 50 0

     修改设备文件权限:chmod 777/dev/mycdev

     其中的mycdev为步骤(2)中要安装的模块名。

(2)编写驱动程序:注意代码中 #define MYCDEV_MAJOR (50)

/* =====================================================================

 *       Filename:  mycdev.c

 *

 *    Description: 

 *

 *        Version:  1.0 (02/26/2013)

 *        Created:  xhzuoxin([email protected])

 *       Compiler:  gcc

 *======================================================================

 */

#include<linux/init.h>

#include<linux/module.h>

#include<linux/types.h>

#include<linux/fs.h>

#include<linux/mm.h>

#include<linux/sched.h>

#include<linux/cdev.h>

#include<asm/system.h>

#include<asm/io.h>

#include<asm/uaccess.h>

#include<linux/kernel.h>

#include<linux/errno.h>

#include<linux/slab.h>

 

MODULE_LICENSE("GPL");

 

#defineMYCDEV_MAJOR               (50)

#defineMYCDEV_SIZE                0x1000

 

staticdev_t mycdev_major = MYCDEV_MAJOR;

 

structmycdev {

    struct cdev cdev;

    unsigned charmem[255];

};

//struct mycdev dev;

structmycdev *devp;

 

 

static int mycdev_open(structinode *inode, struct file *fp)

{

    fp->private_data= devp;

 

    return 0;

}

 

static int mycdev_release(structinode *inode, struct file *fp)

{

    return 0;

}

 

staticssize_t mycdev_read(struct file *fp, char __user *buf, size_t size, loff_t *pos)

{

    unsigned long p =*pos;

    unsigned int count =size;

    struct mycdev *tmp_dev = fp->private_data;

 

    if (p >= MYCDEV_SIZE) {

       return -1;

    }

    if (count > MYCDEV_SIZE - p) {

       count =MYCDEV_SIZE - p;

    }

    if (copy_to_user(buf, (void*)(tmp_dev->mem + p), count) != 0) {

       printk("read error!\n");

       return -1;

    } else {

       *pos +=count;

       printk(KERN_INFO"read %d bytes from %ld\n", count, p);

    }

 

    return count;

}

 

staticssize_t mycdev_write(struct file *fp, const char __user*buf, size_t size, loff_t *pos)

{

    unsigned long p =*pos;

    unsigned int count =size;

    struct mycdev *tmp_dev = fp->private_data;

 

    if (p > MYCDEV_SIZE) {

       return -1;

    }

    if (p > MYCDEV_SIZE - count) {

       count =MYCDEV_SIZE - p;

    }

    if (copy_from_user((void*)(tmp_dev->mem + p), buf, count) != 0) {

       return -1;

    } else {

       *pos +=count;

       printk(KERN_INFO"write %d bytes from %ld\n", count, p);

    }

 

    return count;

}

 

staticloff_t mycdev_llseek(struct file *fp, loff_toff, int whence)

{

//  structmycdev *dev = fp->private_data;

    loff_tnew_pos = 0;

 

    switch(whence) {

    case SEEK_SET:

       new_pos= off;

       break;

    case SEEK_CUR:

       new_pos= fp->f_pos + off;

       break;

    case SEEK_END:

       new_pos= MYCDEV_SIZE + off;

    }

 

    if (new_pos < 0) {

       return -EINVAL;

    } else {

       fp->f_pos= new_pos;

       return new_pos;

    }

}

 

/* paddingg struct file operation */

static const structfile_operations mycdev_fops = {

    .owner =THIS_MODULE,

    .read =mycdev_read,

    .write =mycdev_write,

    .open =mycdev_open,

    .release =mycdev_release,

    .llseek =mycdev_llseek,

};

 

static void setup_mycdev(structmycdev *dev, int index)

{

    int ret;

    int devno = MKDEV(mycdev_major, index);

 

    cdev_init(&dev->cdev,&mycdev_fops);

    dev->cdev.owner= THIS_MODULE;

    dev->cdev.ops= &mycdev_fops;

    ret =cdev_add(&dev->cdev, devno, 1);

 

    if (ret) {

       printk("adding mycdev error!\n");

    }

}

 

static int __init mycdev_init(void)

{

    int ret;

    dev_tdevno = 0;

   

    if (mycdev_major) { /* 静态分配*/

       devno =MKDEV(mycdev_major, 0);

       ret =register_chrdev_region(devno, 1, "mycdev");

    } else {  /* 动态分配 */

       ret =alloc_chrdev_region(&devno, 0, 1, "mycdev");

       mycdev_major= MAJOR(devno);

    }

    devp =kmalloc(sizeof(structmycdev), GFP_KERNEL);

    if (!devp) {

       ret =-ENOMEM;

       unregister_chrdev_region(devno,1);

       return ret;

    }

    memset(devp,0, sizeof(structmycdev));

 

    setup_mycdev(devp,0);

 

    return 0;

}

 

static void __exit mycdev_exit(void)

{

    printk("mycdev module is leaving...\n");

    cdev_del(&devp->cdev);

    kfree(devp);

    unregister_chrdev_region(MKDEV(mycdev_major,0), 1);

}

 

module_init(mycdev_init);

module_exit(mycdev_exit);

此次测试分别在x86平台和Tiny6410平台,Tiny6410平台下的Makefile如下:

ARCH=arm

COMPILE=arm-linux-

 

ifneq ($(KERNELRELEASE),)

obj-m:=mycdev.o

else

# 已经构建好(执行过:make zImage)的Tiny6410内核树目录

KDIR ?= /mnt/HappyStudy/Tiny6410/linux-2.6.38

PWD := $(shell pwd)

endif

 

all:

    make -C$(KDIR) M=$(PWD) modules ARCH=$(ARCH)  CROSS_COMPILE=$(COMPILE)

clean:

    rm -f *.ko*.o *.mod.o *.od.c *.symvers

x86平台下的Makefile如下:

ifneq ($(KERNELRELEASE),)

obj-m:=mycdev.o

 

else

KDIR ?= /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

endif

 

all:

    make -C$(KDIR) M=$(PWD) modules

CROSS_COMPILE=$(COMPILE)

clean:

    rm -f *.ko*.o *.mod.o *.od.c *.symvers

在PC机上make,通过NFS共享或ftp下载到Tiny6410开发板上执行insmodmycdev.ko(超级终端输入)。

注:为了能使用交叉编译,必须在PC机上配置好Tiny6410对应操作系统的内核树。内核树的配置过程如下:

—— 进入源代码目录下

—— cp config_mini6410_s70 .config, makemenuconfig

—— make zImage

—— make modules

我们PC上使用的CentOS环境已经将源码树配置好了,Linux源码在/usr/src目录下,已编译的模块在/lib/modules目录下,这就是在编写PC上驱动程序不用配置而编写嵌入式驱动需要配置内核树的原因。

(3)编写用户态的测试程序如下:

/*

 *=======================================================================

 *       Filename:  usr_test.c

 *

 *    Description: 

 *

 *        Version:  1.0 (02/26/2013)

 *        Created:  xhzuoxin([email protected])

 *       Compiler:  gcc

 *=======================================================================

 */

#include<stdio.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<stdlib.h>

 

intmain(void)

{

    int testdev;

    int n, i, ret;

    char write_buf[] = "xiahouzuoxin";

    char buf[12];

 

    testdev =open("/dev/mycdev", O_RDWR);

 

    if (testdev == -1) {

       printf("cannot open file.\n");

       exit(1);

    }

 

    n = sizeof(write_buf)/sizeof(char);

    if (ret = write(testdev, write_buf, n) < n) {

       printf("write error!\n");

       exit(1);

    }

 

//  close(testdev);

//  testdev =open("/dev/mycdev", O_RDWR);

    lseek(testdev,0, SEEK_SET);

 

    if (testdev == -1) {

       printf("cannot open file.\n");

       exit(1);

    }

 

    if (ret = read(testdev, buf, n) < n) {

       printf("read error!\n");

       exit(1);

    }

 

    for (i=0; i<n; i++) {

       printf("%c", buf[i]);

    }

    printf("\n");

 

    close(testdev);

 

    return 0;

}

在PC机上使用arm-linux-gcc usr_test.c –ousr_test编译,通过NFS共享或ftp下载到Tiny6410开发板上执行./usr_test(超级终端输入)。

你可能感兴趣的:(简单字符设备驱动程序)