linux2.6内核源码注释,一个2.6内核字符设备驱动hello world 注释超详细(zz)

zz from: [url]http://hi.baidu.com/deep_pro/blog/item/b451e687101a302cc65cc390.html[/url]

一个2.6内核字符设备驱动hello world 注释超详细

2008-07-17 17:02

本例是冯国进的 《嵌入式Linux 驱动程序设计从入门到精通》的第一个例子

感觉真是好书   强烈推荐

这个驱动是在内存中分配一个256字节的空间,供用户态应用程序读写。

先是头文件 demo.h

#ifndef _DEMO_H_

#define _DEMO_H_

#include /* needed for the _IOW etc stuff used later */

/********************************************************

* Macros to help debugging

********************************************************/

#undef PDEBUG             /* undef it, just in case */

#ifdef DEMO_DEBUG

#ifdef __KERNEL__

#    define PDEBUG(fmt, args...) printk( KERN_DEBUG "DEMO: " fmt, ## args)

#else//usr space

#    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)

#endif

#else

# define PDEBUG(fmt, args...) /* not debugging: nothing */

#endif

#undef PDEBUGG

#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */

//设备号

#define DEMO_MAJOR 50

#define DEMO_MINOR 0

#define COMMAND1 1

#define COMMAND2 2

//自己定义的设备结构

struct DEMO_dev

{

struct cdev cdev;    /* Char device structure        */

};

//函数申明 原来Linux驱动程序设计这么简单 只需要实现这么几个函数就可以了

ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count,

loff_t *f_pos);

ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count,

loff_t *f_pos);

loff_t DEMO_llseek(struct file *filp, loff_t off, int whence);

int     DEMO_ioctl(struct inode *inode, struct file *filp,

unsigned int cmd, unsigned long arg);

#endif /* _DEMO_H_ */

然后是demo.c

/*

* This file is subject to the terms and conditions of the GNU General Public

* License. See the file "COPYING" in the main directory of this archive

* for more details.

*

* Copyright (C) 2007, 2010 fengGuojin([email]fgjnew@163.com[/email])

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "demo.h"

MODULE_AUTHOR("fgj");

MODULE_LICENSE("Dual BSD/GPL");

//很郁闷的是就算这样 移植到开发板上仍然说这个驱动污染了内核

struct DEMO_dev *DEMO_devices;

//申明自己定义的设备结构的指针

static unsigned char demo_inc=0;

//计数 记录此设备被打开的次数

static u8 demoBuffer[256];

//内核里的数据结构和数据类型还真是诡异啊 这个u8还真费解 原来是无符号8位的数据类型

//http://linux.chinaunix.net/bbs/viewthread.php?tid=797504&page=1

int DEMO_open(struct inode *inode, struct file *filp)

{

struct DEMO_dev *dev;

//只允许打开设备一次

if(demo_inc>0)return -ERESTARTSYS;

demo_inc++;

/*container_of 宏 通过结构中的某个变量获取结构本身的指针

[url]http://hi.baidu.com/%CE%DA%D1%BB%C3%F7/blog/item/b805ae1975f02e4443a9ade3.html[/url]

真是太高级了 我看不懂

*/

dev = container_of(inode->i_cdev, struct DEMO_dev, cdev);

filp->private_data = dev;

return 0;

}

int DEMO_release(struct inode *inode, struct file *filp)

{

demo_inc--;

return 0;

}

ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)

{

int result;

loff_t pos= *f_pos; /* 文件的读写位置 */

if(pos>=256)

{

result=0;

goto out;

}

if(count>(256-pos))

{

count=256-pos;

}

pos += count;

if (copy_to_user(buf,demoBuffer+*f_pos,count))

{

count=-EFAULT; /* 把数据写到应用程序空间 */

goto out;

}

*f_pos = pos; /* 改变文件的读写位置 */

out:

return count;

}

ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)

{

ssize_t retval = -ENOMEM; /* value used in "goto out" statements */

loff_t pos= *f_pos;

if(pos>=256)

{

goto out;

}

//如果要写入的输入大于剩下的内存空间 就只写入剩下的空间数量 以防溢出

if(count>(256-pos))

{

count=256-pos;

}

//剩下的代码 如果c语言扎实的话 ,不难看懂 因为我的表述能力不好 见谅

pos += count;

//copy_from_user 原文说将数据复制到用户空间 ,我觉得应该是将数据复制到内核空间,应该是作者笔误,没什么大不了的

if (copy_from_user(demoBuffer+*f_pos, buf, count)) {

retval = -EFAULT;

goto out;

}

*f_pos = pos;

return count;

out:

return retval;

}

/*我是这几天才大致知道ioctl的作用 ioctl是用来控制设备的 ,unsigned int cmd就是发给设备的命令

ioctl()或许是Linux下最庞杂的函数*/

int DEMO_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)

{

if(cmd==COMMAND1)

{

printk("ioctl command1 successfully\n");

return 0;

}

if(cmd==COMMAND2)

{

printk("ioctl command2 successfully\n");

return 0;

}

printk("ioctl error\n");

return -EFAULT;

}

//llseek 实现随即存取 ,loff_t应该是一个无符号整型 记录文件指针偏移量的 这段代码参考用户态的seek()就不难理解

