高通Audio中ASOC的machine驅動

来源:https://www.cnblogs.com/linhaostudy/archive/2018/02/06/8419231.html
-Advertisement-
Play Games

ASoC被分為Machine、Platform和Codec三大部分,其中的Machine驅動負責Platform和Codec之間的耦合以及部分和設備或板子特定的代碼,再次引用上一節的內容:Machine驅動負責處理機器特有的一些控制項和音頻事件(例如,當播放音頻時,需要先行打開一個放大器);單獨的Pl ...


ASoC被分為Machine、Platform和Codec三大部分,其中的Machine驅動負責Platform和Codec之間的耦合以及部分和設備或板子特定的代碼,再次引用上一節的內容:Machine驅動負責處理機器特有的一些控制項和音頻事件(例如,當播放音頻時,需要先行打開一個放大器);單獨的Platform和Codec驅動是不能工作的,它必須由Machine驅動把它們結合在一起才能完成整個設備的音頻處理工作。

ASoC的一切都從Machine驅動開始,包括音效卡的註冊,綁定Platform和Codec驅動等等;

 

1. 註冊Platform driver:

ASoC把音效卡註冊為Platform Device:

  1 static int msm8x16_asoc_machine_probe(struct platform_device *pdev)
  2 {
  3     struct snd_soc_card *card;
  4     struct msm8916_asoc_mach_data *pdata = NULL;
  5     struct pinctrl *pinctrl;
  6     const char *card_dev_id = "qcom,msm-snd-card-id";
  7     const char *codec_type = "qcom,msm-codec-type";
  8     const char *hs_micbias_type = "qcom,msm-hs-micbias-type";
  9     const char *ext_pa = "qcom,msm-ext-pa";
 10     const char *mclk = "qcom,msm-mclk-freq";
 11     const char *spk_ext_pa = "qcom,msm-spk-ext-pa";
 12     const char *ptr = NULL;
 13     const char *type = NULL;
 14     const char *ext_pa_str = NULL;
 15     int num_strings;
 16     int ret, id, i;
 17 
 18     pr_err("'msm8x16_asoc_machine_probe ......");
 19     pdata = devm_kzalloc(&pdev->dev,
 20             sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL);
 21     if (!pdata) {
 22         dev_err(&pdev->dev, "Can't allocate msm8x16_asoc_mach_data\n");
 23         ret = -ENOMEM;
 24         goto err1;
 25     }
 26 
 27     pdata->vaddr_gpio_mux_spkr_ctl =
 28         ioremap(LPASS_CSR_GP_IO_MUX_SPKR_CTL , 4);
 29     if (!pdata->vaddr_gpio_mux_spkr_ctl) {
 30         pr_err("%s ioremap failure for addr %x",
 31                 __func__, LPASS_CSR_GP_IO_MUX_SPKR_CTL);
 32         ret = -ENOMEM;
 33         goto err;
 34     }
 35     pdata->vaddr_gpio_mux_mic_ctl =
 36         ioremap(LPASS_CSR_GP_IO_MUX_MIC_CTL , 4);
 37     if (!pdata->vaddr_gpio_mux_mic_ctl) {
 38         pr_err("%s ioremap failure for addr %x",
 39                 __func__, LPASS_CSR_GP_IO_MUX_MIC_CTL);
 40         ret = -ENOMEM;
 41         goto err;
 42     }
 43 
 44     pdata->vaddr_gpio_mux_pcm_ctl =
 45         ioremap(LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL, 4);
 46     if (!pdata->vaddr_gpio_mux_pcm_ctl) {
 47         pr_err("%s ioremap failure for addr %x",
 48                 __func__,
 49             LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL);
 50         ret = -ENOMEM;
 51         goto err;
 52     }
 53     ret = of_property_read_u32(pdev->dev.of_node, card_dev_id, &id);
 54     if (ret) {
 55         dev_err(&pdev->dev,
 56             "%s: missing %s in dt node\n", __func__, card_dev_id);
 57         goto err;
 58     }
 59 
 60     pdev->id = id;
 61     if (!pdev->dev.of_node) {
 62         dev_err(&pdev->dev, "No platform supplied from device tree\n");
 63         ret = -EINVAL;
 64         goto err;
 65     }
 66 
 67     ret = of_property_read_u32(pdev->dev.of_node, mclk, &id);
 68     if (ret) {
 69         dev_err(&pdev->dev,
 70             "%s: missing %s in dt node\n", __func__, mclk);
 71         id = DEFAULT_MCLK_RATE;
 72     }
 73     pdata->mclk_freq = id;
 74 
 75     pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node,
 76                 spk_ext_pa, 0);
 77     if (pdata->spk_ext_pa_gpio < 0) {
 78         dev_dbg(&pdev->dev,
 79             "%s: missing %s in dt node\n", __func__, spk_ext_pa);
 80     } else {
 81         if (gpio_is_valid(pdata->spk_ext_pa_gpio)) {
 82             ret = gpio_request(pdata->spk_ext_pa_gpio, "spk_ext_pa_gpio");
 83             if(ret) {
 84                 pr_err("spk ext pa gpio request failed");
 85                 goto err;
 86             }
 87 
 88             ret = gpio_direction_output(pdata->spk_ext_pa_gpio, 1);
 89             if(ret) {
 90                 pr_err("set_direction for spk ext pa gpio failed\n");
 91                 goto err;
 92             }
 93         } else {
 94                 pr_err("%s: Invaild external_speaker gpio: %d", __func__, pdata->spk_ext_pa_gpio);
 95                 ret = -EINVAL;
 96                 goto err;
 97             }
 98             
 99     }
