(七)linux函數接口的使用

前面我們講解了字符設備的驅動模型,有了前面的基礎后,今天學習函數接口就比較容易了

目錄

        • (一)open函數接口
        • (二)read函數接口
        • (三)lseek函數接口
        • (四)用戶空間和用戶空間交換數據
        • (五)通過設備節點提取設備號
        • (六)映射ioremap
        • (七)實例:LED驅動編程

思考一個問題:當我們應用層調用open、read、write、close的時候,內核層是如何實現的呢?
前面學習字符設備驅動模型中有一個file_operation結構體,當我們調用open函數的時候,內核會調用file_operation結構體的open函數指針指向的函數。

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

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);  //llseek對應了系統提供的lseek接口,實現函數指針位置的定位ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//當用戶層調用系統層提供的read接口的時候需要通過此函數指針所指向的接口來實現對應的操作ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//當用戶層調用系統層提供的write接口的時候需要通過此函數指針所指向的接口來實現對應的操作unsigned int (*poll) (struct file *, struct poll_table_struct *);//  當需要進行輪詢操作的時候調用的底層接口,對應了系統層的select和pollint (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);//struct inode *:內核內部用來標識文件的數據結構  int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*fasync) (int, struct file *, int);
};

(一)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 <unistd.h>
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 <sys/types.h>
#include <unistd.h>
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)
{//獲取偏移量需要offset和whence結合loff_t newoff=0;switch(whence){case SEEK_SET: newoff=offset;break;case SEEK_CUR: newoff=fp->f_pos+offset;break;case SEEK_END: newoff=offset+4;break;}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)
{to:用戶空間的地址from:內核空間的地址n:傳遞數據的大小 might_sleep();#define VERIFY_WRITE 1if (access_ok(VERIFY_WRITE, to, n))return __copy_to_user(to, from, n);elsereturn n;
}

copy_from_user:將用戶空間的數據拷貝到內核空間

static inline long copy_from_user(void *to,const void __user * from, unsigned long n)
{might_sleep();if (access_ok(VERIFY_READ, from, n))return __copy_from_user(to, from, n);elsereturn n;
}

(五)通過設備節點提取設備號

//通過設備節點提取次設備號
static inline unsigned iminor(const struct inode *inode)
{return MINOR(inode->i_rdev);
}
//通過設備節點提取次主設備號
static inline unsigned imajor(const struct inode *inode)
{return MAJOR(inode->i_rdev);
}

(六)映射ioremap

程序中在操作物理硬件地址的時候不要直接操作對應的地址,需要先進行映射操作

static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
{return (void __iomem*) (unsigned long)offset;
}
typedef u32 phys_addr_t;
phys_addr_t offset:指的是映射的物理地址
unsigned long size:映射空間的大小
void __iomem *:接收映射后的起始地址

解除映射:

void iounmap (volatile void __iomem *addr)

(七)實例:LED驅動編程

思路:
首先把需要操作的寄存器物理地址進行映射,然后在open函數中做初始化工作,最后在read/write函數中調用copy_to/from_user函數將用戶空間(內核空間)的數據拷貝到內核空間(用戶空間),對數據進行操作
led.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>int i=0;
dev_t dev=0;
#define CDEVCOUNT 5
#define CDEVNAME "cdevdevice"
#define CDEVCLASS "myclass"
#define INODENAME "mycdev"#define ADDRSZIE 8unsigned 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)
{//清空配置寄存器GPM4CON &= ~(0XFFFF<<0);//設置引腳為輸出狀態GPM4CON |= (0x1111<<0);//給指定寄存器初始化GPM4DAT |= (0x0F<<0);printk("cdev_open is install\n");return 0;
}
ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{printk("cdev_read is install\n");return 0;
}
ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset)
{int i=0;char str[4]={-1,-1,-1,-1};int ret =copy_from_user(str,buf,4);for(i=0;i<4;i++){if(str[i]=='0')GPM4DAT |= (1<<i);else if(str[i]=='1')GPM4DAT &=~(1<<i);}printk("cdev_write is install\n");return 0;
}
int cdev_release (struct inode *node, struct file *fp)
{printk("cdev_release is install\n");return 0;
}
struct file_operations fop={.open=cdev_open,.read=cdev_read,.write=cdev_write,.release=cdev_release,
};
static int __init cdev_module_init(void)
{int ret=alloc_chrdev_region(&dev, 0, CDEVCOUNT, CDEVNAME);if(ret){return -1;}cdev=cdev_alloc();if (!cdev)goto out;cdev_init(cdev, &fop);if(cdev_add(cdev, dev, CDEVCOUNT)){goto out1;}printk("cdev_add success\n");cdevclass=class_create(THIS_MODULE,CDEVCLASS);if (IS_ERR(cdevclass)){goto out2;}printk("class_create success\n");for(i=0;i<5;i++)device_create(cdevclass,NULL, dev+i, NULL, "mycdev%d",i );printk("device_create success\n");virt_addr = ioremap(phy_addr, ADDRSZIE);return 0;out:unregister_chrdev_region(dev,CDEVCOUNT);return -2;out1:unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);out2:cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return PTR_ERR(cdevclass);
}
static void __exit cdev_module_cleanup(void)
{for(--i;i>=0;i--)device_destroy(cdevclass,dev+i);printk("device_destroy success\n");class_destroy(cdevclass);cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);iounmap(virt_addr);printk("kfree success\n");
}
module_init(cdev_module_init);
module_exit(cdev_module_cleanup);
MODULE_LICENSE("GPL");

