コード例 #1
0
ファイル: f_acm.c プロジェクト: tinocyngn/sofia-kernel
static void acm_free_func(struct usb_function *f)
{
	struct f_acm		*acm = func_to_acm(f);

	kobject_del(&acm->kobj);
	kobject_put(&acm->kobj);
	kfree(acm);
}
コード例 #2
0
static void acm_free_func(struct usb_function *f)
{
	struct f_acm *acm = func_to_acm(f);

	D("+\n");
	kfree(acm);
	D("-\n");
}
コード例 #3
0
ファイル: f_acm.c プロジェクト: tinocyngn/sofia-kernel
static void acm_disable(struct usb_function *f)
{
	struct f_acm	*acm = func_to_acm(f);
	struct usb_composite_dev *cdev = f->config->cdev;

	DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
	gserial_disconnect(&acm->port);
	usb_ep_disable(acm->notify);
	acm->notify->driver_data = NULL;
}
コード例 #4
0
static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
{
	struct f_acm        *acm = func_to_acm(f);
	D("+\n");
	acm_string_defs[0].id = 0;
	usb_free_all_descriptors(f);
	if (acm->notify_req)
		gs_acm_cdev_free_req(acm->notify, acm->notify_req);
	D("-\n");
}
コード例 #5
0
ファイル: f_acm.c プロジェクト: tinocyngn/sofia-kernel
static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
	struct f_acm		*acm = func_to_acm(f);
	struct usb_composite_dev *cdev = f->config->cdev;

	/* we know alt == 0, so this is an activation or a reset */

	if (intf == acm->ctrl_id) {
		if (acm->notify->driver_data) {
			VDBG(cdev, "reset acm control interface %d\n", intf);
			usb_ep_disable(acm->notify);
		}

		if (!acm->notify->desc)
			if (config_ep_by_speed(cdev->gadget, f, acm->notify))
				return -EINVAL;

		usb_ep_enable(acm->notify);
		acm->notify->driver_data = acm;

	} else if (intf == acm->data_id) {
		if (acm->port.in->driver_data) {
			DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
			gserial_disconnect(&acm->port);
		}
		if (!acm->port.in->desc || !acm->port.out->desc) {
			DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
			if (config_ep_by_speed(cdev->gadget, f,
					       acm->port.in) ||
			    config_ep_by_speed(cdev->gadget, f,
					       acm->port.out)) {
				acm->port.in->desc = NULL;
				acm->port.out->desc = NULL;
				return -EINVAL;
			}
		}
		gserial_connect(&acm->port, acm->port_num);

	} else
		return -EINVAL;

	return 0;
}
コード例 #6
0
static void acm_disable(struct usb_function *f)
{
	struct f_acm    *acm = func_to_acm(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	D("+\n");

	DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);

	gacm_cdev_disconnect(&acm->port);

	if (acm->notify) {
		usb_ep_disable(acm->notify);
		acm->notify->driver_data = NULL;
	}

	bsp_usb_set_enum_stat(acm->data_id, 0);

	D("-\n");
}
コード例 #7
0
ファイル: f_acm.c プロジェクト: tinocyngn/sofia-kernel
/* ACM function driver setup/binding */
static int
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
	struct usb_composite_dev *cdev = c->cdev;
	struct f_acm		*acm = func_to_acm(f);
	struct usb_string	*us;
	int			status;
	struct usb_ep		*ep;

	acm_string_defs[ACM_CTRL_IDX].s = acm->ctrl_string_buf;
	acm_string_defs[ACM_DATA_IDX].s = acm->data_string_buf;
	acm_string_defs[ACM_IAD_IDX].s = acm->iad_string_buf;

	/* maybe allocate device-global string IDs, and patch descriptors */
	us = usb_gstrings_attach(cdev, acm_strings,
			ARRAY_SIZE(acm_string_defs));
	if (IS_ERR(us))
		return PTR_ERR(us);

	acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
	acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
	acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;

	acm_iad_descriptor.bFunctionProtocol = acm->iad_proto;
	acm_control_interface_desc.bInterfaceProtocol = acm->ctrl_intf_proto;

	/* allocate instance-specific interface IDs, and patch descriptors */
	status = usb_interface_id(c, f);
	if (status < 0)
		goto fail;
	acm->ctrl_id = status;
	acm_iad_descriptor.bFirstInterface = status;

	acm_control_interface_desc.bInterfaceNumber = status;
	acm_union_desc .bMasterInterface0 = status;

	status = usb_interface_id(c, f);
	if (status < 0)
		goto fail;
	acm->data_id = status;

	acm_data_interface_desc.bInterfaceNumber = status;
	acm_union_desc.bSlaveInterface0 = status;
	acm_call_mgmt_descriptor.bDataInterface = status;

	status = -ENODEV;

	/* allocate instance-specific endpoints */
	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
	if (!ep)
		goto fail;
	acm->port.in = ep;
	ep->driver_data = cdev;	/* claim */

	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
	if (!ep)
		goto fail;
	acm->port.out = ep;
	ep->driver_data = cdev;	/* claim */

	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
	if (!ep)
		goto fail;
	acm->notify = ep;
	ep->driver_data = cdev;	/* claim */

	/* allocate notification */
	acm->notify_req = gs_alloc_req(ep,
			sizeof(struct usb_cdc_notification) + 2,
			GFP_KERNEL);
	if (!acm->notify_req)
		goto fail;

	acm->notify_req->complete = acm_cdc_notify_complete;
	acm->notify_req->context = acm;

	/* support all relevant hardware speeds... we expect that when
	 * hardware is dual speed, all bulk-capable endpoints work at
	 * both speeds
	 */
	acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
	acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
	acm_hs_notify_desc.bEndpointAddress =
		acm_fs_notify_desc.bEndpointAddress;

	acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
	acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;

	status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
			acm_ss_function);
	if (status)
		goto fail;

	DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
			acm->port_num,
			gadget_is_superspeed(c->cdev->gadget) ? "super" :
			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
			acm->port.in->name, acm->port.out->name,
			acm->notify->name);
	return 0;

fail:
	if (acm->notify_req) {
		gs_free_req(acm->notify, acm->notify_req);
		acm->notify_req = NULL;
	}

	/* we might as well release our claims on endpoints */
	if (acm->notify)
		acm->notify->driver_data = NULL;
	if (acm->port.out)
		acm->port.out->driver_data = NULL;
	if (acm->port.in)
		acm->port.in->driver_data = NULL;

	ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);

	return status;
}
コード例 #8
0
ファイル: f_acm.c プロジェクト: tinocyngn/sofia-kernel
static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
	struct f_acm		*acm = func_to_acm(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	struct usb_request	*req = cdev->req;
	int			value = -EOPNOTSUPP;
	u16			w_index = le16_to_cpu(ctrl->wIndex);
	u16			w_value = le16_to_cpu(ctrl->wValue);
	u16			w_length = le16_to_cpu(ctrl->wLength);

	/* composite driver infrastructure handles everything except
	 * CDC class messages; interface activation uses set_alt().
	 *
	 * Note CDC spec table 4 lists the ACM request profile.  It requires
	 * encapsulated command support ... we don't handle any, and respond
	 * to them by stalling.  Options include get/set/clear comm features
	 * (not that useful) and SEND_BREAK.
	 */
	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {

	/* SET_LINE_CODING ... just read and save what the host sends */
	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
			| USB_CDC_REQ_SET_LINE_CODING:
		if (w_length != sizeof(struct usb_cdc_line_coding)
				|| w_index != acm->ctrl_id)
			goto invalid;

		value = w_length;
		cdev->gadget->ep0->driver_data = acm;
		req->complete = acm_complete_set_line_coding;
		break;

	/* GET_LINE_CODING ... return what host sent, or initial value */
	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
			| USB_CDC_REQ_GET_LINE_CODING:
		if (w_index != acm->ctrl_id)
			goto invalid;

		value = min_t(unsigned, w_length,
				sizeof(struct usb_cdc_line_coding));
		memcpy(req->buf, &acm->port_line_coding, value);
		break;

	/* SET_CONTROL_LINE_STATE ... save what the host sent */
	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
		if (w_index != acm->ctrl_id)
			goto invalid;

		value = 0;

		/* FIXME we should not allow data to flow until the
		 * host sets the ACM_CTRL_DTR bit; and when it clears
		 * that bit, we should return to that no-flow state.
		 */
		acm->port_handshake_bits = w_value;
		break;

	default:
invalid:
		VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
			ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, w_length);
	}

	/* respond with data transfer or status phase? */
	if (value >= 0) {
		DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
			acm->port_num, ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, w_length);
		req->zero = 0;
		req->length = value;
		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
		if (value < 0)
			ERROR(cdev, "acm response on ttyGS%d, err %d\n",
					acm->port_num, value);
	}

	/* device either stalls (value < 0) or reports success */
	return value;
}
コード例 #9
0
/* ACM function driver setup/binding */
static int
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
	struct usb_composite_dev *cdev = c->cdev;
	struct f_acm        *acm = func_to_acm(f);
	struct usb_string   *us;
	int         status;
	struct usb_ep       *ep;

	D("+\n");


	/* REVISIT might want instance-specific strings to help
	 * distinguish instances ...
	 */

	/* maybe allocate device-global string IDs, and patch descriptors */
	us = usb_gstrings_attach(cdev, acm_strings,
				ARRAY_SIZE(acm_string_defs));
	if (IS_ERR(us))
		return PTR_ERR(us);
	acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
	acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
	acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;

	/* allocate instance-specific interface IDs, and patch descriptors */
	status = usb_interface_id(c, f);
	if (status < 0)
		goto fail;

	D("interface id: %d\n", status);

	if (g_acm_is_single_interface) {
		D("single interface\n");
		acm->ctrl_id = acm->data_id = status;
		acm_single_interface_desc.bInterfaceNumber = status;
		acm_call_mgmt_descriptor.bDataInterface = status;
	} else {
		acm->ctrl_id = (u8)status;
		acm_iad_descriptor.bFirstInterface = status;

		acm_control_interface_desc.bInterfaceNumber = status;
		acm_union_desc .bMasterInterface0 = status;

		status = usb_interface_id(c, f);
		if (status < 0)
			goto fail;
		acm->data_id = status;

		acm_data_interface_desc.bInterfaceNumber = status;
		acm_union_desc.bSlaveInterface0 = status;
		acm_call_mgmt_descriptor.bDataInterface = status;
	}

	bsp_usb_add_setup_dev((unsigned)acm->data_id);

	status = -ENODEV;

	/* allocate instance-specific endpoints */
	D("to ep autoconfig\n");
	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
	if (!ep)
		goto fail;
	acm->port.in = ep;
	ep->driver_data = cdev; /* claim */

	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
	if (!ep)
		goto fail;
	acm->port.out = ep;
	ep->driver_data = cdev; /* claim */

	if (acm->support_notify) {
		ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
		if (!ep)
			goto fail;
		acm->notify = ep;
		ep->driver_data = cdev; /* claim */

		/* allocate notification */
		acm->notify_req = gs_acm_cdev_alloc_req(ep,
					sizeof(struct usb_cdc_notification) + 2,
					GFP_KERNEL);
		if (!acm->notify_req)
			goto fail;

		acm->notify_req->complete = acm_cdc_notify_complete;
		acm->notify_req->context = acm;
	} else {
		acm->notify = NULL;
		acm->notify_req = NULL;
	}

	/* support all relevant hardware speeds... we expect that when
	 * hardware is dual speed, all bulk-capable endpoints work at
	 * both speeds
	 */
	D("do desc\n");
	acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
	acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;

	if (acm->support_notify)
		acm_hs_notify_desc.bEndpointAddress =
			acm_fs_notify_desc.bEndpointAddress;

	acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
	acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;

	D("to assign desc\n");
	acm_set_config_vendor(acm);

	status = usb_assign_descriptors(f, acm_fs_cur_function, acm_hs_cur_function,
				acm_ss_cur_function);
	if (status)
		goto fail;

	DBG(cdev, "acm_cdev%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
		acm->port_num,
		gadget_is_superspeed(c->cdev->gadget) ? "super" :
		gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
		acm->port.in->name, acm->port.out->name,
		acm->notify ? acm->notify->name : "null");

	printk(KERN_INFO "acm_cdev%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
		acm->port_num,
		gadget_is_superspeed(c->cdev->gadget) ? "super" :
		gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
		acm->port.in->name, acm->port.out->name,
		acm->notify ? acm->notify->name : "null");

	return 0;

fail:
	if (acm->notify_req)
		gs_acm_cdev_free_req(acm->notify, acm->notify_req);

	/* we might as well release our claims on endpoints */
	if (acm->notify)
		acm->notify->driver_data = NULL;
	if (acm->port.out)
		acm->port.out->driver_data = NULL;
	if (acm->port.in)
		acm->port.in->driver_data = NULL;

	ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);

	D("-\n");
	return status;
}
コード例 #10
0
static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
	struct f_acm        *acm = func_to_acm(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	bool is_setting = 0;
	int ret;

	D("+\n");
	/* we know alt == 0, so this is an activation or a reset */

	/* if it is single interface, intf, acm->ctrl_id and acm->data_id
	 * are the same, so we can setting data and notify interface in the same time.
	 *
	 * if it is multi interface, acm->ctrl_id and acm->data_id are different,
	 * so the setting is go ahead in different times.
	 */
	if (intf == acm->ctrl_id) {
		is_setting = 1;
		if (acm->notify) {
			if (acm->notify->driver_data) {
				VDBG(cdev, "reset acm control interface %d\n", intf);
				usb_ep_disable(acm->notify);
			} else {
				VDBG(cdev, "init acm ctrl interface %d\n", intf);
				if (config_ep_by_speed(cdev->gadget, f, acm->notify))
					return -EINVAL;
			}
			ret = usb_ep_enable(acm->notify);
			if (ret < 0) {
				ERROR(cdev, "Enable acm interface ep failed\n");
				return ret;
			}
			acm->notify->driver_data = acm;
		}
	}

	if (intf == acm->data_id) {
		is_setting = 1;
		if (acm->port.in->driver_data) {
			DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
			gacm_cdev_disconnect(&acm->port);
		}
		if (!acm->port.in->desc || !acm->port.out->desc) {
			DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
			if (config_ep_by_speed(cdev->gadget, f,
								   acm->port.in) ||
				config_ep_by_speed(cdev->gadget, f,
								   acm->port.out)) {
				acm->port.in->desc = NULL;
				acm->port.out->desc = NULL;
				return -EINVAL;
			}
		}
		gacm_cdev_connect(&acm->port, acm->port_num);

		bsp_usb_set_enum_stat(acm->data_id, 1);
	}

	if (!is_setting)
		return -EINVAL;

	D("-\n");
	return 0;
}
コード例 #11
0
static void acm_resume(struct usb_function *f)
{
	struct f_acm *acm = func_to_acm(f);
	gacm_cdev_resume_notify(&acm->port);
}
コード例 #12
0
static void acm_suspend(struct usb_function *f)
{
	struct f_acm *acm = func_to_acm(f);
	gacm_cdev_suspend_notify(&acm->port);
}