/* * USB Type-C Connector Class * * Copyright (C) 2017, Intel Corporation * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/device.h>#include <linux/module.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/usb/typec.h> struct typec_mode { int index; u32 vdo; char *desc; enum typec_port_type roles; struct typec_altmode *alt_mode; unsigned int active:1; char group_name[6]; struct attribute_group group; struct attribute *attrs[5]; struct device_attribute vdo_attr; struct device_attribute desc_attr; struct device_attribute active_attr; struct device_attribute roles_attr;}; struct typec_altmode { struct device dev; u16 svid; int n_modes; struct typec_mode modes[ALTMODE_MAX_MODES]; const struct attribute_group *mode_groups[ALTMODE_MAX_MODES];}; struct typec_plug { struct device dev; enum typec_plug_index index;}; struct typec_cable { struct device dev; enum typec_plug_type type; struct usb_pd_identity *identity; unsigned int active:1;}; struct typec_partner { struct device dev; unsigned int usb_pd:1; struct usb_pd_identity *identity; enum typec_accessory accessory;}; struct typec_port { unsigned int id; struct device dev; int prefer_role; enum typec_data_role data_role; enum typec_role pwr_role; enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; enum typec_port_type port_type; struct mutex port_type_lock; const struct typec_capability *cap;}; #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev) static const struct device_type typec_partner_dev_type;static const struct device_type typec_cable_dev_type;static const struct device_type typec_plug_dev_type;static const struct device_type typec_port_dev_type; #define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type)#define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type)#define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type)#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) static DEFINE_IDA(typec_index_ida);static struct class *typec_class; /* Common attributes */ static const char * const typec_accessory_modes[] = { [TYPEC_ACCESSORY_NONE] = "none", [TYPEC_ACCESSORY_AUDIO] = "analog_audio", [TYPEC_ACCESSORY_DEBUG] = "debug",}; static struct usb_pd_identity *get_pd_identity(struct device *dev){ if (is_typec_partner(dev)) { struct typec_partner *partner = to_typec_partner(dev); return partner->identity; } else if (is_typec_cable(dev)) { struct typec_cable *cable = to_typec_cable(dev); return cable->identity; } return NULL;} static ssize_t id_header_show(struct device *dev, struct device_attribute *attr, char *buf){ struct usb_pd_identity *id = get_pd_identity(dev); return sprintf(buf, "0x%08x\n", id->id_header);}static DEVICE_ATTR_RO(id_header); static ssize_t cert_stat_show(struct device *dev, struct device_attribute *attr, char *buf){ struct usb_pd_identity *id = get_pd_identity(dev); return sprintf(buf, "0x%08x\n", id->cert_stat);}static DEVICE_ATTR_RO(cert_stat); static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf){ struct usb_pd_identity *id = get_pd_identity(dev); return sprintf(buf, "0x%08x\n", id->product);}static DEVICE_ATTR_RO(product); static struct attribute *usb_pd_id_attrs[] = { &dev_attr_id_header.attr, &dev_attr_cert_stat.attr, &dev_attr_product.attr, NULL}; static const struct attribute_group usb_pd_id_group = { .name = "identity", .attrs = usb_pd_id_attrs,}; static const struct attribute_group *usb_pd_id_groups[] = { &usb_pd_id_group, NULL,}; static void typec_report_identity(struct device *dev){ sysfs_notify(&dev->kobj, "identity", "id_header"); sysfs_notify(&dev->kobj, "identity", "cert_stat"); sysfs_notify(&dev->kobj, "identity", "product");} /* ------------------------------------------------------------------------- *//* Alternate Modes */ /** * typec_altmode_update_active - Report Enter/Exit mode * @alt: Handle to the alternate mode * @mode: Mode index * @active: True when the mode has been entered * * If a partner or cable plug executes Enter/Exit Mode command successfully, the * drivers use this routine to report the updated state of the mode. */void typec_altmode_update_active(struct typec_altmode *alt, int mode, bool active){ struct typec_mode *m = &alt->modes[mode]; char dir[6]; if (m->active == active) return; m->active = active; snprintf(dir, sizeof(dir), "mode%d", mode); sysfs_notify(&alt->dev.kobj, dir, "active"); kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE);}EXPORT_SYMBOL_GPL(typec_altmode_update_active); /** * typec_altmode2port - Alternate Mode to USB Type-C port * @alt: The Alternate Mode * * Returns handle to the port that a cable plug or partner with @alt is * connected to. */struct typec_port *typec_altmode2port(struct typec_altmode *alt){ if (is_typec_plug(alt->dev.parent)) return to_typec_port(alt->dev.parent->parent->parent); if (is_typec_partner(alt->dev.parent)) return to_typec_port(alt->dev.parent->parent); if (is_typec_port(alt->dev.parent)) return to_typec_port(alt->dev.parent); return NULL;}EXPORT_SYMBOL_GPL(typec_altmode2port); static ssize_ttypec_altmode_vdo_show(struct device *dev, struct device_attribute *attr, char *buf){ struct typec_mode *mode = container_of(attr, struct typec_mode, vdo_attr); return sprintf(buf, "0x%08x\n", mode->vdo);} static ssize_ttypec_altmode_desc_show(struct device *dev, struct device_attribute *attr, char *buf){ struct typec_mode *mode = container_of(attr, struct typec_mode, desc_attr); return sprintf(buf, "%s\n", mode->desc ? mode->desc : "");} static ssize_ttypec_altmode_active_show(struct device *dev, struct device_attribute *attr, char *buf){ struct typec_mode *mode = container_of(attr, struct typec_mode, active_attr); return sprintf(buf, "%s\n", mode->active ? "yes" : "no");} static ssize_ttypec_altmode_active_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size){ struct typec_mode *mode = container_of(attr, struct typec_mode, active_attr); struct typec_port *port = typec_altmode2port(mode->alt_mode); bool activate; int ret; if (!port->cap->activate_mode) return -EOPNOTSUPP; ret = kstrtobool(buf, &activate); if (ret) return ret; ret = port->cap->activate_mode(port->cap, mode->index, activate); if (ret) return ret; return size;} static ssize_ttypec_altmode_roles_show(struct device *dev, struct device_attribute *attr, char *buf){ struct typec_mode *mode = container_of(attr, struct typec_mode, roles_attr); ssize_t ret; switch (mode->roles) { case TYPEC_PORT_DFP: ret = sprintf(buf, "source\n"); break; case TYPEC_PORT_UFP: ret = sprintf(buf, "sink\n"); break; case TYPEC_PORT_DRP: default: ret = sprintf(buf, "source sink\n"); break; } return ret;} static void typec_init_modes(struct typec_altmode *alt, const struct typec_mode_desc *desc, bool is_port){ int i; for (i = 0; i < alt->n_modes; i++, desc++) { struct typec_mode *mode = &alt->modes[i]; /* Not considering the human readable description critical */ mode->desc = kstrdup(desc->desc, GFP_KERNEL); if (desc->desc && !mode->desc) dev_err(&alt->dev, "failed to copy mode%d desc\n", i); mode->alt_mode = alt; mode->vdo = desc->vdo; mode->roles = desc->roles; mode->index = desc->index; sprintf(mode->group_name, "mode%d", desc->index); sysfs_attr_init(&mode->vdo_attr.attr); mode->vdo_attr.attr.name = "vdo"; mode->vdo_attr.attr.mode = 0444; mode->vdo_attr.show = typec_altmode_vdo_show; sysfs_attr_init(&mode->desc_attr.attr); mode->desc_attr.attr.name = "description"; mode->desc_attr.attr.mode = 0444; mode->desc_attr.show = typec_altmode_desc_show; sysfs_attr_init(&mode->active_attr.attr); mode->active_attr.attr.name = "active"; mode->active_attr.attr.mode = 0644; mode->active_attr.show = typec_altmode_active_show; mode->active_attr.store = typec_altmode_active_store; mode->attrs[0] = &mode->vdo_attr.attr; mode->attrs[1] = &mode->desc_attr.attr; mode->attrs[2] = &mode->active_attr.attr; /* With ports, list the roles that the mode is supported with */ if (is_port) { sysfs_attr_init(&mode->roles_attr.attr); mode->roles_attr.attr.name = "supported_roles"; mode->roles_attr.attr.mode = 0444; mode->roles_attr.show = typec_altmode_roles_show; mode->attrs[3] = &mode->roles_attr.attr; } mode->group.attrs = mode->attrs; mode->group.name = mode->group_name; alt->mode_groups[i] = &mode->group; }} static ssize_t svid_show(struct device *dev, struct device_attribute *attr, char *buf){ struct typec_altmode *alt = to_altmode(dev); return sprintf(buf, "%04x\n", alt->svid);}static DEVICE_ATTR_RO(svid); static struct attribute *typec_altmode_attrs[] = { &dev_attr_svid.attr, NULL};ATTRIBUTE_GROUPS(typec_altmode); static void typec_altmode_release(struct device *dev){ struct typec_altmode *alt = to_altmode(dev); int i; for (i = 0; i < alt->n_modes; i++) kfree(alt->modes[i].desc); kfree(alt);} static const struct device_type typec_altmode_dev_type = { .name = "typec_alternate_mode", .groups = typec_altmode_groups, .release = typec_altmode_release,}; static struct typec_altmode *typec_register_altmode(struct device *parent, const struct typec_altmode_desc *desc){ struct typec_altmode *alt; int ret; alt = kzalloc(sizeof(*alt), GFP_KERNEL); if (!alt) return NULL; alt->svid = desc->svid; alt->n_modes = desc->n_modes; typec_init_modes(alt, desc->modes, is_typec_port(parent)); alt->dev.parent = parent; alt->dev.groups = alt->mode_groups; alt->dev.type = &typec_altmode_dev_type; dev_set_name(&alt->dev, "svid-%04x", alt->svid); ret = device_register(&alt->dev); if (ret) { dev_err(parent, "failed to register alternate mode (%d)\n", ret); put_device(&alt->dev); return NULL; } return alt;} /** * typec_unregister_altmode - Unregister Alternate Mode * @alt: The alternate mode to be unregistered * * Unregister device created with typec_partner_register_altmode(), * typec_plug_register_altmode() or typec_port_register_altmode(). */void typec_unregister_altmode(struct typec_altmode *alt){ if (alt) device_unregister(&alt->dev);}EXPORT_SYMBOL_GPL(typec_unregister_altmode); /* ------------------------------------------------------------------------- *//* Type-C Partners */ static ssize_t accessory_mode_show(struct device *dev, struct device_attribute *attr, char *buf){ struct typec_partner *p = to_typec_partner(dev); return sprintf(buf, "%s\n", typec_accessory_modes[p->accessory]);}static DEVICE_ATTR_RO(accessory_mode); static ssize_t supports_usb_power_delivery_show(struct device *dev, struct device_attribute *attr, char *buf){ struct typec_partner *p = to_typec_partner(dev); return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no");}static DEVICE_ATTR_RO(supports_usb_power_delivery); static struct attribute *typec_partner_attrs[] = { &dev_attr_accessory_mode.attr, &dev_attr_supports_usb_power_delivery.attr, NULL};ATTRIBUTE_GROUPS(typec_partner); static void typec_partner_release(struct device *dev){ struct typec_partner *partner = to_typec_partner(dev); kfree(partner);} static const struct device_type typec_partner_dev_type = { .name = "typec_partner", .groups = typec_partner_groups, .release = typec_partner_release,}; /** * typec_partner_set_identity - Report result from Discover Identity command * @partner: The partner updated identity values * * This routine is used to report that the result of Discover Identity USB power * delivery command has become available. */int typec_partner_set_identity(struct typec_partner *partner){ if (!partner->identity) return -EINVAL; typec_report_identity(&partner->dev); return 0;}EXPORT_SYMBOL_GPL(typec_partner_set_identity); /** * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode * @partner: USB Type-C Partner that supports the alternate mode * @desc: Description of the alternate mode * * This routine is used to register each alternate mode individually that * @partner has listed in response to Discover SVIDs command. The modes for a * SVID listed in response to Discover Modes command need to be listed in an * array in @desc. * * Returns handle to the alternate mode on success or NULL on failure. */struct typec_altmode *Lines 1–500 of 1368. Load more
/* * USB Type-C Connector Class * * Copyright (C) 2017, Intel Corporation * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/device.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/usb/typec.h> struct typec_mode { int index; u32 vdo; char *desc; enum typec_port_type roles; struct typec_altmode *alt_mode; unsigned int active:1; char group_name[6]; struct attribute_group group; struct attribute *attrs[5]; struct device_attribute vdo_attr; struct device_attribute desc_attr; struct device_attribute active_attr; struct device_attribute roles_attr; }; struct typec_altmode { struct device dev; u16 svid; int n_modes; struct typec_mode modes[ALTMODE_MAX_MODES]; const struct attribute_group *mode_groups[ALTMODE_MAX_MODES]; }; struct typec_plug { struct device dev; enum typec_plug_index index; }; struct typec_cable { struct device dev; enum typec_plug_type type; struct usb_pd_identity *identity; unsigned int active:1; }; struct typec_partner { struct device dev; unsigned int usb_pd:1; struct usb_pd_identity *identity; enum typec_accessory accessory; }; struct typec_port { unsigned int id; struct device dev; int prefer_role; enum typec_data_role data_role; enum typec_role pwr_role; enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; enum typec_port_type port_type; struct mutex port_type_lock; const struct typec_capability *cap; }; #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) #define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) #define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) #define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) #define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev) static const struct device_type typec_partner_dev_type; static const struct device_type typec_cable_dev_type; static const struct device_type typec_plug_dev_type; static const struct device_type typec_port_dev_type; #define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type) #define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type) #define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type) #define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) static DEFINE_IDA(typec_index_ida); static struct class *typec_class; /* Common attributes */ static const char * const typec_accessory_modes[] = { [TYPEC_ACCESSORY_NONE] = "none", [TYPEC_ACCESSORY_AUDIO] = "analog_audio", [TYPEC_ACCESSORY_DEBUG] = "debug", }; static struct usb_pd_identity *get_pd_identity(struct device *dev) { if (is_typec_partner(dev)) { struct typec_partner *partner = to_typec_partner(dev); return partner->identity; } else if (is_typec_cable(dev)) { struct typec_cable *cable = to_typec_cable(dev); return cable->identity; } return NULL; } static ssize_t id_header_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_pd_identity *id = get_pd_identity(dev); return sprintf(buf, "0x%08x\n", id->id_header); } static DEVICE_ATTR_RO(id_header); static ssize_t cert_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_pd_identity *id = get_pd_identity(dev); return sprintf(buf, "0x%08x\n", id->cert_stat); } static DEVICE_ATTR_RO(cert_stat); static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_pd_identity *id = get_pd_identity(dev); return sprintf(buf, "0x%08x\n", id->product); } static DEVICE_ATTR_RO(product); static struct attribute *usb_pd_id_attrs[] = { &dev_attr_id_header.attr, &dev_attr_cert_stat.attr, &dev_attr_product.attr, NULL }; static const struct attribute_group usb_pd_id_group = { .name = "identity", .attrs = usb_pd_id_attrs, }; static const struct attribute_group *usb_pd_id_groups[] = { &usb_pd_id_group, NULL, }; static void typec_report_identity(struct device *dev) { sysfs_notify(&dev->kobj, "identity", "id_header"); sysfs_notify(&dev->kobj, "identity", "cert_stat"); sysfs_notify(&dev->kobj, "identity", "product"); } /* ------------------------------------------------------------------------- */ /* Alternate Modes */ /** * typec_altmode_update_active - Report Enter/Exit mode * @alt: Handle to the alternate mode * @mode: Mode index * @active: True when the mode has been entered * * If a partner or cable plug executes Enter/Exit Mode command successfully, the * drivers use this routine to report the updated state of the mode. */ void typec_altmode_update_active(struct typec_altmode *alt, int mode, bool active) { struct typec_mode *m = &alt->modes[mode]; char dir[6]; if (m->active == active) return; m->active = active; snprintf(dir, sizeof(dir), "mode%d", mode); sysfs_notify(&alt->dev.kobj, dir, "active"); kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE); } EXPORT_SYMBOL_GPL(typec_altmode_update_active); /** * typec_altmode2port - Alternate Mode to USB Type-C port * @alt: The Alternate Mode * * Returns handle to the port that a cable plug or partner with @alt is * connected to. */ struct typec_port *typec_altmode2port(struct typec_altmode *alt) { if (is_typec_plug(alt->dev.parent)) return to_typec_port(alt->dev.parent->parent->parent); if (is_typec_partner(alt->dev.parent)) return to_typec_port(alt->dev.parent->parent); if (is_typec_port(alt->dev.parent)) return to_typec_port(alt->dev.parent); return NULL; } EXPORT_SYMBOL_GPL(typec_altmode2port); static ssize_t typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_mode *mode = container_of(attr, struct typec_mode, vdo_attr); return sprintf(buf, "0x%08x\n", mode->vdo); } static ssize_t typec_altmode_desc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_mode *mode = container_of(attr, struct typec_mode, desc_attr); return sprintf(buf, "%s\n", mode->desc ? mode->desc : ""); } static ssize_t typec_altmode_active_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_mode *mode = container_of(attr, struct typec_mode, active_attr); return sprintf(buf, "%s\n", mode->active ? "yes" : "no"); } static ssize_t typec_altmode_active_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct typec_mode *mode = container_of(attr, struct typec_mode, active_attr); struct typec_port *port = typec_altmode2port(mode->alt_mode); bool activate; int ret; if (!port->cap->activate_mode) return -EOPNOTSUPP; ret = kstrtobool(buf, &activate); if (ret) return ret; ret = port->cap->activate_mode(port->cap, mode->index, activate); if (ret) return ret; return size; } static ssize_t typec_altmode_roles_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_mode *mode = container_of(attr, struct typec_mode, roles_attr); ssize_t ret; switch (mode->roles) { case TYPEC_PORT_DFP: ret = sprintf(buf, "source\n"); break; case TYPEC_PORT_UFP: ret = sprintf(buf, "sink\n"); break; case TYPEC_PORT_DRP: default: ret = sprintf(buf, "source sink\n"); break; } return ret; } static void typec_init_modes(struct typec_altmode *alt, const struct typec_mode_desc *desc, bool is_port) { int i; for (i = 0; i < alt->n_modes; i++, desc++) { struct typec_mode *mode = &alt->modes[i]; /* Not considering the human readable description critical */ mode->desc = kstrdup(desc->desc, GFP_KERNEL); if (desc->desc && !mode->desc) dev_err(&alt->dev, "failed to copy mode%d desc\n", i); mode->alt_mode = alt; mode->vdo = desc->vdo; mode->roles = desc->roles; mode->index = desc->index; sprintf(mode->group_name, "mode%d", desc->index); sysfs_attr_init(&mode->vdo_attr.attr); mode->vdo_attr.attr.name = "vdo"; mode->vdo_attr.attr.mode = 0444; mode->vdo_attr.show = typec_altmode_vdo_show; sysfs_attr_init(&mode->desc_attr.attr); mode->desc_attr.attr.name = "description"; mode->desc_attr.attr.mode = 0444; mode->desc_attr.show = typec_altmode_desc_show; sysfs_attr_init(&mode->active_attr.attr); mode->active_attr.attr.name = "active"; mode->active_attr.attr.mode = 0644; mode->active_attr.show = typec_altmode_active_show; mode->active_attr.store = typec_altmode_active_store; mode->attrs[0] = &mode->vdo_attr.attr; mode->attrs[1] = &mode->desc_attr.attr; mode->attrs[2] = &mode->active_attr.attr; /* With ports, list the roles that the mode is supported with */ if (is_port) { sysfs_attr_init(&mode->roles_attr.attr); mode->roles_attr.attr.name = "supported_roles"; mode->roles_attr.attr.mode = 0444; mode->roles_attr.show = typec_altmode_roles_show; mode->attrs[3] = &mode->roles_attr.attr; } mode->group.attrs = mode->attrs; mode->group.name = mode->group_name; alt->mode_groups[i] = &mode->group; } } static ssize_t svid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_altmode *alt = to_altmode(dev); return sprintf(buf, "%04x\n", alt->svid); } static DEVICE_ATTR_RO(svid); static struct attribute *typec_altmode_attrs[] = { &dev_attr_svid.attr, NULL }; ATTRIBUTE_GROUPS(typec_altmode); static void typec_altmode_release(struct device *dev) { struct typec_altmode *alt = to_altmode(dev); int i; for (i = 0; i < alt->n_modes; i++) kfree(alt->modes[i].desc); kfree(alt); } static const struct device_type typec_altmode_dev_type = { .name = "typec_alternate_mode", .groups = typec_altmode_groups, .release = typec_altmode_release, }; static struct typec_altmode * typec_register_altmode(struct device *parent, const struct typec_altmode_desc *desc) { struct typec_altmode *alt; int ret; alt = kzalloc(sizeof(*alt), GFP_KERNEL); if (!alt) return NULL; alt->svid = desc->svid; alt->n_modes = desc->n_modes; typec_init_modes(alt, desc->modes, is_typec_port(parent)); alt->dev.parent = parent; alt->dev.groups = alt->mode_groups; alt->dev.type = &typec_altmode_dev_type; dev_set_name(&alt->dev, "svid-%04x", alt->svid); ret = device_register(&alt->dev); if (ret) { dev_err(parent, "failed to register alternate mode (%d)\n", ret); put_device(&alt->dev); return NULL; } return alt; } /** * typec_unregister_altmode - Unregister Alternate Mode * @alt: The alternate mode to be unregistered * * Unregister device created with typec_partner_register_altmode(), * typec_plug_register_altmode() or typec_port_register_altmode(). */ void typec_unregister_altmode(struct typec_altmode *alt) { if (alt) device_unregister(&alt->dev); } EXPORT_SYMBOL_GPL(typec_unregister_altmode); /* ------------------------------------------------------------------------- */ /* Type-C Partners */ static ssize_t accessory_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_partner *p = to_typec_partner(dev); return sprintf(buf, "%s\n", typec_accessory_modes[p->accessory]); } static DEVICE_ATTR_RO(accessory_mode); static ssize_t supports_usb_power_delivery_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_partner *p = to_typec_partner(dev); return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no"); } static DEVICE_ATTR_RO(supports_usb_power_delivery); static struct attribute *typec_partner_attrs[] = { &dev_attr_accessory_mode.attr, &dev_attr_supports_usb_power_delivery.attr, NULL }; ATTRIBUTE_GROUPS(typec_partner); static void typec_partner_release(struct device *dev) { struct typec_partner *partner = to_typec_partner(dev); kfree(partner); } static const struct device_type typec_partner_dev_type = { .name = "typec_partner", .groups = typec_partner_groups, .release = typec_partner_release, }; /** * typec_partner_set_identity - Report result from Discover Identity command * @partner: The partner updated identity values * * This routine is used to report that the result of Discover Identity USB power * delivery command has become available. */ int typec_partner_set_identity(struct typec_partner *partner) { if (!partner->identity) return -EINVAL; typec_report_identity(&partner->dev); return 0; } EXPORT_SYMBOL_GPL(typec_partner_set_identity); /** * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode * @partner: USB Type-C Partner that supports the alternate mode * @desc: Description of the alternate mode * * This routine is used to register each alternate mode individually that * @partner has listed in response to Discover SVIDs command. The modes for a * SVID listed in response to Discover Modes command need to be listed in an * array in @desc. * * Returns handle to the alternate mode on success or NULL on failure. */ struct typec_altmode *