免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1293 | 回复: 0
打印 上一主题 下一主题

好文共享-转载)Linux USB驱动程序基础 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-04-10 23:55 |只看该作者 |倒序浏览

[color="#02368d"]
Linux USB Driver Basics
Introduction
Drivers are software components that operating systems use to provide
hardware specific services to applications. This webpage attempts to document
the basics of USB drivers on Linux. The goal here is to provide you with a basic
understanding of how USB device drivers on Linux work.
The File Abstraction
Linux, like other Unix derived operating systems, tries to make applications
simpler by providing a common hardware abstraction, the File. Essentially,
interactions with almost all hardware can be abstracted into the same interface
that the operating system provides for manipulating files. Hence, you can "Open"
a driver, "Read" a driver, "Write" to a driver and "Close" a driver. This
abstraction extends all the way into the application, who can use the same
system calls that it uses to open and manipulate files to talk to hardware.
The way this works is that the kernel creates nodes in the file system,
typically located in /dev that represent a particular interface to a driver. To
talk to a particular driver an application will open the /dev entry associated
with that driver. The file descriptor returned by that open is passed to all
future system calls (read, write, select), and is eventually passed to
close.
Besides drivers, Unix also uses this file paradigm for various different
kinds of IPC, and even for socket communications over a network.
It is quite surprising how many completely different kinds of hardware can be
modeled with just 4 operations (open, close, read and write). That said, their
is another very important system call that unix applications developers commonly
use, select(). The select() system call allows applications to poll and
determine whether data could be read from, or written to a file descriptor
without blocking.
Sometimes a piece of hardware provides some functionality that doesn't fit
well into this file centric paradigm. To allow for this, unix applications
typically make use of the ioctl() system call. This call takes a numeric value
that is essentially an identifier for a specific piece of functionality in the
driver.
The Job of the Device Driver
Simply stated, it is the job of the driver to provide functions that the
kernel can use to implement this file programming paradigm. Applications do not
directly call functions in the driver, instead they call functions in libc that
eventually call into the kernel (via the system call interface). The kernel
implementations of these system calls call into your driver.
As you might expect, this means that you're going to be implementating open,
close, read and write inside your driver. To implement select(), the kernel
needs you to implement a method in your driver called poll(). Other than these,
their are only a handfull of other functions that need to be implemented, and
you have a driver!
Loading a Driver
Linux supports essentially two kinds of driver development models: 1)
Compiled into the kernel, 2) Dynamically loadable driver modules. If configured
properly, option 2 has the advantage that drivers can be loaded automatically by
the kernel only when they are actually needed to service an application request.
Option 1 presents a logically cleaner, and slightly faster scenario.
The choice is up to you. That said, we will now cover the basics of loading
and unloading a kernel module into a running kernel.
First, understand that a Linux kernel driver cannot be dynamically linked
against any librarys (not even libc, which many programmers dont even know they
are linking against). The only code external to your driver that your allowed to
make use of are functions that are implemented within the kernel. In fact,
kernel modules are never "Linked", instead they are relocatable object files,
.o's or sometimes .ko's.
The following command is used to install a driver into a running kernel:
[td@objective ~]$ insmod driver.ko
       
The following command is used to show what drivers are currently loaded into
the kernel:
[td@objective ~]$ lsmod
Module                  Size  Used by
md5                     4033  1
ipv6                  263105  22
ipt_REJECT              5441  1
ipt_state               1857  5
ip_conntrack           41369  1 ipt_state
iptable_filter          2881  1
ip_tables              19521  3 ipt_REJECT,ipt_state,iptable_filter
video                  15685  0
button                  4033  0
battery                 9285  0
ac                      4805  0
uhci_hcd               35025  0
ehci_hcd               40013  0
hw_random               5973  0
i2c_i801                8653  0
i2c_core               21313  1 i2c_i801
snd_intel8x0           34177  0
snd_ac97_codec         74937  1 snd_intel8x0
snd_pcm_oss            50673  0
snd_mixer_oss          17729  1 snd_pcm_oss
snd_pcm                98889  3 snd_intel8x0,snd_ac97_codec
snd_timer              32837  1 snd_pcm
snd                    57285  6 snd_intel8x0,snd_ac97_codec
soundcore              10785  1 snd
snd_page_alloc          9669  2 snd_intel8x0,snd_pcm
e1000                 102573  0
dm_snapshot            17413  0
dm_zero                 2113  0
dm_mirror              25645  0
ext3                  130633  2
jbd                    83161  1 ext3
dm_mod                 57333  6 dm_snapshot,dm_zero,dm_mirror
ata_piix                9413  0
libata                 46917  1 ata_piix
sd_mod                 20289  0
scsi_mod              146313  2 libata,sd_mod
       
