驱动基本结构

struct inode 结构体
在内核中,使用一个struct inode 结构体,来唯一的表示一个文件。
一个文件对应一个inode结构体。

struct inode 存储的是文件的属性相关的信息。    
// 当执行一次 mknod 创建的是设备节点文件,此时生成一个 struct inode 结构体

struct inode {
	umode_t			i_mode;  // 打开方式
	unsigned short		i_opflags;  
	kuid_t			i_uid; // uid 用户id
	kgid_t			i_gid;   // 组id
	unsigned int		i_flags;  // 标志位

	const struct inode_operations	*i_op;  // inode节点的操作方法集
	struct super_block	*i_sb;
	struct address_space	*i_mapping;

	/* Stat data, not accessed from path walking */
	unsigned long		i_ino;  // inode号码

	union {
		const unsigned int i_nlink;  // 链接数
		unsigned int __i_nlink;
	};
	dev_t			i_rdev;   // 设备号
	loff_t			i_size;  // 大小
	struct timespec		i_atime;  // 访问时间
	struct timespec		i_mtime;   // 修改时间
	struct timespec		i_ctime;   // 创建时间
	
	unsigned short          i_bytes;
	unsigned int		i_blkbits;
	blkcnt_t		i_blocks;

	const struct file_operations	*i_fop;	/* 文件操作方法集合指针*/

	struct list_head	i_devices;  // 循环双链表
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;  // 字符设备地址指针
	};

	void			*i_private; /*私有数据 */
};

struct file 结构体
struct file 也是一个文件属性信息结构体。应用程序每调用一次open函数,那么内核中生成一个struct file的结构体。

struct file {
	struct path		f_path;   // 路径
	struct inode		*f_inode;	/* cached value */
	const struct file_operations	*f_op;  // 文件操作方法集合
	unsigned int 		f_flags;  // 文件操作标志位
	fmode_t			f_mode;  // 方式
	loff_t			f_pos;  // 偏移量
	struct fown_struct	f_owner;  // 异步结构体
	/* needed for tty driver, and maybe others */
	void			*private_data;
	.....
};	


open() --->   struct file |
open() --->   struct file | ---> struct inode  --> 具体的文件
open() --->   struct file |

应用程序open 如何访问到驱动中 demo_open

struct inode 在内核中使用循环双链表进行管理的:

struct inode   <--->  struct inode  <--->  struct inode <--->  struct inode ....
(设备号)
                            

struct cdev   <--->  struct cdev   <--->  struct cdev <---->  struct cdev  。。。。  // cdev 循环双链表
 (设备号)

两条链表之间,相互探测,按设备号进行匹配,一旦匹配成功,将cdev的地址,fops地址,赋值给 struct inode 成员

循环双链表
// 循环双链表头结点定义
struct list_head {
struct list_head *next, *prev;
};

// 循环双链表结点初始化
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

// 插入节点,头插法
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

// 删除节点
static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}

// 遍历循环双链表
#define list_for_each(pos, head)
for (pos = (head)->next; pos != (head); pos = pos->next)

list_for_each(.. , ..)
{
   。。。
}

// 尾插法
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

已知一个结构体成员,求出此结构体首地址
struct node {
int a;
char ch;
short b; // 绝对地址
int d;
};

int a;

#define container_of(ptr, type, member) ({			\
										const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
										(type *)( (char *)__mptr - offsetof(type,member) );})
功能:已知一个结构体的成员的地址,求出此结构体首地址
参数:ptr   已知的成员地址
      type  结构体的类型
	  member 已知地址的对应的成员的名称
返回值:结构体首地址

/// 求成员的偏移量
 offsetof(type,member) , // type 结构体类型  member 结构体成员名称
 ---->
    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)  // & 取出变量的地址, size_t 强制类型转换,将地址转换成一个整形数据

// typeof 已知成员的名称,求出它的类型
 typeof( ((type *)0)->member ) *__mptr = (ptr);  // short * __mptr = (ptr);

// (char *)__mptr  强制类型转换 ,字符指针地址
(type *)( (char *)__mptr - offsetof(type,member) );

并发的概念
并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源
(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions)

竞态的原因:
1,对称多处理器(SMP)的多个CPU
2,单CPU内进程与抢占它的进程
3,中断(硬中断、软中断、Tasklet、底半部)与进程之间

解决竞态的途径:
    保证对共享资源的互斥访问,所谓互斥访问是指一个执行单元在访问共享资源的时候,其他的执行单元被禁止访问。
访问共享资源的代码区域称为临界区(critical sections),临界区需要被以某种互斥机制加以保护。

互斥机制
1,中断屏蔽
2,原子操作
3,自旋锁
4,信号量

中断屏蔽
local_irq_disable() /* 屏蔽中断 /
. . .
critical section /
临界区*/
. . .
local_irq_enable() /* 开中断*/

// 注意点:local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU内的中断,因此,并不能解决SMP多CPU引发的竞态。

原子操作
对原子变量的操作是一气呵成。

 int flag = 1;
 
 demo_open()
 {
     if(flag == 1)
	 {
	    flag--;  // flag = 0;
		// 打开成功
	 }else
	 {
	    return -EBUSY;
	 }
 }

整型原子操作:
设置原子变量的值
void atomic_set(atomic_t *v, int i); /* 设置原子变量的值为i */
atomic_t v = ATOMIC_INIT(0); /* 定义原子变量v并初始化为0 */

获取原子变量的值
atomic_read(atomic_t *v);  /* 返回原子变量的值*/

原子变量加/减
void atomic_add(int i, atomic_t *v); /* 原子变量增加i */
void atomic_sub(int i, atomic_t *v); /* 原子变量减少i */

原子变量自增/自减
void atomic_inc(atomic_t *v);    /* 原子变量增加1 */
void atomic_dec(atomic_t *v);    /* 原子变量减少1 */

操作并测试
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
上述操作对原子变量执行自增、自减和减操作后(注意没有加)测试其是否为0,为0返回true,否则返回false。
操作并返回
/// #define atomic_sub_and_test(i,v)	(atomic_sub_return((i), (v)) == 0)

int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
上述操作对原子变量进行加/减和自增/自减操作,并返回新的值

自锁锁
自旋锁实际上是忙等锁
自旋锁可能导致系统死锁
自旋锁锁定期间不能调用可能引起进程调度的函数,不能睡眠,不能执行耗时操作。

定义自旋锁
spinlock_t  lock;

初始化自旋锁
spin_lock_init(&lock)

获得自旋锁
spin_lock(&lock)
如果能够立即获得锁,它就马上返回,否则,它将自旋在那里

spin_trylock(&lock)
该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则立即返回假

释放自旋锁
spin_unlock(&lock)

信号量
信号量是内核中用来保护临界资源的一种,信号量保护的资源中,可以进行睡眠。

互斥机制

相关头文件 <linux/semaphore.h>

定义信号量
struct semaphore sem;

初始化信号量
void  sema_init(struct semaphore *sem, int val);

信号量的获取
void down(struct semaphore * sem);
int  down_interruptible(struct semaphore * sem);   // 申请资源的过程中,可以被中断
int  down_trylock(struct semaphore * sem);

信号量的释放
void up(struct semaphore * sem);

互斥体
使用方法及场合和信号量一致

初始化
struct mutex my_mutex;
mutex_init(&my_mutex);

获取互斥体
void inline _ _sched mutex_lock(struct mutex *lock);

释放互斥体
void __sched mutex_unlock(struct mutex *lock);

如何将字符设备驱动与硬件结合起来
1,查看原理图
LED3 GPX1_0

  2,三星的芯片手册
     GPX1CON  0x11000C20    // 控制寄存器,设置管脚模式  , 0x1 输出模式
     GPX1DAT  0x11000C24    // 数据寄存器,		 

     GPX1_0  [3:0]  低四位控制

  3,led驱动编程

地址映射
static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
功能:将物理地址映射为虚拟地址
参数:offset 物理地址
size 大小
返回值:映射后的虚拟地址

static inline void iounmap(void __iomem *addr)
功能:解除映射
参数:addr  虚拟地址

static inline u32 readl(const volatile void __iomem *addr)
功能:读取寄存器的值
参数:虚拟地址
返回值:值

static inline void writel(unsigned int b, volatile void __iomem *addr)
功能:将数据写入寄存器
参数:b  数值
      addr  寄存器的虚拟地址
返回值:无

流水灯
ioctl

 while(1)
 {
     ioctl(fd, LED_ON , &num);
	 sleep(1);
	 ioctl(fd, LED_OFF, &num);
	 
	 num++;
 }	 
 
 驱动:unlocked_ioctl
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值