基于S3C2440开发板LED灯驱动移植
2013-03-13 18:16
190 查看
上一篇文章简述了如何写一个实用性较强的驱动模板,今天我们给这个模板填充一些必要的代码,让它变成一个可用的led驱动程序,实际上大多数字符类驱动也都是基于这种模板形式。
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
/*以上头文件包含如果搞不懂具体起什么作用,建议从系统驱动中拷贝*/
/*#define definition*/
#define DEVICE_NAME "gpio_drv" //设备名称 和文件系统里设备节点名称区分开
#define FIRST_MAJOR 0 //主设备号
#define LED_ON 1 //点亮
#define LED_OFF 0 //熄灭
static unsigned long led_table[] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
static unsigned int led_cfg_table[] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
/*上面两个数组采用源码的基于S3C2410头文件配置好的,如果需要自己配置 参考S3C2440用户手册*/
//.open func 应用程序将来调用的open函数 主要配置io口
static int myfirst_drv_open(struct inode *inode, struct file *filp)
{
int i;
for(i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
}
printk("myfirst_drv_open\n");
/*配置GPIB5 6 7 8 为输出*/
return 0;
}
//.write func 应用程序将来调用write部分 *file问文件句柄 *buf传入的用户(应用程序)数据地址 count为字节长度 一般为4的整数倍 前面buf为地址 一条指令4个字节
static ssize_t myfirst_drv_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int val; //接收用户数据
int i;;
printk("myfirst_drv_write\n");
copy_from_user(&val, buf, count); // copy_to_user();
将用户空间地址buf的数据交互到内核空间 val地址上
if (val == 1) //点亮LED灯
{
for(i = 0; i < 4; i++)
{
s3c2410_gpio_setpin(led_table[i], 0);
}
printk("led on\n");
}
else
{
for(i = 0; i < 4; i++)
{
s3c2410_gpio_setpin(led_table[i], 1);
}
printk("led off\n");
}
return 0;
}
//ioctl控制 应用程序将来调用的ioctl函数*file为应用层返回的文件句柄,cmd为操作命令 arg为传入的参数
static int gpio_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if (arg > 4)
{
return -EINVAL;
}
switch(cmd)
{
case LED_ON:
// 璁剧疆鎸囧畾寮曡剼鐨勮緭鍑虹數骞充负0
s3c2410_gpio_setpin(led_table[arg], 0);
return 0;
case LED_OFF:
// 璁剧疆鎸囧畾寮曡剼鐨勮緭鍑虹數骞充负1
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
//define the file_operations file_operations结构体
static struct file_operations myfirst_drv_fops = {
.owner = THIS_MODULE,
.open = myfirst_drv_open,
.write = myfirst_drv_write,
.ioctl = gpio_ioctl,
};
static struct class *myfirst_drv_class; //定义一个类 方便在/dev目录下建立一个节点
int major; //自动分配主设备号
int first_drv_init(void)
{
//int ret;
//ret = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);
major = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);
//if(ret < 0)//
if(major < 0)
{
printk("myfirst_drv can not register major number!\n");
return major;
}
/*注册一个类 使得mdev可以在/dev目录下自动建立设备节点 不需要手动mknod dev c 224 0这样的操作*/
myfirst_drv_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(myfirst_drv_class))
{
printk("error, fail to init myfirst_drv_class");
return -1;
}
/*如果上面成功注册进mdev的话 下面自动在/dev目录下创建一个设备节点*/
device_create(myfirst_drv_class, NULL, MKDEV(major, 0), NULL, "GPIO_DRV"); //这个就是在/dev下创建的MYFIRST_DRV 设备节点了 应用程序调用
printk("MYFIRST_DRV initialized! \n");
return 0;
}
void first_drv_exit(void)
{
unregister_chrdev(major, DEVICE_NAME); //卸载驱动
device_destroy(myfirst_drv_class, MKDEV(major, 0)); //删掉设备节点
class_destroy(myfirst_drv_class); //注销类
printk("rm -rm MYFIRST_DRV! \n");
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL"); //获得授权
该驱动makefile和上一篇一样 这里不再重复填上
下面看我们的测试程序 如何通过write open ioctl来操作硬件的?
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define LED_ON 1
#define LED_OFF 0
static int fd; //用于存放打开设备时返回的文件句柄
//例如我们想通过设备文件向驱动写入数据的时候 我们的main函数应该设置成下面的方式,下面的参数如何理解呢?我这里贴上一段参考----
int main(int argc, char **argv)
int main(int argc, char **argv)
{
int led_no = 0;
int val ;
int ret;
fd = open("/dev/GPIO_DRV", O_RDWR); //打开设备 并读写权限 上一篇我们后面的参数写的是0 这里需要为O_RDWR,意思是赋予驱动读写权限
if(fd < 0) //成功打开fb返回的肯定是个非零值
{
perror("open device failed");
exit(1);
}
while(1) //每个应用程序都是死循环
{
for(led_no = 0; led_no < 4; led_no++)
{
ioctl(fd, LED_ON, led_no); //点亮一盏灯 这里采用循环点亮方式
sleep(2);
}
led_no = 0;
for(led_no = 0; led_no < 4; led_no++) //又熄灭一个led灯 熄灭2个。。。。。
{
ioctl(fd, LED_OFF, led_no); //熄灭一盏灯
sleep(1);
}
led_no = 0;
if(argc != 2) //如果传入参数不是2个的话 提示信息退出 ./gpio_test on 一定要这种方式
{
printf("Usage :\n");
printf("%s <on|off>\n", argv[0]);
return 0; //退出重新运行应用程序
}
if (strcmp(argv[1], "go") != 0) //如果第二个参数不是go 如果./gpio_test go 则运行流水灯 上面部分
{
if (strcmp(argv[1], "on") == 0) //如果第二个参数为on
{
printf("led app on\n");
val = 1;
}
else if (strcmp(argv[1], "off") == 0) //第二个参数为off
{
printf("led app off\n");
val = 0;
}
ret = write(fd, &val, 4); //写入 fd为文件句柄 传入要写的数据的地址&val 我们就要么1 要么0 所以一个地址就可以了 长度4字节
if(ret < 0) //如果写入成功 返回写入的个数 否则返回-1
{
printf("write failed ret = %d\n", ret);
return 1;
}
return 0;
}
}
//close(fd); //关闭设备
return 0;
}
编写该应用程序的makefile 为了以后开发方便 这里makefile较上篇做了一点改变
CROSS=arm-linux-
DRIVER=gpio_drvtest.c
FILENAME=gpio_test
all: $(FILENAME)
$(FILENAME) : $(DRIVER)
$(CROSS)gcc -g -o $(FILENAME) $(DRIVER)
# $(CROSS)strip FILENAME
clean:
@rm -vf $(FILENAME) *.o *~
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
/*以上头文件包含如果搞不懂具体起什么作用,建议从系统驱动中拷贝*/
/*#define definition*/
#define DEVICE_NAME "gpio_drv" //设备名称 和文件系统里设备节点名称区分开
#define FIRST_MAJOR 0 //主设备号
#define LED_ON 1 //点亮
#define LED_OFF 0 //熄灭
static unsigned long led_table[] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
static unsigned int led_cfg_table[] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
/*上面两个数组采用源码的基于S3C2410头文件配置好的,如果需要自己配置 参考S3C2440用户手册*/
//.open func 应用程序将来调用的open函数 主要配置io口
static int myfirst_drv_open(struct inode *inode, struct file *filp)
{
int i;
for(i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
}
printk("myfirst_drv_open\n");
/*配置GPIB5 6 7 8 为输出*/
return 0;
}
//.write func 应用程序将来调用write部分 *file问文件句柄 *buf传入的用户(应用程序)数据地址 count为字节长度 一般为4的整数倍 前面buf为地址 一条指令4个字节
static ssize_t myfirst_drv_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int val; //接收用户数据
int i;;
printk("myfirst_drv_write\n");
copy_from_user(&val, buf, count); // copy_to_user();
将用户空间地址buf的数据交互到内核空间 val地址上
if (val == 1) //点亮LED灯
{
for(i = 0; i < 4; i++)
{
s3c2410_gpio_setpin(led_table[i], 0);
}
printk("led on\n");
}
else
{
for(i = 0; i < 4; i++)
{
s3c2410_gpio_setpin(led_table[i], 1);
}
printk("led off\n");
}
return 0;
}
//ioctl控制 应用程序将来调用的ioctl函数*file为应用层返回的文件句柄,cmd为操作命令 arg为传入的参数
static int gpio_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if (arg > 4)
{
return -EINVAL;
}
switch(cmd)
{
case LED_ON:
// 璁剧疆鎸囧畾寮曡剼鐨勮緭鍑虹數骞充负0
s3c2410_gpio_setpin(led_table[arg], 0);
return 0;
case LED_OFF:
// 璁剧疆鎸囧畾寮曡剼鐨勮緭鍑虹數骞充负1
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
//define the file_operations file_operations结构体
static struct file_operations myfirst_drv_fops = {
.owner = THIS_MODULE,
.open = myfirst_drv_open,
.write = myfirst_drv_write,
.ioctl = gpio_ioctl,
};
static struct class *myfirst_drv_class; //定义一个类 方便在/dev目录下建立一个节点
int major; //自动分配主设备号
int first_drv_init(void)
{
//int ret;
//ret = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);
major = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);
//if(ret < 0)//
if(major < 0)
{
printk("myfirst_drv can not register major number!\n");
return major;
}
/*注册一个类 使得mdev可以在/dev目录下自动建立设备节点 不需要手动mknod dev c 224 0这样的操作*/
myfirst_drv_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(myfirst_drv_class))
{
printk("error, fail to init myfirst_drv_class");
return -1;
}
/*如果上面成功注册进mdev的话 下面自动在/dev目录下创建一个设备节点*/
device_create(myfirst_drv_class, NULL, MKDEV(major, 0), NULL, "GPIO_DRV"); //这个就是在/dev下创建的MYFIRST_DRV 设备节点了 应用程序调用
printk("MYFIRST_DRV initialized! \n");
return 0;
}
void first_drv_exit(void)
{
unregister_chrdev(major, DEVICE_NAME); //卸载驱动
device_destroy(myfirst_drv_class, MKDEV(major, 0)); //删掉设备节点
class_destroy(myfirst_drv_class); //注销类
printk("rm -rm MYFIRST_DRV! \n");
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL"); //获得授权
该驱动makefile和上一篇一样 这里不再重复填上
下面看我们的测试程序 如何通过write open ioctl来操作硬件的?
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define LED_ON 1
#define LED_OFF 0
static int fd; //用于存放打开设备时返回的文件句柄
//例如我们想通过设备文件向驱动写入数据的时候 我们的main函数应该设置成下面的方式,下面的参数如何理解呢?我这里贴上一段参考----
int main(int argc, char **argv)
argc是命令行中的参数的个数 argv[]对应每一个参数 例如 ./a.exe 1 bb argc将会是3 其中 argv[0]是“./a.exe” argv[1]是“1” argv[2]是“bb”
int main(int argc, char **argv)
{
int led_no = 0;
int val ;
int ret;
fd = open("/dev/GPIO_DRV", O_RDWR); //打开设备 并读写权限 上一篇我们后面的参数写的是0 这里需要为O_RDWR,意思是赋予驱动读写权限
if(fd < 0) //成功打开fb返回的肯定是个非零值
{
perror("open device failed");
exit(1);
}
while(1) //每个应用程序都是死循环
{
for(led_no = 0; led_no < 4; led_no++)
{
ioctl(fd, LED_ON, led_no); //点亮一盏灯 这里采用循环点亮方式
sleep(2);
}
led_no = 0;
for(led_no = 0; led_no < 4; led_no++) //又熄灭一个led灯 熄灭2个。。。。。
{
ioctl(fd, LED_OFF, led_no); //熄灭一盏灯
sleep(1);
}
led_no = 0;
if(argc != 2) //如果传入参数不是2个的话 提示信息退出 ./gpio_test on 一定要这种方式
{
printf("Usage :\n");
printf("%s <on|off>\n", argv[0]);
return 0; //退出重新运行应用程序
}
if (strcmp(argv[1], "go") != 0) //如果第二个参数不是go 如果./gpio_test go 则运行流水灯 上面部分
{
if (strcmp(argv[1], "on") == 0) //如果第二个参数为on
{
printf("led app on\n");
val = 1;
}
else if (strcmp(argv[1], "off") == 0) //第二个参数为off
{
printf("led app off\n");
val = 0;
}
ret = write(fd, &val, 4); //写入 fd为文件句柄 传入要写的数据的地址&val 我们就要么1 要么0 所以一个地址就可以了 长度4字节
if(ret < 0) //如果写入成功 返回写入的个数 否则返回-1
{
printf("write failed ret = %d\n", ret);
return 1;
}
return 0;
}
}
//close(fd); //关闭设备
return 0;
}
编写该应用程序的makefile 为了以后开发方便 这里makefile较上篇做了一点改变
CROSS=arm-linux-
DRIVER=gpio_drvtest.c
FILENAME=gpio_test
all: $(FILENAME)
$(FILENAME) : $(DRIVER)
$(CROSS)gcc -g -o $(FILENAME) $(DRIVER)
# $(CROSS)strip FILENAME
clean:
@rm -vf $(FILENAME) *.o *~
相关文章推荐
- 基于tiny210v2的linux-3.9.6内核驱动移植1:led驱动
- 基于S3C2440的linux-3.6.6移植——LED驱动【转】
- 基于S3C2440的linux-3.6.6移植——LED驱动
- 基于sys文件系统的LED驱动的移植【原创】
- 基于linux2.6.30.4和s3c2440的 platform总线 led驱动
- 基于s3c2410的lcd和触摸屏驱动移植
- 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九)
- 【总结】高通Android LED驱动移植-GPIO,内核定时器
- 基于platform总线的mini2440的led设备驱动例子
- 基于s3c2410 2.6.30内核cs8900网卡驱动的移植
- S5PC100芯片的linux-lcd驱动移植(基于2.6.35.13内核)
- 移植LED和按键驱动
- s3c2440基于linux的gpio led字符设备驱动实践
- Linux芯片级移植与底层驱动(基于3.7.4内核)
- 基于S3C2440的Linux-3.6.6移植——UART驱动
- I.mx6s上移植wm8960驱动(基于linux3.0.101版本)
- 基于sysfs的LED驱动编写以及应用程序的编写(定时器操作)
- 基于S3C2450 + WINCE的背光驱动及背光亮度调节应用程序移植详解之驱动篇
- 基于tiny4412的Linux内核移植 -- SD卡驱动移植(五)
- 基于ubuntu-2.6.35内核的SDIO-WiFi驱动移植使其支持WAP