linux内核实例讲解,[转载]Linux 内核示例程序 usb_skeleton.c 详解

USB能够自动监测设备,并调用相应得驱动程序处理设备,所以其规范实际上是相当复杂的,幸好,我们不必理会大部分细节问题,因为Linux已经提供相应的解决方案。就我现在的理解来说,USB的驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,但我们至少要了解他的功能。形象得说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB通道到达该到的地方,这部分工作由usb_core来完成。当USB设备接到USB控制器接口时,usb_core就检测该设备的一些信息,例如生产厂商ID和产品的ID,或者是设备所属的class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备。里面复杂细节我们不用管,我们要做的是另一块工作--usb的设备驱动。也就是说,我们就等着usb_core告诉我们要工作了,我们才工作。

从开发人员的角度看,每一个usb设备有若干个配置(configuration)组成,每个配置又可以有多个接口(interface),每个接口又有多个设置(setting图中没有给出),而接口本身可能没有端点或者多个端点(end

point)。USB的数据交换通过端点来进行,主机与各个端点之间建立起单向的管道来传输数据。而这些接口可以分为四类:

(1)控制(control)

用于配置设备、获取设备信息、发送命令或者获取设备的状态报告

(2)中断(interrupt)

当USB宿主要求设备传输数据时,中断端点会以一个固定的速率传送少量数据,还用于发送数据到USB设备以控制设备,一般不用于传送大量数据。

(3)批量(bulk)

用于大量数据的可靠传输,如果总线上的空间不足以发送整个批量包,它会被分割成多个包传输。

(4)等时(isochronous)

大量数据的不可靠传输,不保证数据的到达,但保证恒定的数据流,多用于数据采集。

/ *

* USB Skeleton driver - 2.2

*

* Copyright (C) 2001-2004 Greg Kroah-Hartman

(greg@kroah.com)

*

* This program is free software; you can

redistribute it and/or

* modify it under the terms of the GNU

General Public License as

* published by the Free Software Foundation,

version 2.

*

* This driver is based on the 2.6.3 version

of drivers/usb/usb-skeleton.c

* but has been rewritten to be easier to

read and use.

*

* /

#include

#include

#include

#include

#include

#include

#include

#include

#include

/ * 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);

/ * Get a minor range for your devices from the usb maintainer

* /

#define USB_SKEL_MINOR_BASE 192

/ * our private defines. if this grows any larger, use your

own .h file * /

#define MAX_TRANSFER (PAGE_SIZE - 512)

/ * MAX_TRANSFER is chosen so that the VM is not stressed

by

allocations

> PAGE_SIZE and the number of packets in a

page

is an integer 512 is the

largest possible packet on EHCI * /

#define WRITES_IN_FLIGHT 8

/ * arbitrarily chosen * /

/ * 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 * /

/ * urb

means USB request block. * /

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)

static struct usb_driver skel_driver;

static void skel_draw_down(struct usb_skel *dev);

static void skel_delete(struct kref *kref)

{

struct

usb_skel *dev = to_skel_dev(kref);

usb_free_urb(dev->bulk_in_urb);

usb_put_dev(dev->udev);

kfree(dev->bulk_in_buffer);

kfree(dev);

}

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);

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;

mutex_unlock(&dev->io_mutex);

exit:

return

retval;

}

static int skel_release(struct inode *inode, struct file

*file)

{

struct

usb_skel *dev;

dev =

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;

}

static int skel_flush(struct file *file, fl_owner_t id)

{

struct

usb_skel *dev;

int

res;

dev =

file->private_data;

if (dev

== NULL)

{

return -ENODEV;

}

/ *

wait for io to stop * /

mutex_lock(&dev->io_mutex);

skel_draw_down(dev);

/ *

read out errors, leave subsequent opens a clean slate * /

spin_lock_irq(&dev->err_lock);

res =

dev->errors ? (dev->errors == -EPIPE

? -EPIPE : -EIO) : 0;

dev->errors = 0;

spin_unlock_irq(&dev->err_lock);

mutex_unlock(&dev->io_mutex);

return

res;

}

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);

}

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);

/ *

tell everybody to leave the URB alone * /

spin_lock_irq(&dev->err_lock);

dev->ongoing_read = 1;

spin_unlock_irq(&dev->err_lock);

/ * do

it * /

rv =

usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);

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;

}

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)

{

/ * 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;

}

if

(!dev->processed_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;

}

/ *

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;

}

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);

}

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);

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->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_free_coherent(dev->udev,

writesize, buf, urb->transfer_dma);

usb_free_urb(urb);

}

up(&dev->limit_sem);

exit:

return

retval;

}

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,

};

/ *

* 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,

};

/ *

probe是usb子系统自动调用的一个函数,有USB设备接到硬件集线器时,usb子系统会根据production

ID和vendor

ID的组合或者设备的class、subclass跟protocol的组合来识别设备调用相应驱动程序的probe(探测)函数,对于skeleton来说,就是skel_probe。系统会传递给探测函数一个usb_interface

*跟一个struct usb_device_id

*作为参数。他们分别是该USB设备的接口描述(一般会是该设备的第0号接口,该接口的默认设置也是第0号设置)跟它的设备ID描述(包括Vendor

ID、Production ID等)。

* /

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);

//分配内存。kzalloc是对kmalloc的简单封装。

if

(!dev)

{

err("Out of memory");

goto error;

}

kref_init(&dev->kref);

//kref为计数器,该函数初始化计数器为1。

sema_init(&dev->limit_sem,

WRITES_IN_FLIGHT);

//初始化semaphore为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));

dev->interface = 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);

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);

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;

}

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);

/ *

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);

}

static void skel_draw_down(struct usb_skel *dev)

{

int

time;

time =

usb_wait_anchor_empty_timeout(&dev->submitted,

1000);

if

(!time)

{

usb_kill_anchored_urbs(&dev->submitted);

}

usb_kill_urb(dev->bulk_in_urb);

}

static int skel_suspend(struct usb_interface *intf,

pm_message_t message)

{

struct

usb_skel *dev = usb_get_intfdata(intf);

if

(!dev)

{

return 0;

}

skel_draw_down(dev);

return

0;

}

static int skel_resume(struct usb_interface *intf)

{

return

0;

}

static int skel_pre_reset(struct usb_interface *intf)

{

struct

usb_skel *dev = usb_get_intfdata(intf);

mutex_lock(&dev->io_mutex);

skel_draw_down(dev);

return

0;

}

static int skel_post_reset(struct usb_interface *intf)

{

struct

usb_skel *dev = usb_get_intfdata(intf);

/ * we

are sure no URBs are active - no locking needed * /

dev->errors = -EPIPE;

mutex_unlock(&dev->io_mutex);

return

0;

}

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,

};

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;

}

static void __exit usb_skel_exit(void)

{

/ *

deregister this driver with the USB subsystem * /

usb_deregister(&skel_driver);

//取消设备注册

}

module_init(usb_skel_init);  //设定初始化函数

module_exit(usb_skel_exit);  //设定去初始化函数

MODULE_LICENSE("GPL");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值