L二

接上文,看完了结构体,也也应该知道个大概了,整个驱动部分围绕fb_info来,对其进行填充最后调用frambuffer_register将其注册进内核,

接下来看详细分析:

    426 static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
    427 {
    428         int retval = 0;
    429         int __user *argp = (void __user *)arg;
    430
    431         if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
    432                                                (mxcfb_drv_data.suspended ==
    433                                                 false))) < 0) {
    434                 return retval;
    435         }
    436
    437         switch (cmd) {
    438         case MXCFB_SET_GBL_ALPHA:
    439                 {
    440                         struct mxcfb_gbl_alpha ga;
    441                         if (copy_from_user(&ga, (void *)arg, sizeof(ga))) {

 ) {
    442                                 retval = -EFAULT;
    443                                 break;
    444                         }
    445                         retval =
    446                             ipu_sdc_set_global_alpha((bool) ga.enable,
    447                                                      ga.alpha);
    448                         dev_dbg(fbi->device, "Set global alpha to %d\n",
    449                                 ga.alpha);
    450                         break;
    451                 }
    452         case MXCFB_SET_CLR_KEY:
    453                 {
    454                         struct mxcfb_color_key key;
    455                         if (copy_from_user(&key, (void *)arg, sizeof(key))) {
    456                                 retval = -EFAULT;
    457                                 break;
    458                         }
    459                         retval = ipu_sdc_set_color_key(MEM_SDC_BG, key.enable,

nable,
    460                                                        key.color_key);
    461                         dev_dbg(fbi->device, "Set color key to 0x%08X\n",
    462                                 key.color_key);
    463                         break;
    464                 }
    465         case MXCFB_WAIT_FOR_VSYNC:
    466                 {
    467 #ifndef CONFIG_ARCH_MX3
    468                         mxcfb_drv_data.vsync_flag = 0;
    469                         ipu_enable_irq(IPU_IRQ_SDC_DISP3_VSYNC);
    470                         if (!wait_event_interruptible_timeout
    471                             (mxcfb_drv_data.vsync_wq,
    472                              mxcfb_drv_data.vsync_flag != 0, 1 * HZ)) {
    473                                 dev_err(fbi->device,
    474                                         "MXCFB_WAIT_FOR_VSYNC: timeout\n");
    475                                 retval = -ETIME;
    476                                 break;
    477                         } else if (signal_pending(current)) {

  478                                 dev_err(fbi->device,
    479                                         "MXCFB_WAIT_FOR_VSYNC: interrupt received\n");
    480                                 retval = -ERESTARTSYS;
    481                                 break;
    482                         }
    483 #endif
    484                         break;
    485                 }
    486         case MXCFB_GET_FB_IPU_CHAN:
    487                 {
    488                         struct mxcfb_info *mxc_fbi =
    489                                 (struct mxcfb_info *)fbi->par;
    490
    491                         if (put_user(mxc_fbi->ipu_ch, argp))
    492                                 return -EFAULT;
    493
    494                         break;
    495                 }
    496         default:

   497                 retval = -EINVAL;
    498         }
    499         return retval;
    500 }

    826 static struct fb_ops mxcfb_ops = {
    827         .owner = THIS_MODULE,
    828         .fb_set_par = mxcfb_set_par,
    829         .fb_check_var = mxcfb_check_var,
    830         .fb_setcolreg = mxcfb_setcolreg,
    831         .fb_pan_display = mxcfb_pan_display,
    832         .fb_ioctl = mxcfb_ioctl,
    833         .fb_fillrect = cfb_fillrect,
    834         .fb_copyarea = cfb_copyarea,
    835         .fb_imageblit = cfb_imageblit,
    836         .fb_blank = mxcfb_blank,
    837 };
    838

  1. struct fb_info *framebuffer_alloc(size_t size, struct device *dev)  
  2. {  
  3. #define BYTES_PER_LONG (BITS_PER_LONG/8)  
  4. #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))  
  5.     int fb_info_size = sizeof(struct fb_info);  
  6.     struct fb_info *info;  
  7.     char *p;  
  8.   
  9.     if (size)  
  10.         fb_info_size += PADDING;  
  11.   
  12.     p = kzalloc(fb_info_size + size, GFP_KERNEL);  
  13.   
  14.     if (!p)  
  15.         return NULL;  
  16.   
  17.     info = (struct fb_info *) p;  
  18.   
  19.     if (size)  
  20.         info->par = p + fb_info_size;  
  21.   
  22.     info->device = dev;  
  23.   
  24. #ifdef CONFIG_FB_BACKLIGHT  
  25.     mutex_init(&info->bl_curve_mutex);  
  26. #endif   
  27.   
  28.     return info;  
  29. #undef PADDING   
  30. #undef BYTES_PER_LONG   
  31. }  

    125 static int mxcfb_set_fix(struct fb_info *info)
    126 {
    127         struct fb_fix_screeninfo *fix = &info->fix;
    128         struct fb_var_screeninfo *var = &info->var;
    129         struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
    130
    131         if (mxc_fbi->ipu_ch == MEM_SDC_FG)
    132                 strncpy(fix->id, "DISP3 FG", 8);
    133         else
    134                 strncpy(fix->id, "DISP3 BG", 8);
    135
    136         fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
    137
    138         fix->type = FB_TYPE_PACKED_PIXELS;
    139         fix->accel = FB_ACCEL_NONE;
    140         fix->visual = FB_VISUAL_TRUECOLOR;
    141         fix->xpanstep = 1;
    142         fix->ypanstep = 1;
    143
    144         return 0;
    145 }
    146

 

    973 /*
    974  * Main framebuffer functions
    975  */
    976
    977 /*!
    978  * Allocates the DRAM memory for the frame buffer.      This buffer is remapped
    979  * into a non-cached, non-buffered, memory region to allow palette and pixel
    980  * writes to occur without flushing the cache.  Once this area is remapped,
    981  * all virtual memory access to the video memory should occur at the new region.
    982  *
    983  * @param       fbi     framebuffer information pointer
    984  *
    985  * @param       use_internal_ram flag on whether to use internal RAM for memory
    986  *
    987  * @return      Error code indicating success or failure
    988  */

    989 static int mxcfb_map_video_memory(struct fb_info *fbi, bool use_internal_ram)
    990 {
    991         int retval = 0;
    992
    993 #ifdef CONFIG_FB_MXC_INTERNAL_MEM
    994         if (use_internal_ram) {
    995                 fbi->fix.smem_len = FB_RAM_SIZE;
    996                 fbi->fix.smem_start = FB_RAM_BASE_ADDR;
    997                 if (fbi->fix.smem_len <
    998                     (fbi->var.yres_virtual * fbi->fix.line_length)) {
    999                         dev_err(fbi->device,
   1000                                 "Not enough internal RAM for framebuffer configuration\n");
   1001                         retval = -EINVAL;
   1002                         goto err0;
   1003                 }
   1004
   1005                 if (request_mem_region(fbi->fix.smem_start, fbi->fix.smem_len,
   1006                                        fbi->device->driver->name) == NULL) {

L) {
   1007                         dev_err(fbi->device,
   1008                                 "Unable to request internal RAM\n");
   1009                         retval = -ENOMEM;
   1010                         goto err0;
   1011                 }
   1012
   1013                 if (!(fbi->screen_base = ioremap(fbi->fix.smem_start,
   1014                                                  fbi->fix.smem_len))) {
   1015                         dev_err(fbi->device,
   1016                                 "Unable to map fb memory to virtual address\n");
   1017                         release_mem_region(fbi->fix.smem_start,
   1018                                            fbi->fix.smem_len);
   1019                         retval = -EIO;
   1020                         goto err0;
   1021                 }
   1022
   1023                 iram_clk = clk_get(NULL, "iram_clk");
   1024                 clk_enable(iram_clk);
   1025         } else
   1026 #endif

   1027         {
   1028                 fbi->fix.smem_len = fbi->var.yres_virtual *
   1029                     fbi->fix.line_length;
   1030                 fbi->screen_base =
   1031                     dma_alloc_writecombine(fbi->device,
   1032                                            fbi->fix.smem_len,
   1033                                            (dma_addr_t *) & fbi->fix.smem_start,
   1034                                            GFP_DMA);
   1035
   1036                 if (fbi->screen_base == 0) {
   1037                         dev_err(fbi->device,
   1038                                 "Unable to allocate framebuffer memory\n");
   1039                         retval = -EBUSY;
   1040                         goto err0;
   1041                 }
   1042         }
   1043
   1044         dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
   1045                 (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
   1046
   1047         fbi->screen_size = fbi->fix.smem_len;

   1048
   1049         /* Clear the screen */
   1050         memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
   1051
   1052         return 0;
   1053
   1054       err0:
   1055         fbi->fix.smem_len = 0;
   1056         fbi->fix.smem_start = 0;
   1057         fbi->screen_base = NULL;
   1058         return retval;
   1059 }

    147 /*
    148  * Set framebuffer parameters and change the operating mode.
    149  *
    150  * @param       info     framebuffer information pointer
    151  */

    152 static int mxcfb_set_par(struct fb_info *fbi)
    153 {
    154         int retval;
    155         bool use_iram = false;
    156         u32 mem_len;
    157         ipu_di_signal_cfg_t sig_cfg;
    158         ipu_panel_t mode = IPU_PANEL_TFT;
    159         struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
    160
    161         if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
    162                                                (mxcfb_drv_data.suspended ==
    163                                                 false))) < 0) {
    164                 return retval;
    165         }
    166
    167         ipu_disable_irq(mxc_fbi->ipu_ch_irq);
    168         ipu_disable_channel(mxc_fbi->ipu_ch, true);
    169         ipu_uninit_channel(mxc_fbi->ipu_ch);

     170         ipu_clear_irq(mxc_fbi->ipu_ch_irq);
    171         mxcfb_set_fix(fbi);

