/** * __of_device_is_compatible() - Check if the node matches given constraints * @device: pointer to node * @compat: required compatible string, NULL or "" for any match * @type: required device_type value, NULL or "" for any match * @name: required node name, NULL or "" for any match * * Checks if the given @compat, @type and @name strings match the * properties of the given @device. A constraints can be skipped by * passing NULL or an empty string as the constraint. * * Returns 0 for no match, and a positive integer on match. The return * value is a relative score with larger values indicating better * matches. The score is weighted for the most specific compatible value * to get the highest score. Matching type is next, followed by matching * name. Practically speaking, this results in the following priority * order for matches: * * 1. specific compatible && type && name * 2. specific compatible && type * 3. specific compatible && name * 4. specific compatible * 5. general compatible && type && name * 6. general compatible && type * 7. general compatible && name * 8. general compatible * 9. type && name * 10. type * 11. name */ staticint __of_device_is_compatible(conststruct device_node *device, constchar *compat, constchar *type, constchar *name) { structproperty *prop; constchar *cp; int index = 0, score = 0;
/* Compatible match has highest priority */ if (compat && compat[0]) { prop = __of_find_property(device, "compatible", NULL); for (cp = of_prop_next_string(prop, NULL); cp; cp = of_prop_next_string(prop, cp), index++) { if (of_compat_cmp(cp, compat, strlen(compat)) == 0) { score = INT_MAX/2 - (index << 2); break; } } if (!score) return0; }
/* Matching type is better than matching name */ if (type && type[0]) { if (!__of_node_is_type(device, type)) return0; score += 2; }
/* Matching name is a bit better than not */ if (name && name[0]) { if (!of_node_name_eq(device, name)) return0; score++; }
/* * when an rpmsg driver is probed with a channel, we seamlessly create * it an endpoint, binding its rx callback to a unique local rpmsg * address. * * if we need to, we also announce about this channel to the remote * processor (needed in case the driver is exposing an rpmsg service). */ staticintrpmsg_dev_probe(struct device *dev) { structrpmsg_device *rpdev = to_rpmsg_device(dev); structrpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); structrpmsg_channel_infochinfo = {}; structrpmsg_endpoint *ept =NULL; int err;
err = dev_pm_domain_attach(dev, true); if (err) goto out;
/** * rpmsg_device - device that belong to the rpmsg bus * @dev: the device struct * @id: device id (used to match between rpmsg drivers and devices) * @driver_override: driver name to force a match; do not set directly, * because core frees it; use driver_set_override() to * set or clear it. * @src: local address * @dst: destination address * @ept: the rpmsg endpoint of this channel * @announce: if set, rpmsg will announce the creation/removal of this channel */ structrpmsg_device { structdevicedev;// Linux 设备模型的基类 structrpmsg_device_idid;// 设备标识(匹配用) constchar *driver_override; // 强制绑定指定驱动 u32 src; // 本地地址 u32 dst; // 目的地址 structrpmsg_endpoint *ept;// 端点(接收回调绑定于此) bool announce; // 是否向远端宣告生命周期 conststructrpmsg_device_ops *ops;// 后端操作表 };
struct device dev
这是 Linux 设备模型的嵌入基类。rpmsg_device 通过组合而非继承的方式接入内核设备模型。
/** * struct rpmsg_device_ops - indirection table for the rpmsg_device operations * @create_ept: create backend-specific endpoint, required * @announce_create: announce presence of new channel, optional * @announce_destroy: announce destruction of channel, optional * * Indirection table for the operations that a rpmsg backend should implement. * @announce_create and @announce_destroy are optional as the backend might * advertise new channels implicitly by creating the endpoints. */ structrpmsg_device_ops { structrpmsg_endpoint *(*create_ept)(structrpmsg_device *rpdev, rpmsg_rx_cb_tcb, void *priv, structrpmsg_channel_infochinfo);
int (*announce_create)(struct rpmsg_device *ept); int (*announce_destroy)(struct rpmsg_device *ept); };
/** * struct rpmsg_endpoint - binds a local rpmsg address to its user * @rpdev: rpmsg channel device * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @cb_lock: must be taken before accessing/changing @cb * @addr: local rpmsg address * @priv: private data for the driver's use * * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as * it binds an rpmsg address with an rx callback handler. * * Simple rpmsg drivers shouldn't use this struct directly, because * things just work: every rpmsg driver provides an rx callback upon * registering to the bus, and that callback is then bound to its rpmsg * address when the driver is probed. When relevant inbound messages arrive * (i.e. messages which their dst address equals to the src address of * the rpmsg channel), the driver's handler is invoked to process it. * * More complicated drivers though, that do need to allocate additional rpmsg * addresses, and bind them to different rx callbacks, must explicitly * create additional endpoints by themselves (see rpmsg_create_ept()). */ structrpmsg_endpoint { structrpmsg_device *rpdev; structkrefrefcount; rpmsg_rx_cb_t cb; structmutexcb_lock; u32 addr; void *priv;
/** * struct rpmsg_driver - rpmsg driver struct * @drv: underlying device driver * @id_table: rpmsg ids serviced by this driver * @probe: invoked when a matching rpmsg channel (i.e. device) is found * @remove: invoked when the rpmsg channel is removed * @callback: invoked when an inbound message is received on the channel */ structrpmsg_driver { structdevice_driverdrv; conststructrpmsg_device_id *id_table; int (*probe)(struct rpmsg_device *dev); void (*remove)(struct rpmsg_device *dev); int (*callback)(struct rpmsg_device *, void *, int, void *, u32); };
struct rpmsg_driver 是 RPMSG 框架中驱动开发者需要填充的核心结构体,它遵循 Linux 标准设备驱动模型,同时封装了 RPMSG 特有的消息收发语义。
struct device_driver drv
Linux 设备模型基类,用于挂接到 rpmsg_bus
const struct rpmsg_device_id *id_table
驱动支持的设备 ID 表(按服务名匹配)
int (*probe)(struct rpmsg_device *)
匹配成功时调用,执行驱动初始化,参考rpmsg_dev_probe函数
void (*remove)(struct rpmsg_device *dev);
设备移除/驱动卸载时调用,执行清理
int (*callback)(struct rpmsg_device *, void *, int, void *, u32);
/** * struct rpmsg_device_ops - indirection table for the rpmsg_device operations * @create_ept: create backend-specific endpoint, required * @announce_create: announce presence of new channel, optional * @announce_destroy: announce destruction of channel, optional * * Indirection table for the operations that a rpmsg backend should implement. * @announce_create and @announce_destroy are optional as the backend might * advertise new channels implicitly by creating the endpoints. */ structrpmsg_device_ops { structrpmsg_endpoint *(*create_ept)(structrpmsg_device *rpdev, rpmsg_rx_cb_tcb, void *priv, structrpmsg_channel_infochinfo);
int (*announce_create)(struct rpmsg_device *ept); int (*announce_destroy)(struct rpmsg_device *ept); };
/** * struct rpmsg_endpoint_ops - indirection table for rpmsg_endpoint operations * @destroy_ept: see @rpmsg_destroy_ept(), required * @send: see @rpmsg_send(), required * @sendto: see @rpmsg_sendto(), optional * @send_offchannel: see @rpmsg_send_offchannel(), optional * @trysend: see @rpmsg_trysend(), required * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel: see @rpmsg_trysend_offchannel(), optional * @poll: see @rpmsg_poll(), optional * * Indirection table for the operations that a rpmsg backend should implement. * In addition to @destroy_ept, the backend must at least implement @send and * @trysend, while the variants sending data off-channel are optional. */ structrpmsg_endpoint_ops { void (*destroy_ept)(struct rpmsg_endpoint *ept);
int (*send)(struct rpmsg_endpoint *ept, void *data, int len); int (*sendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int (*send_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len);
int (*trysend)(struct rpmsg_endpoint *ept, void *data, int len); int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int (*trysend_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); };
/** * rpmsg_create_ept() - create a new rpmsg_endpoint * @rpdev: rpmsg channel device * @cb: rx callback handler * @priv: private data for the driver's use * @chinfo: channel_info with the local rpmsg address to bind with @cb * * Every rpmsg address in the system is bound to an rx callback (so when * inbound messages arrive, they are dispatched by the rpmsg bus using the * appropriate callback handler) by means of an rpmsg_endpoint struct. * * This function allows drivers to create such an endpoint, and by that, * bind a callback, and possibly some private data too, to an rpmsg address * (either one that is known in advance, or one that will be dynamically * assigned for them). * * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint * is already created for them when they are probed by the rpmsg bus * (using the rx callback provided when they registered to the rpmsg bus). * * So things should just work for simple drivers: they already have an * endpoint, their rx callback is bound to their rpmsg address, and when * relevant inbound messages arrive (i.e. messages which their dst address * equals to the src address of their rpmsg channel), the driver's handler * is invoked to process it. * * That said, more complicated drivers might need to allocate * additional rpmsg addresses, and bind them to different rx callbacks. * To accomplish that, those drivers need to call this function. * * Drivers should provide their @rpdev channel (so the new endpoint would belong * to the same remote processor their channel belongs to), an rx callback * function, an optional private data (which is provided back when the * rx callback is invoked), and an address they want to bind with the * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will * dynamically assign them an available rpmsg address (drivers should have * a very good reason why not to always use RPMSG_ADDR_ANY here). * * Returns a pointer to the endpoint on success, or NULL on error. */ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo) { if (WARN_ON(!rpdev)) returnNULL;
/** * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint * @ept: endpoing to destroy * * Should be used by drivers to destroy an rpmsg endpoint previously * created with rpmsg_create_ept(). As with other types of "free" NULL * is a valid parameter. */ voidrpmsg_destroy_ept(struct rpmsg_endpoint *ept) { if (ept && ept->ops) ept->ops->destroy_ept(ept); } EXPORT_SYMBOL(rpmsg_destroy_ept);
/** * rpmsg_send() - send a message across to the remote processor * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * * This function sends @data of length @len on the @ept endpoint. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address and its associated rpmsg * device destination addresses. * In case there are no TX buffers available, the function will block until * one becomes available, or a timeout of 15 seconds elapses. When the latter * happens, -ERESTARTSYS is returned. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */ intrpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) { if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->send) return -ENXIO;
/** * rpmsg_sendto() - send a message across to the remote processor, specify dst * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * @dst: destination address * * This function sends @data of length @len to the remote @dst address. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address as source. * In case there are no TX buffers available, the function will block until * one becomes available, or a timeout of 15 seconds elapses. When the latter * happens, -ERESTARTSYS is returned. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */ intrpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) { if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->sendto) return -ENXIO;
/** * rpmsg_send_offchannel() - send a message using explicit src/dst addresses * @ept: the rpmsg endpoint * @src: source address * @dst: destination address * @data: payload of message * @len: length of payload * * This function sends @data of length @len to the remote @dst address, * and uses @src as the source address. * The message will be sent to the remote processor which the @ept * endpoint belongs to. * In case there are no TX buffers available, the function will block until * one becomes available, or a timeout of 15 seconds elapses. When the latter * happens, -ERESTARTSYS is returned. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */ intrpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len) { if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->send_offchannel) return -ENXIO;
/** * rpmsg_trysend() - send a message across to the remote processor * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * * This function sends @data of length @len on the @ept endpoint. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address as source and its associated * rpdev's address as destination. * In case there are no TX buffers available, the function will immediately * return -ENOMEM without waiting until one becomes available. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */ intrpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len) { if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->trysend) return -ENXIO;
/** * rpmsg_trysendto() - send a message across to the remote processor, specify dst * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * @dst: destination address * * This function sends @data of length @len to the remote @dst address. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address as source. * In case there are no TX buffers available, the function will immediately * return -ENOMEM without waiting until one becomes available. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */ intrpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) { if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->trysendto) return -ENXIO;
/** * rpmsg_poll() - poll the endpoint's send buffers * @ept: the rpmsg endpoint * @filp: file for poll_wait() * @wait: poll_table for poll_wait() * * Returns mask representing the current state of the endpoint's send buffers */ __poll_trpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait) { if (WARN_ON(!ept)) return0; if (!ept->ops->poll) return0;
/** * rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses * @ept: the rpmsg endpoint * @src: source address * @dst: destination address * @data: payload of message * @len: length of payload * * This function sends @data of length @len to the remote @dst address, * and uses @src as the source address. * The message will be sent to the remote processor which the @ept * endpoint belongs to. * In case there are no TX buffers available, the function will immediately * return -ENOMEM without waiting until one becomes available. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */ intrpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len) { if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->trysend_offchannel) return -ENXIO;
/* * match a rpmsg channel with a channel info struct. * this is used to make sure we're not creating rpmsg devices for channels * that already exist. */ staticintrpmsg_device_match(struct device *dev, void *data) { structrpmsg_channel_info *chinfo = data; structrpmsg_device *rpdev = to_rpmsg_device(dev);
if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) return0;
if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst) return0;
if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) return0;