一.通過程序流程管理
二.通過struct file , struct inode
5.問:
struct inode?
struct file?
如果做到一個驅動程序管理多個硬件設備個體,通過次設備號,共享一個主設備號;
答:
struct inode
{
dev_t i_rdev;
struct cdev *icdev;
...
};
作用:
描述一個文件的物理上的信息
如果文件創建,內核爲這個文件創建一個inode對象來描述這個文件的物理信息
如果文件銷燬,內核也會銷燬對應的inode對象;
重要成員:
i_rdev:如果此文件爲設備文件,此字段保存設備文件的設備號信息,
驅動可以根據此字段來獲取次設備號,通過次設備號來區分各個硬件設備個體;
i_cdev:如果此文件爲字符設備文件,那麼此字段指向字符設備驅動註冊字符設備對象,
例如led_cdev;
struct file
{
const struct file_operations *f_op;
};
作用:
描述一個文件被打開以後的狀態屬性;
如果文件打開(open),那麼內核就會創建一個file對象來描述這個文件打開以後的屬性;
如果文件關閉 ( colse ),那麼內核也會銷燬對應的file對象
重要成員:
f_op: 指向驅動程序註冊的硬件操作方法(led_fops);
問:
struct file_operations中的各個函數的形參有inode指針,有file指針,
這些指針分別指向內核創建的inode對象和file對象,那麼通過inode對象的
i_rdev能夠獲取次設備號,但是對於read,write,unlocked_ioctl的形參沒有
inode指針,如何獲取次設備號呢?
答:
inode和file存在一定的關係:參考內核源碼fbmem.c
struct inode *inode =
file->f_path.dentry->d_inode;
注意:
一個文件僅有一個inode對象,但是可以有多個file對象
案例:
2個LED共享一個驅動,共享一個主設備號,通過次設備號來區分
明確:2個LED設備
設備文件分別是/dev/myled1和/dev/myled2
主設備號共享:
次設備號分別是:0 和 1,個數爲2
硬件設備個數爲2;
cdev對象共享
file_operations操作函數也共享;
注意:通過次設備號區分
實施步驟:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> //struct file_operations
#include <linux/cdev.h> //struct cdev + 設備號
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/uaccess.h> //copy_to_user
#include <linux/device.h>
//聲明描述LED硬件相關的數據結構
struct led_resource {
char *name;
int gpio;
};
//定義初始化LED硬件信息
static struct led_resource led_info[] = {
[0] = {
.name = "LED1",
.gpio = S5PV210_GPC0(3)
},
[1] = {
.name = "LED2",
.gpio = S5PV210_GPC0(4)
}
};
//定義設備號
static dev_t dev;
//定義字符設備對象
static struct cdev led_cdev;
//定義設備類指針
static struct class *cls;
#define LED_ON 0x100001 //開燈命令
#define LED_OFF 0x100002 //關燈命令
static long led_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
//獲取inode對象
struct inode *inode =
file->f_path.dentry->d_inode;
//獲取次設備號
int minor = MINOR(inode->i_rdev);
//解析處理用戶命令
switch(cmd) {
case LED_ON:
gpio_set_value(led_info[minor].gpio, 1);
printk("%s:第%d個燈被打開!\n", __func__, minor+1);
break;
case LED_OFF:
gpio_set_value(led_info[minor].gpio, 0);
printk("%s:第%d個燈被關閉!\n", __func__, minor+1);
break;
default:
return -1;
}
return 0; //成功返回0,失敗返回-1
}
//定義初始化硬件操作方法
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = led_ioctl //向設備發送命令和完成讀寫
};
static int led_init(void)
{
int i;
//申請設備號
alloc_chrdev_region(&dev, 0, 2, "tarena");
//初始化字符設備對象
cdev_init(&led_cdev, &led_fops);
//註冊字符設備對象到內核
cdev_add(&led_cdev, dev, 2);
//申請GPIO資源和配置GPIO爲輸出口,輸出0(省電)
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 0);
}
//創建設備類(長樹枝)
cls = class_create(THIS_MODULE, "tarena");
//創建設備文件
device_create(cls, NULL, MKDEV(MAJOR(dev), 0), NULL, "myled1");
device_create(cls, NULL, MKDEV(MAJOR(dev), 1), NULL, "myled2");
return 0;
}
static void led_exit(void)
{
int i;
//刪除設備文件
device_destroy(cls, MKDEV(MAJOR(dev), 0));
device_destroy(cls, MKDEV(MAJOR(dev), 1));
class_destroy(cls);
//輸出0,釋放GPIO資源
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_set_value(led_info[i].gpio, 0);
gpio_free(led_info[i].gpio);
}
//卸載字符設備對象
cdev_del(&led_cdev);
//釋放設備號
unregister_chrdev_region(dev, 2);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LED_ON 0x100001
#define LED_OFF 0x100002
int main(void)
{
int fd1;
int fd2;
//打開設備
//open->....->調用led_open
fd1 = open("/dev/myled1", O_RDWR);
if (fd1 < 0) {
printf("打開設備失敗!\n");
return -1;
}
fd2 = open("/dev/myled2", O_RDWR);
if (fd2 < 0) {
printf("打開設備失敗!\n");
return -1;
}
while (1) {
ioctl(fd1, LED_ON);
sleep(1);
ioctl(fd2, LED_ON);
sleep(1);
ioctl(fd1, LED_OFF);
sleep(1);
ioctl(fd2, LED_OFF);
sleep(1);
}
//關閉設備
//close->...->調用led_close
close(fd1);
close(fd2);
return 0;
}