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