OLD | NEW |
(Empty) | |
| 1 /* qmidevice.c - gobi QMI device |
| 2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. |
| 3 |
| 4 * This program is free software; you can redistribute it and/or modify |
| 5 * it under the terms of the GNU General Public License version 2 and |
| 6 * only version 2 as published by the Free Software Foundation. |
| 7 |
| 8 * This program is distributed in the hope that it will be useful, |
| 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 * GNU General Public License for more details. |
| 12 |
| 13 * You should have received a copy of the GNU General Public License |
| 14 * along with this program; if not, write to the Free Software |
| 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 16 * 02110-1301, USA. |
| 17 */ |
| 18 |
| 19 #include "qmidevice.h" |
| 20 #include "qcusbnet.h" |
| 21 |
| 22 struct readreq { |
| 23 struct list_head node; |
| 24 void *data; |
| 25 u16 tid; |
| 26 u16 size; |
| 27 }; |
| 28 |
| 29 struct notifyreq { |
| 30 struct list_head node; |
| 31 void (*func)(struct qcusbnet *, u16, void *); |
| 32 u16 tid; |
| 33 void *data; |
| 34 }; |
| 35 |
| 36 struct client { |
| 37 struct list_head node; |
| 38 u16 cid; |
| 39 struct list_head reads; |
| 40 struct list_head notifies; |
| 41 struct list_head urbs; |
| 42 }; |
| 43 |
| 44 struct urbsetup { |
| 45 u8 type; |
| 46 u8 code; |
| 47 u16 value; |
| 48 u16 index; |
| 49 u16 len; |
| 50 }; |
| 51 |
| 52 struct qmihandle { |
| 53 u16 cid; |
| 54 struct qcusbnet *dev; |
| 55 }; |
| 56 |
| 57 extern int qcusbnet_debug; |
| 58 static int qcusbnet2k_fwdelay; |
| 59 |
| 60 static bool device_valid(struct qcusbnet *dev); |
| 61 static struct client *client_bycid(struct qcusbnet *dev, u16 cid); |
| 62 static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data, u
16 size); |
| 63 static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data,
u16 *size); |
| 64 static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid, |
| 65 void (*hook)(struct qcusbnet *, u16 cid, void *), |
| 66 void *data); |
| 67 static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid); |
| 68 static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb); |
| 69 static struct urb *client_delurb(struct qcusbnet *dev, u16 cid); |
| 70 |
| 71 static int devqmi_open(struct inode *inode, struct file *file); |
| 72 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd
, unsigned long arg); |
| 73 static int devqmi_close(struct file *file, fl_owner_t ftable); |
| 74 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, |
| 75 loff_t *pos); |
| 76 static ssize_t devqmi_write(struct file *file, const char __user *buf, |
| 77 size_t size, loff_t *pos); |
| 78 |
| 79 static bool qmi_ready(struct qcusbnet *dev, u16 timeout); |
| 80 static void wds_callback(struct qcusbnet *dev, u16 cid, void *data); |
| 81 static int setup_wds_callback(struct qcusbnet *dev); |
| 82 static int qmidms_getmeid(struct qcusbnet *dev); |
| 83 |
| 84 #define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1) |
| 85 #define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2) |
| 86 #define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3) |
| 87 #define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll |
| 88 #define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll |
| 89 |
| 90 static const struct file_operations devqmi_fops = { |
| 91 .owner = THIS_MODULE, |
| 92 .read = devqmi_read, |
| 93 .write = devqmi_write, |
| 94 .ioctl = devqmi_ioctl, |
| 95 .open = devqmi_open, |
| 96 .flush = devqmi_close, |
| 97 }; |
| 98 |
| 99 #ifdef CONFIG_SMP |
| 100 static inline void assert_locked(struct qcusbnet *dev) |
| 101 { |
| 102 BUG_ON(!spin_is_locked(&dev->qmi.clients_lock)); |
| 103 } |
| 104 #else |
| 105 static inline void assert_locked(struct qcusbnet *dev) |
| 106 { |
| 107 |
| 108 } |
| 109 #endif |
| 110 |
| 111 static bool device_valid(struct qcusbnet *dev) |
| 112 { |
| 113 return dev && dev->valid; |
| 114 } |
| 115 |
| 116 void qc_setdown(struct qcusbnet *dev, u8 reason) |
| 117 { |
| 118 set_bit(reason, &dev->down); |
| 119 netif_carrier_off(dev->usbnet->net); |
| 120 } |
| 121 |
| 122 void qc_cleardown(struct qcusbnet *dev, u8 reason) |
| 123 { |
| 124 clear_bit(reason, &dev->down); |
| 125 if (!dev->down) |
| 126 netif_carrier_on(dev->usbnet->net); |
| 127 } |
| 128 |
| 129 bool qc_isdown(struct qcusbnet *dev, u8 reason) |
| 130 { |
| 131 return test_bit(reason, &dev->down); |
| 132 } |
| 133 |
| 134 static void read_callback(struct urb *urb) |
| 135 { |
| 136 struct list_head *node; |
| 137 int result; |
| 138 u16 cid; |
| 139 struct client *client; |
| 140 void *data; |
| 141 void *copy; |
| 142 u16 size; |
| 143 struct qcusbnet *dev; |
| 144 unsigned long flags; |
| 145 u16 tid; |
| 146 |
| 147 if (!urb) { |
| 148 DBG("bad read URB\n"); |
| 149 return; |
| 150 } |
| 151 |
| 152 dev = urb->context; |
| 153 if (!device_valid(dev)) { |
| 154 DBG("Invalid device!\n"); |
| 155 return; |
| 156 } |
| 157 |
| 158 if (urb->status) { |
| 159 DBG("Read status = %d\n", urb->status); |
| 160 return; |
| 161 } |
| 162 |
| 163 DBG("Read %d bytes\n", urb->actual_length); |
| 164 |
| 165 data = urb->transfer_buffer; |
| 166 size = urb->actual_length; |
| 167 |
| 168 print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET, |
| 169 16, 1, data, size, true); |
| 170 |
| 171 result = qmux_parse(&cid, data, size); |
| 172 if (result < 0) { |
| 173 DBG("Read error parsing QMUX %d\n", result); |
| 174 return; |
| 175 } |
| 176 |
| 177 if (size < result + 3) { |
| 178 DBG("Data buffer too small to parse\n"); |
| 179 return; |
| 180 } |
| 181 |
| 182 if (cid == QMICTL) |
| 183 tid = *(u8 *)(data + result + 1); |
| 184 else |
| 185 tid = *(u16 *)(data + result + 1); |
| 186 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 187 |
| 188 list_for_each(node, &dev->qmi.clients) { |
| 189 client = list_entry(node, struct client, node); |
| 190 if (client->cid == cid || (client->cid | 0xff00) == cid) { |
| 191 copy = kmalloc(size, GFP_ATOMIC); |
| 192 memcpy(copy, data, size); |
| 193 if (!client_addread(dev, client->cid, tid, copy, size))
{ |
| 194 DBG("Error allocating pReadMemListEntry " |
| 195 "read will be discarded\n"); |
| 196 kfree(copy); |
| 197 spin_unlock_irqrestore(&dev->qmi.clients_lock, f
lags); |
| 198 return; |
| 199 } |
| 200 |
| 201 DBG("Creating new readListEntry for client 0x%04X, TID %
x\n", |
| 202 cid, tid); |
| 203 |
| 204 client_notify(dev, client->cid, tid); |
| 205 |
| 206 if (cid >> 8 != 0xff) |
| 207 break; |
| 208 } |
| 209 } |
| 210 |
| 211 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 212 } |
| 213 |
| 214 static void int_callback(struct urb *urb) |
| 215 { |
| 216 int status; |
| 217 int interval; |
| 218 struct qcusbnet *dev = (struct qcusbnet *)urb->context; |
| 219 |
| 220 if (!device_valid(dev)) { |
| 221 DBG("Invalid device!\n"); |
| 222 return; |
| 223 } |
| 224 |
| 225 if (urb->status) { |
| 226 DBG("Int status = %d\n", urb->status); |
| 227 if (urb->status != -EOVERFLOW) |
| 228 return; |
| 229 } else { |
| 230 if ((urb->actual_length == 8) && |
| 231 (*(u64 *)urb->transfer_buffer == CDC_GET_ENCAPSULATED_RESPON
SE)) { |
| 232 usb_fill_control_urb(dev->qmi.readurb, dev->usbnet->udev
, |
| 233 usb_rcvctrlpipe(dev->usbnet->udev,
0), |
| 234 (unsigned char *)dev->qmi.readsetup
, |
| 235 dev->qmi.readbuf, |
| 236 DEFAULT_READ_URB_LENGTH, |
| 237 read_callback, dev); |
| 238 status = usb_submit_urb(dev->qmi.readurb, GFP_ATOMIC); |
| 239 if (status) { |
| 240 DBG("Error submitting Read URB %d\n", status); |
| 241 return; |
| 242 } |
| 243 } else if ((urb->actual_length == 16) && |
| 244 (*(u64 *)urb->transfer_buffer == CDC_CONNECTION_SPEED
_CHANGE)) { |
| 245 /* if upstream or downstream is 0, stop traffic. |
| 246 * Otherwise resume it */ |
| 247 if ((*(u32 *)(urb->transfer_buffer + 8) == 0) || |
| 248 (*(u32 *)(urb->transfer_buffer + 12) == 0)) { |
| 249 qc_setdown(dev, DOWN_CDC_CONNECTION_SPEED); |
| 250 DBG("traffic stopping due to CONNECTION_SPEED_CH
ANGE\n"); |
| 251 } else { |
| 252 qc_cleardown(dev, DOWN_CDC_CONNECTION_SPEED); |
| 253 DBG("resuming traffic due to CONNECTION_SPEED_CH
ANGE\n"); |
| 254 } |
| 255 } else { |
| 256 DBG("ignoring invalid interrupt in packet\n"); |
| 257 print_hex_dump(KERN_INFO, "QCUSBNet2k: ", |
| 258 DUMP_PREFIX_OFFSET, 16, 1, |
| 259 urb->transfer_buffer, |
| 260 urb->actual_length, true); |
| 261 } |
| 262 } |
| 263 |
| 264 interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3; |
| 265 |
| 266 usb_fill_int_urb(urb, urb->dev, urb->pipe, urb->transfer_buffer, |
| 267 urb->transfer_buffer_length, urb->complete, |
| 268 urb->context, interval); |
| 269 status = usb_submit_urb(urb, GFP_ATOMIC); |
| 270 if (status) |
| 271 DBG("Error re-submitting Int URB %d\n", status); |
| 272 return; |
| 273 } |
| 274 |
| 275 int qc_startread(struct qcusbnet *dev) |
| 276 { |
| 277 int interval; |
| 278 |
| 279 if (!device_valid(dev)) { |
| 280 DBG("Invalid device!\n"); |
| 281 return -ENXIO; |
| 282 } |
| 283 |
| 284 dev->qmi.readurb = usb_alloc_urb(0, GFP_KERNEL); |
| 285 if (!dev->qmi.readurb) { |
| 286 DBG("Error allocating read urb\n"); |
| 287 return -ENOMEM; |
| 288 } |
| 289 |
| 290 dev->qmi.inturb = usb_alloc_urb(0, GFP_KERNEL); |
| 291 if (!dev->qmi.inturb) { |
| 292 usb_free_urb(dev->qmi.readurb); |
| 293 DBG("Error allocating int urb\n"); |
| 294 return -ENOMEM; |
| 295 } |
| 296 |
| 297 dev->qmi.readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL); |
| 298 if (!dev->qmi.readbuf) { |
| 299 usb_free_urb(dev->qmi.readurb); |
| 300 usb_free_urb(dev->qmi.inturb); |
| 301 DBG("Error allocating read buffer\n"); |
| 302 return -ENOMEM; |
| 303 } |
| 304 |
| 305 dev->qmi.intbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL); |
| 306 if (!dev->qmi.intbuf) { |
| 307 usb_free_urb(dev->qmi.readurb); |
| 308 usb_free_urb(dev->qmi.inturb); |
| 309 kfree(dev->qmi.readbuf); |
| 310 DBG("Error allocating int buffer\n"); |
| 311 return -ENOMEM; |
| 312 } |
| 313 |
| 314 dev->qmi.readsetup = kmalloc(sizeof(*dev->qmi.readsetup), GFP_KERNEL); |
| 315 if (!dev->qmi.readsetup) { |
| 316 usb_free_urb(dev->qmi.readurb); |
| 317 usb_free_urb(dev->qmi.inturb); |
| 318 kfree(dev->qmi.readbuf); |
| 319 kfree(dev->qmi.intbuf); |
| 320 DBG("Error allocating setup packet buffer\n"); |
| 321 return -ENOMEM; |
| 322 } |
| 323 |
| 324 dev->qmi.readsetup->type = 0xA1; |
| 325 dev->qmi.readsetup->code = 1; |
| 326 dev->qmi.readsetup->value = 0; |
| 327 dev->qmi.readsetup->index = 0; |
| 328 dev->qmi.readsetup->len = DEFAULT_READ_URB_LENGTH; |
| 329 |
| 330 interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3; |
| 331 |
| 332 usb_fill_int_urb(dev->qmi.inturb, dev->usbnet->udev, |
| 333 usb_rcvintpipe(dev->usbnet->udev, 0x81), |
| 334 dev->qmi.intbuf, DEFAULT_READ_URB_LENGTH, |
| 335 int_callback, dev, interval); |
| 336 return usb_submit_urb(dev->qmi.inturb, GFP_KERNEL); |
| 337 } |
| 338 |
| 339 void qc_stopread(struct qcusbnet *dev) |
| 340 { |
| 341 if (dev->qmi.readurb) { |
| 342 DBG("Killing read URB\n"); |
| 343 usb_kill_urb(dev->qmi.readurb); |
| 344 } |
| 345 |
| 346 if (dev->qmi.inturb) { |
| 347 DBG("Killing int URB\n"); |
| 348 usb_kill_urb(dev->qmi.inturb); |
| 349 } |
| 350 |
| 351 kfree(dev->qmi.readsetup); |
| 352 dev->qmi.readsetup = NULL; |
| 353 kfree(dev->qmi.readbuf); |
| 354 dev->qmi.readbuf = NULL; |
| 355 kfree(dev->qmi.intbuf); |
| 356 dev->qmi.intbuf = NULL; |
| 357 |
| 358 usb_free_urb(dev->qmi.readurb); |
| 359 dev->qmi.readurb = NULL; |
| 360 usb_free_urb(dev->qmi.inturb); |
| 361 dev->qmi.inturb = NULL; |
| 362 } |
| 363 |
| 364 static int read_async(struct qcusbnet *dev, u16 cid, u16 tid, |
| 365 void (*hook)(struct qcusbnet *, u16, void *), |
| 366 void *data) |
| 367 { |
| 368 struct list_head *node; |
| 369 struct client *client; |
| 370 struct readreq *readreq; |
| 371 |
| 372 unsigned long flags; |
| 373 |
| 374 if (!device_valid(dev)) { |
| 375 DBG("Invalid device!\n"); |
| 376 return -ENXIO; |
| 377 } |
| 378 |
| 379 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 380 |
| 381 client = client_bycid(dev, cid); |
| 382 if (!client) { |
| 383 DBG("Could not find matching client ID 0x%04X\n", cid); |
| 384 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 385 return -ENXIO; |
| 386 } |
| 387 |
| 388 list_for_each(node, &client->reads) { |
| 389 readreq = list_entry(node, struct readreq, node); |
| 390 if (!tid || tid == readreq->tid) { |
| 391 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 392 hook(dev, cid, data); |
| 393 return 0; |
| 394 } |
| 395 } |
| 396 |
| 397 if (!client_addnotify(dev, cid, tid, hook, data)) |
| 398 DBG("Unable to register for notification\n"); |
| 399 |
| 400 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 401 return 0; |
| 402 } |
| 403 |
| 404 static void upsem(struct qcusbnet *dev, u16 cid, void *data) |
| 405 { |
| 406 DBG("0x%04X\n", cid); |
| 407 up((struct semaphore *)data); |
| 408 } |
| 409 |
| 410 static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid) |
| 411 { |
| 412 struct list_head *node; |
| 413 int result; |
| 414 struct client *client; |
| 415 struct notifyreq *notify; |
| 416 struct semaphore sem; |
| 417 void *data; |
| 418 unsigned long flags; |
| 419 u16 size; |
| 420 |
| 421 if (!device_valid(dev)) { |
| 422 DBG("Invalid device!\n"); |
| 423 return -ENXIO; |
| 424 } |
| 425 |
| 426 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 427 |
| 428 client = client_bycid(dev, cid); |
| 429 if (!client) { |
| 430 DBG("Could not find matching client ID 0x%04X\n", cid); |
| 431 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 432 return -ENXIO; |
| 433 } |
| 434 |
| 435 while (!client_delread(dev, cid, tid, &data, &size)) { |
| 436 sema_init(&sem, 0); |
| 437 if (!client_addnotify(dev, cid, tid, upsem, &sem)) { |
| 438 DBG("unable to register for notification\n"); |
| 439 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 440 return -EFAULT; |
| 441 } |
| 442 |
| 443 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 444 |
| 445 result = down_interruptible(&sem); |
| 446 if (result) { |
| 447 DBG("Interrupted %d\n", result); |
| 448 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 449 list_for_each(node, &client->notifies) { |
| 450 notify = list_entry(node, struct notifyreq, node
); |
| 451 if (notify->data == &sem) { |
| 452 list_del(¬ify->node); |
| 453 kfree(notify); |
| 454 break; |
| 455 } |
| 456 } |
| 457 |
| 458 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 459 return -EINTR; |
| 460 } |
| 461 |
| 462 if (!device_valid(dev)) { |
| 463 DBG("Invalid device!\n"); |
| 464 return -ENXIO; |
| 465 } |
| 466 |
| 467 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 468 } |
| 469 |
| 470 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 471 *buf = data; |
| 472 return size; |
| 473 } |
| 474 |
| 475 static void write_callback(struct urb *urb) |
| 476 { |
| 477 if (!urb) { |
| 478 DBG("null urb\n"); |
| 479 return; |
| 480 } |
| 481 |
| 482 DBG("Write status/size %d/%d\n", urb->status, urb->actual_length); |
| 483 up((struct semaphore *)urb->context); |
| 484 } |
| 485 |
| 486 static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid) |
| 487 { |
| 488 int result; |
| 489 struct semaphore sem; |
| 490 struct urb *urb; |
| 491 struct urbsetup setup; |
| 492 unsigned long flags; |
| 493 |
| 494 if (!device_valid(dev)) { |
| 495 DBG("Invalid device!\n"); |
| 496 return -ENXIO; |
| 497 } |
| 498 |
| 499 urb = usb_alloc_urb(0, GFP_KERNEL); |
| 500 if (!urb) { |
| 501 DBG("URB mem error\n"); |
| 502 return -ENOMEM; |
| 503 } |
| 504 |
| 505 result = qmux_fill(cid, buf, size); |
| 506 if (result < 0) { |
| 507 usb_free_urb(urb); |
| 508 return result; |
| 509 } |
| 510 |
| 511 /* CDC Send Encapsulated Request packet */ |
| 512 setup.type = 0x21; |
| 513 setup.code = 0; |
| 514 setup.value = 0; |
| 515 setup.index = 0; |
| 516 setup.len = 0; |
| 517 setup.len = size; |
| 518 |
| 519 usb_fill_control_urb(urb, dev->usbnet->udev, |
| 520 usb_sndctrlpipe(dev->usbnet->udev, 0), |
| 521 (unsigned char *)&setup, (void *)buf, size, |
| 522 NULL, dev); |
| 523 |
| 524 DBG("Actual Write:\n"); |
| 525 print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET, |
| 526 16, 1, buf, size, true); |
| 527 |
| 528 sema_init(&sem, 0); |
| 529 |
| 530 urb->complete = write_callback; |
| 531 urb->context = &sem; |
| 532 |
| 533 result = usb_autopm_get_interface(dev->iface); |
| 534 if (result < 0) { |
| 535 DBG("unable to resume interface: %d\n", result); |
| 536 if (result == -EPERM) { |
| 537 qc_suspend(dev->iface, PMSG_SUSPEND); |
| 538 } |
| 539 return result; |
| 540 } |
| 541 |
| 542 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 543 |
| 544 if (!client_addurb(dev, cid, urb)) { |
| 545 usb_free_urb(urb); |
| 546 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 547 usb_autopm_put_interface(dev->iface); |
| 548 return -EINVAL; |
| 549 } |
| 550 |
| 551 result = usb_submit_urb(urb, GFP_KERNEL); |
| 552 if (result < 0) { |
| 553 DBG("submit URB error %d\n", result); |
| 554 if (client_delurb(dev, cid) != urb) { |
| 555 DBG("Didn't get write URB back\n"); |
| 556 } |
| 557 |
| 558 usb_free_urb(urb); |
| 559 |
| 560 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 561 usb_autopm_put_interface(dev->iface); |
| 562 return result; |
| 563 } |
| 564 |
| 565 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 566 result = down_interruptible(&sem); |
| 567 if (!device_valid(dev)) { |
| 568 DBG("Invalid device!\n"); |
| 569 return -ENXIO; |
| 570 } |
| 571 |
| 572 usb_autopm_put_interface(dev->iface); |
| 573 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 574 if (client_delurb(dev, cid) != urb) { |
| 575 DBG("Didn't get write URB back\n"); |
| 576 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 577 return -EINVAL; |
| 578 } |
| 579 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 580 |
| 581 if (!result) { |
| 582 if (!urb->status) { |
| 583 result = size; |
| 584 } else { |
| 585 DBG("bad status = %d\n", urb->status); |
| 586 result = urb->status; |
| 587 } |
| 588 } else { |
| 589 DBG("Interrupted %d !!!\n", result); |
| 590 DBG("Device may be in bad state and need reset !!!\n"); |
| 591 usb_kill_urb(urb); |
| 592 } |
| 593 |
| 594 usb_free_urb(urb); |
| 595 return result; |
| 596 } |
| 597 |
| 598 static int client_alloc(struct qcusbnet *dev, u8 type) |
| 599 { |
| 600 u16 cid; |
| 601 struct client *client; |
| 602 int result; |
| 603 void *wbuf; |
| 604 size_t wbufsize; |
| 605 void *rbuf; |
| 606 u16 rbufsize; |
| 607 unsigned long flags; |
| 608 u8 tid; |
| 609 |
| 610 if (!device_valid(dev)) { |
| 611 DBG("Invalid device!\n"); |
| 612 return -ENXIO; |
| 613 } |
| 614 |
| 615 if (type) { |
| 616 tid = atomic_add_return(1, &dev->qmi.qmitid); |
| 617 if (!tid) |
| 618 atomic_add_return(1, &dev->qmi.qmitid); |
| 619 wbuf = qmictl_new_getcid(tid, type, &wbufsize); |
| 620 if (!wbuf) |
| 621 return -ENOMEM; |
| 622 result = write_sync(dev, wbuf, wbufsize, QMICTL); |
| 623 kfree(wbuf); |
| 624 |
| 625 if (result < 0) |
| 626 return result; |
| 627 |
| 628 result = read_sync(dev, &rbuf, QMICTL, tid); |
| 629 if (result < 0) { |
| 630 DBG("bad read data %d\n", result); |
| 631 return result; |
| 632 } |
| 633 rbufsize = result; |
| 634 |
| 635 result = qmictl_alloccid_resp(rbuf, rbufsize, &cid); |
| 636 kfree(rbuf); |
| 637 |
| 638 if (result < 0) |
| 639 return result; |
| 640 } else { |
| 641 cid = 0; |
| 642 } |
| 643 |
| 644 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 645 if (client_bycid(dev, cid)) { |
| 646 DBG("Client memory already exists\n"); |
| 647 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 648 return -ETOOMANYREFS; |
| 649 } |
| 650 |
| 651 client = kmalloc(sizeof(*client), GFP_ATOMIC); |
| 652 if (!client) { |
| 653 DBG("Error allocating read list\n"); |
| 654 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 655 return -ENOMEM; |
| 656 } |
| 657 |
| 658 list_add_tail(&client->node, &dev->qmi.clients); |
| 659 client->cid = cid; |
| 660 INIT_LIST_HEAD(&client->reads); |
| 661 INIT_LIST_HEAD(&client->notifies); |
| 662 INIT_LIST_HEAD(&client->urbs); |
| 663 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 664 return cid; |
| 665 } |
| 666 |
| 667 static void client_free(struct qcusbnet *dev, u16 cid) |
| 668 { |
| 669 struct list_head *node, *tmp; |
| 670 int result; |
| 671 struct client *client; |
| 672 struct urb *urb; |
| 673 void *data; |
| 674 u16 size; |
| 675 void *wbuf; |
| 676 size_t wbufsize; |
| 677 void *rbuf; |
| 678 u16 rbufsize; |
| 679 unsigned long flags; |
| 680 u8 tid; |
| 681 |
| 682 if (!device_valid(dev)) { |
| 683 DBG("invalid device\n"); |
| 684 return; |
| 685 } |
| 686 |
| 687 DBG("releasing 0x%04X\n", cid); |
| 688 |
| 689 if (cid != QMICTL) { |
| 690 tid = atomic_add_return(1, &dev->qmi.qmitid); |
| 691 if (!tid) |
| 692 tid = atomic_add_return(1, &dev->qmi.qmitid); |
| 693 wbuf = qmictl_new_releasecid(tid, cid, &wbufsize); |
| 694 if (!wbuf) { |
| 695 DBG("memory error\n"); |
| 696 } else { |
| 697 result = write_sync(dev, wbuf, wbufsize, QMICTL); |
| 698 kfree(wbuf); |
| 699 |
| 700 if (result < 0) { |
| 701 DBG("bad write status %d\n", result); |
| 702 } else { |
| 703 result = read_sync(dev, &rbuf, QMICTL, tid); |
| 704 if (result < 0) { |
| 705 DBG("bad read status %d\n", result); |
| 706 } else { |
| 707 rbufsize = result; |
| 708 result = qmictl_freecid_resp(rbuf, rbufs
ize); |
| 709 kfree(rbuf); |
| 710 if (result < 0) |
| 711 DBG("error %d parsing response\n
", result); |
| 712 } |
| 713 } |
| 714 } |
| 715 } |
| 716 |
| 717 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 718 list_for_each_safe(node, tmp, &dev->qmi.clients) { |
| 719 client = list_entry(node, struct client, node); |
| 720 if (client->cid == cid) { |
| 721 while (client_notify(dev, cid, 0)) { |
| 722 ; |
| 723 } |
| 724 |
| 725 urb = client_delurb(dev, cid); |
| 726 while (urb != NULL) { |
| 727 usb_kill_urb(urb); |
| 728 usb_free_urb(urb); |
| 729 urb = client_delurb(dev, cid); |
| 730 } |
| 731 |
| 732 while (client_delread(dev, cid, 0, &data, &size)) |
| 733 kfree(data); |
| 734 |
| 735 list_del(&client->node); |
| 736 kfree(client); |
| 737 } |
| 738 } |
| 739 |
| 740 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 741 } |
| 742 |
| 743 struct client *client_bycid(struct qcusbnet *dev, u16 cid) |
| 744 { |
| 745 struct list_head *node; |
| 746 struct client *client; |
| 747 |
| 748 if (!device_valid(dev)) { |
| 749 DBG("Invalid device\n"); |
| 750 return NULL; |
| 751 } |
| 752 |
| 753 assert_locked(dev); |
| 754 |
| 755 list_for_each(node, &dev->qmi.clients) { |
| 756 client = list_entry(node, struct client, node); |
| 757 if (client->cid == cid) |
| 758 return client; |
| 759 } |
| 760 |
| 761 DBG("Could not find client mem 0x%04X\n", cid); |
| 762 return NULL; |
| 763 } |
| 764 |
| 765 static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data, |
| 766 u16 size) |
| 767 { |
| 768 struct client *client; |
| 769 struct readreq *req; |
| 770 |
| 771 assert_locked(dev); |
| 772 |
| 773 client = client_bycid(dev, cid); |
| 774 if (!client) { |
| 775 DBG("Could not find this client's memory 0x%04X\n", cid); |
| 776 return false; |
| 777 } |
| 778 |
| 779 req = kmalloc(sizeof(*req), GFP_ATOMIC); |
| 780 if (!req) { |
| 781 DBG("Mem error\n"); |
| 782 return false; |
| 783 } |
| 784 |
| 785 req->data = data; |
| 786 req->size = size; |
| 787 req->tid = tid; |
| 788 |
| 789 list_add_tail(&req->node, &client->reads); |
| 790 |
| 791 return true; |
| 792 } |
| 793 |
| 794 static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, |
| 795 u16 *size) |
| 796 { |
| 797 struct client *client; |
| 798 struct readreq *req; |
| 799 struct list_head *node; |
| 800 |
| 801 assert_locked(dev); |
| 802 |
| 803 client = client_bycid(dev, cid); |
| 804 if (!client) { |
| 805 DBG("Could not find this client's memory 0x%04X\n", cid); |
| 806 return false; |
| 807 } |
| 808 |
| 809 list_for_each(node, &client->reads) { |
| 810 req = list_entry(node, struct readreq, node); |
| 811 if (!tid || tid == req->tid) { |
| 812 *data = req->data; |
| 813 *size = req->size; |
| 814 list_del(&req->node); |
| 815 kfree(req); |
| 816 return true; |
| 817 } |
| 818 |
| 819 DBG("skipping 0x%04X data TID = %x\n", cid, req->tid); |
| 820 } |
| 821 |
| 822 DBG("No read memory to pop, Client 0x%04X, TID = %x\n", cid, tid); |
| 823 return false; |
| 824 } |
| 825 |
| 826 static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid, |
| 827 void (*hook)(struct qcusbnet *, u16, void *), |
| 828 void *data) |
| 829 { |
| 830 struct client *client; |
| 831 struct notifyreq *req; |
| 832 |
| 833 assert_locked(dev); |
| 834 |
| 835 client = client_bycid(dev, cid); |
| 836 if (!client) { |
| 837 DBG("Could not find this client's memory 0x%04X\n", cid); |
| 838 return false; |
| 839 } |
| 840 |
| 841 req = kmalloc(sizeof(*req), GFP_ATOMIC); |
| 842 if (!req) { |
| 843 DBG("Mem error\n"); |
| 844 return false; |
| 845 } |
| 846 |
| 847 list_add_tail(&req->node, &client->notifies); |
| 848 req->func = hook; |
| 849 req->data = data; |
| 850 req->tid = tid; |
| 851 |
| 852 return true; |
| 853 } |
| 854 |
| 855 static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid) |
| 856 { |
| 857 struct client *client; |
| 858 struct notifyreq *delnotify, *notify; |
| 859 struct list_head *node; |
| 860 |
| 861 assert_locked(dev); |
| 862 |
| 863 client = client_bycid(dev, cid); |
| 864 if (!client) { |
| 865 DBG("Could not find this client's memory 0x%04X\n", cid); |
| 866 return false; |
| 867 } |
| 868 |
| 869 delnotify = NULL; |
| 870 |
| 871 list_for_each(node, &client->notifies) { |
| 872 notify = list_entry(node, struct notifyreq, node); |
| 873 if (!tid || !notify->tid || tid == notify->tid) { |
| 874 delnotify = notify; |
| 875 break; |
| 876 } |
| 877 |
| 878 DBG("skipping data TID = %x\n", notify->tid); |
| 879 } |
| 880 |
| 881 if (delnotify) { |
| 882 list_del(&delnotify->node); |
| 883 if (delnotify->func) { |
| 884 spin_unlock(&dev->qmi.clients_lock); |
| 885 delnotify->func(dev, cid, delnotify->data); |
| 886 spin_lock(&dev->qmi.clients_lock); |
| 887 } |
| 888 kfree(delnotify); |
| 889 return true; |
| 890 } |
| 891 |
| 892 DBG("no one to notify for TID %x\n", tid); |
| 893 return false; |
| 894 } |
| 895 |
| 896 static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb) |
| 897 { |
| 898 struct client *client; |
| 899 struct urbreq *req; |
| 900 |
| 901 assert_locked(dev); |
| 902 |
| 903 client = client_bycid(dev, cid); |
| 904 if (!client) { |
| 905 DBG("Could not find this client's memory 0x%04X\n", cid); |
| 906 return false; |
| 907 } |
| 908 |
| 909 req = kmalloc(sizeof(*req), GFP_ATOMIC); |
| 910 if (!req) { |
| 911 DBG("Mem error\n"); |
| 912 return false; |
| 913 } |
| 914 |
| 915 req->urb = urb; |
| 916 list_add_tail(&req->node, &client->urbs); |
| 917 |
| 918 return true; |
| 919 } |
| 920 |
| 921 static struct urb *client_delurb(struct qcusbnet *dev, u16 cid) |
| 922 { |
| 923 struct client *client; |
| 924 struct urbreq *req; |
| 925 struct urb *urb; |
| 926 |
| 927 assert_locked(dev); |
| 928 |
| 929 client = client_bycid(dev, cid); |
| 930 if (!client) { |
| 931 DBG("Could not find this client's memory 0x%04X\n", cid); |
| 932 return NULL; |
| 933 } |
| 934 |
| 935 if (list_empty(&client->urbs)) { |
| 936 DBG("No URB's to pop\n"); |
| 937 return NULL; |
| 938 } |
| 939 |
| 940 req = list_first_entry(&client->urbs, struct urbreq, node); |
| 941 list_del(&req->node); |
| 942 urb = req->urb; |
| 943 kfree(req); |
| 944 return urb; |
| 945 } |
| 946 |
| 947 static int devqmi_open(struct inode *inode, struct file *file) |
| 948 { |
| 949 struct qmihandle *handle; |
| 950 struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev)
; |
| 951 struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi); |
| 952 |
| 953 if (!device_valid(dev)) { |
| 954 DBG("Invalid device\n"); |
| 955 return -ENXIO; |
| 956 } |
| 957 |
| 958 file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL); |
| 959 if (!file->private_data) { |
| 960 DBG("Mem error\n"); |
| 961 return -ENOMEM; |
| 962 } |
| 963 |
| 964 handle = (struct qmihandle *)file->private_data; |
| 965 handle->cid = (u16)-1; |
| 966 handle->dev = dev; |
| 967 |
| 968 DBG("%p %04x", handle, handle->cid); |
| 969 |
| 970 return 0; |
| 971 } |
| 972 |
| 973 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd
, unsigned long arg) |
| 974 { |
| 975 int result; |
| 976 u32 vidpid; |
| 977 |
| 978 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 979 |
| 980 DBG("%p %04x %08x", handle, handle->cid, cmd); |
| 981 |
| 982 if (!handle) { |
| 983 DBG("Bad file data\n"); |
| 984 return -EBADF; |
| 985 } |
| 986 |
| 987 if (!device_valid(handle->dev)) { |
| 988 DBG("Invalid device! Updating f_ops\n"); |
| 989 file->f_op = file->f_dentry->d_inode->i_fop; |
| 990 return -ENXIO; |
| 991 } |
| 992 |
| 993 switch (cmd) { |
| 994 case IOCTL_QMI_GET_SERVICE_FILE: |
| 995 |
| 996 DBG("Setting up QMI for service %lu\n", arg); |
| 997 if (!(u8)arg) { |
| 998 DBG("Cannot use QMICTL from userspace\n"); |
| 999 return -EINVAL; |
| 1000 } |
| 1001 |
| 1002 if (handle->cid != (u16)-1) { |
| 1003 DBG("Close the current connection before opening a new o
ne\n"); |
| 1004 return -EBADR; |
| 1005 } |
| 1006 |
| 1007 result = client_alloc(handle->dev, (u8)arg); |
| 1008 if (result < 0) |
| 1009 return result; |
| 1010 handle->cid = result; |
| 1011 |
| 1012 return 0; |
| 1013 break; |
| 1014 |
| 1015 |
| 1016 case IOCTL_QMI_GET_DEVICE_VIDPID: |
| 1017 if (!arg) { |
| 1018 DBG("Bad VIDPID buffer\n"); |
| 1019 return -EINVAL; |
| 1020 } |
| 1021 |
| 1022 if (!handle->dev->usbnet) { |
| 1023 DBG("Bad usbnet\n"); |
| 1024 return -ENOMEM; |
| 1025 } |
| 1026 |
| 1027 if (!handle->dev->usbnet->udev) { |
| 1028 DBG("Bad udev\n"); |
| 1029 return -ENOMEM; |
| 1030 } |
| 1031 |
| 1032 vidpid = ((le16_to_cpu(handle->dev->usbnet->udev->descriptor.idV
endor) << 16) |
| 1033 + le16_to_cpu(handle->dev->usbnet->udev->descriptor.id
Product)); |
| 1034 |
| 1035 result = copy_to_user((unsigned int *)arg, &vidpid, 4); |
| 1036 if (result) |
| 1037 DBG("Copy to userspace failure\n"); |
| 1038 |
| 1039 return result; |
| 1040 break; |
| 1041 |
| 1042 case IOCTL_QMI_GET_DEVICE_MEID: |
| 1043 if (!arg) { |
| 1044 DBG("Bad MEID buffer\n"); |
| 1045 return -EINVAL; |
| 1046 } |
| 1047 |
| 1048 result = copy_to_user((unsigned int *)arg, &handle->dev->meid[0]
, 14); |
| 1049 if (result) |
| 1050 DBG("copy to userspace failure\n"); |
| 1051 |
| 1052 return result; |
| 1053 break; |
| 1054 default: |
| 1055 return -EBADRQC; |
| 1056 } |
| 1057 } |
| 1058 |
| 1059 static int devqmi_close(struct file *file, fl_owner_t ftable) |
| 1060 { |
| 1061 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 1062 struct task_struct *task; |
| 1063 struct fdtable *fdtable; |
| 1064 int count = 0; |
| 1065 int used = 0; |
| 1066 unsigned long flags; |
| 1067 |
| 1068 if (!handle) { |
| 1069 DBG("bad file data\n"); |
| 1070 return -EBADF; |
| 1071 } |
| 1072 |
| 1073 if (file_count(file) != 1) { |
| 1074 rcu_read_lock(); |
| 1075 for_each_process(task) { |
| 1076 if (!task || !task->files) |
| 1077 continue; |
| 1078 spin_lock_irqsave(&task->files->file_lock, flags); |
| 1079 fdtable = files_fdtable(task->files); |
| 1080 for (count = 0; count < fdtable->max_fds; count++) { |
| 1081 /* Before this function was called, this file wa
s removed |
| 1082 * from our task's file table so if we find it i
n a file |
| 1083 * table then it is being used by another task |
| 1084 */ |
| 1085 if (fdtable->fd[count] == file) { |
| 1086 used++; |
| 1087 break; |
| 1088 } |
| 1089 } |
| 1090 spin_unlock_irqrestore(&task->files->file_lock, flags); |
| 1091 } |
| 1092 rcu_read_unlock(); |
| 1093 |
| 1094 if (used > 0) { |
| 1095 DBG("not closing, as this FD is open by %d other process
\n", used); |
| 1096 return 0; |
| 1097 } |
| 1098 } |
| 1099 |
| 1100 if (!device_valid(handle->dev)) { |
| 1101 DBG("Invalid device! Updating f_ops\n"); |
| 1102 file->f_op = file->f_dentry->d_inode->i_fop; |
| 1103 return -ENXIO; |
| 1104 } |
| 1105 |
| 1106 DBG("0x%04X\n", handle->cid); |
| 1107 |
| 1108 file->private_data = NULL; |
| 1109 |
| 1110 if (handle->cid != (u16)-1) |
| 1111 client_free(handle->dev, handle->cid); |
| 1112 |
| 1113 kfree(handle); |
| 1114 return 0; |
| 1115 } |
| 1116 |
| 1117 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, |
| 1118 loff_t *pos) |
| 1119 { |
| 1120 int result; |
| 1121 void *data = NULL; |
| 1122 void *smalldata; |
| 1123 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 1124 |
| 1125 if (!handle) { |
| 1126 DBG("Bad file data\n"); |
| 1127 return -EBADF; |
| 1128 } |
| 1129 |
| 1130 if (!device_valid(handle->dev)) { |
| 1131 DBG("Invalid device! Updating f_ops\n"); |
| 1132 file->f_op = file->f_dentry->d_inode->i_fop; |
| 1133 return -ENXIO; |
| 1134 } |
| 1135 |
| 1136 if (handle->cid == (u16)-1) { |
| 1137 DBG("Client ID must be set before reading 0x%04X\n", |
| 1138 handle->cid); |
| 1139 return -EBADR; |
| 1140 } |
| 1141 |
| 1142 result = read_sync(handle->dev, &data, handle->cid, 0); |
| 1143 if (result <= 0) |
| 1144 return result; |
| 1145 |
| 1146 result -= qmux_size; |
| 1147 smalldata = data + qmux_size; |
| 1148 |
| 1149 if (result > size) { |
| 1150 DBG("Read data is too large for amount user has requested\n"); |
| 1151 kfree(data); |
| 1152 return -EOVERFLOW; |
| 1153 } |
| 1154 |
| 1155 if (copy_to_user(buf, smalldata, result)) { |
| 1156 DBG("Error copying read data to user\n"); |
| 1157 result = -EFAULT; |
| 1158 } |
| 1159 |
| 1160 kfree(data); |
| 1161 return result; |
| 1162 } |
| 1163 |
| 1164 static ssize_t devqmi_write(struct file *file, const char __user * buf, |
| 1165 size_t size, loff_t *pos) |
| 1166 { |
| 1167 int status; |
| 1168 void *wbuf; |
| 1169 struct qmihandle *handle = (struct qmihandle *)file->private_data; |
| 1170 |
| 1171 if (!handle) { |
| 1172 DBG("Bad file data\n"); |
| 1173 return -EBADF; |
| 1174 } |
| 1175 |
| 1176 if (!device_valid(handle->dev)) { |
| 1177 DBG("Invalid device! Updating f_ops\n"); |
| 1178 file->f_op = file->f_dentry->d_inode->i_fop; |
| 1179 return -ENXIO; |
| 1180 } |
| 1181 |
| 1182 if (handle->cid == (u16)-1) { |
| 1183 DBG("Client ID must be set before writing 0x%04X\n", |
| 1184 handle->cid); |
| 1185 return -EBADR; |
| 1186 } |
| 1187 |
| 1188 wbuf = kmalloc(size + qmux_size, GFP_KERNEL); |
| 1189 if (!wbuf) |
| 1190 return -ENOMEM; |
| 1191 status = copy_from_user(wbuf + qmux_size, buf, size); |
| 1192 if (status) { |
| 1193 DBG("Unable to copy data from userspace %d\n", status); |
| 1194 kfree(wbuf); |
| 1195 return status; |
| 1196 } |
| 1197 |
| 1198 status = write_sync(handle->dev, wbuf, size + qmux_size, |
| 1199 handle->cid); |
| 1200 |
| 1201 kfree(wbuf); |
| 1202 if (status == size + qmux_size) |
| 1203 return size; |
| 1204 return status; |
| 1205 } |
| 1206 |
| 1207 int qc_register(struct qcusbnet *dev) |
| 1208 { |
| 1209 int result; |
| 1210 int qmiidx = 0; |
| 1211 dev_t devno; |
| 1212 char *name; |
| 1213 |
| 1214 dev->valid = true; |
| 1215 result = client_alloc(dev, QMICTL); |
| 1216 if (result) { |
| 1217 dev->valid = false; |
| 1218 return result; |
| 1219 } |
| 1220 atomic_set(&dev->qmi.qmitid, 1); |
| 1221 |
| 1222 result = qc_startread(dev); |
| 1223 if (result) { |
| 1224 dev->valid = false; |
| 1225 return result; |
| 1226 } |
| 1227 |
| 1228 if (!qmi_ready(dev, 30000)) { |
| 1229 DBG("Device unresponsive to QMI\n"); |
| 1230 return -ETIMEDOUT; |
| 1231 } |
| 1232 |
| 1233 result = setup_wds_callback(dev); |
| 1234 if (result) { |
| 1235 dev->valid = false; |
| 1236 return result; |
| 1237 } |
| 1238 |
| 1239 result = qmidms_getmeid(dev); |
| 1240 if (result) { |
| 1241 dev->valid = false; |
| 1242 return result; |
| 1243 } |
| 1244 |
| 1245 result = alloc_chrdev_region(&devno, 0, 1, "qcqmi"); |
| 1246 if (result < 0) |
| 1247 return result; |
| 1248 |
| 1249 cdev_init(&dev->qmi.cdev, &devqmi_fops); |
| 1250 dev->qmi.cdev.owner = THIS_MODULE; |
| 1251 dev->qmi.cdev.ops = &devqmi_fops; |
| 1252 |
| 1253 result = cdev_add(&dev->qmi.cdev, devno, 1); |
| 1254 if (result) { |
| 1255 DBG("error adding cdev\n"); |
| 1256 return result; |
| 1257 } |
| 1258 |
| 1259 name = strstr(dev->usbnet->net->name, "usb"); |
| 1260 if (!name) { |
| 1261 DBG("Bad net name: %s\n", dev->usbnet->net->name); |
| 1262 return -ENXIO; |
| 1263 } |
| 1264 name += strlen("usb"); |
| 1265 qmiidx = simple_strtoul(name, NULL, 10); |
| 1266 if (qmiidx < 0) { |
| 1267 DBG("Bad minor number\n"); |
| 1268 return -ENXIO; |
| 1269 } |
| 1270 |
| 1271 printk(KERN_INFO "creating qcqmi%d\n", qmiidx); |
| 1272 device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx); |
| 1273 |
| 1274 dev->qmi.devnum = devno; |
| 1275 return 0; |
| 1276 } |
| 1277 |
| 1278 void qc_deregister(struct qcusbnet *dev) |
| 1279 { |
| 1280 struct list_head *node, *tmp; |
| 1281 struct client *client; |
| 1282 struct inode *inode; |
| 1283 struct list_head *inodes; |
| 1284 struct task_struct *task; |
| 1285 struct fdtable *fdtable; |
| 1286 struct file *file; |
| 1287 unsigned long flags; |
| 1288 int count = 0; |
| 1289 |
| 1290 if (!device_valid(dev)) { |
| 1291 DBG("wrong device\n"); |
| 1292 return; |
| 1293 } |
| 1294 |
| 1295 list_for_each_safe(node, tmp, &dev->qmi.clients) { |
| 1296 client = list_entry(node, struct client, node); |
| 1297 DBG("release 0x%04X\n", client->cid); |
| 1298 client_free(dev, client->cid); |
| 1299 } |
| 1300 |
| 1301 qc_stopread(dev); |
| 1302 dev->valid = false; |
| 1303 list_for_each(inodes, &dev->qmi.cdev.list) { |
| 1304 inode = container_of(inodes, struct inode, i_devices); |
| 1305 if (inode != NULL && !IS_ERR(inode)) { |
| 1306 rcu_read_lock(); |
| 1307 for_each_process(task) { |
| 1308 if (!task || !task->files) |
| 1309 continue; |
| 1310 spin_lock_irqsave(&task->files->file_lock, flags
); |
| 1311 fdtable = files_fdtable(task->files); |
| 1312 for (count = 0; count < fdtable->max_fds; count+
+) { |
| 1313 file = fdtable->fd[count]; |
| 1314 if (file != NULL && file->f_dentry != N
ULL) { |
| 1315 if (file->f_dentry->d_inode == i
node) { |
| 1316 rcu_assign_pointer(fdtab
le->fd[count], NULL); |
| 1317 spin_unlock_irqrestore(&
task->files->file_lock, flags); |
| 1318 DBG("forcing close of op
en file handle\n"); |
| 1319 filp_close(file, task->f
iles); |
| 1320 spin_lock_irqsave(&task-
>files->file_lock, flags); |
| 1321 } |
| 1322 } |
| 1323 } |
| 1324 spin_unlock_irqrestore(&task->files->file_lock,
flags); |
| 1325 } |
| 1326 rcu_read_unlock(); |
| 1327 } |
| 1328 } |
| 1329 |
| 1330 if (!IS_ERR(dev->qmi.devclass)) |
| 1331 device_destroy(dev->qmi.devclass, dev->qmi.devnum); |
| 1332 cdev_del(&dev->qmi.cdev); |
| 1333 unregister_chrdev_region(dev->qmi.devnum, 1); |
| 1334 } |
| 1335 |
| 1336 static bool qmi_ready(struct qcusbnet *dev, u16 timeout) |
| 1337 { |
| 1338 int result; |
| 1339 void *wbuf; |
| 1340 size_t wbufsize; |
| 1341 void *rbuf; |
| 1342 u16 rbufsize; |
| 1343 struct semaphore sem; |
| 1344 u16 now; |
| 1345 unsigned long flags; |
| 1346 u8 tid; |
| 1347 |
| 1348 if (!device_valid(dev)) { |
| 1349 DBG("Invalid device\n"); |
| 1350 return -EFAULT; |
| 1351 } |
| 1352 |
| 1353 |
| 1354 for (now = 0; now < timeout; now += 100) { |
| 1355 sema_init(&sem, 0); |
| 1356 |
| 1357 tid = atomic_add_return(1, &dev->qmi.qmitid); |
| 1358 if (!tid) |
| 1359 tid = atomic_add_return(1, &dev->qmi.qmitid); |
| 1360 kfree(wbuf); |
| 1361 wbuf = qmictl_new_ready(tid, &wbufsize); |
| 1362 if (!wbuf) |
| 1363 return -ENOMEM; |
| 1364 |
| 1365 result = read_async(dev, QMICTL, tid, upsem, &sem); |
| 1366 if (result) { |
| 1367 kfree(wbuf); |
| 1368 return false; |
| 1369 } |
| 1370 |
| 1371 write_sync(dev, wbuf, wbufsize, QMICTL); |
| 1372 |
| 1373 msleep(100); |
| 1374 if (!down_trylock(&sem)) { |
| 1375 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 1376 if (client_delread(dev, QMICTL, tid, &rbuf, &rbufsize))
{ |
| 1377 spin_unlock_irqrestore(&dev->qmi.clients_lock, f
lags); |
| 1378 kfree(rbuf); |
| 1379 break; |
| 1380 } |
| 1381 } else { |
| 1382 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 1383 client_notify(dev, QMICTL, tid); |
| 1384 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 1385 } |
| 1386 } |
| 1387 |
| 1388 kfree(wbuf); |
| 1389 |
| 1390 if (now >= timeout) |
| 1391 return false; |
| 1392 |
| 1393 DBG("QMI Ready after %u milliseconds\n", now); |
| 1394 |
| 1395 /* 3580 and newer doesn't need a delay; older needs 5000ms */ |
| 1396 if (qcusbnet2k_fwdelay) |
| 1397 msleep(qcusbnet2k_fwdelay * 1000); |
| 1398 |
| 1399 return true; |
| 1400 } |
| 1401 |
| 1402 static void wds_callback(struct qcusbnet *dev, u16 cid, void *data) |
| 1403 { |
| 1404 bool ret; |
| 1405 int result; |
| 1406 void *rbuf; |
| 1407 u16 rbufsize; |
| 1408 |
| 1409 struct net_device_stats *stats = &(dev->usbnet->net->stats); |
| 1410 |
| 1411 struct qmiwds_stats dstats = { |
| 1412 .txok = (u32)-1, |
| 1413 .rxok = (u32)-1, |
| 1414 .txerr = (u32)-1, |
| 1415 .rxerr = (u32)-1, |
| 1416 .txofl = (u32)-1, |
| 1417 .rxofl = (u32)-1, |
| 1418 .txbytesok = (u64)-1, |
| 1419 .rxbytesok = (u64)-1, |
| 1420 }; |
| 1421 unsigned long flags; |
| 1422 |
| 1423 if (!device_valid(dev)) { |
| 1424 DBG("Invalid device\n"); |
| 1425 return; |
| 1426 } |
| 1427 |
| 1428 spin_lock_irqsave(&dev->qmi.clients_lock, flags); |
| 1429 ret = client_delread(dev, cid, 0, &rbuf, &rbufsize); |
| 1430 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); |
| 1431 |
| 1432 if (!ret) { |
| 1433 DBG("WDS callback failed to get data\n"); |
| 1434 return; |
| 1435 } |
| 1436 |
| 1437 dstats.linkstate = !qc_isdown(dev, DOWN_NO_NDIS_CONNECTION); |
| 1438 dstats.reconfigure = false; |
| 1439 |
| 1440 result = qmiwds_event_resp(rbuf, rbufsize, &dstats); |
| 1441 if (result < 0) { |
| 1442 DBG("bad WDS packet\n"); |
| 1443 } else { |
| 1444 if (dstats.txofl != (u32)-1) |
| 1445 stats->tx_fifo_errors = dstats.txofl; |
| 1446 |
| 1447 if (dstats.rxofl != (u32)-1) |
| 1448 stats->rx_fifo_errors = dstats.rxofl; |
| 1449 |
| 1450 if (dstats.txerr != (u32)-1) |
| 1451 stats->tx_errors = dstats.txerr; |
| 1452 |
| 1453 if (dstats.rxerr != (u32)-1) |
| 1454 stats->rx_errors = dstats.rxerr; |
| 1455 |
| 1456 if (dstats.txok != (u32)-1) |
| 1457 stats->tx_packets = dstats.txok + stats->tx_errors; |
| 1458 |
| 1459 if (dstats.rxok != (u32)-1) |
| 1460 stats->rx_packets = dstats.rxok + stats->rx_errors; |
| 1461 |
| 1462 if (dstats.txbytesok != (u64)-1) |
| 1463 stats->tx_bytes = dstats.txbytesok; |
| 1464 |
| 1465 if (dstats.rxbytesok != (u64)-1) |
| 1466 stats->rx_bytes = dstats.rxbytesok; |
| 1467 |
| 1468 if (dstats.reconfigure) { |
| 1469 DBG("Net device link reset\n"); |
| 1470 qc_setdown(dev, DOWN_NO_NDIS_CONNECTION); |
| 1471 qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION); |
| 1472 } else { |
| 1473 if (dstats.linkstate) { |
| 1474 DBG("Net device link is connected\n"); |
| 1475 qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION); |
| 1476 } else { |
| 1477 DBG("Net device link is disconnected\n"); |
| 1478 qc_setdown(dev, DOWN_NO_NDIS_CONNECTION); |
| 1479 } |
| 1480 } |
| 1481 } |
| 1482 |
| 1483 kfree(rbuf); |
| 1484 |
| 1485 result = read_async(dev, cid, 0, wds_callback, data); |
| 1486 if (result != 0) |
| 1487 DBG("unable to setup next async read\n"); |
| 1488 } |
| 1489 |
| 1490 static int setup_wds_callback(struct qcusbnet *dev) |
| 1491 { |
| 1492 int result; |
| 1493 void *buf; |
| 1494 size_t size; |
| 1495 u16 cid; |
| 1496 |
| 1497 if (!device_valid(dev)) { |
| 1498 DBG("Invalid device\n"); |
| 1499 return -EFAULT; |
| 1500 } |
| 1501 |
| 1502 result = client_alloc(dev, QMIWDS); |
| 1503 if (result < 0) |
| 1504 return result; |
| 1505 cid = result; |
| 1506 |
| 1507 buf = qmiwds_new_seteventreport(1, &size); |
| 1508 if (!buf) |
| 1509 return -ENOMEM; |
| 1510 |
| 1511 result = write_sync(dev, buf, size, cid); |
| 1512 kfree(buf); |
| 1513 |
| 1514 if (result < 0) { |
| 1515 return result; |
| 1516 } |
| 1517 |
| 1518 buf = qmiwds_new_getpkgsrvcstatus(2, &size); |
| 1519 if (buf == NULL) |
| 1520 return -ENOMEM; |
| 1521 |
| 1522 result = write_sync(dev, buf, size, cid); |
| 1523 kfree(buf); |
| 1524 |
| 1525 if (result < 0) |
| 1526 return result; |
| 1527 |
| 1528 result = read_async(dev, cid, 0, wds_callback, NULL); |
| 1529 if (result) { |
| 1530 DBG("unable to setup async read\n"); |
| 1531 return result; |
| 1532 } |
| 1533 |
| 1534 result = usb_control_msg(dev->usbnet->udev, |
| 1535 usb_sndctrlpipe(dev->usbnet->udev, 0), |
| 1536 0x22, 0x21, 1, 0, NULL, 0, 100); |
| 1537 if (result < 0) { |
| 1538 DBG("Bad SetControlLineState status %d\n", result); |
| 1539 return result; |
| 1540 } |
| 1541 |
| 1542 return 0; |
| 1543 } |
| 1544 |
| 1545 static int qmidms_getmeid(struct qcusbnet *dev) |
| 1546 { |
| 1547 int result; |
| 1548 void *wbuf; |
| 1549 size_t wbufsize; |
| 1550 void *rbuf; |
| 1551 u16 rbufsize; |
| 1552 u16 cid; |
| 1553 |
| 1554 if (!device_valid(dev)) { |
| 1555 DBG("Invalid device\n"); |
| 1556 return -EFAULT; |
| 1557 } |
| 1558 |
| 1559 result = client_alloc(dev, QMIDMS); |
| 1560 if (result < 0) |
| 1561 return result; |
| 1562 cid = result; |
| 1563 |
| 1564 wbuf = qmidms_new_getmeid(1, &wbufsize); |
| 1565 if (!wbuf) |
| 1566 return -ENOMEM; |
| 1567 |
| 1568 result = write_sync(dev, wbuf, wbufsize, cid); |
| 1569 kfree(wbuf); |
| 1570 |
| 1571 if (result < 0) |
| 1572 return result; |
| 1573 |
| 1574 result = read_sync(dev, &rbuf, cid, 1); |
| 1575 if (result < 0) |
| 1576 return result; |
| 1577 rbufsize = result; |
| 1578 |
| 1579 result = qmidms_meid_resp(rbuf, rbufsize, &dev->meid[0], 14); |
| 1580 kfree(rbuf); |
| 1581 |
| 1582 if (result < 0) { |
| 1583 DBG("bad get MEID resp\n"); |
| 1584 memset(&dev->meid[0], '0', 14); |
| 1585 } |
| 1586 |
| 1587 client_free(dev, cid); |
| 1588 return 0; |
| 1589 } |
| 1590 |
| 1591 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR); |
| 1592 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware"); |
OLD | NEW |