Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include <linux/usb.h>
- #define USB_VENDOR_ID 0x1546
- #define USB_PRODUCT_ID 0x01a7
- #define USB_DRIVER_NAME "ublox"
- #define UBLOX_MINOR_BASE 192
- #define PRINT_FIELD(structure, field) \
- do { \
- printk(KERN_INFO "%s = %u\n", #field, structure->field); \
- } while (0)
- #define get_parent_ublox_device(kref_variable) container_of(kref_variable, struct ublox_device, kref)
- static int usb_probe(struct usb_interface *interface, const struct usb_device_id *usb_device_id);
- static void usb_disconnect(struct usb_interface *interface);
- static void usb_print_interface_description(struct usb_interface_descriptor *interface_descriptor);
- static int ublox_open(struct inode *inode, struct file *file);
- static int ublox_release(struct inode *inode, struct file *file);
- static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos);
- static void ublox_read_bulk_callback(struct urb *urb);
- static void ublox_destroy_device(struct kref *kref);
- static void print_struct_urb(struct urb *urb);
- static struct usb_device_id supported_usb_ids [] = {
- { USB_DEVICE(USB_VENDOR_ID, USB_PRODUCT_ID) },
- { } /* Terminating entry */
- };
- MODULE_DEVICE_TABLE(usb, supported_usb_ids);
- static struct usb_driver usb_driver = {
- .name = USB_DRIVER_NAME,
- .id_table = supported_usb_ids,
- .probe = usb_probe,
- .disconnect = usb_disconnect
- };
- static const struct file_operations ublox_fops = {
- .owner = THIS_MODULE,
- .open = ublox_open,
- .release = ublox_release,
- .read = ublox_read
- };
- static struct usb_class_driver ublox_class = {
- .name = USB_DRIVER_NAME,
- .fops = &ublox_fops,
- .minor_base = UBLOX_MINOR_BASE
- };
- struct ublox_device {
- struct usb_device *usb_device; /* The usb device to which the ublox device belongs */
- struct usb_interface *interface; /* The interface for the ublox device */
- __u8 bulk_in_bEndpointAddress; /* Address for reading data from ublox device */
- size_t bulk_in_buffer_size; /* Size of the input buffer */
- struct urb *bulk_in_urb; /* URB for reading data */
- unsigned char *bulk_in_buffer; /* Buffer to receive data */
- struct mutex bulk_in_mutex; /* Allow only one bulk read operation at a time */
- wait_queue_head_t bulk_in_wait_queue; /* Allow only one reader and send all others to sleep */
- bool read_in_progress; /* Signal a pending read operation */
- unsigned int transferred_bytes; /* The transferred bytes during the last read operation */
- __u8 bulk_out_bEndpointAddress; /* Address for writing data to ublox device */
- size_t bulk_out_buffer_size; /* Size of the output buffer */
- struct kref kref; /* Reference counter */
- };
- int init_module(void)
- {
- int result;
- printk(KERN_INFO "Initializing %s...\n", USB_DRIVER_NAME);
- result = usb_register(&usb_driver);
- if (result)
- printk(KERN_INFO "Registering %s failed (result = %d)!\n", USB_DRIVER_NAME, result);
- else
- printk(KERN_INFO "Initialized %s.\n", USB_DRIVER_NAME);
- return 0;
- }
- void cleanup_module(void)
- {
- usb_deregister(&usb_driver);
- printk(KERN_INFO "Cleaned up %s.\n", USB_DRIVER_NAME);
- }
- static int usb_probe(struct usb_interface *interface, const struct usb_device_id *usb_device_id)
- {
- struct usb_host_interface *current_interface = interface->cur_altsetting;
- struct ublox_device *ublox_device = NULL;
- int in_endpoint_found = 0;
- int out_endpoint_found = 0;
- int return_value = -EINVAL;
- int i;
- printk(KERN_INFO "Probing USB interface...\n");
- /* Accept only interfaces with two endpoints (one for reading, one for writing) */
- if (current_interface->desc.bNumEndpoints != 2) {
- printk(KERN_INFO "Ignoring USB interface %d!\n", current_interface->desc.bInterfaceNumber);
- usb_print_interface_description(¤t_interface->desc);
- } else {
- ublox_device = kzalloc(sizeof(*ublox_device), GFP_KERNEL);
- if (ublox_device == NULL) {
- return_value = -ENOMEM;
- goto error;
- }
- ublox_device->usb_device = usb_get_dev(interface_to_usbdev(interface));
- ublox_device->interface = interface;
- ublox_device->read_in_progress = 0;
- ublox_device->transferred_bytes = 0;
- mutex_init(&ublox_device->bulk_in_mutex);
- init_waitqueue_head(&ublox_device->bulk_in_wait_queue);
- kref_init(&ublox_device->kref);
- ublox_device->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (ublox_device->bulk_in_urb == NULL) {
- return_value = -ENOMEM;
- goto error;
- }
- for (i = 0; i < current_interface->desc.bNumEndpoints; i++) {
- struct usb_endpoint_descriptor endpoint = current_interface->endpoint[i].desc;
- if (usb_endpoint_is_bulk_in(&endpoint)) {
- ublox_device->bulk_in_bEndpointAddress = endpoint.bEndpointAddress;
- ublox_device->bulk_in_buffer_size = endpoint.wMaxPacketSize;
- ublox_device->bulk_in_buffer = kzalloc(endpoint.wMaxPacketSize, GFP_KERNEL);
- if (ublox_device->bulk_in_buffer == NULL) {
- return_value = -ENOMEM;
- goto error;
- }
- in_endpoint_found = 1;
- }
- if (usb_endpoint_is_bulk_out(&endpoint)) {
- ublox_device->bulk_out_bEndpointAddress = endpoint.bEndpointAddress;
- ublox_device->bulk_out_buffer_size = endpoint.wMaxPacketSize;
- out_endpoint_found = 1;
- }
- }
- if (in_endpoint_found && out_endpoint_found) {
- printk(KERN_INFO "Handling USB interface %d.\n", current_interface->desc.bInterfaceNumber);
- usb_print_interface_description(¤t_interface->desc);
- return_value = usb_register_dev(interface, &ublox_class);
- if (return_value) {
- printk(KERN_ERR "Unable to register USB interface %d!\n", current_interface->desc.bInterfaceNumber);
- goto error;
- }
- usb_set_intfdata(interface, ublox_device);
- }
- else
- goto error;
- }
- printk(KERN_INFO "Probed USB interface.\n");
- return return_value;
- error:
- if (ublox_device)
- kref_put(&ublox_device->kref, ublox_destroy_device);
- return return_value;
- }
- static void usb_disconnect(struct usb_interface *interface)
- {
- struct ublox_device *ublox_device = usb_get_intfdata(interface);
- int minor = interface->minor;
- /* Wait for pending I/O operations and then prevent more I/O operations from starting */
- mutex_lock(&ublox_device->bulk_in_mutex);
- usb_set_intfdata(interface, NULL);
- usb_deregister_dev(interface, &ublox_class);
- kref_put(&ublox_device->kref, ublox_destroy_device);
- mutex_unlock(&ublox_device->bulk_in_mutex);
- printk(KERN_INFO "Disconnected USB interface with minor number %d.\n", minor);
- }
- static void usb_print_interface_description(struct usb_interface_descriptor *interface_descriptor)
- {
- printk(KERN_INFO "Printing interface description...\n");
- PRINT_FIELD(interface_descriptor, bInterfaceNumber);
- PRINT_FIELD(interface_descriptor, bNumEndpoints);
- PRINT_FIELD(interface_descriptor, bInterfaceClass);
- PRINT_FIELD(interface_descriptor, bInterfaceSubClass);
- printk(KERN_INFO "Printed interface description.\n");
- }
- static int ublox_open(struct inode *inode, struct file *file)
- {
- struct ublox_device *ublox_device;
- struct usb_interface *interface;
- int minor;
- int return_value = 0;
- printk(KERN_INFO "Opening ublox device...\n");
- minor = iminor(inode);
- interface = usb_find_interface(&usb_driver, minor);
- if (!interface) {
- printk(KERN_ERR "Cannot find USB device for minor number %d!\n", minor);
- return_value = -ENODEV;
- goto exit;
- }
- ublox_device = usb_get_intfdata(interface);
- if (!ublox_device) {
- printk(KERN_ERR "Cannot find ublox_device data in interface!\n");
- return_value = -ENODEV;
- goto exit;
- }
- kref_get(&ublox_device->kref);
- file->private_data = ublox_device;
- printk(KERN_INFO "Opened ublox device.\n");
- exit:
- return return_value;
- }
- static int ublox_release(struct inode *inode, struct file *file)
- {
- struct ublox_device *ublox_device = file->private_data;
- printk(KERN_INFO "Releasing ublox device...\n");
- if (ublox_device == NULL)
- return -ENODEV;
- kref_put(&ublox_device->kref, ublox_destroy_device);
- printk(KERN_INFO "Released ublox device.\n");
- return 0;
- }
- static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos)
- {
- struct ublox_device *ublox_device = file->private_data;
- int bytes_to_read = 0;
- int return_value = 0;
- if (ublox_device == NULL)
- return -ENODEV;
- bytes_to_read = min(count, ublox_device->bulk_in_buffer_size);
- return_value = mutex_lock_interruptible(&ublox_device->bulk_in_mutex);
- if (return_value < 0)
- return -EINTR;
- printk(KERN_INFO "Reading ublox device...\n");
- printk(KERN_INFO "count = %d\n", count);
- printk(KERN_INFO "pos = %lu\n", (unsigned long)*pos);
- printk(KERN_INFO "pid = %d\n", current->pid);
- if (ublox_device->interface == NULL) { /* disconnect(...) was called in the meantime */
- return_value = -ENODEV;
- goto exit;
- }
- retry:
- usb_fill_bulk_urb(ublox_device->bulk_in_urb,
- ublox_device->usb_device,
- usb_rcvbulkpipe(ublox_device->usb_device, ublox_device->bulk_in_bEndpointAddress),
- ublox_device->bulk_in_buffer,
- bytes_to_read,
- ublox_read_bulk_callback,
- ublox_device);
- ublox_device->read_in_progress = 1;
- return_value = usb_submit_urb(ublox_device->bulk_in_urb, GFP_KERNEL);
- if (return_value) {
- printk(KERN_ERR "usb_submit_urb(...) failed!\n");
- ublox_device->read_in_progress = 0;
- goto exit;
- }
- /* Go to sleep until read operation has finished */
- return_value = wait_event_interruptible(ublox_device->bulk_in_wait_queue, (!ublox_device->read_in_progress));
- if (return_value < 0)
- goto exit;
- if (ublox_device->transferred_bytes > bytes_to_read) {
- return_value = -EIO;
- goto exit;
- } else if (ublox_device->transferred_bytes == 0) {
- goto retry;
- }
- if (copy_to_user(buffer, ublox_device->bulk_in_buffer, ublox_device->transferred_bytes)) {
- return_value = -EFAULT;
- goto exit;
- } else {
- return_value = ublox_device->transferred_bytes;
- }
- exit:
- print_struct_urb(ublox_device->bulk_in_urb);
- printk(KERN_INFO "bytes_to_read = %d\n", bytes_to_read);
- printk(KERN_INFO "transferred_bytes = %d\n", ublox_device->transferred_bytes);
- printk(KERN_INFO "Read ublox device.\n");
- mutex_unlock(&ublox_device->bulk_in_mutex);
- return return_value;
- }
- static void ublox_read_bulk_callback(struct urb *urb)
- {
- struct ublox_device *ublox_device = urb->context;
- int status = urb->status;
- if (status) {
- if (status == -ENOENT || status == -ECONNRESET || status == -ESHUTDOWN)
- printk(KERN_WARNING "Sync/Async/Unlink I/O error: %d\n", status);
- else
- printk(KERN_ERR "Critical I/O error: %d\n", status);
- }
- ublox_device->transferred_bytes = urb->actual_length;
- ublox_device->read_in_progress = 0;
- wake_up_interruptible(&ublox_device->bulk_in_wait_queue);
- }
- static void ublox_destroy_device(struct kref *kref)
- {
- struct ublox_device *ublox_device = get_parent_ublox_device(kref);
- usb_free_urb(ublox_device->bulk_in_urb);
- kfree(ublox_device->bulk_in_buffer);
- kfree(ublox_device);
- }
- static void print_struct_urb(struct urb *urb)
- {
- PRINT_FIELD(urb, unlinked);
- PRINT_FIELD(urb, status);
- PRINT_FIELD(urb, num_sgs);
- PRINT_FIELD(urb, transfer_buffer_length);
- PRINT_FIELD(urb, actual_length);
- }
- MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement