首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

(转)ok6410学习笔记(10.硬件访问之led控制2)

(转)ok6410学习笔记(10.硬件访问之led控制2)

混杂设备驱动的ioremap见ok6410学习笔记(9.混杂设备驱动及硬件访问)
简单字符驱动的ioremap驱动memdev.c

[cpp] view plaincopy


  • /**************************************************************************
  • 文件名:            memdev.c
  • 日期:              2013/06/07   
  • 头文件:            memdev.h
  • 功能:              ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分
  • 环境:              Redhat企业版5  内核版本2.6.18-53.el5  
  • 作者:              Hao  
  • 流程:              1.分配设备号(a.静态申请  b.动态分配)
  •              2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名”  b.自动创建)
  •              3.设备注册(a.设备注册分配  b.设备注册初始化  c.设备注册添加)
  •              4.实现file_operation中的函数
  •                ioctl驱动   
  •                       a.初始化cmd
  •                       b.无参数的命令
  •                           c.读方向的命令
  •                           d.写方向的命令
  •                           e.在ioctl函数中命令的实现
  •              5.设备注销(cdev_del)
  •              6.设备号注销
  • ***************************************************************************/
  • #include <linux/module.h>
  • #include <linux/miscdevice.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 <linux/slab.h>
  • #include <linux/poll.h>
  • #include <linux/device.h>
  • #include <linux/ioport.h>
  • #include <asm/io.h>
  • #include <asm/system.h>
  • #include <asm/uaccess.h>
  • #include <asm/atomic.h>
  • #include <linux/ioctl.h>
  • #include <linux/kernel.h>
  • #include "memdev.h"

  • static
    int mem_major = MEMDEV_MAJOR;  
  • module_param(mem_major, int, S_IRUGO);  
  • struct cdev c_dev;   

  • struct
    class *mem_class;  /*设备的类*/
  • int kernel_num=1991;//用一个全局变量吧  要不不能把先写入的数据保存下来
  • char kernel_buf[40]="hello kernel!!!";  

  • volatile unsigned long GPIOM_VA_BASE;//定义一个全局变量  保存ioremap映射的地址
  • #define GPIOM_CON_VA    GPIOM_VA_BASE      
  • #define GPIOM_DAT_VA    (GPIOM_VA_BASE+0x4)     
  • #define GPIOM_PUD_VA    (GPIOM_VA_BASE+0x8)

  • MODULE_AUTHOR("Hao");  
  • MODULE_LICENSE("GPL");  


  • /**************************************************************************
  • 函数名:                    ok6410_led_setup
  • 函数功能:                   ioremap映射 gpio初始化
  • 函数参数:                   无
  • 函数返回值:                  无
  • ***************************************************************************/
  • static
    void ok6410_led_setup(void)  
  • {  
  •         unsigned long temp;   
  •         request_mem_region(0x7f008820,0x10,"led_name");//申请i/o内存
  •         GPIOM_VA_BASE = (volatile unsigned long)ioremap(0x7f008820, 0x10);//


  •         /*temp&=~0xffff;
  •         temp|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
  •         (*(volatile unsigned long *)GPIOM_VA_BASE)=temp;*/

  •         (*(volatile unsigned long *)GPIOM_VA_BASE)&=~0xffff;      
  •         (*(volatile unsigned long *)GPIOM_VA_BASE)|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);  

  •         temp=0;  
  •         (*(volatile unsigned long *)GPIOM_DAT_VA)=temp;  
  •         //GPIOM_DAT_VA|=0xf;
  •         //GPIOM_DAT_VA=0x00;


  •         /*******************或者使用这种方式****************************/
  • /*      temp&=~0xffff;
  •         temp|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);  
  •         writel(temp, GPIOM_CON_VA);

  •         temp|=0xf;
  •         temp=0;
  •         writel(temp, GPIOM_DAT_VA);*/

  • }  

  • /**************************************************************************
  • 函数名:                    ok6410_led_release
  • 函数功能:                   iounmap撤销映射 release_mem_region撤销申请
  • 函数参数:                   无
  • 函数返回值:                  无
  • ***************************************************************************/
  • static
    void ok6410_led_release(void)  
  • {  
  •     iounmap((void*)GPIOM_VA_BASE);  
  •     release_mem_region(0x7f008820,0x10);  
  • }  
  • /**************************************************************************
  • 函数名:                     memdev_ioctl
  • 函数功能:                   ioctl实现函数  命令实习函数
  • 函数参数:                   无
  • 函数返回值:                 返回ret为正常执行   返回-EINVAL命令号不正确
  • ***************************************************************************/
  • static
    long memdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  
  • {  
  •     int ret=0;  
  •     int err=0;  
  •     int kernel_num=1991;  
  •     //char kernel_buf[20]="hello kernel!!!";

  •     /*先判断命令号是否正确*/
  •     if (_IOC_TYPE(cmd) != CMD_KTYPE) //获得命令的type类型是否正确
  •             return -EINVAL;  
  •         if (_IOC_NR(cmd) > LED_KCMD)    //获得命令的num类型  是否小于命令个数
  •             return -EINVAL;  



  •         /*获命令的数据传输方向   根据各自的方向判断*/
  •         if (_IOC_DIR(cmd) & _IOC_READ)  
  •              err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));/*此函数是根据
  •              内核空间写的 是用来判断 arg应用程序传来的用户空间 是否有效的  所以对于用户空间来说是写*/
  •         else
    if (_IOC_DIR(cmd) & _IOC_WRITE)  
  •              err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));//对于用户空间来说是读   成功返回1  失败返回0
  •         if (err)   
  •             return -EFAULT;  


  •         /*实现CMD的用法*/
  •         switch(cmd)  
  •         {  
  •             case LEDR_KCMD:   
  •                 ret=__put_user(kernel_num, (int *)arg);  //把内核中的int型数据读入用户空间   unsigned long arg就是一个地址值   kernel->arg
  •                 break;  
  •             case LEDW_KCMD:  
  •                 ret=__get_user(kernel_num, (int *)arg);   //arg->kernel_num   把用户空间的数据传递给kernel_num
  •                 printk(KERN_EMERG "WRITE_KCMD is in kernel!!!  kernel_num:%d \n",kernel_num);  
  •                 if(1==kernel_num)  
  •                     {  
  •                             writel(0x0, GPIOM_DAT_VA);//将4个led全部点亮
  •                     }  
  •                 if(0==kernel_num)  
  •                     {  
  •                             writel(0x1f, GPIOM_DAT_VA);//将4个led全部熄灭
  •                     }  
  •                 break;  
  •             default:  
  •                 return -EINVAL;  
  •                 break;  
  •         }     

  • }  


  • int mem_release(struct inode *inode, struct file *filp)  
  • {  
  •     return 0;  
  • }  

  • int mem_open(struct inode *inode,struct file *filp)  
  • {  
  •     return 0;   
  • }  

  • static
    const
    struct file_operations mem_fops =  //定义此字符设备的file_operations
  • {                       //这里是对结构体整体赋值的方式
  •     .owner = THIS_MODULE, //函数名都可以自己定义  都是函数指针
  •     .open = mem_open,  
  •     .release = mem_release,  
  •     .unlocked_ioctl=memdev_ioctl,  
  • };  

  • static
    int memdev_init(void)  
  • {  
  •     ok6410_led_setup();  
  •     int result;  
  •     dev_t dev_num=MKDEV(mem_major,0);//将主设备号和次设备号转换成32位的设备号给   静态申请设备号用的
  •     if(mem_major)  
  •         result=register_chrdev_region(dev_num,2,"newmemdev");//静态申请设备号为dev_num 2个设备  设备名为“newmemdev”
  •     else
  •     {  
  •         result=alloc_chrdev_region(&dev_num,0,2,"newmemdev");//动态分配设备号   设备名可以在/proc/devices文件中找   与/dev路径找文件不同  因为一个设备文件  一个次设备号
  •         mem_major = MAJOR(dev_num);//获得主设备号
  •     }  
  •     if (result < 0)  
  •     {  
  •          return result;   //判断动态分配是否成功  不成功则退出函数
  •     }  
  •                  mem_class = class_create(THIS_MODULE, "mem_class_name");//创建设备类
  •          device_create(mem_class, NULL, MKDEV(mem_major, 0), NULL, "memdev0"); //创建设备文件 文件名为memdev0
  •     /*设备注册 分配已经在前面完成了 为全局变量*/
  •         cdev_init(&c_dev,&mem_fops);//设备注册初始化  将设备号dev_num和file_operation建立联系
  •         c_dev.owner = THIS_MODULE;//*****************貌似可以屏蔽吧*********************
  •         cdev_add(&c_dev,dev_num,2);//设备注册  添加   设备号为dev_num 设备数为2个
  •         return 0;  
  • }  

  • static
    int memdev_exit(void)  
  • {  
  •     ok6410_led_release();  
  •     cdev_del(&c_dev);//设备注销
  •     device_destroy(mem_class, MKDEV(mem_major, 0));/*注销设备*/  
  •     class_destroy(mem_class);  /*删除类*/
  •     unregister_chrdev_region(MKDEV(mem_major, 0), 2);//设备号注销
  •     return 0;  
  • }  

  • module_init(memdev_init);  
  • module_exit(memdev_exit);  


