Linux下的USB总线驱动(02)——USB框架usb-skeleton.c

原文链接:http://www.linuxidc.com/Linux/2012-12/76197p2.htm

USB驱动框架usb-skeleton.c

USB骨架程序可以被看做一个最简单的USB设备驱动的实例。

首先看看USB骨架程序的usb_driver的定义
static struct usb_driver skel_driver = {
      .name =          "skeleton",
      .probe =  skel_probe,				//设备探测
      .disconnect =  skel_disconnect,
      .suspend =      skel_suspend,
      .resume =      skel_resume,
      .pre_reset =    skel_pre_reset,
      .post_reset =  skel_post_reset,
      .id_table =      skel_table,			//设备支持项
      .supports_autosuspend = 1,
};

/* Define these values to match your devices */
#define USB_SKEL_VENDOR_ID	0xfff0
#define USB_SKEL_PRODUCT_ID	0xfff0

/* table of devices that work with this driver */
static const struct usb_device_id skel_table[] = {
	{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
	{ }					/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);

由上面代码可见,通过USB_DEVICE宏定义了设备支持项。

对上面usb_driver的注册和注销发送在USB骨架程序的模块加载和卸载函数中。

static int __init usb_skel_init(void)
{
	int result;

	/* register this driver with the USB subsystem */
	result = usb_register(&skel_driver);			//将该驱动挂在USB总线上
	if (result)
		err("usb_register failed. Error number %d", result);

	return result;
}

一个设备被安装或者有设备插入后,当USB总线上经过match匹配成功,就会调用设备驱动程序中的probe探测函数,向探测函数传递设备的信息,以便确定驱动程序是否支持该设备。

static int skel_probe(struct usb_interface *interface,
		      const struct usb_device_id *id)
{
	struct usb_skel *dev;						//特定设备结构体
	struct usb_host_interface *iface_desc;			//设置结构体
	struct usb_endpoint_descriptor *endpoint;		//端点描述符
	size_t buffer_size;
	int i;
	int retval = -ENOMEM;

	/* allocate memory for our device state and initialize it */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		err("Out of memory");
		goto error;
	}
	kref_init(&dev->kref);							初始化内核引用计数
	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);	//初始化信号量
	mutex_init(&dev->io_mutex);					//初始化互斥锁
	spin_lock_init(&dev->err_lock);					//初始化自旋锁
	init_usb_anchor(&dev->submitted);				
	init_completion(&dev->bulk_in_completion);		//初始化完成量

	dev->udev = usb_get_dev(interface_to_usbdev(interface));	//获取usb_device结构体
	dev->interface = interface;								//获取usb_ interface结构体

	/* set up the endpoint information */
	/* use only the first bulk-in and bulk-out endpoints */
	iface_desc = interface->cur_altsetting;						//由接口获取当前设置
	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {			//根据端点个数逐一扫描端点
		endpoint = &iface_desc->endpoint[i].desc;				//由设置获取端点描述符

		if (!dev->bulk_in_endpointAddr &&
		    usb_endpoint_is_bulk_in(endpoint)) {						//如果端点为批量输入端点
			/* we found a bulk in endpoint */
			buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);		//缓冲大小
			dev->bulk_in_size = buffer_size;
			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;	//端点地址
			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);		//缓冲区
			if (!dev->bulk_in_buffer) {
				err("Could not allocate bulk_in_buffer");
				goto error;
			}
			dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);			//分配URB空间
			if (!dev->bulk_in_urb) {
				err("Could not allocate bulk_in_urb");
				goto error;
			}
		}

		if (!dev->bulk_out_endpointAddr &&
		    usb_endpoint_is_bulk_out(endpoint)) {					//如果端点为批量输出端点
			/* we found a bulk out endpoint */
			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;//端点地址
		}
	}
	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {	//都不是批量端点
		err("Could not find both bulk-in and bulk-out endpoints");
		goto error;
	}

	/* save our data pointer in this interface device */
	usb_set_intfdata(interface, dev);					//将特定设备结构体设置为接口的私有数据

	/* we can register the device now, as it is ready */
	retval = usb_register_dev(interface, &skel_class);	//注册USB设备
	if (retval) {
		/* something prevented us from registering this driver */
		err("Not able to get a minor for this device.");
		usb_set_intfdata(interface, NULL);
		goto error;
	}

	/* let the user know what node this device is now attached to */
	dev_info(&interface->dev,
		 "USB Skeleton device now attached to USBSkel-%d",
		 interface->minor);
	return 0;

error:
	if (dev)
		/* this frees allocated memory */
		kref_put(&dev->kref, skel_delete);
	return retval;
}

通过上面分析,我们知道,usb_driver的probe函数中根据usb_interface的成员寻找第一个批量输入和输出的端点,将端点地址、缓冲区等信息存入USB骨架程序定义的usb_skel结构体中,并将usb_skel通过usb_set_intfdata传为USB接口的私有数据,最后注册USB设备。

我们来看看这个USB骨架程序定义的usb_skel结构体
/* Structure to hold all of our device specific stuff */
struct usb_skel {
	struct usb_device	*udev;				/* the usb device for this device */
	struct usb_interface	*interface;		/* the interface for this device */
	struct semaphore	limit_sem;			/* limiting the number of writes in progress */
	struct usb_anchor	submitted;			/* in case we need to retract our submissions */
	struct urb		*bulk_in_urb;				/* the urb to read data with */
	unsigned char	*bulk_in_buffer;			/* the buffer to receive data */
	size_t		bulk_in_size;				/* the size of the receive buffer */
	size_t		bulk_in_filled;				/* number of bytes in the buffer */
	size_t		bulk_in_copied;			/* already copied to user space */
	__u8		bulk_in_endpointAddr;		/* the address of the bulk in endpoint */
	__u8		bulk_out_endpointAddr;	/* the address of the bulk out endpoint */
	int			errors;					/* the last request tanked */
	int			open_count;				/* count the number of openers */
	bool			ongoing_read;				/* a read is going on */
	bool			processed_urb;			/* indicates we haven't processed the urb */
	spinlock_t		err_lock;				/* lock for errors */
	struct kref		kref;
	struct mutex		io_mutex;			/* synchronize I/O with disconnect */
	struct completion	bulk_in_completion;	/* to wait for an ongoing read */
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)

好了看完了probe,我们再看看disconnect函数

static void skel_disconnect(struct usb_interface *interface)
{
      struct usb_skel *dev;
      int minor = interface->minor;					//获得接口的次设备号
      dev = usb_get_intfdata(interface); 			//获取接口的私有数据
      usb_set_intfdata(interface, NULL);			//设置接口的私有数据为空
	/* give back our minor */
      usb_deregister_dev(interface, &skel_class);		//注销USB设备
	
	/* prevent more I/O from starting */
      mutex_lock(&dev->io_mutex);
      dev->interface = NULL;         
      mutex_unlock(&dev->io_mutex);
	
      usb_kill_anchored_urbs(&dev->submitted);
	/* decrement our usage count */
      kref_put(&dev->kref, skel_delete);
      dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}

我们在skel_probe中最后执行了usb_register_dev(interface, &skel_class)来注册了一个USB设备,我们看看skel_class的定义
/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver skel_class = {
      .name =			"skel%d",
      .fops =			&skel_fops,
      .minor_base =		USB_SKEL_MINOR_BASE,
};

static const struct file_operations skel_fops = {
	.owner =	THIS_MODULE,
	.read =	skel_read,
	.write =	skel_write,
	.open =	skel_open,
	.release =	skel_release,
	.flush =	skel_flush,
	.llseek =	noop_llseek,
};

根据上面代码我们知道,其实我们在probe中注册USB设备的时候使用的skel_class是一个包含file_operations的结构体,而这个结构体正是字符设备文件操作结构体。

我们先来看看这个file_operations中open函数的实现
static int skel_open(struct inode *inode, struct file *file)
{
	struct usb_skel *dev;
	struct usb_interface *interface;
	int subminor;
	int retval = 0;

	subminor = iminor(inode);		//获得次设备号
	//根据usb_driver和次设备号获取设备的接口
interface = usb_find_interface(&skel_driver, subminor);
	if (!interface) {
		err("%s - error, can't find device for minor %d",
		     __func__, subminor);
		retval = -ENODEV;
		goto exit;
	}

	dev = usb_get_intfdata(interface);			//获取接口的私有数据usb_ske
	if (!dev) {
		retval = -ENODEV;
		goto exit;
	}

	/* increment our usage count for the device */
	kref_get(&dev->kref);

	/* lock the device to allow correctly handling errors
	 * in resumption */
	mutex_lock(&dev->io_mutex);

	if (!dev->open_count++) {
		retval = usb_autopm_get_interface(interface);
			if (retval) {
				dev->open_count--;
				mutex_unlock(&dev->io_mutex);
				kref_put(&dev->kref, skel_delete);
				goto exit;
			}
	} /* else { //uncomment this block if you want exclusive open
		retval = -EBUSY;
		dev->open_count--;
		mutex_unlock(&dev->io_mutex);
		kref_put(&dev->kref, skel_delete);
		goto exit;
	} */
	/* prevent the device from being autosuspended */

	/* save our object in the file's private structure */
	file->private_data = dev;			//将usb_skel设置为文件的私有数据
	mutex_unlock(&dev->io_mutex);

exit:
	return retval;
}

这个open函数实现非常简单,它根据usb_driver和次设备号通过usb_find_interface获取USB接口,然后通过usb_get_intfdata获得接口的私有数据并赋值给文件。

好了,我们看看write函数,在write函数中,我们进行了urb的分配、初始化和提交的操作
static ssize_t skel_write(struct file *file, const char *user_buffer,
			  size_t count, loff_t *ppos)
{
	struct usb_skel *dev;
	int retval = 0;
	struct urb *urb = NULL;
	char *buf = NULL;
	size_t writesize = min(count, (size_t)MAX_TRANSFER);		//待写数据大小

	dev = file->private_data;								//获取文件的私有数据

	/* verify that we actually have some data to write */
	if (count == 0)
		goto exit;

	/*
	 * limit the number of URBs in flight to stop a user from using up all
	 * RAM
	 */
	if (!(file->f_flags & O_NONBLOCK)) {					//如果文件采用非阻塞方式
		if (down_interruptible(&dev->limit_sem)) {			//获取限制读的次数的信号量
			retval = -ERESTARTSYS;
			goto exit;
		}
	} else {
		if (down_trylock(&dev->limit_sem)) {
			retval = -EAGAIN;
			goto exit;
		}
	}

	spin_lock_irq(&dev->err_lock);		//关中断
	retval = dev->errors;
	if (retval < 0) {
		/* any error is reported once */
		dev->errors = 0;
		/* to preserve notifications about reset */
		retval = (retval == -EPIPE) ? retval : -EIO;
	}
	spin_unlock_irq(&dev->err_lock);		//开中断
	if (retval < 0)
		goto error;

	/* create a urb, and a buffer for it, and copy the data to the urb */
	urb = usb_alloc_urb(0, GFP_KERNEL);	//分配urb
	if (!urb) {
		retval = -ENOMEM;
		goto error;
	}

	buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
				 &urb->transfer_dma);	//分配写缓冲区
	if (!buf) {
		retval = -ENOMEM;
		goto error;
	}
	//将用户空间数据拷贝到缓冲区
	if (copy_from_user(buf, user_buffer, writesize)) {
		retval = -EFAULT;
		goto error;
	}

	/* this lock makes sure we don't submit URBs to gone devices */
	mutex_lock(&dev->io_mutex);
	if (!dev->interface) {		/* disconnect() was called */
		mutex_unlock(&dev->io_mutex);
		retval = -ENODEV;
		goto error;
	}

	/* initialize the urb properly */
	usb_fill_bulk_urb(urb, dev->udev,
			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
			  buf, writesize, skel_write_bulk_callback, dev);	//填充urb
	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	//urb->transfer_dma有效
	usb_anchor_urb(urb, &dev->submitted);

	/* send the data out the bulk port */
	retval = usb_submit_urb(urb, GFP_KERNEL);				//提交urb
	mutex_unlock(&dev->io_mutex);
	if (retval) {
		err("%s - failed submitting write urb, error %d", __func__,
		    retval);
		goto error_unanchor;
	}

	/*
	 * release our reference to this urb, the USB core will eventually free
	 * it entirely
	 */
	usb_free_urb(urb);


	return writesize;

error_unanchor:
	usb_unanchor_urb(urb);
error:
	if (urb) {
		usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
		usb_free_urb(urb);
	}
	up(&dev->limit_sem);

exit:
	return retval;
}

首先说明一个问题,填充urb后,设置了transfer_flags标志,当transfer_flags中的URB_NO_TRANSFER_DMA_MAP被设置,USB核心使用transfer_dma指向的缓冲区而不是使用transfer_buffer 指向的缓冲区,这表明即将传输DMA缓冲区。当transfer_flags中的URB_NO_SETUP_DMA_MAP被设置,如果控制urb有 DMA缓冲区,USB核心将使用setup_dma指向的缓冲区而不是使用setup_packet指向的缓冲区。

另外,通过上面这个write函数我们知道,当写函数发起的urb结束后,其完成函数skel_write_bulk_callback会被调用,我们继续跟踪
static void skel_write_bulk_callback(struct urb *urb)
{
	struct usb_skel *dev;

	dev = urb->context;

	/* sync/async unlink faults aren't errors */
	if (urb->status) {
		if (!(urb->status == -ENOENT ||
		    urb->status == -ECONNRESET ||
		    urb->status == -ESHUTDOWN))
			err("%s - nonzero write bulk status received: %d",
			    __func__, urb->status);

		spin_lock(&dev->err_lock);
		dev->errors = urb->status;
		spin_unlock(&dev->err_lock);
	}

	/* free up our allocated buffer */
	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
			  urb->transfer_buffer, urb->transfer_dma);
	up(&dev->limit_sem);
}

很明显,skel_write_bulk_callback主要对urb->status进行判断,根据错误提示显示错误信息,然后释放urb空间。

接着,我们看看USB骨架程序的字符设备的read函数
static ssize_t skel_read(struct file *file, char *buffer, size_t count,
			 loff_t *ppos)
{
	struct usb_skel *dev;
	int rv;
	bool ongoing_io;

	dev = file->private_data;					//获得文件私有数据

	/* if we cannot read at all, return EOF */
	if (!dev->bulk_in_urb || !count)			//正在写的时候禁止读操作
		return 0;

	/* no concurrent readers */
	rv = mutex_lock_interruptible(&dev->io_mutex);
	if (rv < 0)
		return rv;

	if (!dev->interface) {		/* disconnect() was called */
		rv = -ENODEV;
		goto exit;
	}

	/* if IO is under way, we must not touch things */
retry:
	spin_lock_irq(&dev->err_lock);
	ongoing_io = dev->ongoing_read;
	spin_unlock_irq(&dev->err_lock);

	if (ongoing_io) {		//USB core正在读取数据,数据没准备好
		/* nonblocking IO shall not wait */
		if (file->f_flags & O_NONBLOCK) {
			rv = -EAGAIN;
			goto exit;
		}
		/*
		 * IO may take forever
		 * hence wait in an interruptible state
		 */
		rv = wait_for_completion_interruptible(&dev->bulk_in_completion);
		if (rv < 0)
			goto exit;
		/*
		 * by waiting we also semiprocessed the urb
		 * we must finish now
		 */
		dev->bulk_in_copied = 0;		//拷贝到用户空间操作已成功
		dev->processed_urb = 1;		//目前已处理好urb
	}

	if (!dev->processed_urb) {			//目前还未处理好urb
		/*
		 * the URB hasn't been processed
		 * do it now
		 */
		wait_for_completion(&dev->bulk_in_completion);	//等待完成
		dev->bulk_in_copied = 0;		//拷贝到用户空间操作已成功
		dev->processed_urb = 1;		//目前已处理好urb
	}

	/* errors must be reported */
	rv = dev->errors;
	if (rv < 0) {
		/* any error is reported once */
		dev->errors = 0;
		/* to preserve notifications about reset */
		rv = (rv == -EPIPE) ? rv : -EIO;
		/* no data to deliver */
		dev->bulk_in_filled = 0;
		/* report it */
		goto exit;
	}

	/*
	 * if the buffer is filled we may satisfy the read
	 * else we need to start IO
	 */

	if (dev->bulk_in_filled) {					//缓冲区有内容
		/* we had read data */
		//可读数据大小为缓冲区内容减去已经拷贝到用户空间的数据大小
		size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
		size_t chunk = min(available, count);	//真正读取的数据大小

		if (!available) {
			/*
			 * all data has been used
			 * actual IO needs to be done
			 */
			rv = skel_do_read_io(dev, count);
			if (rv < 0)
				goto exit;
			else
				goto retry;
		}
		/*
		 * data is available
		 * chunk tells us how much shall be copied
		 */
		//拷贝缓冲区数据到用户空间
		if (copy_to_user(buffer,
				 dev->bulk_in_buffer + dev->bulk_in_copied,
				 chunk))
			rv = -EFAULT;
		else
			rv = chunk;

		dev->bulk_in_copied += chunk;	//目前拷贝完成的数据大小

		/*
		 * if we are asked for more than we have,
		 * we start IO but don't wait
		 */
		if (available < count)
			skel_do_read_io(dev, count - chunk);
	} else {
		/* no data in the buffer */
		rv = skel_do_read_io(dev, count);
		if (rv < 0)
			goto exit;
		else if (!(file->f_flags & O_NONBLOCK))
			goto retry;
		rv = -EAGAIN;
	}
exit:
	mutex_unlock(&dev->io_mutex);
	return rv;
}

通过上面read函数,我们知道,在读取数据时候,如果发现缓冲区没有数据,或者缓冲区的数据小于用户需要读取的数据量时,则会调用IO操作,也就是skel_do_read_io函数。

static int skel_do_read_io(struct usb_skel *dev, size_t count)
{
      int rv;
	
	/* prepare a read */
      usb_fill_bulk_urb(dev->bulk_in_urb,dev->udev,usb_rcvbulkpipe(dev->udev,
                            dev->bulk_in_endpointAddr),dev->bulk_in_buffer,
                    min(dev->bulk_in_size, count),skel_read_bulk_callback,dev);	//填充urb
	/* tell everybody to leave the URB alone */
      spin_lock_irq(&dev->err_lock);
      dev->ongoing_read = 1;											//标志正在读取数据中
      spin_unlock_irq(&dev->err_lock);
	
      rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);				//提交urb
      if (rv < 0) {
              err("%s - failed submitting read urb, error %d",
                    __func__, rv);
              dev->bulk_in_filled = 0;
              rv = (rv == -ENOMEM) ? rv : -EIO;
              spin_lock_irq(&dev->err_lock);
              dev->ongoing_read = 0;
              spin_unlock_irq(&dev->err_lock);
      }
      return rv;
}

好了,其实skel_do_read_io只是完成了urb的填充和提交,USB core读取到了数据后,会调用填充urb时设置的回调函数skel_read_bulk_callback。

static void skel_read_bulk_callback(struct urb *urb)
{
	struct usb_skel *dev;

	dev = urb->context;

	spin_lock(&dev->err_lock);
	/* sync/async unlink faults aren't errors */
	if (urb->status) {//根据返回状态判断是否出错
		if (!(urb->status == -ENOENT ||
		    urb->status == -ECONNRESET ||
		    urb->status == -ESHUTDOWN))
			err("%s - nonzero write bulk status received: %d",
			    __func__, urb->status);

		dev->errors = urb->status;
	} else {
		dev->bulk_in_filled = urb->actual_length;	//记录缓冲区的大小
	}
	dev->ongoing_read = 0;						//已经读取数据完毕
	spin_unlock(&dev->err_lock);

	complete(&dev->bulk_in_completion);			//唤醒skel_read函数
}

到目前为止,我们已经把USB驱动框架usb-skeleton.c分析完了,总结下,其实很简单,在模块加载里面注册 usb_driver,然后在probe函数里初始化一些参数,最重要的是注册了USB设备,这个USB设备相当于一个字符设备,提供 file_operations接口。然后设计open,close,read,write函数,这个open里基本没做什么事情,在write中,通过分配urb、填充urb和提交urb。注意读的urb的分配在probe里申请空间,写的urb的分配在write里申请空间。在这个驱动程序中,我们重点掌握usb_fill_bulk_urb的设计。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值