Linux USB Device Driver Part5 - USB Character driver
In this post, we will try to register our USB Device with the character subsystem (/dev/mydevice) allowing the user space to communicate with the USB Device
To register a USB as character device, we need to call the below API in probe function:
int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
To unregister, call the below API in disconnect function:
void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
To register a USB as character device, we need to call the below API in probe function:
int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
As we know for a device file we need to specify the major number and minor number. The major number is fixed for the character USB devices. It is 180. We need to specify the following in the members of struct usb_class_driver:
1. Name of the device node
2. File operations structure
3. Minor Number
Code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/kernel.h> | |
#include <linux/module.h> | |
#include <linux/usb.h> | |
MODULE_LICENSE("GPL"); | |
#define USB_VENDOR_ID 0x18ec | |
#define USB_PRODUCT_ID 0x3299 | |
#define MYUSB_MINOR_BASE 250 | |
#define DUMP_USB_INTERFACE_DESCRIPTOR( i ) \ | |
{\ | |
pr_info("USB_INTERFACE_DESCRIPTOR:\n"); \ | |
pr_info("-----------------------------\n"); \ | |
pr_info("bLength: 0x%x\n", i.bLength); \ | |
pr_info("bDescriptorType: 0x%x\n", i.bDescriptorType); \ | |
pr_info("bInterfaceNumber: 0x%x\n", i.bInterfaceNumber); \ | |
pr_info("bAlternateSetting: 0x%x\n", i.bAlternateSetting); \ | |
pr_info("bNumEndpoints: 0x%x\n", i.bNumEndpoints); \ | |
pr_info("bInterfaceClass: 0x%x\n", i.bInterfaceClass); \ | |
pr_info("bInterfaceSubClass: 0x%x\n", i.bInterfaceSubClass); \ | |
pr_info("bInterfaceProtocol: 0x%x\n", i.bInterfaceProtocol); \ | |
pr_info("iInterface: 0x%x\n", i.iInterface); \ | |
pr_info("\n"); \ | |
} | |
#define DUMP_USB_ENDPOINT_DESCRIPTOR( e ) \ | |
{\ | |
pr_info("USB_ENDPOINT_DESCRIPTOR:\n"); \ | |
pr_info("------------------------\n"); \ | |
pr_info("bLength: 0x%x\n", e.bLength); \ | |
pr_info("bDescriptorType: 0x%x\n", e.bDescriptorType); \ | |
pr_info("bEndPointAddress: 0x%x\n", e.bEndpointAddress); \ | |
pr_info("bmAttributes: 0x%x\n", e.bmAttributes); \ | |
pr_info("wMaxPacketSize: 0x%x\n", e.wMaxPacketSize); \ | |
pr_info("bInterval: 0x%x\n", e.bInterval); \ | |
pr_info("\n"); \ | |
} | |
const struct usb_device_id usb_table[] = { | |
{ USB_DEVICE(USB_VENDOR_ID, USB_PRODUCT_ID) }, | |
{ } | |
}; | |
MODULE_DEVICE_TABLE(usb, usb_table); | |
static int myusb_open(struct inode *inode, struct file *file) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
static int myusb_release(struct inode *inode, struct file *file) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
static ssize_t myusb_write(struct file *file, const char *user_buffer, | |
size_t count, loff_t *ppos) | |
{ | |
pr_info("%s\n", __func__); | |
return count; | |
} | |
static ssize_t myusb_read(struct file *file, char *buffer, size_t count, | |
loff_t *ppos) | |
{ | |
pr_info("%s\n", __func__); | |
return 0; | |
} | |
static const struct file_operations myusb_fops = { | |
.owner = THIS_MODULE, | |
.open = myusb_open, | |
.release = myusb_release, | |
.read = myusb_read, | |
.write = myusb_write, | |
}; | |
static struct usb_class_driver myusb_class = { | |
.name = "mydevice%d", | |
.fops = &myusb_fops, | |
.minor_base = MYUSB_MINOR_BASE, | |
}; | |
static int usb_probe(struct usb_interface *interface, | |
const struct usb_device_id *id) | |
{ | |
unsigned int i; | |
unsigned int num_endpoints; | |
struct usb_host_interface *iface_desc = interface->cur_altsetting; | |
dev_info(&interface->dev, "USB Driver Probed: Vendor ID:%02x\t" | |
"Product ID:%02x\n", id->idVendor, id->idProduct); | |
num_endpoints = iface_desc->desc.bNumEndpoints; | |
DUMP_USB_INTERFACE_DESCRIPTOR(iface_desc->desc); | |
for (i = 0; i < num_endpoints; i++) { | |
DUMP_USB_ENDPOINT_DESCRIPTOR(iface_desc->endpoint[i].desc); | |
} | |
return usb_register_dev(interface, &myusb_class);; | |
} | |
static void usb_disconnect(struct usb_interface *interface) | |
{ | |
usb_deregister_dev(interface, &myusb_class); | |
dev_info(&interface->dev, "USB Driver Disconected\n"); | |
} | |
static struct usb_driver usb_hello_driver = { | |
.name = "hello", | |
.probe = usb_probe, | |
.disconnect = usb_disconnect, | |
.id_table = usb_table, | |
}; | |
module_usb_driver(usb_hello_driver); |
O/P:
You can observe from the output, after I loaded the module and connected the device, I got four entries (/dev/mydevice*), this is because the device which I am using has four interface. If your device has only one interface, you will get /dev/mydevice0.
To test the write functionality executed: echo "hello" > /dev/mydevice0
To test the read functionality executed: cat < /dev/mydevice0
Comments
Post a Comment