Chinaunix首页 | 论坛 | 博客
  • 博客访问: 601865
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 22:57
个人简介

我本仁慈,奈何苍天不许

文章分类

全部博文(165)

文章存档

2018年(1)

2016年(33)

2015年(5)

2014年(34)

2013年(92)

分类: LINUX

2013-12-26 16:48:38

ARM接口驱动开发

{//?什么是驱动开发

 驱动就是驱使目标设备动作,

 而驱动开发就涉及到如何控制设备运行的相关的硬件和软件控制,它是软硬结合的。

 而linux驱动开发则涉及到linux为更好的管理设备提出的一些框架模型。

}

{//?如何开发一个新驱动

  {//----LED 驱动  (代码见 led) 

1. 看电路图   //找到要控制LED灯得管脚 

   FS_S5PC100_DEV.pdf

   FS_S5PC100_CORE.pdf

    

2. 看芯片手册  //找到LED管脚的控制寄存器的地址

   S5PC100_UM_REV1.02.pdf          //GPG3 搜到 GPG3DAT GPG3CON 

   

                             |--- GPIO

                                |--- 串口

                                |--- SPI

      ---------------           |--- I2C

      |   CPU       |  ---------|--- USB

      | s5pc100     |           |--- LCD

      |  667MHZ     |           |--- VGA

      ---------------           |--- camera

         |        |             |--- 音频口

         |        |             |--- SD

     |-------| |--------        |--- 网口

     |  DDR  | | FLASH |                

     |-------| ---------  

    

  #define S5PC100_GPG3_BASE 0xE0300000

  #define S5PC100_LED_CON  (S5PC100_GPG3_BASE + 0x01C0)

  #define S5PC100_LED_DAT  (S5PC100_GPG3_BASE + 0x01C4)

3. 找一个linux驱动的框架    

  //---driver_char\hello_char\hello.c    //这里使用的字符设备驱动的框架

4. 把裸机的驱动整合到linux驱动框架中。

struct led_info{

  struct cdev *cdev;

    __iomem  *led_con;

    __iomem  *led_dat;

}

led_info  *led;   

   

   static int hello_open (struct inode *inode, struct file *file)

  {

   struct resource *res;

   led = kmalloc(sizeof(led_info),GFP_KERNEL);

   res = request_mem_region(S5PC100_LED_CON, 8, "fsled");

   led->led_con  = ioremap(S5PC100_LED_CON, 4);

   iowrite32(( (ioread32(led->led_con)&(~0x0f))|0x01, led->led_con);

  }

 

5. 在驱动的ioctl 中添加个性的控制  //如 led 的开关控制

6. 修改Makefile 是它能编译后在板子上跑

   更改Makefile

   KERNELDIR ?=  /root/linux-2.6.35-farsight

 为你要编译的内核源码的目录

7. 把编译生成的.ko 文件放到  板子上运行。

 insmod hello.ko

 lsmod 

 mknod /dev/hello c 250 0

 cat /proc/devices 

 ./test 测试程序  //会看到led灯闪烁

 rmmod hello           /* 

                             可能报错rmmod: chdir(2.6.35-g9c29226-dirty): No such file or directory

                              原因是busybox制作时选择简单module配置,会找/lib/modules 让内核导出依赖关系文件            

                              所以rmmod 会坚持依赖关系,这里不重制作busybox了, 直接在/lib/modules 创建 2.6.35-g9c29226-dirty 文件绕过。

                             */ 

{//---led/hello.c              动态映射

#include   

#include 

#include 

#include 

#include 

#include 

#include  //for iowrite32

#include  //for kmalloc

#include  //for request_mem_region

#include "hello.h"

MODULE_LICENSE ("GPL");    

#define DEVICE_MAJOR 250

#define DEVICE_MINOR 0

#define DEVICE_NUM 1

#define DEVICE_NAME "hello"

#define S5PC100_GPG3_BASE 0xE0300000

#define S5PC100_LED_CON  (S5PC100_GPG3_BASE + 0x01C0)

#define S5PC100_LED_DAT  (S5PC100_GPG3_BASE + 0x01C4)

struct led_info{

  struct cdev   *cdev;

  int  __iomem  *led_con;

  int  __iomem  *led_dat;

};

struct led_info  *led;

static int led_on(void)

{

   iowrite32( 1,led->led_dat);

   return 0;

}

static int led_off(void)

{

   iowrite32( 0,led->led_dat);

   return 0;

}

static int hello_open (struct inode *inode, struct file *file)

{

   struct resource *res;

   /* I/O 内存空间

    I/O 内存:  I/O 和内存统一编址  如arm powerpc

    I/O 端口:  I/O 和内存独立编址  如 x86

    

     ---------------------

     | request_mem_region |     申请内存区域, 非必须的,但使用它较安全,系统会自动进行互斥处理

     ----------------------   

     |      ioremap       |     动态映射硬件物理地址到内核地址空间     

     ----------------------

     | ioread32 iowrite32 |     I/O 内存对应的读写函数 

     ----------------------

     |      iounmap       |     解除ioremap的动态映射

     ----------------------  

     | release_mem_region |     释放申请的内存区域

     ----------------------

   */

   res = request_mem_region(S5PC100_LED_CON, 8, DEVICE_NAME);  /* request_mem_region 申请一个开始于start,len字节的I/O内存区。

                     申请成功,返回一个非NULL指针;否则返回NULL

                    系统当前所有I/O内存分配信息都在/proc/iomem文件中列出,

                    你分配失败时,可以看看该文件,看谁先占用了该内存区

                                                                  */

   if(!res)

   { 

       printk("request mem fsled fail\n");

        goto  err_request_mem;

   }

   /*  ioremap用于动态将I/O内存区映射到内核虚拟地址

       phys_addr  为要映射的I/O内存起始地址

       size       为要映射的I/O内存的大小,

       返回值     为被映射到的虚拟地址 

       */

   led->led_con  = ioremap(S5PC100_LED_CON, 4);  //建议先使用request_mem_region后,再ioremap, 较安全

   if(!led->led_con)

   {

       printk("led_con  ioremap fail\n");

       goto err_map_con;

   }

   

   led->led_dat  = ioremap(S5PC100_LED_DAT, 4);

    if(!led->led_dat)

   {

       printk("led_dat  ioremap fail\n");

       goto err_map_dat;

   }  

   iowrite32((ioread32(led->led_con)&(~0x0f))|0x01, led->led_con);

   printk ("device opened\n");

   return 0;

err_map_dat:

   iounmap(led->led_con);

err_map_con:

  release_mem_region(S5PC100_LED_CON,8);

err_request_mem:

   return -1;

}

static int hello_release (struct inode *inode, struct file *file)

{

  iounmap(led->led_dat);

  iounmap(led->led_con);

  release_mem_region(S5PC100_LED_CON,8);

  printk ("device closed\n");

  return 0;

}

int hello_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)

{

  int ret=0;

  switch (cmd) {

  case HELLO_ONE:

    led_on();

    printk ("HELLO_ONE cmd  called\n");

    break;

  case HELLO_TWO:

    led_off();

    printk ("HELLO_TWO cmd called\n");

    break;

  default:

    printk ("cmd other called\n");

    break;

  }

  return ret;

}  

struct file_operations hello_fops = {

 .owner = THIS_MODULE,

 .open  = hello_open,

 .release = hello_release,

 .ioctl = hello_ioctl

};

static int __init hello_init (void)

{     

  int ret;

  dev_t dev_id = 0;

  struct cdev   *cdev;

  dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);

  ret = register_chrdev_region (dev_id, DEVICE_NUM, DEVICE_NAME);  

  if (ret<0) {

    printk (" can't get major number %d\n", DEVICE_MAJOR);

    goto err_reg_cdev;

  }        

  cdev = cdev_alloc();

  if(NULL == cdev)

  {

    printk (" cdev_alloc fail\n");

    goto err_cdev_alloc;

  }

  cdev_init(cdev,&hello_fops);

  cdev->owner = THIS_MODULE;

  cdev->ops = &hello_fops;

  ret = cdev_add(cdev,dev_id,DEVICE_NUM);

  if(ret)

  {

      printk("cdev_add fail %d\n",ret);

      goto err_cdev_add;

  }

  led = kmalloc(sizeof(struct led_info), GFP_KERNEL);

  if (!led) {

printk("led: kzmalloc failure\n");

ret = -ENOMEM;

goto err_kmalloc;

  }

  led->cdev = cdev;

  printk("char device register ok\n");     

  return 0;

err_kmalloc:

err_cdev_add:

  cdev_del(cdev);

err_cdev_alloc:

  unregister_chrdev_region(dev_id,DEVICE_NUM);

err_reg_cdev:

  return ret;

}

static void __exit hello_exit (void)    

{

  dev_t dev_id = 0;

  dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);

  if (led->cdev)

    cdev_del (led->cdev);

   unregister_chrdev_region (dev_id, DEVICE_NUM);

   printk("char unregister ok\n");   

}

module_init (hello_init);

module_exit (hello_exit);  

{//---led_map_desc/hello.c     静态映射

#include   

#include 

#include 

#include 

#include 

#include 

#include         // for writel

#include // for S5PC100_GPG3_BASE 

#include "hello.h"

MODULE_LICENSE ("GPL");    

#define DEVICE_MAJOR 250

#define DEVICE_MINOR 0

#define DEVICE_NUM 1

#define DEVICE_NAME "hello"

struct cdev *cdev;

static int led_on(void)

{

   writel( 0x1, S5PC100_GPG3_BASE + 4);

   return 0;

}

static int led_off(void)

{

   writel( 0, S5PC100_GPG3_BASE + 4);

   return 0;

}

static int hello_open (struct inode *inode, struct file *file)

{

/*  S5PC100_GPG3_BASE由来

            S5PC100_GPG3_BASE    ->S5PC100_GPIO_BASE -> S5P_VA_GPIO -> #define S3C_ADDR_BASE   (0xF4000000) 

                       0xF4000000 + 0x00500000 + 0x01C0 + 4 = 0xF45001C4           

        内核地址静态映射见 arch/arm/mach-s5pc100/mach-smdkc100.c ->smdkc100_map_io ->  s5p_iodesc -> 

                 .virtual        = (unsigned long)S5P_VA_GPIO,   //S5P_VA_GPIO  内核中的地址 0xF4500000

                 .pfn            = __phys_to_pfn(S5P_PA_GPIO),   //S5P_PA_GPIO  实际物理地址 对应datasheet中 0xE0300000

      

         0xE0300000 + 01C0 + 4 =  0xE03001C4  对应 datasheet 中 GPG3  实际物理地址 

         

     注: 内核启动后会自动调用它,完成静态映射    

*/

  writel((readl(S5PC100_GPG3_BASE) & ~0xF) | 0x1, S5PC100_GPG3_BASE);

  printk ("device opened\n");

  return 0;

}

static int hello_release (struct inode *inode, struct file *file)

{

  printk ("device closed\n");

  return 0;

}

int hello_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)

{

  int ret=0;

  switch (cmd) {

  case HELLO_ONE:

    led_on();

    printk ("HELLO_ONE cmd  called\n");

    break;

  case HELLO_TWO:

    led_off();

    printk ("HELLO_TWO cmd called\n");

    break;

  default:

    printk ("cmd other called\n");

    break;

  }

  return ret;

}  

struct file_operations hello_fops = {

 .owner = THIS_MODULE,

 .open  = hello_open,

 .release = hello_release,

 .ioctl = hello_ioctl

};

static int __init hello_init (void)

{     

  int ret;

  dev_t dev_id = 0;

  struct cdev   *cdev;

  dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);

  ret = register_chrdev_region (dev_id, DEVICE_NUM, DEVICE_NAME);  

  if (ret<0) {

    printk (" can't get major number %d\n", DEVICE_MAJOR);

    goto err_reg_cdev;

  }        

  cdev = cdev_alloc();

  if(NULL == cdev)

  {

    printk (" cdev_alloc fail\n");

    goto err_cdev_alloc;

  }

  cdev_init(cdev,&hello_fops);

  cdev->owner = THIS_MODULE;

  cdev->ops = &hello_fops;

  ret = cdev_add(cdev,dev_id,DEVICE_NUM);

  if(ret)

  {

      printk("cdev_add fail %d\n",ret);

      goto err_cdev_add;

  }

  printk("char device register ok\n");     

  return 0;

err_cdev_add:

  cdev_del(cdev);

err_cdev_alloc:

  unregister_chrdev_region(dev_id,DEVICE_NUM);

err_reg_cdev:

  return ret;

}

static void __exit hello_exit (void)    

{

  dev_t dev_id = 0;

  dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);

  if (cdev)

    cdev_del (cdev);

   unregister_chrdev_region (dev_id, DEVICE_NUM);

   printk("char unregister ok\n");   

}

module_init (hello_init);

module_exit (hello_exit);

  }

}

}

}

阅读(702) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~