『壹』 請問Linux驅動程序中,字元設備驅動,塊設備驅動以及網路驅動的區別和比較,學的時候需要注意些什麼
可以講字元設備和塊設備歸為一類,它們都是可以順序/隨機地進行讀取和存儲的單元,二者驅動主要在於塊設備需要具體的burst實現,對訪問也有一定的邊界要求。其他的沒有什麼不同。
網路設備是特殊設備的驅動,它負責接收和發送幀數據,可能是物理幀,也可能是ip數據包,這些特性都有網路驅動決定。它並不存在於/dev下面,所以與一般的設備不同。網路設備是一個net_device結構,並通過register_netdev注冊到系統里,最後通過ifconfig -a的命令就能看到。
不論是什麼設備,設備級的數據傳輸都是基本類似的,內核里的數據表示只是一部分,更重要的是匯流排的訪問,例如串列spi,i2c,並行dma等。
『貳』 嵌入式開發(七):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字元設備驅動簡述-1
Linux系統中,應用程序訪問外設是通過文件的形式來進行的,Linux將所有的外設都看做文件,統一存放在/dev目錄下。
應用程序使用內核提供的標准系統調用來與內核中的驅動程序進行通訊,這些系統調用有:
open(), read(), write(), ioctl(), close() 等等。
file_operations重要的成員
struct inode 結構代表一個實實在在文件,每個文件只對應一個inode;
struct file 結構代表一個打開的文件,同一個文件可以對應多個file結構;
struct file_operations結構代表底層操作硬體函數的集合
找到 first_drv的主設備號是249,如下圖
進階字元設備寫法請看下一篇文章
『肆』 求助字元驅動linux
你這個有點亂哦。。看得頭暈得很。。 還有你這里錯誤的主要地方是。汪孫編寫字元設備模塊的時候不需要再制定交叉編譯器。因為實行makemoles 的時候孝陵衫會找到你內核裡面頂層makefile 裡面的交叉編譯器。
我這里有一個簡單的字元設備模塊。不妨可以借鑒看一下。需要自己手動添加一個設備節點。我程序裡面使用的是主設備號是240 次設備號是0.
原文地址 通過測試:不過還有一點小問題,就是 模塊的卸載需要使用rmmod mymod 不加.ko 具體原因我也沒弄明白。這裡麵包含了 字元設備file_operations 函數巧腔。但是用戶層的應用需要你自己完成。
http://www.ebhou.com/post/chrcdev.html
#include <linux/init.h>
#include <linux/mole.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
//設備驅動應該包含的頭文件
#define MYMODE_SIZE 0x1000
#define MYMODE_MAJOR 240
#define MEM_CLEAR 0x1
static int mymode_major = MYMODE_MAJOR; //設備主節點號
struct mymode_dev{ //定義 設備結構體
struct cdev cdev;
unsigned char mem[MYMODE_SIZE];
};
struct mymode_dev *mymode; //設備結構體指針
static ssize_t mymode_read(struct file * filp,char __user *buf,size_t size,loff_t *ppos) //設備讀操作函數
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mymode_dev *modedev = filp->private_data;
if(p>MYMODE_SIZE)
{
printk(KERN_INFO"err of poss %d\n",p);
return 0;
}
if(count > (MYMODE_SIZE-p))
count = MYMODE_SIZE-p;
if(_to_user(buf,(void *)(modedev->mem+p),count)){
ret = - EFAULT;
}else{
*ppos +=count;
ret = count;
printk(KERN_INFO"read %d bytes from %d\n",count,p);
}
return ret;
}
static ssize_t mymode_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos) //設備寫操作函數
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mymode_dev *modedev = filp->private_data;
if(count > (MYMODE_SIZE-p))
count = MYMODE_SIZE-p;
if(_from_user((modedev->mem+p),buf,count)){
ret = - EFAULT;
}else{
*ppos +=count;
ret = count;
printk(KERN_INFO"write %d bytes from %d\n",count,p);
}
return ret;
}
static loff_t mymode_llseek(struct file *filep,loff_t offset,int orig) //設置偏移定位操作函數
{
loff_t ret = 0;
switch(orig)
{
case 0:
if(offset < 0 )
{
printk(KERN_INFO"ERR OFFSET\n");
ret = - EINVAL;
break;
}
if(offset >= MYMODE_SIZE)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;
}
filep->f_pos = offset;
ret = offset;
break;
case 1:
if(filep->f_pos + offset > MYMODE_SIZE-1)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;
}else if(filep->f_pos + offset <0){
filep->f_pos = 0;
ret = 0 ;
break;
}
filep->f_pos += offset;
ret = offset;
break;
case 2:
if(filep->f_pos + offset > MYMODE_SIZE-1)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;
}else if(filep->f_pos + offset <0){
filep->f_pos = 0;
ret = 0 ;
break;
}
filep->f_pos += offset;
ret = offset;
break;
default:
break;
}
}
static int mymode_ioctl(struct inode *inodep,struct file *filep,unsigned int cmd ,unsigned long arg) //設備ioctl 函數只簡單的實現了對內存區域的清0 當然你也可以自己添加一些你想要實現的功能。不管怎樣你得電腦你做主。
{
struct mymode_dev *modedev = filep->private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(modedev->mem,0,MYMODE_SIZE);
printk(KERN_INFO"Clear the mem to be zero\n");
break;
default:
printk(KERN_INFO"Clear the mem to be zero\n");
break;
}
return 0;
}
static int mymode_open(struct inode *inodep,struct file *filep)
{
filep->private_data = mymode;
return 0;
;
}
static int mymode_release(struct inode *inodep,struct file *filep)
{
return 0;
}
static const struct file_operations mymode_fops={ // cdev 設備的file_operations 結構體 主要實現了 開關 讀寫 設置偏移量 和ioctl 函數
.owner = THIS_MODULE,
.llseek = mymode_llseek,
.read = mymode_read,
.write = mymode_write,
.ioctl = mymode_ioctl,
.open = mymode_open,
.release = mymode_release,
};
int mymode_init(void) //模塊載入初始化函數。設備的入口函數
{
int err,res;
dev_t devno = MKDEV(mymode_major,0);
if(mymode_major)
res = register_chrdev_region(&devno,1,"mymode");//register the cdev no
else{
res = alloc_chrdev_region(&devno,0,1,"mymode");
mymode_major = MAJOR(devno);
}
if(res < 0)
return res;
mymode = kmalloc(sizeof(struct mymode_dev),GFP_KERNEL);
if(mymode == NULL)
{
printk(KERN_INFO"kmalloc failed\n");
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);
return -ENOMEM;
}
memset(mymode,0,sizeof(struct mymode_dev));
cdev_init(&mymode->cdev,&mymode_fops);
mymode->cdev.owner = THIS_MODULE;
err = cdev_add(&mymode->cdev,devno,1);
if(err){
printk("Error %d add mymode ",err);
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);
}
printk("mymode insert\n");
return 0;
}
void mymode_exit(void)//模塊卸載函數 其主要內容是相對於設備初始化函數的反操作。
{
cdev_del(&mymode->cdev);
kfree(mymode);
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);
printk("mymode exit\n");
}
mole_init(mymode_init);
mole_exit(mymode_exit);
MODULE_AUTHOR("pink hou ");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("First Mole");
MODULE_ALIAS("A simplest mole");
這個是完整的C代碼 具體的makefile 參考我的blog借鑒一下吧。這是我通過驗證的 絕對沒問題。
原文地址:http://www.ebhou.com/post/chrcdev.html
『伍』 求助,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
『陸』 字元型設備驅動如何編譯
字元設備驅動程序框架
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;
}
『柒』 arm上linux如何正確的注冊字元設備的驅動程序
奇怪,看不出有問題
『捌』 字元設備和塊設備
提供連續的數據流,是一個線性設備,應用程序可以順序讀取,通常不支持隨機存取。相反,此類設備支持按位元組/字元來讀寫數據。舉例來說,數據機是典型的字元設備。
如:鍵盤、滑鼠、顯示屏
應用程序可以隨機訪問設備數據,程序可自行確定讀取數據的位置。硬碟是典型的塊設備,應用程序可以定址磁碟上的任何位置,並由此讀取數據。此外,數據的讀寫只能以塊(通常是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中的字元設備文件和塊設備文件。
『玖』 linux用mknod怎麼創建設備怎麼用
首先要明白什麼是設備文件,簡單的我們說,操作系統與外部設備(入磁碟驅動器,列印機,modern,終端 等等)都是通過設備文件來進行通信的,在Unix/Linux系統與外部設備通訊之前,這個設備必須首先要有一個設備文件,設備文件均放在/dev目錄下。
一般情況下在安裝系統的時候系統自動創建了很多已檢測到的設備的設備文件,但有時候我們也需要自己手動創建,命令行生成設備文件的方式有 insf,mksf,mknod等等
根據mknod命令的使用參數來看【mknod Name { b | c } Major Minor 】,使用mknod之前,至少要明白以下幾點:
設備文件類型:分為塊設備和字元設備。ls -l /dev 結果顯示第一個欄位有b*** 和 c****,這里即標識了塊設備和字元設備。
字元設備文件----字元設備文件傳送數據給設備的時候,一次傳送一個字元,終端,列印機,繪圖儀,modern等設備都經過字元設備文件傳送數據
塊設備---系統通過塊設備文件存取一個設備的時候,先從內存中的buffer中讀或寫數據,而不是直接傳送數據到物理磁碟,這種方式能有效的提高磁碟和CD-ROMS的I/O性能。磁碟和CD-ROMS即可以使用字元設備文件也可使用塊設備文件。
主號和次號
主號:當在任意目錄使用ls -l 時,結果的第5個欄位就是主號,設備主號代表了這個設備使用的是哪個設備驅動程序
次號:次號是一個24位的十六進制數字,定義了設個設備在系統中的物理的位置
就拿我們常用的創建卷組來看:
先來看看mknod 命令,如果該設備文件你想放在一個特定的文件夾下當然就先創建文件夾
mknod 設備文件名[/dev/xyz] b/c 主號 次號
{ mkdir /dev/vg01
mknod /dev/vg01/group c 64 0X010000}
創建之後,就可以使用你想要創建的設備對於德創建命令了,如我現在的卷組的創建命令:
vgcreate /dev/vg01 /dev/dsk/c*t*d*
一直進行下去,之後的步驟根據不同的設備而不盡相同。
『拾』 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)進行判斷,以確定傳入的緩沖區的確屬於用戶空間。