//初始化fbi->fix变量
    172
    173         mem_len = fbi->var.yres_virtual * fbi->fix.line_length;

//计算显存的大小,根据fbi->var.xres_virtual *fbi->var,bits_per_pixal / 8 * fbi->var.yres_virtual;
    174         if (mem_len > fbi->fix.smem_len) {
    175                 if (fbi->fix.smem_start)
    176                         mxcfb_unmap_video_memory(fbi);
    177
    178 #ifdef CONFIG_FB_MXC_INTERNAL_MEM
    179                 if (mxc_fbi->ipu_ch == MEM_SDC_BG) {
    180                         use_iram = true;
    181                 }
    182 #endif
    183                 if (mxcfb_map_video_memory(fbi, use_iram) < 0)
    184                         return -ENOMEM;
    185         }
    186
    187         ipu_init_channel(mxc_fbi->ipu_ch, NULL);
    188
    189         /* Clear the screen */
    190         memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
    191

    191
    192         if (mxc_fbi->ipu_ch == MEM_SDC_BG) {
    193                 memset(&sig_cfg, 0, sizeof(sig_cfg));
    194                 if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
    195                         sig_cfg.Hsync_pol = true;
    196                 if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
    197                         sig_cfg.Vsync_pol = true;
    198                 if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
    199                         sig_cfg.clk_pol = true;
    200                 if (fbi->var.sync & FB_SYNC_DATA_INVERT)
    201                         sig_cfg.data_pol = true;
    202                 if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
    203                         sig_cfg.enable_pol = true;
    204                 if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
    205                         sig_cfg.clkidle_en = true;
    206                 if (fbi->var.sync & FB_SYNC_SHARP_MODE)
    207                         mode = IPU_PANEL_SHARP_TFT;
    208
    209                 dev_dbg(fbi->device, "pixclock = %ul Hz\n",
    210                         (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
    211
    212                 if (ipu_sdc_init_panel(mode,

    213                                        (PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
    214                                        fbi->var.xres, fbi->var.yres,
    215                                        (fbi->var.sync & FB_SYNC_SWAP_RGB) ?
    216                                        IPU_PIX_FMT_BGR666 : IPU_PIX_FMT_RGB666,
    217                                        fbi->var.left_margin,
    218                                        fbi->var.hsync_len,
    219                                        fbi->var.right_margin,
    220                                        fbi->var.upper_margin,
    221                                        fbi->var.vsync_len,
    222                                        fbi->var.lower_margin, sig_cfg) != 0) {
    223                         dev_err(fbi->device,
    224                                 "mxcfb: Error initializing panel.\n");
    225                         return -EINVAL;
    226                 }
    227
    228                 fbi->mode =
    229                     (struct fb_videomode *)fb_match_mode(&fbi->var,

    230                                                          &fbi->modelist);
    231         }
    232
    233         ipu_disp_set_window_pos(mxc_fbi->ipu_ch, 0, 0);
    234
    235         mxc_fbi->cur_ipu_buf = 1;
    236         sema_init(&mxc_fbi->flip_sem, 1);
    237         fbi->var.xoffset = fbi->var.yoffset = 0;
    238
    239         retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
    240                                          bpp_to_pixfmt(fbi->var.bits_per_pixel),
    241                                          fbi->var.xres, fbi->var.yres,
    242                                          fbi->var.xres_virtual,
    243                                          IPU_ROTATE_NONE,
    244                                          fbi->fix.smem_start +
    245                                          (fbi->fix.line_length * fbi->var.yres),
    246                                          fbi->fix.smem_start,
    247                                          0, 0);
    248         if (retval) {

     249                 dev_err(fbi->device,
    250                         "ipu_init_channel_buffer error %d\n", retval);
    251                 return retval;
    252         }
    253
    254         if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
    255                 ipu_enable_channel(mxc_fbi->ipu_ch);
    256         }
    257
    258         return 0;
    259 }

                                                                        

 

   1098 static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
   1099 {
   1100         struct fb_info *fbi;
   1101         struct mxcfb_info *mxcfbi;
   1102
   1103         /*
   1104          * Allocate sufficient memory for the fb structure
   1105          */
   1106         fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);

// 申请struct fb_info  + struct mxcfb_info
   1107         if (!fbi)
   1108                 return NULL;
   1109
   1110         mxcfbi = (struct mxcfb_info *)fbi->par;
   1111
   1112         fbi->var.activate = FB_ACTIVATE_NOW;
   1113

   1114         fbi->fbops = ops;
   1115         fbi->flags = FBINFO_FLAG_DEFAULT;
   1116         fbi->pseudo_palette = mxcfbi->pseudo_palette;

//指定fbi的调色板为fbi->par->pseudo_palette;
   1117
   1118         spin_lock_init(&mxcfbi->fb_lock);
   1119
   1120         /*
   1121          * Allocate colormap
   1122          */
   1123         fb_alloc_cmap(&fbi->cmap, 16, 0);

//分配颜色位图
   1124
   1125         return fbi;
   1126 }

    612 /*
    613  * mxcfb_blank():
    614  *      Blank the display.
    615  */
    616 static int mxcfb_blank(int blank, struct fb_info *info)
    617 {
    618         int retval;
    619         struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
    620
    621         dev_dbg(info->device, "blank = %d\n", blank);
    622
    623         if (mxc_fbi->blank == blank)
    624                 return 0;
    625
    626         if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
    627                                                (mxcfb_drv_data.suspended ==
    628                                                 false))) < 0) {
    629                 return retval;
    630         }
    631
    632         mxc_fbi->blank = blank;
    633
    634         switch (blank) {
    635         case FB_BLANK_POWERDOWN:
    636         case FB_BLANK_VSYNC_SUSPEND:
    637         case FB_BLANK_HSYNC_SUSPEND:
    638         case FB_BLANK_NORMAL:
    639                 ipu_disable_channel(MEM_SDC_BG, true);
    640                 gpio_lcd_inactive();
    641                 break;
    642         case FB_BLANK_UNBLANK:
    643                 gpio_lcd_active();
    644                 ipu_enable_channel(MEM_SDC_BG);
    645                 break;
    646         }
    647         return 0;
    648 }
 

 

 

