內核:3.8.0-19-generic
文件的管理使用的是 sysfs.(由udev製作的文件系統)
1. 創建字符設備 生成設備節點
2. busybox
Linux System Utilities --->
[*] mdev
[*] Support /etc/mdev.conf
[*] Support command execution at device addition/removal
3. kernel
File systems --->
Pseudo filesystems --->
[*] sysfs file system support
[*] Virtual memory file system support (former shm fs)
[*] Tmpfs POSIX Access Control Lists
4. rootfs
vi ./etc/init.d/rcS
mount -t tmpfs mdev /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mount -t sysfs sysfs /sys
mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
說明:
當使用利用udev製作的文件系統時,Linux內核爲我們提供了一組函數,可以用來在模塊加載的時候自動在/dev目錄下創建相應設備節點,並在卸載模塊時刪除該節點。
內核中定義了struct class結構體,一個struct class結構體類型變量對應一個類(有待商榷),內核同時提供了class_create(…)函數,可以用它來創建一個類,這個類存放於sysfs下面,一旦創建好了這個類,再調用device_create(…)函數來在/dev目錄下創建相應的設備節點。這樣,加載模塊的時候,用戶空間中的udev會自動響應device_create(…)函數,去/sysfs下尋找對應的類從而創建設備節點。
定義在<linux/device.h>中
class結構:該結構體類型變量對應一個設備類,被創建的類存放在/sys目錄下面
device結構:該結構體類型變量對應設備,被創建的設備存放於/sys目錄下面
在加載驅動模塊時,用戶空間中的udev會自動響應device_create()函數,在/sys下尋找對應的類,從而爲這個設備在/dev目錄下創建設備文件
內核版本問題:
在內核2.4版本中使用devfs_register
在內核2.6早起版本中使用class_device_register
2.6.35.7中使用class_create和device_create
類的創建和銷燬
定義在<linux/device.h>中
實現在內核源碼drivers/base/class.c中
類創建
爲設備驅動創建一個設備類
struct class *class_create(struct module *owner, const char *name);
owner:創建設備類的驅動模塊擁有者
name:待創建的設備類的類名稱
返回:創建好的設備類的指針,失敗返回NULL
類銷燬
銷燬設備驅動創建的對應設備類
void class_destroy(struct class *cls);
cls:待銷燬的設備類
定義在<linux/device.h>中
實現在內核源碼drivers/base/core.c中
設備創建
爲設備創建對應的設備文件
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
class:待創建的設備所屬設備類
parent:指向可能存在的父設備的指針
devt:待創建設備的設備號(包括主設備號和次設備號)
drvdata:設備保留的驅動私有數據指針
fmt:待創建的設備文件名稱
返回:創建好的device的指針,失敗返回NULL
設備銷燬
刪除設備對應的設備文件
void device_destroy(struct class *class, dev_t devt);
class:待銷燬的設備所屬設備類
devt:待銷燬設備的設備號(包括主設備號和次設備號)
示例程序(一個簡單的字符驅動):
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/fs.h> /* For file operations */
#include <linux/ioport.h> /* For io-port access */
#include <linux/platform_device.h> /* For platform_driver framework */
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include <linux/io.h> /* For inb/outb/... */
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
int hello_open(struct inode *inode, struct file *file)
{
printk("this is hello_open\n");
return 0;
}
ssize_t hello_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
printk("this is hello_read\n");
return 0;
}
ssize_t hello_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
printk("this is hello_write\n");
return 0;
}
struct file_operations hello_ops = {
.owner = THIS_MODULE,
.open = hello_open,
.read = hello_read,
.write = hello_write
};
static struct cdev hello_devs;
static struct class *first_class;
int major = 200; // 主設備號爲200
/* 入口函數 */
static int __init hello_init(void)
{
int result;
//獲得設備號
dev_t dev=MKDEV(major, 0);
if (major) //如果major不爲0,說明自己申請的主設備號可以用,這時可以靜態分配
result = register_chrdev_region(dev, 1, "xyz");
else
{
/* 設備號的動態分配 */
result = alloc_chrdev_region(&dev, 0, 1, "xyz");
major = MAJOR(dev);
}
//初始cdev_init(),實現fops 與 struct cdev (字符設備結構體)的關聯
cdev_init(&hello_devs, &hello_ops);
hello_devs.owner = THIS_MODULE;
hello_devs.ops = &hello_ops;
//添加cdev到內核 設備號 dev_t 與 struct cdev 的關聯
cdev_add(&hello_devs,dev,1);
printk("The major of the gpio device is %d\n", major);
// automatic create device node
first_class= class_create(THIS_MODULE, "xyz"); /* sysfs */
/* register your own device in sysfs, and this will cause udev to create corresponding device node */
device_create(first_class, NULL, MKDEV(major, 0), NULL, "xyz");
return 0;
}
//出口函數
static void __exit hello_exit(void)
{
//刪除設備與設備類
device_destroy(first_class, MKDEV(major, 0));
class_destroy(first_class);
//註銷cdev
cdev_del(&hello_devs);
//註銷設備號
unregister_chrdev_region(MKDEV(major, 0), 1);
printk("Gpio device uninstalled\n");
}
Makefile 的寫法:
ifneq ($(KERNELRELEASE),)
obj-m := led_drive.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
endif
.PHONY:clean
clean:
@rm -rf *.o *.ko .tmp_versions *~ Module.symvers .*.cmd *.mod.c *.order
執行命令:make
插入模塊:sudo insmod led_drive.ko
插入模塊成功的標誌:
設備節點的查詢:
移除模塊:
rmmod led_drive (不用加.ko結尾)