18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TechnoTrend USB IR Receiver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Sean Young <sean@mess.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/usb.h> 108c2ecf20Sopenharmony_ci#include <linux/usb/input.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/leds.h> 138c2ecf20Sopenharmony_ci#include <media/rc-core.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define DRIVER_NAME "ttusbir" 168c2ecf20Sopenharmony_ci#define DRIVER_DESC "TechnoTrend USB IR Receiver" 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * The Windows driver uses 8 URBS, the original lirc drivers has a 198c2ecf20Sopenharmony_ci * configurable amount (2 default, 4 max). This device generates about 125 208c2ecf20Sopenharmony_ci * messages per second (!), whether IR is idle or not. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#define NUM_URBS 4 238c2ecf20Sopenharmony_ci#define US_PER_BYTE 62 248c2ecf20Sopenharmony_ci#define US_PER_BIT (US_PER_BYTE / 8) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct ttusbir { 278c2ecf20Sopenharmony_ci struct rc_dev *rc; 288c2ecf20Sopenharmony_ci struct device *dev; 298c2ecf20Sopenharmony_ci struct usb_device *udev; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci struct urb *urb[NUM_URBS]; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci struct led_classdev led; 348c2ecf20Sopenharmony_ci struct urb *bulk_urb; 358c2ecf20Sopenharmony_ci uint8_t bulk_buffer[5]; 368c2ecf20Sopenharmony_ci int bulk_out_endp, iso_in_endp; 378c2ecf20Sopenharmony_ci bool led_on, is_led_on; 388c2ecf20Sopenharmony_ci atomic_t led_complete; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci char phys[64]; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic enum led_brightness ttusbir_brightness_get(struct led_classdev *led_dev) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct ttusbir *tt = container_of(led_dev, struct ttusbir, led); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return tt->led_on ? LED_FULL : LED_OFF; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void ttusbir_set_led(struct ttusbir *tt) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci smp_mb(); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (tt->led_on != tt->is_led_on && tt->udev && 578c2ecf20Sopenharmony_ci atomic_add_unless(&tt->led_complete, 1, 1)) { 588c2ecf20Sopenharmony_ci tt->bulk_buffer[4] = tt->is_led_on = tt->led_on; 598c2ecf20Sopenharmony_ci ret = usb_submit_urb(tt->bulk_urb, GFP_ATOMIC); 608c2ecf20Sopenharmony_ci if (ret) { 618c2ecf20Sopenharmony_ci dev_warn(tt->dev, "failed to submit bulk urb: %d\n", 628c2ecf20Sopenharmony_ci ret); 638c2ecf20Sopenharmony_ci atomic_dec(&tt->led_complete); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void ttusbir_brightness_set(struct led_classdev *led_dev, enum 698c2ecf20Sopenharmony_ci led_brightness brightness) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct ttusbir *tt = container_of(led_dev, struct ttusbir, led); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci tt->led_on = brightness != LED_OFF; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ttusbir_set_led(tt); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * The urb cannot be reused until the urb completes 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistatic void ttusbir_bulk_complete(struct urb *urb) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct ttusbir *tt = urb->context; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci atomic_dec(&tt->led_complete); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci switch (urb->status) { 888c2ecf20Sopenharmony_ci case 0: 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case -ECONNRESET: 918c2ecf20Sopenharmony_ci case -ENOENT: 928c2ecf20Sopenharmony_ci case -ESHUTDOWN: 938c2ecf20Sopenharmony_ci usb_unlink_urb(urb); 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci case -EPIPE: 968c2ecf20Sopenharmony_ci default: 978c2ecf20Sopenharmony_ci dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status); 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ttusbir_set_led(tt); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * The data is one bit per sample, a set bit signifying silence and samples 1068c2ecf20Sopenharmony_ci * being MSB first. Bit 0 can contain garbage so take it to be whatever 1078c2ecf20Sopenharmony_ci * bit 1 is, so we don't have unexpected edges. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cistatic void ttusbir_process_ir_data(struct ttusbir *tt, uint8_t *buf) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct ir_raw_event rawir = {}; 1128c2ecf20Sopenharmony_ci unsigned i, v, b; 1138c2ecf20Sopenharmony_ci bool event = false; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0; i < 128; i++) { 1168c2ecf20Sopenharmony_ci v = buf[i] & 0xfe; 1178c2ecf20Sopenharmony_ci switch (v) { 1188c2ecf20Sopenharmony_ci case 0xfe: 1198c2ecf20Sopenharmony_ci rawir.pulse = false; 1208c2ecf20Sopenharmony_ci rawir.duration = US_PER_BYTE; 1218c2ecf20Sopenharmony_ci if (ir_raw_event_store_with_filter(tt->rc, &rawir)) 1228c2ecf20Sopenharmony_ci event = true; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case 0: 1258c2ecf20Sopenharmony_ci rawir.pulse = true; 1268c2ecf20Sopenharmony_ci rawir.duration = US_PER_BYTE; 1278c2ecf20Sopenharmony_ci if (ir_raw_event_store_with_filter(tt->rc, &rawir)) 1288c2ecf20Sopenharmony_ci event = true; 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci default: 1318c2ecf20Sopenharmony_ci /* one edge per byte */ 1328c2ecf20Sopenharmony_ci if (v & 2) { 1338c2ecf20Sopenharmony_ci b = ffz(v | 1); 1348c2ecf20Sopenharmony_ci rawir.pulse = true; 1358c2ecf20Sopenharmony_ci } else { 1368c2ecf20Sopenharmony_ci b = ffs(v) - 1; 1378c2ecf20Sopenharmony_ci rawir.pulse = false; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci rawir.duration = US_PER_BIT * (8 - b); 1418c2ecf20Sopenharmony_ci if (ir_raw_event_store_with_filter(tt->rc, &rawir)) 1428c2ecf20Sopenharmony_ci event = true; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci rawir.pulse = !rawir.pulse; 1458c2ecf20Sopenharmony_ci rawir.duration = US_PER_BIT * b; 1468c2ecf20Sopenharmony_ci if (ir_raw_event_store_with_filter(tt->rc, &rawir)) 1478c2ecf20Sopenharmony_ci event = true; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* don't wakeup when there's nothing to do */ 1538c2ecf20Sopenharmony_ci if (event) 1548c2ecf20Sopenharmony_ci ir_raw_event_handle(tt->rc); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void ttusbir_urb_complete(struct urb *urb) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct ttusbir *tt = urb->context; 1608c2ecf20Sopenharmony_ci int rc; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci switch (urb->status) { 1638c2ecf20Sopenharmony_ci case 0: 1648c2ecf20Sopenharmony_ci ttusbir_process_ir_data(tt, urb->transfer_buffer); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case -ECONNRESET: 1678c2ecf20Sopenharmony_ci case -ENOENT: 1688c2ecf20Sopenharmony_ci case -ESHUTDOWN: 1698c2ecf20Sopenharmony_ci usb_unlink_urb(urb); 1708c2ecf20Sopenharmony_ci return; 1718c2ecf20Sopenharmony_ci case -EPIPE: 1728c2ecf20Sopenharmony_ci default: 1738c2ecf20Sopenharmony_ci dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci rc = usb_submit_urb(urb, GFP_ATOMIC); 1788c2ecf20Sopenharmony_ci if (rc && rc != -ENODEV) 1798c2ecf20Sopenharmony_ci dev_warn(tt->dev, "failed to resubmit urb: %d\n", rc); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int ttusbir_probe(struct usb_interface *intf, 1838c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct ttusbir *tt; 1868c2ecf20Sopenharmony_ci struct usb_interface_descriptor *idesc; 1878c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *desc; 1888c2ecf20Sopenharmony_ci struct rc_dev *rc; 1898c2ecf20Sopenharmony_ci int i, j, ret; 1908c2ecf20Sopenharmony_ci int altsetting = -1; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci tt = kzalloc(sizeof(*tt), GFP_KERNEL); 1938c2ecf20Sopenharmony_ci rc = rc_allocate_device(RC_DRIVER_IR_RAW); 1948c2ecf20Sopenharmony_ci if (!tt || !rc) { 1958c2ecf20Sopenharmony_ci ret = -ENOMEM; 1968c2ecf20Sopenharmony_ci goto out; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* find the correct alt setting */ 2008c2ecf20Sopenharmony_ci for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) { 2018c2ecf20Sopenharmony_ci int max_packet, bulk_out_endp = -1, iso_in_endp = -1; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci idesc = &intf->altsetting[i].desc; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for (j = 0; j < idesc->bNumEndpoints; j++) { 2068c2ecf20Sopenharmony_ci desc = &intf->altsetting[i].endpoint[j].desc; 2078c2ecf20Sopenharmony_ci max_packet = le16_to_cpu(desc->wMaxPacketSize); 2088c2ecf20Sopenharmony_ci if (usb_endpoint_dir_in(desc) && 2098c2ecf20Sopenharmony_ci usb_endpoint_xfer_isoc(desc) && 2108c2ecf20Sopenharmony_ci max_packet == 0x10) 2118c2ecf20Sopenharmony_ci iso_in_endp = j; 2128c2ecf20Sopenharmony_ci else if (usb_endpoint_dir_out(desc) && 2138c2ecf20Sopenharmony_ci usb_endpoint_xfer_bulk(desc) && 2148c2ecf20Sopenharmony_ci max_packet == 0x20) 2158c2ecf20Sopenharmony_ci bulk_out_endp = j; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (bulk_out_endp != -1 && iso_in_endp != -1) { 2188c2ecf20Sopenharmony_ci tt->bulk_out_endp = bulk_out_endp; 2198c2ecf20Sopenharmony_ci tt->iso_in_endp = iso_in_endp; 2208c2ecf20Sopenharmony_ci altsetting = i; 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (altsetting == -1) { 2278c2ecf20Sopenharmony_ci dev_err(&intf->dev, "cannot find expected altsetting\n"); 2288c2ecf20Sopenharmony_ci ret = -ENODEV; 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci tt->dev = &intf->dev; 2338c2ecf20Sopenharmony_ci tt->udev = interface_to_usbdev(intf); 2348c2ecf20Sopenharmony_ci tt->rc = rc; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = usb_set_interface(tt->udev, 0, altsetting); 2378c2ecf20Sopenharmony_ci if (ret) 2388c2ecf20Sopenharmony_ci goto out; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; i++) { 2418c2ecf20Sopenharmony_ci struct urb *urb = usb_alloc_urb(8, GFP_KERNEL); 2428c2ecf20Sopenharmony_ci void *buffer; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (!urb) { 2458c2ecf20Sopenharmony_ci ret = -ENOMEM; 2468c2ecf20Sopenharmony_ci goto out; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci urb->dev = tt->udev; 2508c2ecf20Sopenharmony_ci urb->context = tt; 2518c2ecf20Sopenharmony_ci urb->pipe = usb_rcvisocpipe(tt->udev, tt->iso_in_endp); 2528c2ecf20Sopenharmony_ci urb->interval = 1; 2538c2ecf20Sopenharmony_ci buffer = usb_alloc_coherent(tt->udev, 128, GFP_KERNEL, 2548c2ecf20Sopenharmony_ci &urb->transfer_dma); 2558c2ecf20Sopenharmony_ci if (!buffer) { 2568c2ecf20Sopenharmony_ci usb_free_urb(urb); 2578c2ecf20Sopenharmony_ci ret = -ENOMEM; 2588c2ecf20Sopenharmony_ci goto out; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP | URB_ISO_ASAP; 2618c2ecf20Sopenharmony_ci urb->transfer_buffer = buffer; 2628c2ecf20Sopenharmony_ci urb->complete = ttusbir_urb_complete; 2638c2ecf20Sopenharmony_ci urb->number_of_packets = 8; 2648c2ecf20Sopenharmony_ci urb->transfer_buffer_length = 128; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) { 2678c2ecf20Sopenharmony_ci urb->iso_frame_desc[j].offset = j * 16; 2688c2ecf20Sopenharmony_ci urb->iso_frame_desc[j].length = 16; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci tt->urb[i] = urb; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci tt->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); 2758c2ecf20Sopenharmony_ci if (!tt->bulk_urb) { 2768c2ecf20Sopenharmony_ci ret = -ENOMEM; 2778c2ecf20Sopenharmony_ci goto out; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci tt->bulk_buffer[0] = 0xaa; 2818c2ecf20Sopenharmony_ci tt->bulk_buffer[1] = 0x01; 2828c2ecf20Sopenharmony_ci tt->bulk_buffer[2] = 0x05; 2838c2ecf20Sopenharmony_ci tt->bulk_buffer[3] = 0x01; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci usb_fill_bulk_urb(tt->bulk_urb, tt->udev, usb_sndbulkpipe(tt->udev, 2868c2ecf20Sopenharmony_ci tt->bulk_out_endp), tt->bulk_buffer, sizeof(tt->bulk_buffer), 2878c2ecf20Sopenharmony_ci ttusbir_bulk_complete, tt); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci tt->led.name = "ttusbir:green:power"; 2908c2ecf20Sopenharmony_ci tt->led.default_trigger = "rc-feedback"; 2918c2ecf20Sopenharmony_ci tt->led.brightness_set = ttusbir_brightness_set; 2928c2ecf20Sopenharmony_ci tt->led.brightness_get = ttusbir_brightness_get; 2938c2ecf20Sopenharmony_ci tt->is_led_on = tt->led_on = true; 2948c2ecf20Sopenharmony_ci atomic_set(&tt->led_complete, 0); 2958c2ecf20Sopenharmony_ci ret = led_classdev_register(&intf->dev, &tt->led); 2968c2ecf20Sopenharmony_ci if (ret) 2978c2ecf20Sopenharmony_ci goto out; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci usb_make_path(tt->udev, tt->phys, sizeof(tt->phys)); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci rc->device_name = DRIVER_DESC; 3028c2ecf20Sopenharmony_ci rc->input_phys = tt->phys; 3038c2ecf20Sopenharmony_ci usb_to_input_id(tt->udev, &rc->input_id); 3048c2ecf20Sopenharmony_ci rc->dev.parent = &intf->dev; 3058c2ecf20Sopenharmony_ci rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; 3068c2ecf20Sopenharmony_ci rc->priv = tt; 3078c2ecf20Sopenharmony_ci rc->driver_name = DRIVER_NAME; 3088c2ecf20Sopenharmony_ci rc->map_name = RC_MAP_TT_1500; 3098c2ecf20Sopenharmony_ci rc->min_timeout = 1; 3108c2ecf20Sopenharmony_ci rc->timeout = IR_DEFAULT_TIMEOUT; 3118c2ecf20Sopenharmony_ci rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * The precision is US_PER_BIT, but since every 8th bit can be 3158c2ecf20Sopenharmony_ci * overwritten with garbage the accuracy is at best 2 * US_PER_BIT. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci rc->rx_resolution = 2 * US_PER_BIT; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = rc_register_device(rc); 3208c2ecf20Sopenharmony_ci if (ret) { 3218c2ecf20Sopenharmony_ci dev_err(&intf->dev, "failed to register rc device %d\n", ret); 3228c2ecf20Sopenharmony_ci goto out2; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci usb_set_intfdata(intf, tt); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; i++) { 3288c2ecf20Sopenharmony_ci ret = usb_submit_urb(tt->urb[i], GFP_KERNEL); 3298c2ecf20Sopenharmony_ci if (ret) { 3308c2ecf20Sopenharmony_ci dev_err(tt->dev, "failed to submit urb %d\n", ret); 3318c2ecf20Sopenharmony_ci goto out3; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ciout3: 3378c2ecf20Sopenharmony_ci rc_unregister_device(rc); 3388c2ecf20Sopenharmony_ci rc = NULL; 3398c2ecf20Sopenharmony_ciout2: 3408c2ecf20Sopenharmony_ci led_classdev_unregister(&tt->led); 3418c2ecf20Sopenharmony_ciout: 3428c2ecf20Sopenharmony_ci if (tt) { 3438c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS && tt->urb[i]; i++) { 3448c2ecf20Sopenharmony_ci struct urb *urb = tt->urb[i]; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci usb_kill_urb(urb); 3478c2ecf20Sopenharmony_ci usb_free_coherent(tt->udev, 128, urb->transfer_buffer, 3488c2ecf20Sopenharmony_ci urb->transfer_dma); 3498c2ecf20Sopenharmony_ci usb_free_urb(urb); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci usb_kill_urb(tt->bulk_urb); 3528c2ecf20Sopenharmony_ci usb_free_urb(tt->bulk_urb); 3538c2ecf20Sopenharmony_ci kfree(tt); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci rc_free_device(rc); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic void ttusbir_disconnect(struct usb_interface *intf) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct ttusbir *tt = usb_get_intfdata(intf); 3638c2ecf20Sopenharmony_ci struct usb_device *udev = tt->udev; 3648c2ecf20Sopenharmony_ci int i; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci tt->udev = NULL; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci rc_unregister_device(tt->rc); 3698c2ecf20Sopenharmony_ci led_classdev_unregister(&tt->led); 3708c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; i++) { 3718c2ecf20Sopenharmony_ci usb_kill_urb(tt->urb[i]); 3728c2ecf20Sopenharmony_ci usb_free_coherent(udev, 128, tt->urb[i]->transfer_buffer, 3738c2ecf20Sopenharmony_ci tt->urb[i]->transfer_dma); 3748c2ecf20Sopenharmony_ci usb_free_urb(tt->urb[i]); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci usb_kill_urb(tt->bulk_urb); 3778c2ecf20Sopenharmony_ci usb_free_urb(tt->bulk_urb); 3788c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 3798c2ecf20Sopenharmony_ci kfree(tt); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int ttusbir_suspend(struct usb_interface *intf, pm_message_t message) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct ttusbir *tt = usb_get_intfdata(intf); 3858c2ecf20Sopenharmony_ci int i; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; i++) 3888c2ecf20Sopenharmony_ci usb_kill_urb(tt->urb[i]); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci led_classdev_suspend(&tt->led); 3918c2ecf20Sopenharmony_ci usb_kill_urb(tt->bulk_urb); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int ttusbir_resume(struct usb_interface *intf) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct ttusbir *tt = usb_get_intfdata(intf); 3998c2ecf20Sopenharmony_ci int i, rc; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci tt->is_led_on = true; 4028c2ecf20Sopenharmony_ci led_classdev_resume(&tt->led); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; i++) { 4058c2ecf20Sopenharmony_ci rc = usb_submit_urb(tt->urb[i], GFP_KERNEL); 4068c2ecf20Sopenharmony_ci if (rc) { 4078c2ecf20Sopenharmony_ci dev_warn(tt->dev, "failed to submit urb: %d\n", rc); 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return rc; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic const struct usb_device_id ttusbir_table[] = { 4168c2ecf20Sopenharmony_ci { USB_DEVICE(0x0b48, 0x2003) }, 4178c2ecf20Sopenharmony_ci { } 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic struct usb_driver ttusbir_driver = { 4218c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 4228c2ecf20Sopenharmony_ci .id_table = ttusbir_table, 4238c2ecf20Sopenharmony_ci .probe = ttusbir_probe, 4248c2ecf20Sopenharmony_ci .suspend = ttusbir_suspend, 4258c2ecf20Sopenharmony_ci .resume = ttusbir_resume, 4268c2ecf20Sopenharmony_ci .reset_resume = ttusbir_resume, 4278c2ecf20Sopenharmony_ci .disconnect = ttusbir_disconnect, 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cimodule_usb_driver(ttusbir_driver); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 4338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Young <sean@mess.org>"); 4348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ttusbir_table); 4368c2ecf20Sopenharmony_ci 437