【驱动】Linux初级驱动系列框架

【系统环境搭建】


1.uboot的命令

set serverip 192.168.7.xx
set ipaddr 192.168.7.xxx
set bootcmd tftp 20800000 zImage\;bootm 20800000 //开发模式
set bootcmd nand read 20800000 40000 2000000\;bootm 20800000 //产品模式
set bootargs root=/dev/nfs nfsroot=192.168.7.xx:/opt/rootfs console=ttySAC0,115200 ip=192.168.7.xxx

2.内核配置与编译

2.1 修改交叉工具链,修改顶层目录的Makefile
2.3
生成默认配置,默认配置一般在arch/arm/configs
2.4
根据产品功能需求通过图形化菜单来添加自己的配置(最小系统需要dm9000网卡驱动)
2.5 编译 make zImage或make uImage

3.根文件系统的挂载

3.1 修改nfs的目录

 sudo vi /etc/export
sudo /etc/init.d/nfs-kerner-server restart
sudo exportfs -a

【linux内核模块开发】

1.什么是linux内核模块?
    实际是一个可以在系统启动后,动态加载到系统的代码

2.linux内核模块的优点?
    2.1
动态加载与卸载
    2.2 减少内核镜像的大小
    2.3
节省开发周期

3.linux内核模块的基本组成
    3.1
头文件
    3.2
模块加载函数/模块入口函数
        a.
初始化工作
        b.
资源申请
    3.3
模块卸载函数/模块出口函数
        与模块加载函数做相反的事情
    3.5
权限许可声明

4.linux内核模块的操作命令
    加载内核模块:insmod
xxx.ko
    卸载内核模块:rmmod
xxx
    查看:lsmod

【如何将代码直接编译进内核】

1.将代码拷贝到内核的相应的目录
    2.修改此目录下的Makefile,Kconfig
    3.进入图形化菜单,选择相应的选项 
make menuconfig
    4.重新编译内核


【linux设备驱动开发】


1.linux下设备驱动的基本知识
    a.一切设备皆文件
    b.文件的属性:
      1)设备号
     
2)名字
      3)类型    l d - s p c
b
      4)权限

【linux下字符设备驱动的基本框架】

【字符设备驱动的基本知识点】


字符设备涉及的重要结构体

//驱动人员必须自己构建一个变量
struct file_operations --->表示设备的操作方法
{
struct module *owner;
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*open) (struct inode *, struct file *);
...
};

//老的方法:不需要驱动人员构建,只要调用register_chrdev
//新的方法:需要驱动人员构建
struct cdev ---->表示一个字符设备
{
const struct file_operations *ops;//设备的操作方法
dev_t dev; //设备号
...
};

//1.mknod /dev/xxx c major minor
//2.class_create device_create
struct inode ---->表示一个设备节点:每个目录或者每个文件都对应一个inode结构体(静态的)
{
dev_t i_rdev; //设备号
const struct file_operations *i_fop; /* former ->i_op->default_file_ops def_chr_fops */
struct cdev *i_cdev;
...
};

struct file --->表示一个打开的文件:每一个文件被打开一次,都会创建一个file结构体(动态的)
{
loff_t f_pos; //文件的偏移量
unsigned int f_flags; //标号 open("/dev/xxx",O_RDWR|O_NONBLOCK) file->f_flags |=O_RDWR|O_NONBLOCK
const struct file_operations *f_op; //文件的操作方法
void *private_data;
...
};

【字符设备驱动涉及的关键函数】


a.申请主设备号

老的方法:register_chrdev(major,name,file_Operations); ----->unregister_chrdev
新的方法:register_chrdev_region(dev_t,count,name); ----->unregister_chrdev_region
alloc_chrdev_region(dev_t *,baseminor,count,name);

b.创建cdev结构体的函数

cdev_alloc
cdev_init(struct cdev*, file_operations);
cdev_add
cdev_del

c.创建设备文件的函数

手动创建:mknod /dev/xxx c major minor
动态创建:class_create(owner,name);
 device_create

d.设备号的操作
    设备号用dev_t来表示,它是一个无符号的32位整形数,其中高12位为主设备号,低20位为次设备号

MKDEV(major,minor)
MAJRO(dev_t)
MINOR(dev_t)
iminor(struct inode);
imajor(struct inode);

e.将物理地址映射为虚拟地址
    虚拟地址=ioremap(物理地址,大小)   
---->iounmap(虚拟指针)

f.用户空间到内核空间的数据交互

copy_from_user
copy_to_user

【字符设备驱动的编写流程】

1)实现模块加载函数
     
a.申请主设备号
      b.创建字符设备结构体(struct
cdev),注册字符设备
     
c.创建设备文件
      d.将物理地址映射为虚拟地址
2)实现模块卸载函数
3)构建file_operations结构体
4)实现操作硬件的方法
      xxx_open
xxx_write xxx_read


【linux下中断编程】


1.注册中断/申请中断

/*param1:中断号  mach/irqs.h
*param2:中断服务程序 typedef irqreturn_t (*irq_handler_t)(int, void *); linux/irqreturn.h
* enum irqreturn {
* IRQ_NONE, //如果中断处理失败则返回 0
* IRQ_HANDLED, //如果中断处理成功则返回 1
* IRQ_WAKE_THREAD,
* };
*
* typedef enum irqreturn irqreturn_t;
*param2:中断触发方式 linux/interrupt.h
*param3:名字
*param4:id号,一般共享中断才会用,
*/
ret = request_irq(IRQ_EINT(1), irq_handler_t handler, unsigned long flags, const char * name, void * dev)

2.释放中断

free_irq(unsigned int irq, void * dev_id)

3.实现中断服务程序

typedef irqreturn_t (*irq_handler_t)(int, void *);

【阻塞与非阻塞机制】

2.1
进程的状态转换(就绪态,运行态,休眠态,暂停态,僵死态,死亡态)

2.2
如何让进程进入休眠态
     a.改变进程状态  
------>set_current_state(state_value);
     b.将进程挂入等待队列  
----->add_wait_queue(wait_queue_head_t *q,wait_queue_t
*wq);
     c.让出CPU资源,调度 ------>schedule();

2.3
休眠的原理:
    等待队列头:

struct __wait_queue_head {
spinlock_t lock; /*锁,用来同步*/
struct list_head task_list;/*链表*/
};
typedef struct __wait_queue_head wait_queue_head_t;

任务节点:

struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
void *private; /*应该指向当前进程*/
wait_queue_func_t func; /*唤醒函数*/
struct list_head task_list;/*任务链表*/
};
typedef struct __wait_queue wait_queue_t;

2.4 对队列头与任务节点的操作函数与宏:

任务节点的定义:
    方法一:

/*定义一个节点,并初始化任务节点*/
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, .func = default_wake_function, .task_list = { NULL, NULL } }
#define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

方法二:

:/*定义并初始化一个任务节点*/
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { .private = current, .func = function, .task_list = LIST_HEAD_INIT((name).task_list), }
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)

等待队列头的定义:
    方法一:

/*初始化等待队列头*/
#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), .task_list = { &(name).task_list, &(name).task_list } }
/*定义等待队列头,并且初始化等待队列头*/
#define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

方法二:

/*定义等待队列头*/
wait_queue_head_t *q;
/*初始化等待队列头*/
#define init_waitqueue_head(q) do { static struct lock_class_key __key; __init_waitqueue_head((q), &__key); } while (0)

将任务节点加入队列:

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

【如何在驱动添加阻塞机制】

a. 定义一个等待队列头
     b.
初始化等待队列头
     c.
在适当的地方实现阻塞
         wait_event_interruptiable(wq,condition);
     d.
在适当的地方唤醒
         wake_up_interruptiable(wq);


【异步通知机制】


【linux下poll机制的工作原理】

app:ret=poll(fds,1,3000);    
//标准的系统调用接口

