QUALIOMM 的AP和MODEM之间的share memory通过把共享内存的空间分成N个不定长的数据块,其中SMEM_HEAP_INFO记录每个数据块的地址信息,是否已经分配等,(只能一个宿主先分配),当然SMEM_HEAP_INFO 本身也是一个数据块。各个宿主CPU用这些数据块依照对应的数据结构进行通信,包括PROC_COMM,smem_find,以及建立在特殊数据块上的循环缓冲区smd通道,还有建立在特定通道的函数调用RPC。
共享内存两个基本点:
1、在本身内存内记录分配信息;
2、互斥访问或数据一致性;
3、如果要快速相应要加相互中断通知(可选)。
地址空间的映射和管理:
SMEM_HEAP
* All types of SMEM usage are defined in smem_mem_type.
* The enumeration order inside smem_mem_type is not mapped as the exact order. Refer to smem_buf_data_tbl[] for exact order of each enumeration.
* smem_buf_data_tbl[] is declared as the size of each allocated smem_mem_type in static heap.
* smem_get_offset() is called to count offset for each smem_mem_type in static heap when in runtime.
* For more detail, refer to smem_boot _init().
typedef enum
{SMEM_MEM_FIRST,
SMEM_MEM_PROC = SMEM_MEM_FIRST,
SMEM_FIRST_FIXED_BUFFER = SMEM_MEM_PROC,
SMEM_HEAP_INFO,
……………………
}smem_mem_type;
将1M的share memory分为N个条目。最终实际固定了每个条目的起始地址和长度。(AP端linux kernel的smd.c的smem_alloc2分配函数,可知不能用通常意义上的alloc,只是简单的动态的在尾部曾长数据块,不能真正意义的回收内存空间;总共1M的共享内存,其中64个smd通道占用了64*8K大部分空间)。
struct smem_shared {
struct smem_proc_comm proc_comm[4];
unsigned version[32];
struct smem_heap_info heap_info;
struct smem_heap_entry heap_toc[SMD_HEAP_SIZE];//SMD_HEAP_SIZE = 512
};
这个结构对齐到share memory的起始地址,就是1M共享内存空间的映射。第一个条目proc_comm,其中包括最终要的条目heap_info.这个条目记录了1M share memory的每个条目的起始地址和长度。
上述结构中包含的struct smem_heap_info记录整个SM空间的分配情况,当前的申请地址和剩余空间。
struct smem_heap_entry是记录每个条目的地址空间信息,这是一个最多能记录512个条目地址信息的数组,通常是根据smem_mem_type条目号来寻找地址信息。
AP和MODED等通信的几种策略机制:
1、在底层数据块层次的使用是PROC_COMM,主要是最底层次的处理器间通信,不建议扩展。主要是CPU各路电源,时钟,重启等高优先级的问题。每个处理器只占4个字节。代码实现见msm_proc_comm(),就是写两个字节,发中断给CPU,等待回应。
2、另外一个是smem_alloc, smem_get_entry, smem_find.对我们目前来说,主要应用是在MODEM端写一些结构的数据到SHARE_MEMORY,AP端在合适的时候去取。这部分代码在MODEM分配内存,从AP传递,其中的几个内存管理函数比如smem_alloc是对我们屏蔽的(不能真正意义上的回收free)。注意一般是MODEM端从AP传递,反向传递不建议。
3、下面是建立在smem_alloc这个机制上的SMD,就是循环缓冲区,一端不停的写,另一端不停的读。类似于进程间通信,对上层包装成串口驱动一样的设备。实现机制同样是写数据和中断。读数据回调上层的注册函数,实现代码smd.c。使用SMD机制通道的接口是串口驱动类似的接口,smd_open ,smd_read, smd_write, smd_read_avail,可参考smd_nema.c.比如smd_open("GPSNMEA",&nmea_devp->ch,nmea_devp,nmea_notify);其中nmea_notify是有数据要读的回调函数。在nmea_notify里面去看smd_read_avail多少可读,然后smd_read去读,写就直接调用smd_write.
目前SMD有36组通道,一般成对是使用,一个MODEM向AP,一个AP向MODEM,目前有GPS,DS,DIAG,BT,RPC等使用。
SMD循环缓冲区的应用较多,可在kernel中搜索smd_open,如下:
smd_open("SMD_DIAG",&ctxt->ch,ctxt,smd_diag_notify);
smd_open(p->chname,&p->ch,dev,smd_net_notify);
smd_open("GPSNMEA",&nmea_devp->ch,nmea->devp,nmea_notify);
smd_open("SMD_RPCCALL",&smd_channel, NULL, rpcrouter_smdnotify);
smd_open(name,&info->ch,info,smd_tty_notify);//虚拟出tty设备给应用
4、最后是RPC(remote process call)远程函数调用。实现机制是建立在SMD通道之上的一个机制。使用SMD通道2,命名为RPCCALL,通道3RPCRPY。这个详见smd_rpcrouter_device.c 和smd_rpcrouter.c 。主要用法是,APP调用MODEM函数,其中可选项是MODEM端执行函数之后,是否回调APP的一个函数。在RPC中,留了一个OEM_RAPI 给我们扩展。RPC这个应用最多:
eg:AMSS 调用kernel上报电池电量在msm_battery.c中,
msm_rpc_register_client("battery",BATTERY_RPC_PROG,BATTERY_RPC_VER_2_1,1,msm_battery_cb_func);注册client端函数。对应AMSS的server端。
msm_rpc_register_XXX---->Smd_rpcrouter_client.c & Smd_rpc_router_server.c-----通过msm_rpc_write等函数接口--->smd_rpcroutr.c--->rpcrouter_smd_xprt.c---->smd_open().
另外,smd_rpcrouter.c中的rpcrouter_init---->msm_rpcrouter_init_devices(smd_rpcrouter_device.c)建立一个class_creae(THIS_MODULE,oncrpc)设备节点。smd_rpcrouter_device.c实现这个节点的write,read,pull,iotcl等标准操作。这些操作通过条用msm_rpc_write等同样调用smd_rpcrouter.c的接口。
应用层的vendor/qcom/proprietary/oncrpc/的liboncrpc.so操作这个驱动节点。