100 
101     ret = of_property_read_string(pdev->dev.of_node, codec_type, &ptr);
102     if (ret) {
103         dev_err(&pdev->dev,
104             "%s: missing %s in dt node\n", __func__, codec_type);
105         goto err;
106     }
107     if (pdev->id >= MAX_SND_CARDS) {
108         dev_err(&pdev->dev, "Sound Card parsed is wrong, id=%d\n",
109                 pdev->id);
110         ret = -EINVAL;
111         goto err;
112     }
113     if (!strcmp(ptr, "external")) {
114         dev_info(&pdev->dev, "external codec is configured\n");
115         pdata->codec_type = 1;
116             /*Populate external codec TLMM configs*/
117         ret = populate_ext_snd_card_dt_data(pdev);
118         if (ret < 0) {
119             dev_err(&pdev->dev, "error finding the DT\n"
120                 "params ret=%d\n", ret);
121             goto err;
122         }
123         card = populate_ext_snd_card_dailinks(pdev);
124         if (!card) {
125             dev_err(&pdev->dev, "%s: Card uninitialized\n",
126                 __func__);
127             ret = -EPROBE_DEFER;
128             goto err;
129         }
130     } else {
131         card = populate_ext_snd_card_dailinks(pdev);
132         if (!card) {
133             dev_err(&pdev->dev, "%s: Card uninitialized\n",
134                 __func__);
135             ret = -EPROBE_DEFER;
136             goto err;
137         }
138         dev_info(&pdev->dev, "default codec configured\n");
139         pdata->codec_type = 0;
140         num_strings = of_property_count_strings(pdev->dev.of_node,
141                 ext_pa);
142         if (num_strings < 0) {
143             dev_err(&pdev->dev,
144             "%s: missing %s in dt node or length is incorrect\n",
145                     __func__, ext_pa);
146             goto err;
147         }
148         for (i = 0; i < num_strings; i++) {
149             ret = of_property_read_string_index(pdev->dev.of_node,
150                     ext_pa, i, &ext_pa_str);
151             if (ret) {
152                 dev_err(&pdev->dev,
153                     "%s:of read string %s i %d error %d\n",
154                         __func__, ext_pa, i, ret);
155                 goto err;
156             }
157             if (!strcmp(ext_pa_str, "primary"))
158                 pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID);
159             else if (!strcmp(ext_pa_str, "secondary"))
160                 pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID);
161             else if (!strcmp(ext_pa_str, "tertiary"))
162                 pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID);
163             else if (!strcmp(ext_pa_str, "quaternary"))
164                 pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID);
165         }
166         pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa);
167         pinctrl = devm_pinctrl_get(&pdev->dev);
168         if (IS_ERR(pinctrl)) {
169             pr_err("%s: Unable to get pinctrl handle\n",
170                     __func__);
171             return -EINVAL;
172         }
173         pinctrl_info.pinctrl = pinctrl;
174         ret = get_cdc_gpio_lines(pinctrl, pdata->ext_pa);
175         if (ret < 0) {
176             pr_err("%s: failed to ger the codec gpio's %d\n",
177                     __func__, ret);
178             goto err;
179         }
180     }
181 
182     ret = of_property_read_string(pdev->dev.of_node,
183         hs_micbias_type, &type);
184     if (ret) {
185         dev_err(&pdev->dev, "%s: missing %s in dt node\n",
186             __func__, hs_micbias_type);
187         goto err;
188     }
189     if (!strcmp(type, "external")) {
190         dev_dbg(&pdev->dev, "Headset is using external micbias\n");
191         mbhc_cfg.hs_ext_micbias = true;
192     } else {
193         dev_dbg(&pdev->dev, "Headset is using internal micbias\n");
194         mbhc_cfg.hs_ext_micbias = false;
195     }
196 
197     /* initialize the mclk */
198     pdata->digital_cdc_clk.i2s_cfg_minor_version =
199                     AFE_API_VERSION_I2S_CONFIG;
200     pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
201     pdata->digital_cdc_clk.clk_root = 5;
202     pdata->digital_cdc_clk.reserved = 0;
203     /* initialize the digital codec core clk */
204     pdata->digital_cdc_core_clk.clk_set_minor_version =
205             AFE_API_VERSION_I2S_CONFIG;
206     pdata->digital_cdc_core_clk.clk_id =
207             Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE;
208     pdata->digital_cdc_core_clk.clk_freq_in_hz =
209             pdata->mclk_freq;
210     pdata->digital_cdc_core_clk.clk_attri =
211             Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
212     pdata->digital_cdc_core_clk.clk_root =
213             Q6AFE_LPASS_CLK_ROOT_DEFAULT;
214     pdata->digital_cdc_core_clk.enable = 1;
215     /* Initialize loopback mode to false */
216     pdata->lb_mode = false;
217 
218     msm8x16_setup_hs_jack(pdev, pdata);
219     msm8x16_dt_parse_cap_info(pdev, pdata);
220 
221     card->dev = &pdev->dev;
222     platform_set_drvdata(pdev, card);
223     snd_soc_card_set_drvdata(card, pdata);
224     ret = snd_soc_of_parse_card_name(card, "qcom,model");
225     if (ret)
226         goto err;
227     /* initialize timer */
228     INIT_DELAYED_WORK(&pdata->disable_mclk_work, disable_mclk);
229     mutex_init(&pdata->cdc_mclk_mutex);
230     atomic_set(&pdata->mclk_rsc_ref, 0);
231     atomic_set(&pdata->mclk_enabled, false);
232     atomic_set(&quat_mi2s_clk_ref, 0);
233     atomic_set(&auxpcm_mi2s_clk_ref, 0);
234 
235     ret = snd_soc_of_parse_audio_routing(card,
236             "qcom,audio-routing");
237     if (ret)
238         goto err;
239 
240     ret = msm8x16_populate_dai_link_component_of_node(card);
241     if (ret) {
242         ret = -EPROBE_DEFER;
243         goto err;
244     }
245 
246     ret = snd_soc_register_card(card);
247     if (ret) {
248         dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
249             ret);
250         goto err;
251     }
252 
253     ret = core_get_adsp_ver();
254     if (ret < 0) {
255         ret = -EPROBE_DEFER;
256         dev_info(&pdev->dev, "%s: Get adsp version failed (%d)\n",
257                 __func__, ret);
258         goto err;
259     }
260 
261     return 0;
262 err:
263     if (pdata->vaddr_gpio_mux_spkr_ctl)
264         iounmap(pdata->vaddr_gpio_mux_spkr_ctl);
265     if (pdata->vaddr_gpio_mux_mic_ctl)
266         iounmap(pdata->vaddr_gpio_mux_mic_ctl);
267     if (pdata->vaddr_gpio_mux_pcm_ctl)
268         iounmap(pdata->vaddr_gpio_mux_pcm_ctl);
269     if(gpio_is_valid(pdata->spk_ext_pa_gpio))
270         gpio_free(pdata->spk_ext_pa_gpio);
271     devm_kfree(&pdev->dev, pdata);
272 err1:
273     return ret;
274 }
probe函數

 

