18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Meywa-Denki & KAYAC YUREX 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Tomoki Sekiyama (tomoki.sekiyama@gmail.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/kref.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <linux/usb.h> 168c2ecf20Sopenharmony_ci#include <linux/hid.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Tomoki Sekiyama" 198c2ecf20Sopenharmony_ci#define DRIVER_DESC "Driver for Meywa-Denki & KAYAC YUREX" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define YUREX_VENDOR_ID 0x0c45 228c2ecf20Sopenharmony_ci#define YUREX_PRODUCT_ID 0x1010 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define CMD_ACK '!' 258c2ecf20Sopenharmony_ci#define CMD_ANIMATE 'A' 268c2ecf20Sopenharmony_ci#define CMD_COUNT 'C' 278c2ecf20Sopenharmony_ci#define CMD_LED 'L' 288c2ecf20Sopenharmony_ci#define CMD_READ 'R' 298c2ecf20Sopenharmony_ci#define CMD_SET 'S' 308c2ecf20Sopenharmony_ci#define CMD_VERSION 'V' 318c2ecf20Sopenharmony_ci#define CMD_EOF 0x0d 328c2ecf20Sopenharmony_ci#define CMD_PADDING 0xff 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define YUREX_BUF_SIZE 8 358c2ecf20Sopenharmony_ci#define YUREX_WRITE_TIMEOUT (HZ*2) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* table of devices that work with this driver */ 388c2ecf20Sopenharmony_cistatic struct usb_device_id yurex_table[] = { 398c2ecf20Sopenharmony_ci { USB_DEVICE(YUREX_VENDOR_ID, YUREX_PRODUCT_ID) }, 408c2ecf20Sopenharmony_ci { } /* Terminating entry */ 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, yurex_table); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_DYNAMIC_MINORS 458c2ecf20Sopenharmony_ci#define YUREX_MINOR_BASE 0 468c2ecf20Sopenharmony_ci#else 478c2ecf20Sopenharmony_ci#define YUREX_MINOR_BASE 192 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Structure to hold all of our device specific stuff */ 518c2ecf20Sopenharmony_cistruct usb_yurex { 528c2ecf20Sopenharmony_ci struct usb_device *udev; 538c2ecf20Sopenharmony_ci struct usb_interface *interface; 548c2ecf20Sopenharmony_ci __u8 int_in_endpointAddr; 558c2ecf20Sopenharmony_ci struct urb *urb; /* URB for interrupt in */ 568c2ecf20Sopenharmony_ci unsigned char *int_buffer; /* buffer for intterupt in */ 578c2ecf20Sopenharmony_ci struct urb *cntl_urb; /* URB for control msg */ 588c2ecf20Sopenharmony_ci struct usb_ctrlrequest *cntl_req; /* req for control msg */ 598c2ecf20Sopenharmony_ci unsigned char *cntl_buffer; /* buffer for control msg */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci struct kref kref; 628c2ecf20Sopenharmony_ci struct mutex io_mutex; 638c2ecf20Sopenharmony_ci unsigned long disconnected:1; 648c2ecf20Sopenharmony_ci struct fasync_struct *async_queue; 658c2ecf20Sopenharmony_ci wait_queue_head_t waitq; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci spinlock_t lock; 688c2ecf20Sopenharmony_ci __s64 bbu; /* BBU from device */ 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci#define to_yurex_dev(d) container_of(d, struct usb_yurex, kref) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct usb_driver yurex_driver; 738c2ecf20Sopenharmony_cistatic const struct file_operations yurex_fops; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void yurex_control_callback(struct urb *urb) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct usb_yurex *dev = urb->context; 798c2ecf20Sopenharmony_ci int status = urb->status; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (status) { 828c2ecf20Sopenharmony_ci dev_err(&urb->dev->dev, "%s - control failed: %d\n", 838c2ecf20Sopenharmony_ci __func__, status); 848c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->waitq); 858c2ecf20Sopenharmony_ci return; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci /* on success, sender woken up by CMD_ACK int in, or timeout */ 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void yurex_delete(struct kref *kref) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct usb_yurex *dev = to_yurex_dev(kref); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci dev_dbg(&dev->interface->dev, "%s\n", __func__); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (dev->cntl_urb) { 978c2ecf20Sopenharmony_ci usb_kill_urb(dev->cntl_urb); 988c2ecf20Sopenharmony_ci kfree(dev->cntl_req); 998c2ecf20Sopenharmony_ci usb_free_coherent(dev->udev, YUREX_BUF_SIZE, 1008c2ecf20Sopenharmony_ci dev->cntl_buffer, dev->cntl_urb->transfer_dma); 1018c2ecf20Sopenharmony_ci usb_free_urb(dev->cntl_urb); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci if (dev->urb) { 1048c2ecf20Sopenharmony_ci usb_kill_urb(dev->urb); 1058c2ecf20Sopenharmony_ci usb_free_coherent(dev->udev, YUREX_BUF_SIZE, 1068c2ecf20Sopenharmony_ci dev->int_buffer, dev->urb->transfer_dma); 1078c2ecf20Sopenharmony_ci usb_free_urb(dev->urb); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci usb_put_intf(dev->interface); 1108c2ecf20Sopenharmony_ci usb_put_dev(dev->udev); 1118c2ecf20Sopenharmony_ci kfree(dev); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * usb class driver info in order to get a minor number from the usb core, 1168c2ecf20Sopenharmony_ci * and to have the device registered with the driver core 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_cistatic struct usb_class_driver yurex_class = { 1198c2ecf20Sopenharmony_ci .name = "yurex%d", 1208c2ecf20Sopenharmony_ci .fops = &yurex_fops, 1218c2ecf20Sopenharmony_ci .minor_base = YUREX_MINOR_BASE, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void yurex_interrupt(struct urb *urb) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct usb_yurex *dev = urb->context; 1278c2ecf20Sopenharmony_ci unsigned char *buf = dev->int_buffer; 1288c2ecf20Sopenharmony_ci int status = urb->status; 1298c2ecf20Sopenharmony_ci unsigned long flags; 1308c2ecf20Sopenharmony_ci int retval, i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci switch (status) { 1338c2ecf20Sopenharmony_ci case 0: /*success*/ 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci /* The device is terminated or messed up, give up */ 1368c2ecf20Sopenharmony_ci case -EOVERFLOW: 1378c2ecf20Sopenharmony_ci dev_err(&dev->interface->dev, 1388c2ecf20Sopenharmony_ci "%s - overflow with length %d, actual length is %d\n", 1398c2ecf20Sopenharmony_ci __func__, YUREX_BUF_SIZE, dev->urb->actual_length); 1408c2ecf20Sopenharmony_ci case -ECONNRESET: 1418c2ecf20Sopenharmony_ci case -ENOENT: 1428c2ecf20Sopenharmony_ci case -ESHUTDOWN: 1438c2ecf20Sopenharmony_ci case -EILSEQ: 1448c2ecf20Sopenharmony_ci case -EPROTO: 1458c2ecf20Sopenharmony_ci case -ETIME: 1468c2ecf20Sopenharmony_ci return; 1478c2ecf20Sopenharmony_ci default: 1488c2ecf20Sopenharmony_ci dev_err(&dev->interface->dev, 1498c2ecf20Sopenharmony_ci "%s - unknown status received: %d\n", __func__, status); 1508c2ecf20Sopenharmony_ci return; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* handle received message */ 1548c2ecf20Sopenharmony_ci switch (buf[0]) { 1558c2ecf20Sopenharmony_ci case CMD_COUNT: 1568c2ecf20Sopenharmony_ci case CMD_READ: 1578c2ecf20Sopenharmony_ci if (buf[6] == CMD_EOF) { 1588c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 1598c2ecf20Sopenharmony_ci dev->bbu = 0; 1608c2ecf20Sopenharmony_ci for (i = 1; i < 6; i++) { 1618c2ecf20Sopenharmony_ci dev->bbu += buf[i]; 1628c2ecf20Sopenharmony_ci if (i != 5) 1638c2ecf20Sopenharmony_ci dev->bbu <<= 8; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci dev_dbg(&dev->interface->dev, "%s count: %lld\n", 1668c2ecf20Sopenharmony_ci __func__, dev->bbu); 1678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci kill_fasync(&dev->async_queue, SIGIO, POLL_IN); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci dev_dbg(&dev->interface->dev, 1738c2ecf20Sopenharmony_ci "data format error - no EOF\n"); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case CMD_ACK: 1768c2ecf20Sopenharmony_ci dev_dbg(&dev->interface->dev, "%s ack: %c\n", 1778c2ecf20Sopenharmony_ci __func__, buf[1]); 1788c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->waitq); 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci retval = usb_submit_urb(dev->urb, GFP_ATOMIC); 1838c2ecf20Sopenharmony_ci if (retval) { 1848c2ecf20Sopenharmony_ci dev_err(&dev->interface->dev, "%s - usb_submit_urb failed: %d\n", 1858c2ecf20Sopenharmony_ci __func__, retval); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int yurex_probe(struct usb_interface *interface, const struct usb_device_id *id) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct usb_yurex *dev; 1928c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc; 1938c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 1948c2ecf20Sopenharmony_ci int retval = -ENOMEM; 1958c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 1968c2ecf20Sopenharmony_ci int res; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 1998c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 2008c2ecf20Sopenharmony_ci if (!dev) 2018c2ecf20Sopenharmony_ci goto error; 2028c2ecf20Sopenharmony_ci kref_init(&dev->kref); 2038c2ecf20Sopenharmony_ci mutex_init(&dev->io_mutex); 2048c2ecf20Sopenharmony_ci spin_lock_init(&dev->lock); 2058c2ecf20Sopenharmony_ci init_waitqueue_head(&dev->waitq); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci dev->udev = usb_get_dev(interface_to_usbdev(interface)); 2088c2ecf20Sopenharmony_ci dev->interface = usb_get_intf(interface); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* set up the endpoint information */ 2118c2ecf20Sopenharmony_ci iface_desc = interface->cur_altsetting; 2128c2ecf20Sopenharmony_ci res = usb_find_int_in_endpoint(iface_desc, &endpoint); 2138c2ecf20Sopenharmony_ci if (res) { 2148c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Could not find endpoints\n"); 2158c2ecf20Sopenharmony_ci retval = res; 2168c2ecf20Sopenharmony_ci goto error; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci dev->int_in_endpointAddr = endpoint->bEndpointAddress; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* allocate control URB */ 2228c2ecf20Sopenharmony_ci dev->cntl_urb = usb_alloc_urb(0, GFP_KERNEL); 2238c2ecf20Sopenharmony_ci if (!dev->cntl_urb) 2248c2ecf20Sopenharmony_ci goto error; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* allocate buffer for control req */ 2278c2ecf20Sopenharmony_ci dev->cntl_req = kmalloc(YUREX_BUF_SIZE, GFP_KERNEL); 2288c2ecf20Sopenharmony_ci if (!dev->cntl_req) 2298c2ecf20Sopenharmony_ci goto error; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* allocate buffer for control msg */ 2328c2ecf20Sopenharmony_ci dev->cntl_buffer = usb_alloc_coherent(dev->udev, YUREX_BUF_SIZE, 2338c2ecf20Sopenharmony_ci GFP_KERNEL, 2348c2ecf20Sopenharmony_ci &dev->cntl_urb->transfer_dma); 2358c2ecf20Sopenharmony_ci if (!dev->cntl_buffer) { 2368c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Could not allocate cntl_buffer\n"); 2378c2ecf20Sopenharmony_ci goto error; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* configure control URB */ 2418c2ecf20Sopenharmony_ci dev->cntl_req->bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | 2428c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE; 2438c2ecf20Sopenharmony_ci dev->cntl_req->bRequest = HID_REQ_SET_REPORT; 2448c2ecf20Sopenharmony_ci dev->cntl_req->wValue = cpu_to_le16((HID_OUTPUT_REPORT + 1) << 8); 2458c2ecf20Sopenharmony_ci dev->cntl_req->wIndex = cpu_to_le16(iface_desc->desc.bInterfaceNumber); 2468c2ecf20Sopenharmony_ci dev->cntl_req->wLength = cpu_to_le16(YUREX_BUF_SIZE); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci usb_fill_control_urb(dev->cntl_urb, dev->udev, 2498c2ecf20Sopenharmony_ci usb_sndctrlpipe(dev->udev, 0), 2508c2ecf20Sopenharmony_ci (void *)dev->cntl_req, dev->cntl_buffer, 2518c2ecf20Sopenharmony_ci YUREX_BUF_SIZE, yurex_control_callback, dev); 2528c2ecf20Sopenharmony_ci dev->cntl_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* allocate interrupt URB */ 2568c2ecf20Sopenharmony_ci dev->urb = usb_alloc_urb(0, GFP_KERNEL); 2578c2ecf20Sopenharmony_ci if (!dev->urb) 2588c2ecf20Sopenharmony_ci goto error; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* allocate buffer for interrupt in */ 2618c2ecf20Sopenharmony_ci dev->int_buffer = usb_alloc_coherent(dev->udev, YUREX_BUF_SIZE, 2628c2ecf20Sopenharmony_ci GFP_KERNEL, &dev->urb->transfer_dma); 2638c2ecf20Sopenharmony_ci if (!dev->int_buffer) { 2648c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Could not allocate int_buffer\n"); 2658c2ecf20Sopenharmony_ci goto error; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* configure interrupt URB */ 2698c2ecf20Sopenharmony_ci usb_fill_int_urb(dev->urb, dev->udev, 2708c2ecf20Sopenharmony_ci usb_rcvintpipe(dev->udev, dev->int_in_endpointAddr), 2718c2ecf20Sopenharmony_ci dev->int_buffer, YUREX_BUF_SIZE, yurex_interrupt, 2728c2ecf20Sopenharmony_ci dev, 1); 2738c2ecf20Sopenharmony_ci dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 2748c2ecf20Sopenharmony_ci if (usb_submit_urb(dev->urb, GFP_KERNEL)) { 2758c2ecf20Sopenharmony_ci retval = -EIO; 2768c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Could not submitting URB\n"); 2778c2ecf20Sopenharmony_ci goto error; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* save our data pointer in this interface device */ 2818c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 2828c2ecf20Sopenharmony_ci dev->bbu = -1; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* we can register the device now, as it is ready */ 2858c2ecf20Sopenharmony_ci retval = usb_register_dev(interface, &yurex_class); 2868c2ecf20Sopenharmony_ci if (retval) { 2878c2ecf20Sopenharmony_ci dev_err(&interface->dev, 2888c2ecf20Sopenharmony_ci "Not able to get a minor for this device.\n"); 2898c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 2908c2ecf20Sopenharmony_ci goto error; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci dev_info(&interface->dev, 2948c2ecf20Sopenharmony_ci "USB YUREX device now attached to Yurex #%d\n", 2958c2ecf20Sopenharmony_ci interface->minor); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cierror: 3008c2ecf20Sopenharmony_ci if (dev) 3018c2ecf20Sopenharmony_ci /* this frees allocated memory */ 3028c2ecf20Sopenharmony_ci kref_put(&dev->kref, yurex_delete); 3038c2ecf20Sopenharmony_ci return retval; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void yurex_disconnect(struct usb_interface *interface) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct usb_yurex *dev; 3098c2ecf20Sopenharmony_ci int minor = interface->minor; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci dev = usb_get_intfdata(interface); 3128c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* give back our minor */ 3158c2ecf20Sopenharmony_ci usb_deregister_dev(interface, &yurex_class); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* prevent more I/O from starting */ 3188c2ecf20Sopenharmony_ci usb_poison_urb(dev->urb); 3198c2ecf20Sopenharmony_ci usb_poison_urb(dev->cntl_urb); 3208c2ecf20Sopenharmony_ci mutex_lock(&dev->io_mutex); 3218c2ecf20Sopenharmony_ci dev->disconnected = 1; 3228c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* wakeup waiters */ 3258c2ecf20Sopenharmony_ci kill_fasync(&dev->async_queue, SIGIO, POLL_IN); 3268c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->waitq); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* decrement our usage count */ 3298c2ecf20Sopenharmony_ci kref_put(&dev->kref, yurex_delete); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci dev_info(&interface->dev, "USB YUREX #%d now disconnected\n", minor); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic struct usb_driver yurex_driver = { 3358c2ecf20Sopenharmony_ci .name = "yurex", 3368c2ecf20Sopenharmony_ci .probe = yurex_probe, 3378c2ecf20Sopenharmony_ci .disconnect = yurex_disconnect, 3388c2ecf20Sopenharmony_ci .id_table = yurex_table, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int yurex_fasync(int fd, struct file *file, int on) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct usb_yurex *dev; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dev = file->private_data; 3478c2ecf20Sopenharmony_ci return fasync_helper(fd, file, on, &dev->async_queue); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int yurex_open(struct inode *inode, struct file *file) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct usb_yurex *dev; 3538c2ecf20Sopenharmony_ci struct usb_interface *interface; 3548c2ecf20Sopenharmony_ci int subminor; 3558c2ecf20Sopenharmony_ci int retval = 0; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci subminor = iminor(inode); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci interface = usb_find_interface(&yurex_driver, subminor); 3608c2ecf20Sopenharmony_ci if (!interface) { 3618c2ecf20Sopenharmony_ci printk(KERN_ERR "%s - error, can't find device for minor %d", 3628c2ecf20Sopenharmony_ci __func__, subminor); 3638c2ecf20Sopenharmony_ci retval = -ENODEV; 3648c2ecf20Sopenharmony_ci goto exit; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci dev = usb_get_intfdata(interface); 3688c2ecf20Sopenharmony_ci if (!dev) { 3698c2ecf20Sopenharmony_ci retval = -ENODEV; 3708c2ecf20Sopenharmony_ci goto exit; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* increment our usage count for the device */ 3748c2ecf20Sopenharmony_ci kref_get(&dev->kref); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* save our object in the file's private structure */ 3778c2ecf20Sopenharmony_ci mutex_lock(&dev->io_mutex); 3788c2ecf20Sopenharmony_ci file->private_data = dev; 3798c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciexit: 3828c2ecf20Sopenharmony_ci return retval; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int yurex_release(struct inode *inode, struct file *file) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct usb_yurex *dev; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci dev = file->private_data; 3908c2ecf20Sopenharmony_ci if (dev == NULL) 3918c2ecf20Sopenharmony_ci return -ENODEV; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* decrement the count on our device */ 3948c2ecf20Sopenharmony_ci kref_put(&dev->kref, yurex_delete); 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, 3998c2ecf20Sopenharmony_ci loff_t *ppos) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct usb_yurex *dev; 4028c2ecf20Sopenharmony_ci int len = 0; 4038c2ecf20Sopenharmony_ci char in_buffer[20]; 4048c2ecf20Sopenharmony_ci unsigned long flags; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci dev = file->private_data; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci mutex_lock(&dev->io_mutex); 4098c2ecf20Sopenharmony_ci if (dev->disconnected) { /* already disconnected */ 4108c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 4118c2ecf20Sopenharmony_ci return -ENODEV; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 4158c2ecf20Sopenharmony_ci len = snprintf(in_buffer, 20, "%lld\n", dev->bbu); 4168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 4178c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(len >= sizeof(in_buffer))) 4208c2ecf20Sopenharmony_ci return -EIO; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return simple_read_from_buffer(buffer, count, ppos, in_buffer, len); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic ssize_t yurex_write(struct file *file, const char __user *user_buffer, 4268c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct usb_yurex *dev; 4298c2ecf20Sopenharmony_ci int i, set = 0, retval = 0; 4308c2ecf20Sopenharmony_ci char buffer[16 + 1]; 4318c2ecf20Sopenharmony_ci char *data = buffer; 4328c2ecf20Sopenharmony_ci unsigned long long c, c2 = 0; 4338c2ecf20Sopenharmony_ci signed long timeout = 0; 4348c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci count = min(sizeof(buffer) - 1, count); 4378c2ecf20Sopenharmony_ci dev = file->private_data; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* verify that we actually have some data to write */ 4408c2ecf20Sopenharmony_ci if (count == 0) 4418c2ecf20Sopenharmony_ci goto error; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci mutex_lock(&dev->io_mutex); 4448c2ecf20Sopenharmony_ci if (dev->disconnected) { /* already disconnected */ 4458c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 4468c2ecf20Sopenharmony_ci retval = -ENODEV; 4478c2ecf20Sopenharmony_ci goto error; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (copy_from_user(buffer, user_buffer, count)) { 4518c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 4528c2ecf20Sopenharmony_ci retval = -EFAULT; 4538c2ecf20Sopenharmony_ci goto error; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci buffer[count] = 0; 4568c2ecf20Sopenharmony_ci memset(dev->cntl_buffer, CMD_PADDING, YUREX_BUF_SIZE); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci switch (buffer[0]) { 4598c2ecf20Sopenharmony_ci case CMD_ANIMATE: 4608c2ecf20Sopenharmony_ci case CMD_LED: 4618c2ecf20Sopenharmony_ci dev->cntl_buffer[0] = buffer[0]; 4628c2ecf20Sopenharmony_ci dev->cntl_buffer[1] = buffer[1]; 4638c2ecf20Sopenharmony_ci dev->cntl_buffer[2] = CMD_EOF; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci case CMD_READ: 4668c2ecf20Sopenharmony_ci case CMD_VERSION: 4678c2ecf20Sopenharmony_ci dev->cntl_buffer[0] = buffer[0]; 4688c2ecf20Sopenharmony_ci dev->cntl_buffer[1] = 0x00; 4698c2ecf20Sopenharmony_ci dev->cntl_buffer[2] = CMD_EOF; 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci case CMD_SET: 4728c2ecf20Sopenharmony_ci data++; 4738c2ecf20Sopenharmony_ci fallthrough; 4748c2ecf20Sopenharmony_ci case '0' ... '9': 4758c2ecf20Sopenharmony_ci set = 1; 4768c2ecf20Sopenharmony_ci c = c2 = simple_strtoull(data, NULL, 0); 4778c2ecf20Sopenharmony_ci dev->cntl_buffer[0] = CMD_SET; 4788c2ecf20Sopenharmony_ci for (i = 1; i < 6; i++) { 4798c2ecf20Sopenharmony_ci dev->cntl_buffer[i] = (c>>32) & 0xff; 4808c2ecf20Sopenharmony_ci c <<= 8; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci buffer[6] = CMD_EOF; 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci default: 4858c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 4868c2ecf20Sopenharmony_ci return -EINVAL; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* send the data as the control msg */ 4908c2ecf20Sopenharmony_ci prepare_to_wait(&dev->waitq, &wait, TASK_INTERRUPTIBLE); 4918c2ecf20Sopenharmony_ci dev_dbg(&dev->interface->dev, "%s - submit %c\n", __func__, 4928c2ecf20Sopenharmony_ci dev->cntl_buffer[0]); 4938c2ecf20Sopenharmony_ci retval = usb_submit_urb(dev->cntl_urb, GFP_ATOMIC); 4948c2ecf20Sopenharmony_ci if (retval >= 0) 4958c2ecf20Sopenharmony_ci timeout = schedule_timeout(YUREX_WRITE_TIMEOUT); 4968c2ecf20Sopenharmony_ci finish_wait(&dev->waitq, &wait); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* make sure URB is idle after timeout or (spurious) CMD_ACK */ 4998c2ecf20Sopenharmony_ci usb_kill_urb(dev->cntl_urb); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci mutex_unlock(&dev->io_mutex); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (retval < 0) { 5048c2ecf20Sopenharmony_ci dev_err(&dev->interface->dev, 5058c2ecf20Sopenharmony_ci "%s - failed to send bulk msg, error %d\n", 5068c2ecf20Sopenharmony_ci __func__, retval); 5078c2ecf20Sopenharmony_ci goto error; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci if (set && timeout) 5108c2ecf20Sopenharmony_ci dev->bbu = c2; 5118c2ecf20Sopenharmony_ci return timeout ? count : -EIO; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cierror: 5148c2ecf20Sopenharmony_ci return retval; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic const struct file_operations yurex_fops = { 5188c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5198c2ecf20Sopenharmony_ci .read = yurex_read, 5208c2ecf20Sopenharmony_ci .write = yurex_write, 5218c2ecf20Sopenharmony_ci .open = yurex_open, 5228c2ecf20Sopenharmony_ci .release = yurex_release, 5238c2ecf20Sopenharmony_ci .fasync = yurex_fasync, 5248c2ecf20Sopenharmony_ci .llseek = default_llseek, 5258c2ecf20Sopenharmony_ci}; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cimodule_usb_driver(yurex_driver); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 530