Chinaunix首页 | 论坛 | 博客
  • 博客访问: 411880
  • 博文数量: 36
  • 博客积分: 960
  • 博客等级: 准尉
  • 技术积分: 1368
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-13 19:26
文章分类
文章存档

2018年(3)

2012年(6)

2011年(27)

分类: LINUX

2011-08-08 19:28:44

===========================================
本文系作者原创, 欢迎大家转载!
转载请注明出处:netwalker.blog.chinaunix.net
===========================================
 
硬件平台:
MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0D
Kernel:Linux2.6.28
BootLoader:Uboot1.1.6
 

11. LINUXled驱动解析

Linux2.6.28中的LED驱动位于drivers/char/s3c6410_leds.c

 

  1. static int __init s3c6410_leds_init(void)
  2. {
  3.     int ret = 0;
  4.   unsigned long tmp;
  5.     dev_t devno;
  6.   
  7.   printk(KERN_NOTICE "enter s3c6410_leds_init\n");

  8.   devno = MKDEV(LED_MAJOR,0);
  9.     
  10.      //申请设备号资源,对应到内核的HASH表chrdevs    
  11.      ret = register_chrdev_region(devno,1,DEVICE_NAME);     
  12.      if(ret<0)    
  13.      {
  14.      printk(KERN_NOTICE "can not register led device");
  15.      return ret;    
  16.      }
  17.     
  18.      //初始化字符设备cdev_leds,并安装操作函数    
  19.      cdev_init(&cdev_leds,&s3c6410_leds_fops);    
  20.      cdev_leds.owner = THIS_MODULE;
  21.          
  22.      //通告内核,这样就可以在/proc/devices下看到231 leds设备了    
  23.      ret =cdev_add(&cdev_leds,devno,1);    
  24.      if(ret)    
  25.      {
  26.             printk(KERN_NOTICE "can not add leds device");
  27.      return ret;
  28.      }
  29.     
  30.      //创建/sys下的my_class类文件    
  31.      my_class = class_create(THIS_MODULE,"my_class");    
  32.      if(IS_ERR(my_class))    
  33.      {
  34.              printk("Err: Failed in creating class\n");
  35.      return -1;    
  36.      }
  37.     
  38.      // 创建/dev/leds文件,提供用户空间访问的接口    
  39.      device_create(my_class,NULL,MKDEV(LED_MAJOR,0),NULL,DEVICE_NAME);

  40.    //与lowlevel_init.S初始化类似的操作,但是这里的地址都是虚拟地址    
  41.      //gpm0-3 pull up    
  42.      tmp = __raw_readl(S3C64XX_GPMPUD);     
  43.      tmp &= (~0xFF);    
  44.      tmp |= 0xaa;    
  45.      __raw_writel(tmp,S3C64XX_GPMPUD);    
  46.     
  47.      //gpm0-3 output mode    
  48.      tmp = __raw_readl(S3C64XX_GPMCON);    
  49.      tmp &= (~0xFFFF);    
  50.      tmp |= 0x1111;    
  51.      __raw_writel(tmp,S3C64XX_GPMCON);
  52.     
  53.      //gpm0-3 output 0    
  54.      tmp = __raw_readl(S3C64XX_GPMDAT);    
  55.      tmp |= 0x10;
  56.      __raw_writel(tmp,S3C64XX_GPMDAT);
  57.     
  58.      printk(DEVICE_NAME " initialized\n");
  59.     
  60.      return 0;
  61. }


相关的寄存器虚地址定义在arch/arm/plat-s3c64xx/include/plat/gpio-bank-m.h

  1. #define S3C64XX_GPMCON (S3C64XX_GPM_BASE + 0x00)
  2. #define S3C64XX_GPMDAT (S3C64XX_GPM_BASE + 0x04)
  3. #define S3C64XX_GPMPUD (S3C64XX_GPM_BASE + 0x08)

GPIO M端口的基地址定义在arch/arm/plat-s3c64xx/include/plat/regs-gpio.h

  1. #define S3C64XX_GPM_BASE (S3C64XX_VA_GPIO + 0x0820)

GPIO的虚地址定义在arch/arm/mach-s3c6400/include/mach/map.h

  1. #define S3C64XX_VA_GPIO S3C_ADDR(0x00500000)


GPIO的虚地址是有全局虚地址S3C_ADDR_BASE计算出来的,void __iomem __force *作用是强制转化为地址。arch/arm/plat-s3c/include/plat/map.h

 

  1. #define S3C_ADDR_BASE (0xF4000000)

  2. #ifndef __ASSEMBLY__
  3. #define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))
  4. #else
  5. #define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
  6. #endif

由此可以得到GPM寄存器对应的虚地址分别为:

  1. S3C64XX_GPMCON 0xF4500820
  2. S3C64XX_GPMDAT 0xF4500824
  3. S3C64XX_GPMPUD 0xF4500828


驱动中的接口通过ioctl来提供,

  1. static struct file_operations s3c6410_leds_fops = {
  2.    .owner = THIS_MODULE,
  3.    .ioctl = s3c6410_leds_ioctl,
  4. };

  5. static int s3c6410_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  6. {
  7.  unsigned long tmp;
  8.  switch(cmd)
  9.  {
  10.   case 0: //熄灭
  11.   case 1:// 点亮

  12.   if(arg > 4)
  13.    return -EINVAL;

  14.   tmp = __raw_readl(S3C64XX_GPMDAT);
  15.   if(cmd) //注意cmd为1时点亮动作,对应的位置0
  16.      tmp &= (~(1<<arg));
  17.     else
  18.       tmp |= (1<<arg);
  19.     
  20.     __raw_writel(tmp,S3C64XX_GPMDAT);
  21.     return 0;

  22.  default:
  23.     return -EINVAL;
  24.  }
  25. }

尽管该驱动在编译进内核时,运行led或者led-player可以正常工作,但是当你把选项CONFIG_TE6410_LEDS=y改为CONFIG_TE6410_LEDS=m时,问题出现了。 

进行insmode s3c6410_leds.ko没有问题,而进行rmmod s3c6410_leds时则会提示:

rmmod: chdir(/lib/modules): No such file or directory,关于此问题网上有很多解决方法,比如http://www.cnblogs.com/junmao/articles/1991495.html

 

真正的问题不在于卸载,而在于卸载后再次insmod时提示加载失败,分析源码发现,驱动的卸载载函数存

在问题:

 

  1. static void __exit s3c6410_leds_exit(void)
  2. {
  3.     /* added by lli_njupt */
  4.     device_destroy(my_class, MKDEV(LED_MAJOR,0));
  5.     class_destroy(my_class);

  6.     cdev_del(&cdev_leds);

  7.     unregister_chrdev_region(MKDEV(LED_MAJOR,0),1);

  8.     printk(KERN_NOTICE "s3c2440_leds_exit\n");
  9. }

注意红色部分由笔者添加,加载失败的原因就在于没有从/dev/下注销leds,另外/sys下的class文件也需要注销。如此天下太平了。

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