❶ 字元型驅動設備是怎麼創建設備文件的,發分別列舉手動和自動
1手動mknod
2 自動調用函數
❷ 簡述LINUX驅動中字元設備和塊設備的區別
在Linux驅動中,字元設備和塊設備是兩種不同的設備類型,它們則虛的區別主要在於數據傳輸的方式和設備訪問的方式。
字元設備
字元設備是一種按字元為單位進行數據傳輸的設備,例如串口、鍵盤、滑鼠等。它們通常以流的形式傳輸數據,每次讀寫操作都只能讀寫一個字元。字元設備的訪問方式是困磨隨機訪問,即可以在任何位置讀孫尺燃寫數據。在Linux中,字元設備通常使用文件系統的方式進行訪問,即將字元設備映射為文件,然後通過文件操作函數進行訪問。
塊設備
塊設備是一種按塊為單位進行數據傳輸的設備,例如硬碟、U盤等。它們通常以塊的形式傳輸數據,每次讀寫操作可以讀寫多個塊。塊設備的訪問方式是順序訪問,即必須按照塊的順序進行讀寫操作。在Linux中,塊設備通常使用塊設備驅動程序進行訪問,驅動程序將塊設備映射為塊設備文件,然後通過塊設備操作函數進行訪問。
總的來說,字元設備和塊設備的主要區別在於數據傳輸的方式和設備訪問的方式。字元設備以字元為單位進行數據傳輸,支持隨機訪問;塊設備以塊為單位進行數據傳輸,支持順序訪問。在Linux驅動中,字元設備和塊設備通常使用不同的驅動程序進行訪問。
❸ 字元設備驅動程序由哪幾部分組成
字元設備驅動程序框架
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 device driver 的概念系統調用是操作系統內核和應用程序之間的介面,設備驅動程序是操作系統內核和機器硬體之間的介面.設備驅動程序為應用程序屏蔽了硬體的細節,這樣在應用程判梁序看來,硬體設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬體設備進行操作.設備驅動程序是內核的一部分,它完成以下的功能:
1、對設備初始化和釋放;
2、把數據從內核傳送到硬體和從硬體讀取數據;
3、讀取應用程序傳送給設備文件的數據和回送應用程序請求的數據;
4、檢測和處理設備出現的錯誤.
在Linux操作系統下枯鬧有三類主要的設備文件類型,一是字元設備,二是塊設備,三是網路設備.字元設備和塊設備的主要區別是:在對字元設備發出讀/寫請求時,實際的硬體I/O一般就緊接著發生了,塊設備則不然,它利用一塊系統內存作緩沖區,當用戶進程對設備請求能滿足用戶的要求,就返回請求的數據,如果不能,就調用請求函數來進行實際的I/O操作.塊設備是主要針對磁碟等慢速設備設計的,以免耗費過多的CPU時間來等待.
已經提到,用戶進程是通過設備文件來與實際的硬體打交道.每個設備文件都都有其文件屬性(c/b),表示是字元設備還是塊設備?另外每個文件都有兩個設備號,第一個是主設備號,標識驅動程序,第二個是從設備號,標識使用同一個設備驅動程序的不同的硬體設備,比如有兩個軟盤,就可以用從設備號來區分他們.設備文件的的主設備號必須與設備驅動程序在登記時申請的主設備號一致,否則用戶進程將無法訪問到驅動程序.
最後必須提到的是,在用戶進程調用驅動程序時,系統進入核心態,這時不再是搶先式調度.也就是說,系統必須在你的驅動程序的子函數返回後才能進行其他的工作.如果你的驅動程序陷入死循環,不幸的是你只有重新啟動機器了,然後就是漫長的fsck.
二、實例剖析
我們來寫一個最簡單的字元設備驅動掘敗運程序.雖然它什麼也不做,但是通過它可以了解Linux的設備驅動程序的工作原理.把下面的C代碼輸入機器,你就會獲得一個真正的設備驅動程序.
由於用戶進程是通過設備文件同硬體打交道,對設備文件的操作方式不外乎就是一些系統調用,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系統調用和驅動程序關聯起來呢?這需要了解一個非常關鍵的數據結構:
STruct file_operatiONs {
int (*seek) (struct inode * ,struct file *, off_t ,int);
int (*read) (struct inode * ,struct file *, char ,int);
int (*write) (struct inode * ,struct file *, off_t ,int);
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
int (*select) (struct inode * ,struct file *, int ,select_table *);
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
int (*open) (struct inode * ,struct file *);
int (*release) (struct inode * ,struct file *);
int (*fsync) (struct inode * ,struct file *);
int (*fasync) (struct inode * ,struct file *,int);
int (*check_media_change) (struct inode * ,struct file *);
int (*revalidate) (dev_t dev);
}
這個結構的每一個成員的名字都對應著一個系統調用.用戶進程利用系統調用在對設備文件進行諸如read/write操作時,系統調用通過設備文件的主設備號找到相應的設備驅動程序,然後讀取這個數據結構相應的函數指針,接著把控制權交給該函數.這是linux的設備驅動程序工作的基本原理.既然是這樣,則編寫設備驅動程序的主要工作就是編寫子函數,並填充file_operations的各個域.
下面就開始寫子程序.
#include
❺ 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)進行判斷,以確定傳入的緩沖區的確屬於用戶空間。