584 lines · 13.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Greybus CPort control protocol. * * Copyright 2015 Google Inc. * Copyright 2015 Linaro Ltd. */ #include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/greybus.h> /* Highest control-protocol version supported */#define GB_CONTROL_VERSION_MAJOR	0#define GB_CONTROL_VERSION_MINOR	1 static int gb_control_get_version(struct gb_control *control){	struct gb_interface *intf = control->connection->intf;	struct gb_control_version_request request;	struct gb_control_version_response response;	int ret; 	request.major = GB_CONTROL_VERSION_MAJOR;	request.minor = GB_CONTROL_VERSION_MINOR; 	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_VERSION,				&request, sizeof(request), &response,				sizeof(response));	if (ret) {		dev_err(&intf->dev,			"failed to get control-protocol version: %d\n",			ret);		return ret;	} 	if (response.major > request.major) {		dev_err(&intf->dev,			"unsupported major control-protocol version (%u > %u)\n",			response.major, request.major);		return -ENOTSUPP;	} 	control->protocol_major = response.major;	control->protocol_minor = response.minor; 	dev_dbg(&intf->dev, "%s - %u.%u\n", __func__, response.major,		response.minor); 	return 0;} static int gb_control_get_bundle_version(struct gb_control *control,					 struct gb_bundle *bundle){	struct gb_interface *intf = control->connection->intf;	struct gb_control_bundle_version_request request;	struct gb_control_bundle_version_response response;	int ret; 	request.bundle_id = bundle->id; 	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_BUNDLE_VERSION,				&request, sizeof(request),				&response, sizeof(response));	if (ret) {		dev_err(&intf->dev,			"failed to get bundle %u class version: %d\n",			bundle->id, ret);		return ret;	} 	bundle->class_major = response.major;	bundle->class_minor = response.minor; 	dev_dbg(&intf->dev, "%s - %u: %u.%u\n", __func__, bundle->id,		response.major, response.minor); 	return 0;} int gb_control_get_bundle_versions(struct gb_control *control){	struct gb_interface *intf = control->connection->intf;	struct gb_bundle *bundle;	int ret; 	if (!control->has_bundle_version)		return 0; 	list_for_each_entry(bundle, &intf->bundles, links) {		ret = gb_control_get_bundle_version(control, bundle);		if (ret)			return ret;	} 	return 0;} /* Get Manifest's size from the interface */int gb_control_get_manifest_size_operation(struct gb_interface *intf){	struct gb_control_get_manifest_size_response response;	struct gb_connection *connection = intf->control->connection;	int ret; 	ret = gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST_SIZE,				NULL, 0, &response, sizeof(response));	if (ret) {		dev_err(&connection->intf->dev,			"failed to get manifest size: %d\n", ret);		return ret;	} 	return le16_to_cpu(response.size);} /* Reads Manifest from the interface */int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest,				      size_t size){	struct gb_connection *connection = intf->control->connection; 	return gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST,				NULL, 0, manifest, size);} int gb_control_connected_operation(struct gb_control *control, u16 cport_id){	struct gb_control_connected_request request; 	request.cport_id = cpu_to_le16(cport_id);	return gb_operation_sync(control->connection, GB_CONTROL_TYPE_CONNECTED,				 &request, sizeof(request), NULL, 0);} int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id){	struct gb_control_disconnected_request request; 	request.cport_id = cpu_to_le16(cport_id);	return gb_operation_sync(control->connection,				 GB_CONTROL_TYPE_DISCONNECTED, &request,				 sizeof(request), NULL, 0);} int gb_control_disconnecting_operation(struct gb_control *control,				       u16 cport_id){	struct gb_control_disconnecting_request *request;	struct gb_operation *operation;	int ret; 	operation = gb_operation_create_core(control->connection,					     GB_CONTROL_TYPE_DISCONNECTING,					     sizeof(*request), 0, 0,					     GFP_KERNEL);	if (!operation)		return -ENOMEM; 	request = operation->request->payload;	request->cport_id = cpu_to_le16(cport_id); 	ret = gb_operation_request_send_sync(operation);	if (ret) {		dev_err(&control->dev, "failed to send disconnecting: %d\n",			ret);	} 	gb_operation_put(operation); 	return ret;} int gb_control_mode_switch_operation(struct gb_control *control){	struct gb_operation *operation;	int ret; 	operation = gb_operation_create_core(control->connection,					     GB_CONTROL_TYPE_MODE_SWITCH,					     0, 0,					     GB_OPERATION_FLAG_UNIDIRECTIONAL,					     GFP_KERNEL);	if (!operation)		return -ENOMEM; 	ret = gb_operation_request_send_sync(operation);	if (ret)		dev_err(&control->dev, "failed to send mode switch: %d\n", ret); 	gb_operation_put(operation); 	return ret;} static int gb_control_bundle_pm_status_map(u8 status){	switch (status) {	case GB_CONTROL_BUNDLE_PM_INVAL:		return -EINVAL;	case GB_CONTROL_BUNDLE_PM_BUSY:		return -EBUSY;	case GB_CONTROL_BUNDLE_PM_NA:		return -ENOMSG;	case GB_CONTROL_BUNDLE_PM_FAIL:	default:		return -EREMOTEIO;	}} int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id){	struct gb_control_bundle_pm_request request;	struct gb_control_bundle_pm_response response;	int ret; 	request.bundle_id = bundle_id;	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_BUNDLE_SUSPEND, &request,				sizeof(request), &response, sizeof(response));	if (ret) {		dev_err(&control->dev, "failed to send bundle %u suspend: %d\n",			bundle_id, ret);		return ret;	} 	if (response.status != GB_CONTROL_BUNDLE_PM_OK) {		dev_err(&control->dev, "failed to suspend bundle %u: %d\n",			bundle_id, response.status);		return gb_control_bundle_pm_status_map(response.status);	} 	return 0;} int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id){	struct gb_control_bundle_pm_request request;	struct gb_control_bundle_pm_response response;	int ret; 	request.bundle_id = bundle_id;	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_BUNDLE_RESUME, &request,				sizeof(request), &response, sizeof(response));	if (ret) {		dev_err(&control->dev, "failed to send bundle %u resume: %d\n",			bundle_id, ret);		return ret;	} 	if (response.status != GB_CONTROL_BUNDLE_PM_OK) {		dev_err(&control->dev, "failed to resume bundle %u: %d\n",			bundle_id, response.status);		return gb_control_bundle_pm_status_map(response.status);	} 	return 0;} int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id){	struct gb_control_bundle_pm_request request;	struct gb_control_bundle_pm_response response;	int ret; 	request.bundle_id = bundle_id;	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_BUNDLE_DEACTIVATE, &request,				sizeof(request), &response, sizeof(response));	if (ret) {		dev_err(&control->dev,			"failed to send bundle %u deactivate: %d\n", bundle_id,			ret);		return ret;	} 	if (response.status != GB_CONTROL_BUNDLE_PM_OK) {		dev_err(&control->dev, "failed to deactivate bundle %u: %d\n",			bundle_id, response.status);		return gb_control_bundle_pm_status_map(response.status);	} 	return 0;} int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id){	struct gb_control_bundle_pm_request request;	struct gb_control_bundle_pm_response response;	int ret; 	if (!control->has_bundle_activate)		return 0; 	request.bundle_id = bundle_id;	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_BUNDLE_ACTIVATE, &request,				sizeof(request), &response, sizeof(response));	if (ret) {		dev_err(&control->dev,			"failed to send bundle %u activate: %d\n", bundle_id,			ret);		return ret;	} 	if (response.status != GB_CONTROL_BUNDLE_PM_OK) {		dev_err(&control->dev, "failed to activate bundle %u: %d\n",			bundle_id, response.status);		return gb_control_bundle_pm_status_map(response.status);	} 	return 0;} static int gb_control_interface_pm_status_map(u8 status){	switch (status) {	case GB_CONTROL_INTF_PM_BUSY:		return -EBUSY;	case GB_CONTROL_INTF_PM_NA:		return -ENOMSG;	default:		return -EREMOTEIO;	}} int gb_control_interface_suspend_prepare(struct gb_control *control){	struct gb_control_intf_pm_response response;	int ret; 	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE, NULL, 0,				&response, sizeof(response));	if (ret) {		dev_err(&control->dev,			"failed to send interface suspend prepare: %d\n", ret);		return ret;	} 	if (response.status != GB_CONTROL_INTF_PM_OK) {		dev_err(&control->dev, "interface error while preparing suspend: %d\n",			response.status);		return gb_control_interface_pm_status_map(response.status);	} 	return 0;} int gb_control_interface_deactivate_prepare(struct gb_control *control){	struct gb_control_intf_pm_response response;	int ret; 	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE, NULL,				0, &response, sizeof(response));	if (ret) {		dev_err(&control->dev, "failed to send interface deactivate prepare: %d\n",			ret);		return ret;	} 	if (response.status != GB_CONTROL_INTF_PM_OK) {		dev_err(&control->dev, "interface error while preparing deactivate: %d\n",			response.status);		return gb_control_interface_pm_status_map(response.status);	} 	return 0;} int gb_control_interface_hibernate_abort(struct gb_control *control){	struct gb_control_intf_pm_response response;	int ret; 	ret = gb_operation_sync(control->connection,				GB_CONTROL_TYPE_INTF_HIBERNATE_ABORT, NULL, 0,				&response, sizeof(response));	if (ret) {		dev_err(&control->dev,			"failed to send interface aborting hibernate: %d\n",			ret);		return ret;	} 	if (response.status != GB_CONTROL_INTF_PM_OK) {		dev_err(&control->dev, "interface error while aborting hibernate: %d\n",			response.status);		return gb_control_interface_pm_status_map(response.status);	} 	return 0;} static ssize_t vendor_string_show(struct device *dev,				  struct device_attribute *attr, char *buf){	struct gb_control *control = to_gb_control(dev); 	return scnprintf(buf, PAGE_SIZE, "%s\n", control->vendor_string);}static DEVICE_ATTR_RO(vendor_string); static ssize_t product_string_show(struct device *dev,				   struct device_attribute *attr, char *buf){	struct gb_control *control = to_gb_control(dev); 	return scnprintf(buf, PAGE_SIZE, "%s\n", control->product_string);}static DEVICE_ATTR_RO(product_string); static struct attribute *control_attrs[] = {	&dev_attr_vendor_string.attr,	&dev_attr_product_string.attr,	NULL,};ATTRIBUTE_GROUPS(control); static void gb_control_release(struct device *dev){	struct gb_control *control = to_gb_control(dev); 	gb_connection_destroy(control->connection); 	kfree(control->vendor_string);	kfree(control->product_string); 	kfree(control);} const struct device_type greybus_control_type = {	.name =		"greybus_control",	.release =	gb_control_release,}; struct gb_control *gb_control_create(struct gb_interface *intf){	struct gb_connection *connection;	struct gb_control *control; 	control = kzalloc_obj(*control);	if (!control)		return ERR_PTR(-ENOMEM); 	control->intf = intf; 	connection = gb_connection_create_control(intf);	if (IS_ERR(connection)) {		dev_err(&intf->dev,			"failed to create control connection: %ld\n",			PTR_ERR(connection));		kfree(control);		return ERR_CAST(connection);	} 	control->connection = connection; 	control->dev.parent = &intf->dev;	control->dev.bus = &greybus_bus_type;	control->dev.type = &greybus_control_type;	control->dev.groups = control_groups;	control->dev.dma_mask = intf->dev.dma_mask;	device_initialize(&control->dev);	dev_set_name(&control->dev, "%s.ctrl", dev_name(&intf->dev)); 	gb_connection_set_data(control->connection, control); 	return control;} int gb_control_enable(struct gb_control *control){	int ret; 	dev_dbg(&control->connection->intf->dev, "%s\n", __func__); 	ret = gb_connection_enable_tx(control->connection);	if (ret) {		dev_err(&control->connection->intf->dev,			"failed to enable control connection: %d\n",			ret);		return ret;	} 	ret = gb_control_get_version(control);	if (ret)		goto err_disable_connection; 	if (control->protocol_major > 0 || control->protocol_minor > 1)		control->has_bundle_version = true; 	/* FIXME: use protocol version instead */

Lines 1–500 of 584.