Commit b94295e0 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

greybus: sysfs attributes for functions and more driver core integration.

parent 526c5c8d
...@@ -156,6 +156,78 @@ void greybus_deregister(struct greybus_driver *driver) ...@@ -156,6 +156,78 @@ void greybus_deregister(struct greybus_driver *driver)
EXPORT_SYMBOL_GPL(greybus_deregister); EXPORT_SYMBOL_GPL(greybus_deregister);
static void greybus_module_release(struct device *dev)
{
struct greybus_device *gdev = to_greybus_device(dev);
int i;
for (i = 0; i < gdev->num_strings; ++i)
kfree(gdev->string[i]);
for (i = 0; i < gdev->num_cports; ++i)
kfree(gdev->cport[i]);
kfree(gdev);
}
static struct device_type greybus_module_type = {
.name = "greybus_module",
.release = greybus_module_release,
};
/* Function fields */
#define greybus_function_attr(field) \
static ssize_t function_##field##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct greybus_device *gdev = to_greybus_device(dev); \
return sprintf(buf, "%d\n", gdev->function.field); \
} \
static DEVICE_ATTR_RO(function_##field)
greybus_function_attr(number);
greybus_function_attr(cport);
greybus_function_attr(class);
greybus_function_attr(subclass);
greybus_function_attr(protocol);
static struct attribute *function_attrs[] = {
&dev_attr_function_number.attr,
&dev_attr_function_cport.attr,
&dev_attr_function_class.attr,
&dev_attr_function_subclass.attr,
&dev_attr_function_protocol.attr,
NULL,
};
static umode_t function_attrs_are_visible(struct kobject *kobj,
struct attribute *a, int n)
{
struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj));
// FIXME - make this a dynamic structure to "know" if it really is here
// or not easier?
if (gdev->function.number ||
gdev->function.cport ||
gdev->function.class ||
gdev->function.subclass ||
gdev->function.protocol)
return a->mode;
return 0;
}
static struct attribute_group function_addr_grp = {
.attrs = function_attrs,
.is_visible = function_attrs_are_visible,
};
static const struct attribute_group *greybus_module_groups[] = {
&function_addr_grp,
NULL,
};
static int gb_init_subdevs(struct greybus_device *gdev, static int gb_init_subdevs(struct greybus_device *gdev,
const struct greybus_module_id *id) const struct greybus_module_id *id)
{ {
...@@ -201,7 +273,8 @@ static int create_function(struct greybus_device *gdev, ...@@ -201,7 +273,8 @@ static int create_function(struct greybus_device *gdev,
int header_size = sizeof(struct greybus_descriptor_function); int header_size = sizeof(struct greybus_descriptor_function);
if (desc_size != header_size) { if (desc_size != header_size) {
pr_err("invalid function header size %d\n", desc_size); dev_err(gdev->dev.parent, "invalid function header size %d\n",
desc_size);
return -EINVAL; return -EINVAL;
} }
memcpy(&gdev->function, &desc->function, header_size); memcpy(&gdev->function, &desc->function, header_size);
...@@ -214,7 +287,8 @@ static int create_module_id(struct greybus_device *gdev, ...@@ -214,7 +287,8 @@ static int create_module_id(struct greybus_device *gdev,
int header_size = sizeof(struct greybus_descriptor_module_id); int header_size = sizeof(struct greybus_descriptor_module_id);
if (desc_size != header_size) { if (desc_size != header_size) {
pr_err("invalid module header size %d\n", desc_size); dev_err(gdev->dev.parent, "invalid module header size %d\n",
desc_size);
return -EINVAL; return -EINVAL;
} }
memcpy(&gdev->module_id, &desc->module_id, header_size); memcpy(&gdev->module_id, &desc->module_id, header_size);
...@@ -227,7 +301,8 @@ static int create_serial_number(struct greybus_device *gdev, ...@@ -227,7 +301,8 @@ static int create_serial_number(struct greybus_device *gdev,
int header_size = sizeof(struct greybus_descriptor_serial_number); int header_size = sizeof(struct greybus_descriptor_serial_number);
if (desc_size != header_size) { if (desc_size != header_size) {
pr_err("invalid serial number header size %d\n", desc_size); dev_err(gdev->dev.parent, "invalid serial number header size %d\n",
desc_size);
return -EINVAL; return -EINVAL;
} }
memcpy(&gdev->serial_number, &desc->serial_number, header_size); memcpy(&gdev->serial_number, &desc->serial_number, header_size);
...@@ -242,12 +317,14 @@ static int create_string(struct greybus_device *gdev, ...@@ -242,12 +317,14 @@ static int create_string(struct greybus_device *gdev,
int header_size = sizeof(struct greybus_descriptor_string); int header_size = sizeof(struct greybus_descriptor_string);
if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) { if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) {
pr_err("too many strings for this module!\n"); dev_err(gdev->dev.parent,
"too many strings for this module!\n");
return -EINVAL; return -EINVAL;
} }
if (desc_size < header_size) { if (desc_size < header_size) {
pr_err("invalid string header size %d\n", desc_size); dev_err(gdev->dev.parent, "invalid string header size %d\n",
desc_size);
return -EINVAL; return -EINVAL;
} }
...@@ -259,28 +336,59 @@ static int create_string(struct greybus_device *gdev, ...@@ -259,28 +336,59 @@ static int create_string(struct greybus_device *gdev,
string->length = string_size; string->length = string_size;
string->id = desc->string.id; string->id = desc->string.id;
memcpy(&string->string, &desc->string.string, string_size); memcpy(&string->string, &desc->string.string, string_size);
gdev->string[gdev->num_strings] = string; gdev->string[gdev->num_strings] = string;
gdev->num_strings++; gdev->num_strings++;
return 0; return 0;
} }
static int create_cport(struct greybus_device *gdev,
struct greybus_descriptor *desc, int desc_size)
{
struct gdev_cport *cport;
int header_size = sizeof(struct greybus_descriptor_cport);
if ((gdev->num_cports + 1) >= MAX_CPORTS_PER_MODULE) {
dev_err(gdev->dev.parent, "too many cports for this module!\n");
return -EINVAL;
}
if (desc_size != header_size) {
dev_err(gdev->dev.parent,
"invalid serial number header size %d\n", desc_size);
return -EINVAL;
}
cport = kzalloc(sizeof(*cport), GFP_KERNEL);
if (!cport)
return -ENOMEM;
cport->number = le16_to_cpu(desc->cport.number);
cport->size = le16_to_cpu(desc->cport.size);
cport->speed = desc->cport.speed;
gdev->cport[gdev->num_cports] = cport;
gdev->num_cports++;
return 0;
}
/** /**
* greybus_new_device: * greybus_new_device:
* *
* Pass in a buffer that _should_ be a set of greybus descriptor fields and spit * Pass in a buffer that _should_ be a set of greybus descriptor fields and spit
* out a greybus device structure. * out a greybus device structure.
*/ */
struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) struct greybus_device *greybus_new_device(struct device *parent,
int module_number, u8 *data, int size)
{ {
struct greybus_device *gdev; struct greybus_device *gdev;
struct greybus_descriptor_block_header *block; struct greybus_descriptor_block_header *block;
struct greybus_descriptor *desc; struct greybus_descriptor *desc;
int retval; int retval;
int overall_size; int overall_size;
int header_size;
int desc_size; int desc_size;
int i;
u8 version_major; u8 version_major;
u8 version_minor; u8 version_minor;
...@@ -293,11 +401,19 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) ...@@ -293,11 +401,19 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size)
return NULL; return NULL;
gdev->module_number = module_number; gdev->module_number = module_number;
gdev->dev.parent = parent;
gdev->dev.driver = NULL;
gdev->dev.bus = &greybus_bus_type;
gdev->dev.type = &greybus_module_type;
gdev->dev.groups = greybus_module_groups;
gdev->dev.dma_mask = parent->dma_mask;
device_initialize(&gdev->dev);
dev_set_name(&gdev->dev, "%d", module_number);
block = (struct greybus_descriptor_block_header *)data; block = (struct greybus_descriptor_block_header *)data;
overall_size = le16_to_cpu(block->size); overall_size = le16_to_cpu(block->size);
if (overall_size != size) { if (overall_size != size) {
pr_err("size != block header size, %d != %d\n", size, dev_err(parent, "size != block header size, %d != %d\n", size,
overall_size); overall_size);
goto error; goto error;
} }
...@@ -330,32 +446,14 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) ...@@ -330,32 +446,14 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size)
retval = create_string(gdev, desc, desc_size); retval = create_string(gdev, desc, desc_size);
break; break;
case GREYBUS_TYPE_CPORT: { case GREYBUS_TYPE_CPORT:
struct gdev_cport *cport; retval = create_cport(gdev, desc, desc_size);
header_size = sizeof(struct greybus_descriptor_cport);
if (desc_size != header_size) {
pr_err("invalid serial number header size %d\n",
desc_size);
goto error;
}
cport = kzalloc(sizeof(*cport), GFP_KERNEL);
if (!cport)
goto error;
cport->number = le16_to_cpu(desc->cport.number);
cport->size = le16_to_cpu(desc->cport.size);
cport->speed = desc->cport.speed;
gdev->cport[gdev->num_cports] = cport;
gdev->num_cports++;
// FIXME - check for too many cports...
size -= desc_size;
data += desc_size;
break; break;
}
case GREYBUS_TYPE_INVALID: case GREYBUS_TYPE_INVALID:
default: default:
pr_err("invalid descriptor type %d\n", desc->header.type); dev_err(parent, "invalid descriptor type %d\n",
desc->header.type);
goto error; goto error;
} }
if (retval) if (retval)
...@@ -367,23 +465,25 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) ...@@ -367,23 +465,25 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size)
retval = gb_init_subdevs(gdev, &fake_gb_id); retval = gb_init_subdevs(gdev, &fake_gb_id);
if (retval) if (retval)
goto error; goto error;
// FIXME device_add(&gdev->dev);
return gdev; return gdev;
error: error:
for (i = 0; i < gdev->num_strings; ++i) greybus_module_release(&gdev->dev);
kfree(gdev->string[i]);
for (i = 0; i < gdev->num_cports; ++i)
kfree(gdev->cport[i]);
kfree(gdev);
return NULL; return NULL;
} }
void remove_device(struct greybus_device *gdev) void greybus_remove_device(struct greybus_device *gdev)
{ {
/* tear down all of the "sub device types" for this device */ /* tear down all of the "sub device types" for this device */
gb_i2c_disconnect(gdev); gb_i2c_disconnect(gdev);
gb_gpio_disconnect(gdev); gb_gpio_disconnect(gdev);
gb_sdio_disconnect(gdev); gb_sdio_disconnect(gdev);
gb_tty_disconnect(gdev); gb_tty_disconnect(gdev);
// FIXME - device_remove(&gdev->dev);
} }
static int __init gb_init(void) static int __init gb_init(void)
......
...@@ -173,6 +173,10 @@ void greybus_deregister(struct greybus_driver *driver); ...@@ -173,6 +173,10 @@ void greybus_deregister(struct greybus_driver *driver);
int greybus_disabled(void); int greybus_disabled(void);
struct greybus_device *greybus_new_device(struct device *parent,
int module_number, u8 *data,
int size);
void greybus_remove_device(struct greybus_device *gdev);
/* Internal functions to gb module, move to internal .h file eventually. */ /* Internal functions to gb module, move to internal .h file eventually. */
......
...@@ -50,9 +50,9 @@ enum greybus_function_class { ...@@ -50,9 +50,9 @@ enum greybus_function_class {
struct greybus_descriptor_function { struct greybus_descriptor_function {
__le16 number; __le16 number;
__le16 cport; __le16 cport;
__u8 function_class; /* enum greybus_function_class */ __u8 class; /* enum greybus_function_class */
__u8 function_subclass; __u8 subclass;
__u8 function_protocol; __u8 protocol;
__u8 reserved; __u8 reserved;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment