#include <linux/init.h>// __init __exit
#include <linux/module.h> // module_init module_exit
#include <linux/fs.h> //file_operations
#include <asm/uaccess.h> //copy_from_user copy_to_user
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <asm/string.h>
#include <linux/ioport.h> //request_mem_region
#include <asm/io.h> //ioremap
#include <linux/cdev.h>
#include <linux/device.h>
#define MYNAME "led_dev"
#define DEVNUM 1
#define S5PV210_PA_GPIOJ0CON 0xe0200240
volatile unsigned int *rGPJ0CON = NULL;
volatile unsigned int *rGPJ0DAT = NULL;
static dev_t led_dev_no = 0;
/************ 静态使用cdev**********/
//static struct cdev led_cdev;
/************ 动态使用cdev**********/
static struct cdev *pled_cdev = NULL;
static struct class *pled_dev_class = NULL;
static int led_open(struct inode *inode, struct file *file);
ssize_t led_read(struct file *file, char __user *user, size_t count, loff_t *loff);
ssize_t led_write(struct file *file, const char __user *user, size_t count, loff_t *loff);
static int led_release(struct inode *inode, struct file *file);
static char kbuf[100] = {0};
static const struct file_operations led_fops = {
.owner= THIS_MODULE,
.open= led_open,
.read = led_read,
.write = led_write,
.release= led_release,
};
int led_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_open successful\n");
return 0;
}
ssize_t led_read(struct file *file, char __user *user, size_t ucount, loff_t *loff)
{
printk(KERN_INFO "led_read successful\n");
if (copy_to_user(user,kbuf , ucount))
{
printk(KERN_INFO "copy_to_user fail\n");
return -EINVAL;
}
printk(KERN_INFO "copy_to_user successful\n");
return strlen(kbuf);
}
ssize_t led_write(struct file *file, const char __user *user, size_t ucount, loff_t *loff)
{
printk(KERN_INFO "led_write successful\n");
memset(kbuf,0,sizeof(kbuf));
if (copy_from_user(kbuf, user, ucount))
{
printk(KERN_INFO "copy_from_user fail\n");
return -EINVAL;
}
if(!strcmp(kbuf,"on"))
{
*rGPJ0CON &=0xff000fff;
*rGPJ0CON |=0x00111000;
*rGPJ0DAT &=~((0x01<<3)|(0x01<<4)|(0x01<<5));
}
else if(!strcmp(kbuf,"off"))
{
*rGPJ0CON &=0xff000fff;
*rGPJ0CON |=0x00111000;
*rGPJ0DAT |=((0x01<<3)|(0x01<<4)|(0x01<<5));
}
return ucount;
printk(KERN_INFO "copy_from_user successful\n");
}
int led_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_release successful\n");
return 0;
}
// 模块安装函数
static int __init chrdev_init(void)
{
int ret = -1;
int retval = -1;
printk(KERN_INFO "chrdev_init successful\n");
/******指定主设备号注册*****/
//led_dev_no = MKDEV(250,0);
//retval = register_chrdev_region(led_dev_no, DEVNUM,MYNAME);
/******内核自动分配主设备号注册*****/
retval = alloc_chrdev_region(&led_dev_no,0,DEVNUM,MYNAME);
if (retval)
{
printk(KERN_WARNING "alloc_chrdev_region fail\n.");
ret = -EINVAL;
goto err_reg;
}
printk(KERN_INFO "alloc_chrdev_region successful major = %d,minor = %d \n",MAJOR(led_dev_no),MINOR(led_dev_no));
/****动态申请cdev实体******/
pled_cdev = cdev_alloc();
printk(KERN_INFO "cdev_alloc successful\n");
cdev_init(pled_cdev, &led_fops);
retval = cdev_add(pled_cdev, led_dev_no, DEVNUM);
if (retval)
{
printk(KERN_WARNING "cdev_add fail\n.");
ret = -EINVAL;
goto err_add;
}
printk(KERN_INFO "cdev_add successful\n");
// 注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息
// 给udev,让udev自动创建和删除设备文件
pled_dev_class = class_create(THIS_MODULE, "led_musk");
if (IS_ERR(pled_dev_class))
{
printk(KERN_WARNING "class_create fail\n.");
ret = -EINVAL;
goto err_class_create;
}
device_create(pled_dev_class, NULL, led_dev_no, NULL, "led_dev"); //“led_dev”对应/dev/下的名字
if (request_mem_region(S5PV210_PA_GPIOJ0CON, 8, "GPIOJ0CON") == NULL)
{
printk(KERN_WARNING "failed to get memory region\n");
ret = -ENOENT;
goto err_req;
}
rGPJ0CON = ioremap(S5PV210_PA_GPIOJ0CON,8);
if (rGPJ0CON == NULL)
{
printk(KERN_WARNING "fail to ioremap() region\n");
ret = -ENOENT;
goto err_map;
}
rGPJ0DAT = rGPJ0CON+1;
return 0;
err_map:
release_mem_region(S5PV210_PA_GPIOJ0CON,8);
err_req:
class_destroy(pled_dev_class);
err_class_create:
/***for static cdev******/
//cdev_del(&led_cdev);
/***for dynamic cdev******/
cdev_del(pled_cdev);
err_add:
printk(KERN_INFO "err_add err\n");
unregister_chrdev_region(led_dev_no, DEVNUM);
err_reg:
return ret;
}
// 模块卸载函数
static void __exit chrdev_exit(void)
{
iounmap(rGPJ0CON);
release_mem_region(S5PV210_PA_GPIOJ0CON,8);
device_destroy(pled_dev_class,led_dev_no);
class_destroy(pled_dev_class);
/***for static cdev******/
//cdev_del(&led_cdev);
/***for dynamic cdev******/
cdev_del(pled_cdev);
unregister_chrdev_region(led_dev_no, DEVNUM);
printk(KERN_INFO "chrdev_exit successful\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");// 描述模块的许可证
MODULE_AUTHOR("musk");// 描述模块的作者
MODULE_DESCRIPTION("module test");// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");// 描述模块的别名信息
一. 什么是class
1.1. class 指的是 设备类(device classes),是对于设备的高级抽象。但实际上 class 也是一个结构体,只不过 class 结构体在声明时是按照类的思想来组织其成员的。运用 class,可以让用户空间的程序根据自己要处理的事情来调用设备,而不是根据设备被接入到系统的方式或设备的工作原理来调用
1.2. 一个 struct class 结构体类型变量对应一个类,内核提供了class_create() 函数,可以用它来创建一个类,这个类存放于 sysfs 下面。 一旦创建了类,再调用 device_create() 函数在 /dev 目录下创建相应的设备节点
1.2.1. sysfs虚拟文件系统
a. sysfs是 Linux 内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与 proc 有些类似,但除了与 proc相同的具有查看和设定内核参数功能之外,还有为 Linux 统一设备模型作为管理之用。相比于 proc 文件系统,使用 sysfs导出内核数据的方式更为统一,并且组织的方式更好,它的设计从 proc 中吸取了很多教训
b. 当使用class_create创建时,在/sys/class/中会创建相应文件
二. linux中的mdev机制
2.1.什么是mdev
2.1.1. mdev是busybox自带的一个简化版的udev。适合于嵌入式的应用埸合。其具有使用简单的特点。它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所
需的节点文件。在以busybox 为基础构建嵌入式linux 的根文件系统时,使用它是最优的选择
2.1.2. mdev 的使用在busybox 中的mdev.txt 文档已经将得很详细了
2.2. mdev 用法
2.2.1.执行mdev前要挂载 /sys
a. mount -t tmpfs mdev /dev
b.mount -t sysfs sysfs /sys
2.2.2. 命令内核在增删设备时执行/sbin/mdev,使设备节点会被创建和删除
a. echo /sbin/mdev > /proc/sys/kernel/hotplug
2.2.3. 设置mdev,让它在系统启动时创建所有的设备节点
a. mdev -s
2.2.4. 相关操作bsp 下/etc/init.d/rcS启动开发板时会设置。
PATH=/sbin:/bin:/usr/sbin:/usr/bin
runlevel=S
prevlevel=N
umask 022
export PATH runlevel prevlevel
mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
/bin/hostname -F /etc/sysconfig/HOSTNAME
ifconfig eth0 192.168.1.20
~
- /etc/init.d/rcS 1/19 5%
三. 相关函数分析
3.1. class_create函数分析。
3.1.1. 函数调用层级
class_create
__class_create
__class_register
kset_register
kobject_uevent
/* This is a #define to keep the compiler from merging different
* instances of the __key variable */
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
/**
* class_create - create a struct class structure
* @owner: pointer to the module that is to "own" this struct class
* @name: pointer to a string for the name of this class.
* @key: the lock_class_key for this class; used by mutex lock debugging
*
* This is used to create a struct class pointer that can then be used
* in calls to device_create().
*
* Returns &struct class pointer on success, or ERR_PTR() on error.
*
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
struct class *__class_create(struct module *owner, const char *name,
struct lock_class_key *key)
{
struct class *cls;
int retval;
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
if (!cls) {
retval = -ENOMEM;
goto error;
}
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release;
retval = __class_register(cls, key);
if (retval)
goto error;
return cls;
error:
kfree(cls);
return ERR_PTR(retval);
}
int __class_register(struct class *cls, struct lock_class_key *key)
{
struct class_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name);
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);
INIT_LIST_HEAD(&cp->class_interfaces);
kset_init(&cp->class_dirs);
__mutex_init(&cp->class_mutex, "struct class mutex", key);
error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
if (error) {
kfree(cp);
return error;
}
/* set the default /sys/dev directory for devices of this class */
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;
#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (cls != &block_class)
cp->class_subsys.kobj.kset = class_kset;
#else
cp->class_subsys.kobj.kset = class_kset;
#endif
cp->class_subsys.kobj.ktype = &class_ktype;
cp->class = cls;
cls->p = cp;
error = kset_register(&cp->class_subsys);
if (error) {
kfree(cp);
return error;
}
error = add_class_attrs(class_get(cls));
class_put(cls);
return error;
}
/**
* kset_register - initialize and add a kset.
* @k: kset.
*/
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
kset_init(k);
err = kobject_add_internal(&k->kobj);
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
/**
* kobject_uevent - notify userspace by ending an uevent
*
* @action: action that is happening
* @kobj: struct kobject that the action is happening to
*
* Returns 0 if kobject_uevent() is completed with success or the
* corresponding error when it fails.
*/
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
3.2. device_create函数分析。
3.2.1. 函数调用层级
device_create
device_create_vargs
kobject_set_name_vargs
device_register
device_add
kobject_add
device_create_file
device_create_sys_dev_entry
devtmpfs_create_node
device_add_class_symlinks
device_add_attrs
device_pm_add
kobject_uevent
/**
* device_create - creates a device and registers it with sysfs
* @class: pointer to the struct class that this device should be registered to
* @parent: pointer to the parent struct device of this new device, if any
* @devt: the dev_t for the char device to be added
* @drvdata: the data to be added to the device for callbacks
* @fmt: string for the device's name
*
* This function can be used by char device classes. A struct device
* will be created in sysfs, registered to the specified class.
*
* A "dev" file will be created, showing the dev_t for the device, if
* the dev_t is not 0,0.
* If a pointer to a parent struct device is passed in, the newly created
* struct device will be a child of that device in sysfs.
* The pointer to the struct device will be returned from the call.
* Any further sysfs files that might be required can be created using this
* pointer.
*
* Returns &struct device pointer on success, or ERR_PTR() on error.
*
* Note: the struct class passed to this function must have previously
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
3.2.2. 调用此函数最早会创建/sys/class/xxx 下dev power subsystem uevent等文件
3.3. device_destroy函数
3.3.1. 函数定义:
/**
* device_destroy - removes a device that was created with device_create()
* @class: pointer to the struct class that this device was registered with
* @devt: the dev_t of the device that was previously registered
*
* This call unregisters and cleans up a device that was created with a
* call to device_create().
*/
void device_destroy(struct class *class, dev_t devt)
{
struct device *dev;
dev = class_find_device(class, NULL, &devt, __match_devt);
if (dev) {
put_device(dev);
device_unregister(dev);
}
}
3.4. class_destroy函数
3.4.1.函数定义
/**
* class_destroy - destroys a struct class structure
* @cls: pointer to the struct class that is to be destroyed
*
* Note, the pointer to be destroyed must have been created with a call
* to class_create().
*/
void class_destroy(struct class *cls)
{
if ((cls == NULL) || (IS_ERR(cls)))
return;
class_unregister(cls);
}
四. 应用层调用内核
4.1. 应用层代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define DEVFILE "/dev/led_dev"
int main(void)
{
char buf[100] = {0};
int fd = -1;
if((fd =open(DEVFILE, O_RDWR))<0)
{
perror("open");
return -1;
}
printf("open successful fd = %d\n",fd);
if(write(fd, "on", strlen("on"))<0)
{
perror("write");
return -1;
}
sleep(3);
memset(buf,0,sizeof(buf));
if(read(fd, buf, 3)<0)
{
perror("read");
return -1;
}
printf("read data = %s\n",buf);
if(write(fd, "off", strlen("off"))<0)
{
perror("write");
return -1;
}
sleep(3);
memset(buf,0,sizeof(buf));
if(read(fd, buf, 4)<0)
{
perror("read");
return -1;
}
printf("read data = %s\n",buf);
close(fd);
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4402301/blog/3690372