static int mxcfb_probe(struct platform_device *pdev)
   1137 {
   1138         char *mode = pdev->dev.platform_data;
   1139         struct fb_info *fbi;
   1140         struct mxcfb_info *mxcfbi;
   1141         struct fb_info *fbi_ovl;
   1142         int ret = 0;
   1143
   1144         /*
   1145          * Initialize FB structures
   1146          */
   1147         fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);

// 此函数申请了一个结构为struct fb_info的fbi,设置fbi的成员pseudo_palette(调色板)

//设置fbi成员var.avtive=FB_ACTIVATE_NOW;设置fbi的flag=FBINFO_FLAG_DEFAULT;

//设置fbi的ops,初始化颜色分量(要好好分析)

   1148         if (!fbi) {
   1149                 ret = -ENOMEM;
   1150                 goto err0;
   1151         }
   1152         mxcfbi = (struct mxcfb_info *)fbi->par;

   1153
   1154         mxcfbi->ipu_ch_irq = IPU_IRQ_SDC_BG_EOF;
   1155         mxcfbi->cur_ipu_buf = 0;
   1156         mxcfbi->ipu_ch = MEM_SDC_BG;
   1157
   1158         ipu_sdc_set_global_alpha(true, 0xFF);
   1159         ipu_sdc_set_color_key(MEM_SDC_BG, false, 0);
   1160
   1161         if (ipu_request_irq(IPU_IRQ_SDC_BG_EOF, mxcfb_irq_handler, 0,
   1162                             MXCFB_NAME, fbi) != 0) {
   1163                 dev_err(&pdev->dev, "Error registering BG irq handler.\n");
   1164                 ret = -EBUSY;
   1165                 goto err1;
   1166         }
   1167         ipu_disable_irq(IPU_IRQ_SDC_BG_EOF);
   1168
   1169         if (fb_mode == NULL) {
   1170                 fb_mode = mode;
   1171         }
   1172
   1173         if (!fb_find_mode(&fbi->var, fbi, fb_mode, mxcfb_modedb,

   1174                           mxcfb_modedb_sz, NULL, default_bpp)) {
   1175                 ret = -EBUSY;
   1176                 goto err2;
   1177         }

