結合之前對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)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。