I want to hack Linux USB framework and how it works. So I started my work by reading Linux Device Drivers(LDD). Firstly I present some notes from LDD and then we would be playing around with some simple USB Codes that I found on Web.This is how I learn things.I KNOW THIS IS BORING BUT ITS IMPORTANT TO KNOW
-USB has 4 cables power,ground ,transmit and receive
-there are 2 types of usb devices host and device.
-USB drivers live between different kernel subsystem(block,net,char etc) and USB hardware controller.
-link between USB Driver and USB core is called interface.
-devices consist of configurations, interfaces, and endpoints and how USB drivers
bind to USB interfaces, not the entire USB device.
-zero or more endpoint makes interface,many interface make configuration.each interface as its own driver.
Endpoint:
-The most basic form of USB communication is though something called an endpoint.
-Endpoint are uni-direction.They can be in or out.They carry data.
types of endpoint
CONTROL
-Control endpoints are used to allow access to different parts of the USB device.
-used for configuring the device, retrieving information about the device, sending commands to the device, or retrieving status reports about the device
- small in size.
-every USB device has “endpoint 0” that is used by the USB core to configure
the device at insertion time
INTERRUPT
-Interrupt endpoints transfer small amounts of data at a fixed rate every time the
USB host asks the device for data.
-do not tranfer large data
- transfers are guaranteed by the USB protocol to always have enough reserved bandwidth to make it through
BULK
-Bulk endpoints transfer large amounts of data.
-If there is not enough room on the bus to send the whole BULK packet, it is split up across multiple transfers to or from the device.
ISOCHRONOUS
Isochronous endpoints also transfer large amounts of data, but the data is not
always guaranteed to make it through.
-can handle loss of data.
-Control and bulk endpoints are used for asynchronous data transfers
-USB endpoints are described in the kernel with the structure struct usb_host_endpoint.
-struct usb_endpoint_descriptor has real endpoint information
*** The fields of this structure that drivers care about are:
bEndpointAddress- address/ IN and OUT
bmAttributes-type of endpoint.
wMaxPacketSize- max size that endpoint can handle
bInterval-that is, the time(milisec) between interrupt requests for the endpoint.
Interfaces
-USB endpoints are bundled up into interfaces.
-USB interface handles only one type of logical connection
-USB interface are described in
1)struct usb_interface structure.this structure core pass to driver and hence driver has control on device
struct usb_host_interface *altsetting-contains all alternate setting for that selected interface which contain set of end point described in
2)struct usb_host_endpoint structure.
-unsigned num_altsetting
The number of alternate settings pointed to by the altsetting pointer.
3)struct usb_host_interface *cur_altsetting
gives current setting
4)int minor
gives minor number
Configurations-USB interfaces are themselves bundled up into configurations.
-describes configuration with structure struct usb_host_config
Practical:
-to check usb device on your linux type following command
$lsusb
-for detail description of usb device type
$lsusb -v
USB Urbs
-The USB code in the Linux kernel communicates with all USB devices using some-
thing called a urb
-sends data in asynchronous manner
-urb lifecycle
1.parents-USB device drivers
2.assigned-to endpoint
3.submitted to -core and host controller
4.processed by-host controller
5.end-host controller will notify device driver.
-urbs control api are only used when you are concert with throughput rate(streaming) if you want to transfer only data or control then more simple api are available
struct urb
1)struct usb_device *dev
pointer to device driver to which urb is send
2)unsigned int pipe
endpoint information to which urb is send
3)unsigned int transfer_flags
4)unsigned int transfer_flags
usb driver controls urb via this flags
URB_SHORT_NOT_OK:short read can be considered as error by usb core
URB_ISO_ASAP
URB_NO_TRANSFER_DMA_MAP
URB_NO_SETUP_DMA_MAP
URB_ASYNC_UNLINK
URB_NO_FSBR
URB_ZERO_PACKET
URB_NO_INTERRUPT
void *transfer_buffer:in order to properly access buffer it should have memory
dma_addr_t transfer_dma:buffer to be used to transfer data to USB device using DMA
int transfer_buffer_length:length of buffer transfer_buffer
unsigned char *setup_packet:setup packet for control buffer
usb_complete_t complete
Pointer to the completion handler function that is called by the USB core when
the urb is completely transferred or when an error occurs to the urb.
void *context
Pointer to a data blob that can be set by the USB driver.
int actual_length
When the urb is finished, this variable is set to the actual length of the data
either sent by the urb (for OUT urbs) or received by the urb (for IN urbs.)
int status
When the urb is finished, or being processed by the USB core, this variable is set
to the current status of the urb.
int start_frame
int interval
int number_of_packets
int error_count
struct usb_iso_packet_descriptor iso_frame_desc[0]
Creating and Destroying Urbs
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
void usb_free_urb(struct urb *urb);
Interrupt urbs
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,
void *context, int interval);
a helper function to properly initialize a urb to be
sent to a interrupt endpoint of a USB device:
Bulk urbsvoid usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,
void *context);
Control urbs
void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, unsigned char *setup_packet,
void *transfer_buffer, int buffer_length,
usb_complete_t complete, void *context);
Isochronous urbs
they dont have function as above so they must be initialized "by hand"
Submitting Urbsint usb_submit_urb(struct urb *urb, int mem_flags);
Completing Urbs: The Completion Callback Handler
Canceling Urbs
int usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);
Now this was all about urb in usb.It is used when we need high throughput.Now let us discuss about writing usb device driver
Writing a USB Driver
struct usb_device_id
-__u16 match_flags
This field is usually never set directly but is initialized by the USB_DEVICE type macros described later.
-__u16 idVendor
-__u16 idProduct
-__u16 bcdDevice_lo
-__u16 bcdDevice_hi
-__u8 bInterfaceClass
-__u8 bInterfaceSubClass
-__u8 bInterfaceProtocol
-kernel_ulong_t driver_info
various macros
USB_DEVICE(vendor, product)
USB_DEVICE_VER(vendor, product, lo, hi)
USB_DEVICE_INFO(class, subclass, protocol)
USB_INTERFACE_INFO(class, subclass, protocol)
Registering a USB Driver
struct usb_driver
-struct module *owner
-const char *name
-const struct usb_device_id *id_table
-int (*probe) (struct usb_interface *intf, const struct usb_device_id *id)
-void (*disconnect) (struct usb_interface *intf)
static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton",
.id_table = skel_table,
.probe = skel_probe,
.disconnect = skel_disconnect,
};
some more callback function
int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf)
int (*suspend) (struct usb_interface *intf, u32 state)
int (*resume) (struct usb_interface *intf)
Submitting and Controlling a Urb
usb_control_msg
int usb_control_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype,
__u16 value, __u16 index,
void *data, __u16 size, int timeout);
usb_bulk_msgint usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length,
int timeout);
Now we are almost done.Lets write a simple usb program for pen drive
Practical:
1)make a directory(folder) any where in user space
eg: mkdir myusb
2)In that directory write following program
/* myusb.c*/
//*************************************************************//
//***********************************************************************//
3)make a Makfile
obj-m += myusb.o
all:
gcc -C /lib/modules/$(shell uname -r)/build M=${PWD} modules
clean:
gcc -C /lib/modules/$(shell uname -r)/build M=${PWD} clean
4)save makefile and type make.This would generate .ko file
$make
5)now load this module to kernel with insmod
$insmod myusb.ko
6)Congratulation you have made a pen drive usb module
$lsusb
7)You can unload module
$rmmod myusb.ko
-We now write a code to get information of usb device
//*********************************************************************//
//**********************************************************************//
-do all the steps as above and then load module to kernel
-we are almost done.I would add more to this blog as I find and learn, till then you try out all this thing.
-keep smiling .....cya
-USB has 4 cables power,ground ,transmit and receive
-there are 2 types of usb devices host and device.
-USB drivers live between different kernel subsystem(block,net,char etc) and USB hardware controller.
-link between USB Driver and USB core is called interface.
-devices consist of configurations, interfaces, and endpoints and how USB drivers
bind to USB interfaces, not the entire USB device.
-zero or more endpoint makes interface,many interface make configuration.each interface as its own driver.
Endpoint:
-The most basic form of USB communication is though something called an endpoint.
-Endpoint are uni-direction.They can be in or out.They carry data.
types of endpoint
CONTROL
-Control endpoints are used to allow access to different parts of the USB device.
-used for configuring the device, retrieving information about the device, sending commands to the device, or retrieving status reports about the device
- small in size.
-every USB device has “endpoint 0” that is used by the USB core to configure
the device at insertion time
INTERRUPT
-Interrupt endpoints transfer small amounts of data at a fixed rate every time the
USB host asks the device for data.
-do not tranfer large data
- transfers are guaranteed by the USB protocol to always have enough reserved bandwidth to make it through
BULK
-Bulk endpoints transfer large amounts of data.
-If there is not enough room on the bus to send the whole BULK packet, it is split up across multiple transfers to or from the device.
ISOCHRONOUS
Isochronous endpoints also transfer large amounts of data, but the data is not
always guaranteed to make it through.
-can handle loss of data.
-Control and bulk endpoints are used for asynchronous data transfers
-USB endpoints are described in the kernel with the structure struct usb_host_endpoint.
-struct usb_endpoint_descriptor has real endpoint information
*** The fields of this structure that drivers care about are:
bEndpointAddress- address/ IN and OUT
bmAttributes-type of endpoint.
wMaxPacketSize- max size that endpoint can handle
bInterval-that is, the time(milisec) between interrupt requests for the endpoint.
Interfaces
-USB endpoints are bundled up into interfaces.
-USB interface handles only one type of logical connection
-USB interface are described in
1)struct usb_interface structure.this structure core pass to driver and hence driver has control on device
struct usb_host_interface *altsetting-contains all alternate setting for that selected interface which contain set of end point described in
2)struct usb_host_endpoint structure.
-unsigned num_altsetting
The number of alternate settings pointed to by the altsetting pointer.
3)struct usb_host_interface *cur_altsetting
gives current setting
4)int minor
gives minor number
Configurations-USB interfaces are themselves bundled up into configurations.
-describes configuration with structure struct usb_host_config
Practical:
-to check usb device on your linux type following command
$lsusb
-for detail description of usb device type
$lsusb -v
USB Urbs
-The USB code in the Linux kernel communicates with all USB devices using some-
thing called a urb
-sends data in asynchronous manner
-urb lifecycle
1.parents-USB device drivers
2.assigned-to endpoint
3.submitted to -core and host controller
4.processed by-host controller
5.end-host controller will notify device driver.
-urbs control api are only used when you are concert with throughput rate(streaming) if you want to transfer only data or control then more simple api are available
struct urb
1)struct usb_device *dev
pointer to device driver to which urb is send
2)unsigned int pipe
endpoint information to which urb is send
3)unsigned int transfer_flags
4)unsigned int transfer_flags
usb driver controls urb via this flags
URB_SHORT_NOT_OK:short read can be considered as error by usb core
URB_ISO_ASAP
URB_NO_TRANSFER_DMA_MAP
URB_NO_SETUP_DMA_MAP
URB_ASYNC_UNLINK
URB_NO_FSBR
URB_ZERO_PACKET
URB_NO_INTERRUPT
void *transfer_buffer:in order to properly access buffer it should have memory
dma_addr_t transfer_dma:buffer to be used to transfer data to USB device using DMA
int transfer_buffer_length:length of buffer transfer_buffer
unsigned char *setup_packet:setup packet for control buffer
usb_complete_t complete
Pointer to the completion handler function that is called by the USB core when
the urb is completely transferred or when an error occurs to the urb.
void *context
Pointer to a data blob that can be set by the USB driver.
int actual_length
When the urb is finished, this variable is set to the actual length of the data
either sent by the urb (for OUT urbs) or received by the urb (for IN urbs.)
int status
When the urb is finished, or being processed by the USB core, this variable is set
to the current status of the urb.
int start_frame
int interval
int number_of_packets
int error_count
struct usb_iso_packet_descriptor iso_frame_desc[0]
Creating and Destroying Urbs
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
void usb_free_urb(struct urb *urb);
Interrupt urbs
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,
void *context, int interval);
a helper function to properly initialize a urb to be
sent to a interrupt endpoint of a USB device:
Bulk urbsvoid usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,
void *context);
Control urbs
void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, unsigned char *setup_packet,
void *transfer_buffer, int buffer_length,
usb_complete_t complete, void *context);
Isochronous urbs
they dont have function as above so they must be initialized "by hand"
Submitting Urbsint usb_submit_urb(struct urb *urb, int mem_flags);
Completing Urbs: The Completion Callback Handler
Canceling Urbs
int usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);
Now this was all about urb in usb.It is used when we need high throughput.Now let us discuss about writing usb device driver
Writing a USB Driver
struct usb_device_id
-__u16 match_flags
This field is usually never set directly but is initialized by the USB_DEVICE type macros described later.
-__u16 idVendor
-__u16 idProduct
-__u16 bcdDevice_lo
-__u16 bcdDevice_hi
-__u8 bInterfaceClass
-__u8 bInterfaceSubClass
-__u8 bInterfaceProtocol
-kernel_ulong_t driver_info
various macros
USB_DEVICE(vendor, product)
USB_DEVICE_VER(vendor, product, lo, hi)
USB_DEVICE_INFO(class, subclass, protocol)
USB_INTERFACE_INFO(class, subclass, protocol)
Registering a USB Driver
struct usb_driver
-struct module *owner
-const char *name
-const struct usb_device_id *id_table
-int (*probe) (struct usb_interface *intf, const struct usb_device_id *id)
-void (*disconnect) (struct usb_interface *intf)
static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton",
.id_table = skel_table,
.probe = skel_probe,
.disconnect = skel_disconnect,
};
some more callback function
int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf)
int (*suspend) (struct usb_interface *intf, u32 state)
int (*resume) (struct usb_interface *intf)
Submitting and Controlling a Urb
usb_control_msg
int usb_control_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype,
__u16 value, __u16 index,
void *data, __u16 size, int timeout);
usb_bulk_msgint usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length,
int timeout);
Now we are almost done.Lets write a simple usb program for pen drive
Practical:
1)make a directory(folder) any where in user space
eg: mkdir myusb
2)In that directory write following program
/* myusb.c*/
//*************************************************************//
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
static
int
pen_probe(
struct
usb_interface *interface,
const
struct
usb_device_id *id)
{
printk(KERN_INFO
"Pen drive (%04X:%04X) plugged\n"
, id->idVendor, id->idProduct);
return
0;
}
static
void
pen_disconnect(
struct
usb_interface *interface)
{
printk(KERN_INFO
"Pen drive removed\n"
);
}
static
struct
usb_device_id pen_table[] =
{
{ USB_DEVICE(0x058F, 0x6387) },
{}
/* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, pen_table);
static
struct
usb_driver pen_driver =
{
.name =
"pen_driver"
,
.id_table = pen_table,
.probe = pen_probe,
.disconnect = pen_disconnect,
};
static
int
__init pen_init(
void
)
{
return
usb_register(&pen_driver);
}
static
void
__exit pen_exit(
void
)
{
usb_deregister(&pen_driver);
}
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE(
"GPL"
);
MODULE_AUTHOR(
"Jay Kothari"
);
MODULE_DESCRIPTION(
"USB Pen Registration Driver"
);
3)make a Makfile
obj-m += myusb.o
all:
gcc -C /lib/modules/$(shell uname -r)/build M=${PWD} modules
clean:
gcc -C /lib/modules/$(shell uname -r)/build M=${PWD} clean
4)save makefile and type make.This would generate .ko file
$make
5)now load this module to kernel with insmod
$insmod myusb.ko
6)Congratulation you have made a pen drive usb module
$lsusb
7)You can unload module
$rmmod myusb.ko
-We now write a code to get information of usb device
//*********************************************************************//
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
static
struct
usb_device *device;
static
int
pen_probe(
struct
usb_interface *interface,
const
struct
usb_device_id *id)
{
struct
usb_host_interface *iface_desc;
struct
usb_endpoint_descriptor *endpoint;
int
i;
iface_desc = interface->cur_altsetting;
printk(KERN_INFO
"Pen i/f %d now probed: (%04X:%04X)\n"
,
iface_desc->desc.bInterfaceNumber, id->idVendor, id->idProduct);
printk(KERN_INFO
"ID->bNumEndpoints: %02X\n"
,
iface_desc->desc.bNumEndpoints);
printk(KERN_INFO
"ID->bInterfaceClass: %02X\n"
,
iface_desc->desc.bInterfaceClass);
for
(i = 0; i < iface_desc->desc.bNumEndpoints; i++)
{
endpoint = &iface_desc->endpoint[i].desc;
printk(KERN_INFO
"ED[%d]->bEndpointAddress: 0x%02X\n"
,
i, endpoint->bEndpointAddress);
printk(KERN_INFO
"ED[%d]->bmAttributes: 0x%02X\n"
,
i, endpoint->bmAttributes);
printk(KERN_INFO
"ED[%d]->wMaxPacketSize: 0x%04X (%d)\n"
,
i, endpoint->wMaxPacketSize, endpoint->wMaxPacketSize);
}
device = interface_to_usbdev(interface);
return
0;
}
static
void
pen_disconnect(
struct
usb_interface *interface)
{
printk(KERN_INFO
"Pen i/f %d now disconnected\n"
,
interface->cur_altsetting->desc.bInterfaceNumber);
}
static
struct
usb_device_id pen_table[] =
{
{ USB_DEVICE(0x058F, 0x6387) },
{}
/* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, pen_table);
static
struct
usb_driver pen_driver =
{
.name =
"pen_driver"
,
.probe = pen_probe,
.disconnect = pen_disconnect,
.id_table = pen_table,
};
static
int
__init pen_init(
void
)
{
return
usb_register(&pen_driver);
}
static
void
__exit pen_exit(
void
)
{
usb_deregister(&pen_driver);
}
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE(
"GPL"
);
MODULE_AUTHOR(
"Jay Kothari");
MODULE_DESCRIPTION(
"USB Pen Info Driver"
);
-do all the steps as above and then load module to kernel
-we are almost done.I would add more to this blog as I find and learn, till then you try out all this thing.
-keep smiling .....cya
No comments:
Post a Comment