vfs:long sys_poll(struct pollfd __user *ufds, //系统调用号168
unsigned int nfds,long timeout_msecs)

/*1.转换时间,转换为以jiffies(内核时间片)为单位的时间*/

do_sys_poll(ufds, nfds, &timeout_jiffies);

poll_initwait(&table);
/*3.查询是否有事件*/
do_poll(nfds, head, &table, timeout);
for (;;) {
/*3.1 改变进程状态*/
set_current_state(TASK_INTERRUPTIBLE); //可被信号中断的休眠
/*3.2 将进程挂入等待队列*/
if (do_pollfd(pfd, pt)) {
if (file->f_op && file->f_op->poll) //在此调用驱动程序的poll函数
mask = file->f_op->poll(file, pwait);
return mask;
count++;
pt = NULL;
}
/*3.3 1)count>0 2)时间用完 3)有信号*/
if (count || !*timeout || signal_pending(current))
break;
/*3.4 时间减少*/
*timeout -= __timeout;
/*3.5 调度,休眠(定时休眠)*/
schedule_timeout(__timeout);
}
__set_current_state(TASK_RUNNING);

【poll机制的驱动编程流程】

1.定义一个等待队列头    wait_queue_head_t
buttons_wq;
2.初始化等待队列头  init_waitqueue_head(&buttons_wq);
3.实现poll的功能函数
 
a. 将进程挂入等待队列,但是不会休眠
  poll_wait(file, &buttons_wq, wait);
 
b. 判断是否有事件
4.在中断服务程序中实现唤醒
 
wake_up_interruptible(&buttons_wq);


【阻塞-异步通知-poll轮询比较】


如何在驱动中添加阻塞与非阻塞机制

异步通知的编程流程

poll机制的编程流程


【如何将驱动加入内核】


1.将驱动加入内核目录的驱动目录的相应的目录下

cp  hello.c /home/farsight/work/linux_source/linux-2.6.35.5/drivers/char/

注意:一般在内核中,任何一个目录中都会有一个Makefile
    
同时,任何一个目录中都会有一个Kconfig

2.修改相应目录下的Makefile(参照)
     在linux-2.6.35.5/driver/char/,修改Makefile

obj-$(CONFIG_HELLO)             += hello.o   //在其中添加如下项

【解释Kconfig】

模型1:

config N_GSM  //菜单项
tristate "GSM MUX line discipline support (EXPERIMENTAL)" //菜单项目名字 tristate-属性,表示三态<> y-编译到内核镜像 n-不编译 m-编译成模块
depends on EXPERIMENTAL //依赖
depends on NET
help //帮助文档
This line discipline provides support for the GSM MUX protocol and
presents the mux as a set of 61 individual tty devices.

模型2:

config RIO_OLDPCI
bool "Support really old RIO/PCI cards"
depends on RIO
help
Older RIO PCI cards need some initialization-time configuration t o
determine the IRQ and some control addresses. If you have a RIO and
this doesn‘t seem to work, try setting this to Y.

模型3:

config HVC_CONSOLE
bool "pSeries Hypervisor Virtual Console support"
depends on PPC_PSERIES
select HVC_DRIVER //帮你选择
select HVC_IRQ
help
pSeries machines when partitioned support a hypervisor virtual
console. This driver allows each pSeries partition to have a cons ole
which is accessed via the HMC.

模型4:

config LEGACY_PTYS
bool "Legacy (BSD) PTY support"
default y //默认选上了
---help---
A pseudo terminal (PTY) is a software device consisting of two
halves: a master and a slave. The slave device behaves identical to
a physical terminal; the master device is used by a process to

3.在图像化菜单上,做一个菜单项,必须修改Kconfig

config HELLO
tristate "farsight hello module"
---help---
this is a simple hello module by liucw

4.选择配置项

make menuconfig

@成鹏致远 | 2013-08-20

时间: 2024-10-08 20:49:19

【驱动】Linux初级驱动系列框架的相关文章

