📄 legousbtower.c
字号:
return mask;}/** * tower_llseek */static loff_t tower_llseek (struct file *file, loff_t off, int whence){ return -ESPIPE; /* unseekable */}/** * tower_read */static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos){ struct lego_usb_tower *dev; size_t bytes_to_read; int i; int retval = 0; unsigned long timeout = 0; dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; /* lock this object */ if (down_interruptible (&dev->sem)) { retval = -ERESTARTSYS; goto exit; } /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; err("No device or device unplugged %d", retval); goto unlock_exit; } /* verify that we actually have some data to read */ if (count == 0) { dbg(1, "%s: read request of 0 bytes", __FUNCTION__); goto unlock_exit; } if (read_timeout) { timeout = jiffies + read_timeout * HZ / 1000; } /* wait for data */ tower_check_for_read_packet (dev); while (dev->read_packet_length == 0) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto unlock_exit; } retval = wait_event_interruptible_timeout(dev->read_wait, dev->interrupt_in_done, dev->packet_timeout_jiffies); if (retval < 0) { goto unlock_exit; } /* reset read timeout during read or write activity */ if (read_timeout && (dev->read_buffer_length || dev->interrupt_out_busy)) { timeout = jiffies + read_timeout * HZ / 1000; } /* check for read timeout */ if (read_timeout && time_after (jiffies, timeout)) { retval = -ETIMEDOUT; goto unlock_exit; } tower_check_for_read_packet (dev); } /* copy the data from read_buffer into userspace */ bytes_to_read = min(count, dev->read_packet_length); if (copy_to_user (buffer, dev->read_buffer, bytes_to_read)) { retval = -EFAULT; goto unlock_exit; } spin_lock_irq (&dev->read_buffer_lock); dev->read_buffer_length -= bytes_to_read; dev->read_packet_length -= bytes_to_read; for (i=0; i<dev->read_buffer_length; i++) { dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; } spin_unlock_irq (&dev->read_buffer_lock); retval = bytes_to_read;unlock_exit: /* unlock the device */ up (&dev->sem);exit: dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval;}/** * tower_write */static ssize_t tower_write (struct file *file, const char __user *buffer, size_t count, loff_t *ppos){ struct lego_usb_tower *dev; size_t bytes_to_write; int retval = 0; dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; /* lock this object */ if (down_interruptible (&dev->sem)) { retval = -ERESTARTSYS; goto exit; } /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; err("No device or device unplugged %d", retval); goto unlock_exit; } /* verify that we actually have some data to write */ if (count == 0) { dbg(1, "%s: write request of 0 bytes", __FUNCTION__); goto unlock_exit; } /* wait until previous transfer is finished */ while (dev->interrupt_out_busy) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto unlock_exit; } retval = wait_event_interruptible (dev->write_wait, !dev->interrupt_out_busy); if (retval) { goto unlock_exit; } } /* write the data into interrupt_out_buffer from userspace */ bytes_to_write = min(count, write_buffer_size); dbg(4, "%s: count = %Zd, bytes_to_write = %Zd", __FUNCTION__, count, bytes_to_write); if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; goto unlock_exit; } /* send off the urb */ usb_fill_int_urb(dev->interrupt_out_urb, dev->udev, usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), dev->interrupt_out_buffer, bytes_to_write, tower_interrupt_out_callback, dev, dev->interrupt_out_interval); dev->interrupt_out_busy = 1; wmb(); retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); if (retval) { dev->interrupt_out_busy = 0; err("Couldn't submit interrupt_out_urb %d", retval); goto unlock_exit; } retval = bytes_to_write;unlock_exit: /* unlock the device */ up (&dev->sem);exit: dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval;}/** * tower_interrupt_in_callback */static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs){ struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; int retval; dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); if (urb->status) { if (urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN) { goto exit; } else { dbg(1, "%s: nonzero status received: %d", __FUNCTION__, urb->status); goto resubmit; /* maybe we can recover */ } } if (urb->actual_length > 0) { spin_lock (&dev->read_buffer_lock); if (dev->read_buffer_length + urb->actual_length < read_buffer_size) { memcpy (dev->read_buffer + dev->read_buffer_length, dev->interrupt_in_buffer, urb->actual_length); dev->read_buffer_length += urb->actual_length; dev->read_last_arrival = jiffies; dbg(3, "%s: received %d bytes", __FUNCTION__, urb->actual_length); } else { printk(KERN_WARNING "%s: read_buffer overflow, %d bytes dropped", __FUNCTION__, urb->actual_length); } spin_unlock (&dev->read_buffer_lock); }resubmit: /* resubmit if we're still running */ if (dev->interrupt_in_running && dev->udev) { retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC); if (retval) { err("%s: usb_submit_urb failed (%d)", __FUNCTION__, retval); } }exit: dev->interrupt_in_done = 1; wake_up_interruptible (&dev->read_wait); lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status);}/** * tower_interrupt_out_callback */static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs){ struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); /* sync/async unlink faults aren't errors */ if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { dbg(1, "%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); } dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status);}/** * tower_probe * * Called by the usb core when a new device is connected that it thinks * this driver might be interested in. */static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id){ struct usb_device *udev = interface_to_usbdev(interface); struct lego_usb_tower *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor* endpoint; struct tower_reset_reply reset_reply; struct tower_get_version_reply get_version_reply; int i; int retval = -ENOMEM; int result; dbg(2, "%s: enter", __FUNCTION__); if (udev == NULL) { info ("udev is NULL."); } /* See if the device offered us matches what we can accept */ if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) || (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) { return -ENODEV; } /* allocate memory for our device state and intialize it */ dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL); if (dev == NULL) { err ("Out of memory"); goto exit; } init_MUTEX (&dev->sem); dev->udev = udev; dev->open_count = 0; dev->read_buffer = NULL; dev->read_buffer_length = 0; dev->read_packet_length = 0; spin_lock_init (&dev->read_buffer_lock); dev->packet_timeout_jiffies = packet_timeout * HZ / 1000; dev->read_last_arrival = jiffies; init_waitqueue_head (&dev->read_wait); init_waitqueue_head (&dev->write_wait); dev->interrupt_in_buffer = NULL; dev->interrupt_in_endpoint = NULL; dev->interrupt_in_urb = NULL; dev->interrupt_in_running = 0; dev->interrupt_in_done = 0; dev->interrupt_out_buffer = NULL; dev->interrupt_out_endpoint = NULL; dev->interrupt_out_urb = NULL; dev->interrupt_out_busy = 0; iface_desc = interface->cur_altsetting; /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { dev->interrupt_in_endpoint = endpoint; } if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { dev->interrupt_out_endpoint = endpoint; } } if(dev->interrupt_in_endpoint == NULL) { err("interrupt in endpoint not found"); goto error; } if (dev->interrupt_out_endpoint == NULL) { err("interrupt out endpoint not found"); goto error; } dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL); if (!dev->read_buffer) { err("Couldn't allocate read_buffer"); goto error; } dev->interrupt_in_buffer = kmalloc (dev->interrupt_in_endpoint->wMaxPacketSize, GFP_KERNEL); if (!dev->interrupt_in_buffer) { err("Couldn't allocate interrupt_in_buffer"); goto error; } dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_in_urb) { err("Couldn't allocate interrupt_in_urb"); goto error; } dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { err("Couldn't allocate interrupt_out_buffer"); goto error; } dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_out_urb) { err("Couldn't allocate interrupt_out_urb"); goto error; } dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; /* we can register the device now, as it is ready */ usb_set_intfdata (interface, dev); retval = usb_register_dev (interface, &tower_class); if (retval) { /* something prevented us from registering this driver */ err ("Not able to get a minor for this device."); usb_set_intfdata (interface, NULL); goto error; } dev->minor = interface->minor; /* let the user know what node this device is now attached to */ info ("LEGO USB Tower #%d now attached to major %d minor %d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), USB_MAJOR, dev->minor); /* reset the tower */ result = usb_control_msg (udev, usb_rcvctrlpipe(udev, 0), LEGO_USB_TOWER_REQUEST_RESET, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, &reset_reply, sizeof(reset_reply), HZ); if (result < 0) { err("LEGO USB Tower reset control request failed"); retval = result; goto error; } /* get the firmware version and log it */ result = usb_control_msg (udev, usb_rcvctrlpipe(udev, 0), LEGO_USB_TOWER_REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, &get_version_reply, sizeof(get_version_reply), HZ); if (result < 0) { err("LEGO USB Tower get version control request failed"); retval = result; goto error; } info("LEGO USB Tower firmware version is %d.%d build %d", get_version_reply.major, get_version_reply.minor, le16_to_cpu(get_version_reply.build_no));exit: dbg(2, "%s: leave, return value 0x%.8lx (dev)", __FUNCTION__, (long) dev); return retval;error: tower_delete(dev); return retval;}/** * tower_disconnect * * Called by the usb core when the device is removed from the system. */static void tower_disconnect (struct usb_interface *interface){ struct lego_usb_tower *dev; int minor; dbg(2, "%s: enter", __FUNCTION__); down (&disconnect_sem); dev = usb_get_intfdata (interface); usb_set_intfdata (interface, NULL); down (&dev->sem); minor = dev->minor; /* give back our minor */ usb_deregister_dev (interface, &tower_class); /* if the device is not opened, then we clean up right now */ if (!dev->open_count) { up (&dev->sem); tower_delete (dev); } else { dev->udev = NULL; up (&dev->sem); } up (&disconnect_sem); info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); dbg(2, "%s: leave", __FUNCTION__);}/** * lego_usb_tower_init */static int __init lego_usb_tower_init(void){ int result; int retval = 0; dbg(2, "%s: enter", __FUNCTION__); /* register this driver with the USB subsystem */ result = usb_register(&tower_driver); if (result < 0) { err("usb_register failed for the "__FILE__" driver. Error number %d", result); retval = -1; goto exit; } info(DRIVER_DESC " " DRIVER_VERSION);exit: dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval;}/** * lego_usb_tower_exit */static void __exit lego_usb_tower_exit(void){ dbg(2, "%s: enter", __FUNCTION__); /* deregister this driver with the USB subsystem */ usb_deregister (&tower_driver); dbg(2, "%s: leave", __FUNCTION__);}module_init (lego_usb_tower_init);module_exit (lego_usb_tower_exit);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);#ifdef MODULE_LICENSEMODULE_LICENSE("GPL");#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -