发现baidu文库里有个smem机制总结全的,顺便分享下
QUALCOMM的AP与MODEM之间的share memory通过把共享内存空间分成N个不定长数据块,其中SMEM_HEAP_INFO记录每个数据块的地址信息,是否已经分配等,(只能一个宿主先分配),当然SMEM_HEAP_INFO本身也是一个数据块。各个宿主CPU用这些数据块依照对应的数据结构通信,包括PROC_COMM, smem_find,以及建立在特定数据块上的循环缓冲区smd通道,还有建立在特定通道的函数调用RPC。
(共享内存2个基本点:1在本身内存内记录分配信息 2互斥访问或数据一致性 ;
3(可选).如果要快速响应的必须加相互中断通知)
地址空间的映射与管理:
SMEM_HEAP:(该图版本较老)
typedef enum
{ SMEM_MEM_FIRST,
SMEM_PROC_COMM = SMEM_MEM_FIRST,
SMEM_FIRST_FIXED_BUFFER = SMEM_PROC_COMM,
SMEM_HEAP_INFO,
……
} smem_mem_type;将1M得share memory分为N个条目。最终实际固定了每个条目的起始地址和长度。 (详见AP端linux kernel的smd.c的smem_alloc2分配函数,可知不能通常意义的malloc,只是简单的动态的在尾部增长数据块,不能正真意义的回收内存空间;
总共是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共享内存空间的映射。第1个条目为proc_comm.其中包括最重要的条目heap_info.这个条目记录了1M share memory的每个条目的起始地址和长度。
上述结构体包含的struct smem_heap_info记录整个SM空间的alloc情况,当前的申请地址和剩余空间。
上述结构体包含的struct smem_heap_entry 是记录每个条目的地址空间信息,这个一个最多记载512个条目地址信息的数组,经常是依据smem_mem_type条目号来寻找地址信息。
AP与MODEM等通信的几种策略机制:
1.在底层数据块层次的使用是PROC_COMM,主要是最低层次处理器间通信,不建议扩展。主要是CPU各路电源,时钟,重启等高优先级的问题。每个处理器只占用4各字节。
代码实现见msm_proc_comm函数。就是写2个字节,发中断给另外的CPU,等待回应。
2.另外一个机制是smem_alloc,smem_get_entry,smem_find。对我们目前来说,主要应用是是MODEM端写一些结构的数据到SHARED MEMORY,AP端在合适的时候去取。
这一部分的代码在MODEM分配内存,从AP传递,其中的几个内存管理函数比如smem_alloc是对我们屏蔽的(不能真正意义的回收free)。记住一般是MODEM端从AP传递,反向传递不建议。
3.下面是建立在smem_alloc这个机制上面的SMD。就是循环缓冲区,一端不停的写,另一端不停的读。类似于进程间通信,对上层包装成串口驱动一样的设备。实现机制同样是写数据和中断。读数据回调上层的注册函数。实现代码见smd.c,可不关心,和我们没太多联系。
使用SMD机制通道的接口是串口驱动类似的接口,smd_open,smd_write,smd_read,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);//RMNET
smd_open("GPSNMEA", &nmea_devp->ch, nmea_devp, nmea_notify);
smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify); //smd_rpcrouter.c
smd_open(name, &info->ch, info, smd_tty_notify); //smd_tty.c 虚拟出TTY设备给应用
4.最后是RPC。这个就是远程函数调用。实现机制是建立在SMD通道之上的一个机制。使用SMD通道2,命名为RPCCALL,通道3 RPCRPY。这个见smd_rpcrouter_device.c和smd_rpcrouter.c。主要用法是APP调用MODEM的函数,其中可选项是MODEM端执行完函数后,是否回调APP的一个函数。在RPC中,留了一个OEM_RAPI给我们扩展。具体扩展见qualcomm文档80-VM896-1(附件)。可在KERNEL或应用程序扩展。(应用程序扩展编译时要注意不要包文档里面要求的头文件,否则编译有问题,其他按文档要求就可以)。
RPC:这个的应用最多;
比如AMSS调用kernel上报电池电量在msm_battery.c中 msm_rpc_register_client("battery", BATTERY_RPC_PROG,BATTERY_RPC_VER_2_1,
1, msm_batt_cb_func);注册clinet端函数。对应AMSS的servies端。
msm_rpc_register_XXX----à Smd_rpcrouter_clients.c&Smd_rpcrouter_servies.c----(通过msm_rpc_write等函数接口)---------à smd_rpcrouter.c--àrpcrouter_smd_xprt.c--àsmd_open
另外smd_rpcrouter.c 中的rpcrouter_init ----->msm_rpcrouter_init_devices(smd_rpcrouter_device.c)建立一个class_create(THIS_MODULE, "oncrpc");设备节点。
smd_rpcrouter_device.c实现这个驱动节点的write ;read;poll,ioctl等标准操作。这些操作通过msm_rpc_write等同样调用smd_rpcrouter.c的接口。
应用层的vender/qcom/proprietary/oncrpc的liboncrpc.so操作这个驱动节点。(这样在上层直接建议与MODEM的通信,可以不在kernel开源自己的代码)
Hard/msm7K/librpc的librpc.so操作这个驱动节点。