免费注册 查看新帖 |

Chinaunix

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

Linux USB Driver Basics [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-05-20 22:56 |只看该作者 |倒序浏览
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.
      write
      static 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!
               
               
               

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

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP