Linux驱动基础:msm平台,modem等framework加载
msm平臺,AP和CP封裝在一起,公用一塊內存。所以AP需要負責把整個modem, TZ , rpm等binary拷貝到內存中以供modem等subsystem去運行。那AP這邊是怎么分配這些內存,又是怎么讀出來相關的binary,又如何把binary上傳上去的呢??
相關的feature
CONFIG_FW_LOADER CONFIG_FW_LOADER_USER_HELPER- 1
- 2
modem使用的內存申請
要設置modem的內存大小,必須首先需要確認modem binary的大小,modem需要使用的內存大小等。這個在CMA相關的內容中說過。這里在說一下高通msm8916平臺,modem大小檢查以及修改方法。?
1) modem binary的大小可以從以下編譯的log里邊看出來!!(modem_proc/build/ms目錄下的pplk-XXX.log或者build_xxxx.log)。?
根據大小對齊1MB大小,就是modem binary需要流出來的大小。看如下例子里邊的log,總的大小是77.04,?
所以需要在上面的dtsi文件中留出來78MB就可以。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2) 然后去修改modem_proc/config/xxx/ 目錄下的cust_config.xml文件中修改modem大小
<!-- 85 MB of physical pool--> <physical_pool name="DEFAULT_PHYSPOOL"> <region base="0x88000000" size="0x5500000" /> <region base="0x88000000" size="0x4E00000" /> </physical_pool>- 1
- 2
- 3
- 4
- 5
以下是modem相關的device tree的設置。這些內容也在CMA和ion內存相關的帖子里邊都講過。?
但之前有一個疑問就是,在CMA預留了一段內存之后,會把這個賦值給modem的dev->cma_area,然后在分配需要使用的內存的時候從dev->cma_area中取出來,那這個過程好像跟ion內存沒有什么關系。能不能去掉下面msmxxx-ion.dtsi中?
modem_adsp_mem相關的設置呢???
是可以的!!!其他幾個DMA區(qū)域,如果直接從CMA分配的話,應該都可以從msmxxx-ion.dtsi文件中去掉!!?
也就是說下面qcom,ion-heap-type = “DMA”的部分其實都可以從msm8916-ion.dtsi文件中去掉,不影響。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
modem_adsp_mem指定的區(qū)域,需要分配出來,以供下載modem binary。
//pil_mss_driver_probe()->pil_subsys_init() static int pil_subsys_init(struct modem_data *drv,struct platform_device *pdev) {...drv->subsys_desc.name = "modem";drv->subsys_desc.dev = &pdev->dev;drv->subsys_desc.owner = THIS_MODULE;drv->subsys_desc.shutdown = modem_shutdown;drv->subsys_desc.powerup = modem_powerup;drv->subsys_desc.ramdump = modem_ramdump;drv->subsys_desc.free_memory = modem_free_memory;drv->subsys_desc.crash_shutdown = modem_crash_shutdown;drv->subsys_desc.err_fatal_handler = modem_err_fatal_intr_handler;drv->subsys_desc.stop_ack_handler = modem_stop_ack_intr_handler;drv->subsys_desc.wdog_bite_handler = modem_wdog_bite_intr_handler;drv->subsys = subsys_register(&drv->subsys_desc);drv->ramdump_dev = create_ramdump_device("modem", &pdev->dev);...return ret; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
之后在modem_powerup()的時候,會先根據modem binary的elf結構獨處modem的大小等,然后計算出align之后應該的大小。?
pil_boot()-> request_firmware()讀出elf頭并計算大小等。?
在pil_setup_region()->pil_alloc_region()的時候,傳進去的大小就是從上面讀出來的大小。
- 1
這里看著和實際的內存大小一致!! 可能是因為留出來的CMA區(qū)域的大小正好和這個大小一致才這樣的。?
在實際調試過程中,也可以打印這個大小之后,調整CMA大小。
再看看實際的CMA大小是怎么申請的。
//調用順序 pil_boot()->pil_init_mmap()->pil_setup_region()->pil_alloc_region()-> dma_alloc_attrs()->arm_dma_alloc()->__dma_alloc()->__alloc_from_contiguous()->- 1
- 2
- 3
這個調用的順序,一步一步往下看可以看到,實際上分配的區(qū)域是一塊CMA區(qū)域,而且就是在CMA注冊之后,在相應的platform設備注冊的時候保存到dev->cma_area中的區(qū)域。?
在相應的設備注冊的時候,如果設備的device tree中有”linux,contiguous-region”的時候,就會尋找相應的CMA區(qū)域并進行保留。這都是因為注冊了platform_bus_typ的notifier函數
- 1
看下面的log。
<6>[0.487642] [0:swapper/0:1] cma: Assigned CMA region at 0 to 1de0000.qcom,venus device <6>[0.489469] [0:swapper/0:1] cma: Assigned CMA region at 0 to 4080000.qcom,mss device <6>[0.490756] [0:swapper/0:1] cma: Assigned CMA region at 0 to a21b000.qcom,pronto device <6>[1.125342] [0:swapper/0:1] cma: Assigned CMA region at 0 to 8.qcom,ion-heap device <6>[1.125793] [0:swapper/0:1] cma: Assigned CMA region at 0 to 1b.qcom,ion-heap device <6>[1.126233] [0:swapper/0:1] cma: Assigned CMA region at 0 to 1c.qcom,ion-heap device <6>[1.126671] [0:swapper/0:1] cma: Assigned CMA region at 0 to 17.qcom,ion-heap device <6>[1.127298] [0:swapper/0:1] cma: Assigned CMA region at 0 to 1a.qcom,ion-heap device- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
這里看到4080000.qcom,mss這個device相應的區(qū)域已經保留了CMA區(qū)域。?
然后在上面進行分配的時候,在?
__alloc_from_contiguous()->dma_alloc_from_contiguous()->dev_get_cma_area()函數中取到?
相應的dev->cma_area。
modem相關內存的使用和下載
pil_load_seg()->request_firmware_direct()->_request_firmware()函數身生成相應的device節(jié)點,并通知ueventd去讀取相應的binary然后下載。以下是pil_load_seg里邊打印的正在試圖下載的binary。
<6>[29.129737] [1:init:1] pil_load_seg fw_name = modem.b02 <6>[29.157808] [1:init:1] pil_load_seg fw_name = modem.b07 <6>[29.191477] [1:init:1] pil_load_seg fw_name = modem.b17 <6>[29.348480] [1:init:1] pil_load_seg fw_name = modem.b19 <6>[29.409733] [1:init:1] pil_load_seg fw_name = modem.b20 <6>[29.489639] [1:init:1] pil_load_seg fw_name = modem.b23 <6>[29.519624] [1:init:1] pil_load_seg fw_name = modem.b24 <6>[29.549829] [1:init:1] pil_load_seg fw_name = modem.b25 <6>[29.591918] [1:init:1] pil_load_seg fw_name = modem.b27 <6>[31.997036] [0:wcnss_service:307] pil_load_seg fw_name = wcnss.b02 <6>[32.658390] [0:wcnss_service:307] pil_load_seg fw_name = wcnss.b04 <6>[32.693754] [0:wcnss_service:307] pil_load_seg fw_name = wcnss.b06 <6>[32.848104] [3:wcnss_service:307] pil_load_seg fw_name = wcnss.b09 <6>[32.854061] [3:wcnss_service:307] pil_load_seg fw_name = wcnss.b10 <6>[32.876115] [3:wcnss_service:307] pil_load_seg fw_name = wcnss.b11 <6>[37.384287] [1:TimedEventQueue:771] pil_load_seg fw_name = venus.b02 <6>[37.438222] [1:TimedEventQueue:771] pil_load_seg fw_name = venus.b03 <6>[37.484909] [2:TimedEventQueue:771] pil_load_seg fw_name = venus.b04- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
在 _request_firmware()->fw_load_from_user_helper()->_request_firmware_load()函數中就在生成相應的dev節(jié)點,并通知ueventd。
static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,long timeout) {int retval = 0;struct device *f_dev = &fw_priv->dev;struct firmware_buf *buf = fw_priv->buf;struct bin_attribute *fw_attr_data = buf->dest_addr ?&firmware_direct_attr_data : &firmware_attr_data;/* fall back on userspace loading */buf->is_paged_buf = buf->dest_addr ? false : true;dev_set_uevent_suppress(f_dev, true);/* Need to pin this module until class device is destroyed */__module_get(THIS_MODULE);retval = device_add(f_dev);//以下生成的data和loading節(jié)點,用于ueventd讀取相應的binary,然后通過節(jié)點加載到內存的。//用于下載的節(jié)點,retval = device_create_bin_file(f_dev, fw_attr_data);//生成一個loading的節(jié)點,loading節(jié)點用于控制的retval = device_create_file(f_dev, &dev_attr_loading);if (retval) {dev_err(f_dev, "%s: device_create_file failed\n", __func__);goto err_del_bin_attr;}if (uevent) { //這里正在通知ueventddev_set_uevent_suppress(f_dev, false);dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);if (timeout != MAX_SCHEDULE_TIMEOUT)schedule_delayed_work(&fw_priv->timeout_work, timeout);kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);}wait_for_completion(&buf->completion);cancel_delayed_work_sync(&fw_priv->timeout_work); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
_request_firmware() -> assign_firmware_buf() 這是做什么的??
來看一下ueventd.c文件中是怎么檢測這個然后讀binary,通過loading節(jié)點加載binary的。
int ueventd_main(int argc, char **argv){...while(1) {ufd.revents = 0;nr = poll(&ufd, 1, -1);if (nr <= 0)continue;if (ufd.revents & POLLIN)handle_device_fd();} }void handle_device_fd(){...handle_firmware_event(&uevent);//process_firmware_event() }#define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" #define FIRMWARE_DIR2 "/vendor/firmware" #define FIRMWARE_DIR3 "/firmware/image" #define FIRMWARE_DIR4 "/firmware-modem/image" #define DEVICES_BASE "/devices/soc.0"static void process_firmware_event(struct uevent *uevent){...l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);l = asprintf(&loading, "%sloading", root);l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware);l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware);l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware);l = asprintf(&file4, FIRMWARE_DIR4"/%s", uevent->firmware);loading_fd = open(loading, O_WRONLY);...if(!load_firmware(fw_fd, loading_fd, data_fd)) //加載binaryINFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);elseINFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
以下看看ueventd中,真正把讀到的binary,傳給kernel的函數
static int load_firmware(int fw_fd, int loading_fd, int data_fd) {struct stat st;long len_to_copy;int ret = 0;//fstat查看binary的信息,讀出來size等if(fstat(fw_fd, &st) < 0)return -1;len_to_copy = st.st_size;write(loading_fd, "1", 1); /* start transfer */while (len_to_copy > 0) {char buf[PAGE_SIZE];ssize_t nr;//讀nr = read(fw_fd, buf, sizeof(buf));if(!nr)break;if(nr < 0) {ret = -1;break;}len_to_copy -= nr;while (nr > 0) {ssize_t nw = 0;//寫到data節(jié)點nw = write(data_fd, buf + nw, nr);if(nw <= 0) {ret = -1;goto out;}nr -= nw;}}out:if(!ret) //loading節(jié)點用于通知kernel加載情況!!write(loading_fd, "0", 1); /* successful end of transfer */elsewrite(loading_fd, "-1", 2); /* abort transfer */return ret; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
內核中,data節(jié)點出來write的函數在_request_firmware_load()中根據buf->dest_addr的值有所不同
static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,long timeout) {...struct bin_attribute *fw_attr_data = buf->dest_addr ?&firmware_direct_attr_data : &firmware_attr_data;...}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
在下載modem.bxx的時候應該都是有buf->dest_addr才對
<6>[29.355860] [0:init:1] pil_load_seg fw_name = modem.b02 <6>[29.361829] [0:init:1] fw_load_from_user_helper start <6>[29.368051] [0:init:1] _request_firmware_load buf->dest_addr = 0x86800000 <6>[29.380308] [0:ueventd:230] firmware_loading_store started ... <6>[29.391942] [0:init:1] pil_load_seg fw_name = modem.b07 <6>[29.398801] [0:init:1] fw_load_from_user_helper start <6>[29.404996] [0:init: 1] _request_firmware_load buf->dest_addr = 0x86840000 ...- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
和ioremap_page_range()比較像。
總結
以上是生活随笔為你收集整理的Linux驱动基础:msm平台,modem等framework加载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简谈高通Trustzone的实现
- 下一篇: 使用LogKit进行日志操作