Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6df1ece2 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "Usb: f_gsi: Add uevent support for connect/disconnect events"

parents 73b6503d 4c5808b5
Loading
Loading
Loading
Loading
+104 −1
Original line number Diff line number Diff line
@@ -63,6 +63,8 @@ static void *ipc_log_ctxt;
	} \
} while (0)

static struct class *gsi_class;

/* Deregister misc device and free instance structures */
static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
@@ -1591,11 +1593,21 @@ static unsigned int gsi_xfer_bitrate(struct usb_gadget *g)
		return 19 * 64 * 1 * 1000 * 8;
}

static void gsi_uevent_work(struct work_struct *w)
{
	struct gsi_ctrl_port *c_port =
			container_of(w, struct gsi_ctrl_port, uevent_work);

	if (c_port->dev)
		kobject_uevent(&c_port->dev->kobj, KOBJ_CHANGE);
}

static int gsi_function_ctrl_port_init(struct f_gsi *gsi)
{
	int ret;
	char *cdev_name = NULL;
	int sz = GSI_CTRL_NAME_LEN;
	struct device *dev;

	INIT_LIST_HEAD(&gsi->c_port.cpkt_req_q);
	INIT_LIST_HEAD(&gsi->c_port.cpkt_resp_q);
@@ -1603,6 +1615,9 @@ static int gsi_function_ctrl_port_init(struct f_gsi *gsi)
	spin_lock_init(&gsi->c_port.lock);

	init_waitqueue_head(&gsi->c_port.read_wq);
	INIT_WORK(&gsi->c_port.uevent_work, gsi_uevent_work);
	gsi->c_port.dev = NULL;
	gsi->c_port.uevent_wq = NULL;

	switch (gsi->prot_id) {
	case USB_PROT_RMNET_IPA:
@@ -1632,6 +1647,10 @@ static int gsi_function_ctrl_port_init(struct f_gsi *gsi)
	else
		strlcat(gsi->c_port.name, cdev_name, sz);

	gsi->c_port.uevent_wq = alloc_workqueue(gsi->c_port.name,
						WQ_UNBOUND | WQ_MEM_RECLAIM |
						WQ_FREEZABLE, 1);

	gsi->c_port.ctrl_device.name = gsi->c_port.name;
	gsi->c_port.ctrl_device.fops = &gsi_ctrl_dev_fops;
	gsi->c_port.ctrl_device.minor = MISC_DYNAMIC_MINOR;
@@ -1643,6 +1662,17 @@ static int gsi_function_ctrl_port_init(struct f_gsi *gsi)
		return ret;
	}

	dev = device_create(gsi_class, NULL, MKDEV(0, MISC_DYNAMIC_MINOR),
			&gsi->c_port, gsi->c_port.name);
	if (IS_ERR(dev)) {
		log_event_err("%s: device_create failed for (%s)\n", __func__,
				gsi->c_port.name);
		misc_deregister(&gsi->c_port.ctrl_device);
		return PTR_ERR(dev);
	}

	gsi->c_port.dev = dev;

	return 0;
}

@@ -2081,6 +2111,10 @@ gsi_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
			gsi->rmnet_dtr_status = line_state;
		log_event_dbg("%s: USB_CDC_REQ_SET_CONTROL_LINE_STATE DTR:%d\n",
						__func__, line_state);
		if (gsi->c_port.uevent_wq)
			queue_work(gsi->c_port.uevent_wq,
					&gsi->c_port.uevent_work);

		gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
		value = 0;
		break;
@@ -2445,6 +2479,9 @@ static int gsi_set_alt(struct usb_function *f, unsigned int intf,
				gsi->prot_id == USB_PROT_MBIM_IPA)
		gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);

	if (gsi->c_port.uevent_wq)
		queue_work(gsi->c_port.uevent_wq, &gsi->c_port.uevent_work);

	return 0;

notify_ep_disable:
@@ -2478,6 +2515,10 @@ static void gsi_disable(struct usb_function *f)
	}

	gsi_ctrl_clear_cpkt_queues(gsi, false);

	if (gsi->c_port.uevent_wq)
		queue_work(gsi->c_port.uevent_wq, &gsi->c_port.uevent_work);

	/* send 0 len pkt to qti/qbi/gps to notify state change */
	gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
	gsi->c_port.notify_req_queued = false;
@@ -3680,11 +3721,27 @@ static int gsi_set_inst_name(struct usb_function_instance *fi,
static void gsi_free_inst(struct usb_function_instance *f)
{
	struct gsi_opts *opts = container_of(f, struct gsi_opts, func_inst);
	struct f_gsi *gsi;

	if (opts) {
		if (opts->gsi && opts->gsi->c_port.ctrl_device.fops)
			misc_deregister(&opts->gsi->c_port.ctrl_device);
		kfree(opts->interf_group);

		if (opts->gsi && opts->gsi->c_port.dev) {
			gsi = opts->gsi;

			dev_set_drvdata(gsi->c_port.dev, NULL);

			if (gsi->c_port.uevent_wq) {
				cancel_work_sync(&gsi->c_port.uevent_work);
				destroy_workqueue(gsi->c_port.uevent_wq);
				gsi->c_port.uevent_wq = NULL;
			}

			device_destroy(gsi_class, gsi->c_port.dev->devt);
			gsi->c_port.dev = NULL;
		}
	}

	kfree(opts);
@@ -3724,9 +3781,46 @@ DECLARE_USB_FUNCTION(gsi, gsi_alloc_inst, gsi_alloc);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("GSI function driver");

static int usb_gsi_uevent(struct device *dev, struct kobj_uevent_env *env)
{
	struct gsi_ctrl_port *c_port = dev_get_drvdata(dev);
	struct f_gsi *gsi;
	char *str = "undefined";

	if (!c_port) {
		dev_dbg(dev, "%s: gsi is not initialized\n", __func__);
		add_uevent_var(env, "STATE=%s", str);
		return 0;
	}

	gsi = c_port_to_gsi(c_port);

	switch (gsi->prot_id) {
	case USB_PROT_RMNET_IPA:
	case USB_PROT_RMNET_ETHER:
		str = gsi->rmnet_dtr_status ? "connected" : "disconnected";
		break;
	case USB_PROT_MBIM_IPA:
	case USB_PROT_DIAG_IPA:
	case USB_PROT_DPL_ETHER:
	case USB_PROT_GPS_CTRL:
		str = atomic_read(&gsi->connected) ?
					"connected" : "disconnected";
		break;
	default:
		return 0;
	}

	add_uevent_var(env, "STATE=%s", str);

	log_event_dbg("%s:STATE=%s\n", c_port->name, str);

	return 0;
}

static int fgsi_init(void)
{
	int i;
	int i, ret;

	ipa_usb_wq = alloc_workqueue("k_ipa_usb",
				WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
@@ -3745,6 +3839,15 @@ static int fgsi_init(void)
	if (!ipc_log_ctxt)
		pr_err("%s: Err allocating ipc_log_ctxt\n", __func__);

	gsi_class = class_create(THIS_MODULE, "gsi_usb");
	if (IS_ERR(gsi_class)) {
		ret = PTR_ERR(gsi_class);
		gsi_class = NULL;
		pr_err("%s: class_create() failed:%d\n", __func__, ret);
		return ret;
	}
	gsi_class->dev_uevent = usb_gsi_uevent;

	usb_gsi_debugfs_init();
	return usb_function_register(&gsiusb_func);
}
+4 −0
Original line number Diff line number Diff line
@@ -218,6 +218,10 @@ struct gsi_ctrl_port {
	unsigned int modem_to_host;
	unsigned int cpkt_drop_cnt;
	unsigned int get_encap_cnt;

	struct device *dev;
	struct work_struct uevent_work;
	struct workqueue_struct *uevent_wq;
};

struct gsi_data_port {