Commit a39879fc authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

greybus: host controller additions

Also some gbuf functions starting to get fleshed out.
parent d6e0e1c5
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -449,6 +449,22 @@ void greybus_remove_device(struct greybus_device *gdev)
	// FIXME - device_remove(&gdev->dev);
}

struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
					      struct device *parent)
{
	struct greybus_host_device *hd;

	hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
	if (!hd)
		return NULL;

	kref_init(&hd->kref);

	return hd;
}
EXPORT_SYMBOL_GPL(greybus_create_hd);


static int __init gb_init(void)
{
	int retval;
+87 −26
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@
#include "greybus.h"

static const struct usb_device_id id_table[] = {
	{ USB_DEVICE(0x0000, 0x0000) },		// FIXME
	{ USB_DEVICE(0xffff, 0x0001) },		// FIXME
	{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
@@ -21,18 +21,73 @@ MODULE_DEVICE_TABLE(usb, id_table);
struct es1_ap_dev {
	struct usb_device *usb_dev;
	struct usb_interface *usb_intf;
	struct greybus_host_device *hd;

	__u8 ap_in_endpoint;
	__u8 ap_out_endpoint;
	__u8 ap_comm_endpoint;		/* endpoint to talk to the AP */
	__u8 ap_in_endpoint;		/* bulk in for CPort data */
	__u8 ap_out_endpoint;		/* bulk out for CPort data */
	u8 *ap_buffer;

};

static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd)
{
	return (struct es1_ap_dev *)(hd->hd_priv);
}

/*
 * Hack, we "know" we will only have one of these at any one time, so only
 * create one static structure pointer.
 * Allocate the actual buffer for this gbuf and device and cport
 *
 * We are responsible for setting the following fields in a struct gbuf:
 *	void *hcpriv;
 *	void *transfer_buffer;
 *	u32 transfer_buffer_length;
 */
static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask)
{
	struct es1_ap_dev *es1 = hd_to_es1(gbuf->gdev->hd);
	u8 *buffer;

	/* For ES2 we need to figure out what cport is going to what endpoint,
	 * but for ES1, it's so dirt simple, we don't have a choice...
	 *
	 * Also, do a "slow" allocation now, if we need speed, use a cache
	 */
static struct es1_ap_dev *es1_ap_dev;
	buffer = kmalloc(size + 1, gfp_mask);
	if (!buffer)
		return -ENOMEM;

	/*
	 * we will encode the cport number in the first byte of the buffer, so
	 * set the second byte to be the "transfer buffer"
	 */
	buffer[0] = gbuf->cport->number;
	gbuf->transfer_buffer = &buffer[1];
	gbuf->transfer_buffer_length = size;

	gbuf->hdpriv = es1;	/* really, we could do something else here... */

	return 0;
}

/* Free the memory we allocated with a gbuf */
static void free_gbuf(struct gbuf *gbuf)
{
	u8 *transfer_buffer;
	u8 *buffer;

	transfer_buffer = gbuf->transfer_buffer;
	buffer = &transfer_buffer[-1];	/* yes, we mean -1 */
	kfree(buffer);
}


static struct greybus_host_driver es1_driver = {
	.hd_priv_size = sizeof(struct es1_ap_dev),
	.alloc_gbuf = alloc_gbuf,
	.free_gbuf = free_gbuf,
};


void ap_in_callback(struct urb *urb)
{
@@ -100,19 +155,26 @@ void ap_out_callback(struct urb *urb)
static int ap_probe(struct usb_interface *interface,
		    const struct usb_device_id *id)
{
	struct es1_ap_dev *es1;
	struct greybus_host_device *hd;
	struct usb_device *udev;
	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	size_t buffer_size;
	int i;

	if (es1_ap_dev) {
		dev_err(&interface->dev, "Already have a es1_ap_dev???\n");
		return -ENODEV;
	}
	es1_ap_dev = kzalloc(sizeof(*es1_ap_dev), GFP_KERNEL);
	if (!es1_ap_dev)
	udev = usb_get_dev(interface_to_usbdev(interface));

	hd = greybus_create_hd(&es1_driver, &udev->dev);
	if (!hd)
		return -ENOMEM;

	es1 = hd_to_es1(hd);
	es1->hd = hd;

	/* Control endpoint is the pipe to talk to this AP, so save it off */
	endpoint = &udev->ep0.desc;
	es1->ap_comm_endpoint = endpoint->bEndpointAddress;

	// FIXME
	// figure out endpoint for talking to the AP.
	iface_desc = interface->cur_altsetting;
@@ -120,13 +182,10 @@ static int ap_probe(struct usb_interface *interface,
		endpoint = &iface_desc->endpoint[i].desc;

		if (usb_endpoint_is_bulk_in(endpoint)) {
			buffer_size = usb_endpoint_maxp(endpoint);
			// FIXME - Save buffer_size?
			es1_ap_dev->ap_in_endpoint = endpoint->bEndpointAddress;
			es1->ap_in_endpoint = endpoint->bEndpointAddress;
		}
		if (usb_endpoint_is_bulk_out(endpoint)) {
			// FIXME - anything else about this we need?
			es1_ap_dev->ap_out_endpoint = endpoint->bEndpointAddress;
			es1->ap_out_endpoint = endpoint->bEndpointAddress;
		}
		// FIXME - properly exit once found the AP endpoint
		// FIXME - set up cport endpoints
@@ -135,23 +194,25 @@ static int ap_probe(struct usb_interface *interface,
	// FIXME - allocate buffer
	// FIXME = start up talking, then create the gb "devices" based on what the AP tells us.

	es1_ap_dev->usb_intf = interface;
	es1_ap_dev->usb_dev = usb_get_dev(interface_to_usbdev(interface));
	usb_set_intfdata(interface, es1_ap_dev);
	es1->usb_intf = interface;
	es1->usb_dev = udev;
	usb_set_intfdata(interface, es1);
	return 0;
}

static void ap_disconnect(struct usb_interface *interface)
{
	es1_ap_dev = usb_get_intfdata(interface);
	struct es1_ap_dev *es1;

	es1 = usb_get_intfdata(interface);

	/* Tear down everything! */

	usb_put_dev(es1_ap_dev->usb_dev);
	kfree(es1_ap_dev->ap_buffer);
	kfree(es1_ap_dev);
	es1_ap_dev = NULL;
	usb_put_dev(es1->usb_dev);
	kfree(es1->ap_buffer);

	// FIXME
	//greybus_destroy_hd(es1->hd);
}

static struct usb_driver es1_ap_driver = {
+74 −2
Original line number Diff line number Diff line
@@ -12,21 +12,93 @@
#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"


/**
 * greybus_alloc_gbuf - allocate a greybus buffer
 *
 * @gdev: 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
 * @context: context added to the gbuf by the driver
 *
 * 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_device *gdev,
				struct gdev_cport *cport,
				gfp_t mem_flags)
				gbuf_complete_t complete,
				unsigned int size,
				gfp_t gfp_mask,
				void *context)
{
	struct gbuf *gbuf;
	int retval;

	/*
	 * change this to a slab allocation if it's too slow, but for now, let's
	 * be dumb and simple.
	 */
	gbuf = kzalloc(sizeof(*gbuf), gfp_mask);
	if (!gbuf)
		return NULL;

	kref_init(&gbuf->kref);
	gbuf->gdev = gdev;
	gbuf->cport = cport;
	gbuf->complete = complete;
	gbuf->context = context;

	/* Host controller specific allocation for the actual buffer */
	retval = gbuf->gdev->hd->driver->alloc_gbuf(gbuf, size, gfp_mask);
	if (retval) {
		kfree(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);

	/* let the host controller free what it wants to */
	gbuf->gdev->hd->driver->free_gbuf(gbuf);

	kfree(gbuf);
}

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 mem_flags)
{
+36 −5
Original line number Diff line number Diff line
@@ -52,10 +52,7 @@ typedef void (*gbuf_complete_t)(struct gbuf *gbuf);

struct gbuf {
	struct kref kref;
	void *hcpriv;

	struct list_head anchor_list;
	struct gbuf_anchor *anchor;	// FIXME do we need?
	void *hdpriv;

	struct greybus_device *gdev;
	struct gdev_cport *cport;
@@ -65,8 +62,10 @@ struct gbuf {
	u32 transfer_buffer_length;
	u32 actual_length;

#if 0
	struct scatterlist *sg;		// FIXME do we need?
	int num_sgs;
#endif

	void *context;
	gbuf_complete_t complete;
@@ -92,6 +91,31 @@ struct gb_gpio_device;
struct gb_sdio_host;
struct gb_tty;
struct gb_usb_device;
struct greybus_host_device;

/* Greybus "Host driver" structure, needed by a host controller driver to be
 * able to handle both SVC control as well as "real" greybus messages
 */
struct greybus_host_driver {
	size_t	hd_priv_size;

	int (*start)(struct greybus_host_device *hd);
	int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask);
	void (*free_gbuf)(struct gbuf *gbuf);
};

struct greybus_host_device {
	struct kref	kref;
	const struct greybus_host_driver *driver;
	unsigned long hd_priv_size;

	/* Private data for the host driver */
	unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64))));
};

struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver,
					      struct device *parent);


/* Increase these values if needed */
#define MAX_CPORTS_PER_MODULE	10
@@ -108,6 +132,8 @@ struct greybus_device {
	struct gdev_cport *cport[MAX_CPORTS_PER_MODULE];
	struct gdev_string *string[MAX_STRINGS_PER_MODULE];

	struct greybus_host_device *hd;

	struct gb_i2c_device *gb_i2c_dev;
	struct gb_gpio_device *gb_gpio_dev;
	struct gb_sdio_host *gb_sdio_host;
@@ -118,8 +144,13 @@ struct greybus_device {

struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev,
				struct gdev_cport *cport,
				gfp_t mem_flags);
				gbuf_complete_t complete,
				unsigned int size,
				gfp_t gfp_mask,
				void *context);
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);
int greybus_kill_gbuf(struct gbuf *gbuf);