Linux USB Device Driver Part6 - Storing Private Data
As there are chances to retrieve information related to interface later in the driver, usb provides an API to store pointer to any data type and saves it in usb_interface structure.
static inline void usb_set_intfdata(struct usb_interface *intf, void *data);
To retrieve the data back, we will use the following API.
static inline void *usb_get_intfdata(struct usb_interface *intf);
Let's update over previous driver code, storing a new variable int count in our private data, which represents the number of times the device has been opened
Code:
Output:
Notes:
1. In the probe function, we allocated memory for private data structure, and initialize the members, and call usb_set_intfdata to save the pointer into usb_interface data structure
2. In the open function, we first get the interface structure by using usb_find_interface API and then get the private data structure using usb_get_intfdata API
3. Finally in the disconnect function, we free the memory and set the interface data to NULL
static inline void usb_set_intfdata(struct usb_interface *intf, void *data);
To retrieve the data back, we will use the following API.
static inline void *usb_get_intfdata(struct usb_interface *intf);
Let's update over previous driver code, storing a new variable int count in our private data, which represents the number of times the device has been opened
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> | |
#include <linux/slab.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"); \ | |
} | |
static struct usb_driver usb_hello_driver; | |
//Structure to store private data | |
struct myusb_device { | |
struct usb_device *usb_dev; | |
struct usb_interface *interface; | |
int open_count; | |
}; | |
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) | |
{ | |
struct myusb_device *dev; | |
struct usb_interface *interface; | |
int subminor; | |
int retval = 0; | |
subminor = iminor(inode); | |
interface = usb_find_interface(&usb_hello_driver, subminor); | |
if (!interface) { | |
printk(KERN_ERR "%s -error, can't find device for minor:%d\n", | |
__func__, subminor); | |
retval = -ENODEV; | |
goto exit; | |
} | |
dev = usb_get_intfdata(interface); | |
if (!dev) { | |
printk(KERN_ERR "%s Failed to get interface data\n", __func__); | |
retval = -ENODEV; | |
goto exit; | |
} | |
pr_info("%s: Opening :%d time\n", __func__, ++dev->open_count); | |
exit: | |
return retval; | |
} | |
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; | |
struct myusb_device *dev = NULL; | |
dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
if (dev == NULL) { | |
dev_err(&interface->dev, "Failed to allocate memory of size:%u\n", | |
sizeof(*dev)); | |
return -ENOMEM; | |
} | |
//storing private data | |
dev->interface = interface; | |
dev->usb_dev = interface_to_usbdev(interface); | |
dev->open_count = 0; | |
usb_set_intfdata(interface, dev); | |
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) | |
{ | |
struct myusb_device *dev; | |
dev = usb_get_intfdata(interface); | |
if (dev != NULL) { | |
kfree(dev); | |
usb_set_intfdata(interface, NULL); | |
} | |
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); |
Output:
Notes:
1. In the probe function, we allocated memory for private data structure, and initialize the members, and call usb_set_intfdata to save the pointer into usb_interface data structure
2. In the open function, we first get the interface structure by using usb_find_interface API and then get the private data structure using usb_get_intfdata API
3. Finally in the disconnect function, we free the memory and set the interface data to NULL
Comments
Post a Comment