mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-09 17:17:04 -04:00
greybus: Merge branch 'master' into vibrator-gb
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
greybus-y := core.o \
|
||||
gbuf.o \
|
||||
sysfs.o \
|
||||
debugfs.o \
|
||||
ap.o \
|
||||
|
||||
@@ -29,6 +29,21 @@ struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd,
|
||||
return connection;
|
||||
}
|
||||
|
||||
void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
|
||||
u8 *data, size_t length)
|
||||
{
|
||||
struct gb_connection *connection;
|
||||
|
||||
connection = gb_hd_connection_find(hd, cport_id);
|
||||
if (!connection) {
|
||||
dev_err(hd->parent,
|
||||
"nonexistent connection (%zu bytes dropped)\n", length);
|
||||
return;
|
||||
}
|
||||
gb_connection_operation_recv(connection, data, length);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(greybus_cport_in);
|
||||
|
||||
/*
|
||||
* Allocate an available CPort Id for use for the host side of the
|
||||
* given connection. The lowest-available id is returned, so the
|
||||
|
||||
@@ -53,6 +53,8 @@ void gb_connection_exit(struct gb_connection *connection);
|
||||
struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd,
|
||||
u16 cport_id);
|
||||
|
||||
void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
|
||||
u8 *data, size_t length);
|
||||
__printf(2, 3)
|
||||
void gb_connection_err(struct gb_connection *connection, const char *fmt, ...);
|
||||
|
||||
|
||||
@@ -226,12 +226,6 @@ static int __init gb_init(void)
|
||||
goto error_ap;
|
||||
}
|
||||
|
||||
retval = gb_gbuf_init();
|
||||
if (retval) {
|
||||
pr_err("gb_gbuf_init failed\n");
|
||||
goto error_gbuf;
|
||||
}
|
||||
|
||||
retval = gb_operation_init();
|
||||
if (retval) {
|
||||
pr_err("gb_operation_init failed\n");
|
||||
@@ -250,8 +244,6 @@ static int __init gb_init(void)
|
||||
error_protocol:
|
||||
gb_operation_exit();
|
||||
error_operation:
|
||||
gb_gbuf_exit();
|
||||
error_gbuf:
|
||||
gb_ap_exit();
|
||||
error_ap:
|
||||
bus_unregister(&greybus_bus_type);
|
||||
@@ -265,7 +257,6 @@ static void __exit gb_exit(void)
|
||||
{
|
||||
gb_protocol_exit();
|
||||
gb_operation_exit();
|
||||
gb_gbuf_exit();
|
||||
gb_ap_exit();
|
||||
bus_unregister(&greybus_bus_type);
|
||||
gb_debugfs_cleanup();
|
||||
|
||||
@@ -99,6 +99,9 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size,
|
||||
u32 cport_reserve = gbuf->dest_cport_id == CPORT_ID_BAD ? 0 : 1;
|
||||
u8 *buffer;
|
||||
|
||||
if (gbuf->transfer_buffer)
|
||||
return -EALREADY;
|
||||
|
||||
if (size > ES1_GBUF_MSG_SIZE) {
|
||||
pr_err("guf was asked to be bigger than %ld!\n",
|
||||
ES1_GBUF_MSG_SIZE);
|
||||
@@ -146,6 +149,7 @@ static void free_gbuf_data(struct gbuf *gbuf)
|
||||
if (gbuf->dest_cport_id != CPORT_ID_BAD)
|
||||
transfer_buffer--; /* Back up to cport id */
|
||||
kfree(transfer_buffer);
|
||||
gbuf->transfer_buffer = NULL;
|
||||
}
|
||||
|
||||
#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */
|
||||
@@ -214,6 +218,8 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask)
|
||||
struct urb *urb;
|
||||
|
||||
transfer_buffer = gbuf->transfer_buffer;
|
||||
if (!transfer_buffer)
|
||||
return -EINVAL;
|
||||
buffer = &transfer_buffer[-1]; /* yes, we mean -1 */
|
||||
|
||||
/* Find a free urb */
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Greybus gbuf handling
|
||||
*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "greybus.h"
|
||||
|
||||
static struct kmem_cache *gbuf_head_cache;
|
||||
|
||||
/**
|
||||
* greybus_alloc_gbuf - allocate a greybus buffer
|
||||
*
|
||||
* @gmod: greybus device that wants to allocate this
|
||||
* @cport: cport to send the data to
|
||||
* @complete: callback when the gbuf is finished with
|
||||
* @size: size of the buffer
|
||||
* @gfp_mask: allocation mask
|
||||
*
|
||||
* TODO: someday it will be nice to handle DMA, but for now, due to the
|
||||
* architecture we are stuck with, the greybus core has to allocate the buffer
|
||||
* that the driver can then fill up with the data to be sent out. Curse
|
||||
* hardware designers for this issue...
|
||||
*/
|
||||
struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd,
|
||||
u16 dest_cport_id,
|
||||
unsigned int size,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct gbuf *gbuf;
|
||||
int retval;
|
||||
|
||||
gbuf = kmem_cache_zalloc(gbuf_head_cache, gfp_mask);
|
||||
if (!gbuf)
|
||||
return NULL;
|
||||
|
||||
kref_init(&gbuf->kref);
|
||||
gbuf->hd = hd;
|
||||
gbuf->dest_cport_id = dest_cport_id;
|
||||
gbuf->status = -EBADR; /* Initial value--means "never set" */
|
||||
|
||||
/* Host controller specific allocation for the actual buffer */
|
||||
retval = hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask);
|
||||
if (retval) {
|
||||
kmem_cache_free(gbuf_head_cache, gbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gbuf;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(greybus_alloc_gbuf);
|
||||
|
||||
static DEFINE_MUTEX(gbuf_mutex);
|
||||
|
||||
static void free_gbuf(struct kref *kref)
|
||||
{
|
||||
struct gbuf *gbuf = container_of(kref, struct gbuf, kref);
|
||||
|
||||
gbuf->hd->driver->free_gbuf_data(gbuf);
|
||||
|
||||
kmem_cache_free(gbuf_head_cache, gbuf);
|
||||
mutex_unlock(&gbuf_mutex);
|
||||
}
|
||||
|
||||
void greybus_free_gbuf(struct gbuf *gbuf)
|
||||
{
|
||||
/* drop the reference count and get out of here */
|
||||
kref_put_mutex(&gbuf->kref, free_gbuf, &gbuf_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(greybus_free_gbuf);
|
||||
|
||||
struct gbuf *greybus_get_gbuf(struct gbuf *gbuf)
|
||||
{
|
||||
mutex_lock(&gbuf_mutex);
|
||||
kref_get(&gbuf->kref);
|
||||
mutex_unlock(&gbuf_mutex);
|
||||
return gbuf;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(greybus_get_gbuf);
|
||||
|
||||
int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask)
|
||||
{
|
||||
gbuf->status = -EINPROGRESS;
|
||||
|
||||
return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask);
|
||||
}
|
||||
|
||||
void greybus_kill_gbuf(struct gbuf *gbuf)
|
||||
{
|
||||
if (gbuf->status != -EINPROGRESS)
|
||||
return;
|
||||
|
||||
gbuf->hd->driver->kill_gbuf(gbuf);
|
||||
}
|
||||
|
||||
void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
|
||||
u8 *data, size_t length)
|
||||
{
|
||||
struct gb_connection *connection;
|
||||
|
||||
connection = gb_hd_connection_find(hd, cport_id);
|
||||
if (!connection) {
|
||||
dev_err(hd->parent,
|
||||
"nonexistent connection (%zu bytes dropped)\n", length);
|
||||
return;
|
||||
}
|
||||
gb_connection_operation_recv(connection, data, length);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(greybus_cport_in);
|
||||
|
||||
int gb_gbuf_init(void)
|
||||
{
|
||||
gbuf_head_cache = kmem_cache_create("gbuf_head_cache",
|
||||
sizeof(struct gbuf), 0, 0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gb_gbuf_exit(void)
|
||||
{
|
||||
kmem_cache_destroy(gbuf_head_cache);
|
||||
gbuf_head_cache = NULL;
|
||||
}
|
||||
@@ -153,7 +153,7 @@ static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_co
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "version response %hhu",
|
||||
response->status);
|
||||
@@ -199,7 +199,7 @@ static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_contr
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "line count response %hhu",
|
||||
response->status);
|
||||
@@ -234,7 +234,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -244,7 +244,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "activate response %hhu",
|
||||
response->status);
|
||||
@@ -278,7 +278,7 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -288,7 +288,7 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "deactivate response %hhu",
|
||||
response->status);
|
||||
@@ -320,7 +320,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -330,7 +330,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "get direction response %hhu",
|
||||
response->status);
|
||||
@@ -369,7 +369,7 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -379,7 +379,7 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "direction in response %hhu",
|
||||
response->status);
|
||||
@@ -412,7 +412,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
request->value = value_high ? 1 : 0;
|
||||
|
||||
@@ -423,7 +423,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "direction out response %hhu",
|
||||
response->status);
|
||||
@@ -456,7 +456,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -466,7 +466,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "get value response %hhu",
|
||||
response->status);
|
||||
@@ -507,7 +507,7 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
request->value = value_high ? 1 : 0;
|
||||
|
||||
@@ -518,7 +518,7 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "set value response %hhu",
|
||||
response->status);
|
||||
@@ -554,7 +554,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
request->usec = cpu_to_le16(debounce_usec);
|
||||
|
||||
@@ -565,7 +565,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "set debounce response %hhu",
|
||||
response->status);
|
||||
|
||||
@@ -56,82 +56,6 @@
|
||||
#define HOST_DEV_CPORT_ID_MAX CONFIG_HOST_DEV_CPORT_ID_MAX
|
||||
#define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */
|
||||
|
||||
/*
|
||||
gbuf
|
||||
|
||||
This is the "main" data structure to send / receive Greybus messages
|
||||
|
||||
There are two different "views" of a gbuf structure:
|
||||
- a greybus driver
|
||||
- a greybus host controller
|
||||
|
||||
A Greybus driver needs to worry about the following:
|
||||
- creating a gbuf
|
||||
- putting data into a gbuf
|
||||
- sending a gbuf to a device
|
||||
- receiving a gbuf from a device
|
||||
|
||||
Creating a gbuf:
|
||||
A greybus driver calls greybus_alloc_gbuf()
|
||||
Putting data into a gbuf:
|
||||
copy data into gbuf->transfer_buffer
|
||||
Send a gbuf:
|
||||
A greybus driver calls greybus_submit_gbuf()
|
||||
The completion function in a gbuf will be called if the gbuf is successful
|
||||
or not. That completion function runs in user context. After the
|
||||
completion function is called, the gbuf must not be touched again as the
|
||||
greybus core "owns" it. But, if a greybus driver wants to "hold on" to a
|
||||
gbuf after the completion function has been called, a reference must be
|
||||
grabbed on the gbuf with a call to greybus_get_gbuf(). When finished with
|
||||
the gbuf, call greybus_free_gbuf() and when the last reference count is
|
||||
dropped, it will be removed from the system.
|
||||
Receive a gbuf:
|
||||
A greybus driver calls gb_register_cport_complete() with a pointer to the
|
||||
callback function to be called for when a gbuf is received from a specific
|
||||
cport and device. That callback will be made in user context with a gbuf
|
||||
when it is received. To stop receiving messages, call
|
||||
gb_deregister_cport_complete() for a specific cport.
|
||||
|
||||
|
||||
Greybus Host controller drivers need to provide
|
||||
- a way to allocate the transfer buffer for a gbuf
|
||||
- a way to free the transfer buffer for a gbuf when it is "finished"
|
||||
- a way to submit gbuf for transmissions
|
||||
- notify the core the gbuf is complete
|
||||
- receive gbuf from the wire and submit them to the core
|
||||
- a way to send and receive svc messages
|
||||
Allocate a transfer buffer
|
||||
the host controller function alloc_gbuf_data is called
|
||||
Free a transfer buffer
|
||||
the host controller function free_gbuf_data is called
|
||||
Submit a gbuf to the hardware
|
||||
the host controller function submit_gbuf is called
|
||||
Notify the gbuf is complete
|
||||
the host controller driver must call greybus_gbuf_finished()
|
||||
Submit a SVC message to the hardware
|
||||
the host controller function send_svc_msg is called
|
||||
Receive gbuf messages
|
||||
the host controller driver must call greybus_cport_in() with the data
|
||||
Reveive SVC messages from the hardware
|
||||
The host controller driver must call greybus_svc_in
|
||||
|
||||
|
||||
*/
|
||||
|
||||
struct gbuf {
|
||||
struct kref kref;
|
||||
|
||||
struct greybus_host_device *hd;
|
||||
u16 dest_cport_id; /* Destination CPort id */
|
||||
int status;
|
||||
|
||||
void *transfer_buffer;
|
||||
u32 transfer_buffer_length;
|
||||
|
||||
void *hcd_data; /* for the HCD to track the gbuf */
|
||||
};
|
||||
|
||||
|
||||
/* For SP1 hardware, we are going to "hardcode" each device to have all logical
|
||||
* blocks in order to be able to address them as one unified "unit". Then
|
||||
* higher up layers will then be able to talk to them as one logical block and
|
||||
@@ -144,6 +68,7 @@ struct gbuf {
|
||||
|
||||
struct greybus_host_device;
|
||||
struct svc_msg;
|
||||
struct gbuf;
|
||||
|
||||
/* Greybus "Host driver" structure, needed by a host controller driver to be
|
||||
* able to handle both SVC control as well as "real" greybus messages
|
||||
@@ -177,19 +102,6 @@ struct greybus_host_device {
|
||||
struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd,
|
||||
struct device *parent);
|
||||
void greybus_remove_hd(struct greybus_host_device *hd);
|
||||
void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
|
||||
u8 *data, size_t length);
|
||||
|
||||
struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd,
|
||||
u16 dest_cport_id, unsigned int size,
|
||||
gfp_t gfp_mask);
|
||||
void greybus_free_gbuf(struct gbuf *gbuf);
|
||||
struct gbuf *greybus_get_gbuf(struct gbuf *gbuf);
|
||||
#define greybus_put_gbuf greybus_free_gbuf
|
||||
|
||||
int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags);
|
||||
void greybus_kill_gbuf(struct gbuf *gbuf);
|
||||
|
||||
|
||||
struct greybus_driver {
|
||||
const char *name;
|
||||
|
||||
@@ -118,7 +118,7 @@ static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "version response %hhu",
|
||||
response->status);
|
||||
@@ -170,7 +170,7 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "functionality response %hhu",
|
||||
response->status);
|
||||
@@ -198,7 +198,7 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec)
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->msec = cpu_to_le16(msec);
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -208,7 +208,7 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "timeout response %hhu",
|
||||
response->status);
|
||||
@@ -235,7 +235,7 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev,
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->retries = retries;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -245,7 +245,7 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "retries response %hhu",
|
||||
response->status);
|
||||
@@ -321,7 +321,7 @@ gb_i2c_transfer_request(struct gb_connection *connection,
|
||||
if (!operation)
|
||||
return NULL;
|
||||
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->op_count = cpu_to_le16(op_count);
|
||||
/* Fill in the ops array */
|
||||
op = &request->ops[0];
|
||||
@@ -380,7 +380,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
if (response->status == GB_OP_RETRY) {
|
||||
ret = -EAGAIN;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
/*
|
||||
* XXX This needs to be coordinated with host driver parameters
|
||||
* XXX May need to reduce to allow for message header within a page
|
||||
*/
|
||||
#define GB_OPERATION_MESSAGE_SIZE_MAX 4096
|
||||
|
||||
@@ -71,7 +72,7 @@ static void gb_pending_operation_insert(struct gb_operation *operation)
|
||||
spin_unlock_irq(&gb_operations_lock);
|
||||
|
||||
/* Store the operation id in the request header */
|
||||
header = operation->request->transfer_buffer;
|
||||
header = operation->request.gbuf.transfer_buffer;
|
||||
header->id = cpu_to_le16(operation->id);
|
||||
}
|
||||
|
||||
@@ -102,6 +103,20 @@ gb_pending_operation_find(struct gb_connection *connection, u16 id)
|
||||
return found ? operation : NULL;
|
||||
}
|
||||
|
||||
static int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask)
|
||||
{
|
||||
gbuf->status = -EINPROGRESS;
|
||||
|
||||
return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask);
|
||||
}
|
||||
|
||||
static void greybus_kill_gbuf(struct gbuf *gbuf)
|
||||
{
|
||||
if (gbuf->status != -EINPROGRESS)
|
||||
return;
|
||||
|
||||
gbuf->hd->driver->kill_gbuf(gbuf);
|
||||
}
|
||||
/*
|
||||
* An operations's response message has arrived. If no callback was
|
||||
* supplied it was submitted for asynchronous completion, so we notify
|
||||
@@ -124,7 +139,7 @@ int gb_operation_wait(struct gb_operation *operation)
|
||||
ret = wait_for_completion_interruptible(&operation->completion);
|
||||
/* If interrupted, cancel the in-flight buffer */
|
||||
if (ret < 0)
|
||||
greybus_kill_gbuf(operation->request);
|
||||
greybus_kill_gbuf(&operation->request.gbuf);
|
||||
return ret;
|
||||
|
||||
}
|
||||
@@ -134,7 +149,7 @@ static void gb_operation_request_handle(struct gb_operation *operation)
|
||||
struct gb_protocol *protocol = operation->connection->protocol;
|
||||
struct gb_operation_msg_hdr *header;
|
||||
|
||||
header = operation->request->transfer_buffer;
|
||||
header = operation->request.gbuf.transfer_buffer;
|
||||
|
||||
/*
|
||||
* If the protocol has no incoming request handler, report
|
||||
@@ -164,7 +179,7 @@ static void gb_operation_recv_work(struct work_struct *recv_work)
|
||||
bool incoming_request;
|
||||
|
||||
operation = container_of(recv_work, struct gb_operation, recv_work);
|
||||
incoming_request = operation->response == NULL;
|
||||
incoming_request = operation->response.gbuf.transfer_buffer == NULL;
|
||||
if (incoming_request)
|
||||
gb_operation_request_handle(operation);
|
||||
gb_operation_complete(operation);
|
||||
@@ -196,28 +211,42 @@ static void operation_timeout(struct work_struct *work)
|
||||
* initialize it here (it'll be overwritten by the incoming
|
||||
* message).
|
||||
*/
|
||||
static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation,
|
||||
u8 type, size_t size,
|
||||
bool data_out)
|
||||
static int gb_operation_message_init(struct gb_operation *operation,
|
||||
u8 type, size_t size,
|
||||
bool request, bool data_out)
|
||||
{
|
||||
struct gb_connection *connection = operation->connection;
|
||||
struct greybus_host_device *hd = connection->hd;
|
||||
struct gb_message *message;
|
||||
struct gb_operation_msg_hdr *header;
|
||||
struct gbuf *gbuf;
|
||||
gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC;
|
||||
u16 dest_cport_id;
|
||||
int ret;
|
||||
|
||||
if (size > GB_OPERATION_MESSAGE_SIZE_MAX)
|
||||
return NULL; /* Message too big */
|
||||
return -E2BIG;
|
||||
size += sizeof(*header);
|
||||
|
||||
if (request) {
|
||||
message = &operation->request;
|
||||
} else {
|
||||
message = &operation->response;
|
||||
type |= GB_OPERATION_TYPE_RESPONSE;
|
||||
}
|
||||
gbuf = &message->gbuf;
|
||||
|
||||
if (data_out)
|
||||
dest_cport_id = connection->interface_cport_id;
|
||||
else
|
||||
dest_cport_id = CPORT_ID_BAD;
|
||||
size += sizeof(*header);
|
||||
gbuf = greybus_alloc_gbuf(connection->hd, dest_cport_id,
|
||||
size, gfp_flags);
|
||||
if (!gbuf)
|
||||
return NULL;
|
||||
|
||||
gbuf->hd = hd;
|
||||
gbuf->dest_cport_id = dest_cport_id;
|
||||
gbuf->status = -EBADR; /* Initial value--means "never set" */
|
||||
ret = hd->driver->alloc_gbuf_data(gbuf, size, gfp_flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Fill in the header structure */
|
||||
header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer;
|
||||
@@ -225,7 +254,17 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation,
|
||||
header->id = 0; /* Filled in when submitted */
|
||||
header->type = type;
|
||||
|
||||
return gbuf;
|
||||
message->payload = header + 1;
|
||||
message->operation = operation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gb_operation_message_exit(struct gb_message *message)
|
||||
{
|
||||
message->operation = NULL;
|
||||
message->payload = NULL;
|
||||
message->gbuf.hd->driver->free_gbuf_data(&message->gbuf);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -251,30 +290,23 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection,
|
||||
struct gb_operation *operation;
|
||||
gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC;
|
||||
bool outgoing = response_size != 0;
|
||||
int ret;
|
||||
|
||||
operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags);
|
||||
if (!operation)
|
||||
return NULL;
|
||||
operation->connection = connection;
|
||||
|
||||
operation->request = gb_operation_gbuf_create(operation, type,
|
||||
request_size,
|
||||
outgoing);
|
||||
if (!operation->request)
|
||||
ret = gb_operation_message_init(operation, type, request_size,
|
||||
true, outgoing);
|
||||
if (ret)
|
||||
goto err_cache;
|
||||
operation->request_payload = operation->request->transfer_buffer +
|
||||
sizeof(struct gb_operation_msg_hdr);
|
||||
|
||||
if (outgoing) {
|
||||
type |= GB_OPERATION_TYPE_RESPONSE;
|
||||
operation->response = gb_operation_gbuf_create(operation,
|
||||
type, response_size,
|
||||
false);
|
||||
if (!operation->response)
|
||||
ret = gb_operation_message_init(operation, type, response_size,
|
||||
false, false);
|
||||
if (ret)
|
||||
goto err_request;
|
||||
operation->response_payload =
|
||||
operation->response->transfer_buffer +
|
||||
sizeof(struct gb_operation_msg_hdr);
|
||||
}
|
||||
|
||||
INIT_WORK(&operation->recv_work, gb_operation_recv_work);
|
||||
@@ -290,7 +322,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection,
|
||||
return operation;
|
||||
|
||||
err_request:
|
||||
greybus_free_gbuf(operation->request);
|
||||
gb_operation_message_exit(&operation->request);
|
||||
err_cache:
|
||||
kmem_cache_free(gb_operation_cache, operation);
|
||||
|
||||
@@ -311,8 +343,8 @@ static void _gb_operation_destroy(struct kref *kref)
|
||||
list_del(&operation->links);
|
||||
spin_unlock_irq(&gb_operations_lock);
|
||||
|
||||
greybus_free_gbuf(operation->response);
|
||||
greybus_free_gbuf(operation->request);
|
||||
gb_operation_message_exit(&operation->response);
|
||||
gb_operation_message_exit(&operation->request);
|
||||
|
||||
kmem_cache_free(gb_operation_cache, operation);
|
||||
}
|
||||
@@ -349,7 +381,7 @@ int gb_operation_request_send(struct gb_operation *operation,
|
||||
*/
|
||||
operation->callback = callback;
|
||||
gb_pending_operation_insert(operation);
|
||||
ret = greybus_submit_gbuf(operation->request, GFP_KERNEL);
|
||||
ret = greybus_submit_gbuf(&operation->request.gbuf, GFP_KERNEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -414,7 +446,7 @@ void gb_connection_operation_recv(struct gb_connection *connection,
|
||||
}
|
||||
cancel_delayed_work(&operation->timeout_work);
|
||||
gb_pending_operation_remove(operation);
|
||||
gbuf = operation->response;
|
||||
gbuf = &operation->response.gbuf;
|
||||
if (size > gbuf->transfer_buffer_length) {
|
||||
operation->result = GB_OP_OVERFLOW;
|
||||
gb_connection_err(connection, "recv buffer too small");
|
||||
@@ -429,7 +461,7 @@ void gb_connection_operation_recv(struct gb_connection *connection,
|
||||
gb_connection_err(connection, "can't create operation");
|
||||
return;
|
||||
}
|
||||
gbuf = operation->request;
|
||||
gbuf = &operation->request.gbuf;
|
||||
}
|
||||
|
||||
memcpy(gbuf->transfer_buffer, data, msg_size);
|
||||
@@ -444,9 +476,9 @@ void gb_connection_operation_recv(struct gb_connection *connection,
|
||||
void gb_operation_cancel(struct gb_operation *operation)
|
||||
{
|
||||
operation->canceled = true;
|
||||
greybus_kill_gbuf(operation->request);
|
||||
if (operation->response)
|
||||
greybus_kill_gbuf(operation->response);
|
||||
greybus_kill_gbuf(&operation->request.gbuf);
|
||||
if (operation->response.gbuf.transfer_buffer)
|
||||
greybus_kill_gbuf(&operation->response.gbuf);
|
||||
}
|
||||
|
||||
int gb_operation_init(void)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include <linux/completion.h>
|
||||
|
||||
struct gb_operation;
|
||||
|
||||
enum gb_operation_status {
|
||||
GB_OP_SUCCESS = 0,
|
||||
GB_OP_INVALID = 1,
|
||||
@@ -22,6 +24,23 @@ enum gb_operation_status {
|
||||
GB_OP_TIMEOUT = 0xff,
|
||||
};
|
||||
|
||||
struct gbuf {
|
||||
struct greybus_host_device *hd;
|
||||
u16 dest_cport_id; /* Destination CPort id */
|
||||
int status;
|
||||
|
||||
void *transfer_buffer;
|
||||
u32 transfer_buffer_length;
|
||||
|
||||
void *hcd_data; /* for the HCD to track the gbuf */
|
||||
};
|
||||
|
||||
struct gb_message {
|
||||
void *payload;
|
||||
struct gb_operation *operation;
|
||||
struct gbuf gbuf;
|
||||
};
|
||||
|
||||
/*
|
||||
* A Greybus operation is a remote procedure call performed over a
|
||||
* connection between the AP and a function on Greybus module.
|
||||
@@ -50,12 +69,11 @@ enum gb_operation_status {
|
||||
* is guaranteed to be 64-bit aligned.
|
||||
* XXX and callback?
|
||||
*/
|
||||
struct gb_operation;
|
||||
typedef void (*gb_operation_callback)(struct gb_operation *);
|
||||
struct gb_operation {
|
||||
struct gb_connection *connection;
|
||||
struct gbuf *request;
|
||||
struct gbuf *response;
|
||||
struct gb_message request;
|
||||
struct gb_message response;
|
||||
u16 id;
|
||||
bool canceled;
|
||||
|
||||
@@ -67,10 +85,6 @@ struct gb_operation {
|
||||
|
||||
struct kref kref;
|
||||
struct list_head links; /* connection->{operations,pending} */
|
||||
|
||||
/* These are what's used by caller */
|
||||
void *request_payload;
|
||||
void *response_payload;
|
||||
};
|
||||
|
||||
void gb_connection_operation_recv(struct gb_connection *connection,
|
||||
|
||||
@@ -110,7 +110,7 @@ static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "version response %hhu",
|
||||
response->status);
|
||||
@@ -151,7 +151,7 @@ static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "pwm count response %hhu",
|
||||
response->status);
|
||||
@@ -181,7 +181,7 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc,
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -191,7 +191,7 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "activate response %hhu",
|
||||
response->status);
|
||||
@@ -220,7 +220,7 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc,
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -230,7 +230,7 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "deactivate response %hhu",
|
||||
response->status);
|
||||
@@ -258,7 +258,7 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc,
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
request->duty = duty;
|
||||
request->period = period;
|
||||
@@ -270,7 +270,7 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "config response %hhu",
|
||||
response->status);
|
||||
@@ -299,7 +299,7 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc,
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
request->polarity = polarity;
|
||||
|
||||
@@ -310,7 +310,7 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "set polarity response %hhu",
|
||||
response->status);
|
||||
@@ -339,7 +339,7 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc,
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -349,7 +349,7 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "enable response %hhu",
|
||||
response->status);
|
||||
@@ -378,7 +378,7 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc,
|
||||
sizeof(*request), sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->which = which;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -388,7 +388,7 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "disable response %hhu",
|
||||
response->status);
|
||||
|
||||
@@ -229,7 +229,7 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data)
|
||||
sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->size = cpu_to_le16(size);
|
||||
memcpy(&request->data[0], data, size);
|
||||
|
||||
@@ -241,7 +241,7 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "send data response %hhu",
|
||||
response->status);
|
||||
@@ -267,7 +267,7 @@ static int send_line_coding(struct gb_tty *tty,
|
||||
sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
memcpy(&request->line_coding, line_coding, sizeof(*line_coding));
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -278,7 +278,7 @@ static int send_line_coding(struct gb_tty *tty,
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "send line coding response %hhu",
|
||||
response->status);
|
||||
@@ -304,7 +304,7 @@ static int send_control(struct gb_tty *tty, u16 control)
|
||||
sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->control = cpu_to_le16(control);
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -315,7 +315,7 @@ static int send_control(struct gb_tty *tty, u16 control)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "send control response %hhu",
|
||||
response->status);
|
||||
@@ -345,7 +345,7 @@ static int send_break(struct gb_tty *tty, u8 state)
|
||||
sizeof(*response));
|
||||
if (!operation)
|
||||
return -ENOMEM;
|
||||
request = operation->request_payload;
|
||||
request = operation->request.payload;
|
||||
request->state = state;
|
||||
|
||||
/* Synchronous operation--no callback */
|
||||
@@ -356,7 +356,7 @@ static int send_break(struct gb_tty *tty, u8 state)
|
||||
goto out;
|
||||
}
|
||||
|
||||
response = operation->response_payload;
|
||||
response = operation->response.payload;
|
||||
if (response->status) {
|
||||
gb_connection_err(connection, "send break response %hhu",
|
||||
response->status);
|
||||
|
||||
Reference in New Issue
Block a user