Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 1 | /* |
Ruslan Pisarev | d005864 | 2010-10-20 06:35:54 -0300 | [diff] [blame] | 2 | * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices |
| 3 | * |
| 4 | * Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.de> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation version 2 |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License |
| 16 | * along with this program; if not, write to the Free Software |
| 17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 18 | */ |
| 19 | |
| 20 | #include <linux/module.h> |
| 21 | #include <linux/init.h> |
| 22 | #include <linux/delay.h> |
| 23 | |
| 24 | #include <linux/input.h> |
| 25 | #include <linux/usb.h> |
| 26 | |
Mauro Carvalho Chehab | 6bda964 | 2010-11-17 13:28:38 -0300 | [diff] [blame] | 27 | #include <media/rc-core.h> |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 28 | |
| 29 | #include "tm6000.h" |
| 30 | #include "tm6000-regs.h" |
| 31 | |
| 32 | static unsigned int ir_debug; |
| 33 | module_param(ir_debug, int, 0644); |
| 34 | MODULE_PARM_DESC(ir_debug, "enable debug message [IR]"); |
| 35 | |
| 36 | static unsigned int enable_ir = 1; |
| 37 | module_param(enable_ir, int, 0644); |
Stefan Ringel | 401ad27 | 2010-08-24 16:36:09 -0300 | [diff] [blame] | 38 | MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 39 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 40 | /* number of 50ms for ON-OFF-ON power led */ |
| 41 | /* show IR activity */ |
| 42 | #define PWLED_OFF 2 |
| 43 | |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 44 | #undef dprintk |
| 45 | |
Mauro Carvalho Chehab | 02c7105 | 2010-07-05 17:09:11 -0300 | [diff] [blame] | 46 | #define dprintk(fmt, arg...) \ |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 47 | if (ir_debug) { \ |
| 48 | printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ |
| 49 | } |
| 50 | |
| 51 | struct tm6000_ir_poll_result { |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 52 | u16 rc_data; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 53 | }; |
| 54 | |
| 55 | struct tm6000_IR { |
| 56 | struct tm6000_core *dev; |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 57 | struct rc_dev *rc; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 58 | char name[32]; |
| 59 | char phys[32]; |
| 60 | |
| 61 | /* poll expernal decoder */ |
| 62 | int polling; |
| 63 | struct delayed_work work; |
| 64 | u8 wait:1; |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 65 | u8 key:1; |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 66 | u8 pwled:1; |
| 67 | u8 pwledcnt; |
| 68 | u16 key_addr; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 69 | struct urb *int_urb; |
| 70 | u8 *urb_data; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 71 | |
| 72 | int (*get_key) (struct tm6000_IR *, struct tm6000_ir_poll_result *); |
| 73 | |
| 74 | /* IR device properties */ |
Mauro Carvalho Chehab | 52b6614 | 2010-11-17 14:20:52 -0300 | [diff] [blame] | 75 | u64 rc_type; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 76 | }; |
| 77 | |
| 78 | |
| 79 | void tm6000_ir_wait(struct tm6000_core *dev, u8 state) |
| 80 | { |
| 81 | struct tm6000_IR *ir = dev->ir; |
| 82 | |
| 83 | if (!dev->ir) |
| 84 | return; |
| 85 | |
| 86 | if (state) |
| 87 | ir->wait = 1; |
| 88 | else |
| 89 | ir->wait = 0; |
| 90 | } |
| 91 | |
| 92 | |
| 93 | static int tm6000_ir_config(struct tm6000_IR *ir) |
| 94 | { |
| 95 | struct tm6000_core *dev = ir->dev; |
| 96 | u8 buf[10]; |
| 97 | int rc; |
| 98 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 99 | switch (ir->rc_type) { |
| 100 | case RC_TYPE_NEC: |
| 101 | /* Setup IR decoder for NEC standard 12MHz system clock */ |
| 102 | /* IR_LEADER_CNT = 0.9ms */ |
| 103 | tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER1, 0xaa); |
| 104 | tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER0, 0x30); |
| 105 | /* IR_PULSE_CNT = 0.7ms */ |
| 106 | tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20); |
| 107 | tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0); |
| 108 | /* Remote WAKEUP = enable */ |
| 109 | tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); |
| 110 | /* IR_WKUP_SEL = Low byte in decoded IR data */ |
| 111 | tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff); |
| 112 | /* IR_WKU_ADD code */ |
| 113 | tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0xff); |
| 114 | tm6000_flash_led(dev, 0); |
| 115 | msleep(100); |
| 116 | tm6000_flash_led(dev, 1); |
| 117 | break; |
| 118 | default: |
| 119 | /* hack */ |
| 120 | buf[0] = 0xff; |
| 121 | buf[1] = 0xff; |
| 122 | buf[2] = 0xf2; |
| 123 | buf[3] = 0x2b; |
| 124 | buf[4] = 0x20; |
| 125 | buf[5] = 0x35; |
| 126 | buf[6] = 0x60; |
| 127 | buf[7] = 0x04; |
| 128 | buf[8] = 0xc0; |
| 129 | buf[9] = 0x08; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 130 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 131 | rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | |
| 132 | USB_RECIP_DEVICE, REQ_00_SET_IR_VALUE, 0, 0, buf, 0x0a); |
| 133 | msleep(100); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 134 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 135 | if (rc < 0) { |
| 136 | printk(KERN_INFO "IR configuration failed"); |
| 137 | return rc; |
| 138 | } |
| 139 | break; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 140 | } |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 141 | |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 142 | return 0; |
| 143 | } |
| 144 | |
| 145 | static void tm6000_ir_urb_received(struct urb *urb) |
| 146 | { |
| 147 | struct tm6000_core *dev = urb->context; |
| 148 | struct tm6000_IR *ir = dev->ir; |
| 149 | int rc; |
Mauro Carvalho Chehab | 02c7105 | 2010-07-05 17:09:11 -0300 | [diff] [blame] | 150 | |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 151 | if (urb->status != 0) |
| 152 | printk(KERN_INFO "not ready\n"); |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 153 | else if (urb->actual_length > 0) { |
Mauro Carvalho Chehab | 02c7105 | 2010-07-05 17:09:11 -0300 | [diff] [blame] | 154 | memcpy(ir->urb_data, urb->transfer_buffer, urb->actual_length); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 155 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 156 | dprintk("data %02x %02x %02x %02x\n", ir->urb_data[0], |
| 157 | ir->urb_data[1], ir->urb_data[2], ir->urb_data[3]); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 158 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 159 | ir->key = 1; |
| 160 | } |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 161 | |
| 162 | rc = usb_submit_urb(urb, GFP_ATOMIC); |
| 163 | } |
| 164 | |
| 165 | static int default_polling_getkey(struct tm6000_IR *ir, |
| 166 | struct tm6000_ir_poll_result *poll_result) |
| 167 | { |
| 168 | struct tm6000_core *dev = ir->dev; |
| 169 | int rc; |
| 170 | u8 buf[2]; |
| 171 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 172 | if (ir->wait && !&dev->int_in) |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 173 | return 0; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 174 | |
| 175 | if (&dev->int_in) { |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 176 | switch (ir->rc_type) { |
| 177 | case RC_TYPE_RC5: |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 178 | poll_result->rc_data = ir->urb_data[0]; |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 179 | break; |
| 180 | case RC_TYPE_NEC: |
| 181 | if (ir->urb_data[1] == ((ir->key_addr >> 8) & 0xff)) { |
| 182 | poll_result->rc_data = ir->urb_data[0] |
| 183 | | ir->urb_data[1] << 8; |
| 184 | } |
| 185 | break; |
| 186 | default: |
| 187 | poll_result->rc_data = ir->urb_data[0] |
| 188 | | ir->urb_data[1] << 8; |
| 189 | break; |
| 190 | } |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 191 | } else { |
| 192 | tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); |
| 193 | msleep(10); |
| 194 | tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); |
| 195 | msleep(10); |
| 196 | |
Mauro Carvalho Chehab | 52b6614 | 2010-11-17 14:20:52 -0300 | [diff] [blame] | 197 | if (ir->rc_type == RC_TYPE_RC5) { |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 198 | rc = tm6000_read_write_usb(dev, USB_DIR_IN | |
| 199 | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| 200 | REQ_02_GET_IR_CODE, 0, 0, buf, 1); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 201 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 202 | msleep(10); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 203 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 204 | dprintk("read data=%02x\n", buf[0]); |
| 205 | if (rc < 0) |
| 206 | return rc; |
Mauro Carvalho Chehab | 02c7105 | 2010-07-05 17:09:11 -0300 | [diff] [blame] | 207 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 208 | poll_result->rc_data = buf[0]; |
| 209 | } else { |
| 210 | rc = tm6000_read_write_usb(dev, USB_DIR_IN | |
| 211 | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| 212 | REQ_02_GET_IR_CODE, 0, 0, buf, 2); |
| 213 | |
| 214 | msleep(10); |
| 215 | |
| 216 | dprintk("read data=%04x\n", buf[0] | buf[1] << 8); |
| 217 | if (rc < 0) |
| 218 | return rc; |
| 219 | |
| 220 | poll_result->rc_data = buf[0] | buf[1] << 8; |
| 221 | } |
| 222 | if ((poll_result->rc_data & 0x00ff) != 0xff) |
| 223 | ir->key = 1; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 224 | } |
| 225 | return 0; |
| 226 | } |
| 227 | |
| 228 | static void tm6000_ir_handle_key(struct tm6000_IR *ir) |
| 229 | { |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 230 | struct tm6000_core *dev = ir->dev; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 231 | int result; |
| 232 | struct tm6000_ir_poll_result poll_result; |
| 233 | |
| 234 | /* read the registers containing the IR status */ |
| 235 | result = ir->get_key(ir, &poll_result); |
| 236 | if (result < 0) { |
| 237 | printk(KERN_INFO "ir->get_key() failed %d\n", result); |
| 238 | return; |
| 239 | } |
| 240 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 241 | dprintk("ir->get_key result data=%04x\n", poll_result.rc_data); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 242 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 243 | if (ir->pwled) { |
| 244 | if (ir->pwledcnt >= PWLED_OFF) { |
| 245 | ir->pwled = 0; |
| 246 | ir->pwledcnt = 0; |
| 247 | tm6000_flash_led(dev, 1); |
| 248 | } else |
| 249 | ir->pwledcnt += 1; |
| 250 | } |
| 251 | |
Stefan Ringel | 1b376da | 2010-09-09 14:45:22 -0300 | [diff] [blame] | 252 | if (ir->key) { |
Mauro Carvalho Chehab | ca86674 | 2010-11-17 13:53:11 -0300 | [diff] [blame] | 253 | rc_keydown(ir->rc, poll_result.rc_data, 0); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 254 | ir->key = 0; |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 255 | ir->pwled = 1; |
| 256 | ir->pwledcnt = 0; |
| 257 | tm6000_flash_led(dev, 0); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 258 | } |
| 259 | return; |
| 260 | } |
| 261 | |
| 262 | static void tm6000_ir_work(struct work_struct *work) |
| 263 | { |
| 264 | struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); |
| 265 | |
| 266 | tm6000_ir_handle_key(ir); |
| 267 | schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); |
| 268 | } |
| 269 | |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 270 | static int tm6000_ir_start(struct rc_dev *rc) |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 271 | { |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 272 | struct tm6000_IR *ir = rc->priv; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 273 | |
| 274 | INIT_DELAYED_WORK(&ir->work, tm6000_ir_work); |
| 275 | schedule_delayed_work(&ir->work, 0); |
| 276 | |
| 277 | return 0; |
| 278 | } |
| 279 | |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 280 | static void tm6000_ir_stop(struct rc_dev *rc) |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 281 | { |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 282 | struct tm6000_IR *ir = rc->priv; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 283 | |
| 284 | cancel_delayed_work_sync(&ir->work); |
| 285 | } |
| 286 | |
Thierry Reding | 3d1a51d | 2011-08-04 04:14:01 -0300 | [diff] [blame] | 287 | static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type) |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 288 | { |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 289 | struct tm6000_IR *ir = rc->priv; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 290 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 291 | if (!ir) |
| 292 | return 0; |
| 293 | |
| 294 | if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC)) |
| 295 | ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff); |
| 296 | |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 297 | ir->get_key = default_polling_getkey; |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 298 | ir->rc_type = rc_type; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 299 | |
| 300 | tm6000_ir_config(ir); |
| 301 | /* TODO */ |
| 302 | return 0; |
| 303 | } |
| 304 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 305 | int tm6000_ir_int_start(struct tm6000_core *dev) |
| 306 | { |
| 307 | struct tm6000_IR *ir = dev->ir; |
| 308 | int pipe, size; |
| 309 | int err = -ENOMEM; |
| 310 | |
| 311 | |
| 312 | if (!ir) |
| 313 | return -ENODEV; |
| 314 | |
| 315 | ir->int_urb = usb_alloc_urb(0, GFP_KERNEL); |
Dan Carpenter | 2591418 | 2011-01-03 08:44:38 +0300 | [diff] [blame] | 316 | if (!ir->int_urb) |
| 317 | return -ENOMEM; |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 318 | |
| 319 | pipe = usb_rcvintpipe(dev->udev, |
| 320 | dev->int_in.endp->desc.bEndpointAddress |
| 321 | & USB_ENDPOINT_NUMBER_MASK); |
| 322 | |
| 323 | size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); |
| 324 | dprintk("IR max size: %d\n", size); |
| 325 | |
| 326 | ir->int_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); |
| 327 | if (ir->int_urb->transfer_buffer == NULL) { |
| 328 | usb_free_urb(ir->int_urb); |
| 329 | return err; |
| 330 | } |
| 331 | dprintk("int interval: %d\n", dev->int_in.endp->desc.bInterval); |
| 332 | usb_fill_int_urb(ir->int_urb, dev->udev, pipe, |
| 333 | ir->int_urb->transfer_buffer, size, |
| 334 | tm6000_ir_urb_received, dev, |
| 335 | dev->int_in.endp->desc.bInterval); |
| 336 | err = usb_submit_urb(ir->int_urb, GFP_KERNEL); |
| 337 | if (err) { |
| 338 | kfree(ir->int_urb->transfer_buffer); |
| 339 | usb_free_urb(ir->int_urb); |
| 340 | return err; |
| 341 | } |
| 342 | ir->urb_data = kzalloc(size, GFP_KERNEL); |
| 343 | |
| 344 | return 0; |
| 345 | } |
| 346 | |
| 347 | void tm6000_ir_int_stop(struct tm6000_core *dev) |
| 348 | { |
| 349 | struct tm6000_IR *ir = dev->ir; |
| 350 | |
| 351 | if (!ir) |
| 352 | return; |
| 353 | |
| 354 | usb_kill_urb(ir->int_urb); |
| 355 | kfree(ir->int_urb->transfer_buffer); |
| 356 | usb_free_urb(ir->int_urb); |
| 357 | ir->int_urb = NULL; |
| 358 | kfree(ir->urb_data); |
| 359 | ir->urb_data = NULL; |
| 360 | } |
| 361 | |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 362 | int tm6000_ir_init(struct tm6000_core *dev) |
| 363 | { |
| 364 | struct tm6000_IR *ir; |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 365 | struct rc_dev *rc; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 366 | int err = -ENOMEM; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 367 | |
| 368 | if (!enable_ir) |
| 369 | return -ENODEV; |
| 370 | |
| 371 | if (!dev->caps.has_remote) |
| 372 | return 0; |
| 373 | |
| 374 | if (!dev->ir_codes) |
| 375 | return 0; |
| 376 | |
| 377 | ir = kzalloc(sizeof(*ir), GFP_KERNEL); |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 378 | rc = rc_allocate_device(); |
Dan Carpenter | b01627c | 2011-01-03 08:47:40 +0300 | [diff] [blame] | 379 | if (!ir || !rc) |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 380 | goto out; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 381 | |
| 382 | /* record handles to ourself */ |
| 383 | ir->dev = dev; |
| 384 | dev->ir = ir; |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 385 | ir->rc = rc; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 386 | |
| 387 | /* input einrichten */ |
Mauro Carvalho Chehab | 52b6614 | 2010-11-17 14:20:52 -0300 | [diff] [blame] | 388 | rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC; |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 389 | rc->priv = ir; |
| 390 | rc->change_protocol = tm6000_ir_change_protocol; |
| 391 | rc->open = tm6000_ir_start; |
| 392 | rc->close = tm6000_ir_stop; |
| 393 | rc->driver_type = RC_DRIVER_SCANCODE; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 394 | |
| 395 | ir->polling = 50; |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 396 | ir->pwled = 0; |
| 397 | ir->pwledcnt = 0; |
| 398 | |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 399 | |
| 400 | snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", |
| 401 | dev->name); |
| 402 | |
| 403 | usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); |
| 404 | strlcat(ir->phys, "/input0", sizeof(ir->phys)); |
| 405 | |
Mauro Carvalho Chehab | 52b6614 | 2010-11-17 14:20:52 -0300 | [diff] [blame] | 406 | tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 407 | |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 408 | rc->input_name = ir->name; |
| 409 | rc->input_phys = ir->phys; |
| 410 | rc->input_id.bustype = BUS_USB; |
| 411 | rc->input_id.version = 1; |
| 412 | rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); |
| 413 | rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); |
| 414 | rc->map_name = dev->ir_codes; |
| 415 | rc->driver_name = "tm6000"; |
| 416 | rc->dev.parent = &dev->udev->dev; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 417 | |
| 418 | if (&dev->int_in) { |
| 419 | dprintk("IR over int\n"); |
| 420 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 421 | err = tm6000_ir_int_start(dev); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 422 | |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 423 | if (err) |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 424 | goto out; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | /* ir register */ |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 428 | err = rc_register_device(rc); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 429 | if (err) |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 430 | goto out; |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 431 | |
| 432 | return 0; |
| 433 | |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 434 | out: |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 435 | dev->ir = NULL; |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 436 | rc_free_device(rc); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 437 | kfree(ir); |
| 438 | return err; |
| 439 | } |
| 440 | |
| 441 | int tm6000_ir_fini(struct tm6000_core *dev) |
| 442 | { |
| 443 | struct tm6000_IR *ir = dev->ir; |
| 444 | |
| 445 | /* skip detach on non attached board */ |
| 446 | |
| 447 | if (!ir) |
| 448 | return 0; |
| 449 | |
David Härdeman | d8b4b58 | 2010-10-29 16:08:23 -0300 | [diff] [blame] | 450 | rc_unregister_device(ir->rc); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 451 | |
Curtis McEnroe | 41eb8d6 | 2011-06-02 20:33:33 -0400 | [diff] [blame] | 452 | if (ir->int_urb) |
Dmitri Belimov | 641d211 | 2010-12-22 05:57:46 -0300 | [diff] [blame] | 453 | tm6000_ir_int_stop(dev); |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 454 | |
Stefan Ringel | d064f96 | 2010-06-20 17:16:52 -0300 | [diff] [blame] | 455 | kfree(ir); |
| 456 | dev->ir = NULL; |
| 457 | |
| 458 | return 0; |
| 459 | } |