结合之前对Linux内核的platform总线 ,以及对字符设备的cdev接口的分析,本文将编写基于platform总线与cdev接口的LED设备的实例代码并对其进行分析。
platform总线分析,详见Linux platform驱动模型。
字符设备的cdev接口分析,详见Linux字符设备驱动(一):cdev接口。
硬件接口:
CPU:s5pv210;
LED的GPIO:GPIO_J0_3 ~ GPIO_J0_5;
LED的工作方式:低电平亮,高电平灭。
1. led_device.c
本文将设备信息写成一个模块的形式,需要时加载该模块即可。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
//定义并初始化LED设备的相关资源
static struct resource led_resource =
{.start =
0xE0200240,.end =
0xE0200240 +
8 -
1,.flags =
IORESOURCE_MEM,
};//定义并初始化LED设备信息
static struct platform_device led_dev =
{.name =
"led",
//设备名称.id = -
1,
//设备数量,-1表示只有一个设备.num_resources =
1,
//资源数量.resource = &led_resource,
//资源指针.dev =
{.release =
led_release,},
};//注册LED设备
static int __init led_device_init(
void)
{return platform_device_register(&
led_dev);
}//注销LED设备
static void __exit led_device_exit(
void)
{platform_device_unregister(&
led_dev);
}module_init(led_device_init);
module_exit(led_device_exit);MODULE_AUTHOR("Lin");
MODULE_DESCRIPTION("led device for x210");
MODULE_LICENSE("GPL");
2. led_driver.c
led_driver_init():模块加载函数
platform_driver_register()将驱动对象模块注册到平台总线
led_probe()探测函数,提取相应的信息
platform_get_resource()获取设备资源
request_mem_region()、ioremap()虚拟内存映射
readl()、write()初始化硬件设备
cdev_alloc()申请cdev内存
cdev_init()初始化cdev对象,将cdev与fops结构体绑定
alloc_chrdev_region()申请设备号
cdev_add()注册LED设备对象,将cdev添加至系统字符设备链表中
class_create()创建设备类
device_create()创建设备文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/leds.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/ioctl.h>
#define LED_IOC_MAGIC 'l'
//ioctl幻数
#define LED_IOC_MAXNR 2
//ioctl最大命令序数
#define LED_ON _IO(LED_IOC_MAGIC, 0)
//ioctl自定义命令
#define LED_OFF _IO(LED_IOC_MAGIC, 1)
#define DEVNAME "led"
//设备名称static int led_major =
0;
//主设备号
static int led_minor =
0;
//次设备号
const int led_count =
1;
//次设备数量//GPIO寄存器变量定义
typedef
struct GPJ0REG
{volatile unsigned
int gpj0con;volatile unsigned
int gpj0dat;
}gpj0_reg_t;static gpj0_reg_t *pGPIOREG =
NULL;static dev_t led_devnum;
//设备号
static struct cdev *led_cdev = NULL;
//设备对象static struct class *led_cls = NULL;
//设备类
static struct device *led_dev = NULL;
//设备//LED设备的ioctl函数实现
static int led_ioctl(
struct inode *inode,
struct file *filp, unsigned
int cmd, unsigned
long arg)
{int reg_value =
0;//检测命令的有效性if (_IOC_TYPE(cmd) !=
LED_IOC_MAGIC) return -
EINVAL;if (_IOC_NR(cmd) >
LED_IOC_MAXNR) return -
EINVAL;//根据命令,执行相应的硬件操作switch(cmd) {case LED_ON:reg_value = readl(&pGPIOREG->
gpj0dat); reg_value &= ~((
1 <<
3) | (
1 <<
4) | (
1 <<
5));writel(&pGPIOREG->
gpj0dat, reg_value);break;case LED_OFF:reg_value = readl(&pGPIOREG->
gpj0dat); reg_value |= (
1 <<
3) | (
1 <<
4) | (
1 <<
5);writel(&pGPIOREG->
gpj0dat, reg_value);break;default: return -
EINVAL;}return 0;
}static int led_open(
struct inode *inode,
struct file *
filp)
{return 0;
}static int led_release(
struct inode *inode,
struct file *
filp)
{return 0;
}//LED设备的write函数实现
static ssize_t led_write(
struct file *file,
const char __user *user_buf, size_t count, loff_t *
ppos)
{char kbuf[
20] = {
0};int reg_value =
0;memset(kbuf, 0,
sizeof(kbuf));if (copy_from_user(kbuf, user_buf, count)){return -
EFAULT;}if (kbuf[
0] ==
'0'){reg_value = readl(&(pGPIOREG->
gpj0dat)); reg_value |= (
1 <<
3) | (
1 <<
4) | (
1 <<
5);writel(reg_value, &(pGPIOREG->
gpj0dat));}else{reg_value = readl(&(pGPIOREG->
gpj0dat)); reg_value &= ~((
1 <<
3) | (
1 <<
4) | (
1 <<
5));writel(reg_value, &(pGPIOREG->
gpj0dat));}return 1;
}//定义并初始化LED设备的操作集
static const struct file_operations led_ops =
{.owner =
THIS_MODULE,.open =
led_open,.write =
led_write,.ioctl =
led_ioctl,.release =
led_release,
};//LED设备的probe函数实现
static int led_probe(
struct platform_device *
pdev)
{struct resource *res_led =
NULL;int ret = -
1;int reg_value =
0;int i =
0;/****************************申请资源*******************************///获取资源res_led = platform_get_resource(pdev, IORESOURCE_MEM,
0);if (!
res_led) {return -
ENOMEM;}//动态内存映射if (!request_mem_region(res_led->start, resource_size(res_led),
"GPIOJ0")){return -
EBUSY;}pGPIOREG = ioremap(res_led->
start, resource_size(res_led));if (pGPIOREG ==
NULL) {ret = -
ENOENT;goto ERR_STEP;}/****************************初始化资源*******************************///设置GPIO为输出模式reg_value = readl(&(pGPIOREG->
gpj0con)); reg_value |= (
1 << (
3*
4)) | (
1 << (
4*
4)) | (
1 << (
5*
4));writel(reg_value, &(pGPIOREG->
gpj0con));/***************************创建接口(cdev)***************************///申请cdev内存led_cdev =
cdev_alloc();if (led_cdev ==
NULL){ret = -
ENOMEM;goto ERR_STEP1;}//初始化led_cdev(将led_cdev于led_ops关联)cdev_init(led_cdev, &
led_ops);//申请设备号ret = alloc_chrdev_region(&
led_devnum, led_minor, led_count, DEVNAME);if (ret <
0){goto ERR_STEP1;}//注册LED设备对象(将cdev添加至系统字符设备链表中)ret =
cdev_add(led_cdev, led_devnum, led_count);if (ret <
0){goto ERR_STEP2;}//创建设备类led_cls =
class_create(THIS_MODULE, DEVNAME);if (IS_ERR(led_cls)) {ret =
PTR_ERR(led_cls);goto ERR_STEP3;}//在设备类下创建设备文件led_major =
MAJOR(led_devnum);for(i = led_minor; i < (led_count + led_minor); i++
){led_dev = device_create(led_cls, NULL, MKDEV(led_major, i), NULL,
"%s%d", DEVNAME, i);if(IS_ERR(led_dev)){ret =
PTR_ERR(led_dev);goto ERR_STEP4;}}return 0;/*******************************倒映式错误处理*******************************/
ERR_STEP4:for(--i; i >= led_minor; i--
){device_destroy(led_cls, MKDEV(led_major, i));}class_destroy(led_cls);ERR_STEP3:cdev_del(led_cdev);ERR_STEP2:unregister_chrdev_region(led_devnum, led_count);ERR_STEP1:iounmap(pGPIOREG); ERR_STEP:release_mem_region(res_led->
start, resource_size(res_led));return ret;
}int led_remove(
struct platform_device *
pdev)
{int i =
0;//删除设备文件for(i = led_minor; i < (led_count + led_minor); i++
){device_destroy(led_cls, MKDEV(led_major, i));}//删除设备类
class_destroy(led_cls);//删除设备对象
cdev_del(led_cdev);//注销设备号
unregister_chrdev_region(led_devnum, led_count);//释放内存
iounmap(pGPIOREG);return 0;
}//定义并初始化LED驱动信息
static struct platform_driver led_drv =
{.driver =
{.name =
"led",.owner =
THIS_MODULE,},.probe =
led_probe,.remove =
led_remove,
};//注册LED驱动
static int __init led_driver_init(
void)
{return platform_driver_register(&
led_drv);
}//注销LED驱动
static void __exit led_driver_exit(
void)
{platform_driver_unregister(&
led_drv);
}module_init(led_driver_init);
module_exit(led_driver_exit);MODULE_AUTHOR("Lin");
MODULE_DESCRIPTION("led driver for x210");
MODULE_LICENSE("GPL");
3. 测试所用应用程序
运行应用程序之前,需确保上述两个模块(device、driver)被装载。运行结果表明LED设备能被应用程序操作。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <
string.h>
#include <sys/ioctl.h>
#define FILE_NAME "/dev/led0"
#define LED_ON _IO(LED_IOC_MAGIC, 0)
#define LED_OFF _IO(LED_IOC_MAGIC, 1)
char WriteBuf[
30];
char ReadBuf[
30];
char ScanBuf[
30];int main(
void)
{int fd = -
1;int i =
0;//打开设备文件if ((fd = open(FILE_NAME, O_RDWR)) <
0){printf("%s open error\n", FILE_NAME);return -
1;}while (
1){memset(ScanBuf, 0,
sizeof(ScanBuf));printf("please input data for LED\n");if (scanf(
"%s", ScanBuf)){//打开LED设备if (!strcmp(ScanBuf,
"on")){write(fd, "1",
1);}//关闭LED设备else if (!strcmp(ScanBuf,
"off")){write(fd, "0",
1);}//闪烁LED设备else if (!strcmp(ScanBuf,
"flash")){for (i=
5; i>
0; i--
){ioctl(fd, LED_ON);sleep(1);ioctl(fd, LED_OFF);sleep(1);}}
else {break;}}}close(fd);return 0;
}
转载于:https://www.cnblogs.com/linfeng-learning/p/9376804.html
总结
以上是生活随笔为你收集整理的驱动程序实例(一):LED设备驱动程序( platform + cdev)的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。