memdev.h

[cpp] view plaincopy


  • /**************************************************************************
  • 文件名:            memdev.h
  • 日期:              2013/06/07   
  • 头文件:            memdev.h
  • 功能:              ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分头文件
  •                      定义三个命令
  • 环境:              Redhat企业版5  内核版本2.6.18-53.el5  
  • 作者:              Hao  
  • 流程:              1.分配设备号(a.静态申请  b.动态分配)
  •              2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名”  b.自动创建)
  •              3.设备注册(a.设备注册分配  b.设备注册初始化  c.设备注册添加)
  •              4.实现file_operation中的函数
  •                ioctl驱动   
  •                       a.初始化cmd
  •                       b.无参数的命令
  •                           c.读方向的命令
  •                           d.写方向的命令
  •              5.设备注销(cdev_del)
  •              6.设备号注销
  • ***************************************************************************/
  • #ifndef _MEMDEV_H_
  • #define _MEMDEV_H_

  • #include <linux/ioctl.h>

  • #ifndef MEMDEV_MAJOR
  • #define MEMDEV_MAJOR 251   /*预设的mem的主设备号*/
  • #endif

  • #ifndef MEMDEV_NR_DEVS
  • #define MEMDEV_NR_DEVS 2    /*设备数*/
  • #endif

  • #ifndef MEMDEV_SIZE
  • #define MEMDEV_SIZE 4096   /*开辟的内存大小*/
  • #endif

  • /*mem设备描述结构体*/
  • struct mem_dev                                       
  • {                                                         
  •   char *data;                        
  •   unsigned long size;         
  • };  

  • #define CMD_KTYPE 'k' //定义命令幻数   也叫命令类型


  • #define LEDR_KCMD   _IOR(CMD_KTYPE,1,int)   //定义读方向的命令
  • #define LEDW_KCMD  _IOW(CMD_KTYPE,2,int)  //定义写方向的命令

  • #define LED_KCMD 2  //命令个数  后面判断命令是否有效 用的


  • #endif /* _MEMDEV_H_ */