//fb_find_mode从mxcfb_modedb(struct fb_videomode)中根据fb_mode值找出对于自己的那块屏的参数

//然后初始化fbi->var
   1178         fb_videomode_to_modelist(mxcfb_modedb, mxcfb_modedb_sz, &fbi->modelist);

//将好几块屏的mode添加到链表里,不知道何用
   1179
   1180         /* Default Y virtual size is 2x panel size */
   1181 #ifndef CONFIG_FB_MXC_INTERNAL_MEM
   1182         fbi->var.yres_virtual = fbi->var.yres * 2;
   1183 #endif
   1184
   1185         mxcfb_drv_data.fbi = fbi;
   1186         mxcfb_drv_data.backlight_level = 255;
   1187         mxcfb_drv_data.suspended = false;
   1188         init_waitqueue_head(&mxcfb_drv_data.suspend_wq);
   1189
   1190         mxcfbi->blank = FB_BLANK_NORMAL;
   1191         ret = mxcfb_set_par(fbi);

//初始化fbi->fix  以及显存
   1192         if (ret < 0) {
   1193                 goto err2;
   1194         }

   1195         mxcfb_blank(FB_BLANK_UNBLANK, fbi);
   1196
   1197         /*
   1198          * Register framebuffer
   1199          */
   1200         ret = register_framebuffer(fbi);
   1201         if (ret < 0) {
   1202                 goto err2;
   1203         }
   1204
   1205         /*
   1206          * Initialize Overlay FB structures
   1207          */
   1208         fbi_ovl = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ovl_ops);
   1209         if (!fbi_ovl) {
   1210                 ret = -ENOMEM;
   1211                 goto err3;
   1212         }
   1213         mxcfb_drv_data.fbi_ovl = fbi_ovl;
   1214         mxcfbi = (struct mxcfb_info *)fbi_ovl->par;

   1216         mxcfbi->ipu_ch_irq = IPU_IRQ_SDC_FG_EOF;
   1217         mxcfbi->cur_ipu_buf = 0;
   1218         mxcfbi->ipu_ch = MEM_SDC_FG;
   1219
   1220         if (ipu_request_irq(IPU_IRQ_SDC_FG_EOF, mxcfb_irq_handler, 0,
   1221                             MXCFB_NAME, fbi_ovl) != 0) {
   1222                 dev_err(fbi->device, "Error registering FG irq handler.\n");
   1223                 ret = -EBUSY;
   1224                 goto err4;
   1225         }
   1226         ipu_disable_irq(mxcfbi->ipu_ch_irq);
   1227
   1228         /* Default Y virtual size is 2x panel size */
   1229         fbi_ovl->var = fbi->var;
   1230         fbi_ovl->var.yres_virtual = fbi->var.yres * 2;
   1231
   1232         /* Overlay is blanked by default */
   1233         mxcfbi->blank = FB_BLANK_NORMAL;
   1234
   1235         ret = mxcfb_set_par(fbi_ovl);
   1236         if (ret < 0) {

   1237                 goto err5;
   1238         }
   1239
   1240         /*
   1241          * Register overlay framebuffer
   1242          */
   1243         ret = register_framebuffer(fbi_ovl);
   1244         if (ret < 0) {
   1245                 goto err5;
   1246         }
   1247
   1248         platform_set_drvdata(pdev, &mxcfb_drv_data);
   1249
   1250         init_waitqueue_head(&mxcfb_drv_data.vsync_wq);
   1251         if (!cpu_is_mx31() && !cpu_is_mx32()) {
   1252                 if ((ret = ipu_request_irq(IPU_IRQ_SDC_DISP3_VSYNC,
   1253                                            mxcfb_vsync_irq_handler,
   1254                                            0, MXCFB_NAME,
   1255                                            &mxcfb_drv_data)) < 0) {
   1256                         goto err6;
   1257                 }

   1258                 ipu_disable_irq(IPU_IRQ_SDC_DISP3_VSYNC);
   1259         }
   1260
   1261         printk(KERN_INFO "mxcfb: fb registered, using mode %s\n", fb_mode);
   1262         return 0;
   1263
   1264       err6:
   1265         unregister_framebuffer(fbi_ovl);
   1266       err5:
   1267         ipu_free_irq(IPU_IRQ_SDC_FG_EOF, fbi_ovl);
   1268       err4:
   1269         fb_dealloc_cmap(&fbi_ovl->cmap);
   1270         framebuffer_release(fbi_ovl);
   1271       err3:
   1272         unregister_framebuffer(fbi);
   1273       err2:
   1274         ipu_free_irq(IPU_IRQ_SDC_BG_EOF, fbi);
   1275       err1:
   1276         fb_dealloc_cmap(&fbi->cmap);
   1277         framebuffer_release(fbi);
   1278       err0:

   1279         printk(KERN_ERR "mxcfb: failed to register fb\n");
   1280         return ret;
   1281 }
   1282

 

 

 

 


   1215

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值