loff_t DEMO_llseek(struct file *filp, loff_t off, int whence)

{

loff_t pos;

pos = filp->f_pos;

switch (whence)

{

case 0:

pos = off;

break;

case 1:

pos += off;

break;

case 2:

default:

return -EINVAL;

}

if ((pos>256) || (pos<0))

{

return -EINVAL;

}

return filp->f_pos=pos;

}

//file_operations这个结构体真是相当重要 需要搞清楚它的作用

struct file_operations DEMO_fops = {

.owner =    THIS_MODULE,

.llseek =   DEMO_llseek,

.read =     DEMO_read,

.write =    DEMO_write,

.ioctl =    DEMO_ioctl,

.open =     DEMO_open,

.release = DEMO_release,

};

/*******************************************************

MODULE ROUTINE

*******************************************************/

void DEMO_cleanup_module(void)

{

//在下面的入口函数有讲MKDEV

dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR);

if (DEMO_devices)

{

cdev_del(&DEMO_devices->cdev);

//内核下的内存操作函数还真怪异 习惯就好

kfree(DEMO_devices);

}

//调用unregister_chrdev_region()函数释放分配的一系列设备号

unregister_chrdev_region(devno,1);

}

int DEMO_init_module(void)

{

int result;

// 在内核中,dev_t类型(在中定义)用来保存设备编号——包括主设备号和次设备号

dev_t dev = 0;

/*内核中定义了三个宏来处理主、次设备号:MAJOR和MINOR宏可以从16位数中提取出主、次设备号,而MKDEV宏可以把主、此号合并为一个16位数。

高8位用于主设备号,低8位用于次设备号。

[url]http://www.kerneltravel.net/kernel-book/%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%20%20%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F/11.2.3.htm[/url]*/

dev = MKDEV(DEMO_MAJOR, DEMO_MINOR);

/*获取一个或多个设备编号来使用

如果分配成功进行, register_chrdev_region 的返回值是 0. 出错的情况下, 返回一个负的错误码, 你不能存取请求的区域. */

result = register_chrdev_region(dev, 1, "DEMO");

if (result < 0)

{

printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR);

return result;

}

//为自定义的设备结构申请空间

DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL);

if (!DEMO_devices)

{

result = -ENOMEM;

goto fail;

}

//为新申请的空间清零 我的水平也只能看懂这些简单的函数了

memset(DEMO_devices, 0, sizeof(struct DEMO_dev));

//初始化一个字符驱动 这里我就不了解cdev_init了 只能大概猜个意思

//还有就是 struct file_operations , 这个结构体的作用搞清楚了 就明白为什么Linux 驱动只要实现很少的驱动就可以了

cdev_init(&DEMO_devices->cdev, &DEMO_fops);

DEMO_devices->cdev.owner = THIS_MODULE;

DEMO_devices->cdev.ops = &DEMO_fops;

//在内核中添加字符驱动

result = cdev_add (&DEMO_devices->cdev, dev, 1);

if(result)

{

printk(KERN_NOTICE "Error %d adding DEMO\n", result);

goto fail;

}

return 0;

fail:

//失败了 就调用出口函数擦pp走人了

DEMO_cleanup_module();

return result;

}

//这两个是内核驱动必备的 指明程序入口和出口

module_init(DEMO_init_module);

module_exit(DEMO_cleanup_module);

demo.c文件结束

Makefile

#模块是从demo.o产生的,生成demo.ko

obj-m := demo.o

#/lib/modules/$(shell uname -r)/build是源码所在的路径,-C表示makefile在后面给出的路径中

#Use make M=dir to specify directory of external module to build ,M=$(shell pwd)给出了模块源文件的路径

all:

make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules

clean:

make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

#编译后会生成一个demo.ko的模块文件,用/sbin/insmod ./demo.ko可以进行模块的安装,然后在/proc/modules中可以看到该模块

#可以利用rmmod卸载模块

加载驱动

# insmod demo.ko

然后使用lsmod 或 cat /proc/modules查看驱动是否加载

# mknod /dev/fgj c 50 0 创建设备节点

然后就可以使用下面代码来测试驱动

/*

* This file is subject to the terms and conditions of the GNU General Public

* License. See the file "COPYING" in the main directory of this archive

* for more details.

*

* Copyright (C) 2007, 2010 fengGuojin([email]fgjnew@163.com[/email])

*/

#include

#include

#include

#include

#include

#include

#include

#define COMMAND1 1

#define COMMAND2 2

main()

{

int fd;

int i;

char data[256];

int retval;

fd=open("/dev/fgj",O_RDWR);

if(fd==-1)

{

perror("error open\n");

exit(-1);

}

printf("open /dev/smbus successfully\n");

retval=ioctl(fd,COMMAND1,0);

if(retval==-1)

{

perror("ioctl error\n");

exit(-1);

}

printf("send command1 successfully\n");

retval=write(fd,"fgj",3);

if(retval==-1)

{

perror("write error\n");

exit(-1);

}

retval=lseek(fd,0,0);

if(retval==-1)

{

perror("lseek error\n");

exit(-1);

}

retval=read(fd,data,3);

if(retval==-1)

{

perror("read error\n");

exit(-1);

}

printf("read successfully:%s\n",data);

close(fd);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值