diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7236e47ea9c8..9467aaba2b32 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -158,7 +158,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, if (!connection) return NULL; - retval = ida_simple_get(id_map, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL); + retval = ida_simple_get(id_map, 0, CPORT_ID_MAX, GFP_KERNEL); if (retval < 0) { kfree(connection); return NULL; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 18b5d5f48398..bc9c1ebf7983 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -170,6 +170,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver size_t buffer_size_max) { struct greybus_host_device *hd; + int ret; /* * Validate that the driver implements all of the callbacks @@ -200,12 +201,19 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver if (!hd) return ERR_PTR(-ENOMEM); + ida_init(&hd->cport_id_map); + /* Reserve CPort id 0 */ + ret = ida_simple_get(&hd->cport_id_map, 0, 1, GFP_KERNEL); + if (ret < 0) { + kfree(hd); + return ERR_PTR(ret); + } + kref_init(&hd->kref); hd->parent = parent; hd->driver = driver; INIT_LIST_HEAD(&hd->interfaces); INIT_LIST_HEAD(&hd->connections); - ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; return hd; @@ -246,7 +254,7 @@ static int __init gb_init(void) if (greybus_disabled()) return -ENODEV; - BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD); + BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD); gb_debugfs_init(); diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 9ae824abaf27..f9a6c54cca7a 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -172,6 +172,32 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) usb_free_urb(urb); } +/* + * We (ab)use the operation-message header pad bytes to transfer the + * cport id in order to minimise overhead. + */ +static void +gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) +{ + header->pad[0] = cport_id; +} + +/* Clear the pad bytes used for the CPort id */ +static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) +{ + header->pad[0] = 0; +} + +/* Extract the CPort id packed into the header, and clear it */ +static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) +{ + u16 cport_id = header->pad[0]; + + gb_message_cport_clear(header); + + return cport_id; +} + /* * Returns an opaque cookie value if successful, or a pointer coded * error otherwise. If the caller wishes to cancel the in-flight @@ -182,21 +208,17 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - void *buffer; size_t buffer_size; int retval; struct urb *urb; - buffer = message->buffer; - buffer_size = sizeof(*message->header) + message->payload_size; - /* * The data actually transferred will include an indication * of where the data should be sent. Do one last check of * the target CPort id before filling it in. */ - if (cport_id == CPORT_ID_BAD) { - pr_err("request to send inbound data buffer\n"); + if (!cport_id_valid(cport_id)) { + pr_err("invalid destination cport 0x%02x\n", cport_id); return ERR_PTR(-EINVAL); } @@ -205,21 +227,20 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, if (!urb) return ERR_PTR(-ENOMEM); - /* - * We (ab)use the operation-message header pad bytes to transfer the - * cport id in order to minimise overhead. - */ - put_unaligned_le16(cport_id, message->header->pad); + /* Pack the cport id into the message header */ + gb_message_cport_pack(message->header, cport_id); + + buffer_size = sizeof(*message->header) + message->payload_size; usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), - buffer, buffer_size, + message->buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); free_urb(es1, urb); - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); return ERR_PTR(retval); } @@ -373,12 +394,16 @@ static void cport_in_callback(struct urb *urb) goto exit; } + /* Extract the CPort id, which is packed in the message header */ header = urb->transfer_buffer; - cport_id = get_unaligned_le16(header->pad); - put_unaligned_le16(0, header->pad); + cport_id = gb_message_cport_unpack(header); - greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, + if (cport_id_valid(cport_id)) + greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); + else + dev_err(dev, "%s: invalid cport id 0x%02x received\n", + __func__, cport_id); exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -394,8 +419,7 @@ static void cport_out_callback(struct urb *urb) struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); - /* Clear the pad bytes used for the cport id */ - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); /* * Tell the submitter that the message send (attempt) is @@ -556,6 +580,9 @@ static int ap_probe(struct usb_interface *interface, u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; + /* We need to fit a CPort ID in one byte of a message header */ + BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); + udev = usb_get_dev(interface_to_usbdev(interface)); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 4e99044c1fbe..274d1d419320 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -265,6 +265,32 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) usb_free_urb(urb); } +/* + * We (ab)use the operation-message header pad bytes to transfer the + * cport id in order to minimise overhead. + */ +static void +gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) +{ + header->pad[0] = cport_id; +} + +/* Clear the pad bytes used for the CPort id */ +static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) +{ + header->pad[0] = 0; +} + +/* Extract the CPort id packed into the header, and clear it */ +static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) +{ + u16 cport_id = header->pad[0]; + + gb_message_cport_clear(header); + + return cport_id; +} + /* * Returns an opaque cookie value if successful, or a pointer coded * error otherwise. If the caller wishes to cancel the in-flight @@ -275,22 +301,18 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - void *buffer; size_t buffer_size; int retval; struct urb *urb; int bulk_ep_set; - buffer = message->buffer; - buffer_size = sizeof(*message->header) + message->payload_size; - /* * The data actually transferred will include an indication * of where the data should be sent. Do one last check of * the target CPort id before filling it in. */ - if (cport_id == CPORT_ID_BAD) { - pr_err("request to send inbound data buffer\n"); + if (!cport_id_valid(cport_id)) { + pr_err("invalid destination cport 0x%02x\n", cport_id); return ERR_PTR(-EINVAL); } @@ -299,23 +321,22 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, if (!urb) return ERR_PTR(-ENOMEM); - /* - * We (ab)use the operation-message header pad bytes to transfer the - * cport id in order to minimise overhead. - */ - put_unaligned_le16(cport_id, message->header->pad); + /* Pack the cport id into the message header */ + gb_message_cport_pack(message->header, cport_id); + + buffer_size = sizeof(*message->header) + message->payload_size; bulk_ep_set = cport_to_ep(es1, cport_id); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out[bulk_ep_set].endpoint), - buffer, buffer_size, + message->buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); free_urb(es1, urb); - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); return ERR_PTR(retval); } @@ -473,12 +494,16 @@ static void cport_in_callback(struct urb *urb) goto exit; } + /* Extract the CPort id, which is packed in the message header */ header = urb->transfer_buffer; - cport_id = get_unaligned_le16(header->pad); - put_unaligned_le16(0, header->pad); + cport_id = gb_message_cport_unpack(header); - greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, + if (cport_id_valid(cport_id)) + greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); + else + dev_err(dev, "%s: invalid cport id 0x%02x received\n", + __func__, cport_id); exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -494,8 +519,7 @@ static void cport_out_callback(struct urb *urb) struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); - /* Clear the pad bytes used for the cport id */ - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); /* * Tell the submitter that the message send (attempt) is @@ -656,6 +680,9 @@ static int ap_probe(struct usb_interface *interface, u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; + /* We need to fit a CPort ID in one byte of a message header */ + BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); + udev = usb_get_dev(interface_to_usbdev(interface)); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 05eab4c5f9a0..d727dea6847e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -52,14 +52,14 @@ .serial_number = (s), /* XXX I couldn't get my Kconfig file to be noticed for out-of-tree build */ -#ifndef CONFIG_HOST_DEV_CPORT_ID_MAX -#define CONFIG_HOST_DEV_CPORT_ID_MAX 128 -#endif /* !CONFIG_HOST_DEV_CPORT_ID_MAX */ +#ifndef CONFIG_CPORT_ID_MAX +#define CONFIG_CPORT_ID_MAX 128 +#endif /* !CONFIG_CPORT_ID_MAX */ /* Maximum number of CPorts usable by a host device */ /* XXX This should really be determined by the AP module manifest */ -#define HOST_DEV_CPORT_ID_MAX CONFIG_HOST_DEV_CPORT_ID_MAX -#define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */ +#define CPORT_ID_MAX CONFIG_CPORT_ID_MAX +#define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */ /* 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 @@ -196,5 +196,10 @@ static inline int is_gb_connection(const struct device *dev) return dev->type == &greybus_connection_type; } +static inline bool cport_id_valid(u16 cport_id) +{ + return cport_id && cport_id != CPORT_ID_BAD && cport_id <= CPORT_ID_MAX; +} + #endif /* __KERNEL__ */ #endif /* __LINUX_GREYBUS_H */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index e329f3763176..ad55a73b737b 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -224,9 +224,12 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (desc_cport->bundle != bundle_id) continue; + cport_id = le16_to_cpu(desc_cport->id); + if (cport_id > CPORT_ID_MAX) + goto cleanup; + /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; - cport_id = le16_to_cpu(desc_cport->id); if (!gb_connection_create(bundle, cport_id, protocol_id)) goto cleanup;