简单字符设备驱动程序

转载 2016年06月01日 23:57:42

理论知识参考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(xiahouzuoxin@163.com)

 *       Compiler:  gcc

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

 */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

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(xiahouzuoxin@163.com)

 *       Compiler:  gcc

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

 */

#include

#include

#include

#include

#include

 

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++)="" {="" <="" p="" style="word-wrap: break-word;">

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

    }

    printf("\n");

 

    close(testdev);

 

    return 0;

}

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

简单字符设备驱动程序(一)

理论基础: 1.几个重要的结构体: (1)cedv结构体 struct cdev {      struct kobject      kobj;                      ...
  • sooolo
  • sooolo
  • 2012年03月16日 14:22
  • 719

linux简单的字符设备驱动程序

要求:掌握添加设备驱动程序的方法 内容: 采用模块方法,添加一个新的设备驱动程序。 要求添加字符设备的驱动。 编写一个应用程序,测试添加的驱动程序。 linxu系统中,在应用...

写一个简单的字符设备驱动程序

所需知识 =======         - C 语言编程         - 微处理器编程.对处理器的工作原理有一定的了解,如内存管理、中断等 用户空间和内核空间 ==========...

简单字符设备驱动程序浅分析

根据自己的理解,简单分析了一个字符设备驱动程序,该程序是用一块内存模拟设备!程序代码如下:mendev.c#include #include #include #include #include #i...

实验5.1.1简单字符设备驱动程序设计与验证

1、实验目的:掌握简单字符设备驱动设计规范模式,设备节点创建方法,应用程序的设计和编写方法。 2、实验要求: (A.)在S3C2440(以tq2440和mini2440为平台验证的)平台上编写...

最简单的字符设备驱动程序

首先,先理清一下简单字符设备驱动程序的思路: (1)申请设备号       动态申请:int alloc_chrdev_region(dev_t *dev, unsigned baseminor,...

Linux字符设备驱动程序的一个简单示例

一.源代码: // memdev.c #define MEMDEV_MAJOR 254 /*预设的mem的主设备号*/ #define MEMDEV_NR_DEVS 2 /*设备数*/...
  • ypist
  • ypist
  • 2011年10月17日 19:23
  • 3528

Linux字符设备驱动程序的一个简单示例

摘自:http://www.cnblogs.com/fangyu/archive/2010/09/10/1823662.html Linux字符设备驱动程序的一个简单示例 一.开发环境: ...

linux简单的字符设备驱动程序

要求:掌握添加设备驱动程序的方法   内容:  采用模块方法,添加一个新的设备驱动程序。  要求添加字符设备的驱动。   编写一个应用程序,测试添加的驱动程序。   l...
  • BXD1314
  • BXD1314
  • 2014年03月23日 18:02
  • 595
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:简单字符设备驱动程序
举报原因:
原因补充:

(最多只允许输入30个字)