The following command is used to remove a driver from a running kernel:
[td@objective ~]$ rmmod driver.ko
       
Since you never link your driver, you will not know about unresolved symbols
until you actually load your driver into the kernel. It is during the "insmod"
process that the kernel binds all of the calls to kernel functions in your code
to their actual current locations in memory. This process is called "resolving
symbol dependancies".
Understanding the Universal Serial Bus
From the highest level
As I'm sure you know from your own use of USB, the USB bus, besides offering
communications also offers power to devices connected to it. The USB can offer
up to 500mA for the devices connected to it. Devices that need more than this
can be self powered.
Besides this, your probably aware of the fact that the USB can be expanded
with hubs.
How hardware is about to make your life easier
Thankfully, your driver will not have to communicate directly on the
Universal Serial Bus. Instead, your driver will communicate to the USB host
controller, which will communicate on the bus on your behalf. The USB host
controller is a piece of hardware that acts as a focal point for all of the
CPU's interaction with the USB hardware. It hides most of the complexity of
dealing with USB and also protects the USB hardware from potentially badly
written driver software that might otherwise affect the whole bus.
Their are two kinds of host controller hardware commonly in use, UHCI
(Universal Host Controller Interface) and OHCI (Open Host Controller Interface).
The good news is that both of these host controllers present the same interface
to your driver, so you really dont have to care about what kind of host
controller hardware is present. As you might expect, UHCI & OHCI hardware
has its own driver, that thankfully you will not need to touch. It will be the
job of your driver to communicate (indirectly) with the host controller driver,
to configure, read from or write to devices on the USB.
Types of Communications on the USB
USB devices have the full range of different speed, latency and reliability
requirements. Things like mice and keyboard transfer only small amounts of data,
relatively rarely, but they care a lot about latency. No one like a slow
keyboard.
On the other hand, USB webcams often transfer compressed MPEG video, which is
extremely high bandwidth, but because MPEG was designed to be transmitted over
lossy communications channels, it is OK if the occasional packet is lost.
To support these differing requirements, USB supports a number of
communications types.

  • Control Transfers - Control transfers are used when you need
    reliable (the data MUST get their) communications and you are sending a very
    small amount of data. Essentially, these transfers are commands, and their are a
    few commands that every device is required to support (GET_STATUS,
    CLEAR_FEATURE, SET_FEATURE, SET_ADDRESS, GET_DESCRIPTOR, SET_DESCRIPTOR,
    GET_CONFIGURATION, SET_CONFIGURATION, GET_INTERFACE, SET_INTERFACE,
    SYNCH_FRAME).

  • Bulk Transfers - Bulk transfers are used when you need
    reliable (the data MUST get their) communications and you are sending large
    amounts of data.

  • Interrupt Transfers - Interrupt transfers are simmilar to
    bulk transfers except they are configured to occur automatically at some
    interval. These types of transfers are useful for devices that stream constant
    data of the USB.

  • Isochronous Transfers - Isochronous transfers are very fast,
    and have guaranteed bus bandwidth, but they have no reliability. This transfer
    type seems to have been designed with MPEG in mind.

The code that you write is going to have to specify to the host controller
what types of communcations you want to use. Thankfully, an extremely wide range
of device types fit nicely into the above types of data transfers.
The heirarchical view of the USB
If you think about what I have described so far, it should be fairly apparent
that since you will be dealing only indirectly with actual USB devices and
instead be communicating through a host controller, that host controller must
have some kind of data model for the devices that are connected to it on the USB
bus. It must present some description of the devices connected, and their
capabilities.
The capabilites of a USB device are modeled heirarchically by the host
controller. Your driver will communicate to a USB device by making requests to
nodes of this heirarchy. To fully understand the reasoning for all of the layers
of the heirarchy I am about to tell you about please consider how generic a host
controller must be, given how wide ranging the functionality of USB hardware can
be.
When a USB is first plugged into the bus it is detected by the host
controller. The host controller sends it the GET_DESCRIPTOR command and
retrieves what is called the device descriptor. This structure is itself of a
heirarchical nature and describes the features and capabilities of a particular
device.
This "Device Descriptor" has 1 or more "Configuration Descriptors", which in
turn has 1 or more "Interfaces", which in turn has 0 to N "Endpoints". I know
this is a lot to swallow, but its the genericness and flexibility of this
structure that lets USB work for so many different types of devices.

Remember, we're talking about 1 USB device here. Every device you have
connected to your bus has a structure like this. Simple devices may not have
multiple configurations or interfaces. Almost all devices will have at least 1
endpoint however, which means they will also have at least 1 interface and 1
configuration. The reason for this is that the endpoint is typically the part of
this tree that the application interacts with.
The Importance of Endpoints
Endpoints are logically the parts of this tree that you write to or read
from. If your goal is to read the mpeg coming out of a USB webcam, then you will
be reading that data from an Endpoint. We talked a little about communications
types above (remember Control, Bulk, Interrupt and Isochronous?) but a key to
understanding USB is to understand that Endpoints support a particular type of
communication. If you need to do bulk transfers to your device you'll need to
find an endpoint on your device that supports bulk transfers. If you dont find
it, then your out of luck. You cannot create endpoints, what you find represents
the capabilities that this particular USB hardware is presenting to the host
controller on your machine.
Endpoints can also be used as a secondary form of device identification. We
will discuss the primary form of device identification employed shortly, but for
now consider that you could identify a device by the presence of a particular
heirarchy of configurations, interfaces and endpoints. In fact, the presence of
the right number and types of nodes in this heirarchy is arguably the most
important thing you care about. If a device identifies itself to you as being of
a particular kind, that does you no good if the hardware doesn't support the
endpoints you expect.
Coding details
USB Identification
All USB devices have two very important numbers that serve as our primary
form of device identification. These numbers are the devices Vendor
ID
and Product ID. The idea is that if a company would
mark all of the USB devices it creates with an id that identifies the company,
it should be really easy to determine if a particular device was made by your
company. Because a company might create many different kinds of USB devices,
their is a second number, the Product ID. This number can be used to identify a
particular product.
When a device is plugged into the USB bus, the kernel query's the installed
drivers to find out which supports this particular piece of hardware.
Essentially, the kernel calls a method in your driver called "probe". This
function is passed the vendor and product id's, as well as a structure used to
check for the availability of particular nodes in the devices USB heirarchy. If
everything checks out, your driver should eventually call usb_register_dev() to
let the kernel know that you do indeed handle this particular piece of
hardware.
How it all begins
Now that you know some of the basic fundamentals, it's time we discuss some
of the details in a little more depth. The first driver code that gets called by
the kernel is your module init routine. Likewise, when your driver is unloaded
(via rmmod), the last function that is called is your modules exit routine. It
is the job of your init function to register a struct with the kernel called
'usb_driver'. This struct has information like your drivers name, (indirectly
through another structure) the vendor and product id's of the usb hardware your
driver services, and a pointer to a couple of functions called probe and
disconnect (which we'll talk about shortly).
#define USB_VENDOR_ID 0x22FF
#define USB_PRODUCT_ID_1 0x1111
#define USB_PRODUCT_ID_2 0x2222
static struct usb_device_id generic_usb_id_table [] =
{
  {
    USB_DEVICE(USB_VENDOR_ID,USB_PRODUCT_ID_1)
  },
  {
    USB_DEVICE(USB_VENDOR_ID,USB_PRODUCT_ID_2)
  }
  {
  }
};
MODULE_DEVICE_TABLE (usb, generic_usb_id_table);
static struct usb_driver generic_usb_driver =
{
  .owner = THIS_MODULE,
  .name = "generic_usb_driver",
  .id_table = generic_usb_id_table,
  .probe = generic_usb_probe,
  .disconnect = generic_usb_disconnect
};
static int __init generic_usb_driver_init( void )
{
  return usb_register( &generic_usb_driver );
}
static void __exit generic_usb_driver_exit( void )
{
  usb_deregister( &generic_usb_driver )
}
module_init (generic_usb_driver_init);
module_exit (generic_usb_driver_exit);
      
Their is a bit of macro magic here at the end, but the basic idea is pretty
simple. We fill out a structure and we implement some standard entry points that
register and unregister that structure. What we have done at this point is told
the kernel what vendor and product id's we handle, and what functions it should
call when devices that match those id's are added or removed from the USB
bus.
Getting Probed by the kernel
When a device is plugged into the USB bus, the USB host controller hardware
generates an interrupt that wakes up the host controller driver. Eventually, the
kernel finds the usb driver associated with the vendor and product id of the new
device. It then calls that drivers probe method. When a device is removed, that
also generates an interrupt and eventually, a call to your drivers disconnect
function.
The drivers probe method is very important. First, consider that most USB
drivers dont really run all the time. They are called by the kernel when they
have work to do (such as reading and writing). Since it is usually possible for
more than 1 kind of the same USB device to be plugged in, drivers cannot use
global data to store information about their state. Instead they must declare
some type of structure, which they allocate and populate an instance of when
they are probe()'d by the kernel and they determine that they really do handle
the hardware in question. Theirfore, it is the job of your probe function to
examine the passed usb_interface structure to make sure it has all of the
endpoints you need to function. Once that determination has been made you should
kmalloc() your instance data structure, populate it (perhaps storing endpoint
addresses or pre-allocating URB's) and return it by calling usb_set_intfdata()
on the passed interface object. This instance data is passed back to you when
the kernel calls into your driver from then on.
Another critical thing you have to do in your probe function is call
usb_register_dev, passing a pointer to an instance of a usb_class_driver struct.
This struct (the second structure down) is populated with a number of important
fields.
static struct file_operations generic_fops =
{
  .owner = THIS_MODULE,
  .read = generic_read,
  .write = generic_write,
  .poll = generic_poll,
  .ioctl = generic_ioctl
};
static struct usb_class_driver generic_class =
{
  .name = "generic_%d",
  .fops = &generic_fops,
  .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
  .minor_base = 42
};
      
The first structure above contains pointers to all of the functions used to
support the file paradigm for this driver (read, write, poll and ioctl). The
second structure contains a pointer to the first structure. It also contains a
field that is used by the kernel when it creates your device nodes under /dev.
The .name field of the usb_class_driver structure is used as a format string,
combined with a number, to uniquely identify all of the devices on the USB bus
that are serviced by your particular driver.
writestatic ssize_t generic_write( struct file* file,
                              const char __user* user_buffer,      
                              size_t count,
                              loff_t* ppos );
      
Probably the first thing you'll want to do in your write function is access
the instance data for this device that you allocated in probe(). You do this by
looking at the "private_data" field of the struct file* passed to the function.
Basically, an URB is the unit of communication on the USB bus. Writing or
reading from a device on the USB consists of creating and submitting properly
constructed URBs. Given that, the next thing you need to do is call
usb_alloc_urb() to create the urb for this write operation. Next, you need to
create a kernel memory buffer, attach it to the URB, copy the write data from
user space into your kernel buffer, finalize the initialization of the URB and
finally call usb_submit_urb() to send it.
char* buf = NULL;
struct urb* urb = usb_alloc_urb( 0, GFP_KERNEL );
buf = usb_buffer_alloc( dev->udev, count, GFP_KERNEL, &urb->transfer_dma );
if( buf )
{
  copy_from_user( buf, user_buffer, count );
  usb_fill_bulk_urb( urb, dev->udev,
                     usb_sndbulkpipe(dev->udev,dev->bulk_out_endpointAddr),
                     buf,
                     count,
                     generic_write_bulk_callback,
                     dev );
  usb_submit_urb( urb, GFP_KERNEL );
  usb_free_urb( urb );
}
      
Not the best code in the world, but shows you the steps of what your going to
have to do.
read
Read is a lot simpler than write. Again, the first thing you'll probably want
to do is get your instance data from the private_data field of the passed file*
structure. Once thats done, you can get to the fun stuff:
      usb_bulk_msg( dev->udev,
                    usb_recvbulkpipe( dev->udev, dev->bulk_in_endpointAddr),
                    dev->bulk_in_buffer,
                    min(dev->bulk_in_size,count),
                    &count, HZ*2 );
      copy_to_user( buffer, dev->bulk_in_buffer, count );
      
The heart of your read() implementation may be as simple as this.
ioctl
ioctl is the back door. Essentially, ioctl exists because sometimes not every
piece of hardware can operate exactly like a file in all cases. Sometimes, you
just need to request a specific piece of functionality be performed on your
behalf in the driver. You'd also like to be able to pass parameters to such a
function, and be returned some kind of indication of the success of such a
call.
static int generic_reader_ioctl( struct inode* inode,
                                 struct file* file,
                                 unsigned in cmd,
                                 unsigned long arg )
{
  struct usb_generic_instance_data* dev =
    (struct usb_generic_instance_data*)file->private_data;
  switch( cmd )
  {
    case GET_STATE:
      return dev->global_state;
    break;
    default:
    break;
  }
  return -1;
}
   
The above is a complete ioctl() function that implements a single logical
function, GET_STATE. Any number of additional function can be added here simply
by adding cases to the switch statement.
exit(0);This article is a bit longer than I had hoped it would be. The
goal was a short introduction that would give you a place to start in your
understanding of the Linux USB drivers. I hope I have done at least that much!
[color="#000099"]原文地址
http://www.linuxtecharticles.com/viewarticle.php?name=Basic%20Linux%20USB%20Drivers

               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/63379/showart_526368.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP