cregit-Linux how code gets into the kernel

Release 4.17 drivers/usb/common/roles.c

// SPDX-License-Identifier: GPL-2.0
/*
 * USB Role Switch Support
 *
 * Copyright (C) 2018 Intel Corporation
 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
 *         Hans de Goede <hdegoede@redhat.com>
 */

#include <linux/usb/role.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>


static struct class *role_class;


struct usb_role_switch {
	
struct device dev;
	
struct mutex lock; /* device lock*/
	
enum usb_role role;

	/* From descriptor */
	
struct device *usb2_port;
	
struct device *usb3_port;
	
struct device *udc;
	
usb_role_switch_set_t set;
	
usb_role_switch_get_t get;
	
bool allow_userspace_control;
};


#define to_role_switch(d)	container_of(d, struct usb_role_switch, dev)

/**
 * usb_role_switch_set_role - Set USB role for a switch
 * @sw: USB role switch
 * @role: USB role to be switched to
 *
 * Set USB role @role for @sw.
 */

int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) { int ret; if (IS_ERR_OR_NULL(sw)) return 0; mutex_lock(&sw->lock); ret = sw->set(sw->dev.parent, role); if (!ret) sw->role = role; mutex_unlock(&sw->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus72100.00%1100.00%
Total72100.00%1100.00%

EXPORT_SYMBOL_GPL(usb_role_switch_set_role); /** * usb_role_switch_get_role - Get the USB role for a switch * @sw: USB role switch * * Depending on the role-switch-driver this function returns either a cached * value of the last set role, or reads back the actual value from the hardware. */
enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) { enum usb_role role; if (IS_ERR_OR_NULL(sw)) return USB_ROLE_NONE; mutex_lock(&sw->lock); if (sw->get) role = sw->get(sw->dev.parent); else role = sw->role; mutex_unlock(&sw->lock); return role; }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus70100.00%1100.00%
Total70100.00%1100.00%

EXPORT_SYMBOL_GPL(usb_role_switch_get_role);
static int __switch_match(struct device *dev, const void *name) { return !strcmp((const char *)name, dev_name(dev)); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus33100.00%1100.00%
Total33100.00%1100.00%


static void *usb_role_switch_match(struct device_connection *con, int ep, void *data) { struct device *dev; dev = class_find_device(role_class, NULL, con->endpoint[ep], __switch_match); return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus56100.00%1100.00%
Total56100.00%1100.00%

/** * usb_role_switch_get - Find USB role switch linked with the caller * @dev: The caller device * * Finds and returns role switch linked with @dev. The reference count for the * found switch is incremented. */
struct usb_role_switch *usb_role_switch_get(struct device *dev) { return device_connection_find_match(dev, "usb-role-switch", NULL, usb_role_switch_match); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus24100.00%1100.00%
Total24100.00%1100.00%

EXPORT_SYMBOL_GPL(usb_role_switch_get); /** * usb_role_switch_put - Release handle to a switch * @sw: USB Role Switch * * Decrement reference count for @sw. */
void usb_role_switch_put(struct usb_role_switch *sw) { if (!IS_ERR_OR_NULL(sw)) put_device(&sw->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus26100.00%1100.00%
Total26100.00%1100.00%

EXPORT_SYMBOL_GPL(usb_role_switch_put);
static umode_t usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = container_of(kobj, typeof(*dev), kobj); struct usb_role_switch *sw = to_role_switch(dev); if (sw->allow_userspace_control) return attr->mode; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus61100.00%1100.00%
Total61100.00%1100.00%

static const char * const usb_roles[] = { [USB_ROLE_NONE] = "none", [USB_ROLE_HOST] = "host", [USB_ROLE_DEVICE] = "device", };
static ssize_t role_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_role_switch *sw = to_role_switch(dev); enum usb_role role = usb_role_switch_get_role(sw); return sprintf(buf, "%s\n", usb_roles[role]); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus52100.00%1100.00%
Total52100.00%1100.00%


static ssize_t role_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct usb_role_switch *sw = to_role_switch(dev); int ret; ret = sysfs_match_string(usb_roles, buf); if (ret < 0) { bool res; /* Extra check if the user wants to disable the switch */ ret = kstrtobool(buf, &res); if (ret || res) return -EINVAL; } ret = usb_role_switch_set_role(sw, ret); if (ret) return ret; return size; }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus97100.00%1100.00%
Total97100.00%1100.00%

static DEVICE_ATTR_RW(role); static struct attribute *usb_role_switch_attrs[] = { &dev_attr_role.attr, NULL, }; static const struct attribute_group usb_role_switch_group = { .is_visible = usb_role_switch_is_visible, .attrs = usb_role_switch_attrs, }; static const struct attribute_group *usb_role_switch_groups[] = { &usb_role_switch_group, NULL, };
static int usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) { int ret; ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev)); if (ret) dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n"); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus47100.00%1100.00%
Total47100.00%1100.00%


static void usb_role_switch_release(struct device *dev) { struct usb_role_switch *sw = to_role_switch(dev); kfree(sw); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus26100.00%1100.00%
Total26100.00%1100.00%

static const struct device_type usb_role_dev_type = { .name = "usb_role_switch", .groups = usb_role_switch_groups, .uevent = usb_role_switch_uevent, .release = usb_role_switch_release, }; /** * usb_role_switch_register - Register USB Role Switch * @parent: Parent device for the switch * @desc: Description of the switch * * USB Role Switch is a device capable or choosing the role for USB connector. * On platforms where the USB controller is dual-role capable, the controller * driver will need to register the switch. On platforms where the USB host and * USB device controllers behind the connector are separate, there will be a * mux, and the driver for that mux will need to register the switch. * * Returns handle to a new role switch or ERR_PTR. The content of @desc is * copied. */
struct usb_role_switch * usb_role_switch_register(struct device *parent, const struct usb_role_switch_desc *desc) { struct usb_role_switch *sw; int ret; if (!desc || !desc->set) return ERR_PTR(-EINVAL); sw = kzalloc(sizeof(*sw), GFP_KERNEL); if (!sw) return ERR_PTR(-ENOMEM); mutex_init(&sw->lock); sw->allow_userspace_control = desc->allow_userspace_control; sw->usb2_port = desc->usb2_port; sw->usb3_port = desc->usb3_port; sw->udc = desc->udc; sw->set = desc->set; sw->get = desc->get; sw->dev.parent = parent; sw->dev.class = role_class; sw->dev.type = &usb_role_dev_type; dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); ret = device_register(&sw->dev); if (ret) { put_device(&sw->dev); return ERR_PTR(ret); } /* TODO: Symlinks for the host port and the device controller. */ return sw; }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus198100.00%1100.00%
Total198100.00%1100.00%

EXPORT_SYMBOL_GPL(usb_role_switch_register); /** * usb_role_switch_unregister - Unregsiter USB Role Switch * @sw: USB Role Switch * * Unregister switch that was registered with usb_role_switch_register(). */
void usb_role_switch_unregister(struct usb_role_switch *sw) { if (!IS_ERR_OR_NULL(sw)) device_unregister(&sw->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus26100.00%1100.00%
Total26100.00%1100.00%

EXPORT_SYMBOL_GPL(usb_role_switch_unregister);
static int __init usb_roles_init(void) { role_class = class_create(THIS_MODULE, "usb_role"); return PTR_ERR_OR_ZERO(role_class); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus24100.00%1100.00%
Total24100.00%1100.00%

subsys_initcall(usb_roles_init);
static void __exit usb_roles_exit(void) { class_destroy(role_class); }

Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus14100.00%1100.00%
Total14100.00%1100.00%

module_exit(usb_roles_exit); MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("USB Role Class");

Overall Contributors

PersonTokensPropCommitsCommitProp
Heikki Krogerus1076100.00%1100.00%
Total1076100.00%1100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.