DTS:

 1 sound {
 2         compatible = "qcom,msm8x16-audio-codec";
 3         qcom,model = "msm8x16-skui-snd-card";
 4         qcom,msm-snd-card-id = <0>;
 5         qcom,msm-ext-pa = "secondary";//"primary";
 6         qcom,msm-codec-type = "internal";
 7         qcom,msm-mbhc-hphl-swh = <0>;
 8         qcom,msm-mbhc-gnd-swh = <1>;
 9         qcom,msm-hs-micbias-type = "internal";
10         qcom,audio-routing =
11             "RX_BIAS", "MCLK",
12             "SPK_RX_BIAS", "MCLK",
13             "INT_LDO_H", "MCLK",
14             "MIC BIAS Internal1", "Handset Mic",
15             "MIC BIAS Internal2", "Headset Mic",
16             "MIC BIAS Internal1", "Secondary Mic",
17             "AMIC1", "MIC BIAS Internal1",
18             "AMIC2", "MIC BIAS Internal2",
19             "AMIC3", "MIC BIAS Internal1";
20         pinctrl-names = "cdc_lines_act",
21                 "cdc_lines_sus",
22                 "cdc_lines_sec_ext_act",
23                 "cdc_lines_sec_ext_sus";
24         pinctrl-0 = <&cdc_pdm_lines_act>;
25         pinctrl-1 = <&cdc_pdm_lines_sus>;
26         pinctrl-2 = <&cdc_pdm_lines_act &cdc_ext_pa_act &cdc_ext_pa_ws_act>;
27         pinctrl-3 = <&cdc_pdm_lines_sus &cdc_ext_pa_sus &cdc_ext_pa_ws_sus>;
28     };

 