Linux设备驱动第六篇:高级字符驱动操作之iotcl

在之前我们介绍了怎样实现一个简单的字符设备驱动.并介绍了简单的open,close,read,write等驱动提供的基本功能.可是一个真正的设备驱动往往提供了比简单读写更高级的功能. 这一篇我们就来介绍一些驱动动中使用的一些高级的操作的实现. 大部分驱动除了提供对设备的读写操作外,还须要提供对硬件控制的接口,比方查询一个framebuffer设备能提供多大的分辨率,读取一个RTC设备的时间,设置一个gpio的高低电平等等.而这些对硬件操作能力的实现一般都是通过ioctl方法来实现的 1. 原型介

Linux设备驱动第七篇:高级字符驱动操作之阻塞IO

我们之前介绍过简单的read,write操作,那么会有一个问题:当驱动无法立即响应请求该怎么办?比如一个进程调用read读取数据,当没有数据可读时该怎么办,是立即返回还是等到有数据的时候:另一种情况是进程调用write向设备写数据,如果缓冲区满了或者设备正忙的时候怎么办,是立即返回还是继续等待直到设备可写?这种情况下,一般的缺省做法是使进程睡眠直到请求可以满足为止.本篇就介绍遇到这类问题驱动的处理方法. 睡眠 什么是睡眠?一个进程睡眠意味着它暂时放弃了CPU的运行权,直到某个条件发生后才可再次被

linux设备驱动归纳总结

前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授课).<linux内核设计与实现>第三版.<linux设备驱动程序>第三版和<linux设备驱动开发详解>第一版来归纳的.文章中涉及一些自己的想法,并不能保证所说的一定正确. 我也是一位linux初学者,在这里发博也是想跟大家分享技术,同时也希望别人能够指正错误. 我把一些

linux网络驱动

linux 设备驱动 linux设备驱动在linux中作为linux的内核模块存在.而内核模块可以在系统运行期间动态扩展系统.所以,我们可以在用户空间,使用insmod和rmmod动态安装或卸载模块. 现实世界中存在着大量的设备,这些设备在电气特性和I/O方式上都各不相同.为了简化设备驱动程序员的工作,linux系统从这些各异的设备中提取了共性的特征,将其划分为三大类:字符设备.块设备和网络设备. linux设备驱动模型 Linux设备驱动模型提取了设备操作的共同属性,进行抽象,并将这部分共同的

字符设备驱动、平台设备驱动、设备驱动模型、sysfs的关系

Linux驱动开发的童鞋们来膜拜吧:-)  学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关联的分析.对于开发者而言,能够熟悉某一点并分享出来已很难得,但对于专注传授技术和经验给

[kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关

Linux Framebuffer驱动框架之二软件架构(未完待续)【转】

本文转载自:http://blog.csdn.net/gqb_driver/article/details/12918547 /************************************************************************************************************************************/ 原创作品,转载时请务必以超链接形式标明文章原始出处:http://blog.csdn.net/gqb66

4 linux lcd驱动框架分析

4 linux lcd驱动框架 Linux内核中lcd的驱动是基于帧缓冲framebuffer驱动框架设计的.帧缓冲framebuffer框架是在linux2.2.xx以后的版本中为显示设备提供的一种驱动程序接口,它将显示缓冲区framebuffer进行抽象,屏蔽掉硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区framebuffer进行读写和I/O控制操作.Framebuffer机制模仿显卡的功能,将显卡硬件抽象为一系列的数据结构,通过framebuffer的读写实现对显存的操作.

Linux摄像头驱动学习之:(一)V4L2_框架分析

这段时间开始搞安卓camera底层驱动了,把以前的的Linux视频驱动回顾一下,本篇主要概述一下vfl2(video for linux 2). 一. V4L2框架: video for linux version 2虚拟视频驱动vivi.c分析:1.分配video_device2.设置3.注册:video_register_device vivi_init    vivi_create_instance        v4l2_device_register   // 不是主要, 只是用于初始