对于驱动的学习光看是没有用的,还是得要自己敲一敲代码。
杂项设备算是最简单的LINUX驱动那么就从他开始吧。
在LINUX中写驱动不同于在单片机中,单片机10-20行可以干好的事情LINUX的驱动可能要写到100-200行,虽然有些麻烦但是也有他的好处。功能的固化可以使你的代码更有价值。其实只要了解大致的框架,真正有用的部分还是单片机里的那一套。
实验一:点灯实验
之前其实已经点过灯了,但那是用了别人已有的驱动,这一次就来自己写一个
首先是驱动部分:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-exynos4.h>
#define DRIVER_NAME "myled_ctl"
#define DEVICE_NAME "myled_dev"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("YY");
/*******************************************************/
// process the device api
/*******************************************************/
static int led_open(struct inode* pinode,struct file * pfile)
{
printk(KERN_EMERG"led open!!\n");
return 0;
}
static int led_release(struct inode* pinode,struct file * pfile)
{
printk(KERN_EMERG"led close!!\n");
return 0;
}
static long led_ioctl(struct file * pfile, unsigned int cmd, unsigned long arg)
{
printk("cmd is %d ,arg is %d\n",cmd,arg);
if(cmd > 1)
{
printk(KERN_EMERG "cmd is 0 or 1 \n");
return 0;
}
if(arg==1)
{
printk(KERN_EMERG"LED1 is be setted\n");
gpio_set_value(EXYNOS4_GPL2(0),cmd);
return 0;
}
else if(arg==0)
{
printk(KERN_EMERG"LED2 is be setted\n;");
gpio_set_value(EXYNOS4_GPK1(1),cmd);
return 0;
}
else
{
printk(KERN_EMERG"arg can only be 0 or 1\n;");
return 0;
}
}
static struct file_operations led_ops=
{
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl=led_ioctl,
};
static struct miscdevice led_dev = {
.minor = MISC_DYNAMIC_MINOR,//自动分配设备号
.name = DEVICE_NAME,//设备名
.fops = &led_ops,
};
/*******************************************************/
/* process the driver */
/*******************************************************/
//we need to write something in it to make the device abled
static int led_probe(struct platform_device *pdv)
{
int res;
printk("the driver has prob with the device\n");
res =gpio_request(EXYNOS4_GPL2(0),"LED0");
if(res < 0)//申请失败
{
printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed\n");
return res;
}
res =gpio_request(EXYNOS4_GPK1(1),"LED1");
if(res < 0)//申请失败
{
printk(KERN_EMERG "gpio_request EXYNOS4_GPK1(1) failed\n");
return res;
}
s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT);
gpio_set_value(EXYNOS4_GPL2(0),0);
s3c_gpio_cfgpin(EXYNOS4_GPK1(1),S3C_GPIO_OUTPUT);
gpio_set_value(EXYNOS4_GPK1(1),0);
misc_register(&led_dev);
return 0;
}
static int led_remove (struct platform_device *pdv)
{
printk(KERN_EMERG "\tremove\n");
misc_deregister(&led_dev);
return 0;
}
static void led_shutdown (struct platform_device *pdv)
{
}
static int led_suspend (struct platform_device *pdv,pm_message_t state)
{
return 0;
}
static int led_resume (struct platform_device *pdv)
{
return 0;
}
struct platform_driver myled_driver = {
.probe =led_probe,
.remove =led_remove,
.shutdown=led_shutdown,
.suspend =led_suspend,
.resume =led_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int __init myled_init(void)
{
int state;
printk(KERN_EMERG"ENTER SUCCESS!\n");
state=platform_driver_register(&myled_driver);
printk(KERN_EMERG"\r\n state:%d",state);
return 0;
}
static void __exit myled_exit(void)
{
printk(KERN_EMERG "LED Exit!\n");
platform_driver_unregister(&myled_driver);
}
module_init(myled_init);
module_exit(myled_exit);
然后是测试程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;
char* dev_nod = "/dev/myled_dev";
if((fd = open(dev_nod,O_RDWR|O_NDELAY)) < 0)
{
printf("APP open %s failed\n",dev_nod);
}
else
{
printf("APP open %s success\n",dev_nod);
printf("the cmd input:\r\n%d,%d\r\n",atoi(argv[1]),atoi(argv[2]));
ioctl(fd,atoi(argv[1]),atoi(argv[2]));//灯亮
/*
ioctl(fd,1,1);
sleep(1);
ioctl(fd,0,0);
sleep(1);
ioctl(fd,0,1);
sleep(1);
ioctl(fd,1,0);
*/
}
close(fd);
}
实验二:按键轮询实验
实现六个按键的轮询操作
驱动部分:
#include <linux/init.h>
#include <linux/module.h>
/*driver register*/
#include <linux/platform_device.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>
/*Linux中申请GPIO的头文件*/
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
#include <mach/gpio-exynos4.h>
#define DRIVER_NAME "gpio_read_ctl"
#define DEVICE_NAME "gpio_read_ctl_dev"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("YY");
static int gpio_read_open(struct inode * pinode , struct file * pfile )
{
printk(KERN_EMERG "gpio_read OPEN !!\n");
return 0;
}
static int gpio_read_release(struct inode * pinode, struct file * pfile)
{
printk(KERN_EMERG "gpio_read RELEASE !!\n");
return 0;
}
/*应用中通过ioctl来获取管脚电平*/
static long gpio_read_ioctl(struct file * pfile, unsigned int cmd, unsigned long arg)
{
int ret;
printk("cmd is %d ,arg is %d\n",cmd,arg);
//参数cmd可以是0,1,2,3,4
if(cmd > 5)
{
printk(KERN_EMERG "cmd is 0,1,2,3,4 \n");
return 0;
}
if(arg > 1)
{
printk(KERN_EMERG "arg is only 1 \n");
return 0;
}
switch(cmd)
{
case 0:ret = gpio_get_value(EXYNOS4_GPX1(1));break;//home
case 1:ret = gpio_get_value(EXYNOS4_GPX1(2));break;//back
case 2:ret = gpio_get_value(EXYNOS4_GPX3(3));break;//sleep
case 3:ret = gpio_get_value(EXYNOS4_GPX2(1));break;//vol+
case 4:ret = gpio_get_value(EXYNOS4_GPX2(0));break;//vol-
default:break;
}
return ret;
}
static struct file_operations gpio_read_ops = {
.owner = THIS_MODULE,
.open = gpio_read_open,
.release = gpio_read_release,
.unlocked_ioctl = gpio_read_ioctl,
};
static struct miscdevice gpio_read_dev = {
.minor = MISC_DYNAMIC_MINOR,//自动分配设备号
.name = DEVICE_NAME,//设备名
.fops = &gpio_read_ops,
};
static int gpio_read_probe (struct platform_device *pdv){
int ret;
printk(KERN_EMERG "\tinitialized\n");
/*申请GPIO*/
ret = gpio_request(EXYNOS4_GPX1(1),"SWITCH 0");
if(ret < 0)
{
printk(KERN_EMERG "gpio_request EXYNOS4_GPX1(1) failed\n");
return ret;
}
else
{
/*设置为输入*/
s3c_gpio_cfgpin(EXYNOS4_GPX1(1),S3C_GPIO_INPUT);
/*不上拉不下拉*/
s3c_gpio_setpull(EXYNOS4_GPX1(1),S3C_GPIO_PULL_NONE);
}
ret = gpio_request(EXYNOS4_GPX1(2),"SWITCH 1");
if(ret < 0)
{
printk(KERN_EMERG "gpio_request EXYNOS4_GPX1(2) failed\n");
return ret;
}
else
{
/*设置为输入*/
s3c_gpio_cfgpin(EXYNOS4_GPX1(2),S3C_GPIO_INPUT);
/*不上拉不下拉*/
s3c_gpio_setpull(EXYNOS4_GPX1(2),S3C_GPIO_PULL_NONE);
}
ret = gpio_request(EXYNOS4_GPX3(3),"SWITCH 2");
if(ret < 0)
{
printk(KERN_EMERG "gpio_request EXYNOS4_GPX3(3) failed\n");
return ret;
}
else
{
/*设置为输入*/
s3c_gpio_cfgpin(EXYNOS4_GPX3(3),S3C_GPIO_INPUT);
/*不上拉不下拉*/
s3c_gpio_setpull(EXYNOS4_GPX3(3),S3C_GPIO_PULL_NONE);
}
ret = gpio_request(EXYNOS4_GPX2(1),"SWITCH 3");
if(ret < 0)
{
printk(KERN_EMERG "gpio_request EXYNOS4_GPX2(1) failed\n");
return ret;
}
else
{
/*设置为输入*/
s3c_gpio_cfgpin(EXYNOS4_GPX2(1),S3C_GPIO_INPUT);
/*不上拉不下拉*/
s3c_gpio_setpull(EXYNOS4_GPX2(1),S3C_GPIO_PULL_NONE);
}
/*生成设备节点*/
misc_register(&gpio_read_dev);
return 0;
}
static int gpio_read_remove (struct platform_device *pdv){
printk(KERN_EMERG "\tremove\n");
misc_deregister(&gpio_read_dev);
return 0;
}
static void gpio_read_shutdown (struct platform_device *pdv){
}
static int gpio_read_suspend (struct platform_device *pdv,pm_message_t state){
return 0;
}
static int gpio_read_resume (struct platform_device *pdv){
return 0;
}
struct platform_driver gpio_read_driver = {
.probe = gpio_read_probe,
.remove = gpio_read_remove,
.shutdown = gpio_read_shutdown,
.suspend = gpio_read_suspend,
.resume = gpio_read_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int gpio_read_init(void)
{
int DriverState;
printk(KERN_EMERG "GPIO_READ enter!\n");
DriverState=platform_driver_register(&gpio_read_driver);
printk(KERN_EMERG "\t%d\n",DriverState);
return 0;
}
static void gpio_read_exit(void)
{
printk(KERN_EMERG "GPIO_READ exit!\n");
platform_driver_unregister(&gpio_read_driver);
}
module_init(gpio_read_init);
module_exit(gpio_read_exit);
测试程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc,char **argv)
{
int fd,cmd=0;
char *read_node = "/dev/gpio_read_ctl_dev";
cmd=atoi(argv[1]);
printf("THE CMD IS %d\n",cmd);
if((fd = open(read_node,O_RDWR|O_NDELAY))<0)
{
printf("APP open %s failed\n",read_node);
}
else
{
printf("APP open %s success\n",read_node);
printf("%d io value is %d\n",cmd,ioctl(fd,cmd,0));
}
close(fd);
}
实验现象: