`
xitonga
  • 浏览: 586041 次
文章分类
社区版块
存档分类
最新评论

简单字符设备驱动程序

 
阅读更多

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

* 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(超级终端输入)。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics