分类: LINUX
2010-09-02 08:28:33
usb_driver里面最重要的3个要素都说完了,接下来就是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,
};
Name就是驱动名字了,fops就是这个驱动的文件操作结构体了,minor_base表示次设备的基础设备号,也就是192.比如我使用的时候,产生的文件/dev/skel0 ,主设备号是180,次设备号就是192了。
第二步,就是看skel_fops了,定义如下
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,
};
由于只是一个象征性的骨架程序,因此其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); // 得到需要打开设备的次设备号
interface = usb_find_interface(&skel_driver, subminor); // 通过skel_driver及次设备号找到USB接口指针
if (!interface) { // 没找到返回错误
err ("%s - error, can't find device for minor %d",
__func__, subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface); // 找到则得到其私有保存数据
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; // 接口数据保存在file的私有数据里
mutex_unlock(&dev->io_mutex);
exit:
return retval;
}
对应的release()函数也就是减少在open()中增加的引用计数,如下
static int skel_release(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
dev = (struct usb_skel *)file->private_data;
if (dev == NULL)
return -ENODEV;
/* allow the device to be autosuspended */
mutex_lock(&dev->io_mutex);
if (!--dev->open_count && dev->interface)
usb_autopm_put_interface(dev->interface);
mutex_unlock(&dev->io_mutex);
/* decrement the count on our device */
kref_put(&dev->kref, skel_delete);
return 0;
}
接下来要分析的是读写函数,在访问USB设备时,贯穿于其中的是urb结构体。先看读函数
static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval;
int bytes_read;
dev = (struct usb_skel *)file->private_data; //得到usb_skel指针
mutex_lock(&dev->io_mutex);
if (!dev->interface) { /* disconnect() was called */
retval = -ENODEV;
goto exit;
}
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev, // 从设备进行一次阻塞的批量读
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&bytes_read, 10000);
/* if the read was successful, copy the data to userspace */
if (!retval) { // 若读成功,则将数据复制到用户空间
if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
retval = -EFAULT;
else
retval = bytes_read;
}
exit:
mutex_unlock(&dev->io_mutex);
return retval;
}
在skel_write()函数中进行的关于urb的操作与前面对urb的描述完全对应,即进行了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 = (struct usb_skel *)file->private_data;
/* verify that we actually have some data to write */
if (count == 0) // 验证实际上有数据要写入USB设备
goto exit;
/* limit the number of URBs in flight to stop a user from using up all RAM */
if (down_interruptible(&dev->limit_sem)) { // 限制urb的数量,防止一个用户用光所有的RAM
retval = -ERESTARTSYS;
goto exit;
}
spin_lock_irq(&dev->err_lock);
if ((retval = dev->errors) < 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的缓存区,将数据复制给urb
if (!urb) {
retval = -ENOMEM;
goto error;
}
// 申请DMA内存
buf = usb_buffer_alloc(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 */ // 初始化urb
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, writesize, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);//
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
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_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
up(&dev->limit_sem);
exit:
return retval;
}
写函数发起的urb结束后,其完成函数skel_write_bulk_callback()将被调用,它会进行urb->status的判断,如下所示:
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_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
到这里,整个程序就分析完了。大部分工作内核都帮你做了,USB驱动程序就是这么简单,呵呵。