app_led.c

[cpp] view plaincopy


  • #include <stdio.h>
  • #include <string.h>
  • #include <stdlib.h>
  • #include <unistd.h>
  • #include<sys/types.h>
  • #include<sys/stat.h>
  • #include<fcntl.h>
  • #include "memdev.h"

  • int main(int argc, char *argv[])  
  • {  
  •     int fd=0;  
  •     printf("\n%d\n",*argv[1]);  
  •     unsigned int arg=(unsigned int)(*argv[1]-'0');  
  •     char buf[40]="WRITE_STR_KCMD is in kernel";  
  •     if(-1==(fd=open("/dev/memdev0",O_RDWR)))  //设备节点名称为memdev0
  •     {  
  •         printf("Open Dev Mem0 Error!\n");  
  •         _exit(EXIT_FAILURE);  
  •     }  
  •     printf("begin WRITE_KCMD!!!\n");  //写入一个int型arg
  •     ioctl(fd,LEDW_KCMD,&arg);  
  •     close(fd);  
  •     return 0;  
  • }  


注意的还是ioremap的返回值的利用。
对应飞凌驱动的测试程序[cpp] view plaincopy


  • /*此为ok6410  飞凌提供的驱动的测试程序 */
  • #include <stdio.h>
  • #include <string.h>
  • #include <stdlib.h>
  • #include <unistd.h>
  • #include<sys/types.h>
  • #include<sys/stat.h>
  • #include<fcntl.h>

  • int main(int argc,char *argv[])  
  • {  
  •     int fd=0;  
  •     printf("\n%d\n",*argv[1]);  
  •     unsigned int arg=(unsigned int)(*argv[1]-'0');  //驱动中规定有两个参数第一个当命令用 为0的时候是关灯 为1的时候是开灯
  •     printf("%d\n",arg);  
  •     unsigned int arg1=(unsigned int)(*argv[2]-'0'); //第二参数是第几个灯进行开关
  •     printf("%d\n",arg1);  
  •     char buf[40]="WRITE_STR_KCMD is in kernel";  
  •     if(-1==(fd=open("/dev/leds",O_RDWR)))  //设备节点名称为memdev0
  •     {  
  •         printf("Open Dev Mem0 Error!\n");  
  •         exit(EXIT_FAILURE);  
  •     }  
  •     printf("begin WRITE_KCMD!!!\n");  //写入一个int型arg
  •     ioctl(fd,arg,arg1);  
  •     close(fd);  
  •     return 0;  
  • }  


驱动在/drivers/char/s3c6410_leds.c

[cpp] view plaincopy


  • #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>

  • #include <mach/map.h>
  • #include <mach/regs-clock.h>
  • #include <mach/regs-gpio.h>

  • #include <plat/gpio-cfg.h>
  • #include <mach/gpio-bank-e.h>
  • #include <mach/gpio-bank-m.h>

  • #define DEVICE_NAME "leds"

  • static
    long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  
  • {  
  •     switch(cmd) {  
  •         unsigned tmp;  
  •     case 0:  
  •     case 1:  
  •                  if (arg > 4)   
  •                  {  
  •             return -EINVAL;  
  •          }  
  •         tmp = readl(S3C64XX_GPMDAT);  

  •         if(cmd==0) //close light
  •                   {   
  •             tmp &= (~(1<<arg));  
  •                   }  
  •         else
    //open light
  •                   {   
  •             tmp |= (1<<arg);  
  •                   }  

  •                 writel(tmp,S3C64XX_GPMDAT);  

  •         printk (DEVICE_NAME": %d %d\n", arg, cmd);  
  •         return 0;  
  •     default:  
  •         return -EINVAL;  
  •     }  
  • }  

  • static
    struct file_operations dev_fops = {  
  •     .owner          = THIS_MODULE,  
  •     .unlocked_ioctl = s3c6410_leds_ioctl,  
  • };  

  • static
    struct miscdevice misc = {  
  •     .minor = MISC_DYNAMIC_MINOR,  
  •     .name = DEVICE_NAME,  
  •     .fops = &dev_fops,  
  • };  

  • static
    int __init dev_init(void)  
  • {  
  •     int ret;  

  •        unsigned tmp;  

  •        //gpm0-3 pull up
  •     tmp = readl(S3C64XX_GPMPUD);  
  •     tmp &= (~0xFF);  
  •     tmp |= 0xaa;  
  •     writel(tmp,S3C64XX_GPMPUD);  

  •     //gpm0-3 output mode
  •     tmp =readl(S3C64XX_GPMCON);  
  •     tmp &= (~0xFFFF);  
  •     tmp |= 0x1111;  
  •     writel(tmp,S3C64XX_GPMCON);  

  •     //gpm0-3 output 0
  •     tmp = __raw_readl(S3C64XX_GPMDAT);  
  •     tmp |= 0x10;  
  •     writel(tmp,S3C64XX_GPMDAT);   

  •     ret = misc_register(&misc);  

  •     printk (DEVICE_NAME"\tinitialized\n");  

  •     return ret;  
  • }  

  • static
    void __exit dev_exit(void)  
  • {  
  •     misc_deregister(&misc);  
  • }  

  • module_init(dev_init);  
  • module_exit(dev_exit);  
  • MODULE_LICENSE("GPL");  
  • MODULE_AUTHOR("FORLINX Inc.");  


这个驱动是不能改的,因为是编译进内核的,所有只能用测试程序去适应驱动。2440也有对应的驱动,在国嵌和友善的内核里面。
返回列表