led_app.c

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{char str[]={'1','1','1','0'};int fd= open(argv[1],O_RDWR);if(fd== -1){perror("open");return -1;}write(fd,str,4);close(fd);return 0;
}

Makefile

CFLAG =-C
TARGET = led
TARGET1 = led_app
KERNEL = /mydriver/linux-3.5
obj-m += $(TARGET).oall:make $(CFLAG)  $(KERNEL) M=$(PWD)arm-linux-gcc -o $(TARGET1) $(TARGET1).c
clean:make $(CFLAG)  $(KERNEL) M=$(PWD) clean

本文章僅供學習交流用禁止用作商業用途,文中內容來水枂編輯,如需轉載請告知,謝謝合作

微信公眾號:zhjj0729

微博:文藝to青年

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531414.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531414.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531414.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(八)linux驅動之ioctl的使用

這篇文章給大家講解一下ioctl的簡單使用&#xff0c;關于ioctl更詳細的教程后面有機會單獨寫出來 &#xff08;一&#xff09;什么是ioctl ioctl是設備驅動程序中對設備的I/O通道進行管理的函數。所謂對I/O通道進行管理&#xff0c;就是對設備的一些特性進行控制&#xff0c;例…

(九)linux中斷編程

目錄&#xff08;一&#xff09;linux中斷的介紹&#xff08;二&#xff09;內核中斷的操作過程&#xff08;三&#xff09;實例代碼&#xff08;一&#xff09;linux中斷的介紹 linux內核中的中斷通過中斷子系統來管理。linux系統中有專門的中斷子系統&#xff0c;原理很復雜…

網絡爬蟲(1)

參考&#xff1a;http://www.cnblogs.com/dongkuo/p/4851735.html算法分析我們現在從需求中提取關鍵詞來逐步分析問題。 首先是“種子節點”。它就是一個或多個在爬蟲程序運行前手動給出的URL&#xff08;網址&#xff09;&#xff0c;爬蟲正是下載并解析這些種子URL指向的頁面…

(十)Linux之等待隊列

&#xff08;一&#xff09;阻塞和非阻塞 阻塞&#xff1a;執行設備操作時&#xff0c;若不能獲得資源&#xff0c;則掛起進程進入休眠直到滿足可操作的條件后再操作。 非阻塞&#xff1a;進程在不能進行設備操作時&#xff0c;并不掛起&#xff0c;它要么放棄&#xff0c;要么…

(十一)linux之poll輪詢

目錄&#xff08;一&#xff09;poll輪詢的作用&#xff08;二&#xff09;poll輪詢相關的接口&#xff08;三&#xff09;poll使用流程&#xff08;四&#xff09;實例代碼&#xff08;一&#xff09;poll輪詢的作用 以阻塞的方式打開文件&#xff0c;那么對多個文件讀寫時&a…

校驗碼(海明校驗,CRC冗余校驗,奇偶校驗)

