字元裝置驅動編寫流程以及大概框架
阿新 • • 發佈:2019-01-09
Linux裝置驅動:
Linux裝置驅動分為以下三類:
(1)字元裝置:鍵盤,印表機
(2)塊裝置:硬碟,NAND
(3)網路裝置:網絡卡
對於字元裝置是最基本,最常見的裝置:
對字元裝置的驅動主要完成以下動作:
1、定義一個結構體static struct file_operations變數,其內定義一些裝置的open,read,write,close等控制函式
2、在結構體外分別實現結構體中定義的這些函式
3、向核心中註冊或刪除驅動模組
塊裝置與字元裝置的驅動結構是不同的,但是對於使用者來說沒有什麼區別,塊裝置比字元裝置要複雜,在I/O操作上極為不同表現在緩衝,I/O排程,請求佇列等。
1、操作的硬體介面實現不一樣;
2、資料塊的資料有一定的格式
網路裝置不同於字元和塊裝置,但是有字元和快裝置的部分功能
字元設備註冊有兩種方式,可以以混雜設備註冊:
字元裝置步驟:
(1)分配cdev: struct cdev *cdev_alloc(void);
(2)初始化cdev: void cdev_init(struct cdev *cdev, const struct file_operations *fops);
(3)新增cdev: int cdev_add(struct cdev *p, dev_t dev, unsigned count)
在2.6版本之前字元設備註冊方式:
int register_chrdev(unsigned int major,const char*name,struct file_operations *fops);
其中引數major如果等於0,則表示採用系統動態分配的主裝置號;不為0,則表示靜態註冊。
major 是感興趣的主裝置號, name 是驅動的名子(出現在 /proc/devices), fops 是預設的 file_operations 結構.
一個對 register_chrdev 的呼叫為給定的主編號註冊 0 - 255 的次編號,並且為每一個建立一個預設的 cdev 結構.
使用這個介面的驅動必須準備好處理對所有 256 個次編號的 open 呼叫( 不管它們是否對應真實裝置 ),
它們不能使用大於 255 的主或次編號.register_chrdev函式的major引數如果等於0,則表示採用系統動態分配的主裝置號。
它所做的事情為:
(1). 註冊裝置號, 通過呼叫 __register_chrdev_region() 來實現
(2). 分配一個cdev, 通過呼叫 cdev_alloc() 來實現
(3). 將cdev新增到驅動模型中, 這一步將裝置號和驅動關聯了起來. 通過呼叫 cdev_add() 來實現
(4). 將第一步中建立的 struct char_device_struct 物件的 cdev 指向第二步中分配的cdev. 由於register_chrdev()是老的介面,這一步在新的介面中並不需要.
登出介面函式
int unregister_chrdev(unsignedintmajor,constchar*name)
註冊程式碼簡單流程:
在init函式中註冊字元裝置
#define DEV_NAME "i2c"
#define IIC_MAJOR 145
static int __init init_2420(void)
{
int result;
....
result = register_chrdev(IIC_MAJOR,DEV_NAME,&fops);
...
}
static void __exit eit_2420(void)
{
unregister_chrdev(IIC_MAJOR,DEV_NAME);
}
在2.6新版本中介面函式分別改為
int register_chrdev_region(dev_t first, unsigned int count, char *name);
void unregister_chrdev_region(dev_t first, unsigned int count);
1.一個 cdev 一般它有兩種定義初始化方式:靜態的和動態的。
(1)靜態記憶體定義初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
(2)動態記憶體定義初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;
兩種使用方式的功能是一樣的,只是使用的記憶體區不一樣,一般視實際的資料結構需求而定。
2.註冊一個獨立的cdev裝置的基本過程如下:
(1)、為struct cdev 分配空間(如果已經將struct cdev 嵌入到自己的裝置的特定結構體中,並分配了空間,這步略過!)
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops=&my_ops;
(2)、初始化struct cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
(3)、初始化cdev.owner
cdev.owner = THIS_MODULE;
(4)、cdev設定完成,通知核心struct cdev的資訊(在執行這步之前必須確定你對struct cdev的以上設定已經完成!)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
註冊模板:
struct cdev *cdev;
#define GSDEV_NR_DEVS 1
#define DEV_NAME "i2c"
#define DEV_MAJOR 256
static struct class *i2c_cls;
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
方式1(動態)
static int __init init_2420(void)
{
dev_t devno;
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &fops;
devno = MKDEV(DEV_MAJOR,0);//建立裝置號(主裝置號,次裝置號)
// if( register_chrdev_region(devno,1,DEVICE_NAME))//獲取字元裝置號
// {
//if(alloc_chrdev_region(&devno,0,1,DEVICE_NAME))
// {
// printk("my_sdio:[ERROR] Misc device register failed\n");
// return -1;
// }
// gs_major = MAJOR(devno);
// }
ret = cdev_add(cdev,devno,GSDEV_NR_DEVS);//註冊字元裝置到核心
i2c_cls = class_create(THIS_MODULE,DEVICE_NAME);//建立裝置類別檔案
if(IS_ERR(i2c_cls))
{
printk("class_create failed");
}
else
{
device_create(i2c_cls,NULL,devno,NULL,DEVICE_NAME);建立裝置檔案
}
}
static void __exit eit_2420(void)
{
device_destroy(i2c_cls,MKDEV(gs_major,0));
class_destroy(i2c_cls);
cdev_del(cdev);
//unregister_chrdev_region(MKDEV(gs_major,0),GSDEV_NR_DEVS);
}
方式2(靜態)
major = DEV_MAJOR;
static int __init init_2420(void)
{
dev_t devno;
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
devno = MKDEV(major,0);//建立裝置號(主裝置號,次裝置號)
if( register_chrdev_region(devno,1,DEVICE_NAME))//獲取字元裝置號
{
if(alloc_chrdev_region(&devno,0,1,DEVICE_NAME))
{
printk("my_sdio:[ERROR] Misc device register failed\n");
return -1;
}
major = MAJOR(devno);
}
ret = cdev_add(cdev,devno,GSDEV_NR_DEVS);//註冊字元裝置到核心
}
static void __exit eit_2420(void)
{
unregister_chrdev_region(MKDEV(major,0),GSDEV_NR_DEVS);//釋放佔用裝置號
cdev_del(cdev);//登出裝置
}
按混雜設備註冊方式
註冊方式更為簡單,不用建立裝置節點
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
static struct miscdevice my__dev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &my_fops,
};
static int __init init_2420(void)
{
int ret;
ret = misc_register(&my_dev);
if (ret)
{
printk("my_gpio:[ERROR] Misc device register failed\n");
return ret;
}
}
static void __exit eit_2420(void)
{
misc_deregister(&my_dev);
}
Linux裝置驅動分為以下三類:
(1)字元裝置:鍵盤,印表機
(2)塊裝置:硬碟,NAND
(3)網路裝置:網絡卡
對於字元裝置是最基本,最常見的裝置:
對字元裝置的驅動主要完成以下動作:
1、定義一個結構體static struct file_operations變數,其內定義一些裝置的open,read,write,close等控制函式
2、在結構體外分別實現結構體中定義的這些函式
3、向核心中註冊或刪除驅動模組
塊裝置與字元裝置的驅動結構是不同的,但是對於使用者來說沒有什麼區別,塊裝置比字元裝置要複雜,在I/O操作上極為不同表現在緩衝,I/O排程,請求佇列等。
1、操作的硬體介面實現不一樣;
2、資料塊的資料有一定的格式
網路裝置不同於字元和塊裝置,但是有字元和快裝置的部分功能
字元設備註冊有兩種方式,可以以混雜設備註冊:
字元裝置步驟:
(1)分配cdev: struct cdev *cdev_alloc(void);
(2)初始化cdev: void cdev_init(struct cdev *cdev, const struct file_operations *fops);
(3)新增cdev: int cdev_add(struct cdev *p, dev_t dev, unsigned count)
在2.6版本之前字元設備註冊方式:
int register_chrdev(unsigned int major,const char*name,struct file_operations *fops);
其中引數major如果等於0,則表示採用系統動態分配的主裝置號;不為0,則表示靜態註冊。
major 是感興趣的主裝置號, name 是驅動的名子(出現在 /proc/devices), fops 是預設的 file_operations 結構.
一個對 register_chrdev 的呼叫為給定的主編號註冊 0 - 255 的次編號,並且為每一個建立一個預設的 cdev 結構.
使用這個介面的驅動必須準備好處理對所有 256 個次編號的 open 呼叫( 不管它們是否對應真實裝置 ),
它們不能使用大於 255 的主或次編號.register_chrdev函式的major引數如果等於0,則表示採用系統動態分配的主裝置號。
它所做的事情為:
(1). 註冊裝置號, 通過呼叫 __register_chrdev_region() 來實現
(2). 分配一個cdev, 通過呼叫 cdev_alloc() 來實現
(3). 將cdev新增到驅動模型中, 這一步將裝置號和驅動關聯了起來. 通過呼叫 cdev_add() 來實現
(4). 將第一步中建立的 struct char_device_struct 物件的 cdev 指向第二步中分配的cdev. 由於register_chrdev()是老的介面,這一步在新的介面中並不需要.
登出介面函式
int unregister_chrdev(unsignedintmajor,constchar*name)
註冊程式碼簡單流程:
在init函式中註冊字元裝置
#define DEV_NAME "i2c"
#define IIC_MAJOR 145
static int __init init_2420(void)
{
int result;
....
result = register_chrdev(IIC_MAJOR,DEV_NAME,&fops);
...
}
static void __exit eit_2420(void)
{
unregister_chrdev(IIC_MAJOR,DEV_NAME);
}
在2.6新版本中介面函式分別改為
int register_chrdev_region(dev_t first, unsigned int count, char *name);
void unregister_chrdev_region(dev_t first, unsigned int count);
1.一個 cdev 一般它有兩種定義初始化方式:靜態的和動態的。
(1)靜態記憶體定義初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
(2)動態記憶體定義初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;
兩種使用方式的功能是一樣的,只是使用的記憶體區不一樣,一般視實際的資料結構需求而定。
2.註冊一個獨立的cdev裝置的基本過程如下:
(1)、為struct cdev 分配空間(如果已經將struct cdev 嵌入到自己的裝置的特定結構體中,並分配了空間,這步略過!)
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops=&my_ops;
(2)、初始化struct cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
(3)、初始化cdev.owner
cdev.owner = THIS_MODULE;
(4)、cdev設定完成,通知核心struct cdev的資訊(在執行這步之前必須確定你對struct cdev的以上設定已經完成!)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
註冊模板:
struct cdev *cdev;
#define GSDEV_NR_DEVS 1
#define DEV_NAME "i2c"
#define DEV_MAJOR 256
static struct class *i2c_cls;
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
方式1(動態)
static int __init init_2420(void)
{
dev_t devno;
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &fops;
devno = MKDEV(DEV_MAJOR,0);//建立裝置號(主裝置號,次裝置號)
// if( register_chrdev_region(devno,1,DEVICE_NAME))//獲取字元裝置號
// {
//if(alloc_chrdev_region(&devno,0,1,DEVICE_NAME))
// {
// printk("my_sdio:[ERROR] Misc device register failed\n");
// return -1;
// }
// gs_major = MAJOR(devno);
// }
ret = cdev_add(cdev,devno,GSDEV_NR_DEVS);//註冊字元裝置到核心
i2c_cls = class_create(THIS_MODULE,DEVICE_NAME);//建立裝置類別檔案
if(IS_ERR(i2c_cls))
{
printk("class_create failed");
}
else
{
device_create(i2c_cls,NULL,devno,NULL,DEVICE_NAME);建立裝置檔案
}
}
static void __exit eit_2420(void)
{
device_destroy(i2c_cls,MKDEV(gs_major,0));
class_destroy(i2c_cls);
cdev_del(cdev);
//unregister_chrdev_region(MKDEV(gs_major,0),GSDEV_NR_DEVS);
}
方式2(靜態)
major = DEV_MAJOR;
static int __init init_2420(void)
{
dev_t devno;
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
devno = MKDEV(major,0);//建立裝置號(主裝置號,次裝置號)
if( register_chrdev_region(devno,1,DEVICE_NAME))//獲取字元裝置號
{
if(alloc_chrdev_region(&devno,0,1,DEVICE_NAME))
{
printk("my_sdio:[ERROR] Misc device register failed\n");
return -1;
}
major = MAJOR(devno);
}
ret = cdev_add(cdev,devno,GSDEV_NR_DEVS);//註冊字元裝置到核心
}
static void __exit eit_2420(void)
{
unregister_chrdev_region(MKDEV(major,0),GSDEV_NR_DEVS);//釋放佔用裝置號
cdev_del(cdev);//登出裝置
}
按混雜設備註冊方式
註冊方式更為簡單,不用建立裝置節點
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
static struct miscdevice my__dev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &my_fops,
};
static int __init init_2420(void)
{
int ret;
ret = misc_register(&my_dev);
if (ret)
{
printk("my_gpio:[ERROR] Misc device register failed\n");
return ret;
}
}
static void __exit eit_2420(void)
{
misc_deregister(&my_dev);
}