Chinaunix首页 | 论坛 | 博客
  • 博客访问: 40993
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 330
  • 用 户 组: 普通用户
  • 注册时间: 2015-07-28 17:39
文章分类
文章存档

2015年(31)

我的朋友

分类: 嵌入式

2015-10-31 16:26:45

1.
设备驱动:隐藏设备工作细节,用一套标准化的接口来调用。
一是编译到内核,另外就是编译为模块

模块:动态连接到运行的内核中。即系统运行时能够添加到内核的代码

2.
static int __init init_func(void);
static void __exit exit_func(void);

module_init(init_func);指定入口函数init_func
module_exit(exit_func);指定退出函数exit_func

3.
模块编译:
3.1单独编译
需要的Makefile文件:
obj-m := obj-name.o
KDIR := /lib/modules/`uname -r`/build //内核所在目录,模块要在开发板上运行时,指定为开发板的内核在编译的系统上的安装目录
PWD := $(shell pwd)
all:
        make -C $(KDIR) M=$(PWD) modules

执行make后生成obj-name.ko,

3.2到linux内核编译:
复制代码到对应目录,在Kconfig文件中增加:
config  OBJ-NAME
            tristate "....."
            depends on ARCH_S3C2440             //依赖的架构

在同目录的Makefile文件中增加
obj-$(CONFIG_OBJ-NAME) += obj-name.o

编译配置make menuconfig 中选为编译到模块。

在内核目录执行make modules,
obj-name.ko文件就会生成。

4.模块参数
module_param(variable,type, perm);
参数的传入:
insmod obj-name.ko variable=value
value为传入的值

5.调试
5.1
insmod obj-name.ko         //加载模块
rmmod obj-name              //卸载模块

5.2信息输出
printk(KERN_*** "message...", ....);
__FILE__          //文件名
__LINE__          //行号
用dmesg | tail -n num 查看最后num行信息。

5.3全局变量current:
struct task_strct *current;
current->comm //执行进程的命令名
current->pid //进程的id

5.4模块间调用:

在模块A中定义
type global_val;
EXPORT_SYMBOL(global_val);

在模块B中定义
extern type global_val;
...

type为global_val的类型,如果是函数则就是函数的类型声明。

使用时模块A先加载后卸载

/proc/kallsyms中记录了变量global_val的信息

6.字符驱动
6.1设备号:
dev_t dev;//高12位为主设备号,低20位为次设备号,
int MAJOR(dev_t dev); //主设备号
int MINOR(dev_t dev); //次设备号
dev_t MKDEV(unsigned int major, unsigned int minor); //由major主设备号和minor次设备号建立设备号

6.2
文件struct file_operations *fops;
和设备名char *name;
及设备号dev_t dev;

6.2.1
linux2.4.*内核:
register_chrdev(unsigned int major, const char *name, struct file_operations *fops);    //将三者联系起来,注册到系统内。
unregister_chrdev(unsigned int major, const char *name);    //注销字符设备;

6.2.2
linux2.6.*:
设备号和文件操作结构体封装到cdev结构体中:
strct cdev {
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
dev_t dev
unsigned int count;
};

void cdev_init(struct cdev *cdev, struct file_operations *fops);    //将设备号和文件操作结构体联系起来。

6.2.3注册设备号:
设备号(次设备号通常为0)已知,想连续注册count个,使用函数
int register_chrdev_region(dev_t dev, unsigned int count, const char *name);

设备号未知,希望从设备号从baseminor开始,注册count个,使用函数:
int alloc_chr_region(dev_t *dev, unsigned int baseminor, unsigned int count, const char *name);

6.2.4向系统添加设备:
cdev_add(struct cdev *cdev, dev_t dev, unsigned int count);向系统添加一个字符设备


6.2.5,删除设备,注销设备号:
void cdev_del(struct cdev *cdev);从系统里删除一个字符设备
void unregister_chrdev_region(dev_t dev_major, unsigned int count);将申请的设备号释放


6.2.6理解:
一般cdev和file_operations的成员ower都赋值为THIS_MODULE;
驱动的一个目的是提供一个系统调用的统一接口,这个接口的定义就在file_operations中,要实现这些接口;

总之,就是将设备名,设备号和文件操作结构体三者建立联系,添加到系统,使用完后释放。

6.2.7
系统设备号最好采用alloc_chrdev_region自动声请,
运行模块后查看/proc/devices可以看到新的设备号,
在/dev下建立设备结点,
使用设备就是在程序用这个节点文件,
测试完后及时卸载模块同时删除设备结点。

7.驱动实现共享内存
程序间通信可以是信号,信号量,消息队列,管道共享内存,内存映射,套接字等。
看一下驱动的共享内存:
7.1内核中声请空间函数:
void *kmalloc(size_t size, int flags); //flags 为GFP_KERNEL
void kfree(void *ptr);

7.2实现:
7.2.1将使用到的全局数据做成一个结构体:
#define GMEM_SIZE 1024
struct gmem_dev
{
struct cdev cdev; //cdev
unsigned char mmem[GMEM_SIZE];
};
struct gmem_dev *gmem_devp;

7.2.2声请内存,确认声请成功,初始化声请的空间为0
if( NULL == (gmem_devp = kmalloc(sizeof(struct gmem_dev), GFP_KERNEL))
{ memset(gmem_devp, 0, sizeof(struct gmem_dev)); }
else 
{ ... failed to kmalloc ... }

7.3
struct file在文件打开时被内核创建,传递给文件操作的函数。在open函数中将成员void *private_data;指向分配的数据gmem_devp;

7.4
用函数:
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count);
unsigned long copy_to_user(void *to, const void  __user *from, unsigned long count);
实现内核空间与用户空间数据交互。

7.5理解:就是驱动声请一个空间,多个进程访问。由于用户程序不能直接访问内核空间,驱动中需要实现内核空间和用户空间相互数据拷贝接口的细节。
阅读(547) | 评论(0) | 转发(0) |
0

上一篇:中断

下一篇:部分文件函数

给主人留下些什么吧!~~