<acronym id="xonnx"></acronym>
      <td id="xonnx"></td>
    1. <pre id="xonnx"></pre>

      1. 專注電子技術學習與研究
        當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

        混雜字符設備之LED設備驅動設計及CPU寄存器虛擬內存映射分析

        作者:龔平   來源:本站原創   點擊數:  更新時間:2014年03月14日   【字體:

        混雜字符設備的主要特點是主設備號(10)公用,通過一個鏈表將各個設備關聯起來,設備的識別主要依靠次設備號。

        混雜設備存在自己的結構體:
        1. struct device;

        2.  
        3. struct miscdevice {
           
        4.     int minor;
           
        5.     const char *name;
           
        6.     const struct file_operations *fops;
           
        7.     struct list_head list;
           
        8.     struct device *parent;
        9.     struct device *this_device;
           
        10. };
        其中struct device*可以聯想到自動加載設備文件中的class_create()和device_create()兩個函數。因此樂意推測混雜字符設備是自動加載設備文件的設備驅動。

         
        其中主要的兩個函數分別是misc_register()和misc_deregister(),分別用來添加和去除混雜設備。這兩個函數分別在初始化函數和卸載函數中調用。
        1. static struct class *misc_class;
           

        2.  
        3. static const struct file_operations misc_fops = {
           
        4.     .owner        = THIS_MODULE,
           
        5.     .open        = misc_open,
           
        6. };
        7.  
           
        8. int misc_register(struct miscdevice * misc)
           
        9. {
           
        10.     struct miscdevice *c;
           
        11.     dev_t dev;
           
        12.     int err = 0;
           

        13.  
        14.     INIT_LIST_HEAD(&misc->list);
           

        15.  
        16.     mutex_lock(&misc_mtx);
           
        17.     list_for_each_entry(c, &misc_list, list) {
           
        18.         if (c->minor == misc->minor) {
           
        19.             mutex_unlock(&misc_mtx);
           
        20.             return -EBUSY;
           
        21.         }
           
        22.     }
           

        23.  
        24.     if (misc->minor == MISC_DYNAMIC_MINOR) {
           
        25.         int i = DYNAMIC_MINORS;
           
        26.         while (--i >= 0)
           
        27.             if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
           
        28.                 break;
           
        29.         if (i<0) {
           
        30.             mutex_unlock(&misc_mtx);
           
        31.             return -EBUSY;
           
        32.         }
           
        33.         misc->minor = i;
           
        34.     }
           

        35.  
        36.     if (misc->minor < DYNAMIC_MINORS)
           
        37.         misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
           
        38.     dev = MKDEV(MISC_MAJOR, misc->minor);
           
        39.     /*創建設備*/
        40.     misc->this_device = device_create(misc_class, misc->parent, dev, NULL,
        41.                      "%s", misc->name);
           
        42.     if (IS_ERR(misc->this_device)) {
           
        43.         err = PTR_ERR(misc->this_device);
           
        44.         goto out;
           
        45.     }
           

        46.  
        47.     list_add(&misc->list, &misc_list);
           
        48.  out:
           
        49.     mutex_unlock(&misc_mtx);
           
        50.     return err;
           
        51. }

        int misc_deregister(struct miscdevice *misc)
        {
        int i = misc->minor;

         
        if (list_empty(&misc->list))
        return -EINVAL;

         
        mutex_lock(&misc_mtx);
        list_del(&misc->list);
        /*釋放設備*/
        device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
        if (i < DYNAMIC_MINORS && i>0) {
        misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
        }
        mutex_unlock(&misc_mtx);
        return 0;
        }

         
        static int __init misc_init(void)
        {
        int err;
                ...
        /*創建一個設備類*/
        misc_class = class_create(THIS_MODULE, "misc");
        ...
        return err;
        }
        從源碼中可以知道混雜字符設備就是自動創建設備文件的設備驅動。

         
        LED 的字符設備驅動,由于沒有讀寫操作,只需要完成最控制操作,也就是ioctl函數的實現。由于open函數默認情況下就是打開,所以不去實現也是可以的。 ioctl函數的實現主要包含兩個步驟,分別是定義命令和實現命令。其中的定義命令包含類型、方向、數據大小、以及命令序號,這些都可以按著一定宏定義實現。實現控制也就是ioctl函數的定義,其中包含,三部分:(1)、命令的檢查,類型和序號;(2)、指針參數的可讀可寫檢查;(3)具體命令的實現 (switch-case)。
        LED的實現主要就是控制全亮、全滅,某一個亮,某一個滅。我的開發板是TQ2440,利用了GPB5-GPB8來控制4個LED,只要當端口為低電平時,LED亮,高電平時,LED滅。

         
        具體的實現如下:
        1. #include<linux/module.h>
        2. #include<linux/types.h>
           
        3. #include<linux/fs.h>
           
        4. #include<linux/sched.h>
           
        5. #include<linux/init.h>
           
        6. #include<linux/cdev.h>
           
        7. #include<linux/device.h>
           
        8. #include<linux/mm.h>
           
        9. #include<linux/miscdevice.h>
           
        10. /*平臺相關的頭文件*/
           
        11. #include<mach/regs-gpio.h>
           
        12. #include<mach/hardware.h>
           
        13. #include<linux/errno.h>
           
        14. #include<linux/gpio.h>
           
        15. #include<linux/cdev.h>
           
        16. #include<linux/slab.h>
           
        17. #include<linux/string.h>
           
        18. #include<linux/kernel.h>
           

        19.  

        20.  
        21. /*定義自己的命令*/
           
        22. /*定義幻數,表示具體的設備*/
           
        23. #define LED_MAGIC_NUMBER 'k'
           
        24. #define LED_ALL_ON _IO(LED_MAGIC_NUMBER,0)
           
        25. #define LED_ALL_OFF _IO(LED_MAGIC_NUMBER,1)
           
        26. #define LED_ON _IO(LED_MAGIC_NUMBER,2)
           
        27. #define LED_OFF _IO(LED_MAGIC_NUMBER,3)
           
        28. #define LED_MAX_CMD 4
           

        29.  
        30. /*設備名*/
           
        31. #define DEVICE_NAME "GP_LED"
           
          /*具體的端口號*/
        1. static unsigned int led_table[] =
           
        2. {
           
        3.         S3C2410_GPB5,
        4.         S3C2410_GPB6,
        5.         S3C2410_GPB7,
        6.         S3C2410_GPB8,
           
        7. };
           
        8. /*端口的功能數組*/
        9. static unsigned int led_cfg_table[]=
           
        10. {
           
        11.         S3C2410_GPB5_OUTP,
        12.         S3C2410_GPB6_OUTP,
        13.         S3C2410_GPB7_OUTP,
        14.         S3C2410_GPB8_OUTP,
           

        15.         /*或者采用通用功能*/
        16.         /*
        17. S3C2410_GPIO_OUTPUT,
        18.         S3C2410_GPIO_OUTPUT,
        19.         S3C2410_GPIO_OUTPUT,
        20.         S3C2410_GPIO_OUTPUT,
        21. */
        22. };
           

        23.  
        24. static int s3c2440_led_ioctl(
           
        25.         struct inode * inode,
           
        26.         struct file *file,
           
        27.         unsigned int cmd,
           
        28.         unsigned long arg
           
        29. )
           
        30. {
           
        31.         int i = 0;
           
        32.         /*檢測參數的正確性*/
        33.         if(_IOC_TYPE(cmd)!=LED_MAGIC_NUMBER)
        34.                 return -EINVAL;
        35.         /*檢查命令是否超過一定的界限*/
        36.         if(_IOC_NR(cmd) >= LED_MAX_CMD)
        37.                 return -EINVAL;

        38.         /*檢查arg參數的正確性*/
        39.         if(arg<0 || arg >4)
        40.         {
        41.                 return -EINVAL;
        42.         }
           
        43.         /*命令控制語句*/
        44.         switch(cmd)
           
        45.         {
           
        46.                 case LED_ALL_ON:
           
        47.                 {
           
        48.                         for(i = 0; i < 4; ++ i)
           
        49.                                 s3c2410_gpio_setpin(led_table[arg-i-1],0);
           
        50.                         break;
           
        51.                 }
           
        52.                 case LED_ALL_OFF:
           
        53.                 {
           
        54.                         for(i = 0; i < 4; ++ i)
           
        55.                                 s3c2410_gpio_setpin(led_table[arg-i-1],1);
           
        56.                         break;
           
        57.                 }
           
        58.                 case LED_ON:
           
        59.                 {
           
        60.                         s3c2410_gpio_setpin(led_table[arg],0);
           
        61.                         break;
           
        62.                 }
           
        63.                 case LED_OFF:
           
        64.                 {
           
        65.                         s3c2410_gpio_setpin(led_table[arg],1);
           
        66.                         break;
           
        67.                 }
           
        68.                 default:
           
        69.                 {
           
        70.                         return -EINVAL;
           
        71.                         break;
           
        72.                 }
           
        73.         }
           

        74.  

        75.  
        76.         return 0;
           
        77. }
           

        78.  
        79. /*具體函數*/
           
        80. static const struct file_operations led_fops =
           
        81. {
           
        82.         .owner = THIS_MODULE,
           
        83.         .ioctl = s3c2440_led_ioctl,
           
        84. };
           

        85.  
        86. /*混雜設備類*/
           
        87. static const struct miscdevice misc =
           
        88. {
           
        89.         .minor = MISC_DYNAMIC_MINOR,
           
        90.         .name = DEVICE_NAME,
           
        91.         /*此處是一個地址,而不是一個數*/
           
        92.         .fops = &led_fops,
           
        93. };
           

        94.  
        95. /*初始化*/
           
        96. static int __init dev_init(void)
           
        97. {
           
        98.         int ret;
           

        99.  
        100.         int i;
           

        101.  
        102.         for(i = 0; i<4; ++i)
           
        103.         {
           
        104.                 s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
        105.                 s3c2410_gpio_setpin(led_table[i],1);
           
        106.         }
           

        107.  
        108.         ret = misc_register(&misc);
           
        109.         printk(DEVICE_NAME"\tinitialized\n");
           

        110.  
        111.         return ret;
           
        112. }
           

        113.  
        114. /*退出*/
           
        115. static void __exit dev_exit(void)
           
        116. {
           
        117.         misc_deregister(&misc);
           
        118. }
           

        119.  
        120. module_init(dev_init);
           
        121. module_exit(dev_exit);
           

        122.  
        123. MODULE_LICENSE("GPL");
           
        124. MODULE_AUTHOR("GP");
        測試應用程序如下:
        1. #include<stdio.h>
        2. #include<stdlib.h>
           
        3. #include<unistd.h>
           
        4. #include<fcntl.h>
           
        5. #include<sys/ioctl.h>
           

        6.  
        7. #define LED_MAGIC_NUMBER 'k'
           
        8. #define LED_ALL_ON _IO(LED_MAGIC_NUMBER,0)
           
        9. #define LED_ALL_OFF _IO(LED_MAGIC_NUMBER,1)
           
        10. #define LED_ON _IO(LED_MAGIC_NUMBER,2)
           
        11. #define LED_OFF _IO(LED_MAGIC_NUMBER,3)
           

        12.  
        13. int main(int argc,char *argv[])
           
        14. {
           
        15.         int fd,cmd;
           
        16.         unsigned int arg;
           
        17.         if(argc != 3)
           
        18.         {
           
        19.                 printf("parameter is not right");
           
        20.                 exit(-1);
           
        21.         }
           

        22.  
        23.         cmd = atoi(argv[1]);
           
        24.         arg = atoi(argv[2]);
           

        25.  
        26.         if(cmd > 3 || cmd < 0 || arg > 4 || arg < 0)
           
        27.         {
           
        28.                 printf("The style of command is not right\n");
           
        29.                 exit(-1);
           
        30.         }
           

        31.  
        32.         fd = open("/dev/GP_LED",O_RDWR);
           

        33.  
        34.         if(fd == -1)
           
        35.         {
           
        36.                 printf("Open File wrong!!\n");
           
        37.                 exit(-1);
           
        38.         }
           
        39.         switch(cmd)
           
        40.         {
           
        41.                 case 0:
           
        42.                         cmd = LED_ALL_ON;
           
        43.                         arg = 4;
           
        44.                         break;
           
        45.                 case 1:
           
        46.                         cmd = LED_ALL_OFF;
           
        47.                         arg = 4;
           
        48.                         break;
           
        49.                 case 2:
           
        50.                         cmd = LED_ON;
           
        51.                         break;
           
        52.                 case 3:
           
        53.                         cmd = LED_OFF;
           
        54.                         break;
           
        55.                 default:
           
        56.                         exit(-1);
           
        57.         }
           
        58.         int isOk = ioctl(fd,cmd,arg);
           
        59.         printf("%d",isOk);
           

        60.  
        61.         close(fd);
           

        62.  
        63.         exit(0);
           
        64. }
        分析代碼:
        應用程序沒什么好分析的,關鍵是驅動代碼中的幾個重要的數據結構S3C2410_GPB5-S3C2410_GPB8以及 S3C2410_GPB5_OUTP--S3C2410_GPB8_OUTP和兩個函數 s3c2410_gpio_cfgpin(),s3c2410_gpio_setpin()。
        其中3C2410_GPB5- S3C2410_GPB8是指GPB5-GPB8這四個IO口,Linux中對端口都進行了編號,給予每一個IO口唯一的端口號。同時又將端口分成了很多塊GPx,包括GPA,GPB,...,GPH等。每一塊的起始端口號為(x-1)*32+0,也就是GPA的起始端口號為0,而GPB的起始端口號為 32,依此類推。而S3C2410_GPB5_OUTP是指將GPB5配置為輸出口,每一個IO口都是多功能IO,使用前都需要進行配置。

         
        具體的源碼如下:
        1. ...
        2. /*得到端口號,每一個IO口的端口號是唯一的*/
        3. #define S3C2410_GPB5 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
           
        4. /*定義端口的功能,其中的<<10,是因為在GPBCON的第10bit開始是配置端口B的功能,其他的也類似,只是位不同*/
        5. #define S3C2410_GPB5_INP (0x00 << 10)
           
        6. #define S3C2410_GPB5_OUTP (0x01 << 10)
           
        7. #define S3C2410_GPB5_nXBACK (0x02 << 10)
           
        8. #define S3C2443_GPB5_XBACK (0x03 << 10)
           
        9. #define S3C2400_GPB5_DATA21 (0x02 << 10)
           
        10. #define S3C2400_GPB5_nCTS1 (0x03 << 10)
           

        11.  
        12. #define S3C2410_GPB6 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 6)
           
        13. #define S3C2410_GPB6_INP (0x00 << 12)
           
        14. #define S3C2410_GPB6_OUTP (0x01 << 12)
           
        15. #define S3C2410_GPB6_nXBREQ (0x02 << 12)
           
        16. #define S3C2443_GPB6_XBREQ (0x03 << 12)
           
        17. #define S3C2400_GPB6_DATA22 (0x02 << 12)
           
        18. #define S3C2400_GPB6_nRTS1 (0x03 << 12)
           

        19.  
        20. #define S3C2410_GPB7 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 7)
           
        21. #define S3C2410_GPB7_INP (0x00 << 14)
           
        22. #define S3C2410_GPB7_OUTP (0x01 << 14)
           
        23. #define S3C2410_GPB7_nXDACK1 (0x02 << 14)
           
        24. #define S3C2443_GPB7_XDACK1 (0x03 << 14)
           
        25. #define S3C2400_GPB7_DATA23 (0x02 << 14)
           

        26.  
        27. #define S3C2410_GPB8 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 8)
           
        28. #define S3C2410_GPB8_INP (0x00 << 16)
           
        29. #define S3C2410_GPB8_OUTP (0x01 << 16)
           
        30. #define S3C2410_GPB8_nXDREQ1 (0x02 << 16)
           
        31. #define S3C2400_GPB8_DATA24 (0x02 << 16)
           
        32. ...
         ...
        1. #define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
           
        2. /*將IO分成8塊,便于管理同一類型的端口*/
        3. #define S3C2410_GPIO_BANKA (32*0)
        4. #define S3C2410_GPIO_BANKB (32*1)
        5. #define S3C2410_GPIO_BANKC (32*2)
        6. #define S3C2410_GPIO_BANKD (32*3)
        7. #define S3C2410_GPIO_BANKE (32*4)
        8. #define S3C2410_GPIO_BANKF (32*5)
        9. #define S3C2410_GPIO_BANKG (32*6)
        10. #define S3C2410_GPIO_BANKH (32*7)
        兩個函數s3c2410_gpio_cfgpin(),s3c2410_gpio_setpin()分別表示配置端口(配置功能寄存器)和設置端口(寫讀數據寄存器)。

         
        在linux內核中,通常將將CPU和外設的寄存器從物理地址靜態的映射到了虛擬地址空間中以固定地址開始的一段內存空間上。
        S3C24XXCPU的CPU和外設寄存器映射關系分布如下圖所示:
        具體的實現參看源碼:
        1. /*配置端口的功能寄存器*/
        2. void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
           
        3. {
           
        4.     void __iomem *base = S3C24XX_GPIO_BASE(pin);
           
        5.     /*mask可以用來清零,或者置位*/
        6.     unsigned long mask;
           
        7.     unsigned long con;
           
        8.     unsigned long flags;
           
        9.     
        10.     /*針對GPA的偏移量,因為只有兩種功能,所以只有1bit表示,偏移量也只需要1*/  
        11.     if (pin < S3C2410_GPIO_BANKB) {
           
        12. /*用于將當前端口所在的位置位或者清零
        13.   清零:con &= ~mask;
        14.           置位:con |= mask;
        15. */
        16.         mask = 1 << S3C2410_GPIO_OFFSET(pin);
           
        17.     } 
        18.     /*針對GPB開始的端口,因為功能比較多,需要兩個bit描述一個端口的功能,所以偏移量乘以2*/
        19.     else {
           
        20. /*同樣也可以實現將對應的兩個位置位或者清零*/
        21.         mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
           
        22.     }
           

        23.  
        24.     /*根據功能設置相應的設置相應的位操作,這一部分主要是為了實現對常規命令解析*/
           
        25.     switch (function) {
           
        26.     case S3C2410_GPIO_LEAVE:
           
        27.         mask = 0;
           
        28.         function = 0;
           
        29.         break;
           

        30.  
        31.     /*采用通用命令的形式,需要對相應的位進行解析,因此驅動中也可以將S3C2410_GPB5_OUTP置換為S3C2410_GPIO_OUTPUT*/
           
        32.     case S3C2410_GPIO_INPUT:
           
        33.     case S3C2410_GPIO_OUTPUT:
           
        34.     case S3C2410_GPIO_SFN2:
           
        35.     case S3C2410_GPIO_SFN3:
           
        36.  
        37.         /*從通用方法中提取對應端口的功能*/
        38.         if (pin < S3C2410_GPIO_BANKB) {
           
        39.             function -= 1;
           
        40.             function &= 1;
           
        41.             function <<= S3C2410_GPIO_OFFSET(pin);
           
        42.         } else {
           
        43.             /*根據function確定2個bits的值*/
           
        44.             function &= 3;
           
        45.             /*將function設置到相應的位置,此處是簡單的位操作*/
           
        46.             function <<= S3C2410_GPIO_OFFSET(pin)*2;
           
        47.         }
           
        48.     }
           

        49.  
        50.     /* modify the specified register wwith IRQs off */
           
             /*寫操作都是先讀再寫*/
        1.     /*保存中斷*/
           
        2.     local_irq_save(flags);
           

        3.  
        4.     /*讀寄存器,base是寄存器基地址,而0x0是表示第一個寄存器GPBCON*/
           
        5.     con = __raw_readl(base + 0x00);
           
        6.     /*清零對應的位*/
           
        7.     con &= ~mask;
           
        8.     /*設置相應的位為對應的功能*/
           
        9.     con |= function;
           

        10.  
        11.     /*寫入寄存器*/
           
        12.     __raw_writel(con, base + 0x00);
           

        13.  
        14.     /*恢復中斷*/
           
        15.     local_irq_restore(flags);
           
        16. }
           
         /*寫端口的數據寄存器,也就是GPxDAT的某一端口*/
        1. void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
           
        2. {
           
        3.     /*得到端口所在塊的寄存器基地址*/
           
        4.     void __iomem *base = S3C24XX_GPIO_BASE(pin);
           
        5.     /*得到端口在所在寄存器中具體的偏移量*/
           
        6.     unsigned long offs = S3C2410_GPIO_OFFSET(pin);
           
        7.     unsigned long flags;
           
        8.     unsigned long dat;
           

        9.     /*中斷保存*/
        10.     local_irq_save(flags);
           
        11.     /*讀寄存器GPxDAT,base是所在塊寄存器的基地址,0x4是當前寄存器的偏移量*/
        12.     dat = __raw_readl(base + 0x04);
           
        13.     /*清零該端口當前的數據*/
        14.     dat &= ~(1 << offs);
           
        15.     /*一般是保存在另一個值中*/
        16.     dat |= to << offs;
           
        17.     __raw_writel(dat, base + 0x04);
           

        18.  
        19.     local_irq_restore(flags);
           
        20. }
           

        1. /*關于推到地址的方法按照上面的分布圖對照分析可能比較方便*/
        2. #ifdef CONFIG_CPU_S3C2400
           
        3. #define S3C24XX_GPIO_BASE(x) S3C2400_GPIO_BASE(x)
           
        4. #define S3C24XX_MISCCR S3C2400_MISCCR
           
        5. #else
           
        6. #define S3C24XX_GPIO_BASE(x) S3C2410_GPIO_BASE(x)
           
        7. #define S3C24XX_MISCCR     S3C24XX_GPIOREG2(0x80)
           
        8. #endif
           

        9.  
        10. #define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
           
        11. #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
           

        12.  
        13. /*GPIO的物理地址起始*/
           
        14. #define S3C2410_PA_GPIO     (0x56000000)
           
        15. /*GPIO的虛擬地址起始,實現的方法是在兩個物理地間隔加上UART 虛擬地址的起始*/
           
        16. #define S3C24XX_VA_GPIO     ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
           
        17. /*大小*/
           
        18. #define S3C24XX_SZ_GPIO     SZ_1M
           

        19.  

        20.  
        21. #define S3C24XX_VA_UART     S3C_VA_UART
           
        22. #define S3C2410_PA_UART     (0x50000000)
           
        23. #define S3C24XX_SZ_UART     SZ_1M
           
         
         /*映射的虛擬地址的固定起始地址*/ 
         #define S3C_ADDR_BASE (0xF4000000)      
        1. #ifndef __ASSEMBLY__
           
        2. #define S3C_ADDR(x)    ((void __iomem __force *)S3C_ADDR_BASE + (x))
           
        3. #else
           
        4. #define S3C_ADDR(x)    (S3C_ADDR_BASE + (x))
           
        5. #endif
           

        6.  
        7. #define S3C_VA_IRQ    S3C_ADDR(0x00000000)    /* irq controller(s) */
           
        8. #define S3C_VA_SYS    S3C_ADDR(0x00100000)    /* system control */
           
        9. #define S3C_VA_MEM    S3C_ADDR(0x00200000)    /* system control */
           
        10. #define S3C_VA_TIMER    S3C_ADDR(0x00300000)    /* timer block */
           
        11. #define S3C_VA_WATCHDOG    S3C_ADDR(0x00400000)    /* watchdog */
           
        12. #define S3C_VA_UART    S3C_ADDR(0x01000000)    /* UART */
           

        13.  

        14.  
        15. static unsigned int __raw_readl(unsigned int ptr)
           
        16. {
           
        17.     /*volatile表示ptr中的值是易變性的,*((volatile unsigned int *)ptr)是對地址取值*/
        18.     return *((volatile unsigned int *)ptr);
           
        19. }
           

        20.  

        21.  
        22. static void __raw_writel(unsigned int value, unsigned int ptr)
           
        23. {
           
        24.     /*((volatile unsigned int *)ptr)是對地址賦值*/
        25.     *((volatile unsigned int *)ptr) = value;
           
        26. }
        映射虛擬地址的關系建議自己繪圖,可能更加的直觀,快捷。在這段代碼中的兩個宏語句是比較難以理解的。這兩句宏定義充分利用了位操作的優勢。

         
        /*求端口所在塊寄存器的起始虛擬地址*/
        #define S3C2410_GPIO_BASE(pin)   ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
        /*求端口在所在塊的偏移量*/
        #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) 
        我們加以分析。我們以GPB5、GPA5作為分析對象。
        GPB5的端口ID號是32*(2-1)+5=37,GPA5的端口ID號是32*(1-1)+5=5;
        根據上面的圖可知,S3C24XX_VA_GPIO的值為0xFB000000.
        S3C2410_GPIO_BASE(GPB5) = ((37&~31)>>1)+0xFB000000=0xFB000010  (剛好對應于GPBCON的虛擬地址)
        S3C2410_GPIO_BASE(GPA5) = ((5&~31)>>1)+0xFB000000=0xFB000000 (剛好對應于GPBACON的虛擬地址)
        也就是相當于得到每一塊IO口的基地址也就是GPA,GPB,GPC,...等對應的地址。
        S3C2410_GPIO_OFFSET(GPB5) = 37 & 31 = 5;相當于求32的余數,也就是得到端口在所在塊中的偏移量。

         

         
        LED的驅動很簡單,該驅動是按著ioctl實現的一般步驟實現的,具有延續性。
        關閉窗口

        相關文章

        欧美性色欧美精品视频,99热这里只有精品mp4,日韩高清亚洲日韩精品一区二区,2020国自产拍精品高潮