⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 inode.c

📁 LINUX2.4.18内核下的usb GADGET驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
	}	buf += 4;	len -= 4;	/* NOTE:  audio endpoint extensions not accepted here;	 * just don't include the extra bytes.	 */	/* full/low speed descriptor, then high speed */	if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) {		goto fail1;	}	if (data->desc.bLength != USB_DT_ENDPOINT_SIZE			|| data->desc.bDescriptorType != USB_DT_ENDPOINT)		goto fail0;	if (len != USB_DT_ENDPOINT_SIZE) {		if (len != 2 * USB_DT_ENDPOINT_SIZE)			goto fail0;		if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,					USB_DT_ENDPOINT_SIZE)) {			goto fail1;		}		if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE				|| data->hs_desc.bDescriptorType					!= USB_DT_ENDPOINT) {			DBG(data->dev, "config %s, bad hs length or type\n",					data->name);			goto fail0;		}	}	value = len;	spin_lock_irq (&data->dev->lock);	if (data->dev->state == STATE_DEV_UNBOUND) {		value = -ENOENT;		goto gone;	} else if ((ep = data->ep) == NULL) {		value = -ENODEV;		goto gone;	}	switch (data->dev->gadget->speed) {	case USB_SPEED_LOW:	case USB_SPEED_FULL:		value = usb_ep_enable (ep, &data->desc);		if (value == 0)			data->state = STATE_EP_ENABLED;		break;#ifdef	HIGHSPEED	case USB_SPEED_HIGH:		/* fails if caller didn't provide that descriptor... */		value = usb_ep_enable (ep, &data->hs_desc);		if (value == 0)			data->state = STATE_EP_ENABLED;		break;#endif	default:		DBG (data->dev, "unconnected, %s init deferred\n",				data->name);		data->state = STATE_EP_DEFER_ENABLE;	}	if (value == 0)		fd->f_op = &ep_io_operations;gone:	spin_unlock_irq (&data->dev->lock);	if (value < 0) {fail:		data->desc.bDescriptorType = 0;		data->hs_desc.bDescriptorType = 0;	}	up (&data->lock);	return value;fail0:	value = -EINVAL;	goto fail;fail1:	value = -EFAULT;	goto fail;}static intep_open (struct inode *inode, struct file *fd){	struct ep_data		*data = inode->u.generic_ip;	int			value = -EBUSY;	if (down_interruptible (&data->lock) != 0)		return -EINTR;	spin_lock_irq (&data->dev->lock);	if (data->dev->state == STATE_DEV_UNBOUND)		value = -ENOENT;	else if (data->state == STATE_EP_DISABLED) {		value = 0;		data->state = STATE_EP_READY;		get_ep (data);		fd->private_data = data;		VDEBUG (data->dev, "%s ready\n", data->name);	} else		DBG (data->dev, "%s state %d\n",			data->name, data->state);	spin_unlock_irq (&data->dev->lock);	up (&data->lock);	return value;}/* used before endpoint configuration */static struct file_operations ep_config_operations = {	.owner =	THIS_MODULE,	.open =		ep_open,	.write =	ep_config,	.release =	ep_release,};/*----------------------------------------------------------------------*//* EP0 IMPLEMENTATION can be partly in userspace. * * Drivers that use this facility receive various events, including * control requests the kernel doesn't handle.  Drivers that don't * use this facility may be too simple-minded for real applications. */static inline void ep0_readable (struct dev_data *dev){	wake_up (&dev->wait);	kill_fasync (&dev->fasync, SIGIO, POLL_IN);}static void clean_req (struct usb_ep *ep, struct usb_request *req){	struct dev_data		*dev = ep->driver_data;	if (req->buf != dev->rbuf) {		usb_ep_free_buffer (ep, req->buf, req->dma, req->length);		req->buf = dev->rbuf;		req->dma = DMA_ADDR_INVALID;	}	req->complete = epio_complete;	dev->setup_out_ready = 0;}static void ep0_complete (struct usb_ep *ep, struct usb_request *req){	struct dev_data		*dev = ep->driver_data;	int			free = 1;	/* for control OUT, data must still get to userspace */	if (!dev->setup_in) {		dev->setup_out_error = (req->status != 0);		if (!dev->setup_out_error)			free = 0;		dev->setup_out_ready = 1;		ep0_readable (dev);	} else if (dev->state == STATE_SETUP)		dev->state = STATE_CONNECTED;	/* clean up as appropriate */	if (free && req->buf != &dev->rbuf)		clean_req (ep, req);	req->complete = epio_complete;}static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len){	struct dev_data	*dev = ep->driver_data;	if (dev->setup_out_ready) {		DBG (dev, "ep0 request busy!\n");		return -EBUSY;	}	if (len > sizeof (dev->rbuf))		req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC);	if (req->buf == 0) {		req->buf = dev->rbuf;		return -ENOMEM;	}	req->complete = ep0_complete;	req->length = len;	return 0;}static ssize_tep0_read (struct file *fd, char *buf, size_t len, loff_t *ptr){	struct dev_data			*dev = fd->private_data;	ssize_t				retval;	enum ep0_state			state;	spin_lock_irq (&dev->lock);	/* report fd mode change before acting on it */	if (dev->setup_abort) {		dev->setup_abort = 0;		retval = -EIDRM;		goto done;	}	/* control DATA stage */	if ((state = dev->state) == STATE_SETUP) {		if (dev->setup_in) {		/* stall IN */			VDEBUG(dev, "ep0in stall\n");			(void) usb_ep_set_halt (dev->gadget->ep0);			retval = -EL2HLT;			dev->state = STATE_CONNECTED;		} else if (len == 0) {		/* ack SET_CONFIGURATION etc */			struct usb_ep		*ep = dev->gadget->ep0;			struct usb_request	*req = dev->req;			if ((retval = setup_req (ep, req, 0)) == 0)				retval = usb_ep_queue (ep, req, GFP_ATOMIC);			dev->state = STATE_CONNECTED;		} else {			/* collect OUT data */			if ((fd->f_flags & O_NONBLOCK) != 0					&& !dev->setup_out_ready) {				retval = -EAGAIN;				goto done;			}			spin_unlock_irq (&dev->lock);			retval = wait_event_interruptible (dev->wait,					dev->setup_out_ready != 0);			/* FIXME state could change from under us */			spin_lock_irq (&dev->lock);			if (retval)				goto done;			if (dev->setup_out_error)				retval = -EIO;			else {				len = min (len, dev->req->actual);// FIXME don't call this with the spinlock held ...				if (copy_to_user (buf, &dev->req->buf, len))					retval = -EFAULT;				clean_req (dev->gadget->ep0, dev->req);				/* NOTE userspace can't yet choose to stall */			}		}		goto done;	}	/* else normal: return event data */	if (len < sizeof dev->event [0]) {		retval = -EINVAL;		goto done;	}	len -= len % sizeof (struct usb_gadgetfs_event);	dev->usermode_setup = 1;scan:	/* return queued events right away */	if (dev->ev_next != 0) {		unsigned		i, n;		int			tmp = dev->ev_next;		len = min (len, tmp * sizeof (struct usb_gadgetfs_event));		n = len / sizeof (struct usb_gadgetfs_event);		/* ep0 can't deliver events when STATE_SETUP */		for (i = 0; i < n; i++) {			if (dev->event [i].type == GADGETFS_SETUP) {				len = n = i + 1;				len *= sizeof (struct usb_gadgetfs_event);				n = 0;				break;			}		}		spin_unlock_irq (&dev->lock);		if (copy_to_user (buf, &dev->event, len))			retval = -EFAULT;		else			retval = len;		if (len > 0) {			len /= sizeof (struct usb_gadgetfs_event);			/* NOTE this doesn't guard against broken drivers;			 * concurrent ep0 readers may lose events.			 */			spin_lock_irq (&dev->lock);			dev->ev_next -= len;			if (dev->ev_next != 0)				memmove (&dev->event, &dev->event [len],					sizeof (struct usb_gadgetfs_event)						* (tmp - len));			if (n == 0)				dev->state = STATE_SETUP;			spin_unlock_irq (&dev->lock);		}		return retval;	}	if (fd->f_flags & O_NONBLOCK) {		retval = -EAGAIN;		goto done;	}	switch (state) {	default:		DBG (dev, "fail %s, state %d\n", __FUNCTION__, state);		retval = -ESRCH;		break;	case STATE_UNCONNECTED:	case STATE_CONNECTED:		spin_unlock_irq (&dev->lock);		DBG (dev, "%s wait\n", __FUNCTION__);		/* wait for events */		retval = wait_event_interruptible (dev->wait,				dev->ev_next != 0);		if (retval < 0)			return retval;		spin_lock_irq (&dev->lock);		goto scan;	}done:	spin_unlock_irq (&dev->lock);	return retval;}static struct usb_gadgetfs_event *next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type){	struct usb_gadgetfs_event	*event;	unsigned			i;	switch (type) {	/* these events purge the queue */	case GADGETFS_DISCONNECT:		if (dev->state == STATE_SETUP)			dev->setup_abort = 1;		// FALL THROUGH	case GADGETFS_CONNECT:		dev->ev_next = 0;		break;	case GADGETFS_SETUP:		/* previous request timed out */	case GADGETFS_SUSPEND:		/* same effect */		/* these events can't be repeated */		for (i = 0; i != dev->ev_next; i++) {			if (dev->event [i].type != type)				continue;			DBG (dev, "discard old event %d\n", type);			dev->ev_next--;			if (i == dev->ev_next)				break;			/* indices start at zero, for simplicity */			memmove (&dev->event [i], &dev->event [i + 1],				sizeof (struct usb_gadgetfs_event)					* (dev->ev_next - i));		}		break;	default:		BUG ();	}	event = &dev->event [dev->ev_next++];	BUG_ON (dev->ev_next > N_EVENT);	VDEBUG (dev, "ev %d, next %d\n", type, dev->ev_next);	memset (event, 0, sizeof *event);	event->type = type;	return event;}static ssize_tep0_write (struct file *fd, const char *buf, size_t len, loff_t *ptr){	struct dev_data		*dev = fd->private_data;	ssize_t			retval = -ESRCH;	spin_lock_irq (&dev->lock);	/* report fd mode change before acting on it */	if (dev->setup_abort) {		dev->setup_abort = 0;		retval = -EIDRM;	/* data and/or status stage for control request */	} else if (dev->state == STATE_SETUP) {		/* IN DATA+STATUS caller makes len <= wLength */		if (dev->setup_in) {			retval = setup_req (dev->gadget->ep0, dev->req, len);			if (retval == 0) {				spin_unlock_irq (&dev->lock);				if (copy_from_user (dev->req->buf, buf, len))					retval = -EFAULT;				else					retval = usb_ep_queue (						dev->gadget->ep0, dev->req,						GFP_KERNEL);				if (retval < 0) {					spin_lock_irq (&dev->lock);					clean_req (dev->gadget->ep0, dev->req);					spin_unlock_irq (&dev->lock);				} else					retval = len;				return retval;			}		/* can stall some OUT transfers */		} else if (dev->setup_can_stall) {			VDEBUG(dev, "ep0out stall\n");			(void) usb_ep_set_halt (dev->gadget->ep0);			retval = -EL2HLT;			dev->state = STATE_CONNECTED;		} else {			DBG(dev, "bogus ep0out stall!\n");		}	} else		DBG (dev, "fail %s, state %d\n", __FUNCTION__, dev->state);	spin_unlock_irq (&dev->lock);	return retval;}static intep0_fasync (int f, struct file *fd, int on){	struct dev_data		*dev = fd->private_data;	// caller must F_SETOWN before signal delivery happens	VDEBUG (dev, "%s %s\n", __FUNCTION__, on ? "on" : "off");	return fasync_helper (f, fd, on, &dev->fasync);}static struct usb_gadget_driver gadgetfs_driver;static intdev_release (struct inode *inode, struct file *fd){	struct dev_data		*dev = fd->private_data;	/* closing ep0 === shutdown all */	usb_gadget_unregister_driver (&gadgetfs_driver);	/* at this point "good" hardware has disconnected the	 * device from USB; the host won't see it any more.	 * alternatively, all host requests will time out.	 */	fasync_helper (-1, fd, 0, &dev->fasync);	kfree (dev->buf);	dev->buf = 0;	put_dev (dev);	/* other endpoints were all decoupled from this device */	dev->state = STATE_DEV_DISABLED;	return 0;}static int dev_ioctl (struct inode *inode, struct file *fd,		unsigned code, unsigned long value){	struct dev_data		*dev = fd->private_data;	struct usb_gadget	*gadget = dev->gadget;	if (gadget->ops->ioctl)		return gadget->ops->ioctl (gadget, code, value);	return -ENOTTY;}/* used after device configuration */static struct file_operations ep0_io_operations = {	.owner =	THIS_MODULE,	.read =		ep0_read,	.write =	ep0_write,	.fasync =	ep0_fasync,	// .poll =	ep0_poll,	.ioctl =	dev_ioctl,	.release =	dev_release,};/*----------------------------------------------------------------------*//* The in-kernel gadget driver handles most ep0 issues, in particular * enumerating the single configuration (as provided from user space). * * Unrecognized ep0 requests may be handled in user space. */#ifdef	HIGHSPEEDstatic void make_qualifier (struct dev_data *dev){	struct usb_qualifier_descriptor		qual;	struct usb_device_descriptor		*desc;	qual.bLength = sizeof qual;	qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;	qual.bcdUSB = __constant_cpu_to_le16 (0x0200);	desc = dev->dev;	qual.bDeviceClass = desc->bDeviceClass;	qual.bDeviceSubClass = desc->bDeviceSubClass;	qual.bDeviceProtocol = desc->bDeviceProtocol;	/* assumes ep0 uses the same value for both speeds ... */	qual.bMaxPacketSize0 = desc->bMaxPacketSize0;	qual.bNumConfigurations = 1;	qual.bRESERVED = 0;	memcpy (dev->rbuf, &qual, sizeof qual);}#endifstatic intconfig_buf (struct dev_data *dev, u8 type, unsigned index){	int		len;#ifdef HIGHSPEED	int		hs;#endif	/* only one configuration */	if (index > 0)		return -EINVAL;#ifdef HIGHSPEED	hs = (dev->gadget->speed == USB_SPEED_HIGH);	if (type == USB_DT_OTHER_SPEED_CONFIG)		hs = !hs;	if (hs) {		dev->req->buf = dev->hs_config;		len = le16_to_cpup (&dev->hs_config->wTotalLength);	} else#endif	{		dev->req->buf = dev->config;		len = le16_to_cpup (&dev->config->wTotalLength);	}	((u8 *)dev->req->buf) [1] = type;	return len;}static intgadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl){	struct dev_data			*dev = get_gadget_data (gadget);	struct usb_request		*req = dev->req;	int				value = -EOPNOTSUPP;	struct usb_gadgetfs_event	*event;	spin_lock (&dev->lock);	dev->setup_abort = 0;	if (dev->state == STATE_UNCONNECTED) {		struct usb_ep	*ep;		struct ep_data	*data;		dev->state = STATE_CONNECTED;		dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket;#ifdef	HIGHSPEED		if (gadget->speed == USB_SPEED_HIGH && dev->hs_config == 0) {			ERROR (dev, "no high speed config??\n");			return -EINVAL;		}#endif	/* HIGHSPEED */		INFO (dev, "connected\n");		event = next_event (dev, GADGETFS_CONNECT);		event->u.speed = gadget->speed;		ep0_readable (dev);		list_for_each_entry (ep, &gadget->ep_list, ep_list) {			data = ep->driver_data;			/* ... down_trylock (&data->lock) ... */			if (data->state != STATE_EP_DEFER_ENABLE)				continue;#ifdef	HIGHSPEED			if (gadget->speed == USB_SPEED_HIGH)				value = usb_ep_enable (ep, &data->hs_desc);			else#endif	/* HIGHSPEED */				value = usb_ep_enable (ep, &data->desc);			if (value) {				ERROR (dev, "deferred %s enable --> %d\n",					data->name, value);				continue;			}			data->state = STATE_EP_ENABLED;			wake_up (&data->wait);			DBG (dev, "woke up %s waiters\n", data->name);		}	/* host may have given up waiting for response.  we can miss control	 * requests handled lower down (device/endpoint status and features);	 * then ep0_{read,write} will report the wrong status. controller	 * driver will have aborted pending i/o.	 */	} else if (dev->state == STATE_SETUP)		dev->setup_abort = 1;	req->buf = dev->rbuf;	req->dma = DMA_ADDR_INVALID;	req->context = 0;	value = -EOPNOTSUPP;	switch (ctrl->bRequest) {	case USB_REQ_GET_DESCRIPTOR:		if (ctrl->bRequestType != USB_DIR_IN)			goto unrecognized;		switch (ctrl->wValue >> 8) {		case USB_DT_DEVICE:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -