Linux platform设备驱动
将globalmem字符设备驱动,修改为platform设备驱动
链接:Linux字符设备驱动
1、platform总线、设备与驱动
【注】所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段,例如,我们通常把在SoC内部集成的I2C、RTC、LCD、看门狗等控制器都归纳为platform_device,而它们本身就是字符设备。
2、platform_device结构体定义
3、platform_driver结构体定义
4、device_driver结构体定义
5、将globalmem作为platform设备
在globalmem_probe探针函数完成对字符设备的初始化与注册
在globalmem_remove删除函数完对字符设备的注销以及资源的释放
(1)填充device_driver结构体
static struct device_driver globalmem_driver = {
.bus = &platform_bus_type, //总线类型struct bus_type
.name = "kp", //驱动名称(必须与platform总线设备名称相同)
//原型 int(*probe) (struct device *dev) 参数为设备结构体指针
.probe= globalmem_probe, //探针函数,在注册platform设备时调用 函数指针
//原型 int(*remove) (struct device *dev) 参数为设备结构体指针
.remove=globalmem_remove, //删除函数,在注销platform设备是调用 函数指针
};
(2)填充platform_device结构体
static struct platform_device globalmem_device = {
.name = "kp", //设备名称(必须与platform总线驱动名称相同)
.id = -1,
.dev = {
.release = platform_globalmem_release, //释放资源
}
};
(3)模块加载函数
模块加载函数中完成platform_device和platform_driver的注册
int globalmem_init(void)
{
platform_device_register(&globalmem_device); //注册platform device
return driver_register(&globalmem_driver); //注册platform driver
}
(4)模块卸载函数
模块卸载函数中完成platform_device和platform_driver的注销
void globalmem_exit(void)
{
platform_device_unregister(&globalmem_device); //注销platform device
driver_unregister(&globalmem_driver); //注销platform driver
}
(5)globalmem_probe探针函数
它将在设备驱动模块加载时被执行,完成对字符设备的初始化与注册
static int globalmem_probe(struct globalmem_dev *dev)
{
int result;
//使用宏通过主设备号和次设备号生成dev_t
dev_t devno = MKDEV(globalmem_major, 0); /* "globalmem_major"为主设备号(宏定义),"0"为此设备号*/
printk("initialize start\n");
/* 申请设备号*/
if (globalmem_major)
result = register_chrdev_region(devno, 1, "globalmem"); /*已知起始设备的设备号*/
else {
result = alloc_chrdev_region(&devno, 0, 1, "globalmem"); /* 动态申请设备号 */
globalmem_major = MAJOR(devno);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if (!globalmem_devp) { /*申请失败*/
result = - ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp, 0, sizeof(struct globalmem_dev)); /*设备结构体内存空间清0*/
globalmem_setup_cdev(globalmem_devp, 0); /*初始化并注册cdev字符设备结构*/
printk("globalmem platform set\n");
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
(6)globalmem_remove删除函数
它将在设备驱动模块被卸载时被执行,在globalmem_remove删除函数完对字符设备的注销以及资源的释放
static void globalmem_remove()
{
cdev_del(&globalmem_devp->cdev); /*注销cdev*/
kfree(globalmem_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
printk("globalmem platform unset\n");
}
(7)代码
/*
* A globalmem driver as an example of char device drivers
*
* The initial developer of the original code is Barry Song
* <author@linuxdriver.cn>. All Rights Reserved.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include<linux/device.h>
#define GLOBALMEM_SIZE 0x1000 /*全局内存最大4K字节*/
#define MEM_CLEAR 0x1 /*清0全局内存*/
#define GLOBALMEM_MAJOR 250 /*预设的globalmem的主设备号*/
struct class *myclass;
/*struct class_simple *myclass;*/
static int globalmem_major = GLOBALMEM_MAJOR;
/*globalmem设备结构体*/
struct globalmem_dev {
struct cdev cdev; /*cdev结构体*/
unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/
};
struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
/*文件打开函数*/
int globalmem_open(struct inode *inode, struct file *filp)
{
/*将设备结构体指针赋值给文件私有数据指针*/
filp->private_data = globalmem_devp;
return 0;
}
/*文件释放函数*/
int globalmem_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctl设备控制函数 */
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned
int cmd, unsigned long arg)
{
struct globalmem_dev *dev = filp->private_data;/*获得设备结构体指针*/
switch (cmd) {
case MEM_CLEAR:
memset(dev->mem, 0, GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero\n");
break;
default:
return - EINVAL;
}
return 0;
}
/*读函数*/
static ssize_t globalmem_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 globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= GLOBALMEM_SIZE)
return 0;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p;
/*内核空间->用户空间*/
if (copy_to_user(buf, (void *)(dev->mem + p), count)) {
ret = - EFAULT;
} else {
*ppos += count;
ret = count;
printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
}
return ret;
}
/*写函数*/
static ssize_t globalmem_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 globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= GLOBALMEM_SIZE)
return 0;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p;
/*用户空间->内核空间*/
if (copy_from_user(dev->mem + p, buf, count))
ret = - EFAULT;
else {
*ppos += count;
ret = count;
printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
}
return ret;
}
/* seek文件定位函数 */
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig) {
case 0: /*相对文件开始位置偏移*/
if (offset < 0) {
ret = - EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE) {
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1: /*相对文件当前位置偏移*/
if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
ret = - EINVAL;
break;
}
if ((filp->f_pos + offset) < 0) {
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = - EINVAL;
break;
}
return ret;
}
/*文件操作结构体*/
static const struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
/*初始化并注册cdev*/
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
int err, devno = MKDEV(globalmem_major, index);
cdev_init(&dev->cdev, &globalmem_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
//==========================================================
//********************* globalmem_prebe*********************
static int globalmem_probe(struct globalmem_dev *dev)
{
int result;
//使用宏通过主设备号和次设备号生成dev_t
dev_t devno = MKDEV(globalmem_major, 0); /* "globalmem_major"为主设备号(宏定义),"0"为此设备号*/
printk("initialize start\n");
/* 申请设备号*/
if (globalmem_major)
result = register_chrdev_region(devno, 1, "globalmem"); /*已知起始设备的设备号*/
else {
result = alloc_chrdev_region(&devno, 0, 1, "globalmem"); /* 动态申请设备号 */
globalmem_major = MAJOR(devno);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if (!globalmem_devp) { /*申请失败*/
result = - ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp, 0, sizeof(struct globalmem_dev)); /*设备结构体内存空间清0*/
globalmem_setup_cdev(globalmem_devp, 0); /*初始化并注册cdev字符设备结构*/
printk("globalmem platform set\n");
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
//============================================================
//myclass=class_simple_create(THIS_MODULE,"globalmem");
//class_simple_device_add(myclass,MKDEV(globalmem_major,0),NULL,"globalmem%d",0);
//============================================================
//********************* globalmem_remove *********************
static void globalmem_remove()
{
cdev_del(&globalmem_devp->cdev); /*注销cdev*/
kfree(globalmem_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
printk("globalmem platform unset\n");
}
//============================================================
//class_simple_device_remove(MKDEV(globalmem_major,0)); /
//class_simple_destroy(myclass);
//=========================================================
//********************* device_driver *********************
static struct device_driver globalmem_driver = {
.bus = &platform_bus_type, //总线类型struct bus_type
.name = "kp", //驱动名称(必须与platform总线设备名称相同)
//原型 int(*probe) (struct device *dev) 参数为设备结构体指针
.probe= globalmem_probe, //探针函数,在注册platform设备时调用 函数指针
//原型 int(*remove) (struct device *dev) 参数为设备结构体指针
.remove=globalmem_remove, //删除函数,在注销platform设备是调用 函数指针
};
//=========================================================
static void platform_globalmem_release(struct device * dev)
{
return ; //本例中未获得资源,直接返回
}
//=========================================================
//********************* device_device *********************
static struct platform_device globalmem_device = {
.name = "kp", //设备名称(必须与platform总线驱动名称相同)
.id = -1,
.dev = {
.release = platform_globalmem_release, //释放资源
}
};
//==========================================================
//==========================================================
//********************* globalmem_init *********************
int globalmem_init(void)
{
platform_device_register(&globalmem_device); //注册platform device
return driver_register(&globalmem_driver); //注册platform driver
}
//==========================================================
//==========================================================
//********************* globalmem_exit *********************
void globalmem_exit(void)
{
platform_device_unregister(&globalmem_device); //注销platform device
driver_unregister(&globalmem_driver); //注销platform driver
}
//==========================================================
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("Dual BSD/GPL");
module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);
6、参考
Linux设备驱动开发详解基于最新的Linux4.0内核》作者:宋宝华