요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. _writing-usb-driver:
==========================
Writing USB Device Drivers
==========================
:Author: Greg Kroah-Hartman
Introduction
============
The Linux USB subsystem has grown from supporting only two different
types of devices in the 2.2.7 kernel (mice and keyboards), to over 20
different types of devices in the 2.4 kernel. Linux currently supports
almost all USB class devices (standard types of devices like keyboards,
mice, modems, printers and speakers) and an ever-growing number of
vendor-specific devices (such as USB to serial converters, digital
cameras, Ethernet devices and MP3 players). For a full list of the
different USB devices currently supported, see Resources.
The remaining kinds of USB devices that do not have support on Linux are
almost all vendor-specific devices. Each vendor decides to implement a
custom protocol to talk to their device, so a custom driver usually
needs to be created. Some vendors are open with their USB protocols and
help with the creation of Linux drivers, while others do not publish
them, and developers are forced to reverse-engineer. See Resources for
some links to handy reverse-engineering tools.
Because each different protocol causes a new driver to be created, I
have written a generic USB driver skeleton, modelled after the
pci-skeleton.c file in the kernel source tree upon which many PCI
network drivers have been based. This USB skeleton can be found at
drivers/usb/usb-skeleton.c in the kernel source tree. In this article I
will walk through the basics of the skeleton driver, explaining the
different pieces and what needs to be done to customize it to your
specific device.
Linux USB Basics
================
If you are going to write a Linux USB driver, please become familiar
with the USB protocol specification. It can be found, along with many
other useful documents, at the USB home page (see Resources). An
excellent introduction to the Linux USB subsystem can be found at the
USB Working Devices List (see Resources). It explains how the Linux USB
subsystem is structured and introduces the reader to the concept of USB
urbs (USB Request Blocks), which are essential to USB drivers.
The first thing a Linux USB driver needs to do is register itself with
the Linux USB subsystem, giving it some information about which devices
the driver supports and which functions to call when a device supported
by the driver is inserted or removed from the system. All of this
information is passed to the USB subsystem in the :c:type:`usb_driver`
structure. The skeleton driver declares a :c:type:`usb_driver` as::
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,
};
The variable name is a string that describes the driver. It is used in
informational messages printed to the system log. The probe and
disconnect function pointers are called when a device that matches the
information provided in the ``id_table`` variable is either seen or
removed.
The fops and minor variables are optional. Most USB drivers hook into
another kernel subsystem, such as the SCSI, network or TTY subsystem.
These types of drivers register themselves with the other kernel
subsystem, and any user-space interactions are provided through that
interface. But for drivers that do not have a matching kernel subsystem,
such as MP3 players or scanners, a method of interacting with user space
is needed. The USB subsystem provides a way to register a minor device
number and a set of :c:type:`file_operations` function pointers that enable
this user-space interaction. The skeleton driver needs this kind of
interface, so it provides a minor starting number and a pointer to its
:c:type:`file_operations` functions.
The USB driver is then registered with a call to usb_register(),
usually in the driver's init function, as shown here::
static int __init usb_skel_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&skel_driver);
if (result < 0) {
pr_err("usb_register failed for the %s driver. Error number %d\n",
skel_driver.name, result);
return -1;
}
return 0;
}
module_init(usb_skel_init);
When the driver is unloaded from the system, it needs to deregister
itself with the USB subsystem. This is done with usb_deregister()
function::
static void __exit usb_skel_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&skel_driver);
}
module_exit(usb_skel_exit);
To enable the linux-hotplug system to load the driver automatically when
the device is plugged in, you need to create a ``MODULE_DEVICE_TABLE``.
The following code tells the hotplug scripts that this module supports a
single device with a specific vendor and product ID::
/* table of devices that work with this driver */
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, skel_table);
There are other macros that can be used in describing a struct
:c:type:`usb_device_id` for drivers that support a whole class of USB
drivers. See :ref:`usb.h <usb_header>` for more information on this.
Device operation
================
When a device is plugged into the USB bus that matches the device ID
pattern that your driver registered with the USB core, the probe
function is called. The :c:type:`usb_device` structure, interface number and
the interface ID are passed to the function::
static int skel_probe(struct usb_interface *interface,
const struct usb_device_id *id)
The driver now needs to verify that this device is actually one that it
can accept. If so, it returns 0. If not, or if any error occurs during
initialization, an errorcode (such as ``-ENOMEM`` or ``-ENODEV``) is
returned from the probe function.
In the skeleton driver, we determine what end points are marked as
bulk-in and bulk-out. We create buffers to hold the data that will be
sent and received from the device, and a USB urb to write data to the
device is initialized.
Conversely, when the device is removed from the USB bus, the disconnect
function is called with the device pointer. The driver needs to clean
any private data that has been allocated at this time and to shut down
any pending urbs that are in the USB system.
Now that the device is plugged into the system and the driver is bound
to the device, any of the functions in the :c:type:`file_operations` structure
that were passed to the USB subsystem will be called from a user program
trying to talk to the device. The first function called will be open, as
the program tries to open the device for I/O. We increment our private
usage count and save a pointer to our internal structure in the file
structure. This is done so that future calls to file operations will
enable the driver to determine which device the user is addressing. All
of this is done with the following code::
/* increment our usage count for the device */
kref_get(&dev->kref);
/* save our object in the file's private structure */
file->private_data = dev;
After the open function is called, the read and write functions are
called to receive and send data to the device. In the ``skel_write``
function, we receive a pointer to some data that the user wants to send
to the device and the size of the data. The function determines how much
data it can send to the device based on the size of the write urb it has
created (this size depends on the size of the bulk out end point that
the device has). Then it copies the data from user space to kernel
space, points the urb to the data and submits the urb to the USB
subsystem. This can be seen in the following code::
/* we can only write as much as 1 urb will hold */
size_t writesize = min_t(size_t, count, MAX_TRANSFER);
/* copy the data from user space into our urb */
copy_from_user(buf, user_buffer, writesize);
/* set up our urb */
usb_fill_bulk_urb(urb,
dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf,
writesize,
skel_write_bulk_callback,
dev);
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
dev_err(&dev->interface->dev,
"%s - failed submitting write urb, error %d\n",
__func__, retval);
}
When the write urb is filled up with the proper information using the
:c:func:`usb_fill_bulk_urb` function, we point the urb's completion callback
to call our own ``skel_write_bulk_callback`` function. This function is
called when the urb is finished by the USB subsystem. The callback
function is called in interrupt context, so caution must be taken not to
do very much processing at that time. Our implementation of
``skel_write_bulk_callback`` merely reports if the urb was completed
successfully or not and then returns.
The read function works a bit differently from the write function in
that we do not use an urb to transfer data from the device to the
driver. Instead we call the :c:func:`usb_bulk_msg` function, which can be used
to send or receive data from a device without having to create urbs and
handle urb completion callback functions. We call the :c:func:`usb_bulk_msg`
function, giving it a buffer into which to place any data received from
the device and a timeout value. If the timeout period expires without
receiving any data from the device, the function will fail and return an
error message. This can be shown with the following code::
/* do an immediate bulk read to get data from the device */
retval = usb_bulk_msg (skel->dev,
usb_rcvbulkpipe (skel->dev,
skel->bulk_in_endpointAddr),
skel->bulk_in_buffer,
skel->bulk_in_size,
&count, 5000);
/* if the read was successful, copy the data to user space */
if (!retval) {
if (copy_to_user (buffer, skel->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
The :c:func:`usb_bulk_msg` function can be very useful for doing single reads
or writes to a device; however, if you need to read or write constantly to
a device, it is recommended to set up your own urbs and submit them to
the USB subsystem.
When the user program releases the file handle that it has been using to
talk to the device, the release function in the driver is called. In
this function we decrement our private usage count and wait for possible
pending writes::
/* decrement our usage count for the device */
--skel->open_count;
One of the more difficult problems that USB drivers must be able to
handle smoothly is the fact that the USB device may be removed from the
system at any point in time, even if a program is currently talking to
it. It needs to be able to shut down any current reads and writes and
notify the user-space programs that the device is no longer there. The
following code (function ``skel_delete``) is an example of how to do
this::
static inline void skel_delete (struct usb_skel *dev)
{
kfree (dev->bulk_in_buffer);
if (dev->bulk_out_buffer != NULL)
usb_free_coherent (dev->udev, dev->bulk_out_size,
dev->bulk_out_buffer,
dev->write_urb->transfer_dma);
usb_free_urb (dev->write_urb);
kfree (dev);
}
If a program currently has an open handle to the device, we reset the
flag ``device_present``. For every read, write, release and other
functions that expect a device to be present, the driver first checks
this flag to see if the device is still present. If not, it releases
that the device has disappeared, and a ``-ENODEV`` error is returned to the
user-space program. When the release function is eventually called, it
determines if there is no device and if not, it does the cleanup that
the ``skel_disconnect`` function normally does if there are no open files
on the device (see Listing 5).
Isochronous Data
================
This usb-skeleton driver does not have any examples of interrupt or
isochronous data being sent to or from the device. Interrupt data is
sent almost exactly as bulk data is, with a few minor exceptions.
Isochronous data works differently with continuous streams of data being
sent to or from the device. The audio and video camera drivers are very
good examples of drivers that handle isochronous data and will be useful
if you also need to do this.
Conclusion
==========
Writing Linux USB device drivers is not a difficult task as the
usb-skeleton driver shows. This driver, combined with the other current
USB drivers, should provide enough examples to help a beginning author
create a working driver in a minimal amount of time. The linux-usb-devel
mailing list archives also contain a lot of helpful information.
Resources
=========
The Linux USB Project:
http://www.linux-usb.org/
Linux Hotplug Project:
http://linux-hotplug.sourceforge.net/
linux-usb Mailing List Archives:
https://lore.kernel.org/linux-usb/
Programming Guide for Linux USB Device Drivers:
https://lmu.web.psi.ch/docu/manuals/software_manuals/linux_sl/usb_linux_programming_guide.pdf
USB Home Page: https://www.usb.org
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Linux USB driver 역사와 skeleton의 목적
1-47Linux USB subsystem은 2.2.7 kernel에서 mouse와 keyboard 두 종류만 지원하던 단계에서 2.4 kernel의 20종 이상으로 성장했습니다. 현재는 keyboard, mouse, modem, printer, speaker 같은 거의 모든 USB class device와 USB-to-serial converter, digital camera, Ethernet device, MP3 player 같은 vendor-specific device를 폭넓게 지원합니다.
남은 미지원 장치는 대부분 vendor-specific protocol을 사용합니다. Vendor가 protocol을 공개하고 Linux driver 개발을 돕기도 하지만, 공개하지 않으면 developer가 reverse engineering으로 custom driver를 작성해야 합니다.
Protocol마다 새 driver가 필요하므로 Greg Kroah-Hartman은 PCI `pci-skeleton.c`와 유사한 generic USB skeleton을 만들었습니다. 예제는 `drivers/usb/usb-skeleton.c`에 있으며 특정 device에 맞춰 바꿔야 할 driver 구성 요소를 보여 줍니다.
USB driver를 작성하려면 USB protocol specification, Linux USB subsystem 구조와 USB Request Block(URB)을 먼저 이해해야 합니다. USB Working Devices List와 원문 Resources가 배경 자료를 제공합니다.
.. _writing-usb-driver:
==========================
Writing USB Device Drivers
==========================
:Author: Greg Kroah-Hartman
Introduction
============
The Linux USB subsystem has grown from supporting only two different
types of devices in the 2.2.7 kernel (mice and keyboards), to over 20
different types of devices in the 2.4 kernel. Linux currently supports
almost all USB class devices (standard types of devices like keyboards,
mice, modems, printers and speakers) and an ever-growing number of
vendor-specific devices (such as USB to serial converters, digital
cameras, Ethernet devices and MP3 players). For a full list of the
different USB devices currently supported, see Resources.
The remaining kinds of USB devices that do not have support on Linux are
almost all vendor-specific devices. Each vendor decides to implement a
custom protocol to talk to their device, so a custom driver usually
needs to be created. Some vendors are open with their USB protocols and
help with the creation of Linux drivers, while others do not publish
them, and developers are forced to reverse-engineer. See Resources for
some links to handy reverse-engineering tools.
Because each different protocol causes a new driver to be created, I
have written a generic USB driver skeleton, modelled after the
pci-skeleton.c file in the kernel source tree upon which many PCI
network drivers have been based. This USB skeleton can be found at
drivers/usb/usb-skeleton.c in the kernel source tree. In this article I
will walk through the basics of the skeleton driver, explaining the
different pieces and what needs to be done to customize it to your
specific device.
Linux USB Basics
================
If you are going to write a Linux USB driver, please become familiar
with the USB protocol specification. It can be found, along with many
other useful documents, at the USB home page (see Resources). An
excellent introduction to the Linux USB subsystem can be found at the
USB Working Devices List (see Resources). It explains how the Linux USB
subsystem is structured and introduces the reader to the concept of USB
urbs (USB Request Blocks), which are essential to USB drivers.
usb_driver 등록, minor interface와 device ID table
48-135USB driver는 지원 device 정보와 삽입·제거 때 호출할 함수를 `struct usb_driver`로 USB subsystem에 등록합니다. Skeleton은 이름, `probe`, `disconnect`, suspend·resume, reset 전후 callback, `id_table`, autosuspend 지원을 선언합니다.
Driver 이름은 system log에 사용되고 `id_table`과 맞는 device가 나타나거나 사라질 때 probe·disconnect가 호출됩니다. SCSI, network, TTY처럼 다른 subsystem에 연결되는 driver는 그 subsystem의 userspace interface를 사용합니다.
MP3 player나 scanner처럼 맞는 subsystem이 없으면 USB subsystem에서 minor device number와 `file_operations`를 등록해 userspace가 직접 접근할 수 있습니다. 이 경우 fops와 minor는 필요하지만 다른 subsystem을 쓰는 driver에는 선택 사항입니다.
Module init은 `usb_register(&skel_driver)`를 호출하고 실패를 log·반환합니다. Module exit은 `usb_deregister()`로 반드시 등록을 해제합니다.
Hotplug 자동 module loading을 위해 `usb_device_id skel_table`과 `MODULE_DEVICE_TABLE(usb, skel_table)`을 제공합니다. `USB_DEVICE(vendor, product)` 뒤에는 빈 terminating entry가 필요하며 USB class 전체를 match하는 다른 macro는 `usb.h` 문서를 참고합니다.
ID table을 기반으로 module loading과 interface binding callback이 이어집니다.
The first thing a Linux USB driver needs to do is register itself with
the Linux USB subsystem, giving it some information about which devices
the driver supports and which functions to call when a device supported
by the driver is inserted or removed from the system. All of this
information is passed to the USB subsystem in the :c:type:`usb_driver`
structure. The skeleton driver declares a :c:type:`usb_driver` as::
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,
};
The variable name is a string that describes the driver. It is used in
informational messages printed to the system log. The probe and
disconnect function pointers are called when a device that matches the
information provided in the ``id_table`` variable is either seen or
removed.
The fops and minor variables are optional. Most USB drivers hook into
another kernel subsystem, such as the SCSI, network or TTY subsystem.
These types of drivers register themselves with the other kernel
subsystem, and any user-space interactions are provided through that
interface. But for drivers that do not have a matching kernel subsystem,
such as MP3 players or scanners, a method of interacting with user space
is needed. The USB subsystem provides a way to register a minor device
number and a set of :c:type:`file_operations` function pointers that enable
this user-space interaction. The skeleton driver needs this kind of
interface, so it provides a minor starting number and a pointer to its
:c:type:`file_operations` functions.
The USB driver is then registered with a call to usb_register(),
usually in the driver's init function, as shown here::
static int __init usb_skel_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&skel_driver);
if (result < 0) {
pr_err("usb_register failed for the %s driver. Error number %d\n",
skel_driver.name, result);
return -1;
}
return 0;
}
module_init(usb_skel_init);
When the driver is unloaded from the system, it needs to deregister
itself with the USB subsystem. This is done with usb_deregister()
function::
static void __exit usb_skel_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&skel_driver);
}
module_exit(usb_skel_exit);
To enable the linux-hotplug system to load the driver automatically when
the device is plugged in, you need to create a ``MODULE_DEVICE_TABLE``.
The following code tells the hotplug scripts that this module supports a
single device with a specific vendor and product ID::
/* table of devices that work with this driver */
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, skel_table);
There are other macros that can be used in describing a struct
:c:type:`usb_device_id` for drivers that support a whole class of USB
drivers. See :ref:`usb.h <usb_header>` for more information on this.
Probe 검증, disconnect 정리와 open reference
136-179등록한 device ID pattern과 맞는 장치가 bus에 연결되면 `skel_probe(struct usb_interface *interface, const struct usb_device_id *id)`가 호출됩니다. Driver는 실제로 수용 가능한 장치인지 다시 검증하고 성공하면 0, 초기화 실패나 부적합이면 `-ENOMEM`, `-ENODEV` 같은 error code를 반환합니다.
Skeleton probe는 bulk-in·bulk-out endpoint를 찾고 송수신 buffer를 만들며 device에 쓸 URB를 초기화합니다.
장치가 제거되면 disconnect가 private allocation을 정리하고 USB system에 남은 pending URB를 중단해야 합니다.
Binding 뒤 userspace가 device를 열면 `file_operations.open`이 먼저 호출됩니다. Driver는 `kref_get(&dev->kref)`로 private object 사용 reference를 늘리고 `file->private_data = dev`로 이후 read·write가 어느 device instance를 대상으로 하는지 알 수 있게 합니다.
Device operation
================
When a device is plugged into the USB bus that matches the device ID
pattern that your driver registered with the USB core, the probe
function is called. The :c:type:`usb_device` structure, interface number and
the interface ID are passed to the function::
static int skel_probe(struct usb_interface *interface,
const struct usb_device_id *id)
The driver now needs to verify that this device is actually one that it
can accept. If so, it returns 0. If not, or if any error occurs during
initialization, an errorcode (such as ``-ENOMEM`` or ``-ENODEV``) is
returned from the probe function.
In the skeleton driver, we determine what end points are marked as
bulk-in and bulk-out. We create buffers to hold the data that will be
sent and received from the device, and a USB urb to write data to the
device is initialized.
Conversely, when the device is removed from the USB bus, the disconnect
function is called with the device pointer. The driver needs to clean
any private data that has been allocated at this time and to shut down
any pending urbs that are in the USB system.
Now that the device is plugged into the system and the driver is bound
to the device, any of the functions in the :c:type:`file_operations` structure
that were passed to the USB subsystem will be called from a user program
trying to talk to the device. The first function called will be open, as
the program tries to open the device for I/O. We increment our private
usage count and save a pointer to our internal structure in the file
structure. This is done so that future calls to file operations will
enable the driver to determine which device the user is addressing. All
of this is done with the following code::
/* increment our usage count for the device */
kref_get(&dev->kref);
/* save our object in the file's private structure */
file->private_data = dev;
Bulk write URB 구성과 completion context
180-222`skel_write`는 userspace data pointer와 길이를 받아 한 URB가 담을 수 있는 `MAX_TRANSFER`까지만 `writesize`로 선택합니다. `copy_from_user()`로 kernel buffer에 복사한 뒤 URB를 채웁니다.
`usb_fill_bulk_urb()`에는 target `usb_device`, `usb_sndbulkpipe()`로 만든 bulk-out pipe, buffer와 length, `skel_write_bulk_callback`, private context를 전달합니다. 이후 `usb_submit_urb(urb, GFP_KERNEL)`로 USB subsystem에 전송을 제출하고 오류를 log합니다.
Completion callback은 USB subsystem이 URB를 끝냈을 때 interrupt context에서 호출됩니다. 따라서 오래 걸리는 처리나 sleep을 피해야 하며 skeleton callback은 성공 여부만 보고하고 돌아옵니다.
Userspace buffer를 하나의 asynchronous bulk URB로 전송합니다.
After the open function is called, the read and write functions are
called to receive and send data to the device. In the ``skel_write``
function, we receive a pointer to some data that the user wants to send
to the device and the size of the data. The function determines how much
data it can send to the device based on the size of the write urb it has
created (this size depends on the size of the bulk out end point that
the device has). Then it copies the data from user space to kernel
space, points the urb to the data and submits the urb to the USB
subsystem. This can be seen in the following code::
/* we can only write as much as 1 urb will hold */
size_t writesize = min_t(size_t, count, MAX_TRANSFER);
/* copy the data from user space into our urb */
copy_from_user(buf, user_buffer, writesize);
/* set up our urb */
usb_fill_bulk_urb(urb,
dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf,
writesize,
skel_write_bulk_callback,
dev);
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
dev_err(&dev->interface->dev,
"%s - failed submitting write urb, error %d\n",
__func__, retval);
}
When the write urb is filled up with the proper information using the
:c:func:`usb_fill_bulk_urb` function, we point the urb's completion callback
to call our own ``skel_write_bulk_callback`` function. This function is
called when the urb is finished by the USB subsystem. The callback
function is called in interrupt context, so caution must be taken not to
do very much processing at that time. Our implementation of
``skel_write_bulk_callback`` merely reports if the urb was completed
successfully or not and then returns.
usb_bulk_msg 기반 동기 read
223-252Skeleton read는 직접 URB와 completion callback을 만들지 않고 synchronous helper `usb_bulk_msg()`를 사용합니다. Receive bulk pipe, input buffer와 크기, 실제 byte count pointer, 5000 ms timeout을 전달합니다.
Timeout 안에 data가 없으면 helper가 실패와 error를 반환합니다. 성공하면 `copy_to_user()`로 받은 `count` byte를 userspace에 복사하고 복사 실패는 `-EFAULT`, 성공은 byte count를 반환합니다.
`usb_bulk_msg()`는 단발성 read·write에 편리하지만 지속적으로 전송해야 하는 device는 driver가 자체 URB를 구성해 USB subsystem에 반복 제출하는 방식이 권장됩니다.
The read function works a bit differently from the write function in
that we do not use an urb to transfer data from the device to the
driver. Instead we call the :c:func:`usb_bulk_msg` function, which can be used
to send or receive data from a device without having to create urbs and
handle urb completion callback functions. We call the :c:func:`usb_bulk_msg`
function, giving it a buffer into which to place any data received from
the device and a timeout value. If the timeout period expires without
receiving any data from the device, the function will fail and return an
error message. This can be shown with the following code::
/* do an immediate bulk read to get data from the device */
retval = usb_bulk_msg (skel->dev,
usb_rcvbulkpipe (skel->dev,
skel->bulk_in_endpointAddr),
skel->bulk_in_buffer,
skel->bulk_in_size,
&count, 5000);
/* if the read was successful, copy the data to user space */
if (!retval) {
if (copy_to_user (buffer, skel->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
The :c:func:`usb_bulk_msg` function can be very useful for doing single reads
or writes to a device; however, if you need to read or write constantly to
a device, it is recommended to set up your own urbs and submit them to
the USB subsystem.
Release, surprise removal과 object 정리
253-292Userspace가 file handle을 닫으면 release가 호출되어 private usage count를 줄이고 pending write가 끝나기를 기다립니다. 원문 예제 조각은 `--skel->open_count`를 보여 줍니다.
USB device는 program이 사용 중이어도 언제든 제거될 수 있습니다. Driver는 진행 중 read·write를 중단하고 userspace에 장치가 사라졌음을 알려야 합니다.
`skel_delete()`는 bulk-in buffer를 free하고 coherent bulk-out buffer가 있으면 `usb_free_coherent()`로 DMA mapping과 함께 해제하며 write URB와 private object를 차례로 free합니다.
Open handle이 남아 있으면 disconnect는 `device_present` flag를 reset합니다. Read·write·release 등은 먼저 이 flag를 확인하고 장치가 없으면 `-ENODEV`를 반환합니다. 마지막 release는 device가 없고 open file도 끝난 시점에 disconnect가 즉시 하지 못한 cleanup을 수행합니다.
Disconnect와 열린 file reference가 공동으로 최종 free 시점을 결정합니다.
When the user program releases the file handle that it has been using to
talk to the device, the release function in the driver is called. In
this function we decrement our private usage count and wait for possible
pending writes::
/* decrement our usage count for the device */
--skel->open_count;
One of the more difficult problems that USB drivers must be able to
handle smoothly is the fact that the USB device may be removed from the
system at any point in time, even if a program is currently talking to
it. It needs to be able to shut down any current reads and writes and
notify the user-space programs that the device is no longer there. The
following code (function ``skel_delete``) is an example of how to do
this::
static inline void skel_delete (struct usb_skel *dev)
{
kfree (dev->bulk_in_buffer);
if (dev->bulk_out_buffer != NULL)
usb_free_coherent (dev->udev, dev->bulk_out_size,
dev->bulk_out_buffer,
dev->write_urb->transfer_dma);
usb_free_urb (dev->write_urb);
kfree (dev);
}
If a program currently has an open handle to the device, we reset the
flag ``device_present``. For every read, write, release and other
functions that expect a device to be present, the driver first checks
this flag to see if the device is still present. If not, it releases
that the device has disappeared, and a ``-ENODEV`` error is returned to the
user-space program. When the release function is eventually called, it
determines if there is no device and if not, it does the cleanup that
the ``skel_disconnect`` function normally does if there are no open files
on the device (see Listing 5).
Interrupt·isochronous 전송과 참고 자료
293-328`usb-skeleton`에는 interrupt 또는 isochronous transfer 예제가 없습니다. Interrupt data는 몇 가지 작은 차이를 제외하면 bulk와 거의 같은 방식으로 전송합니다.
Isochronous data는 device와 연속 stream을 주고받는 방식이 다르므로 audio와 video camera driver가 좋은 구현 예입니다.
Skeleton과 현재 USB driver들을 함께 보면 초보 작성자도 작동하는 driver를 시작할 수 있고 linux-usb mailing list archive에서 추가 사례를 찾을 수 있습니다.
원문 Resources에는 Linux USB Project, Linux Hotplug Project, linux-usb archive, Linux USB device driver programming guide PDF, USB Home Page가 포함되며 URL을 원문 block에 보존했습니다.
Isochronous Data
================
This usb-skeleton driver does not have any examples of interrupt or
isochronous data being sent to or from the device. Interrupt data is
sent almost exactly as bulk data is, with a few minor exceptions.
Isochronous data works differently with continuous streams of data being
sent to or from the device. The audio and video camera drivers are very
good examples of drivers that handle isochronous data and will be useful
if you also need to do this.
Conclusion
==========
Writing Linux USB device drivers is not a difficult task as the
usb-skeleton driver shows. This driver, combined with the other current
USB drivers, should provide enough examples to help a beginning author
create a working driver in a minimal amount of time. The linux-usb-devel
mailing list archives also contain a lot of helpful information.
Resources
=========
The Linux USB Project:
http://www.linux-usb.org/
Linux Hotplug Project:
http://linux-hotplug.sourceforge.net/
linux-usb Mailing List Archives:
https://lore.kernel.org/linux-usb/
Programming Guide for Linux USB Device Drivers:
https://lmu.web.psi.ch/docu/manuals/software_manuals/linux_sl/usb_linux_programming_guide.pdf
USB Home Page: https://www.usb.org
요약·해설
writing_usb_driver.rst:1-328USB skeleton driver는 ID table과 usb_driver callback으로 interface에 bind되고, endpoint·buffer·URB를 준비해 userspace file operation을 제공합니다. 물리 unplug가 open handle과 동시에 발생할 수 있으므로 pending I/O 중단, presence 확인, reference 기반 최종 free가 핵심입니다.