日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

linux驱动read函数 copytouser,Linux驱动编程 step-by-step (五)主要的文件操作方法实现...

發(fā)布時(shí)間:2023/12/9 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux驱动read函数 copytouser,Linux驱动编程 step-by-step (五)主要的文件操作方法实现... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

主要的文件操作方法實(shí)現(xiàn)

文件操作函數(shù)有很多的操作接口,驅(qū)動(dòng)編程需要實(shí)現(xiàn)這些接口,在用戶編程時(shí)候系統(tǒng)調(diào)用時(shí)候會(huì)調(diào)用到這些操作

structfile_operations?{

...

loff_t?(*llseek)?(structfile?*,?loff_t,int);

ssize_t?(*read)?(structfile?*,char__user?*,size_t,?loff_t?*);

ssize_t?(*write)?(structfile?*,constchar__user?*,size_t,?loff_t?*);

int(*open)?(structinode?*,structfile?*);

int(*release)?(structinode?*,structfile?*);

...

};

以上只列出了主要的操作,下面會(huì)依次介紹:

本次的測(cè)試代碼上傳在:char_step2

結(jié)構(gòu)體:

首先 我們會(huì)模擬寫一個(gè)不操作任何設(shè)備,而僅僅是存儲(chǔ)的一個(gè)驅(qū)動(dòng)。

定義自己的一個(gè)結(jié)構(gòu)體為:

structsimple_dev{

chardata[MAX_SIMPLE_LEN];

loff_t?count;

structsemaphore?semp;

};

data 保存數(shù)據(jù), count表示文件的數(shù)據(jù)有效的位置, semp是一個(gè)信號(hào)量鎖,在以后的編程中使用,

之后的程序中結(jié)構(gòu)體也會(huì)做相應(yīng)的變化,以適應(yīng)linux編寫驅(qū)動(dòng)的習(xí)慣

open方法:

打開設(shè)備并進(jìn)一步初始化工作,在沒有定義open方法時(shí)內(nèi)核以一種默認(rèn)的方式打開設(shè)備,保證每次都能正確打開。

open方法中有有struct inode參數(shù),包含了設(shè)備號(hào),程序中可以使用次設(shè)備號(hào)得到正操作的設(shè)備

在struct file中主要的操作是private_data指針,他可以傳遞任何自己創(chuàng)建的結(jié)構(gòu)。

總得說來open方法的作用有3

1、獲得操作的設(shè)備(通過設(shè)備號(hào))

2、進(jìn)一步的初始化設(shè)備

3、初始化file結(jié)構(gòu)體的private_data

staticintsimple_open(structinode?*inodp,structfile?*filp)

{

structsimple_dev?*temp_dev?=?NULL;

intminor?=?0;

#if?SIMPLE_DEBUG

printk(KERN_INFO?"In?%s?\n",?__func__);

#endif

minor?=?iminor(inodp);//獲得操作的設(shè)備的次設(shè)備號(hào)

if(minor?>?DEV_COUNT-1){

printk(KERN_ERR?"the?char?dev?in?invalid?\n");

return-ENODEV;

}

#if?SIMPLE_DEBUG

printk(KERN_INFO?"the?minor?is??%d?\n",?minor);

#endif

temp_dev?=?&char2_dev[minor];//獲得真正操作的設(shè)備

/*?進(jìn)一步?初始化設(shè)備?因?yàn)槭遣僮饕粋€(gè)模擬的設(shè)備?故省去*/

filp->private_data?=?temp_dev;?//初始化?private_data

return0;

}

release方法:

主要是對(duì)open進(jìn)一步初始化的操作的反操作

比如open時(shí)候分配了內(nèi)存,在release時(shí)就需要釋放它等

例子中因?yàn)椴僮鲀?nèi)存設(shè)備,故在release時(shí)無需做什么事

read方法:

read 是把設(shè)備中的數(shù)據(jù)傳遞給調(diào)用者

主要步驟

1、檢測(cè)偏移量有效(有些設(shè)備驅(qū)動(dòng)不需要檢測(cè))

2、檢測(cè)用戶空間地址有效

3、將數(shù)據(jù)傳給用戶(在此步驟中調(diào)用的函數(shù)可能會(huì)自己檢測(cè)步驟2)

4、調(diào)整偏移量

5、返回讀到的數(shù)據(jù)長(zhǎng)度

(read write 用法相對(duì)靈活,不要依賴上邊的步驟,設(shè)備驅(qū)動(dòng)程序要根據(jù)設(shè)備特性去設(shè)計(jì)此方法)

這里先介紹一個(gè)會(huì)檢測(cè)用戶空間地址是否有效的copy函數(shù)

用戶調(diào)用read讀設(shè)備,而在內(nèi)核空間就是將數(shù)據(jù)傳給用戶,是一個(gè)to的操作

unsignedlong__must_check?copy_to_user(void__user?*to,constvoid*from,?unsignedlongn)

__must_check表述必須檢測(cè)其返回值,操作成功返回0,不成功返回負(fù)的錯(cuò)誤碼

to是用戶空間指針 也就是read函數(shù)傳入的用戶空間的指針,

from指向設(shè)備要傳送的數(shù)據(jù)

n標(biāo)識(shí)傳入長(zhǎng)度

上圖是 摘自LDD3上的經(jīng)典視圖, 應(yīng)該比較能說明read的方法

staticssize_t?simple_read(structfile?*filp,char__user?*userstr,size_tcount,?loff_t?*loff)

{

structsimple_dev?*dev?=?NULL;

intdata_remain?=?0;

interr;

#if?SIMPLE_DEBUG

printk(KERN_INFO?"In?%s?\n",?__func__);

#endif

dev?????????=?filp->private_data;

data_remain?=?dev->count?-?*loff;

if(MAX_SIMPLE_LEN?

{

printk(KERN_ERR?"the?offset?is?illegal?in?func?%s?\n",__func__?);

return-EINVAL;

}

elseif(data_remain?<=?0)

{

printk(KERN_WARNING?"there?was?not?much?data?in?the?device\n");

return0;

}

else

{

if(count?>?data_remain)

{

#if?SIMPLE_DEBUG

printk(KERN_INFO?"the?data?is?less?than?the?user?want?to?read\n");

#endif

count?=?data_remain;

}

else

{

}

}

err?=?copy_to_user(userstr,?(dev->data)+(*loff),?count);?//調(diào)用內(nèi)核函數(shù)進(jìn)行數(shù)據(jù)拷貝,它會(huì)檢測(cè)用戶地址是否有效

if(err?!=?0)

{

printk(KERN_ERR?"an?error?occured?when?copy?data?to?user\n");

returnerr;

}

else

{

#if?SIMPLE_DEBUG

printk(KERN_INFO?"data?copy?to?user?OK\n");

#endif

*loff?=?*loff?+?count;?//調(diào)整偏移量

returncount;//返回寫入的數(shù)據(jù)量

}

}

write方法:

與read類似 它是從用戶傳數(shù)據(jù)給設(shè)備驅(qū)動(dòng)

從內(nèi)核空間看就是一個(gè)從用戶空間取數(shù)據(jù) 是一個(gè)from操作

long__must_check?strncpy_from_user(char*dst,constchar__user?*src,longcount)

dst 驅(qū)動(dòng)保存數(shù)據(jù)的地址

src 用戶空間傳入的數(shù)據(jù)

count 標(biāo)識(shí)數(shù)據(jù)長(zhǎng)度

staticssize_t?simple_write(structfile?*filp,constchar__user?*userstr,size_tcount,?loff_t?*loff)

{

structsimple_dev?*dev?=?NULL;

interr;

intremain_space?=?0;

#if?SIMPLE_DEBUG

printk(KERN_INFO?"In?%s\n",__func__);

#endif

dev??????????=?filp->private_data;

if(MAX_SIMPLE_LEN?<=?*loff)//檢測(cè)偏移量

{

printk(KERN_ERR?"the?offset?is?illegal?in?func?%s\n",?__func__);

return-EINVAL;

}

else

{

remain_space?=?MAX_SIMPLE_LEN?-?*loff;

if(count?>?remain_space)

{

#if?SIMPLE_DEBUG

printk(KERN_WARNING?"the?data?is?to?long?to?write?to?the?device\n");

#endif

count?=?remain_space;

}

else

{

}

}

err?=?copy_from_user((dev->data)+(*loff),userstr,count);//取得數(shù)據(jù)

if(err?!=?0)

{

printk(KERN_ERR?"an?error?occured?when?copy?data?from?user\n");

returnerr;

}

else

{

#if?SIMPLE_DEBUG

printk(KERN_INFO?"data?copy?from?user?OK\n");

#endif

*loff?=?*loff?+?count;?//跳著偏移

if(*loff?>?dev->count)

{

dev->count?=?*loff;

}

else

{

}

returncount;//返回寫入的數(shù)據(jù)量

}

}

lseek方法:

根據(jù)用戶傳入的參數(shù)調(diào)整文件偏移

mode

SEEK_SET

從文件起始處開始偏移

SEEK_CUR

從文件當(dāng)前位置計(jì)算偏移

SEEK_END

從文件末尾計(jì)算偏移

file結(jié)構(gòu)的f_pos保存了文件的偏移量

在調(diào)整文件偏移后需要 更新file中得f_pos成員

staticloff_t?simple_llseek(structfile?*filp,?loff_t?loff,intmode)

{

structsimple_dev?*dev?=?NULL;

loff_t?tmp_len;

#if?SIMPLE_DEBUG

printk(KERN_INFO?"In?%s\n",__func__);

#endif

dev??????????=?filp->private_data;

switch(?mode?)

{

caseSEEK_SET:

if(?loff?

{

printk(KERN_ERR?"can't?move?above?file?line?%d?\n",?__LINE__);

return-1;

}

elseif(loff?>?dev->count)

{

printk(KERN_ERR?"offset?is?too?long?line?%d\n",?__LINE__);

return-1;

}

else

{

filp->f_pos?=?loff;

}

break;

caseSEEK_CUR:

if((tmp_len?=?filp->f_pos+loff)?

{

printk(KERN_ERR?"can't?move?above?file?line?%d?\n",?__LINE__);

return-1;

}

elseif(tmp_len?>?dev->count)

{

printk(KERN_ERR?"offset?is?too?long?line?%d\n",?__LINE__);

return-1;

}

else

{

filp->f_pos?=?tmp_len;

}

break;

caseSEEK_END:

if((tmp_len?=?dev->count+loff?)?

{

printk(KERN_ERR?"can't?move?above?file?line?%d?\n",?__LINE__);

return-1;

}

elseif(tmp_len?>?dev->count)

{

printk(KERN_ERR?"offset?is?too?long?line?%d\n",?__LINE__);

return-1;

}

else

{

filp->f_pos?=?tmp_len;

}

break;

default:

printk(KERN_INFO?"illigal?lseek?mode!?\n");

return-1;

break;

}

returnfilp->f_pos;

}

總結(jié)

以上是生活随笔為你收集整理的linux驱动read函数 copytouser,Linux驱动编程 step-by-step (五)主要的文件操作方法实现...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。