循環冗余校驗碼 CRC碼利用生成多項式為k個數據位產生r個校驗位進行編碼,其編碼長度為nkr所以又稱 (n,k)碼. CRC碼廣泛應用于數據通信領域和磁介質存儲系統中. CRC理論非常復雜,一般書就給個例題,講講方法.現在簡單介紹下它的原理: 在k位信息碼后接r位校驗碼,對于一個給定的(n,k…

(十二)linux內核定時器

目錄&#xff08;一&#xff09;內核定時器介紹&#xff08;二&#xff09;內核定時器相關接口&#xff08;三&#xff09;使用步驟&#xff08;四&#xff09;實例代碼&#xff08;一&#xff09;內核定時器介紹 內核定時器并不是用來簡單的定時操作&#xff0c;而是在定時時…

java Proxy(代理機制)

我們知道Spring主要有兩大思想&#xff0c;一個是IoC&#xff0c;另一個就是AOP&#xff0c;對于IoC&#xff0c;依賴注入就不用多說了&#xff0c;而對于Spring的核心AOP來說&#xff0c;我們不但要知道怎么通過AOP來滿足的我們的功能&#xff0c;我們更需要學習的是其底層是怎…

(十三)linux中斷底半部分處理機制

這篇文章介紹一下linux中斷的底半部分的tasklet和workquene兩種處理機制&#xff0c;其中tasklet中不能有延時函數&#xff0c;workquene的處理函數可以加入延時操作 目錄&#xff08;一&#xff09;tasklet小任務處理機制&#xff08;1&#xff09;tasklet相關函數接口&#x…

Codeforces Round #326 (Div. 2) B. Pasha and Phone C. Duff and Weight Lifting

B. Pasha and PhonePasha has recently bought a new phone jPager and started adding his friends phone numbers there. Each phone number consists of exactly n digits. Also Pasha has a number k and two sequences of length n?/?k (n is divisible by k) a1,?a2,?…

vmware中裝的ubuntu上不了網

本文章針對橋接方式進行講解&#xff0c;如果需要另外兩種連接方式請參考文末給出的鏈接 &#xff08;一&#xff09;問題 主機和虛擬機可以相互ping通&#xff0c;但是卻不能ping網址 &#xff08;二&#xff09;解決辦法 vmware為我們提供了三種網絡工作模式&#xff0c;…

document.getElementById()與 $()區別

document.getElementById()返回的是DOM對象&#xff0c;而$()返回的是jQuery對象 什么是jQuery對象&#xff1f; ---就是通過jQuery包裝DOM對象后產生的對象。jQuery對象是jQuery獨有的&#xff0c;其可以使用jQuery里的方法。 比如&#xff1a; $("#test").html() 意…

關于gedit的編碼問題

今天由于gedit的編碼格式導致LCD顯示屏的問題&#xff0c;開始沒有想到后來才發現&#xff0c;在這記錄一下 #include <stdio.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h>…

c語言表白程序代碼

雙十一要到了&#xff0c;好激動啊&#xff01;&#xff01;&#xff01; 是時候準備出手了&#xff01; 花了一天的時間寫的表白代碼。 表示自己弱弱的..... 看了網上好多都是js寫的&#xff0c;感覺碉堡了&#xff01;js用的不熟&#xff0c;前端不好&#xff0c;java&#x…

tiny4412移植tslib庫

1、將tslib-1.4.tar.gz拷貝到虛擬機某個路徑進行解壓 2、進入解壓路徑tslib 3、執行#./autogen.sh 如果提示&#xff1a;./autogen.sh: 4: ./autogen.sh: autoreconf: not found 原因&#xff1a;沒有安裝automake工具, 解決辦法:需要安裝此工具&#xff1a; apt-get instal…

移植QT到tiny4412開發板

目錄&#xff08;一&#xff09; 環境準備&#xff08;二&#xff09; Qt源代碼下載&#xff08;三&#xff09; 移植tslib庫&#xff08;四&#xff09;操作流程1.解壓qt源碼包2.配置編譯環境3.生成Makefile4.編譯安裝5.安裝一些庫用來支持 qt6. 添加以下內容到開發板目錄下的…

c++面試常用知識(sizeof計算類的大小,虛擬繼承,重載,隱藏,覆蓋)

一. sizeof計算結構體 注&#xff1a;本機機器字長為64位 1.最普通的類和普通的繼承 #include<iostream> using namespace std;class Parent{ public:void fun(){cout<<"Parent fun"<<endl;} }; class Child : public Parent{ public:void fun(){…

嵌入式面試題(一)

目錄1 關鍵字volatile有什么含義&#xff1f;并給出三個不同的例子2. c和c中的struct有什么不同&#xff1f;3.進程和線程區別4.ARM流水線5.使用斷言6 .嵌入式系統的定義7 局部變量能否和全局變量重名&#xff1f;8 如何引用一個已經定義過的全局變量&#xff1f;9、全局變量可…

能ping通ip但無法ping通域名和localhost //ping: bad address 'www.baidu.com'

錯誤描述&#xff1a; ~ # ping localhost ping: bad address localhost原因&#xff0c;在/etc目錄下缺少hosts文件&#xff0c;將linux中的/etc hosts文件拷入即可 ~ # ping localhost PING localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: seq0 ttl64 tim…

eclipse導入web項目之后項目中出現小紅叉解決辦法

項目中有小紅叉我遇到的最常見的情況&#xff1a; 1、項目代碼本身有問題。&#xff08;這個就不說了&#xff0c;解決錯誤就OK&#xff09; 2、項目中的jar包丟失。&#xff08;有時候eclipse打開時會出現jar包丟失的情況&#xff0c;關閉eclipse重新打開或者重新引入jar包就O…