(七)linux函式介面的使用

2021-10-04 16:32:32 字數 4317 閱讀 8915

前面我們講解了字元裝置的驅動模型,有了前面的基礎後,今天學習函式介面就比較容易了

思考乙個問題:當我們應用層呼叫open、read、write、close的時候,核心層是如何實現的呢?

前面學習字元裝置驅動模型中有乙個file_operation結構體,當我們呼叫open函式的時候,核心會呼叫file_operation結構體的open函式指標指向的函式。

我們來看一下file_operation結構體的樣子:

struct file_operations ;
(一)open函式介面

系統層介面:

int open(const char *pathname, int flags, mode_t mode);

//o_creat

//o_nonblock or o_ndelay

核心層介面:

int (*open) (struct inode *, struct file *);
struct inode :核心中用來標識檔案的資料結構,此資料結構的成員無需程式設計師手動賦值,而是核心中已經賦予了與檔案相對應的操作值

struct file *:該結構體標識了乙個開啟的檔案,系統會為每乙個開啟的檔案關聯乙個struct file 資料結構,是在核心開啟檔案的同時,將該引數傳遞到和檔案操作相關的所有需要該引數的介面中

(二)read函式介面

系統層:

#include ssize_t read(int fd, void *buf, size_t count);
核心層:

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
read介面可以直接將核心空間的資料傳遞到使用者空間,但是一般在開發驅動的過程中不會直接採用這種方式,原因是本操作需要兩個空間位址,即使用者空間和核心空間,使用者空間直接操作核心位址是非常危險的,常用copy_to_user和copy_from_user進行使用者空間和核心空間交換資料。

(三)lseek函式介面

系統層:

#include #include off_t lseek(int fd, off_t offset, int whence);
核心層:

loff_t (*llseek) (struct file *, loff_t, int);
struct file *:檔案結構體

loff_t:上層傳遞的偏移量

int :檔案游標定位狀態

seek_set:將游標定位在檔案的開頭,此時loff_t的值為正數

seek_cur:將游標定位在當前位置,此時loff_t的值為可正可負

seek_edn:將游標定位在檔案的結尾,此時loff_t的值為負數

在這裡面可以實現檔案偏移操作,例如:

loff_t cdev_lseek(struct file *fp, loff_t offset, int whence)

if(newoff >4)newoff=4;

if(newoff<0)newoff=0;

fp->f_pos = newoff;

return newoff;

}

(四)使用者空間和使用者空間交換資料

copy_to_user:將核心空間的資料拷貝到使用者空間

static inline long copy_to_user(void __user *to,const void *from, unsigned long n)

copy_from_user:將使用者空間的資料拷貝到核心空間

static inline long copy_from_user(void *to,const void __user * from, unsigned long n)

(五)通過裝置節點提取裝置號
//通過裝置節點提取次裝置號

static inline unsigned iminor(const struct inode *inode)

//通過裝置節點提取次主裝置號

static inline unsigned imajor(const struct inode *inode)

(六)對映ioremap

程式中在操作物理硬體位址的時候不要直接操作對應的位址,需要先進行對映操作

static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)

typedef u32 phys_addr_t;

unsigned long size:對映空間的大小

解除對映:

void iounmap (volatile void __iomem *addr)
(七)例項:led驅動程式設計

思路:首先把需要操作的暫存器實體地址進行對映,然後在open函式中做初始化工作,最後在read/write函式中呼叫copy_to/from_user函式將使用者空間(核心空間)的資料拷貝到核心空間(使用者空間),對資料進行操作

led.c

#include #include #include #include #include #include #include #include int i=0;

dev_t dev=0;

#define cdevcount 5

#define cdevname "cdevdevice"

#define cdevclass "myclass"

#define inodename "mycdev"

#define addrszie 8

unsigned int phy_addr = 0x110002e0;//對映的起始位址為gpm4con

unsigned int * virt_addr = null;//用來接收對映後的起始位址

struct cdev * cdev=null;

struct class * cdevclass=null;

#define gpm4con (*(volatile unsigned int * )virt_addr)

#define gpm4dat (*(volatile unsigned int * )(virt_addr +1))

int cdev_open (struct inode *node, struct file *file)

ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)

ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset)

; int ret =copy_from_user(str,buf,4);

for(i=0;i<4;i++)

module_init(cdev_module_init);

module_exit(cdev_module_cleanup);

module_license("gpl");

#include#include #include #include int main(int argc, char *ar**)

; int fd= open(ar**[1],o_rdwr);

if(fd== -1)

write(fd,str,4);

close(fd);

return 0;

}

makefile

cflag =-c

target = led

kernel = /mydriver/linux-3.5

obj-m += $(target).o

all:

make $(cflag) $(kernel) m=$(pwd)

arm-linux-gcc -o $(target1) $(target1).c

clean:

make $(cflag) $(kernel) m=$(pwd) clean

微博:文藝to青年

函式式介面的使用

package com.learn.demo01.functionalinte ce 函式式介面的使用 一般可以作為方法的引數和返回值型別 public class demo public static void main string args 呼叫show方法,方法的引數是乙個函式式介面,所以我...

演示函式式介面的使用

description author yrm create 2020 08 16 21 44 演示函式式介面的使用 public class demo1 system.out.println system.out.println 函式型呼叫 handleint new function 10 sys...

介面的使用

由於c 類不能多重繼承。但現實有許多多重繼承的情況。為了避免傳統多重繼承帶來的複雜性問題和滿足多重繼承的需要,就提出了介面的概念。介面提出了一種規範,讓使用介面的程式設計人員要遵守其提出的約定。c 中申明介面時,使用關鍵字inte ce。using system using system.colle...