通過與DTS匹配,開始分析:

(1)、獲取card的id:

1 ret = of_property_read_u32(pdev->dev.of_node, card_dev_id, &id);

 

(2)、設置card的名字:

1 pdev->id = id;
2 dev_set_name(&pdev->dev, "%s.%d", "msm-snd-card", id);

 

(3)、設置codec的類型為external還是internal的:

1 ret = of_property_read_string(pdev->dev.of_node, codec_type, &ptr);
2     if (ret) {
3         dev_err(&pdev->dev,
4             "%s: missing %s in dt node\n", __func__, codec_type);
5         goto err;
6     }

 

(4)、根據external還是internal的card,進入相應的處理函數中:

假設進入internal card:

1 card = &bear_cards[pdev->id];
2 bear_cards[pdev->id].name = dev_name(&pdev->dev);

 

在這裡,bear_cards是一個snd_soc_card的結構體,由設備樹又可知,id=0:

 1 static struct snd_soc_card bear_cards[MAX_SND_CARDS] = {
 2     /* snd_soc_card_msm8x16 */
 3     {
 4         .name        = "msm8x16-snd-card",
 5         .dai_link    = msm8x16_dai,
 6         .num_links    = ARRAY_SIZE(msm8x16_dai),
 7     },
 8     {
 9         .name        = "msm8x16-tapan-snd-card",
10         .dai_link    = msm8x16_9306_dai_links,
11         .num_links    = ARRAY_SIZE(msm8x16_9306_dai_links),
12     },
13     {
14         .name        = "msm8x16-tapan9302-snd-card",
15         .dai_link    = msm8x16_9302_dai_links,
16         .num_links    = ARRAY_SIZE(msm8x16_9302_dai_links),
17     },
18 };

所以用到的只有msm8x16_dai;

  1 /* Digital audio interface glue - connects codec <---> CPU */
  2 static struct snd_soc_dai_link msm8x16_dai[] = {
  3     /* FrontEnd DAI Links */
  4     {/* hw:x,0 */
  5         .name = "MSM8X16 Media1",
  6         .stream_name = "MultiMedia1",
  7         .cpu_dai_name    = "MultiMedia1",
  8         .platform_name  = "msm-pcm-dsp.0",
  9         .dynamic = 1,
 10         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 11             SND_SOC_DPCM_TRIGGER_POST},
 12         .codec_dai_name = "snd-soc-dummy-dai",
 13         .codec_name = "snd-soc-dummy",
 14         .ignore_suspend = 1,
 15         /* this dainlink has playback support */
 16         .ignore_pmdown_time = 1,
 17         .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
 18     },
 19     {/* hw:x,1 */
 20         .name = "MSM8X16 Media2",
 21         .stream_name = "MultiMedia2",
 22         .cpu_dai_name   = "MultiMedia2",
 23         .platform_name  = "msm-pcm-dsp.0",
 24         .dynamic = 1,
 25         .codec_dai_name = "snd-soc-dummy-dai",
 26         .codec_name = "snd-soc-dummy",
 27         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 28             SND_SOC_DPCM_TRIGGER_POST},
 29         .ignore_suspend = 1,
 30         /* this dainlink has playback support */
 31         .ignore_pmdown_time = 1,
 32         .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
 33     },
 34     {/* hw:x,2 */
 35         .name = "Circuit-Switch Voice",
 36         .stream_name = "CS-Voice",
 37         .cpu_dai_name   = "CS-VOICE",
 38         .platform_name  = "msm-pcm-voice",
 39         .dynamic = 1,
 40         .codec_dai_name = "snd-soc-dummy-dai",
 41         .codec_name = "snd-soc-dummy",
 42         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 43             SND_SOC_DPCM_TRIGGER_POST},
 44         .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
 45         .ignore_suspend = 1,
 46         /* this dainlink has playback support */
 47         .ignore_pmdown_time = 1,
 48         .be_id = MSM_FRONTEND_DAI_CS_VOICE,
 49     },
 50     {/* hw:x,3 */
 51         .name = "MSM VoIP",
 52         .stream_name = "VoIP",
 53         .cpu_dai_name    = "VoIP",
 54         .platform_name  = "msm-voip-dsp",
 55         .dynamic = 1,
 56         .trigger = {SND_SOC_DPCM_TRIGGER_POST,
 57             SND_SOC_DPCM_TRIGGER_POST},
 58         .codec_dai_name = "snd-soc-dummy-dai",
 59         .codec_name = "snd-soc-dummy",
 60         .ignore_suspend = 1,
 61         /* this dainlink has playback support */
 62         .ignore_pmdown_time = 1,
 63         .be_id = MSM_FRONTEND_DAI_VOIP,
 64     },
 65     {/* hw:x,4 */
 66         .name = "MSM8X16 LP

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、首先在項目中添加Nuget程式包... 2、然後在NuGet窗體中搜索Log4Net,然後點擊安裝<安裝過程可能會持續幾分鐘,請耐心等待> 3、在項目中添加一個Config文件,如已有App.config,則直接在其中添加內容: 截圖中配置的XML代碼如下: 最後在項目的 AssemblyInf ...
  • 用17寫AspNetCore 也一年了,最近出現了這個問題 : 在點擊發佈的時候 報錯了,構建失敗的問題,剛開始還排查日子,刪除以往的發佈遺留痕跡,後來發現不行, 但是項目在本地運行的時候是好使的,生成也不會報錯。 我們項目中有3個解決方案,只有其中一個可以進行發佈,我就對項目的(共同引用)進行查看 ...
  • 實例產品基於asp.net mvc 5.0框架,源碼下載地址:http://www.jinhusns.com/Products/Download View里所有以@開頭或@(/*代碼*)的部分代碼都會被ASP.NET引擎進行處理。在@{/*代碼體}內的代碼每一行都必須以";"結束如: 而@xxx則不 ...
  • 實例產品基於asp.net mvc 5.0框架,源碼下載地址:http://www.jinhusns.com/Products/Download 在Asp.net Mvc中當你有以下及類似以下需求時你可以使用Filter功能 判斷登錄與否或用戶許可權 決策輸出緩存 防盜鏈 防蜘蛛 本地化與國際化設置 ...
  • 一、原始配置文件的問題 我們在做開發時,不管是B/S還是C/S,現在幾乎所有的項目都會碰到使用配置文件,簡單點的如鏈接字元串、上傳路徑啊,一些API的介面地址等等。複雜點就更多了,如ActiveMQ的配置信息(地址、埠、用戶名、密碼)等等。 在.Net開發中已經在(Web.config/App.c ...
  • 前言 Hi, 大家好,還是星期五,還是Rector,又在圖享網準時和大家見面了。 今天給大家帶來系列教程《一步一步創建ASP.NET MVC5程式[Repository+Autofac+Automapper+SqlSugar]》的第八期了,不知道你有沒有按照教程將前七期的都實際練習一篇呢?如果是,你 ...
  • shell腳本 函數 函數介紹 定義函數 函數使用 互動式環境下定義和使用函數 腳本中定義和使用函數 函數在使用前須先被定義,所以在腳本中使用函數是,必須在腳本開始前定義函數。 調用函數僅使用其函數名即可 示例: cat func1 !/bin/bash func1 hello() { echo " ...
  • shell腳本 基礎 編程基礎 shell腳本說明 變數介紹 變數的作用: 數據暫存 參與運算 表示數據範圍 強類型變數: 變數不經過強制轉換,它永遠是這個數據類型,不允許隱式的類型轉換。 一般定義變數時必須指定類型、參與運算必須符合類型要求。 調用未聲明變數會產生錯誤。 如 java,c 的變數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...