日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

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

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

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

文件操作函數(shù)有很多的操作接口,驅(qū)動編程需要實現(xiàn)這些接口,在用戶編程時候系統(tǒng)調(diào)用時候會調(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?*);

...

};

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

本次的測試代碼上傳在:char_step2

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

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

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

structsimple_dev{

chardata[MAX_SIMPLE_LEN];

loff_t?count;

structsemaphore?semp;

};

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

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

open方法:

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

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

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

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

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

2、進一步的初始化設(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è)備號

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è)備

/*?進一步?初始化設(shè)備?因為是操作一個模擬的設(shè)備?故省去*/

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

return0;

}

release方法:

主要是對open進一步初始化的操作的反操作

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

例子中因為操作內(nèi)存設(shè)備,故在release時無需做什么事

read方法:

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

主要步驟

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

2、檢測用戶空間地址有效

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

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

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

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

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

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

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

__must_check表述必須檢測其返回值,操作成功返回0,不成功返回負的錯誤碼

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

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

n標識傳入長度

上圖是 摘自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ù)進行數(shù)據(jù)拷貝,它會檢測用戶地址是否有效

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ū)動

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

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

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

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

count 標識數(shù)據(jù)長度

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)//檢測偏移量

{

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

從文件當前位置計算偏移

SEEK_END

從文件末尾計算偏移

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 (五)主要的文件操作方法实现...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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