『壹』 misc驅動程序框架的本質是什麼
misc驅動程序框架的本質是通過讀寫/dev內的設備文件,來間接訪問file_operations結構體內的函數,從而實現操作硬體。根據查詢相關公開信息顯示,MISC驅動也稱為雜項驅動,是一種簡單的字元設備驅動。當某些外設無法進行分類的時候就會使用MISC驅動,其通常嵌套在platform匯流排驅動中,實現復雜的驅動。
『貳』 linux字元設備驅動簡述-1
Linux系統中,應用程序訪問外設是通過文件的形式來進行的,Linux將所有的外設都看做文件,統一存放在/dev目錄下。
應用程序使用內核提供的標准系統調用來與內核中的驅動程序進行通訊,這些系統調用有:
open(), read(), write(), ioctl(), close() 等等。
file_operations重要的成員
struct inode 結構代表一個實實在在文件,每個文件只對應一個inode;
struct file 結構代表一個打開的文件,同一個文件可以對應多個file結構;
struct file_operations結構代表底層操作硬體函數的集合
找到 first_drv的主設備號是249,如下圖
進階字元設備寫法請看下一篇文章
『叄』 字元驅動和字元設備驅動有什麼區別
可以講字元設備和字元設備驅動歸為一類,它們都是可以順序/隨機地進行讀取和存儲的單元,二者驅動主要在於塊設備需要具體的burst實現,對訪問也有一定的邊界要求。其他的沒有什麼不同。
網路設備是特殊設備的驅動,它負責接收和發送幀數據,可能是物理幀,也可能是ip數據包,這些特性都有網路驅動決定。它並不存在於/dev下面,所以與一般的設備不同。網路設備是一個net_device結構,並通過register_netdev注冊到系統里,最後通過ifconfig -a的命令就能看到。
不論是什麼設備,設備級的數據傳輸都是基本類似的,內核里的數據表示只是一部分,更重要的是匯流排的訪問,例如串列spi,i2c,並行dma等。
『肆』 RTC設備驅動指
RTC(實時鍾)藉助電池供電,在系統掉電的情況下依然可以正常計時。它通常還具有產生周期性中斷以及鬧鍾(Alarm〉中斷的能力,是一種典型的字元設備。作為一種字元設備驅動,RTC需要有file_operations中介面函數的實現,如open () 、release () 、read () 、poll () 、ioctl ()等,而典型的IOCTL包括RTC_SET_TIME、RTC_ALM_READ、RTC_ALM_SET、RTC_IRQP_SET、RTC_IRQP_READ等,這些對於所有的RTC是通用的,只有底層的具體實現是與設備相關的。
因此,drivers/rtc/tc-dev.c實現了RTC驅動通用的字元設備驅動層,它實現了file_opearations的成員函數以及一些通用的關於RTC的控制代碼,並向底層導出rtc_device_register () 、rtc_device_unregister ()以注冊和注銷RTC;導出rtc_class_ops結構體以描述底層的RTC硬體操作。這個RTC通用層實現的結果是,底層的RTC驅動不再需要關心RTC作為字元設備驅動的具體實現,也無需關心一些通用的RTC控制邏輯。
『伍』 字元設備驅動程序由哪幾部分組成
字元設備驅動程序框架
1、寫出open、write函數
2、告訴內核
1)、定義一個struct file_operations結構並填充好
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_mole變數 */
.open = first_drv_open,
.write = first_drv_write,
};
2)、把struct file_operations結構體告訴內核
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注冊, 告訴內核
相關參數:第一個,設備號,0自動分配主設備號,否則為主設備號0-255
第二個:設備名
第二個:struct file_operations結構體
4)、register_chrdev由誰調用(入口函數調用)
static int first_drv_init(void)
5)、入口函數須使用內核宏來修飾
mole_init(first_drv_init);
mole_init會定義一個結構體,這個結構體裡面有一個函數指針指向first_drv_init這個函數,當我們載入或安裝一個驅動時,內核會自動找到這個結構體,然後調用裡面的函數指針,這個函數指針指向first_drv_init這個函數,first_drv_init這個函數就是把struct file_operations結構體告訴內核
6)、有入口函數就有出口函數
mole_exit(first_drv_exit);
最後加上協議
MODULE_LICENSE("GPL");
3、mdev根據系統信息自動創建設備節點:
每次寫驅動都要手動創建設備文件過於麻煩,使用設備管理文件系統則方便很多。在2.6的內核以前一直使用的是devfs,但是它存在許多缺陷。它創建了大量的設備文件,其實這些設備更本不存在。而且設備與設備文件的映射具有不確定性,比如U盤即可能對應sda,又可能對應sdb。沒有足夠的主/輔設備號。2.6之後的內核引入了sysfs文件系統,它掛載在/sys上,配合udev使用,可以很好的完成devfs的功能,並彌補了那些缺點。(這里說一下,當今內核已經使用netlink了)。
udev是用戶空間的一個應用程序,在嵌入式中用的是mdev,mdev在busybox中。mdev是udev的精簡版。
首先在busybox中添加支持mdev的選項:
Linux System Utilities --->
[*] mdev
[*] Support /etc/mdev.conf
[*] Support subdirs/symlinks
[*] Support regular expressions substitutions when renaming device
[*] Support command execution at device addition/removal
然後修改/etc/init.d/rcS:
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
執行mdev -s :以『-s』為參數調用位於 /sbin目錄寫的mdev(其實是個鏈接,作用是傳遞參數給/bin目錄下的busybox程序並調用它),mdev掃描 /sys/class 和 /sys/block 中所有的類設備目錄,如果在目錄中含有名為「dev」的文件,且文件中包含的是設備號,則mdev就利用這些信息為這個設備在/dev 下創建設備節點文件。一般只在啟動時才執行一次 「mdev -s」。
熱插拔事件:由於啟動時運行了命 令:echo /sbin/mdev > /proc/sys/kernel/hotplug ,那麼當有熱插拔事件產生時,內核就會調用位於 /sbin目錄的mdev。這時mdev通過環境變數中的 ACTION 和 DEVPATH,來確定此次熱插拔事件的動作以及影響了/sys中的那個目錄。接著會看看這個目錄中是否「dev」的屬性文件,如果有就利用這些信息為 這個設備在/dev 下創建設備節點文件
重新打包文件系統,這樣/sys目錄,/dev目錄就有東西了
下面是create_class的原型:
#define class_create(owner, name) /
({ /
static struct lock_class_key __key; /
__class_create(owner, name, &__key); /
})
extern struct class * __must_check __class_create(struct mole *owner,
const char *name,
struct lock_class_key *key);
class_destroy的原型如下:
extern void class_destroy(struct class *cls);
device_create的原型如下:
extern struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...)
__attribute__((format(printf, 5, 6)));
device_destroy的原型如下:
extern void device_destroy(struct class *cls, dev_t devt);
具體使用如下,可參考後面的實例:
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
下面再來看一下應用程序如何找到這個結構體的
在應用程序中我們使用open打開一個設備:如:open(/dev/xxx, O_RDWR);
xxx有一個屬性,如字元設備為c,後面為讀寫許可權,還有主設備名、次設備名,我們注冊時 通過register_chrdev(0, "first_drv", &first_drv_fops)(有主設備號,設備名,struct file_operations結構體)將first_drv_fops結構體注冊到內核數組chrdev中去的,結構體中有open,write函數,那麼應用程序如何找到它的,事實上是根據打開的這個文件的屬性中的設備類型及主設備號在內核數組chrdev裡面找到我們注冊的first_drv_fops,
實例代碼:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
static int first_drv_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置GPF4,5,6為輸出 */
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
//printk("first_drv_write\n");
_from_user(&val, buf, count); // _to_user();
if (val == 1)
{
// 點燈
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
}
else
{
// 滅燈
*gpfdat |= (1<<4) | (1<<5) | (1<<6);
}
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_mole變數 */
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注冊, 告訴內核
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸載
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
iounmap(gpfcon);
}
mole_init(first_drv_init);
mole_exit(first_drv_exit);
MODULE_LICENSE("GPL");
編譯用Makefile文件
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` moles
clean:
make -C $(KERN_DIR) M=`pwd` moles clean
rm -rf moles.order
obj-m += first_drv.o
測試程序:
#include
#include
#include
#include
/* firstdrvtest on
* firstdrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
if (argc != 2)
{
printf("Usage :\n");
printf("%s \n", argv[0]);
return 0;
}
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4);
return 0;
}
『陸』 Linux字元設備驅動的組成
在Linux中,字元設備驅動由如下幾個部分組成。
1.字元設備驅動模塊載入與卸載函數
在字元設備驅動模塊載入函數中應該實現設備號的申請和cdev的注冊,而在卸載函數中應實現設備號
的釋放和cdev的注銷。
Linux內核的編碼習慣是為設備定義一個設備相關的結構體,該結構體包含設備所涉及的cdev、私有
數據及鎖等信息。2.字元設備驅動的file_operations結構體中的成員函數
file_operations結構體中的成員函數是字元設備驅動與內核虛擬文件系統的介面,是用戶空間對Linux
進行系統調用最終的落實者。設備驅動的讀函數中,filp是文件結構體指針,buf是用戶空間內存的地址,該地址在內核空間不宜直
接讀寫,count是要讀的位元組數,f_pos是讀的位置相對於文件開頭的偏移。
設備驅動的寫函數中,filp是文件結構體指針,buf是用戶空間內存的地址,該地址在內核空間不宜直
接讀寫,count是要寫的位元組數,f_pos是寫的位置相對於文件開頭的偏移。
由於用戶空間不能直接訪問內核空間的內存,因此藉助了函數_from_user()完成用戶空間緩沖
區到內核空間的復制,以及_to_user()完成內核空間到用戶空間緩沖區的復制,見代碼第6行和第14
行。
完成內核空間和用戶空間內存復制的_from_user()和_to_user()的原型分別為:
unsigned long _from_user(void *to, const void _ _user *from, unsigned long count);
unsigned long _to_user(void _ _user *to, const void *from, unsigned long count);
上述函數均返回不能被復制的位元組數,因此,如果完全復製成功,返回值為0。如果復制失敗,則返
回負值。如果要復制的內存是簡單類型,如char、int、long等,則可以使用簡單的put_user()和
get_user()讀和寫函數中的_user是一個宏,表明其後的指針指向用戶空間,實際上更多地充當了代碼自注釋的
功能。內核空間雖然可以訪問用戶空間的緩沖區,但是在訪問之前,一般需要先檢查其合法性,通過
access_ok(type,addr,size)進行判斷,以確定傳入的緩沖區的確屬於用戶空間。
『柒』 嵌入式開發(七):linux字元型設備驅動初步
姓名:王芷若 學號:19020100180
學院:電子工程學院
【嵌牛導讀】:本篇文章整理Linux知識點—Linux字元型設備驅動初步。
【嵌牛鼻子】:Linux設備類型,結構體,驅動模塊
【嵌牛提問】:Linux設備有什麼類型?關鍵函數有哪些?
【嵌牛內容】–linux字元型設備驅動初步
一、Linux字元設備驅動初步
1、Linux設備類型
(1)字元設備:只能一個位元組一個位元組的讀寫的設備,不能隨機讀取設備內存中的某一數據,讀取數據需要按照先後順序進行。字元設備是面向流的設備,常見的字元設備如滑鼠、鍵盤、串口、控制台、LED等。
(2)塊設備:是指可以從設備的任意位置讀取一定長度的數據設備。塊設備如硬碟、磁碟、U盤和SD卡等存儲設備。
(3)網路設備:網路設備比較特殊,不在是對文件進行操作,而是由專門的網路介面來實現。應用程序不能直接訪問網路設備驅動程序。在/dev目錄下也沒有文件來表示網路設備。
2、開發流程
在這里插入圖片描述
3、關鍵函數講解(以2.6以下版本內核為例)
(1)驅動模塊注冊register_chrdev()函數
原型:register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
major:主設備號,該值為 0 時,自動運行分配。而實際值不是 0 ;
name:設備名稱;
fops:操作函數,實現驅動定義的open、read、write、close等內核函數與應用程序調用的open、read、write、close間的映射;
返回值:
major 值為 0 ,正常注冊後,返回分配的主設備號。如果分配失敗,返回 EBUSY 的負值 ( -EBUSY ) 。major 值若大於 linux/major.h (2.4內核)中聲明的最大值 (#define MAX_CHRDEV 255) ,則返回EINVAL 的負值 (-EINVAL) 。指定 major 值後,若有注冊的設備,返回 EBUSY 的負值 (-EBUSY)。若正常注冊,則返回 0 值
(2)驅動注銷unregister_chrdev()函數
原型:
#include <linux.fs.h>
int unregister_chrdev (unsigned int major, const char *name)
變數:
major 主設備號
name 設備文件
返回值:
major 值若大於 linux/major.h (2.4 內核)中聲明的最大值 (#define MAX_CHRDEV 255),返回 EINVAL的負值 (-EINVAL)。指定了 major的值後,若將要注銷的 major 值並不是注冊的設備驅動程序,返回 EINVAL的負值 ( -EINVAL )。正常注銷則返回 0值。
(3)File_operation結構體
file_operations結構是建立驅動程序和設備編號的連接,內部是一組函數指針,每個打開的文件,也就是file結構,和一組函數關聯,這些操作主要用來實現系統調用的
struct file_operations {
struct mole *owner;//擁有該結構的模塊的指針,一般為THIS_MODULES
loff_t (*llseek) (struct file *, loff_t, int);//用來修改文件當前的讀寫位置
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//從設備中同步讀取數據
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向設備發送數據
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一個非同步的讀取操作
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一個非同步的寫入操作
int (*readdir) (struct file *, void *, filldir_t);//僅用於讀取目錄,對於設備文件,該欄位為NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *); //輪詢函數,判斷目前是否可以進行非阻塞的讀寫或寫入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //執行設備I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系統,將使用此種函數指針代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系統上,32位的ioctl調用將使用此函數指針代替
int (*mmap) (struct file *, struct vm_area_struct *); //用於請求將設備內存映射到進程地址空間
int (*open) (struct inode *, struct file *); //打開
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *); //關閉
int (*fsync) (struct file *, struct dentry *, int datasync); //刷新待處理的數據
int (*aio_fsync) (struct kiocb *, int datasync); //非同步刷新待處理的數據
int (*fasync) (int, struct file *, int); //通知設備FASYNC標志發生變化
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};
『捌』 linux的設備驅動一般分為幾類各有什麼特點
大致分來為三類,字元驅動,塊自設備驅動,網路設備驅動。
字元設備可以看成是用位元組流存取的文件
塊設備則可以看成是可以任意存取位元組數的字元設備,在應用上只是內核管理數據方式不同
網路設備可以是一個硬體設備,或者是軟體設備,他沒有相應的read write,它是面向流的一種特殊設備。
『玖』 linux中什麼是塊設備和字元設備
| 字元設備
字元設備是能夠像位元組流一樣被訪問的設備,當對字元設備發出讀寫請求,相應的IO操作立即發生。Linux系統中很多設備都是字元設備,如字元終端、串口、鍵盤、滑鼠等。在嵌入式Linux開發中,接觸最多的就是字元設備以及驅動。
| 塊設備
塊設備是Linux系統中進行TO操作時必須以塊為單位進行訪問的設備,塊設備能夠安裝文件系統。塊設備驅動會利用一塊系統內存作為緩沖區,因此對塊設備發出讀寫訪問,並不一定立即產生硬體I/O操作。Linux系統中常見的塊設備有如硬碟、軟碟機等等。
| 網路設備
網路設備既可以是網卡這樣的硬體設備,也可以是一個純軟體設備如回環設備。網路設備由Linux的網路子系統驅動,負責數據包的發送和接收,而不是面向流設備,因此在Linux系統文件系統中網路設備沒有節點。對網路設備的訪問是通過socket調用產生,而不是普通的文件操作如
open/closc和 read/write等。
『拾』 字元設備和塊設備
提供連續的數據流,是一個線性設備,應用程序可以順序讀取,通常不支持隨機存取。相反,此類設備支持按位元組/字元來讀寫數據。舉例來說,數據機是典型的字元設備。
如:鍵盤、滑鼠、顯示屏
應用程序可以隨機訪問設備數據,程序可自行確定讀取數據的位置。硬碟是典型的塊設備,應用程序可以定址磁碟上的任何位置,並由此讀取數據。此外,數據的讀寫只能以塊(通常是512B)的倍數進行。與字元設備不同,塊設備並不支持基於字元的定址。
如:硬碟、U盤
兩種設備本身並沒用嚴格的區分,主要是字元設備和塊設備驅動程序提供的訪問介面(file I/O API)是不一樣的
Linux的設備管理是和文件系統緊密結合的,各種設備都以文件的形式存放在/dev目錄下,稱為設備文件。應用程序可以打開、關閉和讀寫這些設備文件,完成對設備的操作,就像操作普通的數據文件一樣。為了管理這些設備,系統為設備編了號,每個設備號又分為主設備號和次設備號。 主設備號用來區分不同種類的設備,而次設備號用來區分同一類型的多個設備。 對於常用設備,Linux有約定俗成的編號,如硬碟的主設備號是3。
一個字元設備或者塊設備都有一個主設備號和次設備號。主設備號和次設備號統稱為設備號。主設備號用來表示一個特定的驅動程序。次設備號用來表示使用該驅動程序的各設備。 例如一個嵌入式系統,有兩個LED指示燈,LED燈需要獨立的打開或者關閉。那麼,可以寫一個LED燈的字元設備驅動程序,可以將其主設備號注冊成5號設備,次設備號分別為1和2。這里,次設備號就分別表示兩個LED燈。
一般的, 主設備號標識出與設備關聯的設備驅動 。如 /dev/null 和 /dev/port 由 1 號驅動來管理。
現在的 Linux 內核允許多個驅動共享一個主設備號,但更多的設備都遵循一個驅動對一個主設備號的原則。
內核由 次設備號確定當前所指向的是哪個設備。 根據所編寫的驅動程序,可以從內核那裡得到一個直接指向設備的指針,或者使用次設備號作為一個設備本地數組的索引。但不論如何,內核自身幾乎不知道次設備號的什麼事情。
當靜態分配設備號時,需要查看系統中已經存在的設備號,從而決定使用哪個新設備號。可以讀取/proc/devices文件獲得設備的設備號。/proc/devices文件包含字元設備和塊設備的設備號,如下所示:
用於創建Linux中的字元設備文件和塊設備文件。